Um zu verstehen, warum man in C einen Standard eingeführt hat bedarf es ein wenig mehr der Geschichte zu dieser Sprache.C C++ C/C++ Programmieren C Kurs Programmierung Systemprogrammierung Win32 api Win-Api Windows Systemprogrammierung C Programmierung Win32 api Win-Api Windows Systemprogrammierung Kapitel 4: Ein Menü

4.1. Der Quellcode            zurück  zum Inhaltsverzeichnis

Mit dem Vorwissen der Resourcen-Skriptedateien können Sie jetzt auf einfache Weise ein Fenster mit einem Menü erstellen. Zuerst die Resourcen-Skriptedatei:

//Ressourcen-Skriptedatei *.rc

#include <windows.h>
#include "resource.h"

/////////////////////////////////////////////////////////////////
//
// Menü
//

IDR_MENU1 MENU
BEGIN
    POPUP "Datei"
    BEGIN
        MENUITEM "Öffnen",             ID_FILE_OPEN
        MENUITEM "Speichern",          ID_FILE_SAVE
        MENUITEM "Ende",               ID_FILE_EXIT
    END
    POPUP "Optionen"
    BEGIN
        POPUP "Optionen"
        BEGIN
            MENUITEM "Option&1",       ID_OPTIONS_OPTIONS_OPTION1
            MENUITEM "Option&2",       ID_OPTIONS_OPTIONS_OPTION2
        END
    END
    MENUITEM "Über",                   ID_ABOUT
END

/////////////////////////////////////////////////////////////////
//
//Icon
//
ID_ICON              ICON        "goofy.ico"

/////////////////////////////////////////////////////////////////
//
//Stringtabelle
//
STRINGTABLE
BEGIN
        ID_STRING_OPEN,      "Der Dialog Öffnen"
        ID_STRING_SAVE,      "Der Dialog Speichern"
        ID_STRING_OPTION1,   "Erste Option im Untermenü"
        ID_STRING_OPTION2,   "Zweite Option im Untermenü"
        ID_STRING_ABOUT,     "Ein Menü-Beispiel\nCoded by J.Wolf"
END

Die Resource-Datei dürfte Ihnen noch aus dem Kapitel zuvor bekannt sein. Neu hierbei ist die Stringtabelle (STRINGTABLE), welche am Ende hinzugefügt wurde.

Als Nächstes benötigen Sie die Headerdatei resource.h, worin die Werte für die symbolischen Konstanten gespeichert sind. Von dieser Headerdatei macht sowohl die Resourcen-Skriptedatei als auch das Programm gebrauch.

//resource.h - Headerdatei

#define ID_STRING_OPEN                  1
#define ID_STRING_SAVE                  2
#define ID_STRING_OPTION1               3
#define ID_STRING_OPTION2               4
#define ID_STRING_ABOUT                 5

#define IDR_MENU1                       101

#define ID_ICON                         111

#define ID_FILE_OPEN                    40001
#define ID_FILE_SAVE                    40002
#define ID_FILE_EXIT                    40003
#define ID_OPTIONS_OPTIONS_OPTION1      40004
#define ID_OPTIONS_OPTIONS_OPTION2      40005
#define ID_ABOUT                        65535

Und zu guter Letzt, benötigen Sie noch die WIN32-Anwendung, welche im Anschluss etwas genauer analysiert wird.

#include <windows.h>
#include "resource.h"

LPCSTR MainClassName = "Ein Menü-Beispiel";

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMsg,
                         WPARAM wParam, LPARAM lParam);


