65.9K
CodeProject 正在变化。 阅读更多。
Home

regsvr42:从本机 DLL 为无注册 COM 生成 SxS manifest 文件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.95/5 (35投票s)

2008年8月19日

Zlib

5分钟阅读

viewsIcon

337163

downloadIcon

10645

此工具将监视 COM 注册过程并创建并行 (SxS) manifest 文件。

引言

Windows XP 引入了一种新的 COM 激活模型,称为免注册 COM 激活。免注册 COM 激活是 COM 组件的注册表替代方案。注册表信息将驻留在 DotManifest 文件中,该文件可以存储在应用程序自身的文件夹中。

这意味着您无需在注册表中存储信息,而这些信息通常存储在 HKEY_LOCAL_MACHINE 中,从而使普通用户帐户无需在系统中注册即可使用 COM DLL。

这些 DotManifest 文件的问题在于,它们很难手动编写。对于托管程序集,有 Genman32 - 用于为免注册 COM/.NET 互操作的托管程序集生成 Sxs 清单的工具,但没有用于原生 DLL 的工具,因此我决定编写这样一个工具。

背景

以下 MSDN 文章将提供正确的背景信息:Steve White 和 Leslie Muller 的 COM 组件的免注册激活:演练。MSDN 的 并行程序集参考 也非常有帮助。

DotManifest 文件是 XML 文件 — 您可能在 Common Controls 6.0 版本、Windows Vista 提升权限以及(臭名昭著的)Microsoft.VC[8|9]0.CRT.manifestMicrosoft.VC[8|9]0.MFC.manifest 文件中听说过它们。如今,每个应用程序都通过 Manifest Tool 将 DotManifest 文件作为资源类型 24 (RT_MANIFEST) 嵌入其中。典型的 DotManifest 文件看起来像这样

<?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>

监视

为了获取关于 COM 注册过程的信息,我不得不“拦截”以下注册表访问函数

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

拦截是通过使用 Jeffrey Richter 和 Christophe Nasarre 撰写的《Windows via C/C++,第五版》(Microsoft Press,2008 年)一书的第 22 章:DLL 注入和 API 挂钩中介绍的 CAPIHook 类完成的。也可以使用 Microsoft Research 的 Detours 库代替 CAPIHook。我选择后者是因为部署更简单(只需可执行文件,无需签名 DLL)。

设置 CAPIHook 的方法如下

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

...

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

上面的代码会将 RegCreateKeyA 重定向到 Interceptor::RegCreateKeyA,原始函数可以通过 m_RegCreateKeyA 访问。Interceptor::RegCreateKeyA,如下所示

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;
}

在获取所有信息后,信息将被处理并写入 DotManifest 文件。清单文件以 UTF-8 编码。我想指出 fprintf 的 ccs=<encoding> 参数,它被用来将清单文件保存为 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);
    }
}

为了获取尽可能多的信息,首先调用 DllUnregisterServer 函数,然后调用 DllRegisterServer,最后再次调用 DllUnregisterServer。如果您在一个已经注册了 COM DLL 的系统上使用 regsvr42 并仍然希望正常使用它,请不要忘记为该 COM DLL 运行 regsvr32

工具用法

基本用法是

regsvr42 com.dll

这将生成一个名为 com.sxs.manifest 的文件。您可以找出 COM DLL 导出了哪些接口和 coclass。

与客户端应用程序一起使用时,用法是

regsvr42 -client:client.exe com.dll

这将除了生成 com.sxs.manifest 外,还会生成另一个名为 client.exe.manifest 的清单文件。如果 client.exe 已经嵌入了清单文件,则该清单文件的内容将与对 com.sxs 程序集的引用一起保留在 client.exe.manifest 中。

如果您有多个 COM DLL 需要使用,可以使用批量模式,如下

regsvr42 -client:client.exe -batch:file_containing_com_dll_file_names

您可以将所有 COM DLL 放在一个目录中,该目录中将只有一个名为 directory_name.manifest 的清单文件

regsvr42 -client:client.exe -dir:directory_with_com_dlls

如果您有多个包含 COM DLL 的目录,可以使用 -batch 功能,并在批处理文件中写入所有目录的名称。

regsvr42 -client:client.exe -batch:file_containing_directory_names

DirectShow 过滤器

DirectShow 过滤器是 COM DLL。它们有三种类型:源过滤器、转换过滤器和渲染过滤器。

源过滤器会将额外信息添加到注册表中以支持 DirectShow 的智能连接,这些额外信息无法存储在 DotManifest 文件中。如果您的应用程序依赖于源过滤器的智能连接,它将无法正常工作。

对此问题有一个解决方案。例如,与其使用 IGraphBuilder::RenderFile,不如

  • 将源过滤器添加到 DirectShow 图中
  • 获取 IFileSourceFilter 接口并调用 Load 方法
  • 获取源过滤器的输出引脚并调用 IPin::Render

    测试应用程序

    我制作了一个测试应用程序,该应用程序使用上述方法中的源过滤器。该测试应用程序将尝试通过 Orban/Coding Technologies AAC/aacPlus 播放器插件 播放 accPlus 电台

    要测试该应用程序,您需要安装 Orban 插件,然后运行 playradio.exe

    要测试免注册 COM 机制,首先必须取消注册 Orban 插件,方法如下

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

    之后,请检查 playradio.exe 是否显示 类未注册。错误代码:0x80040154

    然后运行 make_manifest.cmd,这是一个方便的批处理文件,它执行 DotManifest 创建,例如 regsvr42 -client:playradio.exe "%ProgramFiles%\Orban\AAC-aacPlus Plugin\aacpParser.dll"。将创建两个 DotManifest 文件

    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>

    文件哈希

    使用 -hash 命令,file 部分将包含一个 SHA1 哈希。为了计算哈希,我使用了 CodeProject 的 CSHA1(作者 Dominik Reichl)。我使用 OpenSSL 验证了哈希的有效性,命令如下:openssl dgst -sha1<com.dll>

    问题是 Manifest Tool mt.exe 认为哈希无效!使用的命令是 mt -manifest aacpParser.sxs.manifest -validate_file_hashes

    解决此问题的方法是使用 Manifest Tool 更新文件哈希,这使得 -hash 函数失效,命令如下:mt -manifest aacpParser.sxs.manifest -hashupdate。如果您知道如何调整 CSHA1 以生成有效的 Manifest Tool SHA1 哈希,请告知。

    调试

    如果您遇到以下消息框

    由于应用程序配置不正确,此应用程序未能启动。重新安装应用程序可能会解决此问题。

    事件查看器 - 系统类别包含有关并行程序集加载错误的信息。

    历史

    2008-08-17 初始发布。

  • © . All rights reserved.