Export (0) Print
Expand All
Abortable Thread Pool
The Analytic Hierarchy Process
API Test Automation in .NET
Asynchronous HttpWebRequests, Interface Implementation, and More
Bad Code? FxCop to the Rescue
Basics of .NET Internationalization
Behind the Scenes: Discover the Design Patterns You're Already Using in the .NET Framework
BigInteger, GetFiles, and More
Binary Serialization of DataSets
Building Voice User Interfaces
Can't Commit?: Volatile Resource Managers in .NET Bring Transactions to the Common Type
CLR Inside Out: Base Class Library Performance Tips and Tricks
CLR Inside Out: Ensuring .NET Framework 2.0 Compatibility
CLR Inside Out: Extending System.Diagnostics
CLR Profiler: No Code Can Hide from the Profiling API in the .NET Framework 2.0
Concurrent Affairs: Build a Richer Thread Synchronization Lock
Custom Cultures: Extend Your Code's Global Reach With New Features In The .NET Framework 2.0
Cutting Edge: Collections and Data Binding
Const in C#, Exception Filters, IWin32Window, and More
Creating a Custom Metrics Tool
DataGridView
DataSets vs. Collections
Determining .NET Assembly and Method References
Experimenting with F#
File Copy Progress, Custom Thread Pools
Finalizers, Assembly Names, MethodInfo, and More
Got Directory Services?: New Ways to Manage Active Directory using the .NET Framework 2.0
High Availability: Keep Your Code Running with the Reliability Features of the .NET Framework
How Microsoft Uses Reflection
ICustomTypeDescriptor, Part 2
ICustomTypeDescriptor, Part 1
Iterating NTFS Streams
JIT and Run: Drill Into .NET Framework Internals to See How the CLR Creates Runtime Objects
Lightweight UI Test Automation with .NET
Low-Level UI Test Automation
Make Your Apps Fly with the New Enterprise Performance Tool
Managed Spy: Deliver The Power Of Spy++ To Windows Forms With Our New Tool
Memory Models: Understand the Impact of Low-Lock Techniques in Multithreaded Apps
Microsoft Java Virtual Machine Update
Microsoft .NET Framework Delivers the Platform for an Integrated, Service-Oriented Web, Part 2
Mini Dump Snapshots and the New SOS
Mutant Power: Create A Simple Mutation Testing System With The .NET Framework
NamedGZipStream, Covariance and Contravariance
.NET Internationalization Utilities
.NET Profiling: Write Profilers With Ease Using High-Level Wrapper Classes
No More Hangs: Advanced Techniques To Avoid And Detect Deadlocks In .NET Apps
The Perfect Host: Create and Host Custom Designers with the .NET Framework 2.0
Phoenix Rising
Scheme Is Love
Security Enhancements in the .NET Framework 2.0
Sepia Tone, StringLogicalComparer, and More
Software Testing Paradoxes
Stay Alert: Use Managed Code To Generate A Secure Audit Trail
Stream Decorator, Single-Instance Apps
StringStream, Methods with Timeouts
SUPERASSERT Goes .NET
Tailor Your Application by Building a Custom Forms Designer with .NET
Test Harness Design Patterns
ThreadPoolPriority, and MethodImplAttribute
ThreadPoolWait and HandleLeakTracker
Three Vital FXCop Rules
A Tidal Wave of Change
To Confirm is Useless, to Undo Divine
Touch All the Bases: Give Your .NET App Brains and Brawn with the Intelligence of Neural Networks
Transactions for Memory
Trustworthy Software
Tune in to Channel 9
UDP Delivers: Take Total Control Of Your Networking With .NET and UDP
UI on the Fly: Use the .NET Framework to Generate and Execute Custom Controls at Run Time
Unexpected Errors in Managed Applications
Unhandled Exceptions and Tracing in the .NET Framework 2.0
Using Combinations to Improve Your Software Test Case Generation
Wandering Code: Write Mobile Agents In .NET To Roam And Interact On Your Network
What Makes Good Code Good?
XML Comments, Late-bound COM, and More
Expand Minimize

