::Главная страница :: С++/Си :: Статьи :: Замечание
Как из программы управлять окном которое созданно другим приложением (закрывать, сворачивать, нажимать в нем кнопки и т.д.

Выполнение этой задачи распадается на два этапа.

Сначала нужно каким-то образом определить хэндл окна, которым мы собираемся манипулировать. Основным инструментом здесь являются функции 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
Тематические ссылки
{Текстовая реклама}
Ваша ссылка Ваша ссылка

Обмен кнопками, ведение статистики, реклама.

 
{Банерная реклама}