Option Strict On

'This enforces strict rules of variable type casting and such. 

'Read more here Option Option Explicit and Option Strict in Visual Basic .NET and in Visual Basic

Imports System.DirectoryServices

'This is the namespace where the all the cool AD classes are.

'See System.DirectoryServices Namespace .Net 4.0 framework

Imports System.IO

'This is used for system IO. duh

Imports System.DirectoryServices.AccountManagement

'This is a relatively new namespace added in .NET 3.5 that I needed to add so I could easily get the users lockout status.

 

Public Class Form1

    'This is what runs when you click the search button.

    Private Sub btnSearch_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnSearch.Click

        Hourglass(True) 'Calls the sub to turn on the wait cursor.

        ListView1.Items.Clear() 'Clear the items from the previous search. 

 

        Using Root As New DirectoryEntry 'Establishes a connection to current loged on users Active Directory

            Using Searcher As New DirectorySearcher(Root) 'Start at the top

                'If a first or last name isn't present search using the user ID field.

                If Not (tbFirst.Text.Length + tbLast.Text.Length) > 1 Then

                    Searcher.Filter = "(&(objectCategory=user)(ANR=" & tbUserID.Text & " * ))" 'This demonstrates an LDAP ANR search filter.

                Else

                    Searcher.Filter = "(&(objectCategory=user)(givenName=" & tbFirst.Text & "*" & ")(sn=" & tbLast.Text & "*" & "))" 'This is an LDAP search filter.

                End If 'to learn more about LDAP queries read this LDAP Query Basics

                Searcher.SearchScope = SearchScope.Subtree 'Start at the top and keep drilling down

                Searcher.PropertiesToLoad.Add("sAMAccountName") 'Load User ID

                Searcher.PropertiesToLoad.Add("displayName") 'Load Display Name

                Searcher.PropertiesToLoad.Add("givenName") 'Load Users first name

                Searcher.PropertiesToLoad.Add("sn")   'Load Users last name

                Searcher.PropertiesToLoad.Add("distinguishedName")   'Users Distinguished name

                Searcher.Sort.PropertyName = "sAMAccountName" 'Sort by user ID

                Searcher.Sort.Direction = System.DirectoryServices.SortDirection.Ascending 'A-Z

                Using users = Searcher.FindAll 'Users contains our searh results

                    'MsgBox(users.Count) 'Would display the total number of matches found

                    If users.Count > 0 Then 'If it's zero then no matches were found

                        'Item 1 through Item 5 are the columns in our 1st listview control. The “Nothing” makes it an empty variable.

                        Dim Item1 As String = Nothing 'User or Contact

                        Dim Item2 As String = Nothing 'sAMAccountName

                        Dim Item3 As String = Nothing 'givenName

                        Dim Item4 As String = Nothing 'sn

                        Dim Item5 As String = Nothing 'distinguishedName

                        Dim strDisplyName As String = Nothing 'DisplayName

 

                        For Each User As SearchResult In users 'goes throug each user in the search results

                            If User.Properties.Contains("displayName") Then '<--This makes sure the property actually exists and has a value

                                strDisplyName = CStr(User.Properties("displayName").Item(0)) '<-- use 0 here because this attribute only has one value

                            End If

 

                            Dim lv As ListViewItem = (ListView1.Items.Add(strDisplyName)) 'Display name goes in the first column of the listview.

 

                            If User.Properties.Contains("sAMAccountName") Then '<--This makes sure the property actually exists and has a value

                                Item1 = CStr(User.Properties("sAMAccountName").Item(0)) 'If it's got a sAMAcountName it's a user object.

                                Item1 = "User" 'The second column is user or contact

                            Else 'If there's no sAMAccountName I assume it's a contact

                                Item1 = "Contact" 'The second column user or contact

                            End If

                            lv.SubItems.Add(Item1) 'Add it to the listview

                            Try  'If this property is Null/Empty it will throw an exception. This traps it.

                                Item2 = CStr(User.Properties("sAMAccountName").Item(0))

                            Catch

                                Item2 = "" 'Since it's null set it to blank.

                            End Try

                            lv.SubItems.Add(Item2) 'Add it to the listview

                            Try  'If this property is Null/Empty it will throw an exception. This traps it.

                                Item3 = User.Properties("givenName").Item(0).ToString

                            Catch

                                Item3 = "" 'Since it's null set it to blank.

                            End Try

                            lv.SubItems.Add(Item3) 'Add it to the listview

                            Try  'If this property is Null/Empty it will throw an exception. This traps it.

                                Item4 = User.Properties("sn").Item(0).ToString

                            Catch

                                Item4 = "" 'Since it's null set it to blank.

                            End Try

                            lv.SubItems.Add(Item4) 'Add it to the listview

                            'Item 5 this item is added to the listview BUT thers's no column defind to display it.

                            lv.SubItems.Add(User.Properties("distinguishedName").Item(0).ToString)

                            'I've found this to be very handy way of storing a value to use at a later point.

                        Next

                    Else

                        Hourglass(False)

                        Exit Sub

                    End If

                End Using

            End Using

        End Using

        Hourglass(False)

    End Sub

    ' An item in the listview has been clicked!

    Private Sub ListView1_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles ListView1.SelectedIndexChanged

        If ListView1.SelectedItems.Count = 1 Then 'I have to do this check or I'll get an index out of range on SelectedItems(0) in the following line.

            lblUserDN.Text = ListView1.SelectedItems(0).SubItems(5).Text 'I display the users DN in a label on the main form.

            'Note: I pulled this from the listviw data stored durring btnSearch_Click sub above.

        End If

 

        Hourglass(True)

        ListView2.Items.Clear() 'Clear email addresses from previous selection

        'Establish connection to specific (The user or contact clicked on) AD object.

        'Here is where we use the data I pulled from the listviw durring btnSearch_Click sub above.

        Using user As New DirectoryEntry("LDAP://" & lblUserDN.Text)

            For Each eAddress As String In user.Properties("proxyAddresses") 'removes any occurances of matching eMail address.             

                Dim parts As String() = eAddress.Split(New Char() {":"c})

                AddItemToListView(parts(0), parts(1))

            Next

 

            If user.Properties.Contains("CanonicalName") Then '<--This makes sure the property actually exists and has a value

                Dim strUserCanonicalName = CStr(user.Properties("CanonicalName")(0)) 'Use 0 here because this attribute only has one value

            End If

            If user.Properties.Contains("description") Then '<--This makes sure the property actually exists and has a value

                Dim strDescription = CStr(user.Properties("description")(0)) ''Use 0 here because this attribute only has one value

                Me.rtbDescription.Text = strDescription

            Else

                Me.rtbDescription.Text = Nothing

            End If

 

            If user.Properties.Contains("sAMAccountName") Then '<--This makes sure the property actually exists and has a value

                Me.tbUserID.Text = CStr(user.Properties("sAMAccountName")(0)) 'Use 0 here because this attribute only has one value

            Else

                Me.tbUserID.Text = Nothing

            End If

 

            If user.Properties.Contains("employeeID") Then '<--This makes sure the property actually exists and has a value

                Me.tbEmpID.Text = CStr(user.Properties("employeeID")(0)) 'Use 0 here because this attribute only has one value

            Else

                Me.tbEmpID.Text = Nothing

            End If

            If user.Properties.Contains("department") Then '<--This makes sure the property actually exists and has a value

                Me.lblCDepartment.Text = CStr(user.Properties("department")(0)) 'Use 0 here because this attribute only has one value

            End If

            If user.Properties.Contains("title") Then '<--This makes sure the property actually exists and has a value

                Me.lblCRole.Text = CStr(user.Properties("title")(0)) 'Use 0 here because this attribute only has one value

            Else

                Me.lblCRole.Text = Nothing

            End If

            'With these next few properties I get the values using a GetADProperty function I wrote. It's does the exact same thing as above.

            tbHome.Text = GetADProperty(user, "homephone")

            tbPager.Text = GetADProperty(user, "pager")

            tbMobile.Text = GetADProperty(user, "mobile")

            tbFax.Text = GetADProperty(user, "facsimileTelephoneNumber")

            tbIPPhone.Text = GetADProperty(user, "ipPhone")

            'The manager property is stored as the distinguished name to the manager. Here we go get it.

            Dim strMgrDN As String = GetADProperty(user, "manager")

            If Not strMgrDN = "" Then  'Check if it's empty

                Using Manager As New DirectoryEntry("LDAP://" & strMgrDN) 'if it's not, set a directory entry point to it.

                    tbManager.Text = GetADProperty(Manager, "displayName") 'Put the display name of the manager in the textbox.

                End Using 'close the connection to AD for this object.

            Else

                tbManager.Text = ""

            End If

 

            rtbNotes.Text = GetADProperty(user, "info")

 

            If user.Properties.Contains("thumbnailPhoto") Then '<--This makes sure the property actually exists and has a value

                Dim bytBLOBData() As Byte = CType((user.Properties("thumbnailPhoto")(0)), Byte()) 'use 0 here because this attribute only has one value

                Using stmBLOBData As New MemoryStream(bytBLOBData) 'Create new memory stream.

                    pbUserImg.Image = Image.FromStream(stmBLOBData) 'Load image from stream

                End Using

            Else

                Me.pbUserImg.Image = Nothing

                pbUserImg.Tag = "NoPicture"

            End If

            'I have to do this check or I'll get an index out of range on SelectedItems(0) in the following line.

            If ListView1.SelectedItems.Count = 1 Then

                If ListView1.SelectedItems(0).SubItems(1).Text = "User" Then

                    lblConjunction.Text = "and"

                    'Check User Account control flags to check if user is disabled

                    If user.Properties.Contains("userAccountControl") Then

                        If IsAccountActive(CInt(user.Properties("userAccountControl")(0))) Then 'calls IsAccountActive function (See end )

                            lblAccountStatus.Text = " Enabled"

                            lblAccountStatus.ForeColor = System.Drawing.Color.Green 'Sets the text color

                        Else

                            lblAccountStatus.Text = "Disabled"

                            lblAccountStatus.ForeColor = System.Drawing.Color.Red 'Sets the text color

                        End If

                    End If

                    'Use System.DirectoryServices.AccountManagement to determine lockout status

                    Dim UserObject As AccountManagement.UserPrincipal = AccountManagement.UserPrincipal.FindByIdentity _

                    (New AccountManagement.PrincipalContext(AccountManagement.ContextType.Domain), Me.tbUserID.Text)

                    If UserObject.IsAccountLockedOut Then

                        lblLocked.Text = "Locked."

                        lblLocked.ForeColor = System.Drawing.Color.Red 'Sets the text color

                    Else

                        lblLocked.Text = "Unlocked."

                        lblLocked.ForeColor = System.Drawing.Color.Green 'Sets the text color

                    End If

                Else

                    lblAccountStatus.Text = "Contact"

                    lblAccountStatus.ForeColor = System.Drawing.Color.Black 'Sets the text color

                    lblLocked.Text = Nothing

                    lblConjunction.Text = Nothing

                End If

                'End use of System.DirectoryServices.AccountManagement to determin lockout status

            End If

 

            If user.Properties.Contains("homeMDB") Then 'if this is present than the account has a mailbox.

                lblHMB.Text = "YES"

            Else

                lblHMB.Text = "NO"

            End If

 

        End Using 'Close the directory entry to the user object

 

        Hourglass(False) 'Turn off wait cursor

 

    End Sub

 

    Public Sub Hourglass(ByVal Show As Boolean) 'Used to toggel wait cursor

        If (Show = True) Then

            System.Windows.Forms.Cursor.Current = System.Windows.Forms.Cursors.WaitCursor

        Else

            System.Windows.Forms.Cursor.Current = System.Windows.Forms.Cursors.Default

        End If

    End Sub

 

    Private Sub AddItemToListView(ByVal Item As String, ByVal Item2 As String)

        Dim lv As ListViewItem = (ListView2.Items.Add(Item))

        If Item = "SMTP" Then

            lv.Font = New Font(lv.Font, FontStyle.Bold)

        End If

        lv.SubItems.Add(Item2)

    End Sub

 

    Public Shared Function IsAccountActive(ByVal userAccountControl As Integer) As Boolean

        'This does a binary AND of userAccountControl and 2 if the flag is set the outcome is 0 if not it's 2

        Dim flagExists As Integer = userAccountControl And &H2

        'if a match is found, then the disabled flag exists within the control flags

        If flagExists > 0 Then

            Return False

        Else

            Return True

        End If

    End Function

 

    ''' <summary>

    ''' Helper method that gets properties for AD users.

    ''' </summary>

    ''' <param name="de">DirectoryEntry to use</param>

    ''' <param name="pName">Property name to get</param>

    Public Shared Function GetADProperty(ByVal de As DirectoryEntry, ByVal pName As String) As String

        Dim pValue As String

        Try

            pValue = de.Properties(pName).Value.ToString() 'When value is found return it. . .

        Catch

            pValue = "" 'When property dosn't exist set value to null and return . . .

            'MsgBox("Property Notfound =" & pName)

        End Try

        Return (pValue) '. . .here

    End Function

 

End Class