14.04.2015 Aufrufe

Kurze Einführung in die Windows ... - GIS-Management

Kurze Einführung in die Windows ... - GIS-Management

Kurze Einführung in die Windows ... - GIS-Management

MEHR ANZEIGEN
WENIGER ANZEIGEN

Sie wollen auch ein ePaper? Erhöhen Sie die Reichweite Ihrer Titel.

YUMPU macht aus Druck-PDFs automatisch weboptimierte ePaper, die Google liebt.

Objektorientierte Programmierung<br />

Prof. Dr. –Ing. Franz-Josef Behr<br />

FACHHOCHSCHULE<br />

HOCHSCHULE FÜR<br />

STUTTGART<br />

TECHNIK<br />

Fachbereich Vermessung und Geo<strong>in</strong>formatik<br />

<strong>Kurze</strong> E<strong>in</strong>führung <strong>in</strong> <strong>die</strong> W<strong>in</strong>dows Programmierung<br />

mittels API<br />

1 E<strong>in</strong>leitung<br />

Wenn Sie <strong>in</strong> den vergangenen Semestern e<strong>in</strong> C++-Programm entwickelt und gestartet haben, geschieht<br />

im Pr<strong>in</strong>zip nicht viel mehr, als dass Code für Programm und Daten <strong>in</strong> den Arbeitsspeicher Ihres<br />

Rechners geladen werden und <strong>die</strong> Ausführung mit dem E<strong>in</strong>sprungspunkt, also der Funktion ma<strong>in</strong>()<br />

beg<strong>in</strong>nt.<br />

Bei W<strong>in</strong>dows-basierten Programmen läuft e<strong>in</strong>iges mehr und anders ab. Dieses „Mehr“ und „Andere“<br />

soll <strong>in</strong> den nachfolgenden Abschnitten vorgestellt werden. Dabei bauen <strong>die</strong> vorgestellten<br />

Beispielprogramme auf dem W<strong>in</strong>dows 32-Bit API (application programm<strong>in</strong>g <strong>in</strong>terface), e<strong>in</strong>er<br />

geme<strong>in</strong>samen Programmierschnittstelle für alle 32-Bit W<strong>in</strong>dows Plattformen auf.<br />

Die hierbei e<strong>in</strong>gesetzten Pr<strong>in</strong>zipien werden Ihnen das Verständnis<br />

? für <strong>die</strong> objektorientierte Entwicklung von W<strong>in</strong>dows-Programmen<br />

? für bestimmte Zusammenhänge beim E<strong>in</strong>satz der COM-Technologie<br />

erleichtern.<br />

Das W<strong>in</strong>dows API ist e<strong>in</strong>e Sammlung von Befehlen, mit denen man Zugriff auf Funktionen und<br />

Objekte <strong>in</strong> W<strong>in</strong>dows hat. API-Befehle s<strong>in</strong>d nicht an e<strong>in</strong>e e<strong>in</strong>zige Programmiersprache gebunden. So<br />

können API - Befehle <strong>in</strong> C++, Visual Basic, Delphi und <strong>in</strong> anderen Sprachen e<strong>in</strong>gesetzt werden. Die<br />

Befehle s<strong>in</strong>d immer gleich, unterschiedlich ist oft der Zugriff auf vordef<strong>in</strong>ierte Strukturen bed<strong>in</strong>gt<br />

durch <strong>die</strong> unterschiedliche Syntax der Programmiersprachen.<br />

2 Nachrichtenschleifen und Fensterprozeduren<br />

Wir beg<strong>in</strong>nen mit unserem ersten W<strong>in</strong>dows-Programm, das gerade mal vier Zeilen umfasst:<br />

#<strong>in</strong>clude <br />

<strong>in</strong>t WINAPI W<strong>in</strong>Ma<strong>in</strong>(HINSTANCE d1, HINSTANCE d2, LPSTR d3, <strong>in</strong>t d4)<br />

{<br />

MessageBox(NULL, "Hello, World!", "", MB_OK);<br />

}<br />

In der ersten Zeile wird <strong>die</strong> W<strong>in</strong>dows API-Headerdatei e<strong>in</strong>gebunden. In <strong>die</strong>se Datei werden wiederum<br />

andere Dateien e<strong>in</strong>gebunden, so dass Sie schließlich alle Prototypen und Konstanten <strong>in</strong> Ihrem<br />

Programm deklariert und verfügbar haben, <strong>die</strong> zum Ablauf API-basierter Programme nötig se<strong>in</strong>. Diese<br />

<strong>in</strong>clude-Anweisung muss deshalb <strong>in</strong> jedem Programm, dass Teile der W<strong>in</strong>dows API benutzt,<br />

vorhanden se<strong>in</strong>!


E<strong>in</strong>leitung 2<br />

Nun zu den verwendeten Funktionen!<br />

2.1 W<strong>in</strong>Ma<strong>in</strong><br />

Die W<strong>in</strong>Ma<strong>in</strong>-Funktion ist das Gegenstück zur ma<strong>in</strong>-Funktion e<strong>in</strong>es Konsolenprogramms und stellt<br />

den E<strong>in</strong>stiegspunkt <strong>in</strong> e<strong>in</strong>e W<strong>in</strong>32-Applikation. dar:<br />

W<strong>in</strong>Ma<strong>in</strong> verfügt über folgende Übergabeparameter:<br />

<strong>in</strong>t WINAPI W<strong>in</strong>Ma<strong>in</strong>(<br />

HINSTANCE hInstance, // handle to current <strong>in</strong>stance<br />

HINSTANCE hPrevInstance, // handle to previous <strong>in</strong>stance<br />

LPSTR lpCmdL<strong>in</strong>e, // po<strong>in</strong>ter to command l<strong>in</strong>e<br />

<strong>in</strong>t nCmdShow // show state of w<strong>in</strong>dow<br />

);<br />

Die Parameter haben folgende Bedeutung:<br />

? hInstance : Handle auf <strong>die</strong> aktuelle Instanz der Applikation.<br />

? hPrevInstance: Handle auf e<strong>in</strong>e andere, früher gestartete Instanz der Applikation. Für e<strong>in</strong>e<br />

