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
23 #include "debugtools.h"
24 #include "dde/dde_private.h"
26 DEFAULT_DEBUG_CHANNEL(ddeml);
28 static WDML_INSTANCE* WDML_InstanceList = NULL;
29 static DWORD WDML_MaxInstanceID = 0; /* OK for present, have to worry about wrap-around later */
30 const char WDML_szEventClass[] = "DdeEventClass";
31 CRITICAL_SECTION WDML_CritSect = CRITICAL_SECTION_INIT("WDML_CritSect");
33 /* ================================================================
35 * Pure DDE (non DDEML) management
37 * ================================================================ */
39 static BOOL DDE_RequirePacking(UINT msg)
51 case WM_DDE_EXECUTE: /* strange, NT 2000 (at least) really uses packing here... */
53 case WM_DDE_REQUEST: /* assuming clipboard formats are 16 bit */
54 case WM_DDE_TERMINATE:
55 case WM_DDE_UNADVISE: /* assuming clipboard formats are 16 bit */
59 TRACE("Unknown message %04x\n", msg);
66 /*****************************************************************
67 * PackDDElParam (USER32.@)
72 LPARAM WINAPI PackDDElParam(UINT msg, UINT uiLo, UINT uiHi)
77 if (!DDE_RequirePacking(msg))
78 return MAKELONG(uiLo, uiHi);
80 if (!(hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, sizeof(UINT) * 2)))
82 ERR("GlobalAlloc failed\n");
86 params = GlobalLock(hMem);
89 ERR("GlobalLock failed\n");
102 /*****************************************************************
103 * UnpackDDElParam (USER32.@)
109 BOOL WINAPI UnpackDDElParam(UINT msg, LPARAM lParam,
110 PUINT uiLo, PUINT uiHi)
115 if (!DDE_RequirePacking(msg))
117 *uiLo = LOWORD(lParam);
118 *uiHi = HIWORD(lParam);
128 hMem = (HGLOBAL)lParam;
130 params = GlobalLock(hMem);
133 ERR("GlobalLock failed\n");
146 /*****************************************************************
147 * FreeDDElParam (USER32.@)
153 BOOL WINAPI FreeDDElParam(UINT msg, LPARAM lParam)
155 HGLOBAL hMem = (HGLOBAL)lParam;
157 if (!DDE_RequirePacking(msg))
164 return GlobalFree(hMem) == (HGLOBAL)NULL;
168 /*****************************************************************
169 * ReuseDDElParam (USER32.@)
174 LPARAM WINAPI ReuseDDElParam(LPARAM lParam, UINT msgIn, UINT msgOut,
175 UINT uiLo, UINT uiHi)
181 in = DDE_RequirePacking(msgIn);
182 out = DDE_RequirePacking(msgOut);
186 return PackDDElParam(msgOut, uiLo, uiHi);
196 FreeDDElParam(msgIn, lParam);
197 return MAKELONG(uiLo, uiHi);
200 hMem = (HGLOBAL)lParam;
202 params = GlobalLock(hMem);
205 ERR("GlobalLock failed\n");
212 TRACE("Reusing pack %08x %08x\n", uiLo, uiHi);
218 /*****************************************************************
219 * ImpersonateDdeClientWindow (USER32.@)
222 * hWndClient [I] handle to DDE client window
223 * hWndServer [I] handle to DDE server window
225 BOOL WINAPI ImpersonateDdeClientWindow(HWND hWndClient, HWND hWndServer)
227 FIXME("(%04x %04x): stub\n", hWndClient, hWndServer);
231 /*****************************************************************
232 * DdeSetQualityOfService (USER32.@)
235 BOOL WINAPI DdeSetQualityOfService(HWND hwndClient, CONST SECURITY_QUALITY_OF_SERVICE *pqosNew,
236 PSECURITY_QUALITY_OF_SERVICE pqosPrev)
238 FIXME("(%04x %p %p): stub\n", hwndClient, pqosNew, pqosPrev);
242 /* ================================================================
244 * Instance management
246 * ================================================================ */
248 /******************************************************************************
249 * IncrementInstanceId
251 * generic routine to increment the max instance Id and allocate a new application instance
253 static void WDML_IncrementInstanceId(WDML_INSTANCE* pInstance)
255 DWORD id = InterlockedIncrement(&WDML_MaxInstanceID);
257 pInstance->instanceID = id;
258 TRACE("New instance id %ld allocated\n", id);
261 /******************************************************************
266 static LRESULT CALLBACK WDML_EventProc(HWND hwndEvent, UINT uMsg, WPARAM wParam, LPARAM lParam)
268 WDML_INSTANCE* pInstance;
273 case WM_WDML_REGISTER:
274 pInstance = WDML_GetInstanceFromWnd(hwndEvent);
275 /* try calling the Callback */
276 if (pInstance && !(pInstance->CBFflags & CBF_SKIP_REGISTRATIONS))
278 hsz1 = WDML_MakeHszFromAtom(pInstance, wParam);
279 hsz2 = WDML_MakeHszFromAtom(pInstance, lParam);
280 WDML_InvokeCallback(pInstance, XTYP_REGISTER, 0, 0, hsz1, hsz2, 0, 0, 0);
281 WDML_DecHSZ(pInstance, hsz1);
282 WDML_DecHSZ(pInstance, hsz2);
286 case WM_WDML_UNREGISTER:
287 pInstance = WDML_GetInstanceFromWnd(hwndEvent);
288 if (pInstance && !(pInstance->CBFflags & CBF_SKIP_UNREGISTRATIONS))
290 hsz1 = WDML_MakeHszFromAtom(pInstance, wParam);
291 hsz2 = WDML_MakeHszFromAtom(pInstance, lParam);
292 WDML_InvokeCallback(pInstance, XTYP_UNREGISTER, 0, 0, hsz1, hsz2, 0, 0, 0);
293 WDML_DecHSZ(pInstance, hsz1);
294 WDML_DecHSZ(pInstance, hsz2);
298 case WM_WDML_CONNECT_CONFIRM:
299 pInstance = WDML_GetInstanceFromWnd(hwndEvent);
300 if (pInstance && !(pInstance->CBFflags & CBF_SKIP_CONNECT_CONFIRMS))
303 /* confirm connection...
304 * lookup for this conv handle
306 HWND client = WIN_GetFullHandle( (HWND)wParam );
307 HWND server = WIN_GetFullHandle( (HWND)lParam );
308 for (pConv = pInstance->convs[WDML_SERVER_SIDE]; pConv != NULL; pConv = pConv->next)
310 if (pConv->hwndClient == client && pConv->hwndServer == server)
315 pConv->wStatus |= ST_ISLOCAL;
317 WDML_InvokeCallback(pInstance, XTYP_CONNECT_CONFIRM, 0, (HCONV)pConv,
318 pConv->hszTopic, pConv->hszService, 0, 0,
319 (pConv->wStatus & ST_ISSELF) ? 1 : 0);
324 return DefWindowProcA(hwndEvent, uMsg, wParam, lParam);
329 /******************************************************************
334 UINT WDML_Initialize(LPDWORD pidInst, PFNCALLBACK pfnCallback,
335 DWORD afCmd, DWORD ulRes, BOOL bUnicode, BOOL b16)
337 WDML_INSTANCE* pInstance;
338 WDML_INSTANCE* reference_inst;
340 WNDCLASSEXA wndclass;
342 TRACE("(%p,%p,0x%lx,%ld)\n",
343 pidInst, pfnCallback, afCmd, ulRes);
347 ERR("Reserved value not zero? What does this mean?\n");
348 /* trap this and no more until we know more */
349 return DMLERR_NO_ERROR;
352 /* grab enough heap for one control struct - not really necessary for re-initialise
353 * but allows us to use same validation routines */
354 pInstance = (WDML_INSTANCE*)HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_INSTANCE));
355 if (pInstance == NULL)
357 /* catastrophe !! warn user & abort */
358 ERR("Instance create failed - out of memory\n");
359 return DMLERR_SYS_ERROR;
361 pInstance->next = NULL;
362 pInstance->monitor = (afCmd | APPCLASS_MONITOR);
364 /* messy bit, spec implies that 'Client Only' can be set in 2 different ways, catch 1 here */
366 pInstance->clientOnly = afCmd & APPCMD_CLIENTONLY;
367 pInstance->instanceID = *pidInst; /* May need to add calling proc Id */
368 pInstance->threadID = GetCurrentThreadId();
369 pInstance->callback = *pfnCallback;
370 pInstance->unicode = bUnicode;
371 pInstance->win16 = b16;
372 pInstance->nodeList = NULL; /* node will be added later */
373 pInstance->monitorFlags = afCmd & MF_MASK;
374 pInstance->servers = NULL;
375 pInstance->convs[0] = NULL;
376 pInstance->convs[1] = NULL;
377 pInstance->links[0] = NULL;
378 pInstance->links[1] = NULL;
380 /* isolate CBF flags in one go, expect this will go the way of all attempts to be clever !! */
382 pInstance->CBFflags = afCmd^((afCmd&MF_MASK)|((afCmd&APPCMD_MASK)|(afCmd&APPCLASS_MASK)));
384 if (!pInstance->clientOnly)
387 /* Check for other way of setting Client-only !! */
389 pInstance->clientOnly =
390 (pInstance->CBFflags & CBF_FAIL_ALLSVRXACTIONS) == CBF_FAIL_ALLSVRXACTIONS;
393 TRACE("instance created - checking validity \n");
397 /* Initialisation of new Instance Identifier */
398 TRACE("new instance, callback %p flags %lX\n",pfnCallback,afCmd);
400 EnterCriticalSection(&WDML_CritSect);
402 if (WDML_InstanceList == NULL)
404 /* can't be another instance in this case, assign to the base pointer */
405 WDML_InstanceList = pInstance;
407 /* since first must force filter of XTYP_CONNECT and XTYP_WILDCONNECT for
409 * ------------------------------- NOTE NOTE NOTE --------------------------
411 * the manual is not clear if this condition
412 * applies to the first call to DdeInitialize from an application, or the
413 * first call for a given callback !!!
416 pInstance->CBFflags = pInstance->CBFflags|APPCMD_FILTERINITS;
417 TRACE("First application instance detected OK\n");
418 /* allocate new instance ID */
419 WDML_IncrementInstanceId(pInstance);
423 /* really need to chain the new one in to the latest here, but after checking conditions
424 * such as trying to start a conversation from an application trying to monitor */
425 reference_inst = WDML_InstanceList;
426 TRACE("Subsequent application instance - starting checks\n");
427 while (reference_inst->next != NULL)
430 * This set of tests will work if application uses same instance Id
431 * at application level once allocated - which is what manual implies
432 * should happen. If someone tries to be
433 * clever (lazy ?) it will fail to pick up that later calls are for
434 * the same application - should we trust them ?
436 if (pInstance->instanceID == reference_inst->instanceID)
438 /* Check 1 - must be same Client-only state */
440 if (pInstance->clientOnly != reference_inst->clientOnly)
442 ret = DMLERR_DLL_USAGE;
446 /* Check 2 - cannot use 'Monitor' with any non-monitor modes */
448 if (pInstance->monitor != reference_inst->monitor)
450 ret = DMLERR_INVALIDPARAMETER;
454 /* Check 3 - must supply different callback address */
456 if (pInstance->callback == reference_inst->callback)
458 ret = DMLERR_DLL_USAGE;
462 reference_inst = reference_inst->next;
464 /* All cleared, add to chain */
466 TRACE("Application Instance checks finished\n");
467 WDML_IncrementInstanceId(pInstance);
468 reference_inst->next = pInstance;
470 LeaveCriticalSection(&WDML_CritSect);
472 *pidInst = pInstance->instanceID;
474 /* for deadlock issues, windows must always be created when outside the critical section */
475 wndclass.cbSize = sizeof(wndclass);
477 wndclass.lpfnWndProc = WDML_EventProc;
478 wndclass.cbClsExtra = 0;
479 wndclass.cbWndExtra = sizeof(DWORD);
480 wndclass.hInstance = 0;
482 wndclass.hCursor = 0;
483 wndclass.hbrBackground = 0;
484 wndclass.lpszMenuName = NULL;
485 wndclass.lpszClassName = WDML_szEventClass;
486 wndclass.hIconSm = 0;
488 RegisterClassExA(&wndclass);
490 pInstance->hwndEvent = CreateWindowA(WDML_szEventClass, NULL,
491 WS_POPUP, 0, 0, 0, 0,
494 SetWindowLongA(pInstance->hwndEvent, GWL_WDML_INSTANCE, (DWORD)pInstance);
496 TRACE("New application instance processing finished OK\n");
500 /* Reinitialisation situation --- FIX */
501 TRACE("reinitialisation of (%p,%p,0x%lx,%ld): stub\n", pidInst, pfnCallback, afCmd, ulRes);
503 EnterCriticalSection(&WDML_CritSect);
505 if (WDML_InstanceList == NULL)
507 ret = DMLERR_DLL_USAGE;
510 HeapFree(GetProcessHeap(), 0, pInstance); /* finished - release heap space used as work store */
511 /* can't reinitialise if we have initialised nothing !! */
512 reference_inst = WDML_InstanceList;
513 /* must first check if we have been given a valid instance to re-initialise !! how do we do that ? */
515 * MS allows initialisation without specifying a callback, should we allow addition of the
516 * callback by a later call to initialise ? - if so this lot will have to change
518 while (reference_inst->next != NULL)
520 if (*pidInst == reference_inst->instanceID && pfnCallback == reference_inst->callback)
522 /* Check 1 - cannot change client-only mode if set via APPCMD_CLIENTONLY */
524 if (reference_inst->clientOnly)
526 if ((reference_inst->CBFflags & CBF_FAIL_ALLSVRXACTIONS) != CBF_FAIL_ALLSVRXACTIONS)
528 /* i.e. Was set to Client-only and through APPCMD_CLIENTONLY */
530 if (!(afCmd & APPCMD_CLIENTONLY))
532 ret = DMLERR_DLL_USAGE;
537 /* Check 2 - cannot change monitor modes */
539 if (pInstance->monitor != reference_inst->monitor)
541 ret = DMLERR_DLL_USAGE;
545 /* Check 3 - trying to set Client-only via APPCMD when not set so previously */
547 if ((afCmd&APPCMD_CLIENTONLY) && !reference_inst->clientOnly)
549 ret = DMLERR_DLL_USAGE;
554 reference_inst = reference_inst->next;
556 if (reference_inst->next == NULL)
558 /* Crazy situation - trying to re-initialize something that has not beeen initialized !!
560 * Manual does not say what we do, cannot return DMLERR_NOT_INITIALIZED so what ?
562 ret = DMLERR_INVALIDPARAMETER;
565 /* All checked - change relevant flags */
567 reference_inst->CBFflags = pInstance->CBFflags;
568 reference_inst->clientOnly = pInstance->clientOnly;
569 reference_inst->monitorFlags = pInstance->monitorFlags;
570 LeaveCriticalSection(&WDML_CritSect);
573 return DMLERR_NO_ERROR;
575 HeapFree(GetProcessHeap(), 0, pInstance);
576 LeaveCriticalSection(&WDML_CritSect);
580 /******************************************************************************
581 * DdeInitializeA (USER32.@)
583 UINT WINAPI DdeInitializeA(LPDWORD pidInst, PFNCALLBACK pfnCallback,
584 DWORD afCmd, DWORD ulRes)
586 return WDML_Initialize(pidInst, pfnCallback, afCmd, ulRes, FALSE, FALSE);
589 /******************************************************************************
590 * DdeInitializeW [USER32.@]
591 * Registers an application with the DDEML
594 * pidInst [I] Pointer to instance identifier
595 * pfnCallback [I] Pointer to callback function
596 * afCmd [I] Set of command and filter flags
600 * Success: DMLERR_NO_ERROR
601 * Failure: DMLERR_DLL_USAGE, DMLERR_INVALIDPARAMETER, DMLERR_SYS_ERROR
603 UINT WINAPI DdeInitializeW(LPDWORD pidInst, PFNCALLBACK pfnCallback,
604 DWORD afCmd, DWORD ulRes)
606 return WDML_Initialize(pidInst, pfnCallback, afCmd, ulRes, TRUE, FALSE);
609 /*****************************************************************
610 * DdeUninitialize [USER32.@] Frees DDEML resources
613 * idInst [I] Instance identifier
620 BOOL WINAPI DdeUninitialize(DWORD idInst)
622 /* Stage one - check if we have a handle for this instance
624 WDML_INSTANCE* pInstance;
625 WDML_INSTANCE* reference_inst;
627 WDML_CONV* pConvNext;
629 EnterCriticalSection(&WDML_CritSect);
631 /* First check instance
633 pInstance = WDML_GetInstance(idInst);
634 if (pInstance == NULL)
636 LeaveCriticalSection(&WDML_CritSect);
638 * Needs something here to record NOT_INITIALIZED ready for DdeGetLastError
643 /* first terminate all conversations client side
644 * this shall close existing links...
646 for (pConv = pInstance->convs[WDML_CLIENT_SIDE]; pConv != NULL; pConv = pConvNext)
648 pConvNext = pConv->next;
649 DdeDisconnect((HCONV)pConv);
651 if (pInstance->convs[WDML_CLIENT_SIDE])
652 FIXME("still pending conversations\n");
654 /* then unregister all known service names */
655 DdeNameService(idInst, 0, 0, DNS_UNREGISTER);
657 /* Free the nodes that were not freed by this instance
658 * and remove the nodes from the list of HSZ nodes.
660 WDML_FreeAllHSZ(pInstance);
662 DestroyWindow(pInstance->hwndEvent);
664 /* OK now delete the instance handle itself */
666 if (WDML_InstanceList == pInstance)
668 /* special case - the first/only entry
670 WDML_InstanceList = pInstance->next;
676 reference_inst = WDML_InstanceList;
677 while (reference_inst->next != pInstance)
679 reference_inst = pInstance->next;
681 reference_inst->next = pInstance->next;
683 /* leave crit sect and release the heap entry
685 HeapFree(GetProcessHeap(), 0, pInstance);
686 LeaveCriticalSection(&WDML_CritSect);
690 /******************************************************************
691 * WDML_NotifyThreadExit
695 void WDML_NotifyThreadDetach(void)
697 WDML_INSTANCE* pInstance;
699 DWORD tid = GetCurrentThreadId();
701 EnterCriticalSection(&WDML_CritSect);
702 for (pInstance = WDML_InstanceList; pInstance != NULL; pInstance = next)
704 next = pInstance->next;
705 if (pInstance->threadID == tid)
707 DdeUninitialize(pInstance->instanceID);
710 LeaveCriticalSection(&WDML_CritSect);
713 /******************************************************************
714 * WDML_InvokeCallback
718 HDDEDATA WDML_InvokeCallback(WDML_INSTANCE* pInstance, UINT uType, UINT uFmt, HCONV hConv,
719 HSZ hsz1, HSZ hsz2, HDDEDATA hdata,
720 DWORD dwData1, DWORD dwData2)
724 if (pInstance == NULL)
726 TRACE("invoking CB%d[%08lx] (%u %u %08lx 0x%x 0x%x %u %lu %lu)\n",
727 pInstance->win16 ? 16 : 32, (DWORD)pInstance->callback, uType, uFmt,
728 (DWORD)hConv, hsz1, hsz2, hdata, dwData1, dwData2);
729 if (pInstance->win16)
731 ret = WDML_InvokeCallback16(pInstance->callback, uType, uFmt, hConv,
732 hsz1, hsz2, hdata, dwData1, dwData2);
736 ret = pInstance->callback(uType, uFmt, hConv, hsz1, hsz2, hdata, dwData1, dwData2);
738 TRACE("done => %08lx\n", (DWORD)ret);
742 /*****************************************************************************
745 * generic routine to return a pointer to the relevant DDE_HANDLE_ENTRY
746 * for an instance Id, or NULL if the entry does not exist
749 WDML_INSTANCE* WDML_GetInstance(DWORD instId)
751 WDML_INSTANCE* pInstance;
753 for (pInstance = WDML_InstanceList; pInstance != NULL; pInstance = pInstance->next)
755 if (pInstance->instanceID == instId)
757 if (GetCurrentThreadId() != pInstance->threadID)
759 FIXME("Tried to get instance from wrong thread\n");
765 TRACE("Instance entry missing\n");
769 /******************************************************************
770 * WDML_GetInstanceFromWnd
774 WDML_INSTANCE* WDML_GetInstanceFromWnd(HWND hWnd)
776 return (WDML_INSTANCE*)GetWindowLongA(hWnd, GWL_WDML_INSTANCE);
779 /******************************************************************************
780 * DdeGetLastError [USER32.@] Gets most recent error code
783 * idInst [I] Instance identifier
788 UINT WINAPI DdeGetLastError(DWORD idInst)
791 WDML_INSTANCE* pInstance;
793 FIXME("(%ld): error reporting is weakly implemented\n", idInst);
795 EnterCriticalSection(&WDML_CritSect);
797 /* First check instance
799 pInstance = WDML_GetInstance(idInst);
800 if (pInstance == NULL)
802 error_code = DMLERR_DLL_NOT_INITIALIZED;
806 error_code = pInstance->lastError;
807 pInstance->lastError = 0;
810 LeaveCriticalSection(&WDML_CritSect);
814 /* ================================================================
818 * ================================================================ */
821 /******************************************************************
826 static HSZNode* WDML_FindNode(WDML_INSTANCE* pInstance, HSZ hsz)
830 if (pInstance == NULL) return NULL;
832 for (pNode = pInstance->nodeList; pNode != NULL; pNode = pNode->next)
834 if (pNode->hsz == hsz) break;
836 if (!pNode) WARN("HSZ 0x%x not found\n", hsz);
840 /******************************************************************
841 * WDML_MakeAtomFromHsz
843 * Creates a global atom from an existing HSZ
844 * Generally used before sending an HSZ as an atom to a remote app
846 ATOM WDML_MakeAtomFromHsz(HSZ hsz)
848 WCHAR nameBuffer[MAX_BUFFER_LEN];
850 if (GetAtomNameW((ATOM)hsz, nameBuffer, MAX_BUFFER_LEN))
851 return GlobalAddAtomW(nameBuffer);
852 WARN("HSZ 0x%xnot found\n", hsz);
856 /******************************************************************
857 * WDML_MakeHszFromAtom
859 * Creates a HSZ from an existing global atom
860 * Generally used while receiving a global atom and transforming it
863 HSZ WDML_MakeHszFromAtom(WDML_INSTANCE* pInstance, ATOM atom)
865 WCHAR nameBuffer[MAX_BUFFER_LEN];
867 GlobalGetAtomNameW(atom, nameBuffer, MAX_BUFFER_LEN);
868 return DdeCreateStringHandleW(pInstance->instanceID, nameBuffer, CP_WINUNICODE);
871 /******************************************************************
876 BOOL WDML_IncHSZ(WDML_INSTANCE* pInstance, HSZ hsz)
880 pNode = WDML_FindNode(pInstance, hsz);
881 if (!pNode) return FALSE;
887 /******************************************************************************
888 * WDML_DecHSZ (INTERNAL)
890 * Decrease the ref count of an HSZ. If it reaches 0, the node is removed from the list
892 * Returns -1 is the HSZ isn't found, otherwise it's the current (after --) of the ref count
894 BOOL WDML_DecHSZ(WDML_INSTANCE* pInstance, HSZ hsz)
896 HSZNode* pPrev = NULL;
899 for (pCurrent = pInstance->nodeList; pCurrent != NULL; pCurrent = (pPrev = pCurrent)->next)
901 /* If we found the node we were looking for and its ref count is one,
904 if (pCurrent->hsz == hsz)
906 if (--pCurrent->refCount == 0)
908 if (pCurrent == pInstance->nodeList)
910 pInstance->nodeList = pCurrent->next;
914 pPrev->next = pCurrent->next;
916 HeapFree(GetProcessHeap(), 0, pCurrent);
922 WARN("HSZ 0x%xnot found\n", hsz);
927 /******************************************************************************
928 * WDML_FreeAllHSZ (INTERNAL)
930 * Frees up all the strings still allocated in the list and
931 * remove all the nodes from the list of HSZ nodes.
933 void WDML_FreeAllHSZ(WDML_INSTANCE* pInstance)
935 /* Free any strings created in this instance.
937 while (pInstance->nodeList != NULL)
939 DdeFreeStringHandle(pInstance->instanceID, pInstance->nodeList->hsz);
943 /******************************************************************************
944 * InsertHSZNode (INTERNAL)
946 * Insert a node to the head of the list.
948 static void WDML_InsertHSZNode(WDML_INSTANCE* pInstance, HSZ hsz)
952 HSZNode* pNew = NULL;
953 /* Create a new node for this HSZ.
955 pNew = (HSZNode*)HeapAlloc(GetProcessHeap(), 0, sizeof(HSZNode));
959 pNew->next = pInstance->nodeList;
961 pInstance->nodeList = pNew;
965 ERR("Primary HSZ Node allocation failed - out of memory\n");
970 /******************************************************************
975 static int WDML_QueryString(WDML_INSTANCE* pInstance, HSZ hsz, LPVOID ptr, DWORD cchMax,
978 WCHAR pString[MAX_BUFFER_LEN];
980 /* If psz is null, we have to return only the length
986 cchMax = MAX_BUFFER_LEN;
992 ret = GetAtomNameA(hsz, ptr, cchMax);
995 ret = GetAtomNameW(hsz, ptr, cchMax);
997 ERR("Unknown code page %d\n", codepage);
1003 /*****************************************************************
1004 * DdeQueryStringA [USER32.@]
1006 DWORD WINAPI DdeQueryStringA(DWORD idInst, HSZ hsz, LPSTR psz, DWORD cchMax, INT iCodePage)
1009 WDML_INSTANCE* pInstance;
1011 TRACE("(%ld, 0x%x, %p, %ld, %d)\n", idInst, hsz, psz, cchMax, iCodePage);
1013 EnterCriticalSection(&WDML_CritSect);
1015 /* First check instance
1017 pInstance = WDML_GetInstance(idInst);
1018 if (pInstance != NULL)
1020 if (iCodePage == 0) iCodePage = CP_WINANSI;
1021 ret = WDML_QueryString(pInstance, hsz, psz, cchMax, iCodePage);
1023 LeaveCriticalSection(&WDML_CritSect);
1025 TRACE("returning %s\n", debugstr_a(psz));
1029 /*****************************************************************
1030 * DdeQueryStringW [USER32.@]
1033 DWORD WINAPI DdeQueryStringW(DWORD idInst, HSZ hsz, LPWSTR psz, DWORD cchMax, INT iCodePage)
1036 WDML_INSTANCE* pInstance;
1038 TRACE("(%ld, 0x%x, %p, %ld, %d)\n",
1039 idInst, hsz, psz, cchMax, iCodePage);
1041 EnterCriticalSection(&WDML_CritSect);
1043 /* First check instance
1045 pInstance = WDML_GetInstance(idInst);
1046 if (pInstance != NULL)
1048 if (iCodePage == 0) iCodePage = CP_WINUNICODE;
1049 ret = WDML_QueryString(pInstance, hsz, psz, cchMax, iCodePage);
1051 LeaveCriticalSection(&WDML_CritSect);
1053 TRACE("returning %s\n", debugstr_w(psz));
1057 /******************************************************************
1062 static HSZ WDML_CreateString(WDML_INSTANCE* pInstance, LPCVOID ptr, int codepage)
1069 hsz = AddAtomA(ptr);
1070 TRACE("added atom %s with HSZ 0x%x, \n", debugstr_a(ptr), hsz);
1073 hsz = AddAtomW(ptr);
1074 TRACE("added atom %s with HSZ 0x%x, \n", debugstr_w(ptr), hsz);
1077 ERR("Unknown code page %d\n", codepage);
1080 WDML_InsertHSZNode(pInstance, hsz);
1084 /*****************************************************************
1085 * DdeCreateStringHandleA [USER32.@]
1088 * Success: String handle
1091 HSZ WINAPI DdeCreateStringHandleA(DWORD idInst, LPCSTR psz, INT codepage)
1094 WDML_INSTANCE* pInstance;
1096 TRACE("(%ld,%p,%d)\n", idInst, psz, codepage);
1098 EnterCriticalSection(&WDML_CritSect);
1100 pInstance = WDML_GetInstance(idInst);
1103 if (codepage == 0) codepage = CP_WINANSI;
1104 hsz = WDML_CreateString(pInstance, psz, codepage);
1107 LeaveCriticalSection(&WDML_CritSect);
1112 /******************************************************************************
1113 * DdeCreateStringHandleW [USER32.@] Creates handle to identify string
1116 * idInst [I] Instance identifier
1117 * psz [I] Pointer to string
1118 * codepage [I] Code page identifier
1120 * Success: String handle
1123 HSZ WINAPI DdeCreateStringHandleW(DWORD idInst, LPCWSTR psz, INT codepage)
1125 WDML_INSTANCE* pInstance;
1128 TRACE("(%ld,%p,%d)\n", idInst, psz, codepage);
1130 EnterCriticalSection(&WDML_CritSect);
1132 pInstance = WDML_GetInstance(idInst);
1135 if (codepage == 0) codepage = CP_WINUNICODE;
1136 hsz = WDML_CreateString(pInstance, psz, codepage);
1138 LeaveCriticalSection(&WDML_CritSect);
1143 /*****************************************************************
1144 * DdeFreeStringHandle (USER32.@)
1145 * RETURNS: success: nonzero
1148 BOOL WINAPI DdeFreeStringHandle(DWORD idInst, HSZ hsz)
1150 WDML_INSTANCE* pInstance;
1153 TRACE("(%ld,0x%x): \n", idInst, hsz);
1155 EnterCriticalSection(&WDML_CritSect);
1157 /* First check instance
1159 pInstance = WDML_GetInstance(idInst);
1161 ret = WDML_DecHSZ(pInstance, hsz);
1163 LeaveCriticalSection(&WDML_CritSect);
1168 /*****************************************************************
1169 * DdeKeepStringHandle (USER32.@)
1171 * RETURNS: success: nonzero
1174 BOOL WINAPI DdeKeepStringHandle(DWORD idInst, HSZ hsz)
1176 WDML_INSTANCE* pInstance;
1179 TRACE("(%ld,0x%x): \n", idInst, hsz);
1181 EnterCriticalSection(&WDML_CritSect);
1183 /* First check instance
1185 pInstance = WDML_GetInstance(idInst);
1187 ret = WDML_IncHSZ(pInstance, hsz);
1189 LeaveCriticalSection(&WDML_CritSect);
1193 /*****************************************************************
1194 * DdeCmpStringHandles (USER32.@)
1196 * Compares the value of two string handles. This comparison is
1197 * not case sensitive.
1200 * -1 The value of hsz1 is zero or less than hsz2
1201 * 0 The values of hsz 1 and 2 are the same or both zero.
1202 * 1 The value of hsz2 is zero of less than hsz1
1204 INT WINAPI DdeCmpStringHandles(HSZ hsz1, HSZ hsz2)
1206 WCHAR psz1[MAX_BUFFER_LEN];
1207 WCHAR psz2[MAX_BUFFER_LEN];
1211 ret1 = GetAtomNameW(hsz1, psz1, MAX_BUFFER_LEN);
1212 ret2 = GetAtomNameW(hsz2, psz2, MAX_BUFFER_LEN);
1214 TRACE("(%x<%s> %x<%s>);\n", hsz1, debugstr_w(psz1), hsz2, debugstr_w(psz2));
1216 /* Make sure we found both strings. */
1217 if (ret1 == 0 && ret2 == 0)
1219 /* If both are not found, return both "zero strings". */
1224 /* If hsz1 is a not found, return hsz1 is "zero string". */
1229 /* If hsz2 is a not found, return hsz2 is "zero string". */
1234 /* Compare the two strings we got (case insensitive). */
1235 ret = lstrcmpiW(psz1, psz2);
1236 /* Since strcmp returns any number smaller than
1237 * 0 when the first string is found to be less than
1238 * the second one we must make sure we are returning
1239 * the proper values.
1254 /* ================================================================
1256 * Data handle management
1258 * ================================================================ */
1260 /*****************************************************************
1261 * DdeCreateDataHandle (USER32.@)
1263 HDDEDATA WINAPI DdeCreateDataHandle(DWORD idInst, LPBYTE pSrc, DWORD cb,
1264 DWORD cbOff, HSZ hszItem, UINT wFmt,
1268 For now, we ignore idInst, hszItem, wFmt, and afCmd.
1269 The purpose of these arguments still need to be investigated.
1274 DDE_DATAHANDLE_HEAD* pDdh;
1276 TRACE("(%ld,%p,%ld,%ld,0x%lx,%d,%d): semi-stub\n",
1277 idInst,pSrc,cb,cbOff,(DWORD)hszItem,wFmt,afCmd);
1279 /* we use the first 4 bytes to store the size */
1280 if (!(hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, cb + sizeof(DDE_DATAHANDLE_HEAD))))
1282 ERR("GlobalAlloc failed\n");
1286 pDdh = (DDE_DATAHANDLE_HEAD*)GlobalLock(hMem);
1287 pDdh->cfFormat = wFmt;
1289 pByte = (LPBYTE)(pDdh + 1);
1292 memcpy(pByte, pSrc + cbOff, cb);
1296 return (HDDEDATA)hMem;
1299 /*****************************************************************
1301 * DdeAddData (USER32.@)
1303 HDDEDATA WINAPI DdeAddData(HDDEDATA hData, LPBYTE pSrc, DWORD cb, DWORD cbOff)
1305 DWORD old_sz, new_sz;
1308 pDst = DdeAccessData(hData, &old_sz);
1309 if (!pDst) return 0;
1311 new_sz = cb + cbOff;
1312 if (new_sz > old_sz)
1314 DdeUnaccessData(hData);
1315 hData = GlobalReAlloc((HGLOBAL)hData, new_sz + sizeof(DDE_DATAHANDLE_HEAD),
1316 GMEM_MOVEABLE | GMEM_DDESHARE);
1317 pDst = DdeAccessData(hData, &old_sz);
1320 if (!pDst) return 0;
1322 memcpy(pDst + cbOff, pSrc, cb);
1323 DdeUnaccessData(hData);
1327 /******************************************************************************
1328 * DdeGetData [USER32.@] Copies data from DDE object to local buffer
1332 * hData [I] Handle to DDE object
1333 * pDst [I] Pointer to destination buffer
1334 * cbMax [I] Amount of data to copy
1335 * cbOff [I] Offset to beginning of data
1338 * Size of memory object associated with handle
1340 DWORD WINAPI DdeGetData(HDDEDATA hData, LPBYTE pDst, DWORD cbMax, DWORD cbOff)
1342 DWORD dwSize, dwRet;
1345 TRACE("(%08lx,%p,%ld,%ld)\n",(DWORD)hData,pDst,cbMax,cbOff);
1347 pByte = DdeAccessData(hData, &dwSize);
1351 if (cbOff + cbMax < dwSize)
1355 else if (cbOff < dwSize)
1357 dwRet = dwSize - cbOff;
1363 if (pDst && dwRet != 0)
1365 memcpy(pDst, pByte + cbOff, dwRet);
1367 DdeUnaccessData(hData);
1376 /*****************************************************************
1377 * DdeAccessData (USER32.@)
1379 LPBYTE WINAPI DdeAccessData(HDDEDATA hData, LPDWORD pcbDataSize)
1381 HGLOBAL hMem = (HGLOBAL)hData;
1382 DDE_DATAHANDLE_HEAD* pDdh;
1384 TRACE("(%08lx,%p)\n", (DWORD)hData, pcbDataSize);
1386 pDdh = (DDE_DATAHANDLE_HEAD*)GlobalLock(hMem);
1389 ERR("Failed on GlobalLock(%04x)\n", hMem);
1393 if (pcbDataSize != NULL)
1395 *pcbDataSize = GlobalSize(hMem) - sizeof(DDE_DATAHANDLE_HEAD);
1398 return (LPBYTE)(pDdh + 1);
1401 /*****************************************************************
1402 * DdeUnaccessData (USER32.@)
1404 BOOL WINAPI DdeUnaccessData(HDDEDATA hData)
1406 HGLOBAL hMem = (HGLOBAL)hData;
1408 TRACE("(0x%lx)\n", (DWORD)hData);
1415 /*****************************************************************
1416 * DdeFreeDataHandle (USER32.@)
1418 BOOL WINAPI DdeFreeDataHandle(HDDEDATA hData)
1420 return GlobalFree((HGLOBAL)hData) == 0;
1423 /* ================================================================
1425 * Global <=> Data handle management
1427 * ================================================================ */
1429 /* Note: we use a DDEDATA, but layout of DDEDATA, DDEADVISE and DDEPOKE structures is similar:
1431 * (bytes) (bits) comment
1432 * 0 16 bit fields for options (release, ackreq, response...)
1433 * 2 16 clipboard format
1434 * 4 ? data to be used
1436 HDDEDATA WDML_Global2DataHandle(HGLOBAL hMem, WINE_DDEHEAD* p)
1443 pDd = GlobalLock(hMem);
1446 if (p) memcpy(p, pDd, sizeof(WINE_DDEHEAD));
1447 ret = DdeCreateDataHandle(0, pDd->Value,
1448 GlobalSize(hMem) - sizeof(WINE_DDEHEAD),
1449 0, 0, pDd->cfFormat, 0);
1456 /******************************************************************
1457 * WDML_DataHandle2Global
1461 HGLOBAL WDML_DataHandle2Global(HDDEDATA hDdeData, BOOL fResponse, BOOL fRelease,
1462 BOOL fDeferUpd, BOOL fAckReq)
1464 DDE_DATAHANDLE_HEAD* pDdh;
1468 dwSize = GlobalSize(hDdeData) - sizeof(DDE_DATAHANDLE_HEAD);
1469 pDdh = (DDE_DATAHANDLE_HEAD*)GlobalLock(hDdeData);
1472 hMem = GlobalAlloc(sizeof(WINE_DDEHEAD) + dwSize, GMEM_MOVEABLE | GMEM_DDESHARE);
1477 wdh = GlobalLock(hMem);
1480 wdh->fResponse = fResponse;
1481 wdh->fRelease = fRelease;
1482 wdh->fDeferUpd = fDeferUpd;
1483 wdh->fAckReq = fAckReq;
1484 wdh->cfFormat = pDdh->cfFormat;
1485 memcpy(wdh + 1, pDdh + 1, dwSize);
1489 GlobalUnlock(hDdeData);
1495 /* ================================================================
1499 * ================================================================ */
1501 /******************************************************************
1506 WDML_SERVER* WDML_AddServer(WDML_INSTANCE* pInstance, HSZ hszService, HSZ hszTopic)
1508 WDML_SERVER* pServer;
1512 pServer = (WDML_SERVER*)HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_SERVER));
1513 if (pServer == NULL) return NULL;
1515 WDML_IncHSZ(pInstance, pServer->hszService = hszService);
1517 DdeQueryStringA(pInstance->instanceID, hszService, buf1, sizeof(buf1), CP_WINANSI);
1518 snprintf(buf2, sizeof(buf2), "%s(0x%08lx)", buf1, GetCurrentProcessId());
1519 pServer->hszServiceSpec = DdeCreateStringHandleA(pInstance->instanceID, buf2, CP_WINANSI);
1521 pServer->atomService = WDML_MakeAtomFromHsz(pServer->hszService);
1522 pServer->atomServiceSpec = WDML_MakeAtomFromHsz(pServer->hszServiceSpec);
1524 pServer->filterOn = TRUE;
1526 pServer->next = pInstance->servers;
1527 pInstance->servers = pServer;
1531 /******************************************************************
1536 void WDML_RemoveServer(WDML_INSTANCE* pInstance, HSZ hszService, HSZ hszTopic)
1538 WDML_SERVER* pPrev = NULL;
1539 WDML_SERVER* pServer = NULL;
1541 WDML_CONV* pConvNext;
1543 pServer = pInstance->servers;
1545 while (pServer != NULL)
1547 if (DdeCmpStringHandles(pServer->hszService, hszService) == 0)
1549 WDML_BroadcastDDEWindows(WDML_szEventClass, WM_WDML_UNREGISTER,
1550 pServer->atomService, pServer->atomServiceSpec);
1551 /* terminate all conversations for given topic */
1552 for (pConv = pInstance->convs[WDML_SERVER_SIDE]; pConv != NULL; pConv = pConvNext)
1554 pConvNext = pConv->next;
1555 if (DdeCmpStringHandles(pConv->hszService, hszService) == 0)
1557 WDML_RemoveConv(pConv, WDML_SERVER_SIDE);
1558 /* don't care about return code (whether client window is present or not) */
1559 PostMessageA(pConv->hwndClient, WM_DDE_TERMINATE, (WPARAM)pConv->hwndServer, 0L);
1562 if (pServer == pInstance->servers)
1564 pInstance->servers = pServer->next;
1568 pPrev->next = pServer->next;
1571 DestroyWindow(pServer->hwndServer);
1572 WDML_DecHSZ(pInstance, pServer->hszServiceSpec);
1573 WDML_DecHSZ(pInstance, pServer->hszService);
1575 GlobalDeleteAtom(pServer->atomService);
1576 GlobalDeleteAtom(pServer->atomServiceSpec);
1578 HeapFree(GetProcessHeap(), 0, pServer);
1583 pServer = pServer->next;
1587 /*****************************************************************************
1590 * generic routine to return a pointer to the relevant ServiceNode
1591 * for a given service name, or NULL if the entry does not exist
1594 WDML_SERVER* WDML_FindServer(WDML_INSTANCE* pInstance, HSZ hszService, HSZ hszTopic)
1596 WDML_SERVER* pServer;
1598 for (pServer = pInstance->servers; pServer != NULL; pServer = pServer->next)
1600 if (hszService == pServer->hszService)
1605 TRACE("Service name missing\n");
1609 /* ================================================================
1611 * Conversation management
1613 * ================================================================ */
1615 /******************************************************************
1620 WDML_CONV* WDML_AddConv(WDML_INSTANCE* pInstance, WDML_SIDE side,
1621 HSZ hszService, HSZ hszTopic, HWND hwndClient, HWND hwndServer)
1625 /* no converstation yet, add it */
1626 pConv = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_CONV));
1627 if (!pConv) return NULL;
1629 pConv->instance = pInstance;
1630 WDML_IncHSZ(pInstance, pConv->hszService = hszService);
1631 WDML_IncHSZ(pInstance, pConv->hszTopic = hszTopic);
1632 pConv->hwndServer = hwndServer;
1633 pConv->hwndClient = hwndClient;
1634 pConv->transactions = NULL;
1636 pConv->wStatus = (side == WDML_CLIENT_SIDE) ? ST_CLIENT : 0L;
1637 /* check if both side of the conversation are of the same instance */
1638 if (GetWindowThreadProcessId(hwndClient, NULL) == GetWindowThreadProcessId(hwndServer, NULL) &&
1639 WDML_GetInstanceFromWnd(hwndClient) == WDML_GetInstanceFromWnd(hwndServer))
1641 pConv->wStatus |= ST_ISSELF;
1643 pConv->wConvst = XST_NULL;
1645 pConv->next = pInstance->convs[side];
1646 pInstance->convs[side] = pConv;
1651 /******************************************************************
1656 WDML_CONV* WDML_FindConv(WDML_INSTANCE* pInstance, WDML_SIDE side,
1657 HSZ hszService, HSZ hszTopic)
1659 WDML_CONV* pCurrent = NULL;
1661 for (pCurrent = pInstance->convs[side]; pCurrent != NULL; pCurrent = pCurrent->next)
1663 if (DdeCmpStringHandles(pCurrent->hszService, hszService) == 0 &&
1664 DdeCmpStringHandles(pCurrent->hszTopic, hszTopic) == 0)
1673 /******************************************************************
1678 void WDML_RemoveConv(WDML_CONV* pRef, WDML_SIDE side)
1680 WDML_CONV* pPrev = NULL;
1681 WDML_CONV* pCurrent;
1683 WDML_XACT* pXActNext;
1689 /* remove any pending transaction */
1690 for (pXAct = pRef->transactions; pXAct != NULL; pXAct = pXActNext)
1692 pXActNext = pXAct->next;
1693 WDML_FreeTransaction(pRef->instance, pXAct, TRUE);
1696 WDML_RemoveAllLinks(pRef->instance, pRef, side);
1698 /* FIXME: should we keep the window around ? it seems so (at least on client side
1699 * to let QueryConvInfo work after conv termination, but also to implement
1702 /* destroy conversation window, but first remove pConv from hWnd.
1703 * this would help the wndProc do appropriate handling upon a WM_DESTROY message
1705 hWnd = (side == WDML_CLIENT_SIDE) ? pRef->hwndClient : pRef->hwndServer;
1706 SetWindowLongA(hWnd, GWL_WDML_CONVERSATION, 0);
1708 DestroyWindow((side == WDML_CLIENT_SIDE) ? pRef->hwndClient : pRef->hwndServer);
1710 WDML_DecHSZ(pRef->instance, pRef->hszService);
1711 WDML_DecHSZ(pRef->instance, pRef->hszTopic);
1713 for (pCurrent = pRef->instance->convs[side]; pCurrent != NULL; pCurrent = (pPrev = pCurrent)->next)
1715 if (pCurrent == pRef)
1717 if (pCurrent == pRef->instance->convs[side])
1719 pRef->instance->convs[side] = pCurrent->next;
1723 pPrev->next = pCurrent->next;
1726 HeapFree(GetProcessHeap(), 0, pCurrent);
1732 /*****************************************************************
1733 * DdeEnableCallback (USER32.@)
1735 BOOL WINAPI DdeEnableCallback(DWORD idInst, HCONV hConv, UINT wCmd)
1737 FIXME("(%ld, 0x%x, %d) stub\n", idInst, hConv, wCmd);
1742 /******************************************************************
1747 WDML_CONV* WDML_GetConv(HCONV hConv, BOOL checkConnected)
1749 WDML_CONV* pConv = (WDML_CONV*)hConv;
1751 /* FIXME: should do better checking */
1753 if (checkConnected && !(pConv->wStatus & ST_CONNECTED))
1755 FIXME("found conv but ain't connected\n");
1758 if (GetCurrentThreadId() != pConv->instance->threadID)
1760 FIXME("wrong thread ID\n");
1767 /******************************************************************
1768 * WDML_GetConvFromWnd
1772 WDML_CONV* WDML_GetConvFromWnd(HWND hWnd)
1774 return (WDML_CONV*)GetWindowLongA(hWnd, GWL_WDML_CONVERSATION);
1777 /******************************************************************
1782 LPARAM WDML_PostAck(WDML_CONV* pConv, WDML_SIDE side, WORD appRetCode,
1783 BOOL fBusy, BOOL fAck, ATOM atom, LPARAM lParam, UINT oldMsg)
1788 if (side == WDML_SERVER_SIDE)
1790 from = pConv->hwndServer;
1791 to = pConv->hwndClient;
1795 to = pConv->hwndServer;
1796 from = pConv->hwndClient;
1799 ddeAck.bAppReturnCode = appRetCode;
1800 ddeAck.reserved = 0;
1801 ddeAck.fBusy = fBusy;
1804 TRACE("Posting a %s ack\n", ddeAck.fAck ? "positive" : "negative");
1807 PostMessageA(to, WM_DDE_ACK, (WPARAM)from,
1808 ReuseDDElParam(lParam, oldMsg, WM_DDE_ACK, *(WORD*)&ddeAck, atom));
1812 lParam = PackDDElParam(WM_DDE_ACK, *(WORD*)&ddeAck, atom);
1813 PostMessageA(to, WM_DDE_ACK, (WPARAM)from, lParam);
1818 /*****************************************************************
1819 * DdeSetUserHandle (USER32.@)
1821 BOOL WINAPI DdeSetUserHandle(HCONV hConv, DWORD id, DWORD hUser)
1826 EnterCriticalSection(&WDML_CritSect);
1828 pConv = WDML_GetConv(hConv, FALSE);
1836 pConv->hUser = hUser;
1842 pXAct = WDML_FindTransaction(pConv, id);
1845 pXAct->hUser = hUser;
1849 pConv->instance->lastError = DMLERR_UNFOUND_QUEUE_ID;
1854 LeaveCriticalSection(&WDML_CritSect);
1858 /******************************************************************
1859 * WDML_GetLocalConvInfo
1863 static BOOL WDML_GetLocalConvInfo(WDML_CONV* pConv, CONVINFO* ci, DWORD id)
1869 ci->hConvPartner = (pConv->wStatus & ST_ISLOCAL) ? (HCONV)((DWORD)pConv | 1) : 0;
1870 ci->hszSvcPartner = pConv->hszService;
1871 ci->hszServiceReq = pConv->hszService; /* FIXME: they shouldn't be the same, should they ? */
1872 ci->hszTopic = pConv->hszTopic;
1873 ci->wStatus = pConv->wStatus;
1875 side = (pConv->wStatus & ST_CLIENT) ? WDML_CLIENT_SIDE : WDML_SERVER_SIDE;
1877 for (pLink = pConv->instance->links[side]; pLink != NULL; pLink = pLink->next)
1879 if (pLink->hConv == (HCONV)pConv)
1881 ci->wStatus |= ST_ADVISE;
1886 /* FIXME: non handled status flags:
1892 ci->wConvst = pConv->wConvst; /* FIXME */
1894 ci->wLastError = 0; /* FIXME: note it's not the instance last error */
1896 ci->ConvCtxt = pConv->convContext;
1897 if (ci->wStatus & ST_CLIENT)
1899 ci->hwnd = pConv->hwndClient;
1900 ci->hwndPartner = pConv->hwndServer;
1904 ci->hwnd = pConv->hwndServer;
1905 ci->hwndPartner = pConv->hwndClient;
1909 ci->hUser = pConv->hUser;
1918 pXAct = WDML_FindTransaction(pConv, id);
1921 ci->hUser = pXAct->hUser;
1922 ci->hszItem = pXAct->hszItem;
1923 ci->wFmt = pXAct->wFmt;
1924 ci->wType = pXAct->wType;
1929 pConv->instance->lastError = DMLERR_UNFOUND_QUEUE_ID;
1935 /******************************************************************
1936 * DdeQueryConvInfo (USER32.@)
1939 UINT WINAPI DdeQueryConvInfo(HCONV hConv, DWORD id, LPCONVINFO lpConvInfo)
1941 UINT ret = lpConvInfo->cb;
1945 EnterCriticalSection(&WDML_CritSect);
1947 pConv = WDML_GetConv(hConv, FALSE);
1948 if (pConv != NULL && !WDML_GetLocalConvInfo(pConv, &ci, id))
1954 pConv = WDML_GetConv(hConv & ~1, FALSE);
1957 FIXME("Request on remote conversation information is not implemented yet\n");
1961 LeaveCriticalSection(&WDML_CritSect);
1963 memcpy(lpConvInfo, &ci, min(lpConvInfo->cb, sizeof(ci)));
1967 /* ================================================================
1969 * Link (hot & warm) management
1971 * ================================================================ */
1973 /******************************************************************
1978 void WDML_AddLink(WDML_INSTANCE* pInstance, HCONV hConv, WDML_SIDE side,
1979 UINT wType, HSZ hszItem, UINT wFmt)
1983 pLink = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_LINK));
1990 pLink->hConv = hConv;
1991 pLink->transactionType = wType;
1992 WDML_IncHSZ(pInstance, pLink->hszItem = hszItem);
1994 pLink->hDdeData = 0;
1995 pLink->next = pInstance->links[side];
1996 pInstance->links[side] = pLink;
1999 /******************************************************************
2004 void WDML_RemoveLink(WDML_INSTANCE* pInstance, HCONV hConv, WDML_SIDE side,
2005 HSZ hszItem, UINT uFmt)
2007 WDML_LINK* pPrev = NULL;
2008 WDML_LINK* pCurrent = NULL;
2010 pCurrent = pInstance->links[side];
2012 while (pCurrent != NULL)
2014 if (pCurrent->hConv == hConv &&
2015 DdeCmpStringHandles(pCurrent->hszItem, hszItem) == 0 &&
2016 pCurrent->uFmt == uFmt)
2018 if (pCurrent == pInstance->links[side])
2020 pInstance->links[side] = pCurrent->next;
2024 pPrev->next = pCurrent->next;
2027 if (pCurrent->hDdeData)
2029 DdeFreeDataHandle(pCurrent->hDdeData);
2032 WDML_DecHSZ(pInstance, pCurrent->hszItem);
2033 HeapFree(GetProcessHeap(), 0, pCurrent);
2038 pCurrent = pCurrent->next;
2042 /* this function is called to remove all links related to the conv.
2043 It should be called from both client and server when terminating
2046 /******************************************************************
2047 * WDML_RemoveAllLinks
2051 void WDML_RemoveAllLinks(WDML_INSTANCE* pInstance, WDML_CONV* pConv, WDML_SIDE side)
2053 WDML_LINK* pPrev = NULL;
2054 WDML_LINK* pCurrent = NULL;
2055 WDML_LINK* pNext = NULL;
2057 pCurrent = pInstance->links[side];
2059 while (pCurrent != NULL)
2061 if (pCurrent->hConv == (HCONV)pConv)
2063 if (pCurrent == pInstance->links[side])
2065 pInstance->links[side] = pCurrent->next;
2066 pNext = pCurrent->next;
2070 pPrev->next = pCurrent->next;
2071 pNext = pCurrent->next;
2074 if (pCurrent->hDdeData)
2076 DdeFreeDataHandle(pCurrent->hDdeData);
2078 WDML_DecHSZ(pInstance, pCurrent->hszItem);
2080 HeapFree(GetProcessHeap(), 0, pCurrent);
2087 pCurrent = pCurrent->next;
2096 /******************************************************************
2101 WDML_LINK* WDML_FindLink(WDML_INSTANCE* pInstance, HCONV hConv, WDML_SIDE side,
2102 HSZ hszItem, UINT uFmt)
2104 WDML_LINK* pCurrent = NULL;
2106 for (pCurrent = pInstance->links[side]; pCurrent != NULL; pCurrent = pCurrent->next)
2108 /* we don't need to check for transaction type as it can be altered */
2110 if (pCurrent->hConv == hConv &&
2111 DdeCmpStringHandles(pCurrent->hszItem, hszItem) == 0 &&
2112 pCurrent->uFmt == uFmt)
2122 /* ================================================================
2124 * Transaction management
2126 * ================================================================ */
2128 /******************************************************************
2129 * WDML_AllocTransaction
2131 * Alloc a transaction structure for handling the message ddeMsg
2133 WDML_XACT* WDML_AllocTransaction(WDML_INSTANCE* pInstance, UINT ddeMsg,
2134 UINT wFmt, HSZ hszItem)
2137 static WORD tid = 1; /* FIXME: wrap around */
2139 pXAct = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_XACT));
2142 pInstance->lastError = DMLERR_MEMORY_ERROR;
2146 pXAct->xActID = tid++;
2147 pXAct->ddeMsg = ddeMsg;
2148 pXAct->hDdeData = 0;
2153 WDML_IncHSZ(pInstance, pXAct->hszItem = hszItem);
2161 /******************************************************************
2162 * WDML_QueueTransaction
2164 * Adds a transaction to the list of transaction
2166 void WDML_QueueTransaction(WDML_CONV* pConv, WDML_XACT* pXAct)
2170 /* advance to last in queue */
2171 for (pt = &pConv->transactions; *pt != NULL; pt = &(*pt)->next);
2175 /******************************************************************
2176 * WDML_UnQueueTransaction
2180 BOOL WDML_UnQueueTransaction(WDML_CONV* pConv, WDML_XACT* pXAct)
2184 for (pt = &pConv->transactions; *pt; pt = &(*pt)->next)
2195 /******************************************************************
2196 * WDML_FreeTransaction
2200 void WDML_FreeTransaction(WDML_INSTANCE* pInstance, WDML_XACT* pXAct, BOOL doFreePmt)
2202 /* free pmt(s) in pXAct too */
2203 if (doFreePmt && pXAct->hMem)
2204 GlobalFree(pXAct->hMem);
2205 WDML_DecHSZ(pInstance, pXAct->hszItem);
2207 HeapFree(GetProcessHeap(), 0, pXAct);
2210 /******************************************************************
2211 * WDML_FindTransaction
2215 WDML_XACT* WDML_FindTransaction(WDML_CONV* pConv, DWORD tid)
2220 for (pXAct = pConv->transactions; pXAct; pXAct = pXAct->next)
2222 if (pXAct->xActID == tid)
2228 /* ================================================================
2230 * Information broadcast across DDEML implementations
2232 * ================================================================ */
2234 struct tagWDML_BroadcastPmt
2242 /******************************************************************
2243 * WDML_BroadcastEnumProc
2247 static BOOL CALLBACK WDML_BroadcastEnumProc(HWND hWnd, LPARAM lParam)
2249 struct tagWDML_BroadcastPmt* s = (struct tagWDML_BroadcastPmt*)lParam;
2252 if (GetClassNameA(hWnd, buffer, sizeof(buffer)) > 0 &&
2253 strcmp(buffer, s->clsName) == 0)
2255 PostMessageA(hWnd, s->uMsg, s->wParam, s->lParam);
2260 /******************************************************************
2261 * WDML_BroadcastDDEWindows
2265 void WDML_BroadcastDDEWindows(const char* clsName, UINT uMsg, WPARAM wParam, LPARAM lParam)
2267 struct tagWDML_BroadcastPmt s;
2269 s.clsName = clsName;
2273 EnumWindows(WDML_BroadcastEnumProc, (LPARAM)&s);