Преамбула Так получилось, что я уже давно пользуюсь мышами Logitech — MX300 и MX310. У них есть дополнительная кнопка над колесиком, на которую можно назначать различные функции.
В старых драйверах (MouseWare) в число этих функций входил «Вызов приложения», который переключался на предыдущее активное окно — примерно то же самое, что происходит, если однократно нажать Alt+Tab. Эта возможность мне сразу понравилась: часто возникает ситуация, когда нужно переключиться на какое-то окно, сделать там что-то (например, скопировать строку) и вернуться назад (соответственно, вставить эту скопированную строку).
Alt+Tab в этом случае оказывается менее удобным (поскольку левую руку необходимо убрать из комбинации Ctrl+C, а затем вернуть в прежнее положение, чтобы нажать Ctrl+V).
Но потом я установил Windows XP x64, и оказалось, что MouseWare недоступна для 64-битных систем.
Для MX310 была обнаружена более современная утилита SetPoint, но функции «Вызов приложения» в ней больше нет. К счастью, нам удалось настроить отправку комбинации клавиш Alt+Tab на нужную кнопку, но мигание окна со списком задач в момент переключения немного раздражало.
Итак, преодолев лень, мне удалось написать небольшую утилиту, которая помогла устранить этот недостаток.
Амбула
По сути, от утилиты требовалось сделать только одно: висеть в фоне и, получив сигнал о нажатии кнопки, переключиться на «соседнее» окно.Проблема оказалась не такой тривиальной, как я думал изначально.
Непосредственно получить сигнал о нажатии кнопки не представляется возможным, так как в списке доступных действий в SetPoint нет такого действия, как «отправить сигнал о нажатии шестой кнопки».
Поэтому пришлось немного схитрить: нажатием кнопки эмулировать какую-то комбинацию клавиш, а программа эту комбинацию ловит. Естественно, вам нужно выбрать что-то, не используемое в обычной работе; Я выбрал Ctrl+Alt+Shift+Z. Вторая трудность заключалась в выборе подходящего окна.
Перемещаться по Z-стеку приложений можно с помощью вызова GetWindow(hwnd, GW_HWNDNEXT), но среди этих окон есть большое количество тех, на которые переключаться не нужно.
Например, невидимка.
Даже если вы оставите только видимые окна, все равно останется много других окон верхнего уровня, которых нет в обычном списке Alt+Tab. Здесь я не смог найти удовлетворительного решения.
Мне удалось погуглить один вариант Переполнение стека , но добиться с его помощью корректного вывода окон мне не удалось.
Также есть источники TaskSwitchXP Однако попытка адаптировать код под мои нужды не удалась (в список попали лишние окна).
Я не мог до конца понять код, поэтому либо я сделал что-то не так, либо код изначально не был предназначен для такого нецелевого использования.
(Впрочем, я продолжу разбираться.
) В итоге я провёл собственное исследование и определил эмпирические условия выбора «правильных» окон (эти условия я перечислю в конце).
Полученный программный код умещается на одной странице:
int WinMainCRTStartup(void) { if (!RegisterHotKey(NULL, 0, MOD_CONTROL | MOD_SHIFT | MOD_ALT, 'Z')) return 1; MSG msg = {0}; while (GetMessage(&msg, NULL, 0, 0)) { if (msg.message == WM_HOTKEY) { HWND current_wnd = GetForegroundWindow(); if (current_wnd == NULL) continue; // Find top-level owner of the current window HWND owner = current_wnd; do { current_wnd = owner; owner = GetWindow(current_wnd, GW_OWNER); } while ((owner != NULL) && IsWindowVisible(owner)); // Find next window in Z-stack to switch to do { current_wnd = GetWindow(current_wnd, GW_HWNDNEXT); if (current_wnd == NULL) break; owner = GetWindow(current_wnd, GW_OWNER); } while (!IsWindowVisible(current_wnd) || ((GetWindowLongPtr(current_wnd, GWL_EXSTYLE) & WS_EX_TOOLWINDOW) != 0) || ((owner != NULL) && IsWindowVisible(owner))); if (current_wnd != NULL) SetForegroundWindow(current_wnd); } } UnregisterHotKey(NULL, 0); return 0; }
Небольшие технические комментарии
- Функция называется WinMainCRTStartup , поскольку ЭЛТ не задействована, поэтому я отключил ее.
В результате скомпилированная программа занимает 3072 байта.
Подробнее об этом можно прочитать на РСДН .
- Эмпирические условия выбора «правильного» окна таковы.
Окно должно:
- быть видимым;
- не иметь расширенного стиля WS_EX_TOOLWINDOW ;
- Окно владельца для этого окна должно отсутствовать или быть невидимым.
- Первый цикл (поиск «корневого» окна-владельца) нужен для того, чтобы справиться с ситуацией, когда текущее окно является «неправильным» окном.
Например, в программе ABBYY Lingvo все окна, в том числе карточные, «неправильные»: это окна верхнего уровня, и владельцем каждого из них является некое фиктивное окно, имеющее флаг видимости, но с нулевыми размерами.
.
Если текущее окно является таким карточным окном, то цикл GetWindow(current_wnd, GW_HWNDNEXT) сначала обращается к этому фиктивному окну Lingvo и, поскольку оно формально «правильное», активирует его.
Те.
Переключение на другое приложение не происходит. Можно было бы вместо цикла использовать GetAncestor(current_wnd, GA_ROOTOWNER), но в этом случае видимость окон не учитывается и результат неверный.
- К сожалению, этот эвристический алгоритм не идеален.
В частности, некорректно работает с окнами Excel 2003 (легко застревает в одном окне).
Правда, отчасти в этом виноват сам Excel, который организует окна каким-то совершенно непонятным образом, так что даже стандартный Alt+Tab может зациклиться на одном окне и вам придется нажать Tab дважды, чтобы пропустить этот цикл.
Я все еще планирую разгадать эту тайну.
Также нужно быть осторожным с виртуальными машинами, поскольку при перехвате ввода с клавиатуры комбинация клавиш перейдет на виртуальную машину, а не на хост-систему.
- Текущая версия не обеспечивает корректный выход из программы (поэтому, вообще говоря, вызов UnregisterHotKey в конце программы излишен, но я оставил это для красоты :-) ).
Если вам необходимо периодически прекращать выполнение программы, но вы не хотите убивать процесс, можно добавить регистрацию еще одного глобального сочетания клавиш, а в цикле обработки сообщения проверять, какая комбинация была нажата, и, если она есть а не Ctrl+Alt+Shift+Z, — выйти из цикла.
-
Что Ждет Индустрию Видеоигр В Будущем?
19 Oct, 24 -
Лучшие Настольные Пк Для Игр
19 Oct, 24 -
Почтовая Кухня №1: Dns
19 Oct, 24 -
Оценка Интересов Пользователей
19 Oct, 24 -
Проблемы С Выпуском Firefox 3
19 Oct, 24 -
Субстики #88
19 Oct, 24