2.メニューを作る

 

※ 修正版をこちらのページにアップしています。将来的にはこのページは削除される可能性があります。

 


1.リソースって何だ?

さて、ウインドウは「何もしない」ウインドウはできましたが、見栄えがいまいちぱっとしません。
まずは、メニューをつけて、それらしいウインドウにしようと思います。

メニューの実装方法はいくつかあります(Win32プログラムでは、ひとつのことをやるのに大体複数のやり方が用意されています。
そのことがよいか悪いかは、考え方次第ですが…)が、今回はリソースを使います。

リソースとは、ウインドウプログラムが使う部品のようなものです。
このため、コンパイルする前に用意しておかなくてはなりません。

さて、通常リソースはリソースエディタで作りますが、今回は使いません。
つまり、ツールの手を借りず自分でシコシコ記述するということです。ちょっと前時代的です。
以下のようにリソースを記述してください。
そして入力後 「FTPManager.rc」 と名前を付けて保存してください。


/* ========================================================================== */
/*          FILE        FTPManager.rc                                         */
/*          CREATE      2006/10/21                                            */
/*          MODIFY      ----/--/--                                            */
/*          REMARK                                                            */
/*          COPYRIGHT   2000-2007, Latin. All Rights Reserved                 */
/* ========================================================================== */
#include "Resource.h"


/* -------------------------------------------------------------------------- */
/*                      メニュー宣言部                                        */
/* -------------------------------------------------------------------------- */
IDM_MENUMAIN MENU DISCARDABLE
{
  POPUP "ファイル(&F)"
  {
    MENUITEM "終了(&X)",          IDM_EXIT
  }
}

IDM_MENUMAIN とか IDM_EXIT は、メニュー自体とメニュー項目の名前です。
「IDM〜」はメニュー のリソースにつけられるプリフィックスで単なる慣例です。別に何でもかまいません。

プログラムでメニュー自体や各項目を使用するときは、この名前を使いますが、実は単なる数値です。
リソースID という定数ですが、これは、自分で宣言しなければなりません。
そこで、定数値を宣言します。 定数値は慣例として、100 〜 32768 の間でとってください。
以下のように定数値を宣言します。そして入力後 「Resource.h」 と名前を付けて保存してください。


/* ========================================================================== */
/*          FILE        Resource.h                                            */
/*          CREATE      2006/10/21                                            */
/*          MODIFY      ----/--/--                                            */
/*          REMARK                                                            */
/*          COPYRIGHT   2000-2007, Latin. All Rights Reserved                 */
/* ========================================================================== */
/* -------------------------------------------------------------------------- */
/*                      メニューアイテム宣言部                                */
/* -------------------------------------------------------------------------- */
#define IDM_MENUMAIN        20000
#define IDM_EXIT            20001

IDM_MENUMAIN とか IDM_EXIT それぞれに、定数値を割り振りました。


2.メニューつきウインドウの作成

さて、リソースを作りましたが、「何もしない」ウインドウで使えるようにする必要があります。
定数値をわからせるために、先ほど作成した、Resource.h をインクルードします。
また、作成した Transit.rc をプロジェクトに参加させるのも忘れないでください。

そして以下のようにソースを修正してください。


  // ウインドウクラス作成
  WndClass.cbSize          = sizeof(WndClass);
  WndClass.style           = CS_HREDRAW | CS_VREDRAW;
  WndClass.lpfnWndProc     = WndProc;
  WndClass.cbClsExtra      = 0;
  WndClass.cbWndExtra      = 0;
  WndClass.hInstance       = hInstance;
  WndClass.hIcon           = LoadIcon(NULL, IDI_APPLICATION);
  WndClass.hIconSm         = LoadIcon(NULL, IDI_APPLICATION);
  WndClass.hCursor         = LoadCursor(NULL, IDC_ARROW);
  WndClass.hbrBackground   = (HBRUSH) GetStockObject(WHITE_BRUSH);
  WndClass.lpszMenuName = NULL;

      

  WndClass.lpszMenuName = MAKEINTRESOURCE(IDM_MENUMAIN);
  WndClass.lpszClassName   = "FTPManager";

ちょっと解説すると、lpszMenuName には、文字列へのポインタを指定しなければなりません。
ですが、IDM_MENUMAIN は、何度もいっているように定数値です。
そこで、文字列へのポインタへの変換を行うのが MAKEINTRESOURCE マクロ です。

そうして、実行してみてください。
次のようなメニューつき画面が表示されると思います。

