2013. 6.27.
石立 喬

Visual Studio 2012 Expressの易しい使い方(3)

――― Win32 アプリケーションを始める ――

 Visual Studio 2012 ExpressのVisual C++を使った、コンソールアプリケーションとWindowsフォームアプリケーションについては、既に簡単に述べた。これらは、旧バージョン用の解説を参照すれば、十分使いこなせるものと思われる。残りは、Win32プロジェクトである。これは、Wiondows API (Application Program Interface)を使うもので、従来述べて来なかった。なぜならば、より詳細なWindowsプログラムの知識が必要になるからであった。
 しかし、Visual Studio 2012 ExpressのVisual C++のテンプレートを使用し、必要な箇所だけコードを修正、追加する方法を用いれば、比較的簡単にWin32プロジェクトを作成できる。したがって、Windowsプログラムに興味があり、学習したい人には、良い機会と思われる。

Win32 アプリケーションのスケルトン作成方法
1)スタート画面から、「新しいプロジェクト」を選択する。
2)「新しいプロジェクト」ウインドウで、左側のテンプレートから、「Visual C++」→「Win32」を選択する。
3)右欄で、「Win32 プロジェクト」を選択し、「OK」をクリックする。
4)「Win32 アプリケーションウイザード」が開くので、「Windows アプリケーション」になっているのを確認して、「次へ」をクリックする。
5)次の画面では、デフォルトのまま(「空のプロジェクト」にチェックをつけない)「完了」をクリックする。

できたスケルトンの内容
 プロジェクト名は、デフォルトで「Win32Project1」となっており、図1に示すように、「Win32Project1.cpp」など、「Win32Project1」の一式のファイルができている。このまま、「ビルド」→「ソリューションのビルド」を行い、正常終了を確認して、「デバッグ」→「デバッグなしで開始」を実行すると、図2に示すウインドウが現れる。図は、ウインドウのサイズをドラッグで縮小し、終了のために、「ファイル」メニューを選択したところである。


図1 できたスケルトンの内容



図2 Win32アプリケーションのスケルトンを実行した結果


Win32Project1.cppの内容
 この段階では、まだスケルトンの状態であるが、
    int APIENTRY _tWinMain -------- 最初に実行されるエントリポイント
    ATOM MyRegisterClass ---------- ウインドウの構成を登録
    BOOL InitInstance ------------- メインウインドウを作成
    LRESULT CALLBACK WndProc ------ メッセージの処理を記述する、プログラムの主要部分
    INT_PTR CALLBACK About -------- バージョン情報
ができている。

ウインドウの背景色の設定
 ウインドウの背景色は、デフォルトで白になっているが、必要に応じて、MyRegisterClassで変更することができる。図3に示す通り(78行〜79行参照)、デフォルトの設定を変更して、レモン色にした。これにより、文字の背景や図形の内部が、デフォルトでは白色に設定されていることが確認できる。


図3 ウインドウの背景色を変更した


