Out-of-context SetWinEventHook() not getting any events

Viewed 37

Why is this program not getting any events ?

This is a global (out-of-context) hook (with its message loop) that's configured for [EVENT_MIN , EVENT_MAX], it should drown in events.

Tried both Console & Window types :

#include <windows.h>
#include <iostream>
#include <thread>

using namespace std;

HWINEVENTHOOK g_hook;
bool stop = false;

// Callback function that handles events.
void CALLBACK HandleWinEvent(HWINEVENTHOOK hook, DWORD event, HWND hwnd, 
  LONG idObject, LONG idChild, 
  DWORD dwEventThread, DWORD dwmsEventTime)
{
  cout<<"Got event "<<event<<'\n'<<std::flush;
}

int EventHook() {

  cout <<"thread EventHook running\n"<<std::flush;
  g_hook = SetWinEventHook(
    EVENT_MIN , EVENT_MAX,  // Range of events.
    NULL,                   // Handle to DLL.
    HandleWinEvent,         // The callback.
    0, 0,                   // Process and thread IDs of interest (0 = all)
    WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS); // Flags.
  cout <<"Hooked\n"<<std::flush;

  MSG msg;
  while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
    if(stop) return 0;
  }
  return 0;
}

// Both console & window are eventless
//int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){
//  int argc;
//  auto *argv = CommandLineToArgvW(GetCommandLine(), &argc);
int main(int argc, char **argv){

  std::thread t1(EventHook); cout<<"start thread EventHook\n"<<std::flush;
  //Sleep(std::wcstoul(argv[1],nullptr,10) * 1000);
  Sleep(std::strtoul(argv[1],nullptr,10) * 1000);

  stop = true;
  t1.join();

  UnhookWinEvent(g_hook);
  return 0;
}

Any help appreciated. Thank you.

1 Answers

PeekMessage() is a non-blocking function. It always exits immediately, returning TRUE if a message was retrieved, otherwise returning FALSE. So, as soon as your thread's message queue is empty (as it likely is initially), PeekMessage() will return FALSE causing your loop to end, and thus your program to exit.

In other words, your program is not likely to run long enough to receive any events.

You need to use GetMessage() instead. It will allow your loop to block your thread waiting for new messages to arrive.

However, that means your stop variable by itself is insufficient to end your thread, as you might not receive new messages for awhile (if at all) after you have changed the variable's value. The loop may be blocked on GetMessage() and not see the variable's new value right away (if at all).

You can work around that by using PostThreadMessage() to post a benign message (like WM_NULL) to the thread's message queue to "wake up" GetMessage() if it is blocked. Or, just get rid of the stop variable altogether and post a WM_QUIT message to your thread's message queue, which will cause GetMessage() to return 0.

Also, you need to call UnhookWinEvent() in the same thread that calls SetWinEventHook().

Try something more like this:

#include <windows.h>
#include <iostream>
#include <thread>

using namespace std;

HWINEVENTHOOK g_hook;
bool stop = false;
DWORD g_tid = 0;

// Callback function that handles events.
void CALLBACK HandleWinEvent(HWINEVENTHOOK hook, DWORD event, HWND hwnd, 
  LONG idObject, LONG idChild, 
  DWORD dwEventThread, DWORD dwmsEventTime)
{
  cout<<"Got event "<<event<<'\n'<<std::flush;
}

int EventHook() {

  g_tid = GetCurrentThreadId();
  WakeByAddressSingle(&g_tid);

  cout <<"thread EventHook running\n"<<std::flush;
  g_hook = SetWinEventHook(
    EVENT_MIN , EVENT_MAX,  // Range of events.
    NULL,                   // Handle to DLL.
    HandleWinEvent,         // The callback.
    0, 0,                   // Process and thread IDs of interest (0 = all)
    WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS); // Flags.
  if (!g_hook) {
    cout <<"Error in Hook\n"<<std::flush;
    return;
  }
  cout <<"Hooked\n"<<std::flush;

  MSG msg;
  while (GetMessage(&msg, NULL, 0, 0))
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
    if (stop) break;
  }

  UnhookWinEvent(g_hook);
  return 0;
}

// Both console & window are eventless
//int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){
//  int argc;
//  auto *argv = CommandLineToArgvW(GetCommandLine(), &argc);
int main(int argc, char **argv){

  std::thread t1(EventHook);
  cout<<"start thread EventHook\n"<<std::flush;

  DWORD tid = 0;
  WaitOnAddress(&g_tid, &tid, sizeof(DWORD), INFINITE);

  //Sleep(std::wcstoul(argv[1],nullptr,10) * 1000);
  Sleep(std::strtoul(argv[1],nullptr,10) * 1000);

  PostThreadMessage(g_tid, WM_NULL, 0, 0);
  t1.join();

  return 0;
}

Alternatively:

#include <windows.h>
#include <iostream>
#include <thread>

using namespace std;

HWINEVENTHOOK g_hook;
DWORD g_tid = 0;

// Callback function that handles events.
void CALLBACK HandleWinEvent(HWINEVENTHOOK hook, DWORD event, HWND hwnd, 
  LONG idObject, LONG idChild, 
  DWORD dwEventThread, DWORD dwmsEventTime)
{
  cout<<"Got event "<<event<<'\n'<<std::flush;
}

int EventHook() {

  g_tid = GetCurrentThreadId();
  WakeByAddressSingle(&g_tid);

  cout <<"thread EventHook running\n"<<std::flush;
  g_hook = SetWinEventHook(
    EVENT_MIN , EVENT_MAX,  // Range of events.
    NULL,                   // Handle to DLL.
    HandleWinEvent,         // The callback.
    0, 0,                   // Process and thread IDs of interest (0 = all)
    WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS); // Flags.
  if (!g_hook) {
    cout <<"Error in Hook\n"<<std::flush;
    return;
  }
  cout <<"Hooked\n"<<std::flush;

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

  UnhookWinEvent(g_hook);
  return 0;
}

// Both console & window are eventless
//int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){
//  int argc;
//  auto *argv = CommandLineToArgvW(GetCommandLine(), &argc);
int main(int argc, char **argv){

  std::thread t1(EventHook);
  cout<<"start thread EventHook\n"<<std::flush;

  DWORD tid = 0;
  WaitOnAddress(&g_tid, &tid, sizeof(DWORD), INFINITE);

  //Sleep(std::wcstoul(argv[1],nullptr,10) * 1000);
  Sleep(std::strtoul(argv[1],nullptr,10) * 1000);

  PostThreadMessage(g_tid, WM_QUIT, 0, 0);
  t1.join();

  return 0;
}
Related