Click here to monitor SSC
  • Av rating:
  • Total votes: 28
  • Total comments: 2
Phil Wilson

Simple COM server registration

09 March 2006

Prerequisites

This article uses the following technologies: C#, XML, COM.

This article discusses:

COM activation context on Windows XP and Windows Server 2003. Working with manifest files and XML. Exposing .NET COM class libraries using interop.

Simple COM registration
using a side-by-side method

When .NET first appeared it wasn’t unusual to hear the question "Is COM dead?" In fact, COM seems to be alive and well, and in this article I’ll look at the way that Windows XP and Windows Server 2003 allow you to use COM in a side-by-side way without installing the usual registration entries – you won’t need to use the Windows registry to use side-by-side COM components.

COM servers traditionally require registration of a CLSID in the registry so that COM, acting on behalf of client programs, can locate the corresponding server on the system and instantiate the requested class. These registry entries identify the path to the server, the COM apartment model, and the type of the server. Whether it’s a DLL or an executable, .NET class libraries can also act as COM servers, and in these cases the path to the COM server points to mscoree.dll, which uses other information in the same registry key to locate the assembly containing the required class.

Installation of COM servers is usually done by copying the file to a common location and adding registry entries that point to it. Because the COM server’s CLSIDs are unique, they occur once in the registry where the path to the server also occurs. This leads to the requirement that the installation location must be the same for all versions of this server because installation of the same COM server at a different location by a different product installation would effectively "steal" the first product’s registration entries. This situation also leads to the requirement that new versions of a COM server must support the existing interfaces used by older clients. In other words the whole idea that a new version of a COM server must support clients using its older interfaces is a consequence of the requirement that new versions of the server replace older versions at install time, because the registration is in one place in the system’s Registry.

Windows XP SP1 and Server 2003 allow you to use side-by-side COM without these entries in the registry. You install your client program and its COM servers in the same folder, together with a manifest file that contains the registration data that would otherwise be in the registry.

The manifest file is associated with the client program, and its name is that of the client executable with the .manifest suffix. Here’s an example manifest file to call a legacy COM DLL:

< ?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<file name="COMNoReg.dll">
<comclass description="Test Com No registration"
clsid="{4CA60BAB-BB22-47F2-BB3C-B68EF1186681}"
progid="COMNoReg.GetString"
threadingModel="apartment" />
</file>
</assembly>

Listing 1: Manifest file for a legacy COM DLL

As you might expect, the registration data is represented as XML, and the file node contains the COM registration data that would normally be in the registry. If this manifest is incorrectly formed, you’ll see an error message when the client process starts – it will fail if the associated manifest is incorrect. In other words, this manifest is checked as part of the Win32 API CreateProcess, and the content saved for later use. So COM doesn’t go to the manifest file when the code asks for a ProgId or to locate a class object; it uses the content built as part of CreateProcess. The error message you see if the manifest is incorrect is "The application has failed to start because the application configuration is incorrect," together with text saying that reinstalling the application may fix this problem. There’s a more helpful error description in the System Event Log, with an Event Source of SideBySide and text describing exactly where the validation failed, such as:

Syntax error in manifest or policy file "C:\Work Sample Stuff\Samples 1\COMNoReg\CallClient\Debug\CallClient.exe.Manifest" on line 3.

The neat thing about this registration scheme is that the code in the COM client program does not need to change at all. A client program is coded in the usual way, such as in this code fragment:

HRESULT hr = 0;
CLSID cls;

hr = CLSIDFromProgID (L"COMNoReg.GetString", &cls);
IDispatch* getit=NULL;
hr = CoCreateInstance (cls, NULL, CLSCTX_INPROC_SERVER, __uuidof(IDispatch), (void**)&getit);

Note that you don’t need to change anything about the COM server either – it’s only the installation and registration methods that have changed. An example directory structure would be an application folder containing the client program (named CallClient.exe in our example), the COM server COMNoReg.DLL, and the manifest file called CallClient.exe.manifest. You can still use a manifest if the COM classes are already registered in the registry because the manifest file takes precedence over the registry.