ウインドウのタイトルとサイズの設定
 ウインドウの初期サイズを決めたり、タイトルバーに名称を入れたりするには、InitInstanceの中にあるCreateWindowの引数の一部を、下記のように書き換える(変更する部分を→で示す)。

   szWindowClass(クラス名)
   szTitle(タイトル)------------------------------ → L"初めてのWin32プログラム"
   WS_OVERLAPPEDWINDOW(ウインドウスタイル)
   CW_USEDEFAULT(ウインドウの開く位置(X軸))
   0(ウインドウの開く位置(Y軸))
   CW_USEDEFAULT(ウインドウの幅(外形)の初期値 --- → 600(一例)
   0(ウインドウの高さ(外形)の初期値 ------------- → 400(一例)
   NULL(親ウインドウのハンドル)
   NULL(メニューウインドウのハンドル)
   hInstance(インスタンスハンドル)
   NULL(ウインドウ作成パラメータ)

 書き換えた状態を図4に示す。


図4 Windowのタイトルとサイズを変更した


描画プログラムはWndProcのcase WS_PAINTに記述する
 文字を画面に表示する場合も、図形を描画する場合も、WS_PAINTメッセージによって実行される。WS_PAINTで実行されるプログラムは、WndProcの中のcase WM_PAINT内に記述する。case文の内部では、変数の宣言ができないので、WndProcの最初の部分に、必要なペンとブラシのハンドルを宣言しておく。
 図5の宣言部で、hUserPenは、ユーザが、任意に太さや色を設定して使用するペン、hUserBrushは、ユーザがパターンや色を設定して使用するブラシ、hDefPenは、デフォルトで用意されているペン(太さ1で黒)を退避させるためのもの、hDefBrushは、同じくデフォルトのブラシ(無地の白)を退避させるためのもの、hNullPenは、図形の周辺を描画させないための空のペン、hNullBrushは、図形の中身を塗りつぶさせないための空のブラシを、それぞれ設定しておくためのものである。


図5 ペンとブラシのハンドルを宣言したWndProc関数の上部


文字の描画(図6の158行〜167行参照)
 白地の背景に黒字で文字を描く場合には、TextOut関数のみで、簡単に文字を描くことができる。しかしその他の場合には、あらかじめ、文字色をSetTextColorで、背景色をSetBkColorで設定しておく必要がある。背景色を透明にすることもでき、その場合はSetBkModeでTRANSPARENTを設定する。
 文字の出力には、TextOut(デバイスコンテキストのハンドル、X座標、Y座標、文字、文字列の長さ)を用いる。デフォルトで、文字コードはUnicodeになっているので、それに合わせて、文字列の前にLを付けておく。文字列の長さは、直接入力してもよい。Unicode文字列の長さを返す関数wcslenは、wide character string length の意味である。

点を打つ(図6の169行〜171行参照)
 点を打つには、SetPixel(デバイスコンテキストのハンドル、X座標、Y座標、色)を用いる。ペンの設定は必要なく、点を打つ都度、色を指定する。

線を引く(図6の173行〜182行参照)
 線の起点をMoveToEx(ハンドル、X座標、Y座標、旧座標のポインタ)で指定する。旧座標のポインタは、古い座標にさかのぼる必要がある場合に使用するもので、その必要がない場合には、NULLとしておく。線の終点は、LineTo(ハンドル、X座標、Y座標)で指定する。ただし、終点は、次のLineTo命令の起点とはなるが、最終点への描画はされない。
 原則的に、線を引く前には、使用するペンをCreatePen(ペンのタイプ、太さ、色)で作成し、SelectObjectで設定しておく必要があるが、デフォルトペンを使用する場合には、その必要がない。


図6 文字、点、線の描画プログラム


ここで、ペンとブラシの設定について
 
白地の背景に黒で線(1ピクセル幅)を描いたり、白地の背景に黒で重なりのない図形(塗りつぶさない)を描いたりする場合には、ペンやブラシを気にしないで良い。なぜなら、あらかじめ設定済みのデフォルトペンやブラシが、そのまま使えるからである。
 しかし、それ以外は、ペンとブラシの操作が付きまとう。図形の周辺と線はユーザが設定するペンで、図形の内部はユーザが設定するブラシで描画しなければいけないからである。
 この場合には、以下の操作を行う(ペンの場合)。
  1)CreatePenで、ユーザペンhUserPen(ペンのハンドル)を作る。
  2)SelectObjectで、hUserPenを設定すると、それまで使っていたペンのハンドルが返されるので、もし、それがデフォルトペンであった場合には、hDefPenとしてしまっておく。
  3)ユーザペンで描画する。
  4)SelectObjectで、デフォルトペンhDefPenに設定を戻す。
  5)DeleteObjectで、使い終わったユーザペンhUserPenを破棄する。
 ただし、デフォルトペンが、直ちに必要でない場合には、4)と5)は省略しても良い。
 これは、ブラシについても同様である。
 図5で、HGDIOBJ型は、GDI(Graphic Device Interface)のオブジェクトのハンドルの意味で、HPENとHBRUSHIを含めることができるので、HPENやHBRUSHを個別に作成する必要はない。

