//
you're reading...
.NET/COM/ActiveX Interop, COM, Programming Issues/Tips, Type Information

How to Determine if a coclass is a Managed Class.

1. Introduction.

1.1 A managed class, when exported to COM, can be identified as being managed.

1.2 This is possible because the type library exporter, which handles the creation of the type library for the managed class library, will insert a special custom IDL attribute for the resulting coclass that represents the managed class.

1.3 This article will show an example of this and will demonstrate how a C++ client application can determine whether a COM coclass is derived from a managed class. I will also show that the name of the managed class can also be determined.

2. A Sample Managed Class.

2.1 The following is a sample code listing for a managed class library written in C# :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace CSServer
{
    [ComVisible(true)]
    [Guid("D74C1421-B3EF-4660-9F39-FC840DA1ED72")]
    [ProgId("CSServer.MyCSharpClass")]
    [ClassInterface(ClassInterfaceType.AutoDual)]
    public class MyCSharpClass
    {
        public void TestMethod01()
        {
        }
    }
}

It contains the definition of the MyCSharpClass class which is exportable to COM. After compilation, it will produce CSServer.dll.

2.2 After being registered to COM using REGASM.EXE a type library “CSServer.tlb” with the following IDL listing will be produced :

// Generated .IDL file (by the OLE/COM Object Viewer)
//
// typelib filename: CSServer.tlb

[
  uuid(6C4A3742-9494-4200-86A7-6C3FEE838AF7),
  version(1.0)
]
library CSServer
{
    // TLib :     // TLib : mscorlib.dll : {BED7F4EA-1A96-11D2-8F08-00A0C9A6186D}
    importlib("mscorlib.tlb");
    // TLib : OLE Automation : {00020430-0000-0000-C000-000000000046}
    importlib("stdole2.tlb");

    // Forward declare all types defined in this typelib
    interface _MyCSharpClass;

    [
      uuid(D74C1421-B3EF-4660-9F39-FC840DA1ED72),
      version(1.0),
        custom({0F21F359-AB84-41E8-9A78-36D110E6D2F9}, "CSServer.MyCSharpClass")
    ]
    coclass MyCSharpClass {
        [default] interface _MyCSharpClass;
        interface _Object;
    };

    [
      odl,
      uuid(16AD7DB4-8266-37E4-8A18-863B40BB2E96),
      hidden,
      dual,
      nonextensible,
      oleautomation,
        custom({0F21F359-AB84-41E8-9A78-36D110E6D2F9}, "CSServer.MyCSharpClass")    

    ]
    interface _MyCSharpClass : IDispatch {
        [id(00000000), propget,
            custom({54FC8F55-38DE-4703-9C4E-250351302B1C}, "1")]
        HRESULT ToString([out, retval] BSTR* pRetVal);
        [id(0x60020001)]
        HRESULT Equals(
                        [in] VARIANT obj,
                        [out, retval] VARIANT_BOOL* pRetVal);
        [id(0x60020002)]
        HRESULT GetHashCode([out, retval] long* pRetVal);
        [id(0x60020003)]
        HRESULT GetType([out, retval] _Type** pRetVal);
        [id(0x60020004)]
        HRESULT TestMethod01();
    };
};

Notice that the coclass statement contains a custom IDL attribute :

custom({0F21F359-AB84-41E8-9A78-36D110E6D2F9}, "CSServer.MyCSharpClass")

2.3 This custom attribute is inserted by the type library exporter when the class library assembly is registered by using REGASM.EXE together with the /tlb option.

2.4 An IDL custom attribute is a GUID-value pair. To learn more about IDL custom attributes, please refer to : IDL custom attribute.

2.5 The particular GUID used here : {0F21F359-AB84-41E8-9A78-36D110E6D2F9} is a well-known one know as GUID_ManagedName. It identifies a name from a managed source.

3. Accessing the coclass Custom Attribute.

3.1 To access the custom attribute of a coclass, we need to access the type library in which the coclass is defined.

3.2 Then, through the use of various methods of the ITypeLib, ITypeInfo and ITypeInfo2 interfaces, we will be able to navigate the type library and access the custom attribute.

3.3 The following C++ listing provides a sample helper function that is able to do this :

