/*++

copyright (c) 1992  Microsoft Corporation

Module Name:

    ddeworkr.cpp

Abstract:

    This module contains the code for the worker routines

Author:

    Srini Koppolu   (srinik)    22-June-1992
    Jason Fuller    (jasonful)  24-July-1992

Revision History:
    Kevin Ross	    (KevinRo)   10-May-1994
    	Mostly added comments, and attempted to clean
	it up.

--*/
#include "ddeproxy.h"

ASSERTDATA

/*
 *  WORKER ROUTINES
 *
 */


INTERNAL_(BOOL) wPostMessageToServer(LPDDE_CHANNEL pChannel,
				     WORD wMsg,
				     LONG lParam,
				     BOOL fFreeOnError)
{
    int c=0;
    intrDebugOut((DEB_ITRACE,
		  "wPostMessageToServer(pChannel=%x,wMsg=%x,lParam=%x,fFreeOnError=%x\n",
		  pChannel,
		  wMsg,
		  lParam,
		  fFreeOnError));
    if (NULL==pChannel)
    {
        AssertSz (0, "Channel missing");
        return FALSE;
    }
    pChannel->wMsg   = wMsg;
    pChannel->lParam = lParam;
    pChannel->hres   = NOERROR;

    while (TRUE && c<10 )
    {
        if (!IsWindow (pChannel->hwndSvr))
        {
       intrDebugOut((DEB_IWARN,
                  "wPostMessageToServer: invalid window %x\n",
                  pChannel->hwndSvr));
            goto errRet;
        }
        if (wTerminateIsComing (pChannel)
            && wMsg != WM_DDE_ACK
            && wMsg != WM_DDE_TERMINATE)
        {
	    intrDebugOut((DEB_IWARN,"Server sent terminate, cannot post\n"));
	    goto errRet;
        }
        if (!PostMessage (pChannel->hwndSvr, wMsg, (WPARAM) pChannel->hwndCli, lParam))
        {
	    intrDebugOut((DEB_IWARN,
                  "wPostMessageToServer: PostMessageFailed, yielding\n"));
            Yield ();
            c++;
        }
        else
            return TRUE;
    }
    AssertSz (0, "PostMessage failed");

errRet:
    intrDebugOut((DEB_IWARN,"wPostMessageToServer returns FALSE\n"));
    if (fFreeOnError)
    {
	DDEFREE(wMsg,lParam);
    }

    return FALSE;
}


// call Ole1ClassFromCLSID then global add atom; returns NULL if error.
INTERNAL_(ATOM) wAtomFromCLSID(REFCLSID rclsid)
{
    WCHAR szClass[MAX_STR];
    ATOM aCls;

    if (Ole1ClassFromCLSID2(rclsid, szClass, sizeof(szClass)) == 0)
        return NULL;
    aCls = wGlobalAddAtom(szClass);
    intrAssert(wIsValidAtom(aCls));
    return aCls;
}

INTERNAL_(ATOM) wGlobalAddAtom(LPCOLESTR sz)
{
    if (sz==NULL || sz[0] == '\0')
    {
        return NULL;
    }

    ATOM a = GlobalAddAtom(sz);
    intrAssert(wIsValidAtom(a));
    return a;
}

INTERNAL_(ATOM) wGlobalAddAtomA(LPCSTR sz)
{
    if (sz==NULL || sz[0] == '\0')
        return NULL;
    ATOM a = GlobalAddAtomA(sz);
    intrAssert(wIsValidAtom(a));
    return a;
}


INTERNAL_(ATOM) wGetExeNameAtom (REFCLSID rclsid)
{
    LONG    cb = MAX_STR;
    WCHAR    key[MAX_STR];
    ATOM    a;

    if (Ole1ClassFromCLSID2(rclsid, key, sizeof(key)) == 0)
        return NULL;

    lstrcat (key, OLESTR("\\protocol\\StdFileEditing\\server"));

    if (RegQueryValue (HKEY_CLASSES_ROOT, key, key, &cb))
    {
        Puts ("ERROR: wGetExeNameAtom failed\n");
        return NULL;
    }
    a = wGlobalAddAtom (key);
    intrAssert(wIsValidAtom(a));
    return a;
}

INTERNAL_(void) wFreeData (HANDLE hData, CLIPFORMAT cfFormat,
                           BOOL fFreeNonGdiHandle)
{
    intrDebugOut((DEB_ITRACE,
		  "wFreeData(hData=%x,cfFormat=%x,FreeNonGDIHandle=%x\n",
		  hData,
		  (USHORT)cfFormat,
		  fFreeNonGdiHandle));

    AssertSz (hData != NULL, "Trying to free NULL handle");
    AssertSz (hData != (HANDLE) 0xcccccccc, "Trying to free handle from a deleted object");

    switch (cfFormat) {
    case CF_METAFILEPICT:
        LPMETAFILEPICT  lpMfp;

        if (lpMfp = (LPMETAFILEPICT) GlobalLock (hData))
	{
	    intrDebugOut((DEB_ITRACE,
		          "wFreeData freeing metafile %x\n",
			  lpMfp->hMF));

	    OleDdeDeleteMetaFile(lpMfp->hMF);
            GlobalUnlock (hData);
        }
        GlobalFree (hData);
        break;

    case CF_BITMAP:
    case CF_PALETTE:
        Verify(DeleteObject (hData));
        break;

    case CF_DIB:
        GlobalFree (hData);
        break;

    default:
        if (fFreeNonGdiHandle)
            GlobalFree (hData);
        break;
    }
}



