/*
* 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 */