在同一解决方案/项目中使用 Visual Studio 以 32 位和 64 位为目标

Posted

技术标签:

【中文标题】在同一解决方案/项目中使用 Visual Studio 以 32 位和 64 位为目标【英文标题】:Targeting both 32bit and 64bit with Visual Studio in same solution/project 【发布时间】:2010-09-13 20:00:03 【问题描述】:

对于如何设置我的 Visual Studio 构建以实现多目标,我有点进退两难。

背景:c# .NET v2.0 带有 p/invoking 到第 3 方 32 位 DLL,SQL compact v3.5 SP1,带有安装项目。 目前,平台目标设置为 x86,因此可以在 Windows x64 上运行。

第 3 方公司刚刚发布了他们的 DLL 的 64 位版本,我想构建一个专用的 64 位程序。

这提出了一些我还没有得到答案的问题。 我想拥有完全相同的代码库。 我必须使用对 32 位 DLL 集或 64 位 DLL 的引用来构建。 (第 3 方和 SQL Server Compact)

这可以通过 2 组新配置(Debug64 和 Release64)解决吗?

我必须创建 2 个单独的设置项目(标准 Visual Studio 项目,没有 Wix 或任何其他实用程序),还是可以在同一个 .msi 中解决?

欢迎任何想法和/或建议。

【问题讨论】:

@Magnus Johansson:您可以使用两种配置来完成一半的目标。 MSI 有点难。 【参考方案1】:

是的,您可以在同一个项目中使用相同的代码库同时定位 x86 和 x64。一般来说,如果您在 VS.NET 中创建正确的解决方案配置,一切都会正常工作(尽管对完全非托管的 DLL 的 P/Invoke 很可能需要一些条件代码):我发现需要特别注意的项目是:

对具有相同名称但具有自己特定位数的外部托管程序集的引用(这也适用于 COM 互操作程序集) MSI 包(如前所述,需要针对 x86 或 x64) 您的 MSI 包中任何基于 .NET 安装程序类的自定义操作

在 VS.NET 中无法完全解决程序集引用问题,因为它只允许您将具有给定名称的引用添加到项目中一次。要解决此问题,请手动编辑您的项目文件(在 VS 中,右键单击解决方案资源管理器中的项目文件,选择卸载项目,然后再次右键单击并选择编辑)。添加对例如 x86 版本程序集的引用后,您的项目文件将包含以下内容:

<Reference Include="Filename, ..., processorArchitecture=x86">
  <HintPath>C:\path\to\x86\DLL</HintPath>
</Reference>

将 Reference 标签包装在 ItemGroup 标签内,指示它适用的解决方案配置,例如:

<ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
   <Reference ...>....</Reference>
</ItemGroup>

然后,复制并粘贴整个 ItemGroup 标记,并对其进行编辑以包含您的 64 位 DLL 的详细信息,例如:

<ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' ">
  <Reference Include="Filename, ..., processorArchitecture=AMD64">
     <HintPath>C:\path\to\x64\DLL</HintPath>
   </Reference>
</ItemGroup>

在 VS.NET 中重新加载您的项目后,Assembly Reference 对话框会因这些更改而有点混乱,并且您可能会遇到一些关于带有错误目标处理器的程序集的警告,但您的所有构建都可以正常工作。

接下来是解决 MSI 问题,不幸的是,这需要一个非 VS.NET 工具:为此我更喜欢 Caphyon 的 Advanced Installer,因为它完成了所涉及的基本技巧 (创建一个通用的 MSI,以及 32 位和 64 位特定的 MSI,并使用 .EXE 安装启动器来提取正确的版本并在运行时进行所需的修复)非常非常好。

您可能可以使用其他工具或Windows Installer XML (WiX) toolset 获得相同的结果,但 Advanced Installer 使事情变得如此简单(而且价格相当实惠),以至于我从未真正考虑过替代品。

可能仍然需要 WiX 的一件事是,即使在使用高级安装程序时,您的 .NET 安装程序类自定义操作也是如此。尽管指定仅应在某些平台上运行的某些操作(分别使用 VersionNT64 和 NOT VersionNT64 执行条件)很简单,但内置 AI 自定义操作将使用 32 位框架执行,即使在 64 位机器上也是如此.

这可能会在未来的版本中得到解决,但现在(或在使用不同的工具创建具有相同问题的 MSI 时),您可以使用 WiX 3.0 的托管自定义操作支持来创建具有相同问题的操作 DLL将使用相应的框架执行的适当位数。


编辑:从 8.1.2 版开始,高级安装程序正确支持 64 位自定义操作。不幸的是,自从我最初的回答以来,它的价格已经上涨了很多,尽管与 InstallShield 及其同类产品相比,它仍然非常物有所值......


