/***************************************************************************\ * DdeFreeStringHandle (DDEML API) * * Description: * Decrement the use count of an HSZ. * * History: * 11-1-91 sanfords Created. \***************************************************************************/ BOOL DdeFreeStringHandle( DWORD idInst, HSZ hsz) { PCL_INSTANCE_INFO pcii; BOOL fRet = FALSE; EnterDDECrit; pcii = ValidateInstance((HANDLE)LongToHandle( idInst )); if (pcii == NULL) { BestSetLastDDEMLError(DMLERR_INVALIDPARAMETER); goto Exit; } if (ValidateHSZ(hsz) == HSZT_INVALID) { SetLastDDEMLError(pcii, DMLERR_INVALIDPARAMETER); goto Exit; } MONHSZ(pcii, hsz, MH_DELETE); fRet = TRUE; if (LATOM_FROM_HSZ(hsz) != 0) { if (DeleteAtom(LATOM_FROM_HSZ(hsz))) { SetLastDDEMLError(pcii, DMLERR_INVALIDPARAMETER); fRet = FALSE; } } Exit: LeaveDDECrit; return (fRet); }
/***************************************************************************\ * DdeKeepStringHandle (DDEML API) * * Description: * Increments the use count of an HSZ. * * History: * 11-1-91 sanfords Created. \***************************************************************************/ BOOL DdeKeepStringHandle( DWORD idInst, HSZ hsz) { PCL_INSTANCE_INFO pcii; BOOL fRet = FALSE; EnterDDECrit; pcii = ValidateInstance((HANDLE)LongToHandle( idInst )); if (pcii == NULL) { BestSetLastDDEMLError(DMLERR_INVALIDPARAMETER); goto Exit; } if (ValidateHSZ(hsz) == HSZT_INVALID) { SetLastDDEMLError(pcii, DMLERR_INVALIDPARAMETER); goto Exit; } if (LATOM_FROM_HSZ(hsz) == 0) { fRet = TRUE; goto Exit; } MONHSZ(pcii, hsz, MH_KEEP); fRet = IncLocalAtomCount(LATOM_FROM_HSZ(hsz)) ? TRUE : FALSE; Exit: LeaveDDECrit; return (fRet); }
/***************************************************************************\ * DdeConnect (DDEML API) * * Description: * Initiates a DDE conversation. * * History: * 11-1-91 sanfords Created. \***************************************************************************/ 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)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); }
/***************************************************************************\ * DdeNameService (DDEML API) * * Description: * Registers, and Unregisters service names and sets the Initiate filter * state for an instance. * * History: * 11-1-91 sanfords Created. \***************************************************************************/ HDDEDATA DdeNameService( DWORD idInst, HSZ hsz1, // service name HSZ hsz2, // reserved for future enhancements UINT afCmd) // DNS_ flags. { BOOL fRet = TRUE; LATOM *plaNameService; PCL_INSTANCE_INFO pcii; EnterDDECrit; pcii = ValidateInstance((HANDLE)LongToHandle( idInst )); if (pcii == NULL) { BestSetLastDDEMLError(DMLERR_INVALIDPARAMETER); fRet = FALSE; goto Exit; } if ((hsz1 && ValidateHSZ(hsz1) == HSZT_INVALID) || hsz2 != 0) { SetLastDDEMLError(pcii, DMLERR_INVALIDPARAMETER); fRet = FALSE; goto Exit; } if (afCmd & DNS_FILTERON && !(pcii->afCmd & APPCMD_FILTERINITS)) { pcii->afCmd |= APPCMD_FILTERINITS; NtUserUpdateInstance(pcii->hInstServer, &pcii->MonitorFlags, pcii->afCmd); } if (afCmd & DNS_FILTEROFF && (pcii->afCmd & APPCMD_FILTERINITS)) { pcii->afCmd &= ~APPCMD_FILTERINITS; NtUserUpdateInstance(pcii->hInstServer, &pcii->MonitorFlags, pcii->afCmd); } if (afCmd & (DNS_REGISTER | DNS_UNREGISTER)) { GATOM ga; if (pcii->afCmd & APPCMD_CLIENTONLY) { SetLastDDEMLError(pcii, DMLERR_DLL_USAGE); fRet = FALSE; goto Exit; } if (hsz1 == 0) { if (afCmd & DNS_REGISTER) { /* * registering NULL is not allowed! */ SetLastDDEMLError(pcii, DMLERR_INVALIDPARAMETER); fRet = FALSE; goto Exit; } /* * unregistering NULL is just like unregistering each * registered name. * * 10/19/90 - made this a synchronous event so that hsz * can be freed by calling app after this call completes * without us having to keep a copy around forever. */ plaNameService = pcii->plaNameService; while (*plaNameService != 0) { ga = LocalToGlobalAtom(*plaNameService); DeleteAtom(*plaNameService); LeaveDDECrit; RegisterService(FALSE, ga, pcii->hwndMother); EnterDDECrit; GlobalDeleteAtom(ga); plaNameService++; } pcii->cNameServiceAlloc = 1; *pcii->plaNameService = 0; goto Exit; } if (afCmd & DNS_REGISTER) { plaNameService = (LATOM *)DDEMLReAlloc(pcii->plaNameService, sizeof(LATOM) * ++pcii->cNameServiceAlloc); if (plaNameService == NULL) { SetLastDDEMLError(pcii, DMLERR_MEMORY_ERROR); pcii->cNameServiceAlloc--; fRet = FALSE; goto Exit; } else { pcii->plaNameService = plaNameService; } IncLocalAtomCount(LATOM_FROM_HSZ(hsz1)); // NameService copy plaNameService[pcii->cNameServiceAlloc - 2] = LATOM_FROM_HSZ(hsz1); plaNameService[pcii->cNameServiceAlloc - 1] = 0; } else { // DNS_UNREGISTER plaNameService = pcii->plaNameService; while (*plaNameService != 0 && *plaNameService != LATOM_FROM_HSZ(hsz1)) { plaNameService++; } if (*plaNameService == 0) { goto Exit; // not found just exit } // // fill empty slot with last entry and fill last entry with 0 // pcii->cNameServiceAlloc--; *plaNameService = pcii->plaNameService[pcii->cNameServiceAlloc - 1]; pcii->plaNameService[pcii->cNameServiceAlloc - 1] = 0; } ga = LocalToGlobalAtom(LATOM_FROM_HSZ(hsz1)); LeaveDDECrit; RegisterService((afCmd & DNS_REGISTER) ? TRUE : FALSE, ga, pcii->hwndMother); EnterDDECrit; GlobalDeleteAtom(ga); } Exit: LeaveDDECrit; return ((HDDEDATA)IntToPtr( fRet )); }
DWORD InternalDdeQueryString( DWORD idInst, HSZ hsz, PVOID psz, DWORD cbMax, INT iCodePage) { PCL_INSTANCE_INFO pcii; DWORD dwRet = 0; WCHAR szw[256]; // BOOL fDefUsed; // LATER EnterDDECrit; pcii = ValidateInstance((HANDLE)LongToHandle( idInst )); if (pcii == NULL) { BestSetLastDDEMLError(DMLERR_INVALIDPARAMETER); goto Exit; } if (ValidateHSZ(hsz) == HSZT_INVALID) { SetLastDDEMLError(pcii, DMLERR_INVALIDPARAMETER); goto Exit; } if (LATOM_FROM_HSZ(hsz) == 0) { if (iCodePage == CP_WINUNICODE) { if (psz != NULL) { *(LPWSTR)psz = L'\0'; } dwRet = sizeof(WCHAR); goto Exit; } else { if (psz != NULL) { *(LPSTR)psz = '\0'; } dwRet = sizeof(CHAR); goto Exit; } } if (psz == NULL) { cbMax = sizeof(szw); psz = (PVOID)szw; } switch (iCodePage) { case CP_WINANSI: dwRet = GetAtomNameA(LATOM_FROM_HSZ(hsz), psz, cbMax); break; default: dwRet = GetAtomNameW(LATOM_FROM_HSZ(hsz), (LPWSTR)psz, cbMax / sizeof(WCHAR)); if (iCodePage != CP_WINUNICODE) { /* * convert psz to the appropriate codepage and count the * characters(ie BYTES for DBCS!) to alter dwRet. */ #ifdef LATER // Does this routine work in place? (i.e. input and output buffer the same). WideCharToMultiByte((UINT)iCodePage, 0, szw, sizeof(szw) / sizeof(WCHAR), (LPSTR)psz, cbMax, NULL, &fDefUsed); #endif dwRet = cbMax + 1; } break; } Exit: LeaveDDECrit; return (dwRet); }
/***************************************************************************\ * 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); }
/***************************************************************************\ * 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 GWL_PCI * client window word. * * After each WM_DDE_INITIALIZE is sent, the GWL_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); SetWindowLong(ie.hwndClient, GWL_SHINST, (LONG)pcii->hInstServer); SetWindowLong(ie.hwndClient, GWL_CHINST, (LONG)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, (DWORD)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, (LONG)&ie); } EnterDDECrit; /* * hConvList may have been destroyed during the enumeration but we are * done with it now so no need to revalidate. */ #ifdef DEBUG { 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 // DEBUG GlobalDeleteAtom(gaService); GlobalDeleteAtom(gaTopic); // // Get the first pci allocated when a WM_DDE_ACK was recieved. // pci = (PCL_CONV_INFO)GetWindowLong(ie.hwndClient, GWL_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); }
/***************************************************************************\ * 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); }
/***************************************************************************\ * 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); }