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

C#P/Invoke:包含函数指针的编组结构,c#,pinvoke,marshalling,structure,function-pointers,C#,Pinvoke,Marshalling,Structure,Function Pointers,很抱歉下面的介绍太冗长了。我需要比我更了解P/Invoke内部结构的人的洞察力 下面是我如何将包含从C到C的函数指针的结构编组。我想知道这是否是最干净和/或最有效的方法 我正在与一个用C编写的本机DLL接口,该DLL提供以下入口点: void* getInterface(int id); 您必须传递以下枚举值之一: enum INTERFACES { FOO, BAR }; 返回指向包含函数指针的结构的指针,如: typedef struct IFOO { void (*meth

很抱歉下面的介绍太冗长了。我需要比我更了解P/Invoke内部结构的人的洞察力

下面是我如何将包含从C到C的函数指针的结构编组。我想知道这是否是最干净和/或最有效的方法

我正在与一个用C编写的本机DLL接口,该DLL提供以下入口点:

void* getInterface(int id);
您必须传递以下枚举值之一:

enum INTERFACES
{
  FOO,
  BAR
};
返回指向包含函数指针的结构的指针,如:

typedef struct IFOO
{
  void (*method1)(void* self, int a, float b);
  void (*method2)(void* self, int a, float b, int c);
} IFoo;
下面是如何在C中使用它:

IFoo* interface = (IFoo*)getInterface(FOO);
interface->method1(obj, 0, 1.0f); // where obj is an instance of an object
                                  // implementing the IFoo interface.
在C#中,我有一个
类,它使用p/Invoke映射
getInterface(int)
入口点

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

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);
}
我是这样用的:

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.
我有以下问题:

  • 使用
    Marshal.GetDelegateForFunctionPointer()
    ,映射整个结构的效率是否低于映射结构内部的单个指针

    由于我基本上不需要接口公开的所有方法,所以我可以做(测试并工作):

  • 使用
    Marshal.PtrToStructure()
    一次映射整个结构时,有没有比我描述的更详细的方法?我的意思是,比必须为每个方法定义委托类型等更少的冗长


  • 编辑:为了清晰和完整,在上面的代码片段中,
    obj
    是使用
    void*createObject(int-type)
    入口点获得的实例


    EDIT2:方法1)的一个优点是,
    Marshal.GetDelegateForFunctionPointer()
    只能从.NET Framework 2.0开始使用。但是,
    Marshal.PrtToStructure()
    始终可用。也就是说,我不确定现在是否值得确保1.0的兼容性


    EDIT3:我尝试使用检查生成的代码,但没有提供太多信息,因为所有有趣的细节都是在助手函数中完成的,比如
    PtrToStructureHelper
    ,并且没有公开。然后,即使我可以看到框架内部完成了什么,那么运行时也有机会进行优化,我不知道具体是什么、为什么和什么时候:)

    然而,我对问题中描述的两种方法进行了基准测试。
    Marshal.PtrToStructure()
    方法比
    Marshal.GetDelegateForFunctionPointer()方法慢约10%;包含所有不感兴趣的函数的
    IntPtr
    s的结构


    我还将
    Marshal.GetDelegateForFunctionPointer()
    与我自己的滚动封送器进行了比较:我对齐表示调用堆栈的
    struct
    ,将其固定在内存中,将其地址传递到本机端,在本机端使用asm中编码的蹦床,以便调用函数使用内存区域作为其参数堆栈(这是可能的,因为
    cdecl
    x86调用约定传递堆栈上的所有函数参数)。计时是等效的。

    我不知道问题1的答案。我希望
    Marshal.PtrToStructure()
    是根据其他封送原语实现的,因此只使用单个
    封送.GetDelegateForFunctionPointer
    ,效率会更高。但这只是一个猜测,值得你为此付出的代价


    至于你的问题2。不,没有比这更详细的方法了。有一种更详细的方法。你可以使用老式的MIDL编译器为你的dll构建一个类型库并加载该类型库。但是MIDL的可用封送选项比你在C#中描述的要有限得多。MIDL编译器是p由于使用起来很困难,您可能最终不得不编写另一个非托管DLL来执行托管代码与目标DLL之间的互操作。

    以下是我将开始的内容

    用法:

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

    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);
            }
        }
    }
    
    关于第1点:

    Marshal.GetDelegateForFunctionPointer()
    如果您的结构包含很多函数指针,而您只使用了很少的函数指针,则更简单。一个(主要)缺点是您必须手动计算函数指针的偏移量(请注意,指针大小在32/64位平台上有所不同)。结构更易于使用,但封送更多数据

    关于第2点:


    我认为不可能采用不太详细的方法。您可以只为要使用的函数定义委托,而对不想使用的函数指针使用伪委托。这样,封送处理可以正常执行,但最终会得到一个包含不可调用委托的结构。

    About 1)我真的不知道是否有可能检查到底做了什么:比如优化器可能会发现只使用了结构的一个委托。无论如何,至少它看起来足够干净。@Gregory,我认为这不太可能。但是你可以使用.NETReflector来反编译代码并确定答案。我就是这样做的。只是这种“漂亮的包装”与问题无关。然后我知道我没有详细解释,但是
    self
    不是指向接口结构的指针。这实际上是一个
    指针。本机库有一个
    void*obj=createObject(int-type)
    入口点。您使用它来实例化类型,然后如果您实例化的类型实现了IFoo接口(
    bool implements(void*obj,int-iface)
    obj
    FOO
    )返回
    true
    ,则允许您调用
    ((IFoo*)getInterface(FOO))->method1(obj,0,1.0f)
    这个最简单的修改是在
    GetFooInterface
    中添加一个
    IntPtr self
    参数。对于这样一个复杂的包装器,我实际上会创建
    Is
    As
    方法来执行强制转换。如果我更进一步,我会动态地创建实现
    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);
            }
        }
    }