Currently I'm using the below code to test if the CTRL+ALT+DEL screen is visible and it is working as expected. The problem is that polling this info consumes CPU and I'm looking for an event based option. Does anyone know another way to detect if this screen is visible?
I only need to know when this screen is closed. I don't really care when it opens. Just that it had been open and is now closed.
To be honest, I found this code and I'm not exactly sure how it is specific, if at all, to the screen I'm referring to. It appears to look for any process creation and deletion events. What this means is that this screen must be opening a new process. Knowing that process name would be helpful too.
var interval = new TimeSpan(0, 0, 3);
const string isWin32Process = "TargetInstance isa \"Win32_Process\"";
// Listen for started processes.
WqlEventQuery startQuery = new WqlEventQuery("__InstanceCreationEvent", interval, isWin32Process);
var _startWatcher = new ManagementEventWatcher(startQuery);
_startWatcher.Start();
_startWatcher.EventArrived += OnStartEventArrived;
// Listen for closed processes.
WqlEventQuery stopQuery = new WqlEventQuery("__InstanceDeletionEvent", interval, isWin32Process);
var _stopWatcher = new ManagementEventWatcher(stopQuery);
_stopWatcher.Start();
_stopWatcher.EventArrived += OnStopEventArrived;
What is the name of this screen? And how do I detect this type of window? It seems like the same type as the login window.
When you press CTRL+ALT+DEL, Windows switches to a another special virtual desktopa that hosts the winlogon
process that is responsible for user login/logoff/lock etc. actions. By using the WinAPI function SetWinEventHook
with the EVENT_SYSTEM_DESKTOPSWITCH
argument you can set up a callback function that is called whenever such a desktop switch occurs:
//Store the callback in a variable so that it is not GC'd
private static readonly WinEventDelegate callback = EventCallback;
static void StartListeningForDesktopSwitch()
{
SetWinEventHook(EVENT_SYSTEM_DESKTOPSWITCH, EVENT_SYSTEM_DESKTOPSWITCH,
IntPtr.Zero, callback, 0, 0, WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNTHREAD);
}
static void EventCallback(IntPtr hWinEventHook, uint eventType,
IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
Console.WriteLine("Desktop switched");
}
Note: If you want to use this in a console application, you have to add a message loop by adding a hidden Form
:
static void Main(string[] args)
{
StartListeningForDesktopSwitch();
// Run message loop
Application.Run(new HiddenForm());
}
private class HiddenForm : Form
{
public HiddenForm()
{
this.FormBorderStyle = FormBorderStyle.None;
this.WindowState = FormWindowState.Minimized;
this.ShowInTaskbar = false;
}
}
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;
Further: The desktop switch also occurs when the user pressed Win+L or a UAC window pops up. Thus, we need a way to detect these other cases. The UAC case is rather trivial, it is enough to check if the process consent.exe
is running during the callback function:
var processes = Process.GetProcessesByName("consent");
if (processes.Length == 0)
Console.WriteLine("This is not a UAC prompt");
The other case, unfortunately, is a bit more complicated. I have only managed to detect wheter a user returns from a lock screen, but not whether they enter it (as you said, this is not relevant for you, but I wanted to mention it anyway).
Detecting whether the session is locked can be done by listening for the
SystemEvents.SessionSwitch
event in our HiddenForm
. The SessionSwitchEventArgs.Reason
property is set to SessionSwitchReason.SessionLock
if this is a lock event, and to SessionSwitchReason.SessionUnlock
if the user unlocks. We only can tell whether a desktop switch was not to the lock screen desktop when we are switching back to the default desktop since the switch desktop event callbacks are called before a session lock and after a session unlock. This leads to the following code for a sample console application:
private static readonly WinEventDelegate callback = EventCallback;
static void Main(string[] args)
{
SetWinEventHook(EVENT_SYSTEM_DESKTOPSWITCH,
EVENT_SYSTEM_DESKTOPSWITCH, IntPtr.Zero, callback, 0, 0,
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNTHREAD);
Application.Run(new HiddenForm());
}
private class HiddenForm : Form
{
public HiddenForm()
{
this.FormBorderStyle = FormBorderStyle.None;
this.WindowState = FormWindowState.Minimized;
this.ShowInTaskbar = false;
SystemEvents.SessionSwitch += SystemEvents_SessionSwitch;
}
private void SystemEvents_SessionSwitch(object sender, SessionSwitchEventArgs e)
{
if (e.Reason == SessionSwitchReason.SessionUnlock)
wasUnlocked = true;
}
}
static bool wasUnlocked = false;
static bool wasOpened = false;
static void EventCallback(IntPtr hWinEventHook, uint eventType,
IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
// Check if UAC dialog is being shown
var processes = Process.GetProcessesByName("consent");
if (processes.Length == 0)
{
if (wasOpened)
{
if (!wasUnlocked)
Console.WriteLine("Exited from CTRL+ALT+DEL");
wasUnlocked = false;
wasOpened = false;
}
else
wasOpened = true;
}
}
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;
a This type of virtual desktop has nothing to do with the newly introduced "virtual desktop" feature in Windows 10
Not sure if this might help. This won't check if the screen is there bur rather if the events have been triggered, to do that you might need to enable the logging of these events, you can do it by opening the group policy editor:
gpedit.msc
→ Computer Configuration → Windows Settings → Security Settings → Advanced Audit Policy Configuration → System Audit Policies → Local Group Policy Object → Logon/Logoff → Audit Other Login/Logoff Events
After it has been enabled, you could listen for the event id 4800
for locking and 4801
for unlocking.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With