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
21 #include "debugtools.h"
22 #include "dde/dde_private.h"
24 DEFAULT_DEBUG_CHANNEL(ddeml);
26 WDML_INSTANCE* WDML_InstanceList = NULL;
27 DWORD WDML_MaxInstanceID = 0; /* OK for present, may have to worry about wrap-around later */
28 static const char DDEInstanceAccess[] = "DDEMaxInstance";
29 static const char DDEHandleAccess[] = "DDEHandleAccess";
30 HANDLE handle_mutex = 0;
31 const char WDML_szEventClass[] = "DdeEventClass";
34 * currently the msg parameter is not used in the packing functions.
35 * it should be used to identify messages which don't actually require the packing operation
36 * but would do with the simple DWORD for lParam
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 BOOL WINAPI ImpersonateDdeClientWindow(
223 HWND hWndClient, /* [in] handle to DDE client window */
224 HWND hWndServer /* [in] handle to DDE server window */
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);
243 /******************************************************************************
244 * IncrementInstanceId
246 * generic routine to increment the max instance Id and allocate a new application instance
248 static DWORD WDML_IncrementInstanceId(WDML_INSTANCE* thisInstance)
250 DWORD id = InterlockedIncrement(&WDML_MaxInstanceID);
252 thisInstance->instanceID = id;
253 TRACE("New instance id %ld allocated\n", id);
254 return DMLERR_NO_ERROR;
257 /******************************************************************************
258 * DdeInitializeA (USER32.@)
260 UINT WINAPI DdeInitializeA(LPDWORD pidInst, PFNCALLBACK pfnCallback,
261 DWORD afCmd, DWORD ulRes)
263 UINT ret = DdeInitializeW(pidInst, pfnCallback, afCmd, ulRes);
265 if (ret == DMLERR_NO_ERROR) {
266 WDML_INSTANCE* thisInstance = WDML_FindInstance(*pidInst);
268 thisInstance->unicode = FALSE;
273 static LRESULT CALLBACK WDML_EventProc(HWND hwndEvent, UINT uMsg, WPARAM wParam, LPARAM lParam)
275 WDML_INSTANCE* thisInstance;
280 case WM_WDML_REGISTER:
281 thisInstance = (WDML_INSTANCE*)GetWindowLongA(hwndEvent, 0);
283 /* try calling the Callback */
284 if (thisInstance->callback != NULL /* && thisInstance->Process_id == GetCurrentProcessId()*/)
286 TRACE("Calling the callback, type=XTYP_REGISTER, CB=0x%lx\n",
287 (DWORD)thisInstance->callback);
289 hDdeData = (thisInstance->callback)(XTYP_REGISTER, 0, 0,
290 (HSZ)wParam, (HSZ)lParam, 0, 0, 0);
292 TRACE("Callback function called - result=%d\n", (INT)hDdeData);
296 case WM_WDML_UNREGISTER:
297 thisInstance = (WDML_INSTANCE*)GetWindowLongA(hwndEvent, 0);
299 if (thisInstance && thisInstance->callback != NULL)
301 if (thisInstance->CBFflags & CBF_SKIP_DISCONNECTS)
303 FIXME("skip callback XTYP_UNREGISTER\n");
307 TRACE("calling callback XTYP_UNREGISTER, idInst=%ld\n",
308 thisInstance->instanceID);
309 (thisInstance->callback)(XTYP_UNREGISTER, 0, 0,
310 (HSZ)wParam, (HSZ)lParam, 0, 0, 0);
314 return DefWindowProcA(hwndEvent, uMsg, wParam, lParam);
317 /******************************************************************************
318 * DdeInitializeW [USER32.@]
319 * Registers an application with the DDEML
322 * pidInst [I] Pointer to instance identifier
323 * pfnCallback [I] Pointer to callback function
324 * afCmd [I] Set of command and filter flags
328 * Success: DMLERR_NO_ERROR
329 * Failure: DMLERR_DLL_USAGE, DMLERR_INVALIDPARAMETER, DMLERR_SYS_ERROR
331 UINT WINAPI DdeInitializeW(LPDWORD pidInst, PFNCALLBACK pfnCallback,
332 DWORD afCmd, DWORD ulRes)
335 /* probably not really capable of handling multiple processes, but should handle
336 * multiple instances within one process */
338 SECURITY_ATTRIBUTES s_attrib;
340 WDML_INSTANCE* thisInstance;
341 WDML_INSTANCE* reference_inst;
343 WNDCLASSEXA wndclass;
347 ERR("Reserved value not zero? What does this mean?\n");
348 FIXME("(%p,%p,0x%lx,%ld): stub\n", pidInst, pfnCallback,
350 /* trap this and no more until we know more */
351 return DMLERR_NO_ERROR;
355 /* this one may be wrong - MS dll seems to accept the condition,
356 leave this until we find out more !! */
359 /* can't set up the instance with nothing to act as a callback */
360 TRACE("No callback provided\n");
361 return DMLERR_INVALIDPARAMETER; /* might be DMLERR_DLL_USAGE */
364 /* grab enough heap for one control struct - not really necessary for re-initialise
365 * but allows us to use same validation routines */
366 thisInstance = (WDML_INSTANCE*)HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_INSTANCE));
367 if (thisInstance == NULL)
369 /* catastrophe !! warn user & abort */
370 ERR("Instance create failed - out of memory\n");
371 return DMLERR_SYS_ERROR;
373 thisInstance->next = NULL;
374 thisInstance->monitor = (afCmd | APPCLASS_MONITOR);
376 /* messy bit, spec implies that 'Client Only' can be set in 2 different ways, catch 1 here */
378 thisInstance->clientOnly = afCmd & APPCMD_CLIENTONLY;
379 thisInstance->instanceID = *pidInst; /* May need to add calling proc Id */
380 thisInstance->callback = *pfnCallback;
381 thisInstance->txnCount = 0;
382 thisInstance->unicode = TRUE;
383 thisInstance->win16 = FALSE;
384 thisInstance->nodeList = NULL; /* node will be added later */
385 thisInstance->monitorFlags = afCmd & MF_MASK;
386 thisInstance->servers = NULL;
387 thisInstance->convs[0] = NULL;
388 thisInstance->convs[1] = NULL;
389 thisInstance->links[0] = NULL;
390 thisInstance->links[1] = NULL;
392 wndclass.cbSize = sizeof(wndclass);
394 wndclass.lpfnWndProc = WDML_EventProc;
395 wndclass.cbClsExtra = 0;
396 wndclass.cbWndExtra = sizeof(DWORD);
397 wndclass.hInstance = 0;
399 wndclass.hCursor = 0;
400 wndclass.hbrBackground = 0;
401 wndclass.lpszMenuName = NULL;
402 wndclass.lpszClassName = WDML_szEventClass;
403 wndclass.hIconSm = 0;
405 RegisterClassExA(&wndclass);
407 thisInstance->hwndEvent = CreateWindowA(WDML_szEventClass, NULL,
408 WS_POPUP, 0, 0, 0, 0,
411 SetWindowLongA(thisInstance->hwndEvent, 0, (DWORD)thisInstance);
413 /* isolate CBF flags in one go, expect this will go the way of all attempts to be clever !! */
415 thisInstance->CBFflags = afCmd^((afCmd&MF_MASK)|((afCmd&APPCMD_MASK)|(afCmd&APPCLASS_MASK)));
417 if (!thisInstance->clientOnly)
420 /* Check for other way of setting Client-only !! */
422 thisInstance->clientOnly =
423 (thisInstance->CBFflags & CBF_FAIL_ALLSVRXACTIONS) == CBF_FAIL_ALLSVRXACTIONS;
426 TRACE("instance created - checking validity \n");
430 /* Initialisation of new Instance Identifier */
431 TRACE("new instance, callback %p flags %lX\n",pfnCallback,afCmd);
432 if (WDML_MaxInstanceID == 0)
434 /* Nothing has been initialised - exit now ! can return TRUE since effect is the same */
435 /* Need to set up Mutex in case it is not already present */
436 s_attrib.bInheritHandle = TRUE;
437 s_attrib.lpSecurityDescriptor = NULL;
438 s_attrib.nLength = sizeof(s_attrib);
439 handle_mutex = CreateMutexA(&s_attrib,0,DDEHandleAccess);
442 ERR("CreateMutex failed - handle list %li\n",GetLastError());
443 HeapFree(GetProcessHeap(), 0, thisInstance);
444 return DMLERR_SYS_ERROR;
447 if (!WDML_WaitForMutex(handle_mutex))
449 return DMLERR_SYS_ERROR;
452 if (WDML_InstanceList == NULL)
454 /* can't be another instance in this case, assign to the base pointer */
455 WDML_InstanceList = thisInstance;
457 /* since first must force filter of XTYP_CONNECT and XTYP_WILDCONNECT for
459 * ------------------------------- NOTE NOTE NOTE --------------------------
461 * the manual is not clear if this condition
462 * applies to the first call to DdeInitialize from an application, or the
463 * first call for a given callback !!!
466 thisInstance->CBFflags = thisInstance->CBFflags|APPCMD_FILTERINITS;
467 TRACE("First application instance detected OK\n");
468 /* allocate new instance ID */
469 if ((err_no = WDML_IncrementInstanceId(thisInstance))) return err_no;
474 /* really need to chain the new one in to the latest here, but after checking conditions
475 * such as trying to start a conversation from an application trying to monitor */
476 reference_inst = WDML_InstanceList;
477 TRACE("Subsequent application instance - starting checks\n");
478 while (reference_inst->next != NULL)
481 * This set of tests will work if application uses same instance Id
482 * at application level once allocated - which is what manual implies
483 * should happen. If someone tries to be
484 * clever (lazy ?) it will fail to pick up that later calls are for
485 * the same application - should we trust them ?
487 if (thisInstance->instanceID == reference_inst->instanceID)
489 /* Check 1 - must be same Client-only state */
491 if (thisInstance->clientOnly != reference_inst->clientOnly)
493 ret = DMLERR_DLL_USAGE;
497 /* Check 2 - cannot use 'Monitor' with any non-monitor modes */
499 if (thisInstance->monitor != reference_inst->monitor)
501 ret = DMLERR_INVALIDPARAMETER;
505 /* Check 3 - must supply different callback address */
507 if (thisInstance->callback == reference_inst->callback)
509 ret = DMLERR_DLL_USAGE;
513 reference_inst = reference_inst->next;
515 /* All cleared, add to chain */
517 TRACE("Application Instance checks finished\n");
518 if ((err_no = WDML_IncrementInstanceId(thisInstance))) return err_no;
519 reference_inst->next = thisInstance;
521 if (WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE)) return DMLERR_SYS_ERROR;
522 *pidInst = thisInstance->instanceID;
523 TRACE("New application instance processing finished OK\n");
527 /* Reinitialisation situation --- FIX */
528 TRACE("reinitialisation of (%p,%p,0x%lx,%ld): stub\n",pidInst,pfnCallback,afCmd,ulRes);
530 if (!WDML_WaitForMutex(handle_mutex))
532 HeapFree(GetProcessHeap(), 0, thisInstance);
533 return DMLERR_SYS_ERROR;
536 if (WDML_InstanceList == NULL)
538 ret = DMLERR_DLL_USAGE;
541 HeapFree(GetProcessHeap(), 0, thisInstance); /* finished - release heap space used as work store */
542 /* can't reinitialise if we have initialised nothing !! */
543 reference_inst = WDML_InstanceList;
544 /* must first check if we have been given a valid instance to re-initialise !! how do we do that ? */
546 * MS allows initialisation without specifying a callback, should we allow addition of the
547 * callback by a later call to initialise ? - if so this lot will have to change
549 while (reference_inst->next != NULL)
551 if (*pidInst == reference_inst->instanceID && pfnCallback == reference_inst->callback)
553 /* Check 1 - cannot change client-only mode if set via APPCMD_CLIENTONLY */
555 if (reference_inst->clientOnly)
557 if ((reference_inst->CBFflags & CBF_FAIL_ALLSVRXACTIONS) != CBF_FAIL_ALLSVRXACTIONS)
559 /* i.e. Was set to Client-only and through APPCMD_CLIENTONLY */
561 if (!(afCmd & APPCMD_CLIENTONLY))
563 ret = DMLERR_DLL_USAGE;
568 /* Check 2 - cannot change monitor modes */
570 if (thisInstance->monitor != reference_inst->monitor)
572 ret = DMLERR_DLL_USAGE;
576 /* Check 3 - trying to set Client-only via APPCMD when not set so previously */
578 if ((afCmd&APPCMD_CLIENTONLY) && !reference_inst->clientOnly)
580 ret = DMLERR_DLL_USAGE;
585 reference_inst = reference_inst->next;
587 if (reference_inst->next == NULL)
589 /* Crazy situation - trying to re-initialize something that has not beeen initialized !!
591 * Manual does not say what we do, cannot return DMLERR_NOT_INITIALIZED so what ?
593 ret = DMLERR_INVALIDPARAMETER;
596 /* All checked - change relevant flags */
598 reference_inst->CBFflags = thisInstance->CBFflags;
599 reference_inst->clientOnly = thisInstance->clientOnly;
600 reference_inst->monitorFlags = thisInstance->monitorFlags;
601 if (WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE))
603 HeapFree(GetProcessHeap(), 0, thisInstance);
604 return DMLERR_SYS_ERROR;
608 return DMLERR_NO_ERROR;
610 HeapFree(GetProcessHeap(), 0, thisInstance);
611 if (WDML_ReleaseMutex(handle_mutex, "handle_mutex", 0))
612 return DMLERR_SYS_ERROR;
616 /*****************************************************************
617 * DdeUninitialize [USER32.@] Frees DDEML resources
620 * idInst [I] Instance identifier
627 BOOL WINAPI DdeUninitialize(DWORD idInst)
629 /* Stage one - check if we have a handle for this instance
631 WDML_INSTANCE* thisInstance;
632 WDML_INSTANCE* reference_inst;
634 if (WDML_MaxInstanceID == 0)
636 /* Nothing has been initialised - exit now ! can return TRUE since effect is the same */
640 if (!WDML_WaitForMutex(handle_mutex))
642 return DMLERR_SYS_ERROR;
644 /* First check instance
646 thisInstance = WDML_FindInstance(idInst);
647 if (thisInstance == NULL)
649 if (WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE)) return FALSE;
651 * Needs something here to record NOT_INITIALIZED ready for DdeGetLastError
655 FIXME("(%ld): partial stub\n", idInst);
657 /* FIXME ++++++++++++++++++++++++++++++++++++++++++
658 * Needs to de-register all service names
662 /* Free the nodes that were not freed by this instance
663 * and remove the nodes from the list of HSZ nodes.
665 WDML_FreeAllHSZ(thisInstance);
667 DestroyWindow(thisInstance->hwndEvent);
669 /* OK now delete the instance handle itself */
671 if (WDML_InstanceList == thisInstance)
673 /* special case - the first/only entry
675 WDML_InstanceList = thisInstance->next;
681 reference_inst = WDML_InstanceList;
682 while (reference_inst->next != thisInstance)
684 reference_inst = thisInstance->next;
686 reference_inst->next = thisInstance->next;
688 /* release the mutex and the heap entry
690 HeapFree(GetProcessHeap(), 0, thisInstance);
691 if (WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE))
693 /* should record something here, but nothing left to hang it from !!
700 /******************************************************************************
701 * RemoveHSZNodes (INTERNAL)
703 * Remove a node from the list of HSZ nodes.
705 static void WDML_RemoveHSZNode(WDML_INSTANCE* thisInstance, HSZ hsz)
707 HSZNode* pPrev = NULL;
708 HSZNode* pCurrent = NULL;
710 /* Set the current node at the start of the list.
712 pCurrent = thisInstance->nodeList;
713 /* While we have more nodes.
715 while (pCurrent != NULL)
717 /* If we found the node we were looking for.
719 if (pCurrent->hsz == hsz)
723 /* If the first node in the list is to to be removed.
724 * Set the global list pointer to the next node.
726 if (pCurrent == thisInstance->nodeList)
728 thisInstance->nodeList = pCurrent->next;
730 /* Just fix the pointers has to skip the current
731 * node so we can delete it.
735 pPrev->next = pCurrent->next;
737 /* Destroy this node.
739 HeapFree(GetProcessHeap(), 0, pCurrent);
742 /* Save the previous node pointer.
745 /* Move on to the next node.
747 pCurrent = pCurrent->next;
751 /******************************************************************************
752 * FreeAndRemoveHSZNodes (INTERNAL)
754 * Frees up all the strings still allocated in the list and
755 * remove all the nodes from the list of HSZ nodes.
757 void WDML_FreeAllHSZ(WDML_INSTANCE* thisInstance)
759 /* Free any strings created in this instance.
761 while (thisInstance->nodeList != NULL)
763 DdeFreeStringHandle(thisInstance->instanceID, thisInstance->nodeList->hsz);
767 /******************************************************************************
768 * GetSecondaryHSZValue (INTERNAL)
770 * Insert a node to the head of the list.
772 static HSZ WDML_GetSecondaryHSZValue(WDML_INSTANCE* thisInstance, HSZ hsz)
778 /* Create and set the Secondary handle */
779 if (thisInstance->unicode)
781 WCHAR wSecondaryString[MAX_BUFFER_LEN];
782 WCHAR wUniqueNum[MAX_BUFFER_LEN];
784 if (DdeQueryStringW(thisInstance->instanceID, hsz,
786 MAX_BUFFER_LEN, CP_WINUNICODE))
788 wsprintfW(wUniqueNum,"(%ld)",
789 (DWORD)thisInstance->instanceID);
790 lstrcatW(wSecondaryString, wUniqueNum);
792 hsz2 = GlobalAddAtomW(wSecondaryString);
797 CHAR SecondaryString[MAX_BUFFER_LEN];
798 CHAR UniqueNum[MAX_BUFFER_LEN];
800 if (DdeQueryStringA(thisInstance->instanceID, hsz,
802 MAX_BUFFER_LEN, CP_WINANSI))
804 wsprintfA(UniqueNum,"(%ld)", thisInstance->instanceID);
805 lstrcatA(SecondaryString, UniqueNum);
807 hsz2 = GlobalAddAtomA(SecondaryString);
814 /******************************************************************************
815 * InsertHSZNode (INTERNAL)
817 * Insert a node to the head of the list.
819 static void WDML_InsertHSZNode(WDML_INSTANCE* thisInstance, HSZ hsz)
823 HSZNode* pNew = NULL;
824 /* Create a new node for this HSZ.
826 pNew = (HSZNode*)HeapAlloc(GetProcessHeap(), 0, sizeof(HSZNode));
829 /* Set the handle value.
833 /* Create and set the Secondary handle */
834 pNew->hsz2 = WDML_GetSecondaryHSZValue(thisInstance, hsz);
836 /* Attach the node to the head of the list. i.e most recently added is first
838 pNew->next = thisInstance->nodeList;
840 /* The new node is now at the head of the list
841 * so set the global list pointer to it.
843 thisInstance->nodeList = pNew;
847 ERR("Primary HSZ Node allocation failed - out of memory\n");
852 /*****************************************************************************
853 * Find_Instance_Entry
855 * generic routine to return a pointer to the relevant DDE_HANDLE_ENTRY
856 * for an instance Id, or NULL if the entry does not exist
858 * ASSUMES the mutex protecting the handle entry list is reserved before calling
860 WDML_INSTANCE* WDML_FindInstance(DWORD InstId)
862 WDML_INSTANCE* thisInstance;
864 thisInstance = WDML_InstanceList;
865 while (thisInstance != NULL)
867 if (thisInstance->instanceID == InstId)
871 thisInstance = thisInstance->next;
873 TRACE("Instance entry missing\n");
877 /******************************************************************************
880 * generic routine to release a reserved mutex
882 DWORD WDML_ReleaseMutex(HANDLE mutex, LPSTR mutex_name, BOOL release_handle_m)
884 if (!ReleaseMutex(mutex))
886 ERR("ReleaseMutex failed - %s mutex %li\n", mutex_name, GetLastError());
887 if (release_handle_m)
889 ReleaseMutex(handle_mutex);
891 return DMLERR_SYS_ERROR;
893 return DMLERR_NO_ERROR;
896 /******************************************************************************
899 * generic routine to wait for the mutex
901 BOOL WDML_WaitForMutex(HANDLE mutex)
905 result = WaitForSingleObject(mutex, INFINITE);
907 /* both errors should never occur */
908 if (WAIT_TIMEOUT == result)
910 ERR("WaitForSingleObject timed out\n");
914 if (WAIT_FAILED == result)
916 ERR("WaitForSingleObject failed - error %li\n", GetLastError());
919 /* TRACE("Handle Mutex created/reserved\n"); */
924 /******************************************************************************
927 * Routine to make an extra Add on an atom to reserve it a bit longer
930 void WDML_ReserveAtom(WDML_INSTANCE* thisInstance, HSZ hsz)
932 if (thisInstance->unicode)
934 WCHAR SNameBuffer[MAX_BUFFER_LEN];
935 GlobalGetAtomNameW(hsz, SNameBuffer, MAX_BUFFER_LEN);
936 GlobalAddAtomW(SNameBuffer);
938 CHAR SNameBuffer[MAX_BUFFER_LEN];
939 GlobalGetAtomNameA(hsz, SNameBuffer, MAX_BUFFER_LEN);
940 GlobalAddAtomA(SNameBuffer);
945 /******************************************************************************
948 * Routine to make a delete on an atom to release it a bit sooner
951 void WDML_ReleaseAtom(WDML_INSTANCE* thisInstance, HSZ hsz)
953 GlobalDeleteAtom(hsz);
957 /*****************************************************************
958 * DdeQueryStringA [USER32.@]
960 DWORD WINAPI DdeQueryStringA(DWORD idInst, HSZ hsz, LPSTR psz, DWORD cchMax, INT iCodePage)
963 CHAR pString[MAX_BUFFER_LEN];
964 WDML_INSTANCE* thisInstance;
966 TRACE("(%ld, 0x%x, %p, %ld, %d): partial stub\n",
967 idInst, hsz, psz, cchMax, iCodePage);
969 if (WDML_MaxInstanceID == 0)
971 /* Nothing has been initialised - exit now ! */
972 /* needs something for DdeGetLAstError even if the manual doesn't say so */
976 if (!WDML_WaitForMutex(handle_mutex))
981 /* First check instance
983 thisInstance = WDML_FindInstance(idInst);
984 if (thisInstance == NULL)
986 if (WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE)) return FALSE;
988 Needs something here to record NOT_INITIALIZED ready for DdeGetLastError
993 if (iCodePage == CP_WINANSI)
995 /* If psz is null, we have to return only the length
1001 cchMax = MAX_BUFFER_LEN;
1004 ret = GlobalGetAtomNameA(hsz, (LPSTR)psz, cchMax);
1007 WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
1009 TRACE("returning pointer\n");
1013 /*****************************************************************
1014 * DdeQueryStringW [USER32.@]
1017 DWORD WINAPI DdeQueryStringW(DWORD idInst, HSZ hsz, LPWSTR psz, DWORD cchMax, INT iCodePage)
1020 WCHAR pString[MAX_BUFFER_LEN];
1023 TRACE("(%ld, 0x%x, %p, %ld, %d): partial-stub\n",
1024 idInst, hsz, psz, cchMax, iCodePage);
1026 if (iCodePage == CP_WINUNICODE)
1028 /* If psz is null, we have to return only the length
1034 cchMax = MAX_BUFFER_LEN;
1035 /* Note: According to documentation if the psz parameter
1036 * was NULL this API must return the length of the string in bytes.
1038 factor = (int)sizeof(WCHAR)/sizeof(BYTE);
1040 ret = GlobalGetAtomNameW(hsz, (LPWSTR)psz, cchMax) * factor;
1045 /*****************************************************************
1046 * DdeCreateStringHandleA [USER32.@]
1049 * Success: String handle
1052 HSZ WINAPI DdeCreateStringHandleA(DWORD idInst, LPCSTR psz, INT codepage)
1055 WDML_INSTANCE* thisInstance;
1057 TRACE("(%ld,%s,%d): partial stub\n",idInst,debugstr_a(psz),codepage);
1059 if (WDML_MaxInstanceID == 0)
1061 /* Nothing has been initialised - exit now ! can return FALSE since effect is the same */
1065 if (!WDML_WaitForMutex(handle_mutex))
1067 return DMLERR_SYS_ERROR;
1071 /* First check instance
1073 thisInstance = WDML_FindInstance(idInst);
1074 if (thisInstance == NULL)
1076 if (WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE)) return 0;
1078 Needs something here to record NOT_INITIALIZED ready for DdeGetLastError
1083 if (codepage == CP_WINANSI)
1085 hsz = GlobalAddAtomA(psz);
1086 /* Save the handle so we know to clean it when
1087 * uninitialize is called.
1089 TRACE("added atom %s with HSZ 0x%x, \n",debugstr_a(psz),hsz);
1090 WDML_InsertHSZNode(thisInstance, hsz);
1091 if (WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE))
1093 thisInstance->lastError = DMLERR_SYS_ERROR;
1096 TRACE("Returning pointer\n");
1101 WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
1103 TRACE("Returning error\n");
1108 /******************************************************************************
1109 * DdeCreateStringHandleW [USER32.@] Creates handle to identify string
1112 * Success: String handle
1115 HSZ WINAPI DdeCreateStringHandleW(
1116 DWORD idInst, /* [in] Instance identifier */
1117 LPCWSTR psz, /* [in] Pointer to string */
1118 INT codepage) /* [in] Code page identifier */
1120 WDML_INSTANCE* thisInstance;
1123 TRACE("(%ld,%s,%d): partial stub\n",idInst,debugstr_w(psz),codepage);
1126 if (WDML_MaxInstanceID == 0)
1128 /* Nothing has been initialised - exit now ! can return FALSE since effect is the same */
1132 if (!WDML_WaitForMutex(handle_mutex))
1134 return DMLERR_SYS_ERROR;
1137 /* First check instance
1139 thisInstance = WDML_FindInstance(idInst);
1140 if (thisInstance == NULL)
1142 if (WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE)) return 0;
1144 Needs something here to record NOT_INITIALIZED ready for DdeGetLastError
1149 FIXME("(%ld,%s,%d): partial stub\n",idInst,debugstr_w(psz),codepage);
1151 if (codepage == CP_WINUNICODE)
1154 * Should we be checking this against the unicode/ascii nature of the call to DdeInitialize ?
1156 hsz = GlobalAddAtomW(psz);
1157 /* Save the handle so we know to clean it when
1158 * uninitialize is called.
1160 WDML_InsertHSZNode(thisInstance, hsz);
1161 if (WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE))
1163 thisInstance->lastError = DMLERR_SYS_ERROR;
1166 TRACE("Returning pointer\n");
1171 WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
1173 TRACE("Returning error\n");
1177 /*****************************************************************
1178 * DdeFreeStringHandle (USER32.@)
1179 * RETURNS: success: nonzero
1182 BOOL WINAPI DdeFreeStringHandle(DWORD idInst, HSZ hsz)
1184 WDML_INSTANCE* thisInstance;
1187 TRACE("(%ld,%d): \n",idInst,hsz);
1189 if (WDML_MaxInstanceID == 0)
1191 /* Nothing has been initialised - exit now ! can return TRUE since effect is the same */
1195 if (!WDML_WaitForMutex(handle_mutex))
1197 return DMLERR_SYS_ERROR;
1200 /* First check instance
1202 thisInstance = WDML_FindInstance(idInst);
1203 if ((thisInstance == NULL) || (thisInstance->nodeList == NULL))
1205 if (WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE)) return TRUE;
1206 /* Nothing has been initialised - exit now ! can return TRUE since effect is the same */
1210 /* Remove the node associated with this HSZ.
1212 hsz2 = thisInstance->nodeList->hsz2; /* save this value first */
1214 WDML_RemoveHSZNode(thisInstance, hsz);
1215 /* Free the string associated with this HSZ.
1217 WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
1220 GlobalDeleteAtom(hsz2);
1222 return GlobalDeleteAtom(hsz) ? 0 : hsz;
1225 /*****************************************************************
1226 * DdeKeepStringHandle (USER32.@)
1228 * RETURNS: success: nonzero
1231 BOOL WINAPI DdeKeepStringHandle(DWORD idInst, HSZ hsz)
1234 WDML_INSTANCE* thisInstance;
1236 TRACE("(%ld,%d): \n",idInst,hsz);
1238 if (WDML_MaxInstanceID == 0)
1240 /* Nothing has been initialised - exit now ! can return FALSE since effect is the same */
1245 if (!WDML_WaitForMutex(handle_mutex))
1250 /* First check instance
1252 thisInstance = WDML_FindInstance(idInst);
1253 if ((thisInstance == NULL) || (thisInstance->nodeList == NULL))
1255 if (WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE)) return FALSE;
1256 /* Nothing has been initialised - exit now ! can return FALSE since effect is the same */
1260 WDML_ReserveAtom(thisInstance, hsz);
1261 WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
1265 /*****************************************************************
1266 * DdeCreateDataHandle (USER32.@)
1268 HDDEDATA WINAPI DdeCreateDataHandle(DWORD idInst, LPBYTE pSrc, DWORD cb,
1269 DWORD cbOff, HSZ hszItem, UINT wFmt,
1273 For now, we ignore idInst, hszItem, wFmt, and afCmd.
1274 The purpose of these arguments still need to be investigated.
1279 DDE_DATAHANDLE_HEAD* pDdh;
1281 TRACE("(%ld,%p,%ld,%ld,0x%lx,%d,%d): semi-stub\n",
1282 idInst,pSrc,cb,cbOff,(DWORD)hszItem,wFmt,afCmd);
1284 /* we use the first 4 bytes to store the size */
1285 if (!(hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, cb + sizeof(DDE_DATAHANDLE_HEAD))))
1287 ERR("GlobalAlloc failed\n");
1291 pDdh = (DDE_DATAHANDLE_HEAD*)GlobalLock(hMem);
1292 pDdh->cfFormat = wFmt;
1294 pByte = (LPBYTE)(pDdh + 1);
1297 memcpy(pByte, pSrc + cbOff, cb);
1301 return (HDDEDATA)hMem;
1304 /*****************************************************************
1306 * DdeAddData (USER32.@)
1308 HDDEDATA WINAPI DdeAddData(HDDEDATA hData, LPBYTE pSrc, DWORD cb, DWORD cbOff)
1310 DWORD old_sz, new_sz;
1313 pDst = DdeAccessData(hData, &old_sz);
1314 if (!pDst) return 0;
1316 new_sz = cb + cbOff;
1317 if (new_sz > old_sz)
1319 DdeUnaccessData(hData);
1320 hData = GlobalReAlloc((HGLOBAL)hData, new_sz + sizeof(DDE_DATAHANDLE_HEAD),
1321 GMEM_MOVEABLE | GMEM_DDESHARE);
1322 pDst = DdeAccessData(hData, &old_sz);
1325 if (!pDst) return 0;
1327 memcpy(pDst + cbOff, pSrc, cb);
1328 DdeUnaccessData(hData);
1332 /*****************************************************************
1333 * DdeSetUserHandle (USER32.@)
1335 BOOL WINAPI DdeSetUserHandle(HCONV hConv, DWORD id, DWORD hUser)
1340 WDML_WaitForMutex(handle_mutex);
1342 pConv = WDML_GetConv(hConv);
1350 pConv->hUser = hUser;
1356 pXAct = WDML_FindTransaction(pConv, id);
1359 pXAct->hUser = hUser;
1363 pConv->thisInstance->lastError = DMLERR_UNFOUND_QUEUE_ID;
1368 WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
1372 /******************************************************************************
1373 * DdeGetData [USER32.@] Copies data from DDE object to local buffer
1376 * Size of memory object associated with handle
1378 DWORD WINAPI DdeGetData(
1379 HDDEDATA hData, /* [in] Handle to DDE object */
1380 LPBYTE pDst, /* [in] Pointer to destination buffer */
1381 DWORD cbMax, /* [in] Amount of data to copy */
1382 DWORD cbOff) /* [in] Offset to beginning of data */
1384 DWORD dwSize, dwRet;
1387 TRACE("(%08lx,%p,%ld,%ld)\n",(DWORD)hData,pDst,cbMax,cbOff);
1389 pByte = DdeAccessData(hData, &dwSize);
1393 if (cbOff + cbMax < dwSize)
1397 else if (cbOff < dwSize)
1399 dwRet = dwSize - cbOff;
1405 if (pDst && dwRet != 0)
1407 memcpy(pDst, pByte + cbOff, dwRet);
1409 DdeUnaccessData(hData);
1418 /*****************************************************************
1419 * DdeAccessData (USER32.@)
1421 LPBYTE WINAPI DdeAccessData(HDDEDATA hData, LPDWORD pcbDataSize)
1423 HGLOBAL hMem = (HGLOBAL)hData;
1424 DDE_DATAHANDLE_HEAD* pDdh;
1426 TRACE("(%08lx,%p)\n", (DWORD)hData, pcbDataSize);
1428 pDdh = (DDE_DATAHANDLE_HEAD*)GlobalLock(hMem);
1431 ERR("Failed on GlobalLock(%04x)\n", hMem);
1435 if (pcbDataSize != NULL)
1437 *pcbDataSize = GlobalSize(hMem) - sizeof(DDE_DATAHANDLE_HEAD);
1440 return (LPBYTE)(pDdh + 1);
1443 /*****************************************************************
1444 * DdeUnaccessData (USER32.@)
1446 BOOL WINAPI DdeUnaccessData(HDDEDATA hData)
1448 HGLOBAL hMem = (HGLOBAL)hData;
1450 TRACE("(0x%lx)\n", (DWORD)hData);
1457 /*****************************************************************
1458 * DdeFreeDataHandle (USER32.@)
1460 BOOL WINAPI DdeFreeDataHandle(HDDEDATA hData)
1462 return GlobalFree((HGLOBAL)hData) == 0;
1465 /* ================================================================
1467 * Global <=> Data handle management
1469 * ================================================================ */
1471 /* Note: we use a DDEDATA, but layout of DDEDATA, DDEADVISE and DDEPOKE structures is similar:
1473 * (bytes) (bits) comment
1474 * 0 16 bit fields for options (release, ackreq, response...)
1475 * 2 16 clipboard format
1476 * 4 ? data to be used
1478 HDDEDATA WDML_Global2DataHandle(HGLOBAL hMem)
1484 pDd = GlobalLock(hMem);
1487 return DdeCreateDataHandle(0, pDd->Value,
1488 GlobalSize(hMem) - (sizeof(DDEDATA) - 1),
1489 0, 0, pDd->cfFormat, 0);
1495 HGLOBAL WDML_DataHandle2Global(HDDEDATA hDdeData, BOOL fResponse, BOOL fRelease,
1496 BOOL fDeferUpd, BOOL fAckReq)
1498 DDE_DATAHANDLE_HEAD* pDdh;
1502 dwSize = GlobalSize(hDdeData) - sizeof(DDE_DATAHANDLE_HEAD);
1503 pDdh = (DDE_DATAHANDLE_HEAD*)GlobalLock(hDdeData);
1506 hMem = GlobalAlloc(sizeof(DDEDATA) - 1 + dwSize,
1507 GMEM_MOVEABLE | GMEM_DDESHARE);
1512 ddeData = GlobalLock(hMem);
1515 ddeData->fResponse = fResponse;
1516 ddeData->fRelease = fRelease;
1517 ddeData->reserved /*fDeferUpd*/ = fDeferUpd;
1518 ddeData->fAckReq = fAckReq;
1519 ddeData->cfFormat = pDdh->cfFormat;
1520 memcpy(ddeData->Value, pDdh + 1, dwSize);
1524 GlobalUnlock(hDdeData);
1530 /*****************************************************************
1531 * DdeEnableCallback (USER32.@)
1533 BOOL WINAPI DdeEnableCallback(DWORD idInst, HCONV hConv, UINT wCmd)
1535 FIXME("(%ld, 0x%x, %d) stub\n", idInst, hConv, wCmd);
1540 /******************************************************************************
1541 * DdeGetLastError [USER32.@] Gets most recent error code
1544 * idInst [I] Instance identifier
1549 UINT WINAPI DdeGetLastError(DWORD idInst)
1552 WDML_INSTANCE* thisInstance;
1554 FIXME("(%ld): error reporting is weakly implemented\n",idInst);
1556 if (WDML_MaxInstanceID == 0)
1558 /* Nothing has been initialised - exit now ! */
1559 return DMLERR_DLL_NOT_INITIALIZED;
1562 if (!WDML_WaitForMutex(handle_mutex))
1564 return DMLERR_SYS_ERROR;
1567 /* First check instance
1569 thisInstance = WDML_FindInstance(idInst);
1570 if (thisInstance == NULL)
1572 error_code = DMLERR_DLL_NOT_INITIALIZED;
1576 error_code = thisInstance->lastError;
1577 thisInstance->lastError = 0;
1580 WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
1584 /*****************************************************************
1585 * DdeCmpStringHandles (USER32.@)
1587 * Compares the value of two string handles. This comparison is
1588 * not case sensitive.
1591 * -1 The value of hsz1 is zero or less than hsz2
1592 * 0 The values of hsz 1 and 2 are the same or both zero.
1593 * 1 The value of hsz2 is zero of less than hsz1
1595 INT WINAPI DdeCmpStringHandles(HSZ hsz1, HSZ hsz2)
1597 CHAR psz1[MAX_BUFFER_LEN];
1598 CHAR psz2[MAX_BUFFER_LEN];
1602 ret1 = GlobalGetAtomNameA(hsz1, psz1, MAX_BUFFER_LEN);
1603 ret2 = GlobalGetAtomNameA(hsz2, psz2, MAX_BUFFER_LEN);
1604 TRACE("(%04lx<%s> %04lx<%s>);\n", (DWORD)hsz1, psz1, (DWORD)hsz2, psz2);
1606 /* Make sure we found both strings.
1608 if (ret1 == 0 && ret2 == 0)
1610 /* If both are not found, return both "zero strings".
1616 /* If hsz1 is a not found, return hsz1 is "zero string".
1622 /* If hsz2 is a not found, return hsz2 is "zero string".
1628 /* Compare the two strings we got (case insensitive).
1630 ret = strcasecmp(psz1, psz2);
1631 /* Since strcmp returns any number smaller than
1632 * 0 when the first string is found to be less than
1633 * the second one we must make sure we are returning
1634 * the proper values.
1649 /******************************************************************
1650 * DdeQueryConvInfo (USER32.@)
1653 UINT WINAPI DdeQueryConvInfo(HCONV hConv, DWORD id, LPCONVINFO lpConvInfo)
1655 UINT ret = lpConvInfo->cb;
1659 FIXME("semi-stub.\n");
1661 WDML_WaitForMutex(handle_mutex);
1663 pConv = WDML_GetConv(hConv);
1666 WDML_ReleaseMutex(handle_mutex, "hande_mutex", FALSE);
1670 ci.hConvPartner = 0; /* FIXME */
1671 ci.hszSvcPartner = pConv->hszService;
1672 ci.hszServiceReq = pConv->hszService; /* FIXME: they shouldn't be the same, should they ? */
1673 ci.hszTopic = pConv->hszTopic;
1674 ci.wStatus = ST_CLIENT /* FIXME */ | ST_CONNECTED;
1675 ci.wConvst = 0; /* FIXME */
1676 ci.wLastError = 0; /* FIXME: note it's not the instance last error */
1678 ci.ConvCtxt = pConv->convContext;
1679 if (ci.wStatus & ST_CLIENT)
1681 ci.hwnd = pConv->hwndClient;
1682 ci.hwndPartner = pConv->hwndServer;
1686 ci.hwnd = pConv->hwndServer;
1687 ci.hwndPartner = pConv->hwndClient;
1691 ci.hUser = pConv->hUser;
1700 pXAct = WDML_FindTransaction(pConv, id);
1703 ci.hUser = pXAct->hUser;
1704 ci.hszItem = 0; /* FIXME */
1705 ci.wFmt = 0; /* FIXME */
1706 ci.wType = 0; /* FIXME */
1711 pConv->thisInstance->lastError = DMLERR_UNFOUND_QUEUE_ID;
1715 WDML_ReleaseMutex(handle_mutex, "hande_mutex", FALSE);
1716 memcpy(lpConvInfo, &ci, min(lpConvInfo->cb, sizeof(ci)));
1720 /* ================================================================
1724 * ================================================================ */
1726 /******************************************************************
1731 WDML_SERVER* WDML_AddServer(WDML_INSTANCE* thisInstance, HSZ hszService, HSZ hszTopic)
1733 WDML_SERVER* pServer;
1735 pServer = (WDML_SERVER*)HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_SERVER));
1736 if (pServer == NULL) return NULL;
1738 pServer->hszService = hszService;
1739 pServer->hszTopic = 0;
1740 pServer->filterOn = TRUE;
1742 pServer->next = thisInstance->servers;
1743 thisInstance->servers = pServer;
1747 /******************************************************************
1752 void WDML_RemoveServer(WDML_INSTANCE* thisInstance, HSZ hszService, HSZ hszTopic)
1754 WDML_SERVER* pPrev = NULL;
1755 WDML_SERVER* pCurrent = NULL;
1757 pCurrent = thisInstance->servers;
1759 while (pCurrent != NULL)
1761 if (DdeCmpStringHandles(pCurrent->hszService, hszService) == 0)
1763 if (pCurrent == thisInstance->servers)
1765 thisInstance->servers = pCurrent->next;
1769 pPrev->next = pCurrent->next;
1772 DestroyWindow(pCurrent->hwndServer);
1774 HeapFree(GetProcessHeap(), 0, pCurrent);
1779 pCurrent = pCurrent->next;
1783 /*****************************************************************************
1786 * generic routine to return a pointer to the relevant ServiceNode
1787 * for a given service name, or NULL if the entry does not exist
1789 * ASSUMES the mutex protecting the handle entry list is reserved before calling
1791 WDML_SERVER* WDML_FindServer(WDML_INSTANCE* thisInstance, HSZ hszService, HSZ hszTopic)
1793 WDML_SERVER* pServer;
1795 for (pServer = thisInstance->servers; pServer != NULL; pServer = pServer->next)
1797 if (hszService == pServer->hszService)
1802 TRACE("Service name missing\n");
1806 /* ================================================================
1808 * Conversation management
1810 * ================================================================ */
1812 /******************************************************************
1817 WDML_CONV* WDML_AddConv(WDML_INSTANCE* thisInstance, WDML_SIDE side,
1818 HSZ hszService, HSZ hszTopic, HWND hwndClient, HWND hwndServer)
1822 /* no converstation yet, add it */
1823 pConv = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_CONV));
1824 if (!pConv) return NULL;
1826 pConv->thisInstance = thisInstance;
1827 pConv->hszService = hszService;
1828 pConv->hszTopic = hszTopic;
1829 pConv->hwndServer = hwndServer;
1830 pConv->hwndClient = hwndClient;
1831 pConv->transactions = NULL;
1834 pConv->next = thisInstance->convs[side];
1835 thisInstance->convs[side] = pConv;
1840 /******************************************************************
1845 WDML_CONV* WDML_FindConv(WDML_INSTANCE* thisInstance, WDML_SIDE side,
1846 HSZ hszService, HSZ hszTopic)
1848 WDML_CONV* pCurrent = NULL;
1850 for (pCurrent = thisInstance->convs[side]; pCurrent != NULL; pCurrent = pCurrent->next)
1852 if (DdeCmpStringHandles(pCurrent->hszService, hszService) == 0 &&
1853 DdeCmpStringHandles(pCurrent->hszTopic, hszTopic) == 0)
1862 /******************************************************************
1867 void WDML_RemoveConv(WDML_INSTANCE* thisInstance, WDML_SIDE side, HCONV hConv)
1869 WDML_CONV* pPrev = NULL;
1870 WDML_CONV* pRef = WDML_GetConv(hConv);
1871 WDML_CONV* pCurrent = NULL;
1875 for (pCurrent = thisInstance->convs[side]; pCurrent != NULL; pCurrent = (pPrev = pCurrent)->next)
1877 if (pCurrent == pRef)
1879 if (pCurrent == thisInstance->convs[side])
1881 thisInstance->convs[side] = pCurrent->next;
1885 pPrev->next = pCurrent->next;
1888 HeapFree(GetProcessHeap(), 0, pCurrent);
1894 /******************************************************************
1899 WDML_CONV* WDML_GetConv(HCONV hConv)
1901 /* FIXME: should do better checking */
1902 return (WDML_CONV*)hConv;
1905 /* ================================================================
1907 * Link (hot & warm) management
1909 * ================================================================ */
1911 /******************************************************************
1916 void WDML_AddLink(WDML_INSTANCE* thisInstance, HCONV hConv, WDML_SIDE side,
1917 UINT wType, HSZ hszItem, UINT wFmt)
1921 TRACE("AddDdeLink was called...\n");
1923 pLink = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_LINK));
1930 pLink->hConv = hConv;
1931 pLink->transactionType = wType;
1932 pLink->hszItem = hszItem;
1934 pLink->hDdeData = 0;
1935 pLink->next = thisInstance->links[side];
1936 thisInstance->links[side] = pLink;
1939 /******************************************************************
1944 void WDML_RemoveLink(WDML_INSTANCE* thisInstance, HCONV hConv, WDML_SIDE side,
1945 HSZ hszItem, UINT uFmt)
1947 WDML_LINK* pPrev = NULL;
1948 WDML_LINK* pCurrent = NULL;
1950 pCurrent = thisInstance->links[side];
1952 while (pCurrent != NULL)
1954 if (pCurrent->hConv == hConv &&
1955 DdeCmpStringHandles(pCurrent->hszItem, hszItem) == 0 &&
1956 pCurrent->uFmt == uFmt)
1958 if (pCurrent == thisInstance->links[side])
1960 thisInstance->links[side] = pCurrent->next;
1964 pPrev->next = pCurrent->next;
1967 if (pCurrent->hDdeData)
1969 DdeFreeDataHandle(pCurrent->hDdeData);
1972 HeapFree(GetProcessHeap(), 0, pCurrent);
1977 pCurrent = pCurrent->next;
1981 /* this function is called to remove all links related to the conv.
1982 It should be called from both client and server when terminating
1985 /******************************************************************
1986 * WDML_RemoveAllLinks
1990 void WDML_RemoveAllLinks(WDML_INSTANCE* thisInstance, HCONV hConv, WDML_SIDE side)
1992 WDML_LINK* pPrev = NULL;
1993 WDML_LINK* pCurrent = NULL;
1994 WDML_LINK* pNext = NULL;
1996 pCurrent = thisInstance->links[side];
1998 while (pCurrent != NULL)
2000 if (pCurrent->hConv == hConv)
2002 if (pCurrent == thisInstance->links[side])
2004 thisInstance->links[side] = pCurrent->next;
2005 pNext = pCurrent->next;
2009 pPrev->next = pCurrent->next;
2010 pNext = pCurrent->next;
2013 if (pCurrent->hDdeData)
2015 DdeFreeDataHandle(pCurrent->hDdeData);
2018 HeapFree(GetProcessHeap(), 0, pCurrent);
2025 pCurrent = pCurrent->next;
2034 /******************************************************************
2039 WDML_LINK* WDML_FindLink(WDML_INSTANCE* thisInstance, HCONV hConv, WDML_SIDE side,
2040 HSZ hszItem, UINT uFmt)
2042 WDML_LINK* pCurrent = NULL;
2044 for (pCurrent = thisInstance->links[side]; pCurrent != NULL; pCurrent = pCurrent->next)
2046 /* we don't need to check for transaction type as
2047 it can be altered */
2049 if (pCurrent->hConv == hConv &&
2050 DdeCmpStringHandles(pCurrent->hszItem, hszItem) == 0 &&
2051 pCurrent->uFmt == uFmt)
2061 /* ================================================================
2063 * Transaction management
2065 * ================================================================ */
2067 /******************************************************************
2068 * WDML_AllocTransaction
2070 * Alloc a transaction structure for handling the message ddeMsg
2072 WDML_XACT* WDML_AllocTransaction(WDML_INSTANCE* thisInstance, UINT ddeMsg)
2075 static WORD tid = 1; /* FIXME: wrap around */
2077 pXAct = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_XACT));
2080 thisInstance->lastError = DMLERR_MEMORY_ERROR;
2084 pXAct->xActID = tid++;
2085 pXAct->ddeMsg = ddeMsg;
2086 pXAct->hDdeData = 0;
2093 /******************************************************************
2094 * WDML_QueueTransaction
2096 * Adds a transaction to the list of transaction
2098 void WDML_QueueTransaction(WDML_CONV* pConv, WDML_XACT* pXAct)
2102 /* advance to last in queue */
2103 for (pt = &pConv->transactions; *pt != NULL; pt = &(*pt)->next);
2107 /******************************************************************
2108 * WDML_UnQueueTransaction
2112 BOOL WDML_UnQueueTransaction(WDML_CONV* pConv, WDML_XACT* pXAct)
2116 for (pt = &pConv->transactions; *pt; pt = &(*pt)->next)
2127 /******************************************************************
2128 * WDML_FreeTransaction
2132 void WDML_FreeTransaction(WDML_XACT* pXAct)
2134 HeapFree(GetProcessHeap(), 0, pXAct);
2137 /******************************************************************
2138 * WDML_FindTransaction
2142 WDML_XACT* WDML_FindTransaction(WDML_CONV* pConv, DWORD tid)
2147 for (pXAct = pConv->transactions; pXAct; pXAct = pXAct->next)
2149 if (pXAct->xActID == tid)
2155 struct tagWDML_BroadcastPmt
2163 /******************************************************************
2164 * WDML_BroadcastEnumProc
2168 static BOOL CALLBACK WDML_BroadcastEnumProc(HWND hWnd, LPARAM lParam)
2170 struct tagWDML_BroadcastPmt* s = (struct tagWDML_BroadcastPmt*)lParam;
2173 if (GetClassNameA(hWnd, buffer, sizeof(buffer)) > 0 &&
2174 strcmp(buffer, s->clsName) == 0)
2176 PostMessageA(hWnd, s->uMsg, s->wParam, s->lParam);
2181 /******************************************************************
2182 * WDML_BroadcastDDEWindows
2186 void WDML_BroadcastDDEWindows(const char* clsName, UINT uMsg, WPARAM wParam, LPARAM lParam)
2188 struct tagWDML_BroadcastPmt s;
2190 s.clsName = clsName;
2194 EnumWindows(WDML_BroadcastEnumProc, (LPARAM)&s);