.net 在 Visual Studio 中构建时有条件地使用 32/64 位引用

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3832552/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 14:46:54  来源:igfitidea点击:

Conditionally use 32/64 bit reference when building in Visual Studio

.netvisual-studio64-bit32bit-64bit

提问by Jonathan Yee

I have a project that builds in 32/64-bit and has corresponding 32/64-bit dependencies. I want to be able to switch configurations and have the correct reference used, but I don't know how to tell Visual Studio to use the architecture-appropriate dependency.

我有一个以 32/64 位构建并具有相应 32/64 位依赖项的项目。我希望能够切换配置并使用正确的参考,但我不知道如何告诉 Visual Studio 使用适合体系结构的依赖项。

Maybe I'm going about this the wrong way, but I want to be able to switch between x86 and x64 in the configuration dropdown, and have the referenced DLL be the right bitness.

也许我用错误的方式来解决这个问题,但我希望能够在配置下拉列表中在 x86 和 x64 之间切换,并且引用的 DLL 是正确的位。

采纳答案by Hugo

Here is what I've done in a previous project, which will require the manual edition of the .csproj file(s). You also need separate directories for the different binaries, ideally siblings of each other, and with the same name as the platform you are targeting.

这是我在以前的项目中所做的,这将需要 .csproj 文件的手动版本。您还需要不同二进制文件的单独目录,最好是彼此的同级文件,并且与您的目标平台同名。

After adding a single platform's references to the project, open the .csproj in a text editor. Before the first <ItemGroup>element within the <Project>element, add the following code, which will help determine which platform you're running (and building) on.

将单个平台的引用添加到项目后,在文本编辑器中打开 .csproj。在<ItemGroup>元素中的第一个元素之前<Project>,添加以下代码,这将有助于确定您在哪个平台上运行(和构建)。

<!-- Properties group for Determining 64bit Architecture -->
<PropertyGroup>
  <CurrentPlatform>x86</CurrentPlatform>
  <CurrentPlatform Condition="'$(PROCESSOR_ARCHITECTURE)'=='AMD64' or '$(PROCESSOR_ARCHITEW6432)'=='AMD64'">AMD64</CurrentPlatform>
</PropertyGroup>

Then, for your platform specific references, you make changes such as the following:

然后,对于您的平台特定参考,您进行如下更改:

<ItemGroup>
  <Reference Include="Leadtools, Version=16.5.0.0, Culture=neutral, PublicKeyToken=9cf889f53ea9b907, processorArchitecture=x86">
    <SpecificVersion>False</SpecificVersion>
    <HintPath>..\..\Lib\Leadtools$(CurrentPlatform)\Leadtools.dll</HintPath>
  </Reference>
  <Reference Include="Leadtools.Codecs, Version=16.5.0.0, Culture=neutral, PublicKeyToken=9cf889f53ea9b907, processorArchitecture=x86">
    <SpecificVersion>False</SpecificVersion>
    <HintPath>..\..\Lib\Leadtools$(CurrentPlatform)\Leadtools.Codecs.dll</HintPath>
  </Reference>
  <Reference Include="Leadtools.ImageProcessing.Core, Version=16.5.0.0, Culture=neutral, PublicKeyToken=9cf889f53ea9b907, processorArchitecture=x86">
    <SpecificVersion>False</SpecificVersion>
    <HintPath>..\..\Lib\Leadtools$(CurrentPlatform)\Leadtools.ImageProcessing.Core.dll</HintPath>
  </Reference>
  <Reference Include="System" />
  <Reference Include="System.Core" />
  <Reference Include="System.Data.Entity" />
  <!--  Other project references -->
</ItemGroup>

Note the use of the $(CurrentPlatform)property, which we defined above. You could, instead, use conditionals for which assemblies to include for which platform. You could also need to:

请注意$(CurrentPlatform)我们在上面定义的属性的使用。相反,您可以使用条件来确定哪些程序集要包含在哪个平台上。您可能还需要:

  • Replace the $(PROCESSOR_ARCHITEW6432)and $(PROCESSOR_ARCHITECTURE)with $(Platform)to consider ONLY the target platform of the projects
  • Alter the platform determination logic in order to be appropriate to the current machine, so that you're not building/referencing a 64 bit binary to execute on a 32 bit platform.
  • 更换$(PROCESSOR_ARCHITEW6432)$(PROCESSOR_ARCHITECTURE)$(Platform)只考虑项目的目标平台
  • 更改平台确定逻辑以适合当前机器,这样您就不会构建/引用 64 位二进制文​​件以在 32 位平台上执行。