INTERNAL_(BOOL) wInitiate (LPDDE_CHANNEL pChannel, ATOM aLow, ATOM aHigh)
{
    intrDebugOut((DEB_ITRACE,"wInitiate(pChannel=%x,aLow=%x,aHigh=%x)\n",
          pChannel, aLow, aHigh));

    intrAssert(wIsValidAtom(aLow));
    if (aLow == (ATOM)0)
    {
	intrDebugOut((DEB_IERROR,"wInitiate Failed, aLow == 0\n"));
	return FALSE;
    }

    pChannel->iAwaitAck = AA_INITIATE;

    SendMessage ((HWND)-1, WM_DDE_INITIATE, (WPARAM) pChannel->hwndCli,
		  MAKE_DDE_LPARAM (WM_DDE_INITIATE, aLow, aHigh));

    pChannel->iAwaitAck = NULL;

    intrDebugOut((DEB_ITRACE,
		  "wInitiate pChannel->hwndSrvr = %x\n",
		  pChannel->hwndSvr));

    return (pChannel->hwndSvr != NULL);
}



INTERNAL_(HRESULT) wScanItemOptions (ATOM aItem, int FAR* lpoptions)
{
    ATOM    aModifier;
    LPOLESTR   lpbuf;
    WCHAR    buf[MAX_STR];

    *lpoptions = ON_CHANGE; // default

    if (!aItem) {
        // NULL item with no modifier means ON_CHANGE for NULL item
        return NOERROR;
    }

    intrAssert(wIsValidAtom(aItem));
    GlobalGetAtomName (aItem, buf, MAX_STR);
    lpbuf = buf;

    while ( *lpbuf && *lpbuf != '/')
           IncLpch (lpbuf);

    // no modifier same as /change

    if (*lpbuf == NULL)
        return NOERROR;

    *lpbuf++ = NULL;        // seperate out the item string
                            // We are using this in the caller.

    if (!(aModifier = GlobalFindAtom (lpbuf)))
    {
        Puts ("ERROR: wScanItemOptions found non-atom modifier\n");
        return ReportResult(0, RPC_E_DDE_SYNTAX_ITEM, 0, 0);
    }

    intrAssert(wIsValidAtom(aModifier));

    if (aModifier == aChange)
        return NOERROR;

    // Is it a save?
    if (aModifier == aSave){
        *lpoptions = ON_SAVE;
        return  NOERROR;
    }
    // Is it a Close?
    if (aModifier == aClose){
        *lpoptions = ON_CLOSE;
        return NOERROR;
    }

    // unknown modifier
    Puts ("ERROR: wScanItemOptions found bad modifier\n");
    return ReportResult(0, RPC_E_DDE_SYNTAX_ITEM, 0, 0);
}


INTERNAL_(BOOL) wClearWaitState (LPDDE_CHANNEL pChannel)
{
    Assert (pChannel);
    // kill if any timer active.
    if (pChannel->wTimer) {
        KillTimer (pChannel->hwndCli, 1);
        pChannel->wTimer = 0;

        if (pChannel->hDdePoke) {
            GlobalFree (pChannel->hDdePoke);
            pChannel->hDdePoke = NULL;
        }

        if (pChannel->hopt) {
            GlobalFree (pChannel->hopt);
            pChannel->hopt = NULL;
        }

	//
	// If the channel is waiting on an Ack, and there is an
	// lParam, then we may need to cleanup the data.

        if (pChannel->iAwaitAck && (pChannel->lParam)) {
            if (pChannel->iAwaitAck == AA_EXECUTE)
	    {
		//
		// KevinRo: Found the following comment in the code.
		// ; // BUGBUG32 - get hData from GET_WM_DDE_EXECUTE_HADATA ??
		// It appears, by looking at what the 16-bit code does,
		// that the goal is to free the handle that was passed as
		// part of the EXECUTE message. Judging by what the 16-bit
		// code did, I have determined that this is correct.
		//
		// The macro used below wanted two parameters. The first was
		// the WPARAM, the second the LPARAM. We don't have the WPARAM.
		// However, it isn't actually used by the macro, so I have
		// cheated and provided 0 as a default
		//
		GlobalFree(GET_WM_DDE_EXECUTE_HDATA(0,pChannel->lParam));

#ifdef KEVINRO_HERE_IS_THE_16_BIT_CODE
                GlobalFree (HIWORD (pChannel->lParam));
#endif
	    }
            else
	    {
		//
		// All of the other DDE messages pass an Atom in the high word.
		// Therefore, we should delete the atom.
		//
		//
		ATOM aTmp;

		aTmp = MGetDDElParamHi(pChannel->wMsg,pChannel->lParam);

		intrAssert(wIsValidAtom(aTmp));
                if (aTmp)
		{
                    GlobalDeleteAtom (aTmp);
		}
            }
	    DDEFREE(pChannel->wMsg,pChannel->lParam);

            // we want to wipe out the lParam
            pChannel->lParam = 0x0;
        }

        return TRUE;
    }

    return FALSE;
}



