Developer Links
SUPPORT FORUMS  
SAMPLE CODE & TIDS  
TEST/DEVELOPMENT KIT INFO
 
DEVELOPER LABS  
NOVELL SUPERLAB  
SUPPORT DOCUMENTS  
STRATEGIC/EXECUTIVE ISSUE TRACKING  
SYSOP PROGRAM  
     
Related Links
 
 
 
 
 
Related Links  
 

 
DEVELOPER SUPPORT HOME  

OCTOBER/NOVEMBER 1993 VOLUME 5 NUMBER 10


INDEX


ARTICLES:

Distributed Applications: NCP Extensions & The NetWare Client SDK

Last year, Novell Professional Developer Bullets presented a series of articles discussing distributed application design. These articles used a sample distributed application built on Novell's IPX interface called DAX (Distributed Application eXample) to illustrate specific design strategies.

The DAX example consists of an NLM providing a calculator service to NetWare clients running on the DOS, NLM and Microsoft Windows platforms. The DOS and Windows portions of DAX were written to the APIs in the NetWare C Interface for DOS and NetWare C Interface for Windows SDKs, respectively. The NLM server and client components were written using the Network C for NLMs SDK.

Since DAX was written, Novell has released new SDKs for both the client and server platforms. The NetWare Client SDK v1.0d is latest version of the SDK that replaces the NetWare C Interface for DOS and NetWare C Interface for Windows SDKs. The NetWare Client SDK supports the DOS, Windows and OS/2 platforms.On the server side, the NLM SDK v3.0 has replaced the Network C for NLMs SDK and the NLM SDK for NetWare 4.0. The NLM SDK supports both the NetWare 3.x and NetWare 4.x server platforms.

One of the new API subsets in the NetWare Client SDK and the NLM SDK exposes a powerful and flexible client/server communication mechanism known as NetWare Core Protocol Extensions (NCPE). The NCPE API allows you to "piggy-back" custom requests on an existing NCP session to a service provider NLM that is running on your NetWare server. As you will soon discover by reviewing the example code, developing distributed applications using NCPE drastically reduces development time.

Recapping DAX

Before discussing the new example, a quick recap of the DAX example is in order. DAX is a distributed application using IPX as its transport. This application is divided into four pieces:
  • The server application
  • The client application
  • The distributed application protocol (DAP) layer
  • The communication protocol (CP) layer
The CP layer houses all the logic for exchanging messages between the client and server portions of the application. The DAP layer contains the logic for the protocol used by the client and server components of the application.

The client calls an API in the DAP layer to perform a math operation. The DAP API formats a protocol packet with a request code and the operands for the request, and calls an API in the CP layer to send the request. On the server side, the CP layer receives the request, extracts the DAP data, and calls an API to enqueue the request to the server application. The request processing thread in the DAP layer decodes the request and calls the API to service the request. A reply packet is formatted by the server application and sent back to the client by a dedicated service reply thread in the CP layer.

Back on the client side, the CP layer receives the reply, and passes the reply data back to the DAP layer. The DAP layer then extracts the results, and passes them back to the client making the request.

For more detailed information on the DAX example, refer to the following issues of Bullets: April 1992, May 1992 and August/September 1992. These articles are also available on the new Bullets InfoBase on Novell's NOVDEV developer forum on CompuServe. The complete source code for the DAX example is also available on CompuServe in the NOVLIB (Library 7, DAX1.ZIP).

BMV: New Example Application

The example discussed in this article is a Bit Map Viewer (BMV) distributed application, based on the original DAX code. This version of BMV consists of a service provider NLM and a Windows client. The client requests bitmap information for each of the bitmaps that the NLM knows about. A list box is created containing the titles of the bitmaps. When a bitmap is selected from the list box, the client requests the corresponding bitmap data from the NLM. The client then sizes the bitmap to fit inside a predefined window and displays it. The code for sizing the bitmap comes from Charles Petzold's, Programming Windows 3.1 (Microsoft Press, 1992, Third Edition).

There are two implementations of the communication protocol layer of BMV: one is based on the IPX CP layer from the DAX example, and the other is based on Novell's NCPE interface. For the IPX-based CP layer, the only changes made were on the client side, all of which were required to support the new NetWare Client SDK.

For the NCPE implementation, the CP layer was eliminated altogether and the DAP layer was modified to call the NCPE APIs instead of those that existed in the original CP layer. Not only does this result in much less code to maintain, it also provides a substantial performance boost and a number of other features not available in the original CP layer. The section on NCPE later in this article discusses these enhancements in more detail.

Porting to the NetWare Client SDK

Since the DAX example uses only a handful of APIs, porting the existing client code to the NetWare Client SDK required only a few trivial changes. The transport interfaces in the NetWare Client SDK did not change, so the main concern was with the Bindery and Connection services calls. These calls implement the name-to-address resolution portion of the client application. As far as the environment is concerned, the NetWare Client SDK replaced most of the DLLs in the NetWare C Interface for Windows with a single DLL, called NWCALLS.DLL. The IPX/SPX calls remain separate in a DLL called NWIPXSPX.DLL.

The most notable changes in the NetWare Client SDK with respect to porting from the NetWare C Interface products are:

  • The addition of the NW prefix to all API calls
  • A required connection handle on most APIs
  • A set of abstracted data type names for the function parameters.
  • The addition of the API NWCallsInit(), a call that initializes the library for use. This call is not required in the Windows environment. However, in the DOS environment, the libraries will not work if you forget to make this call.
  • Finally, you must define one of three constants, NWDOS, NWWIN or NWOS2, before including the header files for the NetWare Client SDK.
To take advantage of these changes to the API sets, the DAX code was altered in several ways to support the NetWare Client SDK. First, in the header file CPC.H, the NWWIN constant was defined before including any of the NetWare Client SDK headers. Then, necessary include statements were added to obtain prototype information for the APIs used in the CP layer.

Second, the module CPINIT.C (where the name to address resolution takes place) required two alterations:

  • A call to obtain the connection handle for the default server
  • A call to perform an integer swap on our object type before passing it to NWReadPropertyValue()
The connection handle must be obtained, since the APIs in the NetWare Client SDK require this parameter to identify the target server that will receive the request. Remember that the NetWare C Interface products used the concept of a preferred server to identify the target of a given request. NWGetDefaultConnectionID() obtains the connection handle of the default server.

The other change made was to perform an integer swap on the object type passed to NWReadPropertyValue(). This API expects object types to be in "hi-lo" format, rather than the native Intel "lo-hi" format. You would only need to make this change in cases in which Novell-defined manifest constants are not used. In other words, if you pass any of the Novell defined constants, e.g., OT_USER, as the object type parameter in NWReadPropertyValue(), your source code need not be changed, since the NetWare Client SDK defines OT_USER in swapped form.

On the server side, CPIPX.NLM required no changes. While porting BMV to the NetWare Client SDK, there was no need to recompile the NLM. Later, after enhancing the maximum packet size macros in the header files, a recompile was necessary. All in all, the changes in the CP layers for this distributed application were fairly minor; the real work started with the modifications to the DAP layer.

DAP Layer Changes in BMV

Because BMV is an entirely new application, it required a new application-specific protocol for communicating between the client and server portions of the application. BMV needed the following five protocol packets:
  • Login to service provider
  • Logout from service provider
  • Get the number of bitmaps
  • Obtain bitmap information
  • Get the bitmap data
Apart from the standard simple request/ reply protocol requests like login and logout, BMV required two new request types not used in the DAX example.

First, an iterative request type, Get BitMap Info was required, which illustrates how to implement a distributed request requiring context from a previous call. Second, a large request/reply type, i.e., one that does not fit in a single reply packet, was needed. Although these request types seem very similar, their implementation is quite different.

The iterative request type is implemented as a simple request/reply that is called one or more times and requires context information from the previous call. The large request is implemented transparently to the application, and is performed inside the DAP API itself.

So, to quickly review what was changed to support the NetWare Client SDK:

The only module containing references to APIs in the Client SDK is DAP001.C. This module uses NWScanObject() to check for the availability of the service. Like NWReadPropertyValue(), NWScanObject() requires a connection handle as its first parameter, and to obtain this handle, it calls NWGetDefaultConnectionID(). Once again, the object type of our server must be swapped before calling NWScanObject(); this step was not necessary with the old NetWare C Interface libraries. In addition to these source changes, the constant NWWIN was defined, and the appropriate header files for function prototyping were included. These steps completed process of porting to the NetWare Client SDK.

As you may have guessed, the Login and Logout protocol packets from DAX were reused without any changes. The remaining protocol requests from DAX were dropped and three new requests were added:

  • A request to retrieve the total number of bitmaps available from the NLM
  • A request to retrieve bitmap information such as an ID, a title, and the bitmap size
  • A request to retrieve the actual bitmap data

New DAP Requests for BMV