As if that wasn’t cool enough, you can also expose COM classes from .NET class libraries in a similar way. The declarations in the manifest file are slightly different because an assembly is being named. Keep in mind that .NET COM DLLs don’t contain the traditional COM entry points such as DllGetClassObject – the classes are instantiated indirectly through mscoree.dll, which locates the assembly and constructs the COM-Callable Wrapper (CCW) that enables the client program to call the .NET class methods. If the DLL named in the example manifest file in Listing 1 referred to a .NET class library, an attempt to call it from the client program would get the error HRESULT 0x800401f9, meaning "Error in the DLL," because the DLL does not export the traditional COM entry points. Consequently the manifest definition of a .NET class library that exposes COM interfaces is slightly different, as shown in Listing 2, where I’ve added a .NET DLL to the manifest.

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

<assemblyidentity name="NetClassLib"
publicKeyToken="d355e8c58bc50a91"
version="1.0.0.0" />
<clrclass clsid="{AFFA0179-0C8F-3F2D-9D38-1021ECC6F69A}"
progid="PDW.NetClass"
threadingModel="Both"
name="NetClassLib.ExportedClass"
runtimeVersion="v1.1.4322">
</clrclass>

<file name="COMNoReg.dll">
<comclass description="Test Com No registration"
clsid="{4CA60BAB-BB22-47F2-BB3C-B68EF1186681}"
progid="COMNoReg.GetString"
threadingModel = "apartment" />
</file>
</assembly>

Listing 2: Interop assembly in the manifest file.

If you’ve ever used Regasm.exe to register a .NET class library for COM Interop, you can see that the entries in the manifest file correspond to those generated in the Registry by running Regasm.exe on the .NET class library. The generated entries are the usual COM entries that would be created under the InprocServer32 key, but assembly information is also added so that Mscoree.dll can locate the class in the specified assembly, where strong name and version are part of the assembly’s identity. In the manifest information, the clrClass node contains the attributes that identify the exported class. It contains the name of the class in the full name format, . (NetClassLib.ExportedClass in this example) as well as the class Guid and ProgId. The .NET class library itself is identified by the assemblyIdentity specification, which names the assembly and its identifying attributes: name, version and public key token.

Although you don’t need to recompile or change your existing COM client code to take advantage of using side-by-side with manifest files, you can choose to embed the manifest file in the resource section of the code file. You do this by adding these lines to your .rc file naming the manifest file:

#define MANIFEST_RESOURCE_ID 1
MANIFEST_RESOURCE_ID RT_MANIFEST "winclient.exe.manifest"

What if you have a manifest in the code file’s resource section and as a separate file? The separate file takes precedence over the manifest in the resource section. This can be useful when maintaining your application. Using the manifest in Listing 2 as an example, you first install the two COM servers and a client program with the manifest in its resources. If you need to ship a corrected version of the .NET class library (with a different strong name or version) you can simultaneously ship a manifest file to override the manifest embedded in the client program’s resources.

If you want to update an existing COM client/server server configuration, there are three methods you can use to have the client call different methods or different versions of the COM server:

You can update an existing installed registered (in the registry) COM server by adding the new version of the COM server to the existing application’s installation folder and also adding a manifest file that will be used by the client programs. This manifest file will take precedence over the registry entries identifying the existing COM server. You can have the manifest in the resources section of a client code file together with the COM servers used privately in the application folder. You can then add some combination of a new COM server and manifest file to the installed application to cause client programs to use a different COM server. You can update the existing installed manifest file to make it refer to a new COM server or to a different method in a COM server.

A self-updating application

To illustrate the versatility of this kind of COM installation, here’s an example using an application where the COM server can be dynamically reconfigured or updated.

Let’s start with an update of the COM server itself. Although such an update would typically consist of replacing the COM server with a new version, I’ll make this a little different by adding a new COM server to the application and updating the client’s manifest file to use this new server. Using the manifest file and the .NET class example above, the existing application is using NetClassLib.Dll, and this has a specific name, version and public key token. So the scenario is that we will add NetClassLib2.Dll to this installed application, and have the application update the manifest file from NetClassLib.Dll to NetClassLib2.Dll. One of the reasons we can do this so easily in the .NET world is that .NET Reflection can give us the attributes required to identify the assembly, as well as the .NET framework support for processing XML.

The following code fragments illustrate how to update the manifest, using a class with a constructor that opens the manifest file, first the basis for a class named ChangeXml that will update the manifest file:

class ChangeXml
{
XmlDocument xd=new XmlDocument();
string _xmlfile=null;
public ChangeXml(string xmlfile)
{
xd.Load (xmlfile);
_xmlfile=xmlfile;
}
}

Listing 3: Basis of a class to update the manifest.

The XmlDocument class is used here because it’s convenient for navigating the XML in the manifest file and updating it. The constructor expects the name of the manifest file, which is then used to instantiate the XmlDocument object xd. We’ll start by creating an instance of this class:

