【C#】对“xxxx::Invoke”类型的已垃圾回收委托进行了回调。这可能会导致应用程序崩溃、损坏和数据丢失。向非托管代码传递委托时,托管应用程序必须让这些委托保持活动状态,直到确信不会再次调用。

来源:互联网 发布:杭州淘宝城附近楼盘 编辑:程序博客网 时间:2024/05/10 18:45

最近在做项目的时候,出现了几次“对“xxxx::Invoke”类型的已垃圾回收委托进行了回调。这可能会导致应用程序崩溃、损坏和数据丢失。向非托管代码传递委托时,托管应用程序必须让这些委托保持活动状态,直到确信不会再次调用它们。”异常。

这种错误,一般出现在托管代码调用非托管代码的过程中,如C#程序要调用c++的某个函数,而这个函数正好有个callback函数。当C#调用完之后,GC有可能会把这个callback给回收掉,如果C++接下来的某个函数中也同样使用了这个callback,就会抛出上述异常。因为C++是非托管的,GC又不知道它接下来还要使用callback所以就把它回收掉了。

这个时候,微软就建议我们使用GC.KeepAlive()方法解决,参考官网例子

KeepAlive 方法的目的是确保对对象的引用存在,该对象有被垃圾回收器过早回收的危险。这种现象可能发生的一种常见情形是,当在托管代码或数据中已没有对该对象的引用,但该对象仍然在非托管代码(如 Win32 API、非托管 DLL 或使用 COM 的方法)中使用。
另一种过早发生垃圾回收的情形是,在一个方法中创建并使用一个对象。此时,当对对象的某个成员的调用仍在执行时,可能会对该对象进行回收,如第一个代码示例所示。
此方法引用 obj,从而使该对象从例程开始到调用此方法的那个位置(按执行顺序)均不符合进行垃圾回收的条件。在 obj 必须可用的指令范围的结尾(而不是开头)编写此方法的代码。
KeepAlive 方法除了延长作为参数传递的对象的生存期之外,不会执行任何操作,也会不产生任何其他副作用。

MSDN上有官方例子可以参考下。我在这里贴出我的代码:

private InteropHelper.MKDriverAcceptGatewayConnectCallback connectCallback;private InteropHelper.MKDriverAcceptGatewayErrorCallback errorCallback;        public void cbMKDriverAcceptGatewayConnect(IntPtr dref, IntPtr sref, IntPtr addr, int socklen)        {         //...         }        public void cbMKDriverAcceptGatewayErrorCallback(IntPtr dref, int error)        {            //...        }        private void XtraForm1_Load(object sender, EventArgs e)        {            connectCallback = new InteropHelper.MKDriverAcceptGatewayConnectCallback(cbMKDriverAcceptGatewayConnect);            errorCallback = new InteropHelper.MKDriverAcceptGatewayErrorCallback(cbMKDriverAcceptGatewayErrorCallback);            drvRef = InteropHelper.MKDriverCreate();            if (drvRef == IntPtr.Zero)            {                MessageBox.Show("加载接口失败");            }            else            {                InteropHelper.MKDriverSetCb(drvRef, connectCallback, errorCallback);                GC.KeepAlive(connectCallback);//关键的两句                GC.KeepAlive(errorCallback);//关键的两句                //如果没有上面的这两句KeepAlive,MKDriverSetCb调用完之后,两个callback就被GC.Collect掉了,以后如果还有调用的话就会抛异常。                timer1.Enabled = true;            }        }        private void timer1_Tick(object sender, EventArgs e)        {            //间隔10ms            if (drvRef != IntPtr.Zero)            {                if (InteropHelper.MKDriverDispatch(drvRef, 0, 0) != 0)                {                    //就是因为上面Load方法内两句GC.KeepAlive没有加,所以在这里会不定时抛出异常。谁知道搞C++的那帮人在MKDriverDispatch里也调用了callback。                    GMessageBox.Wrong("接口授权过期");                    return;                }

欢迎大家,评论交流。

1 0