代码之家  ›  专栏  ›  技术社区  ›  Gregory Pakosz

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

  •  15
  • Gregory Pakosz  · 技术社区  · 16 年前

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

    void* getInterface(int id);
    

    你必须通过考试 getInterface(int) 下列枚举值之一:

    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.
    

    Library 类,该类映射

    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.
    

    我有以下问题:

    1. 映射整个结构的效率是否低于使用 Marshal.GetDelegateForFunctionPointer()

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

      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. Marshal.PtrToStructure() ,有没有比我描述的更详细的方式?我的意思是,比必须为每个方法定义委托类型等更少的冗长?


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


    编辑2:方法1)的一个优点是 Marshal.PrtToStructure() 一直都有。也就是说,我不确定现在是否值得确保1.0的兼容性。


    EDIT3:我尝试使用 Reflector PtrToStructureHelper 并且没有暴露。然后,即使我可以看到框架内部完成了什么,那么运行时也有机会进行优化,我不知道具体是什么、为什么和什么时候:)

    然而,我对问题中描述的两种方法进行了基准测试。这个 Marshal.PtrToStructure() 与传统方法相比,该方法的速度慢了约10% Marshal.GetDelegateForFunctionPointer() 方法有一个包含 IntPtr s表示所有不感兴趣的函数。

    我还比较了 Marshal.GetDelegateForFunctionPointer() struct 表示调用堆栈,将其固定在内存中,将其地址传递到本机端,在本机端我使用asm中编码的蹦床,以便调用函数使用内存区域作为其参数堆栈(这是可能的,因为 cdecl x86调用约定传递堆栈上的所有函数参数)。时间安排相当。

    3 回复  |  直到 5 年前
        1
  •  2
  •   John Knoeller    16 年前

    我不知道你的第一个问题的答案,我希望如此 Marshal.PtrToStructure() 是根据其他封送原语实现的,所以只使用单个封送原语会更有效 Marshal.GetDelegateForFunctionPointer . 但这只是一个猜测,值得你为此付出多少。

    至于你的问题2。不,没有 较少的

        2
  •  5
  •   Sam Harwell    16 年前

    这是我要开始的。

    用法:

    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);
            }
        }
    }
    
        3
  •  1
  •   Laurent Etiemble    16 年前

    Marshal.GetDelegateForFunctionPointer()

    关于第2点:

    我认为不可能采用不那么冗长的方法。您可以只为要使用的函数定义委托,而对不想使用的函数指针使用伪委托。这样,封送处理将执行ok,但您将以包含不可调用委托的结构结束。