I had this written up originally for an internal Wiki at work, however, I've modified it and posted the full process to my blog, if you are interested in the detailed step-by-step instructions.

我最初是为工作中的内部 Wiki 编写的,但是,如果您对详细的分步说明感兴趣,我已经对其进行了修改并将完整过程发布到我的博客

回答by Justin Holzer

AFAIK, if your project requires references that are 32-bit or 64-bit specific (i.e. COM-interop assemblies), and you have no interest in manually editing the .csproj file, then you'll have to create separate 32-bit and 64-bit projects.

AFAIK,如果您的项目需要特定于 32 位或 64 位的引用(即 COM 互操作程序集),并且您对手动编辑 .csproj 文件没有兴趣,那么您必须创建单独的 32 位和64 位项目。

I should note that the following solution is untested, but should work. If you are willing to manually edit the .csproj file, then you should be able to achieve the desired result with a single project. The .csproj file is just an MSBuild script, so for a full reference, look here. Once you open the .csproj file in an editor, locate the <Reference>elements. You should be able to split these elements out in to 3 distinct item groups: references that aren't platform specific, x86-specific references, and x64-specific references.

我应该注意到以下解决方案未经测试,但应该有效。如果您愿意手动编辑 .csproj 文件,那么您应该能够通过单个项目实现所需的结果。.csproj 文件只是一个 MSBuild 脚本,因此有关完整参考,请查看此处。在编辑器中打开 .csproj 文件后,找到<Reference>元素。您应该能够将这些元素分成 3 个不同的项目组:非特定于平台的引用、特定于 x86 的引用和特定于 x64 的引用。

Here is an example that assumes your project is configured with target platforms named "x86" and "x64"

这是一个示例,假设您的项目配置了名为“x86”和“x64”的目标平台

<!-- this group contains references that are not platform specific -->
<ItemGroup>
    <Reference Include="System" />
    <Reference Include="System.Core" />
    <!-- any other references that aren't platform specific -->
</ItemGroup>

<!-- x86 specific references -->
<ItemGroup Condition=" '$(Platform)' == 'x86' ">
    <Reference Include="MyComAssembly.Interop">
        <HintPath>..\..\lib\x86\MyComAssembly.Interop.dll</HintPath>
    </Reference>

    <!-- any additional x86 specific references -->
</ItemGroup>

<!-- x64 specific referneces -->
<ItemGroup Condition=" '$(Platform)' == 'x64' ">
    <Reference Include="MyComAssembly.Interop">
        <HintPath>..\..\lib\x64\MyComAssembly.Interop.dll</HintPath>
    </Reference>

    <!-- any additional x64 specific references -->
</ItemGroup>

Now, when you set your project/solution build configuration to target the x86 or x64 platform, it should include the proper references in each case. Of course, you'll need to play around with the <Reference>elements. You could even setup dummy projects where you add the x86 and x64 references, and then just copy the necessary <Reference>elements from those dummy project files to your "real" project file.

现在,当您将项目/解决方案构建配置设置为面向 x86 或 x64 平台时,它应该在每种情况下都包含正确的引用。当然,你需要玩弄这些<Reference>元素。您甚至可以设置虚拟项目,在其中添加 x86 和 x64 引用,然后只需将<Reference>这些虚拟项目文件中的必要元素复制到“真实”项目文件中。


Edit 1
Here's a link to the common MSBuild project items, which I accidentally left out from the original post: http://msdn.microsoft.com/en-us/library/bb629388.aspx


编辑 1
这是常见 MSBuild 项目项的链接,我不小心从原始帖子中遗漏了该链接:http: //msdn.microsoft.com/en-us/library/bb629388.aspx

回答by Yochai Timmer

You can use a condition to an ItemGroupfor the dll references in the project file.
This will cause visual studio to recheck the condition and references whenever you change the active configuration.
Just add a condition for each configuration.

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

Example:

例子:

 <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 Micke

