Klavye Hızlandırıcılarını Kullanma

Bu bölüm, klavye hızlandırıcılarıyla ilişkili görevleri kapsar.

Hızlandırıcı Tablo Kaynağı Kullanma

Uygulamaya hızlandırıcı desteği eklemenin en yaygın yolu, uygulamanın yürütülebilir dosyasıyla bir hızlandırıcı tablosu kaynağı eklemek ve ardından kaynağı çalışma zamanında yüklemektir.

Bu bölümde aşağıdaki konular ele alınır.

Hızlandırıcı Tablo Kaynağı Oluşturma

Uygulamanızın kaynak tanımı dosyasındaki ACCELERATORS deyimini kullanarak bir accelerator-table kaynağı oluşturursunuz. Tercihen diğer kaynaklardan farklı olarak hızlandırıcı tablosuna bir ad veya kaynak tanımlayıcısı atamanız gerekir. Sistem, kaynağı çalışma zamanında yüklemek için bu tanımlayıcıyı kullanır.

Tanımladığınız her hızlandırıcı, hızlandırıcı tablosunda ayrı bir giriş gerektirir. Her girişte, hızlandırıcıyı ve hızlandırıcının tanımlayıcısını oluşturan tuş vuruşunu (ASCII karakter kodu veya sanal anahtar kodu) tanımlarsınız. Tuş vuruşunun ALT, SHIFT veya CTRL tuşlarıyla birlikte kullanılması gerekip gerekmediğini de belirtmelisiniz. Sanal tuşlar hakkında daha fazla bilgi için bkz. Klavye Girişi.

ASCII tuş vuruşu, ASCII karakteri çift tırnak içine alınarak veya ASCII bayrağıyla birlikte karakterin tamsayı değeri kullanılarak belirtilir. Aşağıdaki örneklerde ASCII hızlandırıcılarının nasıl tanımlanacağı gösterilmektedir.

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

Sanal anahtar kodu tuş vuruşu, tuş vuruşunun alfasayısal anahtar mı yoksa alfasayısal olmayan bir anahtar mı olduğuna bağlı olarak farklı şekilde belirtilir. Alfasayısal anahtar için, anahtarın çift tırnak içine alınmış harfi veya numarası, VIRTKEY bayrağıyla birleştirilir. Alfasayısal olmayan bir anahtar için, belirli bir anahtarın sanal anahtar kodu VIRTKEY bayrağıyla birleştirilir. Aşağıdaki örneklerde sanal anahtar kod hızlandırıcılarının nasıl tanımlanacağı gösterilmektedir.

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

Aşağıdaki örnekte, dosya işlemleri için hızlandırıcıları tanımlayan bir accelerator-table kaynağı gösterilmektedir. Kaynağın adı 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 

Kullanıcının kısayol tuş vuruşuyla birlikte ALT, SHIFT veya CTRL tuşlarına basmasını istiyorsanız, hızlandırıcının tanımında ALT, SHIFT ve CONTROL bayraklarını belirtin. Aşağıda bazı örnekler verilmiştir.

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

Varsayılan olarak, bir hızlandırıcı tuşu bir menü öğesine karşılık geldiğinde sistem menü öğesini vurgular. Tek bir hızlandırıcının vurgulanmasını önlemek için NOINVERT bayrağını kullanabilirsiniz. Aşağıdaki örnekte NOINVERT bayrağının nasıl kullanılacağı gösterilmektedir:

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

Uygulamanızdaki menü öğelerine karşılık gelen hızlandırıcıları tanımlamak için, hızlandırıcıları menü öğelerinin metnine ekleyin. Aşağıdaki örnekte, kaynak tanımı dosyasındaki menü öğesi metnine hızlandırıcıların nasıl dahil olduğu gösterilmektedir.

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 

Hızlandırıcı Tablo Kaynağını Yükleme

Uygulama, LoadAccelerators işlevini çağırarak, kaynağı içeren yürütülebilir dosyayla ilişkili uygulamanın örnek tutamacını ve kaynağın adını veya tanımlayıcısını belirterek hızlandırıcı tablo kaynağını yükler. LoadAccelerators belirtilen hızlandırıcı tablosunu belleğe yükler ve tanıtıcıyı hızlandırıcı tablosuna döndürür.