// wNewHandle (LPSTR, DWORD)
//
// Copy cb bytes from lpstr into a new memory block and return a handle to it.
// If lpstr is an ASCIIZ string, cb must include 1 for the null terminator.
//

INTERNAL_(HANDLE) wNewHandle (LPSTR lpstr, DWORD cb)
{

    HANDLE  hdata  = NULL;
    LPSTR   lpdata = NULL;

    hdata = GlobalAlloc (GMEM_DDESHARE | GMEM_MOVEABLE, cb);
    if (hdata == NULL || (lpdata = (LPSTR) GlobalLock (hdata)) == NULL)
        goto errRtn;

    memcpy (lpdata, lpstr, cb);
    GlobalUnlock (hdata);
    return hdata;

errRtn:
    Puts ("ERROR: wNewHandle\n");
    Assert (0);
    if (lpdata)
        GlobalUnlock (hdata);

    if (hdata)
        GlobalFree (hdata);
    return NULL;
}



// wDupData
//
// Copy data from handle h into a new handle which is returned as *ph.
//

INTERNAL wDupData (LPHANDLE ph, HANDLE h, CLIPFORMAT cf)
{
    Assert (ph);
    RetZ (wIsValidHandle(h, cf));
    *ph = OleDuplicateData (h, cf, GMEM_DDESHARE | GMEM_MOVEABLE);
    RetZ (wIsValidHandle (*ph, cf));
    return NOERROR;
}


// wTransferHandle
//
//
INTERNAL wTransferHandle
    (LPHANDLE phDst,
    LPHANDLE phSrc,
    CLIPFORMAT cf)
{
    RetErr (wDupData (phDst, *phSrc, cf));
    wFreeData (*phSrc, cf, TRUE);
    *phSrc = (HANDLE)0;
    return NOERROR;
}


// wHandleCopy
//
// copy data from hSrc to hDst.
// Both handles must already have memory allocated to them.
//

INTERNAL wHandleCopy (HANDLE hDst, HANDLE hSrc)
{
    LPSTR lpDst, lpSrc;
    DWORD dwSrc;

    if (NULL==hDst || NULL==hSrc)
        return ResultFromScode (E_INVALIDARG);
    if (GlobalSize(hDst) < (dwSrc=GlobalSize(hSrc)))
    {
        HANDLE hDstNew = GlobalReAlloc (hDst, dwSrc, GMEM_DDESHARE | GMEM_MOVEABLE);
        if (hDstNew != hDst)
            return ResultFromScode (E_OUTOFMEMORY);
    }
    if (!(lpDst = (LPSTR) GlobalLock(hDst)))
    {
        intrAssert(!"ERROR: wHandleCopy hDst");
        return ReportResult(0, E_OUTOFMEMORY, 0, 0);
    }
    if (!(lpSrc = (LPSTR) GlobalLock(hSrc)))
    {
   GlobalUnlock(hDst);
        intrAssert (!"ERROR: wHandleCopy hSrc");
        return ReportResult(0, E_OUTOFMEMORY, 0, 0);
    }
    memcpy (lpDst, lpSrc, dwSrc);
    GlobalUnlock(hDst);
    GlobalUnlock(hSrc);
    return NOERROR;
}


// ExtendAtom: Create a new atom, which is the old one plus extension

INTERNAL_(ATOM) wExtendAtom (ATOM aItem, int iAdvOn)
{
    WCHAR    buffer[MAX_STR+1];
    LPOLESTR   lpext;

    buffer[0] = 0;
    // aItem==NULL for embedded objects.
    // If so, there is no item name before the slash.
    if (aItem)
        GlobalGetAtomName (aItem, buffer, MAX_STR);

    switch (iAdvOn) {
        case ON_CHANGE:
            lpext = OLESTR("");
            break;

        case ON_SAVE:
            lpext = OLESTR("/Save");
            break;

        case ON_CLOSE:
            lpext = OLESTR("/Close");
            break;

        default:
            AssertSz (FALSE, "Unknown Advise option");
            break;

    }

    lstrcat (buffer, lpext);
    if (buffer[0])
        return wGlobalAddAtom (buffer);
    else
        return NULL;
        // not an error. For embedded object on-change, aItem==NULL
}




