Von Windows 7 aus via ODBC auf eine benannte SQL-Server-2000-Instanz zugreifen

Heute ist Legacy-Tag!

Issue

Für ein Projekt müssen wir von Microsoft Office Access 2010 auf einen SQL Server 2000 via ODBC zugreifen.

Die Herausforderung ist, dass der SQL Server 2000 als benannte Instanz („Named instance“) auf dem Server installiert ist (Windows 2008 R2). Also so was wie „MEINSERVER\SQL2000“, anstatt nur „MEINSERVER“.

Lokal auf dem Server kann ich wunderbar auf den SQL Server 2000 zugreifen, alleine via Netzwerk von einem PC mit Windows 7 ging es nicht. Es kam immer wieder die Meldung, dass keine Verbindung hergestellt werden konnte.

Lösung

Die Lösung war dann im Microsoft-Knowledge-Base-Artikel „How to connect to SQL Server by using an earlier version of SQL Server“ zu finden:

Vom Prinzip via cliconfg.exe-Programm auf dem Client einen Alias zu IP-Adresse und Port des SQL Server 2000 machen und dann über den Alias zugreifen.

Zitate aus der Lösung:

Server:

Determine the TCP/IP port number of the instance of SQL Server. To do this, use one of the following methods, depending on which version of SQL Server that you are running.

  1. On the server that is running SQL Server 2000, start the Server Network Utility. To do this, click Start, click All Programs, click Microsoft SQL Server, and then click Server Network Utility.
  2. Click the General tab, and then select the instance that you want from the Instances list.
  3. Click TCP/IP, and then click Properties. The TCP/IP port number for this instance is shown. Note this number so that you can use it later.

Client:

Configure the server alias on the client computer. To do this, use one of the following methods, depending on your version of SQL Server.

  1. Start the Client Network Utility. To do this, click Start, click Run, type cliconfg.exe, and then press Enter.
  2. On the General tab, verify that TCP/IP appears in the list under Enabled protocols by order.
  3. Click the Alias tab, and then click Add.
  4. Under Network libraries, select TCP/IP.
  5. In the Server name text box, type the IP address of the server that is running SQL Server 2000.
    Note The IP address that you type here is the one that is configured to use the TCP/IP port number.
  6. Click to clear the Dynamically determine port check box, and then type the port number of the instance of SQL Server 2000 in the Port number text box.
  7. Type a name in the Server alias text box, and then click OK.

Dann ging die ODBC-Verbindung („Verbindungstest“ war erfolgreich).

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.