Novell is now a part of Micro Focus

The Novell Controls for ActiveX and Visual Basic: Searching NDS Field Values

Articles and Tips: article

MORGAN B. ADAIR
Software Engineer
Novell Developer University

01 Oct 1999


Fourth article in a series on NDS programming using Visual Basic and the Novell Controls for ActiveX. Shows you how to search a directory for fields with a particular value. Includes an example program.

Introduction

This article is the fourth in a series on Novell Directory Services (NDS) programming using Visual Basic and the Novell Controls for ActiveX. The last article ("The Novell Controls for ActiveX and Visual Basic: Reading Field Values" in the September 1999 issue of Novell Developer Notes) told how to read the value of NDS directory fields. This article tells how to search a directory for fields with a particular value (for example, to search for User entries with a Surname field of "Schmidt"). As always, I provide an example program to illustrate the technique.

Entries, Layouts, and Fields

In the previous article I talked about the hierarchy of containers in a directory (typically, Organizations and Organizational Units). These containers may contain entries and other containers.

Entries are data structures of a specific type or Layout. For example, the layout for Printer entries specifies that directory entries that represent printer objects will have fields for the name of the print queue(s) that the printer services, the name of the print server that manages the printer, and so on. The layout specifies the type of data that can be associated with a printer. An instance of a Printer object contains data pertaining to a specific printer.

Each Field also has a data type, called the Field Type. There are four simple field types:

  • Boolean

  • Date

  • Long

  • String

There are also nine predefined structure field types, and it is possible to define new structure field types. The predefined structure field types are:

  • NWACL. An Access Control List (ACL). An ACL is the key component in determining Directory access control. The ACL determines which operations a subject entry can perform on another entry.

  • NWEMailAdress. Represents an E-mail address.

  • NWNetAddress. Represents a network address.

  • NWOctetString. Represents a byte string whose value is not interpreted by the Directory

  • NWPath. Represents a file system path.

  • NWPostalAddress. Specifies address information for physical delivery of postal messages.

  • NWStream. A field of binary data that is (like an NWOctetString) not interpreted by the directory. Usually used to store large amounts of data, as with graphic or audio files.

  • NWTimeStamp. A field that marks the time when an event occurred or will occur.

  • NWTypedName. A field that represents a level and in interval associated with an entry.

