.net – Targeting both 32bit and 64bit with Visual Studio in same solution/project

64-bitnetvisual studiox86-64

I have a little dilemma on how to set up my visual studio builds for multi-targeting.

Background: c# .NET v2.0 with p/invoking into 3rd party 32 bit DLL's, SQL compact v3.5 SP1, with a Setup project.
Right now, the platform target is set to x86 so it can be run on Windows x64.

The 3rd party company has just released 64 bit versions of their DLL's and I want to build a dedicated 64bit program.

This raises some questions which I haven't got the answers to yet.
I want to have the exact same code base.
I must build with references to either the 32bit set of DLL's or 64bit DLL's.
(Both 3rd party and SQL Server Compact)

Can this be solved with 2 new sets of configurations (Debug64 and Release64) ?

Must I create 2 separate setup projects(std. visual studio projects, no Wix or any other utility), or can this be solved within the same .msi?

Any ideas and/or recommendations would be welcomed.

Best Answer

Yes, you can target both x86 and x64 with the same code base in the same project. In general, things will Just Work if you create the right solution configurations in VS.NET (although P/Invoke to entirely unmanaged DLLs will most likely require some conditional code): the items that I found to require special attention are:

  • References to outside managed assemblies with the same name but their own specific bitness (this also applies to COM interop assemblies)
  • The MSI package (which, as has already been noted, will need to target either x86 or x64)
  • Any custom .NET Installer Class-based actions in your MSI package

The assembly reference issue can't be solved entirely within VS.NET, as it will only allow you to add a reference with a given name to a project once. To work around this, edit your project file manually (in VS, right-click your project file in the Solution Explorer, select Unload Project, then right-click again and select Edit). After adding a reference to, say, the x86 version of an assembly, your project file will contain something like:

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

Wrap that Reference tag inside an ItemGroup tag indicating the solution configuration it applies to, e.g:

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

Then, copy and paste the entire ItemGroup tag, and edit it to contain the details of your 64-bit DLL, e.g.:

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

After reloading your project in VS.NET, the Assembly Reference dialog will be a bit confused by these changes, and you may encounter some warnings about assemblies with the wrong target processor, but all your builds will work just fine.

Solving the MSI issue is up next, and unfortunately this will require a non-VS.NET tool: I prefer Caphyon's Advanced Installer for that purpose, as it pulls off the basic trick involved (create a common MSI, as well as 32-bit and 64-bit specific MSIs, and use an .EXE setup launcher to extract the right version and do the required fixups at runtime) very, very well.

You can probably achieve the same results using other tools or the Windows Installer XML (WiX) toolset, but Advanced Installer makes things so easy (and is quite affordable at that) that I've never really looked at alternatives.

One thing you may still require WiX for though, even when using Advanced Installer, is for your .NET Installer Class custom actions. Although it's trivial to specify certain actions that should only run on certain platforms (using the VersionNT64 and NOT VersionNT64 execution conditions, respectively), the built-in AI custom actions will be executed using the 32-bit Framework, even on 64-bit machines.

This may be fixed in a future release, but for now (or when using a different tool to create your MSIs that has the same issue), you can use WiX 3.0's managed custom action support to create action DLLs with the proper bitness that will be executed using the corresponding Framework.


Edit: as of version 8.1.2, Advanced Installer correctly supports 64-bit custom actions. Since my original answer, its price has increased quite a bit, unfortunately, even though it's still extremely good value when compared to InstallShield and its ilk...


Edit: If your DLLs are registered in the GAC, you can also use the standard reference tags this way (SQLite as an example):

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

The condition is also reduced down to all build types, release or debug, and just specifies the processor architecture.