Novell is now a part of Micro Focus

Building eDirectory-Enabled Applications using Delphi: ActiveX

Articles and Tips: article

Wolfgang Schreiber
Manager of Developer Support
Novell EMEA
wschreiber@novell.com

01 Dec 2001


This is the second in a series of AppNotes on eDirectory (NDS) programming with Delphi. The first article in the November 2001 issue introduced the two methodologies for eDirectory access: ActiveX controls vs. low-level calls. This second article will take a closer look at the ActiveX approach and review the specific features, benefits, and drawbacks of that approach, while the third article will review the more complex but more efficient low-level technique.


Topics

Delphi, eDirectory-enabled applications, NetWare programming, code samples, ActiveX controls

Products

Novell eDirectory, Delphi

Audience

network programmers

Level

beginner

Prerequisite Skills

familiarity with Delphi programming and Novell eDirectory

Operating System

NetWare

Tools

Novell Developers Kit (NDK)

Sample Code

yes

Introduction

ActiveX controls are OLE objects or Component Object Model (COM) objects. Most frequently (but not necessarily), they are implemented as *.OCX files. Using off-the-shelf components such as ActiveX controls has the advantage that much of the complexity of a given task (like accessing network resources) is hidden from the developer and taken care of within the ActiveX control. This typically leads to a significantly shorter development cycle and smaller code sizes. However, the trade-off may be restricted functionality (not all features that are available in low-level coding may be accessible in the OLE object) or slower performance (the COM object internally might provide unwanted information or features that potentially reduce execution speed).

While the Novell ActiveX controls will never completely match the functionality of a sophisticated, well-written custom application using low-level APIs, they offer a rich set of features without hitting performance too much. Whether you choose ActiveX or low-level calls will depend on your specific programming task. This AppNote series may help you make the right decision.

