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

戏子无情 提交于 2019-12-01 12:41:38

问题


Have some main form on which I am calling file downloading from FTP. When this operation is raised i want to see new form as ShowDialog and progress bar on it to be shown meantime, then show the progress and close new form and back to main form. My code is working however, when it will process is started my main form freezes and after while new form is appearing and then closing. What I would like to correct is to show this new form to be showed straightaway after process is executed. Can you take a look and tell me whats wrong?

This is out of my main form the download process called:

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

FrmProgressBarWinscp is as follows:

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 my own class and used methods:

...
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

...

Further discussion nr 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()

Progress bar form:

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

Point nr. 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()

Point 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()

In some other class (maybe didn't mentioned before this method is placed in diffrent class - my custom one)

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

Interface:

Public Interface InvokeCloseForm
    Sub RUn()
End Interface

Waiting form :

Public Class Waiting
    Implements InvokeCloseForm

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

回答1:


The Session.GetFiles method in blocking.

It means it returns only after the transfer finishes.

The solution is to:

  • Run the WinSCP transfer (the Session.GetFiles) in a separate thread, not to block the GUI thread.

    For that see WinForm Application UI Hangs during Long-Running Operation

  • Handle the Session.FileTransferProgress event.

    Though note that the event handler will be called on the background thread, so you cannot update the progress bar directly from the handler. You have to use the Control.Invoke to make sure the progress bar is updated on the GUI thread.

    For that see How to update the GUI from another thread in C#?

  • A trivial implementation is like:

    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
    
  • You may want to disable the progress form (or its parts) during the operation, if you want to prevent the user from doing some operations.

    Use the .Enabled property of the form or control(s).


Easier, but hacky and generally not recommendable solution, is to call the Application.DoEvents method from your existing SessionFileTransferProgress handler.

And of course, you have to update the progress bar from the the SessionFileTransferProgress as well.

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

And the progress bar's .Minimum and .Maximum must be set before the Session.GetFiles.

But do not do that! That's a wrong approach.

And still, you need to disable the forms/controls the same way as in the correct solution above.



来源:https://stackoverflow.com/questions/33012517/showing-winscp-net-assembly-transfer-progress-on-winforms-progress-bar

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!