编辑:如果您的 DLL 在 GAC 中注册,您也可以通过这种方式使用标准引用标记(以 SQLite 为例):

<ItemGroup Condition="'$(Platform)' == 'x86'">
    <Reference Include="System.Data.SQLite, Version=1.0.80.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=x86" />
</ItemGroup>
<ItemGroup Condition="'$(Platform)' == 'x64'">
    <Reference Include="System.Data.SQLite, Version=1.0.80.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=AMD64" />
</ItemGroup>

条件也简化为所有构建类型、发布或调试,并且只指定处理器架构。

【讨论】:

在 Visual Studio 2008 中,我发现 无法嵌套。一旦我将组下方的新 设置为其余 ,此解决方案就可以正常工作。我还不得不将 x86 更改为 AnyCPU,这可能与我的特定项目的历史有关。 高级安装程序看起来非常棒。 这可能是一个愚蠢的问题,但你如何手动编辑文件? 要在 VS 中编辑文件,在解决方案资源管理器中右键单击项目并找到“卸载项目”。卸载项目后,再次右键单击它并单击“编辑”。编辑项目文件后,保存并再次右键单击项目文件并加载它。如果没有错别字或错误,它将再次加载。如果没有,VS 会告诉你文件的问题。希望有帮助!【参考方案2】:

假设您为两个平台构建了 DLL,它们位于以下位置:

C:\whatever\x86\whatever.dll
C:\whatever\x64\whatever.dll

您只需从这里编辑您的 .csproj 文件:

<HintPath>C:\whatever\x86\whatever.dll</HintPath>

到这里:

<HintPath>C:\whatever\$(Platform)\whatever.dll</HintPath>

然后,您应该能够针对这两个平台构建项目,MSBuild 将在所选平台的正确目录中查找。

【讨论】:

如果它有效,那就太好了,但它没有。至少不适合我。 这不应该是:C:\whatever\$(Platform)\whatever.dll 对我来说在 Visual Studio 2008 上运行良好,但没有像普通的 那样自动将 DLL 复制到构建目标目录。 mdb 的解决方案对我来说效果更好。【参考方案3】:

不确定您问题的全部答案 - 但我想我会在 SQL Compact 3.5 SP1 download page 的附加信息部分指出您正在查看 x64 的评论 - 希望它有所帮助。

由于 SQL Server Compact 中的更改 SP1 和附加 64 位版本 支持,集中安装和混合 32位版本的模式环境 SQL Server Compact 3.5 和 64 位 SQL Server Compact 3.5 SP1 版本 可以创造看似 间歇性问题。为了最小化 潜在的冲突,并启用 托管的平台中立部署 客户端应用程序,集中 安装 64 位版本的 SQL Server Compact 3.5 SP1 使用 Windows 安装程序 (MSI) 文件也 需要安装32位版本 SQL Server Compact 3.5 SP1 MSI 文件。对于仅适用于 需要本机 64 位,私有 64位版本的部署 SQL Server Compact 3.5 SP1 可以 使用。

如果为 64 位客户端分发,我将其解读为“包括 32 位 SQLCE 文件以及 64 位文件”。

我想这让生活变得有趣.. 必须说我喜欢“似乎是间歇性问题”这句话......听起来有点像“你在想象事情,但以防万一,这样做......”

【讨论】:

【参考方案4】:

一个带有 x86/x64 依赖项的 .Net 构建

虽然所有其他答案都为您提供了根据平台进行不同构建的解决方案,但我为您提供了一个选项,即仅具有“AnyCPU”配置并构建适用于您的 x86 和 x64 dll 的构建。

您必须为此编写一些管道代码。

在运行时解决正确的 x86/x64-dll

