Freigeben über


Verwenden von Tastenkombinationen

In diesem Abschnitt werden Aufgaben behandelt, die Tastaturbeschleunigungen zugeordnet sind.

Verwenden einer Accelerator-Tabellenressource

Die gängigste Methode zum Hinzufügen von Acceleratorunterstützung zu einer Anwendung besteht darin, eine Acceleratortabellenressource in die ausführbare Datei der Anwendung einzuschließen und die Ressource dann zur Laufzeit zu laden.

In diesem Abschnitt werden die folgenden Themen behandelt.

Erstellen der Accelerator-Tabellenressource

Sie erstellen eine Acceleratortabellenressource, indem Sie die ACCELERATORS-Anweisung in der Ressourcendefinitionsdatei Ihrer Anwendung verwenden. Sie müssen der Acceleratortabelle einen Namen oder Ressourcenbezeichner zuweisen, vorzugsweise im Gegensatz zu jeder anderen Ressource. Das System verwendet diesen Bezeichner, um die Ressource zur Laufzeit zu laden.

Jeder von Ihnen definierte Accelerator erfordert einen separaten Eintrag in der Accelerator-Tabelle. In jedem Eintrag definieren Sie die Tasteneingabe (entweder einen ASCII-Zeichencode oder einen Virtuellen Schlüsselcode), der den Accelerator und den Bezeichner des Beschleunigers generiert. Sie müssen auch angeben, ob die Tasteneingabe in einer Kombination mit der ALT-, UMSCHALT- oder STRG-TASTE verwendet werden muss. Weitere Informationen zu virtuellen Tasten finden Sie unter Tastatureingabe.

Eine ASCII-Tastenkombination wird entweder durch Einschließen des ASCII-Zeichens in doppelte Anführungszeichen oder durch Verwendung des ganzzahligen Werts des Zeichens in Kombination mit dem ASCII-Flag angegeben. Die folgenden Beispiele zeigen, wie ASCII-Beschleuniger definiert werden.

"A", ID_ACCEL1         ; SHIFT+A 
65,  ID_ACCEL2, ASCII  ; SHIFT+A 

Eine Tasteneingabe mit virtuellem Schlüsselcode wird unterschiedlich angegeben, je nachdem, ob es sich bei der Tasteneingabe um einen alphanumerischen Schlüssel oder einen nicht alphanumerischen Schlüssel handelt. Bei einem alphanumerischen Schlüssel wird der In doppelte Anführungszeichen eingeschlossene Buchstabe oder Zahl des Schlüssels mit dem VIRTKEY-Flag kombiniert. Bei einem nicht alphanumerischen Schlüssel wird der virtuelle Schlüsselcode für den spezifischen Schlüssel mit dem VIRTKEY-Flag kombiniert. Die folgenden Beispiele zeigen, wie Sie Codebeschleuniger für virtuelle Schlüssel definieren.

"a",       ID_ACCEL3, VIRTKEY   ; A (caps-lock on) or a 
VK_INSERT, ID_ACCEL4, VIRTKEY   ; INSERT key 

Das folgende Beispiel zeigt eine Beschleunigertabellenressource, die Accelerators für Dateivorgänge definiert. Der Name der Ressource lautet FileAccel.

FileAccel ACCELERATORS 
BEGIN 
    VK_F12, IDM_OPEN, CONTROL, VIRTKEY  ; CTRL+F12 
    VK_F4,  IDM_CLOSE, ALT, VIRTKEY     ; ALT+F4 
    VK_F12, IDM_SAVE, SHIFT, VIRTKEY    ; SHIFT+F12 
    VK_F12, IDM_SAVEAS, VIRTKEY         ; F12 
END 

Wenn der Benutzer die ALT-, UMSCHALT- oder STRG-TASTE in einer Kombination mit der Tastenkombination drücken soll, geben Sie die Flags ALT, SHIFT und CONTROL in der Definition des Beschleunigers an. Hier einige Beispiele.

"B",   ID_ACCEL5, ALT                   ; ALT_SHIFT+B 
"I",   ID_ACCEL6, CONTROL, VIRTKEY      ; CTRL+I 
VK_F5, ID_ACCEL7, CONTROL, ALT, VIRTKEY ; CTRL+ALT+F5 

