Hi,
please note, that it is hard to help you, if you don't post the exact code you have run. For example: Missing Try
keyword for Catch
block in Download
method - it is not compilable code...
Now to the NullReferenceException
.
When you call BeginDownload
method (or another asynchronous method), this asynchronous method only starts a new thread and "immediately" returns to the calling method. In your case the code flow is this:
Download
method starts
- Connect
- Start background thread by calling
BeginDownload
method and immediately return
- Disconnect
Download
method ends
This means that your code calls Disconnect
much earlier than you surely intended.
When the Disconnect
is called, the download probably not ended yet. The download operation is long running, that is why you need to call it asynchronously (because synchronous call blocks the GUI). Before calling Disconnect
method, you must be sure the Download
is complete. This is why you pass FinishDownload
as a AsyncCallback
parameter to the BeginDownload
method:
FTP.BeginDownload(..., New AsyncCallback(AddressOf FinishDownload), ...)
The FinishDownload
method usually contains the code called after synchronous Download
method. So the FinishDownload
method can look like this:
Disconnect()
NotifyIcon.BalloonTipIcon = ToolTipIcon.Info
NotifyIcon.BalloonTipTitle = "Download completed"
NotifyIcon.BalloonTipText = "Your download is now completed" & vbNewLine & "Enjoy :)"
NotifyIcon.ShowBalloonTip(5000)
SetState(False)
Because NotifyIcon
is the GUI control, its updates must be called from the GUI thread. As I have written in my previous answer, you can achieve this by this construct:
If (Me.InvokeRequired) Then
Me.Invoke(New Action(Of Boolean)(AddressOf SetState), False)
End If
Here are the main changes to your code:
''# download module
Public Sub Download()
Dim remotedir As String = "app/data/123kickit"
Connect()
If Logging.Checked = True Then
FTP.LogWriter = New Rebex.FileLogWriter(currentlog, Rebex.LogLevel.Debug)
End If
Dim result As IAsyncResult = FTP.BeginDownload(remotedir, Application.StartupPath & "some_local_dir", TraversalMode.Recursive, TransferMethod.Copy, ActionOnExistingFiles.OverwriteDifferentSize, New AsyncCallback(AddressOf FinishDownload), Nothing)
''# do nothing after calling BeginDownload and wait until the FinishDownload is invoked from background thread
End Sub
Private Sub FinishDownload(ByVal result As IAsyncResult)
Try
FTP.EndDownload(result)
Catch ex As Exception
MessageBox.Show(ex.ToString())
End Try
Disconnect()
UpdateNotifyIcon()
SetState(False)
End Sub
Private Sub UpdateNotifyIcon()
If (Me.InvokeRequired) Then
Me.Invoke(New Action(AddressOf UpdateNotifyIcon), Nothing)
Exit Sub
End If
NotifyIcon.BalloonTipIcon = ToolTipIcon.Info
NotifyIcon.BalloonTipTitle = "Download completed"
NotifyIcon.BalloonTipText = "Your download is now completed" & vbNewLine & "Enjoy :)"
NotifyIcon.ShowBalloonTip(5000)
End Sub
Private Sub SetState(state As Boolean)
If (Me.InvokeRequired) Then
Me.Invoke(New Action(Of Boolean)(AddressOf SetState), state)
Exit Sub
End If
If state Then
Connected.Text = "Connected"
Else
Connected.Text = "Disconnected"
End If
End Sub
Please note the beginning of UpdateNotifyIcon
method, this is the trick I used in the first answer to call GUI updating code from background thread. It says: If the method is called from background thread, invoke the same method in GUI thread and exit (the work will be done in GUI thread) and if it is called from GUI thread, skip the If
clause and do the work.
Please note that the event handlers are already called in GUI thread, it is great feature of our component, not the asynchronous method standard.
I recommend you to upgrade to .NET 4.5 and use Async-Await pattern which is more easier than BeginMethod-EndMethod pattern.