1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
6 * Copyright 1997 Alexandre Julliard
7 * Copyright 1997 Len White
8 * Copyright 1999 Keith Matthews
10 * Copyright 2001 Eric Pouech
22 #include "debugtools.h"
23 #include "dde/dde_private.h"
25 DEFAULT_DEBUG_CHANNEL(ddeml);
27 static WDML_INSTANCE* WDML_InstanceList = NULL;
28 static DWORD WDML_MaxInstanceID = 0; /* OK for present, have to worry about wrap-around later */
29 const char WDML_szEventClass[] = "DdeEventClass";
30 CRITICAL_SECTION WDML_CritSect = CRITICAL_SECTION_INIT;
32 /* ================================================================
34 * Pure DDE (non DDEML) management
36 * ================================================================ */
38 static BOOL DDE_RequirePacking(UINT msg)
50 case WM_DDE_EXECUTE: /* strange, NT 2000 (at least) really uses packing here... */
52 case WM_DDE_REQUEST: /* assuming clipboard formats are 16 bit */
53 case WM_DDE_TERMINATE:
54 case WM_DDE_UNADVISE: /* assuming clipboard formats are 16 bit */
58 TRACE("Unknown message %04x\n", msg);
65 /*****************************************************************
66 * PackDDElParam (USER32.@)
71 LPARAM WINAPI PackDDElParam(UINT msg, UINT uiLo, UINT uiHi)
76 if (!DDE_RequirePacking(msg))
77 return MAKELONG(uiLo, uiHi);
79 if (!(hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, sizeof(UINT) * 2)))
81 ERR("GlobalAlloc failed\n");
85 params = GlobalLock(hMem);
88 ERR("GlobalLock failed\n");
101 /*****************************************************************
102 * UnpackDDElParam (USER32.@)
108 BOOL WINAPI UnpackDDElParam(UINT msg, LPARAM lParam,
109 PUINT uiLo, PUINT uiHi)
114 if (!DDE_RequirePacking(msg))
116 *uiLo = LOWORD(lParam);
117 *uiHi = HIWORD(lParam);
127 hMem = (HGLOBAL)lParam;
129 params = GlobalLock(hMem);
132 ERR("GlobalLock failed\n");
145 /*****************************************************************
146 * FreeDDElParam (USER32.@)
152 BOOL WINAPI FreeDDElParam(UINT msg, LPARAM lParam)
154 HGLOBAL hMem = (HGLOBAL)lParam;
156 if (!DDE_RequirePacking(msg))
163 return GlobalFree(hMem) == (HGLOBAL)NULL;
167 /*****************************************************************
168 * ReuseDDElParam (USER32.@)
173 LPARAM WINAPI ReuseDDElParam(LPARAM lParam, UINT msgIn, UINT msgOut,
174 UINT uiLo, UINT uiHi)
180 in = DDE_RequirePacking(msgIn);
181 out = DDE_RequirePacking(msgOut);
185 return PackDDElParam(msgOut, uiLo, uiHi);
195 FreeDDElParam(msgIn, lParam);
196 return MAKELONG(uiLo, uiHi);
199 hMem = (HGLOBAL)lParam;
201 params = GlobalLock(hMem);
204 ERR("GlobalLock failed\n");
211 TRACE("Reusing pack %08x %08x\n", uiLo, uiHi);
217 /*****************************************************************
218 * ImpersonateDdeClientWindow (USER32.@)
221 * hWndClient [I] handle to DDE client window
222 * hWndServer [I] handle to DDE server window
224 BOOL WINAPI ImpersonateDdeClientWindow(HWND hWndClient, HWND hWndServer)
226 FIXME("(%04x %04x): stub\n", hWndClient, hWndServer);
230 /*****************************************************************
231 * DdeSetQualityOfService (USER32.@)
234 BOOL WINAPI DdeSetQualityOfService(HWND hwndClient, CONST SECURITY_QUALITY_OF_SERVICE *pqosNew,
235 PSECURITY_QUALITY_OF_SERVICE pqosPrev)
237 FIXME("(%04x %p %p): stub\n", hwndClient, pqosNew, pqosPrev);
241 /* ================================================================
243 * Instance management
245 * ================================================================ */
247 /******************************************************************************
248 * IncrementInstanceId
250 * generic routine to increment the max instance Id and allocate a new application instance
252 static void WDML_IncrementInstanceId(WDML_INSTANCE* pInstance)
254 DWORD id = InterlockedIncrement(&WDML_MaxInstanceID);
256 pInstance->instanceID = id;
257 TRACE("New instance id %ld allocated\n", id);
260 /******************************************************************
265 static LRESULT CALLBACK WDML_EventProc(HWND hwndEvent, UINT uMsg, WPARAM wParam, LPARAM lParam)
267 WDML_INSTANCE* pInstance;
272 case WM_WDML_REGISTER:
273 pInstance = WDML_GetInstanceFromWnd(hwndEvent);
274 /* try calling the Callback */
275 if (pInstance && !(pInstance->CBFflags & CBF_SKIP_REGISTRATIONS))
277 hsz1 = WDML_MakeHszFromAtom(pInstance, wParam);
278 hsz2 = WDML_MakeHszFromAtom(pInstance, lParam);
279 WDML_InvokeCallback(pInstance, XTYP_REGISTER, 0, 0, hsz1, hsz2, 0, 0, 0);
280 WDML_DecHSZ(pInstance, hsz1);
281 WDML_DecHSZ(pInstance, hsz2);
285 case WM_WDML_UNREGISTER:
286 pInstance = WDML_GetInstanceFromWnd(hwndEvent);
287 if (pInstance && !(pInstance->CBFflags & CBF_SKIP_UNREGISTRATIONS))
289 hsz1 = WDML_MakeHszFromAtom(pInstance, wParam);
290 hsz2 = WDML_MakeHszFromAtom(pInstance, lParam);
291 WDML_InvokeCallback(pInstance, XTYP_UNREGISTER, 0, 0, hsz1, hsz2, 0, 0, 0);
292 WDML_DecHSZ(pInstance, hsz1);
293 WDML_DecHSZ(pInstance, hsz2);
297 case WM_WDML_CONNECT_CONFIRM:
298 pInstance = WDML_GetInstanceFromWnd(hwndEvent);
299 if (pInstance && !(pInstance->CBFflags & CBF_SKIP_CONNECT_CONFIRMS))
302 /* confirm connection...
303 * lookup for this conv handle
305 for (pConv = pInstance->convs[WDML_SERVER_SIDE]; pConv != NULL; pConv = pConv->next)
307 if (pConv->hwndClient == (HWND)wParam && pConv->hwndServer == (HWND)lParam)
312 pConv->wStatus |= ST_ISLOCAL;
314 WDML_InvokeCallback(pInstance, XTYP_CONNECT_CONFIRM, 0, (HCONV)pConv,
315 pConv->hszTopic, pConv->hszService, 0, 0,
316 (pConv->wStatus & ST_ISSELF) ? 1 : 0);
321 return DefWindowProcA(hwndEvent, uMsg, wParam, lParam);
326 /******************************************************************
331 UINT WDML_Initialize(LPDWORD pidInst, PFNCALLBACK pfnCallback,
332 DWORD afCmd, DWORD ulRes, BOOL bUnicode, BOOL b16)
334 WDML_INSTANCE* pInstance;
335 WDML_INSTANCE* reference_inst;
337 WNDCLASSEXA wndclass;
339 TRACE("(%p,%p,0x%lx,%ld)\n",
340 pidInst, pfnCallback, afCmd, ulRes);
344 ERR("Reserved value not zero? What does this mean?\n");
345 /* trap this and no more until we know more */
346 return DMLERR_NO_ERROR;
349 /* grab enough heap for one control struct - not really necessary for re-initialise
350 * but allows us to use same validation routines */
351 pInstance = (WDML_INSTANCE*)HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_INSTANCE));
352 if (pInstance == NULL)
354 /* catastrophe !! warn user & abort */
355 ERR("Instance create failed - out of memory\n");
356 return DMLERR_SYS_ERROR;
358 pInstance->next = NULL;
359 pInstance->monitor = (afCmd | APPCLASS_MONITOR);
361 /* messy bit, spec implies that 'Client Only' can be set in 2 different ways, catch 1 here */
363 pInstance->clientOnly = afCmd & APPCMD_CLIENTONLY;
364 pInstance->instanceID = *pidInst; /* May need to add calling proc Id */
365 pInstance->threadID = GetCurrentThreadId();
366 pInstance->callback = *pfnCallback;
367 pInstance->unicode = bUnicode;
368 pInstance->win16 = b16;
369 pInstance->nodeList = NULL; /* node will be added later */
370 pInstance->monitorFlags = afCmd & MF_MASK;
371 pInstance->servers = NULL;
372 pInstance->convs[0] = NULL;
373 pInstance->convs[1] = NULL;
374 pInstance->links[0] = NULL;
375 pInstance->links[1] = NULL;
377 /* isolate CBF flags in one go, expect this will go the way of all attempts to be clever !! */
379 pInstance->CBFflags = afCmd^((afCmd&MF_MASK)|((afCmd&APPCMD_MASK)|(afCmd&APPCLASS_MASK)));
381 if (!pInstance->clientOnly)
384 /* Check for other way of setting Client-only !! */
386 pInstance->clientOnly =
387 (pInstance->CBFflags & CBF_FAIL_ALLSVRXACTIONS) == CBF_FAIL_ALLSVRXACTIONS;
390 TRACE("instance created - checking validity \n");
394 /* Initialisation of new Instance Identifier */
395 TRACE("new instance, callback %p flags %lX\n",pfnCallback,afCmd);
397 EnterCriticalSection(&WDML_CritSect);
399 if (WDML_InstanceList == NULL)
401 /* can't be another instance in this case, assign to the base pointer */
402 WDML_InstanceList = pInstance;
404 /* since first must force filter of XTYP_CONNECT and XTYP_WILDCONNECT for
406 * ------------------------------- NOTE NOTE NOTE --------------------------
408 * the manual is not clear if this condition
409 * applies to the first call to DdeInitialize from an application, or the
410 * first call for a given callback !!!
413 pInstance->CBFflags = pInstance->CBFflags|APPCMD_FILTERINITS;
414 TRACE("First application instance detected OK\n");
415 /* allocate new instance ID */
416 WDML_IncrementInstanceId(pInstance);
420 /* really need to chain the new one in to the latest here, but after checking conditions
421 * such as trying to start a conversation from an application trying to monitor */
422 reference_inst = WDML_InstanceList;
423 TRACE("Subsequent application instance - starting checks\n");
424 while (reference_inst->next != NULL)
427 * This set of tests will work if application uses same instance Id
428 * at application level once allocated - which is what manual implies
429 * should happen. If someone tries to be
430 * clever (lazy ?) it will fail to pick up that later calls are for
431 * the same application - should we trust them ?
433 if (pInstance->instanceID == reference_inst->instanceID)
435 /* Check 1 - must be same Client-only state */
437 if (pInstance->clientOnly != reference_inst->clientOnly)
439 ret = DMLERR_DLL_USAGE;
443 /* Check 2 - cannot use 'Monitor' with any non-monitor modes */
445 if (pInstance->monitor != reference_inst->monitor)
447 ret = DMLERR_INVALIDPARAMETER;
451 /* Check 3 - must supply different callback address */
453 if (pInstance->callback == reference_inst->callback)
455 ret = DMLERR_DLL_USAGE;
459 reference_inst = reference_inst->next;
461 /* All cleared, add to chain */
463 TRACE("Application Instance checks finished\n");
464 WDML_IncrementInstanceId(pInstance);
465 reference_inst->next = pInstance;
467 LeaveCriticalSection(&WDML_CritSect);
469 *pidInst = pInstance->instanceID;
471 /* for deadlock issues, windows must always be created when outside the critical section */
472 wndclass.cbSize = sizeof(wndclass);
474 wndclass.lpfnWndProc = WDML_EventProc;
475 wndclass.cbClsExtra = 0;
476 wndclass.cbWndExtra = sizeof(DWORD);
477 wndclass.hInstance = 0;
479 wndclass.hCursor = 0;
480 wndclass.hbrBackground = 0;
481 wndclass.lpszMenuName = NULL;
482 wndclass.lpszClassName = WDML_szEventClass;
483 wndclass.hIconSm = 0;
485 RegisterClassExA(&wndclass);
487 pInstance->hwndEvent = CreateWindowA(WDML_szEventClass, NULL,
488 WS_POPUP, 0, 0, 0, 0,
491 SetWindowLongA(pInstance->hwndEvent, GWL_WDML_INSTANCE, (DWORD)pInstance);
493 TRACE("New application instance processing finished OK\n");
497 /* Reinitialisation situation --- FIX */
498 TRACE("reinitialisation of (%p,%p,0x%lx,%ld): stub\n", pidInst, pfnCallback, afCmd, ulRes);
500 EnterCriticalSection(&WDML_CritSect);
502 if (WDML_InstanceList == NULL)
504 ret = DMLERR_DLL_USAGE;
507 HeapFree(GetProcessHeap(), 0, pInstance); /* finished - release heap space used as work store */
508 /* can't reinitialise if we have initialised nothing !! */
509 reference_inst = WDML_InstanceList;
510 /* must first check if we have been given a valid instance to re-initialise !! how do we do that ? */
512 * MS allows initialisation without specifying a callback, should we allow addition of the
513 * callback by a later call to initialise ? - if so this lot will have to change
515 while (reference_inst->next != NULL)
517 if (*pidInst == reference_inst->instanceID && pfnCallback == reference_inst->callback)
519 /* Check 1 - cannot change client-only mode if set via APPCMD_CLIENTONLY */
521 if (reference_inst->clientOnly)
523 if ((reference_inst->CBFflags & CBF_FAIL_ALLSVRXACTIONS) != CBF_FAIL_ALLSVRXACTIONS)
525 /* i.e. Was set to Client-only and through APPCMD_CLIENTONLY */
527 if (!(afCmd & APPCMD_CLIENTONLY))
529 ret = DMLERR_DLL_USAGE;
534 /* Check 2 - cannot change monitor modes */
536 if (pInstance->monitor != reference_inst->monitor)
538 ret = DMLERR_DLL_USAGE;
542 /* Check 3 - trying to set Client-only via APPCMD when not set so previously */
544 if ((afCmd&APPCMD_CLIENTONLY) && !reference_inst->clientOnly)
546 ret = DMLERR_DLL_USAGE;
551 reference_inst = reference_inst->next;
553 if (reference_inst->next == NULL)
555 /* Crazy situation - trying to re-initialize something that has not beeen initialized !!
557 * Manual does not say what we do, cannot return DMLERR_NOT_INITIALIZED so what ?
559 ret = DMLERR_INVALIDPARAMETER;
562 /* All checked - change relevant flags */
564 reference_inst->CBFflags = pInstance->CBFflags;
565 reference_inst->clientOnly = pInstance->clientOnly;
566 reference_inst->monitorFlags = pInstance->monitorFlags;
567 LeaveCriticalSection(&WDML_CritSect);
570 return DMLERR_NO_ERROR;
572 HeapFree(GetProcessHeap(), 0, pInstance);
573 LeaveCriticalSection(&WDML_CritSect);
577 /******************************************************************************
578 * DdeInitializeA (USER32.@)
580 UINT WINAPI DdeInitializeA(LPDWORD pidInst, PFNCALLBACK pfnCallback,
581 DWORD afCmd, DWORD ulRes)
583 return WDML_Initialize(pidInst, pfnCallback, afCmd, ulRes, FALSE, FALSE);
586 /******************************************************************************
587 * DdeInitializeW [USER32.@]
588 * Registers an application with the DDEML
591 * pidInst [I] Pointer to instance identifier
592 * pfnCallback [I] Pointer to callback function
593 * afCmd [I] Set of command and filter flags
597 * Success: DMLERR_NO_ERROR
598 * Failure: DMLERR_DLL_USAGE, DMLERR_INVALIDPARAMETER, DMLERR_SYS_ERROR
600 UINT WINAPI DdeInitializeW(LPDWORD pidInst, PFNCALLBACK pfnCallback,
601 DWORD afCmd, DWORD ulRes)
603 return WDML_Initialize(pidInst, pfnCallback, afCmd, ulRes, TRUE, FALSE);
606 /*****************************************************************
607 * DdeUninitialize [USER32.@] Frees DDEML resources
610 * idInst [I] Instance identifier
617 BOOL WINAPI DdeUninitialize(DWORD idInst)
619 /* Stage one - check if we have a handle for this instance
621 WDML_INSTANCE* pInstance;
622 WDML_INSTANCE* reference_inst;
624 WDML_CONV* pConvNext;
626 EnterCriticalSection(&WDML_CritSect);
628 /* First check instance
630 pInstance = WDML_GetInstance(idInst);
631 if (pInstance == NULL)
633 LeaveCriticalSection(&WDML_CritSect);
635 * Needs something here to record NOT_INITIALIZED ready for DdeGetLastError
640 /* first terminate all conversations client side
641 * this shall close existing links...
643 for (pConv = pInstance->convs[WDML_CLIENT_SIDE]; pConv != NULL; pConv = pConvNext)
645 pConvNext = pConv->next;
646 DdeDisconnect((HCONV)pConv);
648 if (pInstance->convs[WDML_CLIENT_SIDE])
649 FIXME("still pending conversations\n");
651 /* then unregister all known service names */
652 DdeNameService(idInst, 0, 0, DNS_UNREGISTER);
654 /* Free the nodes that were not freed by this instance
655 * and remove the nodes from the list of HSZ nodes.
657 WDML_FreeAllHSZ(pInstance);
659 DestroyWindow(pInstance->hwndEvent);
661 /* OK now delete the instance handle itself */
663 if (WDML_InstanceList == pInstance)
665 /* special case - the first/only entry
667 WDML_InstanceList = pInstance->next;
673 reference_inst = WDML_InstanceList;
674 while (reference_inst->next != pInstance)
676 reference_inst = pInstance->next;
678 reference_inst->next = pInstance->next;
680 /* leave crit sect and release the heap entry
682 HeapFree(GetProcessHeap(), 0, pInstance);
683 LeaveCriticalSection(&WDML_CritSect);
687 /******************************************************************
688 * WDML_NotifyThreadExit
692 void WDML_NotifyThreadDetach(void)
694 WDML_INSTANCE* pInstance;
696 DWORD tid = GetCurrentThreadId();
698 EnterCriticalSection(&WDML_CritSect);
699 for (pInstance = WDML_InstanceList; pInstance != NULL; pInstance = next)
701 next = pInstance->next;
702 if (pInstance->threadID == tid)
704 DdeUninitialize(pInstance->instanceID);
707 LeaveCriticalSection(&WDML_CritSect);
710 /******************************************************************
711 * WDML_InvokeCallback
715 HDDEDATA WDML_InvokeCallback(WDML_INSTANCE* pInstance, UINT uType, UINT uFmt, HCONV hConv,
716 HSZ hsz1, HSZ hsz2, HDDEDATA hdata,
717 DWORD dwData1, DWORD dwData2)
721 if (pInstance == NULL)
723 TRACE("invoking CB%d[%08lx] (%u %u %08lx 0x%x 0x%x %u %lu %lu)\n",
724 pInstance->win16 ? 16 : 32, (DWORD)pInstance->callback, uType, uFmt,
725 (DWORD)hConv, hsz1, hsz2, hdata, dwData1, dwData2);
726 if (pInstance->win16)
728 ret = WDML_InvokeCallback16(pInstance->callback, uType, uFmt, hConv,
729 hsz1, hsz2, hdata, dwData1, dwData2);
733 ret = pInstance->callback(uType, uFmt, hConv, hsz1, hsz2, hdata, dwData1, dwData2);
735 TRACE("done => %08lx\n", (DWORD)ret);
739 /*****************************************************************************
742 * generic routine to return a pointer to the relevant DDE_HANDLE_ENTRY
743 * for an instance Id, or NULL if the entry does not exist
746 WDML_INSTANCE* WDML_GetInstance(DWORD instId)
748 WDML_INSTANCE* pInstance;
750 for (pInstance = WDML_InstanceList; pInstance != NULL; pInstance = pInstance->next)
752 if (pInstance->instanceID == instId)
754 if (GetCurrentThreadId() != pInstance->threadID)
756 FIXME("Tried to get instance from wrong thread\n");
762 TRACE("Instance entry missing\n");
766 /******************************************************************
767 * WDML_GetInstanceFromWnd
771 WDML_INSTANCE* WDML_GetInstanceFromWnd(HWND hWnd)
773 return (WDML_INSTANCE*)GetWindowLongA(hWnd, GWL_WDML_INSTANCE);
776 /******************************************************************************
777 * DdeGetLastError [USER32.@] Gets most recent error code
780 * idInst [I] Instance identifier
785 UINT WINAPI DdeGetLastError(DWORD idInst)
788 WDML_INSTANCE* pInstance;
790 FIXME("(%ld): error reporting is weakly implemented\n", idInst);
792 EnterCriticalSection(&WDML_CritSect);
794 /* First check instance
796 pInstance = WDML_GetInstance(idInst);
797 if (pInstance == NULL)
799 error_code = DMLERR_DLL_NOT_INITIALIZED;
803 error_code = pInstance->lastError;
804 pInstance->lastError = 0;
807 LeaveCriticalSection(&WDML_CritSect);
811 /* ================================================================
815 * ================================================================ */
818 /******************************************************************
823 static HSZNode* WDML_FindNode(WDML_INSTANCE* pInstance, HSZ hsz)
827 if (pInstance == NULL) return NULL;
829 for (pNode = pInstance->nodeList; pNode != NULL; pNode = pNode->next)
831 if (pNode->hsz == hsz) break;
833 if (!pNode) WARN("HSZ 0x%x not found\n", hsz);
837 /******************************************************************
838 * WDML_MakeAtomFromHsz
840 * Creates a global atom from an existing HSZ
841 * Generally used before sending an HSZ as an atom to a remote app
843 ATOM WDML_MakeAtomFromHsz(HSZ hsz)
845 WCHAR nameBuffer[MAX_BUFFER_LEN];
847 if (GetAtomNameW((ATOM)hsz, nameBuffer, MAX_BUFFER_LEN))
848 return GlobalAddAtomW(nameBuffer);
849 WARN("HSZ 0x%xnot found\n", hsz);
853 /******************************************************************
854 * WDML_MakeHszFromAtom
856 * Creates a HSZ from an existing global atom
857 * Generally used while receiving a global atom and transforming it
860 HSZ WDML_MakeHszFromAtom(WDML_INSTANCE* pInstance, ATOM atom)
862 WCHAR nameBuffer[MAX_BUFFER_LEN];
864 GlobalGetAtomNameW(atom, nameBuffer, MAX_BUFFER_LEN);
865 return DdeCreateStringHandleW(pInstance->instanceID, nameBuffer, CP_WINUNICODE);
868 /******************************************************************
873 BOOL WDML_IncHSZ(WDML_INSTANCE* pInstance, HSZ hsz)
877 pNode = WDML_FindNode(pInstance, hsz);
878 if (!pNode) return FALSE;
884 /******************************************************************************
885 * WDML_DecHSZ (INTERNAL)
887 * Decrease the ref count of an HSZ. If it reaches 0, the node is removed from the list
889 * Returns -1 is the HSZ isn't found, otherwise it's the current (after --) of the ref count
891 BOOL WDML_DecHSZ(WDML_INSTANCE* pInstance, HSZ hsz)
893 HSZNode* pPrev = NULL;
896 for (pCurrent = pInstance->nodeList; pCurrent != NULL; pCurrent = (pPrev = pCurrent)->next)
898 /* If we found the node we were looking for and its ref count is one,
901 if (pCurrent->hsz == hsz)
903 if (--pCurrent->refCount == 0)
905 if (pCurrent == pInstance->nodeList)
907 pInstance->nodeList = pCurrent->next;
911 pPrev->next = pCurrent->next;
913 HeapFree(GetProcessHeap(), 0, pCurrent);
919 WARN("HSZ 0x%xnot found\n", hsz);
924 /******************************************************************************
925 * WDML_FreeAllHSZ (INTERNAL)
927 * Frees up all the strings still allocated in the list and
928 * remove all the nodes from the list of HSZ nodes.
930 void WDML_FreeAllHSZ(WDML_INSTANCE* pInstance)
932 /* Free any strings created in this instance.
934 while (pInstance->nodeList != NULL)
936 DdeFreeStringHandle(pInstance->instanceID, pInstance->nodeList->hsz);
940 /******************************************************************************
941 * InsertHSZNode (INTERNAL)
943 * Insert a node to the head of the list.
945 static void WDML_InsertHSZNode(WDML_INSTANCE* pInstance, HSZ hsz)
949 HSZNode* pNew = NULL;
950 /* Create a new node for this HSZ.
952 pNew = (HSZNode*)HeapAlloc(GetProcessHeap(), 0, sizeof(HSZNode));
956 pNew->next = pInstance->nodeList;
958 pInstance->nodeList = pNew;
962 ERR("Primary HSZ Node allocation failed - out of memory\n");
967 /******************************************************************
972 static int WDML_QueryString(WDML_INSTANCE* pInstance, HSZ hsz, LPVOID ptr, DWORD cchMax,
975 WCHAR pString[MAX_BUFFER_LEN];
977 /* If psz is null, we have to return only the length
983 cchMax = MAX_BUFFER_LEN;
989 ret = GetAtomNameA(hsz, ptr, cchMax);
992 ret = GetAtomNameW(hsz, ptr, cchMax);
994 ERR("Unknown code page %d\n", codepage);
1000 /*****************************************************************
1001 * DdeQueryStringA [USER32.@]
1003 DWORD WINAPI DdeQueryStringA(DWORD idInst, HSZ hsz, LPSTR psz, DWORD cchMax, INT iCodePage)
1006 WDML_INSTANCE* pInstance;
1008 TRACE("(%ld, 0x%x, %p, %ld, %d)\n", idInst, hsz, psz, cchMax, iCodePage);
1010 EnterCriticalSection(&WDML_CritSect);
1012 /* First check instance
1014 pInstance = WDML_GetInstance(idInst);
1015 if (pInstance != NULL)
1017 if (iCodePage == 0) iCodePage = CP_WINANSI;
1018 ret = WDML_QueryString(pInstance, hsz, psz, cchMax, iCodePage);
1020 LeaveCriticalSection(&WDML_CritSect);
1022 TRACE("returning %s\n", debugstr_a(psz));
1026 /*****************************************************************
1027 * DdeQueryStringW [USER32.@]
1030 DWORD WINAPI DdeQueryStringW(DWORD idInst, HSZ hsz, LPWSTR psz, DWORD cchMax, INT iCodePage)
1033 WDML_INSTANCE* pInstance;
1035 TRACE("(%ld, 0x%x, %p, %ld, %d)\n",
1036 idInst, hsz, psz, cchMax, iCodePage);
1038 EnterCriticalSection(&WDML_CritSect);
1040 /* First check instance
1042 pInstance = WDML_GetInstance(idInst);
1043 if (pInstance != NULL)
1045 if (iCodePage == 0) iCodePage = CP_WINUNICODE;
1046 ret = WDML_QueryString(pInstance, hsz, psz, cchMax, iCodePage);
1048 LeaveCriticalSection(&WDML_CritSect);
1050 TRACE("returning %s\n", debugstr_w(psz));
1054 /******************************************************************
1059 static HSZ WDML_CreateString(WDML_INSTANCE* pInstance, LPCVOID ptr, int codepage)
1066 hsz = AddAtomA(ptr);
1067 TRACE("added atom %s with HSZ 0x%x, \n", debugstr_a(ptr), hsz);
1070 hsz = AddAtomW(ptr);
1071 TRACE("added atom %s with HSZ 0x%x, \n", debugstr_w(ptr), hsz);
1074 ERR("Unknown code page %d\n", codepage);
1077 WDML_InsertHSZNode(pInstance, hsz);
1081 /*****************************************************************
1082 * DdeCreateStringHandleA [USER32.@]
1085 * Success: String handle
1088 HSZ WINAPI DdeCreateStringHandleA(DWORD idInst, LPCSTR psz, INT codepage)
1091 WDML_INSTANCE* pInstance;
1093 TRACE("(%ld,%p,%d)\n", idInst, psz, codepage);
1095 EnterCriticalSection(&WDML_CritSect);
1097 pInstance = WDML_GetInstance(idInst);
1100 if (codepage == 0) codepage = CP_WINANSI;
1101 hsz = WDML_CreateString(pInstance, psz, codepage);
1104 LeaveCriticalSection(&WDML_CritSect);
1109 /******************************************************************************
1110 * DdeCreateStringHandleW [USER32.@] Creates handle to identify string
1113 * idInst [I] Instance identifier
1114 * psz [I] Pointer to string
1115 * codepage [I] Code page identifier
1117 * Success: String handle
1120 HSZ WINAPI DdeCreateStringHandleW(DWORD idInst, LPCWSTR psz, INT codepage)
1122 WDML_INSTANCE* pInstance;
1125 TRACE("(%ld,%p,%d)\n", idInst, psz, codepage);
1127 EnterCriticalSection(&WDML_CritSect);
1129 pInstance = WDML_GetInstance(idInst);
1132 if (codepage == 0) codepage = CP_WINUNICODE;
1133 hsz = WDML_CreateString(pInstance, psz, codepage);
1135 LeaveCriticalSection(&WDML_CritSect);
1140 /*****************************************************************
1141 * DdeFreeStringHandle (USER32.@)
1142 * RETURNS: success: nonzero
1145 BOOL WINAPI DdeFreeStringHandle(DWORD idInst, HSZ hsz)
1147 WDML_INSTANCE* pInstance;
1150 TRACE("(%ld,0x%x): \n", idInst, hsz);
1152 EnterCriticalSection(&WDML_CritSect);
1154 /* First check instance
1156 pInstance = WDML_GetInstance(idInst);
1158 ret = WDML_DecHSZ(pInstance, hsz);
1160 LeaveCriticalSection(&WDML_CritSect);
1165 /*****************************************************************
1166 * DdeKeepStringHandle (USER32.@)
1168 * RETURNS: success: nonzero
1171 BOOL WINAPI DdeKeepStringHandle(DWORD idInst, HSZ hsz)
1173 WDML_INSTANCE* pInstance;
1176 TRACE("(%ld,0x%x): \n", idInst, hsz);
1178 EnterCriticalSection(&WDML_CritSect);
1180 /* First check instance
1182 pInstance = WDML_GetInstance(idInst);
1184 ret = WDML_IncHSZ(pInstance, hsz);
1186 LeaveCriticalSection(&WDML_CritSect);
1190 /*****************************************************************
1191 * DdeCmpStringHandles (USER32.@)
1193 * Compares the value of two string handles. This comparison is
1194 * not case sensitive.
1197 * -1 The value of hsz1 is zero or less than hsz2
1198 * 0 The values of hsz 1 and 2 are the same or both zero.
1199 * 1 The value of hsz2 is zero of less than hsz1
1201 INT WINAPI DdeCmpStringHandles(HSZ hsz1, HSZ hsz2)
1203 WCHAR psz1[MAX_BUFFER_LEN];
1204 WCHAR psz2[MAX_BUFFER_LEN];
1208 ret1 = GetAtomNameW(hsz1, psz1, MAX_BUFFER_LEN);
1209 ret2 = GetAtomNameW(hsz2, psz2, MAX_BUFFER_LEN);
1211 TRACE("(%x<%s> %x<%s>);\n", hsz1, debugstr_w(psz1), hsz2, debugstr_w(psz2));
1213 /* Make sure we found both strings. */
1214 if (ret1 == 0 && ret2 == 0)
1216 /* If both are not found, return both "zero strings". */
1221 /* If hsz1 is a not found, return hsz1 is "zero string". */
1226 /* If hsz2 is a not found, return hsz2 is "zero string". */
1231 /* Compare the two strings we got (case insensitive). */
1232 ret = lstrcmpiW(psz1, psz2);
1233 /* Since strcmp returns any number smaller than
1234 * 0 when the first string is found to be less than
1235 * the second one we must make sure we are returning
1236 * the proper values.
1251 /* ================================================================
1253 * Data handle management
1255 * ================================================================ */
1257 /*****************************************************************
1258 * DdeCreateDataHandle (USER32.@)
1260 HDDEDATA WINAPI DdeCreateDataHandle(DWORD idInst, LPBYTE pSrc, DWORD cb,
1261 DWORD cbOff, HSZ hszItem, UINT wFmt,
1265 For now, we ignore idInst, hszItem, wFmt, and afCmd.
1266 The purpose of these arguments still need to be investigated.
1271 DDE_DATAHANDLE_HEAD* pDdh;
1273 TRACE("(%ld,%p,%ld,%ld,0x%lx,%d,%d): semi-stub\n",
1274 idInst,pSrc,cb,cbOff,(DWORD)hszItem,wFmt,afCmd);
1276 /* we use the first 4 bytes to store the size */
1277 if (!(hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, cb + sizeof(DDE_DATAHANDLE_HEAD))))
1279 ERR("GlobalAlloc failed\n");
1283 pDdh = (DDE_DATAHANDLE_HEAD*)GlobalLock(hMem);
1284 pDdh->cfFormat = wFmt;
1286 pByte = (LPBYTE)(pDdh + 1);
1289 memcpy(pByte, pSrc + cbOff, cb);
1293 return (HDDEDATA)hMem;
1296 /*****************************************************************
1298 * DdeAddData (USER32.@)
1300 HDDEDATA WINAPI DdeAddData(HDDEDATA hData, LPBYTE pSrc, DWORD cb, DWORD cbOff)
1302 DWORD old_sz, new_sz;
1305 pDst = DdeAccessData(hData, &old_sz);
1306 if (!pDst) return 0;
1308 new_sz = cb + cbOff;
1309 if (new_sz > old_sz)
1311 DdeUnaccessData(hData);
1312 hData = GlobalReAlloc((HGLOBAL)hData, new_sz + sizeof(DDE_DATAHANDLE_HEAD),
1313 GMEM_MOVEABLE | GMEM_DDESHARE);
1314 pDst = DdeAccessData(hData, &old_sz);
1317 if (!pDst) return 0;
1319 memcpy(pDst + cbOff, pSrc, cb);
1320 DdeUnaccessData(hData);
1324 /******************************************************************************
1325 * DdeGetData [USER32.@] Copies data from DDE object to local buffer
1329 * hData [I] Handle to DDE object
1330 * pDst [I] Pointer to destination buffer
1331 * cbMax [I] Amount of data to copy
1332 * cbOff [I] Offset to beginning of data
1335 * Size of memory object associated with handle
1337 DWORD WINAPI DdeGetData(HDDEDATA hData, LPBYTE pDst, DWORD cbMax, DWORD cbOff)
1339 DWORD dwSize, dwRet;
1342 TRACE("(%08lx,%p,%ld,%ld)\n",(DWORD)hData,pDst,cbMax,cbOff);
1344 pByte = DdeAccessData(hData, &dwSize);
1348 if (cbOff + cbMax < dwSize)
1352 else if (cbOff < dwSize)
1354 dwRet = dwSize - cbOff;
1360 if (pDst && dwRet != 0)
1362 memcpy(pDst, pByte + cbOff, dwRet);
1364 DdeUnaccessData(hData);
1373 /*****************************************************************
1374 * DdeAccessData (USER32.@)
1376 LPBYTE WINAPI DdeAccessData(HDDEDATA hData, LPDWORD pcbDataSize)
1378 HGLOBAL hMem = (HGLOBAL)hData;
1379 DDE_DATAHANDLE_HEAD* pDdh;
1381 TRACE("(%08lx,%p)\n", (DWORD)hData, pcbDataSize);
1383 pDdh = (DDE_DATAHANDLE_HEAD*)GlobalLock(hMem);
1386 ERR("Failed on GlobalLock(%04x)\n", hMem);
1390 if (pcbDataSize != NULL)
1392 *pcbDataSize = GlobalSize(hMem) - sizeof(DDE_DATAHANDLE_HEAD);
1395 return (LPBYTE)(pDdh + 1);
1398 /*****************************************************************
1399 * DdeUnaccessData (USER32.@)
1401 BOOL WINAPI DdeUnaccessData(HDDEDATA hData)
1403 HGLOBAL hMem = (HGLOBAL)hData;
1405 TRACE("(0x%lx)\n", (DWORD)hData);
1412 /*****************************************************************
1413 * DdeFreeDataHandle (USER32.@)
1415 BOOL WINAPI DdeFreeDataHandle(HDDEDATA hData)
1417 return GlobalFree((HGLOBAL)hData) == 0;
1420 /* ================================================================
1422 * Global <=> Data handle management
1424 * ================================================================ */
1426 /* Note: we use a DDEDATA, but layout of DDEDATA, DDEADVISE and DDEPOKE structures is similar:
1428 * (bytes) (bits) comment
1429 * 0 16 bit fields for options (release, ackreq, response...)
1430 * 2 16 clipboard format
1431 * 4 ? data to be used
1433 HDDEDATA WDML_Global2DataHandle(HGLOBAL hMem, WINE_DDEHEAD* p)
1440 pDd = GlobalLock(hMem);
1443 if (p) memcpy(p, pDd, sizeof(WINE_DDEHEAD));
1444 ret = DdeCreateDataHandle(0, pDd->Value,
1445 GlobalSize(hMem) - sizeof(WINE_DDEHEAD),
1446 0, 0, pDd->cfFormat, 0);
1453 /******************************************************************
1454 * WDML_DataHandle2Global
1458 HGLOBAL WDML_DataHandle2Global(HDDEDATA hDdeData, BOOL fResponse, BOOL fRelease,
1459 BOOL fDeferUpd, BOOL fAckReq)
1461 DDE_DATAHANDLE_HEAD* pDdh;
1465 dwSize = GlobalSize(hDdeData) - sizeof(DDE_DATAHANDLE_HEAD);
1466 pDdh = (DDE_DATAHANDLE_HEAD*)GlobalLock(hDdeData);
1469 hMem = GlobalAlloc(sizeof(WINE_DDEHEAD) + dwSize, GMEM_MOVEABLE | GMEM_DDESHARE);
1474 wdh = GlobalLock(hMem);
1477 wdh->fResponse = fResponse;
1478 wdh->fRelease = fRelease;
1479 wdh->fDeferUpd = fDeferUpd;
1480 wdh->fAckReq = fAckReq;
1481 wdh->cfFormat = pDdh->cfFormat;
1482 memcpy(wdh + 1, pDdh + 1, dwSize);
1486 GlobalUnlock(hDdeData);
1492 /* ================================================================
1496 * ================================================================ */
1498 /******************************************************************
1503 WDML_SERVER* WDML_AddServer(WDML_INSTANCE* pInstance, HSZ hszService, HSZ hszTopic)
1505 WDML_SERVER* pServer;
1509 pServer = (WDML_SERVER*)HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_SERVER));
1510 if (pServer == NULL) return NULL;
1512 WDML_IncHSZ(pInstance, pServer->hszService = hszService);
1514 DdeQueryStringA(pInstance->instanceID, hszService, buf1, sizeof(buf1), CP_WINANSI);
1515 snprintf(buf2, sizeof(buf2), "%s(0x%08lx)", buf1, GetCurrentProcessId());
1516 pServer->hszServiceSpec = DdeCreateStringHandleA(pInstance->instanceID, buf2, CP_WINANSI);
1518 pServer->atomService = WDML_MakeAtomFromHsz(pServer->hszService);
1519 pServer->atomServiceSpec = WDML_MakeAtomFromHsz(pServer->hszServiceSpec);
1521 pServer->filterOn = TRUE;
1523 pServer->next = pInstance->servers;
1524 pInstance->servers = pServer;
1528 /******************************************************************
1533 void WDML_RemoveServer(WDML_INSTANCE* pInstance, HSZ hszService, HSZ hszTopic)
1535 WDML_SERVER* pPrev = NULL;
1536 WDML_SERVER* pServer = NULL;
1538 WDML_CONV* pConvNext;
1540 pServer = pInstance->servers;
1542 while (pServer != NULL)
1544 if (DdeCmpStringHandles(pServer->hszService, hszService) == 0)
1546 WDML_BroadcastDDEWindows(WDML_szEventClass, WM_WDML_UNREGISTER,
1547 pServer->atomService, pServer->atomServiceSpec);
1548 /* terminate all conversations for given topic */
1549 for (pConv = pInstance->convs[WDML_SERVER_SIDE]; pConv != NULL; pConv = pConvNext)
1551 pConvNext = pConv->next;
1552 if (DdeCmpStringHandles(pConv->hszService, hszService) == 0)
1554 WDML_RemoveConv(pConv, WDML_SERVER_SIDE);
1555 /* don't care about return code (whether client window is present or not) */
1556 PostMessageA(pConv->hwndClient, WM_DDE_TERMINATE, (WPARAM)pConv->hwndServer, 0L);
1559 if (pServer == pInstance->servers)
1561 pInstance->servers = pServer->next;
1565 pPrev->next = pServer->next;
1568 DestroyWindow(pServer->hwndServer);
1569 WDML_DecHSZ(pInstance, pServer->hszServiceSpec);
1570 WDML_DecHSZ(pInstance, pServer->hszService);
1572 GlobalDeleteAtom(pServer->atomService);
1573 GlobalDeleteAtom(pServer->atomServiceSpec);
1575 HeapFree(GetProcessHeap(), 0, pServer);
1580 pServer = pServer->next;
1584 /*****************************************************************************
1587 * generic routine to return a pointer to the relevant ServiceNode
1588 * for a given service name, or NULL if the entry does not exist
1591 WDML_SERVER* WDML_FindServer(WDML_INSTANCE* pInstance, HSZ hszService, HSZ hszTopic)
1593 WDML_SERVER* pServer;
1595 for (pServer = pInstance->servers; pServer != NULL; pServer = pServer->next)
1597 if (hszService == pServer->hszService)
1602 TRACE("Service name missing\n");
1606 /* ================================================================
1608 * Conversation management
1610 * ================================================================ */
1612 /******************************************************************
1617 WDML_CONV* WDML_AddConv(WDML_INSTANCE* pInstance, WDML_SIDE side,
1618 HSZ hszService, HSZ hszTopic, HWND hwndClient, HWND hwndServer)
1622 /* no converstation yet, add it */
1623 pConv = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_CONV));
1624 if (!pConv) return NULL;
1626 pConv->instance = pInstance;
1627 WDML_IncHSZ(pInstance, pConv->hszService = hszService);
1628 WDML_IncHSZ(pInstance, pConv->hszTopic = hszTopic);
1629 pConv->hwndServer = hwndServer;
1630 pConv->hwndClient = hwndClient;
1631 pConv->transactions = NULL;
1633 pConv->wStatus = (side == WDML_CLIENT_SIDE) ? ST_CLIENT : 0L;
1634 /* check if both side of the conversation are of the same instance */
1635 if (GetWindowThreadProcessId(hwndClient, NULL) == GetWindowThreadProcessId(hwndServer, NULL) &&
1636 WDML_GetInstanceFromWnd(hwndClient) == WDML_GetInstanceFromWnd(hwndServer))
1638 pConv->wStatus |= ST_ISSELF;
1640 pConv->wConvst = XST_NULL;
1642 pConv->next = pInstance->convs[side];
1643 pInstance->convs[side] = pConv;
1648 /******************************************************************
1653 WDML_CONV* WDML_FindConv(WDML_INSTANCE* pInstance, WDML_SIDE side,
1654 HSZ hszService, HSZ hszTopic)
1656 WDML_CONV* pCurrent = NULL;
1658 for (pCurrent = pInstance->convs[side]; pCurrent != NULL; pCurrent = pCurrent->next)
1660 if (DdeCmpStringHandles(pCurrent->hszService, hszService) == 0 &&
1661 DdeCmpStringHandles(pCurrent->hszTopic, hszTopic) == 0)
1670 /******************************************************************
1675 void WDML_RemoveConv(WDML_CONV* pRef, WDML_SIDE side)
1677 WDML_CONV* pPrev = NULL;
1678 WDML_CONV* pCurrent;
1680 WDML_XACT* pXActNext;
1686 /* remove any pending transaction */
1687 for (pXAct = pRef->transactions; pXAct != NULL; pXAct = pXActNext)
1689 pXActNext = pXAct->next;
1690 WDML_FreeTransaction(pRef->instance, pXAct, TRUE);
1693 WDML_RemoveAllLinks(pRef->instance, pRef, side);
1695 /* FIXME: should we keep the window around ? it seems so (at least on client side
1696 * to let QueryConvInfo work after conv termination, but also to implement
1699 /* destroy conversation window, but first remove pConv from hWnd.
1700 * this would help the wndProc do appropriate handling upon a WM_DESTROY message
1702 hWnd = (side == WDML_CLIENT_SIDE) ? pRef->hwndClient : pRef->hwndServer;
1703 SetWindowLongA(hWnd, GWL_WDML_CONVERSATION, 0);
1705 DestroyWindow((side == WDML_CLIENT_SIDE) ? pRef->hwndClient : pRef->hwndServer);
1707 WDML_DecHSZ(pRef->instance, pRef->hszService);
1708 WDML_DecHSZ(pRef->instance, pRef->hszTopic);
1710 for (pCurrent = pRef->instance->convs[side]; pCurrent != NULL; pCurrent = (pPrev = pCurrent)->next)
1712 if (pCurrent == pRef)
1714 if (pCurrent == pRef->instance->convs[side])
1716 pRef->instance->convs[side] = pCurrent->next;
1720 pPrev->next = pCurrent->next;
1723 HeapFree(GetProcessHeap(), 0, pCurrent);
1729 /*****************************************************************
1730 * DdeEnableCallback (USER32.@)
1732 BOOL WINAPI DdeEnableCallback(DWORD idInst, HCONV hConv, UINT wCmd)
1734 FIXME("(%ld, 0x%x, %d) stub\n", idInst, hConv, wCmd);
1739 /******************************************************************
1744 WDML_CONV* WDML_GetConv(HCONV hConv, BOOL checkConnected)
1746 WDML_CONV* pConv = (WDML_CONV*)hConv;
1748 /* FIXME: should do better checking */
1750 if (checkConnected && !(pConv->wStatus & ST_CONNECTED))
1752 FIXME("found conv but ain't connected\n");
1755 if (GetCurrentThreadId() != pConv->instance->threadID)
1757 FIXME("wrong thread ID\n");
1764 /******************************************************************
1765 * WDML_GetConvFromWnd
1769 WDML_CONV* WDML_GetConvFromWnd(HWND hWnd)
1771 return (WDML_CONV*)GetWindowLongA(hWnd, GWL_WDML_CONVERSATION);
1774 /******************************************************************
1779 LPARAM WDML_PostAck(WDML_CONV* pConv, WDML_SIDE side, WORD appRetCode,
1780 BOOL fBusy, BOOL fAck, ATOM atom, LPARAM lParam, UINT oldMsg)
1785 if (side == WDML_SERVER_SIDE)
1787 from = pConv->hwndServer;
1788 to = pConv->hwndClient;
1792 to = pConv->hwndServer;
1793 from = pConv->hwndClient;
1796 ddeAck.bAppReturnCode = appRetCode;
1797 ddeAck.reserved = 0;
1798 ddeAck.fBusy = fBusy;
1801 TRACE("Posting a %s ack\n", ddeAck.fAck ? "positive" : "negative");
1804 PostMessageA(to, WM_DDE_ACK, (WPARAM)from,
1805 ReuseDDElParam(lParam, oldMsg, WM_DDE_ACK, *(WORD*)&ddeAck, atom));
1809 lParam = PackDDElParam(WM_DDE_ACK, *(WORD*)&ddeAck, atom);
1810 PostMessageA(to, WM_DDE_ACK, (WPARAM)from, lParam);
1815 /*****************************************************************
1816 * DdeSetUserHandle (USER32.@)
1818 BOOL WINAPI DdeSetUserHandle(HCONV hConv, DWORD id, DWORD hUser)
1823 EnterCriticalSection(&WDML_CritSect);
1825 pConv = WDML_GetConv(hConv, FALSE);
1833 pConv->hUser = hUser;
1839 pXAct = WDML_FindTransaction(pConv, id);
1842 pXAct->hUser = hUser;
1846 pConv->instance->lastError = DMLERR_UNFOUND_QUEUE_ID;
1851 LeaveCriticalSection(&WDML_CritSect);
1855 /******************************************************************
1856 * WDML_GetLocalConvInfo
1860 static BOOL WDML_GetLocalConvInfo(WDML_CONV* pConv, CONVINFO* ci, DWORD id)
1866 ci->hConvPartner = (pConv->wStatus & ST_ISLOCAL) ? (HCONV)((DWORD)pConv | 1) : 0;
1867 ci->hszSvcPartner = pConv->hszService;
1868 ci->hszServiceReq = pConv->hszService; /* FIXME: they shouldn't be the same, should they ? */
1869 ci->hszTopic = pConv->hszTopic;
1870 ci->wStatus = pConv->wStatus;
1872 side = (pConv->wStatus & ST_CLIENT) ? WDML_CLIENT_SIDE : WDML_SERVER_SIDE;
1874 for (pLink = pConv->instance->links[side]; pLink != NULL; pLink = pLink->next)
1876 if (pLink->hConv == (HWND)pConv)
1878 ci->wStatus |= ST_ADVISE;
1883 /* FIXME: non handled status flags:
1889 ci->wConvst = pConv->wConvst; /* FIXME */
1891 ci->wLastError = 0; /* FIXME: note it's not the instance last error */
1893 ci->ConvCtxt = pConv->convContext;
1894 if (ci->wStatus & ST_CLIENT)
1896 ci->hwnd = pConv->hwndClient;
1897 ci->hwndPartner = pConv->hwndServer;
1901 ci->hwnd = pConv->hwndServer;
1902 ci->hwndPartner = pConv->hwndClient;
1906 ci->hUser = pConv->hUser;
1915 pXAct = WDML_FindTransaction(pConv, id);
1918 ci->hUser = pXAct->hUser;
1919 ci->hszItem = pXAct->hszItem;
1920 ci->wFmt = pXAct->wFmt;
1921 ci->wType = pXAct->wType;
1926 pConv->instance->lastError = DMLERR_UNFOUND_QUEUE_ID;
1932 /******************************************************************
1933 * DdeQueryConvInfo (USER32.@)
1936 UINT WINAPI DdeQueryConvInfo(HCONV hConv, DWORD id, LPCONVINFO lpConvInfo)
1938 UINT ret = lpConvInfo->cb;
1942 EnterCriticalSection(&WDML_CritSect);
1944 pConv = WDML_GetConv(hConv, FALSE);
1945 if (pConv != NULL && !WDML_GetLocalConvInfo(pConv, &ci, id))
1951 pConv = WDML_GetConv(hConv & ~1, FALSE);
1954 FIXME("Request on remote conversation information is not implemented yet\n");
1958 LeaveCriticalSection(&WDML_CritSect);
1960 memcpy(lpConvInfo, &ci, min(lpConvInfo->cb, sizeof(ci)));
1964 /* ================================================================
1966 * Link (hot & warm) management
1968 * ================================================================ */
1970 /******************************************************************
1975 void WDML_AddLink(WDML_INSTANCE* pInstance, HCONV hConv, WDML_SIDE side,
1976 UINT wType, HSZ hszItem, UINT wFmt)
1980 pLink = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_LINK));
1987 pLink->hConv = hConv;
1988 pLink->transactionType = wType;
1989 WDML_IncHSZ(pInstance, pLink->hszItem = hszItem);
1991 pLink->hDdeData = 0;
1992 pLink->next = pInstance->links[side];
1993 pInstance->links[side] = pLink;
1996 /******************************************************************
2001 void WDML_RemoveLink(WDML_INSTANCE* pInstance, HCONV hConv, WDML_SIDE side,
2002 HSZ hszItem, UINT uFmt)
2004 WDML_LINK* pPrev = NULL;
2005 WDML_LINK* pCurrent = NULL;
2007 pCurrent = pInstance->links[side];
2009 while (pCurrent != NULL)
2011 if (pCurrent->hConv == hConv &&
2012 DdeCmpStringHandles(pCurrent->hszItem, hszItem) == 0 &&
2013 pCurrent->uFmt == uFmt)
2015 if (pCurrent == pInstance->links[side])
2017 pInstance->links[side] = pCurrent->next;
2021 pPrev->next = pCurrent->next;
2024 if (pCurrent->hDdeData)
2026 DdeFreeDataHandle(pCurrent->hDdeData);
2029 WDML_DecHSZ(pInstance, pCurrent->hszItem);
2030 HeapFree(GetProcessHeap(), 0, pCurrent);
2035 pCurrent = pCurrent->next;
2039 /* this function is called to remove all links related to the conv.
2040 It should be called from both client and server when terminating
2043 /******************************************************************
2044 * WDML_RemoveAllLinks
2048 void WDML_RemoveAllLinks(WDML_INSTANCE* pInstance, WDML_CONV* pConv, WDML_SIDE side)
2050 WDML_LINK* pPrev = NULL;
2051 WDML_LINK* pCurrent = NULL;
2052 WDML_LINK* pNext = NULL;
2054 pCurrent = pInstance->links[side];
2056 while (pCurrent != NULL)
2058 if (pCurrent->hConv == (HCONV)pConv)
2060 if (pCurrent == pInstance->links[side])
2062 pInstance->links[side] = pCurrent->next;
2063 pNext = pCurrent->next;
2067 pPrev->next = pCurrent->next;
2068 pNext = pCurrent->next;
2071 if (pCurrent->hDdeData)
2073 DdeFreeDataHandle(pCurrent->hDdeData);
2075 WDML_DecHSZ(pInstance, pCurrent->hszItem);
2077 HeapFree(GetProcessHeap(), 0, pCurrent);
2084 pCurrent = pCurrent->next;
2093 /******************************************************************
2098 WDML_LINK* WDML_FindLink(WDML_INSTANCE* pInstance, HCONV hConv, WDML_SIDE side,
2099 HSZ hszItem, UINT uFmt)
2101 WDML_LINK* pCurrent = NULL;
2103 for (pCurrent = pInstance->links[side]; pCurrent != NULL; pCurrent = pCurrent->next)
2105 /* we don't need to check for transaction type as it can be altered */
2107 if (pCurrent->hConv == hConv &&
2108 DdeCmpStringHandles(pCurrent->hszItem, hszItem) == 0 &&
2109 pCurrent->uFmt == uFmt)
2119 /* ================================================================
2121 * Transaction management
2123 * ================================================================ */
2125 /******************************************************************
2126 * WDML_AllocTransaction
2128 * Alloc a transaction structure for handling the message ddeMsg
2130 WDML_XACT* WDML_AllocTransaction(WDML_INSTANCE* pInstance, UINT ddeMsg,
2131 UINT wFmt, HSZ hszItem)
2134 static WORD tid = 1; /* FIXME: wrap around */
2136 pXAct = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_XACT));
2139 pInstance->lastError = DMLERR_MEMORY_ERROR;
2143 pXAct->xActID = tid++;
2144 pXAct->ddeMsg = ddeMsg;
2145 pXAct->hDdeData = 0;
2150 WDML_IncHSZ(pInstance, pXAct->hszItem = hszItem);
2158 /******************************************************************
2159 * WDML_QueueTransaction
2161 * Adds a transaction to the list of transaction
2163 void WDML_QueueTransaction(WDML_CONV* pConv, WDML_XACT* pXAct)
2167 /* advance to last in queue */
2168 for (pt = &pConv->transactions; *pt != NULL; pt = &(*pt)->next);
2172 /******************************************************************
2173 * WDML_UnQueueTransaction
2177 BOOL WDML_UnQueueTransaction(WDML_CONV* pConv, WDML_XACT* pXAct)
2181 for (pt = &pConv->transactions; *pt; pt = &(*pt)->next)
2192 /******************************************************************
2193 * WDML_FreeTransaction
2197 void WDML_FreeTransaction(WDML_INSTANCE* pInstance, WDML_XACT* pXAct, BOOL doFreePmt)
2199 /* free pmt(s) in pXAct too */
2200 if (doFreePmt && pXAct->hMem)
2201 GlobalFree(pXAct->hMem);
2202 WDML_DecHSZ(pInstance, pXAct->hszItem);
2204 HeapFree(GetProcessHeap(), 0, pXAct);
2207 /******************************************************************
2208 * WDML_FindTransaction
2212 WDML_XACT* WDML_FindTransaction(WDML_CONV* pConv, DWORD tid)
2217 for (pXAct = pConv->transactions; pXAct; pXAct = pXAct->next)
2219 if (pXAct->xActID == tid)
2225 /* ================================================================
2227 * Information broadcast across DDEML implementations
2229 * ================================================================ */
2231 struct tagWDML_BroadcastPmt
2239 /******************************************************************
2240 * WDML_BroadcastEnumProc
2244 static BOOL CALLBACK WDML_BroadcastEnumProc(HWND hWnd, LPARAM lParam)
2246 struct tagWDML_BroadcastPmt* s = (struct tagWDML_BroadcastPmt*)lParam;
2249 if (GetClassNameA(hWnd, buffer, sizeof(buffer)) > 0 &&
2250 strcmp(buffer, s->clsName) == 0)
2252 PostMessageA(hWnd, s->uMsg, s->wParam, s->lParam);
2257 /******************************************************************
2258 * WDML_BroadcastDDEWindows
2262 void WDML_BroadcastDDEWindows(const char* clsName, UINT uMsg, WPARAM wParam, LPARAM lParam)
2264 struct tagWDML_BroadcastPmt s;
2266 s.clsName = clsName;
2270 EnumWindows(WDML_BroadcastEnumProc, (LPARAM)&s);