Windowsプログラミングをはじめよう

ワードやエクセルのようにWindowsで使用できるプログラムをつくろうと思います。しかし、どのようにしてプログラムをつくればいいのでしょうか?

とりあえずウインドウをつくるためにウインドウを表示させる必要があります。そのために画面にウインドウを表示させるコードをかく必要がありそうです。また、マウスが動いたりクリックした際の動作のコーディングも必要ですね。それから、…とやることがおおくて嫌になります。

ここで活躍するのがOS(ここではWindows)です。

OSには、マウスの制御や画面への出力などよく使う命令をまとめた関数が用意されています。

この関数をAPI(Application Programming Interface)関数といいます。このAPIはWindowsのシステムにDLL(Dynamic Link Library)として入っていて、パスもはじめから通っているので自由に使うことができます。

ここでは、Windows APIを使ったプログラムを作って、Windows用のアプリをつくる方法を学びます。

プロジェクトの作成

では、さっそくWindowsプログラミングをしてみましょう。Visal Studioを立ち上げて、

>>プロジェクトの新規作成

を実行します。そして、

>>Win32プロジェクト

を選択し、名前を付けてOKをおします。ここでは「Sample」という名前のプロジェクトにしました。

アプリケーションウィザードが出てきます。その中のアプリケーションの設定の追加のオプションで

空のプロジェクトにチェックをうちます。

スクリーンショット_010114_105434_AM

すると以下のように空のプロジェクトができます。

スクリーンショット_010114_105743_AM

見ての通りフォルダだけの空のプロジェクトです。空なのでcppファイルを作ります。

>>ソースファイルを右クリック>>追加>>新しい項目

を選択し、C++ファイルを選択し名前をつけてcppファイルをつくります。ここでは、Sample.cppファイルとしました。

スクリーンショット_010114_110134_AM

するとソースファイルにSample.cppが追加されます。

コーディングする

ではSample.cppに以下のようにコーディングして実行してください。うまくいったらウインドウが表示されます。最大化や最小化、そして大きさも変えることができます。

スクリーンショット_010114_111829_AM

 

#include <Windows.h>

LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
ATOM InitApp(HINSTANCE);
BOOL InitInstance(HINSTANCE,int);

TCHAR szClassName[]=TEXT("sample");

