//
you're reading...
COM, Programming Issues/Tips

Noncreatable COM Objects – Part 1.

1. Introduction.

1.1 COM coclasses may be declared as noncreatable and interfaces from instances of such coclasses can be stipulated to be obtainable only from interface methods of other coclasses.

1.2 Making a coclass noncreatable can be a good option if instances of that coclass are meant to be used internally and are not meant for public consumption.

1.3 Exposing one or more interfaces from that coclass may be one way to allow external clients access to limited functionality.

1.4 This series of articles will explore how to develop noncreatable coclasses and how to ensure their “uncreatability”. Also, possible problems associated with noncreatable coclasses are studied and suggested solutions provided.

1.5 Throughout this series of articles, I shall be using ATL-based COM classes developed using Visual Studio 2008.

2. Noncreatable Coclasses.

2.1 A noncreatable ATL COM class is generated in the same way like any other : using the ATL wizards. After its basic C++ class code has been generated, its coclass declaration in the IDL file must be decorated with the “nonreatable” IDL attribute. The following is an example :

[
	uuid(1AD3A567-2A66-4A2C-90C3-59A194F7F05E),
	helpstring("NonCreatableClass01 Class"),
	noncreatable
]
coclass NonCreatableClass01
{
	[default] interface INonCreatableClass01;
};

2.2 The “noncreatable” attribute is used to indicate to development tools such as Visual Basic 6.0 of the non-creatability of thus attributed coclasses. Aware of this knowledge, these tools can advise (through compilation errors or warnings) of the potential errors that can occur with the inclusion of coclass creation code.

2.3 Thereafter, in the context of ATL, there are 2 ways to make an ATL COM class non-creatable :

  • Non-usage of the OBJECT_ENTRY_AUTO() macro.
  • Usage of the OBJECT_ENTRY_NON_CREATEABLE_EX_AUTO() macro.

These macros are connected with the Object Map which exists for every COM server written using ATL.

2.4. The ATL Object Map is an array of _ATL_OBJMAP_ENTRY records each of which represents an ATL C++ class that implements a coclass. For a good discussion of ATL Object Maps, please refer to : http://369o.com/data/books/atl/0321159624/ch05lev1sec3.html

2.5 In section 3 onwards we shall do a case study of a COM server which renders itself non-creatable by omiting the use of the OBJECT_ENTRY_AUTO() macro.

2.6 In part 2 of this series of articles, we shall examine a noncreatable COM server which uses the OBJECT_ENTRY_NON_CREATEABLE_EX_AUTO() macro.

3. A Sample COM Class which Omits the OBJECT_ENTRY_AUTO() Macro.

3.1 Shown below is an IDL listing that includes the coclass definition of NonCreatableClass01 which exposes an interface named INonCreatableClass01 :

// NonCreatableObjectsServer.idl : IDL source for NonCreatableObjectsServer
//

// This file will be processed by the MIDL tool to
// produce the type library (NonCreatableObjectsServer.tlb) and marshalling code.

import "oaidl.idl";
import "ocidl.idl";

[
	uuid(055F822B-739D-483C-8740-9BA9F5380D2D),
	version(1.0),
	helpstring("NonCreatableObjectsServer 1.0 Type Library")
]
library NonCreatableObjectsServerLib
{
	importlib("stdole2.tlb");

	[
		object,
		uuid(2C8F4392-E654-4192-A25B-1742F30DE8B8),
		dual,
		nonextensible,
		helpstring("INonCreatableClass01 Interface"),
		pointer_default(unique)
	]
	interface INonCreatableClass01 : IDispatch
	{
		[id(1), helpstring("method Method01")] HRESULT Method01(void);
	};

	[
		uuid(1AD3A567-2A66-4A2C-90C3-59A194F7F05E),
		helpstring("NonCreatableClass01 Class"),
		noncreatable
	]
	coclass NonCreatableClass01
	{
		[default] interface INonCreatableClass01;
	};
};

The INonCreatableClass01 interface exposes only one method Method01().

3.2 Shown below is the full listing of the C++ class CNonCreatableClass01 which is the implementation for the NonCreatableClass01 coclass :