步骤:

    在 csproj 中使用 AnyCPU 决定是在 csprojs 中仅引用 x86 还是 x64 dll。使 UnitTests 设置适应您选择的体系结构设置。这对于在 VisualStudio 中调试/运行测试很重要。 在 Reference-Properties 上将 Copy LocalSpecific Version 设置为 false 通过将此行添加到引用 x86/x64 的所有 csproj 文件中的第一个 PropertyGroup 来消除架构警告: <ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>

    将此 postbuild 脚本添加到您的启动项目,使用并修改此脚本 sp 的路径,它将所有 x86/x64 dll 复制到您的 build bin\x86\ bin\x64\ 的相应子文件夹中

    xcopy /E /H /R /Y /I /D $(SolutionDir)\YourPathToX86Dlls $(TargetDir)\x86 xcopy /E /H /R /Y /I /D $(SolutionDir)\YourPathToX64Dlls $(TargetDir)\x64

    --> 当你现在启动应用程序时,你会得到一个异常 找不到该程序集。

    在应用程序入口点的开头注册 AssemblyResolve 事件

    AppDomain.CurrentDomain.AssemblyResolve += TryResolveArchitectureDependency;
    

    用这个方法:

    /// <summary>
    /// Event Handler for AppDomain.CurrentDomain.AssemblyResolve
    /// </summary>
    /// <param name="sender">The app domain</param>
    /// <param name="resolveEventArgs">The resolve event args</param>
    /// <returns>The architecture dependent assembly</returns>
    public static Assembly TryResolveArchitectureDependency(object sender, ResolveEventArgs resolveEventArgs)
    
        var dllName = resolveEventArgs.Name.Substring(0, resolveEventArgs.Name.IndexOf(","));
    
        var anyCpuAssemblyPath = $".\\dllName.dll";
    
        var architectureName = System.Environment.Is64BitProcess ? "x64" : "x86";
    
        var assemblyPath = $".\\architectureName\\dllName.dll";
    
        if (File.Exists(assemblyPath))
        
            return Assembly.LoadFrom(assemblyPath);
        
    
        return null;
    
    
    如果您有单元测试,请使用具有 AssemblyInitializeAttribute 的方法创建一个 TestClass,并在那里注册上述 TryResolveArchitectureDependency-Handler。 (如果您在 Visual Studio 中运行单个测试,有时不会执行此操作,引用将不会从 UnitTest bin 中解析。因此,第 2 步中的决定很重要。)

好处:

一个安装/构建两个平台

缺点: - 当 x86/x64 dll 不匹配时,编译时没有错误。 - 你仍然应该在这两种模式下运行测试!

可以选择在构建后脚本中使用 Corflags.exe 创建一个专用于 x64 架构的第二个可执行文件

要尝试的其他变体: - 如果您确保在开始时将正确的 dll 复制到二进制文件夹,则不需要 AssemblyResolve 事件处理程序(评估进程架构 -> 将相应的 dll 从 x64/x86 移动到 bin 文件夹并返回。) - 在安装程序中评估架构并删除错误架构的二进制文件并将正确的二进制文件移动到 bin 文件夹。

【讨论】:

【参考方案5】:

关于你的最后一个问题。您很可能无法在单个 MSI 中解决此问题。 如果您使用注册表/系统文件夹或任何相关内容,MSI 本身必须意识到这一点,您必须准备 64 位 MSI 才能在 32 位机器上正确安装。

您可以将产品安装为 32 it 应用程序,并且仍然能够使其作为 64 位应用程序运行,但我认为这可能有点难以实现。

话虽如此,我认为您应该能够为所有内容保留一个代码库。在我目前的工作地点,我们已经做到了。 (但确实需要一些杂耍才能让所有东西一起玩)

希望这会有所帮助。 下面是一些与 32/64 位问题相关的信息的链接: http://blog.typemock.com/2008/07/registry-on-windows-64-bit-double-your.html

【讨论】:

【参考方案6】:

如果您使用 .NET 编写的自定义操作作为 MSI 安装程序的一部分,那么您遇到了另一个问题。

运行这些自定义操作的“垫片”始终为 32 位,那么您的自定义操作也将运行 32 位,无论您指定什么目标。

更多信息和一些忍者动作来解决(基本上将 MSI 更改为使用此 shim 的 64 位版本)

Building an MSI in Visual Studio 2005/2008 to work on a SharePoint 64

64-bit Managed Custom Actions with Visual Studio

【讨论】:

【参考方案7】:

您可以生成两种不同的解决方案,然后将它们合并! 我为 VS 2010 做了这个。它有效。我有 2 个由 CMake 生成的不同解决方案,我将它们合并

【讨论】:

【参考方案8】:

您可以对 ItemGroup 使用条件来获取项目文件中的 dll 引用。 这将导致 Visual Studio 在您更改活动配置时重新检查条件和引用。 只需为每个配置添加一个条件。

例子:

 <ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
    <Reference Include="DLLName">
      <HintPath>..\DLLName.dll</HintPath>
    </Reference>
    <ProjectReference Include="..\MyOtherProject.vcxproj">
      <Project>AAAAAA-000000-BBBB-CCCC-TTTTTTTTTT</Project>
      <Name>MyOtherProject</Name>
    </ProjectReference>
  </ItemGroup>

【讨论】:

以上是关于在同一解决方案/项目中使用 Visual Studio 以 32 位和 64 位为目标的主要内容,如果未能解决你的问题,请参考以下文章

Visual Studio 中的 DB 项目中的 DB 视图在同一项目中看不到同义词

当我在同一解决方案中卸载项目时,Visual Studio 中的引用路径不起作用

visual studio 2015杂症——同一解决方案下的项目无法互相引用

同一网站的Visual Studio 2012和2017行为

visual studio 两个以上sln 引用同一个project ,生成时会改变projectguid问题

Visual Studio项目REST api