2014-12-05 47 views
1

我將不勝感激,幫助解決一個問題,我一直堅持了幾天。C#pInvoke到一個C函數

我有一個本地C++函數類型中聲明這樣:

typedef STATUS (T_TED_AcppBoxDYN_RegisterEventCallback) (
     PEventCallback function, // pointer to the customer callback 
     PVOID param    // custom data (returned in callback) 
     ); 

其中PEventCallbackPEVENT聲明像這樣:

typedef int (*PEventCallback) (PEVENT event, PVOID param); 

typedef struct 
{ 
    int nEventId; 
    void* pParam; 
} EVENT,*PEVENT; 

的C++代碼提供一個指針作爲該類型的功能一個全局變量:

T_TED_AcppBoxDYN_RegisterEventCallback* TED_AcppBoxDYN_RegisterEventCallback 
     = NULL; 

這是init稍後通過此代碼ialized:

#ifdef LOAD_PROC_ADDRESS 
#undef LOAD_PROC_ADDRESS 
#endif 
#define LOAD_PROC_ADDRESS(handle,func) \ 
    if((func=(T_##func*)GetProcAddress(handle,#func))==NULL) \ 
    {\ 
     sMsg.Format("Error occurs while loading entry point\n'%s'\n"\ 
        "from detector DLL '%s'\n", GetName(), #func);\ 
     MessageBox(NULL, sMsg.GetBuffer(), "Load Proc Error", MB_OK | MB_ICONSTOP);\ 
     return (false);\ 
    } 

bool P5100EDllManager::LoadProc() 
{ 
    CString sMsg; 

    HMODULE hDllHandle = GetHandle(); 
    if (hDllHandle == NULL) 
    { 
     return false; // cannot load the proc if the dll has not been loaded 
    } 

    LOAD_PROC_ADDRESS(hDllHandle, TED_AcppBoxDYN_RegisterEventCallback); 
    return true; 
} 

我想調用C#中的指向函數。爲此,我已經定義了一個C#包裝:

public delegate void TDICallBack(IntPtr callbackEvent, IntPtr pParam); 
[DllImport(DLL, EntryPoint = "TED_AcppBoxDYN_RegisterEventCallback", CallingConvention = CallingConvention.Cdecl)] 
private static extern int TED_AcppBoxDYN_RegisterEventCallback(TDICallBack callBack, IntPtr param); 
public void RegisterEventCallback(TDICallBack callBack, IntPtr param) 
{ 
    TED_AcppBoxDYN_RegisterEventCallback(callBack, param); 
} 

我使用它是這樣的:

TdiapiFacade.RegisterEventCallback(OnTdiCallBack, IntPtr.Zero); 

public void OnTdiCallBack(IntPtr ptr, IntPtr param) 
{ 
} 

RegisterEventCallback()似乎順利進行,但在回調函數應該點被調用,應用程序崩潰。正如你所看到的,在這個階段,我甚至沒有解開提供給回調函數的參數。

我需要做些什麼來完成這項工作?

+0

與您的斷言相反,C代碼不聲明任何函數。如果它是有效的C(這對我和CDECL來說似乎很懷疑),那麼它將'T_TED_AcppBoxDYN_RegisterEventCallback'聲明爲一個*別名*,用於函數*類型*。也許它是有效的C++,但即使在這種情況下,它也必須聲明一個類型別名,而不是該類型的實際函數或函數指針。 – 2014-12-05 16:29:11

+0

如何定義'STATUS'? – Dennis 2014-12-05 16:47:10

+0

您不能直接從C#調用非託管C++實例函數。因爲你沒有'this'指針。另外,你的'TDICallback'委託必須是Cdecl。你需要'[UnmanagedFunctionPointer(CallingConvention.Cdecl)]'屬性。請參閱http://stackoverflow.com/questions/5155180/changing-a-c-sharp-delegates-calling-convention-to-cdecl – 2014-12-05 16:50:10

回答

2

P/invoke不允許訪問導出的數據(變量),例如函數指針。你需要在DLL裏面有一個幫助器,或者是一個包裝函數指針的導出函數,或者是一個返回它的函數(返回類型將在C#中被看作是一個委託)。

0

感謝您的輸入

我已經解決了這個問題,通過修改我的回調登記:

void RegisterCB() 
{ 
    var ret = TdiapiFacade.RegisterEventCallback(OnTdiCallBack, new IntPtr()); 
    HandleError(ret); 
} 

這樣:

private TDIAPI.TDICallBack callback; 
void RegisterCB() 
{ 
    callback = new TDIAPI.TDICallBack(OnTdiCallBack); 
    var ret = TdiapiFacade.RegisterEventCallback(callback , new IntPtr()); 
    HandleError(ret); 
} 

此修復該問題,現在我的回調被正確調用。