Screenshoot C#

25 июн. 2009 г. | | |

А поведать в этой маленькой заметке я хочу о скриншотах. Точнее о том, как их можно программно реализовать на C#.
Скриншот, как известно, это моментальный снимок экрана. И делается он одним волшебным нажатием кнопки PrintScreen (PrtScr). Ловкость рук и никакого мошейничества... Далее снимок экрана помещается в буффер обмена Windows, откуда его можно вытащить не менее волшебным нажатием сочетания кнопок Ctrl+V или воспользовавшись пунктом меню "Вставить" графического редактора (или не графического.... каждый вставляет куда хочет ;) ).
А теперь, как это реализовать на C#.

PictureBox screenshootBox = new PictureBox();
//Нажмем на кнопочку PrintScreen
SendKeys.Send("{PRTSC}");
//А теперь получим изображение из буффера обмена
screenshootBox.Image = Clipboard.GetImage();


Вот и всё. Опять же только ловкость рук....
Правда, тут есть маленький нюанс - вышепреведенный код сделает скриншот исключительно окна активного приложения, так как класс SendKeys
предоставляет методы для отправки сообщений о нажатиях клавиш исключительно текущему приложению (http://msdn.microsoft.com/ru-ru/library/system.windows.forms.sendkeys.aspx). Если мы хотим получить скриншот всего экрана, то нужно поступить несколько иначе.
Во-первых, нам надо сделать активным десктоп, а после этого уже послать ему сообщение о нажатии кнопки. Но для начала нам надо получить хэндл десктопа.
Для этого потребуются функции FindWindow, FindWindowEx, SetForegroundWindow, которые нужно для начала импортировать из user32.DLL.

[DllImport("user32.DLL")]
public static extern IntPtr FindWindow(string lpszClass, string lpszWindow);
[DllImport("user32.DLL")]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern bool SetForegroundWindow(IntPtr hwnd);


Только после этого их можно использовать в коде. Код для получения скриншота всего экрана прост:

//ищем хэндл дэсктопа
IntPtr vHandle = FindWindow("Progman", null);
vHandle = FindWindowEx(vHandle, IntPtr.Zero, "SHELLDLL_DefView", null);
vHandle = FindWindowEx(vHandle, IntPtr.Zero, "SysListView32", null);
//сделаем активным окно по полученному хэндлу
SetForegroundWindow(vHandle);
//Нажмем на кнопочку PrintScreen
SendKeys.Send("{PRTSC}"); 


Вот и все.
Маленькую тестовую программку вы можете скачать отсюда (36 KB)

UPD: Спасибо за комментарии. Описанный выше способ получения скриншота лучше не использовать, а статью рассматривать как альтернативный способ выстрелить себе в ногу для сильных духом:
1) если кто-то очистит буффер обмена между вызовами MakeSreenshoot() и GetScreenshoot(), то сделанный снимок экрана пропадёт.
2) весь этот зоопарк с импортированием системных win32 dll в .net и вызовом методов оттуда не есть хорошая практика. Например, теряется кроссплатформенность кода (зависимость от win platform dll), нужны привелегии для импортирования dll ( SecurityPermission(SecurityPermissionFlag.UnmanagedCode) ). Гораздо лучше и проще воспользоваться средствами, предоставляемыми библиотекой .net. Несколько примеров таких решений можно найти в комментариях.

3 коммент.:

Анонимный комментирует...

Омг... Что-то внутри перевернулось когда увидел этот код. Без обид. Если бы во время работы какая-то программа портила буффер обмена - она немедленно попала бы в корзину. В интернете море примеров - и все они используют связку GetDC(0) + BitBlt. Правда чтобы полностью повторить результат нажатия Print Screen нужно добавить флаг CAPTUREBLT (о чем часто забывают)

Unknown комментирует...

Спасибо большое за комментарий ) На выходных подправлю пост, чтобы не вводить никого в заблуждение )

Гость комментирует...

Size ScreenSize = Screen.PrimaryScreen.Bounds.Size;
Bitmap image = new Bitmap(ScreenSize.Width, ScreenSize.Height);
using (Graphics g = Graphics.FromImage(image))
g.CopyFromScreen(Point.Empty, Point.Empty, ScreenSize);
pictureBox1.Image = image;

Отправить комментарий