I'm referencing the x86 DLLs, located in e.g. \component\v3_NET4, in my project. Specific DLLs for x86/x64 are located in sub-folders named "x86" and "x64" resp.

我在我的项目中引用 x86 DLL,例如位于 \component\v3_NET4 中。x86/x64 的特定 DLL 位于名为“x86”和“x64”的子文件夹中。

Then I'm using a pre-build script that copies apropriate DLLs (x86/x64) into the referenced folder, based on $(PlatformName).

然后我使用一个预构建脚本,根据 $(PlatformName) 将适当的 DLL (x86/x64) 复制到引用的文件夹中。

xcopy /s /e /y "$(SolutionDir)..\component\v3_NET4$(PlatformName)\*" "$(SolutionDir)..\component\v3_NET4"

Works for me.

对我来说有效。

回答by Felix Keil

One .Net build with x86/x64 Dependencies

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

While all other answers give you a solution to make different Builds according to the platform, I give you an option to only have the "AnyCPU" configuration and make a build that works with your x86 and x64 dlls.

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

Resolution of correct x86/x64-dlls at runtime

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

Steps:

脚步:

  1. Use AnyCPU in csproj
  2. Decide if you only reference the x86 or the x64 dlls in your csprojs. Adapt the UnitTests settings to the architecture settings you have chosen. It's important for debugging/running the tests inside VisualStudio.
  3. On Reference-Properties set Copy Local& Specific Versionto false
  4. Get rid of the architecture warnings by adding this line to the first PropertyGroupin all of your csproj files where you reference x86/x64: <ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
  5. Add this postbuild script to your startup project, use and modify the paths of this script so that it copies all your x86/x64 dlls in corresponding subfolders of your 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

    --> When you would start application now, you get an exception that the assembly could not be found.

  6. Register the AssemblyResolve event right at the beginning of your application entry point

    AppDomain.CurrentDomain.AssemblyResolve += TryResolveArchitectureDependency;
    

    withthis method:

    /// <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. If you have unit tests make a TestClass with a Method that has an AssemblyInitializeAttribute and also register the above TryResolveArchitectureDependency-Handler there. (This won't be executed sometimes if you run single tests inside visual studio, the references will be resolved not from the UnitTest bin. Therefore the decision in step 2 is important.)
  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 脚本添加到您的启动项目中,使用并修改此脚本的路径,以便它将所有 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

    --> 当您现在启动应用程序时,您会收到无法找到程序集的异常。

  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 中的决定很重要。)

Benefits:

好处:

  • One Installation/Build for both platforms
  • 两个平台的一次安装/构建

Drawbacks: - No errors at compile time when x86/x64 dlls do not match. - You should still run test in both modes!

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

Optionally create a second executable that is exclusive for x64 architecture with Corflags.exe in postbuild script

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

Other Variants to try out: - You would not need the AssemblyResolve event handler if you assure otherwise that the dlls get copied in your binary folder at start (Evaluate Process architecture -> move corresponding dlls from x64/x86 to bin folder and back.) - In Installer evaluate architecture and delete binaries for wrong architecture and move the right ones to the bin folder.

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

回答by Boris Zinchenko

I faced the same problem and spent quite a while searching for a decent solution. Most people offer manual editing of Visual Studio solution files, which is quite tedious, error prone and confusing when exploring these edited files in Visual Studio GUI afterwards. When I already gave up, the solution came up itself. It is very similar to what Micke recommends in his answer above.

我遇到了同样的问题,并花了很长时间寻找合适的解决方案。大多数人提供 Visual Studio 解决方案文件的手动编辑,这在之后在 Visual Studio GUI 中探索这些编辑过的文件时非常乏味、容易出错且令人困惑。当我已经放弃时,解决方案就出现了。这与米克在上面的回答中推荐的非常相似。

In account manager I created two separate build targets for x86 and x64 platforms, as usual. Next, I added a reference to x86 assembly to my project. On this point, I believed that the project is configured for x86 build only and will never build for x64 configuration, unless I will make manual editing of it as suggested by Hugo above.

在客户经理中,我像往常一样为 x86 和 x64 平台创建了两个单独的构建目标。接下来,我在我的项目中添加了对 x86 程序集的引用。在这一点上,我相信该项目仅针对 x86 构建配置,并且永远不会针对 x64 配置构建,除非我按照上面 Hugo 的建议对其进行手动编辑。

