Menggunakan Akselerator Keyboard

Bagian ini mencakup tugas yang terkait dengan akselerator keyboard.

Menggunakan Sumber Daya Tabel Akselerator

Cara paling umum untuk menambahkan dukungan akselerator ke aplikasi adalah dengan menyertakan sumber daya tabel akselerator dengan file yang dapat dieksekusi aplikasi dan kemudian memuat sumber daya pada durasi.

Bagian ini membahas topik berikut.

Membuat Sumber Daya Tabel Akselerator

Anda membuat sumber daya tabel akselerator dengan menggunakan pernyataan ACCELERATORS dalam file definisi sumber daya aplikasi Anda. Anda harus menetapkan nama atau pengidentifikasi sumber daya ke tabel akselerator, sebaiknya tidak seperti sumber daya lainnya. Sistem menggunakan pengidentifikasi ini untuk memuat sumber daya pada durasi.

Setiap akselerator yang Anda tentukan memerlukan entri terpisah dalam tabel akselerator. Dalam setiap entri, Anda menentukan penekanan tombol (baik kode karakter ASCII atau kode kunci virtual) yang menghasilkan akselerator dan pengidentifikasi akselerator. Anda juga harus menentukan apakah penekanan tombol harus digunakan dalam beberapa kombinasi dengan tombol ALT, SHIFT, atau CTRL. Untuk informasi selengkapnya tentang kunci virtual, lihat Input Keyboard.

Penekanan tombol ASCII ditentukan baik dengan mengapit karakter ASCII dalam tanda kutip ganda atau dengan menggunakan nilai bilangan bulat karakter dalam kombinasi dengan bendera ASCII. Contoh berikut menunjukkan cara menentukan akselerator ASCII.

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

Penekanan tombol kode kunci virtual ditentukan secara berbeda tergantung pada apakah penekanan tombol adalah kunci alfanumerik atau kunci non-alfanumerik. Untuk kunci alfanumerik, huruf atau angka kunci, yang diapit dalam tanda kutip ganda, dikombinasikan dengan bendera VIRTKEY . Untuk kunci non-alfanumerik, kode kunci virtual untuk kunci tertentu dikombinasikan dengan bendera VIRTKEY . Contoh berikut menunjukkan cara menentukan akselerator kode kunci virtual.

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

Contoh berikut menunjukkan sumber daya tabel akselerator yang menentukan akselerator untuk operasi file. Nama sumber daya adalah 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 

Jika Anda ingin pengguna menekan tombol ALT, SHIFT, atau CTRL dalam beberapa kombinasi dengan penekanan tombol akselerator, tentukan bendera ALT, SHIFT, dan CONTROL dalam definisi akselerator. Berikut ini adalah beberapa contohnya.

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

Secara default, ketika kunci akselerator sesuai dengan item menu, sistem menyoroti item menu. Anda dapat menggunakan bendera NOINVERT untuk mencegah penyorotan untuk akselerator individual. Contoh berikut menunjukkan cara menggunakan bendera NOINVERT :

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

Untuk menentukan akselerator yang sesuai dengan item menu di aplikasi Anda, sertakan akselerator dalam teks item menu. Contoh berikut menunjukkan cara menyertakan akselerator dalam teks item menu dalam file definisi sumber daya.

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 

Memuat Sumber Daya Tabel Akselerator

Aplikasi memuat sumber daya tabel akselerator dengan memanggil fungsi LoadAccelerators dan menentukan handel instans ke aplikasi yang file yang dapat dieksekusi berisi sumber daya dan nama atau pengidentifikasi sumber daya. LoadAccelerators memuat tabel akselerator yang ditentukan ke dalam memori dan mengembalikan handel ke tabel akselerator.

