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

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

前言:最近在做c#的项目中,出现了下图异常,有异常信息看出,是callback回调函数处引发的异常

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

托管和非托管代码的区别

  • 托管的代码就是把有关底层的操作(比如内存申请,内存释放,垃圾回收等)封装起来了,不能直接进行内存的读取释放之类的和硬件相关的操作。

    优点:安全,不会出现内存泄漏之类问题。

    缺点:不能直接读取内存,性能上有损失,使用不灵活。

  • 非托管的代码刚好相反,可以直接直接进行内存的读取释放之类的和硬件相关的操作。

    ​ 优点:性能高,对开发人员的要求也高。

    ​ 缺点:容易出现内存管理的问题。

    例子:最直观的就是c#不推荐使用指针,而c++就可以使用指针来直接读取内存; c#使用垃圾回收,c++要手动的释放对象……

    异常分析

    通过对托管和非托管代码的理解,想必对此异常有一点眉目了。

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

    解决

    微软建议我们使用GC.KeepAlive()方法解决,官网例子

    KeepAlive 方法的目的是确保对对象的引用存在,该对象有被垃圾回收器过早回收的危险。这种现象可能发生的一种常见情形是,当在托管代码或数据中已没有对该对象的引用,但该对象仍然在非托管代码(如 Win32 API、非托管 DLL 或使用 COM 的方法)中使用。 
    另一种过早发生垃圾回收的情形是,在一个方法中创建并使用一个对象。此时,当对对象的某个成员的调用仍在执行时,可能会对该对象进行回收,如第一个代码示例所示。 
    此方法引用 obj,从而使该对象从例程开始到调用此方法的那个位置(按执行顺序)均不符合进行垃圾回收的条件。在 obj 必须可用的指令范围的结尾(而不是开头)编写此方法的代码。 
    KeepAlive 方法除了延长作为参数传递的对象的生存期之外,不会执行任何操作,也会不产生任何其他副作用。
    
    Structs.IDataCallback callback;
    
    ......
        
      public void StartHsFundFlow(int length)
    		{
    			
    			.....
                 .....
                 .....
    			for (int i = 0; i < hsFundFlowsCodes.Length; i++)
    			{
    				HSFundFlowsSerialID[i] = EmQuantAPI.csq(hsFundFlowsCodes[i],                                                           HSFundFlowsConstant.CSQ_HSFUNDFLOWS,
    					            "Pushtype=" + (int)OptionEnums.Pushtype.ALL,
                                                                      callback, 
                                                                     IntPtr.Zero);
              ...
              ...    
    				GC.KeepAlive(callback);
    			}
    		.......
             ......
    		}
    

    我们需要在可用的指令范围的结尾 ,可以说方法的末尾处,加上GC.KeepAlive(obj);

    参考博文:

    https://blog.****.net/catshitone/article/details/53641498

    https://www.cnblogs.com/tinya/p/4837586.html