VC++5.0入門5
基礎知識 97/8/11

こんにちは。8月ももう半ば、夏休みも終わり近いなぁ、と子どもの頃には思いましたっけ。子どもが生まれたらやたらに自分の子供時代を思い出します。
前回はウインドウズ・アプリケーションの基本を説明しました。もう一度まとめるとこんな感じです。
  1.(最低限)ふたつのクラスが必要ですので、その宣言を書きます。
      ひとつはアプリケーションそのものを表すクラス(以下単にアプリケーションと呼びます。)
      もうひとつはウインドウを表すクラス(以下単にウインドウと呼びます。)
     (本当は、括弧を付けて書くと分かりやすいと思うのですが、そうすると、なぜかすぐ文字
       化けしてしまうのです。どなたか良い方法をご存知なら教えてください。)
  2.それぞれのクラスの必要な関数の定義を書きます。
     (アプリケーションの生成時にウインドウも生成されるようにします。(InitInstance))
  3.アプリケーションのインスタンス(オブジェクト)を生成します。

このまとめを読んで、すぐに頭に入る人は優秀です。そうでない人は、正常な人ですが、もう一度前回(というか前々回)のコードを見て考えてください。
ちょっとわかりづらかったかな、と思うのは、最後にアプリケーションのインスタンスの生成(つまり、オブジェクトを宣言)をしている場所です。それは

CMyApp theApp;

でしたね。これは、C++入門でやった、

Neko Dora("ボス");

なんかと同じ形です。私たちのアプリケーションクラスのコンストラクタは引数を取らないので、上のような形なのです。ここで、CMyAppがアプリケーション(を表す)クラスの名前で、theAppがここで定義しているインスタンスの名前です。この名前は何でも良いようですが、この講座ではいつもtheAppとします。
もちろん、CMyAppやCMyWindowもプログラマが勝手につくるクラスですから名前は自由です。しかし、その基底クラスのCWinAppCFrameWndはマイクロソフトのプログラマさんたちが書いてくれたクラスですので名前は変更できません。また、CreateInitInstanceも決められた関数ですから、勝手に名前を変えることはできません。

それでは「メッセージに対処するコード」を付け加えます。(VC入門1の内容を忘れてしまった人はもう一度見直しておいてください。)今日は、特に、マウスのクリックに反応するプログラムを書きます。
さて、ちょっと、ゆっくり説明します。まず、作りたいプログラムは、マウスをクリックすると何かするプログラムですが、マウスがクリックされたことを、私たちのアプリケーションはどうやって知ることができるでしょうか。、、、。これは、実はウインドウズ(というOS)がやってくれます。(「ウインドウズ(というOS)」と書いてきたのは、ウインドウズはOSの名前で、アプリケーションの窓、つまり、ウインドウとはまったく別物なのですが、よく混乱する人がいるからです。今後、この但し書きは付けないつもりですので、ウインドウのあとにが付くかどうかよく気を付けていてください。)まず、ユーザがマウスをクリックするということは、マウスのボタンがまず押し下げられ、次に、そのボタン放されるわけですね。それは、実は、ウインドウズが感知するのです。そして、アプリケーションに「マウスのボタンが押し下げられたぞ」「ボタンが放されたぞ」というメッセージを送るのです。このメッセージを受け取ったらどうするか、をプログラムに書けば良いのです。
クリックという方がわかりやすいかもしれませんが、クリックは左右どちらかのボタンの押し下げから始まるので、ここでは、「左ボタンの押し下げ」に対するメッセージの処理を考えましょう。つまり、ウインドウズが「マウスの左ボタンが押し下げられたぞ」というメッセージを出したときにどうするかです。それには、次のようなコードを書きます。
#include "afxwin.h"

class CMyWindow: public CFrameWnd
{
public:
    CMyWindow();
    void OnLButtonDown(UINT, CPoint);

    DECLARE_MESSAGE_MAP()
};

class CMyApp:public CWinApp
{
public:
    virtual BOOL InitInstance();
};

CMyWindow::CMyWindow()
{
    Create(NULL, "Hello!", WS_OVERLAPPEDWINDOW);
}

void CMyWindow::OnLButtonDown(UINT, CPoint)
{
    MessageBox("左ボタンが押されたよ。","メッセージだ",MB_OK);
}

BEGIN_MESSAGE_MAP(CMyWindow, CFrameWnd)
  ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()

BOOL CMyApp::InitInstance()
{
    m_pMainWnd=new CMyWindow;
    m_pMainWnd->ShowWindow(m_nCmdShow);
    m_pMainWnd->UpdateWindow();

    return TRUE;
}

CMyApp theApp;
ここで、VC入門3のプログラムとどこが違うか良く見ておいてください。それから、VC入門3で説明したやり方でこのプログラムを実行して見てください。プロジェクト名はsecondにでもしましょう。
実行するとウインドウが開く以外何も起こらないようですが、そのウインドウの中でマウスの左ボタンを押すと「左ボタンが押されたよ。」というメッセージボックスが現れるでしょう。そのメッセージボックスのOKを押せばボックスは消え、元に戻ります。ただそれだけのプログラムなんです。^^;)
説明しましょう。まず、CMyWindowの定義の中にあるvoid OnLButtonDown(UINT, CPoint);は後に回して、次の、DECLARE_MESSAGE_MAP()ですが、これは、簡単に言うと、「このウインドウはウインドウズからのメッセージを処理するよ」と宣言しているのです。ただ、単に、宣言しているだけでは、どのメッセージを処理するかわかりません。これは、具体的に、

