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
C++
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):
C++
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:
C++
[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:
C++
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
C++
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):
C++
[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:
C++
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 |
unsigned char |
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 |
const wchar_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.
Zurück: .NET-Klassen in C++ Windows Forms-Anwendungen