Leaked source code of windows server 2003
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.
 
 
 
 
 
 

1524 lines
45 KiB

/****************************** Module Header ******************************\
* Module Name: connect.c
*
* Copyright (c) 1985 - 1999, Microsoft Corporation
*
* DDE Manager conversation connection functions
*
* Created: 11/3/91 Sanford Staab
*
\***************************************************************************/
#include "precomp.h"
#pragma hdrstop
#include "nddeagnt.h"
//#define TESTING
#ifdef TESTING
ULONG
DbgPrint(
PCH Format,
...
);
VOID
DbgUserBreakPoint(
VOID
);
BOOL ValidateConvList(
HCONVLIST hConvList)
{
PCONVLIST pcl;
PCL_CONV_INFO pci;
PXACT_INFO pxi;
int i;
BOOL fMatch;
if (hConvList == 0) {
return(TRUE);
}
pcl = (PCONVLIST)ValidateCHandle((HANDLE)hConvList,
HTYPE_CONVERSATION_LIST,
HINST_ANY);
for (i = 0; i < pcl->chwnd; i++) {
/*
* all windows in the list are valid
*/
if (!IsWindow(pcl->ahwnd[i])) {
DebugBreak();
}
pci = (PCL_CONV_INFO)GetWindowLongPtr(pcl->ahwnd[i], GWLP_PCI);
/*
* All windows have at least one convinfo associated with them.
*/
if (pci == NULL) {
DebugBreak();
}
fMatch = FALSE;
while (pci != NULL) {
/*
* All non-zombie conversations have hConvList set correctly.
*/
if (pci->hConvList != hConvList &&
TypeFromHandle(pci->ci.hConv) != HTYPE_ZOMBIE_CONVERSATION) {
DebugBreak();
}
/*
* All conversations have hConvList clear or set correctly.
*/
if (pci->hConvList != 0 && pci->hConvList != hConvList) {
DebugBreak();
}
/*
* At least 1 of the conversations references the list
*/
if (pci->hConvList == hConvList) {
fMatch = TRUE;
}
for (pxi = pci->ci.pxiOut; pxi; pxi = pxi->next) {
if ((PCL_CONV_INFO)pxi->pcoi != pci) {
DebugBreak();
}
}
pci = (PCL_CONV_INFO)pci->ci.next;
}
if (!fMatch) {
/*
* At least 1 of the conversations references the list
*/
DebugBreak;
}
}
return(TRUE);
}
VOID ValidateAllConvLists()
{
ApplyFunctionToObjects(HTYPE_CONVERSATION_LIST, HINST_ANY,
(PFNHANDLEAPPLY)ValidateConvList);
}
#else // TESTING
#define ValidateConvList(h)
#define ValidateAllConvLists()
#endif // TESTING
CONVCONTEXT TempConvContext;
CONVCONTEXT DefConvContext = {
sizeof(CONVCONTEXT),
0,
0,
CP_WINANSI,
0L,
0L,
{
sizeof(SECURITY_QUALITY_OF_SERVICE),
SecurityImpersonation,
SECURITY_STATIC_TRACKING,
TRUE
}
};
typedef struct tagINIT_ENUM {
HWND hwndClient;
HWND hwndSkip;
LONG lParam;
LATOM laServiceRequested;
LATOM laTopic;
HCONVLIST hConvList;
DWORD clst;
} INIT_ENUM, *PINIT_ENUM;
BOOL InitiateEnumerationProc(HWND hwndTarget, PINIT_ENUM pie);
VOID DisconnectConv(PCONV_INFO pcoi);
/***************************************************************************\
* DdeConnect (DDEML API)
*
* Description:
* Initiates a DDE conversation.
*
* History:
* 11-1-91 sanfords Created.
\***************************************************************************/
FUNCLOG4(LOG_GENERAL, HCONV, DUMMYCALLINGTYPE, DdeConnect, DWORD, idInst, HSZ, hszService, HSZ, hszTopic, PCONVCONTEXT, pCC)
HCONV DdeConnect(
DWORD idInst,
HSZ hszService,
HSZ hszTopic,
PCONVCONTEXT pCC)
{
PCL_INSTANCE_INFO pcii;
PCL_CONV_INFO pci;
HCONV hConvRet = 0;
HWND hwndTarget = 0;
LATOM aNormalSvcName = 0;
EnterDDECrit;
if (!ValidateConnectParameters((HANDLE)LongToHandle( idInst ), &pcii, &hszService, hszTopic,
&aNormalSvcName, &pCC, &hwndTarget, 0)) {
goto Exit;
}
pci = ConnectConv(pcii, LATOM_FROM_HSZ(hszService), LATOM_FROM_HSZ(hszTopic),
hwndTarget,
(pcii->afCmd & CBF_FAIL_SELFCONNECTIONS) ? pcii->hwndMother : 0,
pCC, 0, CLST_SINGLE_INITIALIZING);
if (pci == NULL) {
SetLastDDEMLError(pcii, DMLERR_NO_CONV_ESTABLISHED);
goto Exit;
} else {
hConvRet = pci->ci.hConv;
}
Exit:
if (aNormalSvcName) {
GlobalDeleteAtom(aNormalSvcName);
}
LeaveDDECrit;
return (hConvRet);
}
/***************************************************************************\
* DdeConnectList (DDEML API)
*
* Description:
* Initiates DDE conversations with multiple servers or adds unique servers
* to an existing conversation list.
*
* History:
* 11-12-91 sanfords Created.
\***************************************************************************/
FUNCLOG5(LOG_GENERAL, HCONVLIST, DUMMYCALLINGTYPE, DdeConnectList, DWORD, idInst, HSZ, hszService, HSZ, hszTopic, HCONVLIST, hConvList, PCONVCONTEXT, pCC)
HCONVLIST DdeConnectList(
DWORD idInst,
HSZ hszService,
HSZ hszTopic,
HCONVLIST hConvList,
PCONVCONTEXT pCC)
{
PCL_INSTANCE_INFO pcii;
PCONV_INFO pcoi, pcoiNew, pcoiExisting, pcoiNext;
HCONVLIST hConvListRet = 0;
HWND hwndTarget = 0;
LATOM aNormalSvcName = 0;
PCONVLIST pcl = NULL, pclTemp = NULL;
HCONVLIST hConvListOld;
int i;
CheckDDECritOut;
EnterDDECrit;
if (!ValidateConnectParameters((HANDLE)LongToHandle( idInst ), &pcii, &hszService, hszTopic,
&aNormalSvcName, &pCC, &hwndTarget, hConvList)) {
goto Exit;
}
ValidateConvList(hConvList);
hConvListOld = hConvList;
pcoi = (PCONV_INFO)ConnectConv(pcii,
LATOM_FROM_HSZ(hszService),
LATOM_FROM_HSZ(hszTopic),
hwndTarget,
(pcii->afCmd & (CBF_FAIL_SELFCONNECTIONS | CBF_FAIL_CONNECTIONS)) ?
pcii->hwndMother : 0,
pCC,
hConvListOld,
CLST_MULT_INITIALIZING);
if (pcoi == NULL) {
/*
* no new connections made
*/
SetLastDDEMLError(pcii, DMLERR_NO_CONV_ESTABLISHED);
hConvListRet = hConvListOld;
goto Exit;
}
/*
* allocate or reallocate the hConvList hwnd list for later addition
* If we already have a valid list, reuse the handle so we don't have
* to alter the preexisting pcoi->hConvList values.
*/
if (hConvListOld == 0) {
pcl = (PCONVLIST)DDEMLAlloc(sizeof(CONVLIST));
if (pcl == NULL) {
SetLastDDEMLError(pcii, DMLERR_MEMORY_ERROR);
DisconnectConv(pcoi);
goto Exit;
}
// pcl->chwnd = 0; LPTR zero inits.
hConvList = (HCONVLIST)CreateHandle((ULONG_PTR)pcl,
HTYPE_CONVERSATION_LIST, InstFromHandle(pcii->hInstClient));
if (hConvList == 0) {
DDEMLFree(pcl);
SetLastDDEMLError(pcii, DMLERR_MEMORY_ERROR);
DisconnectConv(pcoi);
goto Exit;
}
} else {
pcl = (PCONVLIST)GetHandleData((HANDLE)hConvList);
pclTemp = DDEMLReAlloc(pcl, sizeof(CONVLIST) + sizeof(HWND) * pcl->chwnd);
if (pclTemp == NULL) {
SetLastDDEMLError(pcii, DMLERR_MEMORY_ERROR);
hConvListRet = hConvListOld;
DisconnectConv(pcoi);
goto Exit;
}
pcl = pclTemp;
SetHandleData((HANDLE)hConvList, (ULONG_PTR)pcl);
}
ValidateConvList(hConvListOld);
if (hConvListOld) {
/*
* remove duplicates from new conversations
*
* Although we tried to prevent duplicates from happening
* within the initiate enumeration code, wild initiates or
* servers responding with different service names than
* requested could cause duplicates.
*/
/* For each client window... */
for (i = 0; i < pcl->chwnd; i++) {
/* For each existing conversation in that window... */
for (pcoiExisting = (PCONV_INFO)
GetWindowLongPtr(pcl->ahwnd[i], GWLP_PCI);
pcoi != NULL && pcoiExisting != NULL;
pcoiExisting = pcoiExisting->next) {
if (!(pcoiExisting->state & ST_CONNECTED))
continue;
/* For each new conversation... */
for (pcoiNew = pcoi; pcoiNew != NULL; pcoiNew = pcoiNext) {
pcoiNext = pcoiNew->next;
/* see if the new conversation duplicates the existing one */
if (!(pcoiNew->state & ST_CONNECTED))
continue;
UserAssert(((PCL_CONV_INFO)pcoiExisting)->hwndReconnect);
UserAssert(((PCL_CONV_INFO)pcoiNew)->hwndReconnect);
if (((PCL_CONV_INFO)pcoiExisting)->hwndReconnect ==
((PCL_CONV_INFO)pcoiNew)->hwndReconnect &&
pcoiExisting->laTopic == pcoiNew->laTopic &&
pcoiExisting->laService == pcoiNew->laService) {
/*
* duplicate conversation - disconnection causes an unlink
*/
if (pcoiNew == pcoi) {
/*
* We are freeing up the head of the list,
* Reset the head to the next guy.
*/
pcoi = pcoiNext;
}
ValidateConvList(hConvList);
ShutdownConversation(pcoiNew, FALSE);
ValidateConvList(hConvList);
break;
}
}
}
}
for (pcoiExisting = pcoi; pcoiExisting != NULL; pcoiExisting = pcoiExisting->next) {
/*
* if these are all zombies - we DONT want to link it in!
* This is possible because ShutdownConversation() leaves the critical section
* and could allow responding terminates to come through.
*/
if (pcoiExisting->state & ST_CONNECTED) {
goto FoundOne;
}
}
pcoi = NULL; // abandon this guy - he will clean up in time.
FoundOne:
/*
* add new pcoi (if any are left) hwnd to ConvList hwnd list.
*/
if (pcoi != NULL) {
UserAssert(pcoi->hwndConv);
pcl->ahwnd[pcl->chwnd] = pcoi->hwndConv;
pcl->chwnd++;
hConvListRet = hConvList;
} else {
hConvListRet = hConvListOld;
if (!hConvListOld) {
DestroyHandle((HANDLE)hConvList);
}
}
} else { // no hConvListOld
UserAssert(pcoi->hwndConv);
pcl->ahwnd[0] = pcoi->hwndConv;
pcl->chwnd = 1;
hConvListRet = hConvList;
}
if (pcoi != NULL) {
/*
* set hConvList field for all remaining new conversations.
*/
UserAssert(hConvListRet);
for (pcoiNew = pcoi; pcoiNew != NULL; pcoiNew = pcoiNew->next) {
if (pcoiNew->state & ST_CONNECTED) {
((PCL_CONV_INFO)pcoiNew)->hConvList = hConvListRet;
}
}
}
Exit:
if (aNormalSvcName) {
DeleteAtom(aNormalSvcName);
}
ValidateConvList(hConvListRet);
LeaveDDECrit;
return (hConvListRet);
}
/***************************************************************************\
* DdeReconnect (DDEML API)
*
* Description:
* Attempts to reconnect an externally (from the server) terminated
* client side conversation.
*
* History:
* 11-12-91 sanfords Created.
\***************************************************************************/
FUNCLOG1(LOG_GENERAL, HCONV, DUMMYCALLINGTYPE, DdeReconnect, HCONV, hConv)
HCONV DdeReconnect(
HCONV hConv)
{
PCL_INSTANCE_INFO pcii;
PCL_CONV_INFO pci, pciNew;
HCONV hConvRet = 0;
CONVCONTEXT cc;
EnterDDECrit;
pcii = PciiFromHandle((HANDLE)hConv);
if (pcii == NULL) {
BestSetLastDDEMLError(DMLERR_INVALIDPARAMETER);
goto Exit;
}
pci = (PCL_CONV_INFO)ValidateCHandle((HANDLE)hConv,
HTYPE_CLIENT_CONVERSATION, HINST_ANY);
if (pci == NULL) {
SetLastDDEMLError(pcii, DMLERR_INVALIDPARAMETER);
goto Exit;
}
if (pci->ci.state & ST_CONNECTED) {
goto Exit;
}
GetConvContext(pci->ci.hwndConv, (LONG *)&cc);
pciNew = ConnectConv(pcii, pci->ci.laService, pci->ci.laTopic,
pci->hwndReconnect, 0, &cc, 0, CLST_SINGLE_INITIALIZING);
if (pciNew == NULL) {
SetLastDDEMLError(pcii, DMLERR_NO_CONV_ESTABLISHED);
goto Exit;
} else {
hConvRet = pciNew->ci.hConv;
if (pci->ci.cLinks) {
PXACT_INFO pxi;
int iLink;
PADVISE_LINK paLink;
/*
* reestablish advise links
*/
for (paLink = pci->ci.aLinks, iLink = pci->ci.cLinks;
iLink; paLink++, iLink--) {
pxi = (PXACT_INFO)DDEMLAlloc(sizeof(XACT_INFO));
if (pxi == NULL) {
break; // abort relinking
}
pxi->pcoi = (PCONV_INFO)pciNew;
pxi->gaItem = LocalToGlobalAtom(paLink->laItem); // pxi copy
pxi->wFmt = paLink->wFmt;
pxi->wType = (WORD)((paLink->wType >> 12) | XTYP_ADVSTART);
if (ClStartAdvise(pxi)) {
pxi->flags |= XIF_ABANDONED;
} else {
GlobalDeleteAtom(pxi->gaItem);
DDEMLFree(pxi);
}
}
}
}
Exit:
LeaveDDECrit;
return (hConvRet);
}
/***************************************************************************\
* ValidateConnectParameters
*
* Description:
* worker function to handle common validation code.
*
* Note that paNormalSvcName is set to the atom value created upon extracting
* a normal HSZ from an InstanceSpecific HSZ.
*
* History:
* 11-12-91 sanfords Created.
\***************************************************************************/
BOOL ValidateConnectParameters(
HANDLE hInst,
PCL_INSTANCE_INFO *ppcii, // set if valid hInst
HSZ *phszService, // altered if InstSpecific HSZ
HSZ hszTopic,
LATOM *plaNormalSvcName, // set to atom that needs freeing when done
PCONVCONTEXT *ppCC, // set to point to DefConvContext if NULL
HWND *phwndTarget, // set if hszService is InstSpecific
HCONVLIST hConvList)
{
DWORD hszType;
BOOL fError = FALSE;
*ppcii = ValidateInstance(hInst);
if (*ppcii == NULL) {
return (FALSE);
}
hszType = ValidateHSZ(*phszService);
if (hszType == HSZT_INVALID || ValidateHSZ(hszTopic) == HSZT_INVALID) {
SetLastDDEMLError(*ppcii, DMLERR_INVALIDPARAMETER);
return (FALSE);
}
if (hszType == HSZT_INST_SPECIFIC) {
*phwndTarget = ParseInstSpecificAtom(LATOM_FROM_HSZ(*phszService),
plaNormalSvcName);
if (*plaNormalSvcName == 0) {
SetLastDDEMLError(*ppcii, DMLERR_SYS_ERROR);
return (FALSE);
}
*phszService = NORMAL_HSZ_FROM_LATOM(*plaNormalSvcName);
}
if (*ppCC == NULL) {
*ppCC = &DefConvContext;
if ((*ppcii)->flags & IIF_UNICODE) {
(*ppCC)->iCodePage = CP_WINUNICODE;
} else {
(*ppCC)->iCodePage = CP_WINANSI;
}
} else try {
if ((*ppCC)->cb > sizeof(CONVCONTEXT)) {
SetLastDDEMLError(*ppcii, DMLERR_INVALIDPARAMETER);
fError = TRUE;
} else if ((*ppCC)->cb < sizeof(CONVCONTEXT)) {
TempConvContext = DefConvContext;
/*
* we can use this static temp because we are synchronized.
*/
RtlCopyMemory(&TempConvContext, *ppCC, (*ppCC)->cb);
*ppCC = &TempConvContext;
}
} except(W32ExceptionHandler(FALSE, RIP_WARNING)) {
SetLastDDEMLError(*ppcii, DMLERR_INVALIDPARAMETER);
fError = TRUE;
}
if (fError) {
return(FALSE);
}
if (hConvList != 0 &&
!ValidateCHandle((HANDLE)hConvList, HTYPE_CONVERSATION_LIST,
(DWORD)InstFromHandle((*ppcii)->hInstClient))) {
return (FALSE);
}
return (TRUE);
}
/***************************************************************************\
* ConnectConv
*
* Description:
* Work function for all Connect cases.
*
* Method:
*
* To reduce the number of windows we use and to simplify how client
* windows handle multiple WM_DDE_ACK messages during initiation, a
* single client window can handle many conversations, each with
* a different server window.
*
* The client window is created and set to a initiation state via the
* GWL_CONVSTATE window word. Initiates are then sent to enumerated server
* window candidates.
* The GWL_CONVSTATE value is used by the DDEML mother windows
* to determine if only one or several ACKs are desired to minimize
* unnessary message traffic.
*
* The client window GWL_CONVCONTEXT? window words are also used by
* Event Windows to pass context information.
*
* Note that all client and server windows are children of the mother
* window. This reduces the number of top level windows that
* WM_DDE_INITIATES need to hit.
*
* Each WM_DDE_ACK that is received by a client window while in the
* initiation state causes it to create a CL_CONV_INFO structure,
* partially initialize it, and link it into its list of CL_CONV_INFO
* structures. The head of the list is pointed to by the GWLP_PCI
* client window word.
*
* After each WM_DDE_INITIALIZE is sent, the GWLP_PCI value is checked
* to see if it exists and needs initialization to be completed. If
* this is the case the init code knows that at least one ACK was
* received in response to the WM_DDE_INITIALIZE send. The
* initialization of each CL_CONV_INFO struct that needs it is then completed.
*
* Once the broadcasting of WM_DDE_INITIALIZE is done, the init code
* then sets the GWL_CONVSTATE value in the client window to indicate that
* initialization is complete.
*
* Returns:
* The head pci to the client window or NULL if no connections made it.
*
* History:
* 11-1-91 sanfords Created.
\***************************************************************************/
PCL_CONV_INFO ConnectConv(
PCL_INSTANCE_INFO pcii,
LATOM laService,
LATOM laTopic,
HWND hwndTarget, // 0 implies broadcast
HWND hwndSkip, // 0 implies no skips - avoids self-connections.
PCONVCONTEXT pCC,
HCONVLIST hConvList,
DWORD clst)
{
INIT_ENUM ie;
PCL_CONV_INFO pci;
PCONV_INFO pcoi;
GATOM gaService, gaTopic;
CheckDDECritIn;
if (hwndTarget && hwndTarget == hwndSkip) {
return(NULL);
}
LeaveDDECrit;
CheckDDECritOut;
if (pcii->flags & IIF_UNICODE) {
ie.hwndClient = CreateWindowW((LPWSTR)(gpsi->atomSysClass[ICLS_DDEMLCLIENTW]),
L"",
WS_CHILD,
0, 0, 0, 0,
pcii->hwndMother,
(HMENU)0,
(HANDLE)0,
(LPVOID)NULL);
} else {
ie.hwndClient = CreateWindowA((LPSTR)(gpsi->atomSysClass[ICLS_DDEMLCLIENTA]),
"",
WS_CHILD,
0, 0, 0, 0,
pcii->hwndMother,
(HMENU)0,
(HANDLE)0,
(LPVOID)NULL);
}
EnterDDECrit;
if (ie.hwndClient == 0) {
return (NULL);
}
if (pCC != NULL) {
if (!NtUserDdeSetQualityOfService(ie.hwndClient, &(pCC->qos), NULL)) {
SetLastDDEMLError(pcii, DMLERR_MEMORY_ERROR);
goto Error;
}
}
/*
* Note that a pci will be created and allocated for each ACK recieved.
*/
SetConvContext(ie.hwndClient, (LONG *)pCC);
SetWindowLong(ie.hwndClient, GWL_CONVSTATE, clst);
SetWindowLongPtr(ie.hwndClient, GWLP_SHINST, (LONG_PTR)pcii->hInstServer);
SetWindowLongPtr(ie.hwndClient, GWLP_CHINST, (LONG_PTR)pcii->hInstClient);
gaService = LocalToGlobalAtom(laService);
gaTopic = LocalToGlobalAtom(laTopic);
ie.lParam = MAKELONG(gaService, gaTopic);
if (!hwndTarget) {
ie.hwndSkip = hwndSkip;
ie.laServiceRequested = laService;
ie.laTopic = laTopic;
ie.hConvList = hConvList;
ie.clst = clst;
}
LeaveDDECrit;
if (hwndTarget) {
SendMessage(hwndTarget, WM_DDE_INITIATE, (WPARAM)ie.hwndClient,
ie.lParam);
} else {
/*
* Send this message to the nddeagnt app first so it can start
* the netdde services BEFORE we do an enumeration of windows.
* This lets things work the first time. NetDDEAgent caches
* service status so this is the fastest way to do this.
*/
HWND hwndAgent = FindWindowW(SZ_NDDEAGNT_CLASS, SZ_NDDEAGNT_TITLE);
if (hwndAgent) {
SendMessage(hwndAgent,
WM_DDE_INITIATE, (WPARAM)ie.hwndClient, ie.lParam);
}
EnumWindows((WNDENUMPROC)InitiateEnumerationProc, (LPARAM)&ie);
}
EnterDDECrit;
/*
* hConvList may have been destroyed during the enumeration but we are
* done with it now so no need to revalidate.
*/
#if DBG
{
WCHAR sz[10];
if (gaService && GlobalGetAtomName(gaService, sz, 10) == 0) {
RIPMSG1(RIP_ERROR, "Bad Service Atom after Initiate phase: %lX", (DWORD)gaService);
}
if (gaTopic && GlobalGetAtomName(gaTopic, sz, 10) == 0) {
RIPMSG1(RIP_ERROR, "Bad Topic Atom after Initiate phase: %lX", (DWORD)gaTopic);
}
}
#endif // DBG
GlobalDeleteAtom(gaService);
GlobalDeleteAtom(gaTopic);
//
// Get the first pci allocated when a WM_DDE_ACK was recieved.
//
pci = (PCL_CONV_INFO)GetWindowLongPtr(ie.hwndClient, GWLP_PCI);
if (pci == NULL) {
Error:
LeaveDDECrit;
NtUserDestroyWindow(ie.hwndClient);
EnterDDECrit;
return (NULL);
}
SetWindowLong(ie.hwndClient, GWL_CONVSTATE, CLST_CONNECTED);
if (hwndTarget) {
/*
* If hwndTarget was NULL, the enumeration proc took care of this.
*/
pci->hwndReconnect = hwndTarget;
UserAssert(pci->ci.next == NULL);
pci->ci.laServiceRequested = laService;
IncLocalAtomCount(laService); // pci copy
}
if (pcii->MonitorFlags & MF_CONV) {
for (pcoi = (PCONV_INFO)pci; pcoi; pcoi = pcoi->next) {
MONCONV(pcoi, TRUE);
}
}
return (pci);
}
/*
* Undoes the work of ConnectConv()
*/
VOID DisconnectConv(
PCONV_INFO pcoi)
{
PCONV_INFO pcoiNext;
for (; pcoi; pcoi = pcoiNext) {
pcoiNext = pcoi->next;
ShutdownConversation(pcoi, FALSE);
}
}
/***************************************************************************\
* InitiateEnumerationProc (FILE LOCAL)
*
* Description:
* Function used via EnumWindows to enumerate all server window candidates
* during DDE initiation. The enumeration allows DDEML to know what
* window WM_DDE_INITIATE was sent to so that it can be remembered for
* possible reconnection later. (The window that receives the WM_DDE_INITIATE
* message is not necessarily going to be the server window.)
*
* History:
* 11-1-91 sanfords Created.
\***************************************************************************/
BOOL InitiateEnumerationProc(
HWND hwndTarget,
PINIT_ENUM pie)
{
PCL_CONV_INFO pci;
CheckDDECritOut;
if (hwndTarget == pie->hwndSkip) {
return (TRUE);
}
if (pie->hConvList && pie->laTopic && pie->laServiceRequested) {
/*
* Head off duplicates BEFORE we send the WM_DDE_INITIATE messages!
*/
PCONVLIST pcl;
PCONV_INFO pcoiExisting;
int i;
EnterDDECrit;
/*
* We revalidate hConvList here because we left the critical section.
*/
pcl = (PCONVLIST)ValidateCHandle((HANDLE)pie->hConvList,
HTYPE_CONVERSATION_LIST, HINST_ANY);
if (pcl != NULL) {
for (i = 0; i < pcl->chwnd; i++) {
for (pcoiExisting = (PCONV_INFO)GetWindowLongPtr(pcl->ahwnd[i], GWLP_PCI);
pcoiExisting != NULL;
pcoiExisting = pcoiExisting->next) {
if (pcoiExisting->state & ST_CONNECTED &&
((PCL_CONV_INFO)pcoiExisting)->hwndReconnect == hwndTarget &&
pcoiExisting->laTopic == pie->laTopic &&
pcoiExisting->laService == pie->laServiceRequested) {
LeaveDDECrit;
return(TRUE);
}
}
}
}
LeaveDDECrit;
}
CheckDDECritOut;
SendMessage(hwndTarget, WM_DDE_INITIATE, (WPARAM)pie->hwndClient,
pie->lParam);
EnterDDECrit;
//
// During the initiate process, any acks received cause more pci's
// to become linked together under the same hwndClient. Once
// the SendMessage() returns, we set the parts of the new pci's
// that hold initiate context information.
//
pci = (PCL_CONV_INFO)GetWindowLongPtr(pie->hwndClient, GWLP_PCI);
if (pci == NULL) {
LeaveDDECrit;
return (TRUE);
}
while (pci != NULL) {
if (pci->hwndReconnect == 0) { // this one needs updating
pci->hwndReconnect = hwndTarget;
if (pie->laServiceRequested) {
pci->ci.laServiceRequested = pie->laServiceRequested;
IncLocalAtomCount(pie->laServiceRequested); // pci copy
}
}
if (pie->clst == CLST_SINGLE_INITIALIZING) {
break;
}
pci = (PCL_CONV_INFO)pci->ci.next;
}
LeaveDDECrit;
return (pie->clst == CLST_MULT_INITIALIZING);
}
/***************************************************************************\
* SetCommonStateFlags()
*
* Description:
* Common client/server worker function
*
* History:
* 05-12-91 sanfords Created.
\***************************************************************************/
VOID SetCommonStateFlags(
HWND hwndUs,
HWND hwndThem,
PWORD pwFlags)
{
DWORD pidUs, pidThem;
GetWindowThreadProcessId(hwndUs, &pidUs);
GetWindowThreadProcessId(hwndThem, &pidThem);
if (pidUs == pidThem) {
*pwFlags |= ST_INTRA_PROCESS;
}
if (IsWindowUnicode(hwndUs) && IsWindowUnicode(hwndThem)) {
*pwFlags |= ST_UNICODE_EXECUTE;
}
}
/***************************************************************************\
* DdeQueryNextServer (DDEML API)
*
* Description:
* Enumerates conversations within a list.
*
* History:
* 11-12-91 sanfords Created.
\***************************************************************************/
FUNCLOG2(LOG_GENERAL, HCONV, DUMMYCALLINGTYPE, DdeQueryNextServer, HCONVLIST, hConvList, HCONV, hConvPrev)
HCONV DdeQueryNextServer(
HCONVLIST hConvList,
HCONV hConvPrev)
{
HCONV hConvRet = 0;
PCONVLIST pcl;
HWND *phwnd;
int i;
PCL_CONV_INFO pci;
PCL_INSTANCE_INFO pcii;
EnterDDECrit;
pcl = (PCONVLIST)ValidateCHandle((HANDLE)hConvList,
HTYPE_CONVERSATION_LIST, HINST_ANY);
if (pcl == NULL) {
BestSetLastDDEMLError(DMLERR_INVALIDPARAMETER);
goto Exit;
}
if (!pcl->chwnd) { // empty list
goto Exit;
}
pcii = PciiFromHandle((HANDLE)hConvList);
if (pcii == NULL) {
BestSetLastDDEMLError(DMLERR_INVALIDPARAMETER);
goto Exit;
}
pcii->LastError = DMLERR_NO_ERROR;
do {
hConvRet = 0;
if (hConvPrev == 0) {
pci = (PCL_CONV_INFO)GetWindowLongPtr(pcl->ahwnd[0], GWLP_PCI);
if (pci == NULL) {
goto Exit; // Must have all conversations zombied.
}
hConvPrev = hConvRet = pci->ci.hConv;
continue;
}
pci = (PCL_CONV_INFO)ValidateCHandle((HANDLE)hConvPrev,
HTYPE_CLIENT_CONVERSATION, InstFromHandle(hConvList));
if (pci == NULL) {
pci = (PCL_CONV_INFO)ValidateCHandle((HANDLE)hConvPrev,
HTYPE_ZOMBIE_CONVERSATION, InstFromHandle(hConvList));
if (pci == NULL) {
SetLastDDEMLError(pcii, DMLERR_INVALIDPARAMETER);
break;
} else {
goto ZombieSkip;
}
}
if (pci->hConvList != hConvList) {
SetLastDDEMLError(pcii, DMLERR_INVALIDPARAMETER);
break;
}
ZombieSkip:
if (pci->ci.next == NULL) {
/*
* end of list for this window, go to next window
*/
for (phwnd = pcl->ahwnd, i = 0; (i + 1) < pcl->chwnd; i++) {
if (phwnd[i] == pci->ci.hwndConv) {
pci = (PCL_CONV_INFO)GetWindowLongPtr(phwnd[i + 1], GWLP_PCI);
if (pci == NULL) {
break;
}
hConvPrev = hConvRet = pci->ci.hConv;
break;
}
}
} else {
hConvPrev = hConvRet = pci->ci.next->hConv; // next conv for this window.
}
} while (hConvRet && TypeFromHandle(hConvRet) == HTYPE_ZOMBIE_CONVERSATION);
Exit:
LeaveDDECrit;
return (hConvRet);
}
/***************************************************************************\
* DdeDisconnect (DDEML API)
*
* Description:
* Terminates a conversation.
*
* History:
* 11-12-91 sanfords Created.
\***************************************************************************/
FUNCLOG1(LOG_GENERAL, BOOL, DUMMYCALLINGTYPE, DdeDisconnect, HCONV, hConv)
BOOL DdeDisconnect(
HCONV hConv)
{
BOOL fRet = FALSE;
PCONV_INFO pcoi;
PCL_INSTANCE_INFO pcii;
CheckDDECritOut;
EnterDDECrit;
pcoi = (PCONV_INFO)ValidateCHandle((HANDLE)hConv,
HTYPE_CLIENT_CONVERSATION, HINST_ANY);
if (pcoi == NULL) {
pcoi = (PCONV_INFO)ValidateCHandle((HANDLE)hConv,
HTYPE_SERVER_CONVERSATION, HINST_ANY);
}
if (pcoi == NULL) {
BestSetLastDDEMLError(DMLERR_INVALIDPARAMETER);
goto Exit;
}
pcii = PciiFromHandle((HANDLE)hConv);
if (pcii == NULL) {
BestSetLastDDEMLError(DMLERR_INVALIDPARAMETER);
goto Exit;
}
if (pcoi->state & ST_CONNECTED) {
ShutdownConversation(pcoi, FALSE);
}
fRet = TRUE;
Exit:
LeaveDDECrit;
return (fRet);
}
/***************************************************************************\
* DdeDisconnectList (DDEML API)
*
* Description:
* Terminates all conversations in a conversation list and frees the list.
*
* History:
* 11-12-91 sanfords Created.
\***************************************************************************/
FUNCLOG1(LOG_GENERAL, BOOL, DUMMYCALLINGTYPE, DdeDisconnectList, HCONVLIST, hConvList)
BOOL DdeDisconnectList(
HCONVLIST hConvList)
{
BOOL fRet = FALSE;
PCL_INSTANCE_INFO pcii;
PCONVLIST pcl;
PCONV_INFO pcoi, pcoiNext;
int i;
CheckDDECritOut;
EnterDDECrit;
pcl = (PCONVLIST)ValidateCHandle((HANDLE)hConvList,
HTYPE_CONVERSATION_LIST, HINST_ANY);
if (pcl == NULL) {
BestSetLastDDEMLError(DMLERR_INVALIDPARAMETER);
goto Exit;
}
ValidateConvList(hConvList);
pcii = PciiFromHandle((HANDLE)hConvList);
if (pcii == NULL) {
BestSetLastDDEMLError(DMLERR_INVALIDPARAMETER);
goto Exit;
}
for(i = pcl->chwnd - 1; i >= 0; i--) {
pcoi = (PCONV_INFO)GetWindowLongPtr(pcl->ahwnd[i], GWLP_PCI);
while (pcoi != NULL && pcoi->state & ST_CONNECTED) {
pcoiNext = pcoi->next;
ShutdownConversation(pcoi, FALSE); // may unlink pcoi!
pcoi = pcoiNext;
}
}
DestroyHandle((HANDLE)hConvList);
DDEMLFree(pcl);
fRet = TRUE;
Exit:
LeaveDDECrit;
return (fRet);
}
/***************************************************************************\
* ShutdownConversation
*
* Description:
* This function causes an imediate termination of the given conversation
* and generates apropriate callbacks to notify the application.
*
* History:
* 11-12-91 sanfords Created.
\***************************************************************************/
VOID ShutdownConversation(
PCONV_INFO pcoi,
BOOL fMakeCallback)
{
CheckDDECritIn;
if (pcoi->state & ST_CONNECTED) {
pcoi->state &= ~ST_CONNECTED;
if (IsWindow(pcoi->hwndPartner)) {
PostMessage(pcoi->hwndPartner, WM_DDE_TERMINATE,
(WPARAM)pcoi->hwndConv, 0);
}
if (fMakeCallback && !(pcoi->pcii->afCmd & CBF_SKIP_DISCONNECTS)) {
DoCallback(pcoi->pcii, (WORD)XTYP_DISCONNECT, 0, pcoi->hConv,
0, 0, 0, 0, (pcoi->state & ST_ISSELF) ? 1L : 0L);
}
MONCONV(pcoi, FALSE);
}
FreeConversationResources(pcoi);
}
/***************************************************************************\
* UnlinkConvFromOthers
*
* Description:
*
* Helper function to handle ugly cross dependency removal. If we are
* unlinking a conversation that is going zombie, fGoingZombie is TRUE;
*
* Conversations that are going zombie are in phase 1 of a 2 phase unlink.
* Phase 1 unlinks do not remove the pcoi from its hwnd's list.
* All unlinks should result in:
* pcoi->hConvList = 0;
* hConvList/aServerLookup no longer refrences pcoi->hwndConv unless
* one of the pcoi's related to hwndConv is still active.
*
*
* History:
* 3-2-92 sanfords Created.
\***************************************************************************/
VOID UnlinkConvFromOthers(
PCONV_INFO pcoi,
BOOL gGoingZombie)
{
PCONV_INFO pcoiPrev, pcoiFirst, pcoiNow;
PCONVLIST pcl;
int i, cActiveInList = 0;
#ifdef TESTING
DWORD path = 0;
#define ORPATH(x) path |= x;
#else
#define ORPATH(x)
#endif // TESTING
CheckDDECritIn;
/*
* Scan pcoi linked list to get key pointers.
*/
pcoiPrev = NULL;
pcoiFirst = pcoiNow = (PCONV_INFO)GetWindowLongPtr(pcoi->hwndConv, GWLP_PCI);
#ifdef TESTING
/*
* verify that pcoi is in the conv list for this window.
*/
while (pcoiNow != NULL) {
if (pcoiNow == pcoi) {
goto FoundIt;
}
pcoiNow = pcoiNow->next;
}
DebugBreak();
FoundIt:
pcoiNow = pcoiFirst;
#endif // TESTING
UserAssert(pcoiFirst);
while (pcoiNow != NULL) {
if (TypeFromHandle(pcoiNow->hConv) != HTYPE_ZOMBIE_CONVERSATION) {
ORPATH(1);
cActiveInList++;
}
if (pcoiNow->next == pcoi) {
pcoiPrev = pcoiNow;
}
pcoiNow = pcoiNow->next;
}
ValidateAllConvLists();
/*
* Unlink conversation unless its going Zombie.
*/
if (!gGoingZombie) {
ORPATH(2);
if (TypeFromHandle(pcoi->hConv) != HTYPE_ZOMBIE_CONVERSATION) {
ORPATH(4);
cActiveInList--;
}
if (pcoiPrev == NULL) {
ORPATH(8);
pcoiFirst = pcoi->next;
SetWindowLongPtr(pcoi->hwndConv, GWLP_PCI, (LONG_PTR)pcoiFirst);
} else {
pcoiPrev->next = pcoi->next;
}
}
UserAssert(pcoiFirst != NULL || !cActiveInList);
if (cActiveInList == 0) {
ORPATH(0x10);
if (pcoi->state & ST_CLIENT) {
ORPATH(0x20);
if (((PCL_CONV_INFO)pcoi)->hConvList) {
/*
* Remove pcoi's hwnd from its hConvList.
*/
pcl = (PCONVLIST)GetHandleData((HANDLE)((PCL_CONV_INFO)pcoi)->hConvList);
for (i = 0; i < pcl->chwnd; i++) {
if (pcl->ahwnd[i] == pcoi->hwndConv) {
ORPATH(0x40);
pcl->chwnd--;
UserAssert(pcl->ahwnd[pcl->chwnd]);
pcl->ahwnd[i] = pcl->ahwnd[pcl->chwnd];
ValidateConvList(((PCL_CONV_INFO)pcoi)->hConvList);
break;
}
}
ORPATH(0x80);
}
} else { // SERVER
/*
* remove server window from the service/topic lookup table.
*/
ORPATH(0x100);
for (i = 0; i < pcoi->pcii->cServerLookupAlloc; i++) {
if (pcoi->pcii->aServerLookup[i].hwndServer == pcoi->hwndConv) {
ORPATH(0x200);
/*
* Check for appcompat hack
*/
if (GetAppCompatFlags2(VERMAX) & GACF2_DDE) {
DeleteAtom(pcoi->pcii->aServerLookup[i].laService); // delete laService
DeleteAtom(pcoi->pcii->aServerLookup[i].laTopic); // delete laTopic
}
if (--(pcoi->pcii->cServerLookupAlloc)) {
ORPATH(0x400);
pcoi->pcii->aServerLookup[i] =
pcoi->pcii->aServerLookup[pcoi->pcii->cServerLookupAlloc];
} else {
DDEMLFree(pcoi->pcii->aServerLookup);
pcoi->pcii->aServerLookup = NULL;
}
break;
}
}
}
}
#ifdef TESTING
else {
/*
* make sure at this point we have at least one non-zombie
*/
pcoiNow = pcoiFirst;
while (pcoiNow != NULL) {
if (TypeFromHandle(pcoiNow->hConv) != HTYPE_ZOMBIE_CONVERSATION) {
goto Out;
}
pcoiNow = pcoiNow->next;
}
DebugBreak();
Out:
;
}
#endif // TESTING
ValidateAllConvLists();
ORPATH(0x800);
/*
* In any case remove hConvList references from client conversation.
*/
if (pcoi->state & ST_CLIENT) {
#ifdef TESTING
/*
* Verify that the hConvList that is being removed, doesn't reference
* this window.
*/
if (((PCL_CONV_INFO)pcoi)->hConvList && !cActiveInList) {
BOOL fFound = FALSE;
pcl = (PCONVLIST)GetHandleData((HANDLE)((PCL_CONV_INFO)pcoi)->hConvList);
for (i = 0; i < pcl->chwnd; i++) {
if (pcl->ahwnd[i] == pcoi->hwndConv) {
fFound = TRUE;
break;
}
}
UserAssert(!fFound);
}
#endif // TESTING
((PCL_CONV_INFO)pcoi)->hConvList = 0;
pcoi->state &= ~ST_INLIST;
}
/*
* last one out turns out the lights.
*/
if (pcoiFirst == NULL) {
/*
* If the pcoi list is empty, this window can go away.
*/
LeaveDDECrit;
NtUserDestroyWindow(pcoi->hwndConv);
EnterDDECrit;
}
}
/***************************************************************************\
* FreeConversationResources
*
* Description:
* Used when: Client window is disconnected by app, Server window is
* disconnected by either side, or when a conversation is disconnected
* at Uninitialize time.
*
* This function releases all resources held by the pcoi and unlinks it
* from its host window pcoi chian. pcoi is freed once this return s.
*
* History:
* 12-21-91 sanfords Created.
\***************************************************************************/
VOID FreeConversationResources(
PCONV_INFO pcoi)
{
PADVISE_LINK paLink;
PDDE_MESSAGE_QUEUE pdmq;
PXACT_INFO pxi;
CheckDDECritIn;
/*
* Don't free resources on locked conversations.
*/
if (pcoi->cLocks > 0) {
pcoi->state |= ST_FREE_CONV_RES_NOW;
return;
}
/*
* Don't free resources if a synchronous transaction is in effect!
*/
pxi = pcoi->pxiOut;
while (pxi != NULL) {
if (pxi->flags & XIF_SYNCHRONOUS) {
/*
* This conversation is in a synchronous transaction.
* Shutdown the modal loop FIRST, then call this when
* the loop exits.
*/
PostMessage(pcoi->hwndConv, WM_TIMER, TID_TIMEOUT, 0);
pcoi->state |= ST_FREE_CONV_RES_NOW;
return;
}
pxi = pxi->next;
}
/*
* If this is an Intra-Process conversation that hasn't yet received
* a terminate message, make it a zombie. We will call this routine
* again once the terminate arrives or when WaitForZombieTerminate() has
* timed out waiting.
*/
if (pcoi->state & ST_INTRA_PROCESS && !(pcoi->state & ST_TERMINATE_RECEIVED)) {
DestroyHandle((HANDLE)pcoi->hConv);
pcoi->hConv = (HCONV)CreateHandle((ULONG_PTR)pcoi, HTYPE_ZOMBIE_CONVERSATION,
InstFromHandle(pcoi->hConv));
UnlinkConvFromOthers(pcoi, TRUE);
return;
}
/*
* remove any transactions left in progress
*/
while (pcoi->pxiOut != NULL) {
(pcoi->pxiOut->pfnResponse)(pcoi->pxiOut, 0, 0);
}
/*
* Throw away any incoming queued DDE messages.
*/
while (pcoi->dmqOut != NULL) {
pdmq = pcoi->dmqOut;
DumpDDEMessage(!(pcoi->state & ST_INTRA_PROCESS), pdmq->msg, pdmq->lParam);
pcoi->dmqOut = pcoi->dmqOut->next;
if (pcoi->dmqOut == NULL) {
pcoi->dmqIn = NULL;
}
DDEMLFree(pdmq);
}
//
// Remove all link info
//
paLink = pcoi->aLinks;
while (pcoi->cLinks) {
if (pcoi->state & ST_CLIENT) {
MONLINK(pcoi->pcii, FALSE, paLink->wType & XTYPF_NODATA,
pcoi->laService, pcoi->laTopic,
LocalToGlobalAtom(paLink->laItem),
paLink->wFmt, FALSE,
(HCONV)pcoi->hwndPartner, (HCONV)pcoi->hwndConv);
} else {
MONLINK(pcoi->pcii, FALSE, paLink->wType & XTYPF_NODATA,
pcoi->laService, pcoi->laTopic,
LocalToGlobalAtom(paLink->laItem),
paLink->wFmt, TRUE,
(HCONV)pcoi->hwndConv, (HCONV)pcoi->hwndPartner);
}
if (!(pcoi->state & ST_CLIENT)) {
DeleteLinkCount(pcoi->pcii, paLink->pLinkCount);
}
DeleteAtom(paLink->laItem); // link structure copy
paLink++;
pcoi->cLinks--;
}
if (pcoi->aLinks) {
DDEMLFree(pcoi->aLinks);
}
//
// free atoms associated with this conv
//
DeleteAtom(pcoi->laService);
DeleteAtom(pcoi->laTopic);
if (pcoi->laServiceRequested) {
DeleteAtom(pcoi->laServiceRequested);
}
UnlinkConvFromOthers(pcoi, FALSE);
/*
* invalidate app's conversation handle
*/
DestroyHandle((HANDLE)pcoi->hConv);
DDEMLFree(pcoi);
}
BOOL WaitForZombieTerminate(
HANDLE hData)
{
PCONV_INFO pcoi;
MSG msg;
HWND hwnd;
BOOL fTerminated;
DWORD fRet = 0;
CheckDDECritOut;
EnterDDECrit;
fTerminated = FALSE;
while ((pcoi = (PCONV_INFO)ValidateCHandle(hData,
HTYPE_ZOMBIE_CONVERSATION, InstFromHandle(hData))) != NULL &&
!(pcoi->state & ST_TERMINATE_RECEIVED)) {
hwnd = pcoi->hwndConv;
LeaveDDECrit;
while (PeekMessage(&msg, hwnd, WM_DDE_FIRST, WM_DDE_LAST, PM_REMOVE)) {
DispatchMessage(&msg);
if (msg.message == WM_DDE_TERMINATE) {
fTerminated = TRUE;
}
}
if (!fTerminated) {
fRet = MsgWaitForMultipleObjectsEx(0, NULL, 100, QS_POSTMESSAGE, 0);
if (fRet == 0xFFFFFFFF) {
RIPMSG0(RIP_WARNING, "WaitForZombieTerminate: I give up - faking terminate.");
ProcessTerminateMsg(pcoi, pcoi->hwndPartner);
EnterDDECrit;
return(FALSE);
}
}
EnterDDECrit;
}
LeaveDDECrit;
return(TRUE);
}