VC++5.0入門4
基礎知識4 97/8/3

こんにちは。夏ですね。本当は遊びまわりたい季節ですが、大人になるとそうはいかないものですね。(教師は夏休みがあって暇だと思う人が多いようですが、ちゃんと仕事があるんですよ、これがまた。)
それでは、プログラムの説明にいきましょうか。

ところで、みなさんがもし、ウインドウズアプリケーションを(もちろんC++で)書くとしたら、どのように書きますか。もちろん、人によって個性があるわけですが、C++の人なら、まず、

  必要なクラスを考える

でしょう。
ではどんなクラスを作りますか。もちろん、初心者には無理な話ですが、名前とちょっとした機能くらいなら考えられるでしょう。クラスは、普通、ものを表しますから、たぶん、

  アプリケーションとウインドウ

(を表すクラス)を考えるのではないでしょうか。マイクロソフトの超優秀なプログラマさんたちもそう考えたようです。彼ら(彼女ら?)は、ウインドウズアプリケーションの基本として、そのようなクラス、WinAppとCFrameWndを書いてくれました。これらのクラスは、普通のウインドウズアプリケーション(以後ウインドウズは省略できるところでは省略します)とウインドウの基本機能を持ったクラスです。これから、これらのクラスの機能を勉強しなければならないわけですが、それがMFCのめんどうなところです。しかし、初心者にはたぶん書けないだろうクラス群(これらをMFCというのでした)を用意してくれているのですから、文句を言うのもまとはずれです。とりあえず使うことにしましょう。

(以下の説明は前回のコードを見ながら読んでください。また、わかってもわからなくても、とりあえず最後まで読み、考え直すという方針をお勧めします。途中でひっかかるということは、プログラムの勉強ではなるべく止めた方が良いと思います。)

まず、ウインドウの方からいきましょう。MFCにFrameWndがあると書きましたが、自分のプログラムですから、自分のウインドウクラスを作る必要があるわけです。こういう時こそC++の継承が使えるのです。それが、
class CMyWindow:public CFrameWnd
{
public:
    CMyWindow();
};
です。ウインドウの機能のほとんどはCFrameWndにお任せして、自分は、新たにコンストラクタを作るぞ、と宣言しているだけです。それでいいんです。^^;)このコンストラクタは、ウインドウを作るものです。前に、自分のアプリケーションにウインドウを作るには、いろいろな設定をしなければならないので大変だと書きましたが、とりあえず簡単なもので良しとするなら、単に、ウインドウを作ってね、と一言言うだけですましたいものですね。このクラスはそのように作られているのです。つまり、
CMyWindow::CMyWindow()
{
    Create(NULL, "Hello!", WS_OVERLAPPEDWINDOW);
}
と書けば、クラスCMyWindowのオブジェクトが生成されるときに、CFrameWndのCreateという関数が呼び出されてウインドウが生成されるのです。オブジェクトの生成は「C++入門」でやりましたが、簡単ですよね。(ここではnewを使いますが、以下を見てください。)

Createの引数について説明しましょう。はじめのNULLはこのウインドウが普通のフレームウインドウ(というタイプのウインドウ)であることを意味しています。普通はそうですから、とりあえずは、これはこのままで覚えてください。(ちょっと、強引。^^;)二番目の"Hello!"はこのウインドウのタイトルに表示される文字列です。気がついていましたか。もし、気がつかなかったのなら、このプログラムを再度実行して確かめてみてください。そして、元気があったら、ここを書き換えて遊んでください。
そして、最後のWS_OVERLAPPEDWINDOWは、このウインドウのスタイルを指定しているのですが、要するに普通のウインドウであるという意味です。ここを書き換えれば、普通じゃないウインドウなんかもできるのですが、普通で良かったんですよね。では、普通でないのはどういうのか、と思うでしょうが、はじめはあまり出会うこともないでしょうから、今は、普通だけで良し、としてください。(どうしても気になる人はヘルプを見てくださいね。)

さて、次に、アプリケーション(のクラス)を考えます。これも、自分のアプリケーション(クラス)はMFCから派生させればよいわけです。
class CMyApp:public CWinApp
{
public:
    virtual BOOL InitInstance();
};
ここでは、InitInstanceという関数を宣言している以外は、MFCの機能をそのまま使うよ、と言っているのです。実は、このInitInstanceも決められた関数で、(このやり方では)プログラマがこのように定義しなければならない関数です。しかし、中身(の基本)は単純で、単に
BOOL CMyApp::InitInstance()
{
    m_pMainWnd=new CMyWindow();
    m_pMainWnd->ShowWindow(m_nCmdShow);
    m_pMainWnd->UpdateWindow();
    return TRUE;
}
と書くだけです。ちょっと嫌ですか?説明しましょう。まずこの関数はBOOLという値を戻すことになっています。BOOLはTRUEとFALSEという値なのです。これらは「単語」であって値ではないんじゃないかと思うかもしれませんが、実はTRUEとFALSEは値だと定義されているのです。実際、この関数の最後にreturn TRUE;とあるのはTRUEという値を戻しているのです。ここはあまり気にする必要はないと思います。
さて、InitInstanceの中身ですが、その前にm_pMainWndを説明します。これはCWinApp(本当はCWinAppの基底クラス)にあるデータメンバでウインドウ(を表すクラスのオブジェクト)へのポインタです。ちょっとわかりづらいかも知れません。つまりアプリケーションには(普通)ウインドウが付き物です。それでアプリケーションのクラスにはウインドウを格納しておく(とっておく)場所が当然必要なのです。そして、その「場所」を表すのがm_pMainWndなのです。「え〜。そんなデータメンバがあるなんて知らないよ。」と思うでしょう。これについては後でコメントします。まあ、知っていたとしましょう。
そうすると1行目のm_pMainWnd=new CMyWindow();はわかるのではないでしょうか。つまり、前に定義した自分のウインドウを生成(new)し、そのアドレスをm_pMainWndを格納しているのです。こうして、このアプリケーションはウインドウを持つことができたのです。
あと少しです。実は、ここでいうウインドウは目に見えているウインドウではありません。このウインドウを目に見えるようにするには、まず、CFrameWnd(の基底クラス)のShowWindowという関数を呼び出すのがルール(のよう)です。(その引数はなんのこっちゃと思うかもしれませんが、さしあたって、気にする必要はありません。)そして、その後、(普通は)ウインドウの更新という意味のUpdateWindowという関数を呼び出します。これらの関数を呼び出すのにm_pMainWndがポインタであることに注意して、
    m_pMainWnd->ShowWindow(m_nCmdShow);
    m_pMainWnd->UpdateWindow();
