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






4.95/5 (35投票s)
此工具将监视 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.manifest
和 Microsoft.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。
工具用法
基本用法是
这将生成一个名为 com.sxs.manifest 的文件。您可以找出 COM DLL 导出了哪些接口和 coclass。
与客户端应用程序一起使用时,用法是
这将除了生成 com.sxs.manifest 外,还会生成另一个名为 client.exe.manifest 的清单文件。如果 client.exe 已经嵌入了清单文件,则该清单文件的内容将与对 com.sxs 程序集的引用一起保留在 client.exe.manifest 中。
如果您有多个 COM DLL 需要使用,可以使用批量模式,如下
您可以将所有 COM DLL 放在一个目录中,该目录中将只有一个名为 directory_name.manifest 的清单文件
如果您有多个包含 COM DLL 的目录,可以使用 -batch 功能,并在批处理文件中写入所有目录的名称。
DirectShow 过滤器
DirectShow 过滤器是 COM DLL。它们有三种类型:源过滤器、转换过滤器和渲染过滤器。
源过滤器会将额外信息添加到注册表中以支持 DirectShow 的智能连接,这些额外信息无法存储在 DotManifest 文件中。如果您的应用程序依赖于源过滤器的智能连接,它将无法正常工作。
对此问题有一个解决方案。例如,与其使用 IGraphBuilder::RenderFile
,不如
IFileSourceFilter
接口并调用 Load
方法IPin::Render
测试应用程序
我制作了一个测试应用程序,该应用程序使用上述方法中的源过滤器。该测试应用程序将尝试通过 Orban/Coding Technologies AAC/aacPlus 播放器插件 播放 accPlus 电台。
要测试该应用程序,您需要安装 Orban 插件,然后运行 playradio.exe。
要测试免注册 COM 机制,首先必须取消注册 Orban 插件,方法如下
之后,请检查 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 初始发布。