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  

APRIL 1992 VOLUME 4 NUMBER 4


INDEX


MAD'S COLUMN

Hello and welcome to the April 1992 issue of Bullets!

Last month, I found it interesting that within one week two trade magazines stated that NLM development is difficult. A little later, a Novell VP asked me if writing an NLM was hard. Two questions popped into my head. "How do you define 'hard?'" and "Is this a trick question?" I mean, NetWare is the best platform for running high-performance applications, a good compiler and excellent documentation are available, and Novell offers specialized training for NLM development.

I posed the question to several folks in Developer Support with varying levels of NLM experience. During that same week, our Education Department was presenting the "Developing NLMs" class on site. At the end of the course, I asked the students (still new to NLM development) to comment on the statement:

"Writing an NLM is easy!"

The answers were not as varied as you might think. There were two predominant answers:

  1. Writing a simple NLM is easy.
  2. Writing all multi-threaded applications takes a certain mindset, but it is comparable to writing a Windows or OS/2 multi-threaded program.
And one common concern:
  • Resolve the compatibility issues between NetWare versions and API sets.
The two-part article in this and next month's issues offer our observations and commentary on this subject.

Happy_Programming!

Mad Poarch
Director
Developer Support/Service


ARTICLES:

NLMs: From "Hello World" to World-Class Computing (Part I)

NetWare 3.x is a 32-bit operating system designed with an open architecture that allows third-party developers to add specialized enhancements to the operating system by writing NetWare Loadable Modules (NLMs) and distributed applications. NetWare 3.x was designed with NLM technology in mind, but many developers still do not take full advantage of the architecture, believing that NLMs are difficult to design. This two-part article compares the procedures and strategies for designing DOS applications with those for designing NLMs and discusses multi-threading issues. It also uses a working distributed application to explain special considerations associated with NLM development.

Comparing Environments

Most compilers include the familiar example program, "Hello World." Below is a version of "Hello World" that works with nearly any C language compiler:
  #include <stdio.h>

  main()
  {
    printf("Hello World");
  }

Next is a version of "Hello World" that will run as an NLM.

  #include <stdio.h>

  main()
  {
    printf("Hello World");
  }
If you see a difference between the two examples, it is probably a typo!

Compiling an NLM is very straight-forward; in fact, the process is identical to compiling applications for DOS, OS/2 and Windows. The WATCOM C/386 compile line for the "Hello World" example for NLMs and an explanation of each parameter is displayed below:

   wcc386 /3s /Zp1 /d2 hello.c
/3s Use stack-based parameter passing instead of register-based
/Zp1 Byte-align structures
/d2 Include full debug information

To produce an executable load module, you would issue the command:

   wlink @hello.lnk
HELLO.LNK is the ASCII file that contains the linker definition file. Linker definition files contain directives for the linker and provide everything needed to produce a load module for a particular environment.

Since WATCOM C/386 also supports Extended-DOS and OS/2 v2.0, and both environments use the flat memory model, you could link HELLO.OBJ into an NLM, a protected-mode DOS application, or an OS/2 v2.0 application! The WATCOM Linker, WLINK, is the tool that creates the load module for each environment. For the "Hello World" NLM, you would use the linker definition file displayed in Figure 1. The end result of this process is an NLM that can be loaded and run on the server with the command, "load hello."

FIGURE 1: Linker definition file for "Hello World"

   form    novell nlm 'Hello World'
   name    hello
   option  caseexact, nod, stack=8k, map
   debug all debug novell
   file prelude, test
   module clib
   option screenname 'Hello World Screen'
   option threadname 'Hello World '
   import @clib.imp

End of Figure 1

Adding Functionality

