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

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

10   голосов , оценка 4 из 5
Похожие ответы