Codian

KAB-studio > プログラミング > Codian > MFCユーザーのためのAPIプログラミング講座 > すべてはWinMain()から
 
まえつぎ

すべてはWinMain()から

del.icio.us 登録する Yahoo!ブックマーク 詳細を表示 users livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数 はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数 RSSに登録
更新日: 2008/01/08
動作確認環境:Windows XP Professional SP2, Visual C++ 6.0 Professional SP6
サンプルプロジェクト(Visual C++ 6.0用): MxA02_VC6.zip

 それでは、前回のプログラムの解説を始めましょう。
 最初は「一番最初に呼ばれる関数」からです。

WinMain()って何?

 前回紹介したWinMain.cの最初に、「WinMain()」という関数が出てきます。

//////////////////////////////////
// グローバル変数。

const char *const APP_NAME = "MxA02_VC6";       // アプリケーションの名前です。
const char *const CLASS_NAME = "MxA02WndClass";    // ウィンドウクラス名です。

HINSTANCE g_hInstance; // このアプリケーションのインスタンスハンドルです。

////////////////////////////////////////////////////////////////////
// Windowsから最初に呼ばれる関数です。

int WINAPI WinMain
    ( HINSTANCE p_hInstance
    , HINSTANCE p_hPrevInst
    , LPSTR p_pchCmdLine
    , int p_iCmdShow
    )
{
    // インスタンスハンドルをグローバル変数に取っておきます。
    g_hInstance = p_hInstance;

    // ウィンドウクラスを登録します。
    if( RegisterWindowClass() == FALSE )
    {
        return FALSE;
    }

    // ウィンドウの表示を行います。
    if( CreateAndShowWindow( p_iCmdShow ) == FALSE )
    {
        return FALSE;
    }

    // メッセージループを開始します。
    return MessageLoop();
}
//////////////////////////////////
// グローバル変数。

const char *const APP_NAME = "MxA02_VC6";       // アプリケーションの名前です。
const char *const CLASS_NAME = "MxA02WndClass";	// ウィンドウクラス名です。

HINSTANCE g_hInstance; // このアプリケーションのインスタンスハンドルです。

////////////////////////////////////////////////////////////////////
// Windowsから最初に呼ばれる関数です。