W<strong>in</strong>32-basierende Applikation ist <strong>die</strong>ser Parameter immer NULL.<br />

? lpCmdL<strong>in</strong>e: Po<strong>in</strong>ter auf e<strong>in</strong>en null-term<strong>in</strong>ierte Zeichenkette, der <strong>die</strong> Kommandozeile des<br />

Aufrufs <strong>die</strong>ses Programms enthält. Der Programmname ist dar<strong>in</strong> nicht enthalten. Um <strong>die</strong><br />

Kommandozeile vollständig zurückzuerhalten, können Sie ggf. <strong>die</strong> GetCommandL<strong>in</strong>e-<br />

Funktion nutzen.<br />

? nCmdShow: Spezifiziert, wie das Fenster angezeigt wird:<br />

Value<br />

Mean<strong>in</strong>g<br />

SW_HIDE<br />

SW_MINIMIZE<br />

SW_RESTORE<br />

SW_SHOW<br />

SW_SHOWMAXIMIZED<br />

Hides the w<strong>in</strong>dow (and activates another one).<br />

M<strong>in</strong>imizes the specified w<strong>in</strong>dow and activates the<br />

top-level w<strong>in</strong>dow <strong>in</strong> the system's list.<br />

Activates and displays a w<strong>in</strong>dow. If the w<strong>in</strong>dow is<br />

m<strong>in</strong>imized or maximized, the system restores it to its<br />

orig<strong>in</strong>al size and position (same as<br />

SW_SHOWNORMAL).<br />

Activates a w<strong>in</strong>dow and displays it <strong>in</strong> its current size<br />

and position.<br />

Activates a w<strong>in</strong>dow and displays it as a maximized<br />

w<strong>in</strong>dow.


E<strong>in</strong>leitung 3<br />

SW_SHOWMINIMIZED<br />

SW_SHOWMINNOACTIVE<br />

SW_SHOWNA<br />

SW_SHOWNOACTIVATE<br />

SW_SHOWNORMAL<br />

Activates a w<strong>in</strong>dow and displays it as an icon.<br />

Displays a w<strong>in</strong>dow as an icon. The active w<strong>in</strong>dow<br />

rema<strong>in</strong>s active.<br />

Displays a w<strong>in</strong>dow <strong>in</strong> its current state. The active<br />

w<strong>in</strong>dow rema<strong>in</strong>s active.<br />

Displays a w<strong>in</strong>dow <strong>in</strong> its most recent size and<br />

position. The active w<strong>in</strong>dow rema<strong>in</strong>s active.<br />

Activates and displays a w<strong>in</strong>dow. If the w<strong>in</strong>dow is<br />

m<strong>in</strong>imized or maximized, the system restores it to its<br />

orig<strong>in</strong>al size and position (same as SW_RESTORE).<br />

2.1.1 Handles<br />

In der Beschreibung <strong>die</strong>ser Parameter tauscht der Begriff „Handle“ (Handgriff) auf. E<strong>in</strong> Handle liefert<br />

den Bezug zu e<strong>in</strong>em W<strong>in</strong>dows-Objekt. Stellen Sie sich vor, dass alle W<strong>in</strong>dows-Objekte (Fenster,<br />

E<strong>in</strong>gabefelder, Schaltflächen, ...) durchnummeriert s<strong>in</strong>d. E<strong>in</strong> Handle (d.h. e<strong>in</strong>e Handle-Variable)<br />

enthält dann <strong>die</strong> Nummer des zugehörigen Objekts.<br />

Es existieren verschiedene Untertypen von Handles, z. B. Handles für Fenster (Datentyp HWND 1 ),<br />

Handles für Ausgabekontexte (HDC), für Menüs (HMENU) usw.<br />

2.1.2 Instanz<br />

W<strong>in</strong>dows ist e<strong>in</strong> Multitask<strong>in</strong>g-Betriebssystem: Mehrere Programme laufen gleichzeitig ab, e<strong>in</strong><br />

Programm kann auch mehrfach gestartet werden. Sie können zwischen den verschiedenen<br />

Programmen h<strong>in</strong>- und herwechseln.<br />

Jedes laufende Programm wird als Instanz bezeichnet. Wird e<strong>in</strong>e Programm mehrfach gestartet, wird<br />

nur e<strong>in</strong>e Instanz des Programmcodes <strong>in</strong> den Speicher geladen, <strong>die</strong> Daten müssen natürlich getrennt <strong>in</strong><br />

eigenen Speicherbereichen gehalten werden.<br />

2.2 Die MessageBox Funktion<br />

Die MessageBox Funktion gibt <strong>in</strong> e<strong>in</strong>em separaten Fenster e<strong>in</strong>e Nachricht aus. Der erste Parameter<br />

soll normalerweise e<strong>in</strong> Handle auf e<strong>in</strong> Fenster se<strong>in</strong>, zu dem <strong>die</strong> Nachricht gehören soll, aber da wir<br />

ke<strong>in</strong> Fenster besitzen, geben wir Null an. Dies bedeutet, dass <strong>die</strong> Nachricht als eigenständiges<br />

Fenster dargestellt wird.<br />

3 Hello World mit e<strong>in</strong>facher Nachrichtenschleife<br />

Das folgende Programm hello2.c stellt e<strong>in</strong>e den „Hello World“-Text <strong>in</strong> e<strong>in</strong>em eigenen Fenster dar:<br />

1 HWND steht also für Handle [to a] W<strong>in</strong>dow.


E<strong>in</strong>leitung 4<br />

Der zugehörige Source-Code ist <strong>in</strong> folgender Box zusammengestellt:<br />

#<strong>in</strong>clude <br />

<strong>in</strong>t WINAPI W<strong>in</strong>Ma<strong>in</strong>(HINSTANCE hInstance, HINSTANCE d2,<br />

LPSTR d3, <strong>in</strong>t d4)<br />

{<br />

MSG msg;<br />

HWND hwnd;<br />

}<br />

hwnd = CreateW<strong>in</strong>dow("BUTTON", "Hello, World!",<br />

WS_VISIBLE | BS_CENTER, 100, 100, 100, 80,<br />

NULL, NULL, hInstance, NULL);<br />