Obviously, creating a NetWare-Aware application requires much more than a simple printf call. The NetWare 3.x platform provides an extensive set of APIs that you can use to create NetWare-Aware applications. First, you must understand the differences between DOS applications and NLM applications which use the NetWare APIs. BINDSCAN, a utility provided with the Network C for NLMs SDK (Software Developer's Kit), is a useful example for demonstrating these differences. BINDSCAN scans the bindery and displays objects, their properties, and associated property values. A quick examination of the source code for BINDSCAN reveals three APIs that are not supported in the DOS environment:
  • PressAnyKeyToContinue()
    Displays message and waits for a key
  • HideInputCursor()
    Turns off the cursor
  • DisplayInputCursor()
    Turns on the cursor
Correspondingly, to alter BINDSCAN to run under both DOS and NetWare, you would make the changes listed in Figure 2.

FIGURE 2: Required changes for BINDSCAN

#if !defined(__386__)
  #define PressAnyKeyToContinue()    \
{ \
 printf("\nPress any key to continue\r"); \
 getch();   \
}
  #define HideInputCursor()
  #define DisplayInputCursor()
#endif

End of Figure 2
To build an NLM version, run the WMAKE utility using the original makefile. Everything you need to build a NetWare-Aware, 16-bit DOS application is included with Novell's Network C for DOS product. To build the DOS version, use the following command line:
   wcl /zp1 /d2 bindscan.c snit.lib
The WCL utility compiles the BINDSCAN program and then invokes WLINK to link the resulting .OBJ file into an executable load module. If you perform each of these tasks and run the DOS and NLM versions against your own object name on the same server, the DOS and NLM versions will produce identical output.

In the discussion on building the DOS version of BINDSCAN, the example linked in a static library called SNIT.LIB. This library contains all of the NetWare APIs for the DOS environment. In fact, the DOS version also requires that you link it with another static library, CLIBS.LIB. This library contains APIs for WATCOM's implementation of the standard C library.

In the NLM environment, a dynamic library called CLIB.NLM is provided with NetWare 3.x. This library provides most of the APIs available to NLM developers. (See sidebar: The NetWare C Runtime Library NLM.) This is not to say that statically-linked libraries can not be used in the NLM environment, because they can. A utility called WLIB, an object librarian included with the Network C for NLMs SDK, allows you to create static library files which can be linked in with WLINK.

Sidebar: Novell's C Runtime Library NLM

Novell's C Runtime Library NLM (CLib) is included with NetWare 3.x and contains all of the functions used by NLMs created with the Network C for NLMs SDK. This NLM library exports over 600 fully-reentrant APIs that can be used by many NLMs at once. CLib contains an ANSI-compliant API set, as well as several other groups of APIs including NetWare APIs, an execution thread API, a library API and an implementation of the POSIX APIs. Following is a table of selected APIs, showing which APIs are supported in each environment:
API                 DOS  NLM  OS2

printf              X    X    X
open                X    X    X
fopen               X    X    X
signal                   X    X    X
exit                X    X    X
sprintf                  X    X    X
getch               X    X    X
strcmp              X    X    X
malloc                   X    X    X
BeginThread         na   X    1
SuspendThread  na   X    2
EnterCritSec        na   X    3
CreateBinderyObject X    X    4
ScanProperty        X    X    5

1    Function called '_beginthread' in OS/2 C Library.

2    Function called 'DosSuspendThread' in OS/2 C Library.

3    Function called 'DosEnterCritSec' in OS/2 C Library.

4    Function called 'NWCreateBinderyObject'

5    Function called 'NWScanProperty'
Most of the API calls are the same, regardless of environment. The thread APIs (not supported under DOS) are very similar for both OS/2 and NetWare (the only difference is API name). Another difference between the current implementations of the NetWare APIs is that the OS/2 APIs are prefixed with 'NW', and may have different types for the parameters.

End of Sidebar

multi-threaded Applications

Each execution path through the application code is referred to as a thread. Under DOS, you are usually concerned with only a single thread of execution. Some single-threaded applications are certainly viable NLM candidates. However, as network computing becomes more prevalent, many developers are interested in creating more complex applications containing multiple threads of execution and requiring communication between workstation applications and a server application.

Both OS/2 and NetWare provide the capability to create multi-threaded applications. Developing multi-threaded applications for either of these environments poses similar challenges. Some differences between these platforms surface when programming for factors like preemption. Preemption requires you to lock down data structures, since a thread may be suspended at any time. If the update of one or more data structures must be performed as a canonical operation, you must provide synchronization to prevent corruption of the data. For example, if a linked list of data structures is shared by all threads of execution and you need to remove a link from the list, you must "lock" the list while removing the link to prevent another thread from trying to traverse it during this process.

Even in a non-preemptive environment such as NetWare 3.x, there will be situations where multiple threads compete for some type of resource requiring serialized access. If one thread is accessing such a resource and gives up control (perhaps implicitly), another thread may begin to use it which may cause problems. In situations like this one, you must synchronize threads.

There are many ways to synchronize threads, but the easiest is by using local, in-memory semaphores. Both OS/2 and NetWare provide this type of mechanism, with the main difference being only in the API names. Another option for thread synchronization is accomplished by disabling CPU interrupts. If the work you are doing is very short, like removing a link from a linked list, disabling interrupts is usually easier, and carries less overhead. Of course, disabling interrupts is not always an option. If the operating system supports Intel processor ring protection, applications that are not running at the IOPL privilege level are not allowed to disable interrupts.

To illustrate a method for performing thread synchronization, an example program named DBLWIN.C has been included with this article's source listings (see "DAX (Distributed Application Example") for instructions on obtaining the source). DBLWIN.C illustrates the use of local semaphores to serialize access to the local screen's current cursor position. In addition, it creates a debug screen for debug messages, and synchronization to the current screen is also performed using local semaphores. The NLM divides the main screen into two logical windows, then creates two threads, each one being responsible for keeping the time accurate in its assigned window.

Two Multi-thread Design Strategies

When creating a multi-threaded application for any environment, a question that comes to mind is, "Should I create worker threads dynamically, or should I use a pool of threads and distribute the work among them?" Both design strategies have advantages and disadvantages.

In the "dynamic worker thread" strategy, each time a request is received by the server, a thread is started to service the request. This approach is easy to implement, as it requires that you simply spawn a new thread for each new request. On the other hand, this method can consume large amounts of resources and create sufficient overhead on the kernel's run queue. Just considering stack space requirements, a normal thread usually takes about 8K of RAM for its local stack. If you wanted to support 250 clients simultaneously, you would consume two megabytes of RAM just for the thread stack! You must also consider that 250 threads competing for the CPU would increase the amount of time that all threads must wait before regaining CPU control.

In the alternate "pool of threads" approach, a number of worker threads are started when the server application begins, then, as work comes in, a thread is taken from the pool to service it. This prevents the situations listed above from occurring, however it adds the additional overhead of queuing requests in situations where all threads in the pool are already busy.

Which strategy is best for your application? Maybe the first, maybe the second, and maybe a combination of both. It really depends on your application, and you must apply the characteristics of your program to the designs, to determine the best strategy for you.

DAX (Distributed Application Example)

DAX is a working application that demonstrates the fundamentals of distributed computing. This application is an implementation of a client-server calculator. Although DAX is not an ideal distributed application, it allows you to plug in your own code easily. DAX can be downloaded from the NetWire forum on CompuServe (NOVLIB forum, LIB 1, filename: DAXn.ZIP, where "n" is the version). The remainder of this article provides an overview of the components of a distributed application and discusses the communication protocol layer. Part II of this article in the May issue of Bullets will cover distributed application protocol (DAP) and the client and server applications.

The software portion of any distributed application can be broken down into four major components:

  • Communication protocol
  • Distributed application protocol (DAP)
  • Client application
  • Server application
A client application sends requests to the server application using the DAP, a language both applications understand. The messages which make up the DAP are transported back and forth via a communication protocol. Steve Holbrook presents an analogy of this process in his article "Designing Distributed Applications for NetWare," (Oct. 1988 NetWare Technical Journal):

"In the real world, you see client-server relationships in almost every aspect of life. More often, people play the role of client. For instance, when Charlie wants to buy something from a mail-order house, Charlie is the client; the mail-order house is the server; the order form that Charlie fills out is the client-server protocol, speaking in a request/reply language which the order form provides.

"The mail service used to send and receive the order form is the communication protocol (this includes the type of envelope, the addressing scheme, and the method of delivery). The carrier (US Mail, Federal Express, or whatever) is the communication media."

In order to isolate the communication protocol (e.g., IPX or TCP/IP) from the DAP, the example application contains a layer of software and a generic API that separates one from the other. This allows you to change the communication protocol (CP) layer, without changing any of the layers above, including the DAP as well as the client and server applications.

The current version of the example program supports both DOS and NLM clients. To isolate the CP layer from those above, the example application uses a static library containing object modules for both DOS and NLM implementations. The communication protocol used in this example is IPX. The source code can be compiled for either supported environment by defining one of two constants:

"DOSCLIENT"
or
"NLMCLIENT"
To compensate for differences between DOS and NLM implementations of the IPX API, a special header file called "HELPER.H" is used to define some macros that produce the illusion of an identical IPX API set.

On the server side, the CP layer is designed as a standalone NLM which exports several APIs to provide its services to the other application (the server part of the distributed application). The CP layer could have been implemented as a static library, but if you were to add support for other protocols, a static library would force you to rebuild the entire server application, rather than just the underlying communication protocol layer NLM.

DAX is very modular in nature and uses identical file names for the client and server modules to make it easy to match up the pieces. The CP layer is broken into two logical pieces, the client side and the server side. The two sides share a common header file, CP.H, which defines the layout of the data exchanged between the two nodes on the network. The type declaration below displays the structure of the packet defined in CP.H.

typedef struct {

   CPHEADER   cphdr;

   UINT8 msg[CPMAXMSG];

}CPMESSAGE;
The "msg" member is simply defined as an array of characters. Although you could define the layout of this data, you might be tempted to utilize it from code in the CP layer, and you should resist this temptation. If the CP layer "knows" nothing about the data it is transmitting across the network, applications can achieve a very modular design, allowing you to add different protocols with relative ease.

The "cphdr" member contains the information that the CP layer uses to maintain communications between the nodes. In DAX, "cphdr" consists of a signature (for authentication purposes), a server ID for identifying the client node to the server application, and some reserved bytes which could be used for implementing things like large packet support or flow control on connectionless-oriented protocols. Figure 3 contains the structure of the packet header defined in the CP.H structure.

FIGURE 3: Structure of the packet header defined in CP.H

typedef struct {
   UINT32  signature; // signature for packet CPPKTSIG
   UINT32  serverID;  // server ID for this session
   UINT8   reserved[6]; // keep six bytes for later
}CPHEADER;

End of Figure 3

Software Modules

The communication protocol (CP) layer on both the client and server sides consists of four main modules:
  • CPINIT
  • CPCONN
  • CPSEND
  • CPRECV
Each of these modules contains the necessary support for implementing a single part of the CP layer. Some routines in each of these modules contain no code. The entire layer is designed to be very generic, and it can be adapted to almost any underlying network protocol. For example, the CPDeInitializeSendLogic API contains no code (in this implementation, nothing is required to de-initialize send logic). The server side contains one extra module, CPIO, and this module contains support for printing messages under certain error conditions.

CPINIT Module

This module fully initializes the CP layer, preparing it to connect and send and receive messages. CPINIT contains two APIs, CPInitialize and CPDeInitialize. The DAP layer calls the CPDeInitialize API to shut down the CP layer when you no longer require any services from the application server. These two APIs are called by the DAPInitialize and DAPDeInitialize APIs which reside in the DAP layer. Each layer can only communicate with the layer directly below it. This design creates a very modular application, and allows you to replace each layer with a minimum of difficulty. The server side performs the same functions listed for the client side and, in addition, handles service advertising support. After successfully initializing, we begin advertising our service to the network.

CPCONN Module

On the client side, this module contains the support code for connecting to and disconnecting from an application server. The example application uses IPX, which is a connectionless-oriented protocol, so these APIs merely set the flags in the CP control structure that indicate whether a connection exists or not. This module also contains two empty APIs that are not needed for this design but are used as place-holders for future versions.

On the server side, this module contains the routines that accept new sessions with the application server and maintain the array containing client information. Because this version of DAX is connectionless, it accepts new connections dynamically as they are received. On receipt of a client request, DAX uses the sessionID from the CP header to index the client data array. If the network address of the new request does not match the address in that element, a new slot is assigned for the client.

CPSEND Module

CPSEND contains the APIs used to send requests. The API is synchronous -it does not return until it sends the message to the server or an error occurs. The module also contains code to "timeout" the loop, and return an error condition. Finally, CPSEND contains initialization and de-initialization routines.

CPRECV Module

This module contains the code for receiving messages from the application server. Unlike the send API, this API is asynchronous -it does not wait for a reply, but instead checks to see if one has been received, and returns the data if one was. This implementation of CPRECV also contains some low-level authentication support (packet signature only). This module's error handling mechanism for received packets is minimal and could be improved. If a transport error occurs, it returns a "TRANSPORT" error, without reposting the ECB. As an exercise, try implementing an intelligent error handling mechanism.

On the server, this module handles incoming requests from client applications. This module is perhaps the most complex of all server-side CP modules. Like the other modules, it has both initialization and de-initialization routines. It also has an independent thread of execution which handles incoming requests. As new requests are received, this thread identifies the requester, and then calls an API in the DAP layer to process the request. To avoid overloading the receiving thread, the API in the DAP layer queues the request for later processing. This design allows the server application to repost the ECB to listen for new requests.

In May, Bullets will examine the DAP layer and the client and server applications that use it. Next month's issue will also discuss how to port the example application so that you can create your own distributed application for NetWare 3.x. So, stay tuned next month for Part Two of "NLMs: from 'Hello World' to World-Class Computing."


TECHNICAL INSIGHTS:

Btrieve DATE Data Type & Microsoft BASIC (Btrieve (All versions))

When developing Btrieve applications with Microsoft BASIC PDS 7.X or Visual Basic v1.0, the Btrieve DATE data type is required to sort on a date field. The DATE data type field is stored internally as a four-byte value. The day and month are each stored in one-byte binary format. The year is stored as a two-byte integer value. The day is in the first byte, the month is in the second byte, and the year is a two-byte integer following the month.

To efficiently sort on a date field, create a structure using the user-defined TYPE and END TYPE statements for the key buffer.

   TYPE Date
  Day   AS STRING * 1
  Month AS STRING * 1
  Year  AS INTEGER
   END TYPE
Then, name the structure using the DIM statement for BASIC PDS 7.X:
   DIM Birthdate AS Date
Or, name the structure using the Globalstatement for Visual Basic v1.0:
   Global Birthdate AS Date
Next, assign values to these variables using the MKI$ or the CHR$ functions. (Visual Basic v1.0 does not support the MKI$ function, so you must use the CHR$ function instead.) Since the day and month variables need to be stored as a one-byte string, the integer values must be converted to string values using the MKI$ or CHR$ function as shown below.
   Birthdate.Day   = MKI$(20)
   Birthdate.Month = MKI$(6)
   Birthdate.Year  = 1999
OR
   Birthdate.Day   = CHR$(20)
   Birthdate.Month = CHR$(6)
   Birthdate.Year  = 1999
The MKI$ function converts a two-byte integer to a two-byte string. The CHR$ function converts a decimal value to its equivalent ASCII character. Since the day and month variables will never exceed 255, only the low-order byte is being manipulated. Either function will store the same value.

Once these values have been inserted into the Btrieve file, the same birthdate structure can be used to retrieve data with a GET operation. Once the record is returned into the birthdate structure, convert the day and month variables back to their numeric values.

(For BASIC PDS v7.x)

CALL BTRVFAR(GETFIRST%,
   PosBlk$,
   VARPTR(Birthdate),
   VARSEG(Birthdate),
   _BufLen%,
   KeyBuf$,
   KeyNum%)

(For Visual Basic v1.0)

Status%=BTRCALL(GETFIRST%,
 PosBlk$,
 Birthdate,
 BufLen%,
 KeyBuf$,
 KeyBufLen%,
 KeyNum%)
The conversion can be accomplished with the ASC function which returns the numeric value of the equivalent ASCII character.
Day%   = ASC(Birthdate.Day)
Month% = ASC(Birthdate.Month)
Year%  = Birthdate.Year

Preventing NLMs from Unloading

In the March 1992 issue of Bullets (Volume 4, Number 3), the "Technical Insights" section contained a routine that can be used to prevent NetWare Loadable Modules from unloading by "pushing" characters to the console screen using "ungetch" ("n"). One API call (DestroyScreen) was inadvertently omitted and should be included in the routine.

Insert the following API call before the "return" statement at the end of the routine:

  DestroyScreen(NewScrID);

Specifying Server Name/Volume Name in BLOG.CFG (NetWare Btrieve (NLM) v5.15)

If the file, BLOG.CFG, uses ServerName or VolumeName to specify the path to the BROLLFWD log file, BROLLFWD will return the error message, "Cannot find log file." However, if a drive letter is specified instead of ServerName and VolumeName, BROLLFWD will work correctly.

To ensure that BROLLFWD functions properly, use a drive letter to specify the path to the log file.

maxECBs in Windows IPXInitialize() (NetWare C Interface for Windows v1.22)

In Windows Enhanced mode, passing zero for maxECBs in the IPXInitialize() function, and then managing the allocation of ECBs in low memory results in an unrecoverable application error.

In this mode, passing zero for maxECBs is allowed only for compatibility with the Standard mode The zero setting for maxECBs will be ignored and VIPX will manage ECB allocation, so applications should allow VIPX to perform this task. However, in Standard mode, applications can pass zero for maxECBs and then manage their own ECB allocation.

GetBroadcastMessage API Update (NetWare C Interface for DOS v1.2)

In the NetWare C Interface for DOS v1.2, the GetBroadcastMessage API is designed to expect messages of 55 bytes or less to be returned. However, broadcast messages of up to 58 bytes may be returned from the server console. A long message (over 55 bytes) corrupts system memory and may cause a workstation hang. To avoid problems, modify the source code to the GetBroadcastMessage API in the BRODCAST.C source files as shown in Figure 4.

FIGURE 4: Necessary modifications to BRODCAST.C

(Changes marked with "<---")

int GetBroadcastMessage(messageBuffer)
char *messageBuffer;
{
    char sendBuf[3], replyBuf[61]; <---
    int len, ccode;

    *((int *)sendBuf) = 1;
    sendBuf[2] = (char) 1;
    *((int *)replyBuf) = 59;  <---
    replyBuf[2] = (char)58;   <---
    ccode = _ShellRequest ((BYTE)0xE1, (BYTE *)sendBuf,
 (BYTE *)replyBuf);
  if (ccode)
    return (ccode);
    len = (int)replyBuf[2];
    memmove(messageBuffer, replyBuf + 3, len);
    messageBuffer[len] = 0;
    return (0);
}
End of Figure 4

Brequest Hangs If No Network Connection (NetWare Btrieve (NLM) v5.15)

Attempting to load BREQUEST.EXE v5.16 on a workstation that does not have a network connection will hang the workstation. Make sure the NetWare shell is loaded before attempting to load Brequest. If you load Brequest from the AUTOEXEC.BAT file, check the DOS ERRORLEVEL after loading the network shell. If ERRORLEVEL equals one, do not load Brequest. Add the following code to your AUTOEXEC.BAT to test for this condition.
SET B_LOAD=Yes
IPX
NETX
IF ERRORLEVEL 1 SET B_LOAD=NO
...
IF %B_LOAD% == Yes BREQUEST /D:8192
SET B_LOAD=
...

xRestrict & Views with Restrictions (XQL (all versions))

In XQL v2.11, placing a new restriction on a recalled view with a previously defined restriction results in a number of situations, depending on the level of the XQL call. For example, if you create a view with the following restriction:
  CREATE VIEW REST AS

    SELECT * FROM PATIENTS

    WHERE ZIP = "78759"
Then, with XQL Manager level calls, the statement:
  SELECT * FROM REST

    WHERE Last^Name BEGINS WITH "A"
will result in the new restriction being appended to the existing restriction with an "AND" operator. Then, records will be retrieved that satisfy both restrictions (ZIP = "78759" AND Last^Name begins with "A").

Using XQL Primitives, an application would perform an xRecall of the VIEW "REST" and then an xRestrict ("Last^Name BEGINS WITH "A"). However, the overall restriction on the VIEW will be determined by which iOption is specified on the xRestrict call .

If you set the iOption parameter specified in the xRestrict call to one, the new restriction will replace the previously defined restriction. If you set iOption to zero, the new restriction will be appended to the previous restriction with an "AND" operator. You can also set iOption to zero and precede the restriction statement with the "OR" operator (|| Last^Name BEGINS WITH "A"). Then, XQL will append the new restriction to the previous restriction with an "OR" operator.

Loading DLLs Explicitly in Windows (NetWare C Interface for Windows v1.22)

If a Windows program makes a call to any NetWare C Interface for Windows Dynamic Link Libraries (DLLs), the DLL is loaded implicitly (automatically) by Windows at runtime. All of these DLLs except NWIPXSPX.DLL require access to functions in the DLL, NETWARE.DRV. NETWARE.DRV is not loaded unless the NetWare shell is loaded when you start Windows.

To avoid this situation, load the DLLs explicitly instead of implicitly. To load the DLLs, follow the steps outlined in Figure 5.

FIGURE 5: Steps for loading DLLs explicitly in Windows v3.0

A)   Determine if the NETWARE.DRV is loaded:

   HANDLE hNetWare;

     hNetWare = GetModuleHandle("NETWARE.DRV");
     if (hNetWare == NULL)

// not loaded
        else

// loaded!

B)   Try to load the DLL (in this example, NWBIND.DLL):

   HANDLE hDLL;

