internal static void CaptureWindow(IntPtr hwnd, System.Drawing.Bitmap img, System.Windows.Int32Rect clippingRect) { System.Diagnostics.Debug.Assert(img != null); using (var memg = System.Drawing.Graphics.FromImage(img)) { memg.Clear(System.Drawing.Color.Transparent); IntPtr memDC = memg.GetHdc(); #if false // PrintWindow() は Windows XP で追加された API だが、 // これはどうも各ウィンドウに再描画要求メッセージを送るらしく、 // 短い時間間隔で呼び出すと Windows Calculator(電卓、calc.exe)などはちらつく。 // Aero Glass が無効になった状態のウィンドウのスクリーンショットを撮れるので、 // ヘルプ作成などの非リアルタイム用途であれば適している API かも。 // たぶん Aero Auto Color も同様。 // 最小化した状態ではタイトル バーのみがレンダリングされる。 // http://d.hatena.ne.jp/madotate/ User32DllMethodsInvoker.PrintWindow(hwnd, memDC, 0); #else // BitBlt() を使ってウィンドウのデバイス コンテキスト経由で描画内容を DIB ビットマップに転送する。 // 最小化した状態では PrintWindow() 同様、タイトル バーのみがレンダリングされる。 // こちらは DWM API 同様、それなりに高速なのでリアルタイム用途に適している。 // ただし Aero Glass の Window Chrome 領域(タイトル バー含む)がゴミデータになるときがある模様? // そのため、クライアント領域のみ(ただしメニューバーは含まれない)をキャプチャする。 // DirectShow などのビデオ オーバーレイを使っているアプリでどうなるのかは試してみないと分からない。 // キャプチャ自体は常にクライアント左上を基点、デスクトップ サイズを限界として、 // 利用側で配置する際にそれらの情報を考慮するようにする。 IntPtr winDC = User32DllMethodsInvoker.GetWindowDC(hwnd); #if true var intersectRect = MyWin32InteropHelper.CalcClientIntersectRect(hwnd, clippingRect); #if false // 切り出し元の相対位置を使う場合。 int dstX = intersectRect.X; int dstY = intersectRect.Y; #else // 切り出した領域を常に左上に配置する場合。 const int dstX = 0; const int dstY = 0; #endif // ウィンドウ クライアント領域の内容をさらにユーザー定義クリッピング矩形で切り出したものを、ビットマップに転送する。 Gdi32DllMethodsInvoker.BitBlt( memDC, dstX, dstY, intersectRect.Width, intersectRect.Height, winDC, intersectRect.X, intersectRect.Y, Gdi32DllMethodsInvoker.TernaryRasterOperationType.SRCCOPY); #else var winRect = MyWin32InteropHelper.GetWindowRect(hwnd); Gdi32DllMethodsInvoker.BitBlt( memDC, 0, 0, winRect.Width, winRect.Height, winDC, 0, 0, Gdi32DllMethodsInvoker.TernaryRasterOperationType.SRCCOPY); #endif #endif User32DllMethodsInvoker.ReleaseDC(hwnd, winDC); memg.ReleaseHdc(memDC); } }
private static void SaveImageAsPngFile(System.Windows.Media.Imaging.WriteableBitmap bitmap, string fileName, IntPtr hwnd, System.Windows.Int32Rect clippingRect) { if (bitmap == null) { throw new ArgumentNullException("Invalid bitmap!!"); } if (!User32DllMethodsInvoker.IsWindow(hwnd)) { throw new ArgumentException("Invalid window!!"); } // クライアント矩形やユーザー定義クリッピング矩形で切り出した内容のみを保存する。 var intersectRect = MyWin32InteropHelper.CalcClientIntersectRect(hwnd, clippingRect); System.Diagnostics.Debug.Assert(intersectRect.HasArea); var tempSubBitmap = new System.Windows.Media.Imaging.WriteableBitmap(intersectRect.Width, intersectRect.Height, MyDeviceHelper.DefaultDpi, MyDeviceHelper.DefaultDpi, System.Windows.Media.PixelFormats.Pbgra32, null); try { tempSubBitmap.Lock(); bitmap.CopyPixels(new System.Windows.Int32Rect(0, 0, intersectRect.Width, intersectRect.Height), tempSubBitmap.BackBuffer, tempSubBitmap.PixelWidth * tempSubBitmap.PixelHeight * 4, tempSubBitmap.PixelWidth * 4); } catch (Exception) { throw; } finally { tempSubBitmap.Unlock(); } using (var stream = new System.IO.FileStream(fileName, System.IO.FileMode.Create, System.IO.FileAccess.Write)) { var encoder = new System.Windows.Media.Imaging.PngBitmapEncoder(); //encoder.Frames.Add(System.Windows.Media.Imaging.BitmapFrame.Create(bitmap)); encoder.Frames.Add(System.Windows.Media.Imaging.BitmapFrame.Create(tempSubBitmap)); encoder.Save(stream); } tempSubBitmap = null; }