![]() |
1
10
是的,你猜对了。不完全是一个“静态函数”,CLR中有一大堆代码来执行这个神奇的功能。它会自动为thunk生成机器代码,将本地代码的调用转换为托管代码。本机代码获取指向该thunk的函数指针。可能必须转换参数值,这是标准的pinvoke marshaller职责。并且总是来回移动以匹配对托管方法的调用。挖掘存储的委托的目标属性以提供
然而,有一个令人讨厌的小细节让几乎每个人都陷入了麻烦。当不再需要回调时,会再次自动清除这些thunk。CLR没有从本机代码获得任何帮助来确定这一点,当委托对象被垃圾收集时就会发生这种情况。也许你闻到了老鼠的味道,是什么决定了你的程序何时发生这种情况?
这是方法的局部变量。它不会存活很长时间,下一次收集将摧毁它。坏消息是,如果本机代码继续通过thunk进行回调,它将不再存在,这是一个严重的崩溃。当您尝试代码时,不太容易看到,因为这需要一段时间。 你必须确保这不会发生。在类中存储委托对象可能会起作用,但您必须确保类对象存在足够长的时间。无论如何,从片段中猜不出答案。当您还确保再次注销这些回调时,它往往会自行解决,因为这需要存储对象引用以供以后使用。您还可以将它们存储在静态变量中或使用GCHandle。Alloc(),但这当然失去了快速进行实例回调的好处。通过测试正确完成这项工作,感觉很好,请调用GC。调用方中的Collect()。 值得注意的是,通过显式地新建委托,您做得很好。C#语法sugar不需要这样做,因此很难做到这一点。如果仅发生回调 虽然 您对本机代码进行pinvoke调用,这种情况并不少见(如EnumWindows),因此您不必担心,因为pinvoke封送拆收器确保委托对象保持被引用状态。 |
![]() |
2
3
记录如下: 汉斯·帕桑特(HansPassant)提到,我已经直接走进了陷阱。强制垃圾回收导致了空引用异常,因为委托是暂时的:
幸运的是,我已经将关键的两个P/invoke,KINInit(设置回调委托)和KINSolve(实际使用回调)打包到一个专用的托管类中。如前所述,解决方案是让委托由类成员引用:
再次感谢你,汉斯,我从来没有注意到这个缺陷,因为只要没有GC发生,它就可以工作! |
![]() |
Mike Bruno · 访问模拟帐户的私钥 7 年前 |
![]() |
John · 通过P/Invoke使用回调和堆对象的安全方法 8 年前 |
![]() |
Residuum · 带有P/Invoke和指针的泛型 9 年前 |
![]() |
hl3mukkel · 约束与使用SafeHandle的抽象类 9 年前 |