TextOutは使えません
TextOutは,最もシンプルな文字列描画APIです。まず文字を表示するサンプルとしてTextOutが紹介されることが多いのですが、Windows MobileにはTextOutがありません。
その代わりExtTextOutを使用します。ExtTextOutはTextOutの拡張版になります。
ExtTextOut
現在選択されているフォント、背景色、および文字の色を使ってテキストを描画します。必要に応じて、クリッピングしたり不透明にするための長方形領域を指定することもできます。
BOOL ExtTextOut(
HDC hdc, // デバイスコンテキストのハンドル
int X, // 開始位置(基準点)の x 座標
int Y, // 開始位置(基準点)の y 座標
UINT fuOptions, // 長方形領域の使い方のオプション
CONST RECT *lprc, // 長方形領域の入った構造体へのポインタ
LPCTSTR lpString, // 文字列
UINT cbCount, // 文字数
CONST INT *lpDx // 文字間隔の入った配列
);
hdcにはデバイスコンテキストのハンドルを指定します。
x,yにはそれぞれ論理x座標、論理y座標を指定します。文字列を囲む長方形の左上を基準とします。
この座標系はWindowの左上を原点0とし、右方向へ+x,下方向へ+yとなっています。(数学でよく使ったxy座標系からは、y軸の+-が反転しています)
fuOptionsは長方形領域の使い方をオプションで指定します。このパラメータは省略可能です。
lpString に文字列を指定します。
cbCount には文字列に格納されている文字の数を指定します。
lpDx は文字間隔の入った配列です。NULL を指定すると、既定の文字間隔を使います。
デバイスコンテキスト
ExtTextOut関数のhdcに指定するデバイスコンテキストとは何でしょうか。これは描画デバイスの情報を管理している値です。描画デバイスにはプリンタ、プロッタ、ディスプレイ・・・と様々なデバイスがあります。プリンタに出力する場合はプリンタのデバイスコンテキストを取得し描画を行い、ディスプレイに出力する場合はディスプレイのデバイスコンテキストを取得して描画を行います。描画の方法はGDI(Graphics Device Interface)によって抽象化されているので、プリンタに描画するときもディスプレイに描画するときも同じ関数を使用することができます。便利ですね。しかしWindowsMobileの場合は描画デバイスといえばほとんどがディスプレイになるでしょうね。
デバイスコンテキストの取得
ディスプレイのデバイスコンテキストを取得するには、GetDC関数を使います。
GetDC関数
指定されたウィンドウのクライアント領域を取得します。
HDC GetDC(
HWND hWnd // ウィンドウのハンドル
);
hWnd デバイスコンテキストの取得対象となるウィンドウのハンドルを指定します。NULL を指定すると、GetDC は画面全体を表すデバイスコンテキストを取得します。
デバイスコンテキストはHDC型の戻り値として返されます。画面に何か描画を行う関数はこの値を必要とするので、GetDCの戻り値を渡します。
デバイスコンテキストの開放
取得したデバイスコンテキストは、いらなくなったら開放してあげなくてはなりません。
ReleaseDC関数
デバイスコンテキストを解放し、他のアプリケーションから使えるようにします。
int ReleaseDC(
HWND hWnd, // ウィンドウのハンドル
HDC hDC // デバイスコンテキストのハンドル
);
描画の準備
BeginPaint関数は、指定されたウィンドウに対して描画の準備をします。
HDC BeginPaint(
HWND hwnd, // ウィンドウのハンドル
LPPAINTSTRUCT lpPaint // 描画情報を持つ構造体へのポインタ
);
hwnd 再描画するウィンドウのハンドルを識別します。
lpPaint 描画情報を受け取る PAINTSTRUCT 構造体へのポインタです。
PAINTSTRUCT 構造体 ウィンドウのクライアント領域を描画するときに使うことのできる情報を保持します。
typedef struct tagPAINTSTRUCT {
HDC hdc; // 描画に使われるデバイス コンテキストを識別します。
BOOL fErase; // 背景を再描画する必要があるかどうかを指定します。
RECT rcPaint; // 描画が要求されている四角形の左上隅と右下隅を指定します。
BOOL fRestore; // Windows が内部的に使います。
BOOL fIncUpdate; // Windows が内部的に使います。
BYTE rgbReserved[16];// Windows が内部的に使う予約されているメモリ ブロックです。
} PAINTSTRUCT;
描画の終了
EndPaint関数は指定されたウィンドウ内の描画の終わりを示します。
BeginPaint 関数を呼び出したら必ず描画の最後でEndPaint 関数を呼び出さなければなりません。
EndPaint関数
指定されたウィンドウ内の描画の終わりを示します。
BOOL EndPaint(
HWND hWnd, // ウィンドウのハンドル
CONST PAINTSTRUCT *lpPaint // 描画データ
);
では、画面に
「Windows Mobileで最初の文字列を表示する」と「WindowsMobile Win32APIプログラミング」という文字を表示するプログラムを作ってみます。
// text002.cpp
//
// Windowに文字を表示する
#include <windows.h>
#include <tchar.h>
#include <strsafe.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
ATOM InitApp(HINSTANCE);
HWND InitInstance(HINSTANCE, int);
WCHAR szClassName[] = _T("win02"); // ウィンドウクラス。UNICODEとしての文字列定数
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine,int nShowCmd)
{
MSG msg;
BOOL bRet;
HWND hWnd;
HDC hdc; // デバイスコンテキストのハンドル
LPTSTR lpszStr= _T("Windows Mobileで最初の文字列を表示する");
size_t size; // 文字列のサイズを格納する
StringCchLength(lpszStr,STRSAFE_MAX_CCH,&size); // 安全な文字列取得
if (!InitApp(hInstance))
return FALSE;
if (!(hWnd = InitInstance(hInstance,nShowCmd)))
return FALSE;
// 「最初の文字列」を描画
hdc = GetDC(hWnd);
ExtTextOut(
hdc, // デバイスコンテキストのハンドル
0, // 開始位置(基準点)の x 座標
0, // 開始位置(基準点)の y 座標
NULL, // 長方形領域の使い方のオプション
NULL, // 長方形領域の入った構造体へのポインタ
lpszStr,// 文字列
size, // 文字数
NULL // 文字間隔の入った配列
);
while((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) {
if (bRet == -1){
break;
} else {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int)msg.wParam;
}
// ウィンドウクラスの登録
ATOM InitApp(HINSTANCE hInst)
{
WNDCLASS wc;
wc.style = CS_HREDRAW|CS_VREDRAW;
wc.lpfnWndProc = WndProc; // プロシージャ名
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInst;
wc.hIcon = NULL; // 未サポート
wc.hCursor = NULL; // 未サポート
wc.hbrBackground= (HBRUSH) COLOR_WINDOW;
wc.lpszMenuName = NULL; // 未サポート
wc.lpszClassName=(LPCTSTR) szClassName;
return (RegisterClass(&wc));
}
// ウィンドウの生成
HWND InitInstance(HINSTANCE hInst, int nShowCmd)
{
HWND hWnd;
hWnd = CreateWindow(szClassName,_T("Window Title"),
WS_CLIPCHILDREN, // ウィンドウの種類
CW_USEDEFAULT, // x座標
CW_USEDEFAULT, // y座標
CW_USEDEFAULT, // 幅
CW_USEDEFAULT, // 高さ
NULL, // 親ウィンドウのハンドル。親を作るのでNULL
NULL, // メニューハンドルまたは子ウィンドウID
hInst, // インスタンスハンドル
NULL);
if (!hWnd)
return FALSE;
ShowWindow(hWnd, nShowCmd);
UpdateWindow(hWnd);
return hWnd;
}
// ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
PAINTSTRUCT ps;
HDC hdc;
LPTSTR lpszStr= _T("WindowsMobile Win32APIプログラミング");
size_t size; // 文字列のサイズを格納する
StringCchLength(lpszStr,STRSAFE_MAX_CCH,&size); // 安全な文字列取得
switch (msg){
case WM_PAINT:
hdc = BeginPaint(hWnd,&ps); // 描画処理を開始します。
ExtTextOut(
hdc, // デバイスコンテキストのハンドル
0, // 開始位置(基準点)の x 座標
20, // 開始位置(基準点)の y 座標
NULL, // 長方形領域の使い方のオプション
NULL, // 長方形領域の入った構造体へのポインタ
lpszStr,// 文字列
size, // 文字数
NULL // 文字間隔の入った配列
);
EndPaint(hWnd,&ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return (DefWindowProc(hWnd, msg, wp, lp));
}
return 0;
}
実行すると、ウィンドウに文字が表示されます。
最初の文字「Windows Mobileで最初の文字列を表示する」は、WinMain関数内で描画し、「WindowsMobile Win32APIプログラミング」の文字はウインドウプロシージャWndProc内で描画しています。その違いを見てみましょう。
「Windows Mobileで最初の文字列を表示する」は、他の画面に一度隠されると、その部分が消えてしまいます。これは、WinMain関数内でウィンドウ生成後に1度しか描画していないためです。では「WindowsMobile Win32APIプログラミング」の文字はなぜ消えないのでしょうか。
Windowsではウィンドウの一部、または全部に書き直しの必要が生じた場合には、WM_PAINTというメッセージを発行します。ウィンドウプロシージャ内でこのメッセージを受け取った場合は、再度描画処理を行う必要があります。「WindowsMobile Win32APIプログラミング」の文字は、WndProc内のswitch文でWM_PAINTメッセージがきたら文字を描画するようになっているため、文字列が他の画面に隠されても再度描画を行っているので文字が消えないのです。
今日はここまで。