Kochen mit Patrick

Veröffentlicht: 18. Apr 2006
Von Patrick A. Lorenz

In seiner neuen Kolumne stellt Patrick A. Lorenz nützliche Tipps und Tricks für die tägliche Programmierpraxis vor. Die Themen sind bunt gemischt. Diesmal geht es um C#, ASP.NET und die .NET Base Class Library. Dazu kommen der SQL Server und HTML.

dotnetpro


Diesen Artikel können Sie dank freundlicher Unterstützung von dotnetpro auf MSDN Online lesen. dotnetpro ist ein Partner von MSDN Online.
Ausgabe 3/2006


Zur Partnerübersichtsseite von MSDN Online

Auf dieser Seite

 Win32: Screenshots mit C# erstellen
 C#: Die Konvertierung einer Klasse dynamisch prüfen
 BCL: ASCII-Character casten
 SQL: Das Transaction Log leeren
 HTML: Einen MSN Buddy-Namenverlinken
 Der Autor

Win32: Screenshots mit C# erstellen

Zwar haben viele Endanwender mittlerweile die Drucktaste entdeckt und können den so entstanden Screenshot auch prima in Word hineinzaubern. Dennoch hat es sich im technischen Support von Produkten als ausgesprochen praktisch erwiesen, dem Anwender eine Möglichkeit zum Kopieren seiner aktuellen Bildschirmansicht zu spendieren.

Trotz seiner vielen Klassen bietet das .NET Framework bislang noch keine Funktion zum Erstellen von Screenshots. Aber das ist kein Problem, denn schließlich geht’s auch mit ein paar originären Win32-API-Aufrufen. Die benötigten Funktionen stammen aus den Bibliotheken GDI32 und User32. Den Kern bildet die Funktion BitBlt (Bitblock), die Bildbereiche aus einem Device Context (DC) in einen anderen kopiert. Der Ursprungs-DC ist dabei der aktive Desktop, also genau das, was der Benutzer beim Auslösen des Knipsers im Sucher seines virtuellen Fotoapparates gerade sieht. Listing 1 zeigt die Methode CreateScreenshot. Diese erwartet als Parameter ein Fenster-Handle (hWnd) als Int-Ptr. Es handelt sich dabei um die eindeutige Kennzeichnung eines Fensters innerhalb von Windows.

