简体   繁体   English

C#P / Invoke:包含函数指针的编组结构

[英]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. 我需要知道P / Invoke内部人员的洞察力比我更好。

Here is how I'm marshalling structures containing function pointers from C to C#. 这是我如何编组包含从C到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: 我正在使用C编码的本机DLL连接,它提供以下入口点:

void* getInterface(int id);

You have to pass getInterface(int) one of the following enum values: 您必须传递getInterface(int)以下枚举值之一:

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: 以下是您在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. 在C#中,我有一个Library类,它使用P / Invoke映射getInterface(int)入口点。

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() ? 映射整个结构的效率低于使用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? 使用Marshal.PtrToStructure()一次映射整个结构时,是否有比我描述的更简洁的方式? 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. 编辑:为了清晰和完整,在上面的代码片段中, obj是使用void* createObject(int type)入口点获得的实例。


EDIT2: One advantage of method 1) is that Marshal.GetDelegateForFunctionPointer() is only available starting from .NET Framework 2.0. EDIT2:方法1)的一个优点是Marshal.GetDelegateForFunctionPointer()仅从.NET Framework 2.0开始提供。 However, Marshal.PrtToStructure() has always been available. 但是, Marshal.PrtToStructure()一直可用。 That said, I'm not sure it's worth ensuring 1.0 compatibility nowadays. 也就是说,我不确定现在是否值得确保1.0兼容性。


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. 编辑3:我尝试使用Reflector检查生成的代码,但它没有提供太多信息,因为所有有趣的细节都在辅助函数中完成,如PtrToStructureHelper ,并且没有公开。 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; Marshal.PtrToStructure()方法相比, Marshal.GetDelegateForFunctionPointer()方法的速度降低了约10%; that whith a structure containing IntPtr s for all the functions that are not of interest. 对于所有不感兴趣的函数,包含IntPtr的结构。

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). 我还将Marshal.GetDelegateForFunctionPointer()与我自己的滚动编组器进行了比较:我对齐表示调用堆栈的struct ,将其固定在内存中,将其地址传递给本机端,我使用asm编码的trampoline,以便调用函数使用内存区域作为其参数堆栈(这是可能的,因为cdecl x86调用约定传递堆栈上的所有函数参数)。 Timings were equivalent. 时间相当。

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);
        }
    }
}

I don't know that answer to your question 1. I'd expect that Marshal.PtrToStructure() is implemented in terms of the other Marshal primitives, so it would be more efficient to just use the single Marshal.GetDelegateForFunctionPointer . 我不知道你问题的答案1.我希望Marshal.PtrToStructure()是根据其他Marshal原语实现的,所以使用单个Marshal.GetDelegateForFunctionPointer会更有效率。 But that's just a guess - worth what you paid for it. 但这只是一个猜测 - 值得你付出的代价。

As for your question 2. No, there is no less verbose way to do this. 至于你的问题2.没有,有没有做到这一点简洁的方式。 There is a MORE verbose way. 有一种更冗长的方式。 You can use the old style MIDL compiler to build a type library for your dll and the load that type library. 您可以使用旧式MIDL编译器为您的dll和类型库的加载构建类型库。 But the available marshaling options for MIDL are quite a bit more limited that what you can describe in C#. 但是,MIDL的可用编组选项比C#中描述的更有限。 And the MIDL compler is pretty hard to work with, you would probably end up having to write another unmanaged DLL to do the interop between managed code and your target dll. 并且MIDL编译器非常难以使用,您可能最终必须编写另一个非托管DLL来执行托管代码和目标dll之间的互操作。

For point 1: 对于第1点:

Marshal.GetDelegateForFunctionPointer() is more simple if your structure contains a lot of function pointers and you use only a few. Marshal.GetDelegateForFunctionPointer()更简单,如果你的结构包含很多函数指针,你只使用几个。 A (major) drawback is that you have to compute the offset to the function pointer by hand (note that pointer size differs on 32/64 bits platform). 一个(主要)缺点是您必须手动计算函数指针的偏移量(请注意,指针大小在32/64位平台上不同)。 A structure is more easy to use but marshals more data. 结构更易于使用,但可以编组更多数据。

For point 2: 对于第2点:

I don't think a less verbose approach is possible. 我认为不可能采用不那么冗长的方法。 You may only define delegates for the function you want to use, and use a dummy delegate for the function pointers you don't want to use. 您只能为要使用的函数定义委托,并对您不想使用的函数指针使用伪委托。 This way, the marshaling will perform ok, but you end with a structure containing non-callable delegates. 这样,编组将执行正常,但您以包含不可调用的委托的结构结束。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM