问题
I am using AppplicationDeployment class to check if upgrade is available and then upgrade the app like below
Dim AD As System.Deployment.Application.ApplicationDeployment = System.Deployment.Application.ApplicationDeployment.CurrentDeployment
Dim info As System.Deployment.Application.UpdateCheckInfo = Nothing
Me.DialogResult = Windows.Forms.DialogResult.Cancel
Me.Close()
AD.Update()
Application.Restart() // this doesn't work which is still ok.
The restart doesn't work so I am trying to get the upgraded application executable path and update the registry so when user re-starts the system the latest application will be launched.
I am not able to get the path where the application is installed after upgrading. It creates new folder in the c\document...\user.... I know. But, need to get this path and update registry.
Anyone has any pointers?
回答1:
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...
回答2:
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?
回答3:
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
来源:https://stackoverflow.com/questions/19367452/clickonce-application-doesnt-restart-after-upgrade-is-complete