C#如何在结构中进行Pinvoke回调

时间:2013-11-22 14:29:20

标签: c# pinvoke

我正在使用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...
 }

2 个答案:

答案 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#不会做出任何限制,挑战是要使其正确并且需要额外的工作。 为了能够使它正确,它需要您调用的函数有详细记录,因此您确切地知道输入和输出在内存中应该是什么样子。