int WINAPI WinMain
	( HINSTANCE p_hInstance
	, HINSTANCE p_hPrevInst
	, LPSTR p_pchCmdLine
	, int p_iCmdShow
	)
{
	// インスタンスハンドルをグローバル変数に取っておきます。
	g_hInstance = p_hInstance;

	// ウィンドウクラスを登録します。
	if( RegisterWindowClass() == FALSE )
	{
		return FALSE;
	}

	// ウィンドウの表示を行います。
	if( CreateAndShowWindow( p_iCmdShow ) == FALSE )
	{
		return FALSE;
	}

	// メッセージループを開始します。
	return MessageLoop();
}

 WinMain()関数は、アプリケーションを起動したときWindowsが最初に呼び出す関数です。
 エクスプローラーでファイルをダブルクリックすると、Windowsは「WinMain」という名前の関数を探し、それをまず最初に呼び出します。
 そして、この関数が終了したときアプリケーションが終了します。アプリケーションが開始するとWinMain()関数に入り、その関数から抜けた時にアプリケーションが終了するわけです。つまり、この関数は「プログラムの出入り口」というわけです。

 この関数は自分で作る関数ですが、実はAPIの一種です
 通常のAPIはプログラムから呼び出すものですが、一部のAPIは「自分で作る」必要があります。WinMain()関数や、後で説明する「ウィンドウプロシージャ」がこれに当たります。
 これらの関数は自分で作った関数を「呼び出してもらう」ため、引数や戻り値等がちゃんと決まっています。好き勝手作るのではなく、決められた通りに作ることで初めて呼び出してもらえるわけです。引数や戻り値の型についてはWin32 API リファレンスに書かれているので、それに合わせるようにします(参照:MSDN Win32 API WinMain() リファレンス)。

 このWinMain()関数を含めて、APIはC言語形式の関数になっています。
 MFCを使用する場合、クラスとそのメンバ関数を使うことがほとんどなので、このような関数単独のものは見慣れないかもしれません。ただ、MFCにもC言語形式の関数があります。関数名が「Afx」から始まる関数がそうです(たとえばAfxGetInstanceHandle()関数)。もし「Afx」から始まる関数を使用したことがあれば、あれと同じと考えるといいでしょう。
 クラスのメンバ関数がC言語形式の関数と最も違う点は、C言語形式の関数は「メンバ変数を使えない」という点です。
 皆さんがいつも使用している「クラスのメンバ関数」は、クラスの中に持っている変数(メンバ変数)をメンバ関数の中で利用することができます。ところが、このC言語形式の関数は、内部に変数を持ちません。ですから、関数を呼び出すときに引数としていちいち渡さなければなりません。逆に言うと、MFCのクラスはそういった手間を省くようにできているということです

 さて、このWinMain()関数、よく見るとかなり短いです。
 関数の中身が20行ありません。いくつかの関数を呼び出して、そのまま終わってしまいます。
 最初に説明したように、WinMain()関数から抜けるとその時点でアプリケーションが終了します。なんだかこんなに短いとあっさりとアプリケーションが終了してしまいそうですが、実は最後に呼び出すMessageLoop()関数の中に無限ループがあるので心配ありません。そのウィンドウの「×ボタン」を押さない限り無限ループから抜けないので大丈夫です。逆に言うと、「×ボタン」を押すことで無限ループから抜けて、さらにMessageLoop()関数からも返ってくるのでその時点でアプリケーションが終了します。
 WinMain()関数内で呼び出している「RegisterWindowClass()」「CreateAndShowWindow()」「MessageLoop()」の3関数は、APIではなくこのプロジェクトで作成した関数です。これらの関数については、次ページ以降に説明します。

 ちなみにWinMain()関数のように「最初に呼び出され、抜けると終了する関数」のことを「エントリーポイント」と呼びます。憶えておくとちょっといいかもしれません。

インスタンスハンドル

 次に、WinMain()関数の引数と戻り値を見てみましょう。
 第1引数にはHINSTANCEという型の値が渡されています。これはインスタンスハンドルというものです。
 Windowsの仕組みのひとつに「インスタンス」というものがあります。このインスタンスとは、Windowsのアプリケーションが実行された時に必ず確保するメモリ領域のことです。アプリケーションはこの領域に、自分のアプリケーションの情報や、アイコンやメニューといったリソース情報を保存します。つまり自分自身の情報の置き場所、それがインスタンスというわけです。

 インスタンスはアプリケーションの特別な情報が入っていて、他の情報とは区別されています。そのため、このインスタンスというものに度々お世話になることになります。特にリソース関係の処理をする際に必要となります。
 このインスタンスを扱う際には、インスタンスの領域に直接アクセスするのではなく、インスタンスへのポインタを通してアクセスします。このポインタのことを「インスタンスハンドル」と呼びます。WinMain()関数にはこのインスタンスハンドルが渡されてくるので、それを利用してインスタンスにアクセスすることになります。
 ちなみにこの「ハンドル」というものはとても重要な用語ですので名前だけでも憶えておいてください。後で何度となく出てきます。詳しい説明もその時行います。

