Question:
I would like to be able to gain access to NT server local groups for enum, member enum, add member, delete member.
We have a lot of servers as well as NT and OS/2 domains. Because of this, there is a need to synchronize users and group memberships across multiple servers.
I have not found anything that will allow me to access a local group on a server from a VB program running on a client. Can you point me in the right direction?
Answer:
None of the NT Unicode network functions are particularly easy to use. In fact, they are a real pain. However, the following code will do what you want:
' ************************************************
' In a form
' ************************************************
Option Explicit
Private Sub Command1_Click()
On Error Resume Next
Dim p_vntRtn As Variant
Dim p_lngNum As Long
Dim p_lngLoop As Long
Dim p_strGroup As String
p_strGroup = Trim$(Me.List1.List(Me.List1.ListIndex))
If Len(p_strGroup) <= 0 Then
MsgBox "You must select a group first!"
Exit Sub
End If
Me.List2.Clear
p_vntRtn = GetLocalGroupMembers("", p_strGroup)
p_lngNum = UBound(p_vntRtn)
If p_lngNum > 0 Then
For p_lngLoop = 1 To p_lngNum
Me.List2.AddItem p_vntRtn(p_lngLoop)
Next p_lngLoop
End If
End Sub
Private Sub Command2_Click()
On Error Resume Next
Dim p_vntRtn As Variant
Dim p_lngNum As Long
Dim p_lngLoop As Long
Me.List1.Clear
p_vntRtn = GetLocalGroups("")
p_lngNum = UBound(p_vntRtn)
If p_lngNum > 0 Then
For p_lngLoop = 1 To p_lngNum
Me.List1.AddItem p_vntRtn(p_lngLoop)
Next p_lngLoop
End If
End Sub
Private Sub Command3_Click()
Dim p_blnRtn As Boolean
Dim p_strGroup As String
Dim p_strUser As String
p_strGroup = Trim$(Me.List1.List(Me.List1.ListIndex))
If Len(p_strGroup) <= 0 Then
MsgBox "You must select a group first!"
Exit Sub
End If
p_strUser = Trim$(Me.Text1.Text)
If Len(p_strUser) <= 0 Then
MsgBox "You must add a user name first!"
Exit Sub
End If
p_blnRtn = AddDelUserToLocal(p_strGroup, _
p_strUser, _
"", True)
End Sub
Private Sub Command4_Click()
Dim p_blnRtn As Boolean
Dim p_strGroup As String
Dim p_strUser As String
p_strGroup = Trim$(Me.List1.List(Me.List1.ListIndex))
If Len(p_strGroup) <= 0 Then
MsgBox "You must select a group first!"
Exit Sub
End If
p_strUser = Trim$(Me.Text2.Text)
If Len(p_strUser) <= 0 Then
MsgBox "You must add a user name first!"
Exit Sub
End If
p_blnRtn = AddDelUserToLocal(p_strGroup, _
p_strUser, _
"", False)
End Sub
' ************************************************
' In a BAS file
' ************************************************
Option Explicit
Private Const NERR_Success As Long = 0&
Private Const LocalGroupInfo0 As Long = 0&
Private Const LocalGroupInfo1 As Long = 1&
Private Const LocalGroupMembersInfo0 As Long = 0&
Private Const LocalGroupMembersInfo1 As Long = 1&
Private Const LocalGroupMembersInfo2 As Long = 2&
Private Const LocalGroupMembersInfo3 As Long = 3&
Private Const SidTypeUser As Long = 1&
Private Const SidTypeGroup As Long = 2&
Private Const SidTypeDomain As Long = 3&
Private Const SidTypeAlias As Long = 4&
Private Const SidTypeWellKnownGroup As Long = 5&
Private Const SidTypeDeletedAccount As Long = 6&
Private Const SidTypeInvalid As Long = 7&
Private Const SidTypeUnknown As Long = 8&
Public Declare Sub CopyMem _
Lib "kernel32" Alias "RtlMoveMemory" _
(pTo As Any, _
pFrom As Any, _
ByVal lCount As Long)
Private Declare Function lstrlenW _
Lib "kernel32" _
(ByVal psString As Long) As Long
Private Declare Function NetApiBufferFree _
Lib "netapi32.dll" _
(ByVal p_lngPtrBuffer As Long) As Long
Private Type LOCALGROUP_INFO_0
PtrGroupName As Long
End Type
Private Type LOCALGROUP_MEMBERS_INFO_0
PtrSidID As Long
End Type
Private Type LOCALGROUP_MEMBERS_INFO_1
PtrSidID As Long
SidUsage As Long
PtrName As Long
End Type
Private Type LOCALGROUP_MEMBERS_INFO_3
DomainAndName As Long
End Type
Private Declare Function NetLocalGroupEnum _
Lib "netapi32" _
(ByVal psServer As Long, _
ByVal level As Long, _
pPtrBuffer As Long, _
ByVal prefmaxlen As Long, _
EntriesRead As Long, _
TotalEntries As Long, _
ResumeHandle As Long) As Long
Private Declare Function NetLocalGroupGetMembers _
Lib "netapi32" _
(ByVal psServer As Long, _
ByVal psLocalGroupName As Long, _
ByVal level As Long, _
pPtrBuffer As Long, _
ByVal prefmaxlen As Long, _
EntriesRead As Long, _
TotalEntries As Long, _
ResumeHandle As Long) As Long
Private Declare Function NetLocalGroupAddMembers _
Lib "netapi32" _
(ByVal psServer As Long, _
ByVal psLocalGroupName As Long, _
ByVal level As Long, _
pPtrBuffer As Long, _
ByVal membercount As Long) As Long
Private Declare Function NetLocalGroupDelMembers _
Lib "netapi32" _
(ByVal psServer As Long, _
ByVal psLocalGroupName As Long, _
ByVal level As Long, _
pPtrBuffer As Long, _
ByVal membercount As Long) As Long
' ================================================
' Get all the memebers of a selected local group
' ================================================
Public Function GetLocalGroupMembers(ByVal xi_strServerName As String, _
ByVal xi_strLocalGroupName As String) As Variant
Dim p_lngRtn As Long
Dim p_lngPtrBuffer As Long
Dim p_lngPtrServerName As Long
Dim p_lngPtrLocalGroupName As Long
Dim p_lngPreferredMaxLen As Long
Dim p_lngEntriesRead As Long
Dim p_lngTotalEntries As Long
Dim p_lngResumeHwnd As Long
Dim p_lngLoop As Long
Dim p_lngSidType As Long
Dim p_strUserName As String
Dim p_strSidType As String
Dim p_strSID As String
Dim p_typLocalGroupMembers1 As LOCALGROUP_MEMBERS_INFO_1
Dim p_alngMemberInfo() As Long
Dim p_astrMemberInfo() As String
' Convert the server name to a pointer
If Len(Trim$(xi_strServerName)) = 0 Then
p_lngPtrServerName = 0&
Else
p_lngPtrServerName = StrPtr(xi_strServerName)
End If
' Convert the user name to a pointer
If Len(Trim$(p_lngPtrLocalGroupName)) = 0 Then
MsgBox "You must enter a local group name!", vbExclamation, "Input Error"
Exit Function
Else
p_lngPtrLocalGroupName = StrPtr(xi_strLocalGroupName)
End If
p_lngPreferredMaxLen = 4096&
p_lngResumeHwnd = 0&
p_lngRtn = NetLocalGroupGetMembers(p_lngPtrServerName, _
p_lngPtrLocalGroupName, _
LocalGroupMembersInfo1, _
p_lngPtrBuffer, _
p_lngPreferredMaxLen, _
p_lngEntriesRead, _
p_lngTotalEntries, _
p_lngResumeHwnd)
If p_lngRtn = NERR_Success Then
ReDim p_alngMemberInfo(0 To (p_lngEntriesRead * 3) - 1) As Long
ReDim p_astrMemberInfo(1 To p_lngEntriesRead) As String
' This gets the pointers to the structures
CopyMem p_alngMemberInfo(0), _
ByVal p_lngPtrBuffer, _
p_lngEntriesRead * 4 * 3
If p_lngEntriesRead > 0 Then
For p_lngLoop = 0 To p_lngEntriesRead - 1
p_strSidType = TranslateSidUsage(p_alngMemberInfo((p_lngLoop * 3) + 1))
p_strUserName = PointerToStringW(p_alngMemberInfo((p_lngLoop * 3) + 2))
p_astrMemberInfo(p_lngLoop + 1) = p_strUserName & " [" & p_strSidType & "]"
Next p_lngLoop
End If
If p_lngPtrBuffer Then
NetApiBufferFree p_lngPtrBuffer
End If
GetLocalGroupMembers = p_astrMemberInfo
Else
GetLocalGroupMembers = ""
End If
End Function
' ================================================
' Get all of the local groups
' ================================================
Public Function GetLocalGroups(ByVal xi_strServerName As String) As Variant
Dim p_lngRtn As Long
Dim p_lngLoop As Long
Dim p_lngPtrBuffer As Long
Dim p_lngPtrServerName As Long
Dim p_lngPreferredMaxLen As Long
Dim p_lngResumeHwnd As Long
Dim p_lngEntriesRead As Long
Dim p_lngTotalEntries As Long
Dim p_alngGroupInfo() As Long
Dim p_astrGroupInfo() As String
' Convert the server name to a pointer
If Len(Trim$(xi_strServerName)) = 0 Then
p_lngPtrServerName = 0&
Else
p_lngPtrServerName = StrPtr(xi_strServerName)
End If
p_lngPreferredMaxLen = 4096&
p_lngResumeHwnd = 0&
p_lngRtn = NetLocalGroupEnum(p_lngPtrServerName, _
LocalGroupInfo0, _
p_lngPtrBuffer, _
p_lngPreferredMaxLen, _
p_lngEntriesRead, _
p_lngTotalEntries, _
p_lngResumeHwnd)
If p_lngRtn = NERR_Success Then
ReDim p_alngGroupInfo(0 To p_lngEntriesRead - 1) As Long
ReDim p_astrGroupInfo(1 To p_lngEntriesRead) As String
' This gets the pointers to the structures
CopyMem p_alngGroupInfo(0), _
ByVal p_lngPtrBuffer, _
p_lngEntriesRead * 4
If p_lngEntriesRead > 0 Then
For p_lngLoop = 0 To p_lngEntriesRead - 1
p_astrGroupInfo(p_lngLoop + 1) = PointerToStringW(p_alngGroupInfo(p_lngLoop))
Next p_lngLoop
End If
If p_lngPtrBuffer Then
NetApiBufferFree p_lngPtrBuffer
End If
GetLocalGroups = p_astrGroupInfo
Else
GetLocalGroups = ""
End If
End Function
' ================================================
' Either add or delete a user from a selected
' local group (depends upon xi_blnAddUser)
' ================================================
Function AddDelUserToLocal(ByVal xi_strGroupName As String, _
ByVal xi_strUserName As String, _
ByVal xi_strServerName As String, _
ByVal xi_blnAddUser As Boolean) As Boolean
Dim p_lngPtrGroupName As Long
Dim p_lngPtrUserName As Long
Dim p_lngPtrServerName As Long
Dim p_lngMemberCount As Long
Dim p_lngRtn As Long
' Convert the server name to a pointer
If Len(Trim$(xi_strServerName)) = 0 Then
p_lngPtrServerName = 0&
Else
p_lngPtrServerName = StrPtr(xi_strServerName)
End If
' Convert the group name to a pointer
p_lngPtrGroupName = StrPtr(xi_strGroupName)
' Convert the user name to a pointer
p_lngPtrUserName = StrPtr(xi_strUserName)
' Add the user
p_lngMemberCount = 1
If xi_blnAddUser = True Then
p_lngRtn = NetLocalGroupAddMembers(p_lngPtrServerName, _
p_lngPtrGroupName, _
LocalGroupMembersInfo3, _
p_lngPtrUserName, _
p_lngMemberCount)
Else
p_lngRtn = NetLocalGroupDelMembers(p_lngPtrServerName, _
p_lngPtrGroupName, _
LocalGroupMembersInfo3, _
p_lngPtrUserName, _
p_lngMemberCount)
End If
If p_lngRtn = NERR_Success Then
AddDelUserToLocal = True
Else
AddDelUserToLocal = False
End If
End Function
' ================================================
' Convert the SID type to a text string
' ================================================
Private Function TranslateSidUsage(ByVal xi_lngSidType As Long) As String
Dim p_strTmp As String
Select Case xi_lngSidType
Case SidTypeUser
p_strTmp = "User Account"
Case SidTypeGroup
p_strTmp = "Global Group Account"
Case SidTypeDomain
p_strTmp = "Domain Group Account"
Case SidTypeAlias
p_strTmp = "Alias Account"
Case SidTypeWellKnownGroup
p_strTmp = "Well-known Group Account (ie, Everyone)"
Case SidTypeDeletedAccount
p_strTmp = "Deleted Account"
Case SidTypeInvalid
p_strTmp = "Invalid Account"
Case SidTypeUnknown
p_strTmp = "Account Type Cannot Be Determined"
Case Else
p_strTmp = "Bad Parameter for Sid Usage"
End Select
' Set the return value
TranslateSidUsage = p_strTmp
End Function
' ================================================
' Convert a pointer to a string into a string
' ================================================
Public Function PointerToStringW(ByVal xi_lngPtrToString As Long) As String
Dim p_strText As String
Dim p_lngLength As Long
If xi_lngPtrToString Then
p_lngLength = lstrlenW(xi_lngPtrToString)
If p_lngLength > 0 Then
p_strText = Space$(p_lngLength)
CopyMem ByVal StrPtr(p_strText), ByVal xi_lngPtrToString, p_lngLength * 2
End If
End If
PointerToStringW = p_strText
End Function
' ================================================
' Convert a pointer to a string into a string via
' a byte array
' ================================================
Private Function PointerToStringWByte(ByVal xi_lngPtrToString As Long) As String
Dim p_abytBuffer() As Byte
Dim p_lngLen As Long
If xi_lngPtrToString <> 0 Then
p_lngLen = lstrlenW(xi_lngPtrToString) * 2
If p_lngLen Then
ReDim p_abytBuffer(0 To (p_lngLen - 1)) As Byte
CopyMem p_abytBuffer(0), ByVal xi_lngPtrToString, p_lngLen
PointerToStringWByte = p_abytBuffer
End If
End If
End Function