823 lines
26 KiB
C
823 lines
26 KiB
C
/****************************** Module Header ******************************\
|
|
* Module Name: STDPTCL.C
|
|
*
|
|
* This module contains functions that implement the standard DDE protocol
|
|
*
|
|
* Created: 4/2/91 Sanfords
|
|
*
|
|
* Copyright (c) 1991 Microsoft Corporation
|
|
\***************************************************************************/
|
|
|
|
#include <string.h>
|
|
#include <memory.h>
|
|
#include "ddemlp.h"
|
|
|
|
|
|
VOID FreeDdeMsgData(
|
|
WORD msg,
|
|
LPARAM lParam)
|
|
{
|
|
WORD fmt;
|
|
|
|
switch (msg) {
|
|
case WM_DDE_DATA:
|
|
case WM_DDE_POKE:
|
|
/*
|
|
* Only free if fRelease is set!
|
|
*/
|
|
{
|
|
DDEDATA FAR *pDdeData = (DDEDATA FAR *)GLOBALLOCK(LOWORD(lParam));
|
|
if (pDdeData != NULL) {
|
|
if (pDdeData->fRelease) {
|
|
fmt = pDdeData->cfFormat;
|
|
GlobalUnlock(LOWORD(lParam));
|
|
FreeDDEData(LOWORD(lParam), fmt);
|
|
} else {
|
|
GlobalUnlock(LOWORD(lParam));
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_DDE_ADVISE:
|
|
GLOBALFREE(LOWORD(lParam));
|
|
break;
|
|
|
|
case WM_DDE_EXECUTE:
|
|
GLOBALFREE(HIWORD(lParam));
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/***************************** Private Function ****************************\
|
|
* Processes a client transfer request issued by DdeClientTransaction
|
|
* function. This may be synchronous or asynchronous.
|
|
*
|
|
* This function is responsible for properly filling in pXferInfo->pulResult.
|
|
*
|
|
* History:
|
|
* Created 9/1/89 Sanfords
|
|
\***************************************************************************/
|
|
long ClientXferReq(
|
|
PXFERINFO pXferInfo,
|
|
HWND hwnd,
|
|
PCLIENTINFO pci)
|
|
{
|
|
PCQDATA pcqd;
|
|
LAP myLostAck;
|
|
long retVal;
|
|
|
|
if (pci->ci.xad.state != XST_CONNECTED) {
|
|
SETLASTERROR(pci->ci.pai, DMLERR_SERVER_DIED);
|
|
return(0);
|
|
}
|
|
|
|
if (pXferInfo->ulTimeout == TIMEOUT_ASYNC) {
|
|
/*
|
|
* Create a client async queue if needed.
|
|
*/
|
|
if (pci->pQ == NULL)
|
|
pci->pQ = CreateQ(sizeof(CQDATA));
|
|
if (pci->pQ == NULL) {
|
|
SETLASTERROR(pci->ci.pai, DMLERR_MEMORY_ERROR);
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* add a client queue item to track this transaction and return
|
|
* the ID.
|
|
*/
|
|
pcqd = (PCQDATA)Addqi(pci->pQ);
|
|
if (pcqd == NULL) {
|
|
SETLASTERROR(pci->ci.pai, DMLERR_MEMORY_ERROR);
|
|
return(0);
|
|
}
|
|
|
|
IncHszCount(LOWORD(pXferInfo->hszItem)); // structure copy
|
|
hmemcpy((LPBYTE)&pcqd->XferInfo, (LPBYTE)pXferInfo, sizeof(XFERINFO));
|
|
pcqd->xad.state = XST_CONNECTED;
|
|
pcqd->xad.pdata = 0L;
|
|
pcqd->xad.LastError = DMLERR_NO_ERROR;
|
|
pcqd->xad.pXferInfo = &pcqd->XferInfo;
|
|
pcqd->xad.DDEflags = 0;
|
|
/*
|
|
* point pulResult to a safe place
|
|
*/
|
|
pcqd->XferInfo.pulResult = (LPDWORD)&pcqd->xad.DDEflags;
|
|
/*
|
|
* Get transaction started - if it fails, quit now.
|
|
*/
|
|
if ((pcqd->xad.LastError = SendClientReq(pci->ci.pai, &pcqd->xad,
|
|
(HWND)pci->ci.hConvPartner, hwnd)) == DMLERR_SERVER_DIED) {
|
|
pci->ci.fs = pci->ci.fs & ~ST_CONNECTED;
|
|
FreeHsz(LOWORD(pcqd->XferInfo.hszItem));
|
|
Deleteqi(pci->pQ, MAKEID(pcqd));
|
|
/*
|
|
* RARE case of server dyeing in the middle of transaction
|
|
* initiation.
|
|
*/
|
|
SETLASTERROR(pci->ci.pai, DMLERR_SERVER_DIED);
|
|
return(0);
|
|
}
|
|
if (pXferInfo->pulResult != NULL) {
|
|
*pXferInfo->pulResult = MAKEID(pcqd);
|
|
}
|
|
return(1);
|
|
|
|
}
|
|
|
|
/*
|
|
* Set this so messages comming in during the conversation know whats up
|
|
*/
|
|
pci->ci.xad.pXferInfo = pXferInfo;
|
|
|
|
if (SETLASTERROR(pci->ci.pai,
|
|
SendClientReq(pci->ci.pai, &pci->ci.xad, (HWND)pci->ci.hConvPartner, hwnd)) ==
|
|
DMLERR_SERVER_DIED) {
|
|
return(0);
|
|
}
|
|
|
|
// HACK
|
|
// If this is an EXEC and the timeout is 1 sec then this
|
|
// is probably PC Tools 2.0 trying to add items to the shell so
|
|
// crank up the timout.
|
|
|
|
if ((pXferInfo->wType == XTYP_EXECUTE) && (pXferInfo->ulTimeout == 1*1000))
|
|
{
|
|
pXferInfo->ulTimeout = 10*1000;
|
|
}
|
|
|
|
timeout(pci->ci.pai, pXferInfo->ulTimeout, hwnd);
|
|
|
|
retVal = ClientXferRespond(hwnd, &pci->ci.xad, &pci->ci.pai->LastError);
|
|
switch (pci->ci.xad.state) {
|
|
case XST_INCOMPLETE:
|
|
/* now add a record of the ack we expect to eventually get
|
|
* to the lost ack pile. When it arrives we'll know what to
|
|
* free up -- either a memory handle or an atom.
|
|
*/
|
|
myLostAck.type = pXferInfo->wType;
|
|
if (pXferInfo->wType == XTYP_EXECUTE)
|
|
myLostAck.object = HIWORD(pXferInfo->hDataClient);
|
|
else
|
|
myLostAck.object = LOWORD(pXferInfo->hszItem);
|
|
AddPileItem(pLostAckPile, (LPBYTE)&myLostAck, NULL);
|
|
pci->ci.xad.state = XST_CONNECTED;
|
|
}
|
|
if (pci->ci.fs & ST_DISC_ATTEMPTED) {
|
|
/*
|
|
* During this transaction a call to DdeDisconnect was attempted.
|
|
* complete the call now.
|
|
*/
|
|
Disconnect(hwnd, ST_PERM2DIE, pci);
|
|
}
|
|
return(retVal);
|
|
}
|
|
|
|
|
|
|
|
/***************************** Private Function ****************************\
|
|
* This routine sends the apropriate initiation messages for starting a
|
|
* client request according to the transaction data given.
|
|
*
|
|
* returns any appropriate DMLERR_
|
|
*
|
|
* History:
|
|
* Created 9/1/89 Sanfords
|
|
\***************************************************************************/
|
|
WORD SendClientReq(
|
|
PAPPINFO pai,
|
|
PXADATA pXad,
|
|
HWND hwndServer,
|
|
HWND hwnd)
|
|
{
|
|
WORD fsStatus = 0;
|
|
WORD msg;
|
|
WORD lo, hi;
|
|
HANDLE hData;
|
|
|
|
hi = LOWORD(pXad->pXferInfo->hszItem); /* all but exec need this */
|
|
|
|
switch (pXad->pXferInfo->wType) {
|
|
case XTYP_REQUEST:
|
|
msg = WM_DDE_REQUEST;
|
|
IncHszCount(hi); // message copy
|
|
#ifdef DEBUG
|
|
cAtoms--; // don't count this
|
|
#endif
|
|
lo = pXad->pXferInfo->wFmt;
|
|
pXad->state = XST_REQSENT;
|
|
break;
|
|
|
|
case XTYP_POKE:
|
|
msg = WM_DDE_POKE;
|
|
lo = HIWORD(pXad->pXferInfo->hDataClient);
|
|
if (!LOWORD(pXad->pXferInfo->hDataClient & HDATA_APPOWNED))
|
|
hData = lo; // need to free this on failed post.
|
|
pXad->state = XST_POKESENT;
|
|
XmitPrep(pXad->pXferInfo->hDataClient, pai);
|
|
break;
|
|
|
|
case XTYP_EXECUTE:
|
|
msg = WM_DDE_EXECUTE;
|
|
hi = HIWORD(pXad->pXferInfo->hDataClient);
|
|
if (!LOWORD(pXad->pXferInfo->hDataClient & HDATA_APPOWNED))
|
|
hData = hi; // need to free this on failed post.
|
|
lo = 0;
|
|
pXad->state = XST_EXECSENT;
|
|
// we DONT XmitPrep() because we retain responsibility over the
|
|
// data handle during the execute transaction regardless of
|
|
// the server's response.
|
|
// XmitPrep(pXad->pXferInfo->hDataClient, pai);
|
|
break;
|
|
|
|
case XTYP_ADVSTART:
|
|
case XTYP_ADVSTART | XTYPF_NODATA:
|
|
case XTYP_ADVSTART | XTYPF_ACKREQ:
|
|
case XTYP_ADVSTART | XTYPF_NODATA | XTYPF_ACKREQ:
|
|
fsStatus = DDE_FRELEASE | ((pXad->pXferInfo->wType &
|
|
(XTYPF_ACKREQ | XTYPF_NODATA)) << 12);
|
|
msg = WM_DDE_ADVISE;
|
|
if ((hData = AllocDDESel(fsStatus, pXad->pXferInfo->wFmt,
|
|
(DWORD)sizeof(DWORD))) == 0) {
|
|
pXad->state = XST_CONNECTED;
|
|
return(DMLERR_MEMORY_ERROR);
|
|
}
|
|
lo = hData;
|
|
pXad->pXferInfo->hDataClient = (HDDEDATA)MAKELONG(0, hData);
|
|
/* free later if we get nack */
|
|
pXad->state = XST_ADVSENT;
|
|
break;
|
|
|
|
case XTYP_ADVSTOP:
|
|
msg = WM_DDE_UNADVISE;
|
|
lo = pXad->pXferInfo->wFmt;
|
|
pXad->state = XST_UNADVSENT;
|
|
break;
|
|
|
|
default:
|
|
return(DMLERR_INVALIDPARAMETER);
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Send transfer
|
|
*/
|
|
if (IsWindow(hwndServer)) {
|
|
|
|
PCLIENTINFO pci;
|
|
|
|
pci = (PCLIENTINFO)GetWindowLong(hwnd, GWL_PCI);
|
|
|
|
if (!PostDdeMessage(&pci->ci, msg, hwnd, MAKELONG(lo,hi), 0, 0)) {
|
|
pXad->state = XST_CONNECTED;
|
|
if (hData)
|
|
FreeDDEData(hData, pXad->pXferInfo->wFmt);
|
|
return(DMLERR_POSTMSG_FAILED);
|
|
}
|
|
} else {
|
|
/*
|
|
* We lost the server, we are TERMINATED arnold!
|
|
*/
|
|
pXad->state = XST_NULL;
|
|
if (hData)
|
|
FreeDDEData(hData, pXad->pXferInfo->wFmt);
|
|
return(DMLERR_SERVER_DIED);
|
|
}
|
|
return(DMLERR_NO_ERROR);
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID ServerProcessDDEMsg(
|
|
PSERVERINFO psi,
|
|
WORD msg,
|
|
HWND hwndServer,
|
|
HWND hwndClient,
|
|
WORD lo,
|
|
WORD hi)
|
|
{
|
|
PADVLI pAdviseItem;
|
|
WORD wType;
|
|
DWORD dwData1 = 0;
|
|
HDDEDATA hData = 0L;
|
|
WORD wFmt = 0;
|
|
WORD wStat = 0;
|
|
LPSTR pMem;
|
|
HANDLE hMemFree = 0;
|
|
BOOL fQueueOnly = FALSE;
|
|
|
|
/*
|
|
* only respond if this is for us.
|
|
*/
|
|
if (hwndClient != (HWND)psi->ci.hConvPartner)
|
|
return;
|
|
|
|
if (!(psi->ci.fs & ST_CONNECTED)) {
|
|
/*
|
|
* Dde messages have been received AFTER we have terminated. Free up
|
|
* the information if appropriate.
|
|
* BUG: This doesn't handle NACK freeing of associated data.
|
|
*/
|
|
FreeDdeMsgData(msg, MAKELPARAM(lo, hi));
|
|
}
|
|
|
|
switch (msg) {
|
|
case WM_DDE_REQUEST:
|
|
wType = XTYP_REQUEST;
|
|
wFmt = lo;
|
|
break;
|
|
|
|
case WM_DDE_EXECUTE:
|
|
wType = XTYP_EXECUTE;
|
|
/* stuff a special flag into the low word to mark as exec data */
|
|
// We don't call RecvPrep() because we never have responsability for
|
|
// freeing this data.
|
|
hData = (HDDEDATA)MAKELONG(HDATA_EXEC | HDATA_NOAPPFREE | HDATA_READONLY, hi);
|
|
break;
|
|
|
|
case WM_DDE_POKE:
|
|
wType = XTYP_POKE;
|
|
pMem = GLOBALLOCK(lo);
|
|
if (pMem == NULL) {
|
|
SETLASTERROR(psi->ci.pai, DMLERR_MEMORY_ERROR);
|
|
return;
|
|
}
|
|
wFmt = ((DDEPOKE FAR*)pMem)->cfFormat;
|
|
wStat = *(WORD FAR*)pMem;
|
|
hData = RecvPrep(psi->ci.pai, lo, HDATA_NOAPPFREE);
|
|
break;
|
|
|
|
case WM_DDE_ADVISE:
|
|
wType = XTYP_ADVSTART; /* set ST_ADVISE AFTER app oks advise loop */
|
|
pMem = GLOBALLOCK(lo);
|
|
if (pMem == NULL) {
|
|
SETLASTERROR(psi->ci.pai, DMLERR_MEMORY_ERROR);
|
|
return;
|
|
}
|
|
wFmt = ((DDEADVISE FAR*)pMem)->cfFormat;
|
|
wStat = *(WORD FAR*)pMem;
|
|
// If this is NACKed, we don't free it (sicko protocol!#$@) so we
|
|
// have to have this hang around till qreply gets it.
|
|
hMemFree = lo;
|
|
|
|
/*
|
|
* Check if we already are linked on this topic/item/format. If so,
|
|
* skip the callback.
|
|
*/
|
|
fQueueOnly = (BOOL)(DWORD)FindAdvList(psi->ci.pai->pServerAdvList, hwndServer,
|
|
psi->ci.aTopic, (ATOM)hi, wFmt);
|
|
break;
|
|
|
|
case WM_DDE_UNADVISE:
|
|
{
|
|
PADVLI padvli;
|
|
ATOM aItem;
|
|
|
|
if (padvli = FindAdvList(psi->ci.pai->pServerAdvList,
|
|
hwndServer, 0, hi, lo)) {
|
|
wFmt = padvli->wFmt;
|
|
aItem = padvli->aItem;
|
|
wType = XTYP_ADVSTOP;
|
|
MONLINK(psi->ci.pai, FALSE, 0,
|
|
(HSZ)psi->ci.aServerApp, (HSZ)psi->ci.aTopic,
|
|
(HSZ)aItem, wFmt, TRUE,
|
|
MAKEHCONV(hwndServer), psi->ci.hConvPartner);
|
|
if (!DeleteAdvList(psi->ci.pai->pServerAdvList,
|
|
hwndServer, 0, aItem, wFmt)) {
|
|
psi->ci.fs &= ~ST_ADVISE;
|
|
} else {
|
|
while (padvli = FindAdvList(psi->ci.pai->pServerAdvList,
|
|
hwndServer, 0, hi, lo)) {
|
|
/*
|
|
* simulate extra XTYP_ADVSTOP callbacks to server here
|
|
*/
|
|
MONLINK(psi->ci.pai, FALSE, 0,
|
|
(HSZ)psi->ci.aServerApp, (HSZ)psi->ci.aTopic,
|
|
(HSZ)padvli->aItem, padvli->wFmt, TRUE,
|
|
MAKEHCONV(hwndServer), psi->ci.hConvPartner);
|
|
MakeCallback(&psi->ci, MAKEHCONV(hwndServer),
|
|
(HSZ)psi->ci.aTopic,
|
|
(HSZ)padvli->aItem, padvli->wFmt,
|
|
XTYP_ADVSTOP, 0, 0, 0, msg, wStat,
|
|
NULL, // signals qreply to NOT ack
|
|
0, FALSE);
|
|
if (!DeleteAdvList(psi->ci.pai->pServerAdvList,
|
|
hwndServer, 0, padvli->aItem, padvli->wFmt)) {
|
|
psi->ci.fs &= ~ST_ADVISE;
|
|
}
|
|
}
|
|
}
|
|
MakeCallback(&psi->ci, MAKEHCONV(hwndServer), (HSZ)psi->ci.aTopic,
|
|
(HSZ)aItem, wFmt, XTYP_ADVSTOP, 0, 0,
|
|
(HSZ)hi, // item for ACK - see qreply
|
|
msg, wStat, (HWND)psi->ci.hConvPartner, 0, FALSE);
|
|
} else {
|
|
/* unexpected unadvise, NACK it. */
|
|
PostDdeMessage(&psi->ci, WM_DDE_ACK,
|
|
hwndServer, MAKELONG(0, hi), 0, 0);
|
|
return;
|
|
}
|
|
return;
|
|
}
|
|
|
|
case WM_DDE_ACK:
|
|
/*
|
|
* This is an ack in response to the FACKREQ bit being set.
|
|
* See if this refers to one of the advise loops.
|
|
*/
|
|
if ((pAdviseItem = FindAdvList(psi->ci.pai->pServerAdvList,
|
|
hwndServer, 0, hi, wFmt)) &&
|
|
(pAdviseItem->fsStatus & DDE_FACKREQ)) {
|
|
/*
|
|
* Update advise loop status - no longer waiting for an ack.
|
|
*/
|
|
pAdviseItem->fsStatus &= ~ADVST_WAITING;
|
|
if (pAdviseItem->fsStatus & ADVST_CHANGED) {
|
|
PostServerAdvise(hwndServer, psi, pAdviseItem, CADV_LATEACK);
|
|
}
|
|
}
|
|
if (hi)
|
|
GlobalDeleteAtom(hi); // message copy
|
|
// BUG: if a NACK is posted to us, WE need to free any data associated
|
|
// with it.
|
|
return;
|
|
}
|
|
|
|
MakeCallback(&psi->ci, MAKEHCONV(hwndServer), (HSZ)psi->ci.aTopic,
|
|
/* don't know the item on an execute */
|
|
wType == XTYP_EXECUTE ? 0L : MAKELONG(hi,0),
|
|
wFmt, wType, hData, dwData1, 0, msg, wStat, (HWND)psi->ci.hConvPartner,
|
|
hMemFree, fQueueOnly);
|
|
/*
|
|
* all freeing of atoms and hXXXX stuff is in QReply
|
|
*/
|
|
return;
|
|
}
|
|
|
|
|
|
VOID PostServerAdvise(
|
|
HWND hwnd,
|
|
PSERVERINFO psi,
|
|
PADVLI pali,
|
|
WORD cLoops)
|
|
{
|
|
HDDEDATA hData;
|
|
HANDLE hMem;
|
|
|
|
/*
|
|
* get the data from the server.
|
|
*/
|
|
hData = DoCallback(psi->ci.pai, MAKEHCONV(hwnd), (HSZ)psi->ci.aTopic,
|
|
(HSZ)pali->aItem, pali->wFmt, XTYP_ADVREQ, 0L,
|
|
(DWORD)cLoops, 0L);
|
|
|
|
if (!hData) {
|
|
return;
|
|
}
|
|
|
|
hData = DllEntry(&psi->ci, hData);
|
|
|
|
pali->fsStatus &= ~ADVST_CHANGED;
|
|
|
|
/*
|
|
* set Ack Request bit if advise loop calls for it.
|
|
*/
|
|
if (pali->fsStatus & DDE_FACKREQ) {
|
|
LPWORD lpFlags;
|
|
|
|
pali->fsStatus |= ADVST_WAITING;
|
|
|
|
lpFlags = (LPWORD)GLOBALPTR(HIWORD(hData));
|
|
if (lpFlags == NULL) {
|
|
SETLASTERROR(psi->ci.pai, DMLERR_MEMORY_ERROR);
|
|
return;
|
|
}
|
|
if (!(*lpFlags & DDE_FACKREQ)) {
|
|
if (LOWORD(hData) & HDATA_APPOWNED) {
|
|
// can't mess with it, must use a copy.
|
|
hMem = HIWORD(hData);
|
|
hData = CopyHDDEDATA(psi->ci.pai, hData);
|
|
lpFlags = (LPWORD)GLOBALLOCK(HIWORD(hData));
|
|
}
|
|
*lpFlags |= DDE_FACKREQ;
|
|
}
|
|
}
|
|
/*
|
|
* remove local data handle from local list
|
|
*/
|
|
FindPileItem(psi->ci.pai->pHDataPile, CmpHIWORD, (LPBYTE)&hData, FPI_DELETE);
|
|
|
|
/*
|
|
* post data to waiting client.
|
|
*/
|
|
|
|
IncHszCount(pali->aItem); // message copy
|
|
#ifdef DEBUG
|
|
cAtoms--; // Don't count this because its being shipped out.
|
|
#endif
|
|
if (!PostDdeMessage(&psi->ci, WM_DDE_DATA, hwnd,
|
|
MAKELONG(HIWORD(hData), pali->aItem), 0, 0)) {
|
|
FreeDataHandle(psi->ci.pai, hData, TRUE);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************** Private Function ****************************\
|
|
* This routine handles callback replys.
|
|
* QReply is responsible for freeing any atoms or handles used
|
|
* with the message that generated the callback.
|
|
* It also must recover from dead partner windows.
|
|
*
|
|
* History:
|
|
* Created 9/12/89 Sanfords
|
|
\***************************************************************************/
|
|
void QReply(
|
|
PCBLI pcbi,
|
|
HDDEDATA hDataRet)
|
|
{
|
|
PSERVERINFO psi;
|
|
WORD fsStatus, msg;
|
|
WORD loOut, StatusRet, msgAssoc = 0;
|
|
HGLOBAL hGMemRet;
|
|
HGLOBAL hAssoc = 0;
|
|
|
|
SEMCHECKOUT();
|
|
|
|
// most notification callbacks require no work here.
|
|
|
|
if (((pcbi->wType & XCLASS_MASK) == XCLASS_NOTIFICATION) &&
|
|
(pcbi->wType != XTYP_ADVSTOP) &&
|
|
(pcbi->wType != XTYP_XACT_COMPLETE))
|
|
return;
|
|
|
|
StatusRet = LOWORD(hDataRet);
|
|
hGMemRet = HIWORD(hDataRet);
|
|
|
|
if (!IsWindow((HWND)pcbi->hConv)) {
|
|
if (pcbi->wType & XCLASS_DATA && hDataRet && hDataRet != CBR_BLOCK) {
|
|
FreeDataHandle(pcbi->pai, hDataRet, TRUE);
|
|
}
|
|
return;
|
|
}
|
|
|
|
psi = (PSERVERINFO)GetWindowLong((HWND)pcbi->hConv, GWL_PCI);
|
|
|
|
switch (pcbi->msg) {
|
|
case WM_DDE_REQUEST:
|
|
if (hGMemRet) {
|
|
hDataRet = DllEntry(&psi->ci, hDataRet);
|
|
loOut = HIWORD(hDataRet);
|
|
*(WORD FAR*)GLOBALLOCK(loOut) |=
|
|
((pcbi->fsStatus & DDE_FACKREQ) | DDE_FREQUESTED);
|
|
GlobalUnlock(loOut);
|
|
XmitPrep(hDataRet, psi->ci.pai);
|
|
msg = WM_DDE_DATA;
|
|
} else {
|
|
/*
|
|
* send a -ACK
|
|
*/
|
|
loOut = (StatusRet & (DDE_FBUSY | DDE_FAPPSTATUS));
|
|
msg = WM_DDE_ACK;
|
|
}
|
|
// reuse atom from request message
|
|
if (!PostDdeMessage(&psi->ci, msg, (HWND)pcbi->hConv,
|
|
MAKELONG(loOut, LOWORD(pcbi->hszItem)), 0, 0) && msg == WM_DDE_DATA)
|
|
FreeDataHandle(psi->ci.pai, hDataRet, TRUE);
|
|
break;
|
|
|
|
case WM_DDE_POKE:
|
|
if (StatusRet & DDE_FACK) {
|
|
FreeDataHandle(psi->ci.pai, pcbi->hData, TRUE);
|
|
} else {
|
|
// NACKS are properly freed by the 'poker'
|
|
FindPileItem(psi->ci.pai->pHDataPile, CmpHIWORD,
|
|
(LPBYTE)&pcbi->hData, FPI_DELETE);
|
|
hAssoc = hGMemRet;
|
|
msgAssoc = WM_DDE_POKE;
|
|
}
|
|
if (!PostDdeMessage(&psi->ci, WM_DDE_ACK, (HWND)pcbi->hConv,
|
|
MAKELONG(StatusRet & ~DDE_FACKRESERVED, LOWORD(pcbi->hszItem)),
|
|
msgAssoc, hAssoc)) {
|
|
if (!(StatusRet & DDE_FACK)) {
|
|
FreeDDEData(hGMemRet, pcbi->wFmt);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_DDE_EXECUTE:
|
|
/*
|
|
* LOWORD(hDataRet) is supposed to be the proper DDE_ constants to return.
|
|
* we just stick them in the given hData and return
|
|
* it as an ACK.
|
|
*/
|
|
PostDdeMessage(&psi->ci, WM_DDE_ACK, (HWND)pcbi->hConv,
|
|
MAKELONG(StatusRet & ~DDE_FACKRESERVED,HIWORD(pcbi->hData)),
|
|
0, 0);
|
|
break;
|
|
|
|
case WM_DDE_ADVISE:
|
|
/*
|
|
* hDataRet is fStartAdvise
|
|
*/
|
|
if ((BOOL)hDataRet) {
|
|
if (!AddAdvList(psi->ci.pai->pServerAdvList, (HWND)pcbi->hConv, psi->ci.aTopic,
|
|
(ATOM)pcbi->hszItem,
|
|
pcbi->fsStatus & (DDE_FDEFERUPD | DDE_FACKREQ),
|
|
pcbi->wFmt)) {
|
|
SETLASTERROR(psi->ci.pai, DMLERR_MEMORY_ERROR);
|
|
fsStatus = 0;
|
|
} else {
|
|
MONLINK(psi->ci.pai, TRUE, pcbi->fsStatus & DDE_FDEFERUPD,
|
|
(HSZ)psi->ci.aServerApp, (HSZ)psi->ci.aTopic,
|
|
pcbi->hszItem, pcbi->wFmt, TRUE,
|
|
pcbi->hConv, psi->ci.hConvPartner);
|
|
psi->ci.fs |= ST_ADVISE;
|
|
fsStatus = DDE_FACK;
|
|
}
|
|
GlobalUnlock(pcbi->hMemFree);
|
|
GLOBALFREE(pcbi->hMemFree); /* we free the hOptions on ACK */
|
|
} else {
|
|
fsStatus = 0;
|
|
hAssoc = hGMemRet;
|
|
msgAssoc = WM_DDE_ADVISE;
|
|
#ifdef DEBUG
|
|
if (pcbi->hMemFree) {
|
|
LogDdeObject(0xF000, pcbi->hMemFree);
|
|
}
|
|
#endif
|
|
}
|
|
goto AckBack;
|
|
break;
|
|
|
|
case WM_DDE_UNADVISE:
|
|
fsStatus = DDE_FACK;
|
|
if (pcbi->hwndPartner) { // set to null for simulated stops due to WILD stuff
|
|
// dwData2 == aItem to ack - this could have been wild.
|
|
PostDdeMessage(&psi->ci, WM_DDE_ACK, (HWND)pcbi->hConv,
|
|
MAKELONG(fsStatus, LOWORD(pcbi->dwData2)), 0, 0);
|
|
}
|
|
break;
|
|
|
|
case WM_DDE_DATA:
|
|
/*
|
|
* must be an advise data item for the CLIENT or maybe some requested
|
|
* data mistakenly sent here due to the client queue being flushed.
|
|
* hDataRet is fsStatus.
|
|
*/
|
|
/*
|
|
* Clean up the status incase the app is messed up.
|
|
*/
|
|
fsStatus = StatusRet & ~DDE_FACKRESERVED;
|
|
|
|
if (HIWORD(pcbi->hData) &&
|
|
(pcbi->fsStatus & DDE_FRELEASE) &&
|
|
(fsStatus & DDE_FACK || !(pcbi->fsStatus & DDE_FACKREQ)))
|
|
FreeDataHandle(psi->ci.pai, pcbi->hData, TRUE);
|
|
|
|
if (fsStatus & DDE_FBUSY)
|
|
fsStatus &= ~DDE_FACK;
|
|
|
|
if (HIWORD(pcbi->hData) && !(fsStatus & DDE_FACK)) {
|
|
msgAssoc = WM_DDE_DATA;
|
|
hAssoc = HIWORD(pcbi->hData);
|
|
}
|
|
/*
|
|
* send an ack back if requested.
|
|
*/
|
|
if (pcbi->fsStatus & DDE_FACKREQ) {
|
|
AckBack:
|
|
PostDdeMessage(&psi->ci, WM_DDE_ACK, (HWND)pcbi->hConv,
|
|
MAKELONG(fsStatus, LOWORD(pcbi->hszItem)),
|
|
msgAssoc, hAssoc);
|
|
} else {
|
|
if (LOWORD(pcbi->hszItem)) {
|
|
GlobalDeleteAtom(LOWORD(pcbi->hszItem)); // data message copy
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 0:
|
|
switch (pcbi->wType) {
|
|
case XTYP_XACT_COMPLETE:
|
|
FreeHsz(LOWORD(pcbi->hszItem));
|
|
FreeDataHandle(psi->ci.pai, pcbi->hData, TRUE);
|
|
Deleteqi(((PCLIENTINFO)psi)->pQ, pcbi->dwData1);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/***************************** Private Function ****************************\
|
|
* This function assumes that a client transfer request has been completed -
|
|
* or should be completed by the time this is called.
|
|
*
|
|
* pci contains general client info
|
|
* pXad contains the transaction info
|
|
* pErr points to where to place the LastError code.
|
|
*
|
|
* Returns 0 on failure
|
|
* Returns TRUE or a Data Selector on success.
|
|
* On failure, the conversation is left in a XST_INCOMPLETE state.
|
|
* On success, the conversation is left in a XST_CONNECTED state.
|
|
*
|
|
* History:
|
|
* Created 9/1/89 Sanfords
|
|
\***************************************************************************/
|
|
long ClientXferRespond(
|
|
HWND hwndClient,
|
|
PXADATA pXad,
|
|
LPWORD pErr)
|
|
{
|
|
PCLIENTINFO pci;
|
|
|
|
if (pXad->state == XST_INCOMPLETE)
|
|
return(0);
|
|
|
|
pci = (PCLIENTINFO)GetWindowLong(hwndClient, GWL_PCI);
|
|
switch (pXad->pXferInfo->wType) {
|
|
case XTYP_REQUEST:
|
|
if (pXad->state != XST_DATARCVD) {
|
|
if (*pErr == DMLERR_NO_ERROR)
|
|
*pErr = DMLERR_DATAACKTIMEOUT;
|
|
goto failexit;
|
|
}
|
|
pXad->state = XST_CONNECTED;
|
|
return(pXad->pdata); /* this has the handle in low word */
|
|
break;
|
|
|
|
case XTYP_POKE:
|
|
if (pXad->state != XST_POKEACKRCVD) {
|
|
if (*pErr == DMLERR_NO_ERROR)
|
|
*pErr = DMLERR_POKEACKTIMEOUT;
|
|
goto failexit;
|
|
}
|
|
passexit:
|
|
pXad->state = XST_CONNECTED;
|
|
pXad->pdata = TRUE;
|
|
return(TRUE);
|
|
break;
|
|
|
|
case XTYP_EXECUTE:
|
|
if (pXad->state != XST_EXECACKRCVD) {
|
|
if (*pErr == DMLERR_NO_ERROR)
|
|
*pErr = DMLERR_EXECACKTIMEOUT;
|
|
goto failexit;
|
|
}
|
|
goto passexit;
|
|
|
|
case XTYP_ADVSTART:
|
|
case XTYP_ADVSTART | XTYPF_NODATA:
|
|
case XTYP_ADVSTART | XTYPF_ACKREQ:
|
|
case XTYP_ADVSTART | XTYPF_NODATA | XTYPF_ACKREQ:
|
|
if (pXad->state != XST_ADVACKRCVD) {
|
|
if (*pErr == DMLERR_NO_ERROR)
|
|
*pErr = DMLERR_ADVACKTIMEOUT;
|
|
goto failexit;
|
|
}
|
|
AssertF((UINT)(XTYPF_ACKREQ << 12) == DDE_FACKREQ &&
|
|
(UINT)(XTYPF_NODATA << 12) == DDE_FDEFERUPD,
|
|
"XTYPF_ constants are wrong");
|
|
if (!AddAdvList(pci->pClientAdvList, hwndClient, 0,
|
|
LOWORD(pXad->pXferInfo->hszItem),
|
|
(pXad->pXferInfo->wType & (XTYPF_ACKREQ | XTYPF_NODATA)) << 12,
|
|
pXad->pXferInfo->wFmt)) {
|
|
pXad->state = XST_INCOMPLETE;
|
|
SETLASTERROR(pci->ci.pai, DMLERR_MEMORY_ERROR);
|
|
return(FALSE);
|
|
} else {
|
|
pci->ci.fs |= ST_ADVISE;
|
|
MONLINK(pci->ci.pai, TRUE, pXad->pXferInfo->wType & XTYPF_NODATA,
|
|
(HSZ)pci->ci.aServerApp, (HSZ)pci->ci.aTopic,
|
|
pXad->pXferInfo->hszItem, pXad->pXferInfo->wFmt, FALSE,
|
|
pci->ci.hConvPartner, MAKEHCONV(hwndClient));
|
|
goto passexit;
|
|
}
|
|
break;
|
|
|
|
case XTYP_ADVSTOP:
|
|
if (pXad->state != XST_UNADVACKRCVD) {
|
|
if (*pErr == DMLERR_NO_ERROR)
|
|
*pErr = DMLERR_UNADVACKTIMEOUT;
|
|
goto failexit;
|
|
}
|
|
if (!DeleteAdvList(pci->pClientAdvList, 0, 0,
|
|
(ATOM)pXad->pXferInfo->hszItem, pXad->pXferInfo->wFmt))
|
|
pci->ci.fs &= ~ST_ADVISE;
|
|
MONLINK(pci->ci.pai, FALSE, pXad->pXferInfo->wType & XTYPF_NODATA,
|
|
(HSZ)pci->ci.aServerApp, (HSZ)pci->ci.aTopic,
|
|
pXad->pXferInfo->hszItem, pXad->pXferInfo->wFmt, FALSE,
|
|
pci->ci.hConvPartner, MAKEHCONV(hwndClient));
|
|
goto passexit;
|
|
}
|
|
|
|
failexit:
|
|
pXad->state = XST_INCOMPLETE;
|
|
return(0);
|
|
}
|
|
|