hDLL = LoadLibrary("NWBIND.DLL");

if (hDLL < 32)

// error returned (file not found = 2)

   else

// loaded!

C)   Make functions point to their location(s) in the DLL:

// This method uses the function string name

// (very memory hungry; pointers + strings, slow load time)

   GetBinderyObjectID =

(WORD (FAR PASCAL *)()) GetProcAddress(hDLL,

"GetBinderyObjectID");

   // This method uses function ordinal number

 (in this example, 14)

   // (little memory; just pointers, very fast)

   GetBinderyObjectID =

   (WORD (FAR PASCAL *)()) GetProcAddress(hDLL, (LPSTR)14);

End of Figure 5
These three steps also require you to modify the prototypes so that the function names are pointers to functions. For example, to modify the prototype for GetBinderyObjectID(), change the line in the NWBINDRY.H file as shown in Figure 6.

FIGURE 6: Modification to GetBinderyObject() required when loading DLLs explicitly

Old:

extern WORD FAR PASCAL

   GetBinderyObjectID(char   far *objectName,

  WORD   objectType,

  DWORD  far *objectID );

New:

WORD (FAR PASCAL

  *GetBinderyObjectID)(char   far *objectName,

   WORD   objectType,

   DWORD  far *objectID );

End of Figure 6
Do not import the functions from the DLLs you explicitly load. The actual calls to the functions do not have to be changed once all the above steps are taken.

