【问题标题】:How can I target a specific version of the C++ runtime?如何定位特定版本的 C++ 运行时?
【发布时间】:2009-07-06 15:30:35
【问题描述】:

我们有一个非常大的项目,主要是用 C# 编写的,其中有一些用 C++ 编写的小而重要的组件。我们将 .NET 2.0 的 RTM 定位为所需的最低版本。到目前为止,为了满足这个要求,我们确保在我们的构建框中只有 .NET 2.0 的 RTM,以便 C++ 片段链接到该版本。

更新:导致问题的 C++ 程序集是被加载到托管进程中的混合模式 C++ 程序集。

不幸的是,当 confiker 计划在 4 月 1 日做某事时,我们的公司 IT 大力推动所有内容都打上补丁并保持最新状态,因此 3.5 SP1 之前的所有内容都安装在了构建盒上。我们已尝试卸载之前发生的所有内容,但现在我们无法满足最低要求,因为在该特定机器上构建的任何内容都需要 .NET 2.0 SP1。

既然盒子似乎被水洗了,我们不能只卸载有问题的版本,有没有办法构建程序集并明确告诉他们使用 .NET 2.0 的 RTM(即 v2.0.50727.42 )?我已经看到有关使用清单的页面,但我无法弄清楚如何实际实现适当的清单并将其放入程序集中。我的专长是托管世界,所以我对此有点茫然。

谁能解释我如何使这些程序集以 .NET 2.0 RTM SxS 程序集为目标?

谢谢!

