Delen via


Toetsenbordversnellers gebruiken

In deze sectie worden taken behandeld die zijn gekoppeld aan toetsenbordversnellers.

Een acceleratortabelresource gebruiken

De meest voorkomende manier om acceleratorondersteuning toe te voegen aan een toepassing is door een accelerator-tabelresource op te nemen met het uitvoerbare bestand van de toepassing en vervolgens de resource tijdens runtime te laden.

In deze sectie worden de volgende onderwerpen behandeld.

Het maken van de resource voor de acceleratortabel

U maakt een accelerator-tabelresource met behulp van de ACCELERATORS-instructie in het resourcedefinitiebestand van uw toepassing. U moet een naam of resource-id toewijzen aan de acceleratortabel, bij voorkeur in tegenstelling tot die van een andere resource. Het systeem gebruikt deze id om de resource tijdens runtime te laden.

Elke accelerator die u definieert, vereist een afzonderlijke vermelding in de acceleratortabel. In elke vermelding definieert u de toetsaanslag (een ASCII-tekencode of virtuele-sleutelcode) waarmee de accelerator en de id van de accelerator worden gegenereerd. U moet ook opgeven of de toetsaanslag moet worden gebruikt in een combinatie met alt-, Shift- of Ctrl-toetsen. Zie Toetsenbordinvoervoor meer informatie over virtuele toetsen.

Een ASCII-toetsaanslag wordt opgegeven door het ASCII-teken tussen dubbele aanhalingstekens te plaatsen of door de gehele waarde van het teken in combinatie met de ASCII-vlag te gebruiken. In de volgende voorbeelden ziet u hoe u ASCII-accelerators definieert.

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

Een toetsaanslag voor virtuele-sleutelcode wordt anders opgegeven, afhankelijk van of de toetsaanslag een alfanumerieke sleutel of een niet-alfanumerieke sleutel is. Voor een alfanumerieke sleutel wordt de letter of het cijfer van de sleutel, tussen dubbele aanhalingstekens, gecombineerd met de vlag VIRTKEY. Voor een niet-alfanumerieke sleutel wordt de code van de virtuele sleutel voor de specifieke sleutel gecombineerd met de vlag VIRTKEY. In de volgende voorbeelden ziet u hoe u codeversnellers voor virtuele sleutels definieert.

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

In het volgende voorbeeld ziet u een accelerator-tabelresource die accelerators definieert voor bestandsbewerkingen. De naam van de resource is 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 

Als u wilt dat de gebruiker in een combinatie met de toetsaanslag op alt, Shift of Ctrl drukt, geeft u de alt-, Shift- en CONTROL-vlaggen op in de definitie van de accelerator. Hieronder volgen enkele voorbeelden.

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

Wanneer een accelerator-toets overeenkomt met een menu-item, markeert het systeem standaard het menu-item. U kunt de vlag NOINVERT gebruiken om markeringen voor een afzonderlijke accelerator te voorkomen. In het volgende voorbeeld ziet u hoe u de vlag NOINVERT gebruikt:

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

Als u accelerators wilt definiëren die overeenkomen met menu-items in uw toepassing, neemt u de accelerators op in de tekst van de menu-items. In het volgende voorbeeld ziet u hoe u accelerators kunt opnemen in menuopdrachttekst in een resourcedefinitiebestand.

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 

De resource van de acceleratortabel laden

Een toepassing laadt een accelerator-tabelresource door de functie LoadAccelerators aan te roepen en de instantiegreep op te geven van de toepassing waarvan het uitvoerbare bestand de resource bevat, samen met de naam of identifier van de resource. LoadAccelerators laadt de opgegeven acceleratortabel in het geheugen en retourneert de handle naar de acceleratortabel.

