Могу ли я получить растровое изображение произвольного окна в другом процессе приложения?
-
26-09-2019 - |
Вопрос
Я пытаюсь автоматизировать стороннее приложение Win32, где я хочу захватить графический контент определенного окна в определенных временных интервалах. Я нахожусь в ранних этапах этого, и я в настоящее время пытаюсь использовать Microsoft UI Automation API через C #, чтобы сделать большую часть взаимодействия между моим клиентским приложением и внешним приложением. Теперь я могу получить внешнее приложение, чтобы сделать то, что я хочу, чтобы это сделать, но теперь я хочу захватить графику из определенного окна, который, кажется, какой-то сторонний контроль над владельцем. Как я могу это сделать? Окно, которое я хочу захватить, это отмеченный красным прямоугольником в этом изображении:
У меня есть реализация такого рода работ, но он зависит от использования UI внешнего приложения, и это не гарантировано для меня, поэтому я бы предпочел найти что-то более общее.
var p = Process.Start("c:\myapp.exe");
var mainForm = AutomationElement.FromHandle(p.MainWindowHandle);
// "workspace" below is the window whose content I want to capture.
var workspace = mainForm.FindFirst(TreeScope.Descendents,
new PropertyCondition(AutomationElement.ClassNameProperty, "AfxFrameOrView70u"));
var rect = (Rect) workspace.GetCurrentPropertyValue(AutomationElement.BoundingRectangleProperty);
using (var bmp = new Bitmap((int)rect.Width, (int)rect.Height))
{
using (var g = Graphics.FromImage(bmp))
{
g.CopyFromScreen((int)rect.Left, (int)rect.Top, 0, 0, new Size((int)rect.Width, (int)rect.Height));
bmp.Save(@"c:\screenshot.png", ImageFormat.Png);
}
}
Вышеуказанное работает достаточно хорошо, когда автоматическое приложение находится сверху, но он просто вслепую копирует экран в прямоугольнике, поэтому мой код на Mercy, что происходит, чтобы работать на машине и может покрывать окно моего приложения.
Я прочитал несколько предложений, чтобы отправить WM_PRINT
сообщение для окна. Этот вопрос ответ Через несколько месяцев назад казались многообещающими, но когда я использую этот код, я просто получаю белый прямоугольник без фактического содержимого моего контроля.
var prop = (int)workspace.GetCurrentPropertyValue(AutomationElement.NativeWindowHandleProperty);
var hwnd = new IntPtr(prop);
using ( var bmp2 = new Bitmap((int)rect.Width, (int)rect.Height))
{
using (Graphics g = Graphics.FromImage(bmp2))
{
g.FillRectangle(SystemBrushes.Control, 0, 0, (int)rect.Width, (int)rect.Height);
try
{
SendMessage(hwnd, WM_PRINT, g.GetHdc().ToInt32(), (int)(DrawingOptions.PRF_CHILDREN | DrawingOptions.PRF_CLIENT | DrawingOptions.PRF_OWNED));
}
finally
{
g.ReleaseHdc();
}
bmp2.Save(@"c:\screenshot.bmp");
}
}
Итак, во-первых, это даже возможно для меня надежно сохранить растровое изображение содержимого окна? Если да, то, что лучше, а что не так с моим WM_PRINT
с участием SendMessage
пытаться?
Решение
Эта модификация PrintWindow
Пример API На сайте pinvoke.net кажется, сделали трюк.
Bitmap bmp = new Bitmap((int)rect.Width, (int)rect.Height);
Graphics memoryGraphics = Graphics.FromImage(bmp);
IntPtr dc = memoryGraphics.GetHdc();
bool success = PrintWindow(hwnd, dc, 0);
memoryGraphics.ReleaseHdc(dc);
bmp.Save(@"c:\screenshot.bmp");
Это работает, если приложение покрыто другим окном, но он не работает, если приложение минимизируется. Я думаю, что могу жить с этим.
Другие советы
Здесь нет надежный Способ получить растровое изображение из другого приложения, если это приложение не наверху. Это потому, что элементы управления приложения даже не видят, если приложение не видно, и Windows не обязательно даже помнит, что последнее известное содержимое контроля было после этого контроля теряет верхнее положение в z-ряд.
Ваша лучшая ставка состоит в том, чтобы переместить целевое приложение на переднюю часть z-порядка в то время, когда вам нужно сделать скриншот, а затем необязательно восстановить исходный z-порядок z после захвата изображения.