在 WinForm 的进度条上显示 WinSCP .NET 程序集传输进度

Showing WinSCP .NET assembly transfer progress on WinForm's progress bar

有一些主窗体,我在上面调用从 FTP 下载文件。提出此操作后,我希望看到新表单 ShowDialog 和它上面的进度条同时显示,然后显示进度并关闭新表单并返回主表单。然而,我的代码正在运行,当它开始处理时,我的主窗体冻结,然后新窗体出现然后关闭。我想更正的是在执行流程后立即显示此新表格。你能看一下并告诉我有什么问题吗?

这是我主窗体的下载过程调用:

Dim pro As New FrmProgressBarWinscp(WinScp, myremotePicturePath, ladujZdjeciaPath, True)

FrmProgressBarWinscp如下:

Public Class FrmProgressBarWinscp

    Property _winScp As WinScpOperation
    Property _remotePicture As String
    Property _ladujZdjecia As String
    Property _removesource As String

    Public Sub New()
        InitializeComponent()
    End Sub

    Sub New(winscp As WinScpOperation, remotePicture As String, ladujzdjecia As String, removesource As Boolean)
        ' This call is required by the designer.
        InitializeComponent()
        ' Add any initialization after the InitializeComponent() call.
        _winScp = winscp
        _remotePicture = remotePicture
        _ladujZdjecia = ladujzdjecia
        _removesource = removesource
        ShowDialog()
    End Sub

    Sub Run()

        Try
            Cursor = Cursors.WaitCursor
            _winScp.GetFile(_remotePicture, _ladujZdjecia, _removesource)
            ProgressBar1.Minimum = 0
            ProgressBar1.Maximum = 1
            ProgressBar1.Value = 0
            Do
                ProgressBar1.Value = WinScpOperation._lastProgress
                ProgressBar1.Refresh()
            Loop Until ProgressBar1.Value = 1
            Cursor = Cursors.Default
            'Close()

        Catch ex As Exception

        Finally
            If _winScp IsNot Nothing Then
                _winScp.SessionDispose()
            End If

            System.Threading.Thread.Sleep(10000)
            Close()
        End Try

    End Sub

    Private Sub FrmProgressBarWinscp_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Run()
    End Sub
End Class

我自己的Winscp class和使用的方法:

...
Function GetFile(source As String, destination As String, Optional removeSource As Boolean = False)
    Dim result As Boolean = True
    Try

        session.GetFiles(source, destination, removeSource).Check()

    Catch ex As Exception
        result = False
    End Try
    Return result
End Function

Private Shared Sub SessionFileTransferProgress(sender As Object, e As FileTransferProgressEventArgs)
    'Print transfer progress
    _lastProgress = e.FileProgress

End Sub

Public Shared _lastProgress As Integer

...

进一步讨论 3:

Main form:
 Dim tsk As Task(Of Boolean) = Task.Factory.StartNew(Of Boolean)(Function()
                                                                                                Return WinScp.GetFile(myremotePicturePath, ladujZdjeciaPath, True)

                                                                                            End Function)


                            Dim forma As New FrmProgressBar
                            forma.ShowDialog()

进度条形式:

Public Class FrmProgressBar

    Public Sub New()
        InitializeComponent()
    End Sub
    Sub Run()
        Try
            Do
                ProgressBar1.Value = WinScpOperation._lastProgress
                ProgressBar1.Refresh()
            Loop Until ProgressBar1.Value = 1
            Cursor = Cursors.Default

        Catch ex As Exception
        Finally
            MsgBox("before sleep")
            System.Threading.Thread.Sleep(10000)
            MsgBox("after sleep sleep")
            Close()
        End Try
    End Sub

    Private Sub FrmProgressBarWinscp_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Run()
    End Sub
End Class

点编号。 4:

  Dim tsk As Task(Of Boolean) = Task.Factory.StartNew(Of Boolean)(Function()
                                                                                                Return WinScp.GetFile(myremotePicturePath, ladujZdjeciaPath, True)

                                                                                            End Function)


                            Dim pic As New Waiting
                            pic.ShowDialog()

                            Task.WaitAll(tsk)
                            pic.Close()

