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.

Ängstliche Straßenlaternen

Prolog

Sicher kennt Ihr den Spaß von Jugendlichen, das „Straßenlaternenaustreten“, also mit dem Fuß gegen den Kasten unten schaltet die Lampe für eine gewisse Zeit aus. Natürlich habt Ihr so was nie selber gemacht, nur vom Hören-Sagen. Ich natürlich nicht.

Meine Erlebnisse

Folgendes beobachte ich seit ca. 5 Jahren in unregelmäßigen Abständen:

Wenn ich zu Fuß, per Fahrrad oder auch ab und an mit dem Auto an Straßenlaternen vorbeikomme, dann gehen die Lampen just in dem Moment aus, wenn ich daran vorbeikommen.

Also, es passiert nicht mit jeder Laterne und auch nicht jedes Mal wenn ich vorbeikomme, aber die Signifikanz ist deutlich zu hoch, um jedes Mal Zufall zu sein.

Glauben tut mir das natürlich keiner. Lachen mich alle aus, wie immer halt. Dem Jogi habe ich das während des Laufens mal erzählt, und natürlich hat er mich ausgelacht; jedoch genau 10 Sekunden später kamen wir an einer Laterne vorbei und die ging dann natürlich prompt aus. Der hat vielleicht dumm geschaut, der Jogi.

Erklärungsversuche

Anscheinend sind es nur bestimmte Arten von Laternen, die so auf mich reagieren. Ich glaube solche, die mit speziellen Lampen versehen sind (so mit Gasfüllung oder so) und solche, die eh’ schon ein bisschen einen Macken haben (z. B. Glühwendel kurz vor dem Durchbrennen). Bei solchen Laternen habe ich meist eine große Chance, dass eine davon ausgeht wenn ich vorbeikomme.

Also quasi “psychisch labile Laternen”.

Manchmal denke ich auch, dass mein Gemütszustand (gestresst, abgearbeitet, zornig, usw.) entsprechend sein muss, damit eine Lampe ausgeht. Aber das ist natürlich nur eine Vermutung.

Epilog

Alles in allem also ein sehr seltsames Phänomen. Wenn Ihr mir auch nicht glaubt, so hoffe ich trotzdem, dass dieser Artikel zumindest etwas zu Eurer Erheiterung beigetragen hat.

Falls jemand ernsthafte Erklärungsversuche hat, bitte hier als Kommentar schreiben, ich bin sehr gespannt (und nein, “Uwe hat Halluzinationen” ist keine Erklärung).

Ergänzungen

2005-09-20, 5:15 Uhr, 2 °C, mit Rad an Laterne vor Eislinger Rathaus vorbei, Lampe aus, danach gleich wieder an.

AllocSysString

Hartnäckigkeit zahlt sich oft aus: Heute habe ich ein Speicherleck (englisch „Memory leak“) in Zeta Producer korrigiert, das seit Version 5 drin war:

Beim Aufruf der Microsoft Scripting Engine habe ich beim Aufruf der Funktion IActiveScriptParse::ParseScriptText irrtümlicherweise CStringT::AllocSysString aufgerufen und den so angeforderten Speicher selbst nicht mehr freigegeben. Dies hätte ich jedoch machen müssen, was ich in der korrigierten Version nun mache.

Das hätte ich aber machen sollen! „RTFM“ hat Mario mir da gleich geantwortet als ich es ihm erzählt habe.

Hiermit also nochmals der ultimative Artikel wann wer für die Freigabe von Zeichenfolgen die mittels AllocSysString reserviert wurden verantwortlich ist: „Allocating and Releasing Memory for a BSTR (Visual C++ Concepts)„.

Fehlermeldung CS0013 beim Kompilieren einer ASP.NET 1.1-Anwendung mit Microsoft Visual Studio .NET 2003

Beschreibung

Beim Kompilieren einer ASP.NET 1.1-Anwendung mit Microsoft Visual Studio .NET 2003 auf einem Windows 2000 Server via Terminal Services („Remote Desktop“, RDP) trat folgende Fehlermeldung auf:

