Benutzer mit den meisten Antworten
Problem mit InteropsServices, Bitmap, Byte-Array in VB.NET

Frage
-
Hi,
guckt euch mal bitte den Screenshot an.
Für die, die den Wald vor lauter Bäumen nicht sehen, der Fehler ist einem Try...Catch-Bereich aufgetreten. Mein Programm wurde abgebrochen. Aber genau DAS sollte doch NICHT passieren, oder? Aber scheinbar passiert es. Ich frage mich nur, wie kann ich das jetzt noch abfangen? :-(
Gruß
Andy- Bearbeitet AndreasMahub Mittwoch, 29. Januar 2014 05:35 Thread-Titel angepasst
Antworten
-
Hallo,
in bestimmten Fällen werden die Exceptions nicht abgefangen. Darunter zählt auch die AccessViolationException. Das hat den Grund, dass diese Fehler u.a. das System beeinflussen können.Um solche Exception nun doch zu behandeln kannst du die HandleProcessCorruptedStateExceptionsAttribute-Klasse verwenden. Unter Verarbeitung von Ausnahmen bei Beschädigungen erfährst du wie man solche Exceptions trotzdem abfängt (meistens keine gute Idee).
U.a. kannst du deine Methode modifizieren:
<HandledProcessCorruptedStateExceptions> _ Sub DoSome() ... End Sub
Besser wäre natürlich dem Fehler vor zu beugen.Koopakiller [kuːpakɪllɐ] (Tom Lambert)
Webseite |
Code Beispiele |
Facebook |
Twitter |
Snippets
C# ↔ VB.NET Konverter
Markiert bitte beantwortende Posts als Antwort und bewertet Beiträge. Danke.- Als Antwort markiert AndreasMahub Dienstag, 28. Januar 2014 05:37
-
Ich selbst habe das Attribut zwar noch nicht verwendet. Soweit ich aber weiß, muss es vor die Sub/Function in der der Fehler abgefangen werden soll. Ein korrekt zurück gegebener Wert bei einer Function ist natürlich trotzdem noch Pflicht.
Wenn ein unbehandelter Fehler auftritt, springt die Exception Methode für Methode zurück, bis ein Catch-Block oder die CLR erreicht wird. Dort wird der Rückgabewert entsprechend nicht benötigt.Das Attribut zeigt der CLR, dass sie das Verhalten von einem .NET vor 4.0 verwenden soll. Dort wurden die Fehler noch problemlos abgefangen. In dem verlinkten Artikel stehen noch 2 Punkte, wie man es noch lösen kann:
- Wenn Sie Ihren Code, der in Microsoft .NET Framework 3.5 erstellt wurde, erneut kompilieren und in .NET Framework 4.0 ausführen möchten, ohne die Quelle aktualisieren zu müssen, können Sie einen Eintrag in Ihrer Anwendungskonfigurationsdatei hinzufügen: legacyCorruptedStateExceptionsPolicy=true.
- Assemblys, die mit .NET Framework 3.5 oder einer früheren Version der Laufzeit kompiliert wurden, werden Ausnahmen infolge einer Beschädigung verarbeiten können (also das alte Verhalten beibehalten), wenn sie unter .NET Framework 4.0 ausgeführt werden.
Das 1. verlangt eine Änderung des Anwendungsmanifests und das 2. verlangt eine kompilation vor .NET 4.0.
Ich würde den Fehler aber nur lokal bei einer Sub/Function abfangen (Attribut setzen) und den Rest auf .NET Standard agieren lassen.Koopakiller [kuːpakɪllɐ] (Tom Lambert)
Webseite |
Code Beispiele |
Facebook |
Twitter |
Snippets
C# ↔ VB.NET Konverter
Markiert bitte beantwortende Posts als Antwort und bewertet Beiträge. Danke.- Als Antwort markiert AndreasMahub Dienstag, 28. Januar 2014 05:36
-
Hallo Andy,
bei einer AccessViolationException in dem gezeigten Beispiel hast Du einen Pufferüberlauf - da irgendwas abzufangen bringt gar nichts, da mit hoher Wahrscheinlichkeit weitere Objekte in Mitleidenschaft gezogen werden. Und am Ende stürzt das Programm auf jeden Fall ab - dann besser früher als später.
Die einzige Hilfe ist, den Code, den man programmiert hat, sorgfältig zu prüfen, denn da steckt mit Sicherheit der Wurm drin. Und die Ursache zu beseitigen.
Da es wohl um eine Bitmap geht, könnte Deine Berechnung der Größen fehlerhaft sein, lies dazu bitte: Charles Petzold Bitmaps und Pixelbits
Gruß Elmar
- Als Antwort markiert AndreasMahub Dienstag, 28. Januar 2014 05:36
-
Hallo Andy,
allerdings ist eine ungerade Zahl an der Stelle ungewöhnlich, denn die "Streifen" werden i. a. auf gerade Werte (oft 4 Bytes) aufgerundet. Und multipliziert mal einer Anzahl davon kommt immer eine gerade Zahl heraus.
Bei Visual Basic immer verdächtig ist eine Array-Dimensonierung. Da Visual Basic (blöderweise) nicht die Größe sonder die Dimension erwartet. Weswegen üblicherweise etwas wie Dim Array(Größe - 1) programmiert werden muss. Eine Stelle wäre (beim Lesen)
ReDim res.Bytes(res.BytesTotal)
richtig wäre aber:
ReDim res.Bytes(res.BytesTotal - 1)
Wobei ich auf ReDim - denn das kopiert nur überflüssigerweise - verzichten würde und schreiben:
res.Bytes = new Byte(res.BytesTotal - 1) {}
Das es nicht häufiger scheppert, liegt vermutlich daran, dass intern die Speicherausrichtung auf gerade Adressen erfolgt und so ein "Sicherheitsbereich" vorhanden ist.
Gruß Elmar
- Als Antwort markiert AndreasMahub Mittwoch, 29. Januar 2014 05:36
Alle Antworten
-
Hallo,
in bestimmten Fällen werden die Exceptions nicht abgefangen. Darunter zählt auch die AccessViolationException. Das hat den Grund, dass diese Fehler u.a. das System beeinflussen können.Um solche Exception nun doch zu behandeln kannst du die HandleProcessCorruptedStateExceptionsAttribute-Klasse verwenden. Unter Verarbeitung von Ausnahmen bei Beschädigungen erfährst du wie man solche Exceptions trotzdem abfängt (meistens keine gute Idee).
U.a. kannst du deine Methode modifizieren:
<HandledProcessCorruptedStateExceptions> _ Sub DoSome() ... End Sub
Besser wäre natürlich dem Fehler vor zu beugen.Koopakiller [kuːpakɪllɐ] (Tom Lambert)
Webseite |
Code Beispiele |
Facebook |
Twitter |
Snippets
C# ↔ VB.NET Konverter
Markiert bitte beantwortende Posts als Antwort und bewertet Beiträge. Danke.- Als Antwort markiert AndreasMahub Dienstag, 28. Januar 2014 05:37
-
Hallo Tom,
danke für deine ausführliche Antwort. Äh, ja, natürlich würde ich SEHR GERNE den Fehler vorbeugen, aber leider passiert er einfach, und kann mir nicht erklären warum.
Was ich viel schlimmer finde/fand, dass ich ihn nicht abfangen konnte um entsprechend darauf zu reagieren.
Aber mal eine andere Frage: Ich verstehe dein Attribute HandledProcessCorruptedStateExceptions noch nicht so ganz. Soll der so ganz allgemein in die Klasse und die Sub-Routine wird dann trotz Try...Catch aufgerufen? Wenn JA, was macht dann die eigentliche Funktion? Die müsste dann ja ohne Antwort (Return) absterben, oder?!
Gruß
Andy -
Ich selbst habe das Attribut zwar noch nicht verwendet. Soweit ich aber weiß, muss es vor die Sub/Function in der der Fehler abgefangen werden soll. Ein korrekt zurück gegebener Wert bei einer Function ist natürlich trotzdem noch Pflicht.
Wenn ein unbehandelter Fehler auftritt, springt die Exception Methode für Methode zurück, bis ein Catch-Block oder die CLR erreicht wird. Dort wird der Rückgabewert entsprechend nicht benötigt.Das Attribut zeigt der CLR, dass sie das Verhalten von einem .NET vor 4.0 verwenden soll. Dort wurden die Fehler noch problemlos abgefangen. In dem verlinkten Artikel stehen noch 2 Punkte, wie man es noch lösen kann:
- Wenn Sie Ihren Code, der in Microsoft .NET Framework 3.5 erstellt wurde, erneut kompilieren und in .NET Framework 4.0 ausführen möchten, ohne die Quelle aktualisieren zu müssen, können Sie einen Eintrag in Ihrer Anwendungskonfigurationsdatei hinzufügen: legacyCorruptedStateExceptionsPolicy=true.
- Assemblys, die mit .NET Framework 3.5 oder einer früheren Version der Laufzeit kompiliert wurden, werden Ausnahmen infolge einer Beschädigung verarbeiten können (also das alte Verhalten beibehalten), wenn sie unter .NET Framework 4.0 ausgeführt werden.
Das 1. verlangt eine Änderung des Anwendungsmanifests und das 2. verlangt eine kompilation vor .NET 4.0.
Ich würde den Fehler aber nur lokal bei einer Sub/Function abfangen (Attribut setzen) und den Rest auf .NET Standard agieren lassen.Koopakiller [kuːpakɪllɐ] (Tom Lambert)
Webseite |
Code Beispiele |
Facebook |
Twitter |
Snippets
C# ↔ VB.NET Konverter
Markiert bitte beantwortende Posts als Antwort und bewertet Beiträge. Danke.- Als Antwort markiert AndreasMahub Dienstag, 28. Januar 2014 05:36
-
Hallo Andy,
bei einer AccessViolationException in dem gezeigten Beispiel hast Du einen Pufferüberlauf - da irgendwas abzufangen bringt gar nichts, da mit hoher Wahrscheinlichkeit weitere Objekte in Mitleidenschaft gezogen werden. Und am Ende stürzt das Programm auf jeden Fall ab - dann besser früher als später.
Die einzige Hilfe ist, den Code, den man programmiert hat, sorgfältig zu prüfen, denn da steckt mit Sicherheit der Wurm drin. Und die Ursache zu beseitigen.
Da es wohl um eine Bitmap geht, könnte Deine Berechnung der Größen fehlerhaft sein, lies dazu bitte: Charles Petzold Bitmaps und Pixelbits
Gruß Elmar
- Als Antwort markiert AndreasMahub Dienstag, 28. Januar 2014 05:36
-
Hallo Andy,
bei einer AccessViolationException in dem gezeigten Beispiel hast Du einen Pufferüberlauf - da irgendwas abzufangen bringt gar nichts, da mit hoher Wahrscheinlichkeit weitere Objekte in Mitleidenschaft gezogen werden. Und am Ende stürzt das Programm auf jeden Fall ab - dann besser früher als später.
Die einzige Hilfe ist, den Code, den man programmiert hat, sorgfältig zu prüfen, denn da steckt mit Sicherheit der Wurm drin. Und die Ursache zu beseitigen.
Da es wohl um eine Bitmap geht, könnte Deine Berechnung der Größen fehlerhaft sein, lies dazu bitte: Charles Petzold Bitmaps und Pixelbits
Gruß Elmar
Guten Morgen Elmar,
vielen Dank für deine Hilfe. Dann muss ich irgendwo einen ganz minimalen Fehler drin haben. Denn es passiert nur bei einer einzigen Projektdatei (eigenes Format). Ansonsten lief mein Programm in den letzten 4 Monaten einwandfrei durch. Auch Nachts, bei den Testläufen, nie ausgestiegen. Bis jetzt zu diesem "Ding" kam. Hoffentlich wird das nicht zur "Nadel im Heuhaufen" :-D
Gruß
Andy -
Hallo nochmal,
seltsam, der Fehler passiert wirklich bei dem gleichen Song. Zur kurzen Info: Bei diesen Projektdateien handelt es sich um ein eigenes Dateiformat. Darin enthalten sind die MP3 selbst, ein paar weitere Infos als String oder Integer, ein paar Arrays, darunter ein Byte-Array mit der Bitmap und ein Byte-Array mit den DMX-Daten für die Lichtansteuerung.
Bisher habe ich mehrere hunderte Songs (Projekte) wo alles einwandfrei funktioniert. Außer bei diesem einen der weder großartig länger oder großartig kürzer ist. Er ist "normal" wie jeder andere auch.
Um beim speichern aus einer Bitmap ein Byte()-Array zu erzeugen, verwende ich folgende Funktion:
''' <summary> ''' Liest ein übergebenes Bitmap mit Hilfe von LockBits aus und gibt ein Byte-Array zurück. ''' </summary> ''' <param name="BitmapToRead"></param> ''' <returns></returns> ''' <remarks></remarks> Public Function ReadBitmapInAByteArray(BitmapToRead As Bitmap) As BitmapByteDatasStructure Dim res As New BitmapByteDatasStructure Dim TempBitmap As System.Drawing.Bitmap = Nothing Dim bmdata As Drawing.Imaging.BitmapData = Nothing ' Neues Bitmap in der Größe der Picturebox generieren res.BitmapPixelformat = BitmapToRead.PixelFormat res.OrgBitmapWidth = BitmapToRead.Width res.OrgBitmapHeight = BitmapToRead.Height TempBitmap = New System.Drawing.Bitmap(res.OrgBitmapWidth, res.OrgBitmapHeight, res.BitmapPixelformat) TempBitmap = BitmapToRead.Clone bmdata = TempBitmap.LockBits(New System.Drawing.Rectangle(0, 0, res.OrgBitmapWidth, res.OrgBitmapHeight), Imaging.ImageLockMode.ReadOnly, res.BitmapPixelformat) ' Ausrechnen wieviel Speicher pro Zeile und Gesamtspeicher benötigt werden res.BytesPerLine = bmdata.Stride res.BytesTotal = res.BytesPerLine * bmdata.Height ReDim res.Bytes(res.BytesTotal) Runtime.InteropServices.Marshal.Copy(bmdata.Scan0, res.Bytes, 0, res.BytesTotal) TempBitmap.UnlockBits(bmdata) Return res End Function
Und um beim laden aus einem Byte-Array eine Bitmap zu erzeugen diese Funktion:
Public Function WriteBytesInABitmap(BitmapToWrite As Bitmap, Width As Integer, Height As Integer, ByteArray() As Byte) As Bitmap Dim res As Bitmap = Nothing If BitmapToWrite IsNot Nothing Then BitmapToWrite.Dispose() End If res = New Bitmap(Width, Height, Imaging.PixelFormat.Format32bppArgb) Dim bmdata As Drawing.Imaging.BitmapData bmdata = res.LockBits(New Rectangle(0, 0, Width, Height), Imaging.ImageLockMode.WriteOnly, res.PixelFormat) bmdata.Width = Width bmdata.Height = Height Try ' Möglichen Fehler abfangen wie z.B. ' Es wurde versucht, im geschützten Speicher zu lesen oder zu schreiben. Dies ist häufig ein Hinweis darauf, dass anderer Speicher beschädigt ist. Runtime.InteropServices.Marshal.Copy(ByteArray, 0, bmdata.Scan0, ByteArray.Length) Catch ex As Exception MsgBox("Beim Schreiben des Bytes-Arrays in eine Bitmap ist es zu einem Fehler gekommen." & vbCrLf & vbCrLf & "Details:" & vbCrLf & ex.Message, MsgBoxStyle.Critical) End Try res.UnlockBits(bmdata) Return res End Function
Könnt ihr auf anhieb eine "Macke" erkennen? Wie gesagt, bis jetzt haben diese Funktionen in den letzten Monaten einwandfrei funktioniert und hatte gehofft mit dem Kapitel endlich abgeschlossen zu haben. Aber dem ist wohl nicht so :-/
Beim erstellen der Bitmap aus einem Byte-Array schmiert die Funktion an dieser Stelle ab:
Runtime.InteropServices.Marshal.Copy(ByteArray, 0, bmdata.Scan0, ByteArray.Length)
Gruß
Andy
-
Hallo Andy,
Verursacher könnte hier die inkonsistente Verwendung von PixelFormat sein. Zu Anfang legst Du ein Bitmap mit 32bppArgb (4 Bytes per Pixel) an. Arbeitest aber beim LockBits mit res.PixelFormat. Z. B. bei Indexed Formaten oder bei 32 RGB (ohne Alpha) wird weniger Speicher fixiert, siehe auch die Formeln bei Bob Powell.
Gruß Elmar
-
Hallo Elmar,
ich glaube was gefunden zu haben. Mir ist beim vergleichen der Zahlen etwas aufgefallen.
res.Bytes.Length = 3.829.761 ' Was mir zurückgegeben wurde
res.bytesTotal = 3.829.760 ' Selbst ausgerechnet bmdata.Stride * bmdata.Heightres.Bytes.Length war immer eines Höher, als das was ich ausgerechnet habe. Ich habe dann in der Routine wo das ByteArray in eine Bitmap geschrieben wird, kackfrech folgendes gemacht:
Runtime.InteropServices.Marshal.Copy(ByteArray, 0, bmdata.Scan0, ByteArray.Length - 1)
Seit dem wird auch die Datei geöffnet wo meine Funktion immer ausgestiegen ist.
Aber das wird doch jetzt nicht die Lösung sein, oder? ist bestimmt pfusch was ich da gemacht habe...
Gruß
Andy -
Hallo Andy,
allerdings ist eine ungerade Zahl an der Stelle ungewöhnlich, denn die "Streifen" werden i. a. auf gerade Werte (oft 4 Bytes) aufgerundet. Und multipliziert mal einer Anzahl davon kommt immer eine gerade Zahl heraus.
Bei Visual Basic immer verdächtig ist eine Array-Dimensonierung. Da Visual Basic (blöderweise) nicht die Größe sonder die Dimension erwartet. Weswegen üblicherweise etwas wie Dim Array(Größe - 1) programmiert werden muss. Eine Stelle wäre (beim Lesen)
ReDim res.Bytes(res.BytesTotal)
richtig wäre aber:
ReDim res.Bytes(res.BytesTotal - 1)
Wobei ich auf ReDim - denn das kopiert nur überflüssigerweise - verzichten würde und schreiben:
res.Bytes = new Byte(res.BytesTotal - 1) {}
Das es nicht häufiger scheppert, liegt vermutlich daran, dass intern die Speicherausrichtung auf gerade Adressen erfolgt und so ein "Sicherheitsbereich" vorhanden ist.
Gruß Elmar
- Als Antwort markiert AndreasMahub Mittwoch, 29. Januar 2014 05:36
-
Guten Morgen,
sorry wenn ich immer mit so langen "Pause" antworte, aber ich bin derzeit etwas "angeschlagen" :-/
Elmar, Tom, ich danke euch, ich habe gestern Abend mit den ganzen Änderungen einen Testlauf machen lassen und jetzt scheint es zu funktionieren. Es "scheppert" jetzt bei keiner einzigen Datei mehr, bis jetzt jedenfalls :-)
Ich habe auch gleich mal den Thread-Titel angepasst damit andere ggf. mit diesem Thread auch etwas anfangen können :-)
Gruß
Andy