11

I'm on Windows 10 Pro x64 ver 1703 build 15063.726, trying to write a program rebooter service. It runs under my user on admin permissions, every 10 minutes via a scheduled task.

If the server is unavailable or borked:

  • if my computer is locked, the rebooter kills and restarts the server program.
  • if my computer isn't locked, the rebooter shows a dialog to confirm reboot. I may be doing work on the server program and not want a reboot to ruin any debugging.

The only complication is detecting if the computer is locked. I've tried several methods:

  1. OpenInputDesktop()

Returns NULL only if the user has started to input their password since the computer was locked. Besides that returns a valid handle. Presumably the input desktop isn't deselected until the user attempts to login and their input switches.

  1. OpenInputDesktop() vs GetThreadDesktop()

(Passing in GetCurrentThreadID() of course)

Handles still match, probably due to same reason 1 fails.

  1. Process check for LockApp.exe/LockAppHost.exe

Seems to relate to user inputting their password as well... or because my server rebooter app is 32-bit, having problems reading the 64-bit process LockApp.exe.

  1. GetForegroundWindow() returns null when locked

While mentioned as dubious, I tried this too. False negative, said it wasn't locked

  1. Switching desktop manually and seeing if it worked

Described with code example here, this one was also a bust and gave a false negative.

Is there any way left? I'd really rather not have a service just for registering locking/unlocking running constantly.

Or should one of these ways worked already, and my Windows is bugged?

Cheers

Phi
  • 467
  • 5
  • 16
  • 3
    There is no known way to discover this. You can however observe the desktop session switching, in C# use the SystemEvents.SessionSwitch event. – Hans Passant Nov 20 '17 at 14:25
  • It seems to me that what you really want here is a windows service, not a desktop application. – spender Nov 20 '17 at 14:35
  • Using a service that has UI has always been a pain, and this app will be deployed in a portable fashion, so I'd heavily prefer to not go the route of a service. – Phi Nov 20 '17 at 15:02
  • Have you try this: https://stackoverflow.com/questions/44980/how-can-i-programmatically-determine-if-my-workstation-is-locked – Sy Le Nov 20 '17 at 15:36
  • @SyLe I've tried all but the https://stackoverflow.com/a/52102/6442596 one. The rest apply to services - they will say if something has changed from locked <-> unlocked, but not what it is _currently_. – Phi Nov 20 '17 at 18:07

4 Answers4

6

I'm not sure how to do it in C#, but with PowerShell, I checked if the LockApp.exe threads are Suspended to see if the desktop is unlocked or locked, like so:

$lockapp = Get-Process lockapp

for ($i = 0; $i -lt 60; $i++)
{
    $process=[System.Diagnostics.Process]::GetProcessById($lockapp.Id)
    $threads=$process.Threads
    if ($threads[0].WaitReason -eq "Suspended") { "It's unlocked" } else { "It's locked" }
    Sleep 1
}

This script loops for 60 seconds and writes out the locked/unlocked state.

Keith
  • 171
  • 1
  • 4
  • Interesting, but is it suspended when user is not attempting to type in their login or interacting with the login screen? Is it dependent on lock screen settings? What is LockApp's security settings, can a non-admin C# task read it? It's definitely better than my solution, which involved killing LockApp.exe after login via scheduled task, and the app would check whether LockApp was running at all to determine locked or not. – Phi May 16 '18 at 13:12
  • 1
    It seems to be not suspended any time it's locked, regardless of whether it's showing the lock screen image, or the user is trying to enter their password. LockApp.exe runs with the user's account, so I believe an app running as the user should be able to read its process details. – Keith May 17 '18 at 13:53
2
using Microsoft.Win32;

SystemEvents.SessionSwitch += SystemEvents_SessionSwitch;

        private async void SystemEvents_SessionSwitch(object sender, SessionSwitchEventArgs e)
        {
            if (e.Reason == SessionSwitchReason.SessionLock)
            {
                //Do someting
            }
        }
Suraj Rao
  • 29,388
  • 11
  • 94
  • 103
1

Searching for LogonUI running in process list seems to be the way to go.

pslist is 32-bit but in the background, spawns a 64-bit process to list the 64-bit counterparts, so there's no dependency on the C# app being x86 or x64 (or LockApp for that matter).

It's not great, but it works.

Process p = new Process
{
    StartInfo = new ProcessStartInfo
    {
        FileName = @"[path to SysSuite]\pslist.exe",
        Arguments = " -nobanner logonui",
        UseShellExecute = false,
        RedirectStandardOutput = true,
        CreateNoWindow = true
    }
};

p.Start();
p.WaitForExit();
string s = p.StandardOutput.ReadToEnd();
if (!s.Contains("process logonui was not found on "))
{
    Debug.WriteLine("Windows is locked.");
    return true;
}

It's really dumb that Windows has no built-in way for such a basic feature, though.

EDIT: Nope, this way doesn't work either. While LogonUI does run upon lock, and sticks around for a while, I've had it also vanish if the computer isn't interacted with for a couple hours or so. Notably, overnight, my computer gets clogged with dialogs that only spawn that frequently if logged in.

I'm now using SystemEvents.SessionSwitch and a single process as Hans suggested. I was kinda hoping for a current reading thing for future reference, but at this point it's mostly curiosity.

Phi
  • 467
  • 5
  • 16
1

I found that the mouse pointer is always at x=0, y=0 when locked.

Martlark
  • 14,208
  • 13
  • 83
  • 99