ChangeXml cx = new ChangeXml("callclient.exe.manifest");

Then use the UpdateAssembly method to update the manifest file. This method will be called passing the name of the existing assembly in the manifest and the name of the new class library like this:

cx.UpdateAssembly ("NetClassLib", "NetClassLib2.dll");

Error handling is omitted from the method in Listing 4 for the sake of clarity.

public bool UpdateAssembly (string assemblyname, string assemblypath)
{
// Get new assembly properties
AssemblyName NewAssembly =
AssemblyName.GetAssemblyName(assemblypath);
byte [] pkt = NewAssembly.GetPublicKeyToken();
StringBuilder tmp = new StringBuilder();
foreach (byte b in pkt)
{
tmp.Append (b.ToString("x2"));
}
string pkts=tmp.ToString();
Version ver = NewAssembly.Version;
string newname = NewAssembly.Name;
// Find assembly in manifest and update it.
XmlNode node;
node = xd.DocumentElement;
foreach(XmlNode n in node.ChildNodes)
{
string vx = n.Name;
if (vx=="assemblyIdentity")
{
XmlAttribute xtn = n.Attributes ["name"];
if (xtn.Value==assemblyname)
{
XmlAttribute xpk = n.Attributes ["publicKeyToken"];
xpk.Value = pkts;
XmlAttribute xver = n.Attributes["version"];
xver.Value = ver.ToString();
XmlAttribute xname = n.Attributes ["name"];
xname.Value = newname;
break;
}
}
}
xd.Save (_xmlfile);
return true;
}

Listing 4: Updating the manifest with assembly attributes

As you’d expect, there are two main tasks in this method. First, the assembly attributes are obtained from the new assembly using .NET reflection, and secondly the manifest is updated with those attributes. The result is a manifest file that refers to the new assembly, NetClassLib2.Dll, and that contains the correct assembly attributes.

Updating the manifest’s class

At first glance, it seems unnecessary to think about updating the class reference in a manifest because, as we just saw, a new assembly can be substituted for the client to call. But look at the example class library code fragment in Listing 5.

[ComVisible(true), GuidAttribute("AFFA0179-0C8F-3F2D-9D38-1021ECC6F69A")]
[ProgId("PDW.NetClass")]
[ClassInterface(ClassInterfaceType.AutoDual)]
public class ExportedClass: IGetMyString
{
public void GetMyString()
{
Module mod = Assembly.GetExecutingAssembly().GetModules () [0];
string fullPath = mod.FullyQualifiedName;
MessageBox.Show ( "ExportedClass method says " + fullPath);
}
}

[ComVisible(true), GuidAttribute("5FBAC391-A637-47EC-B62E-3AB5E35F4F0D")]
[ProgId("PDW.NetClass")]
public class AnotherExportedClass: IGetMyString
{
public void GetMyString ()
{
Module mod = Assembly.GetExecutingAssembly().GetModules () [0];
string fullPath = mod.FullyQualifiedName;
MessageBox.Show ( "AnotherExportedClass method says " + fullPath);
}
}

Listing 5: Two class methods with the same ProgId

Listing 5 shows two classes that expose the same COM interface using a single ProgId, but they have a different class name and class Guid. The client program doesn’t know this, of course, because it simply requests the class Guid associated with a ProgId and instantiates the resulting class, all of which is specified in the manifest file. This means that the manifest file can be manipulated so that the client program will use whichever class is currently specified in the manifest file assocatiated with the ProgId. In other words, the clrClass specification can be updated depending on which method should be called. The method in Listing 6, added to the ChangeXml class previously described, will do this update, changing the clrClass specification to refer to another class.

public bool UpdateClass(string assemblyname, StringDictionary classatts)
{
XmlNode node;
node = xd.DocumentElement;
foreach(XmlNode n in node.ChildNodes)
{
string vx = n.Name;
if (vx=="assemblyIdentity")
{
XmlAttribute tn = n.Attributes ["name"];
if (tn.Value==assemblyname)
{
XmlNode xcls = n.NextSibling;
if (xcls.Name=="clrClass")
{
// Update class attributes from dictionary
foreach (DictionaryEntry de in classatts)
{
string xx = de.Key.ToString();
XmlAttribute pk = xcls.Attributes [de.Key.ToString()];
if (pk!=null)
{
pk.Value = de.Value.ToString();
}
}
}
break;
}
}
}
xd.Save(_xmlfile);
return true;
}

