升级完成后,Clickonce应用程序不会重新启动 [英] Clickonce application doesn't restart after upgrade is complete

查看:75
本文介绍了升级完成后,Clickonce应用程序不会重新启动的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在将,演示了主要概念。



提示2:致 NullFX ,因为他的 WinApi PostMessage()想法 devzoo



提示3:致 @ cmptrs4now 以获取 IsRestarting 3秒的暂停提示此处



帽子提示4:向 @pstrjds 进行澄清,此处。根据他的建议,我将 Mutex.ReleaseMutex()更改为下面的 Mutex.Close()



HTH

  Friend Class Main 
继承System.Windows.Forms.Form

受保护的重写Sub WndProc(作为消息的ByRef消息)
如果Message.Msg = SingleInstance.WM_SHOWFIRSTINSTANCE然后
ShowWindow()
End If
MyBase.WndProc(Message)
End Sub

Private Sub ShowWindow()
Me.WindowState = FormWindowState.Normal
Me .Focus()
结束子

私有子项cmdUpdate_Click(发送方作为对象,e作为EventArgs)处理cmdUpdate。单击
如果是ApplicationDeployment.IsNetworkDeployed,然后
如果是ApplicationDeployment。 CurrentDeployment.CheckForUpdate(False)
ApplicationDeployment.CurrentDeployment.Update()

MsgBox(应用程序已更新,现在将重新启动。,MsgBoxStyle.Information)

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

Application.Re start()
如果
结束如果
结束Sub
结束类

命名空间My
'以下事件可用于MyApplication:
'
'启动:在创建启动表单之前,在应用程序启动时引发。
’关闭:在所有申请表关闭后引发。如果应用程序异常终止,则不会引发此事件。
’UnhandledException:如果应用程序遇到未处理的异常,则引发。
’StartupNextInstance:启动单实例应用程序且该应用程序已处于活动状态时引发。
’NetworkAvailabilityChanged:在连接或断开网络连接时引发。
部分朋友类MyApplication
私有子MyApplication_Startup(发送者作为对象,e作为ApplicationServices.StartupEventArgs)处理Me.Startup
如果My.Settings.IsRestarting,则
My.Settings.IsRestarting = False
My.Settings.Save()
Thread.Sleep(3000)
End if

If Not SingleInstance.Start()then
SingleInstance .ShowFirstInstance()
e.Cancel = True
End如果
End Sub

Private Sub MyApplication_Shutdown(sender as Object,e As EventArgs)处理Me.Shutdown
SingleInstance.Stop()
结束子
结束类
结束命名空间

公共不可继承类SingleInstance
公共共享只读WM_SHOWFIRSTINSTANCE作为整数= WinApi .RegisterWindowMessage( WM_SHOWFIRSTINSTANCE | {0},ProgramInfo.AssemblyGuid)
私有共享互斥体作为互斥体

公共共享函数Start()作为布尔值
昏暗的生活OnlyInstance作为布尔
Dim sMutexName作为字符串

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

'如果您希望在所有会话中将应用程序限制为单个实例
'(多用户&终端服务),然后使用
’代替:
s sMutexName = String.Format( Global\\ {0},ProgramInfo.AssemblyGuid);

Mutex =新Mutex(True,sMutexName,lIsOnlyInstance)
返回lIsOnlyInstance
结束函数

Public Shared Sub ShowFirstInstance()
WinApi .PostMessage(New IntPtr(WinApi.HWND_BROADCAST),WM_SHOWFIRSTINSTANCE,IntPtr.Zero,IntPtr.Zero)
结束子

公共共享子[Stop]()
Mutex.Close ()
结束子类
结束类

公共不可继承类WinApi
< DllImport( user32)> _
公共共享函数RegisterWindowMessage(消息作为字符串)作为整数
结束函数

< DllImport( user32)> _
公共共享函数PostMessage(以IntPtr形式拥有msg,以Integer形式拥有msg,以IntPtr形式拥有wparam,以IntPtr形式拥有lparam)作为布尔值
结束函数

< DllImport( user32) > _
公共共享函数ShowWindow(hWnd作为IntPtr,nCmdShow作为整数)作为布尔值
结束函数

< DllImport( user32)> _
公共共享函数SetForegroundWindow(hWnd作为IntPtr)作为布尔值
结束函数

公共共享函数RegisterWindowMessage(模板作为字符串,ParamArray值作为Object())作为整数
返回RegisterWindowMessage(String.Format(Template,Values))
结束函数

Public Shared Sub ShowToFront(Window 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

公共不可继承类ProgramInfo
公共共享ReadOnly属性AssemblyGuid作为字符串
获取
昏暗的aAttributes作为Object()

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

如果aAttributes.Length = 0则
AssemblyGuid = String.Empty
其他
AssemblyGuid = DirectCast(aAttributes(0),GuidAttribute)。值
结束如果
结束获取
结束属性
结束类


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?

解决方案

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

这篇关于升级完成后,Clickonce应用程序不会重新启动的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