2013-11-22 59 views
3

我正在使用由.dll,.lib和.h文件組成的3. party SDK。我正在使用.dll來對付PInvoke。和.h文件來查看函數名稱和參數。 (所以我沒有使用.lib文件)。C#如何在結構中調用回調

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

我沒有訪問C代碼的權限,我只能訪問.dlls。所以我不知道什麼時候在C代碼中發生問題。但我期望它是當試圖調用我的C#函數。 –

+0

我現在有同樣的問題。真的希望有人知道了! – koby

回答

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

原因是甲方分配LH_BOOL繼續在內存中,然後我必須設置正確的值。但使用「out」意味着C#將分配該值,並且當3. party嘗試設置它時,它會嘗試設置它無權訪問的值。 「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#不會做任何限制,挑戰在於讓它正確,並且需要額外的工作。 爲了能夠得到它的正確性,它要求你調用的函數是有據可查的,所以你確切知道輸入和輸出應該如何在內存中看起來像。

0

的回調很可能是CDECL,但我會更懷疑這一點:

[DllImport("myDll.dll", EntryPoint = "CallBackFunction", CallingConvention = CallingConvention.Cdecl)] 
public static extern ERROR CallBackFunction(ref CallBackInterface callBack); 

有你是這樣試驗的(如果你不想在C++中使用不同的C++名稱,並且stdcall是默認調用約定的DllImport,和平常一個DLL導出):

[DllImport("myDll.dll")] 
public static extern ERROR CallBackFunction(ref CallBackInterface callBack); 
+0

這是C代碼,而不是C++ –

+0

這沒有什麼區別。 stdcall是Windows的東西,而不是C++的東西。 (其實,這可能是一個帕斯卡的事情,但這是古老的歷史) –

+0

但將其更改爲[DllImport(「myDll.dll」)]不會傷心。 –