The request to retrieve bitmap information is stored in the source file DAP012.C. This request returns information about a single bitmap, including its ID, size and title. It is an iterative request the client application calls multiple times to get information on all of the bitmaps. The only input parameter for the request is a bitmap ID, used to indicate the last bitmap for which information was returned. BMV passes (-1) for the initial call. Subsequent calls use the ID returned to the caller. The request and reply structures used by this protocol packet are shown in Figure 1.

FIGURE 1: Request/reply structures for ListBitMaps DAP (BMV)

typedef struct{                      // list bitmaps request pkt
        UINT32   bitmapid;           // (-1) to start search
}LBRequest;

typedef struct{                      // list bitmaps reply pkt
        UINT32   bitmapid;           // bitmap id of bitmap
        UINT32   bitmapsz;           // length of bitmap
        UINT8    title[DAPMAXTITLE]; // title string ASCIIZ
}LBReply;

END of FIGURE 1
Another new request was added to read the data for a bitmap. In this request, the client passes the ID of the bitmap whose data is required, a buffer large enough to hold the data, and calls the API DAPGetBitMap(). A major difference between this request and all the others is that the bitmap data will not fit in a single reply packet. Rather than forcing the client application to read the data one chunk at a time, this logic was implemented in the DAP API itself (see Figure 2).

FIGURE 2: Reading the data for a bitmap (BMV)

T_RC    DAPGetBitMap(DAPDATA *DAPid,LONG bitmapid, char *buffer)
{
        T_RC        rc;
        GBRequest   *request = (GBRequest *)DAPid->dapRequest.data;
        GBReply     *reply   = (GBReply *)DAPid->dapReply.data;
        //
        //  fill in the request structure
        //
        request->bitmapid = bitmapid;
        request->offset = 0;            // zero to start this off
        //
        //  Send the request
        //
        while( (rc = DAPSendRequest(DAPid,DAPGETBITMAP)) == 0){
            memcpy(buffer,reply->bitmap,reply->bitmapsz);
            //

            //  Done when number returned less than max size
            //
            if( reply->bitmapsz < DAPMAXBITMAP ) break; // done!
            //
            //  Setup to get next chunk
            //
            buffer += reply->bitmapsz;
            request->offset += reply->bitmapsz;
        }
        return rc;
}

END of FIGURE 2

Using NCP Extensions

The other, very different implementation of the CP layer for the BMV example application is based on Novell's NCP Extensions (NCPE) API. This API is supported by NetWare 3.11 and later versions, and requires the NetWare Client SDK to create DOS, Windows or OS/2 client applications which use it. On the server, CLib v3.11d and later support the NCPE API set.

The are many distinct advantages in using NCPE:

  • They are easy to use.
  • They use an existing connection with a server.
  • They allow the use of arbitrary message sizes.
  • They deliver high-performance, reliable packet transfer.
  • They provide built-in error detection & retries.
  • They allow you to benefit from packet signing and other security-related features of NCP.
  • When Novell adds new features to NCP, these features are automatically available to you. NLMs, Windows applications and OS/2 applications do not even need to be re-linked.

In the case of the BMV example application, taking advantage of NCPE substantially reduces the code base. First, NCPE entirely eliminates the CP layer, since NCPE is a client/server protocol. So, CPIPX.NLM is no longer needed on the server side, and CPAPI.LIB can be eliminated on the client side. As far as the DAP layer is concerned, NCPE dramatically simplifies the DAPSendRequest() API used in the IPX-based implementation. Furthermore, the NCPE implementation offers more functionality than the IPX version as well. Figure 3 shows the new DAPSendRequest() API for the NCPE version of the DAP layer.

FIGURE 3: New DAPSendRequest() API for the NCPE version of the DAP layer (BMV)