while (GetMessage(&msg, NULL, 0, 0))<br />

{<br />

if (msg.message == WM_LBUTTONUP)<br />

{<br />

DestroyW<strong>in</strong>dow(hwnd);<br />

PostQuitMessage(0);<br />

}<br />

DispatchMessage(&msg);<br />

}<br />

return msg.wParam;<br />

BUTTON steht für e<strong>in</strong>en bestimmten, vordef<strong>in</strong>ierten Fenstertyp, der als Fensterklasse bezeichnet wird.<br />

Zum Erzeugen des Fensters registriert das Programm zunächst <strong>die</strong>se Fensterklasse:<br />

hwnd = CreateW<strong>in</strong>dow("BUTTON", "Hello, World!",<br />

WS_VISIBLE | BS_CENTER, 100, 100, 100, 80,<br />

NULL, NULL, hInstance, NULL);<br />

Der return-Wert <strong>die</strong>ser Funktion ist e<strong>in</strong> Handle auf das erzeugte Fenster. Es handelt sich tatsächlich<br />

um e<strong>in</strong> Fenster, auch wenn es nur e<strong>in</strong> Button (= Schaltfläche) ist – unter W<strong>in</strong>dows ist fast alles (wie<br />

der Name ja auch deutlich macht) e<strong>in</strong> Fenster!<br />

Selbstverständlich ist es möglich, eigene Fensterklassen zu def<strong>in</strong>ieren, wie wir es später auch tun<br />

werden!<br />

Nach <strong>die</strong>ser Registrierung wird e<strong>in</strong>e Nachrichtenschleife durchlaufen. Dieser Mechanismus gehört<br />

zum wesentlichen Bestandteil von W<strong>in</strong>dows-Anwendungen. Jedes Ereignis, etwa e<strong>in</strong>e<br />

Mausbewegung, e<strong>in</strong> Tastendruck usw., veranlasst W<strong>in</strong>dows, <strong>die</strong>sen „Event“ <strong>in</strong> Form e<strong>in</strong>er Meldung<br />

(message) an das Programm weiterzuleiten, das sich se<strong>in</strong>erseits um <strong>die</strong> Abarbeitung kümmern muss.<br />

while (GetMessage(&msg, NULL, 0, 0))<br />

{<br />

if (msg.message == WM_LBUTTONUP)<br />

{<br />

DestroyW<strong>in</strong>dow(hwnd);<br />

PostQuitMessage(0);


E<strong>in</strong>leitung 5<br />

}<br />

}<br />

DispatchMessage(&msg);<br />

Mit der GetMessage-Funktion holen wir uns e<strong>in</strong>e Meldung (Nachricht) ab und untersuchen sie. In<br />

<strong>die</strong>sem Beispiel werten wir nur <strong>die</strong> Meldung WM_LBUTTONUP aus und rufen gegebenenfalls <strong>die</strong><br />

DestroyW<strong>in</strong>dow- und PostQuitMessage-Funktion auf.<br />

Die DestroyW<strong>in</strong>dow-Function löscht das angegeben Fenster. Besitzt das Fenster K<strong>in</strong>dfenster,<br />

werden <strong>die</strong>se ebenfalls entfernt.<br />

Mit PostQuitMessage wird e<strong>in</strong>e WM_QUIT-Nachricht an das System gesandt, um dem System<br />

anzuzeigen, dass <strong>die</strong> Anwendung beendet werden will. Als Parameter wird der ExitCode der<br />

Anwendung (meist 0) übergeben. Vielfach wird PostQuitMessage als Reaktion auf <strong>die</strong> Nachricht<br />

WM_DESTROY <strong>in</strong> der Nachrichtenschleife der Anwendung aufgerufen.<br />

Wie Sie bereits festgestellt haben, verwendet man <strong>in</strong> der W<strong>in</strong>API-Programmierung eigene<br />

Bezeichnungen für Datentypen. Die nachfolgende Zusammenstellung zeigt <strong>die</strong> Zusammenhänge<br />

zwischen W<strong>in</strong>API und C/C++: 2<br />

Bezeichnung <strong>in</strong> C / C++ Bemerkung<br />

TRUE 1, true<br />

FALSE 0, false<br />

NULL 0 wird <strong>in</strong>sbesondere für Po<strong>in</strong>ter auf "Nichts" verwendet<br />

UINT unsigned <strong>in</strong>t 16bit / 32bit<br />

BYTE unsigned char 8bit<br />

WORD unsigned short 16bit<br />

DWORD unsigned long 32bit<br />

LONG long 32bit<br />

VOID void 3 z.B. als Rückgabewert von Funktionen<br />

LPSTR char* Po<strong>in</strong>ter auf e<strong>in</strong>en Str<strong>in</strong>g (char-Array)<br />

LPCSTR const char* Po<strong>in</strong>ter auf e<strong>in</strong>en konstanten Str<strong>in</strong>g (char-Array)<br />

HANDLE void* Handle für verschiedenste W<strong>in</strong>dows-Elemente<br />

HWND - Handle e<strong>in</strong>es Fensters<br />

PASCAL pascal WINAPI = FAR PASCAL<br />

WPARAM unsigned <strong>in</strong>t 16bit / 32bit<br />

LPARAM long 32bit<br />

2 Quelle: http://www.henkessoft.de/api1.htm<br />

3 engl.: leer, nichts


E<strong>in</strong>leitung 6<br />

LRESULT long 32bit<br />

HINSTANCE - Handle e<strong>in</strong>er Instanz<br />

MSG<br />

Meldung<br />

4 Fensterprozeduren<br />

Tabelle 1: Wesentliche Datentypen <strong>in</strong> W<strong>in</strong>dows.<br />

Die nächste Version unseres Programms registriert se<strong>in</strong>e eigene Fensterklasse:<br />

Der Source dazu wird um e<strong>in</strong>iges umfangreicher:<br />

#<strong>in</strong>clude <br />

void DrawHello(HWND hwnd)<br />

{<br />

HDC hDC;<br />

PAINTSTRUCT pa<strong>in</strong>tStruct;<br />

RECT clientRect;<br />

}<br />

