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

70,493

Solution 1

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.

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.

<!-- 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:

  • 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.

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.

Solution 2

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.

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.

Here is an example that assumes your project is configured with target platforms named "x86" and "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.


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

Solution 3

You can use a condition to an ItemGroup for 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.

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>

Solution 4

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.

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

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

Works for me.

Solution 5

One .Net build with x86/x64 Dependencies

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.

Resolution of correct x86/x64-dlls at runtime

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 Version to false
  4. Get rid of the architecture warnings by adding this line to the first PropertyGroup in 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.)

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!

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

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.

Share:
70,493
Jonathan Yee
Author by

Jonathan Yee

Updated on February 27, 2020

Comments

  • Jonathan Yee
    Jonathan Yee about 4 years

    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.

    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.

  • tjmoore
    tjmoore over 11 years
    Nice. I went with using a conditional on the ItemGroup as per the suggestion below but using $(PROCESSOR_ARCHITEW6432) and $(PROCESSOR_ARCHITECTURE) for the conditions as here. A note is I found $(PROCESSOR_ARCHITECTURE) returns x86 on both 32 and 64 bit platforms but $(PROCESSOR_ARCHITEW6432) returns AMD64 only on 64bit. Something to note if you try to test for x86 (because AMD64 is a derivative of x86 I assume).
  • Hugo
    Hugo over 11 years
    Thanks for that information @tjmoore . On which O/S did you notice this? I just checked mine again (Win7SP1) and says AMD64 for the $(PROCESSOR_ARCHITECTURE), but would definitely like to have as complete and thorough information as possible.
  • Ed S.
    Ed S. about 11 years
    Funny, my search brings me here, and I only need this because I am also using LeadTools... +1
  • Sarah Weinberger
    Sarah Weinberger almost 11 years
    The solution works for the default configuration, but not from my testing not if you change the configuration from the configuration from the Visual Studio (2012 in my case) Solution Configuration dropdown list.
  • Dzyann
    Dzyann about 10 years
    Instead of using $(PROCESSOR_ARCHITEW6432) I used $(Platform) for some reason $(PROCESSOR_ARCHITEW6432) wasn't working.
  • ManicBlowfish
    ManicBlowfish over 7 years
    This is great thank you! This should definitely be the accepted solution!
  • hellodear
    hellodear over 7 years
    Excellent Answer!! Saved my day! Thanks a lot.
  • Yandros
    Yandros about 7 years
    Seriously, this answer's much better and simpler that the accepted one.
  • natenho
    natenho over 6 years
    Is it normal to have duplicated entries in References after doing this?
  • Pedro77
    Pedro77 almost 6 years
    8 year have passed and this still is the best solution? To edit the project file?