Relocation of Source Code for Bullets Articles

The source code for CAPIT. C referenced in "Capturing with the NetWare Print Services" (March 1992 Bullets) can now be downloaded from NOVLIB, LIB 7, filename:CAPITx.ZIP (where "x" is revision number). The source file, MAPIT.C, referenced in "Saving & Restoring Drive Mappings" (February 1992 Bullets) is now located in NOVLIB, LIB 7, filename: MAPIT.ZIP.

COMING EVENTS: 1992 APPLE DEVELOPER CONFERENCE

Novell Walnut Creek invites you to the 1992 Apple Developer Conference, Wednesday, May 13 where we will demonstrate the latest Novell solutions for the Macintosh developer and user. Featured in the Enterprise Systems Expo at the San Jose, California Convention Center, Novell will display Macintosh connectivity solutions including NetWare v3.11 with NetWare for Macintosh, related developer tools and a prototype of the new IPX/SPX for the Macintosh.

This also will be your first opportunity to see DataClub from Novell, the newest addition to Novell solutions for the Macintosh. With DataClub, Macintosh users enjoy the high performance of a dedicated file server with or without dedicated server hardware.

Mark your calendar for May 13 and plan on visiting the Enterprise Systems Expo, at the 1992 Apple Developer Conference.


NIBBLES AND BITS