第 5 点:

 Dim pic As New Waiting
                            pic.ShowDialog()
                            Dim tsk As Task = Task.Factory.StartNew(Sub() WinScp.GetFile(myremotePicturePath, ladujZdjeciaPath, pic, True))




                            Task.WaitAll(tsk)
                            'pic.Close()

在其他一些class中(可能之前没有提到这个方法放在不同的地方class - 我的自定义)

Public Function GetFile(source As String, destination As String, formclose As InvokeCloseForm, Optional removeSource As Boolean = False) As Boolean
        Dim result As Boolean = True
        Try
            session.GetFiles(source, destination, removeSource).Check()
        Catch ex As Exception
            result = False
        End Try
        formclose.RUn()
        Return result
    End Function

接口:

Public Interface InvokeCloseForm
    Sub RUn()
End Interface

等待表格:

Public Class Waiting
    Implements InvokeCloseForm

    Public Sub RUn() Implements InvokeCloseForm.RUn
        Me.Close()
    End Sub
End Class

阻塞中的Session.GetFiles method

意思是returns传输完成后

解决方法是:

  • 运行 WinSCP 传输(Session.GetFiles)在单独的线程中,不阻塞 GUI 线程。

    请参阅 WinForm Application UI Hangs during Long-Running Operation

  • 处理 Session.FileTransferProgress event.

    不过请注意,事件处理程序将在后台线程上调用,因此您不能直接从处理程序更新进度条。您必须使用 Control.Invoke 来确保进度条在 GUI 线程上更新。

    请参阅 How do I update the GUI from another thread?

  • 下面是一个简单的实现。

    更多版本的代码,参见WinSCP文章Displaying FTP/SFTP transfer progress on WinForms ProgressBar

    Public Class ProgressDialog1
    
        Private Sub ProgressDialog1_Load(
                sender As Object, e As EventArgs) Handles MyBase.Load
            ' Run download on a separate thread
            ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf Download))
        End Sub
    
        Private Sub Download(stateInfo As Object)
            ' Setup session options
            Dim mySessionOptions As New SessionOptions
            With mySessionOptions
                ... 
            End With
    
            Using mySession As Session = New Session
                AddHandler mySession.FileTransferProgress,
                    AddressOf SessionFileTransferProgress
    
                ' Connect
                mySession.Open(mySessionOptions)
    
                mySession.GetFiles(<Source>, <Destination>).Check()
            End Using
    
            ' Close form (invoked on GUI thread)
            Invoke(New Action(Sub() Close()))
        End Sub
    
        Private Sub SessionFileTransferProgress(
                sender As Object, e As FileTransferProgressEventArgs)
            ' Update progress bar (on GUI thread)
            ProgressBar1.Invoke(
                New Action(Of Double)(AddressOf UpdateProgress), e.OverallProgress)
        End Sub
    
        Private Sub UpdateProgress(progress As Double)
            ProgressBar1.Value = progress * 100
        End Sub
    End Class
    
  • 如果您想阻止用户进行某些操作,您可能希望在操作过程中禁用进度表(或其部分)。

    使用窗体或控件的 .Enabled 属性。


从您现有的 SessionFileTransferProgress 处理程序中调用 Application.DoEvents 方法是更简单但不可靠且通常不推荐的解决方案。

当然,您还必须从 SessionFileTransferProgress 更新进度条。

Private Shared Sub SessionFileTransferProgress(
        sender As Object, e As FileTransferProgressEventArgs)
    'Print transfer progress
    ProgressBar1.Value = e.FileProgress
    Application.DoEvents
End Sub

并且进度条的.Minimum.Maximum必须设置在Session.GetFiles之前。

但是不要那样做!这是错误的做法。

而且,您仍然需要按照上述正确解决方案中的相同方式禁用 forms/controls。