zurück zur Startseite

API-Windows-Programmierung (ohne MFC)

Inhalt

1. WinMain(...) und windows.h
2. Eigene Fenster erzeugen
3. Zwei Fenster auf Basis einer WNDCLASS-Struktur erzeugen
4. Nachrichtenverarbeitung
5. Interaktionen mit Kindfenstern
6. DLL-Programmierung
 

2. Eigene Fenster erzeugen

In Windows erzeugen wir normalerweise rechteckige Fenster, mit denen wir interagieren.
Daher stellen wir zunächst einige fundamentale Merkmale eines rechteckigen Fensters zusammen,
die wir dann mittels Programm definieren werden:
 
 X,Y...:   Linke obere Ecke (x,y), Höhe (y-size), Breite (x-size) des Fensters
 Icon:   Kleines Bitmap (links oben in der Titelzeile bzw. links in der Taskleiste)
 Sysmenu:   Menü, das sich beim Klick auf das Icon bzw. mit Alt+Space öffnet
 Frame:   kein Rahmen - fixierter Rahmen - mit der Maus veränderbarer Rahmen
 Caption:  Überschrift (String) in der Titelzeile
 Minimize:  Schaltfläche zum Minimieren des Fensters (-> Taskleiste)
 Maximize:  Schaltfläche zum Maximieren des Fensters (-> Taskleiste)
 Cursor:  Zeigerform der Maus innerhalb des Fensters
 Background:  Hintergrundmuster des Fensters
 Menu:  Zum Fenster zugehöriges Menü

Für den Anfang ist das schon eine ganze Menge. Dies ist typisch für Windows. Aufgrund der optischen/grafischen Unterstützung des Programms muß sich der Programmierer von Anfang an um eine Vielzahl grafischer Details kümmern, die mehr mit Design als mit dem eigentlichen Programmierziel zu tun haben. Aber genau dieses Handwerkzeug muß der Windows-Programmierer zu gebrauchen lernen.

Im nächsten Schritt erstellen wir unser erstes Fenster ohne Assistent und ohne MFC-Bibliothek:

Achten Sie auf folgende grundlegenden Bestandteile:
Header-Datei windows.h,
Hauptfunktion WinMain(),
Nachrichtenschleife und
Nachrichten-Funktion WndProc():
 

    #include <windows.h>
    ...

    int WINAPI WinMain(...)
    {
    WNDCLASS wc;
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc. ...
    wc. ...
    wc.lpszClassName = szName;

    RegisterClass ( &wc );

    HWND hwnd = CreateWindow ( szName, ... ); //Hier wird das Fenster erzeugt
    ShowWindow ( hwnd, iCmdShow );
    UpdateWindow ( hwnd);

    //Nachrichtenschleife
    MSGmsg;
        while ( GetMessage ( &msg, NULL, 0, 0 ) )
        {
            TranslateMessage ( &msg ) ;
        DispatchMessage ( &msg ) ;
        }
            return msg.wParam;
     }

    LRESULT CALLBACK WndProc (HWND hwnd,UINT message, ...)
    {
        switch ( message )
        {
          case WM_CREATE:
           ...
          return 0;

          case WM_... :
           ...
          return 0;

          ...

          case WM_CLOSE:
            DestroyWindow(hwnd);
          return 0;

          case WM_DESTROY:
        PostQuitMessage ( 0 );
          return 0;
        }
        return DefWindowProc (...) ;
    }
 

       Windows-WinAPI-Grundgerüst 

Konzentrieren Sie sich zunächst auf die Merkmale und Funktionen des Fensters.
Zum Thema Nachrichtenverarbeitung kommen wir später.

Die Fenstermerkmale werden an zwei Stellen definiert. Die erste Hälfte legen wir in WNDCLASS-Struktur fest.
Ich benutze bewußt den Begriff WNDCLASS-Struktur und nicht den oft zu findenden Begriff Fensterklasse,
denn hier handelt es sich nicht um eine C++-Class im objektorientierten Sinn, sondern schlicht um eine Struktur.
Diese WNDCLASS-Struktur wird mit dem Befehl RegisterClass ( &... ) "registriert".

