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


111

对于如何为多目标设置视觉工作室版本,我有些困惑。

背景:c#.NET v2.0,带有p /调用带有安装程序项目的第三方32位DLL,SQL compact v3.5 SP1。现在,平台目标已设置为x86,因此可以在Windows x64上运行。

这家第三方公司刚刚发布了其DLL的64位版本,我想构建一个专用的64位程序。

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

是否可以使用2套新的配置集(Debug64和Release64)来解决?

我必须创建2个单独的安装项目(std。visual studio项目,没有Wix或任何其他实用程序),还是可以在同一.msi中解决此问题?

任何想法和/或建议都将受到欢迎。


@Magnus Johansson:您可以使用两种配置来完成目标的一半。MSI有点难。
user7116

Answers:


83

是的,您可以在同一项目中使用相同的代码库同时针对x86和x64。通常,如果您在VS.NET中创建正确的解决方案配置,那么一切就将正常进行(尽管P / Invoke到完全不受管的DLL很可能需要一些条件代码):我发现需要特别注意的项目是:

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

程序集引用问题不能完全在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中重新加载项目后,“程序集引用”对话框将对这些更改感到有些困惑,并且您可能会遇到一些有关目标处理器使用错误的程序集的警告,但是所有构建都可以正常工作。

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

使用其他工具或Windows Installer XML(WiX)工具集,您可能可以达到相同的结果,但是Advanced Installer使事情变得如此简单(而且价格合理),我从未真正考虑过替代方案。

即使使用Advanced Installer,您可能仍然需要WiX的一件事是用于.NET Installer类自定义操作。虽然指定仅应在某些平台上运行的某些操作很简单(分别使用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中,我发现<ItemGroup>不能嵌套。一旦将新的<ItemGroup>设置为该组下面的其余<Reference>,则此解决方案就可以正常工作。我还必须将x86更改为AnyCPU,这可能与我的特定项目的历史有关。
奥利弗·博克

该高级安装程序看起来很棒。
Pat

这可能是一个愚蠢的问题,但是如何获得手动编辑的文件?
hrh 2012年

1
要在VS中编辑文件,请在解决方案资源管理器中右键单击项目,然后找到“卸载项目”。卸载项目后,再次右键单击它,然后单击“编辑<项目文件名>”。编辑项目文件后,保存它,然后再次右键单击该项目文件并加载它。如果没有错别字或错误,它将再次加载。如果不是这样,VS会告诉您很多有关文件的问题。希望有帮助!
约翰·鲍曼

好答案!谢谢!
约翰·鲍曼

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将在所选平台的正确目录中查找。


如果可行,这将是一个绝妙的选择,但事实并非如此。至少不适合我。
约翰·希恩

10
那不是吗:<HintPath> C:\ whatever \ $(Platform)\ whatever.dll </ HintPath>
Andreas

对我来说,在Visual Studio 2008上工作正常,但没有像通常的<Reference>一样自动将DLL复制到生成目标目录。mdb的解决方案对我来说效果更好。
奥利弗·博克

1

不确定您问题的总答案-但是我想我会在SQL Compact 3.5 SP1下载页面的“其他信息”部分中指出您正在使用x64 的注释,希望它能对您有所帮助。

由于SQL Server Compact SP1的更改和附加的64位版本支持,SQL Server Compact 3.5的32位版本和SQL Server Compact 3.5 SP1的64位版本的集中安装和混合模式环境可能会创建断断续续的内容问题。为了最大程度地减少发生冲突的可能性,并实现受管理的客户端应用程序的平台中性部署,使用Windows Installer(MSI)文件集中安装64位版本的SQL Server Compact 3.5 SP1还需要安装32位版本的SQL Server Compact 3.5 SP1 MSI文件。对于仅需要本机64位的应用程序,可以利用SQL Server Compact 3.5 SP1的64位版本的私有部署。

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

我想让生活变得有趣。.必须说,我喜欢“似乎是间歇性问题”这一行……​​听起来有点像“您正在想象事物,但是以防万一,做到这一点……”


1

具有x86 / x64依赖关系的一个.Net构建

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

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

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

脚步:

  1. 在csproj中使用AnyCPU
  2. 确定您是否仅在csprojs中引用x86或x64 dll。使UnitTests设置适应您选择的体系结构设置。这对于在Visual Studio中调试/运行测试很重要。
  3. 在参考属性上,将“ 复制本地特定版本”设置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的Method来创建TestClass,并在那里注册上述TryResolveArchitectureDependency-Handler。(有时,如果您在Visual Studio中运行单个测试,则不会执行此操作,因此将无法从UnitTest bin中解析引用。因此,第2步中的决定很重要。)

优点:

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

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

(可选)使用postbuild脚本中的Corflags.exe创建x64体系结构专有的第二个可执行文件

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


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



0

您可以不同地生成两个解决方案,然后将它们合并!我是在VS 2010上做到的。我有2个由CMake生成的不同解决方案,并将它们合并


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>
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.