Как получить активное ChildWindow приложения?
У меня есть проблема: у меня есть обработчик mainWindow определенного приложения, и я хочу имитировать нажатие клавиши на этом приложении...
Я использую API-вызовы sendMessage/postMessage для этого. Причина, по которой я не использую функцию.Net SendKeys или keybd_event интерфейса win32, заключается в том, что они имитируют нажатие клавиш на глобальном уровне. В моем случае целевое приложение не является активным сверху (другое приложение может работать на более высоком уровне z, следовательно, охватывая целевое приложение).
Проблема с sendMessage и postMessage заключается в том, что вы должны передать обработчик точного дочернего окна, в котором вы хотите, чтобы клавиша была нажата. Например, в блокноте, если я отправляю ключ обработчику mainWindow, ничего не происходит, я должен отправить ключ обработчику дочернего окна, которое в основном состоит из белого холста, куда вы можете писать.
Получение обработчика для активного дочернего окна является проблемой. В начале я использовал вызовы API GetTopWindow или GetWindow(GW_CHILD), поскольку он возвращает самое активное дочернее окно. Я продолжал вызывать GetWindow(GW_CHILD) до тех пор, пока у меня не появилось дочернее окно, в котором больше не было дочерних окон. Это работает нормально для некоторых приложений, таких как блокнот или рисование. Однако в некоторых случаях (например, Firefox) это не работает. Причиной этого является то, что родительское окно имеет всю область Firefox, а его дочернее окно имеет открытую веб-страницу (например, Google). Поэтому, когда я запрашиваю самое активное дочернее окно главного окна, оно возвращает единственное дочернее окно, которое соответствует области веб-страницы. Это работает, только если это активное окно (например, если пользователь что-то пишет в текстовом поле определенной страницы). Но если активна, скажем, адресная строка, она не работает, потому что активное окно не дочернее окно, а фактически родительское... и я не могу получить эту информацию программно.
Я действительно нашел способ сделать это, используя API-вызов GetGUIThreadInfo, используя следующий код:
// get thread of the main window handle of the process
var threadId = GetWindowThreadProcessId(firefox.MainWindowHandle, IntPtr.Zero);
// get gui info
var info = new GUITHREADINFO();
info.cbSize = (uint)Marshal.SizeOf(info);
if (!GetGUIThreadInfo(threadId, out info))
throw new Win32Exception();
// send the letter W to the active window
PostMessage(info.hwndActive, WM_KEYDOWN, (IntPtr)Keys.W, IntPtr.Zero);
И это работает очень хорошо: если адресная строка активна, она отправляет букву "W" в адресную строку. Если поисковый текстовый ящик Google активен, он отправляет ему букву "W"... Отлично! Однако этот метод не может быть использован мной по простой причине: если целевое приложение не является активным окном операционной системы, структура ThreadInfo становится пустой. Например, если я нацеливаюсь на firefox, он работает, если firefox активен (самое верхнее приложение, сфокусированное / активное), но если, скажем, блокнот находится поверх firefox, он не работает, он не может получить активный обработчик окна.
Я знаю, что могу решить эту проблему с помощью вызова API setForegroundWindow, чтобы активировать целевое приложение, а затем перехватить обработчик активного дочернего окна, но я не хотел выводить целевое приложение на передний план.
Я также пробовал другие методы, такие как API-вызовы AttachThreadInput() и GetFocus(), которые также работают, но имеют ту же проблему: если целевое приложение не является активным приложением Windows, оно не работает.
Так что в основном мне нужно найти какой-то способ получить обработчик для активного дочернего окна приложения, даже если это приложение не является активным сверху.
Есть идеи? Спасибо
2 ответа
Если все остальное терпит неудачу, вот другая идея: Вы могли бы хотеть рассмотреть использование хуков WH_CBT или WH_CALLWNDPROC, чтобы контролировать, какое дочернее окно целевого потока было сфокусировано последним.
Установите хук CBT (WH_CBT) и прослушайте уведомление HCBT_SETFOCUS. Или используйте перехват WH_CALLWNDPROC и прослушайте сообщение WM_SETFOCUS.
Не делайте много в ловушке, иначе вы затормозите системные ресурсы. Просто сохраните необходимую информацию и опубликуйте собственное сообщение для последующей обработки.