/****************************** Module Header ******************************\
* Module Name: HDATA.C
*
* DDE manager data handle handling routines
*
* Created: 12/14/90 Sanford Staab
*
* Copyright (c) 1988, 1989, 1990 Microsoft Corporation
\***************************************************************************/

#include "ddemlp.h"

HDDEDATA PutData(pSrc, cb, cbOff, aItem, wFmt, afCmd, pai)
LPBYTE pSrc;
DWORD cb;
DWORD cbOff;
ATOM aItem;
WORD wFmt;
WORD afCmd;
PAPPINFO pai;
{
    HANDLE hMem;
    HDDEDATA hdT;
    DIP   dip;

    /* HACK ALERT!
     * make sure the first two words req'd by windows dde is there,
     * UNLESS aItem is null in which case we assume it is EXECUTE
     * data and don't bother.
     */
    if (aItem)
        cbOff += 4L;
    else
        afCmd |= HDATA_EXEC;

    if ((hMem = AllocDDESel(
            (afCmd & HDATA_APPOWNED) ? DDE_FACKREQ : DDE_FRELEASE,
            wFmt, cb + cbOff)) == NULL) {
        SETLASTERROR(pai, DMLERR_MEMORY_ERROR);
        return(0L);
    }


    // add to local list - make sure a similar handle isn't already there.

    hdT = MAKELONG(afCmd, hMem);
#ifdef DEBUG
    if (FindPileItem(pai->pHDataPile, CmpHIWORD, (LPBYTE)&hdT, FPI_DELETE)) {
        AssertF(FALSE, "PutData - unexpected handle in hDataPile");
    }
#endif // DEBUG

    if (AddPileItem(pai->pHDataPile, (LPBYTE)&hdT, CmpHIWORD) == API_ERROR) {
        GLOBALFREE(hMem);
        SETLASTERROR(pai, DMLERR_MEMORY_ERROR);
        return(0L);
    }

    // add to global list if appowned

    if (afCmd & HDATA_APPOWNED) {
        dip.hData = hMem;
        dip.hTask = pai->hTask;
        dip.cCount = 1;
        dip.fFlags = afCmd;
        if (AddPileItem(pDataInfoPile, (LPBYTE)&dip, CmpWORD) == API_ERROR) {
            GLOBALFREE(hMem);
            SETLASTERROR(pai, DMLERR_MEMORY_ERROR);
            return(0L);
        }
    }

    if (pSrc)
        CopyHugeBlock(pSrc, HugeOffset(GLOBALLOCK(hMem), cbOff), cb);

    // LOWORD(hData) always == afCmd flags

    return(MAKELONG(afCmd, hMem));
}



/*
 * This is the internal data handle freeing function.  fInternal is TRUE if
 * this is called from within the DDEML (vs called via DdeFreeDataHandle())
 * It only frees the handle if it is in the local list.
 * Appowned data handles are only freed internally if a non-owner task is
 * doing the freeing.
 * It is important that the LOWORD(hData) be set properly.
 *
 * These features give this function the folowing desired characteristics:
 * 1) Apps cannot free data handles more than once.
 * 2) The DDEML cannot free APPOWNED data handles on behalf of the owner
 *    task. (except on cleanup)
 */
