【VBA×WindowsAPI】GetWindowDC関数の使い方
GetWindowDC関数
GetWindowDC関数は指定したウィンドウのデバイスコンテキストへのハンドルを取得するための関数です。
デバイスコンテキスト(以下DC)とはMicrosoft公式を引用すると下記の通りです。
ディスプレイやプリンターといったデバイスの描画属性に関する情報を保持する Windows のデータ構造体です。 すべての描画呼び出しは、線や図形、テキストを描画するための Windows API をカプセル化するデバイスコンテキスト オブジェクトを通じて行われます。 Windows では、デバイス コンテキストがあることで、デバイスに依存しない描画が可能となっています。 デバイス コンテキストを使用して、画面やプリンター、メタファイルへの描画を行うことができます。
少し難しく記載されていますが簡単にいえばDCとはデバイスの描画担当者のようなものです。
私たち開発者はこのDCという”担当者”に「ウィンドウのこの位置に円を描いてほしい」「ウィンドウのこの位置のピクセルの色を教えてほしい」というような命令をすることで図形の描画やすでに描画されている情報の取得などをすることが可能になります。
DCはウィンドウのハンドルが取得できれば、そのハンドルをもとにそのウィンドウが持つDCへのハンドルも取得することができます。このウィンドウハンドルから、そのウィンドウのDCへのハンドルを取得するための関数がGetWindowDC関数です。
ウィンドウは大きく分けて、いわゆるメインの部分となる「クライアント領域」と、メニューバーやツールバー、ステータスバー、フレーム部分などの「非クライアント領域」に分かれており、複数あるDCハンドル取得用の関数のどれを使うかによって取得できる領域が異なります。
これらのうちGetWindowDC関数は非クライアント領域とクライアント領域を合わせたウィンドウ全体に対するDCハンドルを取得することができます。クライアント領域のみに対するDCハンドルはGetDC関数で取得することができます。(より詳細な設定ができるGetDCEx関数というのもあります)
領域を使い分けることでたとえば図形を描画する際に非クライアント領域まで侵食することを防いだり、描画時の座標計算などを単純化することができます。
使用方法
GetWindowDC関数を使用するにはあらかじめ関数の宣言しておく必要があります。
※宣言をしないと関数は使えずにエラーとなるので書き忘れに注意しましょう。
使用しているWindowsが32bitか64bitかによって宣言時に書く文言が違います。
環境に合わせて以下のいずれかをコードの一番初め(Option Explicitの次の行あたり)に書いておくことで、そのモジュール内で各関数を使うことができるようになります。
Declare PtrSafe Function GetWindowDC Lib “user32” (ByVal hwnd As LongPtr) As LongPtr
Declare Function GetWindowDC Lib “user32” (ByVal hwnd As Long) As Long
上記のどちらを書けばいいかわからない場合は以下のコードをコピペして、モジュールの最上部に書いておきましょう。この構文を書いておくことで自動的に使うことのできる方の構文が使用されます。
VBE上では使えない方の構文が赤色で表示される場合がありますが、実行に影響はありません。
1 2 3 4 5 |
#If VBA7 Then Declare PtrSafe Function GetWindowDC Lib "user32" (ByVal hwnd As LongPtr) As LongPtr #Else Declare Function GetWindowDC Lib "user32" (ByVal hwnd As Long) As Long #End If |
各関数の宣言文は「Private/Public」を付けて各関数の有効範囲を指定することもできます。
・Public Declare PtrSafe Function~ :モジュール外で呼び出し有効
構文
GetWindowDC関数の構文は下記のように書きます。
hdc = GetWindowDC(hwnd)
引数
hwnd (64bit:LongPtr型 / 32bit:Long型)
対象のウィンドウのハンドルを入力します。ここで入力したハンドルが示すウィンドウのDCにアクセスすることが可能になります。引数としてNULL値(0)を入力することも可能で、この場合はウィンドウのDCではなく画面全体のDCにアクセスすることが可能になります。
開発環境によって引数の型が変化するので注意が必要です。
戻り値
hdc (64bit:LongPtr型 / 32bit:Long型)
戻り値は関数が成功した場合、hwndが示すウィンドウ全体のDCへのハンドルです。
(※ウィンドウ全体=クライアント領域+非クライアント領域)
関数が失敗した場合の戻り値はNULL値(0)となります。
引数と同じく開発環境によって戻り値の型が変化するので注意が必要です。
本関数を実行した後は必ずReleaseDC関数を使って取得したDCを解放する必要があります。
これを行わないと不要なメモリが蓄積していきメモリリークに陥ってしまいます。
サンプルコード
以下はGetWindowDC関数とGetDC関数のそれぞれを使ってDCにアクセスし、画面に描画されている指定座標のピクセル色を取得するサンプルコードです。指定座標のピクセルの色を取得するには同じくWindowsAPIのGetPixel関数を使います。このとき座標の基準(原点)はGetPixel関数に引数として渡すDCが持っています。つまり非クライアント領域も含むDCハンドルを取得するGetWindowDC関数は下図でいうと右側となります。(GetDC関数使用時は左側)
下記はウィンドウ名が”コマンド プロンプト”となっているウィンドウのDCハンドルを取得して x=1,y=1 地点のピクセルの色を取得するサンプルコードです。このコードの場合、GetWindowDC関数を使っているためDCはクライアント領域だけでなく非クライアント領域も含まれています。そのため、ウィンドウ名が書かれているタイトルバー部分の色が取得されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
Declare PtrSafe Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As LongPtr Declare PtrSafe Function GetWindowDC Lib "user32" (ByVal hwnd As LongPtr) As LongPtr Declare PtrSafe Function ReleaseDC Lib "user32" (ByVal hwnd As LongPtr, ByVal hdc As LongPtr) As Long Declare PtrSafe Function GetPixel Lib "gdi32" (ByVal hdc As LongPtr, ByVal x As Long, ByVal y As Long) As Long Sub main() Dim hwnd As LongPtr Dim hdc As LongPtr Dim lColor As Long Dim lRet As Long Dim lR As Long, lG As Long, lB As Long 'ウィンドウ名が"コマンド プロンプト"のウィンドウハンドルを取得 hwnd = FindWindow(vbNullString, "コマンド プロンプト") '取得したウィンドウのデバイスコンテキストへのハンドルを取得 hdc = GetWindowDC(hwnd) '取得したウィンドウ((クライアント領域+非クライアント領域)のx=1,y=1地点のピクセルの色を取得 lColor = GetPixel(hdc, 1, 1) 'RGB値変換 lR = lColor Mod 256 lG = Int(lColor / 256) Mod 256 lB = Int(lColor / 256 / 256) '結果をイミディエイトウィンドウに出力 Debug.Print "カラー:" & lColor & "|R:" & lR & "|G:" & lG & "|B:" & lB 'デバイスコンテキストを解放 lRet = ReleaseDC(hwnd, hdc) End Sub |
対して下記はGetWindowDC関数をGetDC関数に置き換えて、それ以外は何も変えていないサンプルコードです。こちらの場合は、対象領域がクライアント領域のみのため、コマンドプロンプトの入力エリア(デフォルトで黒色のエリア)の x=1,y=1 地点のピクセルの色が取得できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
Declare PtrSafe Function FindWindow Lib "User32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As LongPtr Declare PtrSafe Function GetDC Lib "User32" (ByVal hwnd As LongPtr) As LongPtr Declare PtrSafe Function ReleaseDC Lib "User32" (ByVal hwnd As LongPtr, ByVal hdc As LongPtr) As Long Declare PtrSafe Function GetPixel Lib "gdi32" (ByVal hdc As LongPtr, ByVal x As Long, ByVal y As Long) As Long Sub main() Dim hwnd As LongPtr Dim hdc As LongPtr Dim lColor As Long Dim lRet As Long Dim lR As Long, lG As Long, lB As Long 'ウィンドウ名が"コマンド プロンプト"のウィンドウハンドルを取得 hwnd = FindWindow(vbNullString, "コマンド プロンプト") '取得したウィンドウのデバイスコンテキストへのハンドルを取得 hdc = GetDC(hwnd) '取得したウィンドウ(クライアント領域)のx=1,y=1地点のピクセルの色を取得 lColor = GetPixel(hdc, 1, 1) 'RGB値変換 lR = lColor Mod 256 lG = Int(lColor / 256) Mod 256 lB = Int(lColor / 256 / 256) '結果をイミディエイトウィンドウに出力 Debug.Print "カラー:" & lColor & "|R:" & lR & "|G:" & lG & "|B:" & lB 'デバイスコンテキストを解放 lRet = ReleaseDC(hwnd, hdc) End Sub |
VBA×WindowsAPIまとめページ
その他のWindowsAPI関数は下記ページにまとまっているので合わせて参照下さい。
参考
Microsoft公式:GetWindowDC 関数 (winuser.h) – Win32 apps