我們有一個.NET(c#)封裝器,用於一個相當大的C API。在這個包裝器中,用戶可以提供從本機代碼重複調用的回調。將回調從.NET傳遞給C代碼,有效的代碼或不是?
回調看起來是這樣的:
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)]
public delegate void LogCallBack(SlmStream str, string wtsr, IntPtr handle);
用戶可以輸入通過回調:
public void SetLoggingCallback(LogCallBack lcallback,
IntPtr handle)
{
SlmReturn ret = (SlmReturn)Native.SlmSetLoggingCallbackW(ModelPtr,
lcallback,
handle);
if(ret != SlmReturn.SlmRetOk)
{
throw new SlmException(ret,ret.ToString());
}
}
其最終調用:
[DllImport("sulum20.dll",CallingConvention = CallingConvention.StdCall , CharSet = CharSet.Unicode)]
public static extern int SlmSetLoggingCallbackW(IntPtr ModelPtr,
LogCallBack lcallback,
IntPtr handle);
一個用戶調用的回調例程以下列方式(簡化):
string temp;
SetLoggingCallback((str, wtsr, handle) => { temp = wtsr; Console.WriteLine(temp); }, IntPtr.Zero);
這會導致應用程序在某些平臺上崩潰而在其他平臺上崩潰。
所以我的問題仍然存在,這是否有效?
從C/C++世界的到來,有一兩件事讓我爲難:
它是有效的代碼訪問的回調(即「字符串TEMP」)的範圍之外創建類的實例?我的意思是我的猜測是編組需要將它們作爲輸入/輸出參數來控制它。我考慮通過使用句柄參數來嘗試marshalling on my own,但不確定這是否過度殺傷。
更新1:
也許這正是我需要的GCHandle?
更新2:
回調從本機代碼ALA稱爲:
if(logcallback_ != NULL)
{
(logcallback_)(cstream_,chbuf_,logcallbackhandle_);
}
更新3:
隨着
string temp;
LogCallBack logCallback = (str, wtsr, handle) =>
{
temp = "Hello";
};
smodel.SetLoggingCallback(logCallback, IntPtr.Zero);
它給出了同樣的崩潰。
UPDATE 4:
typedef void (ISLMCALL *SlmLogCallBackW)(enum SlmStream,const wchar_t*, void *handle);
UPDATE 5
還試圖與失敗:
var logCallback = new LogCallBack(TargetMethod);
smodel.SetLoggingCallback(logCallback, IntPtr.Zero);
private string _test;
private void TargetMethod(SlmStream str, IntPtr wtsr, IntPtr handle)
{
_test = "Hello";
}
SOLUTION:
使用GCHandler保持委託活着,所以它不是垃圾收集。
謝謝。我不知道編組實際上也會釋放「string wtsr」,這當然不是我想要的。這是一組wchars,但分配在本地並在那裏釋放。 –
順便說一句:SlmStream只是一個簡單的枚舉,沒有危險。 –
好的,我更新了我認爲合適的解決方案 –