So che posso ottenere lo screenshot dell’intero schermo usando Graphics.CopyFromScreen (). Tuttavia, cosa succede se voglio solo lo screenshot di un’applicazione specifica?
Ecco un codice per iniziare:
public void CaptureApplication(string procName) { var proc = Process.GetProcessesByName(procName)[0]; var rect = new User32.Rect(); User32.GetWindowRect(proc.MainWindowHandle, ref rect); int width = rect.right - rect.left; int height = rect.bottom - rect.top; var bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb); Graphics graphics = Graphics.FromImage(bmp); graphics.CopyFromScreen(rect.left, rect.top, 0, 0, new Size(width, height), CopyPixelOperation.SourceCopy); bmp.Save("c:\\tmp\\test.png", ImageFormat.Png); } private class User32 { [StructLayout(LayoutKind.Sequential)] public struct Rect { public int left; public int top; public int right; public int bottom; } [DllImport("user32.dll")] public static extern IntPtr GetWindowRect(IntPtr hWnd, ref Rect rect); }
Funziona, ma ha bisogno di miglioramenti:
PrintWindow win32 api acquisirà una bitmap della finestra anche se la finestra è coperta da altre windows o se è fuori dallo schermo:
[DllImport("user32.dll")] public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect); [DllImport("user32.dll")] public static extern bool PrintWindow(IntPtr hWnd, IntPtr hdcBlt, int nFlags); public static Bitmap PrintWindow(IntPtr hwnd) { RECT rc; GetWindowRect(hwnd, out rc); Bitmap bmp = new Bitmap(rc.Width, rc.Height, PixelFormat.Format32bppArgb); Graphics gfxBmp = Graphics.FromImage(bmp); IntPtr hdcBitmap = gfxBmp.GetHdc(); PrintWindow(hwnd, hdcBitmap, 0); gfxBmp.ReleaseHdc(hdcBitmap); gfxBmp.Dispose(); return bmp; }
Il riferimento a RECT sopra può essere risolto con la seguente class:
[StructLayout(LayoutKind.Sequential)] public struct RECT { private int _Left; private int _Top; private int _Right; private int _Bottom; public RECT(RECT Rectangle) : this(Rectangle.Left, Rectangle.Top, Rectangle.Right, Rectangle.Bottom) { } public RECT(int Left, int Top, int Right, int Bottom) { _Left = Left; _Top = Top; _Right = Right; _Bottom = Bottom; } public int X { get { return _Left; } set { _Left = value; } } public int Y { get { return _Top; } set { _Top = value; } } public int Left { get { return _Left; } set { _Left = value; } } public int Top { get { return _Top; } set { _Top = value; } } public int Right { get { return _Right; } set { _Right = value; } } public int Bottom { get { return _Bottom; } set { _Bottom = value; } } public int Height { get { return _Bottom - _Top; } set { _Bottom = value + _Top; } } public int Width { get { return _Right - _Left; } set { _Right = value + _Left; } } public Point Location { get { return new Point(Left, Top); } set { _Left = value.X; _Top = value.Y; } } public Size Size { get { return new Size(Width, Height); } set { _Right = value.Width + _Left; _Bottom = value.Height + _Top; } } public static implicit operator Rectangle(RECT Rectangle) { return new Rectangle(Rectangle.Left, Rectangle.Top, Rectangle.Width, Rectangle.Height); } public static implicit operator RECT(Rectangle Rectangle) { return new RECT(Rectangle.Left, Rectangle.Top, Rectangle.Right, Rectangle.Bottom); } public static bool operator ==(RECT Rectangle1, RECT Rectangle2) { return Rectangle1.Equals(Rectangle2); } public static bool operator !=(RECT Rectangle1, RECT Rectangle2) { return !Rectangle1.Equals(Rectangle2); } public override string ToString() { return "{Left: " + _Left + "; " + "Top: " + _Top + "; Right: " + _Right + "; Bottom: " + _Bottom + "}"; } public override int GetHashCode() { return ToString().GetHashCode(); } public bool Equals(RECT Rectangle) { return Rectangle.Left == _Left && Rectangle.Top == _Top && Rectangle.Right == _Right && Rectangle.Bottom == _Bottom; } public override bool Equals(object Object) { if (Object is RECT) { return Equals((RECT)Object); } else if (Object is Rectangle) { return Equals(new RECT((Rectangle)Object)); } return false; } }
Sulla base della risposta di Alconja, ho apportato alcuni miglioramenti:
[StructLayout(LayoutKind.Sequential)] public struct Rect { public int left; public int top; public int right; public int bottom; } [DllImport("user32.dll")] private static extern int SetForegroundWindow(IntPtr hWnd); private const int SW_RESTORE = 9; [DllImport("user32.dll")] private static extern IntPtr ShowWindow(IntPtr hWnd, int nCmdShow); [DllImport("user32.dll")] public static extern IntPtr GetWindowRect(IntPtr hWnd, ref Rect rect); public Bitmap CaptureApplication(string procName) { Process proc; // Cater for cases when the process can't be located. try { proc = Process.GetProcessesByName(procName)[0]; } catch (IndexOutOfRangeException e) { return null; } // You need to focus on the application SetForegroundWindow(proc.MainWindowHandle); ShowWindow(proc.MainWindowHandle, SW_RESTORE); // You need some amount of delay, but 1 second may be overkill Thread.Sleep(1000); Rect rect = new Rect(); IntPtr error = GetWindowRect(proc.MainWindowHandle, ref rect); // sometimes it gives error. while (error == (IntPtr)0) { error = GetWindowRect(proc.MainWindowHandle, ref rect); } int width = rect.right - rect.left; int height = rect.bottom - rect.top; Bitmap bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb); Graphics.FromImage(bmp).CopyFromScreen(rect.left, rect.top, 0, 0, new Size(width, height), CopyPixelOperation.SourceCopy); return bmp; }
Potresti cercare in P / Invocare il modo win32 di farlo, un articolo in questo senso … una specie di.
Fondamentalmente, passa attraverso la difficoltà di impostare una DC in una bitmap e inviare WM_PRINT alla finestra dell’applicazione in questione. È piuttosto cattivo, tutto sumto, ma potrebbe funzionare per te.
Funzioni che potrebbero essere necessarie: SendMessage , GetDC , CreateCompatibleBitmp e SelectObject .
Non posso dire di averlo mai fatto prima, ma è così che attaccherei il problema. (Beh, probabilmente lo farei in puro C ma ancora, all’incirca nel modo in cui lo attaccherei).