Native Manifests: let's do COM and forget the Registry...

You might have heard about it, might have seen those filenames ending with the .manifest extension, heard about the side-by-side directory ($env:windir\WinSxS), seen those project properties in Visual C++ but have you ever done anything explicit with that feature?

I would like to share a simple example that involves a custom COM interface on a CoClass being called by a simple executable.

Pretty boring in those days of [...]LINQ[...] and W[.]F topics... I know, I know.

What if we do that without registering anything in the Registry? COM without the Registry?

Yes, you can. I know that there are still a majority of you developing native code so here is the simple client part:

#include <ObjBase.h>
#include "..\Manifests.TheDLL\ManifestsTheDLL_i.h" // Result of IDL compilation of the component.

int wmain() {
if FAILED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED))
return -1;
ITheInterface * theInterface = NULL;
__try {
if FAILED(CoCreateInstance(__uuidof(TheCoClass), NULL, CLSCTX_INPROC, IID_PPV_ARGS(&theInterface)))
return -2;
theInterface->TheMethod(L"Message from the executable.");
} __finally {
if (theInterface)
theInterface->Release();
}
CoUninitialize();
return 0;

}

And the custom part in the ATL-based inproc component:

STDAPI DllRegisterServer() {
return E_FAIL;
}

STDAPI DllUnregisterServer() {
return E_FAIL;
}

...

STDMETHODIMP TheCoClass::TheMethod(wchar_t * message) {
MessageBox(GetDesktopWindow(), message, L"Live from the TheCoClass class", MB_OK);
return S_OK;
}

Build the solution and run the executable, the message box appears! What you've been witnessing is regfree COM! I'll put some resources at the end of this blog entry but for now, let me share a bit more.

First, let's Allow Isolation (default) and "disable UAC" for the DLL:

image

If we don’t disable UAC for the DLL, the application will fail to load the DLL and we’ll have an error event:

Activation context generation failed for "d:\Users\YvesDolc\Documents\Visual Studio 2008\Projects\Manifests\Debug\Manifests.TheApplication.exe".Error in manifest or policy file "d:\Users\YvesDolc\Documents\Visual Studio 2008\Projects\Manifests\Debug\Manifests.TheDLL.DLL" on line 2. The requestedPrivileges element is not allowed in component manifest.

I  set the Assembly Identity below to Manifests.TheDLL, processorArchitecture=X86, version=1.2.3.4, type=win32:

image

Then, under Isolated COM:

image

When building, Manifests.TheDLL.DLL.embed.manifest gets generated, compiled and stored in the resource of the component:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity name="Manifests.TheDLL" processorArchitecture="X86" type="win32" version="1.2.3.4"></assemblyIdentity>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.VC90.DebugCRT" version="9.0.21022.8" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
</dependentAssembly>
</dependency>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.VC90.ATL" version="9.0.21022.8" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
</dependentAssembly>
</dependency>
<file name="Manifests.TheDLL.dll" hashalg="SHA1">
<comClass clsid="{FFA34D8E-7DF5-4D99-8CD0-5784FAC1944B}" tlbid="{5F6C3293-DAA3-4721-9A19-15735B069DB6}" description="TheCoClass Class"></comClass>
<typelib tlbid="{5F6C3293-DAA3-4721-9A19-15735B069DB6}" version="1.0" helpdir="" flags="HASDISKIMAGE"></typelib>
</file>
</assembly>

That's all we need for the component side. On the application side, we set the Additional Manifest Dependencies to type='win32' name='Manifests.TheDLL' version='1.2.3.4' processorArchitecture='X86':  

image

That triggers the generation of the following manifest:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="asInvoker" uiAccess="false"></requestedExecutionLevel>
</requestedPrivileges>
</security>
</trustInfo>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Manifests.TheDLL" version="1.2.3.4" processorArchitecture="X86"></assemblyIdentity>
</dependentAssembly>
</dependency>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.VC90.DebugCRT" version="9.0.21022.8" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
</dependentAssembly>
</dependency>
</assembly>

Voilà.

I'm going to give you the solution with both projects so you can try it.

There’s a lot more to cover but it’s time to go to bed now. À demain si on le veut bien. A dormir chiquitos y chiquitas.

Resources:

  1. Isolated Applications and Side-by-side Assemblies
  2. How To Build and Service Isolated Applications and Side-by-Side Assemblies for Windows XP
  3. Building C/C++ Isolated Applications and Side-by-side Assemblies
  4. Side-by-Side Manifest Maker (I have not tried it but it does look interesting)

Let me add part of a comment I received from David Janson after reviewing a draft of this blog entry:

One thought…

As you say below, every component has a unique identity. However, what is important is that the component *does not change* once it exists. For example, what if you rebuilt the COM control without making any changes. The new component will have the same identity as the old one yet have different content. This is simply not allowed by SxS. Once a component has been installed with an Identity, it simply can’t change.

Which means that Versioning is incredibly important, even during R&D.

Every time you build a new build, you must give that new build a version that is different (higher) than the last installed build. Otherwise you’ll get an error message like “Same identity but manifest contents are different” when you install the new build because the file hash has changed.

Even removing the old version doesn’t quite do the trick because the SxS store uses a form of garbage collection and doesn’t delete the uninstalled files right away. There is a trick but its much better to simply update the version every time you have a new build you want to install.

Manifests (VC++2008).zip