hDC = Beg<strong>in</strong>Pa<strong>in</strong>t(hwnd, &pa<strong>in</strong>tStruct);<br />

if (hDC != NULL)<br />

{<br />

GetClientRect(hwnd, &clientRect);<br />

DPtoLP(hDC, (LPPOINT)&clientRect, 2);<br />

DrawText(hDC, "Hello, World!", -1, &clientRect,<br />

DT_CENTER | DT_VCENTER | DT_SINGLELINE);<br />

EndPa<strong>in</strong>t(hwnd, &pa<strong>in</strong>tStruct);<br />

}<br />

LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg,<br />

WPARAM wParam, LPARAM lParam)<br />

{<br />

switch(uMsg)<br />

{<br />

case WM_PAINT:<br />

DrawHello(hwnd);<br />

break;<br />

case WM_DESTROY:<br />

PostQuitMessage(0);<br />

break;<br />

default:<br />

return DefW<strong>in</strong>dowProc(hwnd, uMsg, wParam, lParam);


E<strong>in</strong>leitung 7<br />

}<br />

}<br />

return 0;<br />

<strong>in</strong>t WINAPI W<strong>in</strong>Ma<strong>in</strong>(HINSTANCE hInstance, HINSTANCE hPrevInstance,<br />

LPSTR d3, <strong>in</strong>t nCmdShow)<br />

{<br />

MSG msg;<br />

HWND hwnd;<br />

WNDCLASS wndClass;<br />

char* szMa<strong>in</strong>WndClass="W<strong>in</strong>TestW<strong>in</strong>"; //Name of ma<strong>in</strong> w<strong>in</strong>dow class<br />

}<br />

if (hPrevInstance == NULL)<br />

{<br />

memset(&wndClass, 0, sizeof(wndClass));<br />

wndClass.style = CS_HREDRAW | CS_VREDRAW;<br />

wndClass.lpfnWndProc = WndProc;<br />

wndClass.hInstance = hInstance;<br />

wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);<br />

wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);<br />

wndClass.lpszClassName = szMa<strong>in</strong>WndClass;<br />

wndClass.lpszMenuName = NULL;<br />

if (!RegisterClass(&wndClass)) return FALSE;<br />

}<br />

hwnd = CreateW<strong>in</strong>dow(szMa<strong>in</strong>WndClass, "HELLO",<br />

WS_OVERLAPPEDWINDOW,<br />

CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,<br />

NULL, NULL, hInstance, NULL);<br />

ShowW<strong>in</strong>dow(hwnd, nCmdShow);<br />

UpdateW<strong>in</strong>dow(hwnd);<br />

while (GetMessage(&msg, NULL, 0, 0))<br />

DispatchMessage(&msg);<br />

return msg.wParam;<br />

E<strong>in</strong>e Vorbemerkung: Vielleicht ist Ihnen aufgefallen, dass alle Variablennamen mit e<strong>in</strong>em Präfix<br />

beg<strong>in</strong>nen? Ich er<strong>in</strong>nere an <strong>die</strong> ungarische Notation – <strong>die</strong>se wird <strong>in</strong> W<strong>in</strong>dows-Programmen <strong>in</strong>tensiv<br />

genutzt!<br />

Alle Variablennamen, <strong>die</strong> nach der ungarischen Notation geschrieben s<strong>in</strong>d, beg<strong>in</strong>nen mit e<strong>in</strong>em Präfix<br />

<strong>in</strong> Kle<strong>in</strong>buchstaben, <strong>die</strong> den Typ der Variable wiedergeben.<br />

Präfix<br />

Datentyp<br />

c<br />

char<br />

by<br />

BYTE (unsigned char)<br />

n<br />

short (<strong>in</strong>t)<br />

i<br />

<strong>in</strong>t (signed <strong>in</strong>t)<br />

x, y <strong>in</strong>t (x- und y-Koorrd<strong>in</strong>aten)<br />

cx, cy<br />

<strong>in</strong>t (Längenangaben <strong>in</strong> e<strong>in</strong>em Koord<strong>in</strong>atensystem, das c steht für 'count')<br />

b, f bool; das f steht für 'Flag'<br />

w<br />

WORD (unsigned short)<br />

l<br />

long<br />

dw<br />

DWORD (unsigned long)<br />

fn<br />

Funktion


E<strong>in</strong>leitung 8<br />

s<br />

sz<br />

h<br />

p<br />

Pp<br />

Str<strong>in</strong>g<br />

ASCIIZ-Str<strong>in</strong>g (genauer: char* abgeschlossen mit '\0')<br />

Handle<br />

Zeiger (po<strong>in</strong>ter)<br />

Doppel Zeiger (**pp)<br />

Nun zurück zu unserem Programm. Se<strong>in</strong>e Ausführung beg<strong>in</strong>nt wie immer mit W<strong>in</strong>Ma<strong>in</strong>.<br />

<strong>in</strong>t WINAPI W<strong>in</strong>Ma<strong>in</strong>(HINSTANCE hInstance, HINSTANCE hPrevInstance,<br />

LPSTR d3, <strong>in</strong>t nCmdShow)<br />

Zu Beg<strong>in</strong>n werden e<strong>in</strong>ige Variablen def<strong>in</strong>iert, deren Datentypen Sie Tabelle 1 entnehmen können:<br />

{<br />

MSG msg;<br />

HWND hwnd;<br />

WNDCLASS wndClass;<br />

wndClass werden wir gleich nutzen. S<strong>in</strong>d Instanzen der Applikation vorhanden, braucht <strong>die</strong>se<br />

Fensterklasse nicht neu <strong>in</strong>itialisiert werden, andernfalls def<strong>in</strong>ieren wir ihre Eigenschaften:.<br />

if (hPrevInstance == NULL)<br />

{<br />

memset(&wndClass, 0, sizeof(wndClass));<br />

wndClass.style = CS_HREDRAW | CS_VREDRAW;<br />

wndClass.lpfnWndProc = WndProc;<br />

wndClass.hInstance = hInstance;<br />

wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);<br />

wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);<br />

wndClass.lpszClassName = "HELLO";<br />

wndClass.lpszMenuName = NULL;<br />

if (!RegisterClass(&wndClass)) return FALSE;<br />