Listing 6: Changing the class associated with the ProgId.

In this method the class attributes are passed in using a StringDictionary object. This uses Name/Value pairs that reflect the Name/Value structure of the XML attributes that will be changed, making it a versatile way to represent the changes we wish to make. The method is used in the following way:

StringDictionary ClassChanges = new StringDictionary();
ClassChanges.Add ("clsid", "{5FBAC391-A637-47EC-B62E-3AB5E35F4F0D}");
ClassChanges.Add ("name", "NetClassLib.AnotherExportedClass");
cx.UpdateClass("NetClassLib", ClassChanges);

These changes result in the client program now calling a different method.

What we have here is a mechanism that can be used to change the class method being called by a legacy COM client program. It should be clear that you can’t achieve this kind of versatility in the traditional "one COM server for all" environment that’s usually associated with COM. This technique can be particularly useful when the COM methods are shims or stubs to some external implementation. Perhaps you can have alternative versions of the COM method that provide tracing and timing instrumentation, allowing you to use either an instrumented version or not. Or perhaps different versions of the method access different databases or different remote computers.

The build process and manifests

I didn’t use reflection to derive the COM class information from the assembly because it wasn’t appropriate for that particular use of two COM methods. .NET reflection is rich enough, however, that you could add generation of the manifest files to your build processes. .NET provides the RegistrationServices class to help find the COM information in an assembly. You just saw how to discover assembly attributes using reflection, and the code fragment shown in Listing 4 can be used to discover the COM information in an assembly. This code in Listing 7 assumes you have initialized an XmlTextWriter xt to create the XML file, and that you have an Assembly asm for which you are creating a manifest.

RegistrationServices rs = new RegistrationServices();
Type [] register = rs.GetRegistrableTypesInAssembly(asm);
foreach (Type t in register)
{
string sprogid = rs.GetProgIdForType(t);
string sg = "{"+t.GUID.ToString()+"}";
string sname = t.FullName;
string srtv = asm.ImageRuntimeVersion;

xt.WriteStartElement ("clrClass");
xt.WriteAttributeString ("clsid", sg);
xt.WriteAttributeString ("progid", sprogid);
xt.WriteAttributeString ("name", sname);
xt.WriteAttributeString ("threadingModel", "both");
xt.WriteAttributeString ("runtimeVersion", srtv);
xt.WriteEndElement ();
}

Listing 7: Creating XML COM entries in the manifest

The framework’s RegistrationServices class is the basis of the Regasm.exe utility that registers COM class information in the system registry. Here it’s being used to find the types that can be registered, after which the code gets the COM registration information and writes it to the manifest file associated with the clrClass element. If you like, this is almost a version of Regasm.exe that writes the COM registration to an XML file instead of a registration file. This example does not include any Type Library information, and for that you’d use the TypeLibConverter class and its ConvertAssemblyToTypeLib to generate a type library for the COM client. The examples here are using the automation interface IDispatch, which means there is no requirement for a type library because the types being used are automation-compatible.

The context APIs

When a COM client instantiates a COM class, CoCreateInstance(Ex) will automatically find the context information that was configured in the manifest. If you want to discover for yourself what is configured for your application (perhaps because you want to find the details of the .NET classes that are configured) you can use the SxsLookupClrGuid Win32 API. This API in Sxs.dll must be linked to dynamically using the GetProcAddress Win32 API. The following code fragment assumes a function pointer declaration for pfn_SxsLookupClrGuid that was derived from calling GetProcAddress:

BOOL FoundClass = (pfn_SxsLookupClrGuid)(
SXS_LOOKUP_CLR_GUID_USE_ACTCTX |
SXS_LOOKUP_CLR_GUID_FIND_CLR_CLASS,
&theGuid,
NULL,
(PVOID) outputBuffer,
(SIZE_T) OUTPUT_BUFFER_SIZE,
&neededBufferSize );

See the Platform SDK documentation for the details (and the example code with this article). The first parameter is a set of flags that in this case specifies that the search should use the activation context (the contents of the manifest for the process) and also that the search is for a .NET runtime class. The second parameter is the address of the Win32 Guid that you’re searching for. When this call is successful the outputBuffer parameter is filled with the contents of a SXS_GUID_INFORMATION_CLR structure:

typedef struct _SXS_GUID_INFORMATION_CLR
{
DWORD cbSize;
DWORD dwFlags;
PCWSTR pcwszRuntimeVersion;
PCWSTR pcwszTypeName;
PCWSTR pcwszAssemblyIdentity;
} SXS_GUID_INFORMATION_CLR, *PSXS_GUID_INFORMATION_CLR;

