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