0

I'm reading the binary file using file stream and I'm using async-await in order to not freeze the UI

Await Task.Run(Sub()
 For i = 0 To file_len Step 512
   fs.Seek(i, SeekOrigin.Begin)       ' look into the next 4096 of the file
   fs.Read(array_for_fat, 0, 512)             ' read it to the buffer
   label2.text = i                      'getting the error here
  Next
 End Sub)

the problem is that I cant update ( I want to update the info on how much the app already have read )

label2.text = i

from this thread

any solution please, how to update the textbox from the async-await sub or maybe I can use something better that fits my purpose? full code sample:

Private Async Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click

        OpenFileDialog1.FileName = ".bin file"
        If OpenFileDialog1.ShowDialog() = Windows.Forms.DialogResult.OK Then
            Label9.Text = System.IO.Path.GetFullPath(OpenFileDialog1.FileName)
        Else
            Exit Sub
        End If

        file_len = New System.IO.FileInfo(Label9.Text).Length.ToString
       If Label9.Text.Contains(".bin") Or Label9.Text.Contains(".BIN") Then
        Else
            Label9.Text = ""
       End If
        Using fs As New FileStream(Label9.Text, FileMode.Open, FileAccess.Read, FileShare.None)
      
            Await Task.Run(Sub()
               For i = 0 To file_len Step 512
                 fs.Seek(i, SeekOrigin.Begin)       ' look into the next 4096 of the file
                 fs.Read(array_for_fat, 0, 512)             ' read it to the buffer
                
              For Each position In Form1.Locate(array_for_fat, bytesToFind)
                    If array_for_fat(0) <> 1 Or array_for_fat(52) <> &H41 Then Continue For                
    'searching for proper data in the 512byte dump from file
                      i = file_len   'stop reading the file when found the dump we need
                       For b = 28 To 100 Step 2
                      If array_for_fat(b) = 0 Then                                                                                                         ' found? execute
                       Exit For
                      End If
                      result &= array_for_fat(b).ToString("X") & array_for_fat(b + 1).ToString("X")
                       Next
                      Exit For
                       Next
                Next
             End Sub)
 end using
end sub

And the Locate sub code ( searching the bytes patter in the bytes array):

Public Function Locate(ByVal self As Byte(), ByVal candidate As Byte()) As Integer()
  If IsEmptyLocate(self, candidate) Then Return Empty
  Dim list = New List(Of Integer)()

 For i As Integer = 0 To self.Length - 1
    If Not IsMatch(self, i, candidate) Then Continue For
    list.Add(i)
 Next

 Return If(list.Count = 0, Empty, list.ToArray())
End Function

Public Function IsEmptyLocate(ByVal array As Byte(), ByVal candidate As 
  Byte()) 
     As Boolean
    Return array Is Nothing OrElse candidate Is Nothing OrElse array.Length = 
     0 OrElse candidate.Length = 0 OrElse candidate.Length > array.Length
 End Function

Public Function IsMatch(ByVal array As Byte(), ByVal position As Integer, 
 ByVal candidate As Byte()) As Boolean
If candidate.Length > (array.Length - position) Then Return False

For i As Integer = 0 To candidate.Length - 1
    If array(position + i) <> candidate(i) Then Return False
Next

Return True

End Function

Jeezy Wonder
  • 31
  • 1
  • 8
  • Don't `Task.Run()` anything, just await [FileStream.ReadAsync](https://learn.microsoft.com/en-us/dotnet/api/system.io.filestream.readasync). – Jimi Sep 19 '20 at 16:54
  • 1
    Note that the Docs show only a method that requires a `CancellationToken`. While you may want to use it, there's also an overload that doesn't need one. - You should disable that Button and re-enable it when the procedure is completed. (Btw, avoid `Task.Run()` there. Really) – Jimi Sep 19 '20 at 17:06
  • @Jimi just read async is till hanging the UI, I guess it is because seeking and for next loop – Jeezy Wonder Sep 21 '20 at 15:01

2 Answers2

0

Take a look at How to: Make thread-safe calls to Windows Forms controls for more details.

Replace label2.text = i with this call (from the article):

Private Sub WriteTextSafe(ByVal value As String)
    If label2.InvokeRequired Then
        Dim d = New SafeCallDelegate(AddressOf WriteTextSafe)
        label2.Invoke(d, New Object() {value})
    Else
        label2.Text = value
    End If