VOID FreeDataHandle(
PAPPINFO pai,
HDDEDATA hData,
BOOL fInternal)
{
    DIP *pDip;
    BOOL fRelease = 0;
    DDEPOKE FAR *pDdeMem;
    WORD fmt;

    TRACEAPIIN((szT, "FreeDataHandle(%lx, %lx, %d)\n", pai, hData, fInternal));

    // appowned data handles are not freed till their count reaches 0.

    if ((LOWORD(hData) & HDATA_APPOWNED) &&
            (pDip = (DIP *)(DWORD)FindPileItem(pDataInfoPile, CmpWORD, PHMEM(hData), 0))) {

        // don't internally free if in the context of the owner

        if (fInternal && (pDip->hTask == pai->hTask)) {
            TRACEAPIOUT((szT, "FreeDataHandle: Internal and of this task - not freed.\n"));
            return;
        }

        if (--pDip->cCount != 0) {
            TRACEAPIOUT((szT, "FreeDataHandle: Ref count not 0 - not freed.\n"));
            return;
        }

        FindPileItem(pDataInfoPile, CmpWORD, PHMEM(hData), FPI_DELETE);
        fRelease = TRUE;
    }

    /*
     * Apps can only free handles in their local list - this guards against
     * multiple frees by an app. (my arnt we nice)
     */
    if (!HIWORD(hData) ||
            !FindPileItem(pai->pHDataPile, CmpHIWORD, (LPBYTE)&hData, FPI_DELETE)) {
        TRACEAPIOUT((szT, "FreeDataHandle: Not in local list - not freed.\n"));
        return;
    }

    if (LOWORD(hData) & HDATA_EXEC) {
        fRelease |= !(LOWORD(hData) & HDATA_APPOWNED);
    } else {
        pDdeMem = (DDEPOKE FAR *)GLOBALLOCK(HIWORD(hData));
        if (pDdeMem == NULL) {
            TRACEAPIOUT((szT, "FreeDataHandle: Lock failed - not freed.\n"));
            return;
        }
        fRelease |= pDdeMem->fRelease;
        fmt = pDdeMem->cfFormat;
        GLOBALUNLOCK(HIWORD(hData));
    }

    if (fRelease) {
        if (LOWORD(hData) & HDATA_EXEC)
            GLOBALFREE(HIWORD(hData));
        else
            FreeDDEData(HIWORD(hData), fmt);
    }
    TRACEAPIOUT((szT, "FreeDataHandle: freed.\n"));
}





/*
 * This function prepairs data handles on entry into the DDEML API.  It has
 * the following characteristics:
 * 1) APPOWNED data handles are copied to a non-appowned handle if being
 *    passed to a non-local app.
 * 2) non-APPOWNED data handles on loan to a callback are copied so they
 *    don't get prematurely freed.
 * 3) The READONLY bit is set. (in the local list)
 */
HDDEDATA DllEntry(
PCOMMONINFO pcomi,
HDDEDATA hData)
{
    if ((!(pcomi->fs & ST_ISLOCAL)) && (LOWORD(hData) & HDATA_APPOWNED) ||
            LOWORD(hData) & HDATA_NOAPPFREE && !(LOWORD(hData) & HDATA_APPOWNED)) {

        // copy APPOWNED data handles to a fresh handle if not a local conv.
        // copy app loaned, non appowned handles as well (this is the
        // relay server case)

        hData = CopyHDDEDATA(pcomi->pai, hData);
    }

    LOWORD(hData) |= HDATA_READONLY;

    AddPileItem(pcomi->pai->pHDataPile, (LPBYTE)&hData, CmpHIWORD);

    // NOTE: the global lists READONLY flag set but thats
    // ok because any hData received from a transaction will always be in
    // the local list due to RecvPrep().

    return(hData);
}



/*
 * removes the data handle from the local list.  This removes responsibility
 * for the data handle from the sending app.   APPOWNED handles are not
 * removed.
 */
VOID XmitPrep(
HDDEDATA hData,
PAPPINFO pai)
{
    if (!(LOWORD(hData) & HDATA_APPOWNED)) {
        FindPileItem(pai->pHDataPile, CmpHIWORD, (LPBYTE)&hData, FPI_DELETE);
    }
}



/*
 * Places the received data handle into the apropriate lists and returns
 * it with the proper flags set.  Returns 0 if invalid hMem.  afCmd should
 * contain any extra flags desired.
 */