BEGIN_MESSAGE_MAP(CMyWindow, CFrameWnd)
  ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()
で示されています。(この辺の文にはセミコロン;が終わりについていないことに注意してください。そういうものなんです。)このようなものをメッセージマップというのですが、最初の1行目がメッセージマップのはじまり、最後の1行がメッセージマップの終わりを意味しています。最初の1行目はCFrameWndの派生クラスであるCMyWindowは以下のメッセージを処理します、という意味です。深く考えずにそう納得してください。そして、真ん中の行が「マウスの左ボタンが押し下げられたというメッセージを受けたら、それを処理するよ」という意味なのです。なんで、ON_WM_...なんて書いてあるのだろうと考え込まないでください。それはマイクロソフトの人がそう決めたからそうなんです。(たぶん。)
このプログラムは、ウインドウズからの「左ボタン押し下げメッセージ」を受けて、それを処理するように書いてある、とわかったと思います。では、どんな処理をするのか、、、。それはvoid OnLButtonDown(UINT, CPoint);という関数に書く決まりなのです。(勝手に名前を変更しないでくださいね。)そういうわけで、CMyWindowの定義にvoid OnLButtonDown(UINT, CPoint);があるのです。もちろん、この関数の定義もきちんと書く必要があります。それが、
void CMyWindow::OnLButtonDown(UINT, CPoint)
{
    MessageBox("左ボタンが押されたよ。","メッセージだ",MB_OK);
}
なのです。ここで、引数は何なんだろうと考える人は優秀です。でも、その説明はちょっと待ってください。どうせ今回は使わないのです。
この関数は、MessageBoxという関数を呼び出しているだけです。この関数の引数は、順に、メッセージボックス内に書かれるメッセージ、メッセージボックスのタイトル、どういうボタンを付けるか(ここではOKボタン)、を表しています。
私がぐじゃぐじゃ言うより、何度かこのプログラムを実行して様子を見た方がわかると思います。特に、メッセージボックス内のメッセージとプログラム内の文(業界風に言うと文字列)の対応を見てください。その対応がわかったら、ちょっとここの文字列だけ書き換えて遊んでみてください。(つまり、実行してみてください。)
みなさん、どうでしょうか。とにかく、大体の流れがわかれば良いのですが、、、。まじめな人ほど悩んでいるかも知れません。仕組みは大体わかったけど、OnLButtonDownなんて決められた関数が出てきてしまった、自分でプログラムを書くときには、こういう関数を知らなければいけないんだろうなぁ。なんか大変そうだなぁ、などと。
実はそれほど大変でもありません。こういう関数を覚える必要はあまりないのです。(といっても代表的なやつは自然に覚えてしまいそうですが、、、。)その理由は、後でやるAppWizardのおかげです。まあ、その話は後でゆっくりしましょう。
AppWizardを使えば、覚える必要はほぼ無くなるのですが、ちょっと、慣れておくのは良いことです。というわけで、マウスの右ボタンが押された場合にメッセージを出すプログラムを考えてみてください。
なんちゃって。実は、説明していないので、できない方が当然です。でも、下の答えをよく見てみれば、なるほどと思うものがあると思います。見てみてください。
#include "afxwin.h"

class CMyWindow: public CFrameWnd
{
public:
    CMyWindow();
    void OnLButtonDown(UINT, CPoint);
    void OnRButtonDown(UINT, CPoint);

    DECLARE_MESSAGE_MAP()
};

class CMyApp:public CWinApp
{
public:
    virtual BOOL InitInstance();
};

CMyWindow::CMyWindow()
{
    Create(NULL, "Hello!", WS_OVERLAPPEDWINDOW);
}

void CMyWindow::OnLButtonDown(UINT, CPoint)
{
    MessageBox("左ボタンが押されたよ。","メッセージだ",MB_OK);
}

void CMyWindow::OnRButtonDown(UINT, CPoint)
{
    MessageBox("右ボタンが押されたよ。","メッセージである",MB_OK);
}

BEGIN_MESSAGE_MAP(CMyWindow, CFrameWnd)
  ON_WM_LBUTTONDOWN()
  ON_WM_RBUTTONDOWN()
END_MESSAGE_MAP()

BOOL CMyApp::InitInstance()
{
    m_pMainWnd=new CMyWindow;
    m_pMainWnd->ShowWindow(m_nCmdShow);
    m_pMainWnd->UpdateWindow();

    return TRUE;
}

CMyApp theApp;
言い忘れました。DECLARE_MESSAGE_MAP()はクラス宣言中の一番最後に置くのが良いようです。(そうでなくても良いのですが、その場合、ちょっと付帯条件があります。)また、メッセージマップの位置ですが、これは、クラス宣言の後(で常識的なところ)なら、どこでも良いようです。実は、これらも覚える必要はありません。
あとひとつ。上のOnLButtonDown(UINT, CPoint)やOnRButtonDown(UINT, CPoint)などの関数は、メッセージを処理するので、メッセージハンドラなどと呼ばれるようです。
いろいろ新しいことが出てきて不安かもしれませんが、この辺でやっていることは、後でAppWizardを理解するための基礎知識です。くどいですが、細かいことを覚える必要はありません。したがって、不安になる必要もないのです。元気があったら、メッセージボックスの中身だけ書き換えて見てください。あまり変なところは、まだいじらない方が良いと思います。
目次のページ
前のページ
後のページ