Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Any CPU not available in C++/C# solution

I have a solution that contains C# and managed C++ projects. It compiles in the solution platform x64 and x86. Since it is managed C++ I wanted to create a 'Any CPU' solution and get rid of the old ones.

I changed the C++ project linker settings to Force Safe IL Image for both x64 and x86.

Next, using the Configuration Manager, I created a new solution platform called 'Any CPU'. Next I added a project platform also called 'Any CPU'.

I proceeded to set all the C# projects to 'Any CPU', but for the C++ I can't do that. The project platform 'Any CPU' is not in the drop down, and there is also no option 'New...'.

VS is adement about it, so I kept it like it was and started a build. To my surprise the result DLL (from the C++ project) was MSIL even though the platform for C++ was x64. Same happens when compiling x32, the resulting DLL is in MSIL.

What gives? Why can't I set the C++ project to 'Any CPU'?

like image 550
Frank Kaaijk Avatar asked Feb 02 '15 14:02

Frank Kaaijk


People also ask

Is any CPU x64?

"Any CPU" means that when the program is started, the . NET Framework will figure out, based on the OS bitness, whether to run your program in 32 bits or 64 bits. There is a difference between x86 and Any CPU: on a x64 system, your executable compiled for X86 will run as a 32-bit executable.

What is any CPU?

Any CPU: Runs as a 64-bit process, can load Any CPU and x64 assemblies, will get BadImageFormatException if it tries to load an x86 assembly. Any CPU-32-bit preferred (default): Runs as a 32-bit process, can load Any CPU and x86 assemblies, will get BadImageFormatException if it tries to load an x64 assembly.

Should I target x86 or x64?

Since x86 apps run on both x64 and x86 systems, the most compatible choice is to build x86. If you MUST build a 64 bit solution, you'll need to target x64 and use our x64 dlls.

How do I change from CPU platform to x86?

In the Configuration Manager dialog, open the Active solution platform drop-down list box and click <New> …. In the New Solution Platform dialog, select x64 in the Type or select the new platform drop-down list box. Select x86 in the Copy settings from drop-down list box. Click OK.


2 Answers

As far as I know, you cannot create an "AnyCPU" project type in Visual Studio for a C++/CLI project. However, you can configure your C++/CLI project (under the "Win32" project type) so that it compiles as pure, safe MSIL, without a target platform. Doing so will allow your C++/CLI DLL assembly to be used with an "AnyCPU" C# project. I.e. it's effectively "AnyCPU", even though that's not its actual name in the Configuration Manager.

In the "C/C++" project settings:

  • Common Language RunTime Support: Safe MSIL Common Language RunTime Support (/clr:safe)

In the "Linker" project settings:

  • CLR Image Type: just make sure this isn't set explicitly to IJW or PURE

Notes:

  • By using the "safe" project type, a few of the compiler and linker options which appear to affect platform type will be ignored. I.e. you don't have to go through and set everything to a non-specific platform type. Just the above. But you may set the other options to something appropriate, if it makes you feel better. :)
  • "Safe" will prevent the use of pointers. If this is an important issue, it is apparently possible to do albeit with a more complicated process. See Creating a pure MSIL assembly from a C++/CLI project? for details.
  • Don't forget that by default, Visual Studio will create C# projects that even though they are "AnyCPU" and even though they are executed on a 64-bit OS, will start up as a 32-bit process. This can hide platform-mismatch issues, if a dependency is x86 instead of pure/safe MSIL as intended. Just something be aware of (you can control this by unchecking the "Prefer 32-bit" option in the C# project's "Build" project properties page).
like image 128
Peter Duniho Avatar answered Oct 31 '22 22:10

Peter Duniho


In order for the C++ functionality to be consumed by a C# dll, the C++ project must produce both x86 and x64 versions of the dll. It is not possible to reference just a x86 or a x64 dll from a C# dll compiled with the AnyCPU setting.

The trick to getting the AnyCPU dll to play with the C++ dll, is at runtime make sure the assembly cannot load the C++ dll and then subscribe to the AppDomain AssemblyResolve event. When the assembly tries to load the dll and fails, then your code has the opportunity to determine which dll needs to be loaded.

Subscribing to the event looks something like this:

System.AppDomain.CurrentDomain.AssemblyResolve += Resolver;

Event handler looks something like this:

System.Reflection.Assembly Resolver(object sender, System.ResolveEventArgs args)
{
     string assembly_dll = new AssemblyName(args.Name).Name + ".dll";
     string assembly_directory = "Parent directory of the C++ dlls";

     Assembly assembly = null;
     if(Environment.Is64BitProcess)
     {
            assembly = Assembly.LoadFile(assembly_directory + @"\x64\" + assembly_dll);
     }
     else
     {
            assembly = Assembly.LoadFile(assembly_directory + @"\x86\" + assembly_dll);
     }
     return assembly;
}

I have created a simple project demonstrating how to access C++ functionality from an AnyCPU dll.

https://github.com/kevin-marshall/Managed.AnyCPU

like image 42
Kevin Marshall Avatar answered Oct 31 '22 21:10

Kevin Marshall