C#P/Invoke:包含函数指针的编组结构
很抱歉下面的介绍太冗长了。我需要比我更了解P/Invoke内部结构的人的洞察力 下面是我如何将包含从C到C的函数指针的结构编组。我想知道这是否是最干净和/或最有效的方法 我正在与一个用C编写的本机DLL接口,该DLL提供以下入口点: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
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);
}
}
}