C# SetWinEventHook每次运行应用程序时仅点击回调一次

C# SetWinEventHook每次运行应用程序时仅点击回调一次,c#,winapi,pinvoke,C#,Winapi,Pinvoke,我有一个类,它在CTRL+ALT+DEL屏幕可见时侦听。当我运行我的应用程序时,它只工作一次,然后再也不会点击回调。有时,它似乎会导致内存泄漏,给我一个系统.AccessViolationException。我知道这个异常与这个钩子有关,因为当我删除钩子代码时,它不会引发这个异常 我做错了什么?为什么它只执行一次回调 public static void StartListeningForDesktopSwitch() { SetWinEventHook(EVENT_SYSTEM_DES

我有一个类,它在CTRL+ALT+DEL屏幕可见时侦听。当我运行我的应用程序时,它只工作一次,然后再也不会点击回调。有时,它似乎会导致内存泄漏,给我一个
系统.AccessViolationException
。我知道这个异常与这个钩子有关,因为当我删除钩子代码时,它不会引发这个异常

我做错了什么?为什么它只执行一次回调

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;
我从Main()调用这个静态类,如下所示:


WindowEventHook.StartListeningForDesktopSwitch()

如何使用外部变量

尝试将回调存储在静态变量中,以防止它被GCed。像这样:

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

    ...
}

如何使用外部变量

尝试将回调存储在静态变量中,以防止它被GCed。像这样:

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

    ...
}

您正在让回调委托获取GC'd。如何避免它获取GC'd?我刚刚尝试将外部变量添加到委托中,但仍然存在问题。这是一个错误,EventCallback委托对象需要存储在静态变量中,或者使用GCHandle.Alloc()保持活动状态。有一个专门的调试助手来诊断这个bug,它警告你,你必须重新打开它。最重要的是,不要写这段代码,它已经存在了。我无法让它与任何
SessionWithReason
@HansPassant一起工作。这与会话交换机无关,并且.NET中没有监视桌面交换机的系统事件。您必须使用P/Invoke手动执行此操作。您正在让回调委托获取GC'd。如何避免它获取GC'd?我刚刚尝试将外部变量添加到委托中,但仍然存在问题。这是一个错误,EventCallback委托对象需要存储在静态变量中,或者使用GCHandle.Alloc()保持活动状态。有一个专门的调试助手来诊断这个bug,它警告你,你必须重新打开它。最重要的是,不要写这段代码,它已经存在了。我无法让它与任何
SessionWithReason
@HansPassant一起工作。这与会话交换机无关,并且.NET中没有监视桌面交换机的系统事件。您必须使用P/Invoke手动完成。这正是我所需要的!谢谢这正是我需要的!谢谢