Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 应用程序(服务)自我升级?_C#_.net_.net 4.0 - Fatal编程技术网

C# 应用程序(服务)自我升级?

C# 应用程序(服务)自我升级?,c#,.net,.net-4.0,C#,.net,.net 4.0,我正在用C#开发一个基于.NET4的应用程序,它作为windows服务运行 我希望这个应用程序能够在它定期连接到的web服务指示它升级自己时进行升级。有没有一个公认的方法来实现这一点?有可能吗 我的想法是这样的: windows服务(.exe)代码将其替换和支持的DLL作为zip下载,并将它们提取到临时目录中。zip还包括一个小的“升级器”可执行文件或脚本 该服务派生一个子进程来运行升级程序,并在命令行上传递目标目录和任何其他必需的信息 服务关闭 升级程序进程等待服务完全停止,然后将必要的文件(

我正在用C#开发一个基于.NET4的应用程序,它作为windows服务运行

我希望这个应用程序能够在它定期连接到的web服务指示它升级自己时进行升级。有没有一个公认的方法来实现这一点?有可能吗

我的想法是这样的:

  • windows服务(.exe)代码将其替换和支持的DLL作为zip下载,并将它们提取到临时目录中。zip还包括一个小的“升级器”可执行文件或脚本
  • 该服务派生一个子进程来运行升级程序,并在命令行上传递目标目录和任何其他必需的信息
  • 服务关闭
  • 升级程序进程等待服务完全停止,然后将必要的文件(new.exe、dll)移动到最终安装目录中,替换旧文件
  • 升级程序重新启动windows服务,该服务生成(升级的).exe,并在启动后退出
  • 这样行吗?您可能会从我的术语和方法中发现我来自UNIX背景,而不是windows背景。我已经在UNIX上实现了这种方法,但我不知道可能存在哪种类型的windows gotchas


    更新:我提出这个问题的主要动机是关于自更新.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()