/* * 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 "_apipch.h" #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 #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 */ /* * 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 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 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 */