// NonCreatableClass01.h : Declaration of the CNonCreatableClass01

#pragma once
#include "resource.h"       // main symbols

#include "NonCreatableObjectsServer_i.h"

#if defined(_WIN32_WCE) && !defined(_CE_DCOM) && !defined(_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA)
#error "Single-threaded COM objects are not properly supported on Windows CE platform, \
such as the Windows Mobile platforms that do not include full DCOM support. \
Define _CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA to force ATL to support creating \
single-thread COM object's and allow use of it's single-threaded COM object implementations. \
The threading model in your rgs file was set to 'Free' as that is the only threading model \
supported in non DCOM Windows CE platforms."
#endif

// CNonCreatableClass01

class ATL_NO_VTABLE CNonCreatableClass01 :
	public CComObjectRootEx<CComSingleThreadModel>,
	public CComCoClass<CNonCreatableClass01, &CLSID_NonCreatableClass01>,
	public IDispatchImpl<INonCreatableClass01, &IID_INonCreatableClass01,
	  &LIBID_NonCreatableObjectsServerLib, /*wMajor =*/ 1, /*wMinor =*/ 0>
{
public:
	CNonCreatableClass01()
	{
	}

	~CNonCreatableClass01()
	{
	}

DECLARE_REGISTRY_RESOURCEID(IDR_NONCREATABLECLASS01)

BEGIN_COM_MAP(CNonCreatableClass01)
	COM_INTERFACE_ENTRY(INonCreatableClass01)
	COM_INTERFACE_ENTRY(IDispatch)
END_COM_MAP()

	DECLARE_PROTECT_FINAL_CONSTRUCT()

	HRESULT FinalConstruct()
	{
		return S_OK;
	}

	void FinalRelease()
	{
	}

	// This method, unfortunately, will never be called.
	// This is because the CNonCreatableClass01 class
	// has no entry in the global _ATL_OBJMAP_ENTRY.
	static void WINAPI ObjectMain(bool bStarting)
	{
		MessageBox (NULL, L"ObjectMain()", L"CNonCreatableClass01", MB_OK);
	}

public:

	STDMETHOD(Method01)(void)
	{
		return S_OK;
	}
};

// To make CNonCreatableClass01 a noncreatable COM coclass,
// the OBJECT_ENTRY_AUTO() macro must be commented out.
//OBJECT_ENTRY_AUTO(__uuidof(NonCreatableClass01), CNonCreatableClass01)

Here are some noteworthy points connected with the code above :

  • The implementation for Method01() is very simple, trivial in fact. It is not important for the purpose of our demonstration in this article.
  • The line which uses the OBJECT_ENTRY_AUTO() macro is inserted by the ATL wizard but I have commented it out.
  • An ObjectMain() function is defined. However, this function will not be called. More on this will be explained later (esp section 8).

3.3 The presence of the OBJECT_ENTRY_AUTO() macro adds an _ATL_OBJMAP_ENTRY record for the ATL C++ class into the ATL Object Map of the server. _ATL_OBJMAP_ENTRY is a structure defined as follows in atlbase.h :

struct _ATL_OBJMAP_ENTRY30
{
	const CLSID* pclsid;
	HRESULT (WINAPI *pfnUpdateRegistry)(BOOL bRegister);
	_ATL_CREATORFUNC* pfnGetClassObject;
	_ATL_CREATORFUNC* pfnCreateInstance;
	IUnknown* pCF;
	DWORD dwRegister;
	_ATL_DESCRIPTIONFUNC* pfnGetObjectDescription;
	_ATL_CATMAPFUNC* pfnGetCategoryMap;
	HRESULT WINAPI RevokeClassObject()
	{
		if (dwRegister == 0)
			return S_OK;
		return CoRevokeClassObject(dwRegister);
	}
	HRESULT WINAPI RegisterClassObject(DWORD dwClsContext, DWORD dwFlags)
	{
		IUnknown* p = NULL;
		if (pfnGetClassObject == NULL)
			return S_OK;
		HRESULT hRes = pfnGetClassObject(pfnCreateInstance, __uuidof(IUnknown), (LPVOID*) &p);
		if (SUCCEEDED(hRes))
			hRes = CoRegisterClassObject(*pclsid, p, dwClsContext, dwFlags, &dwRegister);
		if (p != NULL)
			p->Release();
		return hRes;
	}
// Added in ATL 3.0
	void (WINAPI *pfnObjectMain)(bool bStarting);
};

