我正在使用3. party SDK,它由.dll,.lib和.h文件组成。我正在使用.dll来对付PInvoke。和.h文件一起查看函数名称和参数。 (所以我没有使用.lib文件)。
SDK相当复杂,因此制作PInvoke包装器已被证明是一项挑战。所有函数/结构/枚举都在.h文件中定义。
我正在解析非托管C代码的结构,并且该结构包含2个委托,非托管C代码调用它们。
我在C#中创建结构,并且两个委托都在C#中设置。
当我调用它时,我得到一个'System.AccessViolationException'。
使用
//C#
private CallBackInterface callBack;
public void MyMethod()
{
callBack = new CallBackInterface ();
callBack.event1 = new CallBackInterface.event1_delegate(event1_Handler);
callBack.event2 = new CallBackInterface.event2_delegate(event2_Handler);
CallBackFunction(ref callBack); //Throws a 'System.AccessViolationException'
}
public int event1_Handler(IntPtr Inst, uint type, uint timeMs)
{
Console.WriteLine("Got a callback on event 1!");
return 0;
}
public int event2_Handler(IntPtr Inst, out LH_BOOL Continue)
{
Console.WriteLine("Got a callback on event 2!");
Continue = LH_BOOL.TRUE;
return 0;
}
功能:CallBackFunction
//C
ERROR CallBackFunction(CallBackInterface * callBack);
//C#
[DllImport("myDll.dll", EntryPoint = "CallBackFunction", CallingConvention = CallingConvention.Cdecl)]
public static extern ERROR CallBackFunction(ref CallBackInterface callBack);
结构:CallBackInterface
//C
typedef unsigned long LH_TIME;
typedef struct CallBackInterface_S{
int (*event1) (void* inst, unsigned long type, LH_TIME timeMs);
int (*event2) (void* inst, LH_BOOL* Continue); //continue should be set to tell the unmanaged c code if it should continue or stop.
} CallBackInterface;
//C#
[StructLayout(LayoutKind.Sequential)]
public struct CallBackInterface
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int event1_delegate(IntPtr inst, uint type, uint timeMs);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int event2_delegate(IntPtr inst, out LH_BOOL Continue);
public event1_delegate event1;
public event2_delegate event2;
}
枚举:LH_BOOL
//C Enum: LH_BOOL
typedef enum LH_BOOL_E {
FALSE= 0,
TRUE = 1,
} LH_BOOL;
//C# Enum: LH_BOOL
public enum LH_BOOL
{
FALSE= 0,
TRUE = 1,
}
枚举:错误
//C Enum: ERROR
typedef enum ERROR_E {
OK = 0, //Everything is ok
E_ARG = 1, //Error in the Arguments
E_DATA = 2 //Data error
//And more...
} ERROR;
//C# Enum: ERROR
public enum ERROR
{
OK = 0, //Everything is ok
E_ARG = 1, //Error in the Arguments
E_DATA = 2 //Data error
//And more...
}
答案 0 :(得分:0)
回调可能是cdecl,但我对此更加怀疑:
[DllImport("myDll.dll", EntryPoint = "CallBackFunction", CallingConvention = CallingConvention.Cdecl)]
public static extern ERROR CallBackFunction(ref CallBackInterface callBack);
你有没有这样尝试过(如果你没有尝试在C ++中使用与C#不同的名称,那么EntryPoint是多余的,而stdcall是DllImport的默认调用约定,通常用于DLL导出): / p>
[DllImport("myDll.dll")]
public static extern ERROR CallBackFunction(ref CallBackInterface callBack);
答案 1 :(得分:0)
anwser是,我的CallBackInterface出了问题,我有:
public delegate int event2_delegate(IntPtr inst, out LH_BOOL Continue);
这应该是
public delegate int event2_delegate(IntPtr inst, ref LH_BOOL Continue);
原因是3. party在内存中分配LH_BOOL Continue,然后我必须为其设置正确的值。但是使用" out"表示C#将分配该值,当3.方尝试设置它时,它会尝试设置一个它无权访问的值。 " REF"通过允许将现有值作为参数传递来解决此问题。
所以最终的代码如下:
使用强>
//C#
private CallBackInterface callBack;
public void MyMethod()
{
callBack = new CallBackInterface ();
callBack.event1 = new CallBackInterface.event1_delegate(event1_Handler);
callBack.event2 = new CallBackInterface.event2_delegate(event2_Handler);
CallBackFunction(ref callBack);
}
public int event1_Handler(IntPtr Inst, uint type, uint timeMs)
{
Console.WriteLine("Got a callback on event 1!");
return 0;
}
public int event2_Handler(IntPtr Inst, out LH_BOOL Continue)
{
Console.WriteLine("Got a callback on event 2!");
Continue = LH_BOOL.TRUE;
return 0;
}
功能:CallBackFunction
//C
ERROR CallBackFunction(CallBackInterface * callBack);
//C#
[DllImport("myDll.dll", EntryPoint = "CallBackFunction", CallingConvention = CallingConvention.Cdecl)]
public static extern ERROR CallBackFunction(ref CallBackInterface callBack);
结构:CallBackInterface
//C
typedef unsigned long LH_TIME;
typedef struct CallBackInterface_S{
int (*event1) (void* inst, unsigned long type, LH_TIME timeMs);
int (*event2) (void* inst, LH_BOOL* Continue); //continue should be set to tell the unmanaged c code if it should continue or stop.
} CallBackInterface;
//C#
[StructLayout(LayoutKind.Sequential)]
public struct CallBackInterface
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int event1_delegate(IntPtr inst, uint type, uint timeMs);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int event2_delegate(IntPtr inst, ref LH_BOOL Continue);
public event1_delegate event1;
public event2_delegate event2;
}
枚举:LH_BOOL
//C Enum: LH_BOOL
typedef enum LH_BOOL_E {
FALSE= 0,
TRUE = 1,
} LH_BOOL;
//C# Enum: LH_BOOL
public enum LH_BOOL
{
FALSE= 0,
TRUE = 1,
}
枚举:错误
//C Enum: ERROR
typedef enum ERROR_E {
OK = 0, //Everything is ok
E_ARG = 1, //Error in the Arguments
E_DATA = 2 //Data error
//And more...
} ERROR;
//C# Enum: ERROR
public enum ERROR
{
OK = 0, //Everything is ok
E_ARG = 1, //Error in the Arguments
E_DATA = 2 //Data error
//And more...
}
在使用PInvoke时,我很高兴使用IntPtr作为函数的输入和输出。好处是C#不会做出任何限制,挑战是要使其正确并且需要额外的工作。 为了能够使它正确,它需要您调用的函数有详细记录,因此您确切地知道输入和输出在内存中应该是什么样子。