今は使われない「ひとつ前のインスタンス」

 WinMain()関数の第2引数を見ると、これもHINSTANCE型、つまりインスタンスハンドルです。では、第1引数のインスタンスハンドルとは何が違うのでしょうか。
 実はこの第2引数、現在は使われていません。現在は常にNULLが渡されます。
 この第2引数は、昔のWindows(Windows95より前)では使用されていて、「ひとつ前のインスタンス」を指すインスタンスハンドルが渡されていました。
 あるアプリケーションを2回実行したとします。1回目の実行では、第2引数にNULLが渡されます。2回目の実行では、第2引数に「1回目に実行したアプリケーションのインスタンスハンドル」が渡されます。つまり「1回目の時の第1引数=2回目の時の第2引数」ということです。
 この仕組みを利用して、2回目に実行されたアプリケーションは1回目のアプリケーションにアクセスすることができます。また、1回目に実行したときにはNULLが渡されるので、それを利用して「1回しか起動できないアプリケーション」を作るために利用されたりもしました。

 これが可能だったのは、そもそも「複数のアプリケーションがひとつのメモリ領域内にあったから」です。
 現在のWindows(Windows95以降)では、メモリ領域はアプリケーションごとに独立しているので、他のアプリケーション領域内にあるインスタンスにはアクセスできません。そのため、今では第2引数にNULLが渡されているわけです。

 このように、第2引数はすでに使われていないのですが、だからといってWinMain()関数の引数が3つに減ることはありませんでした。
 これは「APIの仕様はそう簡単に変わらない」ことを示しています。
 APIはかなり昔にできて、それが今でも変わらず使用できます。それは、いい意味で言えば「プログラムを修正せず今のWindowsでも使える」ことであり、悪い意味で言えば「APIの仕様がとても古い」ということです。
 APIには意味不明な慣例や謎な記述が多々ありますが、それも「昔は、ね……」ということで生暖かい目で見ることにしましょう。

WinMain()関数の残りの引数と戻り値

 WinMain()関数の残りの引数と戻り値についても見ておきましょう。

 第3引数は「コマンドライン引数」です。コマンドプロンプトやショートカットで「実行ファイル名」の後ろにスペースを挟んで入力した文字列が渡されます。
 C言語を習ったことのある方は「main()のargvか」と思うかもしれませんが、微妙に違っていて、単語単位で分割されないこと、実行ファイル名は渡されない点が異なります。元々Windowsのウィンドウアプリケーションはコマンドラインで実行することはあまりないので、このあたりはテキトーになっています。
 第4引数は「ウィンドウの表示方法」です。アプリケーションのショートカットを作ると、そこに「実行時の大きさ」という欄があります。ここで「最大化」「最小化」等の設定を行うことができます。その設定がこの引数に渡されます。
 逆に言うと、この設定は「ただアプリケーションに渡される」だけなので、実際に最大化したり最小化したりといった設定をプログラム内で行う必要があります。自動的に行われるわけじゃないということですね。ちなみに今回のサンプルプログラムではちゃんと対応していますので、その方法についてあとで説明します。

 戻り値はint型の値になっています。この値は、「アプリケーションを他のアプリケーションから実行した」時に、実行元に返される値です。「正常終了」や「異常終了」を示す値を返すことで、実行元は実行結果を取得できるというわけです。……が、そういう用途以外には必要ないので、適当な値でも構いません。
 最後に、戻り値の前に付いている「WINAPI」ですが、これは「APIの一種ですよ」ということを示しています。APIによっては「PASCAL」だったり「APIENTRY」だったりしますが、基本的にどれも同じです。
 もう少し具体的に言うと、「WINAPI」は「アプリケーションとWindowsの境界をまたいで呼び出される関数」に付けるものです。WinMain()関数は「アプリケーションの中にあって、Windowsから呼び出される」関数ですし、また通常のAPIは「Windowsにあって、アプリケーションから呼び出される」関数です。このような関数にはWINAPIを付ける決まりになっています(もう少し詳しく知りたい方は「DLLを作ろう!(関数編)」の「装飾名」の項目をご覧ください)。