After a while, I eventually forgot the limitation and accidentally started x64 build. Of course, the build failed. But important was the error message I received. Error message told that assembly named exactly as my referenced x86 assembly is missing in the folder intended as x64 build target for my solution.

过了一会儿,我最终忘记了限制,不小心启动了 x64 构建。当然,构建失败了。但重要的是我收到的错误消息。错误消息表明,在用作我的解决方案的 x64 构建目标的文件夹中缺少与我引用的 x86 程序集完全相同的程序集。

Having noticed this, I have manually copied proper x64 assembly into this directory. Glory! My x64 build miraculously succeeded with proper assembly found and linked implicitly. It was matter of minutes to modify my solution to set a build target directory for x64 assembly to this folder. After these steps solution builds automatically for both x86 and x64 without any manual editing of MSBuild files.

注意到这一点后,我已手动将正确的 x64 程序集复制到此目录中。荣耀!我的 x64 构建奇迹般地成功了,找到并隐式链接了正确的程序集。修改我的解决方案以将 x64 程序集的构建目标目录设置到此文件夹只需几分钟。在这些步骤之后,解决方案会自动为 x86 和 x64 构建,无需手动编辑 MSBuild 文件。

To sum up:

总结:

  1. Create x86 and x64 targets in a single project
  2. Add all proper project references to x86 assemblies
  3. Set one common build target directory for all x64 assemblies
  4. In case you have ready x64 assemblies, just copy them once into your x64 build target directory
  1. 在单个项目中创建 x86 和 x64 目标
  2. 将所有正确的项目引用添加到 x86 程序集
  3. 为所有 x64 程序集设置一个通用构建目标目录
  4. 如果您已准备好 x64 程序集,只需将它们复制一次到您的 x64 构建目标目录中

After completion of these steps your solution will properly build for both x86 and x64 configurations.

完成这些步骤后,您的解决方案将针对 x86 和 x64 配置正确构建。

This worked for me on Visual Studio 2010 .NET 4.0 C# project. Evidently, this is a sort of undocumented internal behavior of Visual Studio, which might be subject of change in 2012, 2013 and 2015 versions. If somebody will try on other versions, please share your experience.

这在 Visual Studio 2010 .NET 4.0 C# 项目中对我有用。显然,这是 Visual Studio 的一种未记录的内部行为,可能会在 2012、2013 和 2015 版本中发生变化。如果有人会尝试其他版本,请分享您的经验。

回答by Jeff H

I ended up using what I consider an easier solution that is sort of an inversion of Micke's. The project is a C# forms app, Visual Studio 2015, with x86 and x64 targets. I referenced one of the .NET assemblies, I used the 32 bit one. In the reference properties, I set "Copy Local" to false. Then I just manually put the appropriate (32 or 64 bit) .Net assembly in each target directory. The actual reference bitness is irrelevant, assuming they have the same capabilities, since it's just defining the external interface. You could also put a post build copy step if you wanted to get fancy. Note this project also had a COM reference, same thing works. The reference defines the objects/interfaces so the bitness of the reference DLL is irrelevant. If both 32 bit and 64 bit COM DLLs are registered, the app will look in the appropriate place in the registry and create the correct 32 or 64 bit COM object. Works for me!

我最终使用了我认为更简单的解决方案,它有点像 Micke 的倒置。该项目是一个 C# 窗体应用程序 Visual Studio 2015,具有 x86 和 x64 目标。我引用了 .NET 程序集之一,我使用了 32 位程序集。在参考属性中,我将“Copy Local”设置为 false。然后我只是手动将适当的(32 或 64 位).Net 程序集放在每个目标目录中。假设它们具有相同的功能,实际引用位数是无关紧要的,因为它只是定义了外部接口。如果您想花哨的话,您还可以放置一个构建后复制步骤。注意这个项目也有一个 COM 引用,同样的事情。引用定义了对象/接口,因此引用 DLL 的位数无关紧要。如果同时注册了 32 位和 64 位 COM DLL,该应用程序将在注册表中的适当位置查找并创建正确的 32 位或 64 位 COM 对象。对我来说有效!