I know I can get the screenshot of the entire screen using Graphics.CopyFromScreen(). However, what if I just want the screenshot of a specific application?
Here's some code to get you started:
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);
using (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);
}
It works, but needs improvement:
- You may want to use a different mechanism to get the process handle (or at least do some defensive coding)
- If your target window isn't in the foreground, you'll end up with a screenshot that's the right size/position, but will just be filled with whatever is in the foreground (you probably want to pull the given window into the foreground first)
- You probably want to do something other than just save the bmp to a temp directory
If you want to print form screen you can use
PrintWindow(Handle);
The PrintWindow win32 api will capture a window bitmap even if the window is covered by other windows or if it is off screen:
[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;
}
The reference to RECT above can be resolved with the following 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;
}
}
what to pass here
) –
Dorso using System.Runtime.InteropServices;
–
Rosemari Process.WaitForIdle()
. I am a simple man and wrapped the extern call to GetWindowRect(hwnd, out rc);
into a do-while loop, with rc.Width == 0 || rc.Height == 0
as the exit condition. –
Peritoneum this.Handle
–
Covalence PrintWindow
. After some digging I found the following in the chromium source: "The PW_RENDERFULLCONTENT flag is undocumented, but works starting in Windows 8.1. It allows for capturing the contents of the window that are drawn using DirectComposition." UINT flags = PW_CLIENTONLY | PW_RENDERFULLCONTENT;
and so I do: int PW_CLIENTONLY = 0x1; int PW_RENDERFULLCONTENT = 0x2; PrintWindow(hwnd, hdcBitmap, PW_CLIENTONLY | PW_RENDERFULLCONTENT);
–
Masquerade Here's some code to get you started:
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);
using (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);
}
It works, but needs improvement:
- You may want to use a different mechanism to get the process handle (or at least do some defensive coding)
- If your target window isn't in the foreground, you'll end up with a screenshot that's the right size/position, but will just be filled with whatever is in the foreground (you probably want to pull the given window into the foreground first)
- You probably want to do something other than just save the bmp to a temp directory
Based on Alconja's answer, I made a few improvements:
[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;
}
Thread.Sleep(1000);
you will have an infinite loop. –
Orme GetWindowRect
with an HWND that is no longer valid will always return zero, meaning that the while
loop in this answer will never exit and just burn CPU forever, which is a pretty serious bug. But otherwise I think this answer is an elegant solution. Maybe just limit to a fixed number of retries, and maybe sleeping a little in between. Or don't retry in this method. –
Ona Graphics
object. –
Ona You could look into P/Invoking the win32 way of doing this, an article to this effect... sort of.
Basically, go through the trouble of setting up a DC to a bitmap and send WM_PRINT to the application window in question. Its pretty nasty, all told, but may work for you.
Functions you may need: SendMessage, GetDC, CreateCompatibleBitmp, and SelectObject.
I can't say I've ever done this before, but this is how I'd attack the problem. (Well, I'd probably do it in pure C but still; roughly the way I'd attack it).
© 2022 - 2024 — McMap. All rights reserved.