|
|
//+----------------------------------------------------------------------------
//
// File:
// global.cpp
//
// Contents:
// Ut functions that deal with HGlobals for debugging;
// see le2int.h
//
// Classes:
//
// Functions:
// UtGlobalAlloc
// UtGlobalReAlloc
// UtGlobalLock
// UtGlobalUnlock
// UtGlobalFree
// UtGlobalFlush
// UtSetClipboardData
//
// History:
// 12/20/93 - ChrisWe - created
// 01/11/94 - alexgo - added VDATEHEAP macros to every function
// 02/25/94 AlexT Add some generic integrity checking
// 03/30/94 AlexT Add UtSetClipboardData
//
// Notes:
//
// These routines are designed to catch bugs that corrupt GlobalAlloc memory.
// We cannot guarantee that all global memory will be manipulated with these
// routines (e.g. OLE might allocate a handle and the client application
// might free it), so we can't require that these routines be used in pairs.
//
//-----------------------------------------------------------------------------
#include <le2int.h>
#if DBG==1 && defined(WIN32)
#include <olesem.hxx>
ASSERTDATA
// undefine these, so we don't call ourselves recursively
// if this module is used, these are defined in le2int.h to replace
// the existing allocator with the functions here
#undef GlobalAlloc
#undef GlobalReAlloc
#undef GlobalLock
#undef GlobalUnlock
#undef GlobalFree
#undef SetClipboardData
// Same ones as in memapi.cxx
#define OLEMEM_ALLOCBYTE 0xde
#define OLEMEM_FREEBYTE 0xed
typedef struct s_GlobalAllocInfo { HGLOBAL hGlobal; // A GlobalAlloc'd HGLOBAL
SIZE_T cbGlobalSize; // GlobalSize(hGlobal)
SIZE_T cbUser; // size requested by caller
ULONG ulIndex; // allocation index (1st, 2nd...)
struct s_GlobalAllocInfo *pNext; } SGLOBALALLOCINFO, *PSGLOBALALLOCINFO;
//+-------------------------------------------------------------------------
//
// Class: CGlobalTrack
//
// Purpose: GlobalAlloc memory tracking
//
// History: 25-Feb-94 AlexT Created
//
// Notes:
//
//--------------------------------------------------------------------------
class CGlobalTrack { public:
//
// We only have a constructor for debug builds, to ensure this object
// is statically allocated. Statically allocated objects are initialized
// to all zeroes, which is what we need.
//
CGlobalTrack();
HGLOBAL cgtGlobalAlloc(UINT uiFlag, SIZE_T cbUser); HGLOBAL cgtGlobalReAlloc(HGLOBAL hGlobal, SIZE_T cbUser, UINT uiFlag); HGLOBAL cgtGlobalFree(HGLOBAL hGlobal); LPVOID cgtGlobalLock(HGLOBAL hGlobal); BOOL cgtGlobalUnlock(HGLOBAL hGlobal);
void cgtVerifyAll(void); void cgtFlushTracking(void); BOOL cgtStopTracking(HGLOBAL hGlobal);
private: SIZE_T CalculateAllocSize(SIZE_T cbUser); void InitializeRegion(HGLOBAL hGlobal, SIZE_T cbStart, SIZE_T cbEnd); void Track(HGLOBAL hGlobal, SIZE_T cbUser); void Retrack(HGLOBAL hOld, HGLOBAL hNew); void VerifyHandle(HGLOBAL hGlobal);
ULONG _ulIndex; PSGLOBALALLOCINFO _pRoot; static COleStaticMutexSem _mxsGlobalMemory; };
COleStaticMutexSem CGlobalTrack::_mxsGlobalMemory;
CGlobalTrack gGlobalTrack;
//+-------------------------------------------------------------------------
//
// Member: CGlobalTrack::CGlobalTrack, public
//
// Synopsis: constructor
//
// History: 28-Feb-94 AlexT Created
//
//--------------------------------------------------------------------------
CGlobalTrack::CGlobalTrack() { Win4Assert (g_fDllState == DLL_STATE_STATIC_CONSTRUCTING); Win4Assert (_pRoot == NULL && _ulIndex == 0); }
//+-------------------------------------------------------------------------
//
// Member: CGlobalTrack::cgtGlobalAlloc, public
//
// Synopsis: Debugging version of GlobalAlloc
//
// Arguments: [uiFlag] -- allocation flags
// [cbUser] -- requested allocation size
//
// Requires: We must return a "real" GlobalAlloc'd pointer, because
// we may not necessarily be the ones to free it.
//
// Returns: HGLOBAL
//
// Algorithm: We allocate an extra amount to form a tail and initialize it
// to a known value.
//
// History: 25-Feb-94 AlexT Added this prologue
//
// Notes:
//
//--------------------------------------------------------------------------
HGLOBAL CGlobalTrack::cgtGlobalAlloc(UINT uiFlag, SIZE_T cbUser) { VDATEHEAP();
SIZE_T cbAlloc; HGLOBAL hGlobal;
cbAlloc = CalculateAllocSize(cbUser); hGlobal = GlobalAlloc(uiFlag, cbAlloc); if (NULL == hGlobal) { LEDebugOut((DEB_WARN, "GlobalAlloc(%ld) failed - %lx\n", cbAlloc, GetLastError())); } else { if (uiFlag & GMEM_ZEROINIT) { // Caller asked for zeroinit, so we only initialize the tail
InitializeRegion(hGlobal, cbUser, cbAlloc); } else { // Caller did not ask for zeroinit, so we initialize the whole
// region
InitializeRegion(hGlobal, 0, cbAlloc); }
Track(hGlobal, cbUser); }
return(hGlobal); }
//+-------------------------------------------------------------------------
//
// Member: CGlobalTrack::cgtGlobalReAlloc, public
//
// Synopsis: Debugging version of GlobalReAlloc
//
// Arguments: [hGlobal] -- handle to reallocate
// [cbUser] -- requested allocation size
// [uiFlag] -- allocation flags
//
// Returns: reallocated handle
//
// Algorithm:
//
// if (modify only)
// reallocate
// else
// reallocate with tail
// initialize tail
//
// update tracking information
//
// History: 25-Feb-94 AlexT Added this prologue
//
// Notes:
//
//--------------------------------------------------------------------------
HGLOBAL CGlobalTrack::cgtGlobalReAlloc(HGLOBAL hGlobal, SIZE_T cbUser, UINT uiFlag) { VDATEHEAP();
HGLOBAL hNew; SIZE_T cbAlloc;
VerifyHandle(hGlobal);
if (uiFlag & GMEM_MODIFY) { // We're not changing sizes, so there's no work for us to do
LEDebugOut((DEB_WARN, "UtGlobalReAlloc modifying global handle\n")); hNew = GlobalReAlloc(hGlobal, cbUser, uiFlag); } else { cbAlloc = CalculateAllocSize(cbUser); hNew = GlobalReAlloc(hGlobal, cbAlloc, uiFlag); if (NULL == hNew) { LEDebugOut((DEB_WARN, "GlobalReAlloc failed - %lx\n", GetLastError())); } else { InitializeRegion(hNew, cbUser, cbAlloc); } }
if (NULL != hNew) { if (uiFlag & GMEM_MODIFY) { // Retrack will only track hNew if we were tracking hGlobal
Retrack(hGlobal, hNew); } else { // We've allocated a new block, so we always want to track the
// new one
cgtStopTracking(hGlobal); Track(hNew, cbUser); } }
return(hNew); }
//+-------------------------------------------------------------------------
//
// Member: CGlobalTrack::cgtGlobalFree, public
//
// Synopsis: Debugging version of GlobalReAlloc
//
// Arguments: [hGlobal] -- global handle to free
//
// Returns: Same as GlobalFree
//
// Algorithm:
//
// History: 25-Feb-94 AlexT Created
//
// Notes:
//
//--------------------------------------------------------------------------
HGLOBAL CGlobalTrack::cgtGlobalFree(HGLOBAL hGlobal) { VDATEHEAP();
HGLOBAL hReturn;
VerifyHandle(hGlobal);
hReturn = GlobalFree(hGlobal);
if (NULL == hReturn) { cgtStopTracking(hGlobal); } else { LEDebugOut((DEB_WARN, "GlobalFree did not free %lx\n", hGlobal)); }
return(hReturn); }
//+-------------------------------------------------------------------------
//
// Member: CGlobalTrack::cgtGlobalLock, public
//
// Synopsis: Debugging version of GlobalLock
//
// Arguments: [hGlobal] -- global memory handle
//
// Returns: Same as GlobalLock
//
// History: 25-Feb-94 AlexT Created
//
// Notes:
//
//--------------------------------------------------------------------------
LPVOID CGlobalTrack::cgtGlobalLock(HGLOBAL hGlobal) { VDATEHEAP();
VerifyHandle(hGlobal); return(GlobalLock(hGlobal)); }
//+-------------------------------------------------------------------------
//
// Member: CGlobalTrack::cgtGlobalUnlock, public
//
// Synopsis: Debugging version of GlobalUnlock
//
// Arguments: [hGlobal] -- global memory handle
//
// Returns: Same as GlobalUnlock
//
// History: 25-Feb-94 AlexT Created
//
// Notes:
//
//--------------------------------------------------------------------------
BOOL CGlobalTrack::cgtGlobalUnlock(HGLOBAL hGlobal) { VDATEHEAP();
VerifyHandle(hGlobal); return(GlobalUnlock(hGlobal)); }
//+-------------------------------------------------------------------------
//
// Member: CGlobalTrack::cgtVerifyAll, public
//
// Synopsis: Verify all tracked handles
//
// History: 28-Feb-94 AlexT Created
//
// Notes:
//
//--------------------------------------------------------------------------
void CGlobalTrack::cgtVerifyAll(void) { VerifyHandle(NULL); }
//+-------------------------------------------------------------------------
//
// Member: CGlobalTrack::cgtFlushTracking
//
// Synopsis: Stops all tracking
//
// Effects: Frees all internal memory
//
// History: 28-Feb-94 AlexT Created
//
// Notes:
//
//--------------------------------------------------------------------------
void CGlobalTrack::cgtFlushTracking(void) { COleStaticLock lck(_mxsGlobalMemory); BOOL bResult;
while (NULL != _pRoot) { bResult = cgtStopTracking(_pRoot->hGlobal); Assert(bResult && "CGT::cgtFlushTracking problem"); } }
//+-------------------------------------------------------------------------
//
// Member: CGlobalTrack::CalculateAllocSize, private
//
// Synopsis: calculate total allocation size (inluding tail)
//
// Arguments: [cbUser] -- requested size
//
// Returns: total count of bytes to allocate
//
// Algorithm: calculate bytes needed to have at least one guard page at the
// end
//
// History: 28-Feb-94 AlexT Created
//
// Notes: By keeping this calculation in one location we make it
// easier to maintain.
//
//--------------------------------------------------------------------------
SIZE_T CGlobalTrack::CalculateAllocSize(SIZE_T cbUser) { SYSTEM_INFO si; SIZE_T cbAlloc;
GetSystemInfo(&si);
// Calculate how many pages are need to cover cbUser
cbAlloc = ((cbUser + si.dwPageSize - 1) / si.dwPageSize) * si.dwPageSize;
// Add an extra page so that the tail is at least one page long
cbAlloc += si.dwPageSize;
return(cbAlloc); }
//+-------------------------------------------------------------------------
//
// Member: CGlobalTrack::InitializeRegion, private
//
// Synopsis: initialize region to bad value
//
// Effects: fills in memory region
//
// Arguments: [hGlobal] -- global memory handle
// [cbStart] -- count of bytes to skip
// [cbEnd] -- end offset (exclusive)
//
// Requires: cbEnd > cbStart
//
// Algorithm: fill in hGlobal from cbStart (inclusive) to cbEnd (exclusive)
//
// History: 28-Feb-94 AlexT Created
//
// Notes:
//
//--------------------------------------------------------------------------
void CGlobalTrack::InitializeRegion(HGLOBAL hGlobal, SIZE_T cbStart, SIZE_T cbEnd) { BYTE *pbStart; BYTE *pb;
Assert(cbStart < cbEnd && "illogical parameters"); Assert(cbEnd <= GlobalSize(hGlobal) && "global memory too small");
// GlobalLock on GMEM_FIXED memory is a nop, so this is a safe call
pbStart = (BYTE *) GlobalLock(hGlobal);
if (NULL == pbStart) { // Shouldn't have failed - (we allocated > 0 bytes)
LEDebugOut((DEB_WARN, "GlobalLock failed - %lx\n", GetLastError())); return; }
// Initialize the tail portion of the memory
for (pb = pbStart + cbStart; pb < pbStart + cbEnd; pb++) { *pb = OLEMEM_ALLOCBYTE; }
GlobalUnlock(hGlobal); }
//+-------------------------------------------------------------------------
//
// Member: CGlobalTrack::Track, private
//
// Synopsis:
//
// Effects:
//
// Arguments: [hGlobal] -- global memory handle
// [cbUser] -- user allocation size
//
// Requires:
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// History: 28-Feb-94 AlexT Created
//
// Notes:
//
//--------------------------------------------------------------------------
void CGlobalTrack::Track(HGLOBAL hGlobal, SIZE_T cbUser) { COleStaticLock lck(_mxsGlobalMemory); PSGLOBALALLOCINFO pgi;
if (cgtStopTracking(hGlobal)) { // If it's already in our list, it's possible that someone else
// freed the HGLOBAL without telling us - remove our stale one
LEDebugOut((DEB_WARN, "CGT::Track - %lx was already in list!\n", hGlobal)); }
pgi = (PSGLOBALALLOCINFO) PrivMemAlloc(sizeof(SGLOBALALLOCINFO)); if (NULL == pgi) { LEDebugOut((DEB_WARN, "CGT::Insert - PrivMemAlloc failed\n"));
// Okay fine - we just won't track this one
return; }
pgi->hGlobal = hGlobal; pgi->cbGlobalSize = GlobalSize(hGlobal); Assert((0 == cbUser || pgi->cbGlobalSize > 0) && "GlobalSize failed - bad handle?"); pgi->cbUser = cbUser; pgi->ulIndex = ++_ulIndex; pgi->pNext = _pRoot; _pRoot = pgi; }
//+-------------------------------------------------------------------------
//
// Member: CGlobalTrack::Retrack, private
//
// Synopsis:
//
// Effects:
//
// Arguments: [hOld] -- previous handle
// [hNew] -- new handle
//
// Modifies:
//
// Algorithm:
//
// History: 28-Feb-94 AlexT Created
//
// Notes:
//
//--------------------------------------------------------------------------
void CGlobalTrack::Retrack(HGLOBAL hOld, HGLOBAL hNew) { COleStaticLock lck(_mxsGlobalMemory); PSGLOBALALLOCINFO pgi;
if (hOld != hNew && cgtStopTracking(hNew)) { // If hNew was already in the list, it's possible that someone else
// freed the HGLOBAL without telling us so we removed the stale one
LEDebugOut((DEB_WARN, "CGT::Retrack - %lx was already in list!\n", hNew)); }
for (pgi = _pRoot; NULL != pgi; pgi = pgi->pNext) { if (pgi->hGlobal == hOld) { pgi->hGlobal = hNew; break; } }
if (NULL == pgi) { // We didn't find hOld
LEDebugOut((DEB_WARN, "CGT::Retrack - hOld (%lx) not found\n", hOld)); } }
//+-------------------------------------------------------------------------
//
// Member: CGlobalTrack::cgtStopTracking, public
//
// Synopsis:
//
// Effects:
//
// Arguments: [hGlobal] -- global handle
//
// Modifies:
//
// Algorithm:
//
// History: 28-Feb-94 AlexT Created
//
// Notes:
//
//--------------------------------------------------------------------------
BOOL CGlobalTrack::cgtStopTracking(HGLOBAL hGlobal) { COleStaticLock lck(_mxsGlobalMemory); PSGLOBALALLOCINFO *ppgi = &_pRoot; PSGLOBALALLOCINFO pgi;
while (*ppgi != NULL && (*ppgi)->hGlobal != hGlobal) { ppgi = &((*ppgi)->pNext); }
if (NULL == *ppgi) { return(FALSE); }
pgi = *ppgi; Assert(pgi->hGlobal == hGlobal && "CGT::cgtStopTracking search problem");
*ppgi = pgi->pNext;
PrivMemFree(pgi); return(TRUE); }
//+-------------------------------------------------------------------------
//
// Member: CGlobalTrack::VerifyHandle, private
//
// Synopsis: Verify global handle
//
// Arguments: [hGlobal] -- global memory handle
//
// Signals: Asserts if bad
//
// Algorithm:
//
// History: 28-Feb-94 AlexT Created
// 22-Jun-94 AlexT Allow for handle to have been freed and
// reallocated under us
//
//--------------------------------------------------------------------------
void CGlobalTrack::VerifyHandle(HGLOBAL hGlobal) { COleStaticLock lck(_mxsGlobalMemory); PSGLOBALALLOCINFO pgi, pgiNext; SIZE_T cbAlloc; BYTE *pbStart; BYTE *pb;
// Note that we use a while loop (recording pgiNext up front) instead
// of a for loop because pgi will get removed from the list if we call
// cgtStopTracking on it
pgi = _pRoot; while (NULL != pgi) { pgiNext = pgi->pNext;
if (NULL == hGlobal || pgi->hGlobal == hGlobal) { if (pgi->cbGlobalSize != GlobalSize(pgi->hGlobal)) { // pgi->hGlobal's size has changed since we started tracking
// it; it must have been freed or reallocated by someone
// else. Stop tracking it.
// This call will remove pgi from the list (so we NULL it to
// make sure we don't try reusing it)!
cgtStopTracking(pgi->hGlobal); pgi = NULL; } else { cbAlloc = CalculateAllocSize(pgi->cbUser);
pbStart = (BYTE *) GlobalLock(pgi->hGlobal);
// it is legitimate to have a zero length (NULL memory) handle
if (NULL == pbStart) { LEDebugOut((DEB_WARN, "GlobalLock failed - %lx\n", GetLastError())); } else { for (pb = pbStart + pgi->cbUser; pb < pbStart + cbAlloc; pb++) { if (*pb != OLEMEM_ALLOCBYTE) break; }
if (pb < pbStart + cbAlloc) { // In general an application may have freed and reallocated
// any HGLOBAL, so we can only warn about corruption.
LEDebugOut((DEB_WARN, "HGLOBAL #%ld may be corrupt\n", pgi->ulIndex)); #ifdef GLOBALDBG
// If GLOBALDBG is true, then all allocations should be
// coming through these routines. In this case we assert
// if we've found corruption.
Assert(0 && "CGlobalTrack::VerifyHandle - HGLOBAL corrupt"); #endif
}
GlobalUnlock(pgi->hGlobal); } } }
pgi = pgiNext; } }
//+-------------------------------------------------------------------------
//
// Function: UtGlobalAlloc, ReAlloc, Free, Lock, Unlock
//
// Synopsis: Debug versions of Global memory routines
//
// Arguments: Same as Windows APIs
//
// History: 28-Feb-94 AlexT Created
//
// Notes: These entry points just call the worker routines
//
//--------------------------------------------------------------------------
extern "C" HGLOBAL WINAPI UtGlobalAlloc(UINT uiFlag, SIZE_T cbUser) { return gGlobalTrack.cgtGlobalAlloc(uiFlag, cbUser); }
extern "C" HGLOBAL WINAPI UtGlobalReAlloc(HGLOBAL hGlobal, SIZE_T cbUser, UINT uiFlag) { return gGlobalTrack.cgtGlobalReAlloc(hGlobal, cbUser, uiFlag); }
extern "C" LPVOID WINAPI UtGlobalLock(HGLOBAL hGlobal) { return gGlobalTrack.cgtGlobalLock(hGlobal); }
extern "C" BOOL WINAPI UtGlobalUnlock(HGLOBAL hGlobal) { return gGlobalTrack.cgtGlobalUnlock(hGlobal); }
extern "C" HGLOBAL WINAPI UtGlobalFree(HGLOBAL hGlobal) { return gGlobalTrack.cgtGlobalFree(hGlobal); }
extern "C" void UtGlobalFlushTracking(void) { gGlobalTrack.cgtFlushTracking(); }
//+-------------------------------------------------------------------------
//
// Function: UtSetClipboardData
//
// Synopsis: Calls Windows SetClipboardData and stops tracking the handle
//
// Arguments: [uFormat] -- clipboard format
// [hMem] -- data handle
//
// Returns: Same as SetClipboard
//
// Algorithm: If SetClipboardData succeeds, stop tracking the handle
//
// History: 30-Mar-94 AlexT Created
//
// Notes:
//
//--------------------------------------------------------------------------
extern "C" HANDLE WINAPI UtSetClipboardData(UINT uFormat, HANDLE hMem) { HANDLE hRet;
hRet = SetClipboardData(uFormat, hMem);
if (NULL != hRet) { gGlobalTrack.cgtStopTracking(hMem); }
return(hRet); }
#endif // DBG==1 && defined(WIN32)
|