INTERNAL_(ATOM) wDupAtom (ATOM a)
{
    WCHAR sz[MAX_STR];

    if (!a)
        return NULL;

    Assert (wIsValidAtom (a));
    GlobalGetAtomName (a, sz, MAX_STR);
    return wGlobalAddAtom (sz);
}



//+---------------------------------------------------------------------------
//
//  Function:   wAtomLen
//
//  Synopsis:   Return the length, in characters, of the atom name.
//      The length includes the NULL. This function returns the
//      length of the UNICODE version of the atom.
//
//  Effects:
//
//  Arguments:  [atom] --
//
//  Requires:
//
//  Returns:
//
//  Signals:
//
//  Modifies:
//
//  Algorithm:
//
//  History:    5-12-94   kevinro   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
INTERNAL_(int) wAtomLen (ATOM atom)
{
    WCHAR    buf[MAX_STR];

    if (!atom)
        return NULL;

    return (GlobalGetAtomName (atom, buf, MAX_STR));
}

//+---------------------------------------------------------------------------
//
//  Function:   wAtomLenA
//
//  Synopsis:   Return the length, in characters, of the atom name.
//      The length includes the NULL This function returns the
//      length of the ANSI version of the atom,
//
//  Effects:
//
//  Arguments:  [atom] --
//
//  Requires:
//
//  Returns:
//
//  Signals:
//
//  Modifies:
//
//  Algorithm:
//
//  History:    5-12-94   kevinro   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
INTERNAL_(int) wAtomLenA (ATOM atom)
{
    char    buf[MAX_STR];

    if (!atom)
        return NULL;

    return (GlobalGetAtomNameA (atom, (LPSTR)buf, MAX_STR));
}



// NOTE: returns address of static buffer.  Use return value immediately.
//


//+---------------------------------------------------------------------------
//
//  Function:   wAtomName
//
//  Synopsis:   Returns a STATIC BUFFER that holds the string name of the
//      atom.
//
//  Effects:
//
//  Arguments:  [atom] --
//
//  Requires:
//
//  Returns:
//
//  Signals:
//
//  Modifies:
//
//  Algorithm:
//
//  History:    5-12-94   kevinro   Commented
//
//  Notes:
//
//  WARNING:    This uses a static buffer, so don't depend on the pointer for
//      very long.
//
//----------------------------------------------------------------------------
INTERNAL_(LPOLESTR) wAtomName (ATOM atom)
{
    static WCHAR buf[MAX_STR];

    if (!atom)
        return NULL;

    if (0==GlobalGetAtomName (atom, buf, MAX_STR))
        return NULL;

    return buf;
}

//+---------------------------------------------------------------------------
//
//  Function:   wAtomName
//
//  Synopsis:   Returns a STATIC BUFFER that holds the string name of the
//      atom.
//
//  Effects:
//
//  Arguments:  [atom] --
//
//  Requires:
//
//  Returns:
//
//  Signals:
//
//  Modifies:
//
//  Algorithm:
//
//  History:    5-12-94   kevinro   Commented
//
//  Notes:
//
//  WARNING:    This uses a static buffer, so don't depend on the pointer for
//      very long.
//
//----------------------------------------------------------------------------
INTERNAL_(LPSTR) wAtomNameA (ATOM atom)
{
    static char buf[MAX_STR];

    if (!atom)
        return NULL;

    if (0==GlobalGetAtomNameA (atom, (LPSTR)buf, MAX_STR))
        return NULL;

    return buf;
}




//+---------------------------------------------------------------------------
//
//  Function:   wHandleFromDdeData
//
//  Synopsis:   Return a handle from the DDEDATA passed in.
//
//  Effects:    This function will return the correct data from the
//		DDEDATA that is referenced by the handle passed in.
//
//		DDEDATA is a small structure that is used in DDE to
//		specify the data type of the buffer, its release
//		semantics, and the actual data.
//
//		In the case of a known format, the handle to the
//		data is extracted from the DDEDATA structure, and
//		the hDdeData is released.
//
//		If its a Native format, the data is either moved
//		within the memory block allocated, or is copied to
//		another block, depending on the fRelease flag in
//		the hDdeData.
//
//  Arguments:  [hDdeData] -- Handle to DDEDATA
//
//  Requires:
//
//  Returns:	A handle to the data. hDdeData will be invalidated
//
//  Signals:
//
//  Modifies:
//
//  Algorithm:
//
//  History:    5-13-94   kevinro   Commented
//
//  Notes:
//
// 	hDdeData is invalid after calling this function
//
//----------------------------------------------------------------------------


