Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

regsvr42: Generate SxS Manifest Files from Native DLLs for Registration-Free COM

0.00/5 (No votes)
19 Aug 2008 3  
This tool will spy on COM registration process and create side by side (SxS) manifest file(s)

Introduction

Windows XP introduced a new COM activation model called Registration-Free COM activation. The Registration-Free COM activation is a registry replacement for COM components. The registry information will reside in a DotManifest file that can be stored in the same folder as the application itself.

This means that you don't have to have information into registry, information which is normally stored into HKEY_LOCAL_MACHINE, thus enabling regular user accounts to use COM DLLs without registering them into the system.

The problem with these DotManifest files is that they are pretty hard to write by hand. For managed assemblies there is Genman32 - A tool to generate Sxs manifest for managed assembly for Registration Free COM/.NET Interop, but there is no tool for native DLLs, this is why I've decided to write such a tool.

Background

The following MSDN article will give the proper background information: Registration-Free Activation of COM Components: A Walkthrough by Steve White and Leslie Muller. Also of great help is the MSDN Side-by-side Assemblies Reference.

A DotManifest file is a XML file — you probably have heard about these in conjunction with Common Controls version 6, or with Windows Vista elevation privileges and not to mention the (in)famous Microsoft.VC[8|9]0.CRT.manifest and Microsoft.VC[8|9]0.MFC.manifest files. These days every application has embedded a DotManifest file inside by the Manifest Tool as a resource type 24 (RT_MANIFEST). A typical DotManifest file looks like:

<?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' />
      </requestedPrivileges>
    </security>

  </trustInfo>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type='win32' name='Microsoft.VC90.CRT' version='9.0.21022.8'
          processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b' />
    </dependentAssembly>
  </dependency>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type='win32' name='Microsoft.VC90.MFC' version='9.0.21022.8'
          processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b' />

    </dependentAssembly>
  </dependency>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type='win32' name='Microsoft.Windows.Common-Controls'
          version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df'
          language="'*'" />
    </dependentAssembly>
  </dependency>
</assembly>

The Spying

In order to get the the information about the COM registration process I had to "intercept" the following registry access functions:

  • RegCreateKeyA
  • RegCreateKeyW
  • RegCreateKeyExA
  • RegCreateKeyExW
  • RegSetValueA
  • RegSetValueW
  • RegSetValueExA
  • RegSetValueExW
  • RegOpenKeyA
  • RegOpenKeyW
  • RegOpenKeyExA
  • RegOpenKeyExW
  • RegCloseKey

The interception was done by using the class CAPIHook presented in Chapter 22: DLL Injection and API Hooking of the book Windows via C/C++, Fifth Edition written by Jeffrey Richter and Christophe Nasarre, Microsoft Press (c) 2008. One can use the Detours library from Microsoft Research instead of CAPIHook. I've decided for the latter because of simpler deployment (just the executable, no signature DLL).

Setting up a CAPIHook is being done like this

std::auto_ptr<CAPIHook> Interceptor::m_RegCreateKeyA;

...

    if (!m_RegCreateKeyA.get())
    {
        m_RegCreateKeyA.reset(new CAPIHook("Advapi32.dll", "RegCreateKeyA",
            (PROC)RegCreateKeyA));
    }

The upper code will redirect RegCreateKeyA through Interceptor::RegCreateKeyA, the original function can be reached through m_RegCreateKeyA. Interceptor::RegCreateKeyA, like this:

LONG WINAPI Interceptor::RegCreateKeyA(HKEY hKey, LPCSTR lpSubKey, PHKEY phkResult)
{
    if (m_doTrace)
    {
        PrintKeyStats(hKey, lpSubKey, __FUNCTIONW__);
    }

    // just to have it initialized
    LONG result = ERROR_ARENA_TRASHED;
    try
    {
        result = ((PFNREGCREATEKEYA)(PROC)*m_RegCreateKeyA)(hKey, lpSubKey, phkResult);
    }
    catch (...)
    {
    }

    if (result == ERROR_SUCCESS)
    {
        InsertSubkeyIntoUserKeyMap(hKey, *phkResult, lpSubKey, __FUNCTIONW__);
    }

    return result;
}

After all the information was acquired then the information is processed and written into DotManifest files. The manifest files are encoded as UTF-8. I would like to point fprintf's ccs=<encoding> parameter which was used to save the manifest files as UTF-8.

void ManifestWriter::WriteToFile(const std::wstring& outputManifestFile)
{
    FILE* file = _wfopen(outputManifestFile.c_str(), L"w, ccs=utf-8");
    if (file)
    {
        fwrite(&*m_data.str().begin(), 1, m_data.str().size() * 2, file);
        fclose(file);
    }
}

