Создание и анализ скриншота (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);
ИИ поможет Вам:
- решить любую задачу по программированию
- объяснить код
- расставить комментарии в коде
- и т.д