int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   PSTR szCmdLine, int iCmdShow)
{
   WNDCLASSEX wc;
   MSG wmsg;
   HWND hWnd;

   wc.cbSize = sizeof(WNDCLASSEX);
   wc.style = 0;
   wc.lpfnWndProc = WndProc;
   wc.cbClsExtra = 0;
   wc.cbWndExtra = 0;
   wc.hInstance = hInstance;
   wc.hIcon = LoadIcon(GetModuleHandle(NULL),
                                MAKEINTRESOURCE(ID_ICON));
   wc.hCursor         = LoadCursor(NULL, IDC_ARROW);
   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
   wc.lpszMenuName  = MAKEINTRESOURCE(IDR_MENU1);
   wc.lpszClassName = MainClassName;
   wc.hIconSm         = (HICON)LoadImage(GetModuleHandle(NULL),
                                        MAKEINTRESOURCE(ID_ICON),
                                        IMAGE_ICON, 16, 16, 0);

   if(!RegisterClassEx(&wc))
      {
       MessageBox(NULL, "Windows Registrations Fehler", "Error!",
                  MB_ICONEXCLAMATION | MB_OK);
       return 0;
      }

    hWnd = CreateWindowEx(WS_EX_CLIENTEDGE, MainClassName,
                          "Menü Beispiel",
                          WS_OVERLAPPEDWINDOW|WS_VISIBLE,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          300,150,NULL,NULL,hInstance, NULL);

    if(hWnd == NULL)
    {
          MessageBox(NULL, "Fehler beim Erstellen des Fensters!",
          "Error!",        MB_ICONEXCLAMATION | MB_OK);
          return 0;
    }

while(GetMessage(&wmsg,NULL,0,0))
   {
      TranslateMessage(&wmsg);
      DispatchMessage(&wmsg);
   }
   return wmsg.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMsg,
                         WPARAM wParam, LPARAM lParam)
{
   char string[255];

   switch (iMsg)
      {
         case WM_CLOSE:
            DestroyWindow(hWnd);
            break;
         case WM_DESTROY:
            PostQuitMessage(0);
            return 0;
         case WM_COMMAND:
            switch(LOWORD(wParam))
               {
                 case ID_FILE_OPEN:
                 LoadString(GetModuleHandle(NULL),ID_STRING_OPEN,
                            string, sizeof(string));
                 MessageBox(hWnd,string,
                            "Öffnen",MB_ICONINFORMATION);
                 break;
                 case ID_FILE_SAVE:
                 LoadString(GetModuleHandle(NULL),ID_STRING_SAVE,
                            string,sizeof(string));
                 MessageBox(hWnd,string,
                            "Speichern",MB_ICONINFORMATION);
                 break;
                 case ID_OPTIONS_OPTIONS_OPTION1:
              LoadString(GetModuleHandle(NULL),ID_STRING_OPTION1,
                         string,sizeof(string));
               MessageBox(hWnd,string,
                          "Option 1",MB_ICONINFORMATION);
               break;
               case ID_OPTIONS_OPTIONS_OPTION2:
              LoadString(GetModuleHandle(NULL),ID_STRING_OPTION2,
                          string,sizeof(string));
              MessageBox(hWnd,string,"Option 2",
                         MB_ICONINFORMATION);
              break;
               case ID_ABOUT:
               LoadString(GetModuleHandle(NULL),ID_STRING_ABOUT,
                          string,sizeof(string));
               MessageBox(hWnd,string,"Über",MB_ICONINFORMATION);
               break;
               case ID_FILE_EXIT:
                  DestroyWindow(hWnd);
                  break;
         }
        break;
      }
   return DefWindowProc(hWnd,iMsg,wParam,lParam);
}

In meinem Verzeichnis, welches ich für das Projekt erstellt habe, befinden sich nun folgende Dateien:

Projektübersicht

Projektübersicht

Im Projektverzeichnis befindet sich das Icon "goofy.ico" (Sie können auch ein anderes verwenden), die Hauptfunktion "main.c", die Resourcen-Skriptedatei lautet "main.rc" und die Headerdatei "resource.h". Die Datei "menu.dev" ist eine Datei, die vom Bloodshed-Dev-C++ erstellt wurde. Darin befinden sich alle Angaben für das Projekt. Unter dem Visual C++-Compiler finden Sie höchstwahrscheinlich noch mehr Dateien darin. Wenn Sie jetzt das Projekt Übersetzten und anschließend ausführen, sollten Sie in folgendes Fenster vor sich haben:

Ein Menü mithilfe einer Resourcen-Skript-Datei erstellt

Ein Menü mithilfe einer Resourcen-Skript-Datei erstellt

Auf den nun folgenden Zeilen soll das Programm ein wenig genauer erläutert werden.

4.2. Listing analysieren            zurück  zum Inhaltsverzeichnis

In den ersten Zeilen des Listings finden Sie nichts Neues außer der Headerdatei resource.h, worin die symbolischen Konstanten für die Resourcen deklariert sind:

#include <windows.h>
#include "resource.h"

LPCSTR MainClassName = "Ein Menü-Beispiel";

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMsg,
                         WPARAM wParam, LPARAM lParam);