Diese Fensterklasse def<strong>in</strong>iert das grundlegende Verhalten e<strong>in</strong>es Fensters (Aufbau, Fenstersymbol,<br />

Menü, ...). Außerdem wird <strong>die</strong> Adresse e<strong>in</strong>er Funktion <strong>in</strong> der Klasse gespeichert, <strong>die</strong> als<br />

Fensterprozedur bezeichnet wird. Wir haben es hierbei mit dem für uns seltenen Fall zu tun, dass wir<br />

bei<br />

wndClass.lpfnWndProc = WndProc;<br />

e<strong>in</strong>en Zeiger auf e<strong>in</strong>e Funktion verwenden!<br />

W<strong>in</strong>dows bietet e<strong>in</strong>e Reihe vordef<strong>in</strong>ierter Fensterklassen (vgl. BUTTON, Kap. 3), es ist aber genauso<br />

möglich, eigene über <strong>die</strong> Funktion RegisterClass zu def<strong>in</strong>ieren (wie <strong>in</strong> <strong>die</strong>sem Fall).<br />

? Der Eigenschaft hInstance müssen wir den Handle zu unserer Programm<strong>in</strong>stanz übergeben,<br />

damit nur unser Programm <strong>die</strong>se Fensterklasse benutzen kann.<br />

? Mit hCursor kann man den Typ des Cursors bestimmen. Wenn man hier Null angibt,<br />

ersche<strong>in</strong>t auf unserem Fenster ke<strong>in</strong> Cursor. Wir laden jedoch mit der Funktion LoadCursor<br />

den Standard-Pfeilcursor.


E<strong>in</strong>leitung 9<br />

? Dann müssen wir noch über <strong>die</strong> Variable hbrBackground 4 <strong>die</strong> H<strong>in</strong>tergrundfarbe des<br />

Fensters festlegen.<br />

Über GetStockObject (BLACK_BRUSH); können Sie alternativ vordef<strong>in</strong>ierte Füllungen<br />

nutzen. Es stehen folgende zur Verfügung:<br />

? WHITE_BRUSH,<br />

? BLACK_BRUSH,<br />

? LTGRAY_BRUSH,<br />

? GRAY_BRUSH,<br />

? DKGRAY_BRUSH,<br />

? HOLLOW_BRUSH ( identisch mit NULL_BRUSH).<br />

Versuchen Sie NULL_BRUSH – damit wird das Fenster durchsichtig!<br />

Sie können auch e<strong>in</strong>e eigene Füllfarbe erzeugen:<br />

HBRUSH MyBrush = CreateSolidBrush( RGB( 0, 150, 255 ) );<br />

Damit stehen Ihnen mittels RGB( rot, grün, blau ) <strong>in</strong>sgesamt 256*256*256 = 16.777.216 Farbwerte<br />

(24 bit) zur Verfügung. Die Werte für <strong>die</strong> E<strong>in</strong>zelfarben s<strong>in</strong>d jeweils von 0 bis 255 e<strong>in</strong>stellbar.<br />

Experimentieren Sie e<strong>in</strong> wenig mit den Farben, damit Sie <strong>die</strong> Wirkung des RGB-Makros verfolgen<br />

können! 5<br />

LoadCursor bietet folgende Standard-Cursor:<br />

IDC_APPSTARTING<br />

IDC_ARROW<br />

IDC_CROSS<br />

IDC_HAND<br />

IDC_HELP<br />

IDC_IBEAM<br />

IDC_NO<br />

IDC_SIZEALL<br />

IDC_SIZENESW<br />

IDC_SIZENS<br />

IDC_SIZENWSE<br />

IDC_SIZEWE<br />

IDC_UPARROW<br />

IDC_WAIT<br />

Standard arrow and small hourglass<br />

Standard arrow<br />

Crosshair<br />

W<strong>in</strong>dows NT 5.0 and later: Hand<br />

Arrow and question mark<br />

I-beam<br />

Slashed circle<br />

Four-po<strong>in</strong>ted arrow po<strong>in</strong>t<strong>in</strong>g north, south, east, and west<br />

Double-po<strong>in</strong>ted arrow po<strong>in</strong>t<strong>in</strong>g northeast and southwest<br />

Double-po<strong>in</strong>ted arrow po<strong>in</strong>t<strong>in</strong>g north and south<br />

Double-po<strong>in</strong>ted arrow po<strong>in</strong>t<strong>in</strong>g northwest and southeast<br />

Double-po<strong>in</strong>ted arrow po<strong>in</strong>t<strong>in</strong>g west and east<br />

Vertical arrow<br />

Hourglass<br />

4 hbr ist <strong>die</strong> Abkürzung für Handle [to a] Brush<br />

5 Quelle: http://www.henkessoft.de/api2.htm


E<strong>in</strong>leitung 10<br />

Übung<br />

Experimentieren Sie mit verschiedenen Cursor-Varianten<br />

Die Membervariable lpszClassName soll den Fensterklassennamen speichern. Über ihn können<br />

Sie später Fenster <strong>die</strong>ser Klasse erzeugen. Die Hauptfensterklasse bekommt üblicherweise den Namen<br />

des Programms.<br />

Da unser Fenster (noch) ke<strong>in</strong> Menü haben soll, geben wir der Membervariable<br />

lpszMenuName den Wert NULL.<br />

Mit der CreateW<strong>in</strong>dow Funktion erstellen wir nun e<strong>in</strong> Fenster gemäß unserer registrierten<br />

Fensterklasse.<br />

hwnd = CreateW<strong>in</strong>dow("HELLO", "HELLO",<br />

WS_OVERLAPPEDWINDOW,<br />

CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,<br />

NULL, NULL, hInstance, NULL);<br />

Dem ersten Parameter, lpClassName, wird der Name der Fensterklasse als Zeichenkette übergeben.<br />

Über den zweiten Parameter können wir den Text <strong>in</strong> der Titelleiste bee<strong>in</strong>flussen. Im dritten Parameter<br />

speichern wir den Stil des Fensters. Die nächsten vier Parameter s<strong>in</strong>d für <strong>die</strong> räumliche Position des<br />