MFCの中のWinMain()関数

 この講座の「はじめに」で、「MFCはAPIを内部で使用している」と書きました。
 ということは、MFCのどこかにこのWinMain()関数があるということです。ではその場所を探してみましょう。
 まず、MFCのソースファイルが置いてある場所を調べましょう。MFCは、実はすべてのプログラムがソースファイルの状態になっているので、中身を見ることができます。
 Visual C++ 6.0の場合、「(インストールしたフォルダ)\MICROSOFT VISUAL STUDIO\VC98\MFC\SRC\」というフォルダにMFCのソースファイルが置いてあります。標準の設定でインストールした場合「C:\PROGRAM FILES\MICROSOFT VISUAL STUDIO\VC98\MFC\SRC\」になります(ただし、インストール時に「MFCのソースファイルをインストールしない」という設定にした場合にはありません。その場合はVisual C++のCD-ROMを入れてソースファイルもインストールしてください。また、Visual C++のバージョンが異なる場合、ディレクトリ構成が異なる可能性がありますのでご注意ください)。

 このフォルダを開くとたくさんのソースファイルが置いてあります。このファイルひとつひとつ開いて見るのは大変なので、検索で探してみましょう。
 複数のファイルの中から検索する場合「ファイルから検索」という機能を使用します。
 Visual C++ 6.0のメニューの、「編集」-「ファイルから検索」を選択してください。「ファイルから検索」ダイアログが表示されます。

 まず「検索文字列」に検索する文字列を入力します。WinMain()関数を探すためここでは「WinMain」を入力します。
 「検索するフォルダ」には、検索対象のファイルがあるフォルダを指定します。ここでは上記の、MFCソースファイルが置いてあるフォルダを指定します。
 この2項目を入力してから「検索」ボタンを押すと、検索を開始します。検索結果はアウトプットウィンドウの「ファイルから検索1」に出力されます。この検索結果の中から見たい箇所を選んでダブルクリックすると、そのファイルが開きます。
 その結果、APPMODUL.CPPというファイルの25行目(バージョンによって行番号は異なります)に、以下の箇所が見つかりました。

extern"C" int WINAPI
_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    LPTSTR lpCmdLine, int nCmdShow)
{
    // call shared/exported WinMain
    return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
}
extern"C" int WINAPI
_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	LPTSTR lpCmdLine, int nCmdShow)
{
	// call shared/exported WinMain
	return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
}

 これがWinMain()関数っぽい……けど、関数名がちょっと違います。_tWinMain()と書かれています。なんか怪しいニセモノみたいです。
 ところがこれはニセモノではなく、れっきとしたWinMain()関数なのです。
 これは、Windowsの文字列の仕組みに関係しています。Windowsで扱う文字列には大きく2種類あって、それをプログラム上で切り替えられる仕組みがあります。
 最初に紹介した「WinMain」という関数名は、そのうち一種類のみに対応した関数名です。対して、MFCの中で使用している「_tWinMain」という関数名は切り替え機能に対応しています。そして、その切り替えを行うことで、内部的に「_tWinMain」が「WinMain」に置き換わります。つまり結局は「_tWinMain」と「WinMain」は同じということになります。
 この切り替え機能の詳しい説明は、この講座の最後、「ポインタと文字列とCStringと」で解説しますのでそれまでしばらくお待ちください。

 さて、話を戻しましょう。
 MFCの中にある_tWinMain()関数、これはWinMain()関数と同じですから、つまり、アプリケーションを実行したらまずこの関数が呼び出される、ということです。
 ここ大事です。「MFCを使用していても、最初はWinMain()関数から始まる」んだということです。
 MFCを使用していると、CなんたらAppやらCなんたらDlgやらCなんたらViewといった、クラスの集まりだけを使用してプログラムを作り、ボタンを押すと対応するメンバ関数が呼ばれる、というイメージができると思います。
 ですが、たとえMFCを使用していても、始まりは1つの関数です。その中でクラスの変数が作られ、さらにあとで説明する「メッセージループ」という所を通って「ボタンに対応するメンバ関数」を呼び出す仕組みになっています。つまり、そのようなメンバ関数でさえも「WinMain()から継ながっている」ということです。なんら脈絡もなく突然呼び出されるわけではない、ということをしっかり憶えておいてください。

 ちなみに、このMFCの_tWinMain()関数を見てみると、単に「AfxWinMain()」という関数を呼び出しているだけです。
 この関数はMFCの関数で、WINMAIN.CPPの21行目にあります。WinMain()関数はAfxWinMain()関数を呼び出しているだけですので、実質的な役割はAfxWinMain()関数が担っているということになります。
 このAfxWinMain()を見てみると、サンプルプログラム1のWinMain()関数とはだいぶ違うように見えますが、実は結構似ています。

    CWinThread* pThread = AfxGetThread();
    CWinApp* pApp = AfxGetApp();

// 略

    // App global initializations (rare)
    if (pApp != NULL && !pApp->InitApplication())
        goto InitFailure;

    // Perform specific initializations
    if (!pThread->InitInstance())
    {

// 略

    }
    nReturnCode = pThread->Run();
	CWinThread* pThread = AfxGetThread();
	CWinApp* pApp = AfxGetApp();