うそだー、エラーが表示されたという方、ごめんなさい。
「Resource.h」 をインクルードしてますか?インクルードの仕方は、「WinMain.cpp」 のあたまの、<Windows.h> の下に "Resource.h" と書くだけです。
自分で作成したヘッダファイルは、<> ではなく、"" で囲みます。

しかし、終了を押しても反応がありません。
当たり前といえば、当たり前です。押したときの処理は、いちいち記述していくのです。
これがウィンドウズプログラミングの本質なのですから。


3.メッセージを処理する

さて、メニューの項目をクリックすると、説明したメッセージキューにメッセージが送出されます。
このときのメッセージとは、WM_COMMAND メッセージ です。
ですから、ウインドウプロシージャの中で、WM_COMMAND メッセージを処理するようにします。

WM_COMMAND
  wNotifyCode = HIWORD(wParam);  // 通知コード
  wID         = LOWORD(wParam);  // 項目ID、 コントロールID、 またはアクセラレータID
  hwndCtl     = (HWND) lParam;   // コントロールのハンドル

WM_COMMAND メッセージは、wParam の下位ワードに項目ID(メニューの「IDM〜」)がセットされますのでこれを捕捉します。
今回は、IDM_EXIT ですね。

そこで、ウインドウプロシージャを以下のように修正します。


LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  /* 関数本体部 */
  switch (uMsg)
  {
    case WM_COMMAND:
      switch (LOWORD(wParam))
      {
        case IDM_EXIT:
          SendMessage(hWnd, WM_CLOSE, 0, 0L);
          break;
        default:
          return (DefWindowProc(hWnd, uMsg, wParam, lParam));
      }
      break;
    case WM_DESTROY:
      PostQuitMessage(0);
      break;
    default:
      return (DefWindowProc(hWnd, uMsg, wParam, lParam));
  }

  return 0L;
}

IDM_EXIT だった場合に、ウィンドウに終了の合図を送出してやる必要があります。
終了の合図が WM_CLOSE メッセージ で、送出するには、 SendMessage() を使います。

LRESULT SendMessage(
  HWND hWnd,      // 送信先ウィンドウのハンドル
  UINT Msg,       // メッセージ
  WPARAM wParam,  // メッセージの最初のパラメータ
  LPARAM lParam   // メッセージの 2 番目のパラメータ
);

第2引数はメッセージですので、WM_CLOSE を指定しますが、第3引数と第4引数はありません。
つまり0を指定します。なぜかというと、WM_CLOSE メッセージ には、パラメータがないからです。

WM_CLOSE
  0  = wParam;  // 未使用
  0L = lParam;  // 未使用

WM_CLOSE メッセージがメッセージキューにたまるといつものように、WndProc() に入ってきますね。
ですが、WndProc() 内部では WM_CLOSE を捕捉していません。
この場合は、最後の DefWindowProc() が補足してウィンドウ メッセージのデフォルト処理を行います。

WM_CLOSE メッセージの場合のメッセージのデフォルト処理がおこなわれると、WM_DESTROY メッセージがメッセージキューにたまります。
すると説明したように、プログラムが終了します。

ところで、lParam の0についている L にはどんな意味があるのでしょうか?
WndProc() の最後のリターンコードにもありますね。ふつうの 0 と何が違うのでしょう。

実は、0L も 0 もまったく同じ 0 です。ですが、C言語では、0 は通常 int型 の定数として扱われます。
WPARAMunsigned int型 なので問題ないですが、LPARAMlong型 なので long型 の定数として扱うために L をつけているのです。


4.アクセスキーってなんだ?

さて、今回作成したメニューをよく見ると、ファイル(F) とありますが、この (F) って何者なのでしょうか?
これは、アクセスキーといって、Alt キーと一緒に特定の文字キーを押すことで、メニュー や ボタンを選択したことになります。
今回でいえば、[ファイル] メニューを開くには、Alt キーを押しながら F キーを押します。
すると、このファイルメニューを選択したことになります。

リソースに記述するときは アクセスキーの前に & (アンバサンド)記号をつけます。
このようにすると、実行時にアンダーラインがつきます。
前後の 小カッコ はなくてもいいのですが、慣例でつけたほうが分かり易いですね。

私はアクセスキーと呼んでいますが、ここらへんはいい加減で、人によってショートカットキーと呼んだりもします。
そもそもこのアクセスキーを使用していらっしゃる方は、どのくらいいるのでしょうか?
マウスを使わない人が使用する機能なのでしょうが、どのプログラムにもついていますのでつけていたほうが無難な機能でしょう。

さて、最後に今回の「WinMain.cpp」の全文を載せておきます。


