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

Noncreatable COM Objects – Part 2.

1. Introduction.

1.1 In part 1 of this series of articles, I discussed the technique of developing a noncreatable COM coclass by omitting the use of the OBJECT_ENTRY_AUTO() macro.

1.2 Here in part 2, another approach will be explored : by the use of the OBJECT_ENTRY_NON_CREATEABLE_EX_AUTO() macro.

1.3 Possible advantages of this technique will be explored.

2. The OBJECT_ENTRY_NON_CREATEABLE_EX_AUTO() Macro.

2.1 This macro expands to the following :

#define OBJECT_ENTRY_NON_CREATEABLE_EX_AUTO(clsid, class) \
	__declspec(selectany) ATL::_ATL_OBJMAP_ENTRY __objMap_##class = {&clsid, \
	class::UpdateRegistry, \
	NULL, \
	NULL, \
	NULL, \
	0, \
	NULL, \
	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)

2.2 It amounts to a definition of a global _ATL_OBJMAP_ENTRY structure and a pointer to it which will be inserted into the server’s Object Map.

2.3 Unlike the example of part 1 in which no _ATL_OBJMAP_ENTRY record was ever inserted into the Object Map for the CNonCreatableClass01 ATL class (the implementation for the NonCreatableClass01 coclass), the example coclass which will be presented in this part 2 will have an _ATL_OBJMAP_ENTRY record in the Object Map.

2.4 What makes this _ATL_OBJMAP_ENTRY record special is the interesting field values set for it via the OBJECT_ENTRY_NON_CREATEABLE_EX_AUTO() macro which are :

  • pfnUpdateRegistry
  • pfnGetClassObject
  • pfnCreateInstance
  • pfnObjectMain

The pfnUpdateRegistry field points to a function that will be called to perform registration of the ATL COM class. This field is set to the ATL class’ static UpdateRegistry() function which is added to the class via the DECLARE_REGISTRY_RESOURCEID() macro.

The pfnGetClassObject field points to a function that creates an instance of the class object and it is set to NULL by the OBJECT_ENTRY_NON_CREATEABLE_EX_AUTO() macro which means that the server’s DllGetClassObject() will return the HRESULT CLASS_E_CLASSNOTAVAILABLE when it is called upon to create an instance of an ATL class which has been attributed with the OBJECT_ENTRY_NON_CREATEABLE_EX_AUTO() macro.

The pfnCreateInstance field points to a function that creates an instance of the class. It is also set to NULL by the macro. However, with the pfnGetClassObject field already set to NULL, the pfnCreateInstance field is redundant for the purpose of creating an instance of the class.

The pfnObjectMain field points to the static class initialization/termination function. This is set by the OBJECT_ENTRY_NON_CREATEABLE_EX_AUTO() macro to the class’ ObjectMain() function which is originally defined by the base CComObjectRootBase class.

2.5 Hence overall, an ATL class which declares a OBJECT_ENTRY_NON_CREATEABLE_EX_AUTO() macro will have the following characteristics :

  • It will not be creatable by external client applications.
  • It will have its coclass information registered in the registry.
  • Upon server start-up/termination, its ObjectMain() function will be called.

2.6 Of the above characteristics, the fact that the ObjectMain() is called is certainly useful. The fact that the coclass information is recorded in the registry does not seem to provide anything useful except in the situation where custom information associated with the coclass is to be stored in the registry.

3. Sample COM Class which uses the OBJECT_ENTRY_NON_CREATEABLE_EX_AUTO() Macro.

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

// 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(9BEEE2AB-AED9-4A80-A7C6-8BA147BCBD56),
		dual,
		nonextensible,
		helpstring("INonCreatableClass02 Interface"),
		pointer_default(unique)
	]
	interface INonCreatableClass02 : IDispatch
	{
		[id(1), helpstring("method Method01")] HRESULT Method01(void);
	};

	[
		uuid(C556335F-238C-4F2C-A6A4-79377BB7E53F),
		helpstring("NonCreatableClass02 Class"),
		noncreatable
	]
	coclass NonCreatableClass02
	{
		[default] interface INonCreatableClass02;
	};
};

