代表问题
我这样做是为了从 C 代码调用非托管函数。 pCallback 是一个函数指针,因此在托管端是一个委托。
[DllImport("MyDLL.dll")]
public static extern Result SetCallback(
IntPtr handle,
Delegate pCallback,
CallbackType Type);
现在我正在设置
public delegate void pfnCallback(uint PromptID, ttsEventType evt, IntPtr lData);
public Form1()
{
pfnCallback cb = new pfnCallback(cback);
(...)
Wrapper.SetCallback(handle, cb, IntPtr.Zero, CallBackType.DEFAULT);
(...)
}
它给我一个错误,说“...当将委托传递给非托管代码时,它们必须保持活动状态...”
任何人都可以帮助我吗?
问候
I've made this to call unmanaged function from C code. pCallback is a function pointer so on the managed side is a delegate.
[DllImport("MyDLL.dll")]
public static extern Result SetCallback(
IntPtr handle,
Delegate pCallback,
CallbackType Type);
Now I am setting
public delegate void pfnCallback(uint PromptID, ttsEventType evt, IntPtr lData);
public Form1()
{
pfnCallback cb = new pfnCallback(cback);
(...)
Wrapper.SetCallback(handle, cb, IntPtr.Zero, CallBackType.DEFAULT);
(...)
}
It gives my an error saying "... when passing delegates to unmanaged code, they must be kept alive ... "
Can anyone help me ?
Regards
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
据我理解该消息,你应该这样做:
As far as I understand the message, you should do it like this:
我相信您需要扩展 cb 的范围,以确保该变量将继续存在,并在非托管代码可能想要调用它时引用回调函数。非托管代码不参与 .NET 引用跟踪,因此如果您不强制在 .NET 代码中保留对回调的引用,它将被框架释放,并且非托管代码将无法在之后正确调用它那。
I believe you need to expand the scope of cb to ensure that that variable will continue to exist and refer to the callback function for as long as the un-managed code might want to call it. Unmanaged code doesn't participate in .NET reference tracking so if you don't force a reference to the callback to be retained in .NET code, it will be freed by the framework and the unmanaged code would be unable to properly call it after that.
您将委托类型声明与委托实例混淆了。是的,你公开了你的代表声明,这是错误的。只有 Form1 类使用它,它应该是私有的。这里重要的是委托的实例,即您使用 new 语句创建的实例。
现在,您将该实例存储在 Form1 构造函数的局部变量中。这会将实例的引用保留几微秒。一旦构造函数完成,该引用就会消失,垃圾收集器可以在此后的任何时候收集委托实例。它无法看到非托管代码保留对其的引用,收集器只能发现托管代码保留的引用。
当非托管代码在收集的委托实例上调用回调时,不会发生任何好事,您会听到一声响亮的轰鸣声。您必须更改代码,以便有对该实例的托管引用。执行此操作的一种简单方法是向 Form1 类添加一个私有成员来存储该实例。
即使这可能还不够好,Form1 对象也会在将来的某个时候被垃圾回收。它还收集委托对象。您还必须确保非托管代码在此之后无法使用回调。鉴于类的名称 (Form1),在这种特定情况下不太可能发生这种情况。但要进行防御性编码,并进行调用以重置表单的 FormClosing 事件处理程序中的回调。
You are confusing the delegate type declaration with the instance of the delegate. Yes, you made your delegate declaration public, that was wrong. Only the Form1 class uses it, it should be private. It is the instance of the delegate that matters here, the one you created with the new statement.
Right now, you are storing the instance in a local variable of the Form1 constructor. That keeps a reference on the instance for a few microseconds. As soon as the constructor completes, that reference is gone and the garbage collector can collect the delegate instance at any point after that. It cannot see that the unmanaged code keeps a reference to it, the collector can only discover references held by managed code.
Nothing good happens when the unmanaged code calls the callback on a collected delegate instance, you'll hear a loud kaboom. You must change your code so that there will be a managed reference to the instance. One easy way to do this is to add a private member to the Form1 class to store the instance.
Even that might not be good enough, the Form1 object will be garbage collected at some point in the future as well. Which collects the delegate object as well. You also must make sure that the unmanaged code cannot use the callback after that happens. Given the name of the class (Form1), that isn't that likely to happen in this specific case. But code defensively and make a call that resets the callback in the form's FormClosing event handler.
全局尝试回调变量
cb
Try the callback variable
cb
globally