8

OK guys, what's going on here? In this VB code:

Module Module1
Sub Main()


    If MsgBox("Restart?", MsgBoxStyle.OkCancel) = MsgBoxResult.Ok Then
        Application.Restart()

        MsgBox("restarting")

    Else
        MsgBox("Cancel")

    End If

End Sub
End Module

If this code is contained within a module, Application.Restart does not end the running application till the End Sub is hit. Any code that appears before then is executed - eg the 'Restarting' messagebox appears. However, if the equivalent code is run within a form then Application.Restart terminates the running application immediately. (Both cases correctly start a new instance). This behaviour does not appear to be documented anywhere - the implication in the docs is that it's synonymous with 'End' as far as the termination of the running instance is concerned. Am I missing something?

peterG
  • 1,651
  • 3
  • 14
  • 23

3 Answers3

5

The best way to answer these questions it to look at the code itself using Reflector (or Microsoft's free for debugging code, when it is available).

With Reflector, you can see (in .NET Framework 4.0) System.Windows.Forms.Application.Restart looks for four different types of applications:

  • the initial check that Assembly.GetEntryAssembly is Nothing, throwing a NotSupportedException if it is;
  • the Process.GetCurrentProcess.MainModule.FileName is ieexec.exe in the same folder as the current .NET Framework (specifically the folder where the module defining Object is);
  • ApplicationDeployment.IsNetworkDeployed is True; and
  • the general case.

All three supported cases determine the method to start the process again, calls Application.ExitInternal and starts the process again.

Application.ExitInternal closes open forms, including the check for a form attempting to abort the close by setting FormClosingEventArgs.Cancel to True. If no form attempts to cancel, the forms are closed and, using ThreadContext.ExitApplication, all ThreadConnexts are cleaned up (Disposed or their ApplicationContext.ExitThread is called).

NB No Thread.Abort is called, so threads are NOT explicitly ended in any way. Also the Windows.Forms ModalApplicationContext, does not even call the ThreadExit "event" that a normal ApplicationContext does.

(Note that all three supported cases in Application.Restart ignore the result of Application.ExitInternal, so if a form does attempt to abort all that happens is any other forms don't get a chance to close, and the ThreadContexts are not cleaned up!)

Importantly for your question, it does NOT attempt to actually exit the current threads or the entire application (other than closing open forms and thread contexts).

However, by the time your MsgBox("restarting") executes the new application has been started.

You need to manually exit the application after calling Application.Restart. In the case of "run[ing] within a form" (you don't show the code where you tested this) either the form is closed and that is what you considered as the current application ending, or extra stuff that Windows.Forms (or VB) sets up means the application is exited by one of the "events" that throw when the clean up that does occur runs.

In other words, before testing it I expected the MsgBox to appear even when this code is in say the Click event of a form, with the form disappearing first, and the application restarting at the same time.

Having tested it, the MsgBox tries to appear, as I hear the beep that corresponds to it, and if I comment it out the beep does not occur. So something causes the application to exit even though it should have a message box open, and even putting a MsgBox in a Finally outside of the Application.Run does not appear on a Restart. (Note a similar effect is seen if you call MsgBox after Application.Exit.)

So something set up by Windows.Forms (or VB) does actually call something like Environment.Exit which calls the Win32Api ExitProcess and does not regard Finally or call Dispose or Finalize.

Note the Application.Restart documentation implies it is not for Console Applications though it currently works fine (except for the not quitting straight away, which is not implied by Application.Exit).

Mark Hurd
  • 10,665
  • 10
  • 68
  • 101
  • Thanks for your thorough reply Mark. The Form version was simply Application.Restart() and then MsgBox("Restarting") in the click event of a button on the form. No messagebox, no beep. – peterG Sep 25 '12 at 22:46
  • Yeah, the message beep would be some sort of race condition. On some machines, I'm sure you'd see the message box appear and then go away without the OK button being pressed because the application has had `ExitProcess` called. I have not confirmed where this call is set up. – Mark Hurd Sep 26 '12 at 00:31
1

I am able to restart the application by closing and disposing all open forms, except the one that is calling.

    For j As Integer = Application.OpenForms.Count - 1 To 0 Step -1
        Dim frm = Application.OpenForms(j)
        If frm.Text <> callingForm.Text Then
            frm.Close()
            frm.Dispose()
        End If
    Next

    Application.Restart()
Chor
  • 11
  • 1
0

This is going to be, admittedly, a bit of a guess based on some fairly top-level reading I've done about Application.Restart(), but I think this is occurring due to the way Restart operates internally.

I think Restart() tries to do as much "intelligent" cleanup as it can for a process that is being terminated, and in what may be considered a fairly simplistic implementation, tracks certain of the things to be "cleaned up," possibly calling Dispose() on them (if applicable), which normally is a reasonable step to take. In your case, I'm going to make the guess that a background thread, or form, holds a reference to something - can't say what - that prevents the code from shutting down. It may become aware that it is executing inside a method, and wants to give that method a chance to complete before killing it - waiting on the completion of that sub/method.

I've seen other instances of Restart actually causing a really strange "Collection was Modified" error when no collection was involved. That's suggesting to me, probably naively, that the internal cleanup Restart is trying to achieve is reposed in a simple list, but in certain circumstances, the cleanup modifies the element in an unexpected way, a way that modifies the collection, causes the exception to be thrown, and aborts the exit/restart.

David W
  • 10,062
  • 34
  • 60
  • Thanks for this, but I was hoping for a more definitive answer really. I can make it work as I expect by simply putting an 'End' right after the application.restart line, but this still leaves me feeling a little uneasy, especially as this is code in an application I'm selling (the code sample I published is a skeleton that still demonstrates the odd behaviour - the production code is a bit more complex.) – peterG Sep 22 '12 at 19:48