The INonCreatableClass02 interface exposes only one method Method01().

3.2 Shown below is the full listing of the ATL class CNonCreatableClass02 which is the implementation for the NonCreatableClass02 coclass :

// NonCreatableClass02.h : Declaration of the CNonCreatableClass02

#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

// CNonCreatableClass02

class ATL_NO_VTABLE CNonCreatableClass02 :
	public CComObjectRootEx<CComSingleThreadModel>,
	public CComCoClass<CNonCreatableClass02, &CLSID_NonCreatableClass02>,
	public IDispatchImpl<INonCreatableClass02,
		&IID_INonCreatableClass02,
		&LIBID_NonCreatableObjectsServerLib,
		/*wMajor =*/ 1, /*wMinor =*/ 0>
{
public:
	CNonCreatableClass02()
	{
	}

	~CNonCreatableClass02()
	{
	}

DECLARE_REGISTRY_RESOURCEID(IDR_NONCREATABLECLASS02)

BEGIN_COM_MAP(CNonCreatableClass02)
	COM_INTERFACE_ENTRY(INonCreatableClass02)
	COM_INTERFACE_ENTRY(IDispatch)
END_COM_MAP()

	DECLARE_PROTECT_FINAL_CONSTRUCT()

	HRESULT FinalConstruct()
	{
		return S_OK;
	}

	void FinalRelease()
	{
	}

	static void WINAPI ObjectMain(bool bStarting)
	{
		MessageBox (NULL, L"ObjectMain()", L"CNonCreatableClass02", MB_OK);
	}	

public:

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

//OBJECT_ENTRY_AUTO(__uuidof(NonCreatableClass02), CNonCreatableClass02)
OBJECT_ENTRY_NON_CREATEABLE_EX_AUTO(__uuidof(NonCreatableClass02), CNonCreatableClass02)

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 and inserted the OBJECT_ENTRY_NON_CREATEABLE_EX_AUTO() macro in its place.
  • An ObjectMain() function is defined. This function will be called when the server is started up and when it is shutdown.
  • Note that the ObjectMain() function of an ATL class is also called when the server is being registered.

4. The Need for a Class Creator.

4.1 Since a noncreatable coclass like NonCreatableClass02 is not creatable by external clients. A class creator like the ObjectCreator demonstrated in part 1 is required.

4.2 This entails the same implication first mentioned in part 1 : that the apartment model of the NonCreatableClass02 coclass will be that of the class creator.

4.3 Hence if an STA-based NonCreatableClass02 coclass is to be created, the class creator must be STA-based.

4.4 Likewise, if an MTA-based NonCreatableClass02 coclass is to be created, the class creator must be MTA-based.

4.5 Now because the OBJECT_ENTRY_NON_CREATEABLE_EX_AUTO() will ensure that information on the NonCreatableClass02 coclass will be recorded in the registry, including its apartment model (indicated by the “ThreadingModel” string value), it would be in line with good practice to ensure that this value is indeed reliable (e.g. if it is “Apartment”, then ensure that the class creator is also an STA coclass).

4.6 See the ObjectCreator example in part 1 for sample code.

5. In Conclusion.

5.1 Here in part 2, I have briefly explained the use of the OBJECT_ENTRY_NON_CREATEABLE_EX_AUTO() macro for developing noncreatable coclasses.

5.2 In my opinion, it is hard to see exactly what value this macro brings to the development of noncreatable coclasses.

5.3 A special creator coclass is still necessary to internally instantiate such coclasses and to return its implemented interfaces.

5.4 The fact that ObjectMain() is called seems to be the only valuable feature.

5.5 In part 3, I shall present a third type of ATL COM class which are registered and which can be initialized/uninitialized (via ObjectMain()) but can only be created internally. Such ATL classes use a private Class Factory class to control creation.

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

No comments yet.

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: