Tuesday, September 9, 2008

Registration Free COM for unmanaged components

By unmanaged components I mean COM servers built with unmanaged/native frameworks; ATL, MFC or (heaven forbid) Win32.

Getting Registration Free COM to work on Windows XP for unmanaged components is similar to getting Registration Free COM to work for managed components.  I'm finding that having the manifest as a standalone file helps a lot during debugging and for managing changes.  Visual Studio 2005 has options for setting a lot of these properties (e.g., assembly identity, dependency fragments, etc...) but I've found it easier to keep them in a single file (included in the project but NOT ending in .manifest because these files are merged no matter what).

The key insight for me was that assembly, in this context, doesn't mean the same thing as an assembly in the managed context.  An assembly is a logical grouping of of Portable Executable (PE) modules (.exes, .dlls, etc...).  It can contain 1 or more DLLs but it's something that the developer creates to control the code that gets executed at runtime (in this case by controlling which module the COM runtime binds to the application).

It MUST have a different name than any of the DLLs (really PEs but you get the point) it contains.  As an example (below), I created an assembly named TestATLLib.X that contains 1 PE - TestATLLib.dll.  That assembly uses the following assembly manifest:


<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1"
manifestVersion="1.0">
<assemblyIdentity
type="win32"
name="TestATLLib.X"
version="1.0.0.0" />
<file name = "TestATLLib.dll">
<comClass
clsid="{EBB0B140-09E7-4C47-B6F1-FACD79FA0F55}"
threadingModel = "Apartment" />
<typelib tlbid="{CC66CB00-53FB-41F3-A745-28D467ED7523}"
version="1.0" helpdir=""/>
</file>
<comInterfaceExternalProxyStub
name="ITestSimpleObj"
iid="{C066CE71-F97C-4CA6-8769-72114D3DDC71}"
proxyStubClsid32="{00020424-0000-0000-C000-000000000046}"
baseInterface="{00000000-0000-0000-C000-000000000046}"
tlbid = "{CC66CB00-53FB-41F3-A745-28D467ED7523}" />
</assembly>







Use project properties -> Manifest Tool -> Input and Output: Additional Manifest Files to have this merged into the automatically generated manifest.  I also set an Output Manifest file ($(OutDir)\$(ProjectName).X.manifest so that I can copy it to the application executable directory after building the application.  The standalone manifest file shouldn't be necessary but I believe there's a problem on Windows XP that prevents that binding process from using the embedded manifest for DLLs.



Now that the unmanaged COM object has been wrapped into an assembly and that assembly has been named and versioned registration free COM requires that the application declare that it depends on the assembly.  This is done with another manifest file (this time an application manifest).  I tested it with a managed client because that's the easiest for me to make but it should work from any client executable.





The application manifest below declares that TestClient needs assembly TestATLLib.X.  Whereas COM usually requires its DLLs to be registered, this test executable will run without the COM object being registered! 



<?xml version="1.0" encoding="UTF-8" standalone="yes"?>


<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">


<assemblyIdentity type="win32"
name="TestClient"
version="1.0.0.0" />


<dependency>


<dependentAssembly>


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


</dependentAssembly>


</dependency>


</assembly>









Although you can embed the application manifest in the executable as a native win32 resource I have tended to place it alongside the executable (it must have the same name as the executable with the additional .manifest extension - so TestClient.exe's application manifest is stored in TestClient.exe.manifest).

No comments :

Post a Comment