CreateAcceleratorTable

アクセラレーターもメニュー同様にリソースファイルに定義するのが一般的ですが、 やはりリソースを使わずに作成することができます。 リソースからロードするときはLoadAcceleratorを使いますが、 リソースを使わない場合はCreateAcceleratorTableを使います。

CreateAcceleratorTableの宣言はこのようになっています。

HACCEL CreateAcceleratorTable(
  LPACCEL lpaccl,  // pointer to structure array with accelerator data
  int cEntries     // number of structures in the array
);
要するにACCEL構造体の配列へのポインタとその要素の数を渡せば作ってくれるようです。 LoadMenuIndirect関数に比べれば楽に作れますね。

ACCEL構造体は以下のとおりです。

typedef struct tagACCEL { // accl 
    BYTE   fVirt; 
    WORD   key; 
    WORD   cmd; 
} ACCEL; 
fVirtには同時にAltキーやCtrlキーを押す必要があるとかを示すフラグで、 以下の値を組み合わせます。
FALT Altキー
FCONTROL Ctrlキー
FNOINVERT 他のアクセラレーターと衝突したときにこっちが優先される(?)
FSHIFT Shiftキー
FVIRTKEY 仮想キーでkeyを定義
FNOINVERTは英語ヘルプを読んだのですが意味がよくわかりませんでした...(実験もしてない) (意味がわかる方がいらっしゃいましたら教えてください)。
keyにはアクセラレーターに割り当てるキーを代入します。 fVirtにFVIRTKEYが含まれていないとF1などが使えないと思うので気をつけてください。
cmdにはWM_COMMANDメッセージのWPARAMのLOWORDに格納される数値で、 メニューアイテムのIDを入れておきます。

以下サンプルです。LoadMenuIndirectのコードを流用しています。

#include<windows.h>

#define IDM_OPEN    0x0101
#define IDM_SAVE    0x0102
#define IDM_QUIT    0x0103
#define IDM_FSCREEN 0x0201

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch(message)
    {
        case WM_COMMAND:
            switch(LOWORD(wParam))
            {
                case IDM_OPEN:
                    MessageBox(hwnd, "Open", "Test", MB_OK);
                    break;
                case IDM_SAVE:
                    MessageBox(hwnd, "Save", "Test", MB_OK);
                    break;
                case IDM_QUIT:
                    MessageBox(hwnd, "終了", "Test", MB_OK);
                    break;
                case IDM_FSCREEN:
                    MessageBox(hwnd, "全画面表示", "Test", MB_OK);
                    break;
            }
            return 0;
        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}

int WINAPI WinMain(HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow)
{
    HWND     hwnd;
    HMENU    hmenu;
    HACCEL   haccel;
    WNDCLASS wc;
    MSG      msg;

    // メニュー
    const WORD menu_template[] =
    {
        //// MENUITEMTEMPLATEHEADER ////
        0, // versionNumber   version number; must be zero 
        0, // offset          offset first MENUITEMTEMPLATE structure 

        //// MENUITEMTEMPLATE ////
        MF_POPUP, 0x30d5, 0x30a1, 0x30a4, 0x30eb, '(', '&', 'F', ')', 0,                                 // ファイル
            0           , IDM_OPEN   ,   '&', 'O', 'p', 'e', 'n', '\t', 'C', 't', 'r', 'l', '+', 'O', 0,     // Open
            0           , IDM_SAVE   ,   '&', 'S', 'a', 'v', 'e', '\t', 'C', 't', 'r', 'l', '+', 'S', 0,     // Save
            MF_SEPARATOR,        0   ,   0,                                                                  // セパレータ
            MF_HILITE   , IDM_QUIT   ,   0x7d42, 0x4e86, '(', '&', 'Q', ')', 0,                              // 終了
        MF_POPUP | MF_HILITE, 0x8868, 0x793a, '(', '&', 'V', ')', 0,                                     // 表示
            MF_HILITE   , IDM_FSCREEN,   0x5168, 0x753b, 0x9762, 0x8868, 0x793a, 0,                          // 全画面表示
    };
    hmenu = LoadMenuIndirect(menu_template);

    // アクセラレーター
    ACCEL accel_table[] = {
        {FVIRTKEY | FNOINVERT | FCONTROL , 'O', IDM_OPEN},
        {FVIRTKEY | FNOINVERT | FCONTROL , 'S', IDM_SAVE},
    };
    haccel = CreateAcceleratorTable(&accel_table[0], sizeof(accel_table)/sizeof(ACCEL));

    wc.style            = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc      = WndProc;
    wc.cbClsExtra       = 0;
    wc.cbWndExtra       = 0;
    wc.hInstance        = hThisInstance;
    wc.hIcon            = LoadIcon(NULL, IDI_WINLOGO );
    wc.hCursor          = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground    = (HBRUSH)GetStockObject(BLACK_BRUSH);
    wc.lpszMenuName     = NULL;
    wc.lpszClassName    = "Sample";
    RegisterClass (&wc);
    hwnd = CreateWindow
            (wc.lpszClassName,
             "サンプル - CreateAcceleratorTable",
             WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_THICKFRAME,
             CW_USEDEFAULT, CW_USEDEFAULT,
             320, 200,
             NULL,
             hmenu,
             hThisInstance,
             NULL
             );
    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    while( GetMessage(&msg, NULL, 0, 0) > 0 )
    {
        if( !TranslateAccelerator(hwnd, haccel, &msg) )
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return msg.wParam;
}