In this structure, pcwszTypeName points to the full name of the assembly, including the namespace, such as "NetClassLib.ExportedClass" in these examples, and pcwszAssemblyIdentity points to the complete assembly identity, "NetClassLib,publicKeyToken="d355e8c58bc50a91″,version="1.0.0.0″".

For more options, take a look at the CreateActCtx Win32 API that lets you name a manifest file. This function returns an activation context handle that you can use in place of the NULL as the third parameter to SxsLookupClrGuid.

These context APIs are intended for developers building Interop applications where you need to do the type of thing that CoCreateInstance does – you can search the application’s context for the details of the configured .NET classes and associate a COM CLSID with a .NET assembly.

Conclusion

Installing COM servers in this side-by-side way is a big win compared to the traditional installation of registry entries that all refer to the same shared COM server, part of the problem area known as DLL Hell. At installation time, all you need do is copy the files to the application folder and you’re done. Your application works even if the COM servers are already registered because your manifest file takes precedence over the registry. You also have more choices for updating your application because you don’t need to consider other clients using the COM servers and you can configure the manifest and your .NET Interop libraries in a number of ways, as you’ve seen in this article.

If you’re supporting operating systems prior to XP SP1 and Server 2003 you may not wish to design your applications to take advantage of this side-by-side technique. But note that Windows Server 2003 supports 64-bit systems, so you can choose to design your 64-bit applications to use this technique right from the start when the 64-bit .NET framework is available.

Phil Wilson

Author profile:

Phil Wilson is a software engineer at Unisys Corporation in California, a Microsoft MVP (Most Valued Professional) and author of The Definitive Guide to Windows Installer, Apress, 2004.

Search for other articles by Phil Wilson

Rate this article:   Avg rating: from a total of 28 votes.


Poor

OK

Good

Great

Must read
Have Your Say
Do you have an opinion on this article? Then add your comment below:
You must be logged in to post to this forum

Click here to log in.


Subject: Simple COM server registration
Posted by: Anonymous (not signed in)
Posted on: Wednesday, February 21, 2007 at 9:10 AM
Message: My compliments on the article.

You mention the side-by-side COM support in Win XP SP1 and Win 2K3; however I was wondering about Win 2k Pro and Win 2K Server. WIll this technique work on those legacy platforms, assuming .NET framework 2.0 or higher is deployed?

Subject: Anonymous comments disabled
Posted by: AnnaL (view profile)
Posted on: Monday, September 03, 2007 at 5:21 AM
Message: We've had to disable anonymous comments on this article due to relentless spamming.

To post a comment please sign in, or register if you are not already a member.

 

Top Rated

Acceptance Testing with FitNesse: Multiplicities and Comparisons
 FitNesse is one of the most popular tools for unit testing since it is designed with a Wiki-style... Read more...

Acceptance Testing with FitNesse: Symbols, Variables and Code-behind Styles
 Although FitNesse can be used as a generic automated testing tool for both applications and databases,... Read more...

Building Performance Metrics into ASP.NET MVC Applications
 When you're instrumenting an ASP.NET MVC or Web API application to monitor its performance while it is... Read more...

Acceptance Testing with FitNesse: Documentation and Infrastructure
 FitNesse is a popular general-purpose wiki-based framework for writing acceptance tests for software... Read more...

TortoiseSVN and Subversion Cookbook Part 11: Subversion and Oracle
 It is only recently that the tools have existed to make source-control easy for database developers.... Read more...

Most Viewed

A Complete URL Rewriting Solution for ASP.NET 2.0
 Ever wondered whether it's possible to create neater URLS, free of bulky Query String parameters?... Read more...

Visual Studio Setup - projects and custom actions
 This article describes the kinds of custom actions that can be used in your Visual Studio setup project. Read more...

.NET Application Architecture: the Data Access Layer
 Find out how to design a robust data access layer for your .NET applications. Read more...

Calling Cross Domain Web Services in AJAX
 The latest craze for mashups involves making cross-domain calls to Web Services from APIs made publicly... Read more...

Web Parts in ASP.NET 2.0
 Most Web Parts implementations allow users to create a single portal page where they can personalize... Read more...

Why Join

Over 400,000 Microsoft professionals subscribe to the Simple-Talk technical journal. Join today, it's fast, simple, free and secure.