INTERNAL_(HANDLE) wHandleFromDdeData
    (HANDLE hDdeData)
{
    intrDebugOut((DEB_ITRACE,"wHandleFromDdeData(%x)\n",hDdeData));
    BOOL fRelease;
    HGLOBAL h = NULL; // return value

    DDEDATA FAR* lpDdeData = (DDEDATA FAR *) GlobalLock (hDdeData);

    //
    // If the handle is invalid, then the lpDdeData will be NULL
    //
    if (!lpDdeData)
    {
   intrDebugOut((DEB_ITRACE,
              "\twHandleFromDdeData(%x) invalid handle\n",
              hDdeData));
        return NULL;
    }


    //
    // The header section of a DDEDATA consists of 2 shorts.
    // That makes it 4 bytes. Due to the new packing values,
    // it turns out that doing a sizeof(DDEDATA) won't work,
    // because the size gets rounded up to a multple of 2
    //
    // We will just hard code the 4 here, since it cannot change
    // for all time anyway.
    //
    #define cbHeader 4
    Assert (cbHeader==4);

    //
    // If the cfFormat is BITMAP or METAFILEPICT, then the
    // handle will be retrieved from the first DWORD of the
    // buffer
    //
    if (lpDdeData->cfFormat == CF_BITMAP ||
        lpDdeData->cfFormat == CF_METAFILEPICT)
    {
       //
       // The alignment here should be fine, since the Value
       // field is DWORD aligned. So, we trust this cast
       //
       h = *(LPHANDLE)lpDdeData->Value;
       Assert (GlobalFlags(h) != GMEM_INVALID_HANDLE);
       fRelease = lpDdeData->fRelease;
       GlobalUnlock (hDdeData);
       if (fRelease)
       {
	   GlobalFree (hDdeData);
       }

       return h;
    }
    else if (lpDdeData->cfFormat == CF_DIB)
    {
       //
       // The alignment here should be fine, since the Value
       // field is DWORD aligned.
       //
       // This changes the memory from fixed to moveable.
       //
        h = GlobalReAlloc (*(LPHANDLE)lpDdeData->Value, 0L,
                              GMEM_MODIFY|GMEM_MOVEABLE);
        Assert (GlobalFlags(h) != GMEM_INVALID_HANDLE);
        fRelease = lpDdeData->fRelease;
        GlobalUnlock (hDdeData);
        if (fRelease)
            GlobalFree (hDdeData);
        return h;
    }


    // Native and other data case
    // dwSize = size of Value array, ie, size of the data itself
    const DWORD dwSize = GlobalSize (hDdeData) - cbHeader;

    if (lpDdeData->fRelease)
    {
        // Move the Value data up over the DDE_DATA header flags.
        memcpy ((LPSTR)lpDdeData, ((LPSTR)lpDdeData)+cbHeader, dwSize);
        GlobalUnlock (hDdeData);
   h = GlobalReAlloc (hDdeData, dwSize, GMEM_MOVEABLE);
        Assert (GlobalFlags(h) != GMEM_INVALID_HANDLE);
        return h;
    }
    else
    {
        // Duplicate the data because the server will free the original.
        h = wNewHandle (((LPSTR)lpDdeData)+cbHeader, dwSize);
        Assert (GlobalFlags(h) != GMEM_INVALID_HANDLE);
        GlobalUnlock (hDdeData);
        return h;
    }
}





//+---------------------------------------------------------------------------
//
//  Method:     CDdeObject::CanCallBack
//
//  Synopsis:   This routine apparently was supposed to determine if a
// 		call back could be made. However, the PeekMessage stuff
//		was commented out.
//
//		So, it returns TRUE if 0 or 1, FALSE but increments lpCount
//		if 2, returns true but decrements lpCount if > 3. Why?
//		Dunno. Need to ask JasonFul
//
//  Effects:
//
//  Arguments:  [lpCount] --
//
//  Requires:
//
//  Returns:
//
//  Signals:
//
//  Modifies:
//
//  Derivation:
//
//  Algorithm:
//
//  History:    5-16-94   kevinro   Commented, confused, and disgusted
//
//  Notes:
//
//----------------------------------------------------------------------------
INTERNAL_(BOOL) CDdeObject::CanCallBack (LPINT lpCount)
{
    switch (*lpCount) {
        case 0:
        case 1:
            return TRUE;

        case 2:
        {
//     MSG msg;
            if (0)
                //!PeekMessage (&msg, m_pDocChannel->hwndCli,0,0, PM_NOREMOVE) ||
               //   msg.message != WM_DDE_DATA)
            {
                Puts ("Server only sent one format (hopefully presentation)\n");
                return TRUE;
            }
            else
            {
                ++(*lpCount);
                return FALSE;
            }
        }

        case 3:
            --(*lpCount);
            return TRUE;

        default:
            AssertSz (FALSE, "012345" + *lpCount);
            return FALSE;
    }
}


INTERNAL_(BOOL) wIsOldServer (ATOM aClass)
{
    LONG    cb = MAX_STR;
    WCHAR    key[MAX_STR];
    int     len;

    if (aClass==(ATOM)0)
        return FALSE;

    if (!GlobalGetAtomName (aClass, key, sizeof(key)))
        return TRUE;

    lstrcat (key, OLESTR("\\protocol\\StdFileEditing\\verb\\"));
    len = lstrlen (key);
    key [len++] = (char) ('0');
    key [len++] = 0;

    if (RegQueryValue (HKEY_CLASSES_ROOT, key, key, &cb))
        return TRUE; // no verbs registered

    return FALSE;
}