Fensters verantwortlich. Der vorletzte Parameter muss wieder der Handle auf unsere Programm<strong>in</strong>stanz<br />

se<strong>in</strong>, damit man das Fenster unserem Programm zuordnen kann. Die anderen drei Parameter s<strong>in</strong>d im<br />

Moment unwichtig, daher belassen wir sie bei Null. Die CreateW<strong>in</strong>dow Funktion liefert als<br />

Rückgabewert den Handle auf das Fenster zurück.<br />

Unser Fenster ist nun erstellt, es ersche<strong>in</strong>t jedoch noch nicht auf dem Bildschirm. Dazu müssen wir <strong>die</strong><br />

ShowW<strong>in</strong>dow Funktion aufrufen. Die UpdateW<strong>in</strong>dow Funktion lässt den Anwendungsbereich, also<br />

den freien Fensterbereich, sofort nach dem Start neu zeichnen.<br />

ShowW<strong>in</strong>dow(hwnd, nCmdShow);<br />

UpdateW<strong>in</strong>dow(hwnd);<br />

Das Fenster ist nun erstellt und wird angezeigt. Der Aufruf von UpdateW<strong>in</strong>dow sendet e<strong>in</strong>e<br />

WM_PAINT-Nachricht, damit der Client-Bereich des Fensters hWnd neu gezeichnet wird.<br />

Nun warten wir auf <strong>die</strong> E<strong>in</strong>gabe bzw. Aktivitäten des Benutzers. Wenn etwas passiert, das unser<br />

Fenster betrifft, <strong>in</strong>formiert uns W<strong>in</strong>dows über <strong>die</strong> entsprechende Nachricht. Jedoch werden uns <strong>die</strong><br />

Nachrichten nicht aufgezwängt, sondern sie werden <strong>in</strong> e<strong>in</strong>er Warteschleife abgelegt, aus der wir sie<br />

erst mittels der GetMessage Funktion abholen müssen.<br />

while (GetMessage(&msg, NULL, 0, 0))<br />

DispatchMessage(&msg);<br />

Wenn ke<strong>in</strong>e Nachricht vorhanden ist, dann bleibt unser Programm <strong>in</strong> der Funktion stehen und<br />

wartet auf <strong>die</strong> nächste Nachricht.


E<strong>in</strong>leitung 11<br />

Die GetMessage Funktion hat folgende Parameter:<br />

BOOL GetMessage(<br />

LPMSG lpMsg, // address of structure with message<br />

HWND hWnd, // handle of w<strong>in</strong>dow<br />

UINT wMsgFilterM<strong>in</strong>, // first message<br />

UINT wMsgFilterMax // last message<br />

);<br />

In dem ersten Parameter wird <strong>die</strong> Nachricht gespeichert. Im zweiten Parameter können wir uns auf das<br />

Abholen von Nachrichten für nur e<strong>in</strong> Fenster beschränken. Wenn wir <strong>die</strong>s wollten, müssten wir hier<br />

den Handle des Fensters e<strong>in</strong>tragen. Jedoch würde dann unser Programm nicht korrekt beenden, da zum<br />

Beispiel <strong>die</strong> Nachricht, <strong>die</strong> PostQuitMessage sendet, nicht für unser Fenster bestimmt ist, da <strong>die</strong>s ja<br />

schon zerstört wurde. Mit dem dritten und vierten Parameter kann man <strong>die</strong> Art der Nachrichten<br />

beschränken. Es werden nur Nachrichten abgeholt, <strong>die</strong> zwischen <strong>die</strong>sen beiden Werten liegen. E<strong>in</strong>e<br />

Ausnahme ist, wenn beide Parameter Null s<strong>in</strong>d, dann werden alle Nachrichten abgeholt.<br />

Bei E<strong>in</strong>tritt e<strong>in</strong>es Ereignisses ruft W<strong>in</strong>dows e<strong>in</strong>e Funktion des Programms auf, wenn e<strong>in</strong>es der im<br />

Aufruf von GetMessage e<strong>in</strong>getreten ist. Diese Funktion (auch Fensterfunktion, W<strong>in</strong>dow Procedure<br />

oder kurz WndProc genannt) ist jedoch nicht an e<strong>in</strong> Programm gebunden, sondern an e<strong>in</strong> Fenster. Es<br />

kommt daher häufig vor, dass e<strong>in</strong> Programm mehrere solcher Funktionen enthält. Über <strong>die</strong> Parameter<br />

der WndProc-Funktion bekommt man <strong>die</strong> Nachricht übergeben.<br />

LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg,<br />

WPARAM wParam, LPARAM lParam)<br />

{<br />

switch(uMsg)<br />

{<br />

case WM_PAINT:<br />

DrawHello(hwnd);<br />

break;<br />

case WM_DESTROY:<br />

PostQuitMessage(0);<br />

break;<br />

default:<br />

return DefW<strong>in</strong>dowProc(hwnd, uMsg, wParam, lParam);<br />

}<br />

return 0;<br />

}<br />

Die WndProc Funktion gibt e<strong>in</strong>en LRESULT Wert zurück. LRESULT ist e<strong>in</strong> typedef 6 auf e<strong>in</strong>en<br />

long Typ. CALLBACK sagt aus, dass <strong>die</strong>se Funktion von W<strong>in</strong>dows aufgerufen wird. Der Name der<br />

WndProc Funktion ist frei wählbar. Sie verfügt über folgende Parameter:<br />

Der erste Parameter ist der Handle zu dem Fenster, für das <strong>die</strong> Nachricht bestimmt ist.<br />

Der zweite Parameter message enthält <strong>die</strong> Kennziffer der Nachricht. Für alle Nachrichten s<strong>in</strong>d <strong>in</strong><br />

6 typedef erzeugt ke<strong>in</strong>en neuen Datentyp, sondern führt lediglich mnemonische Synonyme (e<strong>in</strong>fachere Namen)<br />

oder Alias-Bezeichnungen für e<strong>in</strong>en existierenden Typ e<strong>in</strong>. typedef eignet sich daher besonders zur<br />

Vere<strong>in</strong>fachung komplexer Deklarationen:


E<strong>in</strong>leitung 12<br />

w<strong>in</strong>user.h Konstanten deklariert.<br />

