我们有一个用于相当大的 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++ 世界,一件事让我感到困惑:
访问在回调范围之外创建的类的实例是否有效代码(即“字符串临时”)?我的意思是我的猜测是编组需要将它们作为输入/输出参数才能控制它。我考虑过使用句柄参数自己尝试编组,但不确定这是否过大。
更新 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);
它给出了同样的崩溃。
更新 4:
typedef void (ISLMCALL *SlmLogCallBackW)(enum SlmStream,const wchar_t*, void *handle);
更新 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";
}
解决方案 :
使用 GCHandler 使委托保持活动状态,这样它就不会被垃圾收集。