ここからは少しスピードダウンです。まぁのんびり見ていきましょう。
|
////////////////////////////////////////////////////////////////////
// Hello World! と書き込みます。
void WriteHello( HWND p_hPaintWnd )
{
HDC hPaintDC;
PAINTSTRUCT stPaint;
char chHello[] = "Hello, world!";
hPaintDC = BeginPaint( p_hPaintWnd, &stPaint );
TextOut( hPaintDC, 0, 0, chHello, strlen( chHello ) );
EndPaint( p_hPaintWnd, &stPaint );
return;
}
|
WM_PAINT
さて、前回見たように、今回のWriteHello()という関数は、ウィンドウにWM_PAINTが送られてきた時に呼び出すようにしています。WM_PAINTメッセージは、ウィンドウを描画もしくは再描画しなければならないときに送られてきます。基本的には最大化したり最小化したときに送られてきます。 渡している引数は、メッセージが送られてきた(つまり再描画するように命令された)ウィンドウのハンドルです。ウィンドウはウィンドウズが管理しているため、ウィンドウを操作するためには「ハンドル」が必要になるわけです。 ちなみに「ハンドル」と呼ばれるものはHWNDのようにHがプレフィックスとして付いています。サイズはUINTと同じく32ビットです。一応voidポインタ、つまり何にでも使えるポインタみたいです。 |
ちょっと余談
触れてませんでしたが、C++とCとでの違いについて。 まず変数。変数は必ず関数の最初で宣言します。C++の場合にはどこでもいいんですけどね。これ忘れると無茶苦茶な数のエラーが発生するので気を付けましょう。 もひとつ、コメントについて。C++では//ですが、Cでは本当は/*から*/までがコメントアウトされます。ただ、オプションで//も使えます。まぁ気にしなくていいでしょう。
ああ、本当に余談だ……。 |
デバイスコンテキストを取得しよう
WM_PAINTが送られてきたということで、ウィンドウを描画しなければなりません。こういう時にはまずBeginPaint()というAPIを呼び出します。この関数にウィンドウハンドルを渡すと、渡したウィンドウにくっついたデバイスコンテキストのハンドルが返ってきます。
ではデバイスコンテキストとはなんなのでしょうか。簡単に説明すると画材と画用紙のセットのようなものです。
注意しなければいけないのは、この画材は「一種類につきひとつ」ということです。例えばあるデバイスコンテキストに「MS ゴシック」というフォントが使われていたとします。この時点で文字を描き込むと当然「MS ゴシック」が使われます。 |
ハンドルずくし!
実は、ウィンドウと同じく、デバイスコンテキストもウィンドウズに管理され、ハンドルによってのみ操作できるのです。BeginPaint()で返ってくるのはハンドルであって、デバイスコンテキストそのものではありません。例によって、デバイスコンテキストはどこか手の届かない場所に作製されています。
さらに、実は「画材」、つまりGDIオブジェクトもウィンドウズに管理され、ハンドルによってのみ操作できるのです! 例えば、前述の「MS ゴシック」というフォント。こういう「画材」が初めから存在しているわけではありません。なぜかというと、フォントの情報にはフォント名だけではなく、文字の幅だとか高さだとか、ボールドにするかとかアンダーラインを引くかとかそういった情報も含まれるからです。こういったものを含めて「フォント」という「画材」になるわけです。
注意しなければならないのは、こういった「画材」はウィンドウズ全体でグローバルなものだということです。フォントを作製したら、そのフォントをウィンドウズがずっと持っています。たとえアプリケーションが終了しても。つまり、作製したら削除しなければいけません。
ただし、DeleteObject()を使う時には注意が必要です。もしそのGDIオブジェクトを他のアプリケーションが使用していた場合には、これもまたまずいです。例えば、他のウィンドウのフォントをSelectObject()を使用して他のフォントに換えたとしましょう。この関数を使うと、それまで使われていたGDIオブジェクトが返ってきます。これが「入れ換える」という意味です。
こういったミスをしないためには「GDIオブジェクトをCreateする>SelectObject()で入れ換える>描画する>またSelectObject()を使って元に戻す>CreateしたGDIオブジェクトを削除する」という手順を踏むことです。めんどくさーいって感じですが、実は似たような手順をデバイスコンテキスト以外でも使うので、慣れておきましょう。
この辺の操作はいまいち難しいので、気を付けて扱うようにしましょう。ちなみに筆者の例を言うと、ウィンドウズが所持するイメージリストというのがあって、その中にアイコンとかが入っているんですが、このイメージリストを削除しちゃいました。そしたらスタートメニューのアイコンが消えちゃった(泣)。 |
TextOut()
さて、文字の描画にはTextOut()というAPIを使用します。第1引数には、文字を描き込むデバイスコンテキストのハンドルを渡します。第2、第3引数は描画する位置、第4引数には書き込む文字列、第5引数にはその文字列の長さを渡します。 つまり、フォントや文字の色などは全然指定されていません。こういった「画材」は前述したように事前に入れ換えておかなければならないということです。 ちなみに、文字列についての説明は次回行う予定です。 |
EndPaint()
描画をしたら、EndPaint()を呼び出して「描画が終了しましたよー」と知らせる必要があります。それだけです。 ちなみにPAINTSTRUCT構造体へのポインタを渡していますが、この構造体は基本的に使わないので気にしないでください。 |
MFCと「オブジェクト指向」
MFCでは、WM_PAINTメッセージのハンドラ関数はCWnd::OnPaint()と決まっています。このハンドラを作製すると、必ず関数の最初に「CPaintDC dc(this); // 描画用のデバイス コンテキスト」という行が書かれています。 BeginPaint()なんかはどこに行ったのかというと、実はCPaintDCというクラスの中に入っています。
クラスには「コンストラクタ」と「デストラクタ」という便利な機能があります。「コンストラクタ」はそのクラスの型の変数が宣言されたときに呼び出される関数で、逆に「デストラクタ」は変数が破棄されたときに呼び出される関数です。
で、実際に文字の描画はどのようにするかというと、CDCクラスを使用します。CPaintDCはCDCから派生しているので、CDCの機能をそのまま使うことができます。で、CDCの中に、デバイスコンテキストへのハンドルが入っています。
さらに「画材」つまりGDIオブジェクトも、同じ機能を持つクラスが存在します。ペンならCPen、フォントならCFontといった具合です。これらのクラスはGDIオブジェクトのハンドルを持ち、そのハンドルを使用するAPIと同名のメンバ関数を持っているわけです。
C++言語は「オブジェクト指向な言語」と一応言われます。「オブジェクト指向」とは言ってみれば「物」中心のシステムです。
これをC++の「クラス」に当てはめてみましょう。ウィンドウへのハンドルも、デバイスコンテキストへのハンドルも、GDIオブジェクトへのハンドルも、すべて同じハンドルです。簡単な型キャストで他のハンドルへと変わってしまいます。もし、ウィンドウへのハンドルをTextOut()に渡してしまったら……。
とはいえ、オブジェクト指向にも問題点はあります。例えば、クラスにハンドルを割り当てるにはAttach()というメンバ関数を使用します。ハンドルを割り当てたあと、このクラスが破棄されたときにデストラクタが呼び出されますが、実は自動的にDeleteObject()が呼び出され、オブジェクトが破棄されてしまうのです。これを防ぐためには、破棄される前にDetach()というメンバ関数を呼び出さなければなりません。
ハンドルを扱うクラスは、あくまでハンドルを持っているだけです。ですが、上のようなクラスの機能を考えると、このようなクラスはオブジェクトそのもの(つまりウィンドウであったりデバイスコンテキストであったり)として見なされているように見えます。
筆者としては、クラスをできる限り使用することを奨めます。AppWizardを使用してアプリケーションを作製する限り、MFCの呪縛から逃れることはできません。ハンドルとクラスの混在は非常にバグを生み出しやすい環境だと言えるので、統一するのであればクラスの方、というわけです。 |
今回は文字ばっかりやねぇ……
なんかごたく並べてるだけでなんの役にも立ってないような今回ですが、いかがだったでしょう。次回はひーっCString使ってくれないと文字列操作できないよぉ!!という方々のためにその辺の部分を見ていきたいと思います。 で、次回で前半が終了。後半はメニューやダイアログ、アイコンといった「リソース」について見ていこうかなと思っています。
やっぱり、なんか盛り下がっていくなぁ……。 |
(C)KAB-studio 1997, 1998 ALL RIGHTS RESERVED. |