Fast Answers to Common Questions

Btrieve

Q - What is a status 1015?

A - Status 1015 is returned by Btrieve for OS/2 if you have specified the following environment variable before starting your application:

     SET BTRPARMSCHK=Y
By setting this environment variable, you can isolate errors caused by passing invalid pointers to Btrieve. When this variable is set, Btrieve validates the pointer parameters passed in for READ/ WRITE access over certain ranges. Btrieve validates the position block pointer over a range of 128 bytes; it validates the data length pointer over two bytes (data length only points to an integer); it validates the data buffer over the specified data length; and it validates the key buffer over 255 bytes.

Q - Can I load the Btrieve/NetWare SQL NLMs manually from the console? What will happen if I use the NCF files to load them?

A - If you load these NLMs with the manual 'Load <NLM name>' command at your server console or in your AUTOEXEC.NCF file you may experience the following problems:

  • If you fail to load the corresponding SPX communication NLM (BSPXCOM, or NSSPXCOM), you will receive errors when calling Btrieve or NetWare SQL from a workstation, another server, or when loading BCONSOLE.NLM.
  • If you manually load either BTRIEVE.NLM or NWSQL.NLM, you receive nonzero status codes when you exceed the default load parameters for the respective NLM. The load parameters that you set in BSETUP or NSSETUP are written to BSTART.NCF or NSSTART.NCF respectively, so if you do not load using the NCF file, you are loading the NLMs with default parameter values. You can also call the NCF files from the server's AUTOEXEC.NCF file to load the NLMs automatically with the correct parameters.

