C# - Захват содержимого экрана (.net 2.0)

Бывает много ситуаций, когда необходимо сделать снимок экрана, на пример при поддержке установленного программного обеспечения снимок экрана может очень сильно помочь в описании проблемы.

В этой статье мы рассмотрим как сделать снимок экрана, отобразить его, а так же сохранить на диск.


В далекие времена .NET Framework 1.0, что бы сделать снимок экрана приходилось прибегать к вызову Windows API. В .NET Framework 2.0 в пространстве имен System.Drawing и System.Drawing.Imaging появляются новые классы, которые, благодаря GDI+, позволяют работать с графикой, используя только .NET.

Для демонстрации создадим простую Windows Forms программу, которая будет получать снимок экрана, отображать его на форме, а так же при необходимости сохранять его в виде графического файла.

Разместим на форме 4-е кнопки с именами btnCapture, btnSaveJpg, btnSavePng, btnClose, один PictureBox с именем img, а так же SaveFileDialog с именем dlg.

Перейдем к коду, нам потребуется подключить пространства имен

System.Drawing и System.Drawing.Imaging.

using System.Drawing;
using System.Drawing.Imaging;

Теперь нам понадобится объект Bitmap (растровое изображение), который будет использоваться для хранения снимка экрана, но т.к. он будет нужен как при самом получении изображения, так и при сохранении изображения, то лучше его сделать частью класса frmMain.

public partial class frmMain : Form
{
    private Bitmap captured;

    public frmMain()
    {
        InitializeComponent();
    }
...

Далее создадим процедуру получения снимка экрана по нажатию на кнопку btnCapture. Во первых необходимо определить размер создаваемого изображения, а так же глубину цвета. Эти данные можно получить из свойств объекта Screen. В примере будет использоваться основной экран, что для варианта с одним монитором вполне годится, а в случае с несколькими мониторами необходимо использовать массив Screen.AllScreens[].

Размер изображения:

Rectangle bounds = Screen.PrimaryScreen.Bounds;

Глубина цвета - это количество бит, выделяемых на каждую точку на экране, что в свою очередь определяет количество цветов, которое может принимать эта точка. Для минимизировать использование памяти будем использовать полученное значение для создания растрового изображения:

int colourDepth = Screen.PrimaryScreen.BitsPerPixel;

Что бы использовать полученное значение глубины цвета, необходимо создать объект RixelFormat, который представляет из себя перечисление и присвоить ему соответствующее значение. Существует некоторое ограничение для значения в 8-мь бит, по этому для таких снимков экрана будет создаваться 16-и битное изображение.

PixelFormat format;
switch (colourDepth)
{
    case 8:
    case 16:
        format = PixelFormat.Format16bppRgb565;
        break;

    case 24:
        format = PixelFormat.Format24bppRgb;
        break;

    case 32:
        format = PixelFormat.Format32bppArgb;
        break;

    default:
        format = PixelFormat.Format32bppArgb;
        break;

}

Теперь, зная значение глубины цвета и размера изображения, можно инициализировать объект bitmap, которому ранее было присвоено имя captured:

captured = new Bitmap(bounds.Width, bounds.Height, format);

Переходим к использованию GDI+, создадим поверхность для рисования, которая будет связана с нашим растровым изображением.

Graphics gdi = Graphics.FromImage(captured);

Ну и наконец сделаем снимок экрана, в этом нам поможет метод CopyFromScreen, в него передаются 5-ть параметров, первые два это координаты левого верхнего пикселя копируемого изображения, следующие два это координаты места вставки изображения в "холст" GDI+, последний параметр это размер копируемой области.

gdi.CopyFromScreen(bounds.Left, bounds.Top, 0, 0, bounds.Size);

Ну а теперь можно вывести полученное изображение в PictureBox на нашей форме:

img.BackgroundImageLayout = ImageLayout.Zoom;
img.BackgroundImage = captured;

Вся процедура обработки нажатия на кнопку btnCapture:

private void btnCapture_Click(object sender, EventArgs e)
{
 Rectangle bounds = Screen.PrimaryScreen.Bounds;
 int colourDepth = Screen.PrimaryScreen.BitsPerPixel;
 PixelFormat format;
 switch (colourDepth)
 {
     case 8:
     case 16:
         format = PixelFormat.Format16bppRgb565;
         break;

     case 24:
         format = PixelFormat.Format24bppRgb;
         break;

     case 32:
         format = PixelFormat.Format32bppArgb;
         break;

     default:
         format = PixelFormat.Format32bppArgb;
         break;
 }
 captured = new Bitmap(bounds.Width, bounds.Height, format);
 Graphics gdi = Graphics.FromImage(captured);
 gdi.CopyFromScreen(bounds.Left, bounds.Top, 0, 0, bounds.Size);
 img.BackgroundImageLayout = ImageLayout.Zoom;
 img.BackgroundImage = captured;
}

Теперь создадим процедуры сохранения полученного изображения в файл. Сохранение в формате jpeg при нажатии на кнопке btnSaveJpg. Проверяем инициализирован ли объект bitmap, если да, то открываем диалоговое окно сохранения файла в котором настроен фильтр на отображение только изображений в формате jpeg, если файл выбран, то сохраняем, используя метод Save объекта Bitmap.

private void btnSaveJpg_Click(object sender, EventArgs e)
{
 if (captured != null)
 {
     dlg.Filter = "Jpeg image|*.jpg|All files|*.*";
     dlg.Title = "Save captured screen as jpeg";
     if (dlg.ShowDialog() == DialogResult.OK)
     {
          captured.Save(dlg.FileName, ImageFormat.Jpeg);
     }
 }
}

Сохранение в формате png при нажатии на кнопке btnSavePng.

private void btnSavePng_Click(object sender, EventArgs e)
{
 if (captured != null)
 {
     dlg.Filter = "PNG image|*.png|All files|*.*";
     dlg.Title = "Save captured screen as png";
     if (dlg.ShowDialog() == DialogResult.OK)
     {
         captured.Save(dlg.FileName, ImageFormat.Png);
     }
 }
}

Вот и все.

Comments: 3

arxont
  
Спасибо за пример, я ещё добавил чтобы окно при снятии сворачивалось.
Анонимный
  
Сергей, напишите статью, как сделать скриншот с приложений использующих directx(на c#).
Поскольку этим методом, я так понимаю, сохранится, лишь черный экран.
если можете, оповестите меня в случае написания данной статьи. dan-ver@ukr.net
Юрий
  
Наверно не
captured = new Bitmap(bounds.Width, bounds.Height, format);
а
Bitmap captured = new Bitmap(bounds.Width, bounds.Height, format);