【问题讨论】:

    标签: .net c++ side-by-side


    【解决方案1】:

    虽然我很确定 Christopher 的答案和代码示例(谢谢,Christopher!)是一个更优雅的解决方案的一部分,但我们迫不及待地想把它拿出来,发现一个非常相似但不同的,解决方案。

    第一步是为程序集创建清单:

    <assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
      <dependency>
        <dependentAssembly>
          <assemblyIdentity type='win32' name='Microsoft.VC80.DebugCRT' version='8.0.50608.0' processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b' />
        </dependentAssembly>
      </dependency>
      <dependency>
        <dependentAssembly>
          <assemblyIdentity type='win32' name='Microsoft.VC80.CRT' version='8.0.50608.0' processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b' />
        </dependentAssembly>
      </dependency>
    </assembly>
    

    接下来,您必须在配置属性 -> 链接器 -> 清单文件下将“生成清单”选项设置为“否”,并在配置属性 -> 清单工具 -> 下将“嵌入清单”选项设置为“否”输入和输出。

    最后,要将您的新清单添加到程序集中,请将以下命令添加到项目的构建后步骤:

    mt.exe /manifest "$(ProjectDir)cppassembly.dll.manifest" /outputresource:"$(TargetDir)\cppassembly.dll";#2 -out:"$(TargetDir)\cppassembly.dll.manifest"
    

    构建后,我们可以在 Visual Studio 中打开 dll 以查看 RT_MANIFEST 下的清单并确认它有我们的清单!

    当我将 Christopher 的代码放入 stdafx.h 时,它最终将其添加为 附加 依赖项......清单仍在寻找 v8.0.50727.762。它生成的清单如下所示:

    <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
      <dependency>
        <dependentAssembly>
          <assemblyIdentity type="win32" name="Microsoft.VC80.DebugCRT" version="8.0.50608.0" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
        </dependentAssembly>
      </dependency>
      <dependency>
        <dependentAssembly>
          <assemblyIdentity type="win32" name="Microsoft.VC80.DebugCRT" version="8.0.50727.762" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
        </dependentAssembly>
      </dependency>
      <dependency>
        <dependentAssembly>
          <assemblyIdentity type="win32" name="Microsoft.VC80.CRT" version="8.0.50727.762" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
        </dependentAssembly>
      </dependency>
    </assembly>
    

    我无法找到另一个可以删除或清除现有依赖项的开关。我更喜欢 Christopher 的方法,而不是构建后的步骤,但现在这可行。如果有人对如何清除任何现有的依赖项有任何额外的意见,那就太好了。

    谢谢!

    【讨论】:

    • 约翰,我能够在没有构建步骤的情况下使这个解决方案工作(在 VS 2005 中)。在项目设置清单工具 -> 输入和输出 -> 嵌入清单中,然后将将清单文件输出到您正在使用的文件的名称。在我的情况下,我将清单放在项目目录中(与 .vcproj 目录相同),将其包含在项目文件中,然后简单地输入名称而不输入路径。我确认清单是按原样嵌入的,没有生成的依赖项。它解决了我的问题!太棒了!
    【解决方案2】:

    是的。在您的项目属性中,有一个页面指示运行时。有一个下拉列表列出了所有可用的运行时。选择适合您的那一款。 (对于 VS 2008:右键单击项目 -> 属性,编译选项卡,高级编译器设置按钮 -> 目标框架)

    我们现在就这样做。我们想迁移到 VS 2008,但我们正在逐步进行。所以现在我们有一个 VS 2008 解决方案,但所有项目仍然以 .Net 2.0 为目标。因此,当我们编译和部署时,我们不需要在我们的测试盒上安装 .Net 3.5 的东西。

    更新:

    要强制本机程序链接到特定版本的 .dll,您可能需要使用以下内容:

    #pragma message ("Explicit link to generate a manifest entry for MFC.")
    
    #if defined (_DEBUG)
    
    #pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.VC80.DebugMFC' version='8.0.50608.0' processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b'\"")
    
    #else
    
    #pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.VC80.MFC' version='8.0.50608.0' processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b'\"")
    
    #endif
    

    除了 MFC,您应该找到 .Net .DLL 的正确值。

    有理由相信您不能在同一个盒子上安装 .Net 2.0 SP1 和 .Net 2.0。所以让这个在那个盒子上工作可能会非常非常痛苦。启动一个新的构建 VM 可能会更好,您可以在其上安装旧的、未打补丁的 .Net 框架(如果您甚至可以再掌握它的话。)

    否则,您需要将所有构建时文件复制到当前框,然后根据您的构建类型调整包含和库路径。可能这比它的价值更令人头疼。

    【讨论】:

    • 我相信这只适用于 .Net 项目。我认为这不适用于非托管 C++ 项目。在编译 C++ 代码时,我总是发现 Visual Studio 以最新版本的运行时为目标。
    • 正确...仅适用于托管项目。我希望不受管理的人那么容易。 :)
    • 抱歉,我以为你想要托管 C++。您可以通过选择目标 SDK 来对原生 C++ 执行类似的操作。这基本上涉及设置正确的编译器路径。对于 VS2005 有一个批处理文件叫做 vcvars.bat 你是使用 makefiles 来构建,还是 devenv?
    • 我们正在使用 MSBuild,它在后台为 VC 项目调用 vcbuild。这当然看起来更像我认为需要的。你能澄清一下我把上面的代码放在哪里(我在 C++ 领域不是很热)吗?如果我正确理解您的更新,那段代码是否真的在构建时生成/嵌入清单?
    • 代码放在一个头文件中,所有需要它的翻译单元(C++ 代码实现文件)都包含它。实际上,每个 .dll 只需要在一个翻译单元中,但我更喜欢将它放在适当的标题中。那个 sn-p 是一个预处理器指令,它告诉编译器在构建时生成一个清单。
    【解决方案3】:

    约翰真的让我走上了解决同样问题的正确轨道。我正在对旧的 VS2005 C++(非 CLR)项目进行轻微更改。我的开发机器具有所有更新,因此 VS2005 正在创建对 MFC80 和 MSVCx80 DLL 的最新版本的引用,并且可执行文件不会在目标机器上运行,因为这些版本不可用并且我没有更新这些机器的选项.所以我需要控制嵌入在可执行文件中的清单中的 assmelby 依赖项。这似乎与约翰正在解决的问题相同。从他的其他信息开始,这对我有用。

    在 ProjectDir 中,使用适当的程序集参考信息创建 program.exe.debug.manifestprogram.exe.release.manifest 文件。不要将它们添加到项目或链接器中,并尝试在每个构建中都包含它们。然后设置项目属性如下:

    链接器->清单文件选项

    • 生成清单:否
    • 清单文件:空白
    • 其他清单依赖项:空白
    • 允许隔离:是

    清单工具->输入和输出选项

    • 其他清单文件:$(ProjectDir)$(TargetFileName).$(ConfigurationName).manifest
    • 输入资源清单:空白
    • 嵌入清单:是
    • 输出清单文件:(自动填写)
    • 清单资源文件:(自动填写)
    • 生成目录文件:否
    • 依赖信息文件:$(IntDir)\mt.dep(自动填写)

    从我的测试来看,这似乎不会生成任何其他清单文件,并将您手动编码的清单信息作为 RT_MANIFTEST 资源 #1 嵌入到 EXE 中。

    在 Manifest Tool 选项中,“Output Manifest File”和“Manifest Resource File”即使在我清除它们之后也会自动填写。

    这似乎允许我控制程序集依赖项并让可执行文件在目标机器上运行。额外的好处是我不必使用用于其他目的的构建后步骤。通过操作链接器和清单工具选项,可以获得相同的结果。

    抱歉,我无法上传屏幕截图,但我是新用户,目前还不允许使用图片。

    【讨论】:

      【解决方案4】:

      我知道这是一个 hack,但我已经求助于在外部生成清单,并使用记事本对其进行修改。在紧要关头,它为我解决了问题。就我而言,我希望有一个 VC++ 2005 应用程序指向 .762 CRT。

      祝你好运! 特里

      【讨论】:

        【解决方案5】:

        约翰的回答对我们有用。在 Visual C++ 2005 中,编译器生成包括 762 和 4053 版本的 MFC 和 CRT 的清单。我们从清单中删除了 4053 版本,然后转到上述手动步骤。 (内部代码实际上会抓取 4053,因为它是 762 上公认的安全修复程序,但规范是必要的,否则链接将失败。)

        Ted 的博客 (tedwvc.wordpress.com) 帖子给了我们提示,但他的解决方案对我们不起作用。这种方法在这里有效。

        【讨论】:

          【解决方案6】:

          我认为你想要CorBindToRuntime。这将允许您指定 C++ 加载的 CLR 版本。

          【讨论】:

          • 除非我遗漏了什么,否则我认为这不是我们想要的。 CorBindToRuntime 似乎是一个非托管 API,用于将托管代码引入非托管进程。可执行文件和大部分应用程序是托管的 C#。我们尝试加载到托管进程中的程序集是混合模式 C++ 程序集。有什么我想念的吗?
          • 啊。我处理过的大多数互操作都是相反的。你是对的,除非你重新架构应用程序以拥有一个加载托管代码的非托管可执行文件,否则这可能无济于事。
          猜你喜欢
          • 1970-01-01
          • 2013-03-29
          • 2010-10-23
          • 1970-01-01
          • 2020-05-27
          • 2021-09-05
          • 1970-01-01
          • 2017-04-24
          • 1970-01-01
          相关资源
          最近更新 更多