INTERNAL_(void) wFreePokeData
    (LPDDE_CHANNEL pChannel,
     BOOL fMSDrawBug)
{
    DDEPOKE FAR * lpdde;

    if (!pChannel )
        return;

    if (!pChannel->hDdePoke)
        return;

    if (lpdde = (DDEPOKE FAR *) GlobalLock (pChannel->hDdePoke)) {

        // The old version of MSDraw expects the _contents_ of METAFILEPICT
        // structure, rather than the handle to it, to be part of DDEPOKE.

        if (fMSDrawBug && lpdde->cfFormat==CF_METAFILEPICT) {
	    intrDebugOut((DEB_ITRACE,
			  "wFreePokeData is accomodating MSDraw bug\n"));
            //
	    // This meta file was created in 32-bits, and was not passed
	    // into us by DDE. Therefore, this metafile should not need to
	    // call WOW to be free'd.
	    //
            DeleteMetaFile (((LPMETAFILEPICT) ((LPVOID) &lpdde->Value))->hMF);
        }
        // If there is a normal metafile handle in the Value field,
        // it will be freed (if necessary) by the ReleaseStgMedium()
        // in DO::SetData
        GlobalUnlock (pChannel->hDdePoke);
    }
    GlobalFree (pChannel->hDdePoke);
    pChannel->hDdePoke = NULL;
}




INTERNAL_(HANDLE) wPreparePokeBlock
    (HANDLE hData, CLIPFORMAT cfFormat, ATOM aClass, BOOL bOldSvr)
{
    HANDLE  hDdePoke;
    LPSTR   lpBuf;

    if (!hData)
        return NULL;

    // The old version of MSDraw expects the contents of METAFILEPICT
    // structure to be part of DDEPOKE, rather than the handle to it.
    if ((cfFormat==CF_METAFILEPICT && !(aClass==aMSDraw && bOldSvr))
        || (cfFormat == CF_DIB)
        || (cfFormat == CF_BITMAP)) {

        Verify (lpBuf = wAllocDdePokeBlock (4, cfFormat, &hDdePoke));
        *((HANDLE FAR*)lpBuf) = hData;

    }
    else {
        // Handle the non-metafile case and the MS-Draw bug
        DWORD dwSize = GlobalSize (hData);

	if ((aClass == aMSDraw) && bOldSvr)
	{
	    intrDebugOut((DEB_ITRACE,
			  "wPreparePokeBlock is accomodating MSDraw bug\n"));
	}

        if (lpBuf = wAllocDdePokeBlock (dwSize, cfFormat, &hDdePoke)) {
            memcpy (lpBuf, GlobalLock(hData), dwSize);
            GlobalUnlock (hData);
        }
    }
    GlobalUnlock (hDdePoke);
    return hDdePoke;
}


// wAllocDdePokeBlock
// The caller must unlock *phDdePoke when it is done using the return value
// of this function but before a DDe message is sent using *phDdePoke.
//

INTERNAL_(LPSTR) wAllocDdePokeBlock
    (DWORD dwSize, CLIPFORMAT cfFormat, LPHANDLE phDdePoke)
{
    HANDLE      hdde = NULL;
    DDEPOKE FAR * lpdde = NULL;

    if (!(hdde = GlobalAlloc (GMEM_DDESHARE | GMEM_ZEROINIT,
                 (dwSize + sizeof(DDEPOKE) - sizeof(BYTE) ))))
        return NULL;

    if (!(lpdde = (DDEPOKE FAR*)GlobalLock (hdde))) {
        GlobalFree (hdde);
        return NULL;
    }
    // hdde will be UnLock'ed in wPreparePokeBlock and Free'd in wFreePokeData
    lpdde->fRelease = FALSE;
    lpdde->cfFormat = cfFormat;
    *phDdePoke = hdde;
    return (LPSTR) &(lpdde->Value);
}



INTERNAL_(ULONG) wPixelsToHiMetric
    (ULONG cPixels,
    ULONG cPixelsPerInch)
{
    return cPixels * HIMETRIC_PER_INCH / cPixelsPerInch;
}


// Can ask for icon based on either CLSID or filename
//

INTERNAL GetDefaultIcon (REFCLSID clsidIn, LPCOLESTR szFile, HANDLE FAR* phmfp)
{
    if (!(*phmfp = OleGetIconOfClass(clsidIn, NULL, TRUE)))
        return ResultFromScode(E_OUTOFMEMORY);

    return NOERROR;
}