Bir uygulama herhangi bir zamanda bir hızlandırıcı tablosu kaynağı yükleyebilir. Genellikle tek iş parçacıklı bir uygulama, ana ileti döngüsüne girmeden önce hızlandırıcı tablosunu yükler. Birden çok iş parçacığı kullanan bir uygulama, genellikle her iş parçacığı için ileti döngüsüne girmeden önce hızlandırıcı tablo kaynağını yükler. Bir uygulama veya iş parçacığı, her birinin uygulamadaki belirli bir pencereyle ilişkili birden çok hızlandırıcı tablosu da kullanabilir. Böyle bir uygulama, kullanıcı pencereyi her etkinleştirişinde pencere için hızlandırıcı tablosunu yükler. İş parçacıkları hakkında daha fazla bilgi için bkz. İşlemler ve İş Parçacıkları.

Translate Accelerator İşlevini Çağırma

Hızlandırıcıları işlemek için uygulamanın (veya iş parçacığının) ileti döngüsü, TranslateAccelerator işlevine yönelik bir çağrı içermelidir. TranslateAccelerator tuş vuruşlarını bir hızlandırıcı tablosuyla karşılaştırır ve eşleşme bulursa tuş vuruşlarını bir WM_COMMAND (veya WM_SYSCOMMAND) iletisine çevirir. İşlev daha sonra iletiyi bir pencere yordamına gönderir. TranslateAccelerator işlevinin parametreleri, WM_COMMAND iletilerini alacak pencere tutamacını, hızlandırıcı çevriminde kullanılan hızlandırıcı tablosunun tutamacını ve kuyruktan gelen bir ileti içeren MSG yapısının işaretçisini içerir. Aşağıdaki örnek, bir ileti döngüsü içinden TranslateAccelerator nasıl çağrılmasını gösterir.

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 İletilerini İşleme

Hızlandırıcı kullanıldığında, TranslateAccelerator işlevinde belirtilen pencere bir WM_COMMAND veya WM_SYSCOMMAND iletisi alır. wParam parametresinin düşük sıralı sözcüğü hızlandırıcının tanımlayıcısını içerir. Pencere yordamı, WM_COMMAND iletisinin kaynağını belirlemek ve iletiyi uygun şekilde işlemek için tanımlayıcıyı inceler.

Genellikle, bir hızlandırıcı uygulamadaki bir menü öğesine karşılık geliyorsa, hızlandırıcı ve menü öğesine aynı tanımlayıcı atanır. bir WM_COMMAND iletisinin bir hızlandırıcı tarafından mı yoksa bir menü öğesi tarafından mı oluşturulduğunu bilmeniz gerekiyorsa, wParam parametresinin yüksek sıralı sözcüğünü inceleyebilirsiniz. Bir hızlandırıcı iletiyi oluşturduysa, yüksek sıralı sözcük 1'dir; bir menü öğesi iletiyi oluşturduysa, yüksek sıralı sözcük 0'dır.

Hızlandırıcı Tablosu Kaynağını Yok Etme

Sistem, LoadAccelerators işlevi tarafından yüklenen hızlandırıcı tablosu kaynaklarını otomatik olarak yok eder ve uygulama kapatıldıktan sonra kaynağı bellekten kaldırır.

Yazı Tipi Öznitelikleri için Hızlandırıcı Oluşturma

Bu bölümdeki örnekte aşağıdaki görevlerin nasıl gerçekleştirebileceğiniz gösterilmektedir:

  • Hızlandırma tablosu kaynağı oluşturun.
  • Hızlandırıcı tablosunu çalışma zamanında yükleyin.
  • Bir ileti döngüsünde hızlandırıcıları çevirin.
  • Hızlandırıcılar tarafından oluşturulan WM_COMMAND iletilerini işleme.

Bu görevler, Karakter menüsü ve kullanıcının geçerli yazı tipinin özniteliklerini seçmesine izin veren ilgili hızlandırıcılar içeren bir uygulama bağlamında gösterilir.

Kaynak tanımı dosyasının aşağıdaki bölümü, Karakter menüsünü ve ilişkili hızlandırıcı tablosunu tanımlar. Menü öğelerinin hızlandırıcı tuş vuruşlarını gösterdiğini ve her hızlandırıcının ilişkili menü öğesiyle aynı tanımlayıcıya sahip olduğunu unutmayın.

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

Uygulamanın kaynak dosyasındaki aşağıdaki bölümlerde hızlandırıcıların nasıl uygulanacakları gösterilmektedir.

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

Çalışma Zamanında Oluşturulan Hızlandırıcı Tablosunu Kullanma

Bu konuda, çalışma zamanında oluşturulan hızlandırıcı tablolarının nasıl kullanılacağı açıklanmaktadır.

Run-Time Hızlandırıcı Tablosu Oluşturma

Çalışma zamanında hızlandırıcı tablosu oluşturmanın ilk adımı ACCEL yapıları dizisini doldurmaktır. Dizideki her yapı tabloda bir hızlandırıcı tanımlar. Hızlandırıcının tanımı bayraklarını, anahtarını ve tanımlayıcısını içerir. ACCEL yapısı aşağıdaki biçimdedir.

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

