111

我对如何设置我的视觉工作室构建以实现多目标有点进退两难。

背景: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 个单独的设置项目(std.visual studio 项目,没有 Wix 或任何其他实用程序),还是可以在同一个 .msi 中解决?

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

4

8 回答 8

83

是的,您可以在同一个项目中使用相同的代码库同时定位 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) 工具集获得相同的结果,但是 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>

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

于 2008-09-28T14:06:00.740 回答
27

假设您为两个平台构建了 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 将在所选平台的正确目录中查找。

于 2008-09-28T14:43:46.500 回答
1

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

由于 SQL Server Compact SP1 的更改和额外的 64 位版本支持,32 位版本的 SQL Server Compact 3.5 和 64 位版本的 SQL Server Compact 3.5 SP1 的集中安装和混合模式环境可能会产生看似间歇性的情况问题。为了最大限度地减少冲突的可能性,并启用托管客户端应用程序的平台无关部署,使用 Windows Installer (MSI) 文件集中安装 64 位版本的 SQL Server Compact 3.5 SP1 还需要安装 32 位版本的 SQL Server压缩 3.5 SP1 MSI 文件。对于只需要本机 64 位的应用程序,可以使用 64 位版本的 SQL Server Compact 3.5 SP1 的私有部署。

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

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

于 2008-09-28T13:20:33.580 回答
1

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

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

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

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

脚步:

  1. 在 csproj 中使用 AnyCPU
  2. 确定您是否仅在 csprojs 中引用 x86 或 x64 dll。使 UnitTests 设置适应您选择的体系结构设置。这对于在 VisualStudio 中调试/运行测试很重要。
  3. 在 Reference-Properties 上将Copy Local & Specific Version设置为false
  4. 通过将此行添加到所有引用 x86/x64 的 csproj 文件中 的第一个PropertyGroup来消除架构警告:<ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
  5. 将此 postbuild 脚本添加到您的启动项目中,使用并修改此脚本 sp 的路径,它将所有 x86/x64 dll 复制到构建 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

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

  6. 在应用程序入口点的开头注册 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;
    }
    
  7. 如果您有单元测试,请使用具有 AssemblyInitializeAttribute 的方法创建一个 TestClass,并在那里注册上述 TryResolveArchitectureDependency-Handler。(如果您在 Visual Studio 中运行单个测试,有时不会执行此操作,引用将不会从 UnitTest bin 中解析。因此,步骤 2 中的决定很重要。)

好处:

  • 一个安装/构建两个平台

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

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

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

于 2018-08-03T09:57:24.907 回答
0

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

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

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

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

于 2008-09-28T13:29:03.140 回答
0

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

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

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

在 Visual Studio 2005/2008 中构建 MSI 以在 SharePoint 64 上工作

使用 Visual Studio 的 64 位托管自定义操作

于 2008-11-26T15:29:06.760 回答
0

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

于 2012-07-26T23:26:00.797 回答
0

您可以对项目文件中的 dll 引用使用ItemGroup的条件。
这将导致 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>
于 2013-09-11T14:23:52.480 回答