int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   PSTR szCmdLine, int iCmdShow)
{
   WNDCLASSEX wc;
   MSG wmsg;
   HWND hWnd;

   wc.cbSize = sizeof(WNDCLASSEX);
   wc.style = 0;
   wc.lpfnWndProc = WndProc;
   wc.cbClsExtra = 0;
   wc.cbWndExtra = 0;
   wc.hInstance = hInstance;

In der nächsten Zeile folgt Folgendes:

   wc.hIcon = LoadIcon(GetModuleHandle(NULL),
                        MAKEINTRESOURCE(ID_ICON));

Mit der Funktion LoadIcon(), laden Sie aus der Resourcen-Skriptedatei (*.rc) ein Icon, welches mit dem Resourcen-Typen ICON angegeben wurde. Der erste Parameter der Funktion LoadIcon() ist der Instanz-Handle des Moduls, dessen ausführbare Datei das zu ladende Icon enthält. Dieser Handle wird hier bequem mit der Funktion GetModuleHandle(NULL) zurückgeben. Der zweite Parameter der Funktion LoadIcon() ist ein Zeiger auf einen nullterminierten String, der den Namen der zu ladenden Icon-Resource enthält. Da hierbei eine symbolische Konstante (ID_ICON) verwendet wird, welche Sie in der Headerdatei resource.h deklariert haben, , muss erst mit dem Makro MAKEINTRESOURCE() ein Icon unter der Verwendung der Resourcen-ID geladen werden. Einfach ausgedrückt, konvertiert die Funktion MAKEINTRESOURCE() den Integerwert von der Konstante ID_ICON mithilfe der Resourcen-Sktiptedatei in einen String.

Natürlich können Sie auch mit der Funktion LoadIcon() eines der System-Icons laden. Bspw. mit der Angabe von

wc.hIcon =  LoadIcon(NULL, IDI_QUESTION);  

laden Sie das Standard-Icon IDI_QUESTION. Damit hätten Sie ein Fragezeichen als Icon verwendet, wie Sie in der folgenden Abbildung erkennen können:

Ein System-Icon laden

Ein System-Icon laden

Mehr zur Funktion LoadIcon() entnehmen Sie bitte aus der MSDN-Dokumentation. Genauso wie das Laden eines Icons, verläuft das mit dem Laden eines Mauscursors:

wc.hCursor = LoadCursor(NULL, IDC_ARROW); 

Im Beispiel hier begnügen wir uns mit dem dem Standardcursor, welcher hier mit IDC_ARROW angegeben wird. Die Funktion LoadCursor() besitzt auch dieselben Parameter wie LoadIcon() und kann somit genauso verwendet werden.

Einige weitere Standard-Mauscursors, welche Sie hierbei als zweiten Parameter verwenden können sind: IDC_APPSTARTING, IDC_ARROW, IDC_CROSS, IDC_IBEAM, IDC_NO, IDC_SIZEALL, IDC_SIZE, IDC_SIZENESW, IDC_SIZENS, IDC_SIZENWSE, IDC_SIZEWE, IDC_UPARROW, IDC_WAIT. Sie können gerne diese Cursor-Angaben im Listing testen, um sich ein Bild davon zu machen.

Die nächste Zeile:

wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); 

Anstatt eines vordefinierten Pinsels wird hierbei die Konstante COLOR_WINDOW verwendet. Diese Konstante muss mit einem Typen-Casting (HBRUSH) versehen sein. Bei HBRUSH handelt es sich auch nur wieder um einen Handle. Mit dieser Konstante wird die Hintergrundfarbe des Fensters angegeben. Damit diese nicht einfach grau ist, wurde dieser Werte um eins erhöht, womit der Hintergrund die Farbe weiß hat. Erhöhen Sie bspw. diesen Wert um zwei, hätten Sie einen schwarzen Hintergrund. Probieren Sie am besten selbst ein wenig herum. Außer mit COLOR_WINDOW können Sie auch noch mit anderen Konstanten, der Fensterklasse Systemfarben zuordnen. Mehr dazu können Sie in der MSDN-Dokumentation unter WNDCLASSEX nachlesen.

Weiter geht es mit:

wc.lpszMenuName  = MAKEINTRESOURCE(IDR_MENU1);

Um aus der Resourcen-ID IDR_MENU1, welche in der Headerdatei resource.h deklariert ist, einen nullterminierten String zu machen, wird wieder wie schon bei der Funktion LoadIcon() die Funktion MAKEINTRESOURCE() verwendet.

Weiter geht es mit den Zeilen:

wc.lpszClassName = MainClassName;
wc.hIconSm = (HICON)LoadImage(GetModuleHandle(NULL),
                                 MAKEINTRESOURCE(ID_ICON),
                                 IMAGE_ICON, 16, 16, 0);