Wenn eine Tastenkombination einem Menüelement entspricht, hebt das System standardmäßig das Menüelement hervor. Sie können das NOINVERT-Flag verwenden, um die Hervorhebung für einen einzelnen Accelerator zu verhindern. Das folgende Beispiel zeigt die Verwendung des NOINVERT-Flags :

VK_DELETE, ID_ACCEL8, VIRTKEY, SHIFT, NOINVERT  ; SHIFT+DELETE 

Um Beschleunigungsbeschleunigungen zu definieren, die Menüelementen in Ihrer Anwendung entsprechen, fügen Sie die Accelerators in den Text der Menüelemente ein. Das folgende Beispiel zeigt, wie Sie Beschleunigungsbeschleunigungen in Menüelementtext in eine Ressourcendefinitionsdatei einschließen.

FilePopup MENU 
BEGIN 
    POPUP   "&File" 
    BEGIN 
        MENUITEM    "&New..",           IDM_NEW 
        MENUITEM    "&Open\tCtrl+F12",  IDM_OPEN 
        MENUITEM    "&Close\tAlt+F4"    IDM_CLOSE 
        MENUITEM    "&Save\tShift+F12", IDM_SAVE 
        MENUITEM    "Save &As...\tF12", IDM_SAVEAS 
    END 
END 

Laden der Accelerator-Tabellenressource

Eine Anwendung lädt eine Acceleratortabellenressource, indem sie die LoadAccelerators-Funktion aufruft und das instance Handle für die Anwendung angibt, deren ausführbare Datei die Ressource und den Namen oder bezeichner der Ressource enthält. LoadAccelerators lädt die angegebene Acceleratortabelle in den Arbeitsspeicher und gibt das Handle an die Acceleratortabelle zurück.

Eine Anwendung kann jederzeit eine Beschleunigertabellenressource laden. In der Regel lädt eine Singlethreadanwendung ihre Acceleratortabelle, bevor sie ihre Standard Nachrichtenschleife eingibt. Eine Anwendung, die mehrere Threads verwendet, lädt in der Regel die Accelerator-Table-Ressource für einen Thread, bevor sie die Nachrichtenschleife für den Thread eingibt. Eine Anwendung oder ein Thread kann auch mehrere Acceleratortabellen verwenden, die jeweils einem bestimmten Fenster in der Anwendung zugeordnet sind. Eine solche Anwendung würde die Acceleratortabelle für das Fenster jedes Mal laden, wenn der Benutzer das Fenster aktiviert hat. Weitere Informationen zu Threads finden Sie unter Prozesse und Threads.

Aufrufen der Translate Accelerator-Funktion

Zum Verarbeiten von Beschleunigern muss die Nachrichtenschleife einer Anwendung (oder der Thread) einen Aufruf der TranslateAccelerator-Funktion enthalten. TranslateAccelerator vergleicht Tastenanschläge mit einer Beschleunigungstabelle und übersetzt die Tastenanschläge in eine WM_COMMAND (oder WM_SYSCOMMAND)-Nachricht, wenn eine Übereinstimmung gefunden wird. Die Funktion sendet die Nachricht dann an eine Fensterprozedur. Die Parameter der TranslateAccelerator-Funktion umfassen das Handle für das Fenster, das die WM_COMMAND Nachrichten empfangen soll, das Handle zur Beschleunigertabelle, die zum Übersetzen von Beschleunigern verwendet wird, und einen Zeiger auf eine MSG-Struktur , die eine Nachricht aus der Warteschlange enthält. Das folgende Beispiel zeigt, wie TranslateAccelerator innerhalb einer Nachrichtenschleife aufgerufen wird.

MSG msg;
BOOL bRet;

while ( (bRet = GetMessage(&msg, (HWND) NULL, 0, 0)) != 0)
{
    if (bRet == -1) 
    {
        // handle the error and possibly exit
    }
    else
    { 
        // Check for accelerator keystrokes. 
     
        if (!TranslateAccelerator( 
                hwndMain,      // handle to receiving window 
                haccel,        // handle to active accelerator table 
                &msg))         // message data 
        {
            TranslateMessage(&msg); 
            DispatchMessage(&msg); 
        } 
    } 
}

Verarbeiten WM_COMMAND Nachrichten

