从.NET向C代码传递回调,代码是否有效?
对于一个相当大的c API,我们有一个.NET(c#)包装器。在这个包装器中,用户可以提供从本机代码重复调用的回调 回调如下所示:从.NET向C代码传递回调,代码是否有效?,.net,callback,pinvoke,marshalling,.net,Callback,Pinvoke,Marshalling,对于一个相当大的c API,我们有一个.NET(c#)包装器。在这个包装器中,用户可以提供从本机代码重复调用的回调 回调如下所示: [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] public delegate void LogCallBack(SlmStream str, string wtsr, IntPtr handle); 用户可以通过以下方式输入回调: public vo
[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);
一个用户以以下方式调用回调例程(简化):
这会导致应用程序在某些平台上崩溃,而不是在其他平台上崩溃
所以我的问题是,这是有效的吗
来自C/C++世界,有一件事让我困惑:
访问在回调范围外创建的类的实例(即“string temp”)的代码是否有效?我的意思是,我的猜测是编组需要将它们作为输入/输出参数来控制它。我考虑过使用handle参数进行尝试,但不确定这是否是过度使用
更新1:
也许这就是我需要的
更新2:
从本机代码ala调用回调:
if( logcallback_ != NULL )
{
(logcallback_)(cstream_,chbuf_,logcallbackhandle_);
}
更新3:
与
它给出了同样的崩溃
更新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使委托保持活动状态,这样它就不会被垃圾收集。让我们看看您的委托:
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)]
public delegate void LogCallBack(SlmStream str, string wtsr, IntPtr handle);
首先,我不知道什么是SlmStream
,所以我不能评论它是如何封送的。这当然是一个可能的失败向量
IntPtr
句柄不显示任何问题。可能在本机端是某种类型的指针,可能是void*
代码最明显的问题是字符串参数,wtsr
。封送拆收器假定您将向以null结尾的宽字符数组传递指针。用本机术语,即wchar\u t*
。但是,封送员也负责销毁本机内存。它假设内存是从COM堆分配的,因此调用CoTaskMemFree
我认为您的本机代码可能没有在COM堆上分配以null结尾的字符数组。这当然可以解释某些平台上的崩溃,但不能解释其他平台上的崩溃
以下是一些解决问题的方法:
IntPtr
,并调用委托中的Marshal.PtrToStringUni
,以转换为托管字符串您的评论告诉我,选项2是正确的解决方案。您的代表应该是:
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)]
public delegate void LogCallBack(SlmStream str, IntPtr wtsr, IntPtr handle);
并按如下方式实施:
(str, wtsr, handle) => { Console.WriteLine(Marshal.PtrToStringUni(wtsr)); }
您显然必须做的另一件事是确保委托保持活动状态,以便在本机代码调用它时它仍然存在。让我们看看您的委托:
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)]
public delegate void LogCallBack(SlmStream str, string wtsr, IntPtr handle);
首先,我不知道什么是SlmStream
,所以我不能评论它是如何封送的。这当然是一个可能的失败向量
IntPtr
句柄不显示任何问题。可能在本机端是某种类型的指针,可能是void*
代码最明显的问题是字符串参数,wtsr
。封送拆收器假定您将向以null结尾的宽字符数组传递指针。用本机术语,即wchar\u t*
。但是,封送员也负责销毁本机内存。它假设内存是从COM堆分配的,因此调用CoTaskMemFree
我认为您的本机代码可能没有在COM堆上分配以null结尾的字符数组。这当然可以解释某些平台上的崩溃,但不能解释其他平台上的崩溃
以下是一些解决问题的方法:
IntPtr
,并调用委托中的Marshal.PtrToStringUni
,以转换为托管字符串您的评论告诉我,选项2是正确的解决方案。您的代表应该是:
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)]
public delegate void LogCallBack(SlmStream str, IntPtr wtsr, IntPtr handle);
并按如下方式实施:
(str, wtsr, handle) => { Console.WriteLine(Marshal.PtrToStringUni(wtsr)); }
您显然必须做的另一件事是确保委托保持活动状态,以便在本机代码调用它时它仍然存在。谢谢。我不知道封送员实际上也会释放“字符串wtsr”,这当然不是我想要的。这是一个WCHAR数组,但在本机中分配并在那里释放。顺便说一句:SlmStream只是一个简单的枚举,没有危险。谢谢,非常感谢!显示
logcallback\的本机声明
。此外,我看不到任何证据表明您更换了您的代表。你能不能用一个实函数而不是lambda来证明你真的做到了我的要求。更新了问题以包含声明。我不能改变lambda,直到在几个小时内,因为它的共同工作与另一个,谁现在不在这里。谢谢你的帮助,我稍后会回来的。谢谢。我不知道封送员实际上也会释放“字符串wtsr”,这当然不是我想要的。这是一个WCHAR数组,但在本机中分配并在那里释放。顺便说一句:SlmStream只是一个简单的枚举,没有危险。谢谢,非常感谢!显示logcallback\的本机声明
。此外,我看不到任何证据表明您更换了您的代表。你能不能用一个实函数而不是lambda来证明你真的做到了我的要求。更新了问题以包含声明。我不能改变lambda,直到在几个小时内,因为它的共同工作与另一个,谁现在不在这里。谢谢你的帮助,我稍后会回来的。