Registration-Free Activation of COM Components: A Walkthrough

 

Steve White
Premier Support for Developers, Microsoft UK

Leslie Muller
Global IT Research & Development, Credit Suisse First Boston

July 2005

Summary: The Microsoft Platform SDK does an excellent job of documenting the topics of isolated applications and side-by-side assemblies. However, not everyone equates this topic with that of registration-free activation of COM components. Registration-free COM is a platform feature of great interest to enterprises with locked-down servers and applications isolated on shared infrastructures. This article walks through a working example of the registration-free activation of a native COM component by native clients and, via COM interop, by a managed client. (18 printed pages)

Applies to:
   Microsoft Windows Server 2003
   Microsoft Windows XP
   Microsoft .NET Framework version 1.1
   Microsoft Visual Studio .NET 2003
   Microsoft Visual Studio 6.0

Download the sample that accompanies this article, MSDNRegFreeCOM.msi.

Contents

Introduction
Registration-Free COM Terminology
Running the Sample
Building the Visual C++ COM Server
Building the C++/.NET Client
Registration-Free Activation
The Visual Basic 6.0 COM Server and Client
Incompatible Apartments
Using the Activation Context API
Troubleshooting
Conclusion
Further Reading

Introduction

Registration-free COM is a mechanism available on the Microsoft Windows XP (SP2 for .NET-based components) and Microsoft Windows Server 2003 platforms. As the name suggests, the mechanism enables easy (for example, using XCOPY) deployment of COM components to a machine without the need to register them.

On the target platforms, one of the stages of initializing a process and its dependent modules is to load any associated manifest files into a memory structure called an activation context. In the absence of corresponding registry entries, it is an activation context that provides the binding and activation information the COM run time needs. No special code is required in the COM server or in the client unless you choose to obviate the use of files by building activation contexts yourself using the activation context API.

In this walkthrough I will build a simple native COM component and consume it, both registered and unregistered, from native and managed clients. The component and the native client will be presented in both Visual C++ and Visual Basic 6.0; the managed client will be presented in both C# and Visual Basic .NET. You can download the source code and samples and see them in action right away or you can follow along with the walkthrough and build them yourself step-by-step.

Registration-Free COM Terminology

Anyone familiar with .NET Framework technology will be accustomed to the term assembly, which denotes a set of one or more modules deployed, named, and versioned as a unit, with one module containing a manifest that defines the set. In Registration-free COM, the terms assembly and manifest are borrowed for ideas that are similar in concept but not identical to their .NET counterparts.

Registration-free COM uses assembly to mean a set of one or more PE modules (i.e., either native or managed) deployed, named, and versioned as a unit. Registration-free COM uses manifest to refer to text files with the .manifest extension containing XML, which either defines the identity of an assembly (assembly manifest), together with the binding and activation details of its classes, or defines the identity of an application (application manifest), together with one or more assembly identity references. An assembly manifest file is named for the assembly, and an application manifest file is named for the application.

The term side-by-side (SxS) assemblies refers to the configuration of different versions of the same COM component, via manifest files, in order that they can be loaded simultaneously by different threads without needing to be registered. SxS enables, and is loosely synonymous with, registration-free COM.

Running the Sample

After you've downloaded and extracted the sample code, you'll find a folder called \deployed. In here is the Visual C++ version of the client application (client.exe), its manifest (client.exe.manifest), the Visual C++ version of the COM server (SideBySide.dll) and its manifest (SideBySide.X.manifest). Go ahead and run client.exe. The expected result is that client.exe will activate an instance of SideBySideClass (implemented in SideBySide.dll) and display the result of calling its Version method, which should look like "1.0.0-CPP".

Building the Visual C++ COM Server

Step 1

The first step is to build a COM server. In Visual Studio create a new Visual C++ ATL Project and call it SideBySide. In the ATL Project Wizard, on the Application Settings tab, deselect the Attributed check box and select the Allow merging of proxy/stub code check box.

In Solution Explorer, right-click the project node and choose Add | Add Class.... Select ATL Simple Object and choose Open. Give the class a short name of SideBySideClass and click Finish.