デフォルトで図形(四辺形、円)を描く(図7の184行〜189行参照)
 ペンやブラシを、特に設定しないで、Rectangleや、Ellipseを用いると、外形が黒で、内部が白で塗りつぶされた図形が描画される。ウインドウの背景色が白で、図形に重なりがない場合には、これで十分である。

塗りつぶし図形(四辺形、円)を描く(図7の191行〜202行参照)
 あらかじめ空のペンを用意しておき(図7の192行〜194行)、ユーザ設定のブラシと共にに使用すると、外形が別の色で描画されない図形を描くことができる。
 ペンとブラシを同一色で設定して、図形を描いても良いが、ペンとブラシを同時に新しく設定する必要があり、どちらが簡単かは、ケースバイケースである。


図7 デフォルトと塗りつぶしの図形の描画プログラム


内部が透明な図形(四辺形、円)を描く
 あらかじめ空のブラシを用意しておき(図8の205行〜207行)、通常のペンだけを設定すると、内部が透明な図形を描くことができる(図8の208行〜215行参照)。


図8 内部が塗りつぶされない図形の描画プログラム


 図8で、217行のEndPaint()は、case WM_PAINTの初めのBeginPaint()に対応しており、描画に用いていたハンドルを返して開放するものである。219行のbreakは、case WM_PAINTの終了を意味する。

得られた結果
 図9は、最終的に得られた結果を示す。ウインドウ上には、左上に文字が出力されていて、上から、デフォルトの黒文字と白背景、色設定文字と白背景、色設定文字と透明背景が二つである。
 右には、小さい赤の点(見えにくいが)と、その下に線が引かれている。上の線はデフォルトペンを使用し、下の線は、ユーザ設定のペンを使用している。図形では、左は、デフォルトペンとデフォルトブラシを用いた四辺形と円、中央は、空のペンを用いた塗りつぶし四辺形と円、右は、空のブラシを用いた、中身が透明の四辺形と円である。


図9 得られた結果


表記法について
 テンプレート(ひな形)で与えられたプログラムの一部を修正、追加してプログラムを作成する場合、元のテンプレートの表記法に従わないと、プログラムが不揃いで、読みにくくなる。
 ここでは、ハンガリアン記法とも呼ばれ、変数の頭に、型を表す小文字を付ける方法が使われているので、それに倣った。小文字の後に、頭が大文字の変数名が来るのは、ラクダ記法とも呼ばれる(背中が高く盛り上がっているので)。

Unicodeについて
 Visual Studio 2012 では、デフォルトで、Unicodeを扱うようになっている。Unicode を使用しないときの対応として、_T("文字列")や_tcslenを使うのは良いと思うが、ここでは、Unicode専用に統一した。

メモリーリーク(Memory Leak)とガーベージコレクション(Garbage Collection)について
 メモリーリークは、古くからプログラマの間で注意すべき事項として知られている。使い終わった資源(メモリー領域)は、なるべく、速やかに解放しないと、有効なメモリー領域が漏れ出して減ってしまい(リークする)、動作不能になる、と言うものである。
 それに対し、プログラム言語によっては、自動的に回避する機能を備えているものがある。これが、ガーベージコレクション(ごみ収集)で、たとえば、.NET Framework では、ペンを設定するのに、
    Pen^ pen1=gcnew Pen(Color::Red);
などを用いている。「gc」は、Garbage Collection の意味である。この場合には、プログラム上での解放は必要ない。
 Win32 API には、ガーベージコレクション機能がないので、プログラマーが、破棄コードを記述する必要がある。ただし、ループを用いて、異なったペンやブラシを大量に生成させる場合以外は、原則的に、作成したオブジェクトの破棄は必要ない。しかし、プログラマーに正しい礼儀作法を教える躾(しつけ)としては、やはり、この話題は、避けて通れない。
 図10は、デフォルトペンとデフォルトブラシを元に戻し、ユーザが設定したオブジェクトを破棄するプログラムである。


図10 丁寧なプログラム