12.2 Available Approaches for Writing a Library

LibC supports the following approaches to writing a library:

Each approach has its strengths and weaknesses. Do not attempt to use more than one method or to mix methods in your library's initialization and shutdown code.

Coding a main. If a library has a main function and has linked the LibC prelude file, LibC discovers the main function at run-time and creates a new thread to run it. Your library can then perform its initialization from this main thread and then park the main thread on a semaphore so that the main thread doesn't run off the end and cause the library to exit.

LibC supports this type of library because it is how many CLib libraries have been written, and it provides an easy method to port these CLib libraries to LibC. This is not a recommended method for someone writing a new library.

On NetWare, a library can maintain an active thread for whatever purpose doing so might serve. However, this is not a feature of libraries on other platforms. Doing this is somewhat challenging in that if the thread is not maintained, but allowed to die, its death will naturally entail unloading or attempting to unload the library. Tricks to get around this include parking it on a semaphore, but are left to the imagination of the developer.

Coding a _NonAppStart. If a library writes its own start up function (a _NonAppStart function), the library performs all its initialization and allocation of resources during start up. The start up code should return 0, to indicate that all initialization operations completed successfully and that the NLM should remain loaded. Since the library has no main, a thread is not created for the library. However, once it returns 0 (which means successful initialization), the library remains in memory until unloaded.

This is not the preferred method for libraries because it is a NetWare specific method and is not suitable for open source libraries which need to run on multiple operating systems. Moreover, this method has little to recommend it over using DllMain whose DLL_NLM_STARTUP message is called at the same step in the launching process as _NonAppStart.

The following sample code shows you how to get started with this method.

  #include <string.h>
  #include <netware.h>
  
  struct
  {
     int this;
     long that;
     void *theotherthing;
  } gLibGlobals;
  
  void *gNLMHandle;  // module handle
  rtag_t gAllocRTag; // for calling AllocSleepOK later on
  
  int _NonAppStart   // called by NetWare Loader
  (
     void *NLMhandle,
     void *errorScreen,
     const char *commandline,
     ...
  )
  
  {
     gNLMHandle = handle
     gAllocRTag = AllocateResourceTag(handle, "Foolib’s own memory",
                       AllocSignature);
     if (!gAllocRTag)// (very unlikely error)
        return -1;
     memset(gLibGlobals, 0, sizeof(gLibGlobals));
     return 0;
  }
  

Coding a DllMain. This method is not available until NetWare 5.1 SP6, NetWare 6 SP3, and NetWare 6.5. It involves coding a DllMain function to initialize the library's data structures and to handle messages which enable processes and threads to attach and detach. It should be linked to the libcpre object file.

This method simplifies the porting of Windows libraries to NetWare and allows threads and processes to attach and detach.

The following sample code shows you how to get started with this method. The code does not demonstrate the actions to take for process and thread attach or detach, but is only an example of NLM start-up. Some of the work shown isn’t even necessary. The only really necessary work is to have a DLL_ACTUAL_DLLMAIN message that returns TRUE. NetWare calls DllMain with this message to find out if the library is real and supports a genuine DllMain.

  #include <windows.h>
  #include "private.h"
  
  int DllMain
  (
     void           *hinstDLL,
     unsigned long   fdwReason,
     void           *lvpReserved
  )
  {
     switch (fdwReason)
     {
        case DLL_PROCESS_ATTACH :
        case DLL_PROCESS_DETACH :
        case DLL_THREAD_ATTACH :
        case DLL_THREAD_DETACH :
        case DLL_ACTUAL_DLLMAIN :
           return TRUE;
  
        case DLL_NLM_STARTUP :
           gModuleHandle = lvpReserved;
           gLibId = (int) hinstDLL;
           register_destructor((int) hinstDLL, (int (*)(void *))
                DisposeAppData);
           return TRUE;
  
        case DLL_NLM_SHUTDOWN :
           return TRUE;
     }
     return FALSE;
  }
  

Coding an _init and a _fini. This method is the newest method, simplifies the port of some UNIX style libraries to NetWare, and facilitates the writing of open source libraries. It is the preferred method, unless you need to be compatible with earlier versions of NetWare. It is not officially supported until NetWare 6.5 SP3 and NetWare 5.1 SP8.

To use these functions, you must link your library with the new prelude file, posixpre, rather than libcpre.

The following sample code shows you how to get started with these functions.

  static int initted = FALSE; //a little protection-probably unnecessary
  
  int _init( void ) 
  {
      int err;
  
      if (initted)
          return 0;
  
      gModuleHandle = getnlmhandle();
      if (err=pthread_key_create(&gKey, (void (*)(void *))
          DisposeThrData))
          return err;
  
      initted = TRUE;
   
      return 0;
  }