Wenn ein Accelerator verwendet wird, empfängt das in der TranslateAccelerator-Funktion angegebene Fenster eine WM_COMMAND - oder WM_SYSCOMMAND-Nachricht . Das Wort mit niedriger Reihenfolge des wParam-Parameters enthält den Bezeichner des Accelerators. Die Fensterprozedur untersucht den Bezeichner, um die Quelle der WM_COMMAND Nachricht zu ermitteln und die Nachricht entsprechend zu verarbeiten.

Wenn ein Accelerator einem Menüelement in der Anwendung entspricht, werden dem Accelerator und dem Menüelement in der Regel derselbe Bezeichner zugewiesen. Wenn Sie wissen müssen, ob eine WM_COMMAND Nachricht von einem Accelerator oder von einem Menüelement generiert wurde, können Sie das hochgeordnete Wort des wParam-Parameters untersuchen. Wenn die Nachricht von einem Beschleuniger generiert wurde, ist das Wort mit hoher Reihenfolge 1; wenn die Nachricht von einem Menüelement generiert wurde, lautet das Wort mit hoher Reihenfolge 0.

Zerstören der Accelerator-Tabellenressource

Das System zerstört automatisch Beschleunigertabellenressourcen, die von der LoadAccelerators-Funktion geladen werden, und entfernt die Ressource aus dem Arbeitsspeicher, nachdem die Anwendung geschlossen wurde.

Erstellen von Beschleunigungsbeschleunigungen für Schriftartattribute

Das Beispiel in diesem Abschnitt zeigt, wie Die folgenden Aufgaben ausgeführt werden:

  • Erstellen Sie eine Acceleratortabellenressource.
  • Laden Sie die Acceleratortabelle zur Laufzeit.
  • Übersetzen von Beschleunigungsbeschleunigungen in einer Nachrichtenschleife.
  • Verarbeiten sie WM_COMMAND Nachrichten, die von den Accelerators generiert werden.

Diese Aufgaben werden im Kontext einer Anwendung veranschaulicht, die ein Zeichenmenü und entsprechende Beschleunigungsbeschleunigungen enthält, die es dem Benutzer ermöglichen, Attribute der aktuellen Schriftart auszuwählen.

Der folgende Teil einer Ressourcendefinitionsdatei definiert das Zeichenmenü und die zugehörige Acceleratortabelle. Beachten Sie, dass die Menüelemente die Tastenkombinationen der Beschleuniger anzeigen und dass jeder Beschleuniger denselben Bezeichner wie das zugeordnete Menüelement aufweist.

#include <windows.h> 
#include "acc.h" 
 
MainMenu MENU 
{ 
    POPUP   "&Character" 
    { 
        MENUITEM    "&Regular\tF5",         IDM_REGULAR 
        MENUITEM    "&Bold\tCtrl+B",        IDM_BOLD 
        MENUITEM    "&Italic\tCtrl+I",      IDM_ITALIC 
        MENUITEM    "&Underline\tCtrl+U",   IDM_ULINE 
    }
} 
 
FontAccel ACCELERATORS 
{ 
    VK_F5,  IDM_REGULAR,    VIRTKEY 
    "B",    IDM_BOLD,       CONTROL, VIRTKEY 
    "I",    IDM_ITALIC,     CONTROL, VIRTKEY 
    "U",    IDM_ULINE,      CONTROL, VIRTKEY 
}
 

In den folgenden Abschnitten aus der Quelldatei der Anwendung wird gezeigt, wie die Accelerators implementiert werden.

HWND hwndMain;      // handle to main window 
HANDLE hinstAcc;    // handle to application instance 
int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpCmdLine, int nCmdShow) 
{ 
    MSG msg;            // application messages 
    BOOL bRet;          // for return value of GetMessage
    HACCEL haccel;      // handle to accelerator table 
 
    // Perform the initialization procedure. 
 
    // Create a main window for this application instance. 
 
    hwndMain = CreateWindowEx(0L, "MainWindowClass", 
        "Sample Application", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, 
        hinst, NULL ); 
 
    // If a window cannot be created, return "failure." 
 
    if (!hwndMain) 
        return FALSE; 
 
    // Make the window visible and update its client area. 
 
    ShowWindow(hwndMain, nCmdShow); 
    UpdateWindow(hwndMain); 
 
    // Load the accelerator table. 
 
    haccel = LoadAccelerators(hinstAcc, "FontAccel"); 
    if (haccel == NULL) 
        HandleAccelErr(ERR_LOADING);     // application defined 
 
    // Get and dispatch messages until a WM_QUIT message is 
    // received. 
 
    while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
    {
        if (bRet == -1)
        {
            // handle the error and possibly exit
        }
        else
        { 
            // Check for accelerator keystrokes. 
     
            if (!TranslateAccelerator( 
                    hwndMain,  // handle to receiving window 
                    haccel,    // handle to active accelerator table 
                    &msg))         // message data 
            {
                TranslateMessage(&msg); 
                DispatchMessage(&msg); 
            } 
        } 
    }
    return msg.wParam; 
} 
 
