前回はスクリーン全体をキャプチャしました。今回はスクリーン全体ではなく自分で選んだウィンドウをキャプチャできるように改良してみることにします。
デスクトップ上にあるウィンドウを列挙するにはどうすればいいのでしょうか。
Microsoft Docsではこんなサンプルプログラムが紹介されています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
using System; using System.Runtime.InteropServices; public delegate bool CallBack(int hwnd, int lParam); public class EnumReportApp { [DllImport("user32")] public static extern int EnumWindows(CallBack x, int y); public static void Main() { CallBack myCallBack = new CallBack(EnumReportApp.Report); EnumWindows(myCallBack, 0); } public static bool Report(int hwnd, int lParam) { Console.Write("Window handle is "); Console.WriteLine(hwnd); return true; } } |
まずはメニューがドロップダウンされたらデスクトップ上のウィンドウをメニューのなかから選択できるようにします。
1 2 3 4 5 6 7 8 9 |
public partial class Form1 : Form { // メニューがドロップダウンされたら private void WindowToolStripMenuItem_DropDownOpening(object sender, EventArgs e) { WindowToolStripMenuItem.DropDownItems.Clear(); EnumWindows(new EnumWindowsDelegate(EnumWindowCallBack), IntPtr.Zero); } } |
EnumWindowsをどう書くかですが、
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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
public partial class Form1 : Form { public delegate bool EnumWindowsDelegate(IntPtr hWnd, IntPtr lparam); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public extern static bool EnumWindows(EnumWindowsDelegate lpEnumFunc, IntPtr lparam); [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern int GetWindowTextLength(IntPtr hWnd); [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); [DllImport("user32")] private static extern bool IsWindowVisible(IntPtr hWnd); [DllImport("user32")] private static extern bool IsIconic(IntPtr hWnd); [DllImport("user32.dll")] private static extern int GetWindowRect(IntPtr hwnd, ref RECT lpRect); [StructLayout(LayoutKind.Sequential)] private struct RECT { public int left; public int top; public int right; public int bottom; } private bool EnumWindowCallBack(IntPtr hWnd, IntPtr lparam) { //ウィンドウのタイトルの長さを取得する int textLen = GetWindowTextLength(hWnd); if (0 < textLen) { //ウィンドウのタイトルを取得する StringBuilder tsb = new StringBuilder(textLen + 1); GetWindowText(hWnd, tsb, tsb.Capacity); //ウィンドウのクラス名を取得する StringBuilder csb = new StringBuilder(256); GetClassName(hWnd, csb, csb.Capacity); //結果を表示する // ウィンドウのクラス名が"Progman"なら無視 if (csb.ToString() == "Progman") return true; // 可視ウインドウでないなら無視 if (!IsWindowVisible(hWnd)) return true; // 最小化されているウインドウは無視 if (IsIconic(hWnd)) return true; // ウインドウサイズがゼロなら無視 RECT winRect = new RECT(); GetWindowRect(hWnd, ref winRect); if(winRect.top == winRect.bottom || winRect.right == winRect.left) return true; string windowTitle = tsb.ToString(); ToolStripMenuItem item = new ToolStripMenuItem(); item.Text = windowTitle; item.Tag = hWnd; item.Click += Item_Click; WindowToolStripMenuItem.DropDownItems.Add(item); } return true; } } |
取得しなくていいものは無視しています。
メニューがクリックされたらItem_Clickが呼ばれます。メニューのTagを調べればウィンドウハンドルがわかるようにしています。
1 2 3 4 5 6 7 8 |
public partial class Form1 : Form { private void Item_Click(object sender, EventArgs e) { ToolStripMenuItem item = (ToolStripMenuItem)sender; WindowCapture((IntPtr)item.Tag); } } |
ウィンドウハンドルがわかったらキャプチャします。
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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
public partial class Form1 : Form { int i = 0; string saveFolderPath; string saveFilePath; void WindowCapture(IntPtr hWnd) { if (!System.IO.Directory.Exists(this.saveFolderPath)) { MessageBox.Show("フォルダが指定されていない"); return; } Bitmap bitmap = GetBitmapFromhWnd(hWnd); if (bitmap == null) { MessageBox.Show("Bitmapが取得できない"); return; } if (comboBox1.SelectedIndex == 0) { saveFilePath = String.Format("{0}\\{1}.bmp", saveFolderPath, i); bitmap.Save(saveFilePath, System.Drawing.Imaging.ImageFormat.Bmp); } if (comboBox1.SelectedIndex == 1) { saveFilePath = String.Format("{0}\\{1}.jpg", saveFolderPath, i); bitmap.Save(saveFilePath, System.Drawing.Imaging.ImageFormat.Jpeg); } if (comboBox1.SelectedIndex == 2) { saveFilePath = String.Format("{0}\\{1}.png", saveFolderPath, i); bitmap.Save(saveFilePath, System.Drawing.Imaging.ImageFormat.Png); } if (comboBox1.SelectedIndex == 3) { saveFilePath = String.Format("{0}\\{1}.gif", saveFolderPath, i); bitmap.Save(saveFilePath, System.Drawing.Imaging.ImageFormat.Gif); } if (comboBox1.SelectedIndex == 4) { saveFilePath = String.Format("{0}\\{1}.tiff", saveFolderPath, i); bitmap.Save(saveFilePath, System.Drawing.Imaging.ImageFormat.Tiff); } bitmap.Dispose(); label1.Text = "保存完了:" + saveFilePath; i++; } } |
Bitmapを取得する GetBitmapFromhWnd メソッドですが、
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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
public partial class Form1 : Form { [DllImport("user32.dll")] private static extern IntPtr GetWindowDC(IntPtr hwnd); [DllImport("gdi32.dll")] private static extern int BitBlt(IntPtr hDestDC, int x, int y, int nWidth, int nHeight, IntPtr hSrcDC, int xSrc, int ySrc, int dwRop); [DllImport("user32.dll")] private static extern IntPtr ReleaseDC(IntPtr hwnd, IntPtr hdc); private const int SRCCOPY = 13369376; Bitmap GetBitmapFromhWnd(IntPtr windowHandle) { Graphics g = null; IntPtr winDC = IntPtr.Zero; IntPtr hDC = IntPtr.Zero; try { winDC = GetWindowDC(windowHandle); //ウィンドウの大きさを取得 RECT winRect = new RECT(); GetWindowRect(windowHandle, ref winRect); //Bitmapの作成 Bitmap bmp = new Bitmap(winRect.right - winRect.left, winRect.bottom - winRect.top); //Graphicsの作成 g = Graphics.FromImage(bmp); //Graphicsのデバイスコンテキストを取得 hDC = g.GetHdc(); //Bitmapに画像をコピーする BitBlt(hDC, 0, 0, bmp.Width, bmp.Height, winDC, 0, 0, SRCCOPY); return bmp; } catch { return null; } finally { // 終わったら解放 if(g != null && hDC != IntPtr.Zero) g.ReleaseHdc(hDC); if (g != null) g.Dispose(); if(winDC != IntPtr.Zero) ReleaseDC(windowHandle, winDC); } } } |
これで自分で選択したウィンドウをキャプチャーすることができます。
「まずはメニューがドロップダウンされたらデスクトップ上のウィンドウをメニューのなかから選択できるようにします。」
の部分の『windowHandles.Clear();』なんですが、ほぼ丸コピで記述したところ「現在のコンテキストに”windowHandles”という名前は存在しません」とエラー吐かれました。
大雑把な質問になってしまいますが、どうすればよいでしょうか?
void WindowToolStripMenuItem_DropDownOpening(object sender, EventArgs e)内の windowHandles.Clear(); は必要ありません(当方の記述ミス)。
それから WindowCapture(IntPtr hWnd) を修正しました。
質問の回答と修正、有難うございました