Although the structure contains methods, what matters to the OBJECT_ENTRY_AUTO() macro are its various fields.

3.4 The OBJECT_ENTRY_AUTO() macro is defined as follows in atlcom.h :

#define OBJECT_ENTRY_AUTO(clsid, class) \
	__declspec(selectany) ATL::_ATL_OBJMAP_ENTRY __objMap_##class = {&clsid, \
        class::UpdateRegistry, class::_ClassFactoryCreatorClass::CreateInstance, \
        class::_CreatorClass::CreateInstance, NULL, 0, class::GetObjectDescription, \
        class::GetCategoryMap, class::ObjectMain }; \
	extern "C" __declspec(allocate("ATL$__m")) __declspec(selectany) \
        ATL::_ATL_OBJMAP_ENTRY* const __pobjMap_##class = &__objMap_##class; \
	OBJECT_ENTRY_PRAGMA(class)

It is essentially a definition for a global _ATL_OBJMAP_ENTRY structure and a pointer to it which will be inserted into the server’s Object Map.

3.5 A _ATL_OBJMAP_ENTRY structure contains, among many important information :

  • The coclass’ GUID associated with the class.
  • The class’ registry update function.
  • The class’ class factory creation function.
  • The class’ static ObjectMain() function.

3.6 An ATL COM server uses the class’ registry update function to perform registry update when the DllRegisterServer() global function is called. Without this information available to the Object Map, no information on the coclass will be available in the registry.

The registration process is performed in the AtlComModuleRegisterServer() function (atlbase.h) :

ATLINLINE ATLAPIINL
AtlComModuleRegisterServer(_ATL_COM_MODULE* pComModule, BOOL bRegTypeLib, const CLSID* pCLSID)
{
	ATLASSERT(pComModule != NULL);
	if (pComModule == NULL)
		return E_INVALIDARG;
	ATLASSERT(pComModule->m_hInstTypeLib != NULL);

	HRESULT hr = S_OK;

	for
	(_ATL_OBJMAP_ENTRY** ppEntry = pComModule->m_ppAutoObjMapFirst;
	ppEntry < pComModule->m_ppAutoObjMapLast;
	ppEntry++)
	{
		if (*ppEntry != NULL)
		{
			_ATL_OBJMAP_ENTRY* pEntry = *ppEntry;
			if (pCLSID != NULL)
			{
				if (!IsEqualGUID(*pCLSID, *pEntry->pclsid))
					continue;
			}
			hr = pEntry->pfnUpdateRegistry(TRUE);
			if (FAILED(hr))
				break;
			hr = AtlRegisterClassCategoriesHelper( *pEntry->pclsid,
				pEntry->pfnGetCategoryMap(), TRUE );
			if (FAILED(hr))
				break;
		}
	}

	if (SUCCEEDED(hr) && bRegTypeLib)
		hr = AtlRegisterTypeLib(pComModule->m_hInstTypeLib, 0);

	return hr;
}

As can be seen in the above function, AtlComModuleRegisterServer() loops through the _ATL_OBJMAP_ENTRY entries in the server’s Object Map and calls its pfnUpdateRegistry() function to perform registration of a coclass.

Hence without such a _ATL_OBJMAP_ENTRY record to represent the coclass, there is no pfnUpdateRegistry() function to call and so the coclass cannot be registered.

3.7 The server uses the class’ class factory creation function to instantiate the class when one is required during a call to DllGetClassObject(). Without this information available to the Object Map, an instance of the C++ class (that implements a coclass) cannot be created.

Without an _ATL_OBJMAP_ENTRY record, as far as the server is concerned, CNonCreatableClass01 is not a COM class for public consumption.

3.8 For more information on the OBJECT_ENTRY_AUTO() macro, please refer to : OBJECT_ENTRY_AUTO.

