Search code examples
c#winapipinvoke

SetWinEventHook Only Hitting Callback Once Per App Run


I have this class that listens for when the CTRL + ALT + DEL screen is visible. When I run my app it only works one time then the callback is never hit again. Occasionally it appears to cause a memory leak giving me a System.AccessViolationException. I know this Exception is related to this hook because when I remove the hook code it never raises this exceptions.

What am I doing wrong? Why would it only execute the callback once?

public static void StartListeningForDesktopSwitch()
{
    SetWinEventHook(EVENT_SYSTEM_DESKTOPSWITCH, EVENT_SYSTEM_DESKTOPSWITCH,
        IntPtr.Zero, EventCallback, 0, 0, WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNTHREAD);
}

public static void EventCallback(IntPtr hWinEventHook, uint eventType,
    IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
    //do stuff when secure desktop is shown or hidden
    Log.LogEvent("Info", "Secure Desktop Event", "", "", null);
}

public delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType,
    IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);


[DllImport("user32.dll")]
static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr
        hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess,
    uint idThread, uint dwFlags);

const uint WINEVENT_OUTOFCONTEXT = 0x0000;
const uint WINEVENT_SKIPOWNTHREAD = 0x0001;
const uint EVENT_SYSTEM_DESKTOPSWITCH = 0x0020;

I'm calling this static class from Main() like this:

WindowEventHook.StartListeningForDesktopSwitch();


Solution

  • How did you use the outside variable?

    Try storing the callback in a static variable to keep it from being GCed. Like this:

    public static class WindowEventHook
    {
        private static readonly WinEventDelegate callback = EventCallback;
    
        public static void StartListeningForDesktopSwitch()
        {
            SetWinEventHook(EVENT_SYSTEM_DESKTOPSWITCH, EVENT_SYSTEM_DESKTOPSWITCH,
                IntPtr.Zero, callback, 0, 0, WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNTHREAD);
        }
    
        ...
    }