HDDEDATA RecvPrep(
PAPPINFO pai,
HANDLE hMem,
WORD afCmd)
{
    DIP *pdip;
    HDDEDATA hData;

    if (!hMem)
        return(0);

    // check if its an APPOWNED one, if so, log entry.

    if (pdip = (DIP *)(DWORD)FindPileItem(pDataInfoPile, CmpWORD, (LPBYTE)&hMem, 0)) {
        afCmd |= pdip->fFlags;
        pdip->cCount++;
    }

    // if we got one that isnt fRelease, treat it as appowed.

    if (!(*(LPWORD)GLOBALLOCK(hMem) & DDE_FRELEASE))
        afCmd |= HDATA_APPOWNED;

    GLOBALUNLOCK(hMem);

    // all received handles are readonly.

    hData = (HDDEDATA)MAKELONG(afCmd | HDATA_READONLY, hMem);
    /*
     * Add (or replace) into local list
     */
    AddPileItem(pai->pHDataPile, (LPBYTE)&hData, CmpHIWORD);

    return(hData);
}


HANDLE CopyDDEShareHandle(
HANDLE hMem)
{
    DWORD cb;
    LPBYTE pMem;

    cb = GlobalSize(hMem);
    pMem = GLOBALLOCK(hMem);
    if (pMem == NULL) {
        return(0);
    }
    hMem = GLOBALALLOC(GMEM_DDESHARE, cb);
    CopyHugeBlock(pMem, GLOBALPTR(hMem), cb);
    return(hMem);
}



HBITMAP CopyBitmap(
PAPPINFO pai,
HBITMAP hbm)
{
    BITMAP bm;
    HBITMAP hbm2, hbmOld1, hbmOld2;
    HDC hdc, hdcMem1, hdcMem2;

    if (!GetObject(hbm, sizeof(BITMAP), &bm)) {
        return(0);
    }
    hdc = GetDC(pai->hwndDmg);
    if (!hdc) {
        return(0);
    }
    hdcMem1 = CreateCompatibleDC(hdc);
    if (!hdcMem1) {
        goto Cleanup3;
    }
    hdcMem2 = CreateCompatibleDC(hdc);
    if (!hdcMem2) {
        goto Cleanup2;
    }
    hbmOld1 = SelectObject(hdcMem1, hbm);
    hbm2 = CreateCompatibleBitmap(hdcMem1, bm.bmWidth, bm.bmHeight);
    if (!hbm2) {
        goto Cleanup1;
    }
    hbmOld2 = SelectObject(hdcMem2, hbm2);
    BitBlt(hdcMem2, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem1, 0, 0, SRCCOPY);
    SelectObject(hdcMem1, hbmOld1);
    SelectObject(hdcMem2, hbmOld2);
Cleanup1:
    DeleteDC(hdcMem2);
Cleanup2:
    DeleteDC(hdcMem1);
Cleanup3:
    ReleaseDC(pai->hwndDmg, hdc);
    return(hbm2);
}



HPALETTE CopyPalette(
HPALETTE hpal)
{
    int cPalEntries;
    LOGPALETTE *plp;

    if (!GetObject(hpal, sizeof(int), &cPalEntries)) {
        return(0);
    }
    plp = (LOGPALETTE *)LocalAlloc(LPTR, sizeof(LOGPALETTE) +
            (cPalEntries - 1) * sizeof(PALETTEENTRY));
    if (!plp) {
        return(0);
    }
    if (!GetPaletteEntries(hpal, 0, cPalEntries, plp->palPalEntry)) {
        LocalFree((HLOCAL)plp);
        return(0);
    }
    plp->palVersion = 0x300;
    plp->palNumEntries = (WORD)cPalEntries;
    hpal = CreatePalette(plp);
    if (hpal  &&
            !SetPaletteEntries(hpal, 0, cPalEntries, plp->palPalEntry)) {
        hpal = 0;
    }
    LocalFree((HLOCAL)plp);
    return(hpal);
}