Die nächsten beiden Parameter enthalten je nach Nachricht verschiedene Daten. WPARAM ist vom Typ<br />

unsigned <strong>in</strong>t. Das W stammt noch aus alten Tagen, als e<strong>in</strong> <strong>in</strong>t noch 16 Bit breit war, damals<br />

war WPARAM also noch e<strong>in</strong> Speicherwort.<br />

case WM_PAINT:<br />

DrawHello(hwnd);<br />

break;<br />

Die WM_PAINT-Nachricht <strong>in</strong>formiert darüber, dass e<strong>in</strong> Teil des Fensters oder das gesamte Fensters<br />

der Applikation neu gezeichnet werden muss. Die Applikation ruft, immer wenn sie e<strong>in</strong>e Nachricht<br />

WM_PAINT erhält, <strong>die</strong> Funktion DrawHello auf.<br />

Im Falle e<strong>in</strong>er WM_DESTROY-Nachricht wird <strong>die</strong> Meldung zur Beendigung des Programms an<br />

W<strong>in</strong>Ma<strong>in</strong> weitergeleitet, das e<strong>in</strong>en geordneten Abschluss des Programms vornehmen kann.<br />

Alle übrigen Meldungen werden an <strong>die</strong> Standard-Fensterprozedur DefW<strong>in</strong>dowProc weitergeleitet.<br />

Diese stellt sicher, dass alle Meldungen abgearbeitet werden.<br />

case WM_DESTROY:<br />

PostQuitMessage(0);<br />

break;<br />

default:<br />

return DefW<strong>in</strong>dowProc(hwnd, uMsg, wParam, lParam);<br />

Nun zur Funktion DrawHello:<br />

void DrawHello(HWND hwnd)<br />

{<br />

HDC hDC;<br />

PAINTSTRUCT pa<strong>in</strong>tStruct;<br />

RECT clientRect;<br />

}<br />

hDC = Beg<strong>in</strong>Pa<strong>in</strong>t(hwnd, &pa<strong>in</strong>tStruct);<br />

if (hDC != NULL)<br />

{<br />

GetClientRect(hwnd, &clientRect);<br />

DPtoLP(hDC, (LPPOINT)&clientRect, 2);<br />

DrawText(hDC, "Hello, World!", -1, &clientRect,<br />

DT_CENTER | DT_VCENTER | DT_SINGLELINE);<br />

EndPa<strong>in</strong>t(hwnd, &pa<strong>in</strong>tStruct);<br />

}<br />

Mit der Beg<strong>in</strong>Pa<strong>in</strong>t-Funktion teilen wir W<strong>in</strong>dows mit, dass wir <strong>in</strong> unseren Anwendungsbereich<br />

zeichnen möchten. Der erste Parameter der Beg<strong>in</strong>Pa<strong>in</strong>t Funktion legt das Fenster fest, <strong>in</strong> dem wir<br />

zeichnen wollen. Der zweite Parameter ist e<strong>in</strong> Zeiger auf unsere PAINTSTRUCT Funktion, <strong>in</strong> der<br />

W<strong>in</strong>dows <strong>die</strong> Daten bezüglich des Zeichenbereiches speichern wird. Der Rückgabewert hDC (Device<br />

Context) ist der Handle auf den Zeichenbereich.<br />

Die Beg<strong>in</strong>Pa<strong>in</strong>t Funktion übermalt, wenn es so gewollt war, den zu erneuernden (ungültigen)<br />

Zeichenbereich mit dem H<strong>in</strong>tergrund Füllmuster. Der Zeichenbereich wird danach wieder als gültig<br />

erklärt. Dieses als gültig markieren ist wichtig, denn solange noch e<strong>in</strong> Bereich als ungültig markiert


E<strong>in</strong>leitung 13<br />

ist, wird uns W<strong>in</strong>dows immer wieder e<strong>in</strong>e WM_PAINT Nachricht schicken.<br />

Die DPtoLP-Function konvertiert Koord<strong>in</strong>aten des Geräts (device) <strong>in</strong> logische Koord<strong>in</strong>aten. Dabei<br />

werden verschiedene Gerätee<strong>in</strong>stellungen (mapp<strong>in</strong>g mode, Koord<strong>in</strong>aten des Bezugspunkts, viewport,<br />

...) berücksichtig. Bei e<strong>in</strong>em Bildschirm entsprechen <strong>die</strong> Gerätekoord<strong>in</strong>aten den Pixelkoord<strong>in</strong>aten am<br />

Bildschirm. Logische Koord<strong>in</strong>aten werden applikationsspezifisch durch den Entwickler festgelegt.<br />

GetClientRect(hwnd, &clientRect);<br />

Die GetClientRect-Funktion liefert <strong>die</strong> Koord<strong>in</strong>aten der Clien-Area e<strong>in</strong>es Fensters. Die Client-<br />

Koord<strong>in</strong>aten spezifizieren <strong>die</strong> l<strong>in</strong>ke obere und untere rechte Ecke <strong>die</strong>ser Client-Fläche. Da Client<br />

Koord<strong>in</strong>aten relative zur l<strong>in</strong>ken, oberen Ecke def<strong>in</strong>iert s<strong>in</strong>d, ist l<strong>in</strong>ks oben immer (0,0).<br />

Mit der DrawText Funktion kann man Text formatiert ausgeben. Der erste Parameter ist, wie immer<br />

bei GDI Funktionen, der Handle zu unserem Device Context. Bei der W<strong>in</strong>dows-Programmierung<br />

sprechen wir e<strong>in</strong> Ausgabegerät <strong>in</strong> der Regel nicht direkt an, sondern über e<strong>in</strong>en Gerätekontext. Dies<br />

hat für Sie den Vorteil, dass Sie sich um <strong>die</strong> spezifischen Eigenschaften e<strong>in</strong>es Geräts (Bildschirm,<br />

Drucker, Plotter, Zwischenablage) nicht selbst kümmern müssen, sondern W<strong>in</strong>dows <strong>die</strong> Ausgabe<br />

überlassen. Beachten Sie dabei jedoch, dass <strong>die</strong>se Ausgaben nicht von W<strong>in</strong>dows gespeichert werden.<br />

