This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.

List Locally Logged-on Users

It's useful to be able to query a computer for the users that are logged on
locally. You can do this with Sysinternals' free PsLoggedOn utility, but sometimes
you want to use the information directly in a scripted task (e.g., contacting
users who are logged on to a terminal server that needs a reboot because of
a security patch). Although it's possible to execute the PsLoggedOn utility
and parse its output in a script for use in other scripts, I decided to see
whether I could gather the names of logged-on users by using a script instead
of relying on PsLogged-On.

How PsLoggedOn Works
I started by downloading and examining the C source code for the Ps-LoggedOn
utility to see how it determines locally logged-on users. The technique it uses
isn't complex and is fairly easy to implement by calling the methods in Windows
Management Instrumentation's (WMI's) StdRegProv class. An overview of the technique
PsLoggedOn uses is as follows:

Enumerate the subkeys in the computer's HKEY_USERS registry subtree.

For each subkey in the subtree that contains a SID value, determine whether
the subkey contains a Volatile Environment subkey. If it does, and if the
Volatile Environment subkey contains one or more values, then the user that
the SID represents is currently logged on.

Convert the SID value into the corresponding username.

In Figure 1, the HKEY _USERS subtree is expanded
to show a subkey that has a SID value. You can also see that the subkey contains
a Volatile Environment subkey that contains values. Thus, you know that the
user represented by the SID S-1-5-21-299502267-1078145449-725345543-500 is currently
logged on.

Introducing LoggedOn.js
LoggedOn.js provides a couple of advantages over the PsLoggedOn tool. First,
you don't have to parse Logged-On.js's output when you need a list of logged-on
users for a script. Second, it doesn't rely on any third-party functionality.
Depending on third-party code can be a problem in some environments. LoggedOn.js
uses the same technique as PsLoggedOn to list local logons on a specified computer.
To run the script, you need either Windows XP or later, or Windows 2000 with
Windows Script Host (WSH) 5.6. The script's command-line syntax is

LoggedOn.js <computername>

where computername is the name of the computer whose locally logged-on users you want to retrieve. Logged-On.js connects to the specified computer and lists the users who are logged on locally.

If you need to query Win2K Service Pack 4 (SP4) computers for local logons,
there's a problem you need to know about. Win2K SP4's StdRegProv class has a
bug that prevents WMI-based scripts and programs from querying the HKEY_USERS
subtree. To fix this bug, you need to get a copy of the hotfix mentioned in
the Microsoft article "Cannot Use WMI to Query HKEY Users After You Install
SP4" at http://support.microsoft.com/?kbid= 817478. The hotfix updates the stdprov.dll
file in %SystemRoot%\system 32 and doesn't require a reboot. You'll have to
call Microsoft Product Support Services to get a copy of the hotfix.

Also, for best results, I highly recommend that you use UPHClean, a free Microsoft
utility that monitors a computer when users log off and ensures that user profiles
are completely unloaded. Otherwise, Logged-On.js (and PsLoggedOn as well) might
report logons for users who have since logged off but whose profiles didn't
unload completely. For more information about the UPHClean service, see the
Microsoft article "Troubleshooting profile unload issues" at http://support.microsoft.com/?kbid=837115.

Inside LoggedOn.js
The LoggedOn.js script, excerpted in Listing
1, starts by declaring some global variables. It then calls the Quit method
of the WScript object, with the main function as a parameter. In other words,
the main function's return value is the script's exit code.

The main function declares some variables and parses the command line. It uses
the first unnamed argument (i.e., the first argument on the script's command
line that doesn't start with a / character) as the computer name. Callout C
in Listing 1 shows the try block
that the main function uses to connect to the computer. If the connection succeeds,
the code in the catch block never executes. In case an error occurs, the catch
block causes the main function to display an error message and execute the return
statement with the error number as its argument.

Calling WMI in JScript
JScript doesn't support calling methods that require output parameters. Many
of the StdRegProv methods use output parameters to return their results. Microsoft's
WMI programmers devised an alternative calling convention for WMI methods that
doesn't require output parameters. I discussed this alternative calling convention
in more detail in "Calling WMI Methods with JScript," November 2006, InstantDoc
ID 93402, but here's a quick overview:

Create an InParameters object for the method.

Set the InParameters object's properties equal to the method's input parameters.

Call the method using the Exec-Method_ method to return an Out-Parameters
object.

Access the OutParameters object's properties to determine the results of
the method.

Callout D shows how the main function implements the above steps to execute
the EnumKey method to enumerate the HKEY_USERS subtree. The OutParameters object's
properties will vary depending on the method, but all OutParameters objects
have a ReturnValue property that contains the method's result (a zero indicates
that the method call succeeded). Call-out E shows how the main function checks
whether the EnumKey method succeeded. If the method didn't succeed, the main
function displays an error message and executes the return statement with the
nonzero exit code as its argument.

Next, the main function accesses the sNames property of the OutParameters object,
which contains a SafeArray (also known as a VBArray), and calls the toArray
method to convert it into a JScript array. The function then creates an empty
array to contain the list of users. It's necessary to use an array to contain
the logged-on users because there might be multiple locally logged-on users.
For example, terminal servers typically have many users logged on at once.

The main function's next task is to use the for statement to iterate the array
of subkey names. The main function first creates an uppercase copy of the subkey
name. If the subkey name isn't .DEFAULT and doesn't end with the string _CLASSES,
then the subkey name is a SID value that represents a user. The main function
then calls the GetLoggedOnUser function with the computer name and the subkey
name (a SID value) as parameters. (I describe the GetLoggedOn User function
a little later.) If the Get-LoggedOnUser function returns a non-empty string,
then the main function adds the username to the array of users.

After building the array of logged-on users, the main function checks the array's
length property. If the array is empty, the function outputs a message that
there are no logged-on users and returns with a nonzero value. Otherwise, the
main function echoes the array of logged-on users by calling the ArrayToString
function (at callout A). The ArrayToString function accepts an array as a parameter
and returns a string that contains the elements in the array separated by newlines.

The GetLoggedOnUser Function
The GetLoggedOnUser function (at callout B) looks for a subkey named like this:

HKEY_USERS\<subkey>\Volatile
Environment

where subkey is the second parameter passed to the function and contains
a SID value. If the specified subkey exists and contains one or more values,
the GetLoggedOnUser function retrieves the Win32_SID object that corresponds
to the subkey name (i.e., the user's SID) and builds a domain\ username string
by referencing the Win32_SID object's ReferencedDomain-Name and AccountName
properties. If the GetLoggedOnUser function fails to retrieve the Win32_SID
object, it simply returns the SID value as the username.

The main function outputs the array of logged-on users as a single string,
with each username on a separate line. You can use this output without having
to parse it (as you would with PsLoggedOn). Add this script to your scripting
toolkit, and you'll always be able to tell who is logged on locally to a computer.