In Class View, right-click the ISideBySideClass node and choose Add | Add Method.... In the Add Method Wizard, enter Version as the method name, choose a parameter type of BSTR*, enter pVer as the parameter name, select the retval check box, then click Add and then click Finish.

In Class View, expand the CSideBySideClass node and double-click the Version method. Replace the // TODO line with:

*pVer = SysAllocString(L"1.0.0-CPP");

Produce a release build and copy \release\SideBySide.dll into \deployed.

Building the C++/.NET Client

The next step is to build the client. In this part of the walkthrough you have the option to build either a Visual C++ or a .NET client for the Visual C++ COM server. Needless to say, it is possible to mix and match clients and servers written in Visual C++, Visual Basic 6.0, and .NET. If you wish to do so, you will find the samples trivial to amend so that they work together. The sets of clients and servers are organized as they are in this walkthrough in the interest of presenting code that works as-is.

Step 2 (Option A: Visual C++)

Create a new Visual C++ Win32 Console Project called client in a sibling folder relative to the SideBySide project's folder. In the Win32 Application Wizard, on the Application Settings tab, check the Add support for ATL check box.

Edit stdafx.h and add the following line at the top of the file, immediately after the #pragma once:

#define _WIN32_DCOM

Also in stdafx.h add the following line at the bottom of the file:

#import "..\deployed\SideBySide.dll" no_namespace

Replace the contents of client.cpp with this code:

#include "stdafx.h"
#include <iostream>
using namespace std;

void ErrorDescription(HRESULT hr)
{
    TCHAR* szErrMsg;
    if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
      FORMAT_MESSAGE_FROM_SYSTEM, NULL, hr, 
      MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
      (LPTSTR)&szErrMsg, 0, NULL) != 0)
   {
        cout << szErrMsg << endl;
        LocalFree(szErrMsg);
    }
   else
        cout << "Could not find a description for error 0x" 
          << hex << hr << dec << endl;
}

int _tmain(int argc, _TCHAR* argv[])
{
   CoInitializeEx(0, COINIT_MULTITHREADED);

   {
      ISideBySideClassPtr ptr;
      HRESULT hr = ptr.CreateInstance(__uuidof(SideBySideClass));
      if (SUCCEEDED(hr))
      {
         cout << ptr->Version() << endl;
      }
      ErrorDescription(hr);

      char c;
      cin >> c;
   }

   CoUninitialize();

   return 0;
}

Produce a release build and copy \release\client.exe into \deployed.

Step 2 (Option B: .NET Framework)

In Visual Studio .NET 2003 create a new C# or Visual Basic .NET Console Application called client in a sibling folder relative to the SideBySide project's folder. Add a reference to the SideBySide 1.0 Type Library from the Add Reference dialog's COM tab.

Paste the following code inside the Main method:

C# code

   SideBySideLib.ISideBySideClass obj = 
        new SideBySideLib.SideBySideClassClass();
   Console.WriteLine(obj.Version());
   Console.ReadLine();

Visual Basic .NET code

    Dim obj As SideBySideLib.ISideBySideClass = 
      New SideBySideLib.SideBySideClassClass
    Console.WriteLine(obj.Version())
    Console.ReadLine()

Produce a release build and copy client.exe into \deployed.

Step 3

At present the \deployed folder should contain, apart from some intermediate files, only client.exe and SideBySide.dll, and the latter will have been registered by its build process. To check that your server and client work together under these normal circumstances, run \deployed\client.exe and note the expected output "1.0.0-CPP".

Step 4

This walkthrough is about registration-free COM, so we now need to unregister the SideBySide assembly. At a command prompt, navigate to the \deployed folder and execute the command: regsvr32 /u SideBySide.dll.

Step 5

To see what effect the previous step has had, run \deployed\client.exe again and you will see the message "Class not registered". At this stage we have frustrated the COM runtime from finding the information it needs in the registry but we have yet to make the information available by alternative means. We will remedy that in the following steps.

Registration-Free Activation

Step 6

In the \deployed folder, create an application manifest file (a text file) for the client.exe application and call it client.exe.manifest. Paste the following into the file:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" 
  manifestVersion="1.0">
