Click here to Skip to main content
15,861,366 members
Articles / Programming Languages / C#
Article

Using the COM Component Categories Manager in .NET

Rate me:
Please Sign up or sign in to vote.
4.46/5 (12 votes)
3 May 20044 min read 99.5K   2K   19   10
An article on using the the COM Component Categories Manager in .NET. Focuses mainly on interoping with COM and writing interfaces in MSIL

Image 1

Introduction

When working with COM , the "COM Component Categories Manager" is a system provided COM object that (as the name denotes) provides a way of working with COM Object Categories.

Or quoting the MSDN information:

As the number of available components grows, it becomes increasingly difficult to manage these components. In terms of the interfaces they expose as well as the tasks they perform, many components offer similar functionality, .

It is often necessary to enumerate the components that can be used in a certain context. Examples of this are the Insert Object dialog box used in OLE compound documents and the Insert Control dialog box used in OLE controls. These dialog boxes list all components that fulfill (or claim to fulfill) the interface contracts for compound documents or controls. These existing categories (OLE document, OLE control) do not imply an exact interface signature. OLE documents have to expose a certain set of core interfaces (for example, IOleObject or IPersistStorage) but can also expose additional interfaces such as IOleLink

The object exposes 2 interfaces. ICatInformation for retrieving information on categories and ICatRegister for registering new categories and class implementations.

The ICatInformation interface provides methods for obtaining information about the categories implemented or required by a certain class, as well as information about the categories registered on a given machine.

The ICatRegister interface provides methods for registering and unregistering component category information in the Registry. This includes both the human-readable names of categories and the categories implemented/required by a given component or class.

In the .Net framework, there is much to do about COM interoperability and I was surprised to discover that there is no support at all for the COM Component Categories Manager in the .Net framework.

So ... create it our selves I suppose.

Background

For more information on the COM Component Categories Manager, search the MSDN library for ICatInformation, ICatRegister or Component Categories Manager.

Using the code

To begin. I apologize for the lack of comment in the code. Not enough time and to much to do... But example shows a lot.

Basically the project creates 2 C# classes that wrap the ICatInformation and ICatRegister interfaces and provide more C# style Methods, using Arrays in stead of IEnum interfaces and System.Guid's in stead of ole32.GUID

For example wrapping the

ICatInformation::EnumClassesOfCategories
method:

HRESULT EnumClassesOfCategories(
  ULONG cImplemented,
  CATID rgcatidImpl,
  ULONG cRequired,
  CATID rgcatidReq,
  IEnumCLSID ** ppenumCLSID
);

becomes in the C# CatInformation class...

public Guid[] GetClassesofCategories(Guid[] Implemented, Guid[] Required)

or with only one argument

C#
Guid[] GetClassesofImplCategories(Guid[] Implemented)

and is then used like this:

C#
Guid[] CatImpl = {new Guid("0002E500-0000-0000-C000-000000000046")};
Guid[] ImplList = m_CatInformation.GetClassesofImplCategories(CatImpl);

Or wrapping the ICatRegister::RegisterClassImplCategories method:

HRESULT RegisterClassImplCategories(
  REFCLSID rclsid,
  ULONG cCategories,
  CATID rgcatid[]
);

becomes in the C# CatRegister class...

C#
public void RegisterClassImplCategories(Guid ClsId, Guid[] ImplementedCatIds) 

The CatInformation and CatRegister are normal C# classes. When they are created they get an ICatInformation and ICatRegister COM interface respectively. When the Dispose method of the classes is called the Classes release the COM interface. All class methods convert the parameters if necessary, then call the COM interface method and present the result.

How was it created ?

First of all the project starts with generating a usable .Net wrapper for the COM interfaces ICatInformation and ICatRegister. This is a bit tricky in the beginning. The interfaces are declared in the file "ComCat.idl" in the Platform SDK Include directory. Of course these ar IDL files and these are not usable in Dot Net, so we make a copy of the file and change a few things like adding return values in stead of plain out parameters and adding a library statement.

Then we use the MIDL.EXE compiler to generate a type library. Then use TLBIMP.EXE to generate a dot net import library. We could use the library as it is generated but there are still some things that are not very useful. For example the C statements like CATID rgcatid[] become something like ref ole32.GUID where we would rather see something like Guid[] of course.

So first decompile the dot net "ComCat.dll" file with the ILDASM.EXE tool to "ComCat.il" and start editing the MISL file (the IL files are provided with the source code and the changes are in comment at the top of the file). Finally recompile the MISL file with the ILASM.EXE tool.

Seems a lot of work but it doesn't take a lot of time if you get used to it.

Getting the COM interfaces ?

Then we still have to wrap the COM CoCreateInstance function to create our object.

STDAPI CoCreateInstance(
  REFCLSID rclsid,
  LPUNKNOWN pUnkOuter,
  DWORD dwClsContext,
  REFIID riid,
  LPVOID * ppv
);

as wrapped like this

C#
[DllImport("OLE32.DLL", EntryPoint = "CoCreateInstance", 
  CallingConvention = CallingConvention.StdCall)]
static extern uint CoCreateInstance(ref Guid ClassGuid , 
  IntPtr pUnkOuter, CLSCTX dwClsContext, ref Guid InterfaceGuid, 
   ref IntPtr Result);

and we get the ICatInformation interface like this

C#
uint hr = Ole32.S_OK;
IntPtr CatInformationPtr = IntPtr.Zero;
object CatInformationObj = null;
hr = Ole32.CoCreateInstance(
ref Ole32.CLSID_StdComponentCategoriesMgr,
IntPtr.Zero,
CLSCTX.CLSCTX_INPROC_SERVER,
ref Ole32.IID_ICatInformation,
ref CatInformationPtr);
if (hr != Ole32.S_OK)
{
  throw new COMException(
    "Error creating instance of ICatInformation", (int)hr);
}
CatInformationObj = Marshal.GetObjectForIUnknown(CatInformationPtr);
m_CatInformation = (ICatInformation) CatInformationObj;

Points of Interest

Dot NET Interop works great but the documentation it is hell. Would it be that difficult for Microsoft to give a long list of real world examples for Interop in stead of spreading the information all over the MSDN ?

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Belgium Belgium
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionLicense information Pin
Member 147946516-Apr-20 20:47
Member 147946516-Apr-20 20:47 
General3. Modifying the IL [modified] Pin
Member 18817854-Mar-08 0:05
Member 18817854-Mar-08 0:05 
General2. How to get from IDL to IL Pin
Member 18817853-Mar-08 23:55
Member 18817853-Mar-08 23:55 
General1. Thanks for this article Pin
Member 18817853-Mar-08 23:44
Member 18817853-Mar-08 23:44 
GeneralRegister Category Pin
Member 1957459-Aug-05 13:13
Member 1957459-Aug-05 13:13 
GeneralVB.NET Pin
Virgil Reboton1-Mar-05 2:22
Virgil Reboton1-Mar-05 2:22 
QuestionBug in enumerator? Pin
eejake3-Jun-04 6:25
eejake3-Jun-04 6:25 
AnswerRe: Bug in enumerator? Pin
eejake9-Jun-04 8:27
eejake9-Jun-04 8:27 
QuestionWhy import CoCreateInstance? Pin
casperOne4-May-04 9:18
casperOne4-May-04 9:18 
AnswerRe: Why import CoCreateInstance? Pin
Spaider4-May-04 23:14
Spaider4-May-04 23:14 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.