Auf Basis dieser WNDCLASS-Struktur können wir dann die zweite Hälfte der Fenster-Merkmale festlegen.
Dies erfolgt in der Funktion CreateWindow (... ).

Nachfolgend erstellen wir in einem kompletten Programm unser erstes Fenster:
 
#include <windows.h>

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM); 
//Deklaration der Windows-Nachrichten-Prozedur

int WINAPI WinMain (HINSTANCE hI, HINSTANCE hPrI, PSTR szCmdLine, int iCmdShow)
{
char szName[] = "Fensterklasse";
WNDCLASS wc;

wc.style         = CS_HREDRAW | CS_VREDRAW;   // CS = "class style"
wc.lpfnWndProc   = WndProc;
wc.cbClsExtra    = 0;
wc.cbWndExtra    = 0;
wc.hInstance     = hI;
wc.hIcon         = LoadIcon (NULL, IDI_WINLOGO);
wc.hCursor       = LoadCursor (NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH) GetStockObject (BLACK_BRUSH);
wc.lpszMenuName  = NULL;
wc.lpszClassName = szName;

RegisterClass (&wc);

HWND hwnd = CreateWindow (szName, "", WS_SYSMENU | WS_THICKFRAME, 
                          0, 0, 200, 100, NULL, NULL, hI, NULL);

ShowWindow   (hwnd, iCmdShow);
UpdateWindow (hwnd);

// Nachrichten-Schleife
MSG msg;
    while (GetMessage (&msg, NULL, 0, 0))
    {
        TranslateMessage (&msg);
        DispatchMessage (&msg);
    }
return msg.wParam;
}

// Windows-Nachrichten-Prozedur
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;

switch (message)
{
case WM_PAINT:
    hdc = BeginPaint (hwnd, &ps);
        TextOut (hdc, 20, 20, "Ich bin ein Fenster.", 20);
    EndPaint (hwnd, &ps);
    return 0;

case WM_DESTROY:
    PostQuitMessage (0);
    return 0;
}

return DefWindowProc (hwnd, message, wParam, lParam);
}
 

Wenn wir das obenstehende Programm kompilieren und starten, erscheint folgendes Fenster in der linken oberen Ecke.

Wir analysieren den Programmcode nun bewußt nicht der Reihe nach, sondern untersuchen die Merkmale des Fensters
und prüfen, an welcher Stelle dies in welcher Form codiert ist.

Zunächst zur Geometrie. Fenster sind in Windows normalerweise rechteckig
(andere geometrische Fensterformen sind zwar möglich aber nicht gesondert vorgesehen).
Unser rechteckiges Fenster startet ganz links oben und ist 200 Pixel (Bildpunkte) breit und 100 Pixel hoch.
Das finden wir in CreateWindow() an folgender Stelle:

HWND hwnd = CreateWindow (
szName, "", WS_SYSMENU | WS_THICKFRAME,
0, // initial x position
0, // initial y position
200, // initial x size
100,  // initial y size
NULL, NULL, hI, NULL);

Experimentieren Sie sofort ausgiebig mit diesen vier Zahlen.
Nur durch eigenes Ausprobieren und Beobachten versteht und begreift man die Zusammenhänge wirklich.

Was passiert z.B., wenn man negative Zahlen für den Ursprung angibt?
Testen Sie die Werte -10, -10, 200, 100. Es funktioniert! Macht natürlich keinen besonderen Sinn,
aber das Programm reagiert wie erwartet.

