Search code examples
c#pinvoke.net-assembly

Best way to craft an AnyCPU assembly using either 32-bit or 64-bit native DLLs?


I generated bindings for a native library but realized that imports differ between 32-bit and 64-bit.

Problem:

Not only the entry point differ, but also the calling convention:

32-bit import:

[DllImport("implot", EntryPoint = "??0ImPlotPoint@@QAE@XZ", CallingConvention = CallingConvention.ThisCall)]

64-bit import:

[DllImport("implot", EntryPoint = "??0ImPlotPoint@@QEAA@XZ", CallingConvention = CallingConvention.Cdecl)]

Since these are compile-time constants, there's no way to have an if (64bit) then ... else ... for these imports.

What I've been thinking about:

Using ordinals instead of decorated names as entry points:

That won't work because they don't necessarily point to the same export.

Defer stuff to two inner classes, e.g. NativeMethods32, NativeMethods64:

One would have to put bit-ness checks everywhere to dispatch to the right class, tedious as well.

Generate two inner managed assemblies, one 32-bit, one 64-bit:

Have the AnyCPU assembly dynamically load the appropriate managed assembly at runtime.

Question:

Is there an effective, proven pattern to tackle such problem?


Solution

  • I solved the problem rewriting the entire stuff with Roslyn to produce an AnyCPU module.

    Like so:

    public class Whatever
    {
        internal partial struct __Internal
        {
            // keep fields
            // remove methods
        }
    
        internal partial struct __Internal32
        {
            // remove fields
            // insert 32-bit methods
        }
        
        internal partial struct __Internal64
        {
            // remove fields
            // insert 64-bit methods
        }
        
        public static void Test()
        {
            if (IntPtr.Size == 4)
            {
                __Internal32.Test();
            }
            else
            {
                __Internal64.Test();
            }
        }
    }
    

    The result works very well, the platform target can be switched as usual and it just works.