Как получить активное 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 ответа

Возможно, вы захотите проверить функцию EnumChildWindows.

Если все остальное терпит неудачу, вот другая идея: Вы могли бы хотеть рассмотреть использование хуков WH_CBT или WH_CALLWNDPROC, чтобы контролировать, какое дочернее окно целевого потока было сфокусировано последним.

Установите хук CBT (WH_CBT) и прослушайте уведомление HCBT_SETFOCUS. Или используйте перехват WH_CALLWNDPROC и прослушайте сообщение WM_SETFOCUS.

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

Другие вопросы по тегам