Gre*_*osz 15 c# pinvoke structure function-pointers marshalling
对不起,下面是详细的介绍.我需要知道P/Invoke内部人员的洞察力比我更好.
这是我如何编组包含从C到C#的函数指针的结构.我想知道这是否是最干净和/或最有效的方式.
我正在使用C编码的本机DLL连接,它提供以下入口点:
void* getInterface(int id);
Run Code Online (Sandbox Code Playgroud)
您必须传递getInterface(int)
以下枚举值之一:
enum INTERFACES
{
FOO,
BAR
};
Run Code Online (Sandbox Code Playgroud)
它返回一个指向包含函数指针的结构的指针,如:
typedef struct IFOO
{
void (*method1)(void* self, int a, float b);
void (*method2)(void* self, int a, float b, int c);
} IFoo;
Run Code Online (Sandbox Code Playgroud)
以下是您在C中使用它的方式:
IFoo* interface = (IFoo*)getInterface(FOO);
interface->method1(obj, 0, 1.0f); // where obj is an instance of an object
// implementing the IFoo interface.
Run Code Online (Sandbox Code Playgroud)
在C#中,我有一个使用P/Invoke Library
映射getInterface(int)
入口点的类.
class Library
{
[DllImport("MyDLL"), EntryPoint="getInterface", CallingConvention=CallingConvention.Cdecl)]
public static extern IntPtr GetInterface(int id);
};
Run Code Online (Sandbox Code Playgroud)
然后我定义了:
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);
}
Run Code Online (Sandbox Code Playgroud)
我这样使用它:
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.
Run Code Online (Sandbox Code Playgroud)
我有以下问题:
映射整个结构的效率低于使用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);
}
Run Code Online (Sandbox Code Playgroud)使用时立即映射整个结构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
对于所有不感兴趣的函数,包含s 的结构.
我还比较了Marshal.GetDelegateForFunctionPointer()
我自己的滚动编组器:我对齐一个struct
表示调用堆栈,将其固定在内存中,将其地址传递给本机端,我使用asm编码的trampoline,以便调用函数使用内存区域作为参数stack(这是可能的,因为cdecl
x86调用约定传递了堆栈上的所有函数参数).时间相当.
这就是我要开始的。
用法:
IFoo foo = UnsafeNativeMethods.GetFooInterface();
foo.Method1(0, 1.0f);
Run Code Online (Sandbox Code Playgroud)
执行:
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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
我不知道你的问题 1 的答案。我希望这Marshal.PtrToStructure()
是根据其他 Marshal 原语实现的,因此仅使用单个Marshal.GetDelegateForFunctionPointer
. 但这只是一个猜测——值得你为此付出的代价。
至于你的问题2。不,没有更简洁的方法可以做到这一点。有一种更详细的方法。您可以使用旧式 MIDL 编译器为您的 dll 构建类型库并加载该类型库。但是 MIDL 的可用封送选项比您在 C# 中描述的要有限得多。而且 MIDL 编译器很难使用,您可能最终不得不编写另一个非托管 DLL 来在托管代码和目标 DLL 之间进行互操作。
归档时间: |
|
查看次数: |
9790 次 |
最近记录: |