#pragma twice

KAB-studio > プログラミング > #pragma twice > 188 Version 10.10 アクセラレーターふたたび

#pragma twice 188 Version 10.10 アクセラレーターふたたび

前のページへ 表紙・目次へ 次のページへ

 Version 10.10
アクセラレーターふたたび

前回のメニューに続いて、今回はアクセラレーターを使ってみます
 Version 9.07 ( No.168 ) でやったのだね
あのときのまとめをすると、

・アクセラレーターはリソースのひとつ。
 いわゆるショートカットキーのこと。
・操作は HACCEL というアクセラレーターハンドルを使う。
 MFC だと CMainFrame::m_hAccelTable というのを持ってる。
・操作用 API には
 CopyAcceleratorTable() : コピー。
 CreateAcceleratorTable() : 新規作成。
 DestroyAcceleratorTable() : 削除。
・アクセラレーターを動的に追加するのは面倒。

ってところかな
…… MFC の、って書いてあるってことは、今のだとこれは使えないって
ことだよね
そういうこと。というより、今の段階だとアクセラレーターそのものが使
えないし
げ、そうなの?
とりあえずは試してみようか。まずはリソースにアクセラレーターを追加
します。前回のメニューと同じように【 SimpleWindow リソース】の上で右
クリックして【挿入】を選んで
んで Accelerator を選んで【新規作成】だね。お、何もないアクセラ
レーター?
ここにテスト用のアクセラレーターを追加します。最初の行でダブルク
リックしてプロパティを出して
ほい
 ID は前回のメニューと同じ ID_MENU_TEST 、あとは好きでいいよ
じゃあキーは Ctrl + A っと。タイプは【仮想キー】だよね
そう、まぁデフォルトがそうだろうからそのままで。あと、このアクセラ
レーター全体のリソース ID は……
デフォルトだと IDR_ACCELERATOR1 だね
 IDA_MAIN にしておいた方がいいかな
……これ、どう変えるの?
ワークスペースの方の IDR_ACCELERATOR1 の上で右クリック
あ、プロパティってあった。っていうか、メニューもこうやって変えられ
るんだよね
でもメニューバーからの方が楽だし……さて!
さて?
 Version 9.11 ( No.172 ) で見たように、メニューもアクセラレーター
も、処理としては同じです
つまりコマンド系ってことだよね
そういうこと。というわけで、前回のメニューの時のをそのまま流用しま
す。ウィンドウプロシージャをこう書き換えて

// ウィンドウプロシージャ。
LRESULT CALLBACK WndProc
    ( HWND p_hWnd
    , UINT p_uiMessage
    , WPARAM p_wParam
    , LPARAM p_lParam
    )
{
    if( p_uiMessage == WM_DESTROY )
    {
        // ×ボタンが押されました。
        PostQuitMessage( 0 );
        return 0;
    }
    else if( p_uiMessage == WM_COMMAND )
    {
        if( LOWORD( p_wParam ) == ID_MENU_TEST )
        {
            // ID_MENU_TEST メニュー。
            OutputDebugString( "ID_MENU_TEST : 終了します。\n" );
            PostQuitMessage( 0 );
            return 0;
        }
    }

    // 標準的な処理をします。
    return DefWindowProc( p_hWnd, p_uiMessage, p_wParam, p_lParam );
}

……どこが違うの?
 OutputDebugString() の行を追加しただけ
あー。ってゆーか要らなくない?
うん、なくても問題ないかも。成功すればウィンドウが閉じるわけだし
ま、とりあえずいーや。で……今までの話を総合すると、まだダメなんだ
よね
そうなんです。ちゃんとアクセラレーターが効いていれば、 Ctrl + A を
押したときに、メニューと同じように WM_COMMAND が ID_MENU_TEST を持っ
て送られてくるはずなんだけど
ビルドして実行! やっぱ何も起きないね
それはなぜかっていうと、今の段階だと、ただ普通にキー入力されてるっ
てみなされてるから
つまり Ctrl + A って入力されたってメッセージが来るだけなのね
そう、だからそれを変換する必要があるんです。というわけで、メッセー
ジループをこう書き換えて

// メッセージループ。
int MessageLoop( HINSTANCE p_hInstance )
{
    BOOL bRes;
    MSG stMsg;
    int iAclTranslated;

    // アクセラレーターをリソースから読み込みます。
    HACCEL hAccel
        = LoadAccelerators
            ( p_hInstance
            , MAKEINTRESOURCE( IDA_MAIN ) 
            );

    // メッセージループです。
    while( 1 )
    {
        // メッセージをキューから取り出します。
        bRes = GetMessage( &stMsg, NULL, 0, 0 );
        if  ( 
                ( bRes == 0 )
            || 
                ( bRes == -1 )
            )
        {
            // 終了するのでループから抜けます。
            break;
        }

        iAclTranslated
             = TranslateAccelerator( stMsg.hwnd, hAccel, &stMsg );
        if( iAclTranslated != FALSE )
        {
            // 変換したのでメッセージ変換とディスパッチはしません。
            continue;
        }

        // メッセージを変換します。
        TranslateMessage( &stMsg );
        // ウィンドウプロシージャに送ります。
        DispatchMessage( &stMsg );
    }

    return stMsg.wParam;
}

さらに WinMain() で呼んでる部分をこう書き換えて

    return MessageLoop( p_hInstance );

あ、インスタンスハンドルをメッセージループに渡すようにしたんだ
その理由は、アクセラレーターをリソースから読み込む時に必要だから

    // アクセラレーターをリソースから読み込みます。
    HACCEL hAccel
        = LoadAccelerators
            ( p_hInstance
            , MAKEINTRESOURCE( IDA_MAIN ) 
            );

の部分
前回の LoadMenu() と似てるね
同じリソースを読み込む関数だからね。第1引数にインスタンスハンドル
を、第2引数にアクセラレーターのリソース ID を MAKEINTRESOURCE() に
渡したもの、を渡せばいいから
これを使ってるのが……

        iAclTranslated
             = TranslateAccelerator( stMsg.hwnd, hAccel, &stMsg );

だね
この API は、キー入力されたっていうメッセージを見つけたらそれを変
換してウィンドウプロシージャに送ってくれます
キー入力?
具体的に言うと、まずキー入力は WM_KEYDOWN 
あ、 Version 10.06 ( No.184 ) の TranslateMessage() の話の時に出て
きたのだね
そうそれ。このメッセージを受け取ると、アクセラレーターテーブルの中
に登録されているか調べます
で、 Ctrl + A だと
ウィンドウプロシージャに WM_COMMAND を直接送ります
もちろん ID_MENU_TEST を付けてだよね
もちろん。だからウィンドウプロシージャでそれを受けてあげれば……
そうだ、ビルドして実行、 Ctrl + A っと。おおっ! ウィンドウが閉じ
た! アウトプットウィンドウにも【ID_MENU_TEST : 終了します。】って
出てる!
というわけです
ま、ようするにメッセージを TranslateAccelerator() に渡すようにすれ
ば自動的に変換してくれるってわけね
まぁそれでもいいんだけど…… TranslateAccelerator() をもう少し詳し
くみておくね。第1引数は処理するウィンドウのハンドル
あ、 MSG 構造体ってウィンドウハンドルも持ってるんだね
第2引数はさっきロードしたアクセラレーターのハンドル。第3引数は
メッセージそのもの
で、戻り値を

        if( iAclTranslated != FALSE )
        {
            // 変換したのでメッセージ変換とディスパッチはしません。
            continue;
        }

って感じにチェックしてるね
 TranslateAccelerator() は、メッセージを受け取ってウィンドウプロ
シージャに WM_COMMAND を送ると、それが処理するまで、つまりウィンド
ウプロシージャから抜けるまで返ってきません
え? つまり、メッセージループが止まるっていうこと?
そういうこと。で、処理されたあとに戻ってくるんだけど、それってつ
まり WM_KEYDOWN そのものが処理されたってことだよね
あ! そのままだと WM_KEYDOWN をもう一度ウィンドウプロシージャに
送っちゃう!
だとまずいからここでチェックしてるんです。 TranslateAccelerator() 
はウィンドウプロシージャに無事遅れたら 0 以外を返すから
それを調べて…… continue ってなんだっけ
あ、教えてなかった……これは for や while の先頭に戻るものなんだけ
ど……次回に続く!
ええっ!?

/*
    Preview Next Story!
*/
なんかいきなり続いたね
 continue ってちょっとややこしいからね
そんなに?
そんなでもないんだけど……ちょっとしっかり触れたくて
だから延びるのよー
ま、いまさらそれは考えないってことで
はいはい
というわけで次回
< Version 10.11 プログラムの構造を考える >
につづく!
そしてさらっと4度目の正月を迎えて……
あと何度迎えるやら……
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。