Reputation: 1142
I have a suite of UI tests that I run locally/remotely depending on the situation. Occasionally, the remote computers are automatically restarted whenever there is an important update to apply. This causes all the tests to time out because the remote computers they are running on are locked out and no longer have access to the GUI.
I am wondering how I can do a quick check to see if the computer is locked or not, that way I can fail the tests quickly and log that they are offline.
I found this solution online but it seems more catered to development and not testing.
https://bytes.com/topic/net/answers/770957-get-computer-state-locked-stand-c
Really just want a clean way to check if the current machine is locked or not using C# libraries and put it in a method like the one below.
public bool IsWindowsLocked(){
// Check if the current machine is in a locked state
}
Upvotes: 0
Views: 1537
Reputation: 26907
Unfortunately there aren't really any clean ways to do this, at least not that I could find, unless you are willing to use something like query user
with PsExec
to remotely execute it on each PC as a sub-process, and then parse the results. Even then you don't get a direct answer as to locked status, you would have to go by Idle time, as Status shows one of the users as Active when no one is using the computer.
Then there is the problem of multiple users being logged on a computer, using the Switch User functionality in Windows 7 or higher. In my environment, a PC might have 3 or 4 background users and one console user. In some cases, PCs are used by RDP users. And it turns out there is a special case when you RDP to a computer then later logon to the console or do the opposite, as LogonSession LogonType isn't updated in these cases. Unfortunately, it is also possible to catch a user just logging into a computer, in which case my function will incorrectly say the computer isn't in use.
On my PC and network, this function takes about 0.2 seconds to run, if the PC is on. On some PCs, it may take much longer (up to 20 seconds), as it loads a perfmon provider on the PC. If the PC is off, the timeout is quite long, and if that is a possibility, I would recommend doing a ping check first.
Basically the function uses WMI to get LogonSession and Interactive Desktops information, and Process
to get LogonUI and explorer processes. Since LogonSession returns old sessions that have logged out, and sessions for UAC Admin programs and other (Windows 10) background processes (DWM/UMFD), we only count LogonSessions that have an explorer.exe
process (desktop).
It then combines the information into different cases:
If the number of LogonUI processes is greater than or equal to the number of interactive desktops, the PC is either logged off or locked. If there are any LogonSessions (with explorer) on the PC, it is locked, otherwise it is logged off.
If the number of LogonUI processes is less than the number of interactive desktops, then the PC is in use.
Here is the code:
enum PCUserStatuses {
Locked, // all users are locked
LoggedOff, // No users are logged in
InUse, // A user is using this computer
Unknown // unable to connect to computer / other error
}
PCUserStatuses GetPCUserStatus(string machineName) {
try {
var scope = GetManagementScope(machineName);
scope.Connect();
var explorerProcesses = Process.GetProcessesByName("explorer", machineName)
.Select(p => p.Id.ToString())
.ToHashSet();
var REprocessid = new Regex(@"(?<=Handle="").*?(?="")", RegexOptions.Compiled);
var numberOfLogonSessionsWithExplorer = new ManagementObjectSearcher(scope, new SelectQuery("SELECT * FROM Win32_SessionProcess")).Get()
.Cast<ManagementObject>()
.Where(mo => explorerProcesses.Contains(REprocessid.Match(mo["Dependent"].ToString()).Value))
.Select(mo => mo["Antecedent"].ToString())
.Distinct()
.Count();
var numberOfUserDesktops = new ManagementObjectSearcher(scope, new SelectQuery("select * from win32_Perfrawdata_TermService_TerminalServicesSession")).Get().Count - 1; // don't count Service desktop
var numberOflogonUIProcesses = Process.GetProcessesByName("LogonUI", machineName).Length;
if (numberOflogonUIProcesses >= numberOfUserDesktops) {
if (numberOfLogonSessionsWithExplorer > 0)
return PCUserStatuses.Locked;
else
return PCUserStatuses.LoggedOff;
}
else
return PCUserStatuses.InUse;
}
catch {
return PCUserStatuses.Unknown;
}
}
Upvotes: 2