SetWinEventHook Only Hitting Callback Once Per App Run
Asked Answered
S

1

0

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();

Straphanger answered 2/7, 2017 at 22:53 Comment(9)
You are letting the callback delegate get GC'd.Jewett
What can I do to avoid it from getting GC'd? I just tried adding an outside variable to the delegate and I still have the problem.Straphanger
It is buggy, the EventCallback delegate object needs to be stored in a static variable or kept alive with GCHandle.Alloc(). There is a dedicated debugging assistant to diagnose this bug, it warned you about this, you have to turn it back on. And above all, don't write this code, it is already provided by the framework.Stuart
I could not get it to work with any SessionSwithReasonStraphanger
@HansPassant This has nothing to do with a session switch, and there is no system event in .NET that monitors desktop switches. You have to do it manually using P/Invoke.Comstockery
Hmm, maybe "Secure Desktop Event" is pretty misleading. SystemEvents does notify that, although there is no way, either way, to know that it was the secure desktop. Well, whatever.Stuart
Maybe the wrong name Secure Desktop, but it helps me remember what it is.Straphanger
@HansPassant it works this way, but your opinion matters too. If you can think of another way to accomplish this then I'd love to see it in an answer. Others could benefit from it too.Straphanger
@HansPassant Please tell me which of the events in this list monitors a desktop switch: msdn.microsoft.com/de-de/library/…Comstockery
Q
3

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);
    }

    ...
}
Quaternity answered 3/7, 2017 at 1:57 Comment(1)
That's exactly what I needed! Thanks!Straphanger

© 2022 - 2024 — McMap. All rights reserved.