IMEM.C
/* 
 *  IMEM.C 
 *   
 *  Per-instance global data for WIN32 (trivial), WIN16, and Mac. 
 * 
 *  Copyright 1993-1995 Microsoft Corporation. All Rights Reserved. 
 */ 
 
#pragma warning(disable:4100)   /* unreferenced formal parameter */ 
#pragma warning(disable:4201)   /* nameless struct/union */ 
#pragma warning(disable:4209)   /* benign typedef redefinition */ 
#pragma warning(disable:4214)   /* bit field types other than int */ 
#pragma warning(disable:4001)   /* single line comments */ 
#pragma warning(disable:4115)   /* named type definition in parens */ 
 
#ifdef _WIN32 
#define INC_OLE2 /* Get the OLE2 stuff */ 
#define INC_RPC  /* harmless on Windows NT; Windows 95 needs it */ 
#endif 
#include <windows.h> 
 
#include <windowsx.h> 
#include <mapiwin.h> 
#if defined (_WIN32) && !defined (_MAC) 
#pragma warning(disable:4001)   /* single line comments */ 
#include <objerror.h> 
#include <objbase.h> 
#endif 
#ifdef WIN16 
#include <compobj.h> 
#endif 
#include <mapicode.h> 
#include <mapidbg.h> 
 
#ifdef _MAC 
#include <macname1.h> 
#include <macos\lowmem.h> 
#include <macname2.h> 
#include <utilmac.h> 
#endif 
 
#ifdef  DEBUG 
#define STATIC 
#else 
#define STATIC static 
#endif 
 
#pragma warning (disable:4514)      /* unreferenced inline function */ 
 
#ifdef  WIN16 
 
#pragma code_seg("IMAlloc") 
 
#pragma warning(disable: 4005)      /* redefines MAX_PATH */ 
#include <toolhelp.h> 
#pragma warning(default: 4005) 
 
#pragma warning(disable: 4704)      /* Inline assembler */ 
 
/* 
 *  These arrays are parallel. RgwInstKey holds the stack 
 *  segment of each task that calls the DLL we're in; rgpvInst 
 *  has a pointer to that task's instance globals in the slot with 
 *  the same index. Since all Win16 tasks share the same x86 
 *  segment descriptor tables, no two tasks can have the same stack 
 *  segment. 
 *   
 *  Note carefully the last elements of the initializers. The value 
 *  in rgwInstKey is a sentinel, which will always stop the scan 
 *  whether the value being sought is a valid stack segment or 
 *  zero. 
 */ 
 
STATIC WORD   rgwInstKey[cInstMax+1]= { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0xFFFF }; 
STATIC LPVOID rgpvInst[cInstMax+1]=   { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; 
STATIC DWORD  rgdwPid[cInstMax+1]=    { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; 
 
STATIC WORD   wCachedKey            = 0; 
STATIC LPVOID pvCachedInst          = NULL; 
 
/* 
 -  IFindInst 
 -   
 *  Purpose: 
 *      Used to locate a particular task's instance pointer, and 
 *      also to find a free slot in the table. 
 *   
 *  Arguments: 
 *      The value to look up. This is either a task's stack 
 *      segment, or 0 (if an empty slot is being sought). 
 *   
 *  Returns: 
 *      Returns the index of the given value in rgwInstKey.  
 *      If the value is not present, returns cInstMax. 
 *   
 */ 
 
#pragma warning(disable: 4035)      /* function return value done in asm */ 
 
STATIC int 
IFindInst(WORD w) 
{ 
    _asm 
    { 
        mov cx,cInstMax+1           /* count includes sentinel */ 
        mov ax,ds                   /* point es:di at rgwInstKey */ 
        mov es,ax 
        mov di,OFFSET rgwInstKey 
        mov ax,w                    /* scan for this value */ 
        cld                         /* scan forward... */ 
        repne scasw                 /* go */ 
        mov ax,cx                   /* Convert the number of items remaining */ 
        sub ax,cInstMax+1           /* to the index of the item found. */ 
        inc ax 
        neg ax 
    } 
} 
 
#pragma warning(default: 4035) 
 
/* 
 -  PvGetInstanceGlobals 
 -   
 *  Purpose: 
 *      Returns a pointer to the instance global data structre for 
 *      the current task. 
 *   
 *  Returns: 
 *      Pointer to the instance data structure, or NULL if no 
 *      structure has yet been installed for this task. 
 */ 
 
LPVOID FAR PASCAL 
PvGetInstanceGlobals(void) 
{ 
    int iInst; 
    WORD wMe; 
     
    _asm mov wMe,ss         ; get key for this process 
 
    /* First check cached value */ 
    if (wCachedKey == wMe) 
        return pvCachedInst; 
 
    /* Miss, do the lookup */ 
    iInst = IFindInst(wMe); 
 
    /* Cache and return the found value */ 
    if (iInst != cInstMax) 
    { 
        wCachedKey = wMe; 
        pvCachedInst = rgpvInst[iInst]; 
    } 
    return rgpvInst[iInst];     /* Note: parallel to the lookup sentinel */ 
} 
 
LPVOID FAR PASCAL 
PvGetVerifyInstanceGlobals(DWORD dwPid) 
{ 
    int iInst; 
    WORD wMe; 
     
    _asm mov wMe,ss         ; get key for this process 
 
    /* Always do the lookup */ 
    iInst = IFindInst(wMe); 
 
    /* If SS misses, return null right away */ 
    if (iInst == cInstMax) 
        return NULL; 
 
    /* SS hit, now check the OLE process ID */ 
    if (dwPid != rgdwPid[iInst]) 
    { 
        wCachedKey = 0;         /* Take no chances */ 
        rgwInstKey[iInst] = 0; 
        rgpvInst[iInst] = 0; 
        rgdwPid[iInst] = 0; 
        return NULL; 
    } 
 
    /* Cache and return the found value */ 
    wCachedKey = wMe; 
    pvCachedInst = rgpvInst[iInst]; 
    return pvCachedInst; 
} 
 
LPVOID FAR PASCAL 
PvSlowGetInstanceGlobals(DWORD dwPid) 
{ 
    int iInst; 
     
    /* Always do the lookup */ 
    for (iInst = 0; iInst < cInstMax; ++iInst) 
    { 
        if (rgdwPid[iInst] == dwPid) 
            break; 
    } 
 
    /* If PID misses, return null */ 
    if (iInst == cInstMax) 
        return NULL; 
 
    /* Return the found value. Do not cache; this function is being 
     * called because SS is not what it "normally" is. 
     */ 
    return rgpvInst[iInst]; 
} 
 
/* 
 -  ScSetVerifyInstanceGlobals 
 -   
 *  Purpose: 
 *      Installs or deinstalls instance global data for the current task. 
 *   
 *  Arguments: 
 *      pv          in      Pointer to instance data structure (to 
 *                          install); NULL (to deinstall). 
 *      dwPid       in      Zero or process ID, for better matching. 
 *   
 *  Returns: 
 *      MAPI_E_NOT_ENOUGH_MEMORY if no slot is available in the 
 *      fixed-size table, else 0. 
 */ 
 
LONG FAR PASCAL 
ScSetVerifyInstanceGlobals(LPVOID pv, DWORD dwPid) 
{ 
    int iInst; 
    WORD wMe; 
 
    _asm mov wMe,ss 
    if (pv) 
    { 
        /* I am NOT supposed to be in the array at this time! */ 
        Assert(IFindInst(wMe) == cInstMax); 
 
        /* Installing instance globals. Find a free slot and park there. */ 
        iInst = IFindInst(0); 
        if (iInst == cInstMax) 
        { 
#ifdef  DEBUG 
            OutputDebugString("Instance globals maxed out\r\n"); 
#endif   
            return MAPI_E_NOT_ENOUGH_MEMORY; 
        } 
        rgpvInst[iInst] = pv; 
        rgwInstKey[iInst] = wMe; 
        rgdwPid[iInst] = dwPid; 
 
        /* Set the cache. */ 
        wCachedKey = wMe; 
        pvCachedInst = pv; 
    } 
    else 
    { 
        /* Deinstalling instance globals. Search and destroy. */ 
        iInst = IFindInst(wMe); 
        if (iInst == cInstMax) 
        { 
#ifdef  DEBUG 
            OutputDebugString("No instance globals to reset\r\n"); 
#endif   
            return MAPI_E_NOT_INITIALIZED; 
        } 
        rgpvInst[iInst] = NULL; 
        rgwInstKey[iInst] = 0; 
        rgdwPid[iInst] = 0L; 
 
        /* Clear the cache. */ 
        wCachedKey = 0; 
        pvCachedInst = NULL; 
    } 
 
    return 0; 
} 
 
LONG FAR PASCAL 
ScSetInstanceGlobals(LPVOID pv) 
{ 
    return ScSetVerifyInstanceGlobals(pv, 0L); 
} 
 
BOOL __export FAR PASCAL 
FCleanupInstanceGlobals(WORD wID, DWORD dwData) 
{ 
    int iInst; 
    WORD wMe; 
 
    /* 
     *  Would be nice if we could release the pmalloc 
     *  and the inst structure in this function, but docs say 
     *  don't make Windows calls from this callback. 
     *  That means also NO DEBUG TRACES 
     */ 
 
    /* 
     *  First, double-check that the DLL's data segment is available. 
     *  Code snitched from MSDN article "Loading, Initializing, and 
     *  Terminating a DLL." 
     */ 
    _asm 
    { 
        push cx 
        mov cx, ds          ; get selector of interest 
        lar ax, cx          ; get selector access rights 
        pop cx 
        jnz bail            ; failed, segment is bad 
        test ax, 8000h      ; if bit 8000 is clear, segment is not loaded 
        jz bail             ; we're OK 
    } 
 
    if (wID == NFY_EXITTASK) 
    { 
        _asm mov wMe,ss 
        iInst = IFindInst(wMe); 
 
        if (iInst < cInstMax) 
        { 
            /* Clear this process's entry */ 
            rgpvInst[iInst] = NULL; 
            rgwInstKey[iInst] = 0; 
        } 
 
        /* Clear the cache too */ 
        wCachedKey = 0; 
        pvCachedInst = NULL; 
    } 
 
bail: 
    return 0;       /* don't suppress further notifications */ 
} 
 
#elif defined(_MAC) /* !WIN16 */ 
 
#pragma code_seg("imalloc", "fixed") 
 
/* 
 *  The Mac implementation uses a linked list containing unique keys 
 *  to the calling process and pointers to instance data. This linked 
 *  list is n-dimensional because the Mac version often groups several 
 *  dlls into one exe. 
 * 
 *  The OLE code that TomSax wrote allows us to keep track of the caller's 
 *  %a5 world when we call from another application. This code depends on 
 *  on that. 
 * 
 */ 
 
typedef struct tag_INSTDATA { 
    DWORD                   dwInstKey; 
    DWORD                   dwPid; 
    LPVOID                  lpvInst[kMaxSet]; 
    struct tag_INSTDATA     *next; 
} INSTDATA, *LPINSTDATA, **HINSTDATA; 
 
 
LPINSTDATA      lpInstHead = NULL; 
 
#define PvSlowGetInstanceGlobals(_dw, _dwId)    PvGetVerifyInstanceGlobals(_dw, _dwId) 
 
VOID 
DisposeInstData(LPINSTDATA lpInstPrev, LPINSTDATA lpInst) 
{ 
    HINSTDATA   hInstHead = &lpInstHead; 
     
    /* This better only happen when both elements are NULL! */ 
    if (lpInst->lpvInst[kInstMAPIX] == lpInst->lpvInst[kInstMAPIU]) 
    { 
        /* No inst data, remove element from linked list */ 
        if (lpInst == *hInstHead) 
            *hInstHead = lpInst->next; 
        else 
            lpInstPrev->next = lpInst->next; 
        DisposePtr((Ptr)lpInst); 
    } 
} 
 
/* 
 -  PvGetInstanceGlobals 
 -   
 *  Purpose: 
 *      Returns a pointer to the instance global data structre for 
 *      the current task. 
 *   
 *  Returns: 
 *      Pointer to the instance data structure, or NULL if no 
 *      structure has yet been installed for this task. 
 */ 
 
LPVOID FAR PASCAL 
PvGetInstanceGlobals(WORD wDataSet) 
{ 
    HINSTDATA       hInstHead = &lpInstHead; 
    LPINSTDATA      lpInst = *hInstHead; 
 
#ifdef DEBUG 
    if (wDataSet >= kMaxSet) 
    { 
        DebugStr("\pPvGetInstanceGlobals : This data set has not been defined."); 
        return NULL; 
    } 
#endif 
 
    while (lpInst) 
    { 
        if (lpInst->dwInstKey == (DWORD)LMGetCurrentA5()) 
            break; 
        lpInst = lpInst->next; 
    }  
    return(lpInst->lpvInst[wDataSet]); 
} 
 
LPVOID FAR PASCAL 
PvGetVerifyInstanceGlobals(DWORD dwPid, DWORD wDataSet) 
{ 
    HINSTDATA   hInstHead = &lpInstHead; 
    LPINSTDATA  lpInst, lpInstPrev; 
 
    lpInst = lpInstPrev = *hInstHead; 
 
    /* Always do the lookup */ 
    while (lpInst) 
    { 
        if (lpInst->dwInstKey == (DWORD)LMGetCurrentA5()) 
            break; 
        lpInstPrev = lpInst; 
        lpInst = lpInst->next; 
    } 
 
    /* If PvGetInstanceGlobals() misses, return NULL right away */ 
    if (lpInst->lpvInst[wDataSet] == NULL) 
        return NULL; 
 
    /* Found a match, now check the OLE process ID */ 
    if (dwPid != lpInst->dwPid) 
    { 
        DisposeInstData(lpInstPrev, lpInst); 
        return NULL; 
    } 
 
 
    /* Return the found value */ 
    return lpInst->lpvInst[wDataSet]; 
} 
 
/* 
 -  ScSetVerifyInstanceGlobals 
 -   
 *  Purpose: 
 *      Installs or deinstalls instance global data for the current task. 
 *   
 *  Arguments: 
 *      pv          in      Pointer to instance data structure (to 
 *                          install); NULL (to deinstall). 
 *      dwPid       in      Zero or process ID, for better matching. 
 *      wDataSet    in      Inst data set to init or deinit (MAPIX or MAPIU) 
 *   
 *  Returns: 
 *      MAPI_E_NOT_ENOUGH_MEMORY if a pointer of INSTDATA size cannot be 
 *      created, else 0. 
 */ 
 
LONG FAR PASCAL 
ScSetVerifyInstanceGlobals(LPVOID pv, DWORD dwPid, WORD wDataSet) 
{ 
    HINSTDATA       hInstHead = &lpInstHead; 
    LPINSTDATA      lpInst, lpInstPrev; 
 
    lpInst = lpInstPrev = *hInstHead; 
 
    Assert(wDataSet < kMaxSet); 
 
    /* Find our linked list element and the one before it */ 
    while (lpInst) 
    { 
        if (lpInst->dwInstKey == (DWORD)LMGetCurrentA5()) 
            break; 
        lpInstPrev = lpInst; 
        lpInst = lpInst->next; 
    } 
 
    if (pv) 
    { 
        if (lpInst) 
        { 
            /* I am NOT supposed to be in the array at this time! */ 
            Assert(lpInst->lpvInst[wDataSet] == NULL); 
            lpInst->lpvInst[wDataSet] = pv; 
        } 
        else 
        { 
            /* Add a new linked list element and store <pv> there. */ 
            lpInst = (LPVOID) NewPtrClear(sizeof(INSTDATA)); 
            if (!lpInst) 
            { 
#ifdef  DEBUG 
                OutputDebugString("Instance globals maxed out\r"); 
#endif   
                return MAPI_E_NOT_ENOUGH_MEMORY; 
            } 
            if (lpInstPrev) 
                lpInstPrev->next = lpInst; 
            else 
                *hInstHead = lpInst; 
            lpInst->dwInstKey = (DWORD)LMGetCurrentA5(); 
 
            lpInst->dwPid = dwPid; 
            lpInst->lpvInst[wDataSet] = pv; 
        } 
    } 
    else 
    { 
        /* Deinstalling instance globals. Search and destroy. */ 
        if (lpInst == NULL || lpInst->lpvInst[wDataSet] == NULL) 
        { 
#ifdef  DEBUG 
            OutputDebugString("No instance globals to reset\r"); 
#endif   
            return MAPI_E_NOT_INITIALIZED; 
        } 
        /* The memory for <lpInst->lpvInst[wDataSet]> is disposed of    */ 
        /* elsewhere. just as it was allocated elsewhere.               */ 
        lpInst->lpvInst[wDataSet] = NULL; 
        DisposeInstData(lpInstPrev, lpInst); 
    } 
 
    return 0; 
} 
 
 
LONG FAR PASCAL 
ScSetInstanceGlobals(LPVOID pv, WORD wDataSet) 
{ 
    return ScSetVerifyInstanceGlobals(pv, 0L, wDataSet); 
} 
 
BOOL FAR PASCAL 
FCleanupInstanceGlobals(WORD wID, DWORD dwData) 
{ 
/* 
 * This is no longer used. 
 * 
 */ 
 
#ifdef DEBUG 
    DebugStr("\pCalled FCleanupInstanceGlobals : Empty function"); 
#endif 
 
    return 0; 
} 
 
#else /* !WIN16 && !_MAC */ 
 
/* This is the entire 32-bit implementation for instance globals. */ 
 
VOID FAR *pinstX = NULL; 
 
#endif  /* WIN16 */