This is a Part 2 of a three parts article explaining basics and implementation of COM technology. Part 1 gives a basic introduction to COM architecture. Part 2 shows how to create your own COM server and finally Part 3 shows how to create the COM client and use the COM server created in Part 2. This approach is general and basically, this is the way to interact with any other COM servers also.

In this part I will show how to create a simple COM component using the Microsoft ActiveX Template Library (ATL). ATL is a set of templated C++ classes with which you can easily create efficient COM components. It has special support for key COM features including the IUnknown and IDispatch interfaces, and dual interfaces. ATL can also be used to create ActiveX controls. ATL code can also be used to create single-threaded, apartment-threaded, or free-threaded objects.

In addition to ATL, Microsoft Visual Studio supplies wizards that simplify the process of using ATL as the basis of your development framework.

Using ATL

A significant amount of boilerplate code can be used when creating COM objects. With ATL, Visual Studio provides an easily tailored approach to generating and implementing COM objects. A number of ATL wizards generate boilerplate code, leaving you free to concentrate on the component-specific methods that perform the real work of your COM object.

The code generated by the ATL wizards is based around the core set of templated classes and macros that form ATL. The use of templates (as opposed to the kind of deep inheritance structure used by MFC) enables ATL to produce fast, lightweight code that is suitable for developing components and controls.

A COM object is created using ATL by the following process: 

  1. Create an ATL COM project using the ATL COM AppWizard. The type of project you create will determine the type of server (in-process or out-of-process) that will host your COM objects.  
  2. Insert a new ATL object using the ATL Object Wizard.  
  3. Add methods to your object using the Add Method to Interface Wizard.  
  4. Add properties to your object using the Add Property to Interface Wizard.  
  5. Provide the implementation of your object’s methods.

Step by Step walkthrough

Following steps correspond to Visual Studio 2008 Professional Edition. However, they are simillar in the older versions. You will not encounter major differences.

We will create a simple COM component that has a method that will take a LONG parameter and return its square.

Step 1:

Create a new Visual C++ project. In Project Types, select ATL and in Templates, select ATL Project. Give a name to the project, say, “SampleCOMServer”.

Step 1

Step 1

Step 2:

On clicking OK, ATL Project Wizard will start. In Server type, select DLL, and in Additional options, you may check “Support MFC”. I would recommend this, as it will make your life easier afterwards.

Step 2

Step 2

Click Finish. This will create a skeleton application for you. You might see two projects being created, namely, SampleCOMServer and SampleCOMServerPS. You may safely delete/remove SampleCOMServerPS fromt the solution. This is not required.

You may want to take a look into SampleCOMServer.cpp file. This file contains code for automatic registration and unregistration of the COM component. Also take a look inside the idl file. This will contain the Class ID for each ATL Object that we will create. You may change the Class ID using the guidgen.exe utility.

Step 3:

Until this point, no interfaces have been created. So, now we will add one by adding ATL Simple Object. Just right click the project in Solution Explorer and select Add  and then Class. This will open the Add Class dialog box. Select ATL in the Categories and ATL Simple Object in the Templates. Then click Add.

Step 4:

Now ATL Simple Object Wizard will appear. Here you can type the name of the class. You don’t need to fill up all the textboxes. Just type in the name of the class, say SampleClass. See screenshot below.

Step 4 Page 1

Step 4 Page 1

Click on Next. In the next page, you can select several things like Threading Model etc. You may leave all of them with default selection for now. However, checking ISupportErrorInfo, ConnectionPoints, IObjectWithSite, will help later.

Step 4 Page 2

Step 4 Page 2

Click on Finish. This will create CSampleClass class and ISampleClass interface. You may explore them through Class View. Now we have created an interface. This interface will be exposed for the client side.

Step 5:

Now we need to add methods to the interface, that the clients can call. To add method, right click the interface name in Class View i.e. ISampleClass in our case. Click Add > Add Method.

Give a method name, say SampleMethod. Then you provide the parameters to it.

[in] : To provide “in” parameter, check “in”. Then select the parameter type and give a parameter name. This will be the parameter that the client will supply as input to this method.

[out, retval]: To provide “retval” parameter, select the paremter type. This type should be pointer type e.g. LONG*, then check “retval”. This will be the parameter that the client will get as a return value.

See a sample screenshot below.

Step 5

Step 5

On clicking Next, you will get to set some IDL attributes. you may leave them with default values for now. You may click on Finish.

Step 6:

Take a look into SampleCOMServer.idl file. You will see an entry which looks something like below.

[
object,
uuid(5F9B6614-7728-4E42-B31B-0872A6F42F22),
dual,
nonextensible,
helpstring("ISampleClass Interface"),
pointer_default(unique)
]
interface ISampleClass : IDispatch{
[id(1), helpstring("method SampleMethod")] HRESULT SampleMethod([in] LONG param1, [out,retval] LONG* param2);
};

This entry shows the ClassID of the interface as well as the methods exposed by it. Currently we have added only one method i.e. SampleMethod. And, Class ID is 5F9B6614-7728-4E42-B31B-0872A6F42F22.

You will also see something like,

[
uuid(3640BADB-6767-4E48-933F-B081660E595B),
helpstring("SampleClass Class")
]
coclass SampleClass
{
[default] interface ISampleClass;
[default, source] dispinterface _ISampleClassEvents;
};

This shows the GUID of the co-class, which is 3640BADB-6767-4E48-933F-B081660E595B.

The corresponding implementation class i.e. CSampleClass will also have this method. This is where we will be writing our custom code now. Take a look into SampleClass.h and .cpp file. You will find an entry like below in the header file.

STDMETHOD(SampleMethod)(LONG param1, LONG* param2);

In the SampleClass.cpp file, lets add the code that performs some operation with parameters.


STDMETHODIMP CSampleClass::SampleMethod(LONG param1, LONG* retParam)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
// TODO: Add your implementation code here
*retParam = param1 * param1;
return S_OK;
}

Here I have added a one line code that will square the supplied parameter and update the return parameter with the result.

Step 7:

Go to the project properties and there you may select “Register Output” as “Yes” in the Linker option. This will automatically register the output DLL. Otherwise, you will have to do this manually using the regsvr32.exe utily. Of course, when distributing this DLL, the client or the end user will have to use this utility to register on their computer, or your setup file should do it. But for now, we don’t look into those deployment issues.

You may save all files and rebuild the project. If you are running Windows Vista then you should run the Visual Studio as Administrator. Otherwise the “Register Output” option will fail.

Then you may look into the Debug or Release folder to see the DLL file.

Step 8:

Our process of building a simple COM component is finished. Our COM component exposes single interface called ISampleClass. And, this interface exposes a single method called SampleMethod. You may likewise add several other methods and also the interfaces.

If the registration has succeeded, you may want to take a look inside the registry. So, run regedit.exe. You will see the COM’s registration inside

HKEY_CLASSES_ROOT\CLSID\{3640BADB-6767-4E48-933F-B081660E595B}

You can see that the ProgID is SampleCOMServer.SampleClass.1, currently. We will need this Prog ID later.

—–

This is the end of Part 2. In the next part, I will show you how we can use this COM Component. The steps, I will show, are general and will work with any other third party COM components too.