/***************************************************************************\ * AddInstance * * Description: * Adds a server side instance handle to the instance handle array. * The array index becomes the client-side unique instance index used for * identifying other client side handles. * * Returns: * client side instance handle or 0 on error. * * History: * 11-1-91 sanfords Created. \***************************************************************************/ DWORD AddInstance( HANDLE hInstServer) { int i, iNextFree; PHANDLE ph; if (iFirstFreeInst >= cInstAllocated) { if (cInstAllocated == 0) { aInstance = (PHANDLE)DDEMLAlloc(sizeof(HANDLE) * INST_GROW_COUNT); } else { aInstance = (PHANDLE)DDEMLReAlloc((PVOID)aInstance, sizeof(HANDLE) * (cInstAllocated + INST_GROW_COUNT)); } if (aInstance == 0) { return (0); } ph = &aInstance[cInstAllocated]; i = cInstAllocated + 1; while (i <= cInstAllocated + INST_GROW_COUNT) { *ph++ = (HANDLE)i++; } cInstAllocated += INST_GROW_COUNT; } iNextFree = (int)aInstance[iFirstFreeInst]; if (iNextFree > MAX_INST) { /* * Instance limit for this process exceeded! */ return(0); } aInstance[iFirstFreeInst] = hInstServer; i = iFirstFreeInst; iFirstFreeInst = iNextFree; return ((DWORD)CreateHandle(0, HTYPE_INSTANCE, i)); }
/***************************************************************************\ * SetLastDDEMLError * * Description: * Sets last error value and generates monitor events if monitoring. * * History: * 11-19-91 sanfords Created. \***************************************************************************/ VOID SetLastDDEMLError( PCL_INSTANCE_INFO pcii, DWORD error) { PEVENT_PACKET pep; if (pcii->MonitorFlags & MF_ERRORS && !(pcii->afCmd & APPCLASS_MONITOR)) { pep = (PEVENT_PACKET)DDEMLAlloc(sizeof(EVENT_PACKET) - sizeof(DWORD) + sizeof(MONERRSTRUCT)); if (pep != NULL) { pep->EventType = MF_ERRORS; pep->fSense = TRUE; pep->cbEventData = sizeof(MONERRSTRUCT); #define perrs ((MONERRSTRUCT *)&pep->Data) perrs->cb = sizeof(MONERRSTRUCT); perrs->wLastError = (WORD)error; perrs->dwTime = NtGetTickCount(); perrs->hTask = (HANDLE)pcii->tid; #undef perrs LeaveDDECrit; Event(pep); EnterDDECrit; } } #ifdef DEBUG if (error != 0 && error != DMLERR_NO_CONV_ESTABLISHED) { RIPMSG3(RIP_WARNING, "DDEML Error set=%x, Client Instance=%x, Process=%x.", error, pcii, GetCurrentProcessId()); } #endif pcii->LastError = error; }
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); }
/***************************************************************************\ * 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); }