The documentation for the NWDir control (available on the Web at http://developer.novell.com/ndk/doc.htm) has more information about the properties of structure field types.

In addition to the field type, fields can also be single-valued or multi-valued. For example, Group entries have a Member field that is a multi-valued field of type String. The Member field contains the full names of User entries that have membership in the group.

Searching a Directory

The NWDir control has a Search method that searches for entries with a specified name and layout. The Search method is declared as:

object.Search(NameFilter As String, ClassFilter As String, Scope As NWSearchScope, 

     StartContext As String, [SearchMode As NWSearchMode])

The table below contains descriptions of each of the Search method's parameters.


Parameter Name

Description

NameFilter

Specifies a comma-delimited list of entry names. The asterisk (*) can be used at the end of a name as a wildcard character.

ClassFilter

A comma-delimited list of the names of the layouts that are to be used in the search.

Scope

Indicates if the scope of the search is constrained to the given context (SEARCH_SUBORDINATES), or if it should also encompass subordinate containers (SEARCH_SUBTREE).

StartContext

The full name of the context where the search is to start.

SearchMode

Optional. An NWSearchMode constant that specifies if the search is done synchronously (using the SEARCH_SYNCHRONOUS constant) or asynchronously (using the SEARCH_ASYNCHRONOUS constant). If no parameter is passed, the search is synchronous.

The Search method just initiates a search. When the search is completed, the NWDir control generates a SearchCompeted event. Since searching a large Directory can take several minutes (or more depending on network topology and container replication), it is advisable to use the SEARCH_ASYNCHRONOUS search mode. When searches are done asynchronously, Search returns immediately, and the program can perform other tasks while waiting for the search to complete. For example, if your program uses an asynchronous search, you could provide a cancel button, so that the user can cancel a search that is taking too long. With a synchronous search, program execution is blocked until the search completes.

The SearchCompleted event returns an array of strings containing the full name of every directory entry that matches the specified search parameters. Searching for entries containing a particular field value is therefore a two-step process:

  1. Search for entries with the name and layout that you are interested in. For example, to search for all User entries, pass "*" in the NameFilter parameter, and "User" in the ClassFilter.

  2. Inspect each entry returned by the search method for the field value you are interested in.

Example Program: SearchField

SearchField allows the user to search Fields in a Directory for a specified string. To keep this example simple, we'll only search single-valued string Fields. The program starts out by asking the user to select one of the Directories the workstation is logged in to, and gives the option to log in to another Directory. Since we already created the forms and wrote the code to do that for the ReadField program (see "The Novell Controls for ActiveX and Visual Basic: Reading Field Values" in the September 1999 issue of Novell Developer Notes), we'll reuse those forms in SearchField. After the user has selected a Directory, SearchField displays a form that allows the user to select the parameters that will be used in the search:

  • The layout of the Entries to be searched (User Entries, Server Entries, etc.).

  • The name of the Field within the Layout that will be searched.

  • The context within the Directory where the search will begin.

  • The string to search for.

When the user specifies the parameters and clicks on the Search button, SearchField finds all Entries that match the specified parameters and displays their Full Names in a text box labeled "Search Results." Figure 1 shows the results of a search.

Figure 1: SearchField searches for user entries with "adair" in the Surname field.

Building SearchField

To build the SearchField program:

  1. Start Visual Basic and create a new "Standard EXE" project.

  2. Add the Novell Directory and Session controls to your project:

    1. Open the Components dialog by selecting "Components" from the Project menu, or by right-clicking the Toolbox and selecting "Components" from the pop-up menu.

    2. Scroll down the list of components and check the boxes next to "Novell Directory Control" and "Novell Session Control."

    3. Click OK.

The SearchField program consists of 3 forms and one code module. As mentioned earlier, two of the forms can be copied from the ReadField program.

ConnectionsForm, LoginForm, and Module1

The previous article in this series, "The Novell Controls for ActiveX and Visual Basic: Reading Field Values" (September 1999), had instructions for designing and writing the associated code for two forms, ConnectionsForm and LoginForm, and one code module, Module1. You will need to make one small change to each of the two forms, so you should create a new directory for the SearchField program and copy ConnectionsForm.frm, LoginForm.frm, and Module1.bas to this new directory.

After copying the files, do the following:

  1. Add Module1 to the project by selecting "Add Module" from the Project menu, clicking on the "Existing" tab, and selecting the Module1.bas file that you just copied.

  2. Add the forms to the project by selecting "Add Form" from the Project menu, clicking on the "Existing" tab, and selecting the ConnectionsForm.frm and LoginForm.frm files that you just copied.

  3. For each form, double-click anywhere on the form to open the associated code and change the line of code that says

ReadFieldForm.Show

to

SearchFieldForm.Show

SearchFieldForm

Once the user has selected a Directory to search, the SearchField program displays SearchFieldForm to get the information needed to perform the search. You can create SearchFieldForm by following the instructions below.

Add a new form to your project by selecting "Add Form" from the Project menu and clicking the "Open" button with the "Form" icon selected. Resize the form and add the components and associated attributes as shown in Figure 2.

Figure 2: Designing SearchFieldForm.

Next, you need to enter the code associated with the form and each of its components. When SearchFieldForm loads, it needs to build a list of Layouts that are defined in the Directory the user has selected. It also enters default values for the starting context text box. To enter the code that does this, open the form's load procedure by double-clicking anywhere on the form. Enter the code shown below:

Private Sub Form_Load()
   Dim entry As NWLayoutDescription

   ' Set session's default context to the selected Directory
   NWSess1.DefaultFullName = NWSess1.DefaultFullNameFromTreeName(DefaultTree)
    
   'Set the starting context to search as the current
   '   context in the selected Directory
   ContextTxt.Text = NWDir1.ContextFromFullName(NWSess1.DefaultFullName)
    
   'Get the names of all Layouts in the Directory the user selected
   For Each layoutName In NWDir1.Layouts
       LayoutNamesLst.AddItem layoutName.Name
   Next layoutName
    
   'Select the first Layout
   LayoutNamesLst.Selected(0) = True
End Sub

SearchFieldForm also needs to build a list of Fields that are associated with the Layout that the user selects from the LayoutNamesLst list box. The Form_Load procedure also selects a Layout, and when it does it generates a Click event, just as if the user had selected the first Layout in the list by clicking on it. So, the LayoutNamesLst_Click subroutine can handle either event. Double-click on the LayoutNamesLst list box and enter the code below:

Private Sub LayoutNamesLst_Click()
    Dim entry As NWLayoutDescription
    Dim fieldName As Variant
    
    'Clear out the current list of Field names
    FieldsLst.Clear
    
    'Now put in the names of Fields in the newly-selected Layout
    Set entry = NWDir1.Layouts(LayoutNamesLst.Text)
    
    For Each fieldName In entry.Fields
        'But only Fields of type String and SingleValued
        If (fieldName.TypeName = "String") And _
            (NWDir1.FieldTypes.Item(fieldName.Name).SingleValued = True) Then
            FieldsLst.AddItem fieldName.Name
        End If
    Next fieldName
    
    'Only enable the Search button if there's a Field in the list to select
    If FieldsLst.ListCount = 0 Then
        SearchBtn.Enabled = False
    Else
        FieldsLst.Selected(0) = True
        SearchBtn.Enabled = True
    End If
End Sub

Next, we'll add the code for the two buttons. First, double-click on the Exit button and enter the code that will be executed when the user clicks on it:

Private Sub ExitBtn_Click()
    End
End Sub

Double-click on the Search button to open the button's Click subroutine and enter the code shown below. This is the code that initiates a search, based on the data the user has entered in SearchFieldForm's various controls.

Private Sub SearchBtn_Click()
    'Switch to arrow/hourglass cursor while search is running
    '(asynchronous search returns immediately and triggers 
    'SearchCompleted event when search is completed)
    SearchFieldForm.MousePointer = vbArrowHourglass
    
    'do the search
    NWDir1.Search "*", LayoutNamesLst.Text, SEARCH_SUBTREE, _
        NWDir1.FullNameFromTreeAndContext(DefaultTree, ContextTxt.Text), _
        SEARCH_ASYNCHRONOUS
End Sub

We want to do a case-insensitive search, so enter the following line of code in the general declarations for SearchFieldForm (select "General" from the object list box at the top of SearchFieldForm's code window).

Option Compare Text

When the search is completed, the NWDir control will generate a Search Completed event. The handler for this event will look at each entry that the Search method has returned and compare the value of the field the user has selected with the string the user has provided. Since we're just doing string comparisons, we can use VB's Like operator, which allows the user to use wildcard characters.

To write the code that handles the SearchCompleted event, select "NWDir1" in the object list box at the top of SearchFieldForm's code window, and "SearchCompleted" in the procedure list box. Then enter the code shown below.

Private Sub NWDir1_SearchCompleted(ByVal Results As Variant)
    Dim selectedEntry As NWEntry
    Dim selectedField As String
    Dim numMatches As Integer
    Dim selectedString As Variant
    
    'Enable the error handler
    On Error GoTo ErrorHandler
    Err.Clear
    
    'Restore the arrow cursor
    SearchFieldForm.MousePointer = vbDefault
 'Clear out the Results box
    ResultsTxt.Text = ""
    numMatches = 0
    
    If IsEmpty(Results(0)) Then
        'No matches
        ResultsTxt.Text = "[No matches]"
    Else
        For Each result In Results
            Set selectedEntry = NWDir1.FindEntry(result)
                selectedString = selectedEntry.GetFieldValue(FieldsLst.Text)
                If VarType(selectedString) <> vbEmpty Then
                    If selectedString Like FieldValueTxt.Text Then
                        ResultsTxt.Text = ResultsTxt.Text + result + ": " + _
                                                   selectedString + vbCrLf
                        numMatches = numMatches + 1
                    End If
                End If
        Next result
        
        'Display a count of number of matches returned by the search
        ResultsTxt.Text = ResultsTxt.Text + CStr(numMatches) + " Matches"
    End If
    
Exit Sub

ErrorHandler:
    Select Case Err.Number
        Case 13     'Type mismatch
            MsgBox "Type mismatch"
        Case 601    'Directory Entry Not Found Error
            MsgBox "Directory Entry " + result + " Not Found"
        Case 603    'Directory Field Not Found Error
            ResultsTxt.Text = ResultsTxt.Text + "Field '" + FieldsLst.Text _
                                     +  "' Not Found in " + result + vbCrLf
        Case Else
            MsgBox "Unanticipated error: " + Err.Description, vbExclamation
    End Select
    Err.Clear
    Resume Next
End Sub

Still Want More?

Knowing how to search the directory and read field values is all you need to write some good, basic NDS applications. For example, it would not be difficult to modify the SearchField example to be a simple phone book program. But just reading information out of the directory is not enough. Next we'll look at how to write information into directory fields.

* Originally published in Novell AppNotes


Disclaimer

The origin of this information may be internal or external to Novell. While Novell makes all reasonable efforts to verify this information, Novell does not make explicit or implied claims to its validity.

© Copyright Micro Focus or one of its affiliates