T_RC    DAPSendRequest(DAPDATA *DAPid, UINT16 requestCode)
{
        UINT16  replylen = sizeof DAPid->dapReply;
        //
        //  Init the common DAP fields
        //
        DAPid->dapRequest.requestCode = requestCode;
        //
        //  Send the request, wait for a reply
        //
        if( NWNCPExtensionRequest(DAPid->hconn,
                                  DAPid->serviceID,
                                  &DAPid->dapRequest,
                                  sizeof DAPid->dapRequest,
                                  &DAPid->dapReply,
                                  &replylen) ){
            return DAP_SEND_FAILURE;
        }

END of FIGURE 3
The remainder of the protocol request code for the DAP layer remains unchanged on the client side.

On the server side, a few other changes were necessary. First, the DAPSEND.C and DAPRECV.C modules were eliminated since they were no longer needed. In DAX, DAPSEND.C contained the logic to run the service reply thread (the code that processes queued up replies to be sent back to the client). With NCPE, this logic is unnecessary because NetWare sends back the reply for you. DAPRECV.C contained the logic to run the service request thread (the code that processed queued up requests from the clients). With NCPE, this module is also unneeded, since NetWare calls the extensions handler when it detects a new request has been received. Eliminating these two modules subtantially reduces the code for the NLM implementation of the DAP layer.

A new module called DAPNCPE.C contains all of the logic needed to provide an NLM service based on NCPE. This includes the initialization logic, the actual NCPE Handler API (the API called when an NCPE request is received by NetWare), and a connection down handler.

The connection down handler is called when specific events occur on any NetWare connection. This design allows your NLM to clean up client resources, in case a given connection logs out, is restarted, or is cleared by the NetWare connection watchdog.

Finally, NCPE eliminates the need for the application protocol request to obtain the number of bitmaps available from the NLM. Now, this information is stored in a 32-byte field allocated by CLIB.NLM and associated with the NCP Service. The data stored in this field is returned to a client, whenever it calls one of the NCPE service information APIs, namely NWGetNCPExtensionInfo(), NWGetNCPExtensionInfoByName() or NWScanNCPExtensions().

When you compare the two implementations of BMV, you will have a much better understanding of how easy it is to implement distributed applications using NCPE, rather than a lower-level transport protocol such as IPX or SPX.

After you examine the design differences needed to support the NetWare Client SDK and take advantage of its features, you should be able to port other distributed applications with similar designs with a minimum of difficulty. The full source code for BMV is available on Novell's NOVLIB CompuServe forum (Library 7, BMV0.EXE).

The NetWare Client SDK and the NLM SDK are available to members of the Novell Professional Developers' Program. For more information, call Novell at 1-800-NETWARE (1-800-638- 9273) or 1-801-429-5588 if you are in the USA or Canada. Customers in other countries should contact their local Novell office.

Maintaining Server Connections... Automatically!

Some situations require applications to run 24 hours a day, seven days a week. Often these applications must run without supervision. The only need for human interaction may be to enter a keyboard character to answer the "(A)bort, (R)etry" message the NETX Shell returns when it detects a problem with a network connection. Connections can go down if a file server loses power or if someone accidentally turns it off. Ideally, the application should run without user intervention and most importantly, without having to exit the mission critical application.

This article describes an application named RELOG.C that was designed to be added to existing applications. RELOG.C enables users to automatically log back in to NetWare 3.x servers after connections are lost and regained, and to remap the workstation's drives. The addition of this functionality allows applications to run continuously without human intervention.

RELOG.C employs only one of many design variations envisioned when the code was written. Other situations will require you to modify the code to suit particular applications or needs. RELOG.C is only one example program to demonstrate the measures required to accomplish the task.

This article provides an overview of RELOG.C and addresses four basic tasks performed by the application:

  • Collecting workstation information
  • Establishing when a file server has gone down
  • Cleaning the Shell Table and implementing a fail within the INT 24h handler
  • Determining when a file server has returned
  • Reconnecting to a file server

Collecting Workstation Information

Before you can tell that a file server is down, you must collect certain vital information about the workstation. RELOG.C records which connection IDs are in use, all file servers to which the workstation is connected, what drives are in use and to which file servers these drives are mapped. The information kept in the Server Connection Table - the Server Name Table, Drive Flag Table, Drive Connection Table, and Drive Handle Table - are retrieved via the NetWare System Calls (see Figure 4). These calls will return far pointers to the tables. Also, RELOG.C was created using the Large Memory Model due to the ease of working with far pointers to the Shell tables.

FIGURE 4: Getting server information (RELOG.C)

void GetServerInfo ( void )
{
     BYTE compare;
     int entries = 1;
     union REGS inregs, outregs;
     struct SREGS segregs;
     //
     memset ( serverInfo, 0, sizeof ( serverInfo ) );

     // Server Connection Table
     segread(&segregs);

     inregs.h.ah = 0xEF;
     inregs.h.al = 0x03;
     intdosx(&inregs,&outregs,&segregs);
     idNamePtr = MK_FP(segregs.es,outregs.x.si);

     // Server Name Table
     inregs.x.ax = 0xEF04;
     intdosx (&inregs,&outregs,&segregs);
     fsNamePtr = MK_FP(segregs.es,outregs.x.si);

     //Drive Flag Table
     inregs.x.ax = 0xEF01;
     intdosx (&inregs,&outregs,&segregs);
     driveFlagPtr = MK_FP(segregs.es,outregs.x.si);

     //Drive Connection Table
     inregs.x.ax = 0xEF02;
     intdosx (&inregs,&outregs,&segregs);
     driveConnIDPtr = MK_FP(segregs.es,outregs.x.si);

     //Drive Handle Table
     inregs.x.ax = 0xEF00;
     intdosx (&inregs,&outregs,&segregs);
     driveHndlPtr = MK_FP(segregs.es,outregs.x.si);

     /* copy info from Shell into serverInfo[] */

     while ( entries < 9 )
     {
          compare = *idNamePtr;

          /* copy info only if Slot In Use is set in shell's
           * Connection ID Table
           */

          if ( compare == 0xFF )
          {
             memmove ( serverInfo[entries].fileServerName,fsNamePtr,48 );
             memmove ( serverInfo[entries].network,idNamePtr+2,4 );
             memmove ( serverInfo[entries].node,idNamePtr+6, 6);
             memmove ( serverInfo[entries].socket,idNamePtr+12,2 );
             serverInfo[entries].connID = entries;
          }
          entries ++;

          /* advance pointers */

          fsNamePtr = fsNamePtr +48;
          idNamePtr = idNamePtr +32;
     }

     /* reset pointers to the beginning of the tables */

     fsNamePtr = fsNamePtr -384;
     idNamePtr = idNamePtr -256;

}     /* end GetServerInfo () */

END of FIGURE 4
Information from the Server Connection Table and Server Name Table is stored in the serverInfo structure. This structure allows RELOG.C to mimic the Shell information tables by keeping the Server Name, Server Address, the Server Connection ID, and an extra byte for a flag indicating whether or not the Server is down, without having to read it each time a change is made. The code only maintains connections to those servers to which the workstation is connected when the application starts. RELOG.C does not update the serverInfo structure if another server is added after the application has been started.

The application requires this serverInfo structure because, when more than one server goes down at a time, the Shell uses the available space in the Server Connection and Server Name Tables. Thus, the original ordering of file servers within the Shell may be rearranged.

For example, if you originally start with five servers and servers #4 and #2 go down, if server #4 comes back first, the information for server #4 may occupy the slot previously held by server #2. When server #2 comes back, it will occupy the next available slot (the slot previously held by server #4). Therefore, the original ordering has been rearranged within the Server Connection and Server Name Tables.

The returning servers pick those slots because the Slot In Use flag in the Server Connection Table is reset from "0xFF" to "0x00" to let the Shell know that those slots are available. RELOG.C also needs the serverInfo structure to determine which file servers are down and to keep addresses of the server in order to "ping" or identify them later.

The drive mappings also must be saved into a static variable so they can be restored once file servers return. This section of RELOG.C was taken from another application, MAPIT.C, as presented and described in a previous Bullets article entitled, "Saving and Restoring Drive Mappings," (February 1992, Volume 4, Number 2).

As a final step, an INT 24h handler must be installed to intercept the critical error that occurs when a workstation tries to access a drive mapped to a file server to which it no longer has a valid connection.

Establishing When a File Server has Gone Down

In order to detect when a file server has gone down, RELOG.C attempts to access every network drive using the chdir() function. If a file server connection is no longer active, accessing a mapped drive to that file server will trigger an INT 24h.

Cleaning the Shell Table & Implementing a "Fail"

As mentioned above, when RELOG.C fails to access a drive, its own INT 24 handler is invoked (see Figure 5).

FIGURE 5: INT 24h critical error handler (RELOG.C)

int Handler24 ( int errval, int ax, int bp,int si )
{
    BYTE     componentList[54], majorRevisionNumber,
minorRevisionNumber;
    int      drive, entries, fsNameLen, retval, i;
    int      errorno;
    WORD
SPXconnectionID,tempSocket,maxConnections,availableConnections;
    static   char msg[80];
    unsigned di;
    //

    /* error number that is returned from DOS */
    di = _DI;

    if ( ax < 0 )
    {
        ErrorWin ( "Device error" );
        hardretn ( ABORT );
    }

    /* information concerning which drive letter failed will be */
    /* returned in the BH register                              */

    asm {  /* must place bracket here for inline assembly to work */
         xor   bl,bl
         xchg  bh,bl
         mov   drive,bx
    }

    /* check for General Failure and Network drive */
    if ( ( ( di && 0xC ) == 0x01 ) && ( drv > LASTDOSDRIVE ) )
    {
        entries = 1;

        while ( entries < 9 )
        {
            fsNameLen = strlen ( serverInfo[entries].fileServerName );

            /* look for fileserver name that has gone down */

            retval = strncmp ( serverInfo[entries].fileServerName,
                               maps.drive[drive].path,
                               fsNameLen );

            if ( retval == 0 )
                break;      /* found */

            entries++;
        }

        CleanShell ( serverInfo[entries].connID );
        serverInfo[entries].down = 1;   /* set server down flag */
        hardretn(ABORT);
    }

    else
    .
    .
    .
} /* end Handler24 */

END of FIGURE 5
The handler examines the BH register to find which drive number (0=A, 1=B...) has triggered the INT 24h. Then, it checks the DI register to see which error code DOS passes to the handler. With this information, RELOG.C determines if the operation is a "General Failure" on a network drive. It identifies which server is associated with that drive by comparing every server name saved within the serverInfo structure to the path parameter kept within the searchDrives structure. Once it finds a match, it calls CleanShell().

When a Connection ID is in use, the Slot In Use flag within the Server Connection Table is set to "0xFF" to signify this Connection ID is currently allocated. The CleanShell() routine resets the Slot In Use flag in the Server Connection Table to "0x00" to let the Shell know that the file server is no longer on-line. The Connection ID is passed as a parameter into this function to find the offset within the Server Connection Table. The Shell now knows that this slot is empty and available for reuse. It disregards any information currently stored within this slot.

Once this flag is reset, RELOG.C passes the Connection ID to the function, DeleteMapDrives(). This function deletes information concerning the mapped drives from the Drive Handle Table, the Drive Flag Table, and the Drive Connection ID Table. Since multiple drives may be mapped to a file server, you should probably remove all the drive references associated with that file server. These tables must be cleared, since the NetWare Shell will not clear them in this type of event. As far as the workstation is concerned, it still has a valid connection to the file server, so RELOG.C must implement the mechanism to clean the appropriate tables within the Shell when necessary. This allows the Shell to reuse these slots (see Figure 6).

FIGURE 6: Cleaning the shell (RELOG.C)

/***********************************************************************
** Reset the Slot In Use flag in the Connection ID Table
***********************************************************************/
void CleanShell ( int connID )
{
    char far *tempPtr;
    int i, chkStr;
    //
    tempPtr = idNamePtr;

    /* increment pointer size of table entry */
    idNamePtr = idNamePtr + ( ( connID - 1 ) * 32 );

    /* put 0 in the Slot In Use flag for that entry */
    *idNamePtr = 0;

    idNamePtr = tempPtr;

    DeleteMapDrives ( connID );

}   /* end CleanShell () */

/***********************************************************************
** Delete entries from the Drive Handle Table, the Drive Flag Table,
** and the Drive Connection ID Table
***********************************************************************/
void DeleteMapDrives ( int connID )
{
    int drv, sdrv, dummy;
    char drvl, *pntr, chkStr;
    BYTE temp;
    //
    /* delete all drive references to the downed fileserver */

    for ( drv = LASTDOSDRIVE; drv < MAXMAPPINGS; drv++ )
    {
        temp = *driveFlagPtr;        /* Clear Drive Flag table */
        if ( ( temp != 0x00) && ( maps.drive[drv].connID == connID ) )
          *driveFlagPtr = 0x00;
        driveFlagPtr = driveFlagPtr + 1;

        temp = *driveConnIDPtr;      /* Clear Connection ID table */
        if ( ( temp != 0x00) && ( maps.drive[drv].connID == connID ) )
          *driveConnIDPtr = 0x00;
        driveConnIDPtr = driveConnIDPtr + 1;

        temp = *driveHndlPtr;        /* Clear Connection Handle table */
        if ( ( temp != 0x00) && ( maps.drive[drv].connID == connID ) )
          *driveHndlPtr = 0x00;
        driveHndlPtr = driveHndlPtr + 1;

    }
    /* reset pointers */

    driveFlagPtr = driveFlagPtr - ( MAXMAPPINGS );
    driveConnIDPtr = driveConnIDPtr - ( MAXMAPPINGS );
    driveHndlPtr = driveHndlPtr - ( MAXMAPPINGS );

}    /* end DeleteMapDrives () */

END of FIGURE 6
Once the Shell is cleaned, RELOG.C must implement a fail and return from the INT 24h Handler back to the application. This procedure is accomplished via the hardretn() function. This function restores the BP and SP registers to the values they contained before the error handler was invoked.

Determining When a File Server Has Returned

RELOG.C determines when a file server has returned within the PingServers() routine. This routine is called after RELOG.C tests every drive to determine if a file server is down.

The actual "pinging" of the server is accomplished by calling PingNode(). PingNode() sends a packet on socket "0x0456" to the address taken from the Server Information Table. (The complete source code for PingNode() is included in RELOG1.EXE.) This packet asks the node at that address to return a packet to the function with its SPX diagnostic socket.

If the requested node does not respond within four seconds, the function assumes that the node is unable to respond and returns a status 252. (PingNode() only works if diagnostics is supported for that node, and all NetWare 3.x file servers support diagnostics.) In the example program, the serverInfo structure keeps track of what servers are down and only those servers are "pinged" (see Figure 7).

FIGURE 7: "Pinging" servers with PingNode() (RELOG.C)

void PingServers ( void )
{
    int   entries;
    BYTE  majorRevisionNumber, minorRevisionNumber,
componentList[54];
    WORD  maxConnections,availableConnections,
SPXconnectionID;
    WORD  tempSocket;

    cCode = SPXInitialize( &majorRevisionNumber,
                           &minorRevisionNumber,
                           &maxConnections,
                           &availableConnections);
    tempSocket = 0x00;

    cCode = IPXOpenSocket ( (BYTE *)&tempSocket, 0 );

    *(WORD *)destination.socket = (WORD)IntSwap ( 0x0456 );

    for ( entries = 1; entries < 9; entries++ )
    {

        /* search only for those servers that are down */

        if ( serverInfo[entries].down == 1 )
        {
            /* get the server address */

            memmove (destination.network,
                     serverInfo[entries].network,4);
            memmove (destination.node,
                     serverInfo[entries].node,6);

            /* call to ping a particular server */

            cCode = PingNode ( &destination );

            if ( cCode == 0 )
            {
                printf ( "Server %s is up\n",
                        serverInfo[entries].fileServerName);
                ReConnectServer ( entries );
            }
        }
    }
    IPXCloseSocket ( tempSocket );

} /* end PingServers () */

END of FIGURE 7

Reconnecting to a File Server

Once RELOG.C determines that a downed server has returned, it reads the server information in the serverInfo structure and attaches the workstation to that file server with the AttachToFileServerWithAddress() function. If successful, it calls the LoginToFileServer() function and updates the serverInfo structure. Note: RELOG.C allows you to enter the user name and the password via the keyboard. However, different applications require different solutions for this situations. It is up to you to implement an efficient method of accessing user names and passwords for logging into the server.

As mentioned in the beginning of the article, a file server's information may not reoccupy the same slot in the Shell table, particularly if more than one file server goes down at a time. Thus, the connID field in the serverInfo structure is updated with the Connection ID returned earlier from the AttachToFileServerWithAddress() function call. The down field is also reset to zero and it is safe to remap the drives back to the file server once again.

Although ResetDriveMappings() is fully explained in the February 1992 issue of Bullets, some additional explanation is in order since the original code was modified for RELOG.C. First, only those mapped drives associated with the particular file server are remapped. RELOG.C does not remap every drive again. Second, the searchDrives structure was updated as follows:

map.drive[drv].connID = newConnID;.

This modification associates the drive with its new Connection ID returned from AttachToFileServerWithAddress(). Otherwise, the code for ResetDriveMappings() operates essentially as it did when it was originally presented in previous issues of Bullets.

Although this article explains the basic operation of RELOG.C, the code can be tailored to meet the demands of existing applications. RELOG.C is designed to run with NETX.EXE. It will be tested with VLM.EXE in the future, and the results will be included in the README file attached to RELOG1.EXE. The code was tested with multiple servers, but it does not take into account all possible events that may occur. Therefore, it is up to you to initiate appropriate error routines where necessary.

The complete source for RELOG.C is available on CompuServe on Novell's NOVLIB forum. The self-extracting file named RELOG1.EXE will be posted to Library 1, and moved to library 7 after 30 days. If you have additional questions about any of the issues raised in this article, call 1-800-NETWARE (1-800-638-9273) or 1-801-429-5588 if you are in the USA or Canada. In other countries, contact your local Novell office.


DEVELOPER NEWS:

Creating ALMs

Novell's Visual AppBuilder is a visually-oriented 5GL development environment in which users connect objects and functions together to create custom applications. The objects and their associated functions are known as AppWare Loadable Modules (ALMs).

The ALM SDK allows you to create new ALMs. Through the creation of ALMs, developers can add new objects and functions to the development environment. These additions to the object library appear as if they were original components. In fact, architecturally speaking, they are just the same as the original components.

But Novell does not stop there because Novell is committed to making the AppWare development environment an open development architecture. In support of its commitment, Novell also provides an open interface to AppWare that allows third-party developers to create their own 5GL development tools for working with ALMs.

The Novell Toolmaker SDK provides the interface for development of 5GL tools for the AppWare environment. This SDK will be available in the first quarter of 1994.

AppWare's open development environment distinguishes it from virtually all other development systems, and makes it the most expandable application builder today. Novell believes that providing a wide range of ALMs, as well as a wide range of 5GL tools for the AppWare environment will ensure the success of AppWare as well as its customers.

Introducing the AppWare Bus

The AppWare Bus allows you to create new ALMs that can be added to the Visual AppBuilder Object & Function Palette (or any 5GL tool created with the Novell ToolMaker SDK). You are given access to the AppWare Bus at a number of very strategic points, so that virtually any imaginable functionality is within your grasp.

The architecture of the AppWare Bus makes it possible for the AppWare Bus components to communicate with each other. The architecture also defines where and how you may alter components or introduce new ones.

ALMs consist of the following components. (Notice that some components are provided by Novell, others are created by the developer as components of the ALM, and still others are provided by the user via Visual AppBuilder.)

  • Application Shell - provided by Novell
  • Methods (event handlers) - created by the developer
  • Events - created by the developer
  • Objects - used in Visual AppBuilder by the user
  • Function Chains - used in Visual AppBuilder by the user
The following discussion of the application shell, methods and events, objects, and function chains is specific to creating ALMs for use in Visual AppBuilder.

The Application Shell

At the core of any ALM is a runtime engine called the shell. The shell is responsible for:
  • Launching the application
  • Loading and unloading code routines
  • Storing and loading objects
  • Managing event transmissions between objects.
When you call the AppWare Bus utility routine, you are instructing the shell to perform one of these actions.

The shell performs these actions transparently; you don't need to know anything more about it than to assume it performs these basic tasks. When an application is compiled in Visual AppBuilder, the shell is built into the application automatically.

Events & Methods (Event Handlers)

The event is the runtime communication vehicle for the AppWare Bus. Events act as messengers for instructions and information. Object types "listen" for specific events, and respond in appropriate ways. With few exceptions, the event mechanism operates at runtime only. Events are not commonly used at design time.

Methods (a.k.a. event handlers, in Windows) are responsible for actual code implementation of application functionality. Methods respond to events from the shell, which receives events from the OS and other methods. In the Macintosh environment, methods are known as just that, methods. In the Windows environment, methods are known as event handlers. In either environment, their basic function is the same: to excecute some low-level routine in response to an event from the shell.

For example, the shell receives operating system events, such as "mouse down" and "update" events, queues them, and later sends them to Window methods (event handlers), among others.

You attach methods (or event handlers) to objects via their object type. Each object type - Window, Menu, Database - has a set of method-to-event (event handler) connections.

Objects

Objects are manipulated at runtime by methods (event handlers) that are invoked from AppWare Bus events, developer-defined events, or under user control by function events. Object data is set up at design time in Visual AppBuilder. Internally, objects contain one ore more developer-defined object data blocks to contain such user- selected information.

Function Chains

Visual AppBuilder users connect functions in a sequence to form function chains. A function chain tells the shell the order in which to invoke functions' methods (event handlers) at runtime, in response to a particular signal event. Signal events come only from objects.

ALMs & the AppWare Bus

The AppWare Bus is one of the first commercially available software engines for managing intelligent software components. The AppWare Bus does for applications what the hardware bus did for personal computers -it manages and coordinates the interaction of ALMs.

The Appware Bus provides two main functions: 1) it enables developers to create ALMs, and 2) it provides third-party developers with open access to the AppWare Bus API. Third party developers can not only create ALMs, they can also create their own 5GL development tools, similar in function to Novell's Visual AppBuilder.

