GetStockObjectで標準GDIオブジェクトを利用する
WindowsのGDI(Graphics Device Interface)では、ペン、ブラシ、フォント、パレットなどを使って画面や印刷デバイスへ描画します。毎回これらを自作する方法もありますが、一般的な色やシステム標準フォントで十分な場面では、GetStockObjectを利用するとコードを簡潔に保てます。
この関数が返すのは、OSが用意している標準GDIオブジェクトへのハンドルです。デバイスコンテキストへ選択して使用するのが基本であり、アプリケーションが作成したオブジェクトとは異なるライフサイクルを持つ点に注意が必要です。関連するAPIの分類や宣言を確認するときは、Windows API資料も参照できます。
| オブジェクト種別 | 代表的な識別子 | 主な用途 | 破棄 |
|---|---|---|---|
| ペン | BLACK_PEN、NULL_PEN |
線や図形の境界線 | 不要 |
| ブラシ | WHITE_BRUSH、HOLLOW_BRUSH |
図形内部の塗りつぶし | 不要 |
| フォント | SYSTEM_FONT、DEFAULT_GUI_FONT |
文字描画 | 不要 |
| パレット | DEFAULT_PALETTE |
パレット対応デバイス | 不要 |
| 動的色のペン・ブラシ | DC_PEN、DC_BRUSH |
デバイスコンテキスト単位の色変更 | 不要 |
関数の役割と戻り値
宣言は次のようになっています。
HGDIOBJ GetStockObject(int fnObject);
引数には、取得したい種類を表すストックオブジェクト識別子を指定します。戻り値は汎用的なHGDIOBJ型ですが、実際にはペンならHPEN、ブラシならHBRUSH、フォントならHFONTとして扱えるため、用途に応じてキャストします。指定値が無効な場合はNULLが返ります。
HBRUSH hBrush =
static_cast<HBRUSH>(GetStockObject(LTGRAY_BRUSH));
if (hBrush != NULL) {
// デバイスコンテキストへ選択して使用する
}
代表的なブラシにはBLACK_BRUSH、DKGRAY_BRUSH、GRAY_BRUSH、LTGRAY_BRUSH、WHITE_BRUSHがあります。塗りつぶしを行わない場合はNULL_BRUSHまたは同義のHOLLOW_BRUSHを選択します。ペンにも同様に黒、白、破線なしのNULL_PENなどが用意されています。
デバイスコンテキストへ選択する
取得したオブジェクトは、通常SelectObjectでデバイスコンテキスト(HDC)へ選択してから描画に使います。選択前のオブジェクトを保存し、処理後に元へ戻すことが重要です。ウィンドウの再描画処理では、別の処理が同じHDCの状態を前提にしている場合があるためです。
void PaintSample(HDC hdc)
{
HBRUSH brush = static_cast<HBRUSH>(
GetStockObject(LTGRAY_BRUSH));
HGDIOBJ oldBrush = SelectObject(hdc, brush);
if (oldBrush != NULL && oldBrush != HGDI_ERROR) {
Rectangle(hdc, 20, 20, 180, 100);
SelectObject(hdc, oldBrush);
}
}
SelectObjectの戻り値は、選択前に設定されていたオブジェクトです。ペンやブラシを切り替えるときは、描画が終わった直後に元へ戻すと、後続処理への副作用を抑えられます。標準オブジェクトそのものを変更するのではなく、あくまでHDCの現在状態を切り替えるという捉え方が適切です。
ペンとブラシの色を扱う
DC_PENとDC_BRUSHは、単一の固定色を持つ通常のストックペンやブラシとは少し性質が異なります。これらを選択したHDCに対し、SetDCPenColorまたはSetDCBrushColorを呼び出すことで、描画色をデバイスコンテキスト単位で変更できます。
HPEN pen = static_cast<HPEN>(GetStockObject(DC_PEN));
HBRUSH brush = static_cast<HBRUSH>(GetStockObject(DC_BRUSH));
HGDIOBJ oldPen = SelectObject(hdc, pen);
HGDIOBJ oldBrush = SelectObject(hdc, brush);
SetDCPenColor(hdc, RGB(30, 90, 180));
SetDCBrushColor(hdc, RGB(220, 235, 250));
Rectangle(hdc, 30, 30, 220, 130);
SelectObject(hdc, oldBrush);
SelectObject(hdc, oldPen);
固定色のBLACK_BRUSHなどは設定変更を必要としないため、単純な描画に向いています。複数の色を場面ごとに使うなら、CreatePenやCreateSolidBrushで専用オブジェクトを作る方法もあります。頻繁な色変更を伴う簡易描画では、DC用オブジェクトがGDIハンドルの増加を抑える選択肢になります。
フォント取得時の注意点
フォント関連では、SYSTEM_FONT、SYSTEM_FIXED_FONT、ANSI_VAR_FONT、ANSI_FIXED_FONT、DEFAULT_GUI_FONTなどが使えます。特にDEFAULT_GUI_FONTは、ダイアログや標準コントロールに近い外観の文字を描画したい場合に便利です。
HFONT font = static_cast<HFONT>(
GetStockObject(DEFAULT_GUI_FONT));
HGDIOBJ oldFont = SelectObject(hdc, font);
TextOutW(hdc, 20, 20, L"標準フォント", 6);
SelectObject(hdc, oldFont);
文字列を正しく表示するには、ANSI系APIとUnicode系APIの選択も関係します。TextOutWを使うなら、文字列や周辺の処理もUnicodeを前提にそろえると変換ミスを避けやすくなります。高DPI環境や多言語表示では、標準フォントのサイズや字形が環境によって変わるため、厳密なレイアウトにはCreateFontなどで属性を明示する設計が向いています。
所有権とエラー処理を整理する
GetStockObjectで得たハンドルは、アプリケーションが生成したGDIオブジェクトではありません。そのため、使用後にDeleteObjectを呼んではいけません。標準オブジェクトを削除しようとすると、予期しない失敗や描画状態の問題につながる可能性があります。削除が必要なのは、原則としてCreatePen、CreateBrushIndirect、CreateFontなどで自分が作成したオブジェクトです。
GDIオブジェクトを選択したまま削除することも避けます。自作オブジェクトでは、まず元のオブジェクトをHDCへ戻し、その後にDeleteObjectを実行します。この所有権の考え方は、ファイルハンドルやセキュリティ記述子を扱うWindows APIにも通じます。たとえばファイル作成時の属性指定については、セキュリティ属性の解説を読むと、APIが受け取る構造体とリソース管理を分けて考える助けになります。
GetStockObjectの失敗時はNULLを確認し、SelectObjectでは戻り値がNULLまたはHGDI_ERRORになっていないかを確認します。描画関数自体が失敗しても、最後に元のオブジェクトを復元する処理は実行できるよう、早期リターンを多用しない構成にすると安全です。標準GDIオブジェクトは、取得、選択、復元、破棄不要という流れを守るだけで、基本的な利用を安定させられます。
実装時は、用途に合う識別子を選び、HDCへ選択したオブジェクトを必ず元へ戻し、GetStockObjectの戻り値を削除しないことを確認します。固定色なら標準ペンやブラシ、色を変更するならDC_PENやDC_BRUSHを使い、自作オブジェクトだけを明示的に破棄する、という区分が実用上の要点です。