ChatGPT解决这个技术问题 Extra ChatGPT

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

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.

@Magnus Johansson: you can use two configurations to accomplish half your goal. The MSI is a bit harder.

J
John Baughman

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.


In Visual Studio 2008, I found that s could not be nested. This solution worked fine once I made the new s below the group the rest of the s. I also had to change x86 to AnyCPU, which probably relates to the history of my particular project.
That Advanced Installer looks pretty awesome.
This may be a dumb question but how to you get to the file to edit it manualy?
To edit the file within VS, right click on the project in Solution Explorer and find "Unload Project". Once the project is unloaded, right click on it again and click on "Edit ". After you have edited the project file, save it and right click on the project file again and load it. If no typos or errors, it will load again. If not, VS will tell you pretty much what the problem is with the file. Hope that helps!
K
Kevin Crowell

Let's say you have the DLLs build for both platforms, and they are in the following location:

C:\whatever\x86\whatever.dll
C:\whatever\x64\whatever.dll

You simply need to edit your .csproj file from this:

<HintPath>C:\whatever\x86\whatever.dll</HintPath>

To this:

<HintPath>C:\whatever\$(Platform)\whatever.dll</HintPath>

You should then be able to build your project targeting both platforms, and MSBuild will look in the correct directory for the chosen platform.


This would be brilliant if it worked, but it doesn't. At least not for me.
Isn't that supposed to be: C:\whatever\$(Platform)\whatever.dll
Worked OK on Visual Studio 2008 for me, but did not automatically copy the DLL to the build target directory, as a normal does. mdb's solution worked better for me.
m
mrpbody

Not sure of the total answer to your question - but thought I would point out a comment in the Additional Information section of the SQL Compact 3.5 SP1 download page seeing you are looking at x64 - hope it helps.

Due to changes in SQL Server Compact SP1 and additional 64-bit version support, centrally installed and mixed mode environments of 32-bit version of SQL Server Compact 3.5 and 64-bit version of SQL Server Compact 3.5 SP1 can create what appear to be intermittent problems. To minimize the potential for conflicts, and to enable platform neutral deployment of managed client applications, centrally installing the 64-bit version of SQL Server Compact 3.5 SP1 using the Windows Installer (MSI) file also requires installing the 32-bit version of SQL Server Compact 3.5 SP1 MSI file. For applications that only require native 64-bit, private deployment of the 64-bit version of SQL Server Compact 3.5 SP1 can be utilized.

I read this as "include the 32bit SQLCE files as well as the 64bit files" if distributing for 64bit clients.

Makes life interesting I guess.. must say that I love the "what appears to be intermittent problems" line... sounds a bit like "you are imagining things, but just in case, do this..."


Y
Yochai Timmer

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>

This is a good answer; basically a tl;dr of mdb's answer (which is no bad thing).
F
Felix Keil

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.

You have to write some plumbing code for this.

Resolution of correct x86/x64-dlls at runtime

Steps:

Use AnyCPU in csproj 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. On Reference-Properties set Copy Local & Specific Version to false 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: None Add this postbuild script to your startup project, use and modify the paths of this script sp 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. Register the AssemblyResolve event right at the beginning of your application entry point AppDomain.CurrentDomain.AssemblyResolve += TryResolveArchitectureDependency; withthis method: ///

/// Event Handler for AppDomain.CurrentDomain.AssemblyResolve /// /// The app domain /// The resolve event args /// The architecture dependent assembly 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; } 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 don't need the AssemblyResolve event handler if you assure that the right dlls are copied to 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.


L
Lior Friedman

Regarding your last question. Most likely you cant solve this inside a single MSI. If you are using registry/system folders or anything related, the MSI itself must be aware of this and you must prepare a 64bit MSI to properly install on 32 bit machine.

There is a possibility that you can make you product installed as a 32 it application and still be able to make it run as 64 bit one, but i think that may be somewhat hard to achieve.

that being said i think you should be able to keep a single code base for everything. In my current work place we have managed to do so. (but it did took some juggling to make everything play together)

Hope this helps. Heres a link to some info related to 32/64 bit issues: http://blog.typemock.com/2008/07/registry-on-windows-64-bit-double-your.html


R
Ryan

If you use Custom Actions written in .NET as part of your MSI installer then you have another problem.

The 'shim' that runs these custom actions is always 32bit then your custom action will run 32bit as well, despite what target you specify.

More info & some ninja moves to get around (basically change the MSI to use the 64 bit version of this shim)

Building an MSI in Visual Studio 2005/2008 to work on a SharePoint 64

64-bit Managed Custom Actions with Visual Studio


v
voidMainReturn

You can generate two solutions differently and merge them afterwards! I did this for VS 2010. and it works. I had 2 different solutions generated by CMake and I merged them