3

我需要知道用户何时切换到登录屏幕(由 ctrl-alt-del 触发)以规避 WPF 中的一个讨厌的错误。我想通过在从登录屏幕返回后重新初始化我的 GUI 来解决这个错误。目前它可以工作,但我必须手动触发它。

我找到了 SystemEvents.SessionSwitch,但不幸的是,这仅在注销时触发。

如何通过形成 ctrl-alt-del 来检测登录屏幕何时显示?

4

2 回答 2

4

棘手的是,这不是会话更改,而只是桌面更改。特别是,Ctrl+Alt+Del 切换到与 Winlogon 关联的安全桌面。

我不认为你真的应该检测到这种事情(也就是说,毕竟,拥有“安全桌面”的全部意义),但你可能可以使用 Active Accessibility 钩子来做到这一点。调用该SetWinEventHook函数为事件安装事件挂钩EVENT_SYSTEM_DESKTOPSWITCH查看您收到的通知。

为此,您需要执行以下操作:

  • 确保您在客户端线程上发送消息循环以接收事件通知。对于标准的 WPF 应用程序,这应该不是问题。
  • WINEVENT_OUTOFCONTEXT考虑到您正在使用托管代码,请确保指定标志。您不希望系统尝试将包含回调函数的 DLL 注入每个进程。相反,这将导致回调函数从队列中异步调用;从托管代码的土地上安全得多。
  • 一点点 P/Invoke 魔法。为了让你开始……

    const uint WINEVENT_OUTOFCONTEXT = 0x0;
    const uint EVENT_SYSTEM_DESKTOPSWITCH = 0x0020;
    
    [DllImport("user32.dll")]
    static extern IntPtr SetWinEventHook(uint eventMin,
                                         uint eventMax,
                                         IntPtr hmodWinEventProc,
                                         WinEventDelegate lpfnWinEventProc,
                                         uint idProcess,
                                         uint idThread,
                                         uint dwFlags);
    
    delegate void WinEventDelegate(IntPtr hWinEventHook,
                                   uint event,
                                   IntPtr hwnd,
                                   int idObject,
                                   int idChild,
                                   uint dwEventThread,
                                   uint dwmsEventTime);
    
    [DllImport("user32.dll")]
    static extern bool UnhookWinEvent(IntPtr hWinEventHook);
    
于 2012-05-14T23:52:33.170 回答
3

似乎调用了开始显示登录屏幕的过程LogonUI.exe

使用Windows Management Instrumentation (WMI) 基础结构,您可以侦听启动和关闭的进程。您将需要引用System.Management程序集。

var interval = new TimeSpan( 0, 0, 1 );
const string isWin32Process = "TargetInstance isa \"Win32_Process\"";

// Listen for started processes.
WqlEventQuery startQuery
    = new WqlEventQuery( "__InstanceCreationEvent", interval, isWin32Process );
_startWatcher = new ManagementEventWatcher( startQuery );
_startWatcher.Start();
_startWatcher.EventArrived += OnStartEventArrived;

// Listen for closed processes.
WqlEventQuery stopQuery
    = new WqlEventQuery( "__InstanceDeletionEvent", interval, isWin32Process );
_stopWatcher = new ManagementEventWatcher( stopQuery );
_stopWatcher.Start();
_stopWatcher.EventArrived += OnStopEventArrived;

处理这些事件,您可以获得有关已启动或已关闭进程的信息。通过这种方式,您可以验证何时LogonUI.exe关闭,并随后触发所需的操作。

void OnStopEventArrived( object sender, EventArrivedEventArgs e )
{
    var o = (ManagementBaseObject)e.NewEvent[ "TargetInstance" ];
    string name = (string)o[ "Name" ];

    ...
}
于 2012-05-15T18:55:27.877 回答