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?
4 Answers
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;
}
}

- 5,179
- 3
- 30
- 37
-
3Great solution. I just want to point out that sometimes PixelFormat.Format32bppArgb gives white artifacts. In that case just try to use some other format instead, such as PixelFormat.Format24bppRgb – Dave Jul 22 '12 at 07:31
-
not if its minimized though, also it makes the window you are targeting flicker when you PrintWindow() – string.Empty Sep 04 '13 at 07:23
-
3how do i use this method? PrintWindow(`what to pass here`) – Jan 08 '14 at 15:52
-
The HWND of the top level window you would like to get an image of – Maurice Flanagan Feb 27 '14 at 13:47
-
1how can I get a HWND? – alansiqueira27 Jul 10 '15 at 16:09
-
3@MauriceFlanagan i'm getting entirely black image ?why?? – Madhawa Priyashantha Sep 09 '15 at 14:44
-
@FastSnail Probably because it's a DirectX window, I think there is some way to capture them on Windows 10 but haven't looked into it – Maurice Flanagan Sep 11 '15 at 16:13
-
Thanks for this. Just in case somebody else had to look it up, The referencefor DLLimport is `using System.Runtime.InteropServices;` – MatthewD Dec 09 '15 at 18:56
-
@FastSnail I had the same issue. I guess you are starting a new process and want to capture that. The problem is, that your window is not initialized yet. This is the case even if you call `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. – VSZM Aug 06 '16 at 00:01
-
4Has the black image solution been added to this solution? I'm running into the same thing on Windows 10 – reZach Sep 05 '16 at 19:46
-
@Seva To get the HWND use this `this.Handle` – Aishwarya Shiva Feb 24 '17 at 19:09
-
2Always a black bitmap for UWP apps. I can't even seem to get DWM thumbnails for a UWP app while I'm on the same desktop as the UWP app. However, I can get the DWM thumbnail for a UWP app when I'm not on the same desktop as it. It's driving me crazy. – Michael Z. Nov 09 '17 at 02:05
-
What is the cause when the result of picture is black? – CodeGuru Sep 10 '18 at 07:50
-
1@CodeGuru: for non-UWP apps, likely the window is not visible. Check out GetWindowLongPtr with GWL_STYLE and compare against WS_VISIBLE – Thomas Weller Sep 11 '21 at 21:51
-
2I found a solution to the black image problem. Try putting in the value of 2 as the 3rd parameter (flags) for `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);` – Jargon Jul 20 '22 at 15:34
-
Thank you jargon. I was so happy I found your comment, help me out immensely. I was worried I would need to start using directx to capture the screen and rewrite everything. – Zoetyc Aug 31 '22 at 00:37
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
-
1this doesn't bring the window for the selected process to the foreground in win7, so you will get a screenshot with the active window – greenfeet Aug 13 '15 at 13:27
-
3@alconja i used your code to take a snapshot of notepad .but it took a snapshot of visual studio which was the active window .can we use this to take a picture of none active window ? – Madhawa Priyashantha Sep 09 '15 at 14:36
-
@FastSnail - I was going to suggest you try the other answer, but I see from your comments that that isn't working either... Another option might be to try to find a pinvoke method that pulls the target app/window into the foreground first. For example, [SwitchToThisWindow](http://www.pinvoke.net/default.aspx/user32.switchtothiswindow), maybe... – Alconja Sep 11 '15 at 01:21
-
Does it work if the window is hidden or minimized? CopyScreen would create exception if screen is locked right? – daisy Jan 01 '20 at 13:48
-
@daisy - it's been a while, but no I think this one just takes a screenshot of a region of the screen, so won't work if the window isn't visible. The answer below claims to work in that instance (not sure about locked screens though, sorry). – Alconja Jan 01 '20 at 22:04
-
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;
}

- 3,928
- 4
- 29
- 35
-
3after skimming through this i noticed that, if the process closes during the `Thread.Sleep(1000);` you will have an infinite loop. – string.Empty Sep 04 '13 at 07:32
-
@NicolasTyler is right. To clarify, the issue is that calling `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. – Drew Noakes Oct 11 '16 at 15:32
-
3
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).

- 22,191
- 9
- 88
- 137