当挂钩SetConsoleCtrlHandler时,没有堆栈跟踪的NullReferenceException

本文关键字:堆栈 跟踪 NullReferenceException SetConsoleCtrlHandler | 更新日期: 2023-09-27 18:03:10

使用代码从这个线程挂钩控制台关闭事件,我有时会得到一个没有堆栈跟踪的NullReferenceException(大多数时候我没有)。它在发布和调试中都会发生,"抛出异常时中断"没有帮助(它中断了,但堆栈跟踪仍然为空)。当我正常退出我的应用程序时,我从来没有得到这个异常(这是按回车键,从而释放一个Console.ReadLine)。应用程序事件日志有2个条目:

应用程序:MyApp.exe框架版本:v4.0.30319进程因未处理的异常而终止。异常信息:系统。得到NullReferenceException栈:

:

错误应用程序名称:Gateway.exe,版本:1.0.0.0,时间戳:0x4e284101故障模块名称:unknown,版本:0.0.0.0,时间stamp: 0x00000000异常码:0xc0000005故障偏移量:0x004d41ce故障进程id: 0xf00故障应用开始时间:0x01cc47b827e19a6e应用路径故障:C:'dev'MyApp.exe故障模块路径:报告编号:689c1caa-b3ab-11e0-ba1b-00247e777f12

Google已经揭露了SetConsoleCtrlHandler的一些bug和问题,所以我想知道这是否是一场失败的战斗。

当挂钩SetConsoleCtrlHandler时,没有堆栈跟踪的NullReferenceException

这样的代码最典型的问题是没有保留对委托实例的引用。你传递给SetConsoleCtrlHandler()的第一个参数。垃圾收集器无法看到非托管代码对委托对象持有的引用。因此,当垃圾收集器运行时,这最终会爆炸:

 SetConsoleCtrlHandler(Handler, true);

也就是

 SetConsoleCtrlHandler(new EventHandler(Handler), true);

假设您使用了链接代码中的类型。该代码的作者通过将_handler设置为静态变量来小心地避免了这个问题。与前两行代码创建的临时委托实例相反。将它存储在一个静态变量中可以确保它在程序的生命周期中一直被引用。这是在这种特殊情况下应该做的正确的事情,因为您实际上对程序结束之前的事件感兴趣。

对于正在努力解决这个问题的vb.net bods,我的代码是…

'declaration
Private Declare Function SetConsoleCtrlHandler Lib "kernel32" (ByVal handlerRoutine As ConsoleEventDelegate, ByVal add As Boolean) As Boolean
Public Delegate Function ConsoleEventDelegate(ByVal [event] As ConsoleEvent) As Boolean
'The close function...
Public Function Application_ConsoleEvent(ByVal [event] As ConsoleEvent) As Boolean
    Console.WriteLine("We're closing it all down: ")
    Return False
End Function
'creating the handler. 
If Not SetConsoleCtrlHandler(New ConsoleEventDelegate(AddressOf Application_ConsoleEvent), True) Then
    Console.WriteLine("Unable to install console event handler.")
    Exit Sub
End If