CS0013: Unerwarteter Fehler beim Schreiben der Metadaten in die Datei ‚C:\…\obj\Release\MyDll.dll‘ — ‚Unbekannter Fehler‘.

In Englisch sollte das in etwa so lauten:

CS0013: Unexpected error writing metadata to file ‚C:\…\obj\Release\MyDll.dll‘ — ‚Unknown error‘.

Die Fehlermeldung trat bei einem Projekt auf, das bereits über ein Jahr erfolgreich kompilierte. Nach einer kleinen Codeänderung kam auf einmal diese Fehlermeldung.

Ursache

Die erwähnte „kleine Codeänderung“ war, daß ich eine Klasse erstellt hatte, die von einer anderen Klasse abgeleitet hat, welche in einer dritten Klasse als Unterklasse enthalten war.

Also so was wie das hier:

public class A
{
    ...
    
    public class B
    {
        ....
    }

    ...
}

...

public class C : A.B
{
    ...
}

Scheinbar „verhaspelt“ sich der C#-Compiler da irgendwo. In anderen Fällen hat das selbe Prinzip auch schon oft funktioniert. Nur in diesem Fall hier aus irgendeinem Grund nicht.

Lösung

Nachdem ich die Klasse B „global“ gemacht hatte, kompilierte alles wieder:

public class A
{
    ... 
}

...

public class B
{
    ....
}

...

public class C : B
{
    ...
}

Windows-Explorer zum Brechen langsam bei entferntem Netzwerkkabel

Beschreibung

Auf einem Laptop (oder heißt das Notebook?) hatte ich seit kurzem folgendes Phänomen:

Sobald das Netzwerkkabel entfernt war (und WLAN deaktiviert), war der Windows-Explorer (auch die Shell selbst) mangels Geschwindigkeit praktisch unbenutzbar.

Allein das Startmenü-Öffnen war nur sehr langsam (im Minutenbereich) möglich. Ebenso dauerte es sehr lange, im Windows-Explorer einen Ordner anzuzeigen und dann zu einem anderen Ordner zu wechseln.

Ursache

Mittels CommView habe ich herausgefunden, daß ständig Anfragen ins (nicht vorhandene) Netzwerk gesendet wurden.

Lösung

So sahen die Netzwerkverbindungen des Laptops aus:

Netzwerkverbindungen

(Die betreffende Netzwerkkarte habe ich violett markiert).

Wenn nun das „Erweiterte TCP/IP-Einstellungen“-Dialogfenster dieser LAN-Verbindung angezeigt wurden, war die Option „NetBIOS-Einstellung“ auf den Wert „Standard“ gesetzt.

Sobald ich die Option auf den Wert „NetBIOS über TCP/IP deaktivieren“ aktiviert hatte, lief der Explorer wieder:

Was genau diese Option bewirkt hat sich mir trotz Recherche nicht so ganz erschlossen. Jedoch arbeiten nun alle Windows-Komponenten sowohl mit als auch ohne Netzwerkkabel zu meiner vollen Zufriedenheit.

Mehrfache Datensätze in einer Tabelle mit SQL löschen

Tisch mit Buch, darin Tipps, wie mehrfache Datensätze aus einer SQL-Tabelle zu löschen sind

Gestern habe ich versehentlich in eine Tabelle im Microsoft SQL Server doppelte Datensätze importiert.

Habe den Mario gefragt wie ich die wieder rausbekomme, seine übliche Antwort war natürlich „…geht mit Standard-SQL nicht…„.

Ha! Von wegen! Der Rainer hat genau 30 Sekunden gebraucht um den Artikel „Mehrfache Datensätze anzeigen und mit Ausnahme einer Zeile löschen“ zu finden.

Sieschd, Mario, es gooht doch! 🙂

Im verlinkten Artikel ist für mich die fast ganz unten stehende Abfrage meistens am passenden:

DELETE FROM tbl_Personen

WHERE [Personen-Id] NOT IN
(
    SELECT MIN(X.[Personen-Id])
    FROM tbl_Personen AS X
    GROUP BY X.Nachname, X.Vorname, X.Geburtstag
)

Fehlermeldung „Fehler bei sqlmaint.exe“ bei einem geplanten Auftrag in SQL Server

