GDI+-Fehler 7 (Gdiplus::Win32Error) beim Speichern eines Bilds das aus der Zwischenablage gelesen wurde

Beschreibung

Gerade habe ich folgendes Phänomen erlebt:

Beim Speichern eines GDI+-Bilds (Image– bzw. Bitmap-Klasse) bekam ich einen Statuscode 7 zurückgeliefert, was die Fehlermeldung „Win32Error“ bedeutet.

Also wie bei den meisten GDI+-Fehlermeldungen völlig ungenau und völlig an der wahren Ursache vorbei.

Da ich das ganze im Debugger laufen ließ und dieser bei First-Chance-Exceptions anhält, sah ich die genaue Stelle wo zum ersten Mal ein Fehler auftritt. Nämlich eine Zugriffsverletzung („Access violation“, 0x00000005) innerhalb von GDI+ an der folgenden Stelle im Aufrufstapel:

Bzw. als Textversion:

GdiPlus.dll!GpJpegEncoder::PushPixelData() + 0xa5	
GdiPlus.dll!GpMemoryBitmap::PushIntoSink() + 0x14b	
GdiPlus.dll!GpMemoryBitmap::SaveToStream() + 0xa1	
GdiPlus.dll!GpMemoryBitmap::SaveToFile() + 0x39	
GdiPlus.dll!CopyOnWriteBitmap::DoSave() + 0x206	
GdiPlus.dll!CopyOnWriteBitmap::SaveToFile() + 0x15	
GdiPlus.dll!GpBitmap::SaveToFile() + 0x1f	
GdiPlus.dll!_GdipSaveImageToFile@16() + 0x5f	
zlzdiud.dll!Gdiplus::Image::Save() Zeile 140 + 0x18

Das Bild habe ich aus der Zwischenablage geholt (Zwischenablagenformat CF_DIB) und dann über die Funktion Bitmap::FromBITMAPINFO() ein Bitmap-Objekt daraus gemacht.

Ursache

Nach einigen Tests habe ich herausgefunden, woran es lag:

Sobald ich den Speicher freigegeben hatte, der die Daten aus der Zwischenablage gespeichert hat (ein Instanz der MFC-Klasse CByteArray), schlug der Aufruf von Image::Save() mit oben genannter Fehlermeldung fehl; habe ich den Speicher erst nach dem Aufruf von Image::Save() freigegeben, verlief das Speichern erfolgreich.

Die Ursache ist also (wiedermal) wie bei den meisten GDI+-Routinen: Ich muß selbst dafür sorgen, daß ich den Speicher, aus dem ein ein Bitmap-Objekt erstellt wurde, lange genug reserviere, weil GDI+ sich das nicht selbst in interne Puffer kopiert.

Ist für ehrlich gesagt teilweise ggf. verständlich (Performance, Flexibilität), aber sehr unintuitiv (Komfort)!

Lösung

Den Speicher für ein Bitmap-Objekt, das aus einem Aufruf via Bitmap::FromBITMAPINFO() (oder entsprechend anderer Funktionen) erstellt wurde, muß während der gesamten Lebensdauer des Bitmap-Objekts gültig sein und von der benutzenden Anwendung reserviert bleiben.