Multi-architecture .NET programs

This article describes a way of modifying the DLL load path at runtime on Windows from a .NET application. This may be used to change which native DLLs are loaded, but not which .NET DLLs are loaded.
The problem: The MOSEK .NET API is defined in mosekdotnet.dll which works on both 32bit and 64bit platforms. It does this by loading the native mosekxx6_0.dll, which is linked to mosek6_0.dll in the 32bit distribution and mosek64_6_0.dll in the 64bit distribution. We wish to make a .NET application that includes both libraries and is capable of choosing which to use at runtime.
Please note that .NET applications can be compiled either specifically to target x86, x64 or “any”. If the application was not compiled to target “any”, the method described in this article will not work. Furthermore, we use the PROCESSOR_ARCHITECTURE environment variable to detect the architecture: This may produce the desired result under all circumstances.
First of all, we create a directory structure for the application:
app\         # The application main directory. .NET executables
             # and DLLs go here.
app\app.exe  # The application executable
app\mosek32\ # DLLs from the 32bit MOSEK distro
app\mosek64\ # DLLs from the 64bit MOSEK distro

2.1.1 Using a script to set up the runtime environment

Create a script that detects the platform, sets up the PATH environment variable and starts the application. For example, we can place the following script under app\ and use it to run the actual application:
  1 rem First we extract the path of this .bat file:
  2 set APPDIR=%~d0%~p0
  3 rem Then we check the architecture and modify the PATH variable
  4 if "%PROCESSOR_ARCHITECTURE%" EQU "AMD64" ( 
  5   set "path=%APPDIR%\mosek64;%path%" 
  6 ) else ( 
  7   set "path=%APPDIR%\mosek32;%path%" 
  8 )
  9 rem Finally, we execute the application
 10 app.exe

2.1.2 Modifying the DLL load path while running

It is possible to modify the DLL search path after the .NET application has started. In .NET, native DLLs are located and loaded when they are first accessed rather than when the application is started.
In order for this to work we must modify the search path before any function in the MOSEK API is called. The program will take the general outline:
  1 using System;
  2 using System.Runtime.InteropServices;
  3 using System.Text;
  4 
  5 public class test
  6 {
  7   // Header for the function GetDllDirectory
  8   [DllImport("kernel32.dll",EntryPoint="GetDllDirectory")]
  9   static extern int
 10   _GetDllDirectory
 11   ( int size, [MarshalAs(UnmanagedType.LPStr)] StringBuilder buffer);
 12 
 13   static string GetDllDirectory()
 14   {
 15     StringBuilder b = new StringBuilder(4096);
 16     int res = _GetDllDirectory(4096,b);
 17     return b.ToString();
 18   }
 19 
 20   // Header for the function SetDllDirectory
 21   [DllImport("kernel32.dll",EntryPoint="SetDllDirectory")]
 22   static extern bool
 23   SetDllDirectory
 24   ( [MarshalAs(UnmanagedType.LPStr)] string buffer);
 25 
 26 
 27   // In main we assume that the first argument is the directory where
 28   // the mosekxx_6_0.dll is located.
 29   public static void Main(string[] args)
 30   {
 31     string key  = "PROCESSOR_ARCHITECTURE";
 32     string arch = System.Environment.GetEnvironmentVariable(key); 
 33     string oldpath = GetDllDirectory();
 34     Console.WriteLine("Before modification Dll path is: {0}", oldpath);
 35     
 36     if (arch == "AMD64")
 37       SetDllDirectory(".\\mosek64" + ';' + oldpath);
 38     else 
 39     if (arch == "x86")
 40       SetDllDirectory(";.\\mosek32" + ';' + oldpath);
 41     else
 42       Console.WriteLine("Unknown processor architecture {0}", arch);
 43 
 44     Console.WriteLine("Set Dll path to : {0}", GetDllDirectory());
 45 
 46     // The DLL path now knows where to look, so we can call the first
 47     // MOSEK function:
 48     mosek.Env env = new mosek.Env();
 49 
 50     // etc...
 51   }
 52 }
First, we create a directory containing the source for the example and the mosekdotnet.dll from any of the MOSEK Windows distributions:
app\mosekdotnet.dll
app\multiplatformexe.cs
Start a Visual Studio Command Prompt, and go to app\. Compile the example:
csc /out:app.exe /r:mosekdotnet.dll multiplatformexe.cs
The example requires that we run it from the app\ directory so it knows where the native DLLs are located:
.\app.exe
Using this strategy it is possible to bundle multiple sets of native DLLs with an application in separate subdirectories and choose which to use at runtime (not only architecture dependant binaries, but also, say, debugging enabled DLL).

2.1.3 References