世界で一番有名な"Hello World!"と出力するアプリケーション。まずはMFCを使わずAPIのみ使ってこのアプリケーションを作ってみましょう。
とりあえず楽したい!
「プログラム打ち込むの面倒!」という方は、このページの上の方にサンプルプログラムへのリンクがあります(MxA02_VC6.zip)のでそちらからダウンロードしてください。
いや、地道に打ち込みたい。
という方のために、プロジェクトを一から作成して作る方法をこれから解説します。
Visual C++ 6.0 Professional用の解説になりますのでご注意ください。
プロジェクトの作成
まずVisual C++のメニューから「ファイル」-「新規作製」を選択します。
「新規作成」ダイアログ(図1)が表示されるので、そのダイアログの上部にあるタブから「プロジェクト」を選びます(元々プロジェクトやワークスペースを開いていなかった場合には、初めから表示されています)。
プロジェクトの種類一覧の中から「Win32 Application」を選択してください。あと「プロジェクト名」(ここでは「MxA02_VC6」とします)と「位置」(プロジェクトのフォルダを置く場所です)を適当に決めて、OKボタンを押してください。
次に表示される「Win32 Application」ダイアログ(図2)で「空のプロジェクト」を選択して、「終了」ボタンを押してください。これでプロジェクトが作成されます。
このようにしてプロジェクトを作製すると……本当に何も作製されません。
MFCを使用する場合には、Windowsアプリケーションに必要な最低限のプログラムが自動的に作られるのですが、APIだけで作る場合にはすべて自分で用意しなくてはならないのです。
(ただ、図2のダイアログで「単純なWin32アプリケーション」や「標準的な"Hello World!"アプリケーション」を選択すると、Visual C++が最低限のプログラムだけは用意してくれます。ここでは作り方を憶えるためにもこの2つは使用しません。この講座を一通り読んだら、この2つも試してみてください)
プログラムの入力
プロジェクトができたので、次はプログラムを入力します。
まずはヘッダーファイルを作成しましょう。
Visual C++のメニューから「ファイル」-「新規作製」を選択します。
「新規作成」ダイアログ(図3)が表示されるので、そのダイアログの上部にあるタブから「ファイル」を選びます(おそらく初めから表示されています)。
ファイルの種類一覧から「C/C++ヘッダーファイル」を選択し、「ファイル名」に「WinMain.h」を入力してください。
OKボタンを押すとWinMain.hという空のテキストファイルがプロジェクト内に作られ、表示されます。
このファイルに次のプログラムを入力してください。
int WINAPI WinMain( HINSTANCE p_hInstance, HINSTANCE p_hPreInst, LPSTR p_pchCmdLine, int p_iCmdShow );
BOOL RegisterWindowClass();
BOOL CreateAndShowWindow( int p_iCmdShow );
int MessageLoop();
LRESULT CALLBACK WndProc( HWND p_hWnd, UINT p_uiMsg, WPARAM p_wParam, LPARAM p_lParam );
void WriteHelloWorld( HWND p_hPaintWnd );
// WinMain.h int WINAPI WinMain( HINSTANCE p_hInstance, HINSTANCE p_hPreInst, LPSTR p_pchCmdLine, int p_iCmdShow ); BOOL RegisterWindowClass(); BOOL CreateAndShowWindow( int p_iCmdShow ); int MessageLoop(); LRESULT CALLBACK WndProc( HWND p_hWnd, UINT p_uiMsg, WPARAM p_wParam, LPARAM p_lParam ); void WriteHelloWorld( HWND p_hPaintWnd );
以上でヘッダーファイルが作成できました。
次はソースファイルを作成します。
ヘッダーファイルの時と同じように、メニューの「ファイル」-「新規作製」を選択します。
「新規作成」ダイアログ(図4)が表示されるので、そのダイアログの上部にあるタブから「ファイル」を選びます(やっぱり初めから表示されていると思います)。
ファイルの種類一覧から、今度は「C++ソースファイル」を選択し、「ファイル名」に「WinMain.c」を入力してください。
OKボタンを押すとWinMain.cという空のテキストファイルがプロジェクト内に作られ、表示されます。
このファイルに次のプログラムを入力してください。
#include <windows.h>
#include "WinMain.h"
//////////////////////////////////
// グローバル変数。
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();
}
////////////////////////////////////////////////////////////////////
// ウィンドウクラスを登録します。
BOOL RegisterWindowClass()
{
// ウィンドウクラスの情報を格納する構造体です。
WNDCLASS stWndClass;
// どんなウィンドウクラスを作るのか、設定をセットします。
stWndClass.style = CS_BYTEALIGNWINDOW | CS_HREDRAW | CS_VREDRAW;
stWndClass.lpfnWndProc = WndProc;
stWndClass.cbClsExtra = 0;
stWndClass.cbWndExtra = 0;
stWndClass.hInstance = g_hInstance;
stWndClass.hIcon = LoadIcon( NULL, IDI_EXCLAMATION );
stWndClass.hCursor = LoadCursor( NULL, IDC_ARROW );
stWndClass.hbrBackground = (HBRUSH)COLOR_BACKGROUND;
stWndClass.lpszMenuName = NULL;
stWndClass.lpszClassName = CLASS_NAME;
// ウィンドウクラスを登録します。
if( RegisterClass( &stWndClass ) == 0 )
{
return FALSE;
}
return TRUE;
}
////////////////////////////////////////////////////////////////////
// ウィンドウを作成して表示します。
BOOL CreateAndShowWindow( int p_iCmdShow )
{
// ウィンドウハンドルを取っておく変数。
HWND hWnd;
// ウィンドウを作製します。
hWnd = CreateWindow
( CLASS_NAME
, APP_NAME
, WS_OVERLAPPEDWINDOW
, CW_USEDEFAULT
, 0
, CW_USEDEFAULT
, 0
, NULL
, NULL
, g_hInstance
, NULL
);
// ウィンドウができたかチェックします。
if( hWnd == NULL )
{
return FALSE;
}
// ウィンドウを表示します。
ShowWindow( hWnd, p_iCmdShow );
UpdateWindow( hWnd );
return TRUE;
}
////////////////////////////////////////////////////////////////////
// メッセージループです。
int MessageLoop()
{
BOOL bRes;
MSG stMsg;
// メッセージループです。
while( 1 )
{
// メッセージをキューから取り出します。
bRes = GetMessage( &stMsg, NULL, 0, 0 );
if(
( bRes == 0 ) ||
( bRes == -1 )
)
{
// 終了するのでループから抜けます。
break;
}
// メッセージを変換します。
TranslateMessage( &stMsg );
// ウィンドウプロシージャに送ります。
DispatchMessage( &stMsg );
}
return stMsg.wParam;
}
////////////////////////////////////////////////////////////////////
// ウィンドウプロシージャです。
LRESULT CALLBACK WndProc
( HWND p_hWnd
, UINT p_uiMsg
, WPARAM p_wParam
, LPARAM p_lParam
)
{
// メッセージを処理します。
switch( p_uiMsg )
{
case WM_PAINT:
// 再描画を促されたとき。
// ウィンドウに描画します。
WriteHelloWorld( p_hWnd );
return 0;
case WM_DESTROY:
// xボタンが押されたとき。
// アプリケーションを終了させます。
PostQuitMessage( 0 );
return 0;
}
return DefWindowProc( p_hWnd, p_uiMsg, p_wParam, p_lParam );
}
////////////////////////////////////////////////////////////////////
// Hello World!と出力します。
void WriteHelloWorld( HWND p_hPaintWnd )
{
// 書き込む文字列。
const char *const pchOutput = "Hello, world!";
HDC hPaintDC;
PAINTSTRUCT stPaint;
// デバイスコンテキストへのハンドルを取得します。
hPaintDC = BeginPaint( p_hPaintWnd, &stPaint );
// そのハンドルを通して文字列を出力します。
TextOut( hPaintDC, 0, 0, pchOutput, strlen( pchOutput ) );
// デバイスコンテキストへの描画を終了します。
EndPaint( p_hPaintWnd, &stPaint );
return;
}
// WinMain.c #include <windows.h> #include "WinMain.h" ////////////////////////////////// // グローバル変数。 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(); } //////////////////////////////////////////////////////////////////// // ウィンドウクラスを登録します。 BOOL RegisterWindowClass() { // ウィンドウクラスの情報を格納する構造体です。 WNDCLASS stWndClass; // どんなウィンドウクラスを作るのか、設定をセットします。 stWndClass.style = CS_BYTEALIGNWINDOW | CS_HREDRAW | CS_VREDRAW; stWndClass.lpfnWndProc = WndProc; stWndClass.cbClsExtra = 0; stWndClass.cbWndExtra = 0; stWndClass.hInstance = g_hInstance; stWndClass.hIcon = LoadIcon( NULL, IDI_EXCLAMATION ); stWndClass.hCursor = LoadCursor( NULL, IDC_ARROW ); stWndClass.hbrBackground = (HBRUSH)COLOR_BACKGROUND; stWndClass.lpszMenuName = NULL; stWndClass.lpszClassName = CLASS_NAME; // ウィンドウクラスを登録します。 if( RegisterClass( &stWndClass ) == 0 ) { return FALSE; } return TRUE; } //////////////////////////////////////////////////////////////////// // ウィンドウを作成して表示します。 BOOL CreateAndShowWindow( int p_iCmdShow ) { // ウィンドウハンドルを取っておく変数。 HWND hWnd; // ウィンドウを作製します。 hWnd = CreateWindow ( CLASS_NAME , APP_NAME , WS_OVERLAPPEDWINDOW , CW_USEDEFAULT , 0 , CW_USEDEFAULT , 0 , NULL , NULL , g_hInstance , NULL ); // ウィンドウができたかチェックします。 if( hWnd == NULL ) { return FALSE; } // ウィンドウを表示します。 ShowWindow( hWnd, p_iCmdShow ); UpdateWindow( hWnd ); return TRUE; } //////////////////////////////////////////////////////////////////// // メッセージループです。 int MessageLoop() { BOOL bRes; MSG stMsg; // メッセージループです。 while( 1 ) { // メッセージをキューから取り出します。 bRes = GetMessage( &stMsg, NULL, 0, 0 ); if( ( bRes == 0 ) || ( bRes == -1 ) ) { // 終了するのでループから抜けます。 break; } // メッセージを変換します。 TranslateMessage( &stMsg ); // ウィンドウプロシージャに送ります。 DispatchMessage( &stMsg ); } return stMsg.wParam; } //////////////////////////////////////////////////////////////////// // ウィンドウプロシージャです。 LRESULT CALLBACK WndProc ( HWND p_hWnd , UINT p_uiMsg , WPARAM p_wParam , LPARAM p_lParam ) { // メッセージを処理します。 switch( p_uiMsg ) { case WM_PAINT: // 再描画を促されたとき。 // ウィンドウに描画します。 WriteHelloWorld( p_hWnd ); return 0; case WM_DESTROY: // xボタンが押されたとき。 // アプリケーションを終了させます。 PostQuitMessage( 0 ); return 0; } return DefWindowProc( p_hWnd, p_uiMsg, p_wParam, p_lParam ); } //////////////////////////////////////////////////////////////////// // Hello World!と出力します。 void WriteHelloWorld( HWND p_hPaintWnd ) { // 書き込む文字列。 const char *const pchOutput = "Hello, world!"; HDC hPaintDC; PAINTSTRUCT stPaint; // デバイスコンテキストへのハンドルを取得します。 hPaintDC = BeginPaint( p_hPaintWnd, &stPaint ); // そのハンドルを通して文字列を出力します。 TextOut( hPaintDC, 0, 0, pchOutput, strlen( pchOutput ) ); // デバイスコンテキストへの描画を終了します。 EndPaint( p_hPaintWnd, &stPaint ); return; }
プログラムの入力は以上です。ビルドすればWindowsアプリケーションが完成します。
実行すると以下のようなウィンドウが表示されるでしょう。
このアプリケーションは、ウィンドウを表示し、「Hello World!」と出力する、それだけの機能しか持たないアプリケーションです。
……ってあーた、なんか無茶苦茶長いんでない?
そうです、無茶苦茶長いです。たったこれだけの機能のために長々とプログラムを書く必要があります。
MFCを使わない場合、ただ単に"Hello world!"と出力するためにこれだけのコーディングを行わなければならないのです。だから「"Hello World!"から始めよう!?」なのです。こんな長いアプリケーションが一番簡単なんて……しかも、MFCの時とはまったく違う印象を受けたのではないでしょうか。
ただ、MFCの内部ではこれに似たプログラムが動いています。たとえMFCであっても、Windowsでアプリケーションを動かすためにはこの長々とした処理が必要というわけです。
次回からは、ひとつひとつゆっくりと、このプログラムとMFCを見比べてみることにします。