Invoking an unmanaged 32bit library out of a 64bit process

When loading unmanaged / native libraries from managed .NET code, the normal way is to use the “Platform Invoke (P/Invoke)” mechanism.

[DllImport("shell32.dll", CallingConvention = CallingConvention.Winapi)]
static extern int DllGetVersion(ref DLLVERSIONINFO pdvi);

The problem

P/Invoke has an annoying limitation: You can not load a x86 (32bit) library into a 64bit process and vice versa. This is especially problematic when your application is compiled with the “AnyCPU”-flag - which lets the .NET runtime decide which architecture to use at runtime - and there is only one 32bit version of a specific dll. If recompiling the library against a different architecture is not an option, you have to find another solution.

The idea

Since we can’t load 32bit code into our 64bit process, the idea is to create a separate executable for this task. It would somehow load a library, invoke a specific function and pass the results back to the caller. So this is what the Legacy Wrapper (Link points to GitHub) does. See this example:

// Define delegate matching api function
private delegate int GetSystemMetrics(int index);

// Create new WrapperClient
// Remember to ensure a call to the Dispose()-Method!
using (var client = new WrapperClient())
    // Make calls providing library name, function name, and parameters
    int x = (int)client.Invoke<GetSystemMetrics>("User32.dll", "GetSystemMetrics", new object[] { 0 });
    int y = (int)client.Invoke<GetSystemMetrics>("User32.dll", "GetSystemMetrics", new object[] { 1 });


How does this work? The Legacy Wrapper consists of an executable (the “server”) and a class library (the “client”). With creating a new WrapperClient instance, the library would at first create a random token and pass it as an argument when starting the “server” executable.

string token = Guid.NewGuid().ToString();

// Pass token to child process
Process.Start("LegacyWrapper", token);

The executable will use this token to create a new named pipe and wait for incoming connections:

static void Main(string[] args)
    if (args.Length < 1) return;

    string token = args[0];

    // Create new named pipe with token from client
    using (var pipe = new NamedPipeServerStream(token, PipeDirection.InOut, 1, PipeTransmissionMode.Message))

Now, the client will send binary serialized information about the desired library to the server:

var info = new CallData
    Library = library,
    ProcedureName = function,
    Parameters = args,
    Delegate = typeof(T),
var formatter = new BinaryFormatter();
// Write request to server
formatter.Serialize(_pipe, info);

If the called dll function returns any result, the server will eventually write it to the pipe - after that, the Invoke<T>() method returns. If an exception occurs, it will also be serialized and re-thrown client side.

Security considerations

Named pipes are by default public and visible to anyone who knows its name. The following screenshot was taken using Process Explorer:

Named Pipe Listing

This includes read / write access to all visible pipes. So at the moment you should NOT use the Legacy Wrapper for cryptographic / security purposes, as anyone could connect to the open pipe and read your keys, ciphertext etc.

Further reading

Check out the GitHub repository for more technical details. Feel free to submit any suggestions/issues and contribute to the project.

GitHub Twitter