Нечеловеческий PrintScreen
Воскресенье, Апрель 11th, 2010 | Программирование
Руками PrintScreen делается соответствующей кнопкой на клавиатуре – в результате имеем снимок всего экрана. Если же надо сделать снимок активного окна, то мы нажимаем <Alt>+<PrintScreen>.
Все просто, когда делаешь это руками. Однако сколько проблем возникает, если это надо сделать программно…
Как я уже сказал, проблема представлена в двух ипостасях:
- Снимок всего экрана;
- Снимок конкретного окна.
Рассмотрим первый вариант развития событий – снимок всего экрана.
Опять же, казалось бы, все просто, ан нет. Некоторые люди (и я в том числе) имеют несколько мониторов, т.е. рабочий стол растянут на 2 и более мониторов. Если делать снимок экрана вручную, то проблем не возникает – в скриншот попадают все мониторы, в случае же с программной реализацией, придется сделать снимок для каждого монитора, а затем склеить их воедино.
Вот небольшая функция возвращающая снимок экрана в качестве результата:
/// <summary>
/// Screent Shot
/// </summary>
/// <param name="currentScreen">Экран с которого делается снимок</param>
/// <returns>Снимок экрана</returns>
private Bitmap TakeScreenShot(Screen currentScreen)
{
Bitmap bmpScreenShot = new Bitmap(currentScreen.Bounds.Width,
currentScreen.Bounds.Height,
PixelFormat.Format32bppArgb);
Graphics gScreenShot = Graphics.FromImage(bmpScreenShot);
gScreenShot.CopyFromScreen(currentScreen.Bounds.X,
currentScreen.Bounds.Y,
0, 0,
currentScreen.Bounds.Size,
CopyPixelOperation.SourceCopy);
return bmpScreenShot;
}В качестве параметра в функцию передается класс Screen, снимок которого она должна сделать, использовать данную функцию можно следующим образом:
// Перебираем все мониторы
foreach (Screen scr in Screen.AllScreens)
{
Image img = TakeScreenShot(scr);
}
// Получить снимок главного монитора
Image pr = TakeScreenShot(Screen.PrimaryScreen);Теперь рассмотрим другую проблему – создание снимка отдельного окна. Подводных камней тут еще больше.
Сделать снимок окна можно, только если его не перекрывает другое окно, т.к. снимок окна – снимок экрана только обрезанный по краям окна.
Решение проблемы перекрытия целевого окна каким-либо другим – сделать целевое окно текущим (фоновым/активным).
Данный способ не может решить абсолютно ВСЕ наши проблемы, т.к. перекрывающее окно может иметь статус «поверх всех окон». В таком случае необходимо сделать целевое окно также «поверх всех окон» таким образом наше окно будет выше всех окон, в том числе, и тех, что были «поверх всех окон».
К сожалению, это может продлиться не долго, т.к. другие окна могут узнать, что их «опустили» и снова выбиться вперед
, но у нас будет достаточно времени, чтобы сделать скриншот целевого окна!
Итак, код класса, позволяющего делать скриншоты окон, если известен HWND целевого окна.
public class WindowImgaeCapture
{
/// <summary>
/// Получение снимка окна
/// </summary>
/// <param name="WindowHandle">HWND окна</param>
/// <returns>Скриншот</returns>
public static Image CaptureWindow(IntPtr WindowHandle)
{
if (WindowHandle != null)
{
User32.RECT windowRect = new User32.RECT();
User32.GetWindowRect(WindowHandle, ref windowRect);
int width = windowRect.right - windowRect.left + 1;
int height = windowRect.bottom - windowRect.top + 1;
User32.SetWindowPos(WindowHandle,
(System.IntPtr)User32.HWND_TOPMOST,
0, 0, 0, 0,
User32.SWP_NOMOVE |
User32.SWP_NOSIZE |
User32.SWP_FRAMECHANGED);
IntPtr hdcSrc = User32.GetWindowDC(WindowHandle);
IntPtr hdcDest = GDI32.CreateCompatibleDC(hdcSrc);
IntPtr hBitmap = GDI32.CreateCompatibleBitmap(hdcSrc,
width,
height);
IntPtr hOld = GDI32.SelectObject(hdcDest, hBitmap);
GDI32.BitBlt(hdcDest, 0, 0,
width, height,
hdcSrc, 0, 0,
GDI32.SRCCOPY);
User32.SetWindowPos(WindowHandle,
(System.IntPtr)User32.HWND_NOTOPMOST,
0, 0, 0, 0,
User32.SWP_NOMOVE |
User32.SWP_NOSIZE |
User32.SWP_FRAMECHANGED);
GDI32.SelectObject(hdcDest, hOld);
GDI32.DeleteDC(hdcDest);
User32.ReleaseDC(WindowHandle, hdcSrc);
Image img = Image.FromHbitmap(hBitmap);
GDI32.DeleteObject(hBitmap);
return img;
}
else
return null;
}
private class User32
{
public const int SWP_FRAMECHANGED = 0x0020;
public const int SWP_NOMOVE = 0x0002;
public const int SWP_NOSIZE = 0x0001;
public const int SWP_NOZORDER = 0x0004;
public const int HWND_TOPMOST = -1;
public const int HWND_NOTOPMOST = -2;
[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 GetWindowDC(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDC);
[DllImport("user32.dll")]
public static extern IntPtr GetWindowRect(IntPtr hWnd,
ref RECT rect);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetWindowPos(IntPtr hWnd,
IntPtr hWndInsertAfter,
int x, int y,
int cx, int cy,
uint uFlags);
}
private class GDI32
{
public const int SRCCOPY = 0x00CC0020;
[DllImport("gdi32.dll")]
public static extern bool BitBlt(IntPtr hObject,
int nXDest, int nYDest,
int nWidth, int nHeight, IntPtr hObjectSource,
int nXSrc, int nYSrc, int dwRop);
[DllImport("gdi32.dll")]
public static extern IntPtr CreateCompatibleBitmap(IntPtr hDC,
int nWidth,
int nHeight);
[DllImport("gdi32.dll")]
public static extern IntPtr CreateCompatibleDC(IntPtr hDC);
[DllImport("gdi32.dll")]
public static extern bool DeleteDC(IntPtr hDC);
[DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);
[DllImport("gdi32.dll")]
public static extern IntPtr SelectObject(IntPtr hDC,
IntPtr hObject);
}
}Пример использования данного класса.
Image img = WindowImageCapture.CaptureWindow(hwnd);Хочу заметить, что статус «поверх всех окон» целевому окну выдается только на момент создания скриншота, а потом оно переходит в обычное состояние (к сожалению, если целевое окно уже имело статус «поверх всех окон», то оно станет обычным окном
).
«А где взять этот самый HWND окна?» – его можно получить при помощи функции FindWindow.
FindWindow – WinAPI функция, поэтому ее необходимо импортировать в программу.
Надеюсь, был полезен
.
8 коммент. на Нечеловеческий PrintScreen
А вот я когда вставил код(снимок всех экранов), жалуется на «Элемент «PixelFormat» не существует в текущем контексте.»
А его вообще нигде нет
С окном проблем нет, пока он не делает сам снимок, снимок выходит 1КБ и размером 1Х1 черный пикс…
код такой:
IntPtr hwnd = FindWindowByCaption((IntPtr)0,currentWindowName);
Image img = ScreenshotTaker.CaptureWindow(hwnd);
img.Save(Application.StartupPath+@»\asdas.jpg», ImageFormat.Jpeg);
Кажется в FindWindowByCaption, 0 не идет
а что туда должно идти?
22.03.2011
Вот описание PixelFormat, чтобы он стал доступным надо подключить System.Drawing.Imaging.
Вообще странно что FindWindowByCaption не работает. Что у тебя за ОСь? Если при вызове возникла ошибка, попробуй получить код последней ошибки через Marshal.GetLastWin32Error, вдруг поможет вот список возможных результатов вызова данной функции. А так вообще не рекомендую пользоваться FindWindowByCaption все-таки это алиас для FindWindow – используй его напрямую (так потом будет меньше вопросов к коду от других программистов).
Спасибо за ответ ![]()
Ось – семерка, х64…
Ошибка не вылазит… Просто пикс черный ![]()
Прочитав про FindWindowByCaption узнал что в параметр класса можно ставить НУЛЛ, и он тогда будет идти по наиболее подходящему окну…
23.03.2011
FindWindowByCaption будучи псевдонимом FindWindow, имеет теже входные параметры. У FindWindow первый параметр – класс окна (строка), второй – заголовок окна (строка). В обычном режиме (если указаны оба параметра, и ниодин из них не равен NULL) FindWindow ищет окно заданного класса и с заданным заголовком окна. Если задан только один из параметров, а другой NULL, то ищется соответствие по заданному параметру. К сожалению, я не нашел ни слова о том, что будет если заданным параметрам удовлетворяет несколько окон – какое из них вернет функция?! В данном случае чтобы не надеяться на авось – получить все окна удовлетворяющие заданным требованиям, а потом самим решить какое из них подходит, для этого в WinAPI существует функция EnumWindows, как до нее добраться из C# можно найти здесь.
23.03.2011
Визуальная ошибка не вылезет, или ты смотрел результат вызова функции GetLastWin32Error? Вот меня тут смущает «Win32″, может тебе стоит попробовать Win64, я не проверял, но вдруг есть такая функция.
05.07.2011
Со скрином с монитора проблем не возникает.
А вот скриншот окна программы получается с черной рамкой по сторонам (да еще и больше обычного окна: 1033×747).
Вот например: http://img84.imageshack.us/img84/8586/59117341.jpg
Вроде и код копипастил, ничего не правил, но всё равно не получается.
В чем может быть дело, не знаете?
06.07.2011
To Therefore, странно, в моих тестах все работало без проблем. Попробуйте, сделать приложение с пустой формой задайте ему фиксированную ширину и высоту (например, 800*600) и сделайте с него скриншот, а также выведете ширину и высоту, которая была получена через GetWindowRect (позиция левого верхнего и правого нижнего угла будет помещена в переменную windowRect). Ширина будет равна windowRect.right – windowRect.left, а высота windowRect.bottom – windowRect.top. Я это все к тому говорю, что в появлении рамки может быть виноваты либо неправильное расположение полей структуры User32.RECT и тогда вы получите ширину/высоту не соответствующую заданным вами размерам формы, либо виновата тема, которая уменьшает или частично по краям делает форму прозрачной.
Оставить отзыв
Сначала зарегистрируйтесь.
Свежие записи
Наиболее интересные
- Взлом паролей пользователей ОС Windows - 62 голосов,




(4.82) - Как поставить Windows и Linux Ubuntu на нетбук - 28 голосов,




(4.86) - Чтение и запись в XML фаил (C#) - 26 голосов,




(4.73) - Решение СЛАУ. Метод Гаусса с выбором главного элемента - 22 голосов,




(4.45) - Можно ли играя в Линейку (Lineage II) заработать реальных денег? - 16 голосов,




(3.44) - Назначение клавиши Scroll Lock! А вы знаете зачем она? - 15 голосов,




(4.67) - Нахождение НОД и НОК без лишних слов - 15 голосов,




(4.27) - Раздавая файлы через торренты можно заработать?! - 13 голосов,




(3.15) - Генерация лабиринта - 11 голосов,




(5.00) - Битовые операции. Как быстро проверить является ли число степенью двойки? - 11 голосов,




(4.91)
Рубрики
- Закладки (4)
- Из жизни (34)
- Linux (6)
- Заработок (6)
- Игры (3)
- Тайм менеджмент (2)
- Программирование (52)
- Юмор (7)
Архивы
- Январь 2011 (3)
- Декабрь 2010 (2)
- Сентябрь 2010 (13)
- Август 2010 (4)
- Июль 2010 (5)
- Июнь 2010 (7)
- Апрель 2010 (6)
- Март 2010 (11)
- Февраль 2010 (24)
- Январь 2010 (12)
- Октябрь 2009 (1)
- Сентябрь 2009 (1)






22.03.2011