LRESULT APIENTRY MainWndProc(HWND hwndMain, UINT uMsg, WPARAM wParam, LPARAM lParam) 
{ 
    BYTE fbFontAttrib;        // array of font-attribute flags 
    static HMENU hmenu;       // handle to main menu 
 
    switch (uMsg) 
    { 
        case WM_CREATE: 
 
            // Add a check mark to the Regular menu item to 
            // indicate that it is the default. 
 
            hmenu = GetMenu(hwndMain); 
            CheckMenuItem(hmenu, IDM_REGULAR, MF_BYCOMMAND | 
                MF_CHECKED); 
            return 0; 
 
        case WM_COMMAND: 
            switch (LOWORD(wParam)) 
            { 
                // Process the accelerator and menu commands. 
 
                case IDM_REGULAR: 
                case IDM_BOLD: 
                case IDM_ITALIC: 
                case IDM_ULINE: 
 
                    // GetFontAttributes is an application-defined 
                    // function that sets the menu-item check marks 
                    // and returns the user-selected font attributes. 
 
                    fbFontAttrib = GetFontAttributes( 
                        (BYTE) LOWORD(wParam), hmenu); 
 
                    // SetFontAttributes is an application-defined 
                    // function that creates a font with the 
                    // user-specified attributes the font with 
                    // the main window's device context. 
 
                    SetFontAttributes(fbFontAttrib); 
                    break; 
 
                default: 
                    break; 
            } 
            break; 
 
            // Process other messages. 
 
        default: 
            return DefWindowProc(hwndMain, uMsg, wParam, lParam); 
    } 
    return NULL; 
}

Verwenden einer zur Laufzeit erstellten Accelerator-Tabelle

In diesem Thema wird erläutert, wie Sie Zur Laufzeit erstellte Acceleratortabellen verwenden.

Erstellen einer Run-Time Accelerator-Tabelle

Der erste Schritt beim Erstellen einer Acceleratortabelle zur Laufzeit besteht darin, ein Array von ACCEL-Strukturen zu füllen. Jede Struktur im Array definiert einen Accelerator in der Tabelle. Die Definition eines Accelerators umfasst seine Flags, seinen Schlüssel und seinen Bezeichner. Die ACCEL-Struktur hat das folgende Format.

typedef struct tagACCEL { // accl 
    BYTE   fVirt; 
    WORD   key; 
    WORD   cmd; 
} ACCEL;

Sie definieren die Tastenkombination einer Beschleunigertaste, indem Sie einen ASCII-Zeichencode oder einen Virtuellen Schlüsselcode im Schlüsselelement der ACCEL-Struktur angeben. Wenn Sie einen Virtuellen Schlüsselcode angeben, müssen Sie zuerst das FVIRTKEY-Flag in das fVirt-Element einfügen. Andernfalls interpretiert das System den Code als ASCII-Zeichencode. Sie können das FCONTROL-, FALT- oder FSHIFT-Flag oder alle drei einschließen, um die TASTENKOMBINATION STRG, ALT oder UMSCHALT mit der Tastatureingabe zu kombinieren.

Um die Zugriffstastentabelle zu erstellen, übergeben Sie einen Zeiger auf das Array von ACCEL-Strukturen an die CreateAcceleratorTable-Funktion . CreateAcceleratorTable erstellt die Zugriffstastentabelle und gibt das Handle an die Tabelle zurück.

Verarbeitungsbeschleunigungen

Der Prozess des Ladens und Aufrufens von Beschleunigern, die von einer zur Laufzeit erstellten Acceleratortabelle bereitgestellt werden, entspricht der Verarbeitung von Beschleunigertabellenressourcen. Weitere Informationen finden Sie unter Laden der Zugriffstastentabellenressource durch Verarbeiten WM_COMMAND Nachrichten.