HRESULT GetCustomAttribute
(
	LPCTSTR lpszTypeLibraryPath, // Path to type library.
	REFGUID refguid_data_item, // GUID of data item of which to obtain the Custom Attribute.
	REFGUID refguid_custom_attribute, // GUID of the Custom Attribute.
	VARIANT& varCustomAttributeReceiver // Receiver of the Custom Attribute.
)
{
	_bstr_t bstTypeLibraryPath = lpszTypeLibraryPath;
	ITypeLib* pTypeLib = NULL;
	ITypeInfo* pTypeInfo = NULL;
	ITypeInfo2* pTypeInfo2 = NULL;
	HRESULT hrRet = S_OK;

	// Initialize receiver.
	VariantInit(&varCustomAttributeReceiver);

	hrRet = LoadTypeLib((const OLECHAR FAR*)bstTypeLibraryPath, &pTypeLib);

	if (SUCCEEDED(hrRet))
	{
		if (pTypeLib)
		{
			hrRet = pTypeLib -> GetTypeInfoOfGuid(refguid_data_item, &pTypeInfo);

			pTypeLib->Release();
			pTypeLib = NULL;
		}

		if (pTypeInfo)
		{
			hrRet = pTypeInfo -> QueryInterface(IID_ITypeInfo2, (void**)&pTypeInfo2); 

			pTypeInfo->Release();
			pTypeInfo = NULL;
		}

		if (pTypeInfo2)
		{
			hrRet = pTypeInfo2 -> GetCustData
			(
				refguid_custom_attribute,
				&varCustomAttributeReceiver
			);

			pTypeInfo2 -> Release();
			pTypeInfo2 = NULL;
		}
	}

	return hrRet;
}

3.4 The following is a sample call to GetCustomAttribute() to obtain the custom attribute of the MyCSharpClass coclass :

#import "CSServer.tlb" raw_interfaces_only no_implementation
using namespace CSServer;

// {0F21F359-AB84-41E8-9A78-36D110E6D2F9}
static const GUID GUID_ManagedName =
{ 0x0F21F359, 0xAB84, 0x41E8, { 0x9A, 0x78, 0x36, 0xD1, 0x10, 0xE6, 0xD2, 0xF9 } };

...
...
...

VARIANT varCustomAttribute;

VariantInit(&varCustomAttribute);

GetCustomAttribute
(
  "CSServer.tlb",
  __uuidof(MyCSharpClass),
  GUID_ManagedName,
  varCustomAttribute
);

VariantClear(&varCustomAttribute);

The above code involves the #import of the “CSServer.tlb” type library and a definition of the GUID_ManagedName GUID.

With the #import of “CSServer.tlb”, a type library header file “csserver.tlh” will be produced by the Visual C++ compiler and this makes available the following declaration for the “MyCSharpClass” coclass :

struct __declspec(uuid("d74c1421-b3ef-4660-9f39-fc840da1ed72"))
MyCSharpClass;

This is why we are able to use the code construct “__uuidof(MyCSharpClass)” in the call to GetCustomAttribute() in reference to the CLSID for MyCSharpClass.

3.5 When the code in point 3.4 is run, varCustomAttribute will receive a BSTR with value “CSServer.MyCSharpClass” which is the full name (namespace & classname) of the MyCSharpClass class.

3.6 The fact that varCustomAttribute returned a BSTR indicates that the MyCSharpClass coclass contains a custom attribute with GUID GUID_ManagedName indicates that MyCSharpClass is a managed class.

3.7 If GetCustomAttribute() is run on a coclass that is non-managed, varCustomAttribute will remain as VT_EMPTY (due to it being initialized by VariantInit()).

4. In Conclusion.

4.1 There are several other GUIDs which are used as custom attribute GUIDs. These can be found in cor.h.

4.2 Among these are GUID_ExportedFromComPlus ({90883F05-3D28-11D2-8F17-00A0C9A6186D}), GUID_ForceIEnumerable ({B64784EB-D8D4-4d9b-9ACD-0E30806426F7}), GUID_DispIdOverride ({CD2BC5C9-F452-4326-B714-F9C539D4DA58}), etc.

4.3 These GUIDs are used in tools like TLBIMP.EXE to search for specific IDL custom attributes contained in a type library.

4.4 When the Visual Studio is made to reference a type library, it searches the library item for a custom attribute named GUID_ExportedFromComPlus to prevent importing a type library which has been created from a managed source. See How to Determine if a Type Library was Generated from a .NET assembly for more details.

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: How to Determine if a Type Library was Generated from a .NET assembly. « limbioliong - November 6, 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: