簡體   English   中英

C#P / Invoke:包含函數指針的編組結構

[英]C# P/Invoke: Marshalling structures containing function pointers

對不起,下面是詳細的介紹。 我需要知道P / Invoke內部人員的洞察力比我更好。

這是我如何編組包含從C到C#的函數指針的結構。 我想知道這是否是最干凈和/或最有效的方式。

我正在使用C編碼的本機DLL連接,它提供以下入口點:

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.

在C#中,我有一個Library類,它使用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.

我有以下問題:

  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)入口點獲得的實例。


EDIT2:方法1)的一個優點是Marshal.GetDelegateForFunctionPointer()僅從.NET Framework 2.0開始提供。 但是, Marshal.PrtToStructure()一直可用。 也就是說,我不確定現在是否值得確保1.0兼容性。


編輯3:我嘗試使用Reflector檢查生成的代碼,但它沒有提供太多信息,因為所有有趣的細節都在輔助函數中完成,如PtrToStructureHelper ,並且沒有公開。 然后,即使我能看到在框架內部完成了什么,那么運行時也有機會優化事物,我不確切知道什么,為什么以及何時:)

但是,我對我的問題中描述的兩種方法進行了基准測試。 Marshal.PtrToStructure()方法相比, Marshal.GetDelegateForFunctionPointer()方法的速度降低了約10%; 對於所有不感興趣的函數,包含IntPtr的結構。

我還將Marshal.GetDelegateForFunctionPointer()與我自己的滾動編組器進行了比較:我對齊表示調用堆棧的struct ,將其固定在內存中,將其地址傳遞給本機端,我使用asm編碼的trampoline,以便調用函數使用內存區域作為其參數堆棧(這是可能的,因為cdecl x86調用約定傳遞堆棧上的所有函數參數)。 時間相當。

這就是我要開始的。

用法:

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.PtrToStructure()是根據其他Marshal原語實現的,所以使用單個Marshal.GetDelegateForFunctionPointer會更有效率。 但這只是一個猜測 - 值得你付出的代價。

至於你的問題2.沒有,有沒有做到這一點簡潔的方式。 有一種更冗長的方式。 您可以使用舊式MIDL編譯器為您的dll和類型庫的加載構建類型庫。 但是,MIDL的可用編組選項比C#中描述的更有限。 並且MIDL編譯器非常難以使用,您可能最終必須編寫另一個非托管DLL來執行托管代碼和目標dll之間的互操作。

對於第1點:

Marshal.GetDelegateForFunctionPointer()更簡單,如果你的結構包含很多函數指針,你只使用幾個。 一個(主要)缺點是您必須手動計算函數指針的偏移量(請注意,指針大小在32/64位平台上不同)。 結構更易於使用,但可以編組更多數據。

對於第2點:

我認為不可能采用不那么冗長的方法。 您只能為要使用的函數定義委托,並對您不想使用的函數指針使用偽委托。 這樣,編組將執行正常,但您以包含不可調用的委托的結構結束。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM