Version 9.07
アクセラレーター
「今回はアクセラレーターについて見ていきます」
『メニューのところでちょっと出てきたのだね』
「そうあれ。まず、アクセラレーターそのものを使っておこうか。普通に実
行して、 Ctrl + O を押して」
『ファイルダイアログが出た。あー、【ファイルを開く】の右側にあったヤ
ツね。 Version 9.02 ( No.163 ) でやったのだね』
「そう、あれ。あのとき見たように、メニューアイテムにどう書くかは関係
なくて、アクセラレーターっていうリソースでこの設定をします」
『 ResourceView を見ると Accelerator ってあるけど、これがそう?』
「それがそう。開いてみて」
『出てきた。なんか表みたくなってる』
「この表全体を【アクセラレーターテーブル】って言います」
『テーブル?』
「プログラミングの世界では、表のことをテーブルって呼ぶことは多いか
ら」
『ふーん。で、左の列に【 ID 】、真ん中に【キー】、右端に【タイプ】
ってなってるけど』
「これは、真ん中のキーを押すと左側の ID のコマンドが実行されるって意
味。たとえば ID_FILE_OPEN は」
『 Ctrl + O になってる。だから、さっき Ctrl + O を押すとファイルを開
くダイアログが出たわけね』
「だから……この行のプロパティを出して」
『ほい。 ID とかキーがある。ってことはこのキーのところを A にして』
「ビルドして実行すれば」
『 Ctrl + A でファイルを開くダイアログが出た! なるほどー』
「それに、たとえば ID を ID_MENU_TEST にすれば」
『前回作った CMainFrame::OnMenuTest() が……呼ばれる! そのときの
TRACE0() のがちゃんと出てる』
「つまり、この ID のシステムは、アクセラレーターもメニューも一緒って
こと」
『イベントハンドラは ID ごとに作って、その ID はメニューからもアクセ
ラレーターからも実行できるってわけねー。この辺は分かりやすいね』
「ちなみに、 CMainFrame::OnUpdateMenuTest() の中で、コメントアウトし
てると思う CCmdUI::Enable() の呼び出し部分、これを機能するようにして
FALSE を渡せば」
『メニューを淡色にするのだね……おっ、アクセラレーターも効かなくなっ
た!』
「この部分も共通してるってこと」
『へー、ちょっとすごいかも』
「ま、だからアクセラレーター自体は簡単だけどね」
『質問!』
「はい火美ちゃん」
『プロパティの【タイプ】ってどういう意味? 【ASCII 文字】と
【仮想キー】ってあるけど』
「うん、表の方も【ASCII 文字】は【ASCII】、【仮想キー】は【VIRTKEY】
って表示されるよね」
『 ASCII 文字って前やったよね』
「 Version 5.04 ( No.069 ) で見たね。【ASCII 文字】の場合、押した文
字がそのまま送られてきます」
『ん? それって当たり前じゃないの?』
「場合によっては違うんです。たとえば Ctrl + O 。これ、 CapsLock が
入ってるかどうかで O か o か変わるでしょ」
『うん変わるね』
「 O と o とで ASCII 文字が変わるから、【ASCII 文字】の場合にはこの
大文字と小文字とで違うキーが押されたってことになるんです」
『げ、それってよくなくない?』
「うん、たぶん普通は使えないと思うよ。だから普通は仮想キー。仮想キー
の場合、大文字小文字はこの場合区別しないから」
『ほー』
「この辺の話は、今の Version.9 が終わったあとの、 SDI アプリを SDK
だけで作ったときに出てくるから、ちょっと憶えておいて」
『もしかして根が深い話?』
「結構ねー。あ、ちょっとアクセラレーターの部分、元に戻しておいて」
『元ってゆーと』
「とりあえず ID_FILE_OPEN を Ctrl + O で選べるように戻しておいて」
『ほいほい』
「さて!」
『さて?』
「これから例によってプログラム上でアクセラレーターを操作するんだけ
ど、はっきり言ってちょっと難しいです」
『げげ!』
「でも、もうそろそろ手を出してもいいレベルかなとも思うから、少しがん
ばってみて」
『そのくらいできないとダメってレベル?』
「何がダメかによるけどね……で、これからは前回作った
CMainFrame::OnMenuTest() メンバ関数をベースに話を進めます」
『メニューのイベントハンドラね。これが、前のダイアログの時のボタン代
わりになるんだ』
「そゆこと。で、まず肩慣らしにこのコードを試してみて」
void CMainFrame::OnMenuTest()
{
// アイテム数取得。
int iTableNum
= CopyAcceleratorTable( m_hAccelTable, NULL, 0 );
TRACE( "%d\n", iTableNum );
}
『ビルドして実行。 14 って出た』
「これは、今アクセラレーターテーブルに入ってるアイテムの数を取得する
コード」
『あ、ホントだ、14個あった。でも CopyAcceleratorTable() 、これって
関数名見ても数取得するって感じじゃないよ。コピーするとかなんとか』
「でしょう、この辺がまず難しい感じかな。この関数を選択して F1 押し
て」
『 MSDN 見るのね。うん、 CopyAcceleratorTable() のページが出た。
……あれ? なんか変くない?』
「いつもはダイアログ出るでしょ」
『そう、 MFC のメンバ関数のとか、 API でも日本語のとか。……もしかし
て、どっちもないの?』
「ないんです。 API しかないんです。しかも英文。まずこれが難しい点そ
の1」
『日本語のがないのはちょっときついね……』
「次に、 CopyAcceleratorTable() のページの下の方に Keyboard
Accelerator Functions へのリンクがあるから」
『ほいと。……これって、アクセラレーター関係の API の一覧だよね……
5つしかないよ』
「これが難しい点その2。アクセラレーターを操作するときには、この少な
い API を駆使するしかないんです」
『イヤな予感する……今までだったら、 Insert なんたらとかあってそれ呼
ぶだけだったのに、その気配すらない……』
「そう、アクセラレーターを操作するときには、アイテムひとつひとつの操
作はできないんです。 API がそこまでしっかりと用意されてないんです」
『面倒そう……』
「加えて、ネットも含めて情報が少ないのが難しい点その3」
『日本語サイトなんて数えるしかない……あ、かぶスタがあった』
「っと、そこに書かれてるのはちょっと間違ってるからあまり参考にしない
で」
『ダメじゃん』
「さて、こういう難しい状況の中で、アクセラレーターの追加を少しずつし
てみます。さっきのコードを振り返って、こういう行があるでしょ」
= CopyAcceleratorTable( m_hAccelTable, NULL, 0 );
「この m_hAccelTable 」
『最初に m_ が付いてるからメンバ変数だね』
「これは……っと、ちゃんと見た方がいいかな。 FileView で MainFrm.h
ってファイルを探して開いて」
『ほい。 CMainFrame がここに入ってるんだね』
「この中の最初の方」
class CMainFrame : public CFrameWnd
『 CMainFrame は CFrameWnd から継承してるんだね』
「この辺は Version 2.13 ( No.024 ) や Version 3.1 ( No.026 ) 参照。
というわけで CMainFrame では CFrameWnd の機能が使えます。あ、ちなみ
に CFrameWnd は CWnd から継承してるから」
『そりゃそうよね、フレームウィンドウのクラスなら、ウィンドウなんだか
ら CWnd から継承してないと』
「で、話を戻して。 m_hAccelTable は CFrameWnd のメンバ変数で、
HACCEL っていうアクセラレーターテーブルを操作するためのハンドル」
『ウィンドウハンドルみたいに、ハンドルを中に持ってるんだ』
「これは、アクセラレーターの性質がちょっと特別なせい。アクセラレー
ターを使う時、このハンドルをいちいち TranslateAccelerator() って
API に渡して〈アクセラレーターテーブルにあるかどうか〉っていう
チェックをしないといけないんです」
『そのためにハンドルを持ってるのね』
「だから、アクセラレーターテーブルを作って、そのとき受け取ったハンド
ルをこのメンバ変数に入れれば一応いいことになります。実際のコードはこ
んな感じ」
void CMainFrame::OnMenuTest()
{
// アクセラレーターテーブルを新しく作ります。
ACCEL stAccel;
stAccel.fVirt = FCONTROL | FVIRTKEY;
stAccel.cmd = ID_FILE_OPEN;
stAccel.key = 'B';
HACCEL hAccel
= CreateAcceleratorTable( &stAccel, 1 );
// 古いアクセラレーターテーブルを削除します。
DestroyAcceleratorTable( m_hAccelTable );
// アクセラレーターテーブルへのハンドルを入れ替えます。
m_hAccelTable = hAccel;
}
『あ、ちょっとだけ見慣れた感じが』
「まず ACCEL は構造体で、アクセラレーター1行分のデータを入れます」
『メンバ変数に入れてるの、なんとなく分かるのだね』
stAccel.fVirt = FCONTROL | FVIRTKEY;
「はフラグ。 FCONTROL は Ctrl キーを押しながらっていう場合。
FVIRTKEY は仮想キー」
『次の ID はいつもの【ファイルを開く】のね。その次の 'B' は、 B 、
じゃなくて Ctrl + B を押したらってなるわけね』
「そゆこと。で、 CreateAcceleratorTable() っていう API でアクセラ
レーターテーブルを実際に作ります」
『戻り値にハンドルが返ってくるんだね。第2引数の 1 って?』
「アクセラレーターテーブルの中の数」
『?』
「これが実は重要なポイントになるから、それは次回に置いておいて」
『次の DestroyAcceleratorTable() は…… Destroy ってくらいだから、メ
ンバ変数の古いハンドルを削除してるのかな』
「そう、 Version 7.04 ( No.124 ) とかで説明したとおり、リソースは使
わなくなったら削除する必要があるからね」
『ペンなんかを DeleteObject() するのと同じってことね』
「そゆこと。で、新しいアクセラレーターのハンドルを m_hAccelTable に
入れて完了」
『じゃ、ビルドして実行!』
「まず、最初の段階では Ctrl + B が効かないことを確認して」
『ほい機能しませーん』
「じゃ、メニューの【テスト】を選んで」
『ほい。んで Ctrl + B 、ファイルダイアログが出た!』
「というふうに追加できます」
『……あれ? これってそんな難しくないよーな』
「これだけならね。ちょっと Ctrl + O 押してみて」
『ほい。ファイルダイアログが……表示しない!! 他のアクセラレーター
も全然使えなくなっちゃった!』
「というわけで、難しい話は次回に続きます」
/*
Preview Next Story!
*/
『ってゆーか、水希ちゃんイジワルじゃない?』
「そうじゃなくて、ミスして、それを克服するのが重要だから」
『それは分かるけどー』
「ううん、たぶん分かってない」
『へ?』
「ミスしたあと、適当に試してうまくいった、っていうのじゃダメ」
『あー、なんでそれがダメなのか解らないといけないってわけね』
「というわけで次回」
< Version 9.08 今度こそアクセラレーターを追加する! >
『につづく!』
「だから火美ちゃんにはわざと間違えてもらうんです」
『……なーんかやっぱヤダなー』