C/C++ 独立应用程序和并行程序集疑难解答
更新:2007 年 11 月
如果找不到依赖 Visual C++ 库,则可能无法加载 C/C++ 应用程序。有关可能发生的运行时错误的列表,请参见重新发布 Visual C++ 文件。本节描述了 C/C++ 应用程序加载失败的最常见的原因以及解决这些问题的建议步骤。
如果在未安装 Visual C++ 的计算机上部署应用程序,该计算机崩溃并显示与重新发布 Visual C++ 文件中列出的错误消息类似的消息,则必须执行多项检查才能了解错误的原因。
按照 理解 Visual C++ 应用程序的依赖项 中描述的步骤操作。Dependency Walker 可显示任意特定应用程序或 DLL 的大多数依赖项。如果您发现缺少一些 DLL,请在您要试图运行应用程序的计算机上安装那些 DLL。
操作系统加载程序使用清单来加载应用程序所依赖的程序集。它可以作为资源嵌入二进制文件中,或作为外部文件保存在应用程序的本地文件夹中。若要检查清单是否已嵌入二进制文件中,请在 Visual Studio 中打开二进制文件,并浏览此二进制文件中的资源。应该能找到一个名为 RT_MANIFEST 的资源。如果无法找到嵌入二进制文件内的清单,请查找名称类似于 <binary_name>.<extension>.manifest 的外部文件。
如果不存在清单,则需要确保链接器为项目生成一个清单。需要在项目的“项目属性”对话中检查链接器选项“生成清单”。
说明: 不支持在未生成清单的情况下生成 Visual C++ 项目。Visual C++ 2005 中生成的任意 C/C++ 程序都必须包括一个清单,用于描述该程序在 Visual C++ 库中的依赖项。
如果二进制文件中嵌入了清单,请确保对于此类型的二进制文件来说,RT_MANIFEST 的 ID 正确。对于应用程序,ID 应等于 1;对于大多数 DLL,ID 应等于 2。如果清单位于外部文件中,则使用 XML 编辑器或文本编辑器可将其打开。有关清单和部署规则的更多信息,请参见清单。
说明: 在 Windows XP 上,如果应用程序的本地文件夹中存在外部清单,则操作系统加载程序将使用此清单,而不使用二进制文件中嵌入的清单。在 Windows Server 2003 和 Windows 更高版本上,情况正好相反,即当存在嵌入的清单时,将忽略外部清单而使用嵌入的清单。
建议所有的 DLL 都应有一个嵌入二进制文件内的清单。通过 LoadLibrary 调用加载 DLL 时,将忽略外部清单。有关更多信息,请参见程序集清单。
检查清单中列举的所有程序集是否都已正确地安装在计算机上。清单中的每个程序集都是按照程序集的名称、版本号和处理器架构指定的。如果应用程序依赖于并行程序集,请检查这些程序集是否都已正确地安装在计算机上,以便操作系统加载程序可以使用在程序集搜索顺序中指定的步骤找到它们。请记住,64 位程序集不能在 32 位进程中加载,也不能在 32 位操作系统上执行。
示例
假设有一个应用程序 appl.exe,它是用 Visual C++ 2005 生成的。此应用程序的清单可作为二进制文件资源 RT_MANIFEST(ID 等于 1)嵌入 appl.exe 中,也可以存储为外部文件 appl.exe.manifest。该清单的内容可能类似于:
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.VC90.CRT" version="9.0.xxxxx.y" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
</dependentAssembly>
</dependency>
</assembly>
对于操作系统加载程序,此清单表明 appl.exe 依赖于一个名为 Microsoft.VC90.CRT 的程序集,该程序集的版本为 9.0.xxxxx.y,并且是为 32 位 x86 处理器架构生成的。
可以将依赖并行程序集作为共享程序集或私有程序集安装。例如,Visual Studio 2005 会将 CRT 程序集作为共享并行程序集安装,它可在 %WINDIR%\WinSxS\x86_Microsoft.VC90.CRT_<版本> 目录(如果运行的是 Windows XP)或 %WINDIR%\winsxs\x86_microsoft.vc90.crt_<版本> 目录(如果运行的是 Windows Vista)中找到。
共享 Visual C++ CRT 程序集的 assembly manifest 也安装在 %WINDIR%\WinSxS\Manifests\x86_microsoft.vc90.crt_<版本>.manifest(如果运行的是 Windows XP)或 %WINDIR%\winsxs\Manifests\x86_microsoft.vc90.crt_<版本>.manifest(如果运行的是 Windows Vista)中,该清单用来标识此程序集并列出其内容(属于此程序集的 DLL):
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!-- Copyright © 1981-2001 Microsoft Corporation -->
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<noInheritable/>
<assemblyIdentity type="win32" name="Microsoft.VC90.CRT" version="9.0.xxxxx.yy" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"/>
<file name="msvcr90.dll" hash="3ca5156e8212449db6c622c3d10f37d9adb12c66" hashalg="SHA1"/>
<file name="msvcp90.dll" hash="92cf8a9bb066aea821d324ca4695c69e55b27cff" hashalg="SHA1"/>
<file name="msvcm90.dll" hash="7daa93e1195940502491c987ff372190bf199395" hashalg="SHA1"/>
</assembly>
并行程序集还可以使用发行者配置文件(也称为策略文件),以便在全局上重定向应用程序和程序集,使其从使用并行程序集的一个版本变为使用该程序集的另一版本。您可以在 %WINDIR%\WinSxS\Policies\x86_policy.9.0.Microsoft.VC90.CRT_<版本>.policy(如果运行的是 Windows XP)或 %WINDIR%\winsxs\Manifests\x86_policy.9.0.microsoft.vc90.crt_<版本>.manifest(如果运行的是 Windows Vista)中检查共享 Visual C++ CRT 程序集的策略,该清单包含如下内容:
</assembly>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!-- Copyright © 1981-2001 Microsoft Corporation -->
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity type="win32-policy" name="policy.9.0.Microsoft.VC90.CRT" version="9.0.xxxxx.yy" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"/>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.VC90.CRT" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"/>
<bindingRedirect oldVersion="9.0.aaaaa.bbb-9.0.ccccc.d" newVersion="9.0.xxxxx.yy"/>
</dependentAssembly>
</dependency>
</assembly>
以上策略指定,对于需要此程序集 9.0.aaaaa.bbb 版的任何应用程序或程序集,都应改用此程序集的 9.0.xxxxx.yy 版,此版本是安装在系统上的当前版本。如果在策略文件中指定了应用程序清单中提到的程序集的版本,则加载程序将在 WinSxS 文件夹中查找清单中指定的此程序集的版本,而当未安装该版本时,加载将失败。如果也没有安装版本为 9.0.xxxxx.yy 的程序集,则对于需要版本为 9.0.aaaaa.bbb 的程序集的应用程序,加载将失败。
但是,也可以将 CRT 程序集作为私有并行程序集,安装在应用程序的本地文件夹中。如果操作系统未能找到 CRT 或作为共享程序集的任何其他程序集,它会开始将该程序集作为私有程序集来查找。它将按以下顺序搜索私有程序集:
在应用程序本地文件夹中查找名为 <assemblyName>.manifest 的清单文件。在此示例中,加载程序试图在 appl.exe 所在的文件夹中查找 Microsoft.VC90.CRT.manifest。如果找到该清单,加载程序将从应用程序文件夹中加载 CRT DLL。如果未找到 CRT DLL,加载将失败。
尝试在 appl.exe 本地文件夹中打开文件夹 <assemblyName>,如果存在此文件夹,则从中加载清单文件 <assemblyName>.manifest。如果找到该清单,加载程序将从 <assemblyName> 文件夹中加载 CRT DLL。如果未找到 CRT DLL,加载将失败。
有关加载程序如何搜索依赖程序集的更多详细说明,请参见程序集搜索顺序。如果加载程序未能找到作为私有程序集的依赖程序集,则加载失败,并显示“系统无法执行指定的程序”。若要解决此错误,必须在计算机上将依赖程序集和作为依赖程序集一部分的 DLL 安装为私有程序集或共享程序集。