C++ Builder 5 против Windows Vista или Подлые Модальные Окна
Проблема.
Есть старое приложение, написанное на C++ Builder 5 и собранное на Windows XP. Все, что нужно о нем знать – в определенный момент программа показывает модальное окно с помощью функции ShowMessage(). Это простой и часто используемый прием вывода сообщения, например, об ошибке.

При запуске приложения на Windows Vista что-то пошло не так. В тот момент, когда должно было появится сообщение, основное окно стало заблокированным, но модального окна я не увидел. Единственный способ остановить программу – Alt+Ctr+Del.
Щелкаю правой кнопкой по exe-файлу, Свойства, Совместимость. Выбираю “Windows XP” и пробую запустить программу снова. На этот раз я увидел модальное окно, но лишь на полсекунды. Далее все то же самое – основное окно заблокировано, модального не видно.
Итак, проблема в том, что Vista показывает модальное окно некоторых старых программ за основным, таким образом, добраться до него и нажать-таки кнопку OK совершенно невозможно.
А мне действительно нужна была эта программа и сообщения, которые она выдавала.
Имеем перед глазами следующую картинку (окно заблокировано, тыкать в него мышкой бесполезно):

Задача.
Увидеть модальное окно и нажать в нем кнопку OK для продолжения выполнения программы.
Решение.
Может, было решение и проще, но я решил вооружиться WinAPI. Известно, что есть у Windows функция ShowWindow(). Она принимает дескриптор окна и команду. Команды бывают разные, мне же достаточно двух: спрятать окно и показать окно. Однако сначала мне нужно узнать дескриптор окна; но и это не проблема – WinAPI имеет средства для перебора окон.
Я решил написать консольное Win32 приложение, которое умело бы делать три действия:
- Получить дескрипторы окон с известным заголовком
- Спрятать окно с известным дескриптором
- Показать окно с известным дескриптором
Алгоритм при появлении модального окна:
- Узнать дескриптор основного окна приложения
- Спрятать его
- Прочитать сообщение и нажать кнопку OK в модальном окне
- Показать основное окно
- Продолжить работу
Для поиска окна существует функция FindWindow(), которая позволяет найти дескриптор по заголовку. Однако в моем случае с одним и тем же именем было сразу три окна: основное, модальное и то, что в панели задач. Более того – искать окно мне хотелось не по точному совпадению заголовка, а только по началу строки. То есть вместо полного “Ввод данных” я хочу писать, скажем, “Вво”. Функция EnumWindows() вполне удовлетворила мои требования.
Итак, консольное приложение готово (исходники ниже). Запускаю его, ввожу команду EnumWindows и первые символы заголовка:

Вот результат команды:

Беру первое найденное окно и посылаю ему команду “погаснуть”:

Теперь я вижу желанное модальное окно:

Читаю, нажимаю OK. Показываю основное окно:
![]()
И продолжаю работу.
Можете скачать консольное приложение и посмотреть исходники:
#include <windows.h> #include <stdio.h> BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam) { char str[1024]; char oem[1204]; char* txt; // Получаем заголовок очередного окна. GetWindowTextA(hwnd, str, 1024); // Переводим его в кодировку OEM. CharToOemA(str, oem); // Строка поиска. txt = (char*)lParam; // Ищем по совпадению начала строки. if (strlen(str) > strlen(txt)) str[strlen(txt)] = 0; // Если совпадает - выводим HWND. if (!strcmp(str, txt)) printf("# hwnd = %d : %s\r\n", hwnd, oem); // Продолжаем поиск. return TRUE; } void CmdEnumWindows() { char title[1024]; // Запрос заголовка окна. printf("> window title: "); gets_s(title, 1024); // Кодируем из OEM. OemToCharA(title, title); // Запуск поиска. if (EnumWindows(EnumWindowsProc, (LPARAM)title)) printf("# OK\r\n"); else printf("# ERROR\r\n"); } void CmdShowWindow() { char tmp[32]; int hwnd; int nCmdShow; // Запрос HWND. printf("> hwnd: "); gets_s(tmp, 32); hwnd = atoi(tmp); // Запрос nCmdShow. printf("> nCmdShow (SW_SHOW = 5, SW_HIDE = 0): "); gets_s(tmp, 32); nCmdShow = atoi(tmp); // Вызов WinApi. if (ShowWindow((HWND)hwnd, nCmdShow)) printf("# OK\r\n"); else printf("# ERROR\r\n"); } void main() { // Цикл обработки команд. char cmd[256]; while (strcmp(cmd, "Exit")) { printf("commands: EnumWindows, ShowWindow, Exit\r\n"); printf("> "); gets_s(cmd, 256); if (!strcmp(cmd, "EnumWindows")) { CmdEnumWindows(); } else if (!strcmp(cmd, "ShowWindow")) { CmdShowWindow(); } } }
P.S. Пока писал статью, понял, как решить проблему значительно проще. Уменьшить основное окно и сдвинуть к краю экрана… Но я все равно рад возможности вспомнить WinAPI и старый добрый Си

Вчера был на конференции “Будущее Flash-платформы” в МГУ. Эта первое мероприятие такого рода, которое мне посчастливилось посетить. 
Уверен, что каждому читателю приходилось в своих программах много раз выводить на экран (или куда-нибудь еще) количество чего-нибудь. Если вы это не делали, то, вероятно, вы никогда и не писали программы и читаете наш журнал случайно
Кодикевич где-то раздобыл вычислительный кластер и не придумал ничего лучше, чем просчитать 100 факториал… Получил огрмное число, но не успокоился на этом. Он сложил все цифры в полученном результате и получил второе огромное число. Потом сложил все цифры снова и т.д. пока не получил одноразрядное число.
