Enumerate Users Local or Remote

By | 2019-10-01

This is a pair of class modules that allow you to easily enumerate the currently logged in users on either the local machine or a specified remote machine.

Class Module: clsGetRemoteLoggedInUsers

This class returns the logged in users on a remote or local Workstation when the HostName property is set by your Program. It requires that the Class clsRemoteUsersInfo be added to project as well.

Option Explicit

'Api Structures
Private Type WKSTA_USER_INFO_1
    lngUserName As Long
    lngLogonDomain As Long
    lngOtherDomains As Long
    lngLogonServer As Long
End Type

'Error Constants
Const ERROR_BAD_NETPATH = 53&
Const ERROR_INVALID_NAME = 123&
Const ERROR_NOT_ENOUGH_MEMORY = 8
Const ERROR_INVALID_LEVEL = 124&
Const ERROR_INVALID_PARAMETER = 87
Const ERROR_MORE_DATA = 234
Const NERR_Success As Long = 0&


'Api Declares
Private Declare Function NetWkstaUserEnum Lib "netapi32.dll" _
(ByVal strServerName As String, ByVal dwLevel As Long, _
lpBuffer As Long, ByVal dwPrefMaxLen As Long, _
lpdEntriesRead As Long, lpdTotalEntries As Long, _
lpdResumehandle As Long) As Long

Private Declare Function NetApiBufferFree Lib "netapi32.dll" _
(ByVal pBuffer As Long) As Long

Private Declare Function NetApiBufferSize Lib "netapi32.dll" _
(lpBuffer As Any, lpLength As Long) As Long

Private Declare Sub CopyMem Lib "kernel32.dll" Alias "RtlMoveMemory" _
(pTo As Any, uFrom As Any, ByVal lSize As Long)

Private Declare Function lstrlenW Lib "kernel32.dll" _
(ByVal lpString As Long) As Long

'local variable(s) to hold property value(s)
Private mvarNumberOfAccounts As Integer 'local copy
Private mvarServerName As String 'local copy
Private colUserAccounts As New Collection
Dim mvarUserAccounts As Collection 'local copy

Public Property Get UserAccounts() As Variant
    If mvarUserAccounts Is Nothing Then
        Set mvarUserAccounts = New Collection
    End If
    Set UserAccounts = mvarUserAccounts
End Property

Public Property Let ServerName(ByVal vData As String)
    mvarServerName = vData
    GotServerName (mvarServerName)
End Property

Public Property Get ServerName() As String
    ServerName = mvarServerName
End Property

Public Property Get NumberOfAccounts() As Integer
    NumberOfAccounts = mvarNumberOfAccounts
End Property

Private Function PtrToString(lpwString As Long) As String
    'Convert a LPWSTR pointer to a VB string
    Dim Buffer() As Byte
    Dim nLen As Long

    If lpwString Then
        nLen = lstrlenW(lpwString) * 2
        If nLen Then
            ReDim Buffer(0 To (nLen - 1)) As Byte
            CopyMem Buffer(0), ByVal lpwString, nLen
            PtrToString = Buffer
        End If
    End If
End Function