Nach der Übergabe des Fensterklassen-Namen, wird die Funktion LoadImage() verwendet, um ein Icon aus der Resource-Skriptdatei zu laden. Da auch hier ein Handle (HICON) erwartet wird, müssen Sie ein Typecasting vornehmen. Die ersten beiden Parameter haben dieselbe Bedeutung, wie schon bei den Funktionen LoadIcon() und LoadCursor(). Der erste Parameter ist somit der Instanz-Handle und der zweite Parameter ein nullterminierter String, der den Namen des zu ladenden Bildes enthält. Nein kein Tippfehler, mit der Funktion LoadImage() können Sie außer Icons, auch einen Cursor oder eine Bitmap laden. Was Sie aus der Resourcen-Skriptdatei laden wollen, geben Sie mit dem dritten Parameter an. Hier IMAGE_ICON für ein Icon. Weitere Angaben hierfür währen: IMAGE_BITMAP für eine Bitmap, IMAGE_CURSOR für einen Mauscursor und IMAGE_ENHMETAFILE für eine erweiterte Metadatei. Mit dem vierten und fünften Parameter geben Sie die gewünschte Breite und Höhe des Icons an und der letzte Parameter gibt an, wie das Bild geladen werden soll.

Die restlichen Zeilen in der WinMain()-Funktion beinhalten nichts Neues mehr. Die Fensterklasse wird registiert, das Fenster erzeugt und angezeigt und am Schluss die Nachrichten-Schleife gestartet. Neu hingegen dürfte die Funktion MessageBox() für Sie sein, womit Sie Meldungsfeld erzeugen können:

Fehlermeldung mit MessageBox()

Fehlermeldung mit MessageBox()

MessageBox() wird häufig für Fehler- und Warnmeldungen verwendet. Bspw. folgende MessageBox()

MessageBox(NULL, "Fehler beim Erstellen des Fensters!",
          "Error!", MB_ICONEXCLAMATION | MB_OK);

Mit dem ersten Parameter können Sie den Handle für das Stammfenster angeben. Geben Sie, wie hier, NULL an, hat das Meldungsfeld kein Stammfenster. Als zweiten Parameter geben Sie einen nullterminierten String für die auszugebende Nachricht an. Der dritte Parameter ist ein nullterminierter String für den Titel des Meldungsfeldes. Im letzten Parameter geben Sie den Inhalt und das Verhalten des Dialogfeldes an. Hier geben Sie mit MB_ICONEXCLAMINATION an, das im Meldungsfenster ein Rufezeichen erscheint und mit MB_OK der Ok-Button. Mit MB_OKCANCEL würden zwei Schaltflächen erscheinen, OK und ABBRECHEN.

Wollen Sie bei solchen Nachrichten-Boxen bei Angabe von mehreren Schaltflächen auswerten, ob den nun der OK-Button oder der ABBRECHEN-Button gedrückt wurde, müssen Sie nur den Rückgabewert der Funktion MessageBox() auswerten:

if(MessageBox(NULL, "Fehler beim Erstellen des Fensters!",
    "Error!",MB_ICONEXCLAMATION | MB_OKCANCEL) == IDOK);
          //OK-Button wurde betätigt
else
          //Abbrechen wurde betätigt

Bei Rückgabe von IDOK wurde die OK-Schaltfläche betätigt und ansonsten ABBRECHEN.