<assemblyIdentity
            type = "win32"
            name = "client"
            version = "1.0.0.0" />
<dependency>
            <dependentAssembly>
                        <assemblyIdentity
                                    type="win32"
                                    name="SideBySide.X"
                                    version="1.0.0.0" />
            </dependentAssembly>
</dependency>
</assembly>

Step 7

In the \deployed folder, create a private assembly manifest file (a text file) for the SideBySide.dll component and call it SideBySide.X.manifest. Paste the following into the file:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" 
  manifestVersion="1.0">

<assemblyIdentity
   type="win32"
   name="SideBySide.X"
   version="1.0.0.0" />

<file name = "SideBySide.dll">

<comClass
    clsid="{[CLSID_SideBySideClass]}"
    threadingModel = "Apartment" />

<typelib tlbid="{[LIBID_SideBySide]}"
       version="1.0" helpdir=""/>

</file>

<comInterfaceExternalProxyStub 
    name="ISideBySideClass" 
    iid="{[IID_ISideBySideClass]}"
    proxyStubClsid32="{00020424-0000-0000-C000-000000000046}"
    baseInterface="{00000000-0000-0000-C000-000000000046}"
    tlbid = "{[LIBID_SideBySide]}" />

</assembly>

I've written the GUID values, which will be particular to your project, in the form of placeholders. The respective values of these placeholders can be found as follows, either in the SideBySide project's SideBySide.idl file, or by opening SideBySide.dll in the OLE/COM ObjectViewer (Oleview.exe) tool.

[
   object,
   uuid([IID_ISideBySideClass]),
   dual,
   nonextensible,
   helpstring("ISideBySideClass Interface"),
   pointer_default(unique)
]
interface ISideBySideClass : IDispatch{
   [id(1), helpstring("method Version")] HRESULT 
        Version([out,retval] BSTR* pVer);
};
[
   uuid([LIBID_SideBySide]),
   version(1.0),
   helpstring("SideBySide 1.0 Type Library")
]
library SideBySideLib
{
   importlib("stdole2.tlb");
   [
      uuid([CLSID_SideBySideClass]),
      helpstring("SideBySideClass Class")
   ]
   coclass SideBySideClass
   {
      [default] interface ISideBySideClass;
   };
};

Step 8

At this point I should broach the subject of embedding assembly manifest files as Win32 resources. Where developers are both able and willing to rebuild their COM components, it is advised that an assembly manifest (like the one created in the previous step) is embedded into the COM DLL as a Win32 resource of type RT_MANIFEST (defined in windows.h). In cases where this is not possible, please be careful to give your assembly (and consequently your assembly manifest) a name that differs from that of the COM DLL's file name. So, in the above case, the COM DLL is called SideBySide but the assembly is called SideBySide.X. If you're interested in the reason for this constraint, it is explained in the Troubleshooting section. In this walkthrough the assembly manifest is not embedded to reflect the many real-world cases in which doing so will not be feasible.

Step 9

To verify that, courtesy of the manifest files, your client is once more able to activate the SideBySideClass class, run \deployed\client.exe and note the expected output "1.0.0-CPP".

The Visual Basic 6.0 COM Server and Client

Step 1

The first step is to build a COM server. Create a new Visual Basic 6.0 ActiveX DLL project. In the Project Explorer select the Project1 node and, in the Properties Window, change its name to SideBySide. In the Project Explorer select the Class1 node and, in the Properties Window, change its name to SideBySideClass.

Paste the following subroutine into the Code Window:

Public Function Version()
Version = "1.0.0-VB6"
End Function

Choose File | Make SideBySide.dll... and navigate to the \deployed folder and then click OK. To prevent new GUIDs from being generated each time you make the dll, choose Project | SideBySide Properties..., then click the Component tab, and in the Version Compatibility group select the Binary Compatibility radio button.

Step 2

Create a new Visual Basic 6.0 Standard EXE project. In the Project Explorer select the Project1 node and, in the Properties Window, change its name to client. Choose File | Save Project As and save the form file and the project file in a sibling folder relative to the SideBySide project's folder. Choose Project | References, select the check box next to SideBySide, and click OK.

