4

Is it preferable to register multiple SetWinEventHook() functions with the same WinEventProc() Callback function and handle each event type separately inside the function's code or as many as I want.

EDIT : I posted three different scenarios and I want to know which one is the best and why ?

Case 1 : Single callback, single delegate, multiple hooks

static WinEventDelegate SingleCallbackDelegate = new WinEventDelegate(SingleCallback);

public static void SingleCallback(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
   switch(evenType) :
   case eventId1 : // do work related to event 1
   case eventId2 : // do work related to event 2
   // etc.
}

void SetHooks()
{
    SetWinEventHook(eventId1, eventId1, IntPtr.Zero, SingleCallbackDelegate, 0, 
        0, flags);
    SetWinEventHook(eventId2, eventId2, IntPtr.Zero, SingleCallbackDelegate, 0, 
        0, flags);
}

Case 2 : Single callback, multiple delegates, multiple hooks

static WinEventDelegate CallbackDelegate1 = new WinEventDelegate(SingleCallback);

static WinEventDelegate CallbackDelegate2 = new WinEventDelegate(SingleCallback);

public static void SingleCallback(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
   switch(evenType) :
   case eventId1 : // do work related to event 1
   case eventId2 : // do work related to event 2
   // etc.
}

void SetHooks()
{
    SetWinEventHook(eventId1, eventId1, IntPtr.Zero, CallbackDelegate1, 0, 
        0, flags);
    SetWinEventHook(eventId2, eventId2, IntPtr.Zero, CallbackDelegate2, 0, 
        0, flags);
}

Case 3 : Multiple callbacks, multiple delegates, multiple hooks

static WinEventDelegate CallbackDelegate1 = new WinEventDelegate(Callback1);
public static void Callback1(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
   // do work related to event 1
}

static WinEventDelegate CallbackDelegate2 = new WinEventDelegate(Callback2);
public static void Callback1(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
   // do work related to event 2
}
void SetHooks()
{
    SetWinEventHook(eventId1, eventId1, IntPtr.Zero, CallbackDelegate1, 0, 
        0, flags);
    SetWinEventHook(eventId2, eventId2, IntPtr.Zero, CallbackDelegate2, 0, 
        0, flags);
}
JohnTube
  • 1,782
  • 27
  • 46

1 Answers1

3

There is no point to the second snippet, a single delegate is already good enough to pass to multiple SetWinEventHook() calls. There is maybe a point to the third snippet, different delegate objects are required since the callback target methods are not the same. The only benefit that I can think of is that you can make the method less beefy or customize the behavior of the callback for a specific process or thread.

It is important to keep in mind that SetWinEventHook() is very expensive, there's a great deal of overhead involved in making the callback into your program that can have a significant impact on the way other processes that run on the desktop behave. Anything you do in the callback that takes time is going to make those other processes unresponsive as well. So limiting the number of SetWinEventHook() calls you make is automatically a strong optimization goal.

You can see this back in the way the function is declared, it encourages you to use a small range of eventMin and eventMax values. A simple filter that Windows can use to figure out that making the expensive callback is unnecessary. If your callback is indeed interested in only a few of the events then calling SetWinEventHook multiple times does make sense. You'd still stick with the first snippet since the switch statement ought to be minor.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • Actually I'm only using EVENT_SYSTEM_FOREGROUND and EVENT_OBJECT_NAMECHANGE (calling SetWinEventHook() "system-wide" twice) with (WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS | WINEVENT_SKIPOWNTHREAD) flags. You remember my other [question](http://stackoverflow.com/questions/22081803/force-setwineventhook-to-be-called-from-main-thread), I wanted to avoid setting EVENT_OBJECT_NAMECHANGE for all processes but I couldn't find a way to set the hook at process creation within a same thread ! So know I'm looking for every trick to reduce footprint/overhead and blocking other processes. – JohnTube Mar 05 '14 at 09:24
  • I'm not getting "a single event reported twice" as in the code snippets every SetWinEventHook() is called with different eventId. I don't want to use [eventMin..eventMax] as it would be a big range [0x0003..0x800C] and I will detect more events than I want – JohnTube Mar 05 '14 at 09:29
  • I'm interested in reading more about your second paragraph. Any pointers to further reading? What exactly is blocked while my code is running? What are the effects of that? What are strategies to mitigate this? – Dustin Wyatt Jul 16 '21 at 20:19