Private Sub GotServerName(ByVal strHostName As String)
    Dim lngLevel As Long
    Dim lngPrefmaxlen As Long
    Dim lngEntriesRead As Long
    Dim lngTotalEntries As Long
    Dim lngResumeHandle As Long
    Dim lngReturn As Long
    Dim lngLength As Long
    Dim lngBuffer As Long
    Dim typWkStaInfo(0 To 1000) As WKSTA_USER_INFO_1
    Dim intCount As Integer
    Dim CurrentInfo As clsRemoteUsersInfo

    'Check for the right syntax for the servername
    'Convert it to unicode because the C function wants a LPCWSTR
    'ie LongPointer to a unicode string, the C stands for a constants
    'vbNullString for the local Machine
    If strHostName = "" Then
        strHostName = vbNullString
    Else
        If InStr(strHostName, "\\") <> 0 Then
            strHostName = StrConv(strHostName & vbNullChar, vbUnicode)
        Else
            strHostName = StrConv("\\" & strHostName & vbNullChar, vbUnicode)
        End If
    End If
    'set the resumehandle to the first entry
    lngResumeHandle = 0
    'Define the new Collection
    Set mvarUserAccounts = New Collection
    'Call the function, the -1 passed to dwPrefMaxLen lets the function create its
    'own buffer that will hold all the data returned, I choose to enumerate at level 1
    'you can pass a level 0, feel free to modify
    lngReturn = NetWkstaUserEnum(strHostName, &H1, lngBuffer, -1, lngEntriesRead, lngTotalEntries, lngResumeHandle)
    'if successful ie NERR_Success get the info
    If lngReturn = NERR_Success Then
        'initialize the count variable
        intCount = 0
        'Get the size of the memory allocated
        lngReturn = NetApiBufferSize(ByVal lngBuffer, lngLength)
        'Copy the memory into the array so we can get the information out
        'I imagine this could cause really strange things to happen if you happen to
        'have more then 1000 users logged into this workstation. I tried to dump the info into
        'a dynamic array and VB keep generating a Doctor Watson error everytime the
        'sub exited beats me why, If anybody know email me
        CopyMem typWkStaInfo(0), ByVal lngBuffer, lngLength
        'Get the info out and add it too are collection
        For intCount = 0 To lngTotalEntries - 1
            'temporay object to hold the info
            Set CurrentInfo = New clsRemoteUsersInfo
            'The info returned is actually a LP, which we have to convert
            'I used Andrea Tincani's function which transforms the returned LPWSTR to a string
            CurrentInfo.Username = PtrToString(typWkStaInfo(intCount).lngUserName)
            CurrentInfo.LogonDomain = PtrToString(typWkStaInfo(intCount).lngLogonDomain)
            CurrentInfo.LogonServer = PtrToString(typWkStaInfo(intCount).lngLogonServer)
            CurrentInfo.OtherDomains = PtrToString(typWkStaInfo(intCount).lngOtherDomains)
            'add it to the collection
            mvarUserAccounts.Add CurrentInfo, CurrentInfo.Username
            'destroy our temporary object
            Set CurrentInfo = Nothing
            'One more done
            intCount = intCount + 1
        Next
    Else
        'our function failed lets find out why
        GoTo GetErrMsg
    End If
    'We have to free up the Memory the funtion allocated for our data
    If lngBuffer Then
        Call NetApiBufferFree(ByVal lngBuffer)
    End If

    Exit Sub
GetErrMsg:
    ReturnErrorMsg (lngReturn)
End Sub

Private Function ReturnErrorMsg(ByVal errorcode As Long)
    Select Case errorcode
    Case 53
        MsgBox "Error: Bad netpath"
    Case 123
        MsgBox "Error: Invalid Host Name"
    Case 8
        MsgBox "Error: Not enough Memory"
    Case 124
        MsgBox "Error: Invalid Level, you don't have the authority to run this"
    Case 87
        MsgBox "Error: Invalid Parameter"
    Case 234
        MsgBox "Error: error more data"
    End Select
End Function

Class Module: clsRemoteUsersInfo

Option Explicit
'local variable(s) to hold property value(s)
Private mvarUsername As String
Private mvarLogonDomain As String
Private mvarOtherDomains As String
Private mvarLogonServer As String

Public Property Get LogonServer() As String
    LogonServer = mvarLogonServer
End Property

Public Property Let LogonServer(ByVal vData As String)
    mvarLogonServer = vData
End Property

Public Property Get OtherDomains() As String
    OtherDomains = mvarOtherDomains
End Property

Public Property Let OtherDomains(ByVal vData As String)
    mvarOtherDomains = vData
End Property

Public Property Get LogonDomain() As String
    LogonDomain = mvarLogonDomain
End Property

Public Property Let LogonDomain(ByVal vData As String)
    mvarLogonDomain = vData
End Property

Public Property Get Username() As String
    Username = mvarUsername
End Property

Public Property Let Username(ByVal vData As String)
    mvarUsername = vData
End Property

Usage

Private Sub Form_Load()
    Dim x As New clsGetRemoteLoggedInUsers
    Dim y As Variant

    'Set the remote hostname to get the logged-in users (blank for the local Computer)
    x.ServerName = ""
    'Add a listbox named list1 to your project
    For Each y In x.UserAccounts
        List1.AddItem (y.Username)
        List1.AddItem (y.LogonServer)
        List1.AddItem (y.LogonDomain)
        List1.AddItem (y.OtherDomains)
        List1.AddItem ("")
    Next
End Sub

Attachments

FileUploadedSize
1049-20191001-100225-enumerateusers.zip10/1/2019 10:02:25 AM4355
Author: dwirch

Derek Wirch is a seasoned IT professional with an impressive career dating back to 1986. He brings a wealth of knowledge and hands-on experience that is invaluable to those embarking on their journey in the tech industry.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.