Zerstören einer Run-Time Accelerator-Tabelle

Das System zerstört automatisch Zugriffstastentabellen, die zur Laufzeit erstellt wurden, und entfernt die Ressourcen aus dem Arbeitsspeicher, nachdem die Anwendung geschlossen wurde. Sie können eine Zugriffstastentabelle zerstören und aus dem Arbeitsspeicher entfernen, indem Sie das Handle der Tabelle an die DestroyAcceleratorTable-Funktion übergeben.

Erstellen von bearbeitbaren Zugriffstasten für Benutzer

In diesem Beispiel wird gezeigt, wie Sie ein Dialogfeld erstellen, das es dem Benutzer ermöglicht, die einem Menüelement zugeordnete Zugriffstaste zu ändern. Das Dialogfeld besteht aus einem Kombinationsfeld mit Menüelementen, einem Kombinationsfeld mit den Namen der Tasten und Kontrollkästchen zum Auswählen der TASTEN STRG, ALT und UMSCHALT. Die folgende Abbildung zeigt das Dialogfeld.

Dialogfeld mit Kombinationsfeldern und Kontrollkästchen

Das folgende Beispiel zeigt, wie das Dialogfeld in der Ressourcendefinitionsdatei definiert ist.

EdAccelBox DIALOG 5, 17, 193, 114 
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION 
CAPTION "Edit Accelerators" 
BEGIN 
    COMBOBOX        IDD_MENUITEMS, 10, 22, 52, 53, 
                        CBS_SIMPLE | CBS_SORT | WS_VSCROLL | 
                        WS_TABSTOP 
    CONTROL         "Control", IDD_CNTRL, "Button", 
                        BS_AUTOCHECKBOX | WS_TABSTOP, 
                        76, 35, 40, 10 
    CONTROL         "Alt", IDD_ALT, "Button", 
                        BS_AUTOCHECKBOX | WS_TABSTOP, 
                        76, 48, 40, 10 
    CONTROL         "Shift", IDD_SHIFT, "Button", 
                        BS_AUTOCHECKBOX | WS_TABSTOP, 
                        76, 61, 40, 10 
    COMBOBOX        IDD_KEYSTROKES, 124, 22, 58, 58, 
                        CBS_SIMPLE | CBS_SORT | WS_VSCROLL | 
                        WS_TABSTOP 
    PUSHBUTTON      "Ok", IDOK, 43, 92, 40, 14 
    PUSHBUTTON      "Cancel", IDCANCEL, 103, 92, 40, 14 
    LTEXT           "Select Item:", 101, 10, 12, 43, 8 
    LTEXT           "Select Keystroke:", 102, 123, 12, 60, 8 
END

Die Menüleiste der Anwendung enthält ein Zeichenuntermenü , dessen Elemente Beschleuniger zugeordnet sind.

MainMenu MENU 
{ 
    POPUP "&Character" 
    { 
        MENUITEM    "&Regular\tF5",         IDM_REGULAR 
        MENUITEM    "&Bold\tCtrl+B",        IDM_BOLD 
        MENUITEM    "&Italic\tCtrl+I",      IDM_ITALIC 
        MENUITEM    "&Underline\tCtrl+U",   IDM_ULINE 
    }
} 
 
FontAccel ACCELERATORS 
{ 
    VK_F5,  IDM_REGULAR,    VIRTKEY 
    "B",    IDM_BOLD,       CONTROL, VIRTKEY 
    "I",    IDM_ITALIC,     CONTROL, VIRTKEY 
    "U",    IDM_ULINE,      CONTROL, VIRTKEY 
}
 

Die Menüelementwerte für die Menüvorlage sind Konstanten, die wie folgt in der Headerdatei der Anwendung definiert sind.

#define IDM_REGULAR    1100
#define IDM_BOLD       1200
#define IDM_ITALIC     1300
#define IDM_ULINE      1400

