1359 lines
40 KiB
C
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);
|
||
|
}
|
||
|
|