You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
954 lines
29 KiB
954 lines
29 KiB
/****************************** Module Header ******************************\
|
|
* Module Name: xact.c
|
|
*
|
|
* Copyright (c) 1985 - 1999, Microsoft Corporation
|
|
*
|
|
* DDE Manager transaction processing module
|
|
*
|
|
* Created: 11/3/91 Sanford Staab
|
|
\***************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
|
|
/***************************************************************************\
|
|
* DdeClientTransaction (DDEML API)
|
|
*
|
|
* Description:
|
|
* Initiates all DDE transactions.
|
|
*
|
|
* History:
|
|
* 11-1-91 sanfords Created.
|
|
\***************************************************************************/
|
|
|
|
FUNCLOG8(LOG_GENERAL, HDDEDATA, DUMMYCALLINGTYPE, DdeClientTransaction, LPBYTE, pData, DWORD, cbData, HCONV, hConv, HSZ, hszItem, UINT, wFmt, UINT, wType, DWORD, ulTimeout, LPDWORD, pulResult)
|
|
HDDEDATA DdeClientTransaction(
|
|
LPBYTE pData,
|
|
DWORD cbData,
|
|
HCONV hConv,
|
|
HSZ hszItem,
|
|
UINT wFmt,
|
|
UINT wType,
|
|
DWORD ulTimeout,
|
|
LPDWORD pulResult)
|
|
{
|
|
MSG msg;
|
|
PCL_INSTANCE_INFO pcii = NULL;
|
|
HDDEDATA hRet = 0;
|
|
PCL_CONV_INFO pci;
|
|
PDDEMLDATA pdd = NULL;
|
|
PXACT_INFO pxi;
|
|
BOOL fStarted;
|
|
PDDE_DATA pdde;
|
|
|
|
EnterDDECrit;
|
|
|
|
pci = (PCL_CONV_INFO)ValidateCHandle((HANDLE)hConv,
|
|
HTYPE_CLIENT_CONVERSATION, HINST_ANY);
|
|
if (pci == NULL) {
|
|
BestSetLastDDEMLError(DMLERR_INVALIDPARAMETER);
|
|
goto Exit;
|
|
}
|
|
pcii = pci->ci.pcii;
|
|
if (ulTimeout != TIMEOUT_ASYNC && GetClientInfo()->CI_flags & CI_IN_SYNC_TRANSACTION) {
|
|
SetLastDDEMLError(pcii, DMLERR_REENTRANCY);
|
|
goto Exit;
|
|
}
|
|
if (!(pci->ci.state & ST_CONNECTED)) {
|
|
SetLastDDEMLError(pcii, DMLERR_NO_CONV_ESTABLISHED);
|
|
goto Exit;
|
|
}
|
|
|
|
switch (wType) {
|
|
case XTYP_POKE:
|
|
case XTYP_ADVSTART:
|
|
case XTYP_ADVSTART | XTYPF_NODATA:
|
|
case XTYP_ADVSTART | XTYPF_ACKREQ:
|
|
case XTYP_ADVSTART | XTYPF_NODATA | XTYPF_ACKREQ:
|
|
case XTYP_REQUEST:
|
|
case XTYP_ADVSTOP:
|
|
if (hszItem == 0) {
|
|
SetLastDDEMLError(pcii, DMLERR_INVALIDPARAMETER);
|
|
goto Exit;
|
|
}
|
|
break;
|
|
|
|
case XTYP_EXECUTE: // just ignore wFmt & hszItem
|
|
break;
|
|
|
|
default:
|
|
SetLastDDEMLError(pcii, DMLERR_INVALIDPARAMETER);
|
|
goto Exit;
|
|
}
|
|
|
|
pxi = DDEMLAlloc(sizeof(XACT_INFO));
|
|
if (pxi == NULL) {
|
|
SetLastDDEMLError(pcii, DMLERR_MEMORY_ERROR);
|
|
goto Exit;
|
|
}
|
|
|
|
switch (wType) {
|
|
case XTYP_EXECUTE:
|
|
case XTYP_POKE:
|
|
if ((LONG)cbData == -1L) {
|
|
|
|
// We are accepting an existing data handle for export to another
|
|
// app.
|
|
|
|
pdd = (PDDEMLDATA)ValidateCHandle((HANDLE)pData,
|
|
HTYPE_DATA_HANDLE, HINST_ANY);
|
|
if (pdd == NULL) {
|
|
InvParam:
|
|
SetLastDDEMLError(pcii, DMLERR_INVALIDPARAMETER);
|
|
DDEMLFree(pxi);
|
|
goto Exit;
|
|
}
|
|
|
|
// make sure data handle holds apropriate data for this transaction
|
|
|
|
if ((pdd->flags & HDATA_EXECUTE && wType != XTYP_EXECUTE) ||
|
|
(!(pdd->flags & HDATA_EXECUTE) && wType == XTYP_EXECUTE)) {
|
|
goto InvParam;
|
|
}
|
|
|
|
// To simplify life, use a copy if this handle is potentially
|
|
// a relay or APPOWNED handle.
|
|
|
|
if (pdd->flags & (HDATA_APPOWNED | HDATA_NOAPPFREE)) {
|
|
pxi->hDDESent = CopyDDEData(pdd->hDDE, wType == XTYP_EXECUTE);
|
|
if (!pxi->hDDESent) {
|
|
MemErr:
|
|
DDEMLFree(pxi);
|
|
SetLastDDEMLError(pcii, DMLERR_MEMORY_ERROR);
|
|
goto Exit;
|
|
}
|
|
USERGLOBALLOCK(pxi->hDDESent, pdde);
|
|
if (pdde == NULL) {
|
|
FreeDDEData(pxi->hDDESent, TRUE, TRUE);
|
|
goto MemErr;
|
|
}
|
|
pdde->wStatus = DDE_FRELEASE;
|
|
USERGLOBALUNLOCK(pxi->hDDESent);
|
|
} else {
|
|
pxi->hDDESent = pdd->hDDE;
|
|
}
|
|
|
|
// make sure handle has proper format
|
|
|
|
if (wType == XTYP_POKE) {
|
|
USERGLOBALLOCK(pxi->hDDESent, pdde);
|
|
if (pdde == NULL) {
|
|
goto InvParam;
|
|
}
|
|
pdde->wFmt = (WORD)wFmt;
|
|
USERGLOBALUNLOCK(pxi->hDDESent);
|
|
}
|
|
|
|
} else { // Convert data in buffer into an apropriate hDDE
|
|
|
|
if (wType == XTYP_POKE) {
|
|
pxi->hDDESent = AllocAndSetDDEData(pData, cbData,
|
|
DDE_FRELEASE, (WORD)wFmt);
|
|
} else {
|
|
pxi->hDDESent = AllocAndSetDDEData(pData, cbData, 0, 0);
|
|
}
|
|
if (!pxi->hDDESent) {
|
|
goto MemErr;
|
|
}
|
|
}
|
|
}
|
|
|
|
// FINALLY - start the transaction
|
|
|
|
pxi->pcoi = (PCONV_INFO)pci;
|
|
pxi->gaItem = LocalToGlobalAtom(LATOM_FROM_HSZ(hszItem)); // pxi copy
|
|
pxi->wFmt = (WORD)wFmt;
|
|
pxi->wType = (WORD)wType;
|
|
|
|
switch (wType) {
|
|
case XTYP_ADVSTART:
|
|
case XTYP_ADVSTART | XTYPF_NODATA:
|
|
case XTYP_ADVSTART | XTYPF_ACKREQ:
|
|
case XTYP_ADVSTART | XTYPF_NODATA | XTYPF_ACKREQ:
|
|
fStarted = ClStartAdvise(pxi);
|
|
break;
|
|
|
|
case XTYP_ADVSTOP:
|
|
fStarted = ClStartUnadvise(pxi);
|
|
break;
|
|
|
|
case XTYP_EXECUTE:
|
|
fStarted = ClStartExecute(pxi);
|
|
break;
|
|
|
|
case XTYP_POKE:
|
|
fStarted = ClStartPoke(pxi);
|
|
break;
|
|
|
|
case XTYP_REQUEST:
|
|
fStarted = ClStartRequest(pxi);
|
|
}
|
|
|
|
if (!fStarted) {
|
|
// if we copied or allocated data - free it.
|
|
if (pxi->hDDESent && (pdd == NULL || pxi->hDDESent != pdd->hDDE)) {
|
|
FreeDDEData(pxi->hDDESent, FALSE, TRUE); // free data copy
|
|
}
|
|
GlobalDeleteAtom(pxi->gaItem); // pxi copy
|
|
DDEMLFree(pxi);
|
|
goto Exit;
|
|
}
|
|
|
|
if (pdd != NULL && !(pdd->flags & (HDATA_NOAPPFREE | HDATA_APPOWNED))) {
|
|
|
|
// invalidate given handle on success - unless we copied it because
|
|
// the app will either be return ing it from a callback or potentially
|
|
// using it again.
|
|
|
|
DDEMLFree(pdd);
|
|
DestroyHandle((HANDLE)pData);
|
|
}
|
|
|
|
if (ulTimeout == TIMEOUT_ASYNC) {
|
|
|
|
// asynchronous transaction
|
|
|
|
if (pulResult != NULL) {
|
|
pxi->hXact = CreateHandle((ULONG_PTR)pxi, HTYPE_TRANSACTION,
|
|
InstFromHandle(pcii->hInstClient));
|
|
*pulResult = HandleToUlong(pxi->hXact);
|
|
}
|
|
hRet = (HDDEDATA)TRUE;
|
|
|
|
} else {
|
|
|
|
// synchronous transaction
|
|
|
|
GetClientInfo()->CI_flags |= CI_IN_SYNC_TRANSACTION;
|
|
pcii->flags |= IIF_IN_SYNC_XACT;
|
|
|
|
pxi->flags |= XIF_SYNCHRONOUS;
|
|
NtUserSetTimer(pci->ci.hwndConv, TID_TIMEOUT, ulTimeout, NULL);
|
|
|
|
LeaveDDECrit;
|
|
CheckDDECritOut;
|
|
|
|
GetMessage(&msg, (HWND)NULL, 0, 0);
|
|
|
|
/*
|
|
* stay in modal loop until a timeout happens.
|
|
*/
|
|
while (msg.hwnd != pci->ci.hwndConv || msg.message != WM_TIMER ||
|
|
(msg.wParam != TID_TIMEOUT)) {
|
|
|
|
if (!CallMsgFilter(&msg, MSGF_DDEMGR))
|
|
DispatchMessage(&msg);
|
|
|
|
GetMessage(&msg, (HWND)NULL, 0, 0);
|
|
}
|
|
|
|
EnterDDECrit;
|
|
|
|
NtUserKillTimer(pci->ci.hwndConv, TID_TIMEOUT);
|
|
GetClientInfo()->CI_flags &= ~CI_IN_SYNC_TRANSACTION;
|
|
pcii->flags &= ~IIF_IN_SYNC_XACT;
|
|
|
|
if (pxi->flags & XIF_COMPLETE) {
|
|
if (pulResult != NULL) {
|
|
*pulResult = pxi->wStatus; // NACK status bits
|
|
}
|
|
switch (wType) {
|
|
case XTYP_ADVSTART:
|
|
case XTYP_ADVSTART | XTYPF_NODATA:
|
|
case XTYP_ADVSTART | XTYPF_ACKREQ:
|
|
case XTYP_ADVSTART | XTYPF_NODATA | XTYPF_ACKREQ:
|
|
case XTYP_ADVSTOP:
|
|
case XTYP_EXECUTE:
|
|
case XTYP_POKE:
|
|
hRet = (HDDEDATA)((ULONG_PTR)((pxi->wStatus & DDE_FACK) ? TRUE : FALSE));
|
|
if (!hRet) {
|
|
if (pxi->wStatus & DDE_FBUSY) {
|
|
SetLastDDEMLError(pcii, DMLERR_BUSY);
|
|
} else {
|
|
SetLastDDEMLError(pcii, DMLERR_NOTPROCESSED);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case XTYP_REQUEST:
|
|
if (pxi->hDDEResult == 0) {
|
|
hRet = (HDDEDATA)((ULONG_PTR)((pxi->wStatus & DDE_FACK) ? TRUE : FALSE));
|
|
if (!hRet) {
|
|
if (pxi->wStatus & DDE_FBUSY) {
|
|
SetLastDDEMLError(pcii, DMLERR_BUSY);
|
|
} else {
|
|
SetLastDDEMLError(pcii, DMLERR_NOTPROCESSED);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
// Note that if the incoming data didn't have the DDE_FRELEASE
|
|
// bit set, the transaction code would have made a copy so
|
|
// the app is free to keep is as long as he likes.
|
|
|
|
hRet = InternalCreateDataHandle(pcii, (LPBYTE)pxi->hDDEResult, (DWORD)-1, 0,
|
|
HDATA_READONLY, 0, 0);
|
|
pxi->hDDEResult = 0; // so cleanup doesn't free it.
|
|
}
|
|
|
|
(pxi->pfnResponse)((struct tagXACT_INFO *)pxi, 0, 0); // cleanup transaction
|
|
|
|
} else { // Timed out
|
|
|
|
// abandon the transaction and make it asyncronous so it will
|
|
// clean itself up when the response finally comes in.
|
|
|
|
pxi->flags &= ~XIF_SYNCHRONOUS;
|
|
pxi->flags |= XIF_ABANDONED;
|
|
|
|
switch (wType) {
|
|
case XTYP_ADVSTART:
|
|
case XTYP_ADVSTART | XTYPF_NODATA:
|
|
case XTYP_ADVSTART | XTYPF_ACKREQ:
|
|
case XTYP_ADVSTART | XTYPF_NODATA | XTYPF_ACKREQ:
|
|
SetLastDDEMLError(pcii, DMLERR_ADVACKTIMEOUT);
|
|
break;
|
|
case XTYP_ADVSTOP:
|
|
SetLastDDEMLError(pcii, DMLERR_UNADVACKTIMEOUT);
|
|
break;
|
|
case XTYP_EXECUTE:
|
|
SetLastDDEMLError(pcii, DMLERR_EXECACKTIMEOUT);
|
|
break;
|
|
case XTYP_POKE:
|
|
SetLastDDEMLError(pcii, DMLERR_POKEACKTIMEOUT);
|
|
break;
|
|
case XTYP_REQUEST:
|
|
SetLastDDEMLError(pcii, DMLERR_DATAACKTIMEOUT);
|
|
break;
|
|
}
|
|
// cleanup of pxi happens when transaction actually completes.
|
|
}
|
|
}
|
|
if (pci->ci.state & ST_FREE_CONV_RES_NOW) {
|
|
/*
|
|
* The conversation was terminated during the synchronous transaction
|
|
* so we need to clean up now that we are out of the loop.
|
|
*/
|
|
FreeConversationResources((PCONV_INFO)pci);
|
|
}
|
|
|
|
Exit:
|
|
/*
|
|
* Because this API is capable of blocking DdeUninitialize(), we check
|
|
* before exit to see if it needs to be called.
|
|
*/
|
|
if (pcii != NULL &&
|
|
(pcii->afCmd & APPCMD_UNINIT_ASAP) &&
|
|
// !(pcii->flags & IIF_IN_SYNC_XACT) &&
|
|
!pcii->cInDDEMLCallback) {
|
|
DdeUninitialize(HandleToUlong(pcii->hInstClient));
|
|
hRet = 0;
|
|
}
|
|
LeaveDDECrit;
|
|
return (hRet);
|
|
}
|
|
|
|
|
|
|
|
|
|
/***************************************************************************\
|
|
* GetConvContext
|
|
*
|
|
* Description:
|
|
* Retrieves conversation context information from the DDEML client window
|
|
* given. pl points to a CONVCONTEXT structure.
|
|
*
|
|
* History:
|
|
* 11-12-91 sanfords Created.
|
|
\***************************************************************************/
|
|
VOID GetConvContext(
|
|
HWND hwnd,
|
|
LONG *pl)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < sizeof(CONVCONTEXT); i += 4) {
|
|
*pl++ = GetWindowLong(hwnd, GWL_CONVCONTEXT + i);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* SetConvContext
|
|
*
|
|
* Description:
|
|
*
|
|
* History:
|
|
* 11-19-92 sanfords Created.
|
|
\***************************************************************************/
|
|
VOID SetConvContext(
|
|
HWND hwnd,
|
|
LONG *pl)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < sizeof(CONVCONTEXT); i += 4) {
|
|
SetWindowLong(hwnd, GWL_CONVCONTEXT + i, *pl++);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/***************************************************************************\
|
|
* DdeQueryConvInfo (DDEML API)
|
|
*
|
|
* Description:
|
|
* Retrieves detailed conversation information on a per conversation/
|
|
* transaction basis.
|
|
*
|
|
* History:
|
|
* 11-12-91 sanfords Created.
|
|
\***************************************************************************/
|
|
|
|
FUNCLOG3(LOG_GENERAL, UINT, DUMMYCALLINGTYPE, DdeQueryConvInfo, HCONV, hConv, DWORD, idTransaction, PCONVINFO, pConvInfo)
|
|
UINT DdeQueryConvInfo(
|
|
HCONV hConv,
|
|
DWORD idTransaction,
|
|
PCONVINFO pConvInfo)
|
|
{
|
|
PCONV_INFO pcoi;
|
|
PXACT_INFO pxi;
|
|
CONVINFO ci;
|
|
UINT uiRet = 0;
|
|
|
|
EnterDDECrit;
|
|
|
|
if (!ValidateTransaction(hConv, (HANDLE)LongToHandle( idTransaction ), &pcoi, &pxi)) {
|
|
goto Exit;
|
|
}
|
|
|
|
try {
|
|
if (pConvInfo->cb > sizeof(CONVINFO)) {
|
|
SetLastDDEMLError(pcoi->pcii, DMLERR_INVALIDPARAMETER);
|
|
goto Exit;
|
|
}
|
|
ci.cb = pConvInfo->cb;
|
|
ci.hConvPartner = 0; // no longer supported.
|
|
ci.hszSvcPartner = NORMAL_HSZ_FROM_LATOM(pcoi->laService);
|
|
ci.hszServiceReq = NORMAL_HSZ_FROM_LATOM(pcoi->laServiceRequested);
|
|
ci.hszTopic = NORMAL_HSZ_FROM_LATOM(pcoi->laTopic);
|
|
ci.wStatus = pcoi->state;
|
|
ci.wLastError = (WORD)pcoi->pcii->LastError;
|
|
if (pcoi->state & ST_CLIENT) {
|
|
ci.hConvList = ((PCL_CONV_INFO)pcoi)->hConvList;
|
|
GetConvContext(pcoi->hwndConv, (LONG *)&ci.ConvCtxt);
|
|
} else {
|
|
ci.hConvList = 0;
|
|
if (pcoi->state & ST_ISLOCAL) {
|
|
GetConvContext(pcoi->hwndPartner, (LONG *)&ci.ConvCtxt);
|
|
} else {
|
|
ci.ConvCtxt = DefConvContext;
|
|
}
|
|
}
|
|
if (pxi == NULL) {
|
|
ci.hUser = pcoi->hUser;
|
|
ci.hszItem = 0;
|
|
ci.wFmt = 0;
|
|
ci.wType = 0;
|
|
ci.wConvst = XST_CONNECTED;
|
|
} else {
|
|
ci.hUser = pxi->hUser;
|
|
// BUG - not fixable - This will result in extra local atoms
|
|
// since we can never know when he is done with them.
|
|
ci.hszItem = NORMAL_HSZ_FROM_LATOM(GlobalToLocalAtom(pxi->gaItem));
|
|
ci.wFmt = pxi->wFmt;
|
|
ci.wType = pxi->wType;
|
|
ci.wConvst = pxi->state;
|
|
}
|
|
ci.hwnd = pcoi->hwndConv;
|
|
ci.hwndPartner = pcoi->hwndPartner;
|
|
RtlCopyMemory((LPSTR)pConvInfo, (LPSTR)&ci, pConvInfo->cb);
|
|
} except(W32ExceptionHandler(FALSE, RIP_WARNING)) {
|
|
SetLastDDEMLError(pcoi->pcii, DMLERR_INVALIDPARAMETER);
|
|
goto Exit;
|
|
}
|
|
uiRet = TRUE;
|
|
|
|
Exit:
|
|
LeaveDDECrit;
|
|
return (uiRet);
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* DdeSetUserHandle (DDEML API)
|
|
*
|
|
* Description:
|
|
* Sets a user DWORD on a per conversation/transaction basis.
|
|
*
|
|
* History:
|
|
* 11-12-91 sanfords Created.
|
|
\***************************************************************************/
|
|
|
|
FUNCLOG3(LOG_GENERAL, BOOL, DUMMYCALLINGTYPE, DdeSetUserHandle, HCONV, hConv, DWORD, id, DWORD_PTR, hUser)
|
|
BOOL DdeSetUserHandle(
|
|
HCONV hConv,
|
|
DWORD id,
|
|
DWORD_PTR hUser)
|
|
{
|
|
PCONV_INFO pcoi;
|
|
PXACT_INFO pxi;
|
|
BOOL fRet = FALSE;
|
|
|
|
EnterDDECrit;
|
|
|
|
if (!ValidateTransaction(hConv, (HANDLE)LongToHandle( id ), &pcoi, &pxi)) {
|
|
goto Exit;
|
|
}
|
|
if (pxi == NULL) {
|
|
pcoi->hUser = hUser;
|
|
} else {
|
|
pxi->hUser = hUser;
|
|
}
|
|
fRet = TRUE;
|
|
|
|
Exit:
|
|
LeaveDDECrit;
|
|
return (fRet);
|
|
}
|
|
|
|
|
|
|
|
VOID AbandonTransaction(
|
|
PCONV_INFO pcoi,
|
|
PXACT_INFO pxi)
|
|
{
|
|
if (pxi != NULL) {
|
|
pxi->flags |= XIF_ABANDONED;
|
|
} else {
|
|
for (pxi = pcoi->pxiIn; pxi != NULL; pxi = pxi->next) {
|
|
pxi->flags |= XIF_ABANDONED;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
BOOL AbandonEnumerateProc(
|
|
HWND hwnd,
|
|
LPARAM idTransaction)
|
|
{
|
|
PCONV_INFO pcoi;
|
|
|
|
pcoi = (PCONV_INFO)GetWindowLongPtr(hwnd, GWLP_PCI);
|
|
if (!pcoi || !(pcoi->state & ST_CLIENT)) {
|
|
return(TRUE);
|
|
}
|
|
while (pcoi) {
|
|
AbandonTransaction(pcoi, (PXACT_INFO)idTransaction);
|
|
pcoi = pcoi->next;
|
|
}
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************\
|
|
* DdeAbandonTransaction (DDEML API)
|
|
*
|
|
* Description:
|
|
* Cancels application interest in completing an asynchronous transaction.
|
|
*
|
|
* History:
|
|
* 11-12-91 sanfords Created.
|
|
\***************************************************************************/
|
|
|
|
FUNCLOG3(LOG_GENERAL, BOOL, DUMMYCALLINGTYPE, DdeAbandonTransaction, DWORD, idInst, HCONV, hConv, DWORD, idTransaction)
|
|
BOOL DdeAbandonTransaction(
|
|
DWORD idInst,
|
|
HCONV hConv,
|
|
DWORD idTransaction)
|
|
{
|
|
PCONV_INFO pcoi;
|
|
PXACT_INFO pxi;
|
|
PCL_INSTANCE_INFO pcii;
|
|
BOOL fRet = FALSE;
|
|
|
|
EnterDDECrit;
|
|
|
|
pcii = ValidateInstance((HANDLE)LongToHandle( idInst ));
|
|
|
|
if (hConv == 0 && idTransaction == 0) {
|
|
EnumChildWindows(pcii->hwndMother, AbandonEnumerateProc, 0);
|
|
goto Exit;
|
|
}
|
|
if (idTransaction == 0) {
|
|
idTransaction = QID_SYNC;
|
|
}
|
|
if (!ValidateTransaction(hConv, (HANDLE)LongToHandle( idTransaction ), &pcoi, &pxi)) {
|
|
goto Exit;
|
|
}
|
|
if (pcii == NULL || pcoi->pcii != pcii) {
|
|
SetLastDDEMLError(pcoi->pcii, DMLERR_INVALIDPARAMETER);
|
|
goto Exit;
|
|
}
|
|
AbandonTransaction(pcoi, pxi);
|
|
fRet = TRUE;
|
|
|
|
Exit:
|
|
LeaveDDECrit;
|
|
return (fRet);
|
|
}
|
|
|
|
|
|
|
|
|
|
/***************************************************************************\
|
|
* UpdateLinkIfChanged
|
|
*
|
|
* Description:
|
|
* Helper function for updating a link
|
|
*
|
|
* Returns: TRUE if pxi was used - ie fMustReallocPxi
|
|
*
|
|
* History:
|
|
* 3-11-92 sanfords Created.
|
|
* 8-24-92 sanfords added cLinksToGo
|
|
\***************************************************************************/
|
|
BOOL UpdateLinkIfChanged(
|
|
PADVISE_LINK paLink,
|
|
PXACT_INFO pxi,
|
|
PCONV_INFO pcoi,
|
|
PADVISE_LINK paLinkLast,
|
|
PBOOL pfSwapped,
|
|
DWORD cLinksToGo)
|
|
{
|
|
ADVISE_LINK aLinkT;
|
|
|
|
CheckDDECritIn;
|
|
|
|
*pfSwapped = FALSE;
|
|
if (paLink->state & ADVST_CHANGED && !(paLink->state & ADVST_WAITING)) {
|
|
pxi->pfnResponse = SvRespAdviseDataAck;
|
|
pxi->pcoi = pcoi;
|
|
pxi->gaItem = LocalToGlobalAtom(paLink->laItem); // pxi copy
|
|
pxi->wFmt = paLink->wFmt;
|
|
pxi->wType = paLink->wType;
|
|
paLink->state &= ~ADVST_CHANGED;
|
|
if (SvStartAdviseUpdate(pxi, cLinksToGo)) {
|
|
if (pxi->wType & DDE_FACKREQ) {
|
|
paLink->state |= ADVST_WAITING;
|
|
/*
|
|
* swap paLink with the last non-moved link to make ack search find
|
|
* oldest updated format.
|
|
*/
|
|
if (paLink != paLinkLast) {
|
|
aLinkT = *paLink;
|
|
RtlMoveMemory(paLink, paLink + 1,
|
|
(PBYTE)paLinkLast - (PBYTE)paLink);
|
|
*paLinkLast = aLinkT;
|
|
*pfSwapped = TRUE;
|
|
}
|
|
}
|
|
return(TRUE);
|
|
} else {
|
|
GlobalDeleteAtom(pxi->gaItem); // pxi copy
|
|
return(FALSE);
|
|
}
|
|
}
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* DdePostAdvise (DDEML API)
|
|
*
|
|
* Description:
|
|
* Updates outstanding server advise links as needed.
|
|
*
|
|
* History:
|
|
* 11-12-91 sanfords Created.
|
|
\***************************************************************************/
|
|
|
|
FUNCLOG3(LOG_GENERAL, BOOL, DUMMYCALLINGTYPE, DdePostAdvise, DWORD, idInst, HSZ, hszTopic, HSZ, hszItem)
|
|
BOOL DdePostAdvise(
|
|
DWORD idInst,
|
|
HSZ hszTopic,
|
|
HSZ hszItem)
|
|
{
|
|
PCL_INSTANCE_INFO pcii;
|
|
PSVR_CONV_INFO psi;
|
|
PXACT_INFO pxi;
|
|
PADVISE_LINK paLink;
|
|
BOOL fRet = FALSE, fSwapped, fFound;
|
|
int iServer, iLink;
|
|
PLINK_COUNT pLinkCount;
|
|
#if DBG
|
|
int cLinks;
|
|
#endif
|
|
|
|
EnterDDECrit;
|
|
|
|
pcii = ValidateInstance((HANDLE)LongToHandle( idInst ));
|
|
if (pcii == NULL) {
|
|
BestSetLastDDEMLError(DMLERR_INVALIDPARAMETER);
|
|
goto Exit;
|
|
}
|
|
if ((ValidateHSZ(hszTopic) == HSZT_INVALID) ||
|
|
(ValidateHSZ(hszItem) == HSZT_INVALID)) {
|
|
SetLastDDEMLError(pcii, DMLERR_INVALIDPARAMETER);
|
|
goto Exit;
|
|
}
|
|
|
|
/*
|
|
* Initialize all link counters and check if any links qualify
|
|
*/
|
|
fFound = FALSE;
|
|
for (pLinkCount = pcii->pLinkCount;
|
|
pLinkCount; pLinkCount = pLinkCount->next) {
|
|
pLinkCount->Count = pLinkCount->Total;
|
|
fFound |= pLinkCount->laTopic == LATOM_FROM_HSZ(hszTopic) &&
|
|
pLinkCount->laItem == LATOM_FROM_HSZ(hszItem);
|
|
}
|
|
if (!fFound && hszTopic && hszItem) {
|
|
fRet = TRUE;
|
|
goto Exit;
|
|
}
|
|
|
|
/*
|
|
* preallocate incase we are low on memory.
|
|
*/
|
|
pxi = DDEMLAlloc(sizeof(XACT_INFO));
|
|
if (pxi == NULL) {
|
|
SetLastDDEMLError(pcii, DMLERR_MEMORY_ERROR);
|
|
fRet = FALSE;
|
|
goto Exit;
|
|
}
|
|
|
|
/*
|
|
* For each server window on the specified topic
|
|
*/
|
|
for (iServer = 0; iServer < pcii->cServerLookupAlloc; iServer++) {
|
|
if (hszTopic == 0 ||
|
|
pcii->aServerLookup[iServer].laTopic == LATOM_FROM_HSZ(hszTopic)) {
|
|
|
|
/*
|
|
* For each conversation within that window
|
|
*/
|
|
psi = (PSVR_CONV_INFO)GetWindowLongPtr(
|
|
pcii->aServerLookup[iServer].hwndServer, GWLP_PSI);
|
|
UserAssert(psi != NULL && psi->ci.pcii == pcii); // sanity check
|
|
while (psi != NULL) {
|
|
|
|
|
|
/*
|
|
* UpdateLinkIfChanged might leave the critical section so lock this conversation
|
|
*/
|
|
psi->ci.cLocks++;
|
|
|
|
#if DBG
|
|
/*
|
|
* Rememeber the number of links so we can assert if they change during the loop below
|
|
*/
|
|
cLinks = psi->ci.cLinks;
|
|
#endif
|
|
/*
|
|
* For each active link on the given item...
|
|
*/
|
|
for (paLink = psi->ci.aLinks, iLink = 0;
|
|
iLink < psi->ci.cLinks; paLink++, iLink++) {
|
|
if (hszItem == 0 ||
|
|
paLink->laItem == LATOM_FROM_HSZ(hszItem)) {
|
|
|
|
// Bit of a hack here. For FACKREQ links, we don't want the server to
|
|
// outrun the client so we set the ADVST_WAITING bit till the ack is
|
|
// received. When the ack comes in, the protocol code has to search
|
|
// the aLinks array again to locate the apropriate link state flags and
|
|
// clear the ADVST_WAITING flag. At that time, if the ADVST_CHANGED flag
|
|
// is set, it is cleared and another SvStartAdviseUpdate transaction
|
|
// is started to get the link up to date. To complicate matters,
|
|
// the ACK contains no format information. Thus we need to move
|
|
// the Link info to the end of the list so that the right format
|
|
// is updated when the ack comes in.
|
|
|
|
paLink->state |= ADVST_CHANGED;
|
|
if (UpdateLinkIfChanged(paLink, pxi, &psi->ci,
|
|
&psi->ci.aLinks[psi->ci.cLinks - 1],
|
|
&fSwapped, --paLink->pLinkCount->Count)) {
|
|
if (fSwapped) {
|
|
paLink--;
|
|
}
|
|
/*
|
|
* preallocate for next advise
|
|
*/
|
|
pxi = DDEMLAlloc(sizeof(XACT_INFO));
|
|
if (pxi == NULL) {
|
|
SetLastDDEMLError(pcii, DMLERR_MEMORY_ERROR);
|
|
/*
|
|
* Unlock the conversation
|
|
*/
|
|
psi->ci.cLocks--;
|
|
if ((psi->ci.cLocks == 0) && (psi->ci.state & ST_FREE_CONV_RES_NOW)) {
|
|
RIPMSG1(RIP_ERROR, "DdePostAdvise: Conversation terminated. psi:%#p", psi);
|
|
FreeConversationResources((PCONV_INFO)psi);
|
|
}
|
|
goto Exit;
|
|
}
|
|
}
|
|
/*
|
|
* We might have left the crit sect...
|
|
*/
|
|
UserAssert(pcii == ValidateInstance((HANDLE)LongToHandle( idInst )));
|
|
}
|
|
}
|
|
#if DBG
|
|
if (cLinks != psi->ci.cLinks) {
|
|
RIPMSG1(RIP_ERROR, "DdePostAdvise: cLinks changed. psi:%#p", psi);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* If the converstaion got nuked, stop working on this conversation chain.
|
|
*/
|
|
psi->ci.cLocks--;
|
|
if ((psi->ci.cLocks == 0) && (psi->ci.state & ST_FREE_CONV_RES_NOW)) {
|
|
RIPMSG1(RIP_ERROR, "DdePostAdvise: Conversation terminated. psi:%#p", psi);
|
|
FreeConversationResources((PCONV_INFO)psi);
|
|
break;
|
|
}
|
|
|
|
psi = (PSVR_CONV_INFO)psi->ci.next; // next conversation
|
|
}
|
|
}
|
|
}
|
|
DDEMLFree(pxi);
|
|
fRet = TRUE;
|
|
|
|
Exit:
|
|
/*
|
|
* Because callbacks are capable of blocking DdeUninitialize(), we check
|
|
* before exit to see if it needs to be called.
|
|
*/
|
|
UserAssert(pcii == ValidateInstance((HANDLE)LongToHandle( idInst )));
|
|
if (pcii != NULL &&
|
|
pcii->afCmd & APPCMD_UNINIT_ASAP &&
|
|
!(pcii->flags & IIF_IN_SYNC_XACT) &&
|
|
!pcii->cInDDEMLCallback) {
|
|
DdeUninitialize(HandleToUlong(pcii->hInstClient));
|
|
fRet = TRUE;
|
|
}
|
|
LeaveDDECrit;
|
|
return (fRet);
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* LinkTransaction
|
|
*
|
|
* Description:
|
|
* Adds a transaction structure to the associated conversation's transaction
|
|
* queue.
|
|
*
|
|
* History:
|
|
* 11-12-91 sanfords Created.
|
|
\***************************************************************************/
|
|
VOID LinkTransaction(
|
|
PXACT_INFO pxi)
|
|
{
|
|
CheckDDECritIn;
|
|
|
|
pxi->next = NULL;
|
|
if (pxi->pcoi->pxiOut == NULL) {
|
|
pxi->pcoi->pxiIn = pxi->pcoi->pxiOut = pxi;
|
|
} else {
|
|
pxi->pcoi->pxiIn->next = pxi;
|
|
pxi->pcoi->pxiIn = pxi;
|
|
}
|
|
#if DBG
|
|
/*
|
|
* Temporary check to find stress bug - make sure pxi list is not
|
|
* looped on itself. If it is, this loop will never exit and things
|
|
* will get investigated. (sanfords)
|
|
*/
|
|
{
|
|
PXACT_INFO pxiT;
|
|
|
|
for (pxiT = pxi->pcoi->pxiOut; pxiT != NULL; pxiT = pxiT->next) {
|
|
;
|
|
}
|
|
}
|
|
#endif // DBG
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* UnlinkTransaction
|
|
*
|
|
* Description:
|
|
* Removes a transaction structure from the associated conversation's transaction
|
|
* queue.
|
|
*
|
|
* History:
|
|
* 11-12-91 sanfords Created.
|
|
\***************************************************************************/
|
|
VOID UnlinkTransaction(
|
|
PXACT_INFO pxi)
|
|
{
|
|
CheckDDECritIn;
|
|
if (pxi == pxi->pcoi->pxiOut) {
|
|
pxi->pcoi->pxiOut = pxi->next;
|
|
if (pxi->next == NULL) {
|
|
pxi->pcoi->pxiIn = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* ValidateTransaction
|
|
*
|
|
* Description:
|
|
* Common validation code for DDEML APIs that take a conversation handle
|
|
* and a transaction ID. *ppxi may be null on return if hXact was 0.
|
|
* Returns fSuccess.
|
|
*
|
|
* History:
|
|
* 11-12-91 sanfords Created.
|
|
\***************************************************************************/
|
|
BOOL ValidateTransaction(
|
|
HCONV hConv,
|
|
HANDLE hXact,
|
|
PCONV_INFO *ppcoi,
|
|
PXACT_INFO *ppxi)
|
|
{
|
|
PCL_INSTANCE_INFO pcii;
|
|
|
|
*ppcoi = (PCONV_INFO)ValidateCHandle((HANDLE)hConv,
|
|
HTYPE_CLIENT_CONVERSATION, HINST_ANY);
|
|
if (*ppcoi == NULL) {
|
|
*ppcoi = (PCONV_INFO)ValidateCHandle((HANDLE)hConv,
|
|
HTYPE_SERVER_CONVERSATION, HINST_ANY);
|
|
}
|
|
if (*ppcoi == NULL) {
|
|
BestSetLastDDEMLError(DMLERR_INVALIDPARAMETER);
|
|
return (FALSE);
|
|
}
|
|
pcii = ValidateInstance((*ppcoi)->pcii->hInstClient);
|
|
if (pcii != (*ppcoi)->pcii) {
|
|
BestSetLastDDEMLError(DMLERR_INVALIDPARAMETER);
|
|
return (FALSE);
|
|
}
|
|
|
|
if (hXact == (HANDLE)IntToPtr( QID_SYNC )) {
|
|
*ppxi = NULL;
|
|
} else {
|
|
*ppxi = (PXACT_INFO)ValidateCHandle(hXact, HTYPE_TRANSACTION,
|
|
InstFromHandle((*ppcoi)->pcii->hInstClient));
|
|
if (*ppxi == NULL) {
|
|
SetLastDDEMLError((*ppcoi)->pcii, DMLERR_INVALIDPARAMETER);
|
|
return (FALSE);
|
|
}
|
|
}
|
|
return (TRUE);
|
|
}
|