In der Window Prozedur WndProc() folgt auf den ersten Zeilen erstmal nicht viel Neues.

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMsg,
                         WPARAM wParam, LPARAM lParam)
{
   char string[255];

   switch (iMsg)
      {
         case WM_CLOSE:
            DestroyWindow(hWnd);
            break;
         case WM_DESTROY:
            PostQuitMessage(0);
            return 0;

Als Nächstes erfolgt die Überprüfung auf die Nachricht WM_COMMAND

         case WM_COMMAND:  

Windows sendet diese Nachricht immer, wenn ein Menüelement ausgewählt wurde. Um was für ein Menüelement es sich handelt, wird in den folgenden Zeilen ausgewertet:

            switch(LOWORD(wParam))
               {
                 case ID_FILE_OPEN:
                 LoadString(GetModuleHandle(NULL),ID_STRING_OPEN,
                            string, sizeof(string));
                 MessageBox(hWnd,string,
                            "Öffnen",MB_ICONINFORMATION);
                 break;
…
…
               case ID_FILE_EXIT:
                  DestroyWindow(hWnd);
                  break;
         }

Da die Fallunterscheidungen immer ähnlich ablaufen, wurde der Quelltext hier gekürzt. Wenn also Windows die Nachricht WM_COMMAND sendet, befindet sich die ID der Nachricht in den unteren zwei Bytes des Parameters wParam. Diese zwei Bytes können Sie mit dem Makro LOWORD() auswerten. Wurde bspw. das Menüelement "Öffnen" verwendet, befindet sich in den niedrigeren zwei Bytes der Wert der symbolischen Konstante ID_FILE_OPEN, welche Sie in der Headerdatei resoruce.h deklariert haben. In der nächsten Zeile

                 LoadString(GetModuleHandle(NULL),ID_STRING_OPEN,
                            string, sizeof(string));

laden Sie somit mit der Funktion LoadString() einen entsprechenden String aus der Resourcen-Skriptedatei. Hier wird der String mit der Kennung ID_STRING_OPEN, welche in der Headerdatei resource.h definiert ist, in den String string geladen. Der erste Parameter ist der Instanz-Handle, der zweite die ID-Kennung des Strings in der Resource-Datei, der dritte Parameter ist der Puffer, in dem der String geladen wird und mit dem vierten Parameter geben Sie an wie viele Zeichen maximal in den Puffer kopiert werden sollen.

In diesem Beispiel befindet sich jetzt in string die Zeichenkette "Der Dialog Öffnen". In der nächsten Zeile verwenden Sie diesen String gleich bei der Ausgabe einer Meldebox mit:

                 MessageBox(hWnd,string,
                            "Öffnen",MB_ICONINFORMATION);

MessageBox()

MessageBox()

Genauso verläuft dies auch mit den anderen Menüelementen. Außer im Fall von ID_FILE_EXIT senden Sie mit DestroyWindow() an das Fenster mit dem Handle hWnd die Nachricht WM_DESTROY, womit das Fenster gelöscht wird.

4.3. Ein mehrsprachiges Menü            zurück  zum Inhaltsverzeichnis

Es wurde bereits erwähnt, dass es mithilfe von Resourcen-Skripte einfacher ist, ein Menü in verschiedenen Sprachen zu erstellen. Wie dies gemacht wird, will ich Ihnen nicht vorenthalten. Als Beispiel soll dasselbe verwendet werden wie eben mit dem Menü zuvor. Als erstes hierzu die Resourcen-Skriptdatei.

#include <windows.h>
#include "resource.h"

/////////////////////////////////////////////////////////////////
//
// Menü Deutsch
//

IDR_MENU1 MENU
BEGIN
    POPUP "Datei"
    BEGIN
        MENUITEM "Öffnen",           ID_FILE_OPEN
        MENUITEM "Speichern",        ID_FILE_SAVE   GRAYED
        MENUITEM SEPARATOR
        MENUITEM "Ende",             ID_FILE_EXIT
    END
    POPUP "Sprache"
    BEGIN
        POPUP "Sprachen"
        BEGIN
     MENUITEM "Deutsch",      ID_OPTIONS_OPTIONS_OPTION1  CHECKED
     MENUITEM "Englisch",     ID_OPTIONS_OPTIONS_OPTION2
        END
    END
    MENUITEM "Über",                 ID_ABOUT
END


/////////////////////////////////////////////////////////////////
//
// Menü Englisch
//


IDR_MENU2 MENU
BEGIN
    POPUP "File"
    BEGIN
        MENUITEM "Open",             ID_FILE_OPEN
        MENUITEM "Save",             ID_FILE_SAVE   GRAYED
        MENUITEM SEPARATOR
        MENUITEM "Exit",             ID_FILE_EXIT
    END
    POPUP "Languages"
    BEGIN
        POPUP "Languages"
        BEGIN
     MENUITEM "German",       ID_OPTIONS_OPTIONS_OPTION1
     MENUITEM "English",      ID_OPTIONS_OPTIONS_OPTION2  CHECKED
        END
    END
    MENUITEM "About",                ID_ABOUT
END

/////////////////////////////////////////////////////////////////
//
//Icon
//
ID_ICON              ICON        "goofy.ico"

/////////////////////////////////////////////////////////////////
//
//Stringtabelle Deutsch
//
STRINGTABLE
BEGIN
        ID_STRING_OPEN,      "Der Dialog Öffnen"
        ID_STRING_SAVE,      "Der Dialog Speichern"
        ID_STRING_ABOUT,     "Ein Menü-Beispiel\nCoded by J.Wolf"
END

/////////////////////////////////////////////////////////////////
//
//Stringtabelle Englisch
//
STRINGTABLE
BEGIN
        ID_STRING_OPEN_E,      "The Dialog Open"
        ID_STRING_SAVE_E,      "The Dialog Save"
        ID_STRING_ABOUT_E,     "A Menu-Example\nCoded by J.Wolf"
END

Im Großen und Ganzen enthält diese Resourcen-Skritpedatei nicht viel Neues. Außer, dass es jetzt von jeder Ressource zwei verschiedenen Sprachen gibt. Neu dürfte hier auch für Sie die Option GRAYED sein.

        MENUITEM "Speichern",        ID_FILE_SAVE   GRAYED 

Mit dieser Option ist das Menüelement ID_FILE_SAVE (Speichern) deaktiviert.

Menüelment mit GRAYED deaktivieren

Menüelment mit GRAYED deaktivieren

Weiterhin neu ist auch die Option CHECKED in der Resourcen-Skriptdatei.

     MENUITEM "Deutsch",      ID_OPTIONS_OPTIONS_OPTION1  CHECKED  

Damit sorgen Sie, dass sich vor dem Menüelement "Deutsch" ein Häckchen befindet.

Sprachauswahl im Menü

Sprachauswahl im Menü

Gleiches wird auch im Englischen-Menü verwendet.

     MENUITEM "English",      ID_OPTIONS_OPTIONS_OPTION2  CHECKED 

Aber hierbei, logischerweise, im Menüelement "English". Da Sie jetzt in der Resourcen-Skriptdatei mehrere Resourcen verwenden, müssen Sie die Headerdatei resource.h ebenfalls um einige Konstanten erweitern.

//Stringtabelle: Deutsch
#define ID_STRING_OPEN                  1
#define ID_STRING_SAVE                  2
#define ID_STRING_ABOUT                 5
//Stringtabelle: Englisch
#define ID_STRING_OPEN_E                 6
#define ID_STRING_SAVE_E                 7
#define ID_STRING_ABOUT_E                8
//Menü: Deutsch
#define IDR_MENU1                       101
//Menü: Englisch
#define IDR_MENU2                       102

#define ID_ICON                         111

#define ID_FILE_OPEN                    40001
#define ID_FILE_SAVE                    40002
#define ID_FILE_EXIT                    40003
#define ID_OPTIONS_OPTIONS_OPTION1      40004
#define ID_OPTIONS_OPTIONS_OPTION2      40005
#define ID_ABOUT                        65535

Welche Konstante wohin gehört, lässt sich anhand der Kommentare feststellen.

Zu guter Letzt muss das WIN32-Listing noch ein wenig verändert werden. Sie werden überrascht sein, wie wenig dies ist. In der WinMain()-Funktion müssen Sie erst mal gar nichts ändern. Wir legen hier nur eine globale Variable für die Programm-Instanz an, welche Sie in der Prozedur anschließend benötigen (im Listing fett hervorgehoben). Hier die WinMain() Funktion.

#include <windows.h>
#include "resource.h"

LPCSTR MainClassName = "Ein Menü-Beispiel";

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMsg,
                         WPARAM wParam, LPARAM lParam);
//Globale Variable für Programm-Instanz
HINSTANCE hInst;

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   PSTR szCmdLine, int iCmdShow)
{
   WNDCLASSEX wc;
   MSG wmsg;
   HWND hWnd;

   wc.cbSize = sizeof(WNDCLASSEX);
   wc.style          = 0;
   wc.lpfnWndProc = WndProc;
   wc.cbClsExtra = 0;
   wc.cbWndExtra = 0;
   wc.hInstance = hInstance;
   wc.hIcon = LoadIcon(GetModuleHandle(NULL),
                            MAKEINTRESOURCE(ID_ICON));
   wc.hCursor         = LoadCursor(NULL, IDC_CROSS);
   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
   wc.lpszMenuName  = MAKEINTRESOURCE(IDR_MENU1);
   wc.lpszClassName = MainClassName;
   wc.hIconSm         = (HICON)LoadImage(GetModuleHandle(NULL),
                                        MAKEINTRESOURCE(ID_ICON),
                                        IMAGE_ICON, 16, 16, 0);

   if(!RegisterClassEx(&wc))
      {
       MessageBox(NULL, "Windows Registrations Fehler", "Error!",
                  MB_ICONEXCLAMATION | MB_OK);
       return 0;
      }

    hWnd = CreateWindowEx(WS_EX_CLIENTEDGE, MainClassName,
                          "Menü Beispiel",
                          WS_OVERLAPPEDWINDOW|WS_VISIBLE,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          300,150,NULL,NULL,hInstance, NULL);

    //globale Programm-Instanz initialisieren
    hInst = hInstance;

    if(hWnd == NULL)
    {
          if(MessageBox(NULL, "Fehler beim Erstellen des Fensters!",
          "Error!",        MB_ICONEXCLAMATION | MB_OK) == IDOK);
          return 0;
    }

while(GetMessage(&wmsg,NULL,0,0))
   {
      TranslateMessage(&wmsg);
      DispatchMessage(&wmsg);
   }
   return wmsg.wParam;
}