3.9 Note well, however, that even though the NonCreatableClass01 coclass will not be available for direct creation by client applications, its implemented interfaces are available and must be registered.

3.10 How then do we instantiate a class like CNonCreatableClass01 at runtime when one is needed ? See section 4.

4. Class Creator for CNonCreatableClass01.

4.1 In order to allow a class like CNonCreatableClass01 to be available to client applications (through its implemented interfaces), we must provide a class creator for it.

4.2 The class creator provides a method that returns a pointer to an interface which is implemented by the CNonCreatableClass01 class.

4.3 The following is an IDL listing for the ObjectCreator coclass and its associated IObjectCreator interface :

[
	object,
	uuid(D006D52C-604B-41CB-8EFD-C2102335143F),
	dual,
	nonextensible,
	helpstring("IObjectCreator Interface"),
	pointer_default(unique)
]
interface IObjectCreator : IDispatch
{
	[id(1), helpstring("method CreateObject")] HRESULT CreateObject([out,retval] IUnknown** ppUnkReceiver);
};

[
	uuid(92F7C440-7267-43ED-932E-030046374E04),
	helpstring("ObjectCreator Class")
]
coclass ObjectCreator
{
	[default] interface IObjectCreator;
};

4.4 The ObjectCreator coclass, which is creatable, implements the IObjectCreator interface which provides the CreateObject() method.

4.5 The purpose of the CreateObject() method is to return a pointer to the IUnknown interface of the CNonCreatableClass01 class. From the IUnknown interface, other interfaces of the class, e.g. INonCreatableClass01 may be queried for.

4.6 The following is a full listing for CObjectCreator, the ATL implementation for the ObjectCreator coclass :

// ObjectCreator.h : Declaration of the CObjectCreator

#pragma once
#include "resource.h"       // main symbols

#include "NonCreatableObjectsServer_i.h"
#include "NonCreatableClass01.h"

#if defined(_WIN32_WCE) && !defined(_CE_DCOM) && !defined(_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA)
#error "Single-threaded COM objects are not properly supported on Windows CE platform, \
such as the Windows Mobile platforms that do not include full DCOM support. \
Define _CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA to force ATL to support creating \
single-thread COM object's and allow use of it's single-threaded COM object implementations. \
The threading model in your rgs file was set to 'Free' as that is the only threading model \
supported in non DCOM Windows CE platforms."
#endif

// CObjectCreator

class ATL_NO_VTABLE CObjectCreator :
	public CComObjectRootEx<CComSingleThreadModel>,
	public CComCoClass<CObjectCreator, &CLSID_ObjectCreator>,
	public IDispatchImpl<IObjectCreator, &IID_IObjectCreator,
          &LIBID_NonCreatableObjectsServerLib, /*wMajor =*/ 1, /*wMinor =*/ 0>
{
public:
	CObjectCreator()
	{
	}

	~CObjectCreator()
	{
	}

DECLARE_REGISTRY_RESOURCEID(IDR_OBJECTCREATOR)

BEGIN_COM_MAP(CObjectCreator)
	COM_INTERFACE_ENTRY(IObjectCreator)
	COM_INTERFACE_ENTRY(IDispatch)
END_COM_MAP()

	DECLARE_PROTECT_FINAL_CONSTRUCT()

	HRESULT FinalConstruct()
	{
		return S_OK;
	}

	void FinalRelease()
	{
	}

public:

	STDMETHOD(CreateObject)(IUnknown** ppUnkReceiver)
	{
		CComObject<>* pCNonCreatableClass01 = NULL;

		CComObject<CNonCreatableClass01>::CreateInstance(&pCNonCreatableClass01);

		if (pCNonCreatableClass01)
		{
			return pCNonCreatableClass01 -> QueryInterface(IID_IUnknown, (void**)ppUnkReceiver);
		}

		return E_FAIL;
	}
};

OBJECT_ENTRY_AUTO(__uuidof(ObjectCreator), CObjectCreator)