/* ========================================================================== */
/*          FILE        WinMain.cpp                                           */
/*          CREATE      2006/10/01                                            */
/*          MODIFY      2006/10/21                                            */
/*          REMARK                                                            */
/*          COPYRIGHT   2000-2007, Latin. All Rights Reserved                 */
/* ========================================================================== */
#include <Windows.h>
#include "Resource.h"


/* -------------------------------------------------------------------------- */
/*          CALLBACK    WndProc                                               */
/*          PARAM       HWND        hWnd        : ウィンドウハンドル          */
/*                      UINT        uMsg        : メッセージ                  */
/*                      WPARAM      wParam      : メッセージの追加情報        */
/*                      LPARAM      lParam      : メッセージの追加情報        */
/*          RETURN      LRESULT                                               */
/*                      メッセージ処理の結果                                  */
/*          REMARK                                                            */
/* -------------------------------------------------------------------------- */
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  /* 関数本体部 */
  switch (uMsg)
  {
    case WM_COMMAND:
      switch (LOWORD(wParam))
      {
        case IDM_EXIT:
          SendMessage(hWnd, WM_CLOSE, 0, 0L);
          break;
        default:
          return (DefWindowProc(hWnd, uMsg, wParam, lParam));
      }
      break;
    case WM_DESTROY:
      PostQuitMessage(0);
      break;
    default:
      return (DefWindowProc(hWnd, uMsg, wParam, lParam));
  }

  return 0L;
}

/* -------------------------------------------------------------------------- */
/*          WINAPI      WinMain                                               */
/*          PARAM       HINSTANCE   hInstance   : 現在のインスタンス          */
/*                      HINSTANCE   hPrevInst   : 直前のインスタンス          */
/*                      LPSTR       lpCmdLine   : コマンドライン引数          */
/*                      int         nCmdShow    : ウインドウの表示形式        */
/*          RETURN      int                     :                             */
/*                      メッセージの wParam パラメータ                        */
/*          REMARK      Win32 アプリケーションの初期エントリポイント          */
/*                      Windows システムが呼び出す。                          */
/* -------------------------------------------------------------------------- */
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow)
{
  /* 変数宣言部 */
  HWND              hWnd;
  WNDCLASSEX        WndClass;
  MSG               Msg;


  /* 関数本体部 */
  // ウインドウクラス作成
  WndClass.cbSize        = sizeof(WndClass);
  WndClass.style         = CS_HREDRAW | CS_VREDRAW;
  WndClass.lpfnWndProc   = WndProc;
  WndClass.cbClsExtra    = 0;
  WndClass.cbWndExtra    = 0;
  WndClass.hInstance     = hInstance;
  WndClass.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
  WndClass.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);
  WndClass.hCursor       = LoadCursor(NULL, IDC_ARROW);
  WndClass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
  WndClass.lpszMenuName  = MAKEINTRESOURCE(IDM_MENUMAIN);
  WndClass.lpszClassName = "FTPManager";

  // ウィンドウクラス登録
  if (! RegisterClassEx(&WndClass))
  {
    return false;
  }

  // ウィンドウ作成
  hWnd = CreateWindow(WndClass.lpszClassName, // 登録されているクラス名
                      "FTPManager",           // ウィンドウ名
                      WS_OVERLAPPEDWINDOW,    // ウィンドウスタイル
                      CW_USEDEFAULT,          // ウィンドウの横方向の位置
                      CW_USEDEFAULT,          // ウィンドウの縦方向の位置
                      CW_USEDEFAULT,          // ウィンドウの幅
                      CW_USEDEFAULT,          // ウィンドウの高さ
                      NULL,                   // 親ウィンドウまたはオーナーウィンドウのハンドル
                      NULL,                   // メニューハンドルまたは子ウィンドウ ID
                      hInstance,              // アプリケーションインスタンスのハンドル
                      NULL);                  // ウィンドウ作成データ

  if (! hWnd)
  {
    return false;
  }

  // ウィンドウの表示
  ShowWindow(hWnd, nCmdShow);
  UpdateWindow(hWnd);

  // メッセージループ
  while (GetMessage(&Msg, NULL, 0, 0))
  {
    TranslateMessage(&Msg);
    DispatchMessage(&Msg);
  }

  return (int) Msg.wParam;
}

本日のソース: FTPManager102(VC8).zip   FTPManager102(BCC).zip


※小言

Visual C++ 2005 の Express バージョンや BCC Developer (Borland C++ 5.5.1) には、リソースエディタがありません。
Visual C++ 2005 の Express バージョンがなぜリソースエディタをケチっているのか理解に苦しみます。
このため、このサイトでは特に断りがない場合は、リソース作成に ResEdit を使用しています。


Last Update 2014/01/31 21:21
[Index] [Prev] [Next]