Диалоговые окна и изменения меню, не записанные в скриншоте

Я создаю приложение WinForms, которое записывает шаги внешнего процесса, делая его скриншоты каждые 500 миллисекунд. Я использую следующий код:

Bitmap bmp = new Bitmap(width, height,PixelFormat.Format32bppArgb);
Graphics g = Graphics.FromImage(bmp);
g.CopyFromScreen(rect.left,
                 rect.top,
                 0,
                 0,
                 new Size(width, height),
                 CopyPixelOperation.SourceCopy);

Код работает нормально, но единственная проблема заключается в том, что когда я открываю диалоговое окно из окна внешнего процесса (например: Открытие Save As... диалогового окна в Блокноте), скриншот зависает в исходном окне, а не отображается диалог коробка.

Моя теория заключается в том, что из-за следующего кода, который я использую, чтобы определить, потеряло ли приложение фокус, просто вернитесь к последнему сохраненному снимку экрана:

if (GetForegroundWindow() != proc.MainWindowHandle) //proc is just a process from system process list by Process.GetProcessesByName()
{
   return LastScreenShot;
}
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
private static extern IntPtr GetForegroundWindow();

Но этот код необходим для отображения пользователю только приложения, которое записывается не на любое другое приложение, которое перетаскивается внутри области записи или части рабочего стола внутри области записи. Кроме того, когда я нажимаю меню, он иногда показывает, что меню застыло в затухающем положении, иногда не отображается вообще или показывается, но навигационная подсветка не отображается на скриншоте.

Как я могу решить эту проблему?

Здесь задается аналогичный вопрос Захват экрана не запечатлеет диалоговые окна в моем приложениино это не решает мою проблему, потому что ответ использует тот же код и мое приложение не снимает скриншот всего рабочего стола.

Ответ 1

Если вы не сможете схватить этот дескриптор окна диалогового окна и определить, будет ли ваш дочерний элемент дескриптора вам интересным (маловероятным), вам в основном придется с ним кататься.

Вы можете создать латентность, чтобы можно было продолжать снимать снимок экрана в течение заданного периода времени, когда возникает диалоговое окно, в надежде, что элемент управления будет возвращен исходному дескриптору по истечении заданного периода времени, и только если он Не возвращайся после этого периода времени, чтобы остановить процесс захвата.

Или

Вы можете получить информацию о процессе для дескриптора окна, используя что-то вроде этого....

Найти идентификатор процесса с помощью дескриптора окна

... если процесс владения является тем же самым, что приложение, вероятно, все еще имеет фокус (если не происходит что-то очень странное).

Ответ 2

Но этот код необходим для показа пользователю только приложения, которое записывается не любое другое приложение, которое перетаскивается внутри области записи или части рабочего стола внутри записи область.


Я получаю дескрипторы для дочерних окон, но вопрос в том, как включить их в скриншот с основным окном?

Я думаю, что в этом случае единственным вариантом было бы захватить оба экрана (главное окно и диалог) и объединить их самостоятельно, используя BitBlt() или что-то подобное.

Также имейте в виду, что диалог можно перемещать за пределами области главного окна или даже на второй экран. Поэтому, даже если вы могли бы захватить как один выстрел, возможно, это не то, что вы хотите. Объединив их самостоятельно, вы можете поместить диалог обратно на главный экран.

Конечно, вам нужно будет сделать захват на дескрипторе окна (используя PrintWindow()), а не на экране или на рабочем столе. См. здесь и здесь для некоторых примеров того, как это сделать.


if (GetForegroundWindow() != proc.MainWindowHandle)

Вероятно, это причина, по которой диалог не был захвачен (т.е. никакой захват вообще не выполняется), но даже если вы его исправите или удалите, другое окно можно перетащить внутри области, даже когда диалог открыт.