The following is a general summary for the CObjectCreator class :

  • A OBJECT_ENTRY_AUTO() macro is declared for it which indicates that the ObjectCreator coclass is created through the CObjectCreator class.
  • The CObjectCreator class is declared to use the STA apartment model. This is an significant point which will be expounded on later (see section 7).
  • Its CreateObject() method uses the CComObject<> class’ CreateInstance() method to create an instance of the CNonCreatableClass01 class.

5. Sample Client Source Codes.

5.1 Shown below is a full listing of the source codes of an example VC++ client application that attempts to create an instance of the NonCreatableClass01 coclass directly :

// CPPConsoleClient01.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#import "NonCreatableObjectsServer.tlb" raw_interfaces_only no_implementation
using namespace NonCreatableObjectsServerLib;

void CreateObjectDirectly()
{
	::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);

	if (1)
	{
		INonCreatableClass01Ptr spINonCreatableClass01 = NULL;

		spINonCreatableClass01.CreateInstance(__uuidof(NonCreatableClass01));

		if (spINonCreatableClass01)
		{
		  spINonCreatableClass01 -> Method01();
		}
	}

	::CoUninitialize();
}

int _tmain(int argc, _TCHAR* argv[])
{
	CreateObjectDirectly();
	return 0;
}

The following is a summary of the code above :

  • What will happen is that at the line where an attempt is made to instantiate a NonCreatableClass01 coclass :
spINonCreatableClass01.CreateInstance(__uuidof(NonCreatableClass01));

the error code 0x80040154 (Class not registered) will be returned.

  • This clearly shows that registry information on the NonCreatableClass01 coclass is missing.

5.2 Note however, that even though information on the NonCreatableClass01 coclass is missing from the registry, it is not missing in its type library. This is why it is still possible to write code that references the NonCreatableClass01 coclass and its associated INonCreatableClass01 interface.

5.3 Shown below is a full listing of the source codes of an example VC++ client application that attempts to create an instance of the NonCreatableClass01 coclass via an ObjectCreator coclass instance :

// CPPConsoleClient01.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#import "NonCreatableObjectsServer.tlb" raw_interfaces_only no_implementation
using namespace NonCreatableObjectsServerLib;

void CreateObjectViaObjectCreator()
{
	::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);

	if (1)
	{
		IObjectCreatorPtr spObjectCreator = NULL;
		INonCreatableClass01Ptr spINonCreatableClass01 = NULL;	

		spObjectCreator.CreateInstance(__uuidof(ObjectCreator));

		IUnknownPtr spUnknown = NULL;

		spObjectCreator->CreateObject(&spUnknown);

		if (spUnknown)
		{
		  spUnknown -> QueryInterface(&spINonCreatableClass01);
		}

		if (spINonCreatableClass01)
		{
		  spINonCreatableClass01 -> Method01();
		}
	}

	::CoUninitialize();
}

int _tmain(int argc, _TCHAR* argv[])
{
	CreateObjectViaObjectCreator();
	return 0;
}

The following is a summary of the code above :

  • The main action lies in the CreateObjectViaObjectCreator() function.
  • The code creates an STA (by initializing the main thread with a call to CoInitializeEx() with COINIT_APARTMENTTHREADED) and then proceeds to create an instance of the ObjectCreator coclass in that STA.
  • The ObjectCreator coclass instance is accessed via an IObjectCreator interface pointer and it is used to create an instance of the NonCreatableClass01 coclass.
  • The creation process will be successful.
  • The NonCreatableClass01 coclass is accessed via an INonCreatableClass01 interface pointer.
  • The NonCreatableClass01 coclass instance’s Method01() method is then called.

In this test scenario, both the ObjectCreator coclass instance and the NonCreatableClass01 coclass instance live in the same STA as the main thread of the application (which has been initialized as an STA thread).

6. Noteworthy Points Associated with this Approach.

6.1 The approach as laid out in sections 3 and 4 (by omitting the OBJECT_ENTRY_AUTO() macro and providing a dedicated “object creator” or “class factory” coclass for internal instantiation) will work well as demonstrated in the source codes of section 5.

6.2 However, there some significant points associated with this approach that the reader must realize and possibly act upon if they can lead to problems. These are :

  • The apartment model of the noncreatable coclass instance will depend on the apartment model of the object creator.
  • Without an OBJECT_ENTRY_AUTO() macro declared for an ATL COM class, its static ObjectMain() method will not be called.