int WINAPI WinMain(HINSTANCE hCurInst,HINSTANCE hPrevInst,LPSTR lpsCmdLine,int nCmdShow){
	MSG msg;
	BOOL bRet;

	if(!InitApp(hCurInst))return FALSE;
	if(!InitInstance(hCurInst,nCmdShow))return FALSE;
	while((bRet=GetMessage(&msg,NULL,0,0))!=0){
		if(bRet==-1){
			MessageBox(NULL,TEXT("GetMessageエラー"),TEXT("Error"),MB_OK);
			break;
		}else{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}
	return (int)msg.wParam;
}

ATOM InitApp(HINSTANCE hInst){
	WNDCLASSEX wc;
	wc.cbSize=sizeof(WNDCLASSEX);
	wc.style=CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc=WndProc;
	wc.cbClsExtra=0;
	wc.cbWndExtra=0;
	wc.hInstance=hInst;
	wc.hIcon=(HICON)LoadImage(NULL,
		MAKEINTRESOURCE(IDI_APPLICATION),
		IMAGE_ICON,
		0,
		0,
		LR_DEFAULTSIZE|LR_SHARED);
	wc.hCursor=(HCURSOR)LoadImage(NULL,
		MAKEINTRESOURCE(IDC_ARROW),
		IMAGE_CURSOR,
		0,
		0,
		LR_DEFAULTSIZE|LR_SHARED);
	wc.hbrBackground=(HBRUSH)GetStockObject(BLACK_BRUSH);
	wc.lpszMenuName=NULL;
	wc.lpszClassName=szClassName;
	wc.hIconSm=(HICON)LoadImage(NULL,
		MAKEINTRESOURCE(IDI_APPLICATION),
		IMAGE_ICON,
		0,
		0,
		LR_DEFAULTSIZE|LR_SHARED);
	return (RegisterClassEx(&wc));
}

BOOL InitInstance(HINSTANCE hInst,int nCmdShow){
	HWND hWnd;
	hWnd=CreateWindow(szClassName,
		TEXT("Hello Title"),
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		NULL,
		NULL,
		hInst,
		NULL);
	if(!hWnd)return FALSE;
	ShowWindow(hWnd,nCmdShow);
	UpdateWindow(hWnd);
	return TRUE;
}

LRESULT CALLBACK WndProc(HWND hWnd,UINT msg,WPARAM wp, LPARAM lp){
	switch(msg){
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return (DefWindowProc(hWnd,msg,wp,lp));
	}
	return 0;
}

長いですが順を追って説明していきます。

解説1-WinMAinの引数-

まず、このプログラムでは

int WINAPI WinMain(HINSTANCE hCurInst,
 HINSTANCE hPrevInst,
 LPSTR lpsCmdLine,
 int nCmdShow)

からスタートします。では、引数についてみていきます。

HINSTANCE hCurInst

HINSTANCEとわけのわからない型がでてきました。HINSTANCEはどういう型かというとプログラムの番号を示す型です。Windows側がこのプログラムに番号をつけるわけです。続いて、

HINSTANCE hPrevInst

16ビット時代の名残として残っているものです。32ビットのプログラムでは使用しないので無視してOKです。

LPSTR lpsCmdLine

LP=ポインタ、STR=charの意味なのでLPSTRはchar*だと思ってください。そして、lpsCmdLineはコマンドラインからも命令を使う際に使用します。コマンドラインからのプログラムでないのが通常だと思うので、こちらも使うことはないでしょう。

int cCmdShow

この変数はウインドウの表示状態に関する値です。ウインドウを表示させる際に情報として使います。

解説2-WinMainの中身-

ではWinMainの中身をみていきましょう。WinMainでは以下のような流れでプログラムが進みます。

  1. メインウインドウの定義と登録(InitApp)
  2. ウインドウの作成(InitInstance)
  3. メッセージループの作成

解説3-InitAppの中身 メインウインドウの定義と登録-

メインウインドウの定義と登録はInitAppで行っています。ウインドウの性質を定義するためWNDCLASSEX構造体に情報をかいていきます。

typedef struct{
  UINT cbSize;   //構造体のサイズ
  UINT style;    //クラスのスタイル
  WNDPROC lpfnWndProc; //ウインドウプロシージャ
  int cbClsExtra;
  int cbWndExtra;
  HINSTANCE hInstance; //インスタントハンドル
  HICON hIcon;  //アイコン
  HCURSOR hCursor;//カーソル
  HBRUSH hbrBackground;  //背景色
  LPCTSTR lpszMenuName;  //メニュー名
  LPCTSTR lpszClassName; //クラス名
  HICON hIconSm;         //小さいアイコン
}WNDCLASSEX, *PWNDCLASSEX;
  1. cvSizeは構造体のサイズを指定します。具体的にはsizeof(WNDCLASSEX)となります。
  2. styleにはウインドウのスタイルを指定します。通常CS_HREDRAW|CS_VREDRAWと、CS_HREDRAWは平行方向にウインドウサイズが変わったら再描写するという意味です。CS_VREDRAWは垂直方向です。
  3. lpfnWndProcにはウインドウプロシージャのアドレスを指定します。
  4. hInstanceには、WinMainから取得したhCurInstを指定します。
  5. hIconにはアイコンを指定します。
  6. hCursorにはマウスカーソルを指定します。LoadImageのIDC_ARROWによりマウスカーソルを通常の矢印にしています。IDC_HELP、IDC_CROSS、IDC_WAITなどいろいろなカーソルがあるので試してみてください。
  7. hbrBackgroundには背景を塗りつぶす色を設定します。たとえば、WHITE_BRUSHなら白、BLACK_BRUSHなら黒になります。
  8. lpszMenuNameには、メニューの名前を指定します。
  9. lpszClassNameには、クラスの名前を指定します。

WNDCLASSEX構造体ができたらRegisterClassEx関数を使ってウインドウを登録します。

ATOM RegisterClassEx(CONST WNDCLASSEX* lpwcx);

ATOMは整数値で関数が失敗すると0を返します。

解説4-InitInstance ウインドウの作成-

ウインドウの登録が済んだので、ウインドウを作成します。ウインドウの作成にはCreateWindow関数を使います。

HWND CreateWindow(
  LPCTSTR lpClassName,
  LPCTSTR lpWindowName,
  DWORD dwStyle,
  int x,int y,int nWidth,int nHeight,
  HWND hWndParent,
  HMENU hMenu,
  HINSTANCE hInstance,
  LPVOID lpParam
);
  1. lpClassNameには、WNDCLASSEX構造体のlpszClassNameで指定したウインドウクラス名を指定します。
  2. lpWindowNameにはウインドウの名前を指定します。これがウインドウに表示されるタイトルとなります。
  3. dwStyleにはウインドウスタイルを指定します。WS_OVERLAPPEDWINDOWを指定すれば、サイズ変更可能、最大化・最小化をもつウインドウとなります。
  4. x,yはウインドウの表示位置で、nWidth,nHeightはウインドウの幅と高さです。
  5. hWndParentには親ウインドウのハンドルを指定します。ここでは親はないのでNULLです。
  6. hMenuにはメニューを指定します。
  7. hInstanceにはインスタンスハンドルを指定します。
  8. lpParamはNULLにします。

ウインドウの作成が済んだので、ShowWindowで表示します。

BOOL ShowWindow(
  HWND hWnd,
  int nCmdShow
);
  1. hWndにはウインドウハンドルを指定します。
  2. nCmdShowには表示状態を指定します。

解説5-メッセージループの作成-

ウインドウの作成が済んだら、ユーザーからの命令を待ちます。

	while((bRet=GetMessage(&msg,NULL,0,0))!=0){
		if(bRet==-1){
			MessageBox(NULL,TEXT("GetMessageエラー"),TEXT("Error"),MB_OK);
			break;
		}else{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}

命令はGetMessage関数でうけとります。

この関数の戻り値は

  • WM_QUITという終了メッセージを受けると0
  • 関数が失敗すると-1
  • WM_QUIT以外のメッセージなら0と-1以外の値

となります。関数が失敗することはほとんどないので、-1を受け取る確率は低いでしょう。

そして、メッセージはlpMsgで指定した場所に格納されます。

受け取ったメッセージはTranslateMessage関数で仮想キーメッセージから文字メッセージへ変換されます。そして、DispatchMessage関数でメッセージをウインドウプロシージャへ送ります。

解説6- ウインドウプロシージャ

ウインドウプロシージャWndProcでユーザーからの命令を処理します。

LRESULT CALLBACK WndProc(HWND hWnd,UINT msg,WPARAM wp, LPARAM lp){
	switch(msg){
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return (DefWindowProc(hWnd,msg,wp,lp));
	}
	return 0;
}

ウインドウが閉じられたときは、WM_DESTROYをメッセージとして受け取ります。このメッセージがきたら

PostQuitMessage(0);

これにより、WM_QUITメッセージが送られ、プログラムが終了します。

そして、メッセージに対して自分で処理をする場合は0を返します。処理をシステムに任せる場合はDefWindowProg関数を使って処理をします。

以上で解説を終わります。長くなりましたが、これでC言語のレベルからWindows APIを用いてウインドウズのプログラムを作れるようになりました。プログラムの仕組みがある程度、理解できたら十分です。

著者:安井 真人(やすい まさと)