Как определить цикл сообщений при использовании SetWinEventHook()?

#c #winapi #hook

Вопрос:

Я пытаюсь прослушивать события в окне стороннего приложения, в котором у меня нет источника.

Я не понимаю эту часть документации, в которой говорится:

Клиентский поток, вызывающий SetWinEventHook, должен иметь цикл сообщений для получения событий.

Как мне «определить» этот цикл сообщений?

В моем коде крючка он никогда не достигает switch :

 void CALLBACK WinEventProc(
    HWINEVENTHOOK hWinEventHook,
    DWORD event,
    HWND hwnd,
    LONG idObject,
    LONG idChild,
    DWORD dwEventThread,
    DWORD dwmsEventTime
)
{   
    switch (event) {
        case EVENT_SYSTEM_MINIMIZESTART:
            ...
            break;
        case EVENT_SYSTEM_MINIMIZEEND:
            ...
            break;
    }
}

HWINEVENTHOOK hWinEventHook;

int EventHook() {

    hWinEventHook = SetWinEventHook(
        EVENT_SYSTEM_MINIMIZESTART,         // eventMin
        EVENT_SYSTEM_MINIMIZEEND,           // eventMax
        NULL,                               // hmodWinEventProc
        WinEventProc,                       // pfnWinEventProc
        4834,                               // idProcess
        0,                                  // idThread
        WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS); //dwFlags

    MSG msg;
    while (GetMessage(amp;msg, NULL, 0, 0))
    {
        TranslateMessage(amp;msg);
        DispatchMessage(amp;msg);
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    std::thread t1(EventHook);
    ...
}
 

Я изменил код, как было предложено в комментариях, но теперь моя программа выходит из строя;

Он выходит из строя, когда доходит до линии:

while (GetMessage(amp;msg, NULL, 0, 0))

авария

Комментарии:

1. «Цикл сообщений» — это сокращение для вызова GetMessage и DispatchMessage повторного своевременного вызова. while Цикл в вашем примере на самом деле является циклом сообщений. В той мере, в какой существует проблема, она кроется в другом. Вы проверили SetWinEventHook , удался ли вызов? Является 4834 ли действительным идентификатор какого-либо процесса? Вы свернули или восстановили какое-либо окно, принадлежащее этому процессу?

2. @IgorTandetnik Вы имеете в виду, если hWinEventHook возвращает значение? Оно делает.

3. У вас есть цикл сообщений, но он недостаточно хорош, когда вы используете std::async. Требование состоит в том, чтобы цикл сообщений выполнялся в том же потоке, который установил крючок. Легко исправить, вам просто вообще не нужно, чтобы он был асинхронным.

4. И вам, вероятно, также необходимо скомпилировать для Windows вместо консоли, чтобы основной поток был потоком пользовательского интерфейса, а код, вероятно, должен запускаться из основного потока или, по крайней мере, из потока STA. Я думаю, что крючки несколько трудно понять и понять правильно, и если они не будут выполнены должным образом, это может сильно повлиять на стабильность системы. Лучше всего избегать их, если у вас нет другого выбора и вы не готовы много читать…

Ответ №1:

Цикл сообщений должен быть в основном, в вашем случае после вызова SetWinEventHook. И поскольку у вас нет другого способа выхода из вашей программы, вы, вероятно, захотите создать диалоговое окно с кнопкой выхода, и в обработчике для этой кнопки BN_CLICKED используйте вызов MessageBox, чтобы подтвердить, что пользователь хочет выйти, и если да, то сделайте PostQuitMessage). Подтверждение заключается в том, что в противном случае было бы слишком легко выйти.

Комментарии:

1. Я изменил сообщение, добавил отладочную информацию, я не понимаю , что вы имеете в виду, нужно включить main , я сомневаюсь, как запустить его во вторичном потоке.

Ответ №2:

Цикл сообщений, который вы показали, в полном порядке (после его перемещения EventHook() ).

«Авария» на самом деле не является катастрофой. Это просто программа внезапно завершает работу, потому что поток все еще работает, когда std::thread объект уничтожен, поскольку вы не вызывали join() или detach() не запускали его до того, как он вышел из области видимости при _tmain() выходе.

По std::thread::~thread :

Уничтожает объект потока.

Если *this имеет связанный поток ( joinable() == true ), std::terminate() вызывается.

Примечания

Объект потока не имеет связанного потока (и его безопасно уничтожать) после

  • он был сконструирован по умолчанию
  • он был перенесен с
  • join() был вызван
  • detach() был вызван

Итак, симпатичный звонок t1.join() перед _tmain() выходом:

 int _tmain(int argc, _TCHAR* argv[])
{
    std::thread t1(EventHook);
    ... 
    t1.join();
    return 0;
}
 

Комментарии:

1. Как избежать уничтожения нити? Я попытался определить t1 как глобальную переменную, и теперь я получил эту ошибку: call of an object of a class type without appropriate operator() or conversion functions to pointer-to-function type

2. Мне нужно выполнить больше кода после вызова EventHook() , вот почему я спрашиваю, как запустить его во вторичном потоке.

3. @LeandroBoinha ничто в вашем вопросе или предыдущих комментариях не говорит о том, что вы хотите запустить другой код во время выполнения крючка. Если вам нужна нить, просто вызовите join() ее перед выходом.

4. Мне не нужен поток, я пытаюсь добиться того, чтобы функция EventHook() выполнялась в потоке и одновременно выполняла остальную часть моего кода в другом потоке. Если я вызову join, он застрянет в цикле сообщений EventHook()

5. @LeandroBoinha » Мне не нужна нить » — вы противоречите себе. » Если я вызову join, то он застрянет в цикле сообщений EventHook() » — нет, этого не произойдет. join() заблокирует поток, который его вызывает, то есть поток, в котором _tmain() выполняется. EventHook() , и его цикл сообщений будет продолжать работать разблокированным в своем собственном потоке.