Создание и анализ скриншота (BitBlt) - C#
Формулировка задачи:
Добрый день.
Делаю скриншот части окна стороннего приложения и анализирую получившийся результат.
Например, я хочу анализировать квадратик 100 x 100 пикселей в правом нижнем углу окна приложения.
Очень много информации удалось найти, но понимания, как правильно, и почему именно так, нет.
Использую как .Net 3.5, так и .Net 4.0. Хотелось бы, чтобы правильно работало везде.
Сразу вопросы:
Кусок текста целиком:
Вопрос 1. Можно ли, в принципе, реализовывать задачу так (ниже подробное описание)?
Вопрос такой потому, что я часто встречал реализации через SelectObject, CreateCompatibleDC, CreateCompatibleBitmap и так далее.Вопрос 2. Иногда в bitmap попадает чернота, почему?
Именно иногда. То есть обычно всё работает, но, порой, просто чёрная картинка получается.Вопрос 3. Почему координаты части изображения в BitBlt так странно выставляются?
Поясню. Берём картинку Width=400, Height=400. Хотим получить квадрат в правом углу размером 100x100. Координаты для второй частиBitBlt
какие надо ставить? Я ставлю Width * 0.25, Height * 0.25. И... Нет, не получается на выходе то, что требуется. Всё время область куда-то смещена. Почему так? Мне каждый раз приходится подбирать коэффициенты, чтобы вырезалось именно то, что я хочу. Итак, как делаю я:1.
Получаю хендл окна приложения:_handle = NativeMethods.FindWindow(null, WindowName);
2.
Извлекаю дескриптор дисплейного контекста (не до конца понимаю, что это):_windowDc = NativeMethods.GetDC(_handle);
3.
Получаю размер окна приложения:NativeMethods.GetWindowRect(_handle, ref _rc); _gWidth = _rc.Right - _rc.Left; _gHeight = _rc.Bottom - _rc.Top;
4.
Создаю картинку нужного размера (например 100x100):using (var pImage = new Bitmap(tWidth, tHeight, PixelFormat.Format32bppRgb))
5.
Вот тут я не уверен, что так можно, но у меня так работает. Создаю графику, при помощи которой "наполняю" картинку нужным содержанием:using (var graph = Graphics.FromImage(pImage))
6.
Получаю дескриптор контекста устройства этой графики (опять же, не очень понимаю, что это):var hdcDest = graph.GetHdc();
7.
Создаю картинку (sWidth, sHeight - это координаты левого верхнего угла моего квадрата 100x100):NativeMethods.BitBlt(hdcDest, 0, 0, _gWidth, _gHeight, _windowDc, sWidth, sHeight, 13369376);
8.
Освобождаю дескриптор:graph.ReleaseHdc(hdcDest);
9.
Ищу необходимые пиксели на получившейся картинке:var pix = pImage.GetPixel(x, y);
private static IntPtr _windowDc = IntPtr.Zero; private const int Srcopy = 13369376;//(0x00CC0020); private const string WindowName = "BlueStacks App Player"; private void ScreenCapturePre() { _handle = IntPtr.Zero; _handle = NativeMethods.FindWindow(null, WindowName); NativeMethods.GetWindowRect(_handle, ref _rc); _gWidth = _rc.Right - _rc.Left; _gHeight = _rc.Bottom - _rc.Top; } private bool ScreenCapture(int tWidth, int tHeight, int sWidth, int sHeight, int rMin, int rMax, int gMin, int gMax, int bMin, int bMax) { try { if (_windowDc == IntPtr.Zero) _windowDc = NativeMethods.GetDC(_handle); if (_windowDc == IntPtr.Zero) { MessageBox.Show(@"Не удаётся получить область окна приложения"); return true; } using (var pImage = new Bitmap(tWidth, tHeight, PixelFormat.Format32bppRgb)) { using (var graph = Graphics.FromImage(pImage)) { var hdcDest = graph.GetHdc(); try { NativeMethods.BitBlt(hdcDest, 0, 0, _gWidth, _gHeight, _windowDc, sWidth, sHeight, Srcopy); } finally { graph.ReleaseHdc(hdcDest); } } Thread.Sleep(100); using (var bmp = pImage) //Можно напрямую использовать pImage - ничего не меняется. { if (rMin == 230) bmp.Save("red_check.jpg"); else if (rMin == 240) bmp.Save("white_check.jpg"); else if (rMin == 180) bmp.Save("orange_check.jpg"); var y = tHeight / 2; var xFrom = tWidth / 2 - 10; var xTo = tWidth / 2 + 11; var count = 0; for (var x = xFrom; x < xTo; x++) { var pix = bmp.GetPixel(x, y); if (pix.R >= rMin && pix.R <= rMax && pix.G >= gMin && pix.G <= gMax && pix.B >= bMin && pix.B <= bMax) //200, 50, 50 - Red! { return true; } if (pix.R + pix.G + pix.B == 0) count++; } if (count > 15) return true; } } } catch { //ignore } return false; } //end of ScreenCapture
Поправочка. Чтобы получить правый нижний угол, надо ставить Width * 0.75, Height * 0.75.
Я так и делаю, конечно. Но, всё съезжает... Чуть ниже, чуть выше.
Приходится играться с коэффициентами, ставить что-то вроде Width * 0.68, Height * 0.71.
Решение задачи: «Создание и анализ скриншота (BitBlt)»
textual
Листинг программы
[DllImport(@"dwmapi.dll")] private static extern int DwmGetWindowAttribute(IntPtr hwnd, int dwAttribute, out Rect pvAttribute, int cbAttribute);
ИИ поможет Вам:
- решить любую задачу по программированию
- объяснить код
- расставить комментарии в коде
- и т.д