C# 应用程序(服务)自我升级?
我正在用C#开发一个基于.NET4的应用程序,它作为windows服务运行 我希望这个应用程序能够在它定期连接到的web服务指示它升级自己时进行升级。有没有一个公认的方法来实现这一点?有可能吗 我的想法是这样的:C# 应用程序(服务)自我升级?,c#,.net,.net-4.0,C#,.net,.net 4.0,我正在用C#开发一个基于.NET4的应用程序,它作为windows服务运行 我希望这个应用程序能够在它定期连接到的web服务指示它升级自己时进行升级。有没有一个公认的方法来实现这一点?有可能吗 我的想法是这样的: windows服务(.exe)代码将其替换和支持的DLL作为zip下载,并将它们提取到临时目录中。zip还包括一个小的“升级器”可执行文件或脚本 该服务派生一个子进程来运行升级程序,并在命令行上传递目标目录和任何其他必需的信息 服务关闭 升级程序进程等待服务完全停止,然后将必要的文件(
更新:我提出这个问题的主要动机是关于自更新.NET应用程序的技术可行性(如何就地替换.dll等)。正如评论中所指出的,在实现这样的功能时还需要考虑很多其他因素,特别是在验证所应用的新软件组件是否确实合法方面的安全问题。这些也很重要,但不是特定于.NET或Windows(imo)。当然,对这些领域的评论是受欢迎的,但它们不是我目前最关心的问题。你应该看看本周早些时候的热门媒体菲尔·哈克(Phil Haack)的评论。我不认为这正是你想要的,但它可能会节省你一些时间。NuGet在任何情况下都是好时光。这可以使用ClickOnce完成,但可能没有达到您想要的程度 看看这门课
Imports System.Deployment.Application
Imports System.ComponentModel
Public Class UpdateChecker
Public Enum UpdateType
Automatic
Manual
End Enum
Private Shared MyInstance As UpdateChecker
Public Shared ReadOnly Property Current() As UpdateChecker
Get
If MyInstance Is Nothing Then
MyInstance = New UpdateChecker
End If
Return MyInstance
End Get
End Property
Private WithEvents CurrDeployment As ApplicationDeployment
Private CurrType As UpdateType
Private _checking As Boolean = False
Private _lastErrorSentOnCheck As DateTime?
Public ReadOnly Property LastUpdateCheck() As DateTime?
Get
If CurrDeployment IsNot Nothing Then
Return CurrDeployment.TimeOfLastUpdateCheck
End If
Return Nothing
End Get
End Property
Public Sub CheckAsync(ByVal checkType As UpdateType)
Try
Dim show As Boolean = (checkType = UpdateType.Manual)
If ApplicationDeployment.IsNetworkDeployed AndAlso _
Not WindowActive(show) AndAlso Not _checking AndAlso _
(checkType = UpdateType.Manual OrElse Not LastUpdateCheck.HasValue OrElse LastUpdateCheck.Value.AddMinutes(60) <= Date.UtcNow) Then
_checking = True
CurrDeployment = ApplicationDeployment.CurrentDeployment
CurrType = checkType
Dim bw As New BackgroundWorker
AddHandler bw.RunWorkerCompleted, AddressOf CurrDeployment_CheckForUpdateCompleted
AddHandler bw.DoWork, AddressOf StartAsync
If CurrType = UpdateType.Manual Then ShowWindow()
bw.RunWorkerAsync()
ElseIf checkType = UpdateType.Manual AndAlso _checking Then
CurrType = checkType
WindowActive(True)
ElseIf checkType = UpdateType.Manual AndAlso Not ApplicationDeployment.IsNetworkDeployed Then
MessageBox.Show(MainForm, "Cannot check for updates.", "Update", MessageBoxButtons.OK, MessageBoxIcon.Information)
End If
Catch ex As Exception
If Not _lastErrorSentOnCheck.HasValue OrElse _lastErrorSentOnCheck.Value.AddHours(1) <= Now Then
_lastErrorSentOnCheck = Now
My.Application.LogError(ex, New StringPair("Update Check", checkType.ToString))
End If
End Try
End Sub
Private Sub StartAsync(ByVal sender As Object, ByVal e As DoWorkEventArgs)
e.Result = CurrDeployment.CheckForDetailedUpdate
End Sub
Private Sub ShowWindow()
My.Forms.frmUpdates.MdiParent = MainForm
AddHandler My.Forms.frmUpdates.FormClosing, AddressOf frmUpdates_FormClosing
My.Forms.frmUpdates.Show()
End Sub
Protected Sub frmUpdates_FormClosing(ByVal sender As Object, ByVal e As Windows.Forms.FormClosingEventArgs)
My.Forms.frmUpdates = Nothing
End Sub
Private Function WindowActive(ByVal onTop As Boolean) As Boolean
If Not My.Forms.frmUpdates Is Nothing Then
If Not My.Forms.frmUpdates.Visible AndAlso onTop Then
My.Forms.frmUpdates.MdiParent = MainForm
My.Forms.frmUpdates.Show()
ElseIf onTop Then
My.Forms.frmUpdates.Activate()
End If
Return True
End If
Return False
End Function
Private Sub CurrDeployment_CheckForUpdateCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)
If MainForm.InvokeRequired Then
MainForm.Invoke(New RunWorkerCompletedEventHandler(AddressOf CurrDeployment_CheckForUpdateCompleted), sender, e)
Else
If e.Error IsNot Nothing Then
If WindowActive(True) Then My.Forms.frmUpdates.ShowError("Please try again later.")
If Not _lastErrorSentOnCheck.HasValue OrElse _lastErrorSentOnCheck.Value.AddHours(1) <= Now Then
_lastErrorSentOnCheck = Now
My.Application.LogError(e.Error, New StringPair("Update Check Async", CurrType.ToString))
End If
Else
Dim updateInfo As UpdateCheckInfo = DirectCast(e.Result, UpdateCheckInfo)
Select Case CurrType
Case UpdateType.Manual
If WindowActive(False) Then My.Forms.frmUpdates.ShowCheckComplete(updateInfo)
Case UpdateType.Automatic
If updateInfo.UpdateAvailable Then
If Not WindowActive(True) Then ShowWindow()
My.Forms.frmUpdates.ShowCheckComplete(updateInfo)
End If
End Select
End If
_checking = False
End If
DirectCast(sender, BackgroundWorker).Dispose()
End Sub
Public Sub UpdateAsync()
If ApplicationDeployment.IsNetworkDeployed Then
CurrDeployment = ApplicationDeployment.CurrentDeployment
Dim bw As New BackgroundWorker
AddHandler bw.RunWorkerCompleted, AddressOf CurrDeployment_UpdateCompleted
AddHandler bw.DoWork, AddressOf StartUpdateAsync
My.Forms.frmUpdates.ShowUpdateStart()
bw.RunWorkerAsync()
End If
End Sub
Public Sub StartUpdateAsync()
CurrDeployment.Update()
End Sub
Private Sub CurrDeployment_UpdateCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) Handles CurrDeployment.UpdateCompleted
If MainForm.InvokeRequired Then
MainForm.Invoke(New AsyncCompletedEventHandler(AddressOf CurrDeployment_UpdateCompleted), sender, e)
Else
If e.Error IsNot Nothing Then
If WindowActive(True) Then My.Forms.frmUpdates.ShowError("Please try again later or close and re-open the application to automatically retrieve updates.")
My.Application.LogError(e.Error, New StringPair("Update Async", CurrType.ToString))
Else
If WindowActive(True) Then My.Forms.frmUpdates.ShowUpdateComplete()
End If
End If
End Sub
End Class
下面是下载更新的代码
UpdateChecker.Current.UpdateAsync()
用户必须退出并启动应用程序才能获得新版本或更新
更新完成后,您还可以使用Application.Restart重新启动应用程序
当程序不运行时,它不会更新,但ClickOnce可以在程序启动时检查它的更新。
< P>如果应用程序部署在专用网络(Intranet)内,您可以考虑使用。请参阅本文。您的方法当然是合理的,但除非您在LocalSystem帐户下运行(不推荐!),否则您无权写入应用程序文件夹或启动/停止自己的服务。即使您在用户帐户下运行,也可能会因为UAC而出现问题,尽管我不确定这一点。无论如何,在用户帐户下运行是不好的,因为它需要用户输入帐户密码,并且由于密码更改、锁定等而产生额外的复杂性。您必须从安装程序中设置这两个帐户的ACL,安装服务时必须运行提升版。不要忘记保护web服务和更新包。在internet上自动执行不受信任的代码,特别是作为特权服务运行时,是一件非常糟糕的事情。因为正如ChrisV所说,它是安全的,所以这是一个好主意。您可以使用updrader派生的进程通过服务控制管理器停止和恢复服务。“分叉”进程可能最好作为另一个windows服务实现。此windows服务可能负责启动/停止和检查更新。这就是病毒检查程序(如Norton Anti-virus)如何与实时更新服务配合使用。在UNIX中,它确实更容易,因为分支子进程的机制已经存在很长时间了。正如我所说,如果两个服务在职责完全分离的情况下运行,则更新程序服务将成为一段可重用的代码,您可以将其用于其他服务,因为它将没有依赖关系。我不认为在升级完成后使用windows服务启动应用程序有任何好处。谁更新更新程序?这对windows服务不起作用,除非它以使用ClickOnce的用户身份运行。请注意,ClickOnce也无法直接安装windows服务,除非它以完全信任的方式运行。 UpdateChecker.Current.UpdateAsync()