In order to get all the information possible, first DllUnregisterServer function is called, then DllRegisterServer and at the end again DllUnregisterServer. If you use regsvr42 on a system where you already have the COM DLL registered and still want to use it normally don't forget to run regsvr32 for that COM DLL.

Tool Usage

The basic usage is:

regsvr42 com.dll

which will generate a file named com.sxs.manifest. You can find out what interfaces and coclasses the COM DLL exports.

When used with a client application the usage is:

regsvr42 -client:client.exe com.dll

which will generate besides com.sxs.manifest another manifest file named client.exe.manifest. If client.exe already has a manifest file embedded, the contents of that manifest file are preserved into client.exe.manifest alongside with the reference to com.sxs assembly.

If you have more than one COM DLLs you want to use can use the tool in batch mode like:

regsvr42 -client:client.exe -batch:file_containing_com_dll_file_names

You can put all the COM DLLs inside one directory and there will be just one manifest file inside the directory named directory_name.manifest

regsvr42 -client:client.exe -dir:directory_with_com_dlls

If you have more than one directories with COM DLLs you can use the -batch function with all the names of the directories written in the batch file.

regsvr42 -client:client.exe -batch:file_containing_directory_names

DirectShow Filters

DirectShow filters are COM DLLs. They come in three flavours: source filters, transform filters and render filters.

The source filters add extra information into registry to support DirectShow's intelligent connect, this extra information cannot be stored into a DotManifest file. If your application relays on Intelligent Connect for a source filter, it will not work correctly.

There is one solution for this problem. For example instead of using IGraphBuilder::RenderFile one should:

  • Add the source filter in to the DirectShow graph
  • Acquire the IFileSourceFilter interface and call the Load method
  • Acquire the output pin of the source filter and call IPin::Render

    Test Application

    I have made a test application which uses a source filter as described above. The test application will try to play an accPlus radio station by the use of Orban/Coding Technologies AAC/aacPlus Player Plugin

    To test the application you need to have the Orban Plugin installed and the just run playradio.exe.

    To test the Registration-Free COM mechanism first you have to unregister the Orban Plugin like:

    regsvr32 /u "%ProgramFiles%\Orban\AAC-aacPlus Plugin\aacpParser.dll"

    After that check to see that playradio.exe displays Class not registered. ErrorCode: 0x80040154.

    Then run make_manifest.cmd, a convenience batch file, which does the DotManifest creation like regsvr42 -client:playradio.exe "%ProgramFiles%\Orban\AAC-aacPlus Plugin\aacpParser.dll". Two DotManifest files will be created:

    aacpParser.sxs.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="aacpParser.sxs"
        version="1.0.0.0" />
    
    <file name="C:\\Program Files\\Orban\\AAC-aacPlus Plugin\\aacpParser.dll">
    
        <comClass
            description="ORBAN-CT AAC/aacPlus Stream Parser"
            clsid="{301F7BDA-B1F8-4453-82B2-0B9187DF3F3F}"
            threadingModel="Both" />
    
        <comClass
            description="ORBAN-CT AAC/aacPlus Stream Parser About Page"
            clsid="{CA920EED-F427-41B8-838F-33FCF47D5306}"
            threadingModel="Both" />
    
    </file>
    
    </assembly>

    playradio.exe.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="aacpParser.sxs"
                      version="1.0.0.0" />
              </dependentAssembly>
      </dependency>
    </assembly>

    File Hash

    By using the command -hash the file sections will contain a SHA1 hash. To compute the hash I have used CodeProject's CSHA1 by Dominik Reichl. I have verified the validity of the hash by using OpenSSL like this openssl dgst -sha1<com.dll>.

    The problem is that the Manifest Tool mt.exe doesn't think that the hash is valid! The command used was mt -manifest aacpParser.sxs.manifest -validate_file_hashes

    The solution to this problem is to use the Manifest Tool to update the file hash, which renders the -hash function useless, like this: mt -manifest aacpParser.sxs.manifest -hashupdate If anybody knows how to adapt the CSHA1 to produce valid Manifest Tool SHA1 hashes please let me know.

    Debugging

    In case you encounter the following MessageBox:

    This application has failed to start because the application configuration is incorrect. Reinstalling the application may fix this problem.

    The Event Viewer - System category contains information about the loading errors of side by side assemblies.

    History

    2008-08-17 Initial release.

  • License

    This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

    A list of licenses authors might use can be found here