// 略

	// App global initializations (rare)
	if (pApp != NULL && !pApp->InitApplication())
		goto InitFailure;

	// Perform specific initializations
	if (!pThread->InitInstance())
	{

// 略

	}
	nReturnCode = pThread->Run();

 AfxWinMain()関数内でCWinThreadクラスのポインタpThreadや、CWinAppクラスのポインタpAppを使用しています。
 これらのポインタを通して呼び出しているメンバ関数が、実はサンプルプログラム1で呼び出している各関数と対応しています。
 具体的には、「pApp->InitApplication()」はRegisterWindowClass()関数の呼び出しに、「pThread->InitInstance()」はCreateAndShowWindow()関数の呼び出しに、「pThread->Run()」はMessageLoop()関数の呼び出しに対応しています。実際に行ってる処理はMFCの方が複雑ですが、やっていることはだいたい同じ、MFCの場合にはメンバ関数で処理しているということです。

グローバル変数にインスタンスハンドルを入れとこう

 WinMain()関数内で呼び出している各関数については次ページ以降で説明していくとして、このページの残りはWinMain()関数の外側について見ていきましょう。
 サンプルプログラムでは3つのグローバル変数を使用しています。その内のg_hInstance変数について説明します。
 この変数には、WinMain()関数で渡されたインスタンスハンドルをセットしています。

HINSTANCE g_hInstance; // このアプリケーションのインスタンスハンドルです。

// 略