Testen Sie nun. ... -1000, -1000, 200, 100 ...
Das Fenster ist aktiv, aber nicht im für den Betrachter sichtbaren Bereich.
Schließen können Sie jetzt nur noch mittels Tastenkombination Alt+F4,
da Sie mit der Maus ja nicht am Systemmenü anpacken können.
Die Maus kann nur im sichtbaren Bereich agieren, während die Tastatur hier auch "unsichtbare" Fenster erreicht.
Es gibt aber noch einen anderen Weg, das Fenster zu schließen.
Mit der Tastenkombination Alt+Leertaste können Sie das Systemmenü aktivieren.
Dann können Sie auf "Schließen" klicken oder mit der Entertaste bestätigen.
Zum Systemmenü gehört übrigens auch das "x" ganz rechts,
das den direkten Zugang zum Schließen per Mausklick bietet.

Setzen Sie nun die Werte bitte wieder auf  0, 0, 200, 100  zurück,
damit wir wieder das vollständig sichtbare Fenster erzeugen.
Soweit zur Positionierung unseres Fensters.

Wo steht nun eigentlich der Code für das "Windows-Logo"-Icon (bunte Fahne) ?
Das ist Bestandteil der WNDCLASS-Struktur wc:

wc.style         = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc   = WndProc;
wc.cbClsExtra    = 0;
wc.cbWndExtra    = 0;
wc.hInstance     = hI;
wc.hIcon    = LoadIcon (NULL, IDI_WINLOGO);
wc.hCursor       = LoadCursor (NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH) GetStockObject (BLACK_BRUSH);
wc.lpszMenuName  = NULL;
wc.lpszClassName = szName;

Windows hat folgende vorgegebene Möglichkeiten für den zweiten Parameter:

IDI_APPLICATION     Default application icon.
IDI_ERROR                 Hand-shaped icon.
IDI_INFORMATION    Asterisk icon.
IDI_QUESTION           Question mark icon.
IDI_WARNING             Exclamation point icon.
IDI_WINLOGO             Windows logo icon.

Was machen wir nun? Natürlich probieren wir sofort sämtliche Varianten aus!
Nur durch Ausprobieren und Eintippen prägen sich die symbolischen Begriffe
und deren Erscheinungsbild ein.

IDI_APPLICATION :

IDI_ERROR:

IDI_INFORMATION:

IDI_QUESTION:

IDI_WARNING:

Bevor wir unser erstes Fenster weiter untersuchen, eine wichtige Frage:
Woher weiß CreateWindow(), welche registrierte WNDCLASS-Struktur es als Grundlage nehmen soll?
Die Verknüpfung zwischen der WNDCLASS-Struktur und CreateWindow(...) wird über einen konstanten Zeiger LPCTSTR
auf den Namen der Fensterklasse gebildet:
 
 char szName[ ] = "Fensterklasse";
 ...
 wc.lpszClassName = szName;
 ...
 HWND hwnd = CreateWindow (szName, ...

Hier sehen Sie, wie dieser Name die gewählte WNDCLASS-Struktur mit CreateWindow() verknüpft.

Nun zum Kopf unseres Fensters. Dort finden wir vor allem zwei Dinge:

Das Systemmenü (sysmenu) und den Fenstertitel (caption).

Schauen wir in den Programmcode:

HWND hwnd = CreateWindow (szName, "", WS_SYSMENU | WS_THICKFRAME, ...

Beginnen wir mit dem Titel. Zur Zeit ist der Bereich zwischen den Anführungszeichen leer (Leerstring).
Schreiben wir einen Titel hinein:

HWND hwnd = CreateWindow (szName, "Dies ist unser erstes kleines Fenster",
                            WS_SYSMENU | WS_THICKFRAME, ...

Wir erhalten folgendes Erscheinungsbild. Unser Titel wurde aufgrund Überlänge übrigens abgeschnitten:

Das liegt daran, dass die Breite des Fensters (z.Z. 200 Pixel) zu gering ist, um diesen langen Titel darzustellen.
Wir könnten nun natürlich kompliziert die benötigte Breite berechnen, den Platz für das Icon (links) und das "X" (rechts) addieren,
um diesen Wert dann für die Fensterbreite zu verwenden. Das werden wir aber nicht machen, sondern wir überlassen zunächst dem Betriebssystem die Wahl für die Fensterbreite. Wie geht dies?

Die Antwort findet man in MSDN mittels Suchbegriff CreateWindow.
Man setzt die Fensterbreite auf CW_USEDEFAULT.
Hierbei wird jedoch leider auch die angegebene Höhe (hier: 100) ignoriert :

HWND hwnd = CreateWindow
(
    szName,
    "Dies ist unser erstes kleines Fenster",     // window caption
    WS_SYSMENU | WS_THICKFRAME,          // window style
    0,
    0,
    CW_USEDEFAULT,
    100,
    NULL,
    NULL,
    hI,
    NULL
);

Das Fenster wird jetzt richtig groß:

Wenn unsere Höhe einfach ignoriert wird, überlassen wir dem Betriebssystem "beleidigt" einfach die gesamte Geometrie:

HWND hwnd = CreateWindow
(
    szName,
    "Dies ist unser erstes kleines Fenster",
    WS_SYSMENU | WS_THICKFRAME,
    CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
    NULL,
    NULL,
    hI,
    NULL
);

Mal schauen, was passiert, wenn wir das Programm und damit das zughörige Fenster z.B. dreimal erzeugen.
Die Fenster werden in diesem Fall (CW_USEDEFAULT) vom Betriebssystem "kaskadenförmig" angeordnet:

Nun übernehmen wir sofort wieder selbst die Regie. Ändern Sie auf folgende Werte ab:

HWND hwnd = CreateWindow
(
    szName,
    "Dies ist unser erstes kleines Fenster",
    WS_SYSMENU | WS_THICKFRAME,
    0, 0, 400, 100,
    NULL,
    NULL,
    hI,
    NULL
);

Bei 400 Pixel Breite wird der Titel vollständig angezeigt:

Verweilen wir zunächst im oberen Teil des Fensters. Von links nach rechts sehen wir
das Icon für das Systemmenü,
die Titelzeile (caption) und
das "x" des Systemmenüs.

Von anderen Windowsanwendungen kennen Sie sicher die Möglichkeit zum Minimieren ("Wegklicken, Ablegen")
und zum Maximieren ("Vollbild") des Fensters. Dies fehlt unserem Fenster noch. Also nichts wie ran:

HWND hwnd = CreateWindow
(
    szName,
    "Dies ist unser erstes kleines Fenster",
    WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX,
    0, 0, 400, 100,
    NULL,
    NULL,
    hI,
    NULL
);

Zunächst fügen wir (Code siehe oben) den Stil WS_MINIMIZEBOX hinzu. Das ergibt dann folgendes Bild:

Sie sehen, dass Minimieren und Maximieren eine funktionelle Einheit bilden. Aktiviert wurde bisher nur die MINIMIZEBOX.

Das testen wir auch noch umgekehrt, also nur MAXIMIZEBOX:

HWND hwnd = CreateWindow
(
    szName,
    "Dies ist unser erstes kleines Fenster",
    WS_SYSMENU | WS_THICKFRAME | WS_MAXIMIZEBOX,
    0, 0, 400, 100,
    NULL,
    NULL,
    hI,
    NULL
);

Zum guten Schluß fügen wir beide hinzu:

HWND hwnd = CreateWindow (szName, "Dies ist unser erstes kleines Fenster",
WS_SYSMENU | WS_THICKFRAME | WS_MAXIMIZEBOX | WS_MINIMIZEBOX,
0, 0, 400, 100, NULL, NULL, hI, NULL);

Kann man eigentlich in diesem Zusammenhang das Systemmenü weglassen?

HWND hwnd = CreateWindow (szName, "Dies ist unser erstes kleines Fenster",
WS_THICKFRAME | WS_MAXIMIZEBOX | WS_MINIMIZEBOX,
0, 0, 400, 100, NULL, NULL, hI, NULL);

Die Antwort ist: Nein! Ohne Systemmenü gibt es kein Icon und keine Minimieren-/Maximieren-Box:

Der Titel ist jedoch nach wie vor sichtbar.

Nun vereinfachen wir das Ganze mittels eines Kombinationsbegriffes:

HWND hwnd = CreateWindow (szName, "Dies ist unser erstes kleines Fenster",
WS_OVERLAPPEDWINDOW, 0, 0, 400, 100, NULL, NULL, hI, NULL);
 
WS_OVERLAPPEDWINDOW kombiniert folgende Window-Styles:

WS_OVERLAPPED,
WS_CAPTION,
WS_SYSMENU,
WS_THICKFRAME,
WS_MINIMIZEBOX,
WS_MAXIMIZEBOX.

Damit macht der Kopf des Fensters einen (gewohnten) vollständigen Eindruck.
Unser Fenster hat ein Systemmenü und einen Titel.
Man kann die Größe durch Ziehen am Rahmen verändern und per Mausklick minimieren/maximieren.


 

Jetzt verlassen wir Kopf und Rahmen und bewegen uns in den Anwendungsbereich (client area),
der sich unterhalb des Kopfbereiches befindet. Zunächst fällt der schwarze Hintergrund auf.
Dieser wird in der WNDCLASS-Struktur festgelegt:

...
wc.style         = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc  = WndProc;
wc.cbClsExtra    = 0;
wc.cbWndExtra    = 0;
wc.hInstance     = hI;
wc.hIcon         = LoadIcon (NULL, IDI_WINLOGO);
wc.hCursor       = LoadCursor (NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH) GetStockObject (BLACK_BRUSH);
wc.lpszMenuName  = NULL;
wc.lpszClassName = szName;
...

Die Standard-Auswahl an "Farbpinseln" (BRUSH) ist nicht gerade berauschend.
Farbe spielt hier wahrlich keine große Rolle:

WHITE_BRUSH,
BLACK_BRUSH,
LTGRAY_BRUSH, GRAY_BRUSH, DKGRAY_BRUSH,
HOLLOW_BRUSH ( identisch mit NULL_BRUSH ).

Probieren Sie sofort alle aus. Besonders lustig ist die NULL_BRUSH.
Damit wird das Fenster durchsichtig(!):

Schieben Sie das Fenster mal nach links über den Rand und holen Sie es wieder zurück.
Sieht dies bei Ihnen ähnlich aus? Das ist Windows, wie es keiner will. Oder doch? Es gibt fast alles.

Sie sind mit schwarz, weiß und grau nicht zufrieden?
Dann müssen wir mit CreateSolidBrush(...) eine eigene HBRUSH erzeugen! Auf geht’s:

int WINAPI WinMain (HINSTANCE hI, HINSTANCE hPrI, PSTR szCmdLine, int iCmdShow)
{
    char szName[ ] = "Fensterklasse";
    HBRUSH MyBrush = CreateSolidBrush( RGB( 0, 150, 255 ) );

    WNDCLASS wc;
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hI;
    wc.hIcon = LoadIcon (NULL, IDI_WINLOGO);
    wc.hCursor = LoadCursor (NULL, IDC_ARROW);
    wc.hbrBackground = MyBrush;
    wc.lpszMenuName = NULL;
    wc.lpszClassName = szName;

    RegisterClass (&wc);

    HWND hwnd = CreateWindow (szName, "Dies ist unser erstes kleines Fenster",
    WS_OVERLAPPEDWINDOW, 0, 0, 400, 100, NULL, NULL, hI, NULL);

    ShowWindow (hwnd, iCmdShow);
    UpdateWindow (hwnd);
    ...

Damit stehen Ihnen mittels RGB( rot, grün, blau ) insgesamt 256*256*256 = 16.777.216 Farbwerte (24 bit)
zur Verfügung. Die Werte für die Einzelfarben sind jeweils von 0 bis 255 einstellbar.
Experimentieren Sie ein wenig mit den Farben, damit Sie die Wirkung des RGB-Makros verfolgen können.

Nachfolgend sehen Sie unser aktuelles Fenster, das den gewählten Hintergrund mit RGB( 0, 150, 255) verwendet:

Hätten Sie das vorhandene WHITE_BRUSH gewählt, wäre Ihnen evtl. entgangen,
dass die Funktion TextOut(...) standardisiert schwarze Schrift (BLACK_PEN)
auf weißem Hintergrund (WHITE_BRUSH) einsetzt.

Wenn wir gerade bei den Farben sind, verändern wir sowohl Schrift- als auch Hintergrundfarbe bezüglich TextOut(...).
Zunächst der Ausgangscode, der sich im Bereich der Nachrichtenbearbeitung für unser Fenster befindet:

case WM_PAINT:
    hdc = BeginPaint (hwnd, &ps);
      TextOut (hdc, 20, 20, "Ich bin ein Fenster.", 20); //device context, x, y, string, string-Länge
    EndPaint (hwnd, &ps);
return 0;

WM steht für Windows Message.
hdc steht für handle to device context.
Solche Device Contexts benötigt man, um Ausgaben auf Bildschirm oder Drucker durchzuführen.
Innerhalb der Nachricht WM_PAINT, die ausgelöst wird, wenn das Fenster neu gezeichnet werden muß,
besorgt man sich einen hdc mittels BeginPaint(...) und gibt diesen wieder frei durch EndPaint(...).
Außerhalb der Reaktion auf WM_PAINT ist das entsprechende Paar übrigens GetDC(...) und ReleaseDC(...).

Wenn wir nun die Farben ändern wollen, erledigen wir dies über diesen handle hdc mittels RGB-Makro:

case WM_PAINT:
  hdc = BeginPaint (hwnd, &ps);
        SetTextColor( hdc, RGB( 255,   0,  0) );  // rot
        SetBkColor  ( hdc, RGB( 255, 255,  0) );  // gelb
        TextOut     ( hdc, 20, 20, "Ich bin ein Fenster.", 20);
    EndPaint (hwnd, &ps);
return 0;

Machen Sie sich bitte die beiden Handles hwnd (handle auf ein Fenster) und hdc (handle auf einen Gerätekontext) bewußt.
Nach dem Kompilieren / Starten sehen wir unser Farbenspiel:

Testen Sie hier munter weiter, um ein Gefühl für den Umgang mit den Farben zu erhalten.

Übrigens können Sie mit

int SetBkMode
(
    HDC hdc,  // handle of device context
    int iBkMode // flag specifying background mode
);

durch Wahl des Hintergrund-Modus TRANSPARENT die Hintergrundfarbe ignorieren.
Zurückschalten können Sie mit OPAQUE.


Bevor wir uns der in Windows sehr wichtigen Nachrichtenverarbeitung zuwenden, schauen wir uns an,
welche Fenstermerkmale und -funktionen wir bisher betrachtet haben (rot markiert) und welche uns noch fehlen (blau markiert).
Die schwarz dargestellten Befehle sind sozusagen das notwendige Gerüst:

int WINAPI WinMain (HINSTANCE hI, HINSTANCE hPrI, PSTR szCmdLine, int iCmdShow)
{
    char szName[] = "Fensterklasse";
    HBRUSH MyBrush = CreateSolidBrush( RGB( 0, 150, 255 ) );

    WNDCLASS wc;
    wc.style            = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc      = WndProc;   // Nachrichtenbearbeitungsfunktion
    wc.cbClsExtra       = 0;
    wc.cbWndExtra       = 0;
    wc.hInstance        = hI;
    wc.hIcon            = LoadIcon (NULL, IDI_WINLOGO);
    wc.hCursor          = LoadCursor (NULL, IDC_ARROW);
    wc.hbrBackground    = MyBrush;
    wc.lpszMenuName     = NULL;
    wc.lpszClassName    = szName;

    RegisterClass (&wc);

    HWND hwnd = CreateWindow
    (
     szName,
      "Dies ist unser erstes kleines Fenster",
      WS_OVERLAPPEDWINDOW,
      0, 0, 400, 100,
      NULL,
      NULL,
      hI,
      NULL
    );
...

Beginnen wir von oben nach unten:

Hier zur Übersicht die Festlegung der WNDCLASS-Struktur:
 
 typedef struct _WNDCLASS
 {
    UINT         style;
    WNDPROC      lpfnWndProc;
    int          cbClsExtra;
    int          cbWndExtra;
    HANDLE       hInstance;
    HICON        hIcon;
    HCURSOR      hCursor;
    HBRUSH       hbrBackground;
    LPCTSTR      lpszMenuName;
    LPCTSTR      lpszClassName;
 } WNDCLASS;

Das erste Strukturelement style faßt verschiedene Verhaltensweisen zusammen.
Wichtig sind hier die von uns gewählten Styles zum Neuzeichnen des Fensters
bei horizontaler bzw. vertikaler Größenveränderung:

wc.style = CS_HREDRAW | CS_VREDRAW;

Wenn Sie diese beiden Styles weglassen mittels

wc.style = 0;

dann beobachten Sie, dass das Fenster bei einfachen Größenveränderungen nicht neugezeichnet wird.
Weitere Styles findet man z.B. in MSDN unter dem Suchbegriff WNDCLASS.

Gehen wir der Vollständigkeit halber zu den nächsten beiden Elementen, die uns im Moment noch nicht berühren müssen:

(Originaltext aus MSDN)
cbClsExtra
Specifies the number of extra bytes to allocate following the window-class structure.
The system initializes the bytes to zero.
cbWndExtra
Specifies the number of extra bytes to allocate following the window instance.
The system initializes the bytes to zero. If an application uses WNDCLASS
to register a dialog box created by using the CLASS directive in the resource file,
it must set this member to DLGWINDOWEXTRA.
Nach dem Icon, das wir oben bereits untersucht hatten, folgt

HCURSOR hCursor

In unserem Programm finden Sie diesbezüglich in der WNDCLASS-Struktur:

wc.hCursor = LoadCursor (NULL, IDC_ARROW);

LoadCursor bietet folgende Standard-Cursor:
 
IDC_APPSTARTING    Standard arrow and small hourglass
IDC_ARROW          Standard arrow
IDC_CROSS         Crosshair
IDC_HAND           Windows NT 5.0 and later: Hand
IDC_HELP           Arrow and question mark
IDC_IBEAM          I-beam
IDC_NO             Slashed circle
IDC_SIZEALL        Four-pointed arrow pointing north, south, east, and west
IDC_SIZENESW       Double-pointed arrow pointing northeast and southwest
IDC_SIZENS         Double-pointed arrow pointing north and south
IDC_SIZENWSE       Double-pointed arrow pointing northwest and southeast 
IDC_SIZEWE         Double-pointed arrow pointing west and east
IDC_UPARROW        Vertical arrow
IDC_WAIT           Hourglass

Testen Sie ausführlich, damit Sie eine geistige Verbindung herstellen zwischen der Cursor-ID und dem Aussehen.
Da Sie momentan mit dem Cursor in unserem Fenster nichts ausrichten können, wäre z.B. folgendes angebracht:

wc.hCursor = LoadCursor (NULL, IDC_NO);

Wie sich das Fenster ohne festgelegten Cursor verhält, können Sie mit

wc.hCursor = 0;

überprüfen. Sehr unschön! Wieder einmal: Windows, wie es keiner will.

 
Falls wir unserem Fenster in der WNDCLASS-Struktur ein Menü zuordnen wollen, benötigen wir das Strukturelement
lpszMenuName, ein Zeiger auf einen null-terminierten character string, der den Ressourcen-Namen des Menüs festlegt.


weiter zu Teil 3: Zwei Fenster auf Basis einer WNDCLASS-Struktur erzeugen

zurück zur Startseite