/***************************************************************************\ * DdeDisconnectList (DDEML API) * * Description: * Terminates all conversations in a conversation list and frees the list. * * History: * 11-12-91 sanfords Created. \***************************************************************************/ 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)GetWindowLong(pcl->ahwnd[i], GWL_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); }
UINT InternalDdeInitialize( LPDWORD pidInst, PFNCALLBACK pfnCallback, DWORD afCmd, BOOL fUnicode) { UINT uiRet = DMLERR_MEMORY_ERROR; register PCL_INSTANCE_INFO pcii; if (afCmd & APPCLASS_MONITOR) { afCmd |= CBF_MONMASK; } if (afCmd & APPCMD_CLIENTONLY) { afCmd |= CBF_FAIL_CONNECTIONS; } EnterDDECrit; if (*pidInst != 0) { pcii = ValidateInstance((HANDLE)LongToHandle( *pidInst )); if (pcii == NULL) { uiRet = DMLERR_INVALIDPARAMETER; goto Exit; } // only allow certain bits to be changed on reinitialize call pcii->afCmd = (pcii->afCmd & ~(CBF_MASK | MF_MASK)) | (afCmd & (CBF_MASK | MF_MASK)); LeaveDDECrit; NtUserUpdateInstance(pcii->hInstServer, &pcii->MonitorFlags, afCmd); return (DMLERR_NO_ERROR); } pcii = (PCL_INSTANCE_INFO)DDEMLAlloc(sizeof(CL_INSTANCE_INFO)); if (pcii == NULL) { uiRet = DMLERR_MEMORY_ERROR; goto Exit; } pcii->plaNameService = (LATOM *)DDEMLAlloc(sizeof(LATOM)); if (pcii->plaNameService == NULL) { uiRet = DMLERR_MEMORY_ERROR; goto Backout3; } // *pcii->plaNameService = 0; // zero init takes care of this pcii->cNameServiceAlloc = 1; /* * Flag this window as being create from a diff hmod as the app so * hotkeys don't take it as the first window created in the app and * assign it as the hotkey. */ pcii->hwndMother = _CreateWindowEx(0, (LPTSTR)(gpsi->atomSysClass[ICLS_DDEMLMOTHER]), L"", WS_POPUP, 0, 0, 0, 0, (HWND)0, (HMENU)0, 0, (LPVOID)NULL, CW_FLAGS_DIFFHMOD); if (pcii->hwndMother == 0) { uiRet = DMLERR_SYS_ERROR; goto Backout2; } SetWindowLongPtr(pcii->hwndMother, GWLP_INSTANCE_INFO, (LONG_PTR)pcii); pcii->afCmd = afCmd | APPCMD_FILTERINITS; pcii->pfnCallback = pfnCallback; // pcii->LastError = DMLERR_NO_ERROR; // zero init pcii->tid = GetCurrentThreadId(); // pcii->aServerLookup = NULL; // zero init // pcii->cServerLookupAlloc = 0; // zero init // pcii->ConvStartupState = 0; // zero init - Not blocked. // pcii->flags = 0; // zero init // pcii->cInDDEMLCallback = 0; // zero init // pcii->pLinkCounts = NULL; // zero init // Do this last when the client side is ready for whatever events // flying around may come charging in. LeaveDDECrit; uiRet = NtUserDdeInitialize(&pcii->hInstServer, &pcii->hwndEvent, &pcii->MonitorFlags, pcii->afCmd, pcii); EnterDDECrit; if (uiRet != DMLERR_NO_ERROR) { Backout: NtUserDestroyWindow(pcii->hwndMother); Backout2: DDEMLFree(pcii->plaNameService); Backout3: DDEMLFree(pcii); goto Exit; } pcii->hInstClient = AddInstance(pcii->hInstServer); *pidInst = HandleToUlong(pcii->hInstClient); if (pcii->hInstClient == 0) { LeaveDDECrit; NtUserCallOneParam((ULONG_PTR)pcii->hInstServer, SFI__CSDDEUNINITIALIZE); EnterDDECrit; uiRet = DMLERR_MEMORY_ERROR; goto Backout; } SetHandleData(pcii->hInstClient, (ULONG_PTR)pcii); pcii->next = pciiList; pciiList = pcii; if (fUnicode) { pcii->flags |= IIF_UNICODE; } uiRet = DMLERR_NO_ERROR; Exit: LeaveDDECrit; return (uiRet); }
/***************************************************************************\ * DdeUninitialize (DDEML API) * * Description: * Shuts down a DDEML instance and frees all associated resources. * * History: * 11-12-91 sanfords Created. \***************************************************************************/ BOOL DdeUninitialize( DWORD idInst) { PCL_INSTANCE_INFO pcii, pciiPrev; BOOL fRet = FALSE; CheckDDECritOut; EnterDDECrit; pcii = ValidateInstance((HANDLE)LongToHandle( idInst )); if (pcii == NULL) { BestSetLastDDEMLError(DMLERR_INVALIDPARAMETER); goto Exit; } /* * If this thread is in the middle of a synchronous transaction or * a callback, we need to back out of those first. */ if ((pcii->flags & IIF_IN_SYNC_XACT) || pcii->cInDDEMLCallback) { pcii->afCmd |= APPCMD_UNINIT_ASAP; fRet = TRUE; goto Exit; } ApplyFunctionToObjects(HTYPE_CONVERSATION_LIST, InstFromHandle(pcii->hInstClient), (PFNHANDLEAPPLY)DdeDisconnectList); ApplyFunctionToObjects(HTYPE_CLIENT_CONVERSATION, InstFromHandle(pcii->hInstClient), (PFNHANDLEAPPLY)DdeDisconnect); ApplyFunctionToObjects(HTYPE_SERVER_CONVERSATION, InstFromHandle(pcii->hInstClient), (PFNHANDLEAPPLY)DdeDisconnect); ApplyFunctionToObjects(HTYPE_ZOMBIE_CONVERSATION, InstFromHandle(pcii->hInstClient), (PFNHANDLEAPPLY)WaitForZombieTerminate); ApplyFunctionToObjects(HTYPE_DATA_HANDLE, InstFromHandle(pcii->hInstClient), (PFNHANDLEAPPLY)ApplyFreeDataHandle); LeaveDDECrit; NtUserCallOneParam((ULONG_PTR)pcii->hInstServer, SFI__CSDDEUNINITIALIZE); NtUserDestroyWindow(pcii->hwndMother); EnterDDECrit; DDEMLFree(pcii->plaNameService); DestroyInstance(pcii->hInstClient); // unlink pcii from pciiList if (pciiList == pcii) { pciiList = pciiList->next; } else { for (pciiPrev = pciiList; pciiPrev != NULL && pciiPrev->next != pcii; pciiPrev = pciiPrev->next) { ; } if (pciiPrev != NULL) { pciiPrev->next = pcii->next; } } DDEMLFree(pcii); fRet = TRUE; Exit: LeaveDDECrit; return (fRet); }
/***************************************************************************\ * DoCallback * * Description: * Performs a synchronous callback to the given instance's callback proc. * * History: * 11-12-91 sanfords Created. \***************************************************************************/ HDDEDATA DoCallback( PCL_INSTANCE_INFO pcii, WORD wType, WORD wFmt, HCONV hConv, HSZ hsz1, HSZ hsz2, HDDEDATA hData, DWORD dw1, DWORD dw2) { HDDEDATA hDataRet; PCLIENTINFO pci; CheckDDECritIn; /* * Zombie conversations don't generate callbacks! */ if (hConv && TypeFromHandle(hConv) == HTYPE_ZOMBIE_CONVERSATION) { return(0); } pci = GetClientInfo(); pci->cInDDEMLCallback++; pcii->cInDDEMLCallback++; pci->hDdemlCallbackInst = pcii->hInstClient; LeaveDDECrit; CheckDDECritOut; hDataRet = (*pcii->pfnCallback)((UINT)wType, (UINT)wFmt, hConv, hsz1, hsz2, hData, dw1, dw2); EnterDDECrit; pcii->cInDDEMLCallback--; pci->cInDDEMLCallback--; if (!(pcii->afCmd & APPCLASS_MONITOR) && pcii->MonitorFlags & MF_CALLBACKS) { PEVENT_PACKET pep; pep = (PEVENT_PACKET)DDEMLAlloc(sizeof(EVENT_PACKET) - sizeof(DWORD) + sizeof(MONCBSTRUCT)); if (pep != NULL) { pep->EventType = MF_CALLBACKS; pep->fSense = TRUE; pep->cbEventData = sizeof(MONCBSTRUCT); #define pcbs ((MONCBSTRUCT *)&pep->Data) pcbs->cb = sizeof(MONCBSTRUCT); pcbs->dwTime = NtGetTickCount(); pcbs->hTask = (HANDLE)pcii->tid; pcbs->dwRet = (DWORD)hDataRet; pcbs->wType = wType; pcbs->wFmt = wFmt; pcbs->hConv = hConv; pcbs->hsz1 = (HSZ)LocalToGlobalAtom(LATOM_FROM_HSZ(hsz1)); pcbs->hsz2 = (HSZ)LocalToGlobalAtom(LATOM_FROM_HSZ(hsz2)); pcbs->hData = hData; pcbs->dwData1 = dw1; pcbs->dwData2 = dw2; if (((wType == XTYP_CONNECT) || (wType == XTYP_WILDCONNECT)) && dw1) { RtlCopyMemory(&pcbs->cc, (PVOID)dw1, sizeof(CONVCONTEXT)); } LeaveDDECrit; if (wType & XCLASS_DATA) { if (hDataRet && hDataRet != CBR_BLOCK) { pcbs->cbData = DdeGetData(hDataRet, (LPBYTE)pcbs->Data, 32, 0); } } else if (hData) { pcbs->cbData = DdeGetData(hData, (LPBYTE)pcbs->Data, 32, 0); } Event(pep); EnterDDECrit; GlobalDeleteAtom(LATOM_FROM_HSZ(pcbs->hsz1)); GlobalDeleteAtom(LATOM_FROM_HSZ(pcbs->hsz2)); DDEMLFree(pep); #undef pcbs } } return (hDataRet); }
/***************************************************************************\ * DdeReconnect (DDEML API) * * Description: * Attempts to reconnect an externally (from the server) terminated * client side conversation. * * History: * 11-12-91 sanfords Created. \***************************************************************************/ 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); } } } }
/***************************************************************************\ * 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. \***************************************************************************/ 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; HCONVLIST hConvListOld; int i; CheckDDECritOut; EnterDDECrit; if (!ValidateConnectParameters((HANDLE)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((DWORD)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); pcl = DDEMLReAlloc(pcl, sizeof(CONVLIST) + sizeof(HWND) * pcl->chwnd); if (pcl == NULL) { SetLastDDEMLError(pcii, DMLERR_MEMORY_ERROR); hConvListRet = hConvListOld; DisconnectConv(pcoi); goto Exit; } SetHandleData((HANDLE)hConvList, (DWORD)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) GetWindowLong(pcl->ahwnd[i], GWL_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); }
/***************************************************************************\ * 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((DWORD)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 incomming 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); }
/***************************************************************************\ * 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)GetWindowLong(pcoi->hwndConv, GWL_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; SetWindowLong(pcoi->hwndConv, GWL_PCI, (LONG)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); 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; } }