Jetzt müssen Sie noch die Windows-Prozedur WndProc() ein wenig verändern.

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMsg,
                         WPARAM wParam, LPARAM lParam)
{
   char string[255];
   HMENU hMenu = GetMenu(hWnd);
   UINT uState = GetMenuState(hMenu,
                              ID_OPTIONS_OPTIONS_OPTION1,
                              MF_BYCOMMAND);

   switch (iMsg)
      {
         case WM_CLOSE:
            DestroyWindow(hWnd);
            break;
         case WM_DESTROY:
            PostQuitMessage(0);
            return 0;
         case WM_COMMAND:
            switch(LOWORD(wParam))
               {
                 case ID_FILE_OPEN:
                 if(uState & MFS_CHECKED){
                 LoadString(GetModuleHandle(NULL),
                            ID_STRING_OPEN,
                            string, sizeof(string));
                            }
                 else{
                 LoadString(GetModuleHandle(NULL),
                            ID_STRING_OPEN_E,
                            string, sizeof(string));
                 }
                 MessageBox(hWnd,string,
                            "Dialog",MB_ICONINFORMATION);
                 break;
                 case ID_FILE_SAVE:
                 if(uState & MFS_CHECKED){
                 LoadString(GetModuleHandle(NULL),
                            ID_STRING_SAVE,
                            string, sizeof(string));
                            }
                 else{
                 LoadString(GetModuleHandle(NULL),
                            ID_STRING_SAVE_E,
                            string, sizeof(string));
                 }
                 MessageBox(hWnd,string,
                            "Dialog",MB_ICONINFORMATION);
                 break;
                 case ID_OPTIONS_OPTIONS_OPTION1:
               SetMenu(hWnd, LoadMenu(hInst,
                       MAKEINTRESOURCE(IDR_MENU1)));
               break;
               case ID_OPTIONS_OPTIONS_OPTION2:
               SetMenu(hWnd, LoadMenu(hInst,
                       MAKEINTRESOURCE(IDR_MENU2)));
              break;
               case ID_ABOUT:
                 if(uState & MFS_CHECKED){
                 LoadString(GetModuleHandle(NULL),
                            ID_STRING_ABOUT,
                            string, sizeof(string));
                            }
                 else{
                 LoadString(GetModuleHandle(NULL),
                            ID_STRING_ABOUT_E,
                            string, sizeof(string));
                 }
               MessageBox(hWnd,string,"Dialog",
                          MB_ICONINFORMATION);
               break;
               case ID_FILE_EXIT:
                  DestroyWindow(hWnd);
                  break;
         }
        break;
      }
   return DefWindowProc(hWnd,iMsg,wParam,lParam);
}

