Make sure that HWND comparisons are always done with full 32-bit
[wine/multimedia.git] / dlls / user / dde / misc.c
blob532f005d6a8be2a12ce75895db38c20830f18bd0
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
3 /*
4 * DDEML library
6 * Copyright 1997 Alexandre Julliard
7 * Copyright 1997 Len White
8 * Copyright 1999 Keith Matthews
9 * Copyright 2000 Corel
10 * Copyright 2001 Eric Pouech
13 #include <string.h>
14 #include <stdio.h>
15 #include "winbase.h"
16 #include "windef.h"
17 #include "wingdi.h"
18 #include "winuser.h"
19 #include "winerror.h"
20 #include "dde.h"
21 #include "ddeml.h"
22 #include "win.h"
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)
41 BOOL ret;
43 switch (msg)
45 case WM_DDE_ACK:
46 case WM_DDE_ADVISE:
47 case WM_DDE_DATA:
48 case WM_DDE_POKE:
49 ret = TRUE;
50 break;
51 case WM_DDE_EXECUTE: /* strange, NT 2000 (at least) really uses packing here... */
52 case WM_DDE_INITIATE:
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 */
56 ret = FALSE;
57 break;
58 default:
59 TRACE("Unknown message %04x\n", msg);
60 ret = FALSE;
61 break;
63 return ret;
66 /*****************************************************************
67 * PackDDElParam (USER32.@)
69 * RETURNS
70 * the packed lParam
72 LPARAM WINAPI PackDDElParam(UINT msg, UINT uiLo, UINT uiHi)
74 HGLOBAL hMem;
75 UINT* params;
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");
83 return 0;
86 params = GlobalLock(hMem);
87 if (params == NULL)
89 ERR("GlobalLock failed\n");
90 return 0;
93 params[0] = uiLo;
94 params[1] = uiHi;
96 GlobalUnlock(hMem);
98 return (LPARAM)hMem;
102 /*****************************************************************
103 * UnpackDDElParam (USER32.@)
105 * RETURNS
106 * success: nonzero
107 * failure: zero
109 BOOL WINAPI UnpackDDElParam(UINT msg, LPARAM lParam,
110 PUINT uiLo, PUINT uiHi)
112 HGLOBAL hMem;
113 UINT *params;
115 if (!DDE_RequirePacking(msg))
117 *uiLo = LOWORD(lParam);
118 *uiHi = HIWORD(lParam);
120 return TRUE;
123 if (lParam == 0)
125 return FALSE;
128 hMem = (HGLOBAL)lParam;
130 params = GlobalLock(hMem);
131 if (params == NULL)
133 ERR("GlobalLock failed\n");
134 return FALSE;
137 *uiLo = params[0];
138 *uiHi = params[1];
140 GlobalUnlock(hMem);
142 return TRUE;
146 /*****************************************************************
147 * FreeDDElParam (USER32.@)
149 * RETURNS
150 * success: nonzero
151 * failure: zero
153 BOOL WINAPI FreeDDElParam(UINT msg, LPARAM lParam)
155 HGLOBAL hMem = (HGLOBAL)lParam;
157 if (!DDE_RequirePacking(msg))
158 return TRUE;
160 if (lParam == 0)
162 return FALSE;
164 return GlobalFree(hMem) == (HGLOBAL)NULL;
168 /*****************************************************************
169 * ReuseDDElParam (USER32.@)
171 * RETURNS
172 * the packed lParam
174 LPARAM WINAPI ReuseDDElParam(LPARAM lParam, UINT msgIn, UINT msgOut,
175 UINT uiLo, UINT uiHi)
177 HGLOBAL hMem;
178 UINT* params;
179 BOOL in, out;
181 in = DDE_RequirePacking(msgIn);
182 out = DDE_RequirePacking(msgOut);
184 if (!in)
186 return PackDDElParam(msgOut, uiLo, uiHi);
189 if (lParam == 0)
191 return FALSE;
194 if (!out)
196 FreeDDElParam(msgIn, lParam);
197 return MAKELONG(uiLo, uiHi);
200 hMem = (HGLOBAL)lParam;
202 params = GlobalLock(hMem);
203 if (params == NULL)
205 ERR("GlobalLock failed\n");
206 return 0;
209 params[0] = uiLo;
210 params[1] = uiHi;
212 TRACE("Reusing pack %08x %08x\n", uiLo, uiHi);
214 GlobalLock(hMem);
215 return lParam;
218 /*****************************************************************
219 * ImpersonateDdeClientWindow (USER32.@)
221 * PARAMS
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);
228 return FALSE;
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);
239 return TRUE;
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 /******************************************************************
262 * WDML_EventProc
266 static LRESULT CALLBACK WDML_EventProc(HWND hwndEvent, UINT uMsg, WPARAM wParam, LPARAM lParam)
268 WDML_INSTANCE* pInstance;
269 HSZ hsz1, hsz2;
271 switch (uMsg)
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);
284 break;
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);
296 break;
298 case WM_WDML_CONNECT_CONFIRM:
299 pInstance = WDML_GetInstanceFromWnd(hwndEvent);
300 if (pInstance && !(pInstance->CBFflags & CBF_SKIP_CONNECT_CONFIRMS))
302 WDML_CONV* pConv;
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)
311 break;
313 if (pConv)
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);
322 break;
323 default:
324 return DefWindowProcA(hwndEvent, uMsg, wParam, lParam);
326 return 0;
329 /******************************************************************
330 * WDML_Initialize
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;
339 UINT ret;
340 WNDCLASSEXA wndclass;
342 TRACE("(%p,%p,0x%lx,%ld)\n",
343 pidInst, pfnCallback, afCmd, ulRes);
345 if (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");
395 if (*pidInst == 0)
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
408 * present
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);
421 else
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;
443 goto theError;
446 /* Check 2 - cannot use 'Monitor' with any non-monitor modes */
448 if (pInstance->monitor != reference_inst->monitor)
450 ret = DMLERR_INVALIDPARAMETER;
451 goto theError;
454 /* Check 3 - must supply different callback address */
456 if (pInstance->callback == reference_inst->callback)
458 ret = DMLERR_DLL_USAGE;
459 goto theError;
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);
476 wndclass.style = 0;
477 wndclass.lpfnWndProc = WDML_EventProc;
478 wndclass.cbClsExtra = 0;
479 wndclass.cbWndExtra = sizeof(DWORD);
480 wndclass.hInstance = 0;
481 wndclass.hIcon = 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,
492 0, 0, 0, 0);
494 SetWindowLongA(pInstance->hwndEvent, GWL_WDML_INSTANCE, (DWORD)pInstance);
496 TRACE("New application instance processing finished OK\n");
498 else
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;
508 goto theError;
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;
533 goto theError;
537 /* Check 2 - cannot change monitor modes */
539 if (pInstance->monitor != reference_inst->monitor)
541 ret = DMLERR_DLL_USAGE;
542 goto theError;
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;
550 goto theError;
552 break;
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;
563 goto theError;
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;
574 theError:
575 HeapFree(GetProcessHeap(), 0, pInstance);
576 LeaveCriticalSection(&WDML_CritSect);
577 return ret;
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
593 * PARAMS
594 * pidInst [I] Pointer to instance identifier
595 * pfnCallback [I] Pointer to callback function
596 * afCmd [I] Set of command and filter flags
597 * ulRes [I] Reserved
599 * RETURNS
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
612 * PARAMS
613 * idInst [I] Instance identifier
615 * RETURNS
616 * Success: TRUE
617 * Failure: FALSE
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;
626 WDML_CONV* pConv;
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
640 return FALSE;
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;
672 else
674 /* general case
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);
687 return TRUE;
690 /******************************************************************
691 * WDML_NotifyThreadExit
695 void WDML_NotifyThreadDetach(void)
697 WDML_INSTANCE* pInstance;
698 WDML_INSTANCE* next;
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)
722 HDDEDATA ret;
724 if (pInstance == NULL)
725 return (HDDEDATA)0;
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);
734 else
736 ret = pInstance->callback(uType, uFmt, hConv, hsz1, hsz2, hdata, dwData1, dwData2);
738 TRACE("done => %08lx\n", (DWORD)ret);
739 return ret;
742 /*****************************************************************************
743 * WDML_GetInstance
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");
760 continue;
762 return pInstance;
765 TRACE("Instance entry missing\n");
766 return NULL;
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
782 * PARAMS
783 * idInst [I] Instance identifier
785 * RETURNS
786 * Last error code
788 UINT WINAPI DdeGetLastError(DWORD idInst)
790 DWORD error_code;
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;
804 else
806 error_code = pInstance->lastError;
807 pInstance->lastError = 0;
810 LeaveCriticalSection(&WDML_CritSect);
811 return error_code;
814 /* ================================================================
816 * String management
818 * ================================================================ */
821 /******************************************************************
822 * WDML_FindNode
826 static HSZNode* WDML_FindNode(WDML_INSTANCE* pInstance, HSZ hsz)
828 HSZNode* pNode;
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);
837 return pNode;
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);
853 return 0;
856 /******************************************************************
857 * WDML_MakeHszFromAtom
859 * Creates a HSZ from an existing global atom
860 * Generally used while receiving a global atom and transforming it
861 * into an HSZ
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 /******************************************************************
872 * WDML_IncHSZ
876 BOOL WDML_IncHSZ(WDML_INSTANCE* pInstance, HSZ hsz)
878 HSZNode* pNode;
880 pNode = WDML_FindNode(pInstance, hsz);
881 if (!pNode) return FALSE;
883 pNode->refCount++;
884 return TRUE;
887 /******************************************************************************
888 * WDML_DecHSZ (INTERNAL)
890 * Decrease the ref count of an HSZ. If it reaches 0, the node is removed from the list
891 * of HSZ nodes
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;
897 HSZNode* pCurrent;
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,
902 * we can remove it
904 if (pCurrent->hsz == hsz)
906 if (--pCurrent->refCount == 0)
908 if (pCurrent == pInstance->nodeList)
910 pInstance->nodeList = pCurrent->next;
912 else
914 pPrev->next = pCurrent->next;
916 HeapFree(GetProcessHeap(), 0, pCurrent);
917 DeleteAtom(hsz);
919 return TRUE;
922 WARN("HSZ 0x%xnot found\n", hsz);
924 return FALSE;
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)
950 if (hsz != 0)
952 HSZNode* pNew = NULL;
953 /* Create a new node for this HSZ.
955 pNew = (HSZNode*)HeapAlloc(GetProcessHeap(), 0, sizeof(HSZNode));
956 if (pNew != NULL)
958 pNew->hsz = hsz;
959 pNew->next = pInstance->nodeList;
960 pNew->refCount = 1;
961 pInstance->nodeList = pNew;
963 else
965 ERR("Primary HSZ Node allocation failed - out of memory\n");
970 /******************************************************************
971 * WDML_QueryString
975 static int WDML_QueryString(WDML_INSTANCE* pInstance, HSZ hsz, LPVOID ptr, DWORD cchMax,
976 int codepage)
978 WCHAR pString[MAX_BUFFER_LEN];
979 int ret;
980 /* If psz is null, we have to return only the length
981 * of the string.
983 if (ptr == NULL)
985 ptr = pString;
986 cchMax = MAX_BUFFER_LEN;
989 switch (codepage)
991 case CP_WINANSI:
992 ret = GetAtomNameA(hsz, ptr, cchMax);
993 break;
994 case CP_WINUNICODE:
995 ret = GetAtomNameW(hsz, ptr, cchMax);
996 default:
997 ERR("Unknown code page %d\n", codepage);
998 ret = 0;
1000 return ret;
1003 /*****************************************************************
1004 * DdeQueryStringA [USER32.@]
1006 DWORD WINAPI DdeQueryStringA(DWORD idInst, HSZ hsz, LPSTR psz, DWORD cchMax, INT iCodePage)
1008 DWORD ret = 0;
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));
1026 return ret;
1029 /*****************************************************************
1030 * DdeQueryStringW [USER32.@]
1033 DWORD WINAPI DdeQueryStringW(DWORD idInst, HSZ hsz, LPWSTR psz, DWORD cchMax, INT iCodePage)
1035 DWORD ret = 0;
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));
1054 return ret;
1057 /******************************************************************
1058 * DML_CreateString
1062 static HSZ WDML_CreateString(WDML_INSTANCE* pInstance, LPCVOID ptr, int codepage)
1064 HSZ hsz;
1066 switch (codepage)
1068 case CP_WINANSI:
1069 hsz = AddAtomA(ptr);
1070 TRACE("added atom %s with HSZ 0x%x, \n", debugstr_a(ptr), hsz);
1071 break;
1072 case CP_WINUNICODE:
1073 hsz = AddAtomW(ptr);
1074 TRACE("added atom %s with HSZ 0x%x, \n", debugstr_w(ptr), hsz);
1075 break;
1076 default:
1077 ERR("Unknown code page %d\n", codepage);
1078 return 0;
1080 WDML_InsertHSZNode(pInstance, hsz);
1081 return hsz;
1084 /*****************************************************************
1085 * DdeCreateStringHandleA [USER32.@]
1087 * RETURNS
1088 * Success: String handle
1089 * Failure: 0
1091 HSZ WINAPI DdeCreateStringHandleA(DWORD idInst, LPCSTR psz, INT codepage)
1093 HSZ hsz = 0;
1094 WDML_INSTANCE* pInstance;
1096 TRACE("(%ld,%p,%d)\n", idInst, psz, codepage);
1098 EnterCriticalSection(&WDML_CritSect);
1100 pInstance = WDML_GetInstance(idInst);
1101 if (pInstance)
1103 if (codepage == 0) codepage = CP_WINANSI;
1104 hsz = WDML_CreateString(pInstance, psz, codepage);
1107 LeaveCriticalSection(&WDML_CritSect);
1108 return hsz;
1112 /******************************************************************************
1113 * DdeCreateStringHandleW [USER32.@] Creates handle to identify string
1115 * PARAMS
1116 * idInst [I] Instance identifier
1117 * psz [I] Pointer to string
1118 * codepage [I] Code page identifier
1119 * RETURNS
1120 * Success: String handle
1121 * Failure: 0
1123 HSZ WINAPI DdeCreateStringHandleW(DWORD idInst, LPCWSTR psz, INT codepage)
1125 WDML_INSTANCE* pInstance;
1126 HSZ hsz = 0;
1128 TRACE("(%ld,%p,%d)\n", idInst, psz, codepage);
1130 EnterCriticalSection(&WDML_CritSect);
1132 pInstance = WDML_GetInstance(idInst);
1133 if (pInstance)
1135 if (codepage == 0) codepage = CP_WINUNICODE;
1136 hsz = WDML_CreateString(pInstance, psz, codepage);
1138 LeaveCriticalSection(&WDML_CritSect);
1140 return hsz;
1143 /*****************************************************************
1144 * DdeFreeStringHandle (USER32.@)
1145 * RETURNS: success: nonzero
1146 * fail: zero
1148 BOOL WINAPI DdeFreeStringHandle(DWORD idInst, HSZ hsz)
1150 WDML_INSTANCE* pInstance;
1151 BOOL ret = FALSE;
1153 TRACE("(%ld,0x%x): \n", idInst, hsz);
1155 EnterCriticalSection(&WDML_CritSect);
1157 /* First check instance
1159 pInstance = WDML_GetInstance(idInst);
1160 if (pInstance)
1161 ret = WDML_DecHSZ(pInstance, hsz);
1163 LeaveCriticalSection(&WDML_CritSect);
1165 return ret;
1168 /*****************************************************************
1169 * DdeKeepStringHandle (USER32.@)
1171 * RETURNS: success: nonzero
1172 * fail: zero
1174 BOOL WINAPI DdeKeepStringHandle(DWORD idInst, HSZ hsz)
1176 WDML_INSTANCE* pInstance;
1177 BOOL ret = FALSE;
1179 TRACE("(%ld,0x%x): \n", idInst, hsz);
1181 EnterCriticalSection(&WDML_CritSect);
1183 /* First check instance
1185 pInstance = WDML_GetInstance(idInst);
1186 if (pInstance)
1187 ret = WDML_IncHSZ(pInstance, hsz);
1189 LeaveCriticalSection(&WDML_CritSect);
1190 return ret;
1193 /*****************************************************************
1194 * DdeCmpStringHandles (USER32.@)
1196 * Compares the value of two string handles. This comparison is
1197 * not case sensitive.
1199 * Returns:
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];
1208 int ret = 0;
1209 int ret1, ret2;
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". */
1220 ret = 0;
1222 else if (ret1 == 0)
1224 /* If hsz1 is a not found, return hsz1 is "zero string". */
1225 ret = -1;
1227 else if (ret2 == 0)
1229 /* If hsz2 is a not found, return hsz2 is "zero string". */
1230 ret = 1;
1232 else
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.
1241 if (ret < 0)
1243 ret = -1;
1245 else if (ret > 0)
1247 ret = 1;
1251 return ret;
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,
1265 UINT afCmd)
1268 For now, we ignore idInst, hszItem, wFmt, and afCmd.
1269 The purpose of these arguments still need to be investigated.
1272 HGLOBAL hMem;
1273 LPBYTE pByte;
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");
1283 return 0;
1286 pDdh = (DDE_DATAHANDLE_HEAD*)GlobalLock(hMem);
1287 pDdh->cfFormat = wFmt;
1289 pByte = (LPBYTE)(pDdh + 1);
1290 if (pSrc)
1292 memcpy(pByte, pSrc + cbOff, cb);
1294 GlobalUnlock(hMem);
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;
1306 LPBYTE pDst;
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);
1324 return hData;
1327 /******************************************************************************
1328 * DdeGetData [USER32.@] Copies data from DDE object to local buffer
1331 * PARAMS
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
1337 * RETURNS
1338 * Size of memory object associated with handle
1340 DWORD WINAPI DdeGetData(HDDEDATA hData, LPBYTE pDst, DWORD cbMax, DWORD cbOff)
1342 DWORD dwSize, dwRet;
1343 LPBYTE pByte;
1345 TRACE("(%08lx,%p,%ld,%ld)\n",(DWORD)hData,pDst,cbMax,cbOff);
1347 pByte = DdeAccessData(hData, &dwSize);
1349 if (pByte)
1351 if (cbOff + cbMax < dwSize)
1353 dwRet = cbMax;
1355 else if (cbOff < dwSize)
1357 dwRet = dwSize - cbOff;
1359 else
1361 dwRet = 0;
1363 if (pDst && dwRet != 0)
1365 memcpy(pDst, pByte + cbOff, dwRet);
1367 DdeUnaccessData(hData);
1369 else
1371 dwRet = 0;
1373 return dwRet;
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);
1387 if (pDdh == NULL)
1389 ERR("Failed on GlobalLock(%04x)\n", hMem);
1390 return 0;
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);
1410 GlobalUnlock(hMem);
1412 return TRUE;
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:
1430 * offset size
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)
1438 DDEDATA* pDd;
1439 HDDEDATA ret = 0;
1441 if (hMem)
1443 pDd = GlobalLock(hMem);
1444 if (pDd)
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);
1450 GlobalUnlock(hMem);
1453 return ret;
1456 /******************************************************************
1457 * WDML_DataHandle2Global
1461 HGLOBAL WDML_DataHandle2Global(HDDEDATA hDdeData, BOOL fResponse, BOOL fRelease,
1462 BOOL fDeferUpd, BOOL fAckReq)
1464 DDE_DATAHANDLE_HEAD* pDdh;
1465 DWORD dwSize;
1466 HGLOBAL hMem = 0;
1468 dwSize = GlobalSize(hDdeData) - sizeof(DDE_DATAHANDLE_HEAD);
1469 pDdh = (DDE_DATAHANDLE_HEAD*)GlobalLock(hDdeData);
1470 if (dwSize && pDdh)
1472 hMem = GlobalAlloc(sizeof(WINE_DDEHEAD) + dwSize, GMEM_MOVEABLE | GMEM_DDESHARE);
1473 if (hMem)
1475 WINE_DDEHEAD* wdh;
1477 wdh = GlobalLock(hMem);
1478 if (wdh)
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);
1486 GlobalUnlock(hMem);
1489 GlobalUnlock(hDdeData);
1492 return hMem;
1495 /* ================================================================
1497 * Server management
1499 * ================================================================ */
1501 /******************************************************************
1502 * WDML_AddServer
1506 WDML_SERVER* WDML_AddServer(WDML_INSTANCE* pInstance, HSZ hszService, HSZ hszTopic)
1508 WDML_SERVER* pServer;
1509 char buf1[256];
1510 char buf2[256];
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;
1528 return pServer;
1531 /******************************************************************
1532 * WDML_RemoveServer
1536 void WDML_RemoveServer(WDML_INSTANCE* pInstance, HSZ hszService, HSZ hszTopic)
1538 WDML_SERVER* pPrev = NULL;
1539 WDML_SERVER* pServer = NULL;
1540 WDML_CONV* pConv;
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;
1566 else
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);
1579 break;
1582 pPrev = pServer;
1583 pServer = pServer->next;
1587 /*****************************************************************************
1588 * WDML_FindServer
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)
1602 return pServer;
1605 TRACE("Service name missing\n");
1606 return NULL;
1609 /* ================================================================
1611 * Conversation management
1613 * ================================================================ */
1615 /******************************************************************
1616 * WDML_AddConv
1620 WDML_CONV* WDML_AddConv(WDML_INSTANCE* pInstance, WDML_SIDE side,
1621 HSZ hszService, HSZ hszTopic, HWND hwndClient, HWND hwndServer)
1623 WDML_CONV* pConv;
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;
1635 pConv->hUser = 0;
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;
1648 return pConv;
1651 /******************************************************************
1652 * WDML_FindConv
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)
1666 return pCurrent;
1670 return NULL;
1673 /******************************************************************
1674 * WDML_RemoveConv
1678 void WDML_RemoveConv(WDML_CONV* pRef, WDML_SIDE side)
1680 WDML_CONV* pPrev = NULL;
1681 WDML_CONV* pCurrent;
1682 WDML_XACT* pXAct;
1683 WDML_XACT* pXActNext;
1684 HWND hWnd;
1686 if (!pRef)
1687 return;
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
1700 * DdeReconnect...
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;
1721 else
1723 pPrev->next = pCurrent->next;
1726 HeapFree(GetProcessHeap(), 0, pCurrent);
1727 break;
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);
1739 return 0;
1742 /******************************************************************
1743 * WDML_GetConv
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");
1756 return NULL;
1758 if (GetCurrentThreadId() != pConv->instance->threadID)
1760 FIXME("wrong thread ID\n");
1761 return NULL;
1764 return pConv;
1767 /******************************************************************
1768 * WDML_GetConvFromWnd
1772 WDML_CONV* WDML_GetConvFromWnd(HWND hWnd)
1774 return (WDML_CONV*)GetWindowLongA(hWnd, GWL_WDML_CONVERSATION);
1777 /******************************************************************
1778 * WDML_PostAck
1782 LPARAM WDML_PostAck(WDML_CONV* pConv, WDML_SIDE side, WORD appRetCode,
1783 BOOL fBusy, BOOL fAck, ATOM atom, LPARAM lParam, UINT oldMsg)
1785 DDEACK ddeAck;
1786 HWND from, to;
1788 if (side == WDML_SERVER_SIDE)
1790 from = pConv->hwndServer;
1791 to = pConv->hwndClient;
1793 else
1795 to = pConv->hwndServer;
1796 from = pConv->hwndClient;
1799 ddeAck.bAppReturnCode = appRetCode;
1800 ddeAck.reserved = 0;
1801 ddeAck.fBusy = fBusy;
1802 ddeAck.fAck = fAck;
1804 TRACE("Posting a %s ack\n", ddeAck.fAck ? "positive" : "negative");
1806 if (lParam) {
1807 PostMessageA(to, WM_DDE_ACK, (WPARAM)from,
1808 ReuseDDElParam(lParam, oldMsg, WM_DDE_ACK, *(WORD*)&ddeAck, atom));
1810 else
1812 lParam = PackDDElParam(WM_DDE_ACK, *(WORD*)&ddeAck, atom);
1813 PostMessageA(to, WM_DDE_ACK, (WPARAM)from, lParam);
1815 return lParam;
1818 /*****************************************************************
1819 * DdeSetUserHandle (USER32.@)
1821 BOOL WINAPI DdeSetUserHandle(HCONV hConv, DWORD id, DWORD hUser)
1823 WDML_CONV* pConv;
1824 BOOL ret = TRUE;
1826 EnterCriticalSection(&WDML_CritSect);
1828 pConv = WDML_GetConv(hConv, FALSE);
1829 if (pConv == NULL)
1831 ret = FALSE;
1832 goto theError;
1834 if (id == QID_SYNC)
1836 pConv->hUser = hUser;
1838 else
1840 WDML_XACT* pXAct;
1842 pXAct = WDML_FindTransaction(pConv, id);
1843 if (pXAct)
1845 pXAct->hUser = hUser;
1847 else
1849 pConv->instance->lastError = DMLERR_UNFOUND_QUEUE_ID;
1850 ret = FALSE;
1853 theError:
1854 LeaveCriticalSection(&WDML_CritSect);
1855 return ret;
1858 /******************************************************************
1859 * WDML_GetLocalConvInfo
1863 static BOOL WDML_GetLocalConvInfo(WDML_CONV* pConv, CONVINFO* ci, DWORD id)
1865 BOOL ret = TRUE;
1866 WDML_LINK* pLink;
1867 WDML_SIDE side;
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;
1882 break;
1886 /* FIXME: non handled status flags:
1887 ST_BLOCKED
1888 ST_BLOCKNEXT
1889 ST_INLIST
1892 ci->wConvst = pConv->wConvst; /* FIXME */
1894 ci->wLastError = 0; /* FIXME: note it's not the instance last error */
1895 ci->hConvList = 0;
1896 ci->ConvCtxt = pConv->convContext;
1897 if (ci->wStatus & ST_CLIENT)
1899 ci->hwnd = pConv->hwndClient;
1900 ci->hwndPartner = pConv->hwndServer;
1902 else
1904 ci->hwnd = pConv->hwndServer;
1905 ci->hwndPartner = pConv->hwndClient;
1907 if (id == QID_SYNC)
1909 ci->hUser = pConv->hUser;
1910 ci->hszItem = 0;
1911 ci->wFmt = 0;
1912 ci->wType = 0;
1914 else
1916 WDML_XACT* pXAct;
1918 pXAct = WDML_FindTransaction(pConv, id);
1919 if (pXAct)
1921 ci->hUser = pXAct->hUser;
1922 ci->hszItem = pXAct->hszItem;
1923 ci->wFmt = pXAct->wFmt;
1924 ci->wType = pXAct->wType;
1926 else
1928 ret = 0;
1929 pConv->instance->lastError = DMLERR_UNFOUND_QUEUE_ID;
1932 return ret;
1935 /******************************************************************
1936 * DdeQueryConvInfo (USER32.@)
1939 UINT WINAPI DdeQueryConvInfo(HCONV hConv, DWORD id, LPCONVINFO lpConvInfo)
1941 UINT ret = lpConvInfo->cb;
1942 CONVINFO ci;
1943 WDML_CONV* pConv;
1945 EnterCriticalSection(&WDML_CritSect);
1947 pConv = WDML_GetConv(hConv, FALSE);
1948 if (pConv != NULL && !WDML_GetLocalConvInfo(pConv, &ci, id))
1950 ret = 0;
1952 else if (hConv & 1)
1954 pConv = WDML_GetConv(hConv & ~1, FALSE);
1955 if (pConv != NULL)
1957 FIXME("Request on remote conversation information is not implemented yet\n");
1958 ret = 0;
1961 LeaveCriticalSection(&WDML_CritSect);
1962 if (ret != 0)
1963 memcpy(lpConvInfo, &ci, min(lpConvInfo->cb, sizeof(ci)));
1964 return ret;
1967 /* ================================================================
1969 * Link (hot & warm) management
1971 * ================================================================ */
1973 /******************************************************************
1974 * WDML_AddLink
1978 void WDML_AddLink(WDML_INSTANCE* pInstance, HCONV hConv, WDML_SIDE side,
1979 UINT wType, HSZ hszItem, UINT wFmt)
1981 WDML_LINK* pLink;
1983 pLink = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_LINK));
1984 if (pLink == NULL)
1986 ERR("OOM\n");
1987 return;
1990 pLink->hConv = hConv;
1991 pLink->transactionType = wType;
1992 WDML_IncHSZ(pInstance, pLink->hszItem = hszItem);
1993 pLink->uFmt = wFmt;
1994 pLink->hDdeData = 0;
1995 pLink->next = pInstance->links[side];
1996 pInstance->links[side] = pLink;
1999 /******************************************************************
2000 * WDML_RemoveLink
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;
2022 else
2024 pPrev->next = pCurrent->next;
2027 if (pCurrent->hDdeData)
2029 DdeFreeDataHandle(pCurrent->hDdeData);
2032 WDML_DecHSZ(pInstance, pCurrent->hszItem);
2033 HeapFree(GetProcessHeap(), 0, pCurrent);
2034 break;
2037 pPrev = 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
2044 the conversation.
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;
2068 else
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);
2081 pCurrent = NULL;
2084 if (pCurrent)
2086 pPrev = pCurrent;
2087 pCurrent = pCurrent->next;
2089 else
2091 pCurrent = pNext;
2096 /******************************************************************
2097 * WDML_FindLink
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)
2114 break;
2119 return pCurrent;
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)
2136 WDML_XACT* pXAct;
2137 static WORD tid = 1; /* FIXME: wrap around */
2139 pXAct = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_XACT));
2140 if (!pXAct)
2142 pInstance->lastError = DMLERR_MEMORY_ERROR;
2143 return NULL;
2146 pXAct->xActID = tid++;
2147 pXAct->ddeMsg = ddeMsg;
2148 pXAct->hDdeData = 0;
2149 pXAct->hUser = 0;
2150 pXAct->next = NULL;
2151 pXAct->wType = 0;
2152 pXAct->wFmt = wFmt;
2153 WDML_IncHSZ(pInstance, pXAct->hszItem = hszItem);
2154 pXAct->atom = 0;
2155 pXAct->hMem = 0;
2156 pXAct->lParam = 0;
2158 return pXAct;
2161 /******************************************************************
2162 * WDML_QueueTransaction
2164 * Adds a transaction to the list of transaction
2166 void WDML_QueueTransaction(WDML_CONV* pConv, WDML_XACT* pXAct)
2168 WDML_XACT** pt;
2170 /* advance to last in queue */
2171 for (pt = &pConv->transactions; *pt != NULL; pt = &(*pt)->next);
2172 *pt = pXAct;
2175 /******************************************************************
2176 * WDML_UnQueueTransaction
2180 BOOL WDML_UnQueueTransaction(WDML_CONV* pConv, WDML_XACT* pXAct)
2182 WDML_XACT** pt;
2184 for (pt = &pConv->transactions; *pt; pt = &(*pt)->next)
2186 if (*pt == pXAct)
2188 *pt = pXAct->next;
2189 return TRUE;
2192 return FALSE;
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)
2217 WDML_XACT* pXAct;
2219 tid = HIWORD(tid);
2220 for (pXAct = pConv->transactions; pXAct; pXAct = pXAct->next)
2222 if (pXAct->xActID == tid)
2223 break;
2225 return pXAct;
2228 /* ================================================================
2230 * Information broadcast across DDEML implementations
2232 * ================================================================ */
2234 struct tagWDML_BroadcastPmt
2236 LPCSTR clsName;
2237 UINT uMsg;
2238 WPARAM wParam;
2239 LPARAM lParam;
2242 /******************************************************************
2243 * WDML_BroadcastEnumProc
2247 static BOOL CALLBACK WDML_BroadcastEnumProc(HWND hWnd, LPARAM lParam)
2249 struct tagWDML_BroadcastPmt* s = (struct tagWDML_BroadcastPmt*)lParam;
2250 char buffer[128];
2252 if (GetClassNameA(hWnd, buffer, sizeof(buffer)) > 0 &&
2253 strcmp(buffer, s->clsName) == 0)
2255 PostMessageA(hWnd, s->uMsg, s->wParam, s->lParam);
2257 return TRUE;
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;
2270 s.uMsg = uMsg;
2271 s.wParam = wParam;
2272 s.lParam = lParam;
2273 EnumWindows(WDML_BroadcastEnumProc, (LPARAM)&s);