Захват хэндла чужого окна

Тема в разделе "MS Visual C++", создана пользователем admin, 8 авг 2003.

  1. admin

    admin Well-Known Member

    Регистрация:
    8 авг 2003
    Сообщения:
    2.811
    Симпатии:
    0
    Можно ли из моей программы управлять окном которое создано
    другим приложением (закрывать, сворачивать, нажимать в нем кнопки
    и т.д.), если да то как?
     
  2. Гость

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

    Сначала нужно каким-то образом определить хэндл окна, которым
    мы собираемся манипулировать. Основным инструментом здесь
    являются функции FindWindow(Ex), которые ищут окно по заданному
    классу и/или заголовку. В определении и того, и другого сильно
    помогает программа Spy++. Рассмотрим пример поиска HWND
    стандартной кнопки "Пуск". Сначала используем Spy++, чтобы
    определить классы панели задач и самой кнопки; оказывается, их
    имена "Shell_TrayWnd" и "Button" соответственно. Затем
    используем FindWindow(Ex).
    Код (Text):
    HWND hWnd;
    hWnd = FindWindow("Shell_TrayWnd", NULL);
    hWnd = FindWindowEx(hWnd, NULL, "Button", NULL);

    if (IsWindow(hWnd))
    {
    // Кнопка найдена, работаем с ней
    }
    Ещё один набор функций, которые могут помочь в поиске хэндла
    чужого окна - это EnumChildWindows, EnumThreadWindows и
    EnumWindows, перечисляющие все окна, принадлежащие заданному
    окну, все окна заданного потока и все окна в системе
    соответственно. За описанием этих функций следует обратиться к
    документации. Кроме перечисленного можно упомянуть случай,
    когда приложение специально проектируется для взаимодействие с
    другим посредством обмена сообщениями. Например, одно
    приложение запускает другое, а затем обменивается с ним данными
    посредством WM_COPYDATA. В этом случае вполне уместно передать
    хэндл окна (это 4-хбайтовое целое) как параметр командной
    строки. После того, как хэндл окна определён, можно переходить
    ко второму этапу - управлению окном. Многие функции позволяют
    работать с окном, вне зависимости от того, какому процессу оно
    принадлежит. Характерные примеры таких функций - ShowWindow и
    SetForegroundWindow. Для примера рассмотрим, как спрятать
    кнопку "Пуск", получать хэндл которой мы уже научились.
    Код (Text):
    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.
    //
    Код (Text):
    #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, просто подключите её к программе
    (проще всего сделать это неявным методом), а затем выполните
    код:
    Код (Text):
    extern "C" void KillWndNow(HWND hWnd);
    ...
    HWND hWnd;
    // Ищем окно
    KillWndNow(hWnd);
    Хотя код DLL сам по себе и небольшой, в нём есть несколько
    тонкостей, на которые я хотел бы обратить ваше внимание.
    Во-первых, я поместил переменную hWndToKill в разделяемый
    сегмент. Поскольку функция DestroyWindow вызывается в потоке
    чужого процесса, необходимо предусмотреть некоторый способ
    передачи хэндла окна через границы процессов. Разделяемая
    переменная - наиболее простое средство достичь цели. Во-вторых,
    DLL, содержащая функцию хука, не будет спроектирована на
    адресное пространство чужого процесса, пока функция хука
    реально не понадобится. В нашем случае хук имеет тип
    WH_GETMESSAGE, а значит DLL не загрузится, пока поток не
    получит какое-либо сообщение. Поэтому я посылаю ему сообщение
    WM_NULL (с кодом 0), чтобы вынудить ОС загрузить DLL.
    В-третьих, обратите внимание на применение события для
    синхронизации потоков в нашем и целевом процессах. Разумеется,
    для этой цели можно использовать и любой другой механизм
    синхронизации потоков.
     
  3. Kernel

    Kernel Гость

    Это смотря для каких целей. Если надо найти конкретное окно то FindWindow как и описано выше. А если просто так, то лучше использовать EnumWindows. Я в своё время от нефиг делать на этом EnumWindows написал небольшую прогу которая ВСЕМ окошкам устанавливала круглые регионы и периодически перемещала их по экрану ... смотрелось довольно весело :D
     
  4. admin

    admin Well-Known Member

    Регистрация:
    8 авг 2003
    Сообщения:
    2.811
    Симпатии:
    0
    Kernel
    Спасибо. Попробую :D
     
Загрузка...

Поделиться этой страницей