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