Создание и анализ скриншота (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.

Получаю хендл окна приложения:
Листинг программы
  1. _handle = NativeMethods.FindWindow(null, WindowName);

2.

Извлекаю дескриптор дисплейного контекста (не до конца понимаю, что это):
Листинг программы
  1. _windowDc = NativeMethods.GetDC(_handle);

3.

Получаю размер окна приложения:
Листинг программы
  1. NativeMethods.GetWindowRect(_handle, ref _rc);
  2. _gWidth = _rc.Right - _rc.Left;
  3. _gHeight = _rc.Bottom - _rc.Top;

4.

Создаю картинку нужного размера (например 100x100):
Листинг программы
  1. using (var pImage = new Bitmap(tWidth, tHeight, PixelFormat.Format32bppRgb))

5.

Вот тут я не уверен, что так можно, но у меня так работает. Создаю графику, при помощи которой "наполняю" картинку нужным содержанием:
Листинг программы
  1. using (var graph = Graphics.FromImage(pImage))

6.

Получаю дескриптор контекста устройства этой графики (опять же, не очень понимаю, что это):
Листинг программы
  1. var hdcDest = graph.GetHdc();

7.

Создаю картинку (sWidth, sHeight - это координаты левого верхнего угла моего квадрата 100x100):
Листинг программы
  1. NativeMethods.BitBlt(hdcDest, 0, 0, _gWidth, _gHeight, _windowDc, sWidth, sHeight, 13369376);

8.

Освобождаю дескриптор:
Листинг программы
  1. graph.ReleaseHdc(hdcDest);

9.

Ищу необходимые пиксели на получившейся картинке:
Листинг программы
  1. var pix = pImage.GetPixel(x, y);
Кусок текста целиком:
Листинг программы
  1. private static IntPtr _windowDc = IntPtr.Zero;
  2. private const int Srcopy = 13369376;//(0x00CC0020);
  3. private const string WindowName = "BlueStacks App Player";
  4. private void ScreenCapturePre()
  5. {
  6. _handle = IntPtr.Zero;
  7. _handle = NativeMethods.FindWindow(null, WindowName);
  8. NativeMethods.GetWindowRect(_handle, ref _rc);
  9. _gWidth = _rc.Right - _rc.Left;
  10. _gHeight = _rc.Bottom - _rc.Top;
  11. }
  12. private bool ScreenCapture(int tWidth, int tHeight, int sWidth, int sHeight, int rMin, int rMax, int gMin, int gMax, int bMin, int bMax)
  13. {
  14. try
  15. {
  16. if (_windowDc == IntPtr.Zero)
  17. _windowDc = NativeMethods.GetDC(_handle);
  18. if (_windowDc == IntPtr.Zero)
  19. {
  20. MessageBox.Show(@"Не удаётся получить область окна приложения");
  21. return true;
  22. }
  23. using (var pImage = new Bitmap(tWidth, tHeight, PixelFormat.Format32bppRgb))
  24. {
  25. using (var graph = Graphics.FromImage(pImage))
  26. {
  27. var hdcDest = graph.GetHdc();
  28. try
  29. {
  30. NativeMethods.BitBlt(hdcDest, 0, 0, _gWidth, _gHeight, _windowDc, sWidth, sHeight, Srcopy);
  31. }
  32. finally
  33. {
  34. graph.ReleaseHdc(hdcDest);
  35. }
  36. }
  37. Thread.Sleep(100);
  38. using (var bmp = pImage) //Можно напрямую использовать pImage - ничего не меняется.
  39. {
  40. if (rMin == 230)
  41. bmp.Save("red_check.jpg");
  42. else if (rMin == 240)
  43. bmp.Save("white_check.jpg");
  44. else if (rMin == 180)
  45. bmp.Save("orange_check.jpg");
  46. var y = tHeight / 2;
  47. var xFrom = tWidth / 2 - 10;
  48. var xTo = tWidth / 2 + 11;
  49. var count = 0;
  50. for (var x = xFrom; x < xTo; x++)
  51. {
  52. var pix = bmp.GetPixel(x, y);
  53. if (pix.R >= rMin && pix.R <= rMax &&
  54. pix.G >= gMin && pix.G <= gMax &&
  55. pix.B >= bMin && pix.B <= bMax) //200, 50, 50 - Red!
  56. {
  57. return true;
  58. }
  59. if (pix.R + pix.G + pix.B == 0)
  60. count++;
  61. }
  62. if (count > 15)
  63. return true;
  64. }
  65. }
  66.  
  67. }
  68. catch
  69. {
  70. //ignore
  71. }
  72. return false;
  73. } //end of ScreenCapture
Поправочка. Чтобы получить правый нижний угол, надо ставить Width * 0.75, Height * 0.75. Я так и делаю, конечно. Но, всё съезжает... Чуть ниже, чуть выше. Приходится играться с коэффициентами, ставить что-то вроде Width * 0.68, Height * 0.71.

Решение задачи: «Создание и анализ скриншота (BitBlt)»

textual
Листинг программы
  1. [DllImport(@"dwmapi.dll")]
  2. private static extern int DwmGetWindowAttribute(IntPtr hwnd, int dwAttribute, out Rect pvAttribute, int cbAttribute);

ИИ поможет Вам:


  • решить любую задачу по программированию
  • объяснить код
  • расставить комментарии в коде
  • и т.д
Попробуйте бесплатно

Оцени полезность:

10   голосов , оценка 4 из 5

Нужна аналогичная работа?

Оформи быстрый заказ и узнай стоимость

Бесплатно
Оформите заказ и авторы начнут откликаться уже через 10 минут
Похожие ответы