The AppWare Bus provides an interface for each of these functions:

  • AppWare Bus Module Interface (ABMI) - used by developers to create ALMs. Provided in the ALM SDK, ABMI consists of the ALM Management Routines.
  • AppWare Bus Tool Interface (ABTI) - used by developers to create 5GL development tools for the AppWare environment. Provided in the Toolmaker SDK, ABTI consists of the Project Management and Compiler Routines.
The ALM Management, Project Management, and Compiler Routines are the routines that make up the AppWare Bus API.

ALM Management Routines

ALM Management Routines allow you to query the bus to determine the presence of objects and functions, to instantiate objects (create instances of objects), to invoke attribute editors of objects, and to manage and coordinate runtime interaction among ALMs.

Querying routines can be used to describe the number and types of objects and functions that have been instantiated. Instantiation routines support the capability to "drag and drop" new objects directly into the layout. These object instantiation routines operate at design time and at runtime.

Routines are also provided to invoke the attribute editor for another object. These routines allow you to invoke another editor from within the original editor. An object only needs to use these routines if it references other objects. These routines operate at design time.

Routines that control interactions among ALMs operate at runtime only and determine how objects respond to events. For example, the AppWare Bus includes a start-up event. When this event is sent to an object, the object responds by invoking its methods. For example, when the Window object receives the startup event, it responds by opening any window instances that the developer designated to be opened at startup.