HDDEDATA CopyHDDEDATA(
PAPPINFO pai,
HDDEDATA hData)
{
    HANDLE hMem;
    LPDDE_DATA lpdded;
    HDDEDATA hdT;
    LPMETAFILEPICT pmfPict;

    if (!HIWORD(hData))
        return(hData);
    hMem = CopyDDEShareHandle((HANDLE)HIWORD(hData));

    if (!hMem)
        return(NULL);

    if (!(LOWORD(hData) & HDATA_EXEC)) {
        lpdded = (LPDDE_DATA)GLOBALLOCK(hMem);
        if (lpdded == NULL) {
            return(NULL);
        }
        lpdded->wStatus |= DDE_FRELEASE;
        if (lpdded != NULL) {
            switch (lpdded->wFmt) {
            case CF_BITMAP:
            case CF_DSPBITMAP:
                lpdded->wData = CopyBitmap(pai, lpdded->wData);
                break;

            case CF_PALETTE:
                lpdded->wData = (WORD)CopyPalette((HPALETTE)lpdded->wData);
                break;

            case CF_DIB:
                lpdded->wData = (WORD)CopyDDEShareHandle((HANDLE)lpdded->wData);
                break;

            case CF_METAFILEPICT:
            case CF_DSPMETAFILEPICT:
                lpdded->wData = (WORD)CopyDDEShareHandle((HANDLE)lpdded->wData);
                if (lpdded->wData) {
                    pmfPict = (LPMETAFILEPICT)GLOBALLOCK((HANDLE)lpdded->wData);
                    if (pmfPict != NULL) {
                        pmfPict->hMF = CopyMetaFile(pmfPict->hMF, NULL);
                        GLOBALUNLOCK((HANDLE)lpdded->wData);
                    }
                    GLOBALUNLOCK((HANDLE)lpdded->wData);
                }
                break;
#ifdef CF_ENHMETAFILE
            case CF_ENHMETAFILE:
                lpdded->wData = (WORD)CopyEnhMetaFile(*((HENHMETAFILE FAR *)(&lpdded->wData)), NULL);
                break;
#endif   // bad because it makes chicago and NT binaries different.
            }
            GLOBALUNLOCK(hMem);
        }
    }

    hdT = MAKELONG(LOWORD(hData) & ~(HDATA_APPOWNED | HDATA_NOAPPFREE), hMem);
    AddPileItem(pai->pHDataPile, (LPBYTE)&hdT, NULL);

    return(hdT);
}


VOID FreeDDEData(
HANDLE hMem,
WORD wFmt)
{
    DDEDATA FAR *pDdeData;

    /*
     * This handles the special cases for formats that hold imbedded
     * objects.  (CF_BITMAP, CF_METAFILEPICT).
     *
     * The data handle is assumed to be unlocked.
     */

    // may need to add "Printer_Picture" for excel/word interaction" but
    // this is just between the two of them and they don't use DDEML.
    // raor says OLE only worries about these formats as well.

    pDdeData = (DDEDATA FAR *)GLOBALLOCK(hMem);
    if (pDdeData == NULL) {
        return;
    }

    switch (wFmt) {
    case CF_BITMAP:
    case CF_DSPBITMAP:
    case CF_PALETTE:
        DeleteObject(*(HANDLE FAR *)(&pDdeData->Value));
        break;

    case CF_DIB:
        /*
         * DIBs are allocated by app so we don't use the macro here.
         */
        GlobalFree(*(HANDLE FAR *)(&pDdeData->Value));
        break;

    case CF_METAFILEPICT:
    case CF_DSPMETAFILEPICT:
        {
            HANDLE hmfPict;
            LPMETAFILEPICT pmfPict;

            /*
             * EXCEL sickness -  metafile is a handle to a METAFILEPICT
             * struct which holds a metafile.  (2 levels of indirection!)
             *
             * We don't use the GLOBAL macros here because these objects
             * are allocated by the app.  DDEML knows not their history.
             */

        hmfPict = *(HANDLE FAR *)(&pDdeData->Value);
        pmfPict = (LPMETAFILEPICT)GlobalLock(hmfPict);
        if (pmfPict != NULL) {
            DeleteMetaFile(pmfPict->hMF);
        }
        GlobalUnlock(hmfPict);
        GlobalFree(hmfPict);
        }
        break;

#ifdef CF_ENHMETAFILE
    case CF_ENHMETAFILE:
        DeleteEnhMetaFile(*(HENHMETAFILE FAR *)(&pDdeData->Value));
        break;
#endif  // This is bad - it forces different binaries for chicago and NT!
    }

    GLOBALUNLOCK(hMem);
    GLOBALFREE(hMem);
}