Win32-DLLs und Win32-API Funktionen in Windows Forms-Anwendungen
Veröffentlicht: Dezember 2009
Von Richard Kaiser und Alexander Kaiser
In einer C++ Windows Forms-Anwendung können auch Win32-DLLs verwendet werden. Das wird im Folgenden am Beispiel von Win32-API Funktionen gezeigt.
Dieser Artikel ist ein kurzer Auszug aus dem Buch „C++ mit Microsoft Visual C++ 2008“ (ISBN 978-3540238690), das C++ mitsamt den Visual C++-Erweiterungen (C++/CLI) umfassend darstellt. Der Verfasser dieses Buches ist ein erfahrener C++- und C#-Trainer, der auch für Firmenschulungen zur Verfügung steht.
Die Funktionen der Win32-API sind unter .NET normalerweise nicht notwendig, da für sie in der .NET Klassenbibliothek meist Klassen und Funktionen mit derselben Funktionalität zur Verfügung stehen, die einfacher sind und besser zu .NET passen. Wenn man Win32-Programme auf .NET portieren will, kann diese Gegenüberstellung von Win32 und .NET Funktionen hilfreich sein.
Da die Win32-Funktionen auch unter CLR-Anwendungen verfügbar sind, kann man Quelltexte mit solchen Funktionen auch in CLR-Projekten verwenden und so den Aufwand sparen, sie in die .NET-Klassenbibliothek zu portieren. Als Beispiel soll die Win32-API Funktion
int MessageBox(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType);
dienen, die wie MessageBox::Show ein Fenster mit einer Meldung anzeigt.
Für den Aufruf einer solchen Funktion in einer CLR-Anwendung gibt es im Wesentlichen zwei Möglichkeiten, die im Folgenden unter 1. und 2. beschrieben sind. Diese Möglichkeiten bestehen aber nicht nur für Funktionen der Win32-API, sondern für Funktionen aus beliebigen Win32-DLLs. Sie setzen voraus, dass man zuvor die InteropServices mit
using namespace System::Runtime::InteropServices;
verwendet (das ist für DLLImport notwendig).
1. Falls die Parameter und Rückgabetypen der DLL-Funktion spezielle Namen verwenden, definiert man zuerst diese Namen (siehe dazu die Tabelle unten):
typedef void* HWND;
typedef wchar_t* LPCTSTR;
typedef unsigned int UINT;
Danach gibt man ein DLLImport-Attribut mit dem Namen der DLL (hier „user32“), dem Zeichensatz (oft CharSet::Auto) und dem Prototyp der Funktion an:
[DllImport("user32.dll", CharSet=CharSet::Auto)]
extern "C" int MessageBox(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType);
Stören Sie sich nicht an der vielleicht ungewohnten Syntax mit den eckigen Klammern. Das war’s nämlich schon: Die DLL-Funktion kann dann folgendermaßen aufgerufen werden:
void call_Win32Messagebox()
{
const LPCTSTR pText = L"Hello World! ";
const LPCTSTR pCaption = L"Win32 MessageBox";
MessageBox(0, pText, pCaption, 0);
}
2. Mit den Datentypen und Funktionen aus System::Runtime::InteropServices geht es sogar noch etwas einfacher. Damit kann man Funktionen aus Win32-DLLs auch mit Argumenten eines C++/CLI-Typs aufrufen und so die Definition der Datentypen unter 1. sparen. Dazu gibt man nach
using namespace System; // für String, IntPtr usw.
wieder das DLLImport-Attribut (wie unter 1.) an. Beim Prototyp der Funktion gibt man aber nicht die Win32-Datentypen an, sondern C++/CLI-Parametertypen, die den Datentypen aus der Win32-Deklaration entsprechen (siehe auch dazu die Tabelle unten):
[DllImport("user32", CharSet=CharSet::Auto)]
int MessageBox(IntPtr hWnd, String^ pText,
String ^ pCaption, unsigned int uType);
Dann kann man die Funktion der Win32-DLL mit Argumenten eines C++/CLI-Typs (z.B. String) aufrufen:
void call_Win32Messagebox()
{
String^ pText = "Hello World! ";
String^ pCaption = "Win32 MessageBox";
MessageBox(IntPtr(0), pText, pCaption, 0);
}
Die unter 1. und 2. verwendeten Datentypen ergeben sich aus dieser Tabelle:
nicht verwaltete Typen aus wtypes.h |
Win32-Datentypen aus C | C++/CLI-Datentyp |
HANDLE, HWND | void* | System::IntPtr |
BYTE | unsignedchar | System::Byte |
SHORT | short | System::Int16 |
WORD | unsigned short | System::UInt16 |
INT | int | System::Int32 |
UINT | unsigned int | System::UInt32 |
LONG, BOOL | long | System::Int32 |
DWORD, ULONG | unsigned long | System::UInt32 |
CHAR | char | System::Char |
LPSTR | char* | System::String oder System::Text::StringBuilder |
LPCSTR | const char* | System::String oder System::Text::StringBuilder |
LPWSTR | wchar_t* | System::String oder System::Text::StringBuilder |
LPCWSTR | constwchar_t* | System::String oder System::Text::StringBuilder |
FLOAT | float | System::Single |
DOUBLE | double | System::Double |
Die Namen der ersten beiden Spalten sind auch nach #include <windows.h> verfügbar, aber das kann nicht aufgelöste externe Symbole beim Linken zur Folge haben.