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

How to Determine if a Type Library was Generated from a .NET assembly.

1. Introduction.

1.1 When a .NET assembly (DLL or EXE) that contains a COM-visible class is compiled and registered via REGASM.EXE, a type library will be generated.

1.2 A type library may also be generated directly via TLBEXP.EXE.

1.3 The fact that this type library was generated from a managed source can be detected.

1.4 This is possible because the type library exporter, which handles the creation of the type library for the assembly, will insert a special custom IDL attribute for the type library.

1.5 This article will show an example of this and will demonstrate how a C++ client application can determine whether a type library is derived from a .NET assembly.

2. A Sample Assembly.

2.1 The following is an extremely simple 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 CSCOMServer
{
    [ComVisible(true)]
    public class CSCOMClass
    {
    }
}

It contains only one COM-visible class named CSCOMClass. After compilation, it produces the class library CSCOMServer.dll.

2.2 I then create a type library from this class library using TLBEXP :

tlbexp CSCOMServer.dll /out:CSCOMServer.tlb

2.3 The following will be the contents of such a type library in IDL :

[
  uuid(0852199A-2E95-49E9-96D1-5D499BE8BD61),
  version(1.0)
]
library CSCOMServer
{
    // 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 _CSCOMClass;

    [
      uuid(E18D31BE-5B5D-39AC-A1B7-745009884002),
      version(1.0),
        custom({0F21F359-AB84-41E8-9A78-36D110E6D2F9}, "CSCOMServer.CSCOMClass")
    ]
    coclass CSCOMClass {
        [default] interface _CSCOMClass;
        interface _Object;
    };

    [
      odl,
      uuid(D6D1C92A-2B7D-3839-99D5-54C89212CE55),
      hidden,
      dual,
      oleautomation,
        custom({0F21F359-AB84-41E8-9A78-36D110E6D2F9}, "CSCOMServer.CSCOMClass")    

    ]
    interface _CSCOMClass : IDispatch {
    };
};

2.4 Readers who have read my article How to Determine if a coclass is a Managed Class would recognize the GUID_ManagedName ({0F21F359-AB84-41E8-9A78-36D110E6D2F9}) custom attribute being applied to the CSCOMClass coclass which is a managed class exported to COM.

2.5 This custom attribute is inserted into the coclass definition of the output type library when the class library assembly is exported by TLBEXP.EXE.

2.6 Also inserted into the output type library by TLBEXP.EXE will be a custom attribute for the library statement itself which is the following part in the IDL :

[
  uuid(0852199A-2E95-49E9-96D1-5D499BE8BD61),
  version(1.0)
]
library CSCOMServer
{
  ...
}

2.7 The custom attribute applied to the CSCOMServer library item is identified by the GUID {90883F05-3D28-11D2-8F17-00A0C9A6186D}. This is a better known as GUID_ExportedFromComPlus.

2.8 Custom attributes, when applied to the library statement, are hidden when the type library is viewed by the OLEVIEW.EXE tool. Hence we do not see the presence of this custom attribute in the library statement of the IDL code of point 2.3.

2.9 This, however, does not prevent us from being able to detect its presence when we load the type library in code using the LoadTypeLib() API and then navigating through the type library using the ITypeLib and the ITypeLib2 interfaces. I will provide a demonstration of this in the next section.

3. Accessing the Type Library Custom Attribute.

3.1 As mentioned in point 2.9, to access the custom attribute of a type library, we need to first load the type library in code using the LoadTypeLib() API.

3.2 This API will return to us a pointer to an ITypeLib interface. We need to QueryInterface() this ITypeLib interface pointer for a ITypeLib2 interface.

3.3 Once we are able to access the ITypeLib2 interface, we can use its GetCustData() method to obtain a VARIANT which represents the value for the custom attribute identified by the GUID_ExportedFromComPlus GUID.

3.4 The following C++ code (for a console application) demonstrates this :

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

#include "stdafx.h"
#include <windows.h>
#include <comutil.h>

// {90883F05-3D28-11D2-8F17-00A0C9A6186D}
static const GUID GUID_ExportedFromComPlus =
{ 0x90883F05, 0x3D28, 0x11D2, { 0x8F, 0x17, 0x00, 0xA0, 0xC9, 0xA6, 0x18, 0x6D } };

HRESULT GetTypeLibCustomAttribute
(
	LPCTSTR lpszTypeLibraryPath, // Path to type library.
	REFGUID refguid_custom_attribute, // GUID of the Custom Attribute.
	VARIANT& varCustomAttributeReceiver // Receiver of the Custom Attribute.
)
{
	_bstr_t bstTypeLibraryPath = lpszTypeLibraryPath;
	ITypeLib* pTypeLib = NULL;
	ITypeLib2* pITypeLib2 = NULL;
	HRESULT hrRet = S_OK;

	// Initialize receiver.
	VariantInit(&varCustomAttributeReceiver);

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

	if (SUCCEEDED(hrRet))
	{
		if (pTypeLib)
		{
			hrRet = pTypeLib -> QueryInterface(IID_ITypeLib2, (void**)&pITypeLib2);

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

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

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

	return hrRet;
} 

int _tmain(int argc, _TCHAR* argv[])
{
	VARIANT varCustomAttribute;

	VariantInit(&varCustomAttribute);

	GetTypeLibCustomAttribute
	(
		"CSCOMServer.tlb",
		GUID_ExportedFromComPlus,
		varCustomAttribute
	);

	VariantClear(&varCustomAttribute);		

	return 0;
}

The above source contains a listing of the GetTypeLibCustomAttribute() helper function which performs the tasks as laid out in points 3.1 through 3.3.

When this code is run and the GetTypeLibCustomAttribute() helper function returns, the varCustomAttribute will contain the following BSTR value :

"CSCOMServer, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"

3.5 This BSTR value, however, is not as important as the fact that the type library contains a custom attribute identified by GUID_ExportedFromComPlus. This signifies the fact that the type library was generated from a .NET assembly, a managed source.

3.6 If GetTypeLibCustomAttribute() is run on a type library generated from a non-managed COM server, varCustomAttribute will remain as VT_EMPTY (due to it being initialized by VariantInit()).

4. In Conclusion.

4.1 If CSCOMServer.tlb was referenced in a managed code project, the following error message will be displayed :

A reference to ‘CSCOMServer.tlb’ could not be added.

The ActiveX type library ‘CSCOMServer.tlb’ was exported from a .NET assembly and cannot be added as a reference.

Add a reference to the .NET assembly instead.

4.2 This error message is invoked precisely due to the fact that the library object contained in the type library is marked with the GUID_ExportedFromComPlus custom attribute.

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 coclass is a Managed Class. « 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: