/***************************************************************************\ * DestroyInstance * * Description: * Removes an instance from the aInstance table. This does nothing for * the server side instance info. * * History: * 11-19-91 sanfords Created. \***************************************************************************/ HANDLE DestroyInstance( int hInstClient) { register HANDLE hInstServerRet = 0; DestroyHandle((HANDLE)hInstClient); hInstServerRet = aInstance[InstFromHandle(hInstClient)]; aInstance[InstFromHandle(hInstClient)] = (HANDLE)iFirstFreeInst; iFirstFreeInst = InstFromHandle(hInstClient); return (hInstServerRet); }
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); }
/***************************************************************************\ * 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); }
/***************************************************************************\ * DdeEnableCallback (DDEML API) * * Description: * Turns on and off asynchronous callbacks (BLOCKABLE). * * History: * 11-12-91 sanfords Created. \***************************************************************************/ BOOL DdeEnableCallback( DWORD idInst, HCONV hConv, UINT wCmd) { BOOL fRet = FALSE; PCL_INSTANCE_INFO pcii; PCONV_INFO pcoi; ENABLE_ENUM_STRUCT ees; EnterDDECrit; pcii = (PCL_INSTANCE_INFO)ValidateInstance((HANDLE)idInst); if (pcii == NULL) { BestSetLastDDEMLError(DMLERR_INVALIDPARAMETER); goto Exit; } switch (wCmd) { case EC_QUERYWAITING: case EC_DISABLE: case EC_ENABLEONE: case EC_ENABLEALL: break; default: SetLastDDEMLError(pcii, DMLERR_INVALIDPARAMETER); goto Exit; } if (hConv) { pcoi = (PCONV_INFO)ValidateCHandle((HANDLE)hConv, HTYPE_CLIENT_CONVERSATION, InstFromHandle(idInst)); if (pcoi == NULL) { pcoi = (PCONV_INFO)ValidateCHandle((HANDLE)hConv, HTYPE_SERVER_CONVERSATION, InstFromHandle(idInst)); } if (pcoi == NULL) { SetLastDDEMLError(pcii, DMLERR_INVALIDPARAMETER); goto Exit; } pcoi->cLocks++; fRet = SetEnableState(pcoi, wCmd); switch (wCmd) { case EC_ENABLEALL: case EC_ENABLEONE: CheckForQueuedMessages(pcoi); } pcoi->cLocks--; if (pcoi->cLocks == 0 && pcoi->state & ST_FREE_CONV_RES_NOW) { FreeConversationResources(pcoi); } } else { if (wCmd == EC_ENABLEONE) { wCmd = EC_ENABLEONEOFALL; } switch (wCmd) { case EC_ENABLEONEOFALL: pcii->ConvStartupState = ST_BLOCKNEXT | ST_BLOCKALLNEXT; break; case EC_DISABLE: pcii->ConvStartupState = ST_BLOCKED; break; case EC_ENABLEALL: pcii->ConvStartupState = 0; break; } ees.pfRet = &fRet; ees.wCmd = wCmd; switch (wCmd) { case EC_ENABLEALL: ees.wCmd2 = EC_CHECKQUEUE; break; case EC_ENABLEONEOFALL: ees.wCmd2 = EC_CHECKQUEUEONCE; break; default: ees.wCmd2 = 0; } EnumChildWindows(pcii->hwndMother, (WNDENUMPROC)EnableEnumProc, (LONG)&ees); } Exit: LeaveDDECrit; return (fRet); }
/***************************************************************************\ * DdeQueryNextServer (DDEML API) * * Description: * Enumerates conversations within a list. * * History: * 11-12-91 sanfords Created. \***************************************************************************/ 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)GetWindowLong(pcl->ahwnd[0], GWL_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)GetWindowLong(phwnd[i + 1], GWL_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); }
/***************************************************************************\ * 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(EXCEPTION_EXECUTE_HANDLER) { 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); }
/***************************************************************************\ * 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); }