Aplikasi dapat memuat sumber daya tabel akselerator kapan saja. Biasanya, aplikasi berulir tunggal memuat tabel akseleratornya sebelum memasukkan perulangan pesan utamanya. Aplikasi yang menggunakan beberapa utas biasanya memuat sumber daya tabel akselerator untuk utas sebelum memasukkan perulangan pesan untuk utas. Aplikasi atau utas mungkin juga menggunakan beberapa tabel akselerator, masing-masing terkait dengan jendela tertentu dalam aplikasi. Aplikasi seperti itu akan memuat tabel akselerator untuk jendela setiap kali pengguna mengaktifkan jendela. Untuk informasi selengkapnya tentang utas, lihat Proses dan Utas.

Memanggil Fungsi Translate Accelerator

Untuk memproses akselerator, perulangan pesan aplikasi (atau utas) harus berisi panggilan ke fungsi TranslateAccelerator . TranslateAccelerator membandingkan penekanan tombol dengan tabel akselerator dan, jika menemukan kecocokan, menerjemahkan penekanan tombol ke dalam pesan WM_COMMAND (atau WM_SYSCOMMAND). Fungsi kemudian mengirim pesan ke prosedur jendela. Parameter fungsi TranslateAccelerator mencakup handel ke jendela yang menerima pesan WM_COMMAND , handel ke tabel akselerator yang digunakan untuk menerjemahkan akselerator, dan penunjuk ke struktur MSG yang berisi pesan dari antrean. Contoh berikut menunjukkan cara memanggil TranslateAccelerator dari dalam perulangan pesan.

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

Memproses Pesan WM_COMMAND

Ketika akselerator digunakan, jendela yang ditentukan dalam fungsi TranslateAccelerator menerima pesan WM_COMMAND atau WM_SYSCOMMAND . Kata berurutan rendah dari parameter wParam berisi pengidentifikasi akselerator. Prosedur jendela memeriksa pengidentifikasi untuk menentukan sumber pesan WM_COMMAND dan memproses pesan yang sesuai.

Biasanya, jika akselerator sesuai dengan item menu dalam aplikasi, akselerator dan item menu diberi pengidentifikasi yang sama. Jika Anda perlu mengetahui apakah pesan WM_COMMAND dihasilkan oleh akselerator atau oleh item menu, Anda dapat memeriksa kata urutan tinggi dari parameter wParam . Jika akselerator menghasilkan pesan, kata urutan tinggi adalah 1; jika item menu menghasilkan pesan, kata berurutan tinggi adalah 0.

Menghancurkan Sumber Daya Tabel Akselerator

Sistem secara otomatis menghancurkan sumber daya tabel akselerator yang dimuat oleh fungsi LoadAccelerators , menghapus sumber daya dari memori setelah aplikasi ditutup.

Membuat Akselerator untuk Atribut Font

Contoh di bagian ini memperlihatkan cara melakukan tugas berikut:

  • Buat sumber daya tabel akselerator.
  • Muat tabel akselerator pada durasi.
  • Menerjemahkan akselerator dalam perulangan pesan.
  • Proses WM_COMMAND pesan yang dihasilkan oleh akselerator.

Tugas-tugas ini ditunjukkan dalam konteks aplikasi yang menyertakan menu Karakter dan akselerator terkait yang memungkinkan pengguna untuk memilih atribut font saat ini.

Bagian berikut dari file definisi sumber daya menentukan menu Karakter dan tabel akselerator terkait. Perhatikan bahwa item menu menunjukkan penekanan tombol akselerator dan bahwa setiap akselerator memiliki pengidentifikasi yang sama dengan item menu terkait.

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

Bagian berikut dari file sumber aplikasi menunjukkan cara mengimplementasikan akselerator.

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

Menggunakan Tabel Akselerator yang Dibuat pada Run Time

Topik ini membahas cara menggunakan tabel akselerator yang dibuat pada durasi.

Membuat Tabel Akselerator Run-Time

