mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
534 lines
11 KiB
534 lines
11 KiB
#include "ctlspriv.h"
|
|
|
|
#ifdef DEBUG
|
|
|
|
#if defined(WIN32) && !defined(WINNT)
|
|
#define DEBUG_BREAK _try { _asm { int 3 } } _except (EXCEPTION_EXECUTE_HANDLER) {;}
|
|
#else
|
|
#define DEBUG_BREAK DebugBreak();
|
|
#endif
|
|
|
|
//========== Debug output routines =========================================
|
|
|
|
#ifdef DEBUG
|
|
UINT wDebugMask = 0; // this gets initialized in commctrl.c, _ProcessAttach()
|
|
#endif
|
|
|
|
UINT WINAPI SetDebugMask(UINT mask)
|
|
{
|
|
#ifdef DEBUG
|
|
UINT wOld = wDebugMask;
|
|
wDebugMask = mask;
|
|
|
|
return wOld;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
UINT WINAPI GetDebugMask()
|
|
{
|
|
#ifdef DEBUG
|
|
return wDebugMask;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
void WINAPI AssertFailed(LPCTSTR pszFile, int line)
|
|
{
|
|
#ifdef DEBUG
|
|
LPCTSTR psz;
|
|
TCHAR ach[256];
|
|
static TCHAR szAssertFailed[] = TEXT("Assertion failed in %s on line %d\r\n");
|
|
|
|
// Strip off path info from filename string, if present.
|
|
//
|
|
if (wDebugMask & DM_ASSERT)
|
|
{
|
|
for (psz = pszFile + lstrlen(pszFile); psz != pszFile; psz=AnsiPrev(pszFile, psz))
|
|
{
|
|
if ((AnsiPrev(pszFile, psz)!= (psz-2)) && *(psz - 1) == TEXT('\\'))
|
|
break;
|
|
}
|
|
wsprintf(ach, szAssertFailed, psz, line);
|
|
OutputDebugString(ach);
|
|
|
|
DEBUG_BREAK
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void WINCAPI _AssertMsg(BOOL f, LPCTSTR pszMsg, ...)
|
|
{
|
|
#ifdef DEBUG
|
|
TCHAR ach[256];
|
|
va_list marker;
|
|
|
|
va_start( marker, pszMsg );
|
|
|
|
if (!f && (wDebugMask & DM_ASSERT))
|
|
{
|
|
wvsprintf(ach, pszMsg, marker);
|
|
OutputDebugString(ach);
|
|
OutputDebugString((LPCTSTR)TEXT("\r\n"));
|
|
DEBUG_BREAK
|
|
}
|
|
va_end(marker);
|
|
#endif
|
|
}
|
|
|
|
void WINCAPI _DebugMsg(UINT mask, LPCTSTR pszMsg, ...)
|
|
{
|
|
#ifdef DEBUG
|
|
#ifdef WIN32
|
|
TCHAR ach[2*MAX_PATH+40]; // Handles 2*largest path + slop for message
|
|
#else // WIN32
|
|
TCHAR ach[MAX_PATH+40]; // Handles largest path + slop for message
|
|
#endif // WIN32
|
|
va_list marker;
|
|
|
|
va_start( marker, pszMsg );
|
|
|
|
if (wDebugMask & mask)
|
|
{
|
|
wvsprintf(ach, pszMsg, marker);
|
|
OutputDebugString((LPCTSTR)TEXT("COMCTL32: "));
|
|
OutputDebugString(ach);
|
|
OutputDebugString((LPCTSTR)TEXT("\r\n"));
|
|
}
|
|
va_end(marker);
|
|
#endif
|
|
}
|
|
|
|
#endif //DEBUG
|
|
|
|
//========== Memory Management =============================================
|
|
|
|
#ifndef WIN32
|
|
|
|
#define MAX_WORD 0xffff
|
|
|
|
DECLARE_HANDLE(HHEAP);
|
|
|
|
typedef struct { // maps to the bottom of a 16bit DS
|
|
WORD reserved[8];
|
|
WORD cAlloc;
|
|
WORD cbAllocFailed;
|
|
HHEAP hhpFirst;
|
|
HHEAP hhpNext;
|
|
} HEAP;
|
|
|
|
#define PHEAP(hhp) ((HEAP FAR*)MAKELP(hhp, 0))
|
|
#define MAKEHP(sel, off) ((void _huge*)MAKELP((sel), (off)))
|
|
|
|
#define CBSUBALLOCMAX 0x0000f000L
|
|
|
|
HHEAP g_hhpFirst = NULL;
|
|
|
|
BOOL NEAR DestroyHeap(HHEAP hhp);
|
|
|
|
void Mem_Terminate()
|
|
{
|
|
while (g_hhpFirst)
|
|
DestroyHeap(g_hhpFirst);
|
|
}
|
|
|
|
BOOL NEAR CreateHeap(WORD cbInitial)
|
|
{
|
|
HHEAP hhp;
|
|
|
|
if (cbInitial < 1024)
|
|
cbInitial = 1024;
|
|
|
|
hhp = (HHEAP)GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT | GMEM_SHARE, cbInitial);
|
|
|
|
if (!hhp)
|
|
return FALSE;
|
|
|
|
if (!LocalInit((WORD)hhp, sizeof(HEAP), cbInitial - 1))
|
|
{
|
|
GlobalFree(hhp);
|
|
return FALSE;
|
|
}
|
|
|
|
PHEAP(hhp)->cAlloc = 0;
|
|
PHEAP(hhp)->cbAllocFailed = MAX_WORD;
|
|
PHEAP(hhp)->hhpNext = g_hhpFirst;
|
|
g_hhpFirst = hhp;
|
|
|
|
DebugMsg(DM_TRACE, TEXT("CreateHeap: added new local heap %x"), hhp);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#pragma optimize("o", off) // linked list removals don't optimize correctly
|
|
BOOL NEAR DestroyHeap(HHEAP hhp)
|
|
{
|
|
Assert(hhp);
|
|
Assert(g_hhpFirst);
|
|
|
|
if (g_hhpFirst == hhp)
|
|
{
|
|
g_hhpFirst = PHEAP(hhp)->hhpNext;
|
|
}
|
|
else
|
|
{
|
|
HHEAP hhpT = g_hhpFirst;
|
|
|
|
while (PHEAP(hhpT)->hhpNext != hhp)
|
|
{
|
|
hhpT = PHEAP(hhpT)->hhpNext;
|
|
if (!hhpT)
|
|
return FALSE;
|
|
}
|
|
|
|
PHEAP(hhpT)->hhpNext = PHEAP(hhp)->hhpNext;
|
|
}
|
|
if (GlobalFree((HGLOBAL)hhp) != NULL)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
#pragma optimize("", on) // back to default optimizations
|
|
|
|
#pragma optimize("lge", off) // Suppress warnings associated with use of _asm...
|
|
void NEAR* NEAR HeapAlloc(HHEAP hhp, WORD cb)
|
|
{
|
|
void NEAR* pb;
|
|
|
|
_asm {
|
|
push ds
|
|
mov ds,hhp
|
|
}
|
|
|
|
pb = (void NEAR*)LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, cb);
|
|
|
|
if (pb)
|
|
((HEAP NEAR*)0)->cAlloc++;
|
|
|
|
_asm {
|
|
pop ds
|
|
}
|
|
|
|
return pb;
|
|
}
|
|
#pragma optimize("o", off) // linked list removals don't optimize correctly
|
|
|
|
void _huge* WINAPI Alloc(long cb)
|
|
{
|
|
void NEAR* pb;
|
|
HHEAP hhp;
|
|
HHEAP hhpPrev;
|
|
|
|
// If this is a big allocation, just do a global alloc.
|
|
//
|
|
if (cb > CBSUBALLOCMAX)
|
|
{
|
|
void FAR* lpb = MAKEHP(GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT | GMEM_SHARE, cb), 0);
|
|
if (!lpb)
|
|
DebugMsg(DM_ERROR, TEXT("Alloc: out of memory"));
|
|
return lpb;
|
|
}
|
|
|
|
hhp = g_hhpFirst;
|
|
|
|
while (TRUE)
|
|
{
|
|
if (hhp == NULL)
|
|
{
|
|
if (!CreateHeap(0))
|
|
{
|
|
DebugMsg(DM_ERROR, TEXT("Alloc: out of memory"));
|
|
return NULL;
|
|
}
|
|
|
|
hhp = g_hhpFirst;
|
|
}
|
|
|
|
pb = HeapAlloc(hhp, (WORD)cb);
|
|
if (pb)
|
|
return MAKEHP(hhp, pb);
|
|
|
|
// Record the size of the allocation that failed.
|
|
// Later attempts to allocate more than this amount
|
|
// will not succeed. This gets reset anytime anything
|
|
// is freed in the heap.
|
|
//
|
|
PHEAP(hhp)->cbAllocFailed = (WORD)cb;
|
|
|
|
// First heap is full... see if there's room in any other heap...
|
|
//
|
|
for (hhpPrev = hhp; hhp = PHEAP(hhp)->hhpNext; hhpPrev = hhp)
|
|
{
|
|
// If the last allocation to fail in this heap
|
|
// is not larger than cb, don't even try an allocation.
|
|
//
|
|
if ((WORD)cb >= PHEAP(hhp)->cbAllocFailed)
|
|
continue;
|
|
|
|
pb = HeapAlloc(hhp, (WORD)cb);
|
|
if (pb)
|
|
{
|
|
// This heap had room: move it to the front...
|
|
//
|
|
PHEAP(hhpPrev)->hhpNext = PHEAP(hhp)->hhpNext;
|
|
PHEAP(hhp)->hhpNext = g_hhpFirst;
|
|
g_hhpFirst = hhp;
|
|
|
|
return MAKEHP(hhp, pb);
|
|
}
|
|
else
|
|
{
|
|
// The alloc failed. Set cbAllocFailed...
|
|
//
|
|
PHEAP(hhp)->cbAllocFailed = (WORD)cb;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#pragma optimize("", on) // back to default optimizations
|
|
|
|
#pragma optimize("lge", off) // Suppress warnings associated with use of _asm...
|
|
|
|
void _huge* WINAPI ReAlloc(void _huge* pb, long cb)
|
|
{
|
|
void NEAR* pbNew;
|
|
void _huge* lpbNew;
|
|
UINT cbOld;
|
|
|
|
// BUGBUG, does not work with cb > 64k
|
|
if (!pb)
|
|
return Alloc(cb);
|
|
|
|
if (OFFSETOF(pb) == 0)
|
|
return MAKEHP(GlobalReAlloc((HGLOBAL)SELECTOROF(pb), cb, GMEM_MOVEABLE | GMEM_ZEROINIT), 0);
|
|
|
|
_asm {
|
|
push ds
|
|
mov ds,word ptr [pb+2]
|
|
}
|
|
|
|
pbNew = (void NEAR*)LocalReAlloc((HLOCAL)OFFSETOF(pb), (int)cb, LMEM_MOVEABLE | LMEM_ZEROINIT);
|
|
if (!pbNew)
|
|
cbOld = LocalSize((HLOCAL)OFFSETOF(pb));
|
|
|
|
_asm {
|
|
pop ds
|
|
}
|
|
|
|
if (pbNew)
|
|
return MAKEHP(SELECTOROF(pb), pbNew);
|
|
|
|
lpbNew = Alloc(cb);
|
|
if (lpbNew)
|
|
{
|
|
hmemcpy((void FAR*)lpbNew, (void FAR*)pb, cbOld);
|
|
Free(pb);
|
|
}
|
|
else
|
|
{
|
|
DebugMsg(DM_ERROR, TEXT("ReAlloc: out of memory"));
|
|
}
|
|
return lpbNew;
|
|
}
|
|
|
|
BOOL WINAPI Free(void _huge* pb)
|
|
{
|
|
BOOL fSuccess;
|
|
UINT cAlloc;
|
|
|
|
if (OFFSETOF(pb) == 0)
|
|
return (GlobalFree((HGLOBAL)SELECTOROF(pb)) == NULL);
|
|
|
|
_asm {
|
|
push ds
|
|
mov ds,word ptr [pb+2]
|
|
}
|
|
|
|
fSuccess = (LocalFree((HLOCAL)OFFSETOF(pb)) ? FALSE : TRUE);
|
|
|
|
cAlloc = 1;
|
|
if (fSuccess)
|
|
{
|
|
cAlloc = --((HEAP NEAR*)0)->cAlloc;
|
|
((HEAP NEAR*)0)->cbAllocFailed = MAX_WORD;
|
|
}
|
|
|
|
_asm {
|
|
pop ds
|
|
}
|
|
|
|
if (cAlloc == 0)
|
|
DestroyHeap((HHEAP)SELECTOROF(pb));
|
|
|
|
return fSuccess;
|
|
}
|
|
|
|
DWORD WINAPI GetSize(void _huge* pb)
|
|
{
|
|
WORD wSize;
|
|
|
|
if (OFFSETOF(pb) == 0)
|
|
return GlobalSize((HGLOBAL)SELECTOROF(pb));
|
|
|
|
_asm {
|
|
push ds
|
|
mov ds,word ptr [pb+2]
|
|
}
|
|
|
|
wSize = LocalSize((HLOCAL)OFFSETOF(pb));
|
|
|
|
_asm {
|
|
pop ds
|
|
}
|
|
|
|
return (DWORD)wSize;
|
|
}
|
|
|
|
|
|
#pragma optimize("", on)
|
|
|
|
#else // WIN32
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Define a Global Shared Heap that we use allocate memory out of that we
|
|
// Need to share between multiple instances.
|
|
HANDLE g_hSharedHeap = NULL;
|
|
#define GROWABLE 0
|
|
#define MAXHEAPSIZE GROWABLE //(2097152 * 2)
|
|
#define HEAP_SHARED 0x04000000 /* put heap in shared memory */
|
|
|
|
void Mem_Terminate()
|
|
{
|
|
// Assuming that everything else has exited
|
|
//
|
|
if (g_hSharedHeap != NULL)
|
|
HeapDestroy(g_hSharedHeap);
|
|
g_hSharedHeap = NULL;
|
|
}
|
|
|
|
void * WINAPI Alloc(long cb)
|
|
{
|
|
// I will assume that this is the only one that needs the checks to
|
|
// see if the heap has been previously created or not
|
|
|
|
if (g_hSharedHeap == NULL)
|
|
{
|
|
ENTERCRITICAL
|
|
if (g_hSharedHeap == NULL)
|
|
{
|
|
g_hSharedHeap = HeapCreate(HEAP_SHARED, 1, MAXHEAPSIZE);
|
|
}
|
|
LEAVECRITICAL
|
|
|
|
// If still NULL we have problems!
|
|
if (g_hSharedHeap == NULL)
|
|
return(NULL);
|
|
}
|
|
|
|
return HeapAlloc(g_hSharedHeap, HEAP_ZERO_MEMORY, cb);
|
|
}
|
|
|
|
void * WINAPI ReAlloc(void * pb, long cb)
|
|
{
|
|
if (pb == NULL)
|
|
return Alloc(cb);
|
|
|
|
return HeapReAlloc(g_hSharedHeap, HEAP_ZERO_MEMORY, pb, cb);
|
|
}
|
|
|
|
BOOL WINAPI Free(void * pb)
|
|
{
|
|
return HeapFree(g_hSharedHeap, 0, pb);
|
|
}
|
|
|
|
DWORD WINAPI GetSize(void * pb)
|
|
{
|
|
return HeapSize(g_hSharedHeap, 0, pb);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// The following functions are for debug only and are used to try to
|
|
// calculate memory usage.
|
|
//
|
|
#ifdef DEBUG
|
|
typedef struct _HEAPTRACE
|
|
{
|
|
DWORD cAlloc;
|
|
DWORD cFailure;
|
|
DWORD cReAlloc;
|
|
DWORD cbMaxTotal;
|
|
DWORD cCurAlloc;
|
|
DWORD cbCurTotal;
|
|
} HEAPTRACE;
|
|
|
|
HEAPTRACE g_htShell = {0}; // Start of zero...
|
|
|
|
LPVOID WINAPI ControlAlloc(HANDLE hheap, DWORD cb)
|
|
{
|
|
LPVOID lp = HeapAlloc(hheap, HEAP_ZERO_MEMORY, cb);;
|
|
if (lp == NULL)
|
|
{
|
|
g_htShell.cFailure++;
|
|
return NULL;
|
|
}
|
|
|
|
// Update counts.
|
|
g_htShell.cAlloc++;
|
|
g_htShell.cCurAlloc++;
|
|
g_htShell.cbCurTotal += cb;
|
|
if (g_htShell.cbCurTotal > g_htShell.cbMaxTotal)
|
|
g_htShell.cbMaxTotal = g_htShell.cbCurTotal;
|
|
|
|
return lp;
|
|
}
|
|
|
|
LPVOID WINAPI ControlReAlloc(HANDLE hheap, LPVOID pb, DWORD cb)
|
|
{
|
|
LPVOID lp;
|
|
DWORD cbOld;
|
|
|
|
cbOld = HeapSize(hheap, 0, pb);
|
|
|
|
lp = HeapReAlloc(hheap, HEAP_ZERO_MEMORY, pb,cb);
|
|
if (lp == NULL)
|
|
{
|
|
g_htShell.cFailure++;
|
|
return NULL;
|
|
}
|
|
|
|
// Update counts.
|
|
g_htShell.cReAlloc++;
|
|
g_htShell.cbCurTotal += cb - cbOld;
|
|
if (g_htShell.cbCurTotal > g_htShell.cbMaxTotal)
|
|
g_htShell.cbMaxTotal = g_htShell.cbCurTotal;
|
|
|
|
return lp;
|
|
}
|
|
|
|
BOOL WINAPI ControlFree(HANDLE hheap, LPVOID pb)
|
|
{
|
|
DWORD cbOld = HeapSize(hheap, 0, pb);
|
|
BOOL fRet = HeapFree(hheap, 0, pb);
|
|
if (fRet)
|
|
{
|
|
// Update counts.
|
|
g_htShell.cCurAlloc--;
|
|
g_htShell.cbCurTotal -= cbOld;
|
|
}
|
|
|
|
return(fRet);
|
|
}
|
|
|
|
DWORD WINAPI ControlSize(HANDLE hheap, LPVOID pb)
|
|
{
|
|
return HeapSize(hheap, 0, pb);
|
|
}
|
|
#endif // DEBUG
|
|
|
|
#endif // WIN32
|
|
|
|
#if defined(FULL_DEBUG) && defined(WIN32)
|
|
#include "..\inc\deballoc.c"
|
|
#endif // defined(FULL_DEBUG) && defined(WIN32)
|