Monday, June 4, 2007

Isolated COM

One of the worst documented features of Visual Studio is Isolated COM. Actually, there's lots of documentation on Isolated COM, but none of it is particularly useful. The mechanics of making Isolated COM work under Visual C++ are actually pretty easy, so this short article tells you what to do.

Isolated COM allows your application to use ActiveX components without having to register them. The original vision of this was to allow XCOPY deployment of the application, but Isolated COM has many benefits. You can have a private copy of the DLL without worrying that another application will install an older or newer copy that breaks your application. Isolated COM also allows you to successfully install and run on non-Administrator accounts.

A short history of how activation works: In Windows 9x and NT, ActiveX components were found by looking in HKEY_CLASSES_ROOT, which was really a link to HKEY_LOCAL_MACHINE\Software\Classes. Windows 2000 added support for also defining components in HKEY_CURRENT_USER\Software\Classes and HKEY_CLASSES_ROOT became a merged view of HKLM and HKCU. Finally, Windows XP and Windows Vista added support for looking for COM information in the manifest of the executable. This means that the manifest contains the DLL name, CLSID information, interface information, and type library pointers - most of the information that would normally appear in HKCR.

As you might guess, these definitions can be quite lengthy and creating them by hand is error prone. Visual Studio 2005, which supports manifests, includes support for automatically generating and embedding the correct XML declarations.

To switch from "old fashioned" ActiveX usage to Isolated COM, follow these steps:

1. First, make sure components are created with their CLSID and not the ProgId. In other words, don't use Word.Document, use {F4754C9B-64F5-4B40-8AF4-679732AC0607}. If you are using the #import command in Visual C++, you can use the __uuidof operator instead of a string. For example:

HRESULT hr = pDoc.CreateInstance("Word.Document");
HRESULT hr = pDoc.CreateInstance(__uuidof(Word::Document));

2. If you made any changes for #1, test your code with the DLL registered the normal way and make sure it works. Also make sure that your code checks and handles all HRESULT return values.

3. Unregister the ActiveX DLL using: regsvr32 /u waycool.dll

4. Copy the DLL to be in the same directory as your executable.

5. In Visual Studio 2005:
  • Open the property sheet for the project.
  • Click the plus sign next to "Manifest Tool"
  • Click on "Isolated COM".
  • Next to "Type Library File" enter "waycool.tlb"
  • Next to "Component File Name" enter "waycool.dll"
  • Build the project.
6. Run your application in the debugger and single step through all of the activation code to make sure it is working properly.

Once you've added support for Isolated COM, don't forget to update your installer so that ActiveX components aren't automatically registered under Windows XP and Vista. Also don't forget to retest your software under Win9x and Win2K if you support those versions of Windows.

3 comments:

  1. Thanks for the article, but we don't get this to work for multiple COM DLLs. Consider two COM objects in two separate DLLs each implementing the interface IFoo. Therefore, an entry for "IFoo" shows up in both manifest files. The problem is that the main application (*.exe) fails to start. The error log says "syntax error" in the "IFoo" line of the _second_ server manifest file being parsed. Looks like "isolated COM" does not like common interfaces...

    Any ideas?
    Jörn

    ReplyDelete
  2. That's a ni ce and helpful post. But in my case I have one application and three com objects (with different interfaces) that need to be referenced. So how to enter three isolated com references in the manifest tool? The input boxes seem to accept only one reference.

    ReplyDelete
    Replies
    1. I had this exact problem. Enter each one by itself . Build it to create the manifest and copy the appropriate lines out of the manifest into a separate file. Do this for the two components that are least likely to change and combine them into the same manifest file (this may require additional XML scaffolding.) Then you enter the third COM object in the blank and you enter that external manifest file into the project.

      I know I'm leaving out some steps, but that's the gist of what you need to do. At this point I'm not in a position to be able to document the process in any more detail.

      Delete