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); }
/***************************************************************************\ * 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); }
/***************************************************************************\ * 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); }
/***************************************************************************\ * 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; } }