Double-click the main form in the Form designer and paste the following code inside Sub Form_Load():

    Dim obj As New SideBySideClass
    MsgBox obj.Version()

Choose File | Make client.exe... and navigate to the \deployed folder and then choose OK.

Step 3

At present the \deployed folder should contain, apart from some intermediate files, only client.exe and SideBySide.dll; and the latter will have been registered by its build process. To check that your server and client work together under these normal circumstances, run \deployed\client.exe and note the expected output "1.0.0-VB6".

Step 4

This walkthrough is about registration-free COM, so we now need to unregister the SideBySide assembly. At a command prompt, navigate to the \deployed folder and execute the command: regsvr32 /u SideBySide.dll.

Step 5

To see what effect the previous step has had, run \deployed\client.exe again and you will see the message "Run-time error '429': ActiveX component can't create object". At this stage we have frustrated the COM runtime from finding the information it needs in the registry, but we have yet to make the information available by alternative means. We will remedy that in the following steps.

Step 6

In the \deployed folder, create an application manifest file (a text file) for the client.exe application and call it client.exe.manifest. Paste the following into the file:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" 
  manifestVersion="1.0">
<assemblyIdentity
            type = "win32"
            name = "client"
            version = "1.0.0.0" />
<dependency>
            <dependentAssembly>
                        <assemblyIdentity
                                    type="win32"
                                    name="SideBySide.X"
                                    version="1.0.0.0" />
            </dependentAssembly>
</dependency>
</assembly>

Step 7

In the \deployed folder, create a private assembly manifest file (a text file) for the SideBySide.dll component and call it SideBySide.X.manifest. Paste the following into the file:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" 
  manifestVersion="1.0">

<assemblyIdentity
   type="win32"
   name="SideBySide.X"
   version="1.0.0.0" />

<file name = "SideBySide.dll">

<comClass
    clsid="{[CLSID_SideBySideClass]}"
    threadingModel = "Apartment" />

<typelib tlbid="{[LIBID_SideBySide]}"
       version="1.0" helpdir=""/>

</file>

<comInterfaceExternalProxyStub 
    name="_SideBySideClass" 
    iid="{[IID__SideBySideClass]}"
    proxyStubClsid32="{00020424-0000-0000-C000-000000000046}"
    baseInterface="{00000000-0000-0000-C000-000000000046}"
    tlbid = "{[LIBID_SideBySide]}" />

</assembly>

I've written the GUID values, which will be particular to your project, in the form of placeholders. The respective values of these placeholders can be found by opening SideBySide.dll in the OLE/COM ObjectViewer (Oleview.exe) tool.

[
  uuid([LIBID_SideBySide]),
  version(1.0),
  custom(50867B00-BB69-11D0-A8FF-00A0C9110059, 8169)

]
library SideBySide
{
    // TLib :     // TLib : OLE Automation : {00020430-0000-0000-
C000-000000000046}
    importlib("stdole2.tlb");

    // Forward declare all types defined in this typelib
    interface _SideBySideClass;

    [
      odl,
      uuid([IID__SideBySideClass]),
      version(1.0),
      hidden,
      dual,
      nonextensible,
      oleautomation
    ]
    interface _SideBySideClass : IDispatch {
        [id(0x60030000)]
        HRESULT Version([out, retval] VARIANT* );
    };

    [
      uuid([CLSID_SideBySideClass]),
      version(1.0)
    ]
    coclass SideBySideClass {
        [default] interface _SideBySideClass;
    };
};

Step 8

To verify that, courtesy of the manifest files, your client is once more able to activate the SideBySideClass class, run \deployed\client.exe and note the expected output "1.0.0-VB6".

Incompatible Apartments

All the COM servers in this walkthrough are built to run in a Single-Threaded Apartment (i.e., they are STA, or Apartment-threaded, components). All the clients are STA except for the C++ client, which is MTA (its thread runs in a Multi-Threaded Apartment). So there is one case in which the client and server live in incompatible apartments and in this case cross-apartment marshaling of COM calls needs to take place between a proxy and a stub. It is this case that makes use of the following section of the assembly manifest file:

