/****************************** 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 <memory.h>

#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);
        }
    }

}