Bei bestimmten Fensteroperationen (wie Verschieben, M<strong>in</strong>imieren, Vergrößern, <strong>in</strong> den Vordergrund<br />

holen) muss also Ihr Programm dafür sorgen, angeregt durch <strong>die</strong> vom Betriebssystem gesendeten<br />

Botschaften, den Fenster<strong>in</strong>halt neu zu zeichnen!<br />

Der zweite Parameter ist e<strong>in</strong> Zeiger auf den auszudruckenden Text (<strong>in</strong> <strong>die</strong>sem Fall e<strong>in</strong>e<br />

Zeichenkettenkonstante). Im dritten Parameter muss man <strong>die</strong> Länge des Textes angeben; <strong>die</strong>s<br />

geschieht hier durch Übergabe des zuvor bestimmten Client-Rects. Mit dem vierten Parameter kann<br />

man festlegen, wie der Text formatiert werden soll. Wir benutzen <strong>die</strong> Konstanten DT_SINGLELINE,<br />

DT_CENTER und DT_VCENTER, <strong>die</strong> den Text e<strong>in</strong>zeilig und zentriert ausgeben lassen.<br />

Am Ende jeder Zeichenoperation müssen wir mit der EndPa<strong>in</strong>t Funktion W<strong>in</strong>dows mitteilen, dass wir<br />

nichts mehr zeichnen möchten. Die Funktion gibt den Device Context wieder frei.<br />

5 Nachrichtenschleifen und Fensterprozeduren<br />

In e<strong>in</strong>er Fensterprozedur kann ebenfalls e<strong>in</strong>e Nachrichtenschleife enthalten se<strong>in</strong>. Im folgenden<br />

Programm implementieren wir e<strong>in</strong>e grundlegende Zeichnungsfunktionalität.<br />

#<strong>in</strong>clude <br />

void AddSegmentAtMessagePos(HDC hDC, HWND hwnd, BOOL bDraw)<br />

{<br />

DWORD dwPos;<br />

POINTS po<strong>in</strong>ts;<br />

POINT po<strong>in</strong>t;<br />

}<br />

dwPos = GetMessagePos();<br />

po<strong>in</strong>ts = MAKEPOINTS(dwPos);<br />

po<strong>in</strong>t.x = po<strong>in</strong>ts.x;<br />

po<strong>in</strong>t.y = po<strong>in</strong>ts.y;<br />

ScreenToClient(hwnd, &po<strong>in</strong>t);<br />

DPtoLP(hDC, &po<strong>in</strong>t, 1);<br />

if (bDraw) L<strong>in</strong>eTo(hDC, po<strong>in</strong>t.x, po<strong>in</strong>t.y);<br />

else MoveToEx(hDC, po<strong>in</strong>t.x, po<strong>in</strong>t.y, NULL);


E<strong>in</strong>leitung 14<br />

void DrawHello(HWND hwnd)<br />

{<br />

HDC hDC;<br />

MSG msg;<br />

if (GetCapture() != NULL) return;<br />

hDC = GetDC(hwnd);<br />

if (hDC != NULL)<br />

{<br />

SetCapture(hwnd);<br />

AddSegmentAtMessagePos(hDC, hwnd, FALSE);<br />

while(GetMessage(&msg, NULL, 0, 0))<br />

{<br />

if (GetCapture() != hwnd) break;<br />

switch (msg.message)<br />

{<br />

case WM_MOUSEMOVE:<br />

AddSegmentAtMessagePos(hDC, hwnd, TRUE);<br />

break;<br />

case WM_LBUTTONUP:<br />

goto ExitLoop;<br />

default:<br />

DispatchMessage(&msg);<br />

}<br />

}<br />

ExitLoop:<br />

ReleaseCapture();<br />

ReleaseDC(hwnd, hDC);<br />

}<br />

}<br />

LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg,<br />

WPARAM wParam, LPARAM lParam)<br />

{<br />

switch(uMsg)<br />

{<br />

case WM_LBUTTONDOWN:<br />

DrawHello(hwnd);<br />

break;<br />

case WM_DESTROY:<br />

PostQuitMessage(0);<br />

break;<br />

default:<br />

return DefW<strong>in</strong>dowProc(hwnd, uMsg, wParam, lParam);<br />

}<br />

return 0;<br />

}<br />

<strong>in</strong>t WINAPI W<strong>in</strong>Ma<strong>in</strong>(HINSTANCE hInstance, HINSTANCE hPrevInstance,<br />

LPSTR d3, <strong>in</strong>t nCmdShow)<br />

{<br />

MSG msg;<br />

HWND hwnd;<br />

WNDCLASS wndClass;<br />

if (hPrevInstance == NULL)<br />

{<br />

memset(&wndClass, 0, sizeof(wndClass));<br />

wndClass.style = CS_HREDRAW | CS_VREDRAW;<br />

wndClass.lpfnWndProc = WndProc;


E<strong>in</strong>leitung 15<br />

wndClass.hInstance = hInstance;<br />

}<br />

wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);<br />

wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);<br />

wndClass.lpszClassName = "HELLO";<br />

if (!RegisterClass(&wndClass)) return FALSE;<br />

}<br />

hwnd = CreateW<strong>in</strong>dow("HELLO", "HELLO",<br />

WS_OVERLAPPEDWINDOW,<br />

CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,<br />

NULL, NULL, hInstance, NULL);<br />

ShowW<strong>in</strong>dow(hwnd, nCmdShow);<br />

UpdateW<strong>in</strong>dow(hwnd);<br />

while (GetMessage(&msg, NULL, 0, 0))<br />

DispatchMessage(&msg);<br />

return msg.wParam;<br />

Machen Sie sich mit dem Programm vertraut! Was hat es mit den beiden Nachrichtenschleifen auf<br />

sich?<br />

Was geschieht, wenn<br />

? Sie das Programm m<strong>in</strong>imieren?<br />

? Sie e<strong>in</strong> anderes Programmfenster über das Anwendungsfenster schieben?

Hurra! Ihre Datei wurde hochgeladen und ist bereit für die Veröffentlichung.

Erfolgreich gespeichert!

Leider ist etwas schief gelaufen!