Das Dialogfeld verwendet ein Array von anwendungsdefinierten VKEY-Strukturen, die jeweils eine Tastatureingabe-Text-Zeichenfolge und eine Beschleuniger-Text-Zeichenfolge enthalten. Wenn das Dialogfeld erstellt wird, analysiert es das Array und fügt jede Tastenkombinationszeichenfolge dem Kombinationsfeld Tastenkombination auswählen hinzu. Wenn der Benutzer auf die Schaltfläche OK klickt, sucht das Dialogfeld die ausgewählte Tastatureingabe-Text-Zeichenfolge und ruft die entsprechende Tastenkombinations-Text-Zeichenfolge ab. Das Dialogfeld fügt die Tastenkombinationszeichenfolge an den Text des Menüelements an, das der Benutzer ausgewählt hat. Das folgende Beispiel zeigt das Array von VKEY-Strukturen:

// VKey Lookup Support 
 
#define MAXKEYS 25 
 
typedef struct _VKEYS { 
    char *pKeyName; 
    char *pKeyString; 
} VKEYS; 
 
VKEYS vkeys[MAXKEYS] = { 
    "BkSp",     "Back Space", 
    "PgUp",     "Page Up", 
    "PgDn",     "Page Down", 
    "End",      "End", 
    "Home",     "Home", 
    "Lft",      "Left", 
    "Up",       "Up", 
    "Rgt",      "Right", 
    "Dn",       "Down", 
    "Ins",      "Insert", 
    "Del",      "Delete", 
    "Mult",     "Multiply", 
    "Add",      "Add", 
    "Sub",      "Subtract", 
    "DecPt",    "Decimal Point", 
    "Div",      "Divide", 
    "F2",       "F2", 
    "F3",       "F3", 
    "F5",       "F5", 
    "F6",       "F6", 
    "F7",       "F7", 
    "F8",       "F8", 
    "F9",       "F9", 
    "F11",      "F11", 
    "F12",      "F12" 
};

Die Initialisierungsprozedur des Dialogfelds füllt die Kombinationsfelder Element auswählen und Tastenkombination auswählen aus. Nachdem der Benutzer ein Menüelement und eine zugeordnete Zugriffstaste ausgewählt hat, überprüft das Dialogfeld die Steuerelemente im Dialogfeld, um die Auswahl des Benutzers abzurufen, aktualisiert den Text des Menüelements und erstellt dann eine neue Zugriffstastentabelle, die die benutzerdefinierte neue Zugriffstaste enthält. Das folgende Beispiel zeigt die Dialogfeldprozedur. Beachten Sie, dass Sie in Ihrer Fensterprozedur initialisieren müssen.

// Global variables 
 
HWND hwndMain;      // handle to main window 
HACCEL haccel;      // handle to accelerator table 
 
// Dialog-box procedure 
 