ACCEL yapısının üyesi anahtarında bir ASCII karakter kodu veya sanal anahtar kodu belirterek hızlandırıcının tuş vuruşunu tanımlarsınız. Bir sanal anahtar kodu belirtirseniz, önce fVirt üyesine FVIRTKEY bayrağını eklemeniz gerekir; aksi takdirde, sistem kodu ASCII karakter kodu olarak yorumlar. CTRL, ALT veya SHIFT tuşunu tuş vuruşuyla birleştirmek için FCONTROL, FALTveya FSHIFT bayrağını ya da üçünü de ekleyebilirsiniz.

Hızlandırıcı tablosunu oluşturmak için, CreateAcceleratorTable işlevine ACCEL yapıları dizisine bir işaretçi geçirin. CreateAcceleratorTable hızlandırıcı tablosunu oluşturur ve tanıtıcıyı tabloya döndürür.

İşlem Hızlandırıcıları

Çalışma zamanında oluşturulan bir hızlandırıcı tablosu tarafından sağlanan hızlandırıcıları yükleme ve çağırma işlemi, hızlandırıcı tablosu kaynağı tarafından sağlanan işlemlerin işlenmesiyle aynıdır. Daha fazla bilgi için bkz. Hızlandırıcı Tablosu Kaynağını Yükleme aracılığıyla WM_COMMAND İletileri İşleme.

Bir Run-Time Hızlandırıcı Tablosunu Yok Etme

Sistem, çalışma zamanında oluşturulan hızlandırıcı tablolarını otomatik olarak yok eder ve uygulama kapatıldıktan sonra kaynakları bellekten kaldırır. Bir hızlandırıcı tablosunu yok edebilir ve tablonun tutamacını DestroyAcceleratorTable işlevine geçirerek daha önce bellekten kaldırabilirsiniz.

Kullanıcı Tarafından Düzenlenebilir Hızlandırıcılar Oluşturma

Bu örnekte, kullanıcının bir menü öğesiyle ilişkili hızlandırıcıyı değiştirmesine olanak tanıyan bir iletişim kutusunun nasıl derlendiğini gösterilmektedir. İletişim kutusu, menü öğelerini içeren bir birleşik giriş kutusundan, anahtarların adlarını içeren bir birleşik giriş kutusundan ve CTRL, ALT ve SHIFT tuşlarını seçme onay kutularından oluşur. Aşağıdaki çizimde iletişim kutusu gösterilmektedir.

Birleşik giriş kutuları ve onay kutularıiletişim kutusu

Aşağıdaki örnek, iletişim kutusunun kaynak tanımı dosyasında nasıl tanımlandığını gösterir.

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

Uygulamanın menü çubuğunda yer alan, öğeleri hızlandırıcılarla ilişkilendirilmiş olan bir Karakter alt menüsü bulunur.

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 
}
 

Menü şablonunun menü öğesi değerleri, uygulamanın üst bilgi dosyasında aşağıdaki gibi tanımlanan sabitlerdir.

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

İletişim kutusu, her biri tuş vuruşu metin dizesi ve hızlandırıcı-metin dizesi içeren, uygulama tanımlı VKEY yapılarından oluşan bir dizi kullanır. İletişim kutusu oluşturulduğunda, dizi ayrıştırılır ve her tuş vuruşu metin ifadesi Tuş Vuruşu Seç açılır kutusuna eklenir. Kullanıcı Tamam düğmesine tıkladığında, iletişim kutusu seçili tuş vuruş metin dizisini arar ve karşılık gelen hızlandırıcı metin dizisini getirir. İletişim kutusu, hızlandırıcı metin dizesini kullanıcının seçtiği menü öğesinin metnine ekler. Aşağıdaki örnekte VKEY yapıları dizisi gösterilmektedir:

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

İletişim kutusunun başlatma prosedürü, Öğe Seç ve Tuş Vuruşu Seç açılır kutularını doldurur. Kullanıcı bir menü öğesini ve ilişkili hızlandırıcıyı seçtikten sonra, iletişim kutusu kullanıcının seçimini almak için iletişim kutusundaki denetimleri inceler, menü öğesinin metnini güncelleştirir ve ardından kullanıcı tanımlı yeni hızlandırıcıyı içeren yeni bir hızlandırıcı tablosu oluşturur. Aşağıdaki örnekte iletişim kutusu yordamı gösterilmektedir. Pencere işleminizde başlatmanız gerektiğini unutmayın.

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