GetDCとReleaseDCでデバイスコンテキストを取得・解放する
一時的なGDIの描画環境が必要なときは、GetDCでウィンドウのクライアント領域を表すデバイスコンテキスト(DC)を取得し、ReleaseDCで同じ対象へ返します。DCは描画先だけでなく、選択中のフォントや線種なども保持するため、取得と解放を正しく対にすることが重要です。
この方式は、入力イベントやタイマーの処理から、その場で図形や文字を描く場合に使います。一方、ウィンドウ再描画を表すWM_PAINTでは、更新領域を扱えるBeginPaintとEndPaintが適合します。使い分けを誤ると、再描画時に内容が消えたり、DCを取得したまま放置したりします。
DCの役割と取得する領域
デバイスコンテキストは、GDIのLineToやTextOutなどの描画命令が宛先とする描画環境をまとめます。選択オブジェクトやクリップ領域などの状態も持つため、単なる「書き込み先の番号」ではなく、現在の描画設定を保持する資源だと考えると扱いやすくなります。DCの基本概念と関連関数はWindows API全般で確認できます。
GetDCへウィンドウハンドルを渡すと、そのウィンドウのクライアント領域を対象とするDCが得られます。ウィンドウハンドルをNULLにすると画面全体を対象とするDCになります。ReleaseDCでは、取得時と同じウィンドウハンドルを指定する必要があり、画面全体を取得した場合はNULLを渡します。DCは短い処理内で使い、不要になった直後に解放するのが基本です。
GetDCで一時的なDCを取得する
GetDCは、対象ウィンドウのクライアント領域绘制用のHDCを返します。取得できない場合はNULLになるため、描画処理へ進む前に必ず確認します。戻り値はGDIのDCハンドルであり、ファイルハンドルCommonsenseのようにCloseHandleで閉じる対象ではありません。
HDC hdc = ::GetDC(hWnd);
if (hdc == nullptr) {
return;
}
// ここにGDIの描画処理を書く
::ReleaseDC(hWnd, hdc);
GetDCは更新領域を検証するAPIではありません。そのためWM_PAINTの中では、原則としてBeginPaintとEndPaintを使います。また、GetDCはコモンコントロールのDCを取得できません。コモンコントロールを扱う必要がある場合は、GetWindowDCによってウィンドウ全体を対象とするDCを取得します。
ReleaseDCで取得分を必ず戻す
ReleaseDCは、GetDCで取得したDCをウィンドウへ返します。成功すると1、失敗すると0が返ります。取得に成功したDCは、解放漏れなく1回だけReleaseDCへ渡す必要がありますが、解放結果が0でも同じDCを無条件に再解放することはできません。
DCはGDIの資源であり、CloseHandleでは解放できません。また、ReleaseDCはSelectObjectで選んだフォントやブラシを削除したり、元の選択状態へ戻したりまで行うañosません。選択を切り替えた場合は元のオブジェクトへ戻し、その後に生成したフォントなどをDeleteObjectで破棄してください。BeginPaintで得たDCにはEndPaintを使います。
描画目的ごとのDC使い分け
DCの取得方法は、描く領域と描画の Wab目的によって変わります。WM_PAINT에서는更新領域を伴うBeginPaintが基本で、入力直後の即時描画にはGetDCが適します。罫線やタイトルバーなど非クライアント領域も描く場合は、GetWindowDCを選ぶ必要があります。
| API | 主な対象 | 主な用途 | 解放の手段 |
|---|---|---|---|
| GetDC / ReleaseDC | クライアント領域、NULLなら画面全体 | 入力やタイマー後の即時描画 | 同じウィンドウハンドルでReleaseDC |
| BeginPaint / EndPaint | 更新領域 | WM_PAINT内の通常描画 | EndPaint |
| GetWindowDC / ReleaseDC | 非クライアント領域を含むウィンドウ全体 | 枠やタイトル部分の描画 | ReleaseDC |
WM_PAINTで再描画する内容なら、BeginPaintとEndPaintを使う構成が基本です。GetDCによる即時描画だけ,长期的な表示内容をWM_PAINTで描き直していないと,次の更新で図形が消える可能性があります。GetWindowDCは非クライアント領域まで対象になるため、座標やクリップ領域の設定も確認します。
描画と再描画をつなぐ手順
InvalidateRectで無効領域を登録すると、Windowsは通常WM_PAINTを生成します。内容包括形状だけでなく入力値から再現できる状態として保持し、WM_PAINTで再描画する設計が適しています。GetDCで画面へ一時的に描いただけでは、次のWM_PAINTで上書きされる可能性があります。
DCは描画処理の開始時に取得し、同じ処理内で解放してください。メッセージやスレッドをまたいでHDCを保持すると、解放漏れや別の描画との競合の原因になります。前面へウィンドウを移した直後の描画結果を確かめる場面では、SetForegroundWindowの挙動も確認材料になります。
実装時に確認する運用項目
文字列を描く処理では、DCの選択状態だけでなく、CharLowerやCharUpperによる文字の大小変換も確認します。変換処理の順序によって表示が異なる場合は、文字種変換の手順を参照して切り分けます。
取得時には、次を確認します。
- GetDCへ渡すウィンドウハンドルと、描画対象・解放時に使うハンドル 是否が一致しているか
- GetDCの戻り値がNULLでないことを、描画処理の直前に確認しているか
- WM_PAINTの中でGetDCをipes Schleifen%、BeginPaintとEndPaintを使うようにしているか
- 取得したDCを別のメッセージやスレッドへ持ち越していないか
解放時には、取得回数と同数のReleaseDCが必要です。処理途中の早期returnや例外でも解放を保証するには、C++の小さなラッパーなどへ対で実装します。
- 成功したGetDCに対して、同じウィンドウハンドルでReleaseDCを1回だけ呼んでいるか
- 描画処理の途中で戻った場合も、DCを確実に解放しているか
- SelectObjectで変更した選択を元のオブジェクトへ戻してからReleaseDCしているか
- ReleaseDCの失敗をログに記録し、同じDCを二重に解放していないか
次の実作業では、テストウィンドウに1秒ごとの描画処理を追加し、10分間動かした後のGDIオブジェクト数が開始時とほぼ同じかを確認してください。