c++ , windows 2000 - Remotely get locally logged  username

Posted on 2007-10-03
Last Modified: 2013-12-23

I'm currently trying to remotely find who is locally logged into windows 2000 workstations.  I have tried a couple of wmi methods: Reading the UserName property of the Win32_ComputerSystem object; and running the GetOwner() method of the Win32_Process object associated with the explorer.exe process.  Both of these methods work fine with windows XP, and on *some* of my windows 2000 workstations.

I am aware of using NetWkstaUserEnum to get logged on users, but that lists all logged on users, including any batch processes running, which is not what I want.

If someone knows of a good way to find just the locally logged on user, some reasoning on why the WMI methods would work on some windows 2000 workstations and not others, or a way to narrow NetWkstaUserEnum down to just the locally logged in user I would be very greatful.

Question by:mikerotch1000
    LVL 39

    Expert Comment

    >>>> If someone knows of a good way to find just the locally logged on user
    If you write a little service which logs the locally logged on users at the server, you could compare that list with that of NetWkstaUserEnum to filter both the batch jobs and the ones already logged out. In case your clienst have a fixed IP address or your server acts as a DHCP server as well and you have access to the list of IP addresses, you might check the 'users' of the NetWkstaUserEnum whether they are available via TCP/IP. Another - maybe more simple way - is to have all batch login user's 'special' usernames which easily can be filtered.

    >>>> why the WMI methods would work on some windows 2000 workstations
    >>>> and not others,
    I assume you already read the MSDN regarding that:
    Windows 2000:  If you call this function on a domain controller that is running Active Directory, access is allowed or denied based on the access control list (ACL) for the securable object. The default ACL permits all authenticated users and members of the " Pre-Windows 2000 compatible access" group to view the information. By default, the "Pre-Windows 2000 compatible access" group includes Everyone as a member. This enables anonymous access to the information if the system allows anonymous access. If you call this function on a member server or workstation, all authenticated users can view the information. Anonymous access is also permitted if the RestrictAnonymous policy setting allows anonymous access.

    Regards, Alex


    Author Comment


    Thank you for responding to my question.

    I'd rather not rely on a service that dumps who just logged on to the server.  I might as well just read event 672's from the security logs on the dc (server).  This event reliably tells me when someone has logged onto a workstation at an ip address.

    Now I might be wrong about this, but I don't think that NetWkstaUserEnum is part of WMI ... allthought it might utilize it.  I did find the Windows 2000 notes that you posted on the NetWkstaUserEnum page():

    But I did not find it on the pages of any of the WMI objects / methods I'm using.



    I did look into the RestrictAnonymous policy on a windows 2000 workstation that I can get the logged in username from and compared it to that of a windows 2000 workstation that I can not get the logged in username from.  They are identical - "None.  Rely on default permissions".

    Now, I'm not sure what the default permissions are, so I suppose that those could be the culprit.  

    The thing is that I'm connecting to this systems via WMI with a Domain Administrator account, so I'm not quite sure that the RestrictAnonymous policy would affect this.

    If you have any thoughts on this WMI issue I would greatly appreciate them.

    Thanks again

    Author Comment


    I just noticed that there is at least 1 windows XP machine that does not return a username from my wmi calls
    LVL 41

    Accepted Solution

    Here is the technique that I use...  It's written in VB.Net, but you'll get the point

    Imports Microsoft.Win32

    ' Class to get the name of the remotely logged in user

    Public Class GetUser

    #Region "API Region"
        'BOOL LookupAccountSid(
        '  LPCTSTR lpSystemName,
        '  PSID lpSid,
        '  LPTSTR lpName,
        '  LPDWORD cchName,
        '  LPTSTR lpReferencedDomainName,
        '  LPDWORD cchReferencedDomainName,
        '  PSID_NAME_USE peUse
        Private Declare Auto Function LookupAccountSid Lib "advapi32.dll" ( _
            ByVal lpSystemName As String, _
            ByVal lpSid As IntPtr, _
            ByVal lpName As String, _
            ByRef cchName As Integer, _
            ByVal lpReferenceDomainName As String, _
            ByRef cchReferencedDomainName As Integer, _
            ByRef peUse As Integer _
        ) As Boolean

        'BOOL ConvertStringSidToSid(
        '  LPCTSTR StringSid,
        '  PSID* Sid
        Private Declare Auto Function ConvertStringSidToSid Lib "advapi32.dll" ( _
            ByVal StringSid As String, _
            ByRef Sid As IntPtr _
        ) As Boolean

        Private Const NAME_SIZE As Integer = 64
    #End Region

        Public Function GetRemoteUser(ByVal RemotePC As String) As String
            Dim reg_hku, key, subkey As RegistryKey
            Dim user, keyname, CurKey, UserName, DomainName As String
            Dim name_len, domain_len, peUse As Integer
            Dim got_it As Boolean
            Dim Sid As IntPtr

            CurKey = ""
            user = "Error"
                reg_hku = RegistryKey.OpenRemoteBaseKey(RegistryHive.Users, RemotePC)
                If Not IsNothing(reg_hku) Then
                    ' Find the User key that has "Volatile Environment"... that's
                    ' either the current user or the last user logged in.
                    got_it = False
                    For Each keyname In reg_hku.GetSubKeyNames()
                        key = reg_hku.OpenSubKey(keyname, False)
                        If Not IsNothing(key) Then
                            If Array.IndexOf(key.GetSubKeyNames(), "Volatile Environment") >= 0 Then
                                CurKey = keyname
                                ' for WinXP's alternate login, we must check to
                                ' see if the CLIENTNAME is "Console"
                                subkey = key.OpenSubKey("Volatile Environment")
                                If Not IsNothing(subkey) Then
                                    If subkey.GetValue("CLIENTNAME", "").ToString = "Console" Then
                                        got_it = True
                                    End If
                                End If
                            End If
                            If got_it Then
                                Exit For
                            End If
                        End If
                End If

                ' Nope, didn't find anything
                If CurKey = "" Then
                    Return "Nobody"
                End If

                ' In rare cases, the user's registry hive is still loaded.  So
                ' to test if there is anybody currently logged on, we see if
                ' the remote PC has an explorer.exe process running
                If Process.GetProcessesByName("explorer", RemotePC).Length = 0 Then
                    Return "Nobody"
                End If

                ' Now let's get the logon name from the string version of the
                ' Sid (which is the registry key name)
                If ConvertStringSidToSid(CurKey, Sid) = False Then
                    Return "Error"
                End If

                name_len = NAME_SIZE
                domain_len = NAME_SIZE
                UserName = Space(name_len)
                DomainName = Space(domain_len)

                ' look up the Account associated with that SID
                If LookupAccountSid(RemotePC, Sid, UserName, name_len, DomainName, domain_len, peUse) = False Then
                    Return "Error"
                End If
                If domain_len > 0 Then
                    user = Left(DomainName, domain_len) & "\" & Left(UserName, name_len)
                    user = RemotePC & "\" & Left(UserName, name_len)
                End If
            Catch ex As Exception
                Return "Error"
            End Try

            Return user
        End Function
    End Class

    Author Comment

    graye:  Thank you so much for posting.  your solution is excellent and did the trick for me

    Featured Post

    Live - One-on-One C++ Help from Top Experts

    Solve your toughest problems, fast.
    C++ experts are online now and ready to help you.

    Join & Write a Comment

    Have you ever set up your wireless router at home or in the office to find that you little pop-up bubble in the bottom right-hand corner of Windows read "IP Conflict - One of more computers on the network have been assigned the following IP address"…
    Many of us in IT utilize a combination of roaming profiles and folder redirection to ensure user information carries over from one workstation to another; in my environment, it was to enable virtualization without needing a separate desktop for each…
    The goal of the video will be to teach the user the difference and consequence of passing data by value vs passing data by reference in C++. An example of passing data by value as well as an example of passing data by reference will be be given. Bot…
    The viewer will learn how to use the return statement in functions in C++. The video will also teach the user how to pass data to a function and have the function return data back for further processing.

    729 members asked questions and received personalized solutions in the past 7 days.

    Join the community of 500,000 technology professionals and ask your questions.

    Join & Ask a Question

    Need Help in Real-Time?

    Connect with top rated Experts

    19 Experts available now in Live!

    Get 1:1 Help Now