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
The simplest way of doing this is probably to handle the AppDomain.ProcessExit event, which is raised when the application's parent process exits.
For example:
Module MyApp
Sub Main()
' Attach the event handler method
AddHandler AppDomain.CurrentDomain.ProcessExit, AddressOf MyApp_ProcessExit
' Do something
' ...
Environment.Exit(0)
End Sub
Private Sub MyApp_ProcessExit(sender As Object, e As EventArgs)
Console.WriteLine("App Is Exiting...")
End Sub
End Module
But calling Environment.Exit may not be the best solution to your original problem. In general, the only time it is necessary to use this method is when there might be other foreground threads running. And in that case, it's worth investigating ways of gracefully terminating those other threads without resorting to draconian measures that kill the entire process.
Environment.Exit, despite the somewhat pleasant-sounding name, is a pretty brutal measure. It's not quite as bad as clicking "End Task" in the Windows Task Manager (and note that if you do that, the ProcessExit event will not be raised, meaning that the above suggestion will not work), but it's probably not the solution you really want, either.
Pointers to functions are represented in .Net as delegates. A delegate is a kind of object and, like any other object, if there are no references to it then it will be garbage collected.
The expression (AddressOf Application_ConsoleEvent)
creates the delegate. SetConsoleCtrlHandler
is a native function, so it doesn't understand delegates; it receives a raw function pointer. So the sequence of operations is:
(AddressOf Application_ConsoleEvent)
creates the delegate.SetConsoleCtrlHandler
receives a raw function pointer that points to the delegate, and stores the raw pointer for later use.You need to declare a variable to keep a reference to the delegate. My VB is very rusty, but something like:
Shared keepAlive As ConsoleEventDelegate
Then
keepAlive = AddressOf ConsoleEventDelegate
If Not SetConsoleCtrlHandler(keepAlive, True) Then
'etc.
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...
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.