如何在 Windows 关机时正常关闭控制台应用程序 [英] How to Close Console Application Gracefully on Windows Shutdown
问题描述
我正在尝试在 Windows 关闭时正常关闭我的 vb.net 控制台应用程序.我找到了调用 Win32 函数 SetConsoleCtrlHandler 的示例,它们基本上都如下所示:
I am trying to close my vb.net console app gracefully when windows shutdown occurs. I have found examples that call the Win32 function SetConsoleCtrlHandler that all basically look like this:
Module Module1
Public Enum ConsoleEvent
CTRL_C_EVENT = 0
CTRL_BREAK_EVENT = 1
CTRL_CLOSE_EVENT = 2
CTRL_LOGOFF_EVENT = 5
CTRL_SHUTDOWN_EVENT = 6
End Enum
Private Declare Function SetConsoleCtrlHandler Lib "kernel32" (ByVal handlerRoutine As ConsoleEventDelegate, ByVal add As Boolean) As Boolean
Public Delegate Function ConsoleEventDelegate(ByVal MyEvent As ConsoleEvent) As Boolean
Sub Main()
If Not SetConsoleCtrlHandler(AddressOf Application_ConsoleEvent, True) Then
Console.Write("Unable to install console event handler.")
End If
'Main loop
Do While True
Threading.Thread.Sleep(500)
Console.WriteLine("Main loop executing")
Loop
End Sub
Public Function Application_ConsoleEvent(ByVal [event] As ConsoleEvent) As Boolean
Dim cancel As Boolean = False
Select Case [event]
Case ConsoleEvent.CTRL_C_EVENT
MsgBox("CTRL+C received!")
Case ConsoleEvent.CTRL_BREAK_EVENT
MsgBox("CTRL+BREAK received!")
Case ConsoleEvent.CTRL_CLOSE_EVENT
MsgBox("Program being closed!")
Case ConsoleEvent.CTRL_LOGOFF_EVENT
MsgBox("User is logging off!")
Case ConsoleEvent.CTRL_SHUTDOWN_EVENT
MsgBox("Windows is shutting down.")
' My cleanup code here
End Select
Return cancel ' handling the event.
End Function
这很好用,直到我将它合并到我现有的程序中,当我遇到这个异常时:
This works fine until I incorporate it into muy existing program when I get this exception:
CallbackOnCollectedDelegate 被检测到消息:对类型为AISLogger!AISLogger.Module1+ConsoleEventDelegate::Invoke"的垃圾回收委托进行了回调.这可能会导致应用程序崩溃、损坏和数据丢失.将委托传递给非托管代码时,托管应用程序必须使它们保持活动状态,直到保证它们永远不会被调用.
CallbackOnCollectedDelegate was detected Message: A callback was made on a garbage collected delegate of type 'AISLogger!AISLogger.Module1+ConsoleEventDelegate::Invoke'. This may cause application crashes, corruption and data loss. When passing delegates to unmanaged code, they must be kept alive by the managed application until it is guaranteed that they will never be called.
大量搜索表明问题是由未引用委托对象引起的,因此超出范围,因此被垃圾收集器处理.这似乎是通过在上面的示例中将 GC.Collect 添加到主循环中并在关闭控制台窗口或按 ctrl-C 时获得相同的异常来确认的.问题是,我不明白引用委托"是什么意思?对我来说,这听起来像是将变量分配给函数???我怎样才能在VB中做到这一点?有很多 C# 示例,但我无法将它们翻译成 VB.
Much searching indicates that the problem is caused by the the delegate object not being referenced and so is going out of scope and so being disposed of by the garbage collector. This seems to be confirmed by adding a GC.Collect into the main loop in the example above and getting the same exception when closing the console window or pressing ctrl-C. The trouble is, I don't understand what is meant by 'referencing the delegate'? This sounds to me like assigning a variable to a function??? How can I do this in VB? There are lots of C# examples of this but I can't translate them into VB.
谢谢.
推荐答案
If Not SetConsoleCtrlHandler(AddressOf Application_ConsoleEvent, True) Then
这句话会让你陷入困境.它即时创建一个委托实例并将其传递给非托管代码.但是垃圾收集器无法看到该非托管代码持有的引用.你需要自己存储它,这样它就不会被垃圾收集.让它看起来像这样:
This is the statement that gets you into trouble. It creates a delegate instance on-the-fly and passes it to unmanaged code. But the garbage collector cannot see the reference held by that unmanaged code. You'll need to store it yourself so it won't be garbage collected. Make that look like this:
Private handler As ConsoleEventDelegate
Sub Main()
handler = AddressOf Application_ConsoleEvent
If Not SetConsoleCtrlHandler(handler, True) Then
'' etc...
handler 变量现在保持对它的引用,并在程序的生命周期内保持引用,因为它是在模块中声明的.
The handler variable now keeps it referenced and does so for the life of the program since it is declared in a Module.
顺便说一句,您无法取消关闭,但这是另一个问题.
You cannot cancel a shutdown btw, but that's another issue.
这篇关于如何在 Windows 关机时正常关闭控制台应用程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!