#ifdef OLD
    VDATEPTROUT (phmfp, HICON);
    VDATEPTRIN (szFile, char);
    WCHAR szExe[MAX_STR];
    HICON hicon;
    HDC   hdc;
    METAFILEPICT FAR* pmfp=NULL;
    HRESULT hresult;
    static int cxIcon = 0;
    static int cyIcon = 0;
    static int cxIconHiMetric = 0;
    static int cyIconHiMetric = 0;

    *phmfp = NULL;
    CLSID clsid;
    if (clsidIn != CLSID_NULL)
    {
        clsid = clsidIn;
    }
    else
    {
        RetErr (GetClassFile (szFile, &clsid));
    }
    ATOM aExe = wGetExeNameAtom (clsid);
    if (0==GlobalGetAtomName (aExe, szExe, MAX_STR))
    {
        Assert (0);
        return ReportResult(0, E_UNEXPECTED, 0, 0);
    }
    hicon = ExtractIcon (hmodOLE2, szExe, 0/*first icon*/);
    if (((HICON) 1)==hicon || NULL==hicon)
    {
        // ExtractIcon failed, so we can't support DVASPECT_ICON
        return ResultFromScode (DV_E_DVASPECT);
    }
    *phmfp = GlobalAlloc (GMEM_DDESHARE | GMEM_MOVEABLE, sizeof(METAFILEPICT));
    ErrZS (*phmfp, E_OUTOFMEMORY);
    pmfp = (METAFILEPICT FAR*) GlobalLock (*phmfp);
    ErrZS (pmfp, E_OUTOFMEMORY);
    if (0==cxIcon)
    {
        // In units of pixels
        Verify (cxIcon = GetSystemMetrics (SM_CXICON));
        Verify (cyIcon = GetSystemMetrics (SM_CYICON));
        // In units of .01 millimeter
        cxIconHiMetric = (int)(long)wPixelsToHiMetric (cxIcon, giPpliX) ;
        cyIconHiMetric = (int)(long)wPixelsToHiMetric (cyIcon, giPpliY) ;
    }
    pmfp->mm   = MM_ANISOTROPIC;
    pmfp->xExt = cxIconHiMetric;
    pmfp->yExt = cyIconHiMetric;
    hdc = CreateMetaFile (NULL);
    SetWindowOrg (hdc, 0, 0);
    SetWindowExt (hdc, cxIcon, cyIcon);
    DrawIcon (hdc, 0, 0, hicon);
    pmfp->hMF = CloseMetaFile (hdc);
    ErrZ (pmfp->hMF);
    Assert (wIsValidHandle (pmfp->hMF, NULL));
    GlobalUnlock (*phmfp);
    Assert (wIsValidHandle (*phmfp, CF_METAFILEPICT));
    return NOERROR;

  errRtn:
    if (pmfp)
        GlobalUnlock (*phmfp);
    if (*phmfp)
        GlobalFree (*phmfp);
    return hresult;
}
#endif

#define DWTIMEOUT 1000L

INTERNAL wTimedGetMessage
    (LPMSG pmsg,
    HWND hwnd,
    WORD wFirst,
    WORD wLast)
{
    DWORD dwStartTickCount = GetTickCount();
    while (!PeekMessage (pmsg, hwnd, wFirst, wLast, PM_REMOVE))
    {
        if (GetTickCount() - dwStartTickCount > DWTIMEOUT)
        {
            if (!IsWindow (hwnd))
                return ResultFromScode (RPC_E_CONNECTION_LOST);
            else
                return ResultFromScode (RPC_E_SERVER_DIED);
        }
    }
    return NOERROR;
}


INTERNAL wNormalize
    (LPFORMATETC pformatetcIn,
    LPFORMATETC  pformatetcOut)
{
    if (pformatetcIn->cfFormat == 0
        && pformatetcIn->ptd == NULL            // Is WildCard
        && pformatetcIn->dwAspect == -1L
        && pformatetcIn->lindex == -1L
        && pformatetcIn->tymed == -1L)
    {
        pformatetcOut->cfFormat = CF_METAFILEPICT;
        pformatetcOut->ptd      = NULL;
        pformatetcOut->dwAspect = DVASPECT_CONTENT;
        pformatetcOut->lindex = DEF_LINDEX;
        pformatetcOut->tymed = TYMED_MFPICT;
    }
    else
    {
        memcpy (pformatetcOut, pformatetcIn, sizeof(FORMATETC));
    }
    return NOERROR;
}