6.3 These points will be expounded in greater detail in individual sections below.

7. The Apartment of a Noncreatable COM class.

7.1 The fact that the ObjectCreator coclass is an STA-based implies that any instance of the NonCreatableClass01 coclass will likewise be STA-based.

7.2 This will be so no matter what apartment model the CNonCreatableClass01 ATL class was designed to use.

7.3 In general, the apartment model of any coclass is based on the apartment model of its class factory. Focusing solely on in-proc-servers (i.e. DLLs), the general scheme of things is as follows :

  • For any creatable coclass that is housed in an in-proc server, its apartment model must be declared in the registry.
  • For example, the apartment model for the ObjectCreator coclass (CLSID : {92F7C440-7267-43ED-932E-030046374E04}) is STA and this is clearly indicated in the registry :

  • When the COM sub-system is required to create an instance of that coclass, it will look up the registry under the relevant CLSID to locate the path to the server executable.
  • The COM-subsystem will then determine what is the apartment model of the target coclass. This is determined by the “ThreadingModel” string value.
  • Using this information, COM will create an instance of the class factory of the target coclass in a thread that is compatible with its apartment model.
  • If such a thread or such a compatible apartment does not exist, one will be created.
  • The class factory will then be called upon to create an instance of the target coclass in the same thread in which the class factory is itself created.
  • In this way, the newly created coclass instance will live and breathe in the target apartment.

7.4 The above description is the standard technique behind coclass instantiation performed using APIs like CoCreateInstance().

7.5 In this sense, the class factory should be apartment-neutral, or at least be able to safely perform coclass instantiation in any apartment.

7.6 An equivalent but opposite logic for apartment assignment is applied when the IObjectCreator::CreateObject() method is used to create an instance of the NonCreatableClass01 coclass. In this case the apartment of the “class factory” determines that of the target coclass and not the other way around.

7.7 This is because the ObjectCreator coclass instance is a full-fledged, created object when it is used to instantiate a NonCreatableClass01 coclass. As such, it must already belong to some apartment when its CreateObject() method is called.

7.8 To see an example of how the apartment model of the NonCreatableClass01 coclass will always follow that of the ObjectCreator coclass, we will use the example code given in point 5.3 albeit with a very small code change :

// CPPConsoleClient01.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#import "NonCreatableObjectsServer.tlb" raw_interfaces_only no_implementation
using namespace NonCreatableObjectsServerLib;

void CreateObjectViaObjectCreator()
{
	::CoInitializeEx(NULL, COINIT_MULTITHREADED);

	if (1)
	{
		IObjectCreatorPtr spObjectCreator = NULL;
		INonCreatableClass01Ptr spINonCreatableClass01 = NULL;	

		spObjectCreator.CreateInstance(__uuidof(ObjectCreator));

		IUnknownPtr spUnknown = NULL;

		spObjectCreator->CreateObject(&spUnknown);

		if (spUnknown)
		{
		  spUnknown -> QueryInterface(&spINonCreatableClass01);
		}

		if (spINonCreatableClass01)
		{
		  spINonCreatableClass01 -> Method01();
		}
	}

	::CoUninitialize();
}

int _tmain(int argc, _TCHAR* argv[])
{
	CreateObjectViaObjectCreator();
	return 0;
}

This time, instead of call CoInitializeEx() with COINIT_APARTMENTTHREADED, we call it with COINIT_MULTITHREADED. The following is a general summary of the effects of this change :

  • This time, the main thread of the application is initialized as an MTA thread (by the call to CoInitializeEx() with COINIT_MULTITHREADED).
  • When an instance of the ObjectCreator coclass is created, an STA will be created by the COM sub-system and the ObjectCreator instance will live in that STA thread.
  • This is because the ObjectCreator coclass is declared (in the registry) to be an STA-based coclass.
  • When the ObjectCreator object is called to instantiate a NonCreatableClass01 coclass instance (via its CreateObject() method), the new NonCreatableClass01 instance will be created in the same STA thread of the ObjectCreator object.