public static Image CreateScreenshot(IntPtr hWnd) {
   IntPtr hSorceDC = GetWindowDC(hWnd);
   RECT rect = new RECT();
   GetWindowRect(hWnd, ref rect);
   int width = rect.right - rect.left;
   int height = rect.bottom - rect.top;
   IntPtr hDestDC = CreateCompatibleDC(hSorceDC);
   IntPtr hBitmap = CreateCompatibleBitmap(hSorceDC, width, height);
   IntPtr hObject = SelectObject(hDestDC, hBitmap);
   BitBlt(hDestDC, 0, 0, width, height, hSorceDC, 0, 0, SRCCOPY);
   SelectObject(hDestDC, hObject);
   DeleteDC(hDestDC);
   ReleaseDC(hWnd, hSorceDC);
   Image screenshot = Image.FromHbitmap(hBitmap);
   DeleteObject(hBitmap);
   return screenshot;
}
private const int SRCCOPY = 0x00CC0020;
[DllImport("gdi32.dll")]
private static extern bool BitBlt(IntPtr hObject, int nXDest, int
nYDest, int nWidth, int nHeight, IntPtr hObjectSource, int nXSrc, int
nYSrc, int dwRop);
[DllImport("gdi32.dll")]
private static extern IntPtr CreateCompatibleBitmap(IntPtr hDC, int
nWidth, int nHeight);
[DllImport("gdi32.dll")]
private static extern IntPtr CreateCompatibleDC(IntPtr hDC);
[DllImport("gdi32.dll")]
private static extern bool DeleteDC(IntPtr hDC);
[DllImport("gdi32.dll")]
private static extern bool DeleteObject(IntPtr hObject);
[DllImport("gdi32.dll")]
private static extern IntPtr SelectObject(IntPtr hDC,IntPtr hObject);
[StructLayout(LayoutKind.Sequential)]
private struct RECT {
   public int left;
   public int top;
   public int right;
   public int bottom;
}
[DllImport("user32.dll")]
private static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll")]
private static extern IntPtr GetWindowDC(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern IntPtr ReleaseDC(IntPtr hWnd,IntPtr hDC);
[DllImport("user32.dll")]
private static extern IntPtr GetWindowRect(IntPtr hWnd,ref RECT rect);

Listing 1: Screenshots mit dem Win32 API erstellen.

Folgerichtig kann nicht nur der gesamte Desktop abfotografiert werden, sondern auch selektiv ein Fensterbereich. Über das Fenster werden der zugehörige Device Context sowie Position und Größe des Fensters (GetWindowDC und GetWindowRect) bestimmt. Die gewonnenen Angaben werden dazu benutzt, um einen leeren DC und ein leeres Bild (hBitmap) anzulegen, in das nun mittels BitBlt der gewünschte Bildschirmbereich kopiert wird. Aus dem erzeugten Bitmap-Handle können Sie anschließend ein Image-Objekt erzeugen. Und schon sind Sie nach einem Ausflug in das Win32 API wohlbehalten zurück in der verwalteten Welt. Der Einsatz der neuen Methode ist ganz einfach, beispielsweise auch in einer Konsolenapplikation. Die nachfolgenden Zeilen zeigen den Aufruf einer Hilfsmethode, die das generierte Image-Objekt als Bitmap auf der Festplatte speichert und anschließend als Prozess startet. Dies führt zur Anzeige des Screenshots im dafür registrierten Bildeditor.

[STAThread]
static void Main(string[] args) {
   string fileName = "c:\\screenshot.bmp";
   SaveScreenshot(GetDesktopWindow(), fileName);
   Process.Start(fileName);
}
public static void SaveScreenshot(IntPtr hWnd,
string fileName) {
   using(Image screenshot = CreateScreenshot(hWnd))
   {
      screenshot.Save(fileName, ImageFormat.Bmp);
   }
}

Das Beispiel weist noch eine Besonderheit auf: Statt einem spezifischen Fenster wird über die Funktion GetDesktopWindow das Window-Handle des Desktops gewonnen und folgerichtig der gesamte sichtbare Bildschirmbereich fotografiert.

Ein Tipp: In realen Applikationen ist es sinnvoll, das Auslösen der Screenshots ein klein wenig zu verzögern. Hierzu können Sie die Methode Thread.Sleep verwenden. Um nur das aktuelle Fenster einer Windows- Applikation zu fotografieren, übergeben Sie der Methode die Fenstereigenschaft Handle. Alternativ können Sie mit der Win32-API-Funktion GetActiveWindow das aktuelle Fenster umwandeln.

C#: Die Konvertierung einer Klasse dynamisch prüfen

Wer im Dschungel der OOP unterwegs ist, verwendet Schnittstellen und Polymorphie. Oft ist die Überprüfung eines erhaltenen Objekts auf die Implementierung einer Schnittstelle oder Basisklasse notwendig. In C# stehen hierzu die Schlüsselwörter is und as zur Verfügung, mit deren Hilfe sich die Konvertierung prüfen beziehungsweise sicher durchführen lässt. Das Schlüsselwort as liefert im negativen Fall schlicht null, anstatt dass wie bei einer regulären Konvertierung eine Ausnahme ausgelöst wird.

Das Beispiel zeigt eine Schnittstelle, eine diese implementierende Klasse sowie eine autarke Klasse. Die Ausgabe der Überprüfung mit dem is-Schlüsselwort ergibt, dass eine Instanz der ersten Klasse logischerweise zu der Schnittstelle konvertierbar ist, eine Instanz der zweiten hingegen nicht.

interface IDummy {}
public class DummyClass : IDummy {}
public class OtherClass {}
class Class1 {
   [STAThread]
   static void Main(string[] args) {
      DummyClass cls1 = new DummyClass();
      OtherClass cls2 = new OtherClass();
      Console.WriteLine(cls1 is IDummy);
      Console.WriteLine(cls2 is IDummy);
      Console.WriteLine("Done.");
      Console.Read();
   }
}

Bis hierher ist das Beispiel nichts Besonderes, doch was passiert, wenn gar keine Instanz einer Klasse vorliegt, sondern lediglich die Klasse selbst? Für diesen Fall bietet C# keinerlei Unterstützung an. Abhilfe verspricht die Verwendung von Reflection. Die Metadaten eines .NET-Datentyps werden über die Type-Klasse repräsentiert.

Über eine Instanz der Type-Klasse lassen sich umfangreiche Informationen über den Datentyp in Erfahrung bringen. Eine solche Instanz liefert beispielsweise der typeof-Operator. Anschließend können Sie über die Methode IsAssignable-From des Zieldatentyps ermitteln, ob ein anderer Datentyp auf diesen konvertiert werden kann. Die Anwendung sieht wie folgt aus, das Ergebnis entspricht dem vorhergehenden:

Type typeIDummy = typeof(IDummy);
Type typeDummyClass = typeof(DummyClass);
Type typeOtherClass = typeof(OtherClass);
Console.WriteLine(typeIDummy.IsAssignableFrom(
typeDummyClass));
Console.WriteLine(typeIDummy.IsAssignableFrom(
typeOtherClass));

BCL: ASCII-Character casten

Wie lässt sich eigentlich ein byte-Wert in das entsprechende Zeichen eines Zeichensatzes umwandeln? Ein Cast des entsprechenden int-Wertes (zum Beispiel 132 = „) auf char sollte es möglich machen, oder?

char c = (char) 132;
Console.WriteLine(c);

Leider nein, denn dieses Beispiel liefert in der Konsole nur ein Fragezeichen. Besser klappt es mit der Encoding-Klasse aus dem Namespace System.Text der Base Class Library. Über die statische Methode GetEncoding wird der gewünschte Zeichensatz abgefragt. Dessen Methode GetString wird nun ein byte-Array übergeben, das inline mit dem Wert 132 initialisiert wird. Und schon zeigt die Konsole das gewünschte Anführungszeichen an.

Encoding encoding = Encoding.GetEncoding(1252);
Console.WriteLine(encoding.GetString(new byte[]
{ 132 }));

SQL: Das Transaction Log leeren

Das Transaktionsprotokoll eines SQL Servers hat je nach Konfiguration die unangenehme Angewohnheit, mit der Zeit stark zu wachsen. Statt von Fast Food ernährt sich das Log von Transaktionen, aber auch diese machen auf die Dauer dick. Hin und wieder heißt es da Fett lassen, und das geht beim SQL Server ganz ohne Ernährungsumstellung. Die folgende Zeile setzt das Protokoll der angegebenen Datenbank zurück:

DUMP TRANSACTION <dbname> WITH NO_LOG

Ab SQL Server Version 2000 kann alternativ auch der folgende Aufruf verwendet werden:

BACKUP LOG <dbname> WITH NO_LOG

In beiden Fällen ersetzen Sie <dbname> durch den Namen der gewünschten Datenbank und verzichten dabei auf die spitzen Klammern.

HTML: Einen MSN Buddy-Namenverlinken

Die Geister scheiden sich an der Frage, ob Instant Messaging Sitte oder Unsitte ist, aber praktisch ist es allemal. Und wer nichts gegen einen Plausch unter Freunden hat, der kann nun ganz einfach selbige in seinen MSN Messenger einladen.

Die folgenden Links zeigen, wie man (s)einen MSN-Kontakt in einer HTMLSeite verknüpft. Dabei lässt sich nicht nur der Kontakt zur eigenen Buddy-Liste hinzufügen, sondern auch direkt ein Text-, Voice- oder Video-Chat starten. Vorausgesetzt natürlich, der Empfänger akzeptiert eine entsprechende Einladung.

<a href="msnim:add?contact=email@adresse">Add</a>
<a href="msnim:chat?contact=email@adresse">Chat</a>
<a href="msnim:voice?contact=email@adresse">Voice Chat</a>
<a href="msnim:video?contact=email@adresse ">Video Chat</a>

Der Autor

Patrick A. Lorenz ist CEO und CTO der PGK Software & Communication GmbH. Er betreut die dotnetpro-Website und ist Autor mehrerer Fachbücher zu ASP.NET. Sie erreichen ihn über www.pgk.de oder www.asp-buch.de.


Anzeigen: