| Introduction to COM - What It Is and How to Use It. |
| Written by Michael Dunn |
| Friday, 30 January 2009 16:00 |
Purpose of this ArticleI have written this tutorial for programmers who are just starting out in COM and need some help in understanding the basics. The article briefly covers the COM specification, and then explains some COM terminology and describes how to reuse existing COM components. This article does not cover writing your own COM objects or interfaces. Updates:
July 22, 2000: Added a couple of more paragraphs IntroductionCOM (Component Object Model) is the popular TLA (three-letter acronym) that seems to be everywhere in the Windows world these days. There are tons of new technologies coming out all the time, all based on COM. The documentation throws around lots of terms like COM object, interface, server, and so on, but it all assumes you"re familiar with how COM works and how to use it. This article introduces COM from the beginning, describes the underlying mechanisms involved, and shows how to use COM objects provided by others (specifically, the Windows shell). By the end of the article, you will be able to use the COM objects built-in to Windows and provided by third parties. This article assumes you are proficient in C++. I use a little bit of MFC and ATL in the sample code, but I will explain the code thoroughly, so you should be able to follow along if you are not familiar with MFC or ATL. The sections in this article are: COM - What Exactly Is It? - A quick introduction to the COM standard, and the problems it was created to solve. You don"t need to know this to use COM, but I"d still recommend reading it to get an understanding of why things are done the way they are in COM. Definitions of the Basic Elements - COM terminology and descriptions of what those terms represent. Working with COM Objects - An overview of how to create, use, and destroy COM objects.
The Base Interface - IUnknown - A description of the methods in the base interface, Pay Close Attention - String Handling - How to handle strings in COM code. Bringing it All Together - Sample Code - Two sets of sample code that illustrate all the concepts discussed in the article.
Handling HRESULTs - A description of the References - Books you should expense if your employer will let you. :) COM - What exactly is it?COM is, simply put, a method for sharing binary code across different applications and languages. This is unlike the C++ approach, which promotes reuse of source code. ATL is a perfect example of this. While source-level reuse works fine, it only works for C++. It also introduces the possibility of name collisions, not to mention bloat from having multiple copies of the code in your projects. Windows lets you share code at the binary level using DLLs. After all, that"s how Windows apps function - reusing kernel32.dll, user32.dll, etc. But since the DLLs are written to a C interface, they can only be used by C or languages that understand the C calling convention. This puts the burden of sharing on the programming language implementer, instead of on the DLL itself. MFC introduced another binary sharing mechanism with MFC extension DLLs. But these are even more restrictive - you can only use them from an MFC app. COM solves all these problems by defining a binary standard, meaning that COM specifies that the binary modules (the DLLs and EXEs) must be compiled to match a specific structure. The standard also specifies exactly how COM objects must be organized in memory. The binaries must also not depend on any feature of any programming language (such as name decoration in C++). Once that"s done, the modules can be accessed easily from any programming language. A binary standard puts the burden of compatibility on the compiler that produces the binaries, which makes it much easier for the folks who come along later and need to use those binaries. The structure of COM objects in memory just happens to use the same structure that is used by C++ virtual functions, so that"s why a lot of COM code uses C++. But remember, the language that the module is written in is irrelevant, because the resulting binary is usable by all languages. Incidentally, COM is not Win32-specific. It could, in theory, be ported to Unix or any other OS. However, I have never seem COM mentioned outside of the Windows world. Definitions of the Basic Elements
Let"s go from the bottom up. An interface is simply a group of functions. Those functions are called methods. Interface names start with I, for example Interfaces may inherit from other interfaces. Inheritance works just like single inheritance in C++. Multiple inheritance is not allowed with interfaces. A coclass (short for component object class) is contained in a DLL or EXE, and contains the code behind one or more interfaces. The coclass is said to implement those interfaces. A COM object is an instance of a coclass in memory. Note that a COM "class" is not the same as a C++ "class", although it is often the case that the implementation of a COM class is a C++ class. A COM server is a binary (DLL or EXE) that contains on or more coclasses. Registration is the process of creating registry entries that tell Windows where a COM server is located. Unregistration is the opposite - removing those registry entries. A GUID (rhymes with "fluid", stands for globally unique identifier) is a 128-bit number. GUIDs are COM"s language-independent way of identifying things. Each interface and coclass has a GUID. Since GUIDs are unique throughout the world, name collisions are avoided (as long as you use the COM API to create them). You will also see the term UUID (which stands for universally unique identifier) at times. UUIDs and GUIDs are, for all practical purposes, the same. A class ID, or CLSID, is a GUID that names a coclass. An interface ID, or IID, is a GUID that names an interface. There are two reasons GUIDs are used so extensively in COM:
An HRESULT is an integral type used by COM to return error and success codes. It is not a "handle" to anything, despite the H prefix. I"ll have more to say about HRESULTs and how to test them later on. Finally, the COM library is the part of the OS that you interact with when doing COM-related stuff. Often, the COM library is referred to as just "COM," but I will not do that here, to avoid confusion. Working with COM Objects
Every language has its own way of dealing with objects. For example, in C++ you create them on the stack, or use Creating a new object
Deleting objects
Now, in between those two stages of creating and destroying the object, you actually have to use it. When you create a COM object, you tell the COM library what interface you need. If the object is created successfully, the COM library returns a pointer to the requested interface. You can then call methods through that pointer, just as if it were a pointer to a regular C++ object. Creating a COM object
To create a COM object and get an interface from the object, you call the COM library API
HRESULT CoCreateInstance (
REFCLSID rclsid,
LPUNKNOWN pUnkOuter,
DWORD dwClsContext,
REFIID riid,
LPVOID* ppv );
The parameters are:
When you call
Here"s a sample call, which instantiates a HRESULT hr; IShellLink* pISL; hr = CoCreateInstance ( CLSID_ShellLink, // CLSID of coclass NULL, // not used - aggregation CLSCTX_INPROC_SERVER, // type of server IID_IShellLink, // IID of interface (void**) &pISL ); // Pointer to our interface pointer if ( SUCCEEDED ( hr ) ) { // Call methods using pISL here. } else { // Couldn"t create the COM object. hr holds the error code. }
First we declare an Deleting a COM object
As stated before, you don"t free COM objects, you just tell them that you"re done using them. The
If your app uses a lot of different COM objects, it"s vitally important to call
Continuing the above example, here"s how you would use
// Create COM object as above. Then...
if ( SUCCEEDED ( hr ) )
{
// Call methods using pISL here.
// Tell the COM object that we"re done with it.
pISL->Release();
}
The The Base Interface - IUnknown
Every COM interface is derived from
We"ve already seen
HRESULT IUnknown::QueryInterface (
REFIID iid,
void** ppv );
The parameters are:
Let"s continue our shell link example. The coclass for making shell links implements
HRESULT hr;
IPersistFile* pIPF;
hr = pISL->QueryInterface ( IID_IPersistFile, (void**) &pIPF );
You then test Pay Close Attention - String HandlingI need to make a detour for a few moments, and discuss how to handle strings in COM code. If you are familiar with how Unicode and ANSI strings work, and know how to convert between the two, then you can skip this section. Otherwise, read on.
Whenever a COM method returns a string, that string will be in Unicode. (Well, all methods that are written to the COM spec, that is!) Unicode is a character encoding scheme, like ASCII, only all characters are 2 bytes long. If you want to get the string into a more manageable state, you should convert it to a
When you get a Unicode string back from a COM method, you can convert it to a
WideCharToMultiByte()
You can convert a Unicode string to an ANSI string with the
int WideCharToMultiByte (
UINT CodePage,
DWORD dwFlags,
LPCWSTR lpWideCharStr,
int cchWideChar,
LPSTR lpMultiByteStr,
int cbMultiByte,
LPCSTR lpDefaultChar,
LPBOOL lpUsedDefaultChar );
The parameters are:
Whew, a lot of boring details! Like always, the docs make it seem much more complicated than it really is. Here"s an example showing how to use the API:
// Assuming we already have a Unicode string wszSomeString...
char szANSIString [MAX_PATH];
WideCharToMultiByte ( CP_ACP, // ANSI code page
WC_COMPOSITECHECK, // Check for accented characters
wszSomeString, // Source Unicode string
-1, // -1 means string is zero-terminated
szANSIString, // Destination char string
sizeof(szANSIString), // Size of buffer
NULL, // No default character
NULL ); // Don"t care about this flag
After this call, wcstombs()
The CRT function
size_t wcstombs (
char* mbstr,
const wchar_t* wcstr,
size_t count );
The parameters are:
wcstombs ( szANSIString, wszSomeString, sizeof(szANSIString) );
CString
The MFC
// Assuming we already have wszSomeString...
CString str1 ( wszSomeString ); // Convert with a constructor.
CString str2;
str2 = wszSomeString; // Convert with an assignment operator.
ATL macros
ATL has a handy set of macros for converting strings. To convert a Unicode string to ANSI, use the
#include <atlconv.h>
// Again assuming we have wszSomeString...
{
char szANSIString [MAX_PATH];
USES_CONVERSION; // Declare local variable used by the macros.
lstrcpy ( szANSIString, OLE2A(wszSomeString) );
}
The
There is an Sticking with Unicode
On the other hand, you can just keep the string in Unicode if you won"t be doing anything complicated with the string. If you"re writing a console app, you can print Unicode strings with the
wcout << wszSomeString;
But keep in mind that
wcout << L"The Oracle says..." << endl << wszOracleResponse;
If you keep a string in Unicode, there are a couple of restrictions:
Bringing it All Together - Sample CodeFollowing are two examples that illustrate the COM concepts covered in the article. The code is also contained in the article"s sample project. Using a COM object with a single interfaceThe first example shows how to use a COM object that exposes a single interface. This is the simplest case you"ll ever encounter. The code uses the Active Desktop coclass contained in the shell to retrieve the filename of the current wallpaper. You will need to have the Active Desktop installed for this code to work. The steps involved are:
WCHAR wszWallpaper [MAX_PATH];
CString strPath;
HRESULT hr;
IActiveDesktop* pIAD;
// 1. Initialize the COM library (make Windows load the DLLs). Normally you would
// call this in your InitInstance() or other startup code. In MFC apps, use
// AfxOleInit() instead.
CoInitialize ( NULL );
// 2. Create a COM object, using the Active Desktop coclass provided by the shell.
// The 4th parameter tells COM what interface we want (IActiveDesktop).
hr = CoCreateInstance ( CLSID_ActiveDesktop,
NULL,
CLSCTX_INPROC_SERVER,
IID_IActiveDesktop,
(void**) &pIAD );
if ( SUCCEEDED(hr) )
{
// 3. If the COM object was created, call its GetWallpaper() method.
hr = pIAD->GetWallpaper ( wszWallpaper, MAX_PATH, 0 );
if ( SUCCEEDED(hr) )
{
// 4. If GetWallpaper() succeeded, print the filename it returned.
// Note that I"m using wcout to display the Unicode string wszWallpaper.
// wcout is the Unicode equivalent of cout.
wcout << L"Wallpaper path is:n " << wszWallpaper << endl << endl;
}
else
{
cout << _T("GetWallpaper() failed.") << endl << endl;
}
// 5. Release the interface.
pIAD->Release();
}
else
{
cout << _T("CoCreateInstance() failed.") << endl << endl;
}
// 6. Uninit the COM library. In MFC apps, this is not necessary since MFC does
// it for us.
CoUninitialize();
In this sample, I used Using a COM object with a multiple interfaces
The second example shows how to use The steps involved are:
CString sWallpaper = wszWallpaper; // Convert the wallpaper path to ANSI IShellLink* pISL; IPersistFile* pIPF; // 1. Initialize the COM library (make Windows load the DLLs). Normally you would // call this in your InitInstance() or other startup code. In MFC apps, use // AfxOleInit() instead. CoInitialize ( NULL ); 2. Create a COM object, using the Shell Link coclass provided by the shell. // The 4th parameter tells COM what interface we want (IShellLink). hr = CoCreateInstance ( CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void**) &pISL ); if ( SUCCEEDED(hr) ) { // 3. Set the path of the shortcut"s target (the wallpaper file). hr = pISL->SetPath ( sWallpaper ); if ( SUCCEEDED(hr) ) { // 4. Get a second interface (IPersistFile) from the COM object. hr = pISL->QueryInterface ( IID_IPersistFile, (void**) &pIPF ); if ( SUCCEEDED(hr) ) { // 5. Call the Save() method to save the shortcut to a file. The // first parameter is a Unicode string. hr = pIPF->Save ( L"C:\wallpaper.lnk", FALSE ); // 6a. Release the IPersistFile interface. pIPF->Release(); } } // 6b. Release the IShellLink interface. pISL->Release(); } // Printing of error messages omitted here. // 7. Uninit the COM library. In MFC apps, this is not necessary since MFC // does it for us. CoUninitialize(); Handling HRESULTs
I"ve already shown some simple error handling, using the
An
If you look up error codes in the
Fortunately, there are easier ways to determine the meaning of an
You can also look up
ReferencesEssential COM by Don Box, ISBN 0-201-63446-5. Everything you"d ever want to know about the COM spec and IDL (interface definition language). The first two chapters go into great detail about the COM spec and the problems it was designed to solve. MFC Internals by George Shepherd and Scot Wingo, ISBN 0-201-40721-3. Contains an in-depth look at MFC"s COM support. Beginning ATL 3 COM Programming by Richard Grimes, et al, ISBN 1-861001-20-7. This book goes into depth about about writing your own COM components using ATL. Something wrong with this article? Report it |
| Last Updated on Friday, 06 February 2009 16:00 |
Recent Programming Articles
Enhanced rich edit controlI needed a dialog box app based on RichEdit control. I have searched over the Net and I found Jeremy Iversons article. I downloaded the code and studied it. It was not what I have needed but it was a good start point. So, my app is a kind of a RTF word processor. It has menus, to... .NET | Saturday, 31 January 2009 |
CheckListBox based on ListBox that supports ReadOnlyThis is based on article at http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2384558&SiteID=1&mode=1 where I extended the code provided by ... .NET | Saturday, 31 January 2009 |
Introduction to COM - What It Is and How to Use It.COM (Component Object Model) is the popular TLA (three-letter acronym) that seems to be everywhere in the Windows world these days. There are tons of new technologies coming out all the time, all based on COM. The documentation throws around lots of terms like COM object... General | Saturday, 31 January 2009 |
Introduction to the Validation Application BlockAny application that takes input from user or other system must ensure that the information is correct format, in terms of some rules that specify by several ways.Like a registration form has lots field like social security number, credit card, email has s... .NET | Saturday, 31 January 2009 |
WPF Multi-Lingual at RuntimeBackground Look of the Current CrypTool Program (C/C++, MFC) ... General | Saturday, 31 January 2009 |
Opening / Saving multiple types of documents in MFC MDI applicationsThis is a simple visual C++ 6.0 multiple document (MDI) project that allows opening / saving multiple types of documents. Normally, when you start an MDI project, you are allowed to associate one file type (i.e. extension) with your project. If you want to be able to open / save m... C/C++ | Saturday, 31 January 2009 |
Multiple Monitor SupportC/C++ | Saturday, 31 January 2009 |
VB.NET Wav file graphical viewer controlFirstly, I would like to acknowledge that the some of the methods for this control came from Steve McMahons (http://www.vbaccelerator.com/) VB6 version, so, all the credit for the idea goes to him. The contribution I have... C/C++ | Saturday, 31 January 2009 |
Weak Events in C#What exactly are events? Part 1: Listener-side weak events ... C# | Saturday, 31 January 2009 |
Using Events and Delegates in C#There are a number of articles available which deal with delegates. The objective of this article is to present events and delegates in a very straightforward fashion, which will enable and encourage developers to include events in classes that they design and develop, as well as ... C# | Saturday, 31 January 2009 |
How to use Crystal Reports with Access database in C#There are many ways to present data to users. For example, you can write code to loop through records and print them inside your Windows form. However, to do such a code any work beyond basic formatting can be very complicated to program. With Crystal Reports, you can quickly crea... C# | Saturday, 31 January 2009 |
Manifest Reader for Ant ToolThis article presents a simple but quite useful ant task for reading MANIFEST.MF file contained in a jar and selectively print its content. Java | Saturday, 31 January 2009 |
Task Pattern meets the Command PatternCommunicating across a network can be time consuming manifesting itself as an unresponsive user interface. Avoiding long and non-deterministic operations on the UI thread will not increase overall performance but it will give the user some degree of confidence that the application... General | Saturday, 31 January 2009 |
Source Code Line CounterWhile updating my cv, I wanted a quick way of seeing how many lines of code my application contained (160,430 lines!). I havent looked into Visual Studio Add-ins too much so I wrote a simple stand-alone application that has a few nice features. .NET | Friday, 30 January 2009 |
CCheckComboBox IIThis control is actually a modification of the CCheckComboBox class that was written by Magnus Egelberg, Lunalogik... .NET | Friday, 30 January 2009 |
Writing to and read from the console - From a GUI application using the same cout/cin and printf/scanfHi folks, let me start with an interesting problem. I was developing an MFC dialog based application, and now I wanted to write some custom code in the OnPaint() function. Done! Unfortunately, in the first run, my application was blown up. I found that the problem was... .NET | Friday, 30 January 2009 |
Outlook style GUI - three-way split formI saw a post on one of the boards where someone was asking whether typical GUIs are possible using the .NET framework. The one that came to my mind instantly was the Outlook Express GUI. I dont mean the Outlook folder bar or its functionality. I meant the three-way split. MFC pe... .NET | Friday, 30 January 2009 |
Read and Update CAPS or NUM lock status from your applicationA few months back, I was developing a desktop application for a client. After completing the requirements, I was enhancing the UI specially the main form. In the previous versions of Microsoft Word (before ver. 2007 ), there are some label which are clickable an... .NET | Friday, 30 January 2009 |
Plain C Resampling DLLWhen I first saw Libor Tinkas great article on resampling Image Resizing - Outperform GDI+, I said to myself: "very good stuff indeed, I really need a similar tool for GDI based b... Multimedia | Friday, 30 January 2009 |
Ray casting in a 2D tile-based environmentRay casting is a technique widely used in 3D environments. It consists in casting a "ray" from a given position in a given direction. The algorithm will then tell you things like where a collision happened, what was the normal of the surface, etc. In this article, I will... Multimedia | Friday, 30 January 2009 |
|
More in: C#, C/C++, .NET, Java, Game Dev, Multimedia, General |
|
Latest Articles
- AntiHisto
- Thumbnail images in PHP
- Online Dating Websites
- Hack to enforce the cache of an XmlDataSource to invalidate
- AutoSearch SELECT tag
- Nine ASP.NET Site Navigation Problem Solutions: Part 1
- Enhanced rich edit control
- CheckListBox based on ListBox that supports ReadOnly
- Introduction to COM - What It Is and How to Use It.
- Introduction to the Validation Application Block

