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

527 lines
15 KiB
C

/****************************** Module Header ******************************\
* Module Name: DMGDDE.C
*
* This module contains functions used for interfacing with DDE structures
* and such.
*
* Created: 12/23/88 sanfords
*
* Copyright (c) 1988, 1989 Microsoft Corporation
\***************************************************************************/
#include "ddemlp.h"
VOID FreeDdeMsgData(WORD msg, LPARAM lParam);
UINT EmptyQueueTimerId = 0;
/***************************** Private Function ****************************\
* timeout()
*
* This routine creates a timer for hwndTimeout. It then runs a modal loop
* which will exit once the pai->wTimeoutStatus word indicates things are
* either done (TOS_DONE), aborted (TOS_ABORT), or the system is shutting
* down (TOS_SHUTDOWN). A values of TOS_TICK is used to support timouts
* >64K in length.
*
* Returns fSuccess, ie TRUE if TOS_DONE was received. before TOS_ABORT.
*
* PUBDOC START
* Synchronous client transaction modal loops:
*
* During Synchronous transactions, a client application will enter a modal
* loop while waiting for the server to respond to the request. If an
* application wishes to filter messages to the modal loop, it may do so
* by setting a message filter tied to MSGF_DDEMGR. Applications should
* be aware however that the DDEMGR modal loop processes private messages
* in the WM_USER range, WM_DDE messages, and WM_TIMER messages with timer IDs
* using the TID_ constants defined in ddeml.h.
* These messages must not be filtered by an application!!!
*
* PUBDOC END
*
* History:
* Created sanfords 12/19/88
\***************************************************************************/
BOOL timeout(
PAPPINFO pai,
DWORD ulTimeout,
HWND hwndTimeout)
{
MSG msg;
PAPPINFO paiT;
SEMENTER();
/*
* We check all instances in this task (thread) since we cannot let
* one thread enter a modal loop two levels deep.
*/
paiT = NULL;
while (paiT = GetCurrentAppInfo(paiT)) {
if (paiT->hwndTimer) {
SETLASTERROR(pai, DMLERR_REENTRANCY);
AssertF(FALSE, "Recursive timeout call");
SEMLEAVE();
return(FALSE);
}
}
pai->hwndTimer = hwndTimeout;
SEMLEAVE();
if (!SetTimer(hwndTimeout, TID_TIMEOUT,
ulTimeout > 0xffffL ? 0xffff : (WORD)ulTimeout, NULL)) {
SETLASTERROR(pai, DMLERR_SYS_ERROR);
return(FALSE);
}
if (ulTimeout < 0xffff0000) {
ulTimeout += 0x00010000;
}
//
// We use this instance-wide global variable to note timeouts so that
// we don't need to rely on PostMessage() to work when faking timeouts.
//
do {
ulTimeout -= 0x00010000;
if (ulTimeout <= 0xffffL) {
// the last timeout should be shorter than 0xffff
SetTimer(hwndTimeout, TID_TIMEOUT, (WORD)ulTimeout, NULL);
}
pai->wTimeoutStatus = TOS_CLEAR;
/*
* stay in modal loop until a timeout happens.
*/
while (pai->wTimeoutStatus == TOS_CLEAR) {
if (!GetMessage(&msg, (HWND)NULL, 0, 0)) {
/*
* Somebody posted a WM_QUIT message - get out of this
* timer loop and repost so main loop gets it. This
* fixes a bug where some apps (petzolds ShowPop) use
* rapid synchronous transactions which interfere with
* their proper closing.
*/
pai->wTimeoutStatus = TOS_ABORT;
PostMessage(msg.hwnd, WM_QUIT, 0, 0);
} else {
if (!CallMsgFilter(&msg, MSGF_DDEMGR))
DispatchMessage(&msg);
}
}
} while (pai->wTimeoutStatus == TOS_TICK && HIWORD(ulTimeout));
KillTimer(hwndTimeout, TID_TIMEOUT);
//
// remove any remaining timeout message in the queue.
//
while (PeekMessage(&msg, hwndTimeout, WM_TIMER, WM_TIMER,
PM_NOYIELD | PM_REMOVE)) {
if (msg.message == WM_QUIT) {
/*
* Windows BUG: This call will succeed on WM_QUIT messages!
*/
PostQuitMessage(0);
break;
}
}
SEMENTER();
pai->hwndTimer = 0;
SEMLEAVE();
/*
* post a callback check incase we blocked callbacks due to being
* in a timeout.
*/
if (!PostMessage(pai->hwndDmg, UM_CHECKCBQ, 0, (DWORD)(LPSTR)pai)) {
SETLASTERROR(pai, DMLERR_SYS_ERROR);
}
return(TRUE);
}
/***************************** Private Function ****************************\
* Allocates global DDE memory and fills in first two words with fsStatus
* and wFmt.
*
* History: created 6/15/90 rich gartland
\***************************************************************************/
HANDLE AllocDDESel(fsStatus, wFmt, cbData)
WORD fsStatus;
WORD wFmt;
DWORD cbData;
{
HANDLE hMem = NULL;
DDEDATA FAR * pMem;
SEMENTER();
if (!cbData)
cbData++; // fixes GLOBALALLOC bug where 0 size object allocation fails
if ((hMem = GLOBALALLOC(GMEM_DDESHARE, cbData))) {
pMem = (DDEDATA FAR * )GLOBALPTR(hMem);
*(WORD FAR * )pMem = fsStatus;
pMem->cfFormat = wFmt;
}
SEMLEAVE();
return(hMem);
}
/***************************** Private Function ****************************\
* This routine institutes a callback directly if psi->fEnableCB is set
* and calls QReply to complete the transaction,
* otherwise it places the data into the queue for processing.
*
* Since hData may be freed by the app at any time once the callback is
* issued, we cannot depend on it being there for QReply. Therefore we
* save all the pertinant data in the queue along with it.
*
* Returns fSuccess.
*
* History:
* Created 9/12/89 Sanfords
\***************************************************************************/
BOOL MakeCallback(
PCOMMONINFO pcoi,
HCONV hConv,
HSZ hszTopic,
HSZ hszItem,
WORD wFmt,
WORD wType,
HDDEDATA hData,
DWORD dwData1,
DWORD dwData2,
WORD msg,
WORD fsStatus,
HWND hwndPartner,
HANDLE hMemFree,
BOOL fQueueOnly)
{
PCBLI pcbli;
SEMENTER();
pcbli = (PCBLI)NewLstItem(pcoi->pai->plstCB, ILST_LAST);
if (pcbli == NULL) {
SETLASTERROR(pcoi->pai, DMLERR_MEMORY_ERROR);
SEMLEAVE();
return(FALSE);
}
pcbli->hConv = hConv;
pcbli->hszTopic = hszTopic;
pcbli->hszItem = hszItem;
pcbli->wFmt = wFmt;
pcbli->wType = wType;
pcbli->hData = hData;
pcbli->dwData1 = dwData1;
pcbli->dwData2 = dwData2;
pcbli->msg = msg;
pcbli->fsStatus = fsStatus;
pcbli->hwndPartner = hwndPartner;
pcbli->hMemFree = hMemFree;
pcbli->pai = pcoi->pai;
pcbli->fQueueOnly = fQueueOnly;
SEMLEAVE();
if (!(pcoi->fs & ST_BLOCKED))
if (!PostMessage(pcoi->pai->hwndDmg, UM_CHECKCBQ,
0, (DWORD)(LPSTR)pcoi->pai)) {
SETLASTERROR(pcoi->pai, DMLERR_SYS_ERROR);
}
#ifdef DEBUG
if (hMemFree) {
LogDdeObject(0xB000, hMemFree);
}
#endif
return(TRUE);
}
#define MAX_PMRETRIES 3
//
// This routine extends the size of the windows message queue by queueing
// up failed posts on the sender side. This avoids the problems of full
// client queues and of windows behavior of giving DDE messages priority.
//
BOOL PostDdeMessage(
PCOMMONINFO pcoi, // senders COMMONINFO
WORD msg,
HWND hwndFrom, // == wParam
LONG lParam,
WORD msgAssoc,
HGLOBAL hAssoc)
{
LPMQL pmql;
PPMQI ppmqi;
int cTries;
HANDLE hTaskFrom, hTaskTo;
HWND hwndTo;
PQST pMQ;
hwndTo = (HWND)pcoi->hConvPartner;
if (!IsWindow(hwndTo)) {
return(FALSE);
}
hTaskTo = GetWindowTask(hwndTo);
/*
* locate message overflow queue for our target task (pMQ)
*/
for (pmql = gMessageQueueList; pmql; pmql = pmql->next) {
if (pmql->hTaskTo == hTaskTo) {
break;
}
}
if (pmql != NULL) {
pMQ = pmql->pMQ;
} else {
pMQ = NULL;
}
/*
* See if any messages are already queued up
*/
if (pMQ && pMQ->cItems) {
if (msg == WM_DDE_TERMINATE) {
/*
* remove any non-terminate queued messages from us to them.
*/
ppmqi = (PPMQI)FindNextQi(pMQ, NULL, FALSE);
while (ppmqi) {
FreeDdeMsgData(ppmqi->msg, ppmqi->lParam);
FreeDdeMsgData(ppmqi->msgAssoc,
MAKELPARAM(ppmqi->hAssoc, ppmqi->hAssoc));
ppmqi = (PPMQI)FindNextQi(pMQ, (PQUEUEITEM)ppmqi,
ppmqi->hwndTo == hwndTo &&
ppmqi->wParam == hwndFrom);
}
pMQ = NULL; // so we just post it
} else {
// add the latest post attempt
ppmqi = (PPMQI)Addqi(pMQ);
if (ppmqi == NULL) {
SETLASTERROR(pcoi->pai, DMLERR_MEMORY_ERROR);
return(FALSE); // out of memory
}
ppmqi->hwndTo = hwndTo;
ppmqi->msg = msg;
ppmqi->wParam = hwndFrom;
ppmqi->lParam = lParam;
ppmqi->hAssoc = hAssoc;
ppmqi->msgAssoc = msgAssoc;
}
}
if (pMQ == NULL || pMQ->cItems == 0) {
// just post the given message - no queue involved.
cTries = 0;
hTaskFrom = GetWindowTask(hwndFrom);
while (!PostMessage(hwndTo, msg, hwndFrom, lParam)) {
/*
* we yielded so recheck target window
*/
if (!IsWindow(hwndTo)) {
return(FALSE);
}
/*
* Give reciever a chance to clean out his queue
*/
if (hTaskTo != hTaskFrom) {
Yield();
} else if (!(pcoi->pai->wFlags & AWF_INPOSTDDEMSG)) {
MSG msgs;
PAPPINFO pai;
pcoi->pai->wFlags |= AWF_INPOSTDDEMSG;
/*
* Reciever is US!
*
* We need to empty our queue of stuff so we can post more
* to ourselves.
*/
while (PeekMessage((MSG FAR *)&msgs, NULL,
WM_DDE_FIRST, WM_DDE_LAST, PM_REMOVE)) {
DispatchMessage((MSG FAR *)&msgs);
}
/*
* tell all instances in this task to process their
* callbacks so we can clear our queue.
*/
for (pai = pAppInfoList; pai != NULL; pai = pai->next) {
if (pai->hTask == hTaskFrom) {
CheckCBQ(pai);
}
}
pcoi->pai->wFlags &= ~AWF_INPOSTDDEMSG;
}
if (cTries++ > MAX_PMRETRIES) {
/*
* relocate message overflow queue for our target task (pMQ)
* We need to do this again because we gave up control
* with the dispatch message and CheckCBQ calls.
*/
for (pmql = gMessageQueueList; pmql; pmql = pmql->next) {
if (pmql->hTaskTo == hTaskTo) {
break;
}
}
if (pmql == NULL) {
/*
* create and link in a new queue for the target task
*/
pmql = (LPMQL)FarAllocMem(hheapDmg, sizeof(MQL));
if (pmql == NULL) {
SETLASTERROR(pcoi->pai, DMLERR_MEMORY_ERROR);
return(FALSE);
}
pmql->pMQ = CreateQ(sizeof(PMQI));
if (pmql->pMQ == NULL) {
FarFreeMem(pmql);
SETLASTERROR(pcoi->pai, DMLERR_MEMORY_ERROR);
return(FALSE);
}
pmql->hTaskTo = hTaskTo;
pmql->next = gMessageQueueList;
gMessageQueueList = pmql;
}
pMQ = pmql->pMQ;
ppmqi = (PPMQI)Addqi(pMQ);
if (ppmqi == NULL) {
SETLASTERROR(pcoi->pai, DMLERR_MEMORY_ERROR);
return(FALSE); // out of memory
}
ppmqi->hwndTo = hwndTo;
ppmqi->msg = msg;
ppmqi->wParam = hwndFrom;
ppmqi->lParam = lParam;
ppmqi->hAssoc = hAssoc;
ppmqi->msgAssoc = msgAssoc;
return(TRUE);
}
}
#ifdef DEBUG
LogDdeObject(msg | 0x1000, lParam);
if (msgAssoc) {
LogDdeObject(msgAssoc | 0x9000, MAKELPARAM(hAssoc, hAssoc));
}
#endif
return(TRUE);
}
// come here if the queue exists - empty it as far as we can.
EmptyDDEPostQ();
return(TRUE);
}
//
// EmptyDDEPost
//
// This function checks the DDE post queue list and emptys it as far as
// possible.
//
BOOL EmptyDDEPostQ()
{
PPMQI ppmqi;
LPMQL pPMQL, pPMQLPrev;
PQST pMQ;
BOOL fMoreToDo = FALSE;
pPMQLPrev = NULL;
pPMQL = gMessageQueueList;
while (pPMQL) {
pMQ = pPMQL->pMQ;
while (pMQ->cItems) {
ppmqi = (PPMQI)Findqi(pMQ, QID_OLDEST);
if (!PostMessage(ppmqi->hwndTo, ppmqi->msg, ppmqi->wParam, ppmqi->lParam)) {
if (IsWindow(ppmqi->hwndTo)) {
fMoreToDo = TRUE;
break; // skip to next target queue
} else {
FreeDdeMsgData(ppmqi->msg, ppmqi->lParam);
FreeDdeMsgData(ppmqi->msgAssoc,
MAKELPARAM(ppmqi->hAssoc, ppmqi->hAssoc));
}
} else {
#ifdef DEBUG
LogDdeObject(ppmqi->msg | 0x2000, ppmqi->lParam);
if (ppmqi->msgAssoc) {
LogDdeObject(ppmqi->msgAssoc | 0xA000,
MAKELPARAM(ppmqi->hAssoc, ppmqi->hAssoc));
}
#endif
}
Deleteqi(pMQ, QID_OLDEST);
}
if (pMQ->cItems == 0) {
/*
* Delete needless queue (selector)
*/
DestroyQ(pMQ);
if (pPMQLPrev) {
pPMQLPrev->next = pPMQL->next;
FarFreeMem(pPMQL);
pPMQL = pPMQLPrev;
} else {
gMessageQueueList = gMessageQueueList->next;
FarFreeMem(pPMQL);
pPMQL = gMessageQueueList;
continue;
}
}
pPMQLPrev = pPMQL;
pPMQL = pPMQL->next;
}
if (fMoreToDo & !EmptyQueueTimerId) {
EmptyQueueTimerId = SetTimer(NULL, TID_EMPTYPOSTQ,
TIMEOUT_QUEUECHECK, (TIMERPROC)EmptyQTimerProc);
}
return(fMoreToDo);
}
/*
* Used to asynchronously check overflow message queues w/o using PostMessage()
*/
void CALLBACK EmptyQTimerProc(
HWND hwnd,
UINT msg,
UINT tid,
DWORD dwTime)
{
KillTimer(NULL, EmptyQueueTimerId);
EmptyQueueTimerId = 0;
EmptyDDEPostQ();
}