73

I want to capture the screen in my code to get an image - like using the 'print screen' button on the keyboard .

Does anyone have an idea how to do this? I have no starting point.

4444
  • 3,541
  • 10
  • 32
  • 43
Omar Abid
  • 15,753
  • 28
  • 77
  • 108

6 Answers6

121

You can use the Graphics.CopyFromScreen() method.

//Create a new bitmap.
var bmpScreenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width,
                               Screen.PrimaryScreen.Bounds.Height,
                               PixelFormat.Format32bppArgb);

// Create a graphics object from the bitmap.
var gfxScreenshot = Graphics.FromImage(bmpScreenshot);

// Take the screenshot from the upper left corner to the right bottom corner.
gfxScreenshot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X,
                            Screen.PrimaryScreen.Bounds.Y,
                            0,
                            0,
                            Screen.PrimaryScreen.Bounds.Size,
                            CopyPixelOperation.SourceCopy);

// Save the screenshot to the specified path that the user has chosen.
bmpScreenshot.Save("Screenshot.png", ImageFormat.Png);
Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
Gary Willoughby
  • 50,926
  • 41
  • 133
  • 199
  • 1
    Your answer is incorrect please update it with ( g.CopyFromScreen( 0, 0, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy); – Mohsen Afshin Jan 27 '13 at 06:12
  • 3
    Yes, take a look at the .NET Screen object to get the other screens. Notice in the above call i use `Screen.PrimaryScreen` to get the primary screen? You can use `Screen.AllScreens` to get an array of them all. `Screen.AllScreens[n].Bounds.Width` etc... – Gary Willoughby Aug 01 '13 at 08:34
  • Here we are in 2016 and this still works perfectly. Thanks! – Erick G. Hagstrom Jun 24 '16 at 16:19
  • 3
    if change display setting **Change the size of all items** to **larger**, the code can only capture part of the screen. how to solve? – Lei Yang Mar 30 '17 at 07:50
10

I had two problems with the accepted answer.

  1. It doesn't capture all screens in a multi-monitor setup.
  2. The width and height returned by the Screen class are incorrect when display scaling is used and your application is not declared dpiAware.

Here's my updated solution using the Screen.AllScreens static property and calling EnumDisplaySettings using p/invoke to get the real screen resolution.

using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;

class Program
{
    const int ENUM_CURRENT_SETTINGS = -1;

    static void Main()
    {
        foreach (Screen screen in Screen.AllScreens)
        {
            DEVMODE dm = new DEVMODE();
            dm.dmSize = (short)Marshal.SizeOf(typeof(DEVMODE));
            EnumDisplaySettings(screen.DeviceName, ENUM_CURRENT_SETTINGS, ref dm);

            using (Bitmap bmp = new Bitmap(dm.dmPelsWidth, dm.dmPelsHeight))
            using (Graphics g = Graphics.FromImage(bmp))
            {
                g.CopyFromScreen(dm.dmPositionX, dm.dmPositionY, 0, 0, bmp.Size);
                bmp.Save(screen.DeviceName.Split('\\').Last() + ".png");
            }
        }
    }

    [DllImport("user32.dll")]
    public static extern bool EnumDisplaySettings(string lpszDeviceName, int iModeNum, ref DEVMODE lpDevMode);

    [StructLayout(LayoutKind.Sequential)]
    public struct DEVMODE
    {
        private const int CCHDEVICENAME = 0x20;
        private const int CCHFORMNAME = 0x20;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
        public string dmDeviceName;
        public short dmSpecVersion;
        public short dmDriverVersion;
        public short dmSize;
        public short dmDriverExtra;
        public int dmFields;
        public int dmPositionX;
        public int dmPositionY;
        public ScreenOrientation dmDisplayOrientation;
        public int dmDisplayFixedOutput;
        public short dmColor;
        public short dmDuplex;
        public short dmYResolution;
        public short dmTTOption;
        public short dmCollate;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
        public string dmFormName;
        public short dmLogPixels;
        public int dmBitsPerPel;
        public int dmPelsWidth;
        public int dmPelsHeight;
        public int dmDisplayFlags;
        public int dmDisplayFrequency;
        public int dmICMMethod;
        public int dmICMIntent;
        public int dmMediaType;
        public int dmDitherType;
        public int dmReserved1;
        public int dmReserved2;
        public int dmPanningWidth;
        public int dmPanningHeight;
    }
}

References:

https://stackoverflow.com/a/36864741/987968 http://pinvoke.net/default.aspx/user32/EnumDisplaySettings.html?diff=y

colton7909
  • 824
  • 12
  • 16
8
// Use this version to capture the full extended desktop (i.e. multiple screens)

Bitmap screenshot = new Bitmap(SystemInformation.VirtualScreen.Width, 
                               SystemInformation.VirtualScreen.Height, 
                               PixelFormat.Format32bppArgb);
Graphics screenGraph = Graphics.FromImage(screenshot);
screenGraph.CopyFromScreen(SystemInformation.VirtualScreen.X, 
                           SystemInformation.VirtualScreen.Y, 
                           0, 
                           0, 
                           SystemInformation.VirtualScreen.Size, 
                           CopyPixelOperation.SourceCopy);

screenshot.Save("Screenshot.png", System.Drawing.Imaging.ImageFormat.Png);
RomSteady
  • 398
  • 2
  • 13
Skhanpara
  • 91
  • 1
  • 2
  • Your code doesn't out put a proper image. Is it supposed to be a JPEG or a PNG? – Jason Foglia Aug 26 '14 at 20:11
  • 3
    Works well, but there is a general UI freeze. Incompatible for projects (as mine) which need to analyze screen 10-20 times per second. – Jurion Apr 16 '15 at 02:13
1

Try this code

Bitmap bmp = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
Graphics gr = Graphics.FromImage(bmp);
gr.CopyFromScreen(0, 0, 0, 0, bmp.Size);
pictureBox1.Image = bmp;
bmp.Save("img.png",System.Drawing.Imaging.ImageFormat.Png);
user4340666
  • 1,453
  • 15
  • 36
0
Bitmap memoryImage;
//Set full width, height for image
memoryImage = new Bitmap(Screen.PrimaryScreen.Bounds.Width,
                       Screen.PrimaryScreen.Bounds.Height,
                       PixelFormat.Format32bppArgb);
Size s = new Size(memoryImage.Width, memoryImage.Height);
Graphics memoryGraphics = Graphics.FromImage(memoryImage);
memoryGraphics.CopyFromScreen(0, 0, 0, 0, s);
string str = "";
try
{
    str = string.Format(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) +
          @"\Screenshot.png");//Set folder to save image
}
catch { };
memoryImage.save(str);
Mohammad Akbari
  • 4,486
  • 6
  • 43
  • 74
  • It is recommended to add some explanation to your code so that people reading your answer would understand better and easier. – Ibo Sep 28 '17 at 05:09
0

I had been wondering how to do the same thing in FORTRAN to save me a bunch of manual intervention using PrintScreen, paste to Paint etc. I just figured out how: I read the pixels using GETPIXEL_RGB for any section of the screen I want, and, having prepared a .bmp header and written it to the .bmp file, follow by writing the pixel data. However, I don't think it's going to capture 10 to 20 sctre

Paul
  • 114
  • 3