|
|
/****************************** Module Header ******************************\
* * Module Name: DMGWNDP.C * * This module contains all the window procs for the DDE manager. * * Created: 12/23/88 sanfords * * Copyright (c) 1988, 1989 Microsoft Corporation \***************************************************************************/ #include "ddemlp.h"
VOID FreeDdeMsgData(WORD msg, LPARAM lParam);
/*
* ----------------CLIENT SECTION------------------ * * Each client conversation has associated with it a window and a queue. * A conversation has one synchronous transaction and may have many * asynchronous transactions. A transaction is differientiated by its * state and other pertinant data. A transaction may be synchronous, * asynchronous, (initiated by DdeMgrClientTransaction()), or it may be external, * (initiated by an advise loop.) * * A transaction is active if it is in the middle of tranfer, otherwise * it is shutdown. A shutdown transaction is either successful or * failed. When an asynchronous transaction shuts down, the client * is notified via the callback function. (XTYP_XACT_COMPLETE) * * The synchronous transaction, when active, is in a timeout loop which * can shut-down the transaction at the end of a predefined time period. * Shutdown synchronous transactions imediately transfer their information * to the client application by returning to DdeClientTransaction(). * * active asynchronous transactions remain in the client queue until removed * by the client application via DdeAbandonTransaction() or by transaction * completion. * * external transactions take place when the client is in an advise * data loop. These transactions pass through the callback function to * the client to be accepted.(XTYP_ADVDATA) */
/***************************** Private Function ****************************\
* long EXPENTRY ClientWndProc(hwnd, msg, mp1, mp2); * * This window controls a single DDE conversation from the CLIENT side. * If closed, it will automaticly abort any conversationn in progress. * It maintains an internal list of any extra WM_DDEINITIATEACK messages * it receives so that it can be queried later about this information. * Any extra WM_DDEINITIATEACK messages comming in will be immediately * terminated. * It also maintains an internal list of all items which currently are * in active ADVISE loops. * * History: * Created 12/16/88 SANFORDS \***************************************************************************/ LONG EXPENTRY ClientWndProc(hwnd, msg, wParam, lParam) HWND hwnd; WORD msg; WORD wParam; DWORD lParam; { register PCLIENTINFO pci; long mrData;
#ifdef DEBUG
LogDdeObject(msg | 0x4000, lParam); #endif
pci = (PCLIENTINFO)GetWindowLong(hwnd, GWL_PCI);
switch (msg) { case WM_CREATE: return(ClientCreate(hwnd, LPCREATESTRUCT_GETPAI(lParam))); break;
case UM_SETBLOCK: pci->ci.fs = (pci->ci.fs & ~(ST_BLOCKED | ST_BLOCKNEXT)) | wParam; if (!wParam || wParam & ST_BLOCKNEXT) { EmptyDDEPostQ(); } break;
case WM_DDE_ACK: if (pci->ci.xad.state == XST_INIT1 || pci->ci.xad.state == XST_INIT2) { ClientInitAck(hwnd, pci, wParam, (ATOM)LOWORD(lParam),(ATOM)HIWORD(lParam)); //
// This always returns TRUE -NOT BECAUSE THAT'S WHAT THE PROTOCOL
// CALLS FOR but because some bad sample code got out and so
// a lot of apps out there will delete the WM_DDE_ACK atoms
// if a FALSE is returned.
//
return(TRUE); } else { DoClientDDEmsg(pci, hwnd, msg, (HWND)wParam, lParam); return(0); } break;
case WM_DDE_DATA: DoClientDDEmsg(pci, hwnd, msg, (HWND)wParam, lParam); break;
case UM_QUERY: /*
* wParam = info index. * lParam = pData. If pData==0, return data else copy into pData. */ switch (wParam) { case Q_CLIENT: mrData = TRUE; break;
case Q_APPINFO: mrData = (long)(LPSTR)pci->ci.pai; break; } if (lParam == 0) return(mrData); else *(long FAR *)lParam = mrData; return(1); break;
case WM_DDE_TERMINATE: case UM_TERMINATE: Terminate(hwnd, wParam, pci); break;
case WM_TIMER: if (wParam == TID_TIMEOUT) { pci->ci.pai->wTimeoutStatus |= TOS_TICK; } break;
case UM_DISCONNECT: Disconnect(hwnd, wParam, pci); break;
case WM_DESTROY: SEMCHECKOUT(); if (pci->ci.fs & ST_CONNECTED) { pci->ci.fs &= ~ST_PERM2DIE; // stops infinite loop
Disconnect(hwnd, 0, pci); } if (pci->ci.fs & ST_NOTIFYONDEATH) { HWND hwndOwner;
hwndOwner = GetWindow(hwnd, GW_OWNER); if (hwndOwner) PostMessage(hwndOwner, UM_DISCONNECT, ST_IM_DEAD, 0L); } SEMENTER(); DestroyQ(pci->pQ); pci->pQ = NULL; DestroyQ(pci->ci.pPMQ); pci->ci.pPMQ = NULL; CleanupAdvList(hwnd, pci); DestroyLst(pci->pClientAdvList); if (pci->ci.xad.state != XST_INIT1) { FreeHsz(LOWORD(pci->ci.hszSvcReq)); FreeHsz(pci->ci.aServerApp); FreeHsz(pci->ci.aTopic); } /*
* remove all plstCB entries that reference this window. */ { PCBLI pli, pliNext;
for (pli = (PCBLI)pci->ci.pai->plstCB->pItemFirst; pli != NULL; pli = (PCBLI)pliNext) { pliNext = (PCBLI)pli->next; if ((HWND)pli->hConv == hwnd) { if (((PCBLI)pli)->hMemFree) { GLOBALFREE(((PCBLI)pli)->hMemFree); } RemoveLstItem(pci->ci.pai->plstCB, (PLITEM)pli); } } } FarFreeMem((LPBYTE)pci); SEMLEAVE(); // fall through
default: return(DefWindowProc(hwnd, msg, wParam, lParam)); break; } return(0); }
/***************************** Private Function ****************************\
* This handles client window processing of WM_DDE_ACK and WM_DDE_DATA msgs. * (Note that Acks to INITIATE messages are handled in ClientInitAck.) * On exit pddes is freed. * * History: * Created 9/1/89 Sanfords \***************************************************************************/ BOOL DoClientDDEmsg( PCLIENTINFO pci, HWND hwndClient, WORD msg, HWND hwndServer, DWORD lParam) { PCQDATA pqd; int i; ATOM aItem; LAP far *lpLostAck;
if (!(pci->ci.fs & ST_CONNECTED)) { FreeDdeMsgData(msg, lParam); return(FALSE); }
/*
* Check if it fits the synchronous transaction data */ if (fExpectedMsg(&pci->ci.xad, lParam, msg)) { if (AdvanceXaction(hwndClient, pci, &pci->ci.xad, lParam, msg, &pci->ci.pai->LastError)) { if (pci->ci.pai->hwndTimer) { pci->ci.pai->wTimeoutStatus |= TOS_DONE; } } return TRUE; }
/*
* See if it fits any asynchronous transaction data - if any exist */ if (pci->pQ != NULL && pci->pQ->pqiHead != NULL) { SEMENTER(); pqd = (PCQDATA)pci->pQ->pqiHead; /*
* cycle from oldest to newest. */ for (i = pci->pQ->cItems; i; i--) { pqd = (PCQDATA)pqd->next; if (!fExpectedMsg(&pqd->xad, lParam, msg)) continue; if (AdvanceXaction(hwndClient, pci, &pqd->xad, lParam, msg, &pqd->xad.LastError)) { ClientXferRespond(hwndClient, &pqd->xad, &pqd->xad.LastError); SEMLEAVE(); pci->ci.pai->LastError = pqd->xad.LastError; if (!pqd->xad.fAbandoned) { MakeCallback(&pci->ci, MAKEHCONV(hwndClient), (HSZ)pci->ci.aTopic, pqd->xad.pXferInfo->hszItem, pqd->xad.pXferInfo->wFmt, XTYP_XACT_COMPLETE, pqd->xad.pdata, MAKEID(pqd), (DWORD)pqd->xad.DDEflags, 0, 0, hwndServer, 0, FALSE); } return TRUE; } SEMLEAVE(); return FALSE; } SEMLEAVE(); } /*
* It doesn't fit anything, assume its an advise data message. */ if (msg == WM_DDE_DATA) { DDE_DATA FAR *pMem; PADVLI padvli; WORD wStatus; WORD wFmt;
aItem = HIWORD(lParam); if (LOWORD(lParam)) { pMem = (DDE_DATA FAR*)GLOBALLOCK((HANDLE)LOWORD(lParam)); if (pMem == NULL) { SETLASTERROR(pci->ci.pai, DMLERR_MEMORY_ERROR); return(FALSE); } wFmt = pMem->wFmt; wStatus = pMem->wStatus; GLOBALUNLOCK((HANDLE)LOWORD(lParam)); } else { padvli = FindAdvList(pci->pClientAdvList, 0, 0, aItem, 0); if (padvli != NULL) { wFmt = padvli->wFmt; } else { wFmt = 0; } wStatus = DDE_FACK; }
if (wStatus & DDE_FREQUESTED) {
// Its out of line - drop it.
if (wStatus & DDE_FACKREQ) { // ACK it
PostDdeMessage(&pci->ci, WM_DDE_ACK, hwndClient, MAKELONG(DDE_FACK, aItem), 0, 0); } FreeDDEData((HANDLE)LOWORD(lParam), wFmt); if (aItem) GlobalDeleteAtom(aItem); return FALSE; } MakeCallback(&pci->ci, MAKEHCONV(hwndClient), (HSZ)pci->ci.aTopic, (HSZ)aItem, wFmt, XTYP_ADVDATA, RecvPrep(pci->ci.pai, LOWORD(lParam), HDATA_NOAPPFREE), 0, 0, msg, pMem ? wStatus : 0, (HWND)pci->ci.hConvPartner, 0, FALSE); return TRUE; }
AssertF(pci->ci.xad.state != XST_INIT1 && pci->ci.xad.state != XST_INIT2, "Init logic problem"); AssertF(msg == WM_DDE_ACK, "DoClientDDEMsg() logic problem");
/*
* throw it away ... first find the lost ack in in the lost ack pile */
if (lpLostAck = (LAP far *)FindPileItem(pLostAckPile, CmpWORD, PHMEM(lParam), FPI_DELETE)) { if (lpLostAck->type == XTYP_EXECUTE) { GLOBALFREE((HANDLE)HIWORD(lParam)); } else { if (HIWORD(lParam)) { GlobalDeleteAtom(HIWORD(lParam)); // message copy
} } } else { AssertF(FALSE, "DoClientDDEmsg: could not find lost ack"); // its a fairly safe assumption we didn't get a random execute ACK
// back so free the atom.
if (HIWORD(lParam)) { GlobalDeleteAtom(HIWORD(lParam)); // message copy
} }
return FALSE; }
/***************************** Private Function ****************************\
* This routine matches a conversation transaction with a DDE message. If * the state, wType, format, itemname dde structure data and the message * received all agree, TRUE is returned. It only handles DATA or ACK messages. * * History: * Created 9/1/89 Sanfords \***************************************************************************/ BOOL fExpectedMsg( PXADATA pXad, DWORD lParam, WORD msg) { DDEDATA FAR *pMem;
if (msg == WM_DDE_DATA) { BOOL fRet;
if (pXad->state != XST_REQSENT) return(FALSE);
if (!(pMem = (DDEDATA FAR*)GLOBALLOCK(LOWORD(lParam)))) return(FALSE);
/* make sure the format and item name match */
fRet = pMem->fResponse && ((WORD)pMem->cfFormat == pXad->pXferInfo->wFmt) && (HIWORD(lParam) == LOWORD(pXad->pXferInfo->hszItem)); GLOBALUNLOCK(LOWORD(lParam)); return(fRet); }
switch (pXad->state) { case XST_REQSENT: case XST_POKESENT: case XST_ADVSENT: case XST_UNADVSENT: return((msg == WM_DDE_ACK) && HIWORD(lParam) == LOWORD(pXad->pXferInfo->hszItem)); break;
case XST_EXECSENT: /* we expect an ACK with a data handle matching that sent */ return((msg == WM_DDE_ACK) && (HIWORD(lParam) == HIWORD(pXad->pXferInfo->hDataClient))); break; }
return(FALSE); }
/***************************** Private Function ****************************\
* This function assumes that msg is an apropriate message for the transaction * referenced by pXad. It acts on msg as apropriate. pddes is the DDESTRUCT * associated with msg. * * Returns fSuccess ie: transaction is ready to close up. * * History: * Created 9/1/89 Sanfords \***************************************************************************/ BOOL AdvanceXaction(hwnd, pci, pXad, lParam, msg, pErr) HWND hwnd; PCLIENTINFO pci; PXADATA pXad; DWORD lParam; WORD msg; LPWORD pErr; { HANDLE hData; LPSTR pMem; WORD lo,hi;
pXad->DDEflags = 0; lo = LOWORD(lParam); hi = HIWORD(lParam);
switch (msg) { case WM_DDE_ACK: if (pXad->state == XST_EXECSENT || !(lo & DDE_FACK)) FreeDataHandle(pci->ci.pai, pXad->pXferInfo->hDataClient, TRUE); if (pXad->pXferInfo->pulResult != NULL) *(LPWORD)pXad->pXferInfo->pulResult = lo;
switch (pXad->state) { case XST_ADVSENT: case XST_EXECSENT: case XST_POKESENT: case XST_REQSENT: case XST_UNADVSENT: if (lo & DDE_FACK) { /*
* handle successes */ switch (pXad->state) { case XST_POKESENT: pXad->state = XST_POKEACKRCVD; break;
case XST_EXECSENT: pXad->state = XST_EXECACKRCVD; break;
case XST_ADVSENT: pXad->state = XST_ADVACKRCVD; break;
case XST_UNADVSENT: pXad->state = XST_UNADVACKRCVD; break;
case XST_REQSENT: /*
* requests are not expected to send a +ACK. only * -ACK or data. We ignore a +ACK to a request. */ return(FALSE); } } else { // NACK
/*
* handle the expected ACK failures. */ hData = (HANDLE)HIWORD(pXad->pXferInfo->hDataClient); *pErr = DMLERR_NOTPROCESSED; if (lo & DDE_FBUSY) *pErr = DMLERR_BUSY; switch (pXad->state) { case XST_POKESENT: /* free the hData sent with original message */ /* but only if fRelease was set */ pMem = GLOBALLOCK(hData); /* we stowed the handle in lo word */ if (pMem && ((DDEPOKE FAR*)pMem)->fRelease) FreeDDEData(hData, ((DDEPOKE FAR*)pMem)->cfFormat); break;
case XST_ADVSENT: /* free the hOptions sent with original message */ /* we stowed the handle in hDataClient */ GLOBALFREE(hData); break;
} pXad->state = XST_INCOMPLETE; } } return(TRUE); break;
case WM_DDE_DATA: switch (pXad->state) { case XST_REQSENT: case XST_ADVSENT: pXad->state = XST_DATARCVD; /*
* send an ack if requested - we dare not return the given * lParam because it may be a data item sent to several * clients and we would mess up the fsStatus word for * all processes involved. */ pMem = GLOBALLOCK((HANDLE)lo);
if (pMem != NULL) { if (!((DDEDATA FAR*)pMem)->fRelease) { //
// Since this is potentially a synchronous request which
// the app has to free, we must give the app a copy
// so the origonal one can safely be freed by the server.
//
pXad->pdata = CopyHDDEDATA(pci->ci.pai, MAKELONG(0, lo)); } else { pXad->pdata = RecvPrep(pci->ci.pai, lo, 0); } if (((DDEDATA FAR*)pMem)->fAckReq) { // reuse atom from data message
PostDdeMessage(&pci->ci, WM_DDE_ACK, hwnd, MAKELONG(DDE_FACK, hi), 0, 0); } else { if (hi) { GlobalDeleteAtom(hi); // free message copy
} } } return(TRUE); break; } } return(FALSE); }
VOID CheckCBQ( PAPPINFO pai) { PCBLI pli, pliNext; PCLIENTINFO pci; BOOL fBreak; DWORD dwRet;
/*
* This is where we actually do callbacks. We do them via this * window proc so that we can asynchronously institute callbacks * via a PostMsg(). */ SEMCHECKOUT(); SEMENTER(); /*
* process all enabled conversation callbacks. */ fBreak = FALSE; for (pli = (PCBLI)pai->plstCB->pItemFirst; pai->lpMemReserve && !fBreak && pli; pli = (PCBLI)pliNext) { pliNext = (PCBLI)pli->next;
if (pai->cInProcess) // covers us on recursion
break;
// auto-flush for dead conversations.
if (!IsWindow((HWND)pli->hConv) || ((pci = (PCLIENTINFO)GetWindowLong((HWND)pli->hConv, GWL_PCI)) == NULL) || !(pci->ci.fs & ST_CONNECTED)) { /*
* auto-flush for disconnected conversations. */ if (((PCBLI)pli)->hMemFree) { GLOBALFREE(((PCBLI)pli)->hMemFree); } RemoveLstItem(pai->plstCB, (PLITEM)pli); continue; }
if (pci->ci.fs & ST_BLOCKED) continue;
if (pci->ci.fs & ST_BLOCKNEXT) { pci->ci.fs |= ST_BLOCKED; pci->ci.fs &= ~ST_BLOCKNEXT; }
if (pli->fQueueOnly) { dwRet = 0; #ifdef DEBUG
if (pli->hMemFree) { LogDdeObject(0xE000, pli->hMemFree); } #endif
} else { /*
* make the actual callback here. */ #ifdef DEBUG
if (pli->hMemFree) { LogDdeObject(0xD000, pli->hMemFree); } #endif
dwRet = DoCallback(pai, pli->hConv, pli->hszTopic, pli->hszItem, pli->wFmt, pli->wType, pli->hData, pli->dwData1, pli->dwData2); }
/*
* If the callback resulted in a BLOCK, disable this conversation. */ if (dwRet == CBR_BLOCK && !(pli->wType & XTYPF_NOBLOCK)) { pci->ci.fs |= ST_BLOCKED; continue; } else { /*
* otherwise finish processing the callback. */ QReply(pli, dwRet); RemoveLstItem(pai->plstCB, (PLITEM)pli); } } SEMLEAVE(); }
/*
* This function handles disconnecting a conversation window. afCmd contains * ST_ flags that describe what actions to take. */ void Disconnect( HWND hwnd, WORD afCmd, PCLIENTINFO pci) { if (afCmd & ST_CHECKPARTNER) { if (!IsWindow((HWND)pci->ci.hConvPartner)) { if (pci->ci.fs & ST_TERM_WAITING) { pci->ci.fs &= ~ST_TERM_WAITING; pci->ci.pai->cZombies--; TRACETERM((szT, "Disconnect: Checked partner is dead. Zombies decremented.\n")); pci->ci.fs |= ST_TERMINATED; } } afCmd &= ~ST_CHECKPARTNER; }
// Do NOT do disconnects within timeout loops!
if (pci->ci.pai->hwndTimer == hwnd) { pci->ci.pai->wTimeoutStatus |= TOS_ABORT; pci->ci.fs |= ST_DISC_ATTEMPTED; TRACETERM((szT, "Disconnect: defering disconnect of %x. Aborting timeout loop.\n", hwnd)); return; } /*
* note disconnect call for ddespy apps */ MONCONN(pci->ci.pai, pci->ci.aServerApp, pci->ci.aTopic, ((pci->ci.fs & ST_CLIENT) ? hwnd : (HWND)pci->ci.hConvPartner), ((pci->ci.fs & ST_CLIENT) ? (HWND)pci->ci.hConvPartner : hwnd), FALSE); /*
* or in optional ST_PERM2DIE bit from caller */ pci->ci.fs |= afCmd;
/*
* Terminate states fall through the following stages: * * 1) connected, not_waiting(for ack term) * 2) disconnected, waiting * 3) disconnected, not_waiting, terminated * 4) disconnected, not_waiting, terminated, perm to die * 5) self destruct window * * If the disconnect operation was originated by the other side: * * 1) connected, not_waiting * 2) disconected, not_waiting, terminated * 3) disconnected, not_waiting, teriminated perm to die * 4) self desstruct window * * Note that a postmessage may fail for 2 reasons: * 1) the partner window is dead - in which case we just dispense * with terminates altogether and pretend we are terminated. * 2) the target queue is full. This won't happen on NT but * could on win31. PostDdeMessage handles this case by * queueing the outgoing message on our side and continuing * to try to get it posted and hangs around for the ACK. * This function can only fail if the target window died or * we ran out of memory. In either case, we have to punt on * terminates and consider ourselves disconnected and terminated. * * When we do get into a state where we are waiting for a * terminate, we increment our zombie count. This gets * decremented when either we get the expected terminate or * our partner window dies/postmessage fails. */
if (pci->ci.fs & ST_CONNECTED) { if (pci->ci.fs & ST_CLIENT) { AbandonTransaction(hwnd, pci->ci.pai, NULL, FALSE); } CleanupAdvList(hwnd, pci);
pci->ci.fs &= ~ST_CONNECTED;
if (PostDdeMessage(&pci->ci, WM_DDE_TERMINATE, hwnd, 0L, 0, 0)) { if (!(pci->ci.fs & ST_TERM_WAITING)) { pci->ci.fs |= ST_TERM_WAITING; pci->ci.pai->cZombies++; TRACETERM((szT, "cZombies incremented...")); } TRACETERM((szT, "Disconnect: Posted Terminate(%x->%x)\n", hwnd, (HWND)pci->ci.hConvPartner, ((LPAPPINFO)pci->ci.pai)->cZombies)); } else { pci->ci.fs |= ST_TERMINATED; if (pci->ci.fs & ST_TERM_WAITING) { pci->ci.fs &= ~ST_TERM_WAITING; pci->ci.pai->cZombies--; TRACETERM((szT, "cZombies decremented...")); } TRACETERM((szT, "Disconnect: Terminate post(%x->%x) failed.\n", hwnd, (HWND)pci->ci.hConvPartner)); } pci->ci.xad.state = XST_NULL; }
TRACETERM((szT, "Disconnect: cZombies=%d[%x:%x].\n", pci->ci.pai->cZombies, HIWORD(&((LPAPPINFO)pci->ci.pai)->cZombies), LOWORD(&((LPAPPINFO)pci->ci.pai)->cZombies)));
/*
* Self destruction is only allowed when we have permission to die, * are disconnected, are not waiting for a terminate, and are * terminated. */ if ((pci->ci.fs & (ST_CONNECTED | ST_PERM2DIE | ST_TERMINATED | ST_TERM_WAITING)) == (ST_PERM2DIE | ST_TERMINATED)) { DestroyWindow(hwnd); TRACETERM((szT, "Disconnect: Destroying %x.\n", hwnd)); } }
/*
* This function handles WM_DDE_TERMINATE processing for a conversation window. */ void Terminate( HWND hwnd, HWND hwndFrom, PCLIENTINFO pci) { SEMCHECKOUT();
/*
* Only accept terminates from whom we are talking to. Anything else * is noise. */ if (hwndFrom != (HWND)pci->ci.hConvPartner) { // bogus extra-ack terminate - ignore
TRACETERM((szT, "Terminate: %x is ignoring terminate from %x. Partner should be %x!\n", hwnd, hwndFrom, (HWND)pci->ci.hConvPartner)); return; }
/*
* If we are in a timeout loop, cancel it first. We will come back * here when we recieve our self-posted terminate message. */ if (pci->ci.pai->hwndTimer == hwnd) { pci->ci.pai->wTimeoutStatus |= TOS_ABORT; PostMessage(hwnd, UM_TERMINATE, hwndFrom, 0); TRACETERM((szT, "Terminate: Canceling timeout loop for %x.\n", pci->ci.pai)); return; }
if (pci->ci.fs & ST_CONNECTED) { /*
* unexpected/initial external terminate case */ if (pci->ci.fs & ST_CLIENT) { /*
* Abandon any async transactions that may be in progress * on this conversation. */ AbandonTransaction(hwnd, pci->ci.pai, NULL, FALSE); }
/*
* Make any remaining queued up callbacks first. */ CheckCBQ(pci->ci.pai);
pci->ci.fs &= ~ST_CONNECTED; pci->ci.fs |= ST_TERMINATED | ST_PERM2DIE; TRACETERM((szT, "Terminate: received in connected state.(%x<-%x), fs=%x\n", hwnd, (HWND)pci->ci.hConvPartner, pci->ci.fs)); MONCONN(pci->ci.pai, pci->ci.aServerApp, pci->ci.aTopic, (pci->ci.fs & ST_CLIENT) ? hwnd : (HWND)pci->ci.hConvPartner, (pci->ci.fs & ST_CLIENT) ? (HWND)pci->ci.hConvPartner : hwnd, FALSE);
if (PostDdeMessage(&pci->ci, WM_DDE_TERMINATE, hwnd, 0L, 0, 0)) { TRACETERM((szT, "Terminate: Posting ack terminate(%x->%x).\n", hwnd, (HWND)pci->ci.hConvPartner)); } else { TRACETERM((szT, "Terminate: Posting ack terminate(%x->%x) failed.\n", hwnd, (HWND)pci->ci.hConvPartner)); } DoCallback(pci->ci.pai, MAKEHCONV(hwnd), 0, 0, 0, XTYP_DISCONNECT, 0L, 0L, pci->ci.fs & ST_ISSELF ? 1 : 0); pci->ci.xad.state = XST_NULL;
CleanupAdvList(hwnd, pci);
}
if (pci->ci.fs & ST_TERM_WAITING) { pci->ci.fs &= ~ST_TERM_WAITING; pci->ci.pai->cZombies--; TRACETERM((szT, "cZombies decremented...")); /*
* expected external terminate case */ TRACETERM((szT, "Terminate: Received ack terminate(%x<-%x), cZombies=%d[%x:%x].\n", hwnd, (HWND)pci->ci.hConvPartner, ((LPAPPINFO)pci->ci.pai)->cZombies, HIWORD(&((LPAPPINFO)pci->ci.pai)->cZombies), LOWORD(&((LPAPPINFO)pci->ci.pai)->cZombies)));
}
pci->ci.fs |= ST_TERMINATED;
if (pci->ci.fs & ST_PERM2DIE) { DestroyWindow(hwnd); TRACETERM((szT, "Terminate: Destroying %x.\n", hwnd)); } }
/*
* ----------------------------SERVER SECTION-------------------------------- */
/***************************** Public Function ****************************\
* long EXPENTRY ServerWndProc(hwnd, msg, mp1, mp2) * HWND hwnd; * WORD msg; * MPARAM mp1; * MPARAM mp2; * * DESCRIPTION: * This processes DDE conversations from the server end. * It stores internal information and acts much like a state machine. * If closed, it will automaticly abort any conversation in progress. * It also maintains an internal list of all items which currently are * in active ADVISE loops. * PUBDOC START * These server windows have the feature that a conversation can be * re-initiated with them by a client. The Client merely terminates * the conversation and then re-initiates by using a SendMsg to this * window. This allows a client to change the topic of the conversation * or to pass the conversation on to another client window without * loosing the server it initiated with. This is quite useful for * wild initiates. * PUBDOC END * * History: * 10/18/89 sanfords Added hack to make hszItem==0L when offszItem==offabData. * 1/4/89 sanfords created \***************************************************************************/ long EXPENTRY ServerWndProc(hwnd, msg, wParam, lParam) HWND hwnd; WORD msg; WORD wParam; DWORD lParam; {
register PSERVERINFO psi; long mrData; HDDEDATA hData = 0L; WORD wFmt = 0; WORD wStat = 0;
#ifdef DEBUG
LogDdeObject(msg | 0x8000, lParam); #endif
psi = (PSERVERINFO)GetWindowLong(hwnd, GWL_PCI);
switch (msg) { case WM_DDE_REQUEST: case WM_DDE_ACK: case WM_DDE_ADVISE: case WM_DDE_UNADVISE: case WM_DDE_POKE: case WM_DDE_EXECUTE: ServerProcessDDEMsg(psi, msg, hwnd, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)); return(0); }
switch (msg) { case WM_CREATE: return(ServerCreate(hwnd, LPCREATESTRUCT_GETPAI(lParam))); break;
case UM_SETBLOCK: psi->ci.fs = (psi->ci.fs & ~(ST_BLOCKED | ST_BLOCKNEXT)) | wParam; if (!wParam || wParam & ST_BLOCKNEXT) { EmptyDDEPostQ(); } break;
case UMSR_CHGPARTNER: psi->ci.hConvPartner = MAKEHCONV(wParam); break;
case UM_QUERY: /*
* wParam = info index. * lParam = pData. If pData==0, return data else copy into pData. */ switch (wParam) { case Q_CLIENT: mrData = FALSE; break;
case Q_APPINFO: mrData = (long)(LPSTR)psi->ci.pai; break; } if (lParam == 0) return(mrData); else *(long FAR *)lParam = mrData; return(1); break;
case WM_DDE_TERMINATE: case UM_TERMINATE: Terminate(hwnd, (HWND)wParam, (PCLIENTINFO)psi); break;
case UM_DISCONNECT: Disconnect(hwnd, wParam, (PCLIENTINFO)psi); break;
case WM_TIMER: if (wParam == TID_TIMEOUT) { psi->ci.pai->wTimeoutStatus |= TOS_TICK; } break;
case WM_DESTROY: SEMCHECKOUT(); /*
* Send ourselves a terminate and free local data. */ if (psi->ci.fs & ST_CONNECTED) { psi->ci.fs &= ~ST_PERM2DIE; // stops infinite loop
Disconnect(hwnd, 0, (PCLIENTINFO)psi); } if (psi->ci.fs & ST_NOTIFYONDEATH) { PostMessage(psi->ci.pai->hwndSvrRoot, UM_DISCONNECT, ST_IM_DEAD, 0L); } SEMENTER(); CleanupAdvList(hwnd, (PCLIENTINFO)psi); FreeHsz(psi->ci.aServerApp); FreeHsz(LOWORD(psi->ci.hszSvcReq)); FreeHsz(psi->ci.aTopic); DestroyQ(psi->ci.pPMQ); psi->ci.pPMQ = NULL; /*
* remove all plstCB entries that reference this window. */ { PCBLI pli, pliNext;
for (pli = (PCBLI)psi->ci.pai->plstCB->pItemFirst; pli != NULL; pli = (PCBLI)pliNext) { pliNext = (PCBLI)pli->next; if ((HWND)pli->hConv == hwnd) { if (((PCBLI)pli)->hMemFree) { GLOBALFREE(((PCBLI)pli)->hMemFree); } RemoveLstItem(psi->ci.pai->plstCB, (PLITEM)pli); } } } FarFreeMem((LPBYTE)psi); SEMLEAVE(); // fall through
default: return(DefWindowProc(hwnd, msg, wParam, lParam)); break; } return(0); }
/*
* ----------------FRAME SECTION------------------ * * A frame window exists on behalf of every registered thread. It * handles conversation initiation and therefore issues callbacks * to the server app as needed to notify or query the server app. * The callback queue is always bypassed for these synchronous * events. */
/***************************** Private Function ****************************\
* long EXPENTRY subframeWndProc(hwnd, msg, mp1, mp2) * HWND hwnd; * WORD msg; * MPARAM mp1; * MPARAM mp2; * * This routine takes care of setting up server windows as needed to respond * to incomming WM_DDE_INTIIATE messages. It is subclassed from the top * level frame of the server application. * * History: created 12/20/88 sanfords \***************************************************************************/ long EXPENTRY subframeWndProc(hwnd, msg, wParam, lParam) HWND hwnd; WORD msg; WORD wParam; DWORD lParam; { switch (msg) { case WM_CREATE: SetWindowWord(hwnd, GWW_PAI, (WORD)LPCREATESTRUCT_GETPAI(lParam)); break;
case WM_DDE_INITIATE: ServerFrameInitConv((PAPPINFO)GetWindowWord(hwnd, GWW_PAI), hwnd, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)); break;
default: return(DefWindowProc(hwnd, msg, wParam, lParam)); break; } }
/*
* This version only goes one level deep */ VOID ChildMsg( HWND hwndParent, WORD msg, WORD wParam, DWORD lParam, BOOL fPost) { register HWND hwnd; register HWND hwndNext;
if (!IsWindow(hwndParent)) { return; }
if (!(hwnd = GetWindow(hwndParent, GW_CHILD))) return;
do { // incase hwnd goes away during send or post
hwndNext = GetWindow(hwnd, GW_HWNDNEXT); if (fPost) { PostMessage(hwnd, msg, wParam, lParam); } else { SendMessage(hwnd, msg, wParam, lParam); } hwnd = hwndNext; } while (hwnd); }
/*
* main application window - parent of all others in app. */ long EXPENTRY DmgWndProc(hwnd, msg, wParam, lParam) HWND hwnd; WORD msg; WORD wParam; DWORD lParam; { #define pai ((PAPPINFO)lParam)
hwnd; wParam;
switch (msg) { case WM_CREATE: SetWindowWord(hwnd, GWW_PAI, (WORD)LPCREATESTRUCT_GETPAI(lParam)); break;
case UM_REGISTER: case UM_UNREGISTER: return(ProcessRegistrationMessage(hwnd, msg, wParam, lParam)); break;
case UM_FIXHEAP: { // lParam = pai;
PCBLI pcbli; // current point of search
PCBLI pcbliStart; // search start point
PCBLI pcbliNextStart; // next search start point
if (pai->cInProcess) { // repost and wait till this is clear.
PostMessage(hwnd, UM_FIXHEAP, 0, lParam); return(0); }
/*
* We are probably in an impossible situation here - the callback queue * is likely stuffed full of advise data callbacks because the * server data changes are outrunning the client's ability to * process them. * * Here we attempt to remove all duplicated advise data callbacks from * the queue leaving only the most recent entries. We assume we can * do this now because we are not InProcess and this is in response * to a post message. */
SEMENTER();
pcbliStart = (PCBLI)pai->plstCB->pItemFirst;
do {
while (pcbliStart && pcbliStart->wType != XTYP_ADVDATA) { pcbliStart = (PCBLI)pcbliStart->next; }
if (!pcbliStart) { break; }
pcbli = (PCBLI)pcbliStart->next;
if (!pcbli) { break; }
while (pcbli) {
if (pcbli->wType == XTYP_ADVDATA && pcbli->hConv == pcbliStart->hConv && pcbli->hszItem == pcbliStart->hszItem && pcbli->wFmt == pcbliStart->wFmt) {
// Match, remove older copy
QReply(pcbliStart, DDE_FBUSY); pcbliNextStart = (PCBLI)pcbliStart->next; RemoveLstItem(pai->plstCB, (PLITEM)pcbliStart); pcbliStart = pcbliNextStart; break; } else {
pcbli = (PCBLI)pcbli->next; } } if (!pcbli) { pcbliStart = (PCBLI)pcbliStart->next; } } while (TRUE);
if (pai->lpMemReserve == NULL) { pai->lpMemReserve = FarAllocMem(pai->hheapApp, CB_RESERVE); }
SEMLEAVE(); } // Fall Through...
case UM_CHECKCBQ: CheckCBQ(pai); return(0); break;
default: DefWindowProc(hwnd, msg, wParam, lParam); break; } #undef pai
}
// this proc creates a window that only hangs around till its children are
// all gone and its been given the ok to go.
long EXPENTRY ConvListWndProc(hwnd, msg, wParam, lParam) HWND hwnd; WORD msg; WORD wParam; DWORD lParam; { switch (msg) { case WM_CREATE: SetWindowWord(hwnd, GWW_STATE, 0); SetWindowWord(hwnd, GWW_CHECKVAL, ++hwInst); if (((LPCREATESTRUCT)lParam)->lpCreateParams) { SetWindowWord(hwnd, GWW_PAI, (WORD)LPCREATESTRUCT_GETPAI(lParam)); } else { SetWindowWord(hwnd, GWW_PAI, 0); }
break;
case UM_SETBLOCK: ChildMsg(hwnd, UM_SETBLOCK, wParam, lParam, FALSE); break;
case UM_DISCONNECT: switch (wParam) {
case ST_PERM2DIE: SetWindowWord(hwnd, GWW_STATE, ST_PERM2DIE); ChildMsg(hwnd, UM_DISCONNECT, ST_PERM2DIE | ST_NOTIFYONDEATH, 0L, FALSE); case ST_IM_DEAD: if (GetWindowWord(hwnd, GWW_STATE) == ST_PERM2DIE && !GetWindow(hwnd, GW_CHILD)) { DestroyWindow(hwnd); } break; } break;
default: return(DefWindowProc(hwnd, msg, wParam, lParam)); break; } }
HDDEDATA DoCallback( PAPPINFO pai, HCONV hConv, HSZ hszTopic, HSZ hszItem, WORD wFmt, WORD wType, HDDEDATA hData, DWORD dwData1, DWORD dwData2) { HDDEDATA dwRet, dwT; EXTDATAINFO edi;
SEMCHECKIN();
/*
* in case we somehow call this before initialization is complete. */ if (pai == NULL || pai->pfnCallback == NULL) return(0);
/*
* skip callbacks filtered out. */ if (aulmapType[(wType & XTYP_MASK) >> XTYP_SHIFT] & pai->afCmd) { /*
* filtered. */ return(0); } /*
* neuter callbacks once in DdeUninitiate() mode. (No data in or out) */ if (pai->wFlags & AWF_UNINITCALLED && wType & (XCLASS_DATA | XCLASS_FLAGS)) { return(0); }
if (hData) { // map ingoing data handle
edi.pai = pai; edi.hData = hData | HDATA_NOAPPFREE; hData = (HDDEDATA)(LPSTR)&edi; }
pai->cInProcess++;
TRACEAPIIN((szT, "DDEMLCallback(%hx, %hx, %x, %x, %x, %x, %x, %x)\n", wType, wFmt, hConv, hszTopic, hszItem, hData, dwData1, dwData2));
SEMLEAVE(); dwRet = (*pai->pfnCallback) (wType, wFmt, hConv, hszTopic, hszItem, hData, dwData1, dwData2);
TRACEAPIOUT((szT, "DDEMLCallback:%x\n", dwRet));
if (cMonitor && wType != XTYP_MONITOR) { // Copy the hData otherwise we SendMsg a pointer to stuff on the stack
// which doesn't work too well if we're running from a 32bit app!
if (hData) { LPBYTE pDataNew = GLOBALPTR(GLOBALALLOC(GPTR, sizeof(edi))); if (pDataNew) { hmemcpy(pDataNew, &edi, sizeof(edi)); MonBrdcastCB(pai, wType, wFmt, hConv, hszTopic, hszItem, (HDDEDATA)pDataNew, dwData1, dwData2, dwRet); GLOBALFREE((HGLOBAL)HIWORD(pDataNew)); } } else { MonBrdcastCB(pai, wType, wFmt, hConv, hszTopic, hszItem, hData, dwData1, dwData2, dwRet); } }
SEMENTER(); pai->cInProcess--;
// unmap outcomming data handle.
if (dwRet && wType & XCLASS_DATA && dwRet != CBR_BLOCK) { dwT = (HDDEDATA)((LPEXTDATAINFO)dwRet)->hData; if (!(LOWORD(dwT) & HDATA_APPOWNED)) { FarFreeMem((LPSTR)dwRet); } dwRet = dwT; }
return(dwRet); }
|