以前にやったデスクトップ画面全体のキャプチャに続き、今回は任意のウィンドウのキャプチャを行ってみましょう。とりあえずは、最前面でアクティブになっているウィンドウ(フォアグラウンドウィンドウ)のキャプチャを行ってみることにします。
ウィンドウキャプチャは、次のような手順で行います。
そこで、まずは対象となるウィンドウのハンドルを取得する方法から説明していきましょう。
デスクトップ上の最前面でアクティブになっているウィンドウのことをフォアグラウンドウィンドウと呼びます。フォアグラウンドウィンドウのハンドルを取得するには、user32.dllが提供するGetForegroundWindow関数を呼び出します。
HWND GetForegroundWindow(VOID);
この関数は引数を持ちません。関数の戻り値として、現在のフォアグラウンドウィンドウのハンドルが返ります。どのウィンドウもアクティブでない場合、戻り値は0 (NULL) になります。
フォアグラウンドウィンドウの代わりに、デスクトップウィンドウのハンドルを取得することもできます。デスクトップウィンドウはデスクトップ画面全体を覆っているウィンドウのことで、このデスクトップウィンドウのキャプチャを行うことで、以前にも説明したスクリーンキャプチャを行うことができます。こちらは、以前のものとは別の手続きでスクリーンキャプチャを行うものです。
デスクトップウィンドウのハンドルを取得するにはGetDesktopWindow関数を使います。この関数はuser32.dllにより提供されており、次のように定義されています。
HWND GetDesktopWindow(VOID);
この関数もまた引数を持ちません。関数の戻り値として、デスクトップウィンドウのハンドルが返ります。
実際のところ、後でウィンドウに対するデバイスコンテキストを取得する際に、ウィンドウハンドルの代わりに0 (NULL) を指定することによってデスクトップ画面に対するデバイスコンテキストを取得することができるので、画面のサイズをウィンドウハンドルからではなくHSP標準関数ginfoから取得するのであれば、あえてデスクトップウィンドウのハンドルを取得する必要はないのですが。
対象ウィンドウのハンドルを取得できたら、次にそのウィンドウのサイズを取得してしまうことにします。今回のウィンドウキャプチャでは、クライアント領域(アプリケーションの描画領域)だけの画像の取得と、非クライアント領域(タイトルバーやメニューなど)を含めたウィンドウ全体の画像の取得をそれぞれ行います。ウィンドウのサイズはそれぞれ異なるので、それらのサイズを取得する手続きも異なります。
対象ウィンドウのクライアント領域(アプリケーションによる描画領域)のみのサイズを取得したい場合には、user32.dllより提供されているGetClientRect関数を使います。
BOOL GetClientRect( HWND hWnd, // window handle LPRECT pRect // window rectangle );
最初のhWndパラメータには、対象ウィンドウのハンドルを指定します。
次のpRectパラメータには、RECT構造体のアドレスを指定します。以前にも記述しましたが、RECT構造体は以下のように定義されています。
typedef struct tagRECT { LONG left; // upper-left X LONG top; // upper-left Y LONG right; // lower-right X LONG bottom; // lower-right Y } RECT, *PRECT, *NPRECT, *LPRECT;
GetClientRect関数にRECT構造体のアドレスを渡すと、この構造体にウィンドウのクライアント領域の座標が格納されます。クライアント座標は常にクライアント領域の左上の点を基準としているので、leftメンバとtopメンバは常に0になります。rightメンバとbottomメンバがそれぞれ右下の点のクライアント座標(x座標とy座標)であり、これらがそのままクライアント領域の幅と高さを表しています。
ウィンドウのクライアント領域だけでなく、タイトルバー、メニューバー、スクロールバーといった非クライアント領域も含めた対象ウィンドウ全体のサイズを取得するには、GetWindowRect関数を使います。この関数はuser32.dllによって提供されており、以下のように定義されています。
BOOL GetWindowRect( HWND hWnd, // window handle LPRECT pRect // window rectangle );
2つの引数hWndパラメータとpRectパラメータについては、上記のGetClientRect関数と同じです。GetWindowRect関数の場合には、クライアント領域の座標ではなく、ウィンドウ全体の座標がRECT構造体に格納されます。
GetWindowRect関数で取得される座標はスクリーン座標で表されています。これは、スクリーンの左上の点を基準(座標ゼロ)とするものです。そのため、実際のウィンドウの幅はrightメンバからleftメンバを引いた値に、高さはbottomメンバからtopメンバを引いた値になります。
次に、対象ウィンドウの描画を行っているデバイスコンテキストのハンドルを取得しなければいけません。これについても、クライアント領域(アプリケーションの描画領域)だけのデバイスコンテキストの場合と、非クライアント領域(タイトルバーやメニューなど)を含めたウィンドウ全体のデバイスコンテキストの場合とで、手続きが異なります。
ウィンドウのクライアント領域のみを取得したい場合には、クライアント領域のみに割り当てられているデバイスコンテキストのハンドルを取得します。これには、user32.dllより提供されているGetDC関数を使います。
HDC GetDC( HWND hWnd // window handle );
hWndパラメータにはウィンドウハンドルを指定します。ここで指定されたウィンドウのクライアント領域に対するデバイスコンテキストのハンドルが戻り値として取得されます。
hWndパラメータに0 (NULL) を指定することもできます。この場合には、デスクトップ画面に対するデバイスコンテキストのハンドルが取得されます。
非クライアント領域も含めたウィンドウ全体に対するデバイスコンテキストのハンドルを取得するには、GetWindowDC関数を使います。この関数はuser32.dllによって提供されており、以下のように定義されています。
HDC GetWindowDC( HWND hWnd // window handle );
hWndパラメータにはウィンドウハンドルを指定します。ここで指定されたウィンドウの全領域に対するデバイスコンテキストのハンドルが戻り値として取得されます。
この関数の場合も、hWndパラメータに0 (NULL) を指定することによって、デスクトップ画面に対するデバイスコンテキストのハンドルを取得することができます。
対象ウィンドウのサイズとデバイスコンテキストのハンドルを取得できたら、2つのデバイスコンテキスト間での画像コピーを行います。スクリーンキャプチャの際にも説明したように、画像のコピーにはgdi32.dllが提供するBitBlt関数を使います。
BOOL BitBlt( HDC hdcDest, // destination DC int nXDest, // destination X-coord int nYDest, // destination Y-coord int nWidth, // width int nHeight, // height HDC hdcSource, // source DC int nXSource, // source X-coord int nYSource, // source Y-coord DWORD dwRaster // raster operation code );
コピー元として取得したデバイスコンテキストのハンドルを指定し、コピー先のデバイスコンテキストのハンドルとしてはシステム変数hwndの値を指定します。ラスターオペレーションコード(dwRasterパラメータ)は前回と同じく0x00CC0020 (SRCCOPY) と0x40000000 (CAPTUREBLT) の組み合わせを指定します。
画像のコピーが終了したら、取得したデバイスコンテキストを解放しなければいけません。これには、user32.dllのReleaseDC関数を呼び出します。
int ReleaseDC( HWND hWnd, // window handle HDC hDC // DC handle );
最初のhWndパラメータには対象ウィンドウのハンドルを、次のhDCパラメータにはGetDC関数やGetWindowDC関数で取得されたデバイスコンテキストのハンドルを指定します。
1つ注意しておくべきことは、以前のスクリーンキャプチャではCreateDC関数を使って取得したデスクトップ画面のデバイスコンテキストをDeleteDCで解放していましたが、今回のウィンドウキャプチャではGetDC関数またはGetWindowDC関数で取得したデバイスコンテキストをReleaseDC関数で解放しているということです。これらの関数の対応を間違えてはいけません。
今回のスクリプトでは、モジュール機能を使用しています。モジュール内で新たに定義されている命令は、指定されたウィンドウの画像をHSPのバッファウィンドウにコピーするCaptureWindow命令、アクティブなウィンドウの画像をコピーするCaptureActiveWindow命令、デスクトップ画面の画像をコピーするCaptureDesktopScreen命令の3つです。CaptureActiveWindow命令とCaptureDesktopScreen命令については、対象ウィンドウ(フォアグラウンドウィンドウやデスクトップウィンドウ)のハンドルを取得した直後にCaptureWindow命令を実行するという手続きをとっています。
#module ; ======== API 関数の定義 ======== #uselib "user32.dll" #cfunc GetDC "GetDC" int #cfunc GetWindowDC "GetWindowDC" int #func ReleaseDC "ReleaseDC" int,int #cfunc GetForegroundWindow "GetForegroundWindow" #cfunc GetDesktopWindow "GetDesktopWindow" #func GetWindowRect "GetWindowRect" int,int #func GetClientRect "GetClientRect" int,int #uselib "gdi32.dll" #func BitBlt "BitBlt" int,int,int,int,int,int,int,int,int #define NULL 0 #define SRCCOPY 0x00CC0020 #define CAPTUREBLT 0x40000000 ; ======== 指定されたウィンドウのキャプチャ ======== #deffunc CaptureWindow int hwndTarget, int clientOnly ; ウィンドウのサイズ取得 dim rect, 4 ; RECT構造体 if clientOnly { GetClientRect hwndTarget, varptr(rect) } else { GetWindowRect hwndTarget, varptr(rect) } if stat == 0 { return 1 ; エラー発生時 stat = 1 } sx = rect(2) - rect(0) sy = rect(3) - rect(1) ; 現在の描画先バッファウィンドウをコピー先として初期化 buffer ginfo_sel, sx, sy, 0 ; デバイスコンテキストのハンドル取得 if clientOnly { hdcTarget = GetDC(hwndTarget) } else { hdcTarget = GetWindowDC(hwndTarget) } ; 画像をコピー BitBlt hdc, 0, 0, sx, sy, hdcTarget, 0, 0, SRCCOPY | CAPTUREBLT ; デバイスコンテキスト解放 ReleaseDC hwndTarget, hdcTarget return 0 ; 正常終了時 stat = 0 ; ======== アクティブウィンドウのキャプチャ ======== #deffunc CaptureActiveWindow int clientOnly ; アクティブウィンドウのハンドルを取得してウィンドウキャプチャ CaptureWindow GetForegroundWindow(), clientOnly return ; ======== デスクトップ画面のキャプチャ ======== #deffunc CaptureDesktopScreen ; デスクトップウィンドウのハンドルを取得してウィンドウキャプチャ CaptureWindow GetDesktopWindow() return #global ; ===== ここからメインスクリプト ===== screen 0, 300, 150 objsize 300, 50 button "Active Window (Full)", *capture button "Active Window (Client Area)", *capture button "Desktop Screen", *capture stop *capture ; 押されたボタンのオブジェクトIDを取得 pressedButtonId = stat ; ===== 画面のキャプチャ ===== gsel 0, -1 ; ウィンドウをいったん消す buffer 2, 1, 1 ; コピー用バッファ画面(サイズはダミー、とりあえず1×1) wait 50 ; アクティブウィンドウ取得は十分なwaitが必要 ; 押されたボタンを判別して処理を決定 switch pressedButtonId case 0 ; Active Window (Full) CaptureActiveWindow 0 swbreak case 1 ; Active Window (Client Area) CaptureActiveWindow 1 swbreak case 2 ; Desktop Screen CaptureDesktopScreen swbreak swend gsel 0, 1 dialog "bmp", 17, "ビットマップファイル" if stat { gsel 2 : bmpsave refstr } stop