[C/C++] Win32 API: Fenster

Dieses Thema im Forum "Programmier Tutorials" wurde erstellt von terraNova, 9. Juni 2008 .

Schlagworte:
Status des Themas:
Es sind keine weiteren Antworten möglich.
  1. 9. Juni 2008
    Win32 API: Fenster

    Win32 API - Fenster erstellung

    Um ein Fenster zu erstellen benötigt man nicht viel,
    und man wird es nach einiger Zeit auch flüssig von der Hand schreiben können,
    sodass man nicht jedesmal nachlesen muss.

    Vorerst einmal will ich euch die zu verwendenden Funktionen und Strukturen vorstellen:

    Die WNDCLASSEX-Struktur ( Window Class Extended ):

    Diese Struktur ist wie folgt definiert:

    typedef struct {
    UINT cbSize;
    UINT style;
    WNDPROC lpfnWndProc;
    int cbClsExtra;
    int cbWndExtra;
    HINSTANCE hInstance;
    HICON hIcon;
    HCURSOR hCursor;
    HBRUSH hbrBackground;
    LPCTSTR lpszMenuName;
    LPCTSTR lpszClassName;
    HICON hIconSm;
    } WNDCLASSEX, *PWNDCLASSEX;

    • cbSize: gibt an wie groß die Struktur ist, mit der wir gerade arbeiten.
    • style: Wie soll unser Fenster hinterher genau aussehen?
    • lpfnWndProc: (lpfn: (ungarische Notation) long pointer function ) Hier wird ein Zeiger zu der Funktion angegeben, die unsere Nachrichten empfängt und abarbeitet.
    • cbClsExtra: Unnötig, daher auf 0 setzen. Wir brauchen keinen zusätzlichen Speicher für unsere Struktur
    • cbWndExtra: s.o.
    • hInstance: Eine Instanz zu unserem Programm, wo sich unsere "Nachrichtenverwaltung" befindet.
    • hIcon: Ein Handle zu einem Bild für unser Programm ( Titelleiste )
    • hCursor: Ein Handle zu einem Bild für unseren Mauszeiger, entweder aus der Resource-Datei unseres Programmes, oder die Windowseigene ( IDC_ARROW )
    • hbrBrackground: Ein Handle zu einem Brush, mit dem unserer Fensterhintergrund gefüllt wird.
    • lpszMenuName: Für ein Menu wird hier ein Nullterminierter-String benötigt, der ebenfalls in unserer Resource-Datei befindet.
    • lpszClassName: Der Name unserer Fenster Klasse.
    • hIconSm: Siehe hIcon, nur für die kleinen Icons .


    Die MSG-Struktur:

    typedef struct {
    HWND hwnd;
    UINT message;
    WPARAM wParam;
    LPARAM lParam;
    DWORD time;
    POINT pt;
    } MSG, *PMSG;

    • hwnd: Ein Handle vom Fenster, das die Nachricht empfangen hat.
    • message: Die Nachricht.
    • wParam: Zusätliche Informationen.
    • lParam: s.o.
    • time: Gibt an, wann die Nachricht versendet wurde.
    • pt: Gibt an, wo sich der Mauszeiger gerade befindet.


    Die Funktion RegisterClassEx ( Register Class Extended ) ist wie folgt definiert:

    ATOM RegisterClassEx( WNDCLASSEX* wnd );

    Diese Funktion registriert unsere Fensterklasse, bei einem Fehlschlag
    wird 0 zurückgegeben, die wahrscheinlichkeit ist ziehmlich gering, dass das passiert.


    Die Funktion CreateWindow:

    HWND CreateWindow(
    LPCTSTR lpClassName,
    LPCTSTR lpWindowName,
    DWORD dwStyle,
    int x,
    int y,
    int nWidth,
    int nHeight,
    HWND hWndParent,
    HMENU hMenu,
    HINSTANCE hInstance,
    LPVOID lpParam
    );

    Die Funktion gibt ein Handle zu unserem erstellten Fenster zurück,
    um zu Prüfen ob ein Fehler existiert, gilt das Handle mit dem Makro FAILED(x) zu überprüfen,
    oder auf NULL zu checken.

    • lpClassName: String der Klasse, die wir auch bei der Struktur WNDCLASSEX angegeben haben
    • lpWindowName: Text der auf der Titelleiste des Fensters erscheinen soll.
    • dwStyle: Fenstereigenschaften, wie z.b. ein Rahmen.
    • x, y: Koordinaten des Fensters.
    • nWidth, nHeight: Größe und Breite des Fensters.
    • hWndParent: Gibt es ein Fenster, zu dem dieses hier gehören soll( Also Eltern? ).
    • hMenu: Ein Handle zu einem Menu.
    • hInstance: Ein Handle zu der Instanz der Applikation.
    • lParam: Angabe nicht nötig.


    Die Funktionen ShowWindow und UpdateWindow:

    BOOL ShowWindow(
    HWND hWnd,
    int nCmdShow
    );

    BOOL UpdateWindow(
    HWND hWnd
    );

    Bei beiden Funktionen muss ein Handle zum Fenster angegeben werden,
    mit einem unterschied bei der Funktion ShowWindow, nämlich wird hier noch
    beim zweiten Parameter nCmdShow angegeben, ob man das Fenster zeigen
    oder nicht zeigen will( SW_SHOW, SW_HIDE ).

    Mit ShowWindow gebt ihr an ob ihr das Fenster zeigen wollt, oder eben nicht,
    und mit UpdateWindow wird das Fenster dann gezeichnet.


    Die Funktion PeekMessage:

    BOOL PeekMessage(
    LPMSG lpMsg,
    HWND hWnd,
    UINT wMsgFilterMin,
    UINT wMsgFilterMax,
    UINT wRemoveMsg
    );

    • lpMsg: Ein Zeiger zu einer MSG-Struktur.
    • hWnd: Von welchem Fenster sollen die Nachrichten empfangen werden. NULL wird übergeben, um von jedem Thread dieses Programmes die Nachricht zu bekommen.
    • wMsgFilterMin: Jede Nachricht ist ein Konstanter Wert, hier wird angegeben welcher Wert der minimalste Wert ist, den wir empfangen wollen.
    • wMsgFilterMax: s.o.
    • wRemoveMsg: Hier gibt man an, ob man nach dem erhalten der Nachricht, die Nachricht löschen will, damit diese nicht mehr für andere Threads erhältlich ist.

    DispatchMessage und TranslateMessage:

    DispatchMessage und TranslateMessage erwarten beide nur einen Parameter:
    einen Zeiger zu einer MSG-Struktur.

    TranslateMessage bereitet die Struktur vor und DispatchMessage schickt diese dann an unsere
    "Nachrichtenverwaltung" um bearbeitet zu werden.

    So. Soviel zur Theorie. Klingt beim ersten durchlesen nach viel Information, was es eigentlich garnicht ist.
    Es ist mehr oder weniger sehr einfach zu merken, weil es eben auch logisch ist.

    Ich schreibe nun einmal ein stück Quellcode, und erläutere die Schritte, etc. mit Kommentaren.

    Code:
    #define WIN32_LEAN_AND_MEAN 
    // Nachdem WIN32_LEAN_AND_MEAN per Präprozessor definiert wurde,
    // werden unnötige Teile einfach ausgelassen, was unsere Anwendug natürlich kleiner 
    // macht.
    // 
    #include <windows.h>
    
    // Hier definieren wir unsere Funktion, die die Nachrichten bearbeitet.
    // 
    LRESULT CALLBACK WindowProc ( HWND hWindow, UINT uiMessage, WPARAM wParam, LPARAM lParam );
    
    #define WINDOW_WIDTH 640
    #define WINDOW_HEIGHT 480
    
    int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdline, int nshowcmd )
    {
     const char c_acWindowClass[] = "Fenster";
     HWND hWindow = NULL;
     MSG sMessage;
     WNDCLASSEX wnd;
     
     ZeroMemory( &wnd, sizeof( WNDCLASSEX ) );
     ZeroMemory( &sMessage, sizeof( MSG ) );
     
     wnd.cbClsExtra = 0; // Wir brauchen keinen zusätzlichen Speicher
     wnd.cbWndExtra = 0; // S.o.
     wnd.cbSize = sizeof( WNDCLASSEX ); // Die größe dieser Struktur
     wnd.hbrBackground = (HBRUSH)GetStockObject( WHITE_BRUSH ); // Ein Brush mit dem unserer Hintergrund gefüllt wird
     wnd.hCursor = (HCURSOR)LoadCursor( NULL, IDC_ARROW ); // Ein Icon für unseren Mauszeiger
     wnd.hIcon = 0; // Wir verwenden den standard Icon für unsere Titelleiste
     wnd.hIconSm = 0; // S.o.
     wnd.hInstance = hInstance; // Eine Instanz zu diesem Programm
     wnd.lpfnWndProc = WindowProc; // Unsere Nachrichtenfunktion
     wnd.lpszClassName = c_acWindowClass; // Der Klassenname
     wnd.lpszMenuName = NULL; // Wir verwenden hier kein Menü
     wnd.style = CS_HREDRAW|CS_VREDRAW; // Zeichnet das Fenster nochmal komplett neu, bei veränderung/vergrößerung
     
     if( FAILED( RegisterClassEx( &wnd ) ) ) // Unsere Fensterklasse registrieren
     {
     MessageBoxA( 0, "Failed to register windowclass.", "Error", 0 );
     return -1;
     }
     
     hWindow = CreateWindowA( c_acWindowClass, c_acWindowClass, // Klassenname, Titelleiste 
     WS_OVERLAPPEDWINDOW, // Rahmen, etc.
     50, 50, WINDOW_WIDTH, WINDOW_HEIGHT, // x-, y Koordinaten, Breite, Höhe
     0, 0, hInstance, 0 ); // Parent, Menu, Instanz, lParam
     
     if( FAILED( hWindow ) ) // Wurde das Fenster erstellt?
     {
     MessageBoxA( 0, "Failed to create window.", "Error", 0 );
     return -1;
     } 
     
     ShowWindow( hWindow, SW_SHOW ); // Fenster zeigen
     UpdateWindow( hWindow ); // und zeichnen
     
     while( WM_QUIT != sMessage.message ) // Solange Nachrichten empfangen, bis das Fenster die Nachricht zum schließen bekommt.
     { 
     if( TRUE == PeekMessageA( &sMessage, 0, 0, 0, PM_REMOVE ) ) // Nachricht bekommen, keine Filterangabe, Nachricht löschen
     {
     TranslateMessage( &sMessage ); 
     DispatchMessage( &sMessage );
     }
     }
     
     return sMessage.wParam;
    }
    
    LRESULT CALLBACK WindowProc ( HWND hWindow, UINT uiMessage, WPARAM wParam, LPARAM lParam ) 
    { 
     switch( uiMessage ) // Überprüfen welche Konstante, bzw. Nachricht wir empfangen haben
     { 
     case WM_DESTROY: // Ist es WM_DESTROY( Bei beendigung eines Programmes, z.b. )?
     {
     PostQuitMessage(0); // Alles zum aufräumen vorbereiten
     
     return 0;
     }
     }
     
     return DefWindowProc( hWindow, uiMessage, wParam, lParam ); // Nachricht konnte nicht bearbeitet werden -> von "Windows" bearbeiten lassen.
    }
    So das war's!
    Wie gesagt, je öfter ihr ein Fenster erstellt, desto einfacher wird euch das
    erstellen des Codes fallen.

    Es wird lediglich eine Klasse für unser Fenster erstellt, welche die Informationen von unserem Fenster hat, danach wird diese registriert. Jetzt wird unser Fenster erstellt und es werden die ganze Zeit über die Nachrichten, die das Fenster erhält, bearbeitet.

    Falls es Fragen gibt, können diese mir gerne geschickt werden, und jenachdem
    wie sinnvoll diese Frage ist werde ich diese hier nochmal hineineditieren.
     
  2. Video Script

    Videos zum Themenbereich

    * gefundene Videos auf YouTube, anhand der Überschrift.