INTERNAL wVerifyFormatEtc
    (LPFORMATETC pformatetc)
{
    intrDebugOut((DEB_ITRACE,
          "wVerifyFormatEtc(pformatetc=%x)\n",
          pformatetc));

    VDATEPTRIN  (pformatetc, FORMATETC);
    if (!HasValidLINDEX(pformatetc))
    {
        intrDebugOut((DEB_IERROR, "\t!HasValidLINDEX(pformatetc)\n"));
        return(DV_E_LINDEX);
    }

    if (0==(pformatetc->tymed & (TYMED_HGLOBAL | TYMED_MFPICT | TYMED_GDI)))
    {
   intrDebugOut((DEB_IERROR,
              "\t0==(pformatetc->tymed & (TYMED_HGLOBAL | TYMED_MFPICT | TYMED_GDI))\n"));
   return ResultFromScode (DV_E_TYMED);
    }
    if (0==(UtFormatToTymed (pformatetc->cfFormat) & pformatetc->tymed))
    {
   intrDebugOut((DEB_IERROR,
              "\t0==(UtFormatToTymed (pformatetc->cfFormat) & pformatetc->tymed)\n"));
        return ResultFromScode (DV_E_TYMED);
    }
    if (0==(pformatetc->dwAspect & (DVASPECT_CONTENT | DVASPECT_ICON)))
    {
   intrDebugOut((DEB_IERROR,
              "\t0==(pformatetc->dwAspect & (DVASPECT_CONTENT | DVASPECT_ICON))\n"));

        return ResultFromScode (DV_E_DVASPECT);
    }
    if (pformatetc->dwAspect & DVASPECT_ICON)
    {
        if (CF_METAFILEPICT != pformatetc->cfFormat)
   {
   intrDebugOut((DEB_IERROR,
              "\tCF_METAFILEPICT != pformatetc->cfFormat\n"));
            return ResultFromScode (DV_E_CLIPFORMAT);
   }

        if (0==(pformatetc->tymed & TYMED_MFPICT))
   {
   intrDebugOut((DEB_IERROR,
              "\t0==(pformatetc->tymed & TYMED_MFPICT)\n"));
            return ResultFromScode (DV_E_TYMED);
   }
    }
    if (pformatetc->ptd)
    {
        if (IsBadReadPtr (pformatetc->ptd, sizeof (DWORD))
            || IsBadReadPtr (pformatetc->ptd, (size_t)pformatetc->ptd->tdSize))
        {
       intrDebugOut((DEB_IERROR,"\tDV_E_DVTARGETDEVICE\n"));

            return ResultFromScode (DV_E_DVTARGETDEVICE);
        }
    }
    return NOERROR;
}



INTERNAL wClassesMatch
    (REFCLSID clsidIn,
    LPOLESTR szFile)
{
    CLSID clsid;
    if (NOERROR==GetClassFile (szFile, &clsid))
    {
        return clsid==clsidIn ? NOERROR : ResultFromScode (S_FALSE);
    }
    else
    {
        // If we can't determine the class of the file (because it's
        // not a real file) then OK.  Bug 3937.
        return NOERROR;
    }
}



#ifdef KEVINRO_DUPLICATECODE

This routine also appears in ole1.lib in the OLE232\OLE1 directory

INTERNAL wWriteFmtUserType
    (LPSTORAGE pstg,
    REFCLSID   clsid)
{
    HRESULT hresult    = NOERROR;
    LPOLESTR   szProgID   = NULL;
    LPOLESTR   szUserType = NULL;

    ErrRtnH (ProgIDFromCLSID (clsid, &szProgID));
    ErrRtnH (OleRegGetUserType (clsid, USERCLASSTYPE_FULL, &szUserType));
    ErrRtnH (WriteFmtUserTypeStg (pstg, RegisterClipboardFormat (szProgID),
                                    szUserType));
  errRtn:
    delete szProgID;
    delete szUserType;
    return hresult;
}
#endif

#if DBG == 1

INTERNAL_(BOOL) wIsValidHandle
    (HANDLE h,
    CLIPFORMAT cf)    // cf==NULL means normal memory
{
    LPVOID p;
    if (CF_BITMAP == cf)
    {
        BITMAP bm;
        return (0 != GetObject (h, sizeof(BITMAP), (LPVOID) &bm));
    }
    if (CF_PALETTE == cf)
    {
        WORD w;
        return (0 != GetObject (h, sizeof(w), (LPVOID) &w));
    }
    if (!(p=GlobalLock(h)))
    {
        Puts ("Invalid handle");
        Puth (h);
        Putn();
        return FALSE;
    }
    if (IsBadReadPtr (p, (WPARAM) min (UINT_MAX, GlobalSize(h))))
    {
        GlobalUnlock (h);
        return FALSE;
    }
    GlobalUnlock (h);
    return TRUE;
}
INTERNAL_(BOOL) wIsValidAtom (ATOM a)
{
    WCHAR sz[MAX_STR];
    if (a==0)
        return TRUE;
    if (a < 0xC000)
        return FALSE;
    if (0==GlobalGetAtomName (a, sz, MAX_STR))
        return FALSE;
    if ('\0'==sz[0])
        return FALSE;
    return TRUE;
}


// A "gentle" assert used in reterr.h
//


INTERNAL_(void) wWarn
    (LPSTR sz,
    LPSTR szFile,
    int iLine)
{
    intrDebugOut((DEB_WARN,
		  "Warning: %s:%u %s\n",
		  szFile,iLine,sz));
}

#endif // DBG


