Clickonce application doesn't restart after upgrade is complete

风流意气都作罢 提交于 2019-12-02 09:59:31

You can try restarting the application like so:

Dim applicationEntryPoint = ApplicationDeployment.CurrentDeployment.UpdatedApplicationFullName

Process.Start(applicationEntryPoint)

// Call any code to shut down the current instance of the application here...

I recommend putting in some kind a messagebox or tracing at the point where it calls Application.Restart to make sure it's really calling it. If you verify that, and it still doesn't work, then could you possibly have something locked and it won't restart until it's closed?

InteXX

This is likely because you're using VB's native Single-Instance Application feature in your ClickOnce-deployed app. The feature uses a Mutex under the hood, which doesn't get released in time for the app to restart. Thus the behavior you're seeing--it doesn't restart.

I had the same problem just now. To fix it I had to use a VB translation and slight reworking of the trick I found here. In essence what we need is a shared instance of Mutex and a 3-second pause on Application.Restart() to give the exiting version time to release its Mutex. Don't forget to UNCHECK 'Make single instance application' on the Application tab of your project's Property Pages.

Here's the code I ended up with, below. This way we can have the best of all worlds--VB's nifty Application Framework that we enjoy so much, Single-Instance functionality and ClickOnce API restarts. All at once. I'm giddy.

Hat tip #1: To devzoo, for his CodeProject posting, demonstrating the main concept.

Hat tip #2: To NullFX, for his WinApi PostMessage() idea, as quoted by devzoo.

Hat tip #3: To @cmptrs4now for his IsRestarting 3-second pause idea here.

Hat tip #4: To @pstrjds for his clarification here. Per his advice I changed Mutex.ReleaseMutex() to Mutex.Close() below. This looks to be safer.

HTH

Friend Class Main
  Inherits System.Windows.Forms.Form

  Protected Overrides Sub WndProc(ByRef Message As Message)
    If Message.Msg = SingleInstance.WM_SHOWFIRSTINSTANCE Then
      ShowWindow()
    End If
    MyBase.WndProc(Message)
  End Sub

  Private Sub ShowWindow()
    Me.WindowState = FormWindowState.Normal
    Me.Focus()
  End Sub

  Private Sub cmdUpdate_Click(Sender As Object, e As EventArgs) Handles cmdUpdate.Click
    If ApplicationDeployment.IsNetworkDeployed Then
      If ApplicationDeployment.CurrentDeployment.CheckForUpdate(False)
        ApplicationDeployment.CurrentDeployment.Update()

        MsgBox("The application has been updated and will now restart.", MsgBoxStyle.Information)

        My.Settings.IsRestarting = True
        My.Settings.Save()

        Application.Restart()
      End If
    End If
  End Sub
End Class

Namespace My
  ' The following events are availble for MyApplication:
  ' 
  ' Startup: Raised when the application starts, before the startup form is created.
  ' Shutdown: Raised after all application forms are closed.  This event is not raised if the application terminates abnormally.
  ' UnhandledException: Raised if the application encounters an unhandled exception.
  ' StartupNextInstance: Raised when launching a single-instance application and the application is already active. 
  ' NetworkAvailabilityChanged: Raised when the network connection is connected or disconnected.
  Partial Friend Class MyApplication
    Private Sub MyApplication_Startup(sender As Object, e As ApplicationServices.StartupEventArgs) Handles Me.Startup
      If My.Settings.IsRestarting Then
        My.Settings.IsRestarting = False
        My.Settings.Save()
        Thread.Sleep(3000)
      End If

      If Not SingleInstance.Start() Then
        SingleInstance.ShowFirstInstance()
        e.Cancel = True
      End If
    End Sub

    Private Sub MyApplication_Shutdown(sender As Object, e As EventArgs) Handles Me.Shutdown
      SingleInstance.Stop()
    End Sub
  End Class
End Namespace

Public NotInheritable Class SingleInstance
  Public Shared ReadOnly WM_SHOWFIRSTINSTANCE As Integer = WinApi.RegisterWindowMessage("WM_SHOWFIRSTINSTANCE|{0}", ProgramInfo.AssemblyGuid)
  Private Shared Mutex As Mutex

  Public Shared Function Start() As Boolean
    Dim lIsOnlyInstance As Boolean
    Dim sMutexName As String

    lIsOnlyInstance = False
    sMutexName = String.Format("Local\{0}", ProgramInfo.AssemblyGuid)

    ' If you want your app to be limited to a single instance
    ' across ALL SESSIONS (multiple users & terminal services),
    ' then use the following line instead:
    ' sMutexName = String.Format("Global\\{0}", ProgramInfo.AssemblyGuid);

    Mutex = New Mutex(True, sMutexName, lIsOnlyInstance)
    Return lIsOnlyInstance
  End Function

  Public Shared Sub ShowFirstInstance()
    WinApi.PostMessage(New IntPtr(WinApi.HWND_BROADCAST), WM_SHOWFIRSTINSTANCE, IntPtr.Zero, IntPtr.Zero)
  End Sub

  Public Shared Sub [Stop]()
    Mutex.Close()
  End Sub
End Class

Public NotInheritable Class WinApi
  <DllImport("user32")> _
  Public Shared Function RegisterWindowMessage(message As String) As Integer
  End Function

  <DllImport("user32")> _
  Public Shared Function PostMessage(hwnd As IntPtr, msg As Integer, wparam As IntPtr, lparam As IntPtr) As Boolean
  End Function

  <DllImport("user32")> _
  Public Shared Function ShowWindow(hWnd As IntPtr, nCmdShow As Integer) As Boolean
  End Function

  <DllImport("user32")> _
  Public Shared Function SetForegroundWindow(hWnd As IntPtr) As Boolean
  End Function

  Public Shared Function RegisterWindowMessage(Template As String, ParamArray Values As Object()) As Integer
    Return RegisterWindowMessage(String.Format(Template, Values))
  End Function

  Public Shared Sub ShowToFront(Window As IntPtr)
    ShowWindow(Window, SW_SHOWNORMAL)
    SetForegroundWindow(Window)
  End Sub

  Public Const HWND_BROADCAST As Integer = &HFFFF
  Public Const SW_SHOWNORMAL As Integer = 1
End Class

Public NotInheritable Class ProgramInfo
  Public Shared ReadOnly Property AssemblyGuid As String
    Get
      Dim aAttributes As Object()

      aAttributes = Assembly.GetEntryAssembly.GetCustomAttributes(GetType(GuidAttribute), False)

      If aAttributes.Length = 0 Then
        AssemblyGuid = String.Empty
      Else
        AssemblyGuid = DirectCast(aAttributes(0), GuidAttribute).Value
      End If
    End Get
  End Property
End Class
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!