としてあるのです。要するに、この2行があればウインドウがはじめにきちんと表示される、と考えて良いのです。
InitInstanceの中身はちょっとわかりずらいですね。それにm_pMainWndだのShowWindowだのと、初心者の知らないことばかりです。でも、私の結論は「気にしない」です。そういうものなのです。どうせ、この中身(の4行)を自分流に書き換えることは(普通)ないので、大体の意味がわかったなら、とにかく、しばらくはこう書いていれば良い、ということもできます。それに不思議なことに見慣れるとどうということはなくなります。
さらに言うと、少ししたらAppWizardを使う予定ですが、そうなると、InitInstanceの必要な部分はAppWizardが自動で書いてくれます。意味がまったくわからないととても嫌ですが、大体の意味がわかり、流れがわかれば、それで十分だと思うのです。
さて、もう一息。今までやってきたことは、クラスの定義です。実際にプログラムを進行させるには、そのオブジェクトを生成する必要があります。それが、最後の
CMyApp theApp;
です。ここで、自分のアプリケーションのオブジェクト(つまり、アプリケーションの実体ですね)が生成されているのです。すると、CMyAppのInitInstanceが自動で呼び出されて、アプリケーションに付随するウインドウが生成され、アプリケーションに格納(登録)されます。別個にウインドウを生成する必要はないのです。
これで終わりです。、、、、、というと、みなさんどう思いますか。C++プログラミングに慣れてきた人なら、たぶん、誰でも「あれ。mainはどうしちゃったの」と思うでしょう。「VC入門1」を読んでちゃんとおぼえている人は「え〜と。ウインドウズアプリケーションでは、mainがWinMainに変わるって言ってたっけ。それにしても、WinMainとやらはどこにあるんだろう。」と思ったことでしょう。
実は、私は、main改めWinMainがどこに居るのか、本当はよく知りません。VCのヘルプには「WinMain はクラス ライブラリによって提供され、アプリケーションの起動時に呼び出されます。」と書かれています。これは、要するに、こういう事です。

「WinMainの仕事はMFCが見えないところでちゃんとやってあげるから、プログラマは書かなくていいよ。」

ですから、WinMiainが正確にどこに居るのか知らなくてよいのです。これは、main(やWinMain)に慣れているプログラマにはショックです。(私にはショックでした。それで、一時、MFCなんかやりたくない、とまで思ったほどです。最初に同じ感想を持った人は多いようです。)でも、よく考えると、WinMain(くどいですが、mainに相当する、ウインドウズプログラムでのプログラム「本体」)には、あまり仕事が無いのです。そもそも「C++入門」にあるような「文字ベース」のプログラムでは(普通)mainはプログラムのメインな「流れ」を記述する場所でした。しかし、ウインドウズアプリケーションには、そのような流れがありません。流れはユーザ(とウインドウズというOS)がマウスをクリックしたり(メッセージを送信したり)して、その場その場で作っていくものです。このようなタイプのプログラムには「メッセージに対処するコード」が必要と「VC入門1」に書きました。その部分こそウインドウズプログラムの「中心」なわけですが、これはWinMainには入れられないのです。(まあ、そうなんです。)では、WinMainは何かというと、 プログラムをはじめるよ、というしるし程度の意味しかないのです。そうすると、プログラマがわざわざ書くこともない、自動にして、見えなくしてしまえ、、、となるわけです。いえ、マイクロソフトの人に聞いたわけじゃありません。講談師と教師は「見てきたように言う」と言われますが、以上は「消えたWinMain事件」に関する私の推理です。でも、たぶん、そうでしょう。
何にしても、

  mainに相当する部分(WinMain)がMFCプログラムには無い

のです。そして、やることは、

  CWinApp(の派生クラス)のオブジェクトを生成する

なのです。
これで「VC入門3」のプログラムの解説が終わりました。不満は多いでしょうが、大体のところがわかれば良いと思います。あとはぼちぼちやっていきましょう。
ところで 「メッセージに対処するコード」ってなんだ、と思った人は、ちゃんとついてきている人です。それは、まだやっていないのです。次回にやってみましょう。それまでは、Createの"Hello!"を書き換えて遊んでおいてください。ここに自分の名前なんかを入れると、自分がとっても偉くなったような気分を味わえます。
目次のページ
前のページ
後のページ