This AppNote will not discuss the various available Novell ActiveX controls in detail, but will focus instead on the their implementation in Delphi. If you would like to learn more about these controls and their usage, refer to the following articles that have been published in Novell AppNotes (all available online at http://www.novell.com/appnotes/):

  • "How to Use the Novell Directory Control (NWDir)" - Part 1 March 2001, Part 2 May 2001, Part 3 August 2001

  • "How to Access NDS from HTML or ASP" - Part 1 March 2001, Part 2 April 2001, Part 3 May 2001, Part 4 July 2001

  • "Building an NDS-Enabled Application Using Visual Basic and the Novell Controls for ActiveX" - Part 1 July 2000, Part 2 February 2001

  • "Programming with the Novell Controls for ActiveX and Visual Basic: Getting Started" (Novell Developer Notes, June 1999)

Most of the previous AppNotes focussed on implementations with Visual Basic (VB). Due to its ease of use, VB has become a very popular programming language for Rapid Application Development (RAD) and can also easily be integrated into Office applications or Web pages. Many people, including myself, prefer Borland's Delphi to VB, since Delphi combines the power and flexibility of C with the ease of use of a RAD environment. You may also combine the use of ActiveX controls with low-level calls to have the best of both worlds.

The sample code for the examples in this AppNote can be downloaded from the Web at http://developer.novell.com/research/appnotes/download.htm.

Overview of ActiveX Controls

While this AppNote will focus on just a few of the ActiveX controls available from Novell, it is helpful to get an overview of the currently-existing controls. They are summarized in the following table.


Control Name
Short Title
Description

NDS / eDirectory

NWDir

Directory Control

Accesses Novell eDirectory, allowing you to add, delete, search, and manage Directory entries, and extend and mange the Directory schema

NWDirA

Directory Administration Control

Allows you to set up and control eDirectory partitions and replicas; also to access, create, merge, modify, or synchronize partitions and replicas in an eDirectory tree

NWSecStr

SecretStore Control

Provides features to manage a user's SecretStore; allows you to create or remove application secret IDs; application secrets can also be accessed from the user's SecretStore; you can also lock and unlock the SecretStore

NWDirQ

Directory Query Control

Queries Novell eDirectory and retrieves information about Directory entries; allows you to set the filter, time limit, and maximum number of entries for a search

NWUsrGrp

User Group Control

Used to manipulate User and Group objects in an eDirectory tree; provides features to retrieve, add, delete, or modify a User or Group object

NWDirAuth

Directory Authenticator Control

Verifies the identity of an object in eDirectory; validation can be done by verifying the password of the object or by validating its access rights over other objects

NWNDSDom

NDS Corporate Edition Domain Control

Enables you to add a User object to an NDS Domain object; user can also be assigned to a Local or Global Group of an existing domain

NWCatA

Catalog Administration Control

Used to manage Catalog objects; a Catalog is an eDirectory object that stores user-specified data from an eDirectory tree in a Catalog database

LDAP

NWIDir

Internet Directory Control

Allows you to create network applications that access Lightweight Directory Access Protocol (LDAP), a protocol for accessing Internet directory services

NWIDirS

Internet Directory Schema Control

Allows you to manage the schema in directories that provide access through LDAP

NWIDirE

Internet Directory Entries Control

Used to manage entries in directories through LDAP; you can add, delete, and move the entries and attributes

NWIDirQ

Internet Directory Query Control

Queries directories through LDAP; allows you to set the filter, maximum number of entries, and time limit for a search

General Administration

NWAppA

Application Administration Control

Provides features to set up and manage the Application Management and Distribution features in ZENworks

NWSess

Session Control

Abstracts features of a network session, allowing you to log in using the NetWare Client 32 dialog box, list current drive mappings, and run user-specific login scripts

NWBind

Bindery Control

Accesses the NetWare bindery database to manage, search, back up, and restore functions on a server, and to administer network entries

NWVolA

Volume Administration Control

Provides features to manage a NetWare volume, letting you view and modify volumes, directories, and files

NWSrvA

Server Administration Control

Provides features to manage NetWare servers; contains properties and methods to view or edit primary server parameters and feature settings

Printing

NWDPPrtA

NDPS Printer Administration Control

Simplifies the management of NDPS Printer objects in eDirectory; allows you to manage jobs, job configurations, queues, notifications, access control, and so on

NWPQA

Print Queue Administration Control

Provides features to manage NetWare print queues and print jobs

NWPSA

Print Server Administration Control

Used to set up and control a print server and its associated printers from a remote workstation

Comm.

NWCliSkt

NWSrvSkt

Client and Server Socket Controls

Provide features to design client/server applications that communicate using either TCP or SPX protocols

NWPrSkt

Peer Socket Controls

Provides connectionless socket communications with IPX and UPD socket features

Browse/Select

NWSelect

Selector Control

A visual control used to select a network object; when the control is initiated, a dialog box lists the eDirectory and Bindery objects present on the network

NWBrowse

Browser Control

Allows you to view an eDirectory tree, the file system, or an LDAP directory; lists various NetWare resources in different types of views

Of course, this AppNote cannot possibly cover all of these controls, so it will focus on a few that you can use to access eDirectory.

Installation and Registration

There are two main operations that you need to perform before you can develop applications with the Novell ActiveX controls:

  • Tell Windows all about it (copy and register the controls)

  • Tell Delphi all about it (create type libraries)

Copy and Register the Controls

This step is typically performed during the installation of the controls on your workstation. First, download the controls from Novell's DeveloperNet Web site at http://developer.novell.com/ndk/ocx.htm. If you are not yet a registered member of DeveloperNet, choose the free electronic membership for now.

Once you have completed the download, run the self-extractable OCX.EXE file. The program will copy the controls, the Windows help files, and some sample code to a directory on your local drive (for example, C:\Novell\ActiveX\). It will also add the controls to your Windows registry. Some controls have dependencies on other software (see the OCX.TXT file for details). If these prerequisites are not met, the control may not be registered.

If you ever want to manually register a control, you can use the Borland-provided tool REGSRV32.EXE or the ready-made RegOcx.bat, both of which will be installed with the controls. Also, if you want to deploy your application on other machines, you will have to register the controls on these target machines using one of the methods mentioned above.

Create Type Libraries

Before you can create your first Delphi application with the controls, you need to inform Delphi about their existence. Only after the creation of a Delphi type library Delphi is able to perform a syntax check and use the features of code completion.

Just as with the previous step, this needs to be done only once. Start Delphi and, from the main menu, select Components | Import ActiveX Control. Choose one of the Novell controls (they all start with "Novell ") and click on the Install button. Accept all upcoming information screens.

Repeat this step for each control you plan to use. For the purposes of this AppNote, you just need the controls named "Novell Directory Control." the "Novell Directory Query Control," and the "Novell Network Selector Control."

The new controls should now show up as icons on the ActiveX tab in your Delphi IDE. Delphi will remember the new controls you have registered the next time you start it.

During this registration, Delphi will read the ActiveX control and create a type library file in the compiler directory (for example, C:\Program Files\Borland\ Delphi5\Imports). It will use this library to validate your source code during compilation.

Note: If you replace an ActiveX control with an updated version, you should first unregister the control, then register the new version and re-import it into Delphi. Otherwise Delphi would access the new control with the old type libraries, which could cause unpredictable results.

Getting Started

Before you start on your first ActiveX application in Delphi, here are some suggestions which may be useful during your programming work.

Get Yourself Some Help

I would recommend opening the online documentation or the Windows help files for ActiveX so that you can easily switch between your code and the documentation. The documentation contains general information about each control, detailed lists of properties, methods, and events of each object, as well as complex object diagrams (which I like the most).

Figure 1 is a small excerpt of the help file for the Novell Directory control.

A portion of the online help for NWDir.

This section of the help document shows the details of the central NWDir object. It lists the available properties of this object: for example, the "Entries" collection containing all eDirectory objects in the selected container, and the "Layouts" collection containing the schema definitions of the eDirectory tree.

Figure 1 also shows the available methods for the central NWDir object; for example, the "Search( )" method you can use to locate objects in the tree. It also lists the available events, such as the "EntryAdded" event that you can use to call a function when an object is successfully added to your tree.

Again, there isn't nearly enough space in this AppNote to cover more than a small fraction of the available ActiveX controls. If you want to find out more about these, I recommend that you take a look at some of the sample code, and then, with the documentation handy, start experimenting on your own.

Let's Talk About X

You are probably asking yourself, "After I have decided which ActiveX control I'd like to use, how do I proceed?" The answer is easy: After you have successfully registered the controls to Windows and Delphi, just create a new Delphi form and drag the selected control from the ActiveX component bar in your IDE onto the form.

Most of the controls introduced above are non-visual controls-they are visible only at design-time and do not have their own visual interface at run-time. The two exceptions are the visual Novell Browser Control and the Novell Selector Control: these are visible both at run-time and at design-time.

Well, enough small talk. Let's look at some code.

Common eDirectory Tasks

This section presents a series of common eDirectory tasks and looks at some implementations with the Novell controls. Since the main purpose of this section is to introduce the new functionality, I will not include sophisticated error handling. In practice, however, you should embed many of the statements that access ActiveX features into "try except" clauses, since the preferred way for an ActiveX control to return an error is to throw an exception.

To make the implementation of the following code samples as simple as possible, the user interface will be reduced to the bare minimum.

To run the test applications, simply start Delphi, create a new application, and add one button and one list box. Then drag the NWDir component from the ActiveX tab in your IDE onto the form. Since the NWDir component is non-visual, it doesn't matter where you place it: it will only be visible at design time. Your new form, which you will use for most of the examples in this AppNote, should look similar to the one shown in Figure 2.

Basic form that will be used in the examples.

Task 1: Who Am I

To get information about the name of your current tree and container, as well as your own login name, you can use some of the base properties of the NWDir object.

In design mode, click on Button1 and complete the code so it looks like this:

procedure TForm1.Button1Click(Sender: TObject);
begin
    Listbox1.Items.Add(NWDir1.ShortName);
    Listbox1.Items.Add(NWDir1.FullName);
    Listbox1.Items.Add(NWDir1.LoginName);
end;

Now save and run the program. It will display the short container name (for example, "users"), the long container name (for example, "NDS:\\MyTree\ MyOrg\ MyOU\Users"), and finally your own user name (for example, "NDS:\\MyTree\MyOrg\MyOU\Users\MySelf").

Task 2: Browse Objects

After you have retrieved your own container and user name, how do you detect the other objects in the container? One of NWDir's key properties is helpful: The Entries collection (or "Array" if you prefer) is an object of type NWEntries, and it contains a list of objects in the current container. The number of items in this collection is stored in the Entries.Count property, and each object can be accessed with the Entries.Item[ ] property.

If you look in the help file (see Figure 3), you can find out that the individual elements of this NWEntries collection are objects of type NWEntry. This NWEntry object has some helpful properties like ShortName, LongName, or Layout. And since you'd also like to retrieve the object type (or "Layout name" in ActiveX terminology), you can use the Layout property (which turns out to be another object with its own properties and methods).

A portion of the online help for NWEntry.

Replace the previous sample code lines with this Delphi sequence:

procedure TForm1.Button1Click(Sender: TObject);
VAR
    i  : Integer;
    x1 : NWEntries;
    x2 : NWEntry;
begin
    x1 := NWDir1.Entries;            // store the collection
    for i := 1 to x1.Count do begin
        x2 := x1.Item[i-1];          // store a single object
        Listbox1.Items.Add(x2.Layout.Name + ': ' + x2.ShortName);
        Application.ProcessMessages; // update display
        end;
end;

As you run this application, it should retrieve and display all objects in your current container. The list will include the objects' types and short names.

Task 3: Set Tree and Context

So far, you have only worked with the objects in the current container. Changing the tree and the container is simply done by setting the NWDir.FullName property to the tree/context of your choice. This can be done with a variety of approaches:

  • Use the Novell Selector control to select a tree/container

  • Get input from the user

  • Set the property at design time

  • Use the Novell Session control to scan the trees you're connected to

The next example demonstrates the first two of these approaches. To do this, add an edit box and the NWSelect control to your project. Then add the FormCreate event to the form and the Edit1DblClick event to the edit box. Now add these lines of code:

procedure TForm1.FormCreate(Sender: TObject);
begin     // show the current tree/context
    Edit1.Text := NWDir1.FullName;
end;

procedure TForm1.Edit1DblClick(Sender: TObject);
begin     // select new container
    NWSelect1.FullName   := NWDir1.FullName;         // search base
    NWSelect1.ViewType   := NWViewType(selViewList); // view: list
    NWSelect1.TypeFilter := 'Organizational Unit, Organization, Country';
    if NWSelect1.Show                                // call selector
        then Edit1.Text := NWSelect1.FullName;        // show new container
end;

procedure TForm1.Button1Click(Sender: TObject);
VAR
    i  : Integer;
    x1 : NWEntries;
    x2 : NWEntry;
begin
    Listbox1.Clear;
    try
        NWDir1.FullName := Edit1.Text;   // set selected context
        x1 := NWDir1.Entries;            // store the collection
        for i := 1 to x1.Count do begin
            x2 := x1.Item[i-1];          // store a single object
            Listbox1.Items.Add(x2.Layout.Name + ': ' + x2.ShortName);
            Application.ProcessMessages; // update display
            end;
        except
            on e: Exception do Listbox1.Items.Add(e.Message);
        end;
end;

What did you accomplish in this update? In the FormCreate event, you simply displayed the current tree and context in an edit box. If the user double-clicks in the edit box, the Selector control will be launched after its starting context and display type have been set. The filter has been set to show only container objects (Organizational Unit, Organization, Country), since this is what you want to select. Once a container is selected, it will be shown in your edit box.

The Button1Click event will now set the current context of your NWDir control to whatever the edit box contains, thereby allowing for manual user modification of the tree and context. To be on the safe side, a "try except" clause is used.

Task 4: Read Attributes

So far you have been reading object names and their types, but you haven't yet tried reading object attributes. The list of available attributes for a given object can be accessed in the schema documentation (in the ActiveX help files), or in the properties of the NWDir control (at design time). They can also be read dynamically from the schema with NWDir calls at run time.

One problem that needs to be considered is that some attributes are single-valued, such as "Account Balance," while others are multi-valued, such as "Group Membership." Also, you need to take into account a variety of attribute data types (numbers, strings, and so on). The easiest way to handle this situation is to treat all attribute data as Variant arrays.

This approach is taken in the next example. Again, you need the basic form with an added edit box (see Figure 4).

Basic form with an added edit box.

procedure TForm1.FormCreate(Sender: TObject);
begin     // start with a simple attribute
    Edit1.Text := 'Surname';
end;

procedure TForm1.Button1Click(Sender: TObject);
VAR
    i,j : Integer;
    x1  : NWEntries;
    x2  : NWEntry;
    V   : Variant;
begin
    Listbox1.Clear;
    try
        x1 := NWDir1.Entries;            // store the collection
        for i := 1 to x1.Count do begin
            x2 := x1.Item[i-1];          // store a single object
            Listbox1.Items.Add(x2.ShortName);
            try  // read the attribute into the Variant array V
                V := x2.GetFieldValue(Edit1.Text, Unassigned, TRUE);
                for j := 0 to VarArrayHighBound(V, VarArrayDimCount(V)) do
                    if String(V[j])<>''
                        then Listbox1.Items.Add(
                        '  '+Edit1.Text+': '+String(V[j]));
                except
                end;
            Application.ProcessMessages; // update display
            end;
        except
            on e: Exception do Listbox1.Items.Add(e.Message);
        end;
end;

You can read each attribute that has been entered into your edit box. Since each attribute is treated as a variant array, you need to identify this array's dimensions using the functions VarArrayHighBound and VarArrayDimCount (see the Delphi help for more information on these functions). Then you can read each array element and display it as a string.

Try running this application and replace the "Surname" attribute with other attributes such as "Account Balance," "ACL," or "'Group Membership."

Task 5: Change Attributes

Changing attributes is not much more difficult than reading attributes. This example is a simple application that changes your own surname-at least in eDirectory.

Note: The simple, single-valued string attribute "Surname" was chosen for its ease of demonstration. To change multi-valued attributes, you would have to take a slightly different approach: read all existing attribute values into an array, do all modifications to this array in memory, and then save the whole array back to the object. The current implementation of the NWDir control does not allow for the addition or removal of individual attribute values in a set.

When you test this application, be aware that you are now changing eDirectory attributes. To be on the safe side, you should run this application in a testing environment or with appropriate care.

For the purposes of this example, add a second button and a second edit box to your project. (see Figure 5).

Basic form with an extra button and edit box added.

Then add OnClick methods to both buttons and the list box. Complete the code so that it looks like this:

procedure TForm1.Button1Click(Sender: TObject);
VAR       // list objects in current context
    i   : Integer;
    x1  : NWEntries;
    x2  : NWEntry;
begin
    Listbox1.Clear;
    try
        x1 := NWDir1.Entries;            // store the collection
        for i := 1 to x1.Count do begin
            x2 := x1.Item[i-1];          // store a single object
            Listbox1.Items.Add(x2.FullName);
            Application.ProcessMessages; // update display
            end;
        except
            on e: Exception do Listbox1.Items.Add(e.Message);
        end;
end;

procedure TForm1.ListBox1Click(Sender: TObject);
VAR       // read the Surname attribute
    U   : String;
    xu  : NWEntry;
begin
    // start with some hosekeeping
    ButtonSave.Enabled := false;
    Edit1.Text := '';
    Edit2.Text := '';
    // locate the selected user object
    U          := Listbox1.Items[Listbox1.Itemindex];
    xu         := NWDir1.FindEntry(U, '', '');
    // accept only user objects, here
    if (xu.Layout.Name <> 'User') then exit;
    // show the selected user name
    Edit1.Text := U;
    ButtonSave.Enabled := true;
    try  // read the Surname into Edit2.Text
        Edit2.Text := xu.GetFieldValue('Surname', 'n/a', FALSE);
        except
            on e: Exception do ShowMessage(e.Message);
        end;
end;

procedure TForm1.ButtonSaveClick(Sender: TObject);
VAR
    xu  : NWEntry;
begin
    // locate the selected user object
    xu         := NWDir1.FindEntry(Edit1.Text, '', '');
    if (xu.Layout.Name <> 'User') then exit;
    try  // save Edit2.Text as new Surname
        xu.SetFieldValue('Surname', Edit2.Text);
        xu.Update;  // submit changes
        except
            on e: Exception do ShowMessage(e.Message);
        end;
end;

This small application allows you to select a user from the list box, read its surname, change the surname in the edit box, and save it back to eDirectory. For a Delphi sample that updates multi-valued attributes, visit the Developer Support sample area at http://developer.novell.com/support/sample.htm. and look for DXAddMmb

Task 6: Read the Schema

Reading the schema on-the-fly doesn't require much work. Use the simple form with one list box and one button. You'll need to create a nested loop: the outer loop reads the layouts (classes or object types), while the inner loop reads the field descriptions (attribute names) for each class.

procedure TForm1.Button1Click(Sender: TObject);
VAR       // display classes and attributes
    i,j : Integer;
    L   : NWLayoutDescriptions;
    F   : NWFieldDescriptions;
begin
    Listbox1.Clear;
    try
        L := NWDir1.Layouts;             // store the classes collection
        for i := 1 to L.Count do begin
            Listbox1.Items.Add(L.Item[i-1].Name);
            F := L.Item[i-1].Fields;     // store the fields collection
            for j := 1 to F.Count do     // display fields (attributes)
                Listbox1.Items.Add('   '+F.Item[j-1].Name);
            Application.ProcessMessages; // update display
            end;
        except
            on e: Exception do Listbox1.Items.Add(e.Message);
        end;
end;

Reading the schema is really easy, and it might be helpful in situations where the user selects an object and the application offers further options dependent on what attributes are available for the selected object.

Task 7: Create Objects

Creating objects can also be easy-at least the creation of the new entry with its mandatory attributes can be done quickly. Here is a bare-bones code sample (again, use just a list box and an edit box):

procedure TForm1.Button1Click(Sender: TObject);
VAR       // create a new user
    x1   : NWEntries;
    x2   : NWEntry;
begin
    x1 := NWDir1.Entries;
    Listbox1.Items.Add('Adding user '+ Edit1.Text);
    try
        // add the new object
        x2 := x1.Add('User', Edit1.Text);
        // add mandatory attributes
        x2.SetFieldValue('Surname', 'Something stupid');
        // assign password
        x2.SetPassword('SECRET', Null);
        // submit changes
        x2.Update;
        Listbox1.Items.Add('  User added');
        except
            on e: Exception do Listbox1.Items.Add('  '+e.Message);
        end;
end;

If you are setting user passwords, you should always pass them in uppercase. All NetWare tools (including NWAdmin and the client Login function) convert passwords to uppercase, so new users will not be able to log in if your application saved lowercase passwords.

If you would like to use templates to create users, you should use the NWUsrGrp control instead of the NWDir control. NWUsrGrp also makes it easier to create home directories than does the general-purpose NWDir control.

Task 8: Search for Objects

Now consider another frequent task: to query eDirectory for objects that match certain criteria. Typical instances may be "Locate all queue objects in the tree" or "List the users that haven't logged in for a while."

While the NWDir control has a Search method that can filter on object types, there is a somewhat more flexible control specialized on eDirectory queries: the Novell Directory Query control (NWDirQuery). For this example, use the basic form with the button, the list box, and an additional edit box. Drag the NWDirQuery control onto the form.

procedure TForm1.FormCreate(Sender: TObject);
begin
    Edit1.text := NWDirQuery1.Fullname;  // set current container
end;

procedure TForm1.Button1Click(Sender: TObject);
VAR       // submit the search to NDS
    QueryResults: NWQueryResults;
begin
    try
        NWDirQuery1.FullName       := Edit1.Text;
        NWDirQuery1.Filter         := 'User(Surname=S*)';
        // set fields to be retrieved
        NWDirQuery1.Fields         := 'CN, Surname, Last Login Time';
        // current container or subtree
        NWDirQuery1.SearchScope    := qrySearchSubtree;
        // sync or async
        NWDirQuery1.SearchMode     := 0;
        NWDirQuery1.MaximumResults := 100;
        NWDirQuery1.SortKeys       := 'Surname';
        QueryResults               := NWDirQuery1.Search;
        NWDirQuery1SearchCompleted(Sender, QueryResults);
        except
            on e: exception do ShowMessage(E.Message);
        end;
end;

procedure TForm1.NWDirQuery1SearchCompleted(Sender: TObject;
    const QueryResults: INWQueryResults);
VAR       // get the results
    Q : NWQueryResult;
    i : Integer;
    V : Variant;

    procedure ShowAttribute(attrName: String);
    Var
        k : Integer;
    begin
        try
            V := Q.FieldValue[attrName];
            for k := 0 to VarArrayHighBound(V, VarArrayDimCount(V)) do
                ListBox1.Items.Add(Format('   %s: %s',
				[attrName, String(V[k])]));
            except
                on E: Exception do ListBox1.Items.Add(E.Message);
            end;
    end;

begin
    ListBox1.Clear;
    ListBox1.Items.Add(NWDirQuery1.Filter);
    for i := 1 to QueryResults.Count do begin
        Q := QueryResults.Item[i-1];         // one result (object)
        ListBox1.Items.Add(Q.FullName);      // show object name
        ShowAttribute('CN');
        ShowAttribute('Surname');
        ShowAttribute('Last Login Time');
        end;
end;

This simple query has a built-in filter searching for users that have surnames starting with the letter "S." These objects will be sorted by their "Surname" attribute, and they will be displayed with three selected attributes.

Task 9: The Browser

Last but not least is an example of how to include a visual tree browser in your application.

Create a new application/form, add an edit box, and then add the Novell Browser control. Your form should look similar to the one shown in Figure 6.

Basic form with an edit box and the Browser control.

Now add a FormCreate event and the NWBrowse1EntryClicked event, and then add these few lines of code:

procedure TForm1.FormCreate(Sender: TObject);
begin
    NWBrowse1.Control  := NWDir1.DefaultDispatch;
    NWBrowse1.Refresh;
end;

procedure TForm1.NWBrowse1EntryClicked(Sender: TObject;
    const Entry: INWNetworkName);
begin
    Edit1.Text := Entry.FullName;
end;

With just a couple of lines, you have given your application a visual eDirectory browser that looks similar to the NWAdmin browser.

Performance and Tuning

There is a simple rule of thumb that may significantly speed-up your ActiveX application: Avoid accessing large collections in loops! The reason behind this simple rule is that the controls try to refresh the contents of the collection whenever they are accessed. Imagine that you wanted to access an Organizational Unit with 1000 leaf objects using a code sequence like this:

for i := 1 to NWDir1.Entries.Count do Listbox1.Items.Add(NWDir1.Entries.Item[i-1].ShortName);

Your application would pass this loop 1000 times. Whenever it passed the first of these lines, it would re-scan the container to read and count all its child objects. This would cause a significant and unnecessary performance hit.

However, if you add a variable that caches the NWEntries collection, you could speed up this process approximately by a factor of 1000:

VAR
    V : NWEntries;
    i : integer;

V := NWDir1.Entries;  // store collection in cache
for i := 1 to V.Count do
    Listbox1.Items.Add(V.Item[i-1].ShortName);

With this small modification, you can significantly reduce network traffic and boost performance.

Deployment Considerations

If you intend to distribute your application in-house or to customers, there are a few things you need to consider.

Registry

Before the application can access an ActiveX control, the control must be registered to Windows and entered into the registry. An easy way to do this is to do the complete OCX installation by running OCX.EXE on the target work- station. In many situations, though, it may not be desirable to install all ActiveX components, including the help files and samples, on a client workstation. In these cases, you could ship the required controls together with your application and call the registration program (REGSRV32.EXE) during the installation process.

NetWare Client

In your installation process, you should also be aware of the prerequisites for each of the controls you use. Most controls (except for the LDAP-based controls) require the Novell Client 32 software to be installed on the workstation. There may be additional dependencies; check the OCX.TXT file that comes with the controls for details.

Legal Constraints

Before shipping the controls or any other Novell software to customers, you should check the disclaimer and copyright information for that software. In the case of Novell's ActiveX controls, the copyright section in OCX.TXT explicitly states: "Novell hereby provides express written consent to redistribute Novell's ActiveX Controls provided with this Novell NDK."

Compatibility of Updates

Since Novell publishes regular updates to the ActiveX controls on the NDK and on the DeveloperNet Web site, customers may check for updates to take advantage of future bug fixes or performance improvements. However, there is no guarantee that future versions of the components will be 100% compatible with the previous version you used to develop your application. Your application might break if the customer downloads an incompatible ActiveX update.

Configure Context at Design-Time or Runtime

You may have noticed in the sample applications above that you did not have to specify an initial context for the NWDir component. At design time, the component identified your default context and saved it with the executable. If you run this application in a different tree (for example, at a customer site), it would try accessing this hard-coded context and throw an exception.

To be on the safe side, you should identify the available/default trees and containers at application start-up. An easy way to do this is to use the Novell Session control, which provides properties like ConnectedTrees and AvailableTrees. If you do so, your application will be independent of the eDirectory environment.

Caveats

Delphi Bugs

Borland has a history of problems with their import of ActiveX components. In general, you should check for the latest product updates on Borland's patches web site at http://www.inprise.com/devsupport. Here are a few to be aware of now:

  • C++Builder. Borland's C++Builder v.4.0 couldn't import the controls until after the service packs were applied.

  • Delphi 2, 3, and 5. These versions of Delphi had no ActiveX import problems.

  • Delphi 4. Delphi 4 had some minor import problems that have never been fixed. They mainly affect Novell's Session control, causing it to fail to import some properties. For example, the property NWSess1.ConnectedServers is not recognized by Delphi 4. In these cases there is typically a workaround: whenever a documented property or method is not recognized by Delphi 4, insert the property "ControlInterface" (for example, use NWSess1.Control- Interface.ConnectedServers instead).

  • Delphi 6. Delphi 6 had severe problems: typical error messages during the ActiveX import are errors about Get_ and Set_ methods, syntax errors about "( [..] )" constructions, or warnings that no return value is specified. These problems seem to be resolved with the first Delphi 6 service pack.

Redeclaration of ActiveX Types

Various Novell controls may use the same name for different objects. For example, the object NWEntry is declared in the Novell Directory control and references an eDirectory object, and the same type NWEntry is used in the Volume Administration control, where it refers to a file system entry.

If you use two controls that redefine the same type in the same application, you should tell Delphi which declaration you want to refer to. For example, instead of using this simple declaration:

Var
    x, y: NWEntry;

replace it with a more specific declaration:

Var
    x: NWDirLib_TLB.NWEntry;
    y: NWVolAdmLib_TLB.NWEntry;

Conclusion

After working through the examples in this AppNote, you should have a good idea of the features and benefits of using ActiveX controls to access eDirectory from Delphi. Using these controls to develop simple and powerful applications can be amazingly fast, and the performance typically is good. Indeed, one of the main advantages of using ActiveX controls is the significantly reduced development time-after all, that's what RAD is all about.

However, you might feel restricted by the predefined objects, properties, and methods that a control provides, or you may need even higher performance. In these cases, you may use low-level APIs that directly call into the Novell Client DLLs and provide unsurpassed performance and flexibility. The next AppNote article in this series looks into this low-level approach and shows you how you can combine this approach with ease of development by using some of the Novell Delphi helper units. So stay tuned!

* 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