...
<typelib tlbid="{[LIBID_SideBySide]}"
       version="1.0" helpdir=""/>

</file>

<comInterfaceExternalProxyStub 
    name="ISideBySideClass" 
    iid="{[IID_ISideBySideClass]}"
    proxyStubClsid32="{00020424-0000-0000-C000-000000000046}"
    baseInterface="{00000000-0000-0000-C000-000000000046}"
    tlbid = "{[LIBID_SideBySide]}" />
...

These elements provide information that would otherwise be present in the registry. The comInterfaceExternalProxyStub element provides enough information for type library marshalling to occur and it is appropriate for COM interfaces that derive from IDispatch (which includes all Automation interfaces). In these cases ole32.dll provides the external proxy-stub used (i.e., external to the files in the assembly). If your COM components implement only dispatch or dual interfaces then this is the element you should use.

Custom interfaces are a little more rare and for these you have to do a little more work. Consider an interface called ISxSCustom that derives directly from IUnknown and is not automation-compatible. For SideBySideClass to implement ISxSCustom, and for clients to call its methods in a registration-free scenario, I need to add a comInterfaceProxyStub element to my assembly manifest. As suggested by the element name, this time the proxy-stub is not external: it is provided either by SideBySide.dll (if I checked the Allow merging of proxy/stub code check box in the ATL Project Wizard) or in SideBySidePS.dll. If my proxy-stub is merged then the new element is a child of the existing file element:

<file name = "SideBySide.dll">
   ...
<comInterfaceProxyStub
name="ISxSCustom" 
iid="{[IID_ISxSCustom]}" />
</file>

Otherwise I need to add a further file element declaring the proxy-stub dll:

<file name = "SideBySide.dll"> ... </file>
<file name = "SideBySidePS.dll">
<comInterfaceProxyStub
name="ISxSCustom" 
iid="{[IID_ISxSCustom]}" />
</file>

If I hadn't needed to illustrate the elements described above, I would have built the Visual C++ COM server to be both-threaded so that its coclasses would have been activated in the client's MTA. In the event that the client and the server exist in the same apartment, the elements just discussed are ignored. However, I urge you not to depend on this circumstance because there may be situations outside of your control where a component is activated in a different apartment than its client, even if their threading models are consistent. Considering the relatively small effort required, I think it's advisable to always include proxy-stub configuration in your manifests.

Using the Activation Context API

In the Introduction section I mentioned that one of the stages of initializing an application's process, on the platforms to which this article applies, is to locate an application manifest file. The application manifest file contains references to the assemblies on which the application has dependencies. In the case of the registration-free activation of native COM components, what is meant by an assembly is a set of one or more COM DLLs collected under a common identity and version.

If your application knows a priori the potential set of coclasses to be directly or indirectly activated during its lifetime, then application manifests are convenient. But there is a relatively rare class of application (e.g., grid servers) that by design do not, prior to runtime, know the modules they will load. In this case, a way of referencing assembly manifest files after process initialization is called for. This is satisfied by the activation context API, the use of which obviates an application manifest file. At its simplest, the technique is to initialize an ACTCTX structure with the location of the assembly manifest file and then create and activate an activation context from it. The following instructions assume you have already built the Visual C++ client application described in Step 2 (Option A).

Edit stdafx.h and add the following line immediately after your definition of _WIN32_DCOM:

#define _WIN32_FUSION 0x0100 // this causes activation context 
structs and APIs to be included.

If you look at the _tmain function in client.cpp, in between the COM initialize and uninitialize calls, you will see a compound statement that activates and calls the SideBySideClass. We need to move this compound statement (everything between the curly braces) inside a new section of code that initializes the activation context as follows:

   ACTCTX actCtx;
   memset((void*)&actCtx, 0, sizeof(ACTCTX));
   actCtx.cbSize = sizeof(ACTCTX);
   actCtx.lpSource = "SideBySide.X.manifest";

   HANDLE hCtx = ::CreateActCtx(&actCtx);
   if (hCtx == INVALID_HANDLE_VALUE)
      cout << "CreateActCtx returned: INVALID_HANDLE_VALUE" 
             << endl;
   else
   {
      ULONG_PTR cookie;
      if (::ActivateActCtx(hCtx, &cookie))
      {
         // previous compound statement goes here...
         ::DeactivateActCtx(0, cookie);
      }
   }