Langkah pertama dalam membuat tabel akselerator pada waktu proses adalah mengisi array struktur ACCEL . Setiap struktur dalam array mendefinisikan akselerator dalam tabel. Definisi akselerator mencakup bendera, kuncinya, dan pengidentifikasinya. Struktur ACCEL memiliki bentuk berikut.

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

Anda menentukan penekanan tombol akselerator dengan menentukan kode karakter ASCII atau kode kunci virtual dalam anggota kunci struktur ACCEL . Jika Anda menentukan kode kunci virtual, Anda harus terlebih dahulu menyertakan bendera FVIRTKEY di anggota fVirt ; jika tidak, sistem menafsirkan kode sebagai kode karakter ASCII. Anda dapat menyertakan bendera FCONTROL, FALT, atau FSHIFT , atau ketiganya, untuk menggabungkan tombol CTRL, ALT, atau SHIFT dengan penekanan tombol.

Untuk membuat tabel akselerator, teruskan penunjuk ke array struktur ACCEL ke fungsi CreateAcceleratorTable . CreateAcceleratorTable membuat tabel akselerator dan mengembalikan handel ke tabel.

Akselerator Pemrosesan

Proses memuat dan memanggil akselerator yang disediakan oleh tabel akselerator yang dibuat pada durasi sama dengan pemrosesan yang disediakan oleh sumber daya akselerator-tabel. Untuk informasi selengkapnya, lihat Memuat Sumber Daya Tabel Akselerator melalui Memproses Pesan WM_COMMAND.

Menghancurkan Tabel Akselerator Run-Time

Sistem secara otomatis menghancurkan tabel akselerator yang dibuat pada durasi, menghapus sumber daya dari memori setelah aplikasi ditutup. Anda dapat menghancurkan tabel akselerator dan menghapusnya dari memori sebelumnya dengan meneruskan handel tabel ke fungsi DestroyAcceleratorTable .

Membuat Akselerator yang Dapat Diedit Pengguna

Contoh ini memperlihatkan cara membuat kotak dialog yang memungkinkan pengguna mengubah akselerator yang terkait dengan item menu. Kotak dialog terdiri dari kotak kombo yang berisi item menu, kotak kombo yang berisi nama kunci, dan kotak centang untuk memilih tombol CTRL, ALT, dan SHIFT. Ilustrasi berikut ini memperlihatkan kotak dialog.

kotak dialog dengan kotak kombo dan kotak centang

Contoh berikut menunjukkan bagaimana kotak dialog ditentukan dalam file definisi sumber daya.

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

Bilah menu aplikasi berisi submenu Karakter yang itemnya memiliki akselerator yang terkait dengannya.

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 
}
 

Nilai item menu untuk templat menu adalah konstanta yang didefinisikan sebagai berikut dalam file header aplikasi.

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

Kotak dialog menggunakan array struktur VKEY yang ditentukan aplikasi, masing-masing berisi string keystroke-text dan string akselerator-teks. Saat kotak dialog dibuat, kotak dialog mengurai array dan menambahkan setiap string keystroke-text ke kotak kombo Select Keystroke . Saat pengguna mengklik tombol OK , kotak dialog mencari string keystroke-text yang dipilih dan mengambil string akselerator-teks yang sesuai. Kotak dialog menambahkan string akselerator-teks ke teks item menu yang dipilih pengguna. Contoh berikut menunjukkan array struktur VKEY:

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

Prosedur inisialisasi kotak dialog mengisi kotak kombo Pilih Item dan Pilih Penekanan Tombol . Setelah pengguna memilih item menu dan akselerator terkait, kotak dialog memeriksa kontrol dalam kotak dialog untuk mendapatkan pilihan pengguna, memperbarui teks item menu, lalu membuat tabel akselerator baru yang berisi akselerator baru yang ditentukan pengguna. Contoh berikut menunjukkan prosedur kotak dialog. Perhatikan bahwa Anda harus menginisialisasi dalam prosedur jendela Anda.

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