Hence for an STA-based ObjectCreator coclass, its NonCreatableClass01 coclass instance offspring will live in the same STA apartment.

7.9 What if the object creator uses the MTA apartment model ? The answer is the same : the non creatable coclass instance will live in the same MTA apartment as the object creator, albeit, since many threads can live in the MTA, methods and properties of the coclass instance may be invoked from any MTA thread.

7.10 One conclusion that can be drawn from the observation described in this section is that the creator for a noncreatable coclass must use the same apartment model of the target coclass. If the noncreatable coclass is to use the STA model, its creator must be an STA-based coclass. If it is to be MTA-based, its creator must likewise be MTA-based.

8. The ObjectMain() Function.

8.1 Every ATL COM class is equipped with a static ObjectMain() function which is called once when the server is initialized, and again when it is terminated. For more information on the ObjectMain() function, please refer to : ObjectMain().

8.2 This function can be very useful for performing one time initialization/uninitialization for an ATL class. It is called upon server startup and shutdown before any instances of the ATL class is ever requested.

8.3 The ObjectMain() function of the ATL class, if it is to be invoked at all, must be registered by using the OBJECT_ENTRY_AUTO() macro.

8.4 For an in-proc-server (i.e. DLL), the ObjectMain() function of an ATL class is invoked in the constructor and destructor of the ATL module class :

template <class T>
class ATL_NO_VTABLE CAtlDllModuleT : public CAtlModuleT<T>
{
public :
	CAtlDllModuleT() throw()
	{
		_AtlComModule.ExecuteObjectMain(true);
		...
		...
		...
	}

	~CAtlDllModuleT() throw()
	{
		...
		...
		...
		_AtlComModule.ExecuteObjectMain(false);
	}
	...
	...
	...
}

8.5 And the code for CAtlComModule::ExecuteObjectMain() :

class CAtlComModule : public _ATL_COM_MODULE
{
public:
	...
	...
	...
	// Call ObjectMain for all the objects.
	void ExecuteObjectMain(bool bStarting)
	{
		for (_ATL_OBJMAP_ENTRY** ppEntry = m_ppAutoObjMapFirst;
			ppEntry < m_ppAutoObjMapLast;
			ppEntry++)
		{
			if (*ppEntry != NULL)
				(*ppEntry)->pfnObjectMain(bStarting);
		}
	}
};

As can be seen in the above code, the Object Map is used to invoke each ATL class’ ObjectMain() function (via the pfnObjectMain function pointer).

8.6 Hence without declaring an OBJECT_ENTRY_AUTO() macro for the class, there will not be a _ATL_OBJMAP_ENTRY record for the class in the Object Map of the ATL server. Consequently, its ObjectMain() function will not be called.

8.7 The impact of this all depends on the design of the ATL class and it need not be all that disadvantageous of course.

9. In Conclusion.

9.1 Here in part 1 I have demonstrated one technique for developing a noncreatable COM coclass using ATL : by the non-usage of the OBJECT_ENTRY_AUTO() macro.

9.2 Some significant points regarding this technique are discussed : the noncreatble coclass’ apartment model and the fact that its ObjectMain() function will not be called.

9.3 In part 2, we shall explore another tecnique for developing a noncreatable COM coclass : the use of the OBJECT_ENTRY_NON_CREATEABLE_EX_AUTO() macro.

Advertisements

About Lim Bio Liong

I've been in software development for nearly 20 years specializing in C , COM and C#. It's truly an exicting time we live in, with so much resources at our disposal to gain and share knowledge. I hope my blog will serve a small part in this global knowledge sharing network. For many years now I've been deeply involved with C development work. However since circa 2010, my current work has required me to use more and more on C# with a particular focus on COM interop. I've also written several articles for CodeProject. However, in recent years I've concentrated my time more on helping others in the MSDN forums. Please feel free to leave a comment whenever you have any constructive criticism over any of my blog posts.

Discussion

Trackbacks/Pingbacks

  1. Pingback: Noncreatable COM Objects – Part 2. « limbioliong - December 23, 2011

  2. Pingback: Noncreatable COM Objects – Part 3. « limbioliong - December 26, 2011

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: