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

问题描述:

对不起,后面的详细介绍。我需要的洞察力从别人知道比我更好的P / Invoke的内部结构。

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

下面是我如何编组包含函数指针从C到C#结构。我想知道它是否是做它的清洁和/或最有效的方式。

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.

我在一机DLL在C语言编写的,它提供了以下条目接口点:

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

void* getInterface(int id);

您必须通过 getInterface(INT)以下枚举值之一:

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;

和这里是你如何用C使用它:

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.

在C#我有一个图书馆类映射 getInterface(INT)使用入口点的p / Invoke。

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

和我使用这种方式:

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. 是映射整个结构比使用 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);
}


  • 在映射整个结构一次使用 Marshal.PtrToStructure(),有没有比我所描述的更简洁的方法吗?我的意思是不必定义的委托类型,每方法等那么繁琐?

  • 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?

    编辑:为了清楚和完整起见,在上面的代码片段, OBJ 无效*的CreateObject(获得一个实例。整型)入口点


    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:方法的一个优点1)是 Marshal.GetDelegateForFunctionPointer()只能从.NET框架2.0开始。然而, Marshal.PrtToStructure()一直可用。那就是说,我不知道它的价值,确保1.0兼容时下

    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:我试图检查生成的代码使用反射但它并没有提供多少信息,因为所有有趣的细节在辅助函数做这样 PtrToStructureHelper 并没有暴露。这样,即使我能看到什么在框架内部完成,则运行时有机会,优化东西拿走,我不知道到底是什么,为什么以及何时:)

    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 :)

    不过,我在基准测试我的问题描述的两种方法。在 Marshal.PtrToStructure()办法是由一个因素慢10%左右相比, Marshal.GetDelegateForFunctionPointer()办法;那蒙山含的IntPtr 献给所有那些不感兴趣的功能结构。

    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.

    我也比较 Marshal.GetDelegateForFunctionPointer()用我自己的滚动编组:我对齐结构表示调用堆栈,用别针把它在内存中,它的地址传递给本机端,我用一个蹦床在ASM编码,以便在调用函数使用的内存区域作为参数堆栈(这是可能的,因为 CDECL 86主叫惯例通过堆栈上的所有功能参数)。时序分别相当于

    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.

  • 我不知道回答你的问题1,我想到的是 Marshal.PtrToStructure()中的其他元帅基元来实现的,所以这将是更有效地只使用单一的 Marshal.GetDelegateForFunctionPointer 。但是,这只是一个猜测 - 值得你为它付出什么

    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. But that's just a guess - worth what you paid for it.

    至于你的问题2.没有,没有的的详细办法做这个。还有一个更详细的方法。您可以使用老式MIDL编译器来构建一个类型库为您的DLL和负载的类型库。但对于MIDL可用编组选项是相当多的限制,使得你可以在C#描述。而MIDL compler是相当困难的工作,你可能会最终不得不编写另一个非托管的DLL做托管代码和目标DLL之间的互操作。

    As for your question 2. No, there is no less verbose way to do this. 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. But the available marshaling options for MIDL are quite a bit more limited that what you can describe in 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.