The above code runs before any coclasses are activated. The code simply reads the assembly manifest file (whose name is hard-coded in this example, but should correspond to whichever assembly you wish to load dynamically) into an activation context that it then activates (i.e., makes current). From this point onwards the activation of coclasses occurs as before. You may now discard client.exe.manifest.

An alternative to the direct activation context API, but available only on Windows Server 2003, is the Microsoft.Windows.ActCtx object.

Needless to say, there is a lot more to the activation context API than I have shown here and you can find a link to the full API documentation in the Further Reading section.

Troubleshooting

As we've seen, the registration-free activation of COM components requires no special code in the server or in the client. All that's required is a matching pair of manifest files.

I suggest you approach your own registration-free development the way this walkthrough does. Specifically: first get to a known state by seeing your client working with a registered server; then unregister the server and verify that your error message is what you expected; and finally, remedy the situation by crafting and deploying manifest files. This way your troubleshooting efforts around registration-free activation will be confined to the structure of your manifest files (and the correct embedding of the assembly manifest if you choose to do so).

When troubleshooting registration-free COM issues, the Event Viewer on Windows Server 2003 is your friend. When Windows XP or Windows Server 2003 detects a configuration error it will typically show an error message box titled for the application you have launched and containing the message "This application has failed to start because the application configuration is incorrect. Reinstalling the application may fix this problem." I advise that whenever you see this message you reproduce the problem on Windows Server 2003, consult the System Event Log and look for events from the SideBySide source. The reason I don't suggest that you look at the Windows XP Event Log in these cases is that it will invariably contain a message such as "Generate Activation Context failed for [path]\[application filename].Manifest. Reference error message: The operation completed successfully," which doesn't help identify the problem.

The schemas of the various manifest files are documented in the Platform SDK under the heading Manifest Files Reference, and the schema validation tool Manifestchk.vbs is available, so here I will only call out a few points relevant to the walkthrough. First let's examine the assembly manifest file. For an example, look back to Step 7.

You will recall that, in the registration-free COM sense, an assembly is an abstract idea with which you associate one or more physical files by means of the contents of the assembly manifest file. The name of the assembly appears in three places and must be identical in each: in the name attribute of the assembly manifest file's assemblyIdentity element; in the name attribute of the application manifest file's dependentAssembly/assemblyIdentity element; and the name of the assembly manifest file itself, excluding the .manifest extension. If the manifest file name does not match the name in the application manifest then you will see the following message in the Windows Server 2003 System Event Log: "Dependent Assembly [value of name attribute in application manifest] could not be found and Last Error was The referenced assembly is not installed on your system." If the name element in the assembly manifest is incorrect then you will see the following message in the Windows Server 2003 System Event Log: "Component identity found in manifest does not match the identity of the component requested."

If SideBySide.dll had been a .NET Framework-based component, then we would have been obliged to embed the assembly manifest file into the SideBySide assembly as a Win32 resource (and we would have named the manifest file after the .NET assembly—i.e., SideBySide.manifest). However, due to the assembly-loader's searching sequence, for native COM components it is optional to embed the manifest into the module. Before the assembly-loader looks for [AssemblyName].manifest, it looks for [AssemblyName].dll and searches in it for a Win32 resource of type RT_MANIFEST. The configuration inside the resource must have an AssemblyIdentity element that matches [AssemblyName] as well as the other attributes in the application manifest's AssemblyIdentity reference.