Een toepassing kan op elk gewenst moment een acceleratortabel-resource laden. Normaal gesproken laadt een toepassing met één thread de acceleratortabel voordat de hoofdberichtlus begint. Een toepassing die meerdere threads gebruikt laadt doorgaans de accelerator-tableresource voor een thread voordat je de berichtlus voor de thread invoert. Een toepassing of thread kan meerdere acceleratortabellen gebruiken, waarbij elke tabel is gekoppeld aan een specifiek venster in de toepassing. Een dergelijke toepassing laadt de acceleratortabel voor het venster telkens wanneer de gebruiker het venster heeft geactiveerd. Zie Processen en Threadsvoor meer informatie over threads.

De functie Translate Accelerator aanroepen

Als u accelerators wilt verwerken, moet de berichtlus van een toepassing (of thread) een aanroep naar de TranslateAccelerator- functie bevatten. TranslateAccelerator toetsaanslagen vergelijkt met een acceleratortabel en, als er een overeenkomst wordt gevonden, worden de toetsaanslagen omgezet in een WM_COMMAND (of WM_SYSCOMMAND) bericht. De functie verzendt het bericht vervolgens naar een vensterprocedure. De parameters van de TranslateAccelerator- functie omvatten de handle naar het venster dat de WM_COMMAND- berichten ontvangt, de handle naar de acceleratortabel die wordt gebruikt om accelerators te vertalen, en een pointer naar een MSG- structuur die een bericht uit de wachtrij bevat. In het volgende voorbeeld ziet u hoe u TranslateAccelerator- aanroept vanuit een berichtenlus.

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); 
        } 
    } 
}

WM_COMMAND berichten verwerken

Wanneer een accelerator wordt gebruikt, ontvangt het venster dat is opgegeven in de TranslateAccelerator functie een WM_COMMAND of WM_SYSCOMMAND bericht. Het woord in lage volgorde van de parameter wParam bevat de id van de accelerator. De vensterprocedure onderzoekt de identificatie om de bron van het WM_COMMAND-bericht vast te stellen en het bericht dienovereenkomstig te behandelen.

Als een accelerator overeenkomt met een menu-item in de toepassing, krijgen de accelerator en het menu-item dezelfde id toegewezen. Als u wilt weten of een WM_COMMAND bericht is gegenereerd door een accelerator of een menu-item, kunt u het woord in hoge volgorde van de parameter wParam bekijken. Als een accelerator het bericht heeft gegenereerd, is het woord in hoge volgorde 1; als een menu-item het bericht heeft gegenereerd, is het woord in hoge volgorde 0.

Het vernietigen van de acceleratortabelresource

Het systeem vernietigt automatisch resources uit de acceleratortabel die door de LoadAccelerators functie worden geladen, en verwijdert de resource uit het geheugen nadat de toepassing is gesloten.

Snelkoppelingen maken voor lettertypekenmerken

In het voorbeeld in deze sectie ziet u hoe u de volgende taken uitvoert:

  • Maak een accelerator-tabelresource.
  • Laad de acceleratortabel tijdens runtime.
  • Vertaal sneltoetsen in een berichtenlus.
  • Verwerken WM_COMMAND berichten die zijn gegenereerd door de accelerators.

Deze taken worden gedemonstreerd in de context van een toepassing met een menu Character en bijbehorende accelerators waarmee de gebruiker kenmerken van het huidige lettertype kan selecteren.

In het volgende gedeelte van een resourcedefinitiebestand wordt het menu Character en de bijbehorende acceleratortabel gedefinieerd. Houd er rekening mee dat in de menu-items de sneltoetsen worden weergegeven en dat elke sneltoets dezelfde identificatie heeft als de bijbehorende menuopdracht.

#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 de volgende secties van het bronbestand van de toepassing ziet u hoe u de accelerators implementeert.

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; 
}

Een acceleratortabel gebruiken die tijdens runtime is gemaakt

In dit onderwerp wordt beschreven hoe u acceleratortabellen gebruikt die tijdens de runtime zijn gemaakt.

Een Run-Time Accelerator-tabel maken

De eerste stap bij het maken van een acceleratortabel tijdens runtime is het vullen van een matrix van ACCEL- structuren. Elke structuur in de matrix definieert een accelerator in de tabel. De definitie van een accelerator bevat de vlaggen, de sleutel en de identificator. De ACCEL- structuur heeft de volgende vorm.

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

