Feature:

  Dynamic Link Modules
  
Description:

  Dynamic Link Modules - Introduction
  
  Following are definitions of some of the terms and phrases used in the
  description of Dynamic Link Modules:
  
      built-ins - variables and functions that are actually part of
     MicroStation, but are accessible to MDL applications. They are also
     available to Dynamic Link Modules.
     
      Dynamic Link Modules (DLM) - modules that can be loaded and linked
     by MicroStation at runtime. These modules are compiled into native
     machine code. Dynamic Link Modules are created using the host
     operating system's tools such as the compiler and linker. The MDL
     tools are not used to create a Dynamic Link Module.
     
      Dynamic Link Specification (DLS) - used to specify what symbols
     can be resolved from a specific Dynamic Link Module. A Dynamic Link
     Specification source file is compiled using the program "dlmspec"
     (supplied by Bentley Systems) to create a Dynamic Link
     Specification object file. A Dynamic Link Specification object file
     is used when linking an MDL application to tell mlink what symbols
     will be resolved from the Dynamic Link Module.
     
      native code - code created by using standard compilers and
     linkers. The keyword nativeCode in a declaration tells the MDL
     compiler that the function or variable being declared is part of
     MicroStation or a DLM. The function or variable being declared is
     not part of the MDL application but is accessible from the MDL
     application.
     
      pseudo-code instructions - instructions generated by the MDL
     compiler and understood by MicroStation's MDL interpreter.
     
      task ID - the name used for an MDL or MicroCSL application. This
     name appears in MicroStation input queue elements. It also is used
     in MicroStation commands that refer to MDL applications. For
     example, in the command "MDL UNLOAD QDIM", QDIM is the task ID. It
     is also displayed in the MDL Applications dialog box.
     
      resources - in this document, resource does not refer to the kind
     of resources that are managed by the MicroStation Resource Manager.
     It refers to anything a DLM or MicroStation may allocate and
     release for an MDL application. Examples are memory and file
     handles.
     
      user hooks - MDL functions called by native code functions. Both
     MicroStation and DLM's can call MDL user hooks.
     
  Dynamic linking addresses a number of developer concerns. These are:
  
      performance of MDL.
     
      shared libraries for their MDL applications to use. Developers
     often have multiple applications with common code.
     
      access to object-code libraries (with some limitations).
     
      ability to use the MDL builtin functions from native machine code.
     
  The main concerns with Dynamic Link Modules:
  
      printf may be the best debugging tool. The Clipper is the only
     platform with source level debugging for DLM's.  On the PC, the
     PharLap debugger takes the symbols from the DLM.
     
      The features of dynamic linking are not guaranteed to be available
     on all platforms.  In fact, it still is not known if it will be
     available on the Macintosh.
     
      Applications share the stack with MicroStation. On some platforms
     including the PC, the applications are limited by the size of
     MicroStation's stack.  This is not a problem on any of the Unix
     platforms.
     
      DLM's may be restricted to position independent code. When this is
     necessary, it does not seem to create any problems. This is
     achieved through compiler flags.
     
      There might be problems when different Dynamic Link Modules define
     symbols of the same name. On both the HP and Sparc, if 2 DLM's define
     the global data with the same name, then both applications share the
     same data.  DLM's generally should be coded with as few globals as 
     possible, and all of the globals named so that the names are likely
     to be unique across applications.
     
      In general, it is dangerous to have a DLM that needs to be linked with
     any system libraries. When linking with a system library, it is very
     difficult to link in the desired modules from the library without
     bringing in extra modules. For example, the link step may bring in a
     new copy of heap management but may not have any of the heap
     management data structures initialized.

     There are 2 known limitiations with the Sparc dynamic linking. First, all 
     data must be initialized because the SunOS utility ld.so does not correctly
     initialize the BSS segment. Second, if a function in a DLM tries to call 
     another function in same DLM but from a different source module, and 
     MicroStation has a function with the same name as the called function, then 
     MicroStation's function is called instead of the one in the same DLM.

  When Should a DLM Be Used

  A DLM should not be the key component of an application's architecture. If
  the bulk of an application must be in native code, the application should
  use the external program interface.

  A DLM is ideal when an MDL application or group of applications need 
  functions that must be compiled in native code either due to performance
  considerations, or because the functions must access some system features
  that are not available through MDL. These functions can be packaged 
  together in a DLM to be used by these applications.

  Use of System Libraries

  Whenver possible, use MicroStation's built-in functions instead of
  functions from system libraries.  The set of MicroStation's built-in
  functions includes nearly the entire ANSI C library.

  Using system libraries can sometimes become quite complicated.  It
  can be very difficult to figure out how to build the DLM.  It increases
  the risks that it will be more difficult to move the application to
  MicroStation upgrades, operating system upgrades, or other operating 
  systems.  It also increases the risk of problems resulting from 
  interaction with other applications.

  Examples of functions that must not be resolved from system libraries
  are malloc and fopen. If a DLM brings in its own copy of malloc, then
  the MicroStation process will have 2 copies of malloc.  Each copy of
  malloc will operate as if it has exclusive control of controlling 
  MicroStation's heap.  This generally works for a while, and then 
  eventually leads to heap problems and a crash. This problem occurs
  even if malloc is resolved from the C shared library. When malloc is
  resolved from the C shared library, it also causes the linker to bind in
  the static data structures used in tracking heap management.  Having this
  data structure duplicated leads to the heap management problems.

  Typically, it should be safe to use library functions that that do not
  save any state information in global data structures.  fopen and malloc
  are dangerous because they save state information in global data structures.
  fopen saves state information in the iob.  malloc saves state information 
  in the heap management data structures.  kill is okay because is does
  not save state information.  It just sends a signal to another process 
  and returns.

  It is okay to use the shared libraries that MicroStation uses.  The 
  XWindows versions of MicroStation use the X shared libraries and the 
  C shared libraries.  The Environ V version of MicroStation uses the 
  libtools2 shared library and the C shared library.  Howver, on the 
  Intergraph workstation, the DLM should not use functions that require 
  global data structures.

  On the Intergraph workstation, MicroStation version 4.4 generates an
  error messages and refuses to load a DLM if the DLM redefines malloc,
  calloc, free, fopen, brk, sbrk, or iob.
  
  Dynamic Link Modules - Changes to MDL Source
  
  The MDL compiler generates special instructions for accessing
  functions and variables that are not part of the MDL program.
  Therefore, it is necessary to identify for the compiler the symbols
  that refer to functions and variables that are not in the MDL
  application.  To indicate that these symbols refer to code or data
  implemented in native code rather than MDL, declare them using a
  storage class of nativeCode.  The following declarations tell the MDL
  compiler that "exampleFunc" is a function that was compiled to machine
  code, and that "exampleVar" is a variable defined in a native module.
  
      nativeCode int exampleFunc (int firstParam);
      nativeCode long exampleVar;
  
  When the MDL compiler compiles code that uses a function pointer, it
  must know whether that pointer points to an MDL function or a
  nativeCode function.  By default, function pointers in MDL refer to
  MDL functions.  A typedef or variable can be designated as a pointer
  to a native-code function through use of the pointerToNative pragma.  The
  following lines would be used to create a typedef for pointing to a
  native code function.
  
      typedef int (*FuncP)();
      #pragma pointerToNative funcP
  
  The syntax for the pointerToNative pragma is:
  
      #pragma pointerToNative <object-name> [,] ....
  
  where object-name refers to a variable or typedef name.  The
  pointerToNative pragma accepts a comma-separated list of object-names.
  Each object-name must be a typedef name or variable name. The object
  must have type pointer-to-function.
  
  Dynamic Link Modules - Access to MicroStation's Built-ins
  
  On some platforms, DLM source files must include the file "dloadlib.h"
  to be able to access built-in functions using the same names MDL
  programs use. Dloadlib.h contains define statements that cause the C
  preprocessor to replace references to MDL built-ins with references to
  the internal names for the built-in functions and variables.
  
  Dloadlib.h is platform specific.  On both the Intergraph platform and
  PC's, it does not do anything.  Because of the way dynamic linking is
  implemented on these platforms, it is not necessary to translate any
  of the names.  The versions for the HP and SPARC contain a lot of
  defines because there are a lot names to translate.
  
  If a source file includes dloadlib.h, then it should be possible to
  use that source file unchanged on all platforms both as MDL source and
  as DLM source. All of the differences should be handled by dloadlib.h.
  
  Dynamic Link Modules - Linking an MDL Program with a DLM
  
  The MDL linker "mlink" must know how to resolve the symbols for the
  DLM. It must know what symbols can be resolved at runtime. Since the
  DLM itself cannot be linked with the MDL program, a Dynamic Link
  Specification (DLS) object file is used. The DLS object file specifies
  the name of the dynamic link module, the list of symbols to be
  resolved from the dynamic link module, and the version number of the
  library. All of this information can be linked with the MDL program so
  that MicroStation can use it at runtime to dynamically load and link
  the DLM.
  
  The syntax for Dynamic Link Specification files is described in the
  section "Dynamic Link Modules - Dynamic Link Specification Source
  Files"..
  
  "mlink" treats DLS object files like libraries. It does not make the
  DLS object file part of the program unless the DLS object file
  resolves symbols required by the mdl object files. Since it is treated
  like a library, the DLS object file must appear in the link step after
  any MDL object files that use functions or variables from the
  corresponding DLM.
  
  The Dynamic Link Specification object file must be specified for every
  link step that may resolve symbols from that file. To simplify this,
  mlink now uses the environment variable MLINK_STDLIB.  MLINK_STDLIB
  specifies a list of object files to be included in a link step. This
  list may be up to 400 characters. The entries in the list are blank
  separated. After processing the command line, mlink gets the value of
  MLINK_STDLIB. If the environment variable MLINK_STDLIB is defined,
  mlink interprets it as a list of files. This list is appended to the
  list of input files specified in the command line.
  
  Dynamic Link Modules - Runtime Concerns
  
  MicroStation's DLM's are modeled after standard dynamically linked
  shared libraries. MicroStation does not create a process (does not
  allocate a process descriptor or MDL descriptor) for a DLM. A DLM is
  primarily a set of functions that can be called by MDL programs. A DLM
  is loaded only if it is required by an MDL program. The DLM is
  required by an MDL program if "mlink" resolved references for the
  program from a corresponding Dynamic Link Specification object file.
  
  Generally, a DLM executes only when a function in the DLM is called by
  an MDL program or if MicroStation calls one of the DLM's hook
  functions.
  
  If a DLM is called by an MDL program, then that MDL program is the
  current process while the DLM executes. If the DLM calls any functions
  that allocate resources that must belong to a process, then the
  resources will belong to the MDL program.
  
  It is difficult to predict what the current process will be when
  MicroStation calls a DLM hook function. For example, a DLM can have a
  hook that is called when an MDL program is unloaded, When MicroStation
  calls this hook, the current process is random. It may be the MDL
  program affected by what is going on at the time, or it may be
  MicroStation. If it is important for the DLM to know what MDL program
  is affected, then a pointer to the MDL descriptor is provided as a
  parameter.
  
  MicroStation never loads more than 1 copy of a DLM. If there are
  multiple requests to load the DLM, then MicroStation keeps track of
  the number of requests. Each time there is a request to unload the
  DLM, MicroStation decrements the counter of opens. If the counter goes
  to 0 then the DLM is physically unloaded.
  
  Dynamic Link Modules - Function Pointers as Parameters to
  MicroStation's Built-in Functions
  
  DLM function pointers and MDL function pointers cannot be used
  interchangeably. Using an MDL function pointer where a real function
  pointer is expected would cause a fault.  Also, using a real function
  pointer where an MDL function pointer is expected would cause a fault.
  
  There are a lot of MDL functions for establishing user hooks. These
  functions generally have a form similar to mdlSystem_setFunction
  (<function-type>, <function-pointer>). The <function-pointer> must be
  an offset into the current MDL application. It cannot be a pointer
  into the DLM. Therefore, a DLM can call one of these functions to set
  up a hook for an MDL application, but it cannot use these to set up a
  hook for itself.
  
  In MicroStation's FDF files, all function parameters that are MDL
  function pointers are declared as type MdlFunctionP.  MdlFunctionP is
  declared in mdl.h as:
  
      #if defined (mdl)
      typedef int (*MdlFunctionP)();
  
      #else
      typedef unsigned long   MdlFunctionP;
      #endif
  
  If a source file includes the FDF files, then it should be impossible
  to pass real function pointers where MDL function offsets are
  expected. Declarations such as the one for mdlSystem_setFunction
  should cause the compiler to catch all invalid uses of the these
  functions.
  
      void mdlSystem_setFunction
      (
      int             type,
      MdlFunctionP    function
      );
  
  Dynamic Link Modules - Identifying MDL Applications
  
  It may be important for some DLM's to keep track of certain MDL
  applications, particularly if the DLM is used by more than 1 MDL
  application, and the DLM allocates and frees resources for these
  applications. The DLM should be able to detect when the application is
  removed. It should then free any of the application's resources that
  the application failed to free. MicroStation provides built-in
  functions to let a DLM track an MDL application. MicroStation can
  notify a DLM whenever an MDL application is unloaded.
  
  Internally, MicroStation manages data structures called MDL
  descriptors to track the status of MDL applications. The format of the
  MDL descriptor is not published. However, the pointers to the MDL
  descriptors are still very important. Internally, most of the
  functions used to manage MDL functions require a pointer to an MDL
  descriptor as a parameter.  A DLM can call the function
  mdlSystem_getCurrMdlDesc to get a pointer to the MDL descriptor of the
  MDL application that called it. The task ID associated with an MDL
  descriptor can be determined with the function "mdlSystem_getMdlTaskID
  (mdlDescP)". The MDL descriptor for a task can be determined with
  "mdlSystem_findMdlDesc (taskIdP)" where taskIdP points to the
  application's task ID.  This task ID is not case sensitive.
  
  The MDL descriptor is essentially the internal name, and the task ID
  is the external name. The task ID is also used in MicroStation input
  queue elements.
  
  A DLM can have an unload hook that is called whenever an MDL
  application is unloaded. It can use this hook to free any resources
  allocated by the DLM for the MDL application.
  
  See the section "Dynamic Link Modules - Function Call Specifications"
  for more information on these functions.
  
  Dynamic Link Modules - Calling Custom User Hooks
  
  This section describes how native code can call MDL functions. Many
  names are used for this mechanism. Some of the names are "call backs",
  "user hooks", "asynchronous functions", and "filters". The term "user
  hooks" is used in this document.
  
  The mechanism described here works regardless of whether or not the
  native code was called by an MDL function. The mechanism works even if
  the native code is called by one MDL application and calls a function
  in another MDL application.
  
  A DLM cannot directly call functions in MDL programs. It must use
  dlmSystem_callMdlFunction to call these functions.  This does not
  mean that they cannot call MDL built-in functions.  A DLM must use
  use dlmSystem_callMdlFunction to call functions in MDL programs, but
  can call built-in functions directly.
  
  To call an MDL function, the native code must know the offset for that
  function, and the address of the MDL descriptor for the application
  that contains that function.  An MDL function pointer is not an
  absolute addresses. It is an offset from the start of the
  application's code segment.  The MDL linker forces the offset to be 8
  or greater, so 0 can be used to represent a NULL MDL function pointer.
  
  MicroStation needs a pointer to the MDL descriptor to determine the
  start of the code segment so that it can figure out the absolute
  address of the function.
  
  When MicroStation calls an MDL function, its saves the current value
  of currMdlDesc, and sets it to the valued passed into
  dlmSystem_callMdlFunction. When dlmSystem_callMdlFunction returns,
  MicroStation restores currMdlDesc.
  
  See the section "Dynamic Link Specifications - New Built-in Functions"
  for more information on dlmSystem_callMdlFunction.
  
  Dynamic Link Modules - Determining When an MDL Program is Unloaded
  
  It is often important to know when an MDL application is being
  unloaded. For example, if a DLM calls an MDL application's user hook
  after the application has been unloaded, then something random will
  happen. Also, if the DLM is allocating and tracking resources for an
  MDL application, then it must know to free those resources when the
  application is unloaded.
  
  The DLM can use an unload hook to learn when an MDL application is
  unloaded. To install an unload hook, the DLM must call
  "dlmSystem_setFunction (DLM_SYSTEM_MDL_UNLOAD, <dlmID>,
  <unloadFunctionP>)". When an MDL application is unloaded, MicroStation
  will call the unload hook passing the application's MDL descriptor as
  a parameter.
  
  Each DLM may have an unload hook, but a given DLM may have only 1
  hook. To remove an unload hook, call "dlmSystem_setFunction
  (DLM_SYSTEM_MDL_UNLOAD, <dlmID>, NULL)".See the description of
  userHook_mdlUnload for more information on this user hook.
  
  Dynamic Link Modules - Application-Specific Resources versus System
  Resources
  
  There are a number of functions that allocate resources for an MDL
  program. These resources then belong to the MDL program.  Examples are
  dlmSystem_mdlMalloc and dlmSystem_mdlFopen. These handle malloc and
  fopen calls from MDL programs.
  
  Files opened with dlmSystem_mdlFopen must always be closed with
  dlmSystem_mdlFclose. Otherwise, the FILE pointer returned by
  dlmSystem_mdlFopen may be used just like a FILE pointer returned by
  fopen. In fact, dlmSystem_mdlFopen returns the FILE pointer returned
  to it by fopen.
  
  Memory allocated by dlmSystem_mdlMalloc, dlmSystem_mdlCalloc, or
  dlmSystem_mdlRealloc must be freed by dlmSystem_mdlFree. Memory
  allocated by malloc must be freed by calling free. An MDL application
  cannot free memory that a builtin or DLM function allocated by calling
  malloc directly. Memory allocated by calling dlmSystem_mdlMalloc is
  automatically freed by MicroStation when the MDL application is
  unloaded.
  
  Dynamic Link Modules - Dynamic Link Specification Source Files
  
  This section describes the syntax used for Dynamic Link Specification
  source files. The program dlmspec is used to compile a Dynamic Link
  Specification source file producing a Dynamic Link Specification
  object file. The object file is used at link time to specify
  information on symbols that are to be resolved at runtime.  Normal MDL
  object files (.mo files) that need to have symbols resolved from a
  given Dynamic Link Specification object file must appear in mlink's
  command line prior to the Dynamic Link Specification file.
  
  The statements in a Dynamic Link Specification file can be categorized
  as preprocessor directives and commands. Comments are also supported.
  Comments are bracketed with /* and */.
  
  All preprocessor directives contain "#" at the start of a line.
  "#include" can be used to include other source files. "if", "else",
  "elif", "endif", "ifdef", and "endif" can be used for conditional
  compilation. "define" can be used to define values used for
  conditional compilation. It cannot be used to define macros.  The
  Dynamic Link Specification compiler "dlmspec" defines standard
  constants that can be used for conditional compilation. The defines
  that are built in for the appropriate platforms are msdos, pm386,
  IP32, clipper, unix, sparc, hp700, macintosh, vax, BIG_ENDIAN,
  EnvironV, and XWindow.
  
  The commands all begin with "%". The commands are Version, Functions,
  EndFunctions, Variables, EndVariables, ModuleName, and End.
  
  %Version is followed by a version number. This version number is saved
  in the Dynamic Link Specification object file. It is also saved with
  the application at link time. At load time, this version number is
  provided to the DLM's initialization function.
  
  %Functions tells dlmspec to start processing the subsequent symbols as
  function names. It is used to specify the list of functions that can
  be resolved at runtime. %EndFunctions is used to signal the end of the
  list. Any number of pairs of %Functions %EndFunctions can be used, but
  they cannot be embedded.
  
  %Variables tells dlmspec to start processing the subsequent symbols as
  variable names. It is used to specify the list of variables that can
  be resolved at runtime. %EndVariables is used to signal the end of the
  list. Any number of pairs of %Variables %EndVariables can be used.
  
  %ModuleName is followed by the name of a file. For DLM's, this is used
  at runtime to determine the name of the file containing the DLM. The
  file name suffix usually is not specified. It is system-specific. It
  is provided by MicroStation at runtime. The defaults are ".out"
  (Clipper), ".rex" (PC), ".so" (Sparc), and ".sl" (HP700).  

  An application should not use more than one DLM source file to 
  describe one DLM.
  
  The final command in a Dynamic Link Module specification source file
  must be %End.
  
  6. Additional Include Files
  
  The following files are provided:
  
      dloadlib.h - this file contains #defines that map the MDL names
     into the real MicroStation names if the MicroStation names are
     needed to load the DLM. This file is system specific. On platforms
     where the operating system resolves dynamic links, it has a lot of
     entries.  On systems such as the PC and Intergraph platform where
     MicroStation resolves the dynamic links, MicroStation maps the
     names at runtime so this file is not needed. On these platforms, a
     dummy dloadlib.h is provided.
     
      dlmfuncs.h - this contains declarations and definitions that are
     only used by DLM's.
     
      fdf files for all of the MDL builtin's that are not in the
     standard C library. Most of these built-ins are available to both
     DLM's and MDL programs.. Both MDL source and DLM source can use the
     FDF files. The files are provided in the mdl/include directory
     along with all of the MDL header files. The file dlmsys.fdf
     declares functions that are available only to DLM's.  The other FDF
     files declare functions that are available to both DLM's and MDL
     programs.
     
  Dynamic Link Modules - Example
  
  The mdl/examples/dlink directory contains an example that illustrates
  most of the important concepts needed to implement a DLM. This example
  implements the Unix file io functions read, write, open, close, etc.
  To compile and link this example, just run "bmake -I$MS/mdl/include/
  fileio". Doing this will build the application including both the MDL
  program and the DLM. Then load the MDL applications testio1 or testio2.
  MicroStation will see that the application needs the DLM and will
  automatically load it.
  
  The concepts illustrated in the example are:
  
      Dynamic Link Specification source file.
     
      Sample make file.
     
      Associating resources - file handles in this case - with a
     specific MDL application.
     
      Using 2 names for a given function - the name used within the DLM
     and the name used for access from an MDL program.
     
      Support for MDL user hooks called from the DLM.
     
      DLM hook functions.
     
      Use of an initialization function.
     
      Use of the error reporting function.
     
  DLM Example - Dynamic Link Specification Source File
  
  The Dynamic Link Specification source file is fileio.dls. It has
  sections specifying the version number, module name, and function
  names. The %Version command specifies a number that is stored with the
  Dynamic Link Specification object file. It is also stored with any MDL
  application created with this object file. When the DLM is loaded into
  memory, MicroStation calls the DLM's initialization function. The
  version number is passed as one of the parameters. In this example,
  the initialization function initialize in filemain.c verifies that the
  version number is less than 0x500. If it 0x500 or greater, then it
  rejects the load request and displays a message saying that the
  versions are incompatible.
  
  The %ModuleName command specifies the name of the file that contains
  the DLM. In this example it provides the name "fileio" to specify that
  the DLM is in the file "fileio.out" (Clipper), "fileio.rex" (PC),
  "fileio.so" (Sparc), or "fileio.sl" (HP700).
  
  The %Functions section specifies what functions can be accessed from
  an MDL program that is linked with this DLM. For each function, if
  only 1 function name is given then that name is used both within the
  MDL program and within the DLM. If a specification also contains a
  name in parentheses, then the name preceding the parentheses is used
  by the MDL program and the name in the parentheses is the name used by
  the DLM. Consider the case "open (fileio_open)". When the MDL program
  calls the function open, it is calling the DLM's function fileio_open.
  
  This example contains an empty Variables section. This section would
  be used for listing the variables that can be accessed from the MDL
  application. The format is the same as for functions.
  
  DLM Example - Managing Application Resources
  
  When MicroStation allocates resources for an MDL task, it associates
  those resources with that MDL task. If the MDL task is unloaded, then
  MicroStation must free those resources. Since a DLM should appear to
  be part of MDL, it must behave the same way. When an MDL application
  is unloaded, a DLM must also release all resources it allocated for
  that application.
  
  In this example, file handles are allocated by open, create, and dup.
  Therefore, the DLM must intercept all calls to these functions and
  record what MDL application called the function.
  
  The first step in the procedure is to establish different internal and
  external names for these functions. This is done in the Dynamic Link
  Specification file. Note that the specification for open is "open
  (fileio_open)". This specifies that the name "open" can be used in an
  MDL program. When the MDL program calls open, MDL will call the DLM's
  function fileio_open.
  
  The function fileio_open must associate the current MDL application
  and the file handle returned by the real open. To get an identifier
  for the MDL application, it calls mdlSystem_getCurrMdlDesc. Then it
  finds a free entry in the array fileList. It that entry, it records
  both the file handle and the MDL descriptor.
  
  The DLM also intercepts calls to close. When the MDL application calls
  close, the DLM's function fileio_close intercepts the call. It clears
  the data structure where it had established that the handle was
  associated with this MDL application.
  
  The final step in controlling the  file handles is to guarantee that
  when the MDL application is unloaded, all of its open files will be
  closed. To do this, the DLM sets up an MDL unload function
  mdlUnloadHook to be called whenever an MDL application is unloaded.
  MicroStation passes a pointer to the MDL descriptor when it calls the
  unload hooks. The DLM uses this pointer to determine what files are
  owned by the MDL application that is being unloaded. It closes all of
  these files.
  
  DLM Example - Support of MDL User Hooks
  
  This application supports a user hook to be called whenever an MDL
  application opens, creates, or dups a file.
  
  MDL applications set up the user hook by calling fileio_setFunction.
  This function records the offset of the function and the current MDL
  descriptor. Whenever an MDL application opens, creates, or dups a file
  the DLM calls all of the hooks using the function
  dlmSystem_callMdlFunction. The first 2 parameters for this function
  are a pointer to an MDL descriptor and the offset to the MDL function.
  The remaining parameters are variable. These parameters are passed to
  the MDL function.
  
  DLM Example - DLM Hook Functions
  
  The DLM "fileio" sets up a hook function to be called whenever an MDL
  application is unloaded. It does this by calling
  dlmSystem_setFunction. Notice that this function is similar to the
  mdlSystem_setFunction except that the second parameter identifies the
  DLM. When an MDL application sets a user hook it does not have to
  identify itself because MicroStation always knows what MDL application
  is active. However, MicroStation does not have any concept of active
  DLM. Therefore, a DLM must identify itself it the
  dlmSystem_setFunction call. When MicroStation calls the DLM's
  initialization function, it provides the DLM's identifier as one of
  the parameters. The DLM uses this identifier as the second argument in
  calls to dlmSystem_setFunction.
  
  DLM Example - Use of Initialization Function
  
  The DLM fileio contains an initialization function. This function is
  called immediately after the DLM is loaded. Typically, this is used to
  verify that the versions are compatible and to set up any hook
  functions that are required.
  
  The initialization function must always be called initialize.
  
  If the initialization function returns anything other than SUCCESS
  then the load fails and the MDL application is unloaded.
  
  See the section "Dynamic Link Modules - New Built-in Functions" for
  more information on initialize.
  
  DLM Example - Use of Error Reporting Function
  
  Typically, when a load fails a lot of error messages are required to
  completely explain what happened. The message fields in the command
  window are not sufficient for this. Therefore, load errors are
  reported with the function dlmSystem_displayError. The format for this
  is the same as for printf, except that the messages should not have a
  newline. The function dlmSystem_displayError appends a newline to
  every message. The dlmSystem_displayError function is also available
  to DLM's.
  
  DLM Example - Platform Specific Notes
  
  At the time this document was written, the example had been tested on
  a SPARCstation, an Interpro running an Environ V version of
  MicroStation, a PC, and an HP. This section describes some of the
  problems that were encountered.
  
  In general, DLM's should not use system libraries.  This example violates 
  this rule because one purpose of this example is to illustrate how to 
  handle some of the difficult problems that arise in linking DLM's.
  
  On the Intergraph platform, a DLM is a relocatable object file. To
  create a relocatable object file, use the Unix utility ld to combine
  object files. Specify -r in the command line to specify that the
  output is a relocatable object file, not an executable file.  ld must
  be used to start the link.  Neither acc or cc pass the -r flag to the
  ld.
  
  On the Intergraph platform, MicroStation can resolve symbols from a
  shared library only if MicroStation is linked with that shared
  library. Currently, only the tools and C shared libraries are
  supported. The X windows version of ustation32, programs will be able
  to use the X windows shared library.

  In this example, the link is performed in 2 steps. First fileresl.out
  is created. This resolves references to symbols that are to be
  resolved from the system libraries instead of from MicroStation. 
  This step resolves as few symbols as possible.  Then fileresl.out
  is linked with the other object files to create fileio.out. There
  are a lot of references unresolved in fileio.out.  These are
  all resolved at runtime when fileio.out is loaded.  If the link
  step only contained 1 step, and the link step that builds fileio.out
  used the libraries, then these references would be resolved from
  the libraries instead of from MicroStation. That could cause some
  important data structures to be duplicated, possibly breaking the
  heap management functions such as malloc and free.
  
  Since nearly all standard C functions are available as built-in
  functions, a portable DLM should not have to use any of the shared
  libraries. In fact, many DLM's will not be linked with any libraries.
  All of the references will be resolved at runtime from MicroStation's
  built-ins. It should be able to have all of the symbols resolved from
  MicroStation's built-ins at runtime.
  
  On the SPARC Station the DLM is a sharable object file. This is done
  automatically. There is nothing unusual in the compilation or link
  steps, except that ".so" is specified as the file suffix for the
  sharable object the link step creates.
  
  On the SPARC, it is necessary for the DLM to provide intermediate
  functions to call functions that are in the shared library. MDL
  applications can not directly call functions from a system shared
  library. The MDL application must call a function in the DLM that in
  turn calls the function in the shared library. The DLM in this example
  has a function fileio_perror that does nothing other than call perror
  with the same parameters. This is needed because the runtime load step
  would fail when MicroStation tried to get the address of perror for
  the MDL application. The system call used for that would fail and
  would terminate MicroStation. On other systems, it is possible to have
  the MDL functions call library functions directly. For example, on the
  Interpro the fileio_perror intermediate function is not required. In
  this example, the intermediate functions are used regardless of
  platform so that the code can be as similar as possible on all
  platforms.

  On the SPARC, do not specify the standard libraries in the Dynamic 
  Link Module's link step.  If the link step does not specify the
  standard libraries, the Dynamic Link Module still can use functions
  and variables from those libraries. All of the symbols are resolved at 
  runtime.  Starting with Release 4.1.2 of the SunOS, if
  both MicroStation and the dynamic link module specify the same
  shared library then at runtime the following message occurs:

      "ld.so: conflicting usage of dlopen'ed dependents"
  
  On the SPARC, MicroStation never unloads a DLM. There is a bug in the
  SunOS that causes MicroStation to crash when a DLM is reloaded.  To
  get around this problem, MicroStation always keeps the DLM loaded.
  This is most significant to developers who must restart MicroStation
  to use a new version of a DLM.
  
  On the HP700, the DLM is a shared library. All of the object files
  must contain position-independent code. This is achieved by specifying
  "+z" in the compilation step. Specify "-b" in the command line of the
  load step to specify that the output file is a shared library.
  
  On the PC, the DLM is a REX file that is created by the Phar Lap
  linker and then processed by the BSI-supplied utility linkdlm to help
  the runtime code find all references that need to be fixed up.
  
  A REX file is a standard Phar Lap relocatable executable. The REX file
  must be linked with a number of standard BSI-supplied object files and
  libraries. Both  linkdlm and MicroStation's runtime verify that these
  files were included in the link step.
  
  The object file ustnfrst.obj must be the first input file specified in
  the link step.  The library file dlmsyms.lib must be specified after
  all of the object files. This contains definitions for all of the
  symbols that can be resolved from MicroStation. ustnlast.lib must be
  the last file in the link step.
  
  After creating the REX file, run it through linkdlm. The command to
  invoke linkdlm is
  
    "linkdlm -r<rex file> -o<output file>"
  
  where <rex file> specifies a rex file that was created by the Phar Lap
  linker and <output file> specifies the outname of the output file. The
  output will be used as a DLM.
  
  Dynamic Link Modules - Debugging
  
  Source level debugging is only available on the Intergraph platform.
  
  To debug a DLM on an Interpro, just start MicroStation with dbg. After
  the DLM has been loaded, hit ctrl-C to get into the debugger. Then you
  can set breakpoints in the DLM. To invoke the debugger immediately
  after the DLM is loaded but before the initialize function is called,
  set a breakpoint on the function "dlink_debugHook". This MicroStation
  function is called immediately after a DLM is loaded.
  
  Dynamic Link Specifications - New Built-in Functions
  
  This section documents the following built-in functions:
  
  dlmSystem_callMdlFunction, dlmSystem_setFunction, dlmSystem_mdlMalloc,
  dlmSystem_calloc, dlmSystem_mdlFree, dlmSystem_mdlRealloc,
  dlmSystem_mdlFopen, dlmSystem_mdlFclose, dlmSystem_mdlTmpfile,
  dlmSystem_mdlFreopen, mdlSystem_getCurrMdlDesc, mdlSystem_findMdlDesc,
  and mdlSystem_getCurrTaskId
  
  It also documents the following hook functions that can be defined in
  the DLM: initialize, userHook_mdlUnload, userHook_dlmUnload.
  
  The DLM does not need to use the names userHook_mdlUnload and
  userHook_dlmUnload. These hook functions are designated to
  MicroStation by address only using dlmSystem_setFunction.
  
  The name initialize must be used for the initialization function.
  MicroStation uses the DLM's symbol table to find the address of this
  function.
  
  The functions dlmSystem_mdlMalloc, dlmSystem_mdlCalloc,
  dlmSystem_mdlRealloc, and dlmSystem_mdlFree have the same parameters
  and return values as malloc, calloc, realloc, and free. These
  dlmSystem_mdl... functions associate the memory with the current MDL
  application.  When that application is unloaded, the memory is
  automatically freed.  To allocate and free memory without associating
  it with the current MDL application, use malloc, calloc, realloc, and
  free.
  
  The functions dlmSystem_mdlFopen, dlmSystem_mdlFclose,
  dlmSystem_mdlTmpfile, and dlmSystem_mdlFreopen have the same
  parameters and return values as fopen, fclose, tmpfile, and freopen.
  These dlmSystem_mdl... functions associate the file pointer with the
  current MDL application. When the application is unloaded, the file is
  automatically closed. To manipulate files without associating them
  with the MDL application, use the standard functions fopen, fclose,
  tmpfile, and freopen.
  
  Summary
  
  #include "dlmsys.fdf"
  
  int dlmSystem_callMdlFunction
  
  (
  void           *mdlDescP,
  MdlFunctionP    functionOffset,
  ...             /* Parameters for the MDL function */
  )
  
  Description
  
  The dlmSystem_callMdlFunction function is used to call an MDL
  function. The mdlDescP parameter points to the MDL descriptor for the
  function to be called.
  
  The functionOffset parameter is an offset to the function to be
  called. This is an MDL function pointer.
  
  The variable argument list represents the arguments to be passed to
  the MDL function. Up to 15 arguments may be passed. MicroStation
  automatically determines the number and types of arguments expected by
  the function. (It does this by examining the function's header. This
  header is actually part of the MDL program loaded by MDL. It is
  created automatically by the MDL compiler.) Using this information,
  MicroStation moves the arguments from MicroStation's stack to the MDL
  application's stack and dispatches the function.
  
  Return Value
  
  dlmSystem_callMdlFunction returns the value returned by the MDL
  application. It only returns integer values. If
  dlmSystem_callMdlFunction  is not able to call the function, then it
  returns 0. This only happens if the offset is 0, or the pointer to the
  MDL descriptor is NULL.
  
  Summary
  
  #include "dlmsys.fdf"
  
  int dlmSystem_setFunction
  (
  int           functionType,
  void         *dlmID,
  int         (*funcP)()
  )
  
  Description
  
  The dlmSystem_setFunction function is used to designate a DLM user
  hook. The functionType parameter specifies the type of hook. Valid
  values are DLM_SYSTEM_MDL_UNLOAD and DLM_SYSTEM_DLM_UNLOAD.
  
  The dlmID parameter identifies the DLM setting up the hook. This value
  was supplied by MicroStation in the call to the DLM's initialization
  function.
  
  The funcP parameter specifies the function to be called when the
  designated event occurs. A value of NULL cancels the outstanding hook.
  
  Return Value
  
  dlmSystem_setFunction returns SUCCESS if it was able to set up the
  hook. Otherwise, it returns ERROR.
  
  Summary
  
  #include "mssystem.fdf"
  
  void *mdlSystem_getCurrMdlDesc
  (
  void
  )
  
  void *mdlSystem_findMdlDesc
  (
  char       *taskIdP
  )
  
  char *mdlSystem_getMdlTaskId
  (
  void        *mdlDescP
  )
  
  
  
  Description
  
  The mdlSystem_getCurrMdlDesc function is used to retrieve a pointer to
  the MDL descriptor of the currently active MDL application. This
  pointer is used a parameter to a number of functions.
  
  The mdlSystem_findMdlDesc function is used to retrieve a pointer to
  the MDL descriptor of the MDL application specified by taskIdP. The
  string functionType points to is the task ID of an MDL application.
  mdlSystem_findMdlDesc is not case sensitive. It makes a copy of the
  string and converts it to upper case before starting the search.
  
  The mdlSystem_getMdlTaskId function is used to retrieve a pointer to
  the task ID of an MDL application. mdlDescP specifies the MDL
  descriptor of a loaded MDL application.
  
  Return Value
  
  mdlSystem_getCurrMdlDesc and mdlSystem_findMdlDesc both return
  pointers to MDL descriptors. mdlSystem_getCurrMdlDesc always returns a
  non-NULL value. If no MDL application is active, then it returns a
  pointer to MicroStation's MDL descriptor. mdlSystem_findMdlDesc
  returns NULL if the specified task ID does not name a loaded MDL
  application. mdlSystem_getMdlTaskId returns a pointer to the task ID
  of the specified MDL application.
  
  INITIALIZE
  
  int initialize
  (
  char           *fileNameP,
  char           *taskIdP,
  void           *dlmID,
  unsigned long   initParameter
  )
  
  Description
  
  MicroStation calls the initialize function as the final step of
  loading an MDL application that requires DLM. If the DLM is required
  by more than 1 MDL program, then the DLM is only loaded once but the
  initialize function is called for each MDL program.
  
  The parameter fileNameP points to the name of the file that contains
  the DLM. The name includes the entire path.
  
  taskIdP points to the task ID of the MDL program being loaded.
  
  dlmID is a pointer that uniquely identifies the DLM. The DLM should
  use this value if it calls any functions that take a DLM ID as a
  parameter. initParameter is the value specified with the %Version
  command in the Dynamic Link Specification source file.
  
  Return Value
  
  initialize may return 0 to allow the load to continue. If initialize
  returns a non-zero value then the load of the MDL program is aborted.
  If no other MDL programs have the DLM loaded, then the DLM is also
  unloaded.
  
  userHook_mdlUnload, userHook_dlmUnload
  
  void userHook_mdlUnload
  (
  void           *mdlDescP,
  unsigned char  *taskIdP,
  unsigned char  *fileNameP
  )
  
  void userHook_dlmUnload
  (
  void
  )
  
  Description
  
  MicroStation calls the userHook_mdlUnload user hook functions whenever
  an MDL application is unloaded. Both the mdlDescP and the taskIdP
  identify the MDL application being unloaded. Either parameter is
  enough to uniquely identify the application. Both parameters are
  supplied just as a matter of convenience. mdlDescP may or may not be
  the same as the current MDL descriptor. Never depend on the value of
  the current MDL descriptor when this unload hook is called.
  
  MicroStation calls the userHook_dlmUnload function prior to unloading
  the DLM that designated that hook.
  
  Return Value
  
  Both of these hook functions are type void. MicroStation ignores the
  return values of both of these functions.
  
  
  
Feature:

  MDL Debugger Changes
  
Description:

  If source is compiled using make files similar to those distributed
  with the MDL examples, then it is no longer necessary to use the
  environment variable MS_DBGSOURCE to tell the debugger where to find
  the source.  These make files use the entire path to specify the names
  of source files.  These complete names are now stored with the object
  files and MDL programs. The debugger first uses this information to
  find the source files before trying to use the information in
  MS_DBGSOURCE.
  
  On the Intergraph platform, if the environment variable MS_DBGOUT is
  set, the MDL debugger then uses the terminal session that started
  MicroStation for input and output instead of using the MicroStation
  debugger window. That way, the terminal session can be used for
  debugging DLM's and an MDL program. The MDL debugger does not work in
  curses mode. It only supports line mode.  The MDL debugger supports
  cntl-P for previous line, cntl-N for next line, cntl-B for back 1
  character, cntl-F for forward 1 character, and backspace.  It also
  supports ESCAPE to clear the current command line.
  