Project Management Routines

Project Management Routines allow the developer to determine the format of AppWare projects and be able to read and write AppWare project files. These routines allow third-party developers to create their own AppWare application projects that work side-by-side with other application projects created with Visual AppBuilder and other third-party development tools.

Compiler Routines

The compiler routines are provided to allow you to compile objects and functions you have created into applications. These routines provide third-party developers with open access to the AppWare compiler routines.

Constructing an ALM

To create ALMs using the ALM SDK, you must have a thorough understanding of the operating environment for which you are building the ALM. The following are the requirements for developers building ALMs for the Macintosh and Windows environments:
  • For Windows, you must be familiar with Windows from an end user's perspective; have a good understanding of the Windows SDK; have a working understanding of Pascal, C, C++, or Assembly; and be familiar with the process of writing and compiling a DLL.
  • For the Macintosh environment, you must be thoroughly familiar with the Macintosh from an end user's perspective; have a good understanding of the Macintosh Toolbox and operating system (as described in Apple Computer's Inside Macintosh); have a working understanding of Pascal, C, or Assembly; and be familiar with the process of writing and compiling a code resource, such as CDEF, WDEF, or MDEF.
In the AppWare Bus, you create objects, functions, and methods (event handlers) by writing and compiling code routines, then adding them to the palette of components presented to the user in the Visual AppBuilder Object & Function Palette. One unique feature of the AppWare Bus is that you may write your additions in whichever language you prefer as long as your code meets the requirements for your operating environment as follows:
  • In Windows, the language must be suitable to create a DLL.
  • In the Macintosh environment, your development system must accept an MPS .o library file. (This file contains about three dozen utility routines required to access the AppWare Bus.) Your system must have the ability to compile and save a code resource.
In general, the steps required to create ALMs are as follows:
  1. Obtain a Component ID.

    Every component must have a unique four-character ID. Contact Novell Developer Support to obtain an ID for the component you are creating.

  2. Write and compile the code.

    In the Macintosh environment, you must collect the individual resources that build the component, and add them to the resource fork of a file. In Microsoft Windows, all of the code and resources for all of the functions and events associated with your object type can be placed in one DLL, simplifying the organization of the DLLs required to run a compiled application.

  3. Load and configure the component.

    You load and configure components through the Configuration Editor.

Distributing Your ALM

You do not need to understand conventional object oriented programming to build the AppWare Bus objects and functions. To develop objects and functions in the Macintosh environment, you must be reasonably proficient in using any typical Macintosh development system, like THINK Pascal or C, or MPW Pascal, C or Assembly. For Windows, you must be proficient in Windows development.

The ALM SDK and the AppWare Bus handle most object orientation automatically, leaving you free to develop objects and functions with more functionality and in a fraction of the time.

Ordering the ALM SDK

To order the ALM SDK, call 1-800-NETWARE (1-800-638-9273) or 1-801-429-5588 in the USA and Canada. In all other countries, please contact your local Novell office.

Companies Make Plans for AppWare

As a developer, you know it is important to determine the direction that the industry is heading early so that you can make your own corresponding changes in product direction and focus. Bullets would like to provide you with a brief glimpse into how some industry leaders are making plans to use AppWare and AppWare Loadable Modules (ALMs) to make their network application development faster and more efficient, while enabling it to run on more platforms transparently.

Gupta to Design Database ALMs

At BrainShare Dallas '93, Gupta announced it will build an ALM version of its best selling SQLBase database engine and a Quest ALM for use in Visual AppBuilder, as well as deliever an AppWare version of its SQLWindows development tool. Gupta announced it will also be incorporating the AppWare Foundation in the next generation of all of its SQLWindows and Quest products. The announcement means that in the near future developers will be provided with tools that will make them more productive and effective in producing network applications.

The Gupta ALMs will provide for development of full-featured multi-database applications. Gupta's standalone SQLBase ALM will be a bundled component of every Visual AppBuilder. The embedded SQL engine has a five megabyte capacity. The bundling agreement gives Visual AppBuilder developers access to over 10,000 corporate developers. The SQLBase ALM is scheduled for completion in January of 1994.

Gupta also announced plans to release a Quest ALM. Quest is a graphics data access and reporting tool that provides corporate and in-house developers with query, form, report, and catalog functions. Gupta plans to convert these functions into ALM components. Using Visual AppBuilder, developers will have access to a full-featured database application by quickly implementing one or more of the Quest components in their own applications.

And for developers, the news gets even better because Gupta will be implementing the core technologies of AppWare (the AppWare Bus and ALMs) in its SQLWindows development environment. Specifically, SQLWindows will be able to take advantage of the ALMs from Novell and third-party developers of ALMs. Already designed for ease in programming (by circumventing C and the Microsoft Windows Tookit), SQLWindows immediately becomes an even more desirable development environment because developers are able to construct complete applications by linking ALMs.

Gupta plans delivery of the SQLBase ALM by June 1994 and the Quest ALM during the third quarter.

By developing all of its next generation development tools to the AppWare Foundation, Gupta SQLWindows will be be available on both Macintosh and UNIX, in addition to the Microsoft Windows platform for which it was originally written and it will gain additional portability as new platforms are added to the AppWare Foundation.

Borland, WordPerfect Announce Development Plans for AppWare

This month, Borland International, Inc. announced that it will incorporate Novell's AppWare Foundation into its ObjectWindows Library (OWL) technology to provide developers with comprehensive C++ based cross-platform development library. ObjectWindows is an application framework that provides a robust set of pre-fabricated, reusable classes for developing object-oriented Microsoft Windows applications.

With over 300,000 users, it is the most popular C++ framework for developing applications for Microsoft Windows in C++. The AppWare Foundation will provide a set of C++ libraries that give developers cross-platform functionality, regardless of operating system, graphical user interface, or network service. Developers write to a single API and then simply recompile for different operating environments.

As a leading cross-platform developer, WordPerfect followed with the announcement that it will be using Novell's AppWare Foundation as a standard development platform in WordPerfect's Core Technologies department. This department is responsible for developing core code and functionality that is product- and platform-independent. The core code is then incorporated into all of WordPerfect's products.

Because it is so time-consuming and resource-intensive to produce cross-platform code, WordPerfect was looking for a way to shorten the time it takes to bring its products to market across multiple platforms. The AppWare Foundation will be the key element in allowing the WordPerfect developers to bring their products (running on multiple platforms) to market in a fraction of the time required before.


TECHNICAL INSIGHTS:

NetWare API Call Fails with 0x8801 (NetWare Client SDK v1.00d)

The programming example on pp 256-257 of the Netware Programmer's Guide for C (Version 1.0, June 1993 ed.) fails on NWGetNextConnectionID() with a return code of "0x8801." Several valid values were tried as the parameter to this function call, but all caused the function to fail with 0x8801.

To make the example run successfully, make the changes outlined in Figure 8.

FIGURE 8: Necessary Changes to example code with NWGetNextConnectionID()

memset (connListBuffer, 0, 8);

cCode = NWGetConnectionList (0, connListBuffer, 8, &numberOfConnections);

connHandle = connNum= 0;

