从非托管C++回调C#很棘手.我从这篇MSDN文章 和这个stackoverflow技巧中了解了大部分所需的 内容, 结果在调试器中运行良好.但是在调试器之外它失败了"对象引用没有设置为对象的实例".
这是(简化的)C#代码:
class CSharpCode
{
delegate void CallbackDelegate();
void DoCSharp()
{
CallbackDelegate callbackDelegate = TheCallback;
IntPtr callbackDelegatePointer = Marshal.GetFunctionPointerForDelegate(callbackDelegate);
GCHandle gchCallbackDelegate = GCHandle.Alloc(callbackDelegatePointer);
GC.Collect(); // create max space for unmanaged allocations
CppCliCode.DoCppCli(callbackDelegatePointer);
}
public static void TheCallback()
{
MessageBox.Show("It worked");
}
}
Run Code Online (Sandbox Code Playgroud)
这是C++代码:
#pragma managed
public ref class CppCliCode
{
static void DoCppCli(IntPtr^ callbackDelegatePointer)
{
callback theCallback = static_cast<callback>(callbackDelegatePointer->ToPointer());
DoCpp(theCallback);
}
}
#pragma unmanaged
typedef void (__stdcall *callback)();
void DoCpp(callback theCallback)
{
theCallback();
}
Run Code Online (Sandbox Code Playgroud)
错误发生在调用theCallback()和到达之间TheCallback().该错误表明某些不可见的托管对象已成为null.
如果我删除GC.Collect(),问题就会消失.但这只是意味着它有一天会再次出现,因为GC恰好在错误的时刻发生.
GCHandle保护代表不被收集,但允许重新定位.MSDN文章说"如果一个委托由垃圾收集重新定位,它不会影响底层托管回调,因此Alloc用于添加对委托的引用,允许重定位委托,但防止丢弃.使用GCHandle而不是pin_ptr减少了托管堆的碎片潜力."
怎么了?
您必须分配委托本身,而不是它IntPtr.此外,您必须在GCHandle完成CSharpCode实例时释放.
class CSharpCode : IDisposible
{
delegate void CallbackDelegate();
GCHandle gchCallbackDelegate;
void DoCSharp()
{
CallbackDelegate callbackDelegate = TheCallback;
IntPtr callbackDelegatePointer = Marshal.GetFunctionPointerForDelegate(callbackDelegate);
gchCallbackDelegate = GCHandle.Alloc(callbackDelegate); // !!!!
GC.Collect(); // create max space for unmanaged allocations
CppCliCode.DoCppCli(callbackDelegatePointer);
}
public void Dispose()
{
CleanUp();
}
~CSharpCode()
{
CleanUp();
}
CleanUp()
{
if(gchCallbackDelegate.IsAllocated)
gchCallbackDelegate.Free();
}
}
Run Code Online (Sandbox Code Playgroud)
顺便说一句,我希望你有更强大的命名系统.如姓名DoCSharp,TheCallBack,theCallBack等给了我一个很难理解的问题.
| 归档时间: |
|
| 查看次数: |
4655 次 |
| 最近记录: |