Hierzu wieder eine etwas genauere Erläuterung zu den Veränderungen in der Prozedur WndProc().

   HMENU hMenu = GetMenu(hWnd); 

HMENU ist ein Menü-Handle für ein Fenster. Diesen ermitteln Sie gleich bei der Deklaration mit der Funktion GetMenu(). Als Parameter übergeben Sie dieser Funktion den Handle für das Fenster und als Rückgabewert bekommen Sie den Menü-Handle.

Hinweis
 

Es wurde zwar bereits erwähnt aber man kann es nicht oft genug sagen da viele Anfänger immer etwas verwirrt sind über den Begriff Handle. Mal verwenden wir dazu HMENU mal HBRUSH, dann wieder HWND. Es handelt sich aber letztendlich immer nur um einen Integerwert. Auf den ersten Blick ist das zwar fremd aber bei einem umfangreichen Listing kann dies sehr hilfreich sein. Man weiß gleich bei der Deklaration, worum es sich bei dem Handle handelt.

In der nächsten Zeile ermitteln Sie mit

   UINT uState = GetMenuState(hMenu,
                              ID_OPTIONS_OPTIONS_OPTION1,
                              MF_BYCOMMAND);

der Funktion GetMenuState() den Status eines Menüelements. Diese Funktion wird sehr häufig verwendet, zum Feststellen, ob ein Menüelement markiert (CHECKED), deaktiviert oder ausgegraut (GRAYED) ist. Der erste Parameter ist dabei der Handle für das Menü, welchen Sie eine Zeile zuvor mit GetMenu() ermittelt haben. Mit dem zweiten Parameter geben Sie die ID oder Position des Menüelements an. Hier wird die Resourcen-ID ID_OPTIONS_OPTIONS_OPTION1 verwendet, welche für das Menüelement der Sprache "Deutsch" steht. Im dritten Parameter geben Sie an, wie der zweite Parameter gehandhabt werden soll. Mit der Angabe von MF_BYCOMMAND geben Sie an, dass der Wert des zweiten Parameters ein Menü-ID darstellt. Wenn Sie für den dritten Parameter die Option MF_BYPOSITION verwenden würden, stellte der Wert des zweiten Parameters eine nullbasierte relative Position des Menüelements da.

