text: //+----------------------------------------------------------------------------
  //
  //	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)
  