End Sub

Another example: How can I cancel a thread?

''' <summary>
''' Thead-safe add value to list.
''' </summary>
''' <param name="value">The value.</param>
Private Sub UpdateLabel2(value As String)
    If label2.InvokeRequired Then
        label2.Invoke(Sub() label2.Text = value)
    Else
       label2.Text = value
    End If
End Sub

Tip: Make your coding-life less error-prone by naming your controls and variables then label1, label2. After a year+ when you return to this code, will you remember what label1 is without having to look at the form?

Pete -S-
  • 542
  • 5
  • 13
  • i have tried both examples, and it works but it doesn't execute the code that I have added in the topic edit ( I didn't put it early because I thought it would work with your examples) Locate sub ( which I am using the find the bytes pattern in 512 bytes array, I'm actually trying to find the data in the file) and the problem is that if I use your both examples the locate sub is not executing the app just continue to read the file till the end, but without displaying the info on the label the app executes the locate sub perfectly – Jeezy Wonder Sep 21 '20 at 15:26
  • @JeezyWonder: I suspect the issue is `Form1.Locate` because that is on the UI thread, you would have to incorporate the same InvokeRequired. Can you move the `Locate` in a class that you create inside of your threaded code? – Pete -S- Sep 21 '20 at 16:55
  • yes i can move to the class where my above code is located, should I just move it or add some invokerequried to this locate sub? – Jeezy Wonder Sep 22 '20 at 10:55
  • @JeezyWonder My experience, yes... in order to update the UI thread from a thread. – Pete -S- Sep 22 '20 at 12:42
  • i have put the locate sun in the class where the code above located - the same problem, should I make this locate sun async or i don't know – Jeezy Wonder Sep 23 '20 at 17:47
  • What does Form1.Locate do? – Pete -S- Sep 23 '20 at 19:16
  • it is searching byte pattern in a byte array (I'm reading 512 bytes from 2gb binary file and check it for the pattern with locate sub) I have taken locate sub from this thread: https://stackoverflow.com/questions/283456/byte-array-pattern-search - the Jb Evain's solution ( the one with 54 upvotes), I have converted it to visual basic lang. I have also added two more subs which forgot to add early the IsEMptySub and IsMatch sub which both work with Locate sub – Jeezy Wonder Sep 24 '20 at 15:48
  • @JeezyWonder were you able to get this code to work without introducing multi-threading first? – Pete -S- Sep 24 '20 at 16:47
  • yes it is working like a charm, it is also working in a async await sub, but when I want to update label text ui with your code example , it is stops working, the label is updating value and the file is reading but seems like it skips the locate sub Code inside of async await sub, but again if I don’t updating the label it is working – Jeezy Wonder Sep 24 '20 at 18:03
  • @JeezyWonder and no exceptions thrown? – Pete -S- Sep 24 '20 at 19:24
  • @JeezyWonder I suspect that the exception is being eaten in the thread; however, unfortunately, I'm out of ideas without running the code. – Pete -S- Sep 25 '20 at 14:33
  • okay the code is working, but why it take x100 times slower then I read without updating the label, I'm using your WriteTextSafe(i) in the Await Task.Run(Sub() – Jeezy Wonder Sep 25 '20 at 16:47
  • `WriteTextSafe` is the method from Microsoft's documentation. Did you try the other option? I wonder if there is something else going on. Threading can be fickle to debug. – Pete -S- Sep 27 '20 at 02:43
0

You can use Progress(Of T) for that:

Dim progress As IProgress(Of Integer) = New Progress(Of Integer)(Sub (value as Integer) label2.Text = value)
Await Task.Run(Sub()
 For i = 0 To file_len Step 512
   fs.Seek(i, SeekOrigin.Begin)
   fs.Read(array_for_fat, 0, 512)
   progress(i)
  Next
 End Sub)
Paulo Morgado
  • 14,111
  • 3
  • 31
  • 59
  • Morgano, I have added the code with Locate sub in the topic edit( which I am using the find the bytes pattern in 512 bytes array, I'm actually trying to find the data in the file) and the problem is that if I use your progress T the locate sub is not executing the app just continue to read the file till the end, but without displaying the info on the label the app executes the locate sub perfectly – Jeezy Wonder Sep 21 '20 at 15:24
  • Sorry, Visual Basic is a read-only language for me. – Paulo Morgado Sep 21 '20 at 18:55