int WINAPI WinMain
    ( HINSTANCE p_hInstance
    , HINSTANCE p_hPrevInst
    , LPSTR p_pchCmdLine
    , int p_iCmdShow
    )
{
    // インスタンスハンドルをグローバル変数に取っておきます。
    g_hInstance = p_hInstance;
HINSTANCE g_hInstance; // このアプリケーションのインスタンスハンドルです。

// 略

int WINAPI WinMain
	( HINSTANCE p_hInstance
	, HINSTANCE p_hPrevInst
	, LPSTR p_pchCmdLine
	, int p_iCmdShow
	)
{
	// インスタンスハンドルをグローバル変数に取っておきます。
	g_hInstance = p_hInstance;

 グローバル変数とは、「どの関数からでもアクセスできる変数」のことです。
 メンバ関数にとってのメンバ変数のように、「引数に渡さなくても使える変数」のことです。とっても便利そうですが、「グローバル」の名の通り、本当にどこからでもアクセスできてしまうため、頻繁に書き換える用途としてグローバル変数を使用することは激しくおすすめしません
 今回のg_hInstance変数は、ほとんど定数のように扱うことを目的としています。最初に入れておいて、あとで書き換えることはしません。実行中にインスタンスハンドルが変わることはあり得ないからです。
 ではなぜグローバル変数に入れておくのかというと、インスタンスハンドルは「引数に渡さなければいけないAPIが多い」からです。インスタンスにはアプリケーション固有の情報が入っているため、APIがその情報にアクセスするために引数に渡す必要があります。その時に簡単に渡せるよう、このようにグローバル変数にしておくといいでしょう。
 ちなみに「グローバル変数は絶対に嫌だ!」という方は、GetWindowLong()関数というAPIを使用することで自分自身のインスタンスハンドルを取得することができますのでそちらをお試しください。

 このインスタンスハンドルをMFCで取得する場合にはAfxGetInstanceHandle()関数を使用します。この関数はC言語形式の関数で、自分自身のインスタンスハンドルを返します。MFCを使用している際にインスタンスハンドルが必要になったらこの関数を呼びましょう。
 ちなみに、グローバル変数はMFCにもあります。
 おそらく一番なじみ深いのは、各アプリケーションのCWinApp派生クラス(CなんたらAppクラス)型のtheApp変数でしょう。この変数は「なんたらApp.cpp」ソースファイルでグローバル変数として宣言されています。

/////////////////////////////////////////////////////////////////////////////
// 唯一の CMxA09_VC6App オブジェクト

CMxA09_VC6App theApp;
/////////////////////////////////////////////////////////////////////////////
// 唯一の CMxA09_VC6App オブジェクト

CMxA09_VC6App theApp;

 この変数はグローバル変数ですので、アプリケーション全体からアクセスできます。このクラスそのものが「アプリケーション全体を管理するクラス」ですので、それを踏まえてVisual C++がこの変数を用意してくれるのでしょう。

APIのヘッダーファイルとライブラリファイル

 最後に、インクルードするヘッダーファイルについて説明します。
 APIやCランタイムライブラリのような「他の所にある関数」を呼び出す場合、「その関数の宣言が書かれているヘッダーファイルをインクルードする」必要があります(参照:Codian - ソースファイルとヘッダーファイル)。
 WinMain.cソースファイルでは、「windows.h」と「WinMain.h」の2つのファイルをインクルードしています。

// WinMain.c
#include <windows.h>
#include "WinMain.h"
// WinMain.c
#include <windows.h>
#include "WinMain.h"


 windows.hは、WindowsのAPIを使用する際にインクルードするヘッダーファイルです。
 Windowsの基本的な機能を実行する関数や、Windows内で使用する型(LPCTSTR等)は、このヘッダーファイルをインクルードすれば使うことができます。ですので、とりあえずこのファイルだけインクルードしておきましょう。
 また、使用するAPIがヘッダーファイルに含まれているかは、MSDNのAPIリファレンスの一番下にある「対応情報」(英語版は「Quick Info」)に書かれています。

 この欄にある「ヘッダー」項目の「***.hをインクルード」と書かれている箇所を探してください。この「***.h」をインクルードすれば使うことができます。

 MFCの場合、このインクルードを自動的に行ってくれます。
 MFCを使用するプロジェクトを作成すると、StdAfx.hというヘッダーファイルが自動的に作成されます。

// stdafx.h : 標準のシステム インクルード ファイル、
//            または参照回数が多く、かつあまり変更されない
//            プロジェクト専用のインクルード ファイルを記述します。
//

#if !defined(AFX_STDAFX_H__D17F15BD_E9D7_4722_8D5E_7A2ABBF64C22__INCLUDED_)
#define AFX_STDAFX_H__D17F15BD_E9D7_4722_8D5E_7A2ABBF64C22__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#define VC_EXTRALEAN        // Windows ヘッダーから殆ど使用されないスタッフを除外します。

#include <afxwin.h>         // MFC のコアおよび標準コンポーネント
#include <afxext.h>         // MFC の拡張部分
#include <afxdisp.h>        // MFC のオートメーション クラス
#include <afxdtctl.h>        // MFC の Internet Explorer 4 コモン コントロール サポート
#ifndef _AFX_NO_AFXCMN_SUPPORT
#include <afxcmn.h>            // MFC の Windows コモン コントロール サポート
#endif // _AFX_NO_AFXCMN_SUPPORT


//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ は前行の直前に追加の宣言を挿入します。

#endif // !defined(AFX_STDAFX_H__D17F15BD_E9D7_4722_8D5E_7A2ABBF64C22__INCLUDED_)
// stdafx.h : 標準のシステム インクルード ファイル、
//            または参照回数が多く、かつあまり変更されない
//            プロジェクト専用のインクルード ファイルを記述します。
//

#if !defined(AFX_STDAFX_H__D17F15BD_E9D7_4722_8D5E_7A2ABBF64C22__INCLUDED_)
#define AFX_STDAFX_H__D17F15BD_E9D7_4722_8D5E_7A2ABBF64C22__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#define VC_EXTRALEAN		// Windows ヘッダーから殆ど使用されないスタッフを除外します。

#include <afxwin.h>         // MFC のコアおよび標準コンポーネント
#include <afxext.h>         // MFC の拡張部分
#include <afxdisp.h>        // MFC のオートメーション クラス
#include <afxdtctl.h>		// MFC の Internet Explorer 4 コモン コントロール サポート
#ifndef _AFX_NO_AFXCMN_SUPPORT
#include <afxcmn.h>			// MFC の Windows コモン コントロール サポート
#endif // _AFX_NO_AFXCMN_SUPPORT


//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ は前行の直前に追加の宣言を挿入します。

#endif // !defined(AFX_STDAFX_H__D17F15BD_E9D7_4722_8D5E_7A2ABBF64C22__INCLUDED_)

 このStdAfx.hヘッダーファイル内で、afxwin.h等のヘッダーファイルをインクルードしています。
 これらははMFC内のヘッダーファイルで、この中でwindows.hといったAPIのヘッダーファイルをインクルードしています。
 具体的には、AFXV_W32.Hというヘッダーファイル内でインクルードしています(MFCのヘッダーファイルは「(インストールしたフォルダ)\MICROSOFT VISUAL STUDIO\VC98\MFC\Include\」にあります)。StdAfx.hから、AFXWIN.H→AFX.H→AFXVER_.H→AFXV_W32.Hという順番でインクルードしているため、MFCを使用しているプロジェクトではAPIもそのまま使うことができます。

 逆に言うと、このような形でMFCでインクルードしていないヘッダーファイルは、自分でインクルードしなければいけないということです。そのような場合には、StdAfx.hの、サンプルプログラム7であれば「// Microsoft Visual C++ は前行の直前に追加の宣言を挿入します。」の下に記述するといいでしょう。

 もうひとつ、APIを使用するためには「ライブラリファイル」をインポートする必要があります。
 「ライブラリファイル」とは、拡張子が「.lib」のファイルで、APIを使用する際に必要な「どのDLLにどの関数があるのか」という情報が書かれたファイルです。これをしないとAPIを使用することができません(参照:Codian - 「自作」DLLを使おう!!)。
 どのAPIにどのライブラリファイルが必要か、という情報は図6で紹介した「対応情報」に書かれています。
 ではそのライブラリファイルをどのようにインポートするのかというと、通常は「プロジェクトの設定」で行います。
 Visual C++のメニューの「プロジェクト」-「設定」を選択すると、「プロジェクトの設定」ダイアログが表示されます。
 この「リンク」タブの「一般」ページにある「オブジェクト/ライブラリモジュール」という欄で、使用するライブラリファイルをスペース区切りで指定します。

 もっとも、実はVisual C++が自動的に「通常必要なライブラリファイル」を指定してくれています。ですから、基本的にここでライブラリファイルを指定する必要はありません。必要あるとすれば、他の人が作ったDLLや、自分で作ったDLLを使う時くらいでしょう。

 あれ、と思った方もいるでしょう。
 実は、MFCアプリケーションを作製したときには、これらのライブラリファイルが書き込まれていません。MFCを使用するプロジェクトでは、この欄が空欄になっているのです。
 実は、MFCではライブラリファイルの指定がヘッダーファイルの中で行われています。
 具体的には、AFX.Hヘッダーファイルの最初の方に書かれています。

#pragma comment(lib, "kernel32.lib")
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "gdi32.lib")
#pragma comment(lib, "comdlg32.lib")
#pragma comment(lib, "winspool.lib")
#pragma comment(lib, "advapi32.lib")
#pragma comment(lib, "shell32.lib")
#pragma comment(lib, "comctl32.lib")
#pragma comment(lib, "kernel32.lib")
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "gdi32.lib")
#pragma comment(lib, "comdlg32.lib")
#pragma comment(lib, "winspool.lib")
#pragma comment(lib, "advapi32.lib")
#pragma comment(lib, "shell32.lib")
#pragma comment(lib, "comctl32.lib")

 ここで使われている「#pragma」は、プリプロセッサのひとつで、特殊な機能をいくつも持っています。その内のひとつが、ライブラリへのリンクです。このように指定することで、「プロジェクトの設定」ダイアログで指定しなくても、プログラム内で指定することができるというわけです。
 ただしこれも、MFCで使用しないAPIを使用する場合には、自分でライブラリファイルをインポートする必要があるということです。その際には、「対応情報」のページを参考に、「プロジェクトの設定」ダイアログで指定するようにしましょう。

 次回はウィンドウを作成するために必要な「ウィンドウクラスの登録」について説明します。

3. すべてはWinMain()から
※このページは、Visual C++ 6.0を用いた C++言語プログラミングの解説を行うCodianの一コンテンツです。
詳しい説明はCodianのトップページをご覧ください。