|
::Главная страница :: С++/Си :: Статьи :: Замечание |
Выполнение этой задачи распадается на два этапа.
Сначала нужно каким-то образом определить хэндл окна, которым мы собираемся манипулировать. Основным инструментом здесь являются функции FindWindow(Ex), которые ищут окно по заданному классу и/или заголовку. В определении и того, и другого сильно помогает программа Spy++. Рассмотрим пример поиска HWND стандартной кнопки "Пуск". Сначала используем Spy++, чтобы определить классы панели задач и самой кнопки; оказывается, их имена "Shell_TrayWnd" и "Button" соответственно. Затем используем
FindWindow(Ex). HWND hWnd; hWnd = FindWindow("Shell_TrayWnd", NULL); hWnd = FindWindowEx(hWnd, NULL, "Button", NULL); if (IsWindow(hWnd)) { // Кнопка найдена, работаем с ней } |
Ещё один набор функций, которые могут помочь в поиске хэндла чужого окна - это EnumChildWindows, EnumThreadWindows и EnumWindows, перечисляющие все окна, принадлежащие заданному окну, все окна заданного потока и все окна в системе соответственно. За описанием этих функций следует обратиться к документации. Кроме перечисленного можно упомянуть случай, когда приложение специально проектируется для взаимодействие с другим посредством обмена сообщениями. Например, одно приложение запускает другое, а затем обменивается с ним данными посредством WM_COPYDATA. В этом случае вполне уместно передать хэндл окна (это 4-хбайтовое целое) как параметр командной строки. После того, как хэндл окна определён, можно переходить ко второму этапу - управлению окном. Многие функции позволяют работать с окном, вне зависимости от того, какому процессу оно принадлежит. Характерные примеры таких функций - ShowWindow и SetForegroundWindow. Для примера рассмотрим, как спрятать кнопку "Пуск", получать хэндл которой мы уже научились.
HWND hWnd; hWnd = FindWindow("Shell_TrayWnd", NULL); hWnd = FindWindowEx(hWnd, NULL, "Button", NULL); if (IsWindow(hWnd)) { ShowWindow(hWnd, SW_HIDE); Sleep(5000); ShowWindow(hWnd, SW_SHOW); // Показываем обратно } |
Кроме использования подобных функций, можно посылать окну сообщения. Например, послав кнопке BM_CLICK (с помощью PostMessage), мы как бы нажимаем на неё. Проблемы возникают с функциями, которые позволяют работать только с окнами, созданными в том же потоке, в котором вызывается функция. В качестве примера приведу функцию DestroyWindow. Похожая проблема возникает, когда нужно "сабкласить" окно чужого процесса. В этих случаях необходимо внедрить свой код в чужой процесс и выполнить его в чужом потоке; удобнее всего сделать эт о, если код оформлен в виде DLL.
Существует несколько способов внедрить DLL в чужой процесс. Я покажу один из них; он достаточно прост и работает на всех Win32-платформах (Windows 9x, Windows NT), но в некоторых случаях недостаточно точен. Этот способ подразумевает установку хука на поток, создавший интересующее нас окно. При этом DLL, содержащая функцию хука, загружается системой в адресное пространство чужого процесса. Это как раз то, что нам нужно. А функцию хука вполне можно оставить пустой.
Рассмотрим пример DLL, которая уничтожает окно чужого процесса. (Такие вещи нужно делать, только полностью отдавая себе отчёт о возможных последствиях. Процесс, оставленный без окна, имеет хорошие шансы "рухнуть").
// _KillDll.cpp : Defines the entry point for the DLL
application. // #include <windows.h> // Создаём переменную в разделяемом сегменте, // чтобы передать HWND из программы в DLL в чужом процессе. #pragma comment(linker, "/SECTION:SHARED,RWS") #pragma data_seg("SHARED") __declspec(allocate("SHARED")) HWND hWndToKill = NULL; #pragma data_seg() BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { if (ul_reason_for_call == DLL_PROCESS_ATTACH && IsWindow(hWndToKill) && GetWindowThreadProcessId(hWndToKill, NULL) == GetCurrentThreadId()) { // Если окно существует и принадлежит текущему потоку, убиваем его. HANDLE hEvent = OpenEvent(NULL, FALSE, "{1F6C5480-155E-11d5-93A8-444553540000}"); DestroyWindow(hWndToKill); SetEvent(hEvent); CloseHandle(hEvent); } return TRUE; } // Пустая функция хука. LRESULT CALLBACK GetMsgProc(int code, WPARAM wParam, LPARAM lParam) { return 1; } extern "C" __declspec(dllexport) void KillWndNow(HWND hWnd) { if (!IsWindow(hWnd)) return; hWndToKill = hWnd; HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, "{1F6C5480-155E-11d5-93A8-444553540000}"); DWORD dwThread = GetWindowThreadProcessId(hWnd, NULL); HHOOK hHook = SetWindowsHookEx( WH_GETMESSAGE, GetMsgProc, GetModuleHandle("_KillDll.dll"), dwThread); PostThreadMessage(dwThread, WM_NULL, 0, 0); WaitForSingleObject(hEvent, INFINITE); CloseHandle(hEvent); UnhookWindowsHookEx(hHook); } |
Чтобы использовать эту DLL, просто подключите её к программе (проще всего сделать это неявным методом), а затем выполните код:
extern "C" void KillWndNow(HWND hWnd); ... HWND hWnd; // Ищем окно KillWndNow(hWnd); |
Хотя код DLL сам по себе и небольшой, в нём есть несколько тонкостей, на которые я хотел бы обратить ваше внимание. Во-первых, я поместил переменную hWndToKill в разделяемый сегмент. Поскольку функция DestroyWindow вызывается в потоке чужого процесса, необходимо предусмотреть некоторый способ передачи хэндла окна через границы процессов. Разделяемая переменная - наиболее простое средство достичь цели. Во-вторых, DLL, содержащая функцию хука, не будет спроектирована на адресное пространство чужого процесса, пока функция хука реально не понадобится. В нашем случае хук имеет тип WH_GETMESSAGE, а значит DLL не загрузится, пока поток не получит какое-либо сообщение. Поэтому я посылаю ему сообщение WM_NULL (с кодом 0), чтобы вынудить ОС загрузить DLL. В-третьих, обратите внимание на применение события для синхронизации потоков в нашем и целевом процессах. Разумеется, для этой цели можно использовать и любой другой механизм синхронизации потоков. -
Александр Шаргин rudankort@mail.ru
Тематические
ссылки
|
Ваша ссылка | Ваша ссылка |
Обмен кнопками, ведение статистики, реклама. |
|||