Hexdump ohne Hexerei ...

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 10 Antworten in diesem Thema. Der letzte Beitrag () ist von RodFromGermany.

    Hexdump ohne Hexerei ...

    Hi,

    ich möchte gern eine Routine schreiben, die den Inhalt einer Datei hexadezimal und im Character Format anzeigt. Dazu habe ich die Datei zunächst mit ReadlAllBytes in einen Byte Array eingelesen.

    Die einzelnen Bytes kann ich sehr einfach mit .Tostring("x2") in das hexadezimale äquivalent umwandeln.

    Das Anzeigen der Character Darstellung habe ich mit Cstr() versucht. Aber das liefert mir nicht das Zeichen, sondern gibt einfach den numerischen Wert des Byte im Character Format zurück.

    VB.NET-Quellcode

    1. For Each myByte In FileByteArray
    2. strDisplay &= CStr(myByte) & " "


    Anbei ein Screenshot zur Erbauung. Wie man sieht ist die "rechte" Seite des HexDisplays nicht richtig. Das ist es also nicht, was ich will.

    Nun weiß ich ja, dass man ein Byte nicht einfach in ein Zeichen umsetzen kann - was angezeigt wird, hängt u.a. von der Encodierung ab. Ich möchte einfach das Byte in ein Zeichen umwandeln, wenn dies "problemlos" möglich ist. z.B. ist x'20 ein "Blank" und x'31 die Ziffer "1". Wenn es sich um nichtdarstellbare Sonderzeichen handelt, dann soll ein "Punkt" ausgegeben werden. Also etwas in dieser Art:

    "hugo1234....ABCD

    Ich habe versucht die Character Darstellung wie folgt zu erzeugen:

    VB.NET-Quellcode

    1. System.Text.Encoding.ASCII.GetString(myByte)


    Aber das scheitert,weil GetString einen ARRAY von Bytes erwartet und kein einzelnes Byte.

    Any bright ideas, wie ich das lösen kann ?

    LG
    Peter

    Bilder
    • s 2017-11-23 17-02-475.jpg

      22,65 kB, 1.187×106, 109 mal angesehen
    @Peter329 So was:

    VB.NET-Quellcode

    1. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    2. ListBox1.Items.Clear()
    3. For i = 0 To 255
    4. Dim txt = String.Format("{0:X2} {1}", i, Convert.ToChar(i))
    5. ListBox1.Items.Add(txt)
    6. Next
    7. End Sub

    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Supi ! Das war es was ich gesucht hatte !

    Mein HexDisplay sieht jetzt schon ganz manierlich aus. (s. Anhang)

    Allerdings würde ich jetzt noch gern zwei Funktionen dazu stricken.

    1. Wenn man wie im angehängten Bildchen Spalten mit der Maus auswählt, dann wäre es doch schön, wenn die entsprechenden Zeichen im rechten "Charakter Display" ebenfalls eingefärbt würden. Das wäre hilfreich beim Zuordnen der Hexzeichen. Bei einer Textbox könnte man das mit .Select(...) realisieren. Ich fürchte bei einer Datagridview Zelle ist das nicht so ganz einfach. Oder irre ich mich da ?

    2. Ich würde gern nach dem ausgewählten HexString suchen ... Die Basis ist der eingelesene ByteArray. Mir fällt da eigentlich nur ein, die ausgewählten Bytes in einen String umzuwandeln (das ist natürlich unproblematisch) und dann auch den eingelesenen ByteArray ebenfalls in einen String zu konvertieren. Das Ist dann allerdings ein Problem, weil die eingelesene Datei vielleicht ein paar MB groß sein kann ! Hat vielleicht jemand irgend welche schlauen Ideen, wie man das mit dem Suchen performant hinbekommen kann ?

    LG
    Peter
    Bilder
    • s 2017-11-23 20-33-162.jpg

      89,02 kB, 868×370, 89 mal angesehen
    Spät kam die Einsicht. Doch sie kam ... :)

    Ich habe eine Lösung gefunden, die eigentlich völlig naheliegend ist. Man darf den "Character Display nicht mit einer einzigen Spalte abbilden, sondern man muss für jedes Zeichen eine eigene Spalte definieren. Und schon wird die Sache ganz einfach (s. angehängtes Bildchen).

    So habe ich das kodiert:

    VB.NET-Quellcode

    1. Private Sub dgvHexDump_SelectionChanged(sender As Object, e As EventArgs) Handles dgvHexDump.SelectionChanged
    2. If blnDisableSelectionChanged Then Exit Sub 'Avoid recursion
    3. blnDisableSelectionChanged = True
    4. If dgvHexDump.SelectedCells.Count > 0 Then
    5. For k As Integer = 0 To dgvHexDump.SelectedCells.Count - 1
    6. Dim ik As Integer = dgvHexDump.SelectedCells(k).RowIndex
    7. Dim jk As Integer = dgvHexDump.SelectedCells(k).ColumnIndex
    8. If jk = 0 OrElse 'Exclude spacing columns
    9. jk = 9 OrElse
    10. jk = 18 OrElse
    11. jk = 36 Then
    12. dgvHexDump.Item(jk, ik).Selected = False
    13. Continue For
    14. End If
    15. Dim jk2 As Integer
    16. If jk < 18 Then jk2 = jk + 18 'Select character
    17. If jk > 18 Then jk2 = jk - 18 'Select hex character
    18. dgvHexDump.Item(jk2, ik).Selected = True
    19. Next
    20. End If
    21. blnDisableSelectionChanged = False
    22. End Sub


    Die Sache mit der "Recursion" dürfte klar sein - ich setze ja die Selection innerhalb der Event Routine ... und da darf ich nicht erneut feuern

    Trotzdem geht da irgendwas schief !

    Wenn man LANGSAM mit der Maus die Felder markiert ist alles Palletti ! Nur wenn man das schnell macht, klappt das nicht, weil es dann zu "Überholvorgängen" kommt ... die Maus selektiert ein neues Feld, ehe die Verarbeitung der vorangehenden Auswahl abgeschlossen ist. (s. angehängtes Bildchen).

    Im dümmsten Fall kommt es dann zu Stack-Überläufen, weil der erste Aufruf endet und das Flag blnDisabled ausschaltet, während die zweite Routine noch läuft ! Oder es kommt zu Range Verletzungen, weil die Maus die SelectedCells Auflistung verändert, während die vorangehende Routine diese Auflistung auswertet.

    Ich weiß, das ist vielleicht viel verlangt ... aber versteht jemand das knifflige Problem und kann mir dabei (nachsichtig) helfen. Das dürfte eine Sache für absolute Spezialisten sein ...

    LG
    Peter
    Bilder
    • s 2017-11-24 11-07-143.jpg

      99,54 kB, 925×378, 78 mal angesehen
    • s 2017-11-24 11-19-224.jpg

      99,35 kB, 925×378, 84 mal angesehen

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Peter329“ ()

    Peter329 schrieb:

    weil es dann zu "Überholvorgängen" kommt
    Gute Beschreibung.
    Wenn Du die Prozedur identifiziert hast, in der das vorkommt, machst Du da ein SyncLook rein und feddich.
    docs.microsoft.com/de-de/dotne…ements/synclock-statement
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Also erst einmal recht herzlichen Dank dafür, dass du dich freundlicherweise mit meinen Problemem befasst. Das sollte man nicht vergessen, denn schließlich kann ich dir außer einem virtuellen Daumen nix zukommen lassen. Nicht mal ein großes Pils, dass ich meinen Ratgebern im normalen Leben sehr gern zukommen lassen würde. :)

    Ich hab den LInk gelesen:

    "Acquires an exclusive lock for a statement block before executing the block. "

    Aber das scheint mir leider das Problem nicht zu lösen! Denn mit dem Flag "blnDisabled..." erziele ich genau diesen Effekt ja schon.

    Das Problem liegt doch eher darin, dass die "SelectionChanged" Routine, sowohl mit der Mouse als auch programmatisch gefeuert werden kann ! Insbesondere ist das blöde, dass die Mouse die "SelectedCells" Liste verändern kann, während die Verarbeitung noch läuft. Da hilft auch kein Lock, oder ?

    Ideal wäre es, wenn ich das Event "SelectionChanged" suspendieren könnte, während die Verarbeitung läuft. Ich hab so etwas wie folgt versucht:

    VB.NET-Quellcode

    1. Private Sub dgvHexDump_SelectionChanged(sender As Object, e As EventArgs) Handles dgvHexDump.SelectionChanged
    2. Debug.Print("Selection Start intCount=" &
    3. intDisableSelectionChanged.ToString &
    4. " Selcount=" & dgvHexDump.SelectedCells.Count.ToString &
    5. " Enabled=" & dgvHexDump.Enabled.ToString)
    6. If intDisableSelectionChanged > 0 Then Exit Sub 'Avoid recursion
    7. dgvHexDump.Enabled = False
    8. intDisableSelectionChanged += 1


    Ich zähle jetzt, wie oft, das Ereignis SelectionChanged feuert ... und beim ersten Feuern disable ich die DatagridView ...

    Jetzt krieg ich jedenfalls keinen Stack Overflow mehr. Aber die Routine läuft jetzt sehr "hakelig" und manche Zellen werden (fehlerhafterweise) beim Markieren "vergessen".

    Das Problem verlangt schon einiges an abstraktem Denken ... und so richtig verstehe ich (trotz Debug.Print Trace) immer noch nicht wie der Hase läuft.

    Aber ich bin mir fast sicher, dass RFG mit seiner unermesslichen Erfahrung da etwas zur Lösung beitragen kann. :)

    LG
    Peter

    Peter329 schrieb:

    etwas zur Lösung beitragen kann
    Vielleicht postest Du mal das (bereinigte) Projekt oder zumindest einen Teil, der den Effekt reproduziert.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    jau, das will ich gern machen ... weil ich das Problem ausgesprochen spannend finde. Aber der HexEditor ist Teil eines Browsers mit ca. 20.000 Zeilen Code. Das muss ich dann erst mal in ein kleines Testprojekt einkochen, weil man sonst "den Wald vor lauter Bäumen" nicht sieht. Ein bissl Geduld also ...

    LG
    Peter
    @Peter329 Immer mit der Ruhe.
    Son Hex-Editor hatte ich mal zu Windows 3-Zeiten, das ist schon ein paar Wochen her.
    Inzwischen brauch ich so was eig nicht mehr.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Hi,

    falls es jemanden interessieren sollte, ich hab mein Problem jetzt gelöst !

    Man muss einfach einen Schritt zurück treten und nachdenken. Dann wird alles oft ganz einfach.

    Dass ich in der SelectionChanged Routine, das Ereignis SelectionChanged nochmal auslöse, ist nicht blöde. Sondern es ist SAUBLÖDE ! :D

    Mit dem Räderwerk (wie EDR es nennt) des .NET sollte man sich besser nicht auf einen Disput einlassen. Und tatsächlich benötige ich das ja auch gar nicht. Ich will die Zellen doch nicht AUSWÄHLEN, sondern ich will sie EINFÄRBEN. Und durch das Färben löse ich kein SelectionChanged Event aus ! Voila!

    Natürlich verschwindet die Färbung jetzt nicht mehr von selbst, etwa durch dgv.ClearSelection(). Zu diesen Zeitpunkt "weiß" ich auch nicht mehr, welche Zellen zuvor ausgewählt waren, denn die dgv.SelectedCells() Liste ist ja bereits geändert. Deshalb merke ich mir die Koordinaten der von mir eingefärbten Zellen in einer Liste. Und bei jedem neuen SelectionChanged Event setze ich die Farbe zurück.

    So hab ich das jetzt gelöst:

    VB.NET-Quellcode

    1. Public Structure Coord
    2. Dim row As Integer
    3. Dim col As Integer
    4. End Structure
    5. Dim CoordList As New List(Of Coord)


    VB.NET-Quellcode

    1. Private Sub dgvHexDump_SelectionChanged(sender As Object, e As EventArgs) Handles dgvHexDump.SelectionChanged
    2. For k As Integer = 0 To CoordList.Count - 1 'Reset all previously colored cells
    3. dgvHexDump.Item(CoordList(k).col, CoordList(k).row).Style.BackColor = SystemColors.Window
    4. Next
    5. CoordList.Clear() 'Empty colored cells coordinate list
    6. Dim currentRow As Integer = dgvHexDump.RowCount 'Set current row high index value
    7. If dgvHexDump.SelectedCells.Count > 0 Then 'Cells selected
    8. For k As Integer = 0 To dgvHexDump.SelectedCells.Count - 1 'Process all selected cells
    9. Dim i As Integer = dgvHexDump.SelectedCells(k).RowIndex
    10. If currentRow > i Then currentRow = i 'Get minimum selected cell row index
    11. Dim j As Integer = dgvHexDump.SelectedCells(k).ColumnIndex
    12. If j = 0 OrElse 'Rowid column
    13. j = 1 OrElse 'Address column
    14. j = 10 OrElse 'Spacing columns
    15. j = 19 OrElse
    16. j = 37 Then Continue For 'Skip columns
    17. Dim j2 As Integer 'Alternate column index
    18. If j <= 18 Then j2 = j + 18 'Select character column
    19. If j >= 19 Then j2 = j - 18 'Select hex character column
    20. dgvHexDump.Item(j2, i).Style.BackColor = Color.Khaki
    21. Dim myCoord As New Coord 'Remember colored cell coordinates
    22. myCoord.row = i
    23. myCoord.col = j2
    24. CoordList.Add(myCoord)
    25. Next
    26. End If
    27. If currentRow = dgvHexDump.RowCount Then currentRow = -1 'No cell selected
    28. lblCount.Text = "Line " & (currentRow + 1).ToString("n0") & " of " & dgvHexDump.RowCount.ToString("n0")
    29. End Sub


    Das Dingens funktioniert jetzt hervorragend ! Für Verbessungsvorschläge meines möglicherweise "hausbackenen" Codings wäre ich dankbar.

    Vielen Dank nochmal an RFG für seine unendliche Geduld ... :)

    LG
    Peter

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Peter329“ ()

    Peter329 schrieb:

    VB.NET-Quellcode

    1. If j = 0 OrElse 'Rowid column
    2. j = 1 OrElse 'Address column
    3. j = 10 OrElse 'Spacing columns
    4. j = 19 OrElse
    5. j = 37 Then Continue For 'Skip columns
    machst Du

    VB.NET-Quellcode

    1. Select Case j
    2. Case 1, 10, 19, 37
    3. Continue For
    4. End Select
    das ist etwas übersichtlicher
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!