NetWare for SAA v1.2

Q - Do you need a separate 5250 gateway to connect to an IBM miniframe AS/400?

A - No, NetWare for SAA v1.2 supports both 5250 and 3270 mainframe and miniframe connections.

Xtrieve PLUS for DOS v4.10

Q - In Xtrieve PLUS v4.10, when I use the Suppress Blanks option and use the following layout:
     field1 field2 field3
then, if field2 has no data, that data for field3 is placed where the data for field2 would have been. Is there any way to make the Suppress Blanks option work only "down the page" and not "across the page?" A - No. In this situation, you should not use the Suppress Blanks option.

NetWare C Interface for DOS v1.2

Q - Why is the source code to LoginToFileServer(), VerifyBinderyObjectPassword(), and ChangeBinderyObjectPassord() not included with the NetWare C Interface for DOS v1.2?

A - The encryption algorithm is part of the login source code, and is not available to the public. You can, however, obtain a "generic" AsmLoginToFileServer() and related functions that can be called by any compiler that supports both a code and data segment and C-style parameter passing on the stack. Currently, there is no support for Turbo Pascal-based calls to these functions, as Turbo Pascal requires there to be no data segment. Contact the Developer Support Group at Novell Austin to request this set of object files.

VIPX.386 v1.10

Tip When using the NetWare Windows driver, VIPX.386 v1.10, use the versions of IPX.OBJ/IPXODI.COM and NWIPXSPX.DLL that are supplied with the VIPX.ZIP file.