However, if [AssemblyName].dll is found but it does not contain a matching manifest, then the assembly-loading mechanism stops and does not continue to look for [AssemblyName].manifest. At the time of writing, this is true of the assembly-loader on Windows XP (which in this circumstance will display its usual message that "the application configuration is incorrect"), but not on Windows Server 2003. On Windows Server 2003, searching does continue and will find the manifest file even if it has matched the name of a module. However, I urge you not to depend on this behavior. Rather, to ensure you consistently support both platforms, I'd recommend that you embed your assembly manifest in your assembly as an RT_MANIFEST resource whenever feasible. Where it's not feasible, you must give your assembly manifest file a different name from that of any module in the same folder. Alternatively, place your COM component in a child folder of the assembly manifest's folder and reference this child path in the assembly manifest's file element.

The assemblyIdentity element defines the identity of the assembly. For native COM components neither its name nor its version attribute need match that of any physical file, although it's a good idea to apply some kind of consistency.

The file element is the parent to the comClass, typelib, and comInterfaceProxyStub elements. Its purpose is to locate the physical files comprising the assembly. If the file element's name attribute does not correctly reference the files in the file system, then CoCreateInstance will return a HRESULT corresponding to either "Class not registered" or "The specified module could not be found". So that you can install different versions of your COM component in different folders, the name attribute can include a path. On Windows XP SP2 the path can be relative or absolute and can reference a folder anywhere in the file system. Windows Server 2003 requires the path to reference either the application root or a child folder, and if you try to violate this rule then you will see the following message in the Windows Server 2003 System Event Log: "Syntax error in manifest or policy file [assembly manifest filename] [...] The value [...] is invalid."

The comClass element has only one mandatory attribute: clsid. If the application attempts to activate an unregistered coclass whose CLSID is not listed in a comClass element in the assembly manifest, then CoCreateInstance will return a HRESULT with the value REGDB_E_CLASSNOTREG (0x80040154), the message text for which is "Class not registered".

As I've said, the typelib and comInterface[External]ProxyStub elements are required in the event that the client and server exist in different apartments, so the following errors can be seen only when these elements are processed. Configuration errors in these elements cause CoCreateInstance to return a HRESULT that corresponds to the messages "Library not registered", "Error loading type library/DLL", or "no such interface supported". If you see any of these messages, double-check your GUIDs and make sure all the mandatory attributes are present. You will find the manifest file schema in the Platform SDK.

Now let's turn our attention to the application manifest file. For an example, look back to Step 6. The application manifest must be named in the format [application filename].manifest. So, in the walkthrough it was named client.exe.manifest to make it clear it should be read whenever client.exe is loaded into a process. If this is not done correctly, then CoCreateInstance will return a HRESULT with the value REGDB_E_CLASSNOTREG (0x80040154), the message text for which is "Class not registered".

The most important element in the application manifest is the dependentAssembly/assemblyIdentity element. This element is a reference to the equivalent one in the assembly manifest and the two must match exactly. A good way of ensuring that they do so is to copy the element from the assembly manifest and paste it here. If there is any difference whatsoever, you will see the following message in the Windows Server 2003 System Event Log: "Component identity found in manifest does not match the identity of the component requested."

Conclusion

Registration-free COM is a technology that liberates COM components from a dependency on the Windows registry and consequently liberates the applications that use them from requiring dedicated servers. It enables applications with dependencies on different versions of the same COM component to share an infrastructure and to load those various COM component versions side by side in an echo of the .NET Framework versioning and deployment mechanism.

This article walks you through a demonstration of the registration-free activation of native COM components by native client applications written in both Visual C++ and Visual Basic 6.0 and by a managed client. It explains some of how the mechanism works and underlined some possible configuration errors and how to troubleshoot them.

Further Reading

 

About the author

Steve White is an Application Development Consultant working in the Premier Support for Developers team at Microsoft UK. He supports customers developing with Visual C#, Windows Forms, and ASP.NET. His blog has more information about his interests in music, visualizations, and programming.

Leslie Muller is a technologist with the Research & Development team at Credit Suisse First Boston. Leslie has 12 years experience as a developer and technical architect, working in environments such as financial services, technology startups, industrial automation and defence. When he’s not programming or doing research he enjoys skiing, ice hockey and when possible doing slightly mad things with motorized vehicles in extreme environments like Iceland or the Rockies.

Show:
© 2014 Microsoft