【WPF】特定のウィンドウプロセスの座標を取得する
まず、クラス内に定義している変数や構造体、WindousAPIからusingして使っている関数達の定義を載せておきます。
//ウィンドウ名からウィンドウハンドルを取得(WindowsAPIの関数) [DllImport("user32.dll")] public static extern IntPtr FindWindow(string lpClassName, string lpWindowName); //指定したウィンドウの外接する四角形の寸法を取得します(WindowsAPIの関数) [DllImport("user32.dll")] public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect); //他のウィンドウプロセスを最前面に持ってくるために定義するWindowsAPIの関数 [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags); private const uint SWP_NOSIZE = 0x0001;//ウィンドウのサイズを変更しないようにするための規定フラグ private const uint SWP_NOMOVE = 0x0002;//ウィンドウの位置を変更しないようにするための規定フラグ private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);//ウィンドウを最前面に配置 //ウィンドウの座標を構造体で管理する(RECTという構造体名にするのがセオリーらしい) public struct RECT { public int Left;//ウィンドウの左端 public int Top;//ウィンドウの上端 public int Right;//ウィンドウの右端 public int Bottom;//ウィンドウの下端 }
そしてこのプログラムの肝である関数
キャプチャした座標や、領域、そして一番重要な「キャプチャした範囲のピクセルデータ(画像)を返す」関数です。
よって、返り値は画像や座標等のデータを持っている「Bitmap型」になります。
public static Bitmap CaptureWindow(string windowTitle) { // ウィンドウのハンドルを名前から検索して取得 IntPtr hWnd = FindWindow(null, windowTitle); //取得したウィンドウ(ぷよテト)を最前面に配置 //SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); //ウィンドウハンドルが見つからなかった場合 if (hWnd == IntPtr.Zero) { MessageBox.Show("ハンドルが見つかりませんでした"); return null; } // ウィンドウ(ぷよテト)の位置座標を取得し、Rect型であるwindowRectに代入 if (GetWindowRect(hWnd, out RECT windowRect)) { //ウィンドウの縦横の長さを算出 int width = windowRect.Right - windowRect.Left; int height = windowRect.Bottom - windowRect.Top; // ウィンドウのスクリーンショットを格納するBitmapオブジェクトを作成 Bitmap screenshot = new Bitmap(width, height); //ここでスクリーンショットをキャプチャ変数(screenshot)に保存する。 using (Graphics graphics = Graphics.FromImage(screenshot)) { graphics.CopyFromScreen(windowRect.Left, windowRect.Top, 0, 0, new System.Drawing.Size(width, height)); //以下引数の説明 //windowRect.Left および windowRect.Top: ウィンドウをキャプチャを開始する範囲の左上隅を示します。 //0 , 0: ウィンドウの左上からの相対的な位置を指定します。通常は0を使用します。 //new System.Drawing.Size: キャプチャする領域の幅と高さを指定します。 } return screenshot; } return null; }
順に追っていきます
// ウィンドウ(ぷよテト)の位置座標を取得し、Rect型であるwindowRectに代入 if (GetWindowRect(hWnd, out RECT windowRect)){"処理"}
まずGetWindowRect関数はWindowsAPIの関数です。ウィンドウの座標を取得する必要があるのでusingして使っています。
そしてwindowRectがウィンドウの座標を保持していることが、のちのち重要になってきます。(初見の時完全に忘れていた)
// ウィンドウのスクリーンショットを格納するBitmapオブジェクトを作成 Bitmap screenshot = new Bitmap(width, height);
この処理は、ウィンドウの座標や縦横の長さの情報を最終的に持つ変数を定義しています。
画面全体(1920*1080)のうち「ウィンドウの座標がどこにあって、どのくらいの縦横の長さなのか」の情報を持つことになる。
この関数の返り値になります。
using (Graphics graphics = Graphics.FromImage(screenshot)) { graphics.CopyFromScreen(windowRect.Left, windowRect.Top, 0, 0, new System.Drawing.Size(width, height)); } return screenshot;
このコードがやってることは、実は結構ややこしいです。
ここも順に追って説明していきます。
まずGraphics とBitmapの説明ですが、
Bitmapは「描画対象(キャンバス)」の役割を持ちます。
そしてGraphicsは、「描画操作を実行するための手段(グラフィックスコンテキスト)」の役割を持ちます。
Graphicsのオブジェクトは、screenshot上に描画操作を実行するためのインターフェースを提供します。
(Graphicsオブジェクトが描画操作をするわけではなく、それらが持つ'関数'が行います)
具体的には、CopyFromScreen() メソッドを使用して、画面上(ディスプレイに表示されている画面上)のピクセルデータを screenshot にコピーしたりすることができます。
勿論、キャプチャしたいウィンドウの座標と領域指定をします。(後述)
Graphics graphics = Graphics.FromImage(screenshot)
そしてまずこのコードは、Graphics型の変数を定義しています。
先ほども言いましたが、「Graphicsオブジェクト」が持つ関数を使って「screenshot」上に描画操作を行うためです
Graphics.FromImageメソッドは、指定されたBitmapオブジェクト(今回ならscreenshot)に対して、2Dグラフィック描画操作を行うためのGraphicsオブジェクトを作成するための方法です。Graphicsオブジェクトは、図形、テキスト、イメージなどをBitmapに描画するために使用されます。
そして注意なのが、ここの定義で「graphics」と「screenshot」が関連付けられています。
どういうことかというと、
Graphics オブジェクトを作成した後、そのオブジェクトを使用して行われた描画操作は、指定した Bitmap オブジェクト(ここでは screenshot)に影響を与えるようになります。
それは、Graphics オブジェクトが対象となる Bitmap オブジェクトと連動して描画操作を行うためです。
graphicsが何か処理をするときは、screenshotに何か追加していると思ってください。
graphics.CopyFromScreen(windowRect.Left, windowRect.Top, 0, 0, new System.Drawing.Size(width, height));
そしてこのコードの「CopyFromScreen()」は、
Graphics オブジェクトである graphics を介してscreenshotに描画操作を行います。
具体的には、スクリーン上の指定された領域をキャプチャし、そのピクセルデータをscreenshot に渡します。
この時点で、screenshot にはキャプチャされた画像が含まれていることになります。(ウィンドウキャプチャ完了)
CopyFromScreenの引数の説明ですが、
第1,2引数「windowRect.Left ,windowRect.Top」は先ほど重要になるといった、ウィンドウの座標を引数に取っています。
(初見の時忘れていたので、これらの値をずっと「0,0」(ディスプレイの左上の座標)だと思っていました。通常は0が入っています。)
一々言わなくても分かりますが、
windowRect.Leftは「ウィンドウの左側のx座標」、windowRect.Topは「ウィンドウの上側のy座標」です。
第3,4引数「0,0」の引数は、ウィンドウの左上からの相対的な位置を指定します。通常は0を使用します。
そしてnew System.Drawing.Size(width, height)は、キャプチャする領域の幅と高さを指定します。
今回の場合はウィンドウの幅と高さになりますね。
そして最後に
return screenshot;
のコードで、「ウィンドウキャプチャをして、そのピクセルデータ(画像)を持った」screenshotを返しています。
graphicsのメモリ開放をした方がいいのですが、これら一連の処理は、
usingステートメントを使用しているのでusingを抜けると勝手に破棄されます。(メモリ解放)