U definieert de toetsaanslag van een accelerator door een ASCII-tekencode of een virtuele toetscode op te geven in het toets lid van de ACCEL structuur. Als u een code voor een virtuele sleutel opgeeft, moet u eerst de vlag FVIRTKEY opnemen in het lid fVirt; anders interpreteert het systeem de code als een ASCII-tekencode. U kunt de vlaggen FCONTROL, FALTof FSHIFT, of alle drie, gebruiken om de Ctrl, Alt of Shift-toets te combineren met de toetsaanslag.

Als u de acceleratortabel wilt maken, geeft u een aanwijzer door aan de matrix van ACCEL- structuren aan de functie CreateAcceleratorTable. CreateAcceleratorTable maakt de acceleratortabel en retourneert de handle naar de tabel.

Verwerkingsversnellers

Het proces voor het laden en aanroepen van accelerators die worden geleverd door een acceleratortabel die tijdens runtime is gemaakt, is hetzelfde als het verwerken van de acceleratortabel-resource. Zie voor meer informatie het laden van de acceleratortabelresource tot en met het verwerken van WM_COMMAND-berichten.

Een Run-Time Accelerator-tabel vernietigen

Het systeem vernietigt automatisch acceleratortabellen die tijdens runtime zijn gemaakt, en verwijdert de resources uit het geheugen nadat de toepassing is gesloten. U kunt een acceleratortabel vernietigen en deze eerder uit het geheugen verwijderen door de greep van de tabel door te geven aan de DestroyAcceleratorTable functie.

Bewerkbare accelerators voor gebruikers maken

In dit voorbeeld ziet u hoe u een dialoogvenster maakt waarmee de gebruiker de accelerator kan wijzigen die is gekoppeld aan een menu-item. Het dialoogvenster bestaat uit een keuzelijst met invoervak met menu-items, een keuzelijst met invoervak met de namen van sleutels en selectievakjes voor het selecteren van de Ctrl-, Alt- en Shift-toetsen. In de volgende afbeelding ziet u het dialoogvenster.

dialoogvenster met keuzelijsten met tekstinvoer, selectievakjes

In het volgende voorbeeld ziet u hoe het dialoogvenster wordt gedefinieerd in het resourcedefinitiebestand.

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

De menubalk van een toepassing bevat een submenu Teken, waarvan de items sneltoetsen hebben.

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 
}
 

De menu-itemwaarden voor de menusjabloon zijn constanten die als volgt zijn gedefinieerd in het headerbestand van de toepassing.

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

Het dialoogvenster maakt gebruik van een array van door de toepassing gedefinieerde VKEY-structuren, elk met een toetsaanslag tekstreeks en een versneller-tekstreeks. Wanneer het dialoogvenster wordt gemaakt, wordt de array geparseerd en wordt elke tekstreeks van toetsaanslagen toegevoegd aan de Toetsaanslag selecteren keuzelijst. Wanneer de gebruiker op de knop OK klikt, wordt in het dialoogvenster de geselecteerde toetsenreeks opgezocht en wordt de bijbehorende versnellertoetsreeks opgehaald. In het dialoogvenster wordt de tekenreeks accelerator-tekst toegevoegd aan de tekst van het menu-item dat de gebruiker heeft geselecteerd. In het volgende voorbeeld ziet u de matrix van VKEY-structuren:

// 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" 
};

De initialisatieprocedure vult de Item selecteren en Toetsaanslag selecteren keuzelijstvakken in het dialoogvenster. Nadat de gebruiker een menu-item en de bijbehorende accelerator heeft geselecteerd, bekijkt het dialoogvenster de besturingselementen in het dialoogvenster om de selectie van de gebruiker op te halen, werkt u de tekst van het menu-item bij en maakt u vervolgens een nieuwe acceleratortabel die de door de gebruiker gedefinieerde nieuwe accelerator bevat. In het volgende voorbeeld ziet u de procedure in het dialoogvenster. Houd er rekening mee dat u uw venster-procedure moet initialiseren.

// 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; 
}