Bis zur Zeile

                 case ID_FILE_OPEN:  

verläuft jetzt alles nach gewohnten Bild ab. In der nächsten Zeile befindet sich jetzt Folgendes:

                 if(uState & MFS_CHECKED){  

Damit überprüfen Sie jetzt, ob das Menüelement "Deutsch", dessen Status Sie ja zu Beginn der Prozedur mit GetMenuState() erfragt haben, ge"checked" ist. Dies machen Sie mit dem bitweisen UND-Operator und der Konstante MFS_CECKED. Gibt diese Bedingung TRUE (1) zurück, ist das Menüelement mit einem Häckchen markiert. Also laden Sie mit der nächsten Zeile

                 LoadString(GetModuleHandle(NULL),
                            ID_STRING_OPEN,
                            string, sizeof(string));

den deutschen String für ID_STRING_OPEN aus der Resourcen-Skriptdatei in string. Ist die if-Bedingung hingegen unwahr (FALSE), dann wird mit

                 else{
                 LoadString(GetModuleHandle(NULL),
                            ID_STRING_OPEN_E,
                            string, sizeof(string));
                 }

eben der englischsprachige String ID_STRING_OPEN_E aus der Resourcen-Skriptdatei geladen. Je nachdem, in welcher Sprache, das Programm gerade ausführt wird, wird mit der folgenden Zeile

                 MessageBox(hWnd,string,
                            "Dialog",MB_ICONINFORMATION);

eine Nachrichten-Box erstellt und angezeigt.

Deutsche Version gewählt

Deutsche Version gewählt

Englische Version gewählt

Englische Version gewählt

Mit den nächsten beiden Fallunterscheidungen

                 case ID_OPTIONS_OPTIONS_OPTION1:
               SetMenu(hWnd, LoadMenu(hInst,
                       MAKEINTRESOURCE(IDR_MENU1)));
               break;

               case ID_OPTIONS_OPTIONS_OPTION2:
               SetMenu(hWnd, LoadMenu(hInst,
                       MAKEINTRESOURCE(IDR_MENU2)));
              break;

überprüfen Sie, ob das Menüelement mit der ID ID_OPTIONS_OPTIONS_OPTION1 (Deutsch) oder mit ID_OPTIONS_OPTIONS_OPTION2 (Englisch) betätigt wurde. Je nach Fall ordnen Sie dem Fenster mit der Funktion SetMenu() ein entsprechendes Menü zu. Der erste Parameter von SetMenu() ist das Handle für das Fenster. Der zweite Parameter ist ein Handle für das Menü. Da Sie hierbei ein Menü erst aus einer Resourcen-Skriptedatei laden müssen, setzten Sie hier die Funktion LoadMenu() ein. Diese Funktion benötigt als ersten Parameter die Programm-Instanz, welchen Sie zu Beginn als globale Instanz angelegt haben. Der zweite Parameter ist wieder ein nullterminierter String, welchen Sie sich hier mit dem Makro MAKEINTRESOURCE() ermitteln lassen. Mehr ist nicht nötig, um ein mehrsprachiges Menü zu erstellen.

Deutsches Menü

Deutsches Menü

Englisches Menü

Englisches Menü

Weiter mit Kapitel 5: Dialogfelder            zum Inhaltsverzeichnis