Network C for NLMs v2.0b

Tip Have you ever received a message like, "Module did not release 1264 resources. Resource: Small memory allocations," and wondered how to track down exactly which memory you did not release? The following procedure identifies which pointers must be freed. It works with NetWare v3.11 with CLib v3.11 (with debug symbols).
  1. Drop into the Internal Debugger.
  2. Set a breakpoint exactly like this:
         b=malloc-4d+[dmalloc-9]
    
  3. Run your NLM.
  4. When the NLM exits, the debugger is entered once for each pointer. In the debugger, register EAX contains a pointer to un-freed memory.

OS/2 Developer's Kit v1.3a

When using OS/2 version1.3 and NetWare, be sure to apply all the patches to the NetWare OS/2 requester. Un-patched requesters can produce unpredictable results in Btrieve and NetWare SQL. The current patches are on CompuServe (NOVLIB forum, LIB 5, filename NSD003.ZIP. This file is also available from the Developer Support Group at Novell Austin.

NOVELL EDUCATION

Novell Education offers classes for many of the Database & Development Products including Btrieve, XQL, NetWare SQL, Xtrieve PLUS and the NetWare Client APIs. These classes are designed to introduce participants to the powerful features of Novell's development tools, database products, and NetWare services. Nearly 50% of class time is devoted to hands-on activities.
  • 901 - Programming with NetWare Client APIs
    June 10-12 Sunnyvale, CA
  • 905 - Programming with Btrieve
    May 12-14 Austin, TX
    July 8-10 Sunnyvale, CA
  • 906 - Programming with Btrieve: Advanced Features
    June 16-18 Austin, TX
  • 907 - Xtrieve PLUS
    May 20-21 Sunnyvale, CA
    July 14-15 Dallas, TX
  • 910 - Programming with XQL
    June 10-12 Austin, TX
  • 920 - Programming with Network Communication Services
    June 15-16 Sunnyvale, CA
  • 930 - Developing NetWare Loadable Modules (NLMs)
    May 13-15 Sunnyvale, CA
    July 15-17 Washington, DC
To register or obtain information on pricing, location and course content for classes held in the US, call 1-800-233-3382, 1-801-429-5508 or 1-800-NET-WARE. International customers should call 1-801-429-5508. All courses are subject to cancellation or rescheduling within two weeks of the scheduled date, depending on enrollment.

For information on availability, location, and prices of international classes, contact your local Novell office.


FUN & FACTS

Test your "developing" skills with the various Professional Development Series products by taking our Fun & Facts quiz. Have fun and good luck! (See the end of this section for answers.)
1.   How many bits are in an "int" in NLM environment?

A.   8

B    16

C.   24

D.   32
2.   What CLIB.NLM function generates unique strings for use as a valid filename?

A.   UniqueFileName()

B.   tmpnam()

C.   tmpfile()
3.   In the NetWare C Interface for DOS v1.2, what is the name of the header file that contains
the definitions for all the C structures needed for IPX and SPX?

A.   nxt.h

B.   ipxspx.h

C.   nit.h
4.   In the NetWare C Interface for DOS v1.2, what is the name of the function that initializes
the ECB's immediate address field?

A.   IPXGetLocalTarget()

B.   IPXImmediate()

C.   IPXInitialize()
5.   What are the two modes of services provided by TLI (Transport Layer Interface)?

A.   Connection mode & Connectionless mode

B.   Real mode & Standard mode

C.   Connect mode & Disconnect mode
6.   In NetWare SQL, which of the following is not a valid scalar function for string
manipulation?

A.   LENGTH

B.   REVERSE

C.   RIGHT

D.   UPPER
7.   If you use both XQLP and XQLM function calls, which Login function should you use?

A.   xLogin

B.   XQLLogin
8.   What is the predefined function key in Xtrieve PLUS to accelerate command file replay?

A.   F6

B.   F8

C.   F2

D.   F1
9.   Xtrieve PLUS v4.10 has a username called PUBLIC. What is the password for PUBLIC?

A.   PUBLIC

B.   PUB

C.   There is no password

D.   PUBLIC is not a valid username
10.  In Btrieve, what is the name of the file that contains the list of all Btrieve files to be
logged?

A.   BLOG.BTR

B.   BLOG.CFG

C.   BTRLOG.CFG
11.  In the NetWare 2.x environment, the bindery is composed of two files. What are the
names of these two files?

A.   NET$BIND.SYS and NET$PROP.SYS

B.   NET$BIND.SYS and NET$OBJ.SYS

C.   NET$BIND.SYS and NET$BVAL.SYS
FUN & FACTS ANSWERS:
1.   D
2.   C
3.   A
4.   A
5.   A
6.   B
7.   B
8.   C
9.   C
10.  B
11.  C

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.

ACKNOWLEDGEMENTS

Publisher: Mad Poarch
Editor: Kirk R. Humphries
Design: Creative Services, Provo

Contributing authors: Linda Anderson, Vitek Boruvka, Neda Eslami, David Harris, Sudz Khawaja, Ken Lowrie, Rudy McNeese, Clint McVey, Mike Shoemaker, John E. Stewart, Aslam Tejani, Maggie Walczynski, Chip Webb

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

(c) 1992 Novell, Inc. All rights reserved.

Novell, the N design, NetWare, Btrieve, XQL and LAN WorkPlace are registered trademarks, NetWare System Calls for DOS, NetWare Loadable Module (NLM), NetWare SQL, NetWare Btrieve, Xtrieve PLUS, NetWare C Interface for DOS, NetWare System Interface Technical Overview, NetWare RPC, NetWare RPC 386, NetWare LU6.2, Report Executive, and Professional Development Series (PDS) are trademarks, and NetWire, Direct Connect and Professional Developers' Program (PDP) are servicemarks of Novell, Inc. Apple, Macintosh and AppleTalk are registered trademarks of Apple Computer, Inc. CompuServe is registered trademark of CompuServe Corporation. Microsoft is a registered trademark, and Windows is a trademark of Microsoft, Inc. IBM and OS/2 are registered trademarks, and SAA is a trademark of International Business Machines Corporation. Sun Microsystems and NFS are registered trademarks of Sun Microsystems, Inc. WATCOM is a trademark of WATCOM Systems, Inc.