Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# P/Invoke: Marshalling structures containing function pointers

Sorry for the verbose introduction that follows. I need insight from someone knowing P/Invoke internals better than I do.

Here is how I'm marshalling structures containing function pointers from C to C#. I would like to know whether it's the cleanest and/or most efficient way of doing it.

I'm interfacing with a native DLL coded in C that provides the following entry point:

void* getInterface(int id);

You have to pass getInterface(int) one of the following enum values:

enum INTERFACES
{
  FOO,
  BAR
};

Which returns a pointer to a structure containing function pointers like:

typedef struct IFOO
{
  void (*method1)(void* self, int a, float b);
  void (*method2)(void* self, int a, float b, int c);
} IFoo;

And here is how you use it in C:

IFoo* interface = (IFoo*)getInterface(FOO);
interface->method1(obj, 0, 1.0f); // where obj is an instance of an object
                                  // implementing the IFoo interface.

In C# I have a Library class that maps the getInterface(int) entry point using P/Invoke.

class Library
{
  [DllImport("MyDLL"), EntryPoint="getInterface", CallingConvention=CallingConvention.Cdecl)]
  public static extern IntPtr GetInterface(int id);
};

Then I defined:

struct IFoo
{
  public M1 method1;
  public M2 method2;


  [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
  public delegate void M1(IntPtr self, int a, float b);

  [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
  public delegate void M2(IntPtr self, int a, float b, int c);
}

And I'm using it this way:

IntPtr address = Library.GetInterface((int)Interfaces.FOO);
IFoo i = (IFoo)Marshal.PtrToStructure(address, typeof(IFoo));

i.method1(obj, 0, 1.0f): // where obj is an instance of an object
                         // implementing the IFoo interface.

I have the following questions:

  1. Is mapping the whole structure less efficient than mapping a single pointer inside the structure using Marshal.GetDelegateForFunctionPointer()?

    Since I mostly don't need all the methods exposed by an interface, I can do (tested and works):

    unsafe
    {
      IntPtr address = Library.GetInterface(id);
      IntPtr m2address = new IntPtr(((void**)address.toPointer())[1]);
    
      M2 method2 = (M2)Marshal.GetDelegateForFunctionPointer(m2address, typeof(M2));
    
      method2(obj, 0, 1.0f, 1);
    }
    
  2. When mapping the whole structure at once using Marshal.PtrToStructure(), is there a less verbose way than what I described? I mean less verbose than having to define the delegate types for every methods etc?


EDIT: For the sake of clarity and completeness, in the code snippets above, obj is an instance obtained with the void* createObject(int type) entry point.


EDIT2: One advantage of method 1) is that Marshal.GetDelegateForFunctionPointer() is only available starting from .NET Framework 2.0. However, Marshal.PrtToStructure() has always been available. That said, I'm not sure it's worth ensuring 1.0 compatibility nowadays.


EDIT3: I tried to inspect the generated code using Reflector but it doesn't give much information since all the interesting details are done in helper functions like PtrToStructureHelper and are not exposed. Then, even if I could see what's done in the framework internals, then the runtime has the opportunity to optimize things away and I don't know exactly what, why and when :)

However, I benchmarked the two approaches described in my question. The Marshal.PtrToStructure() approach was slower by a factor around 10% compared to the Marshal.GetDelegateForFunctionPointer() approach; that whith a structure containing IntPtrs for all the functions that are not of interest.

I also compared the Marshal.GetDelegateForFunctionPointer() with my own rolled marshaller: I align a struct representing the call stack, pin it in memory, pass its address to the native side where I use a trampoline coded in asm so that the call function uses the memory area as its parameter stack (this is possible since the cdecl x86 calling convention passes all the function parameters on the stack). Timings were equivalent.

like image 429
Gregory Pakosz Avatar asked Dec 28 '09 11:12

Gregory Pakosz


People also ask

What C is used for?

C programming language is a machine-independent programming language that is mainly used to create many types of applications and operating systems such as Windows, and other complicated programs such as the Oracle database, Git, Python interpreter, and games and is considered a programming foundation in the process of ...

What is the full name of C?

In the real sense it has no meaning or full form. It was developed by Dennis Ritchie and Ken Thompson at AT&T bell Lab. First, they used to call it as B language then later they made some improvement into it and renamed it as C and its superscript as C++ which was invented by Dr.

What is C in C language?

What is C? C is a general-purpose programming language created by Dennis Ritchie at the Bell Laboratories in 1972. It is a very popular language, despite being old. C is strongly associated with UNIX, as it was developed to write the UNIX operating system.

Is C language easy?

C is a general-purpose language that most programmers learn before moving on to more complex languages. From Unix and Windows to Tic Tac Toe and Photoshop, several of the most commonly used applications today have been built on C. It is easy to learn because: A simple syntax with only 32 keywords.


1 Answers

Here's what I would start with.

Usage:

IFoo foo = UnsafeNativeMethods.GetFooInterface();
foo.Method1(0, 1.0f);

Implementation:

internal interface IFoo
{
    void Method1(int a, float b);
    void Method2(int a, float b, int c);
}

internal static class UnsafeNativeMethods
{
    public static IFoo GetFooInterface()
    {
        IntPtr self = GetInterface(InterfaceType.Foo);
        NativeFoo nativeFoo = (NativeFoo)Marshal.PtrToStructure(self, typeof(NativeFoo));
        return new NativeFooWrapper(self, nativeFoo.Method1, nativeFoo.Method2);
    }

    [DllImport("mydll.dll", EntryPoint = "getInterface", CallingConvention = CallingConvention.Cdecl)]
    private static extern IntPtr GetInterface(InterfaceType id);

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    private delegate void Method1Delegate(IntPtr self, int a, float b);

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    private delegate void Method2Delegate(IntPtr self, int a, float b, int c);

    private enum InterfaceType
    {
        Foo,
        Bar
    }

    private struct NativeFoo
    {
        public Method1Delegate Method1;
        public Method2Delegate Method2;
    }

    private sealed class NativeFooWrapper : IFoo
    {
        private IntPtr _self;
        private Method1Delegate _method1;
        private Method2Delegate _method2;

        public NativeFooWrapper(IntPtr self, Method1Delegate method1, Method2Delegate method2)
        {
            this._self = self;
            this._method1 = method1;
            this._method2 = method2;
        }

        public void Method1(int a, float b)
        {
            _method1(_self, a, b);
        }

        public void Method2(int a, float b, int c)
        {
            _method2(_self, a, b, c);
        }
    }
}
like image 61
Sam Harwell Avatar answered Oct 25 '22 15:10

Sam Harwell