    Multinst creates a multi instance DLL that maintains separate
    data for each task that links to it.  This is done by maintaining
    a list of tasks that have linked to the DLL.  Different tasks are
    identified by the value of their SS (Stack Segment).  Data for
    each task is stored in a block of memory obtained through 
    GlobalAlloc().  When a tasks calls a function in the DLL, the
    DLL looks up the task in its list, and gets the segment
    (selector) of the block of global memory that contains that tasks
    data.  This segment address (selector) is then placed into the DS
    register.  After this point all static data and the local heap
    are specific to the task that called the DLL.  

    The first time a task calls into the DLL, a new block of memory is
    allocated for the data for that task.  The initial values for the 
    static variables are copied into the block, and a local heap is
    initialized.  

    When a task that uses the DLL shuts down, it needs to  call the 
    UnregisterTask() function.  This removes the task from the DLL's 
    list and frees the data associated with the task.  If the task does
    not call UnregisterTask(), it is possible that later another task 
    might link to the DLL that has the same SS as the first task.  Since
    the first task did not unregister itself that SS will be associated
    with a DS that is no longer present, and will cause a UAE. 

    The following 6 lines of code need to be placed at the beginning of
    each exported function in the DLL to load the corrected data segment.  
     
      if((wDS = LoadInstanceData())==0)     Get DS for this instance
                return;                     if DS==0 then we are out of 
                                            memory so return  
        _asm{
               mov     ax,wDS
               mov     DS,ax
           }

    Description of functions:
         LibMain()          - Saves the initial values of static variables 
                              and the initial size of the DataSegement 
                              and heap.
         WEP()              - Required by a DLL.
         StoreData()        - Saves a string in the local heap.
         GetData()          - Gets a previously stored string.        
         FillHeap()         - Allocates memory in the local heap until 
                              LocalAlloc() fails.  Used to demonstrated 
                              that the local heap will grow.
         InitInstanceData() - Saves initial values of static variables
                              and initializes list of tasks.
         LoadInstanceData() - Gets the segment address (selector) to the
                              data for the task that called the DLL.
         LookUpTask()       - Given the SS of the task, looks up the 
                              segment address (selector) for the data
                              for the task.  If the task is not in the 
                              list, this function returns zero.
         AddTask()          - Allocates data for the task, initializes
                              the data, and adds the task to the list. 
         UnregisterTask()   - Removes the current task from the task list.
                              This needs to be called when the current
                              task is shutting down.  
