You've already accepted an answer but just as an alternative a BackgroundWorker can also be used for something like this. In my case the FTP to get the original files happens very quickly so this snippet from the DoWork event is for downloading those files to a printer. 
Dim cnt As Integer = docs.Count
Dim i As Integer = 1
For Each d As String In docs
    bgwTest.ReportProgress(BGW_State.S2_UpdStat, "Downloading file " & i.ToString & " of " & cnt.ToString)
    Dim fs As New IO.FileStream(My.Application.Info.DirectoryPath & "\labels\" & d, IO.FileMode.Open)
    Dim br As New IO.BinaryReader(fs)
    Dim bytes() As Byte = br.ReadBytes(CInt(br.BaseStream.Length))
    br.Close()
    fs.Close()
    For x = 0 To numPorts - 1
        If Port(x).IsOpen = True Then
            Port(x).Write(bytes, 0, bytes.Length)
        End If
    Next
    If bytes.Length > 2400 Then
        'these sleeps are because it is only 1-way comm to printer so I have no idea when printer is ready for next file
        System.Threading.Thread.Sleep(20000)
    Else
        System.Threading.Thread.Sleep(5000)
    End If
    i = i + 1
Next
In the ReportProgress event... (of course, you need to set WorkerReportsProgress property to True)
Private Sub bgwTest_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles bgwTest.ProgressChanged
    Select Case e.ProgressPercentage
        'BGW_State is just a simple enum for the state,
        'which determines which UI controls I need to use.
        'Clearly I copy/pasted from a program that had 15 "states" :)
        Case BGW_State.S2_UpdStat
            Dim s As String = CType(e.UserState, String)
            lblStatus.Text = s
            lblStatus.Refresh()
        Case BGW_State.S15_ShowMessage
            Dim s As String = CType(e.UserState, String)
            MessageBox.Show(s)
    End Select
End Sub