windows-nt/Source/XPSP1/NT/base/mvdm/wow16/ddeml/dmgwndp.c
2020-09-26 16:20:57 +08:00

1359 lines
40 KiB
C

/****************************** 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);
}