Fehlerbeschreibung

Es kommt eine Meldung „Fehler bei sqlmaint.exe. [SQLSTATE 42000] (Fehler 22029). Fehler bei Schritt“ und der Auftrag ist mit einem roten X als fehlgeschlagen markiert.

Ursache

Der Aufruf der Anwendung „sqlmaint.exe“ schlug fehl.

Lösung

Zunächst den Auftrag anschauen und das T-SQL-Skripts des Auftrags analysieren. Dies kann z.B. wie folgt aussehen:

EXECUTE
    master.dbo.xp_sqlmaint
    N"-PlanID 99F45116-2803-46BB-A95D-058C5D8E72EC
    -WriteHistory
    -VrfyBackup
    -BkUpOnlyIfClean
    -CkDBRepair
    -BkUpMedia DISK
    -BkUpDB "c:mybackfolder"
    -DelBkUps 4DAYS
    -BkExt "BAK""

Der Aufruf „xp_sqlmaint“ ist eine Erweiterte Gespeicherte Prozedur die schlicht ein Wrapper für die Anwendung „sqlmaint.exe“ ist.

Wenn nun „sqlmaint.exe“ direkt von der Befehlszeile aus aufgerufen wird mit den Parametern die auch an „xp_sqlmaint“ übergeben wird, liefert „sqlmaint.exe“ eine sehr detaillierte Fehlermeldung warum der Aufruf tatsächlich fehlschlug.

Mit dieser Fehlermeldung dann die tatsächliche Ursache beseitigen.

Noch einfacher ist es, die Ausgabe von „sqlmaint.exe“ wenn es durch „xp_sqlmaint“ aufgerufen wird sich direkt ausgeben zu lassen. Dazu:

  1. Eigenschaften des T-SQL-Skripts des Auftrags anzeigen.
  2. Registerkarte „Erweitert“
  3. Im Feld Ausgabedatei einen Datei angeben, möglichst als UNC-Pfad, z. B. „\\myserver\myfolder\sqlmaint-output.txt“. Diese Ausgabe dann analysieren.

Tipps

  • „sqlmaint.exe“ ist normalerweise im Ordner „C:\Programme\Microsoft SQL Server\MSSQL\Binn“ gespeichert. (bzw. „C:\Programme\Microsoft SQL Server\MSSQL.1\MSSQL\Binn“ für SQL Server 2005)
  • Ein Fehler war „Error 21268: [SQL-DMO]Row or column specified is outside the range of the specified query result set„. Ein Autor schlug vor, die Begrenzung der Zeilenanzahl für Protokolle aufzuheben, ein anderer „dbcc checkdb“ auf der Datenbank auszuführen (im SQL Query Analyzer). Es wurden dann auch tatsächlich Fehler in der „msdb“ Datenbank gefunden. Um diese zu beseitigen:
    1. Den SQL-Server Dienst anhalten
    2. Den SQL-Server manuell starten „C:\Programme\Microsoft SQL Server\MSSQL\Binn>sqlservr.exe -m“
    3. „ISQL.exe“ starten
      „C:\Programme\Microsoft SQL Server\MSSQL\Binn>isql -U sa“ und den Reparieren-Befehl ausführen, z.B.:
      1> alter database msdb set SINGLE_USER
      2> DBCC CHECKDB („msdb“, REPAIR_ALLOW_DATA_LOSS)
      3> alter database msdb set MULTI_USER
      4> GO

    Für den SQL Server 2005 lauten die entsprechenden Befehle/Ordner:

    1. Den SQL-Server Dienst anhalten
    2. Den SQL-Server manuell starten „C:\Programme\Microsoft SQL Server\MSSQL.1\MSSQL\Binn>sqlservr.exe -m
    3. „OSQL.exe“ starten
      C:\Programme\Microsoft SQL Server\90\Tools\Binn>osql -U sa“ und den Reparieren-Befehl ausführen, z.B.:
      1> alter database msdb set SINGLE_USER
      2> DBCC CHECKDB („msdb“, REPAIR_ALLOW_DATA_LOSS)
      3> alter database msdb set MULTI_USER
      4> GO

(Stichwörter für Suchmaschinen: )