/****************************** Module Header ******************************\ * Module Name: DMGMEM.C * * DDE Manager memory management functions. * * Created: 5/31/90 Rich Gartland * * This module contains routines which mimic memory management functions * used by the OS/2 version of the DDEMGR library. Some are emulations * of OS/2 calls, and others emulate DDEMGR macros built on OS/2 calls. * Old function new function * -------------------------------------- * WinCreateHeap DmgCreateHeap * WinDestroyHeap DmgDestroyHeap * FarAllocMem FarAllocMem * FarFreeMem FarFreeMem * * Copyright (c) 1990, Aldus Corporation \***************************************************************************/ #include "ddemlp.h" #include #ifdef DEBUG #define GML_FREE 1 #define GML_ALLOC 2 #define MAX_CLOGS 500 #define STKTRACE_LEN 3 typedef struct _GMLOG { HGLOBAL h; WORD flags; // GML_ WORD msg; WORD cLocks; WORD stktrace[STKTRACE_LEN]; WORD stktracePrev[STKTRACE_LEN]; } GMLOG, far * LPGMLOG; GMLOG gmlog[MAX_CLOGS]; WORD cGmLogs = 0; int TraceApiLevel = 0; VOID TraceApiIn( LPSTR psz) { char szT[10]; wsprintf(szT, "%2d | ", TraceApiLevel); TraceApiLevel++; OutputDebugString(szT); OutputDebugString(psz); if (bDbgFlags & DBF_STOPONTRACE) { DebugBreak(); } } VOID TraceApiOut( LPSTR psz) { char szT[10]; TraceApiLevel--; wsprintf(szT, "%2d | ", TraceApiLevel); OutputDebugString(szT); OutputDebugString(psz); if (bDbgFlags & DBF_STOPONTRACE) { DebugBreak(); } } VOID DumpGmObject( LPGMLOG pgmlog) { char szT[100]; wsprintf(szT, "\n\rh=%4x flags=%4x msg=%4x stacks:\n\r%04x %04x %04x %04x %04x\n\r%04x %04x %04x %04x %04x", pgmlog->h, pgmlog->flags, pgmlog->msg, pgmlog->stktrace[0], pgmlog->stktrace[1], pgmlog->stktrace[2], pgmlog->stktrace[3], pgmlog->stktrace[4], pgmlog->stktracePrev[0], pgmlog->stktracePrev[1], pgmlog->stktracePrev[2], pgmlog->stktracePrev[3], pgmlog->stktracePrev[4] ); OutputDebugString(szT); } HGLOBAL LogGlobalReAlloc( HGLOBAL h, DWORD cb, UINT flags) { HGLOBAL hRet; WORD i; hRet = GlobalReAlloc(h, cb, flags); if (bDbgFlags & DBF_LOGALLOCS && h != hRet) { if (hRet != NULL) { for (i = 0; i < cGmLogs; i++) { if ((gmlog[i].h & 0xFFFE) == (h & 0xFFFE)) { gmlog[i].flags = GML_FREE; hmemcpy(gmlog[i].stktracePrev, gmlog[i].stktrace, sizeof(WORD) * STKTRACE_LEN); StkTrace(STKTRACE_LEN, gmlog[i].stktrace); } if ((gmlog[i].h & 0xFFFE) == (hRet & 0xFFFE)) { gmlog[i].flags = GML_ALLOC; hmemcpy(gmlog[i].stktracePrev, gmlog[i].stktrace, sizeof(WORD) * STKTRACE_LEN); StkTrace(STKTRACE_LEN, gmlog[i].stktrace); return(hRet); } } if (cGmLogs >= MAX_CLOGS) { OutputDebugString("\n\rGlobal logging table overflow."); DumpGlobalLogs(); DebugBreak(); return(hRet); } gmlog[cGmLogs].flags = GML_ALLOC; gmlog[cGmLogs].msg = 0; gmlog[cGmLogs].h = hRet; gmlog[cGmLogs].cLocks = 0; hmemcpy(gmlog[cGmLogs].stktracePrev, gmlog[cGmLogs].stktrace, sizeof(WORD) * STKTRACE_LEN); StkTrace(STKTRACE_LEN, gmlog[cGmLogs].stktrace); cGmLogs++; } } return(hRet); } HGLOBAL LogGlobalAlloc( UINT flags, DWORD cb) { HGLOBAL hRet; WORD i; hRet = GlobalAlloc(flags, cb); if (bDbgFlags & DBF_LOGALLOCS) { if (hRet != NULL) { for (i = 0; i < cGmLogs; i++) { if ((gmlog[i].h & 0xFFFE) == (hRet & 0xFFFE)) { gmlog[i].flags = GML_ALLOC; hmemcpy(gmlog[i].stktracePrev, gmlog[i].stktrace, sizeof(WORD) * STKTRACE_LEN); StkTrace(STKTRACE_LEN, gmlog[i].stktrace); return(hRet); } } if (cGmLogs >= MAX_CLOGS) { OutputDebugString("\n\rGlobal logging table overflow."); DumpGlobalLogs(); DebugBreak(); return(hRet); } gmlog[cGmLogs].flags = GML_ALLOC; gmlog[cGmLogs].msg = 0; gmlog[cGmLogs].h = hRet; gmlog[cGmLogs].cLocks = 0; hmemcpy(gmlog[cGmLogs].stktracePrev, gmlog[cGmLogs].stktrace, sizeof(WORD) * STKTRACE_LEN); StkTrace(STKTRACE_LEN, gmlog[cGmLogs].stktrace); cGmLogs++; } } return(hRet); } void FAR * LogGlobalLock( HGLOBAL h) { WORD i; if (bDbgFlags & DBF_LOGALLOCS) { for (i = 0; i < cGmLogs; i++) { if ((gmlog[i].h & 0xFFFE) == (h & 0xFFFE)) { break; } } if (i < cGmLogs) { gmlog[i].cLocks++; if (gmlog[i].flags == GML_FREE) { DumpGmObject(&gmlog[i]); OutputDebugString("\n\rGlobalLock will fail."); DebugBreak(); } } } return(GlobalLock(h)); } BOOL LogGlobalUnlock( HGLOBAL h) { WORD i; if (bDbgFlags & DBF_LOGALLOCS) { for (i = 0; i < cGmLogs; i++) { if ((gmlog[i].h & 0xFFFE) == (h & 0xFFFE)) { break; } } if (i < cGmLogs) { if (gmlog[i].cLocks == 0 || gmlog[i].flags == GML_FREE) { DumpGmObject(&gmlog[i]); OutputDebugString("\n\rGlobalUnlock will fail."); DebugBreak(); } gmlog[i].cLocks--; } } return(GlobalUnlock(h)); } HGLOBAL LogGlobalFree( HGLOBAL h) { WORD i; if (bDbgFlags & DBF_LOGALLOCS) { for (i = 0; i < cGmLogs; i++) { if ((gmlog[i].h & 0xFFFE) == (h & 0xFFFE)) { if (gmlog[i].flags == GML_FREE) { DumpGmObject(&gmlog[i]); OutputDebugString("\n\rFreeing free object.\n\r"); DebugBreak(); } gmlog[i].flags = GML_FREE; hmemcpy(gmlog[i].stktracePrev, gmlog[i].stktrace, sizeof(WORD) * STKTRACE_LEN); StkTrace(STKTRACE_LEN, gmlog[i].stktrace); return(GlobalFree(h)); } } OutputDebugString("\n\rGlobal object being freed not found in logs."); DebugBreak(); } return(GlobalFree(h)); } VOID LogDdeObject( UINT msg, LONG lParam) { HGLOBAL h; WORD i; char szT[100]; if (bDbgFlags & DBF_LOGALLOCS) { switch (msg & 0x0FFF) { case WM_DDE_DATA: case WM_DDE_POKE: case WM_DDE_ADVISE: case 0: h = LOWORD(lParam); break; case WM_DDE_EXECUTE: h = HIWORD(lParam); break; default: return; } if (h == 0) { return; } for (i = 0; i < cGmLogs; i++) { if ((gmlog[i].h & 0xFFFE) == (h & 0xFFFE)) { if (gmlog[i].flags == GML_FREE) { DumpGmObject(&gmlog[i]); wsprintf(szT, "\n\rLogging free DDE Object! [%4x]\n\r", msg); OutputDebugString(szT); DebugBreak(); } if (msg & 0xFFF) { gmlog[i].msg = msg; } else { gmlog[i].msg = (gmlog[i].msg & 0x0FFF) | msg; } break; } } } } VOID DumpGlobalLogs() { WORD i; char szT[100]; if (bDbgFlags & DBF_LOGALLOCS) { wsprintf(szT, "\n\rDumpGlobalLogs - cGmLogs = %d", cGmLogs); OutputDebugString(szT); for (i = 0; i < cGmLogs; i++) { if (gmlog[i].flags == GML_ALLOC) { DumpGmObject(&gmlog[i]); } } wsprintf(szT, "\n\rDDEML CS=%04x\n\r", HIWORD((LPVOID)DumpGlobalLogs)); OutputDebugString(szT); } } #endif // DEBUG /***************************** Private Function ****************************\ * * Creates a new heap and returns a handle to it. * Returns NULL on error. * * * History: * Created 5/31/90 Rich Gartland \***************************************************************************/ HANDLE DmgCreateHeap(wSize) WORD wSize; { HANDLE hMem; DWORD dwSize; dwSize = wSize; /* Allocate the memory from a global data segment */ if (!(hMem = GLOBALALLOC(GMEM_MOVEABLE, dwSize))) return(NULL); /* use LocalInit to establish heap mgmt structures in the seg */ if (!LocalInit(hMem, NULL, wSize - 1)) { GLOBALFREE(hMem); return(NULL); } return(hMem); } /***************************** Private Function ****************************\ * * Destroys a heap previously created with DmgCreateHeap. * Returns nonzero on error. * * * History: * Created 5/31/90 Rich Gartland \***************************************************************************/ HANDLE DmgDestroyHeap(hheap) HANDLE hheap; { /* now free the block and return the result (NULL if success) */ return(GLOBALFREE(hheap)); } /* * Attempts to recover from memory allocation errors. * * Returns fRetry - ok to attempt reallocation. */ BOOL ProcessMemError( HANDLE hheap) { PAPPINFO pai; // first locate what instance this heap is assocaited with SEMENTER(); pai = pAppInfoList; while (pai && pai->hheapApp != hheap) { pai = pai->next; } if (!pai) { SEMLEAVE(); return(FALSE); // not associated with an instance, no recourse. } /* * Free our emergency reserve memory and post a message to our master * window to handle heap cleanup. */ if (pai->lpMemReserve) { FarFreeMem(pai->lpMemReserve); pai->lpMemReserve = NULL; MONERROR(pai, DMLERR_LOW_MEMORY); DoCallback(pai, NULL, 0, 0, 0, XTYP_ERROR, NULL, DMLERR_LOW_MEMORY, 0L); SEMLEAVE(); if (!PostMessage(pai->hwndDmg, UM_FIXHEAP, 0, (LONG)(LPSTR)pai)) { SETLASTERROR(pai, DMLERR_SYS_ERROR); return(FALSE); } return(TRUE); } return(FALSE); // no reserve memory, were dead bud. } /***************************** Private Function ****************************\ * * Allocates a new block of a given size from a heap. * Returns NULL on error, far pointer to the block otherwise. * * * History: * Created 5/31/90 Rich Gartland \***************************************************************************/ LPVOID FarAllocMem(hheap, wSize) HANDLE hheap; WORD wSize; { LPSTR lpMem; PSTR pMem; WORD wSaveDS; /* lock the handle to get a far pointer */ lpMem = (LPSTR)GLOBALPTR(hheap); if (!lpMem) return(NULL); do { /* Do some magic here using the segment selector, to switch our * ds to the heap's segment. Then, our LocalAlloc will work fine. */ wSaveDS = SwitchDS(HIWORD(lpMem)); /* Allocate the block */ // Note: if you remove the LMEM_FIXED flag you will break the handle // validation in DdeFreeDataHandle & get a big handle leak!! pMem = (PSTR)LocalAlloc((WORD)LPTR, wSize); // LPTR = fixed | zeroinit SwitchDS(wSaveDS); } while (pMem == NULL && ProcessMemError(hheap)); #ifdef WATCHHEAPS if (pMem) { LogAlloc((DWORD)MAKELONG(pMem, HIWORD(lpMem)), wSize, RGB(0xff, 0, 0), hInstance); } #endif /* set up the far return value, based on the success of LocalAlloc */ return (LPSTR)(pMem ? MAKELONG(pMem, HIWORD(lpMem)) : NULL); } /***************************** Private Function ****************************\ * * Frees a block of a given size from a heap. * Returns NULL on success, far pointer to the block otherwise. * * * History: * Created 5/31/90 Rich Gartland \***************************************************************************/ void FarFreeMem( LPVOID lpMem) { WORD wSaveDS; #ifdef WATCHHEAPS WORD sz; #endif /* Do some magic here using the segment selector, to switch our * ds to the heap's segment. Then, our LocalFree will work fine. */ wSaveDS = SwitchDS(HIWORD(lpMem)); #ifdef WATCHHEAPS sz = LocalSize((LOWORD((DWORD)lpMem))); #endif /* Free the block */ LocalFree(LocalHandle(LOWORD((DWORD)lpMem))); SwitchDS(wSaveDS); #ifdef WATCHHEAPS LogAlloc((DWORD)lpMem, sz, RGB(0x80, 0x80, 0x80), hInstance); #endif } int FAR PASCAL WEP (int); int FAR PASCAL LibMain(HANDLE, WORD, WORD, LPCSTR); #pragma alloc_text(INIT_TEXT,LibMain,WEP) /***************************** Private Function ****************************\ * * Does some initialization for the DLL. Called from LibEntry.asm * Returns 1 on success, 0 otherwise. * * * History: * Created 6/5/90 Rich Gartland \***************************************************************************/ int FAR PASCAL LibMain (hI, wDS, cbHS, lpszCL) HANDLE hI; /* instance handle */ WORD wDS; /* data segment */ WORD cbHS; /* heapsize */ LPCSTR lpszCL; /* command line */ { extern ATOM gatomDDEMLMom; extern ATOM gatomDMGClass; #if 0 /* We won't unlock the data segment, as typically happens here */ /* Init the semaphore -- probably just a stub now */ SEMINIT(); #endif /* set up the global instance handle variable */ hInstance = hI; /* set up class atoms. Note we use RegisterWindowMessage because * it comes from the same user atom table used for class atoms. */ gatomDDEMLMom = RegisterWindowMessage("DDEMLMom"); gatomDMGClass = RegisterWindowMessage("DMGClass"); return(1); } VOID RegisterClasses() { WNDCLASS cls; cls.hIcon = NULL; cls.hCursor = NULL; cls.lpszMenuName = NULL; cls.hbrBackground = NULL; cls.style = 0; // CS_GLOBALCLASS cls.hInstance = hInstance; cls.cbClsExtra = 0; cls.cbWndExtra = sizeof(VOID FAR *) + sizeof(WORD); cls.lpszClassName = SZCLIENTCLASS; cls.lpfnWndProc = (WNDPROC)ClientWndProc; RegisterClass(&cls); // cls.cbWndExtra = sizeof(VOID FAR *) + sizeof(WORD); cls.lpszClassName = SZSERVERCLASS; cls.lpfnWndProc = (WNDPROC)ServerWndProc; RegisterClass(&cls); // cls.cbWndExtra = sizeof(VOID FAR *) + sizeof(WORD); cls.lpszClassName = SZDMGCLASS; cls.lpfnWndProc = (WNDPROC)DmgWndProc; RegisterClass(&cls); cls.cbWndExtra = sizeof(VOID FAR *) + sizeof(WORD) + sizeof(WORD); cls.lpszClassName = SZCONVLISTCLASS; cls.lpfnWndProc = (WNDPROC)ConvListWndProc; RegisterClass(&cls); cls.cbWndExtra = sizeof(VOID FAR *); cls.lpszClassName = SZMONITORCLASS; cls.lpfnWndProc = (WNDPROC)MonitorWndProc; RegisterClass(&cls); cls.cbWndExtra = sizeof(VOID FAR *); cls.lpszClassName = SZFRAMECLASS; cls.lpfnWndProc = (WNDPROC)subframeWndProc; RegisterClass(&cls); #ifdef WATCHHEAPS cls.cbWndExtra = 0; cls.lpszClassName = SZHEAPWATCHCLASS; cls.lpfnWndProc = DefWindowProc; cls.hCursor = LoadCursor(NULL, IDC_ARROW); cls.hbrBackground = GetStockObject(WHITE_BRUSH); RegisterClass(&cls); #endif // WATCHHEAPS } #if 0 VOID UnregisterClasses() { UnregisterClass(SZCLIENTCLASS, hInstance); UnregisterClass(SZSERVERCLASS, hInstance); UnregisterClass(SZDMGCLASS, hInstance); UnregisterClass(SZCONVLISTCLASS, hInstance); UnregisterClass(SZMONITORCLASS, hInstance); UnregisterClass(SZFRAMECLASS, hInstance); #ifdef WATCHHEAPS UnregisterClass(SZHEAPWATCHCLASS, hInstance); #endif } #endif /***************************** Private Function ****************************\ * * Does the termination for the DLL. * Returns 1 on success, 0 otherwise. * * * History: * Created 6/5/90 Rich Gartland \***************************************************************************/ int FAR PASCAL WEP (nParameter) int nParameter; { if (nParameter == WEP_SYSTEM_EXIT) { /* DdeUninitialize(); */ return(1); } else { if (nParameter == WEP_FREE_DLL) { /* DdeUninitialize(); */ return(1); } } }