BOOL CALLBACK EdAccelProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) 
{ 
    int nCurSel;            // index of list box item 
    UINT idItem;            // menu-item identifier 
    UINT uItemPos;          // menu-item position 
    UINT i, j = 0;          // loop counters 
    static UINT cItems;     // count of items in menu 
    char szTemp[32];        // temporary buffer 
    char szAccelText[32];   // buffer for accelerator text 
    char szKeyStroke[16];   // buffer for keystroke text 
    static char szItem[32]; // buffer for menu-item text 
    HWND hwndCtl;           // handle to control window 
    static HMENU hmenu;     // handle to "Character" menu 
    PCHAR pch, pch2;        // pointers for string copying 
    WORD wVKCode;           // accelerator virtual-key code 
    BYTE fAccelFlags;       // fVirt flags for ACCEL structure 
    LPACCEL lpaccelNew;     // pointer to new accelerator table 
    HACCEL haccelOld;       // handle to old accelerator table 
    int cAccelerators;      // number of accelerators in table 
    static BOOL fItemSelected = FALSE; // item selection flag 
    static BOOL fKeySelected = FALSE;  // key selection flag 
    HRESULT hr;
    INT numTCHAR;           // TCHARs in listbox text
 
    switch (uMsg) 
    { 
        case WM_INITDIALOG: 
 
            // Get the handle to the menu-item combo box. 
 
            hwndCtl = GetDlgItem(hwndDlg, IDD_MENUITEMS); 
 
            // Get the handle to the Character submenu and
            // count the number of items it has. In this example, 
            // the menu has position 0. You must alter this value 
            // if you add additional menus. 
            hmenu = GetSubMenu(GetMenu(hwndMain), 0); 
            cItems = GetMenuItemCount(hmenu); 
 
            // Get the text of each item, strip out the '&' and 
            // the accelerator text, and add the text to the 
            // menu-item combo box. 
 
            for (i = 0; i < cItems; i++) 
            { 
                if (!(GetMenuString(hmenu, i, szTemp, 
                        sizeof(szTemp)/sizeof(TCHAR), MF_BYPOSITION))) 
                    continue; 
                for (pch = szTemp, pch2 = szItem; *pch != '\0'; ) 
                { 
                    if (*pch != '&') 
                    { 
                        if (*pch == '\t') 
                        { 
                            *pch = '\0'; 
                            *pch2 = '\0'; 
                        } 
                        else *pch2++ = *pch++; 
                    } 
                    else pch++; 
                } 
                SendMessage(hwndCtl, CB_ADDSTRING, 0, 
                    (LONG) (LPSTR) szItem); 
            } 
 
            // Now fill the keystroke combo box with the list of 
            // keystrokes that will be allowed for accelerators. 
            // The list of keystrokes is in the application-defined 
            // structure called "vkeys". 
 
            hwndCtl = GetDlgItem(hwndDlg, IDD_KEYSTROKES); 
            for (i = 0; i < MAXKEYS; i++) 
            {
                SendMessage(hwndCtl, CB_ADDSTRING, 0, 
                    (LONG) (LPSTR) vkeys[i].pKeyString); 
            }
 
            return TRUE; 
 
        case WM_COMMAND: 
            switch (LOWORD(wParam)) 
            { 
                case IDD_MENUITEMS: 
 
                    // The user must select an item from the combo 
                    // box. This flag is checked during IDOK
                    // processing to be sure a selection was made. 
 
                    fItemSelected = TRUE; 
                    return 0; 
 
                case IDD_KEYSTROKES: 
 
                    // The user must select an item from the combo
                    // box. This flag is checked during IDOK
                    // processing to be sure a selection was made. 
 
                    fKeySelected = TRUE; 
 
                    return 0; 
 
                case IDOK: 
 
                    // If the user has not selected a menu item 
                    // and a keystroke, display a reminder in a 
                    // message box. 
 
                    if (!fItemSelected || !fKeySelected) 
                    { 
                        MessageBox(hwndDlg, 
                            "Item or key not selected.", NULL, 
                            MB_OK); 
                        return 0; 
                    } 
 
                    // Determine whether the CTRL, ALT, and SHIFT 
                    // keys are selected. Concatenate the 
                    // appropriate strings to the accelerator- 
                    // text buffer, and set the appropriate 
                    // accelerator flags. 
 
                    szAccelText[0] = '\0'; 
                    hwndCtl = GetDlgItem(hwndDlg, IDD_CNTRL); 
                    if (SendMessage(hwndCtl, BM_GETCHECK, 0, 0) == 1) 
                    { 
                        hr = StringCchCat(szAccelText, 32, "Ctl+");
                        if (FAILED(hr))
                        {
                        // TODO: write error handler
                        }
                        fAccelFlags |= FCONTROL; 
                    } 
                    hwndCtl = GetDlgItem(hwndDlg, IDD_ALT); 
                    if (SendMessage(hwndCtl, BM_GETCHECK, 0, 0) == 1) 
                    { 
                        hr = StringCchCat(szAccelText, 32, "Alt+");
                        if (FAILED(hr))
                        {
                        // TODO: write error handler
                        }
                        fAccelFlags |= FALT; 
                    } 
                    hwndCtl = GetDlgItem(hwndDlg, IDD_SHIFT); 
                    if (SendMessage(hwndCtl, BM_GETCHECK, 0, 0) == 1) 
                    {
                        hr = StringCchCat(szAccelText, 32, "Shft+");
                        if (FAILED(hr))
                        {
                        // TODO: write error handler
                        }
                        fAccelFlags |= FSHIFT; 
                    } 
 
                    // Get the selected keystroke, and look up the 
                    // accelerator text and the virtual-key code 
                    // for the keystroke in the vkeys structure. 
 
                    hwndCtl = GetDlgItem(hwndDlg, IDD_KEYSTROKES); 
                    nCurSel = (int) SendMessage(hwndCtl, 
                        CB_GETCURSEL, 0, 0);
                    numTCHAR = SendMessage(hwndCtl, CB_GETLBTEXTLEN, 
                        nCursel, 0); 
                    if (numTCHAR <= 15)
                        {                   
                        SendMessage(hwndCtl, CB_GETLBTEXT, 
                            nCurSel, (LONG) (LPSTR) szKeyStroke);
                        }
                    else
                        {
                        // TODO: writer error handler
                        }
                         
                    for (i = 0; i < MAXKEYS; i++) 
                    {
                    //
                    // lstrcmp requires that both parameters are
                    // null-terminated.
                    //
                        if(lstrcmp(vkeys[i].pKeyString, szKeyStroke) 
                            == 0) 
                        { 
                            hr = StringCchCopy(szKeyStroke, 16, vkeys[i].pKeyName);
                            if (FAILED(hr))
                            {
                            // TODO: write error handler
                            }
                            break; 
                        } 
                    } 
 
                    // Concatenate the keystroke text to the 
                    // "Ctl+","Alt+", or "Shft+" string. 
 
                        hr = StringCchCat(szAccelText, 32, szKeyStroke);
                        if (FAILED(hr))
                        {
                        // TODO: write error handler
                        }
 
                    // Determine the position in the menu of the 
                    // selected menu item. Menu items in the 
                    // "Character" menu have positions 0,2,3, and 4.
                    // Note: the lstrcmp parameters must be
                    // null-terminated. 
 
                    if (lstrcmp(szItem, "Regular") == 0) 
                        uItemPos = 0; 
                    else if (lstrcmp(szItem, "Bold") == 0) 
                        uItemPos = 2; 
                    else if (lstrcmp(szItem, "Italic") == 0) 
                        uItemPos = 3; 
                    else if (lstrcmp(szItem, "Underline") == 0) 
                        uItemPos = 4; 
 
                    // Get the string that corresponds to the 
                    // selected item. 
 
                    GetMenuString(hmenu, uItemPos, szItem, 
                        sizeof(szItem)/sizeof(TCHAR), MF_BYPOSITION); 
 
                    // Append the new accelerator text to the 
                    // menu-item text. 
 
                    for (pch = szItem; *pch != '\t'; pch++); 
                        ++pch; 
 
                    for (pch2 = szAccelText; *pch2 != '\0'; pch2++) 
                        *pch++ = *pch2; 
                    *pch = '\0'; 
 
                    // Modify the menu item to reflect the new 
                    // accelerator text. 
 
                    idItem = GetMenuItemID(hmenu, uItemPos); 
                    ModifyMenu(hmenu, idItem, MF_BYCOMMAND | 
                        MF_STRING, idItem, szItem); 
 
                    // Reset the selection flags. 
 
                    fItemSelected = FALSE; 
                    fKeySelected = FALSE; 
 
                    // Save the current accelerator table. 
 
                    haccelOld = haccel; 
 
                    // Count the number of entries in the current 
                    // table, allocate a buffer for the table, and 
                    // then copy the table into the buffer. 
 
                    cAccelerators = CopyAcceleratorTable( 
                        haccelOld, NULL, 0); 
                    lpaccelNew = (LPACCEL) LocalAlloc(LPTR, 
                        cAccelerators * sizeof(ACCEL)); 
 
                    if (lpaccelNew != NULL) 
                    {
                        CopyAcceleratorTable(haccel, lpaccelNew, 
                            cAccelerators); 
                    }
 
                    // Find the accelerator that the user modified 
                    // and change its flags and virtual-key code 
                    // as appropriate. 
 
                    for (i = 0; i < (UINT) cAccelerators; i++) 
                    { 
                           if (lpaccelNew[i].cmd == (WORD) idItem)
                        {
                            lpaccelNew[i].fVirt = fAccelFlags; 
                            lpaccelNew[i].key = wVKCode; 
                        }
                    } 
 
                    // Create the new accelerator table, and 
                    // destroy the old one. 
 
                    DestroyAcceleratorTable(haccelOld); 
                    haccel = CreateAcceleratorTable(lpaccelNew, 
                        cAccelerators); 
 
                    // Destroy the dialog box. 
 
                    EndDialog(hwndDlg, TRUE); 
                    return 0; 
 
                case IDCANCEL: 
                    EndDialog(hwndDlg, TRUE); 
                    return TRUE; 
 
                default: 
                    break; 
            } 
        default: 
            break; 
    } 
    return FALSE; 
}