do {
    if (cCode = NWDSGetConnectionInfo (connListBuffer[connNum],
                        NULL, &connType, NULL, serverName,
                        NULL, NULL, NULL, NULL, NULL))
        printf ("\nNWNDSGetConnectionInfo failed with %04X.", cCode);
    else
    {
      printf ("\n\n%d\tID = %04X", ++connNum, connListBuffer[connNum]);
      printf ("\nServer Name = %s", serverName);
      printf ("\nConnection Type = ");
      if (connType & NWNDS_CONNECTION)
         {
         printf ("NDS");
         if (connType & NWNDS_AUTHENTICATED)
            printf (" (Auth)");
         else
            printf (" (Not Auth)");
         }
      else
      {
        printf ("Bindery");
        if (connType & NWNDS_AUTHENTICATED)
           printf (" (Logged In)");
      }
    }
} while (connNum

CreateScreen() And NetWare 4.01 (NLM SDK v3.00)

Some NLMs developed under NetWare 3.1x that access the System Console screen receive messages from the NetWare 4.01 Operating System regarding unreleased memory resources when the NLM terminates.

This situation is apparent in NetWare 4.01 because CreateScreen() now returns a screen handle to each screen for each NLM calling CreateScreen(). To remove the unreleased memory resources warning message, call DestroyScreen() before terminating the NLM.

Btrieve NLM Allows Duplicate Records (NetWare Btrieve (NLM) v6.10b)

Btrieve NLM v6.10b will insert duplicate records into a Btrieve v5.x format file under the following circumstances:
  • If each page holds only one record
  • If the Btrieve file is in v5.x format
  • If an Insert operation returns a status 5 (Duplicate Key Value), and then a subsequent Insert is successful. In this case, the successful record as well as a data page with the record that failed is written to the file.
If a key path is always used to retrieve data, then the "bad record" (with the duplicate value) will never be seen. However, if STEP operations are used, Btrieve will return the duplicate records that were written to the data pages. Thus, a BUTIL -RECOVER followed by a BUTIL -LOAD will produce duplicate errors.

To avoid this situation, rebuild files in Btrieve v6.x format or use a page size that can hold more than one record.

Documentation For xConvert and XQLConvert (NetWare SQL v3.0)

The documentation for the NetWare SQL functions xConvert and XQLConvert describes the sRetVal parameter for Option 0 as follows: You must initialize sRetVal so that it is at least as large as iDSize. If iDSize is smaller than the default mask or the mask you specify, NetWare SQL returns the number of bytes specified in iDSize and a non-zero status.

In past versions of NetWare SQL, if iDSize was smaller than the mask, but the data would still fit into that size of a buffer, the data was returned. In NetWare SQL v3.0, the size checking is done first, and if the iDSize parameter is not large enough for the mask, the buffer is filled with the "*" character.

For xConvert and XQLConvert to properly convert NetWare SQL data, the iDSize parameter should be set to a value at least as large as the size of the mask, and the sRetVal buffer should be allocated to at least that size.

Alternatively, the application can pass in a shorter mask.

NetWare Btrieve in Primary I/O Engine (NetWare Btrieve (NLM) v6.10)

With Btrieve v6.10b, BROUTER and BTRVSTUB.NLM are provided to allow NetWare SFTIII users to run Btrieve from the I/O engine. These components do not work if run from the Primary I/O engine. However, they do work if they are run in the Secondary I/O engine. Only run Brouter/Btrvstub in the Secondary I/O engine.

AIOCOMX.NLM & NLM SDK v3.0 (NLM SDK v3.0)

The AIOCOMX.NLM dated June 3, 1993, that comes with the NLM SDK v3.0, will not properly load. It returns with the error message:

Loader cannot find public symbol: AIORegisterDriverA

An incorrect version of AIOCOMX.NLM shipped with the NLM SDK v3.0. Continue using the older version of AIOCOMX.NLM.

New Default NDBTIMEOUT (NetWare SQL v3.0)

After applying the v3.00d patches to NetWare SQL, the OS/2 default NDBTIMEOUT value is (-1). This timeout value tells NetWare SQL to wait indefinitely for a response from the file server. When run with the NetWare OS/2 Requester v2.01, NetWare SQL processes can wait for an indefinite period.

The timeout value should not be set to wait indefinitely. When a server has gone down the NetWare SQL OS/2 Requester should be able to time out. Always set the NDBTIMEOUT to a specific value using the following syntax:

SET NDBTIMEOUT=

This statement can be executed from the workstation's CONFIG.SYS file, or from the command line before the first NetWare SQL application is started (i.e., before NDBCOMM.DLL loads). The timeout value is measured in milliseconds, so an example setting is:

SET NDBTIMEOUT=900000 (NDBCOMM will wait 15 minutes for a reply)

A number of NetWare SQL queries may cause NetWare SQL to build one or more temporary sort files. When tables with many records are involved, this process may take some time, in which case you may need to set this parameter to a high value.

Reentrant NLMs & _Prelude (NLM SDK v3.00)

When writing reentrant NLMs, be sure to call _Prelude from within the NLM that is being loaded. This method is the standard way of writing reentrant NLMs. However, some developers have attempted to write library NLMs which store common functions for other NLMs and are also defined as reentrant. These library NLMs then call _Prelude for all of their other reentrant NLMs. This method tricks CLIB into thinking the main function is being executed for the library NLM, not the NLM that was actually being loaded.

To avoid problems, always call _Prelude from the NLM that you are loading.

Fields Required To Submit Queue Job (NetWare Client SDK v1.0d)

The documentation for the NetWare API NWCreateQueueFile2() in the NetWare Client SDK implies that the function will fill out the job structure. It does not mention that several fields in the job structure must be filled out before it is called.

The targetServerID, jobControlFlags and targetExecutionTime fields of the job structure must be filled in before the function will work. The targetServerID field is usually set to "0xFFFFFFFF" so that any server can service the job. The targetExecutionTime field will normally be filled with six bytes of "0xFFs" for immediate execution. Finally, jobControlFlags is usually set to "QF_AUTO_START."

Inconsistent SPX Header Fields (NLM SDK v3.0)

The documentation for the NLM SDK v3.0 incorrectly states that, in the SPX header, "The allocNumber minus the ackNumber equals the number of posted listens outstanding on the connection socket." Actually, the allocNumber and ackNumber are always the same regardless of how many outstanding listens are posted on the connection socket.

The documentation will be corrected in the next release of the SDK.

NetWare 4.0x & "???SERV" NLMs (NLM SDK v3.0)

The NOVSERV, PARSERV, and SERSERV NLMs from the WATCOM C Compiler v9.01 will abend NetWare 4.0x servers at load time.

To prevent this abend, contact the WATCOM BBS at 1-519-884- 2103 and download NLMDEBUG.ZIP from library 17. This file contains ???SERV4 NLMs. However, since WATCOM no longer supports v9.01, you may wish to upgrade to v9.5 instead.

When Does Btrieve Delete Delta Files? (NetWare Btrieve (NLM) v6.10b)

With Btrieve NLM 6.10b, Delta files are deleted as soon as the End Continuous Operations command is executed. Upon executing this operation, Btrieve writes the updates from the Delta files to the corresponding Btrieve files and deletes the Delta files.

Versions of Btrieve before v6.10b delete Delta files created during continuous operations after the last user closes the corresponding Btrieve files. When End Continuous Operations is executed, updates are made to the Btrieve files and the size of the Delta files is changed to zero. However, the Delta files are not deleted until the last user closes the actual Btrieve files.

Mismatched Quotes Generates Status 298 (NetWare SQL v3.00d)

With NetWare SQL v3.00d, using the EXISTS statement with a SELECT statement generates a status 298 (Mismatched Quotes). Figure 9 contains an example illustrating this sequence of calls. Adding the extra parentheses around the first EXISTS statement resolves this status code.

FIGURE 9: EXISTS statement requiring extra parentheses

SELECT RegionCd, CanTrack, 'DAL', '1993', '005000', 'CDITTM01',
        '', '', '', 'N', 'N', 'E', '', CanRecvd
FROM    CanSkill A
WHERE   SkillCd = '     858' AND
        SkillYrs >=  5 AND
        CanRecvd >=  '05/17/93' AND
        CanRecvd <=  '06/15/93'
AND
      (EXISTS (SELECT SkillCd from CanSkill B
               WHERE (B.CanTrack = a.Cantrack AND
               B.RegionCd = A.RegionCd) AND
               SkillCd = '     397' AND SkillYrs >= 3))
AND
       EXISTS (SELECT CanTrack FROM  Candidat C
               WHERE A.CanTrack = c.CanTrack AND
               A.RegionCd = C.RegionCd AND CanStatus = 'Y' )

END of FIGURE 9

Dictionary Files for NetWare SQL (NetWare SQL v3.00)

When creating a new dictionary with NetWare SQL (or XQL) v2.x, FIELD.DDF is created with four indexes and INDEX.DDF is created with two indexes. With NetWare SQL v3.0, each of these files is created with an additional index. When NetWare SQL attempts to access a set of dictionaries created with a prior version, it automatically adds the extra index to both files when it opens the dictionary. If these indexes are added incorrectly, a status 6 (Invalid Key Number) may occur during later processing.

For example, a customer had a set of dictionaries that had become corrupt, and the attempt to add the new index to INDEX.DDF produced a status 5 (Duplicate Key Value). When attempting to access these dictionary files with Xtrieve PLUS, the failed attempt to create the index was not reported to the user, but a later attempt to bring up a file in a view produced the status 6 as a result of the missing index.

The process of adding the new indexes should be transparent to the user, and will be performed the first time the dictionary is used with NetWare SQL v3.0. If the dictionary has become corrupt, it will have to be rebuilt before NetWare SQL v3.0 will be able to use it. Refer to the January 1993 issue of Bullets for more information on rebuilding corrupt dictionaries.

NCONTROL Parameter Options (NetWare NCONTROL v2.6)

The documentation for NCONTROL states that, to create error and status log files, you should pass a "/E" and a "/L" parameter. However, these parameters must be issued in lower case, as follows:

MYTEST /e=MYTEST.ERR /l=MYTEST.LOG

If you issue the command with upper case parameters (i.e., /E and /L), the application will execute, but the log files will not be created.

Setting Volume Space Limit (NetWare C Interface for DOS v1.20)

The SYSCON utility allows you to restrict volume disk space for an object by setting the Limit Volume Space flag. According to the NetWare C Interface for DOS manual, you can remove this restriction programatically by setting the restriction parameter to zero in SetVolRestriction(). However, after passing this parameter, NetWare returns the error message, "Insufficient Disk Space," when you try to create a file.

Passing zero in the restriction parameter clears the Volume Space Limit, but it does not reset the Limit Volume Space flag. To clear both, pass the value "0x7FFFFFFF" in the parameter.

GetSpecificCapture-Flags() & VLM.EXE v1.03 (NetWare C Interface for DOS v1.2)

After a EndSpecificLPTCapture() call is performed on a specific LPT, GetSpecificCaptureFlags() does not return the capture flags that were previously set for that LPT under VLM.EXE v1.03. This call works under NETX v3.32 and earlier.

This call is not supported under VLM.EXE v1.03. After EndSpecificLPTCapture() is called, the status of the capture flags for that LPT is undetermined. For the application to recapture the LPT with the same flags, the application must perform a GetSpecificCaptureFlags() and save the flags to a static variable or a file, and then retrieve that information before the next StartSpecificLPTCapture().


NIBBLES AND BITS:

Fast Answers to Common Questions

NetWare

Q - Is there any way to specify the "hold" option at capture time?

A - You can't actually specify the "hold" option at capture time. There is no flag in the CAPTURE command allowing you to hold a job. Technically, it is not possible with the current implementation of the shell, since the HOLD flag is an attribute of the job in a print queue. At capture time, the job does not yet exist; it is created with the first byte that you send to the captured port.

There are, however, two ways to put a "hold" on the print job:

  1. Specify a non-existing form number. Doing so will leave the job untouched until you change its form number (e.g. in PCONSOLE).
  2. Use the CAPTURE TI=0 NoAuto command to leave the job in the queue until you explicitly run another CAPTURE or ENDCAP. This solution has the disadvantage that some NetWare-aware applications internally send their own ENDCAP signal to the server, which causes the job to be printed.

NLM SDK v3.0

Q - Can I obtain the NetWare serial number without being logged in as Supervisor equivalence?

A - If you are logged in you can use "SYSCON/File Server Information" to retrieve the serial numbers of any server that you are attached to as Supervisor or "SV" equivalence.

If you do not have "SV" equivalence, SYSCON will not display the serial numbers.

The NetWare APIs do not have this restriction. Use SERIAL.ZIP on CompuServe/NovUser containing two tools that allow you to retrieve the serial numbers of servers from an NLM or from a DOS client without even being logged in.

Q - The documentation for NWGetBroadcastMessage() and NWSendBroadcastMessage() and the hints in the Netware Programmer's Guide for C mention the option of exchanging messages of more that 58 bytes. It states, "For NetWare v3.11c and above, the message can be from 1 to 254 bytes including a null terminator".

I have the most recent version of NetWare 3.x (v3.11). but can only exchange a maximum of 58 bytes. Why?

A - You can send messages with more than 58 bytes, but the NCP call GetBroadcastMessage (Fct E1 01) returns up to 58 bytes This has been tested and verified with VLMs on NetWare v3.11 and CLIB v3.11d.

NetWare 4.0x behaves differently. If you send a broadcast message to a connection on a NetWare 4.0x file server you will be able to retrieve the message in several fragments with a maximum of 55 bytes each. For example, if someone sends a 200- bytes broadcast message to you via a NetWare 4.0x server, you must submit four separate NCPs to retrieve the info in four segments of 55, 55, 55, and 35 bytes.

Q - But, what about buffering in the server? A file server will only store one broadcast message per connection--does this long 200-byte message count as 1 or 4 messages?

A - The 200-byte message counts as one message.

Q - Does ScanTrustees() have any specific return codes, or does it return a specific value in one of its parameters if it cannot find any more trustees? The manual is not clear.

A - This API returns decimal 156 (0x9C ERR_NO_MORE_TRUSTEES) as a return code if it cannot find any trustees for the specified path.

Tip The MSGLIB.EXE which ships with the NLM SDK 3.00 causes a memory exception/machine hang/machine reboot. An updated version of IBM$RUN.OVL is required which allows MSGLIB.EXE to to handle long messages. This file is available on Novell's NDEVREL forum (Library 7, IBMRUN.ZIP).

NetWare Client SDK

Q - What do I need to do to my program to make it compatible with Novell's VERSION.EXE utility? That is, what C code needs to be in the program so that VERSION.EXE will display the version?

A - Include the following global declaration in the source code:

char *VERSION="VeRsIoN=x.x";

where "x.x" is the version number.

NDEBUG.NLM

Tip NDEBUG fails to release some resources (e.g., short term memory allocations) when unloaded. Also, from NDEBUG, if you issue the following command:

#process load xxxx

to load xxxx.NLM to debug, the display box reports an error even though the NLM loads successfully. Currently, there is no workaround for this situation.

NetWare SQL

Tip With NetWare SQL v3.00d, the FETCH call will always return at least one record, with an aggregate value of zero for SUM, AVG, and COUNT; and a null value for MIN and MAX, which will depend on the type and size of the field used in these aggregates. The data returned from a v3.00d aggregate function complies with the ANSI SQL Standard definition.

In versions of NetWare SQL before v3.00d, aggregate functions may or may not have returned a record. For example, the aggregate function, SUM, would not return a record if there were no records to sum according to the restrictions placed on the view. Thus, a FETCH call would yield a record count equal to zero.

Q - When I load NSREQ with "/w" set to two and the "Tasks" variable in NOVDB.INI also set to two, I can only run two Windows NetWare SQL applications. The third application receives a status 2007 (Invalid Parameter) on XQLLogin. Also, if the maximum number of sessions has been reached, this status code may be returned. Why does NetWare SQL return this status code?

A - Usually, if NetWare SQL reaches the maximum number of sessions, it returns a status 266 (Number of Sessions Logged in Exceeds Maximum). However, the application may receive status 2007 instead of 266. For example, if NetWare SQL five-user version is loaded at the server, when the sixth application tries to login to the database, NetWare SQL will return different status codes depending on the values specified for "/w" and "Tasks." In this situation, if "/w" and "Tasks" are set to five, status 2007 is returned; if "/w" and "Tasks" are set to six, status 266 is returned.

Be sure to specify appropriate values for "/w" (on the NSREQ command line) and "Tasks" (in the NOVDB.INI file).

Q - When I use XQLI to execute a CREATE TABLE statement such as:

CREATE TABLE tablename
     USING filename

     (Code CHAR(3), Name CHAR(15))

     WITH INDEX ( Code UNIQUE )
The table has an index, but I can't drop it later via the DROP INDEX statement. How can an index that is not named be dropped from a table in NetWare SQL?

A - To drop a non-named index, it must be dropped at the Btrieve level. That is, the BUTIL -DROP command must be used. Next, use Xtrieve PLUS to change the DDFs. To do this operation, use the DICTIONARY\REORGANIZE options. Select the "Yes" option when the REPLACE window pops up. When you come to the Index section, use the ESCAPE key to bring up the menu and select the REMOVE option. Delete the index definition. Finally, select the FINISH - DON'T CREATE option. Now the definitions within the DDFs match the Btrieve file. To add an index, use the CREATE INDEX statement to add an index with an index name.

NetWare Btrieve

Tip Delaying Service Advertising Protocol (SAP) packets for more than one minute causes all file servers on the other opposite side of a CISCO router from the Btrieve NLM to delete the NLM from the bindery, producing a status 20 or a status 95.

CISCO routers can be configured to delay SAP packets by setting the NOVELL OUT-SAP-DELAY parameter in the port configuration setup. The LENGTH parameter is measured in milliseconds. To prevent the Btrieve NLM from being deleted from the bindery, be sure to set this parameter to a value that will not delay SAP packets too long.

Tip Btrieve 6.x files that have a page size of 512K cannot exceed two gigabytes in file size. This limit results from the way Btrieve 6.x files use PAT pages to access the pages of the Btrieve file and is calculated as follows:

  128 (# of pages that a PAT can reference)
* 32K (# of PAT pages a Btrieve file can contain)
* 512 (the physical size of the Btrieve page)
----------------------------------------------------
     2 Gigabytes total file size
If you need to make a larger file, use a larger page size. Files with any other page size can grow up to four gigabytes in size.

Q - I am using NetWare Btrieve v6.0, but sometimes I need to be able to switch back to a NetWare Btrieve 5.x system. How can I do this?

A - If your files are still in NetWare Btrieve v5.x format, you can load Btrieve v6.x with the -d parameter, forcing new files to be generated in 5.x format. Otherwise, the only way to switch back to Btrieve v5.x is to save the contents of your v6.x format file, then generate a v5.x clone and load the data into the v5.x file.

Q - When running BREQUEST in an OS/2 DOS box, I receive a status 12 when I try to open a file that I know exists. Why can't I open the file?

A - This status code can be returned when running BREQUEST with v2.00 of the NetWare OS/2 Requester. Make sure to update your requester to v2.01.

Tip The following error messages may be displayed when running the SAVE or RECOVER options of BUTIL.NLM v6.10x:

BUTIL-6.10-53: Btrieve error 59 occurred for file or command butiltmp.btr.
BUTIL-6.10-72: BUTIL has recovered 0 records.

BUTIL-6.10-9: The command did not complete due to an unrecoverable error.
These messages result whenever BUTIL tries to create a file called BUTILTMP.BTR in the same directory as the Btrieve file to be saved or recovered, and this file already exists in the directory.

To avoid these messages, delete or rename the BUTILTMP.BTR file before performing the BUTIL -SAVE or BUTIL -RECOVER. BUTILTMP.BTR is a temporary file created by BUTIL so that it can check if the current Btrieve engine running on the server supports "chunk" operations.


DEVELOPER EDUCATION:

Available Novell Developer Education Courses

Novell Developer Education offers several courses for developers who use Novell's development & database tools, including NetWare SQL, Btrieve, Xtrieve PLUS, and the NetWare Client APIs.
904 - Btrieve: An Overview
905 - Programming with Btrieve
907 - Xtrieve PLUS
911 - NetWare Database Administrator
912 - Programming with NetWare SQL
930 - Developing NetWare Loadable Modules (NLMs)
940 - NetWare Programming: Basic Services
945 - NetWare Programming: Protocol Support
To obtain information on pricing, location, scheduling, & course content for classes held in the US, call 1-800-233-3382, 1-801-429-5508 or 1-800-NETWARE. For information on classes outside of the US, contact your local Novell office.

CURRENT PATCHES FOR NOVELL DEVELOPMENT TOOLS

The latest NetWare drivers, example code for NetWare API development tools, OS/2 requester patches, and patches for Novell's database products are available on Novell's NetWire forum on CompuServe. New information is first stored in NOVLIB library 1, and moved to library 7 after a period of 30 days. If you do not have access to CompuServe, request these files from Novell's Developer Support Group at 1-800-NETWARE (1-800-638-9273) or 1-801-429-5588.

When calling, be ready to provide a shipping address, disk preference (5.25", 3.5", HD, or DD) and, if you prefer overnight delivery, your company's Federal Express account number. If you do not provide a Federal Express account number, the patch disk will be sent to you via regular mail.

Novell Professional Developers' Program members can obtain updated NetWare API SDK components from Novell's NOVDEV forum on CompuServe. For more information on Novell developer programs contact Novell at 1-800-NETWARE (1-800-638-9273) or 1-801-429-5588.


CONTACTING NOVELL

Developer Support

Developers in the U.S. and Canada may contact Novell Developer Support via telephone, fax, BBS or electronic mail via CompuServe, MHS or the Internet.

Voice

For both presales and postsales support, call Novell Developer Support between 8:00 a.m. and 5:00 p.m. Mountain Time at 1-801-429-5588. Many calls to Novell Developer Support are passed to a software support engineer immediately. Calls to Novell Developer Support generally will be acknowledged or answered within four hours.

Fax

If you prefer, you may contact Novell Developer Support via fax at 1-801-429-2990. Faxed questions are acknowledged or answered within 24 hours.

Developer BBS

If you do not have access to either CompuServe or the Internet, send test cases to our BBS. Set your communication software to N-8-1 and call 1-801-429-5836.

E-mail: CompuServe

Post your messages in the appropriate section addressed to Novell at 76701,171. These messages will receive a response within 24 hours.

E-mail: MHS

You may direct your questions and comments to Novell Developer Support via Novell's Message Handling Service E-mail facility. Address your messages to devsup@novell. These messages will receive a response within 24 hours.

E-mail: Internet (SMTP)

An alternative method of contacting Novell Developer Support is through the Internet E-mail system. Send your messages to devsup@novell.com. These messages will receive a response within 24 hours.

NetWire on CompuServe

Novell's NetWire forum is open to all registered CompuServe subscribers. Through the NDEVSUP forum, professional developers writing applications with Novell development tools can gain access to information specific to Novell development. Support, patches, periodicals and product information, as well as information on all of the programs and services provided for Novell developers are accessible through this forum. Post your messages in the appropriate section addressed to Novell at 76701,171. Technical questions will be acknowledged or answered by Novell Developer Support within 24 hours.

Novell Products and SDKs

Novell products on the "Currently Shipping Developer Products" list (see page 15 of this issue), including the Red Box Products, are available to Novell Professional Developer's Program members. Call 1-800-RED-WORD (1-800-733-9673) or 1-303-894-4135 to order these products or to receive additional information. To order other Red Box Products not listed, contact your local Novell office or Novell Authorized Reseller.

DeveloperNet Labs

For information on DeveloperNet Labs development tools, education classes and product certification, in the U.S. and Canada call 1-800-453-1267 ext. 5544 or 1-801-429-5544, or call your local Novell office (see back cover of this issue).

Novell Developer Relations

Novell Professional Developers or those wishing to become members may contact Novell Developer Relations via telephone, fax, or electronic mail via Compuserve, MHS or the Internet.

Voice

For general information or questions on Novell Developer Relations programs, in the U.S. or Canada call 1-800-RED-WORD (1-800-733-9673). All others call 1-801-429-5281.

Fax

If you prefer, you may contact Novell Developer Relations via fax at 1-801-429-7207.

NetWire on CompuServe

Novell's NetWire forum is open to all registered Compuserve subscribers. Through the NDEVREL forum, Novell Professional Developer's Program-related issues or general questions may be posted. Through the NDEVINFO forum, customers who do not have products may post pre-sales product information questions on all Novell SDKs.

E-mail: MHS or Internet (SMTP)

You may direct your questions and comments to Novell Developer Relations via Novell's Message Handling Service E-mail facility. Address your messages to devprog@novell.com. Use the same address when going through the Internet E-mail system.

ACKNOWLEDGMENTS

Publisher: Mad Poarch
Editor: Kirk R. Humphries
Design: Creative Services, Provo
Articles: Ken Lowrie, Drue Reeves & Michael Spano
News: Holly Roff

Contributors: Linda Anderson, Neda Eslami, Kumar Gaddam, Laura Heater, Clint McVey, Chris Ojeda, Matt Pinsonneault, Bill Prentice, Bob Quinlan, Drue Reeves, Wolfgang Schreiber, Michael A. Spano & Aslam Tejani

Special thanks to the Developer Support, Development, Developer Relations, Product Marketing, and Marketing Communications staff who contributed time and valuable input.

(c) 1993 Novell, Inc. All rights reserved.
1993 Novell, Inc. All rights reserved. Novell, the N design, NetWare, Btrieve, XQL and LANalyzer are registered trademarks; LAN WorlShop, NetWare SFT III, NetWare Loadable Module, NLM, Global MHS, NetWare MHS, NetWare System Calls for DOS, NetWare SQL, NetWare Btrieve, NetWare C Interface for DOS, Report Executive, NetWare Asynchronous Services Interface (NASI), NetWare Management System, Xtrieve PLUS, DeveloperNet Labs, UnixWare, AppWare, AppWare Foundation, AppWare Loadable Module, ALM, AppWare Bus, Visual AppBuilder, IPX, and MacIPX are trademarks; and NetWire and Professional Developers' Program are service marks of Novell, Inc. IBM and OS/2 are registered trademarks, and NetBIOS and SAA are trademarks of International Business Machines Corporation. Microsoft is a registered trademark and Windows is a trademark of Microsoft Corporation. Apple and Macintosh are registered trademarks of Apple Computer, Inc. CompuServe is a registered trademark of CompuServe Corporation. Sun Microsystems and NFS are registered trademarks of Sun Microsystems, Inc. UNIX is a trademark of UNIX Systems Laboratories, Inc. in the USA and other countries. UNIX Systems Laboratories is a wholly-owned subsidiary of Novell, Inc. WATCOM is a registered trademarks of WATCOM Systems, Inc. WordPerfect is a registered trademark of WordPerfect Corporation. SQLBase and SQLWindows are registered trademarks, and Gupta and Quest are trademarks of Gupta Technologies, Inc.