Introduction

This article and topic will probably inspire reluctance from code gurus. But, even after 7 years and a massive use in today's Windows-centric world, I often see people wondering the simplest questions about COM.

Microsoft introduced several years ago the ATL library, hoping that ready-to-use macros would simplify the COM development process. But in practice, the macros tend to obfuscate what the code really does and is expected to do. Furthermore, one obvious thing at the basis of this article is that ATL macros produce un-debuggable code. Macros are expanded by the C/C++ preprocessor, which means it's virtually impossible to figure out what might be wrong in any ATL-based code.

That's why I shall in this article, show how to write working COM components from scratch, and without a single macro. Hope you find it useful.

The remainder of this article provides three (hopefully reusable) sample COM implementations :

a simple COM DLL

an automation-enabled COM DLL

an automation-enabled COM exe

Plus several working test environments (C/C++ and VB).

A simple COM DLL

You'll find above, a working simplecomserver.zip package which is the resulting code for the following steps.

Start VC6/7, create a new project called simplecomserver. Select the WIN32 dynamic library project wizard, and opt for the simple DLL project option.

So far, you should have a project with stdafx.h/cpp precompiled headers, plus simplecomserver.cpp with this code:

We have an interface inside an object (coclass keyword), itself part of a set of objects (there's only one here) belonging to a type-library (library keyword). Both interfaces, objects and type-libraries have ids that uniquely identify them: it's produced by a tool such as guidgen.exe (available from MSDEV / Tools menu).

Add simplecomserver.idl to your project files, right-click on it and compile it. This produces the type-library in the Debug folder. simplecomserver.tlb is a binary-compiled IDL file, nothing else. The type library is what applications and languages are going to read and parse, to extract interface names, method names, parameter qualifiers and so on at run-time. That's why the type library is such an important piece to worry about.

Since we provide a C/C++ implementation for this COM server, we are also going to ask the type-library compiler to produce an interface header to derive from. Right-click on simplecomserver.idl, select Settings, then from the MIDL tab provide the "Output header file name" field with the simplecomserver_i.h value. Compile the IDL file again.

This auto-generated header file is somewhat complex to read, because it carries a lot of implementation details the COM library could not hide. But we don't really have to care about it. Let's just remember the header file is a class declaration we are going to implement. That's easy so far, so let's create a new header file, simplecomserverImpl.h, and provide it with this declaration:

If you compare this with what we declared in the .idl file a couple of minutes ago, you'll notice it looks much the same and is just expressed 100% with C/C++ syntax. We can also note 3 methods there from the IUnknown interface. Those methods help to manage the COM server life cycle by providing routing capabilities (QueryInterface), and safe reference counting (AddRef, Release). But there's no mystery or hidden trick, we are going to implement these methods right now. Let's create an implementation file simplecomserverImpl.cpp, and paste the code below:

Our COM server is implemented so far. Of course anyone who heard of COM in his life knows that a COM server needs to be registered before being used. Indeed the client application, whatever application it is, is going to use indirectly the COM library, which in turns looks up a dictionary of known COM servers in the registry (the HKEY_CLASSES_ROOT\CLSID key).

So what we are going to do is make sure that our build process also automatically registers our COM server. This is going to require a few steps and you'd better attach your seat belt! Of course, the fine thing is that we will never be required to create the many registry keys by hand, I mean by raw and stupid registry-related code. There would be some work there since we need to register the COM object itself, the type-library, and the interface.

Let's begin by adding a few exported functions. Those exported functions are available from outside and allow the registration tool (ever heard of regsvr32.exe?) to request for registration. That's the DllRegisterServer function. Let's add a new type of file: simplecomserver.def and paste what's below in it:

A .def file just tells the linker to let the listed functions be available from outside. For instance, you can use the dumpbin /exports command line (MSDEV tool, requires the MSDEVDIR in the path), or even the Dependency Walker (another MSDEV tool) to see them as soon as this file is added to the project files, and the project built.

A few notes here: first of all, the 4 functions are prefixed with STDAPI, that's just to tell the linker to export them without any C++ mangling (otherwise @ and numbers would appear around the function names, somewhat unnecessary as for now). We also provide a new DllMain implementation replacing the default code provided by the class wizard a couple of minutes ago. Now you need to remove what the class wizard generated for us in simplecomserver.cpp (otherwise the linker would complain of a duplicate DllMain() implementation).

DllRegisterServer() and DllUnregisteServer() are the entry points for the registration. At this point, we are not going to dive much into details, let's just remember that the type-library knows the ins and outs of the COM server and will perform most of the actual registration itself.

And we have DllGetClassObject(). This one is the entry-point used by the outside to create an instance of our COM server. For odd reason, those who invented COM wanted to have intermediate objects to play with. So let their be the class factory. This object behaves much like a COM object itself, although it doesn't have an associated IDL file. DllGetClassObject() is called by the COM plumbing on behalf of the outside application, and expects in return a valid pointer to an IClassFactory interface. This interface (again an interface is a simple class) implements the CreateInstance() construction method, and that one actually instantiates our COM server. As you can see, so far there's not much to worry about the class factory. We just copy/paste this code in simplecomserverImpl.cpp:

I hope all this registry mess is clearer to you now. Basically you can see that registering a COM component is just a matter of adding the IDs of interfaces, objects, and type-libraries in the right places. Again, no hidden trick here.

For any reason, issues may occur while performing the registration and you may not get the successful "DllRegisterServer in simplecomserver.dll succeeded." message. But we don't have to worry, here are the steps to debug the registration:

In the DllMain() implementation, add a call to DllRegisterServer(). Doing so, each time the DLL is loaded, DllRegisterServer() will be called. Remember that's a temporary change, if you don't remove this call when registration seems to work fine, what it will do will be self-registration any time the DLL is loaded.

Put a breakpoint inside DllRegisterServer()

Start debug, and browse for any launch process to start with. In fact we are telling the debugger to start a process and automatically load our DLL.

Debug the registration code.

If you want to un-register your component, just add the -u option in the regsvr32 shell command. As a result, DllUnregisterServer() will be called instead.

Anyone interested to test the COM server with a C/C++ client application, here is the code to do so (see the TestSimplecomserver.dsp project in the zip package):

Applications with built-in automation languages (VB, Perl, Python, ...) cannot yet get to work with it. In fact, we must derive our interface from IDispatch instead of IUnknown, and provide a default implementation for 4 methods. We are going to see this in the next section.

It's a DLL. To make a COM server live behind the boundaries of a separate .EXE, we need some extra code. We are going to see this in the next section.

An automation COM DLL

We are not going to build the automation-enabled COM DLL from scratch, since it's only a minor change over the simple COM server DLL. However, you can as an exercise try to do so.

The resulting code is provided in the automcomserver.zip package.

What we are going to do is replace the IUnknown interface support by IDispatch support (which itself inherits IUnknown, this is the reason why the 3 methods QueryInterface(), AddRef() and Release() won't go away). But then why? Basically the IDispatch interface provides a convenient programmatic way to parse the type-library, and list the method names exposed by an anonymous IDispatch-derived interface. This is aimed to provide what's known as late binding. In other words, thanks to late binding, a client application does not need anymore to statically link with an interface it uses. When, in the simple COM server DLL, we include the simplecomserver_i.h interface declaration, we are in fact providing the compiler a static function vtable. This is not always good. Late binding allows to retrieve the position of an interface function by giving its name. In the long term, this provides software developers with a versatile binding system. This service is provided by IDispatch, a sort of pointer to the type-library.

But that's not enough, what about the parameters? Automation languages are supposed to be able to guess method params on-the-fly at design-time, or even only at run-time. At design time, the problem is solved easily. In fact, when you registered your COM server, you registered the type-library as well, thus any automation-enabled language can read it on your behalf, extract all objects, interfaces, methods, and params, and then expose it through intellisense (for instance). If you are using VB, you typically add the type-library with the Tools \ References menu. Then the object browser lists all the things mentioned. So we are able to call methods discovered on-the-fly at design-time, but what about run-time? At run-time, the automation engine needs to perform type binding, and that's what the other methods exposed by IDispatch are for. They provide entry points to the type-library and to an underlying API exposed by the operating system allows to extract all tiny details of your parameters: are they [in] ?, are they BSTR ?, ...

Although all of this sounds a bit scary, we are not going to embarrass ourselves with the details. In fact, most of the IDispatch implementation will be a default implementation already provided by one of the COM libraries. There we go:

So let's take back the .idl interface, and make the following changes on it:

Replace IUnknown by IDispatch, so to reflect the ICOMServer interface now supports automation

Add the [id(1)] prefix at the left of the Name method signature. This id maps the method with an index in a vtable, and is used to discover methods.

Before you run it, don't forget to go to Tools \ References and add a reference to the automcomserver.tlb type-library.

The reason why we don't pass a string as input parameter, unlike what the .idl interface suggests, is that when a param is explicitly tagged [out,retval] it's actually a result value, hence the assignment to the szname variable.

An automation COM exe

For many reasons it's better to run a COM component behind a separate process. Process isolation for security and performance reasons are just to name a few. But that's where things start to get a bit trickier. Indeed, for the end user a .exe COM server will be used exactly the same way as if it was a .dll: CoCreateInstance, QueryInterface, method calls, Release. But the developers have to pay all the implementation details.

We are going to reuse our code, and add a message pump, and register the class object to another table known by the COM library, and used for processes only.

The resulting code is provided in the automexeserver.zip package.

Let's create a new project, Win32 application this time instead Win32 dynamic library and call it automexeserver. Make sure you get this in automexeserver.cpp:

Of course, you need to make a few changes internally in the files so to reflect the new automexeserver name: you need to replace the #include statements that were referencing automcomserver headers, and you need to go to the Project Settings and change the MIDL options for automexeserver.idl: fill the "Output header filename" field with automexeserver_i.h.

As you have probably guessed by now, we don't need automcomserver.def anymore, which was used to declare the exported functions. In fact, a process does not need to export any function since the method call used will be LPC/RPC, not the simple in-process method call we've seen so far. No need to be frightened, we are not going to dive into the marshalling frenzy. So relax.

It's ok to build the project, but it won't work as expected yet.

First of all, let's add some muscles to the application entry-point. Here we are going to initialize ourselves against the internal table of COM processes, and then we are going through breakable message pump. automexeserver.cpp should reflect exactly this :

As you can see, the command line used to start the process is checked against a possible /regserver or /unregserver argument. Indeed, COM processes are not registered with the standard regsvr32 command line. The fact that we have DllRegisterServer() and DllUnregisterServer() should not be misunderstood. We have quite handy registry helpers that we used since the beginning. So it would be stupid not to use them again. But the Dll prefix does not mean we are registering a DLL. In fact, the almost only difference in the registration is we have at some point a LocalServer32 registry key instead of InProcServer32.

Then we register ourselves in the internal COM class object table. (used by the R.O.T.).

Then there is the message pump, waiting for a WM_QUIT message, while processing all other messages along the way. It's important to note that this WM_QUIT message is sent by the operating system whenever you kill the process by hand. And we are going to mimic this to kill ourselves whenever the client application does not need us anymore.

To finish the implementation, we provide to code the 4 mentioned functions in automexeserverImpl.cpp, namely DllRegisterServer(), DllUnregisterServer(), CoEXEInitialize() and CoEXEUninitialize():

Now you can build this project. Don't forget the shell command line: automexeserver.exe /regserver before you use the COM server. You can test that one against the VB code snippet for instance (don't remember to reference the type-library, now it's automexeserver.tlb).

If you want to test the component using a C/C++ client application, use the TestAutomexeserver.dsp project for instance (the only difference with TestSimplecomserver.dsp is the inclusion of the proper interface header files. Experience shows that if you include wrong files, then at run-time a GPF is almost guaranteed!).

And we are done, finally!! Was it that hard?

This article has provided you three real world code samples reflecting the 3 kind of code you need whenever you come nearby the COM frontier.

Hope you find them useful.

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.

Comments and Discussions

Yes, on the server side, a connection point container is barely more than a specialized interface. Implementing it is easy : for instance if you create a COM object using the Visual Studio ATL wizard, then you get a default connection point implementation you can learn from.
Also, plenty of such code on the net.

Thanks, I'll try this aproach. Another question: can the "connection points" mechanism be used from within VB client application. In other words: can VB application respond on events fired from remote (located on remote PC) DCOM server ?

When I compile the example and register the automexeserver.exe, everything runs fine. But when I move the automexeserver.exe into another directory and reregister, the testprogram keeps crashing. I figured out I have to keep the .tlb in same directory with the .exe. Is there some way to compile the .tlb into the .exe so that only one file is to be shipped?

If you take a look at the original, you'll find a statement that references the .tlb file, which means your .tlb becomes part of the resources when the code is compiled. Thanks to this, there is no need to keep the .tlb file aside.
Of course, if you edit the .rc file, or make any change that alters the above, you might end up without the embedded .tlb file and in that case indeed you need to have the file in the same folder.

If I don't want the oleautomation attribute attached to my interface how do I generate the proxy-stub files, and use them (preferably generate them programmatically, if there is some way to #pragma it)?

What if my interfaces may not be oleautomation compliant, or I wish to marshal them myself (like marshalling a tree structure would be more efficient to just marshal the values of the tree and reconstruct it on the other side). Under those circumstances don't I need the to put code in the proxy and stub?

From watching the debugger finish it looks like each time I call the server with a new object request I start a new thread. Is it possible to run more than one object in the same thread? And if so, how do I control this. In theory I would like to have a defined amount of threads, and run an object in a thread that I specify from the pool that I've created.

"each time I call the server with a new object request I start a new thread"

This doesn't sound right. Of course, whenever you start a localserver32 com object, that object lives in a separate process, thus a separate thread than your client thread, but of course the client thread is none affected by this.
And that's even more obvious with in-proc dlls.

I am Customising a COM Server ,Already the certain interfaces are written in OPC.IDL file,and corresponding stubs(OpC.h and OPC_i.cpp).
I need to add some more interfaces. I have written them in the same OPC.IDL and try to compile it it is Giving 19 Errors saying that
OPC.IDL(182) : error MIDL2311 : statements outside library block are illegal in mktyplib compatability mode : [ Interface 'IOPCServerPublicGroups' ]

I am Customising a COM Server ,Already the certain interfaces are written in OPC.IDL file,and corresponding stubs(OpC.h and OPC_i.cpp).
I need to add some more interfaces. I have written them in the same OPC.IDL and try to compile it it is Giving 19 Errors saying that
OPC.IDL(182) : error MIDL2311 : statements outside library block are illegal in mktyplib compatability mode : [ Interface 'IOPCServerPublicGroups' ]
and E:\OPC\GEFANUC\Opcode\OPC.IDL(132) : error MIDL2096 : duplicated attribute : [uuid] [ Interface 'IOPCServer' ]

Even if i do nothing(nothing added in IDL file) and try to compile IDL file it shows thae same error
What could be the reason,
Please Suggest Me
Thanking you

In your article you have taken 2 function i.e
UnregisterServer() and RegisterServer() which are declared in Register.h and .cpp , What are these Files are they Machine generated code, or library files, where i can get these files, The above 2 functions are necessary or is there any other method. Please suggest me in detail.
The article is Dam Good for a beginner.Please give explanasion about Registry files.
I am using VC++ 6.0 .

Yes these files create some of the registry keys involved in the proper registration of a COM object. The fact is, the Windows COM API call ::RegisterTypeLib() which is called first doesn't create everything in the registry so we need to add a few things.
Alternatively, if you have a .rgs file (.rgs files are produced by the ATL Wizard in DevStudio) then you can use the .rgs registrar facility which registers everything for you. No surprise here, the .rgs declares all libraries, objects and interfaces and does all the dirty registration work by hand.

Thank you Very Much for your responce,
But still i am not Clear, How can i create Register.c and register.h files, Everything in your article is clear except about the register files, Please let me know how to create the Register.h and .c files.

Wh ynot just reuse the files from the sample projects. They allow you to register libraries, progids, com objects and com interfaces. I guess this should be enough for most purposes.

And again, another interesting alternative is to have a .rgs file created for you when you use the ATL wizard. In this scenario, you create a COM skeleton, then add all interfaces and objects in your idl file. Make sure to use the Visual Studio class viewer when you add interfaces and methods, since that's the only way to notify the .rgs file that there are more things to take into account. Open the classviewer tab, right-click, and some COM menu options should appear. Once you're done with this, you can grab the resulting .IDL and .RGS file in another project.

Thanks for such nice article, it really helped me develop my life's first EXE COM Server....
But I have the following question..
1. You have done a dispatch interface derivation, i tried initially with IUnknown but my interface never gets registered, it never shows for my class in the Ole Viewer. What need to be done for IUnknown as RegisterTypeLib only works with IDispatch.....