75f48955013644eb0c138a1b1a992e80e3cafd83
[reactos.git] / reactos / lib / user32 / misc / dde.c
1 /*
2 * DDEML library
3 *
4 * Copyright 1997 Alexandre Julliard
5 * Copyright 1997 Len White
6 * Copyright 1999 Keith Matthews
7 * Copyright 2000 Corel
8 * Copyright 2001 Eric Pouech
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 */
24
25 #include "wine/config.h"
26 #include "wine/port.h"
27
28 #include <string.h>
29 #include <stdarg.h>
30 #include <stdio.h>
31 #include "windef.h"
32 #include "winbase.h"
33 #include "wingdi.h"
34 #include "winuser.h"
35 #include "winerror.h"
36 #include "dde.h"
37 #include "ddeml.h"
38 #include "wine/debug.h"
39 #include "dde_private.h"
40
41 WINE_DEFAULT_DEBUG_CHANNEL(ddeml);
42
43 /* convert between ATOM and HSZ avoiding compiler warnings */
44 #define ATOM2HSZ(atom) ((HSZ) (ULONG_PTR)(atom))
45 #define HSZ2ATOM(hsz) ((ATOM) (ULONG_PTR)(hsz))
46
47 static WDML_INSTANCE* WDML_InstanceList = NULL;
48 static DWORD WDML_MaxInstanceID = 0; /* OK for present, have to worry about wrap-around later */
49 const char WDML_szEventClass[] = "DdeEventClass";
50
51 static CRITICAL_SECTION_DEBUG critsect_debug =
52 {
53 0, 0, &WDML_CritSect,
54 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
55 0, 0, { 0, (DWORD)(__FILE__ ": WDML_CritSect") }
56 };
57 CRITICAL_SECTION WDML_CritSect = { &critsect_debug, -1, 0, 0, 0, 0 };
58
59 /* ================================================================
60 *
61 * Pure DDE (non DDEML) management
62 *
63 * ================================================================ */
64
65
66 /*****************************************************************
67 * PackDDElParam (USER32.@)
68 *
69 * RETURNS
70 * the packed lParam
71 */
72 LPARAM WINAPI PackDDElParam(UINT msg, UINT_PTR uiLo, UINT_PTR uiHi)
73 {
74 HGLOBAL hMem;
75 UINT_PTR *params;
76
77 switch (msg)
78 {
79 case WM_DDE_ACK:
80 case WM_DDE_ADVISE:
81 case WM_DDE_DATA:
82 case WM_DDE_POKE:
83 if (!(hMem = GlobalAlloc(GMEM_DDESHARE, sizeof(UINT_PTR) * 2)))
84 {
85 ERR("GlobalAlloc failed\n");
86 return 0;
87 }
88 if (!(params = GlobalLock(hMem)))
89 {
90 ERR("GlobalLock failed (%p)\n", hMem);
91 return 0;
92 }
93 params[0] = uiLo;
94 params[1] = uiHi;
95 GlobalUnlock(hMem);
96 return (LPARAM)hMem;
97
98 case WM_DDE_EXECUTE:
99 return uiHi;
100
101 default:
102 return MAKELPARAM(uiLo, uiHi);
103 }
104 }
105
106
107 /*****************************************************************
108 * UnpackDDElParam (USER32.@)
109 *
110 * RETURNS
111 * success: nonzero
112 * failure: zero
113 */
114 BOOL WINAPI UnpackDDElParam(UINT msg, LPARAM lParam,
115 PUINT_PTR uiLo, PUINT_PTR uiHi)
116 {
117 UINT_PTR *params;
118
119 switch (msg)
120 {
121 case WM_DDE_ACK:
122 case WM_DDE_ADVISE:
123 case WM_DDE_DATA:
124 case WM_DDE_POKE:
125 if (!lParam) return FALSE;
126 if (!(params = GlobalLock( (HGLOBAL)lParam )))
127 {
128 ERR("GlobalLock failed (%lx)\n", lParam);
129 return FALSE;
130 }
131 TRACE("unpacked: low %08x, high %08x\n", params[0], params[1]);
132 if (uiLo) *uiLo = params[0];
133 if (uiHi) *uiHi = params[1];
134 GlobalUnlock( (HGLOBAL)lParam );
135 return TRUE;
136
137 case WM_DDE_EXECUTE:
138 if (uiLo) *uiLo = 0;
139 if (uiHi) *uiHi = lParam;
140 return TRUE;
141
142 default:
143 if (uiLo) *uiLo = LOWORD(lParam);
144 if (uiHi) *uiHi = HIWORD(lParam);
145 return TRUE;
146 }
147 }
148
149
150 /*****************************************************************
151 * FreeDDElParam (USER32.@)
152 *
153 * RETURNS
154 * success: nonzero
155 * failure: zero
156 */
157 BOOL WINAPI FreeDDElParam(UINT msg, LPARAM lParam)
158 {
159 switch (msg)
160 {
161 case WM_DDE_ACK:
162 case WM_DDE_ADVISE:
163 case WM_DDE_DATA:
164 case WM_DDE_POKE:
165 /* first check if it's a global handle */
166 if (!GlobalHandle( (LPVOID)lParam )) return TRUE;
167 return !GlobalFree( (HGLOBAL)lParam );
168
169 default:
170 return TRUE;
171 }
172 }
173
174
175 /*****************************************************************
176 * ReuseDDElParam (USER32.@)
177 *
178 * RETURNS
179 * the packed lParam
180 */
181 LPARAM WINAPI ReuseDDElParam(LPARAM lParam, UINT msgIn, UINT msgOut,
182 UINT_PTR uiLo, UINT_PTR uiHi)
183 {
184 UINT_PTR *params;
185
186 switch (msgIn)
187 {
188 case WM_DDE_ACK:
189 case WM_DDE_ADVISE:
190 case WM_DDE_DATA:
191 case WM_DDE_POKE:
192 switch(msgOut)
193 {
194 case WM_DDE_ACK:
195 case WM_DDE_ADVISE:
196 case WM_DDE_DATA:
197 case WM_DDE_POKE:
198 if (!lParam) return 0;
199 if (!(params = GlobalLock( (HGLOBAL)lParam )))
200 {
201 ERR("GlobalLock failed\n");
202 return 0;
203 }
204 params[0] = uiLo;
205 params[1] = uiHi;
206 TRACE("Reusing pack %08x %08x\n", uiLo, uiHi);
207 GlobalLock( (HGLOBAL)lParam );
208 return lParam;
209
210 case WM_DDE_EXECUTE:
211 FreeDDElParam( msgIn, lParam );
212 return uiHi;
213
214 default:
215 FreeDDElParam( msgIn, lParam );
216 return MAKELPARAM(uiLo, uiHi);
217 }
218
219 default:
220 return PackDDElParam( msgOut, uiLo, uiHi );
221 }
222 }
223
224 /*****************************************************************
225 * ImpersonateDdeClientWindow (USER32.@)
226 *
227 * PARAMS
228 * hWndClient [I] handle to DDE client window
229 * hWndServer [I] handle to DDE server window
230 */
231 BOOL WINAPI ImpersonateDdeClientWindow(HWND hWndClient, HWND hWndServer)
232 {
233 FIXME("(%p %p): stub\n", hWndClient, hWndServer);
234 return FALSE;
235 }
236
237 /*****************************************************************
238 * DdeSetQualityOfService (USER32.@)
239 */
240
241 BOOL WINAPI DdeSetQualityOfService(HWND hwndClient, CONST SECURITY_QUALITY_OF_SERVICE *pqosNew,
242 PSECURITY_QUALITY_OF_SERVICE pqosPrev)
243 {
244 FIXME("(%p %p %p): stub\n", hwndClient, pqosNew, pqosPrev);
245 return TRUE;
246 }
247
248 /* ================================================================
249 *
250 * Instance management
251 *
252 * ================================================================ */
253
254 /******************************************************************************
255 * IncrementInstanceId
256 *
257 * generic routine to increment the max instance Id and allocate a new application instance
258 */
259 static void WDML_IncrementInstanceId(WDML_INSTANCE* pInstance)
260 {
261 DWORD id = InterlockedIncrement((PLONG)&WDML_MaxInstanceID);
262
263 pInstance->instanceID = id;
264 TRACE("New instance id %ld allocated\n", id);
265 }
266
267 /******************************************************************
268 * WDML_EventProc
269 *
270 *
271 */
272 static LRESULT CALLBACK WDML_EventProc(HWND hwndEvent, UINT uMsg, WPARAM wParam, LPARAM lParam)
273 {
274 WDML_INSTANCE* pInstance;
275 HSZ hsz1, hsz2;
276
277 switch (uMsg)
278 {
279 case WM_WDML_REGISTER:
280 pInstance = WDML_GetInstanceFromWnd(hwndEvent);
281 /* try calling the Callback */
282 if (pInstance && !(pInstance->CBFflags & CBF_SKIP_REGISTRATIONS))
283 {
284 hsz1 = WDML_MakeHszFromAtom(pInstance, wParam);
285 hsz2 = WDML_MakeHszFromAtom(pInstance, lParam);
286 WDML_InvokeCallback(pInstance, XTYP_REGISTER, 0, 0, hsz1, hsz2, 0, 0, 0);
287 WDML_DecHSZ(pInstance, hsz1);
288 WDML_DecHSZ(pInstance, hsz2);
289 }
290 break;
291
292 case WM_WDML_UNREGISTER:
293 pInstance = WDML_GetInstanceFromWnd(hwndEvent);
294 if (pInstance && !(pInstance->CBFflags & CBF_SKIP_UNREGISTRATIONS))
295 {
296 hsz1 = WDML_MakeHszFromAtom(pInstance, wParam);
297 hsz2 = WDML_MakeHszFromAtom(pInstance, lParam);
298 WDML_InvokeCallback(pInstance, XTYP_UNREGISTER, 0, 0, hsz1, hsz2, 0, 0, 0);
299 WDML_DecHSZ(pInstance, hsz1);
300 WDML_DecHSZ(pInstance, hsz2);
301 }
302 break;
303
304 case WM_WDML_CONNECT_CONFIRM:
305 pInstance = WDML_GetInstanceFromWnd(hwndEvent);
306 if (pInstance && !(pInstance->CBFflags & CBF_SKIP_CONNECT_CONFIRMS))
307 {
308 WDML_CONV* pConv;
309 /* confirm connection...
310 * lookup for this conv handle
311 */
312 HWND client = (HWND)wParam;
313 HWND server = (HWND)lParam;
314 for (pConv = pInstance->convs[WDML_SERVER_SIDE]; pConv != NULL; pConv = pConv->next)
315 {
316 if (pConv->hwndClient == client && pConv->hwndServer == server)
317 break;
318 }
319 if (pConv)
320 {
321 pConv->wStatus |= ST_ISLOCAL;
322
323 WDML_InvokeCallback(pInstance, XTYP_CONNECT_CONFIRM, 0, (HCONV)pConv,
324 pConv->hszTopic, pConv->hszService, 0, 0,
325 (pConv->wStatus & ST_ISSELF) ? 1 : 0);
326 }
327 }
328 break;
329 default:
330 return DefWindowProcA(hwndEvent, uMsg, wParam, lParam);
331 }
332 return 0;
333 }
334
335 /******************************************************************
336 * WDML_Initialize
337 *
338 *
339 */
340 UINT WDML_Initialize(LPDWORD pidInst, PFNCALLBACK pfnCallback,
341 DWORD afCmd, DWORD ulRes, BOOL bUnicode, BOOL b16)
342 {
343 WDML_INSTANCE* pInstance;
344 WDML_INSTANCE* reference_inst;
345 UINT ret;
346 WNDCLASSEXA wndclass;
347
348 TRACE("(%p,%p,0x%lx,%ld)\n",
349 pidInst, pfnCallback, afCmd, ulRes);
350
351 if (ulRes)
352 {
353 ERR("Reserved value not zero? What does this mean?\n");
354 /* trap this and no more until we know more */
355 return DMLERR_NO_ERROR;
356 }
357
358 /* grab enough heap for one control struct - not really necessary for re-initialise
359 * but allows us to use same validation routines */
360 pInstance = (WDML_INSTANCE*)HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_INSTANCE));
361 if (pInstance == NULL)
362 {
363 /* catastrophe !! warn user & abort */
364 ERR("Instance create failed - out of memory\n");
365 return DMLERR_SYS_ERROR;
366 }
367 pInstance->next = NULL;
368 pInstance->monitor = (afCmd | APPCLASS_MONITOR);
369
370 /* messy bit, spec implies that 'Client Only' can be set in 2 different ways, catch 1 here */
371
372 pInstance->clientOnly = afCmd & APPCMD_CLIENTONLY;
373 pInstance->instanceID = *pidInst; /* May need to add calling proc Id */
374 pInstance->threadID = GetCurrentThreadId();
375 pInstance->callback = *pfnCallback;
376 pInstance->unicode = bUnicode;
377 pInstance->win16 = b16;
378 pInstance->nodeList = NULL; /* node will be added later */
379 pInstance->monitorFlags = afCmd & MF_MASK;
380 pInstance->servers = NULL;
381 pInstance->convs[0] = NULL;
382 pInstance->convs[1] = NULL;
383 pInstance->links[0] = NULL;
384 pInstance->links[1] = NULL;
385
386 /* isolate CBF flags in one go, expect this will go the way of all attempts to be clever !! */
387
388 pInstance->CBFflags = afCmd^((afCmd&MF_MASK)|((afCmd&APPCMD_MASK)|(afCmd&APPCLASS_MASK)));
389
390 if (!pInstance->clientOnly)
391 {
392 /* Check for other way of setting Client-only !! */
393 pInstance->clientOnly =
394 (pInstance->CBFflags & CBF_FAIL_ALLSVRXACTIONS) == CBF_FAIL_ALLSVRXACTIONS;
395 }
396
397 TRACE("instance created - checking validity \n");
398
399 if (*pidInst == 0)
400 {
401 /* Initialisation of new Instance Identifier */
402 TRACE("new instance, callback %p flags %lX\n",pfnCallback,afCmd);
403
404 EnterCriticalSection(&WDML_CritSect);
405
406 if (WDML_InstanceList == NULL)
407 {
408 /* can't be another instance in this case, assign to the base pointer */
409 WDML_InstanceList = pInstance;
410
411 /* since first must force filter of XTYP_CONNECT and XTYP_WILDCONNECT for
412 * present
413 * ------------------------------- NOTE NOTE NOTE --------------------------
414 *
415 * the manual is not clear if this condition
416 * applies to the first call to DdeInitialize from an application, or the
417 * first call for a given callback !!!
418 */
419
420 pInstance->CBFflags = pInstance->CBFflags|APPCMD_FILTERINITS;
421 TRACE("First application instance detected OK\n");
422 /* allocate new instance ID */
423 WDML_IncrementInstanceId(pInstance);
424 }
425 else
426 {
427 /* really need to chain the new one in to the latest here, but after checking conditions
428 * such as trying to start a conversation from an application trying to monitor */
429 reference_inst = WDML_InstanceList;
430 TRACE("Subsequent application instance - starting checks\n");
431 while (reference_inst->next != NULL)
432 {
433 /*
434 * This set of tests will work if application uses same instance Id
435 * at application level once allocated - which is what manual implies
436 * should happen. If someone tries to be
437 * clever (lazy ?) it will fail to pick up that later calls are for
438 * the same application - should we trust them ?
439 */
440 if (pInstance->instanceID == reference_inst->instanceID)
441 {
442 /* Check 1 - must be same Client-only state */
443
444 if (pInstance->clientOnly != reference_inst->clientOnly)
445 {
446 ret = DMLERR_DLL_USAGE;
447 goto theError;
448 }
449
450 /* Check 2 - cannot use 'Monitor' with any non-monitor modes */
451
452 if (pInstance->monitor != reference_inst->monitor)
453 {
454 ret = DMLERR_INVALIDPARAMETER;
455 goto theError;
456 }
457
458 /* Check 3 - must supply different callback address */
459
460 if (pInstance->callback == reference_inst->callback)
461 {
462 ret = DMLERR_DLL_USAGE;
463 goto theError;
464 }
465 }
466 reference_inst = reference_inst->next;
467 }
468 /* All cleared, add to chain */
469
470 TRACE("Application Instance checks finished\n");
471 WDML_IncrementInstanceId(pInstance);
472 reference_inst->next = pInstance;
473 }
474 LeaveCriticalSection(&WDML_CritSect);
475
476 *pidInst = pInstance->instanceID;
477
478 /* for deadlock issues, windows must always be created when outside the critical section */
479 wndclass.cbSize = sizeof(wndclass);
480 wndclass.style = 0;
481 wndclass.lpfnWndProc = WDML_EventProc;
482 wndclass.cbClsExtra = 0;
483 wndclass.cbWndExtra = sizeof(DWORD);
484 wndclass.hInstance = 0;
485 wndclass.hIcon = 0;
486 wndclass.hCursor = 0;
487 wndclass.hbrBackground = 0;
488 wndclass.lpszMenuName = NULL;
489 wndclass.lpszClassName = WDML_szEventClass;
490 wndclass.hIconSm = 0;
491
492 RegisterClassExA(&wndclass);
493
494 pInstance->hwndEvent = CreateWindowA(WDML_szEventClass, NULL,
495 WS_POPUP, 0, 0, 0, 0,
496 0, 0, 0, 0);
497
498 SetWindowLongA(pInstance->hwndEvent, GWL_WDML_INSTANCE, (DWORD)pInstance);
499
500 TRACE("New application instance processing finished OK\n");
501 }
502 else
503 {
504 /* Reinitialisation situation --- FIX */
505 TRACE("reinitialisation of (%p,%p,0x%lx,%ld): stub\n", pidInst, pfnCallback, afCmd, ulRes);
506
507 EnterCriticalSection(&WDML_CritSect);
508
509 if (WDML_InstanceList == NULL)
510 {
511 ret = DMLERR_INVALIDPARAMETER;
512 goto theError;
513 }
514 HeapFree(GetProcessHeap(), 0, pInstance); /* finished - release heap space used as work store */
515 /* can't reinitialise if we have initialised nothing !! */
516 reference_inst = WDML_InstanceList;
517 /* must first check if we have been given a valid instance to re-initialise !! how do we do that ? */
518 /*
519 * MS allows initialisation without specifying a callback, should we allow addition of the
520 * callback by a later call to initialise ? - if so this lot will have to change
521 */
522 while (reference_inst->next != NULL)
523 {
524 if (*pidInst == reference_inst->instanceID && pfnCallback == reference_inst->callback)
525 {
526 /* Check 1 - cannot change client-only mode if set via APPCMD_CLIENTONLY */
527
528 if (reference_inst->clientOnly)
529 {
530 if ((reference_inst->CBFflags & CBF_FAIL_ALLSVRXACTIONS) != CBF_FAIL_ALLSVRXACTIONS)
531 {
532 /* i.e. Was set to Client-only and through APPCMD_CLIENTONLY */
533
534 if (!(afCmd & APPCMD_CLIENTONLY))
535 {
536 ret = DMLERR_INVALIDPARAMETER;
537 goto theError;
538 }
539 }
540 }
541 /* Check 2 - cannot change monitor modes */
542
543 if (pInstance->monitor != reference_inst->monitor)
544 {
545 ret = DMLERR_INVALIDPARAMETER;
546 goto theError;
547 }
548
549 /* Check 3 - trying to set Client-only via APPCMD when not set so previously */
550
551 if ((afCmd&APPCMD_CLIENTONLY) && !reference_inst->clientOnly)
552 {
553 ret = DMLERR_INVALIDPARAMETER;
554 goto theError;
555 }
556 break;
557 }
558 reference_inst = reference_inst->next;
559 }
560 if (reference_inst->next == NULL)
561 {
562 ret = DMLERR_INVALIDPARAMETER;
563 goto theError;
564 }
565 /* All checked - change relevant flags */
566
567 reference_inst->CBFflags = pInstance->CBFflags;
568 reference_inst->clientOnly = pInstance->clientOnly;
569 reference_inst->monitorFlags = pInstance->monitorFlags;
570 LeaveCriticalSection(&WDML_CritSect);
571 }
572
573 return DMLERR_NO_ERROR;
574 theError:
575 HeapFree(GetProcessHeap(), 0, pInstance);
576 LeaveCriticalSection(&WDML_CritSect);
577 return ret;
578 }
579
580 /******************************************************************************
581 * DdeInitializeA (USER32.@)
582 */
583 UINT WINAPI DdeInitializeA(LPDWORD pidInst, PFNCALLBACK pfnCallback,
584 DWORD afCmd, DWORD ulRes)
585 {
586 return WDML_Initialize(pidInst, pfnCallback, afCmd, ulRes, FALSE, FALSE);
587 }
588
589 /******************************************************************************
590 * DdeInitializeW [USER32.@]
591 * Registers an application with the DDEML
592 *
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
598 *
599 * RETURNS
600 * Success: DMLERR_NO_ERROR
601 * Failure: DMLERR_DLL_USAGE, DMLERR_INVALIDPARAMETER, DMLERR_SYS_ERROR
602 */
603 UINT WINAPI DdeInitializeW(LPDWORD pidInst, PFNCALLBACK pfnCallback,
604 DWORD afCmd, DWORD ulRes)
605 {
606 return WDML_Initialize(pidInst, pfnCallback, afCmd, ulRes, TRUE, FALSE);
607 }
608
609 /*****************************************************************
610 * DdeUninitialize [USER32.@] Frees DDEML resources
611 *
612 * PARAMS
613 * idInst [I] Instance identifier
614 *
615 * RETURNS
616 * Success: TRUE
617 * Failure: FALSE
618 */
619
620 BOOL WINAPI DdeUninitialize(DWORD idInst)
621 {
622 /* Stage one - check if we have a handle for this instance
623 */
624 WDML_INSTANCE* pInstance;
625 WDML_CONV* pConv;
626 WDML_CONV* pConvNext;
627
628 TRACE("(%ld)\n", idInst);
629
630 EnterCriticalSection(&WDML_CritSect);
631
632 /* First check instance
633 */
634 pInstance = WDML_GetInstance(idInst);
635 if (pInstance == NULL)
636 {
637 LeaveCriticalSection(&WDML_CritSect);
638 /*
639 * Needs something here to record NOT_INITIALIZED ready for DdeGetLastError
640 */
641 return FALSE;
642 }
643
644 /* first terminate all conversations client side
645 * this shall close existing links...
646 */
647 for (pConv = pInstance->convs[WDML_CLIENT_SIDE]; pConv != NULL; pConv = pConvNext)
648 {
649 pConvNext = pConv->next;
650 DdeDisconnect((HCONV)pConv);
651 }
652 if (pInstance->convs[WDML_CLIENT_SIDE])
653 FIXME("still pending conversations\n");
654
655 /* then unregister all known service names */
656 DdeNameService(idInst, 0, 0, DNS_UNREGISTER);
657
658 /* Free the nodes that were not freed by this instance
659 * and remove the nodes from the list of HSZ nodes.
660 */
661 WDML_FreeAllHSZ(pInstance);
662
663 DestroyWindow(pInstance->hwndEvent);
664
665 /* OK now delete the instance handle itself */
666
667 if (WDML_InstanceList == pInstance)
668 {
669 /* special case - the first/only entry */
670 WDML_InstanceList = pInstance->next;
671 }
672 else
673 {
674 /* general case, remove entry */
675 WDML_INSTANCE* inst;
676
677 for (inst = WDML_InstanceList; inst->next != pInstance; inst = inst->next);
678 inst->next = pInstance->next;
679 }
680 /* leave crit sect and release the heap entry
681 */
682 HeapFree(GetProcessHeap(), 0, pInstance);
683 LeaveCriticalSection(&WDML_CritSect);
684 return TRUE;
685 }
686
687 /******************************************************************
688 * WDML_NotifyThreadExit
689 *
690 *
691 */
692 void WDML_NotifyThreadDetach(void)
693 {
694 WDML_INSTANCE* pInstance;
695 WDML_INSTANCE* next;
696 DWORD tid = GetCurrentThreadId();
697
698 EnterCriticalSection(&WDML_CritSect);
699 for (pInstance = WDML_InstanceList; pInstance != NULL; pInstance = next)
700 {
701 next = pInstance->next;
702 if (pInstance->threadID == tid)
703 {
704 DdeUninitialize(pInstance->instanceID);
705 }
706 }
707 LeaveCriticalSection(&WDML_CritSect);
708 }
709
710 /******************************************************************
711 * WDML_InvokeCallback
712 *
713 *
714 */
715 HDDEDATA WDML_InvokeCallback(WDML_INSTANCE* pInstance, UINT uType, UINT uFmt, HCONV hConv,
716 HSZ hsz1, HSZ hsz2, HDDEDATA hdata,
717 DWORD dwData1, DWORD dwData2)
718 {
719 HDDEDATA ret;
720
721 if (pInstance == NULL)
722 return NULL;
723 TRACE("invoking CB%d[%p] (%x %x %p %p %p %p %lx %lx)\n",
724 pInstance->win16 ? 16 : 32, pInstance->callback, uType, uFmt,
725 hConv, hsz1, hsz2, hdata, dwData1, dwData2);
726 ret = pInstance->callback(uType, uFmt, hConv, hsz1, hsz2, hdata, dwData1, dwData2);
727 TRACE("done => %p\n", ret);
728 return ret;
729 }
730
731 /*****************************************************************************
732 * WDML_GetInstance
733 *
734 * generic routine to return a pointer to the relevant DDE_HANDLE_ENTRY
735 * for an instance Id, or NULL if the entry does not exist
736 *
737 */
738 WDML_INSTANCE* WDML_GetInstance(DWORD instId)
739 {
740 WDML_INSTANCE* pInstance;
741
742 for (pInstance = WDML_InstanceList; pInstance != NULL; pInstance = pInstance->next)
743 {
744 if (pInstance->instanceID == instId)
745 {
746 if (GetCurrentThreadId() != pInstance->threadID)
747 {
748 FIXME("Tried to get instance from wrong thread\n");
749 continue;
750 }
751 return pInstance;
752 }
753 }
754 TRACE("Instance entry missing\n");
755 return NULL;
756 }
757
758 /******************************************************************
759 * WDML_GetInstanceFromWnd
760 *
761 *
762 */
763 WDML_INSTANCE* WDML_GetInstanceFromWnd(HWND hWnd)
764 {
765 return (WDML_INSTANCE*)GetWindowLongA(hWnd, GWL_WDML_INSTANCE);
766 }
767
768 /******************************************************************************
769 * DdeGetLastError [USER32.@] Gets most recent error code
770 *
771 * PARAMS
772 * idInst [I] Instance identifier
773 *
774 * RETURNS
775 * Last error code
776 */
777 UINT WINAPI DdeGetLastError(DWORD idInst)
778 {
779 DWORD error_code;
780 WDML_INSTANCE* pInstance;
781
782 EnterCriticalSection(&WDML_CritSect);
783
784 /* First check instance
785 */
786 pInstance = WDML_GetInstance(idInst);
787 if (pInstance == NULL)
788 {
789 error_code = DMLERR_INVALIDPARAMETER;
790 }
791 else
792 {
793 error_code = pInstance->lastError;
794 pInstance->lastError = 0;
795 }
796
797 LeaveCriticalSection(&WDML_CritSect);
798 return error_code;
799 }
800
801 /* ================================================================
802 *
803 * String management
804 *
805 * ================================================================ */
806
807
808 /******************************************************************
809 * WDML_FindNode
810 *
811 *
812 */
813 static HSZNode* WDML_FindNode(WDML_INSTANCE* pInstance, HSZ hsz)
814 {
815 HSZNode* pNode;
816
817 if (pInstance == NULL) return NULL;
818
819 for (pNode = pInstance->nodeList; pNode != NULL; pNode = pNode->next)
820 {
821 if (pNode->hsz == hsz) break;
822 }
823 if (!pNode) WARN("HSZ %p not found\n", hsz);
824 return pNode;
825 }
826
827 /******************************************************************
828 * WDML_MakeAtomFromHsz
829 *
830 * Creates a global atom from an existing HSZ
831 * Generally used before sending an HSZ as an atom to a remote app
832 */
833 ATOM WDML_MakeAtomFromHsz(HSZ hsz)
834 {
835 WCHAR nameBuffer[MAX_BUFFER_LEN];
836
837 if (GetAtomNameW(HSZ2ATOM(hsz), nameBuffer, MAX_BUFFER_LEN))
838 return GlobalAddAtomW(nameBuffer);
839 WARN("HSZ %p not found\n", hsz);
840 return 0;
841 }
842
843 /******************************************************************
844 * WDML_MakeHszFromAtom
845 *
846 * Creates a HSZ from an existing global atom
847 * Generally used while receiving a global atom and transforming it
848 * into an HSZ
849 */
850 HSZ WDML_MakeHszFromAtom(WDML_INSTANCE* pInstance, ATOM atom)
851 {
852 WCHAR nameBuffer[MAX_BUFFER_LEN];
853
854 if (!atom) return NULL;
855
856 if (GlobalGetAtomNameW(atom, nameBuffer, MAX_BUFFER_LEN))
857 {
858 TRACE("%x => %s\n", atom, debugstr_w(nameBuffer));
859 return DdeCreateStringHandleW(pInstance->instanceID, nameBuffer, CP_WINUNICODE);
860 }
861 WARN("ATOM 0x%x not found\n", atom);
862 return 0;
863 }
864
865 /******************************************************************
866 * WDML_IncHSZ
867 *
868 *
869 */
870 BOOL WDML_IncHSZ(WDML_INSTANCE* pInstance, HSZ hsz)
871 {
872 HSZNode* pNode;
873
874 pNode = WDML_FindNode(pInstance, hsz);
875 if (!pNode) return FALSE;
876
877 pNode->refCount++;
878 return TRUE;
879 }
880
881 /******************************************************************************
882 * WDML_DecHSZ (INTERNAL)
883 *
884 * Decrease the ref count of an HSZ. If it reaches 0, the node is removed from the list
885 * of HSZ nodes
886 * Returns -1 is the HSZ isn't found, otherwise it's the current (after --) of the ref count
887 */
888 BOOL WDML_DecHSZ(WDML_INSTANCE* pInstance, HSZ hsz)
889 {
890 HSZNode* pPrev = NULL;
891 HSZNode* pCurrent;
892
893 for (pCurrent = pInstance->nodeList; pCurrent != NULL; pCurrent = (pPrev = pCurrent)->next)
894 {
895 /* If we found the node we were looking for and its ref count is one,
896 * we can remove it
897 */
898 if (pCurrent->hsz == hsz)
899 {
900 if (--pCurrent->refCount == 0)
901 {
902 if (pCurrent == pInstance->nodeList)
903 {
904 pInstance->nodeList = pCurrent->next;
905 }
906 else
907 {
908 pPrev->next = pCurrent->next;
909 }
910 HeapFree(GetProcessHeap(), 0, pCurrent);
911 DeleteAtom(HSZ2ATOM(hsz));
912 }
913 return TRUE;
914 }
915 }
916 WARN("HSZ %p not found\n", hsz);
917
918 return FALSE;
919 }
920
921 /******************************************************************************
922 * WDML_FreeAllHSZ (INTERNAL)
923 *
924 * Frees up all the strings still allocated in the list and
925 * remove all the nodes from the list of HSZ nodes.
926 */
927 void WDML_FreeAllHSZ(WDML_INSTANCE* pInstance)
928 {
929 /* Free any strings created in this instance.
930 */
931 while (pInstance->nodeList != NULL)
932 {
933 DdeFreeStringHandle(pInstance->instanceID, pInstance->nodeList->hsz);
934 }
935 }
936
937 /******************************************************************************
938 * InsertHSZNode (INTERNAL)
939 *
940 * Insert a node to the head of the list.
941 */
942 static void WDML_InsertHSZNode(WDML_INSTANCE* pInstance, HSZ hsz)
943 {
944 if (hsz != 0)
945 {
946 HSZNode* pNew = NULL;
947 /* Create a new node for this HSZ.
948 */
949 pNew = (HSZNode*)HeapAlloc(GetProcessHeap(), 0, sizeof(HSZNode));
950 if (pNew != NULL)
951 {
952 pNew->hsz = hsz;
953 pNew->next = pInstance->nodeList;
954 pNew->refCount = 1;
955 pInstance->nodeList = pNew;
956 }
957 else
958 {
959 ERR("Primary HSZ Node allocation failed - out of memory\n");
960 }
961 }
962 }
963
964 /******************************************************************
965 * WDML_QueryString
966 *
967 *
968 */
969 static int WDML_QueryString(WDML_INSTANCE* pInstance, HSZ hsz, LPVOID ptr, DWORD cchMax,
970 int codepage)
971 {
972 WCHAR pString[MAX_BUFFER_LEN];
973 int ret;
974 /* If psz is null, we have to return only the length
975 * of the string.
976 */
977 if (ptr == NULL)
978 {
979 ptr = pString;
980 cchMax = MAX_BUFFER_LEN;
981 }
982
983 switch (codepage)
984 {
985 case CP_WINANSI:
986 ret = GetAtomNameA(HSZ2ATOM(hsz), ptr, cchMax);
987 break;
988 case CP_WINUNICODE:
989 ret = GetAtomNameW(HSZ2ATOM(hsz), ptr, cchMax);
990 break;
991 default:
992 ERR("Unknown code page %d\n", codepage);
993 ret = 0;
994 }
995 return ret;
996 }
997
998 /*****************************************************************
999 * DdeQueryStringA [USER32.@]
1000 */
1001 DWORD WINAPI DdeQueryStringA(DWORD idInst, HSZ hsz, LPSTR psz, DWORD cchMax, INT iCodePage)
1002 {
1003 DWORD ret = 0;
1004 WDML_INSTANCE* pInstance;
1005
1006 TRACE("(%ld, %p, %p, %ld, %d)\n", idInst, hsz, psz, cchMax, iCodePage);
1007
1008 EnterCriticalSection(&WDML_CritSect);
1009
1010 /* First check instance
1011 */
1012 pInstance = WDML_GetInstance(idInst);
1013 if (pInstance != NULL)
1014 {
1015 if (iCodePage == 0) iCodePage = CP_WINANSI;
1016 ret = WDML_QueryString(pInstance, hsz, psz, cchMax, iCodePage);
1017 }
1018 LeaveCriticalSection(&WDML_CritSect);
1019
1020 TRACE("returning %ld (%s)\n", ret, debugstr_a(psz));
1021 return ret;
1022 }
1023
1024 /*****************************************************************
1025 * DdeQueryStringW [USER32.@]
1026 */
1027
1028 DWORD WINAPI DdeQueryStringW(DWORD idInst, HSZ hsz, LPWSTR psz, DWORD cchMax, INT iCodePage)
1029 {
1030 DWORD ret = 0;
1031 WDML_INSTANCE* pInstance;
1032
1033 TRACE("(%ld, %p, %p, %ld, %d)\n", idInst, hsz, psz, cchMax, iCodePage);
1034
1035 EnterCriticalSection(&WDML_CritSect);
1036
1037 /* First check instance
1038 */
1039 pInstance = WDML_GetInstance(idInst);
1040 if (pInstance != NULL)
1041 {
1042 if (iCodePage == 0) iCodePage = CP_WINUNICODE;
1043 ret = WDML_QueryString(pInstance, hsz, psz, cchMax, iCodePage);
1044 }
1045 LeaveCriticalSection(&WDML_CritSect);
1046
1047 TRACE("returning %ld (%s)\n", ret, debugstr_w(psz));
1048 return ret;
1049 }
1050
1051 /******************************************************************
1052 * DML_CreateString
1053 *
1054 *
1055 */
1056 static HSZ WDML_CreateString(WDML_INSTANCE* pInstance, LPCVOID ptr, int codepage)
1057 {
1058 HSZ hsz;
1059
1060 switch (codepage)
1061 {
1062 case CP_WINANSI:
1063 hsz = ATOM2HSZ(AddAtomA(ptr));
1064 TRACE("added atom %s with HSZ %p, \n", debugstr_a(ptr), hsz);
1065 break;
1066 case CP_WINUNICODE:
1067 hsz = ATOM2HSZ(AddAtomW(ptr));
1068 TRACE("added atom %s with HSZ %p, \n", debugstr_w(ptr), hsz);
1069 break;
1070 default:
1071 ERR("Unknown code page %d\n", codepage);
1072 return 0;
1073 }
1074 WDML_InsertHSZNode(pInstance, hsz);
1075 return hsz;
1076 }
1077
1078 /*****************************************************************
1079 * DdeCreateStringHandleA [USER32.@]
1080 *
1081 * RETURNS
1082 * Success: String handle
1083 * Failure: 0
1084 */
1085 HSZ WINAPI DdeCreateStringHandleA(DWORD idInst, LPSTR psz, INT codepage)
1086 {
1087 HSZ hsz = 0;
1088 WDML_INSTANCE* pInstance;
1089
1090 TRACE("(%ld,%s,%d)\n", idInst, debugstr_a(psz), codepage);
1091
1092 EnterCriticalSection(&WDML_CritSect);
1093
1094 pInstance = WDML_GetInstance(idInst);
1095 if (pInstance)
1096 {
1097 if (codepage == 0) codepage = CP_WINANSI;
1098 hsz = WDML_CreateString(pInstance, psz, codepage);
1099 }
1100
1101 LeaveCriticalSection(&WDML_CritSect);
1102 return hsz;
1103 }
1104
1105
1106 /******************************************************************************
1107 * DdeCreateStringHandleW [USER32.@] Creates handle to identify string
1108 *
1109 * PARAMS
1110 * idInst [I] Instance identifier
1111 * psz [I] Pointer to string
1112 * codepage [I] Code page identifier
1113 * RETURNS
1114 * Success: String handle
1115 * Failure: 0
1116 */
1117 HSZ WINAPI DdeCreateStringHandleW(DWORD idInst, LPWSTR psz, INT codepage)
1118 {
1119 WDML_INSTANCE* pInstance;
1120 HSZ hsz = 0;
1121
1122 TRACE("(%ld,%s,%d)\n", idInst, debugstr_w(psz), codepage);
1123
1124 EnterCriticalSection(&WDML_CritSect);
1125
1126 pInstance = WDML_GetInstance(idInst);
1127 if (pInstance)
1128 {
1129 if (codepage == 0) codepage = CP_WINUNICODE;
1130 hsz = WDML_CreateString(pInstance, psz, codepage);
1131 }
1132 LeaveCriticalSection(&WDML_CritSect);
1133
1134 return hsz;
1135 }
1136
1137 /*****************************************************************
1138 * DdeFreeStringHandle (USER32.@)
1139 * RETURNS: success: nonzero
1140 * fail: zero
1141 */
1142 BOOL WINAPI DdeFreeStringHandle(DWORD idInst, HSZ hsz)
1143 {
1144 WDML_INSTANCE* pInstance;
1145 BOOL ret = FALSE;
1146
1147 TRACE("(%ld,%p): \n", idInst, hsz);
1148
1149 EnterCriticalSection(&WDML_CritSect);
1150
1151 /* First check instance
1152 */
1153 pInstance = WDML_GetInstance(idInst);
1154 if (pInstance)
1155 ret = WDML_DecHSZ(pInstance, hsz);
1156
1157 LeaveCriticalSection(&WDML_CritSect);
1158
1159 return ret;
1160 }
1161
1162 /*****************************************************************
1163 * DdeKeepStringHandle (USER32.@)
1164 *
1165 * RETURNS: success: nonzero
1166 * fail: zero
1167 */
1168 BOOL WINAPI DdeKeepStringHandle(DWORD idInst, HSZ hsz)
1169 {
1170 WDML_INSTANCE* pInstance;
1171 BOOL ret = FALSE;
1172
1173 TRACE("(%ld,%p): \n", idInst, hsz);
1174
1175 EnterCriticalSection(&WDML_CritSect);
1176
1177 /* First check instance
1178 */
1179 pInstance = WDML_GetInstance(idInst);
1180 if (pInstance)
1181 ret = WDML_IncHSZ(pInstance, hsz);
1182
1183 LeaveCriticalSection(&WDML_CritSect);
1184 return ret;
1185 }
1186
1187 /*****************************************************************
1188 * DdeCmpStringHandles (USER32.@)
1189 *
1190 * Compares the value of two string handles. This comparison is
1191 * not case sensitive.
1192 *
1193 * Returns:
1194 * -1 The value of hsz1 is zero or less than hsz2
1195 * 0 The values of hsz 1 and 2 are the same or both zero.
1196 * 1 The value of hsz2 is zero of less than hsz1
1197 */
1198 INT WINAPI DdeCmpStringHandles(HSZ hsz1, HSZ hsz2)
1199 {
1200 WCHAR psz1[MAX_BUFFER_LEN];
1201 WCHAR psz2[MAX_BUFFER_LEN];
1202 int ret = 0;
1203 int ret1, ret2;
1204
1205 ret1 = GetAtomNameW(HSZ2ATOM(hsz1), psz1, MAX_BUFFER_LEN);
1206 ret2 = GetAtomNameW(HSZ2ATOM(hsz2), psz2, MAX_BUFFER_LEN);
1207
1208 TRACE("(%p<%s> %p<%s>);\n", hsz1, debugstr_w(psz1), hsz2, debugstr_w(psz2));
1209
1210 /* Make sure we found both strings. */
1211 if (ret1 == 0 && ret2 == 0)
1212 {
1213 /* If both are not found, return both "zero strings". */
1214 ret = 0;
1215 }
1216 else if (ret1 == 0)
1217 {
1218 /* If hsz1 is a not found, return hsz1 is "zero string". */
1219 ret = -1;
1220 }
1221 else if (ret2 == 0)
1222 {
1223 /* If hsz2 is a not found, return hsz2 is "zero string". */
1224 ret = 1;
1225 }
1226 else
1227 {
1228 /* Compare the two strings we got (case insensitive). */
1229 ret = lstrcmpiW(psz1, psz2);
1230 /* Since strcmp returns any number smaller than
1231 * 0 when the first string is found to be less than
1232 * the second one we must make sure we are returning
1233 * the proper values.
1234 */
1235 if (ret < 0)
1236 {
1237 ret = -1;
1238 }
1239 else if (ret > 0)
1240 {
1241 ret = 1;
1242 }
1243 }
1244
1245 return ret;
1246 }
1247
1248 /* ================================================================
1249 *
1250 * Data handle management
1251 *
1252 * ================================================================ */
1253
1254 /*****************************************************************
1255 * DdeCreateDataHandle (USER32.@)
1256 */
1257 HDDEDATA WINAPI DdeCreateDataHandle(DWORD idInst, LPBYTE pSrc, DWORD cb, DWORD cbOff,
1258 HSZ hszItem, UINT wFmt, UINT afCmd)
1259 {
1260 /* For now, we ignore idInst, hszItem.
1261 * The purpose of these arguments still need to be investigated.
1262 */
1263
1264 HGLOBAL hMem;
1265 LPBYTE pByte;
1266 DDE_DATAHANDLE_HEAD* pDdh;
1267 WCHAR psz[MAX_BUFFER_LEN];
1268
1269 GetAtomNameW(HSZ2ATOM(hszItem), psz, MAX_BUFFER_LEN);
1270
1271 TRACE("(%ld,%p,cb %ld, cbOff %ld,%p <%s>,%x,%x)\n",
1272 idInst, pSrc, cb, cbOff, hszItem, debugstr_w(psz), wFmt, afCmd);
1273
1274 if (afCmd != 0 && afCmd != HDATA_APPOWNED)
1275 return 0;
1276
1277 /* we use the first 4 bytes to store the size */
1278 if (!(hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, cb + cbOff + sizeof(DDE_DATAHANDLE_HEAD))))
1279 {
1280 ERR("GlobalAlloc failed\n");
1281 return 0;
1282 }
1283
1284 pDdh = (DDE_DATAHANDLE_HEAD*)GlobalLock(hMem);
1285 if (!pDdh)
1286 {
1287 GlobalFree(hMem);
1288 return 0;
1289 }
1290
1291 pDdh->cfFormat = wFmt;
1292 pDdh->bAppOwned = (afCmd == HDATA_APPOWNED);
1293
1294 pByte = (LPBYTE)(pDdh + 1);
1295 if (pSrc)
1296 {
1297 memcpy(pByte, pSrc + cbOff, cb);
1298 }
1299 GlobalUnlock(hMem);
1300
1301 return (HDDEDATA)hMem;
1302 }
1303
1304 /*****************************************************************
1305 *
1306 * DdeAddData (USER32.@)
1307 */
1308 HDDEDATA WINAPI DdeAddData(HDDEDATA hData, LPBYTE pSrc, DWORD cb, DWORD cbOff)
1309 {
1310 DWORD old_sz, new_sz;
1311 LPBYTE pDst;
1312
1313 TRACE("(%p,%p,cb %ld, cbOff %ld)\n", hData, pSrc, cb, cbOff);
1314
1315 pDst = DdeAccessData(hData, &old_sz);
1316 if (!pDst) return 0;
1317
1318 new_sz = cb + cbOff;
1319 if (new_sz > old_sz)
1320 {
1321 DdeUnaccessData(hData);
1322 hData = GlobalReAlloc((HGLOBAL)hData, new_sz + sizeof(DDE_DATAHANDLE_HEAD),
1323 GMEM_MOVEABLE | GMEM_DDESHARE);
1324 pDst = DdeAccessData(hData, &old_sz);
1325 }
1326
1327 if (!pDst) return 0;
1328
1329 memcpy(pDst + cbOff, pSrc, cb);
1330 DdeUnaccessData(hData);
1331 return hData;
1332 }
1333
1334 /******************************************************************************
1335 * DdeGetData [USER32.@] Copies data from DDE object to local buffer
1336 *
1337 *
1338 * PARAMS
1339 * hData [I] Handle to DDE object
1340 * pDst [I] Pointer to destination buffer
1341 * cbMax [I] Amount of data to copy
1342 * cbOff [I] Offset to beginning of data
1343 *
1344 * RETURNS
1345 * Size of memory object associated with handle
1346 */
1347 DWORD WINAPI DdeGetData(HDDEDATA hData, LPBYTE pDst, DWORD cbMax, DWORD cbOff)
1348 {
1349 DWORD dwSize, dwRet;
1350 LPBYTE pByte;
1351
1352 TRACE("(%p,%p,%ld,%ld)\n", hData, pDst, cbMax, cbOff);
1353
1354 pByte = DdeAccessData(hData, &dwSize);
1355
1356 if (pByte)
1357 {
1358 if (!pDst)
1359 {
1360 dwRet = dwSize;
1361 }
1362 else if (cbOff + cbMax < dwSize)
1363 {
1364 dwRet = cbMax;
1365 }
1366 else if (cbOff < dwSize)
1367 {
1368 dwRet = dwSize - cbOff;
1369 }
1370 else
1371 {
1372 dwRet = 0;
1373 }
1374 if (pDst && dwRet != 0)
1375 {
1376 memcpy(pDst, pByte + cbOff, dwRet);
1377 }
1378 DdeUnaccessData(hData);
1379 }
1380 else
1381 {
1382 dwRet = 0;
1383 }
1384 return dwRet;
1385 }
1386
1387 /*****************************************************************
1388 * DdeAccessData (USER32.@)
1389 */
1390 LPBYTE WINAPI DdeAccessData(HDDEDATA hData, LPDWORD pcbDataSize)
1391 {
1392 HGLOBAL hMem = (HGLOBAL)hData;
1393 DDE_DATAHANDLE_HEAD* pDdh;
1394
1395 TRACE("(%p,%p)\n", hData, pcbDataSize);
1396
1397 pDdh = (DDE_DATAHANDLE_HEAD*)GlobalLock(hMem);
1398 if (pDdh == NULL)
1399 {
1400 ERR("Failed on GlobalLock(%p)\n", hMem);
1401 return 0;
1402 }
1403
1404 if (pcbDataSize != NULL)
1405 {
1406 *pcbDataSize = GlobalSize(hMem) - sizeof(DDE_DATAHANDLE_HEAD);
1407 }
1408 TRACE("=> %p (%lu)\n", pDdh + 1, GlobalSize(hMem) - sizeof(DDE_DATAHANDLE_HEAD));
1409 return (LPBYTE)(pDdh + 1);
1410 }
1411
1412 /*****************************************************************
1413 * DdeUnaccessData (USER32.@)
1414 */
1415 BOOL WINAPI DdeUnaccessData(HDDEDATA hData)
1416 {
1417 HGLOBAL hMem = (HGLOBAL)hData;
1418
1419 TRACE("(%p)\n", hData);
1420
1421 GlobalUnlock(hMem);
1422
1423 return TRUE;
1424 }
1425
1426 /*****************************************************************
1427 * DdeFreeDataHandle (USER32.@)
1428 */
1429 BOOL WINAPI DdeFreeDataHandle(HDDEDATA hData)
1430 {
1431 TRACE("(%p)\n", hData);
1432 return GlobalFree((HGLOBAL)hData) == 0;
1433 }
1434
1435 /******************************************************************
1436 * WDML_IsAppOwned
1437 *
1438 *
1439 */
1440 BOOL WDML_IsAppOwned(HDDEDATA hData)
1441 {
1442 DDE_DATAHANDLE_HEAD* pDdh;
1443 BOOL ret = FALSE;
1444
1445 pDdh = (DDE_DATAHANDLE_HEAD*)GlobalLock((HGLOBAL)hData);
1446 if (pDdh != NULL)
1447 {
1448 ret = pDdh->bAppOwned;
1449 GlobalUnlock((HGLOBAL)hData);
1450 }
1451 return ret;
1452 }
1453
1454 /* ================================================================
1455 *
1456 * Global <=> Data handle management
1457 *
1458 * ================================================================ */
1459
1460 /* Note: we use a DDEDATA, but layout of DDEDATA, DDEADVISE and DDEPOKE structures is similar:
1461 * offset size
1462 * (bytes) (bits) comment
1463 * 0 16 bit fields for options (release, ackreq, response...)
1464 * 2 16 clipboard format
1465 * 4 ? data to be used
1466 */
1467 HDDEDATA WDML_Global2DataHandle(HGLOBAL hMem, WINE_DDEHEAD* p)
1468 {
1469 DDEDATA* pDd;
1470 HDDEDATA ret = 0;
1471 DWORD size;
1472
1473 if (hMem)
1474 {
1475 pDd = GlobalLock(hMem);
1476 size = GlobalSize(hMem) - sizeof(WINE_DDEHEAD);
1477 if (pDd)
1478 {
1479 if (p) memcpy(p, pDd, sizeof(WINE_DDEHEAD));
1480 switch (pDd->cfFormat)
1481 {
1482 default:
1483 FIXME("Unsupported format (%d) for data... assuming raw information\n",
1484 pDd->cfFormat);
1485 /* fall thru */
1486 case 0:
1487 case CF_TEXT:
1488 ret = DdeCreateDataHandle(0, pDd->Value, size, 0, 0, pDd->cfFormat, 0);
1489 break;
1490 case CF_BITMAP:
1491 if (size >= sizeof(BITMAP))
1492 {
1493 BITMAP* bmp = (BITMAP*)pDd->Value;
1494 int count = bmp->bmWidthBytes * bmp->bmHeight * bmp->bmPlanes;
1495 if (size >= sizeof(BITMAP) + count)
1496 {
1497 HBITMAP hbmp;
1498
1499 if ((hbmp = CreateBitmap(bmp->bmWidth, bmp->bmHeight,
1500 bmp->bmPlanes, bmp->bmBitsPixel,
1501 pDd->Value + sizeof(BITMAP))))
1502 {
1503 ret = DdeCreateDataHandle(0, (LPBYTE)&hbmp, sizeof(hbmp),
1504 0, 0, CF_BITMAP, 0);
1505 }
1506 else ERR("Can't create bmp\n");
1507 }
1508 else
1509 {
1510 ERR("Wrong count: %lu / %d\n", size, sizeof(BITMAP) + count);
1511 }
1512 } else ERR("No bitmap header\n");
1513 break;
1514 }
1515 GlobalUnlock(hMem);
1516 }
1517 }
1518 return ret;
1519 }
1520
1521 /******************************************************************
1522 * WDML_DataHandle2Global
1523 *
1524 *
1525 */
1526 HGLOBAL WDML_DataHandle2Global(HDDEDATA hDdeData, BOOL fResponse, BOOL fRelease,
1527 BOOL fDeferUpd, BOOL fAckReq)
1528 {
1529 DDE_DATAHANDLE_HEAD* pDdh;
1530 DWORD dwSize;
1531 HGLOBAL hMem = 0;
1532
1533 dwSize = GlobalSize((HGLOBAL)hDdeData) - sizeof(DDE_DATAHANDLE_HEAD);
1534 pDdh = (DDE_DATAHANDLE_HEAD*)GlobalLock((HGLOBAL)hDdeData);
1535 if (dwSize && pDdh)
1536 {
1537 WINE_DDEHEAD* wdh = NULL;
1538
1539 switch (pDdh->cfFormat)
1540 {
1541 default:
1542 FIXME("Unsupported format (%d) for data... passing raw information\n", pDdh->cfFormat);
1543 /* fall thru */
1544 case 0:
1545 case CF_TEXT:
1546 hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, sizeof(WINE_DDEHEAD) + dwSize);
1547 if (hMem && (wdh = GlobalLock(hMem)))
1548 {
1549 memcpy(wdh + 1, pDdh + 1, dwSize);
1550 }
1551 break;
1552 case CF_BITMAP:
1553 if (dwSize >= sizeof(HBITMAP))
1554 {
1555 BITMAP bmp;
1556 DWORD count;
1557 HBITMAP hbmp = *(HBITMAP*)(pDdh + 1);
1558
1559 if (GetObjectA(hbmp, sizeof(bmp), &bmp))
1560 {
1561 count = bmp.bmWidthBytes * bmp.bmHeight;
1562 hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE,
1563 sizeof(WINE_DDEHEAD) + sizeof(bmp) + count);
1564 if (hMem && (wdh = GlobalLock(hMem)))
1565 {
1566 memcpy(wdh + 1, &bmp, sizeof(bmp));
1567 GetBitmapBits(hbmp, count, ((char*)(wdh + 1)) + sizeof(bmp));
1568 }
1569 }
1570 }
1571 break;
1572 }
1573 if (wdh)
1574 {
1575 wdh->unused = 0;
1576 wdh->fResponse = fResponse;
1577 wdh->fRelease = fRelease;
1578 wdh->fDeferUpd = fDeferUpd;
1579 wdh->fAckReq = fAckReq;
1580 wdh->cfFormat = pDdh->cfFormat;
1581 GlobalUnlock(hMem);
1582 }
1583 GlobalUnlock((HGLOBAL)hDdeData);
1584 }
1585
1586 return hMem;
1587 }
1588
1589 /* ================================================================
1590 *
1591 * Server management
1592 *
1593 * ================================================================ */
1594
1595 /******************************************************************
1596 * WDML_AddServer
1597 *
1598 *
1599 */
1600 WDML_SERVER* WDML_AddServer(WDML_INSTANCE* pInstance, HSZ hszService, HSZ hszTopic)
1601 {
1602 WDML_SERVER* pServer;
1603 char buf1[256];
1604 char buf2[256];
1605
1606 pServer = (WDML_SERVER*)HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_SERVER));
1607 if (pServer == NULL) return NULL;
1608
1609 WDML_IncHSZ(pInstance, pServer->hszService = hszService);
1610
1611 DdeQueryStringA(pInstance->instanceID, hszService, buf1, sizeof(buf1), CP_WINANSI);
1612 _snprintf(buf2, sizeof(buf2), "%s(0x%08lx)", buf1, GetCurrentProcessId());
1613 pServer->hszServiceSpec = DdeCreateStringHandleA(pInstance->instanceID, buf2, CP_WINANSI);
1614
1615 pServer->atomService = WDML_MakeAtomFromHsz(pServer->hszService);
1616 pServer->atomServiceSpec = WDML_MakeAtomFromHsz(pServer->hszServiceSpec);
1617
1618 pServer->filterOn = TRUE;
1619
1620 pServer->next = pInstance->servers;
1621 pInstance->servers = pServer;
1622 return pServer;
1623 }
1624
1625 /******************************************************************
1626 * WDML_RemoveServer
1627 *
1628 *
1629 */
1630 void WDML_RemoveServer(WDML_INSTANCE* pInstance, HSZ hszService, HSZ hszTopic)
1631 {
1632 WDML_SERVER* pPrev = NULL;
1633 WDML_SERVER* pServer = NULL;
1634 WDML_CONV* pConv;
1635 WDML_CONV* pConvNext;
1636
1637 pServer = pInstance->servers;
1638
1639 while (pServer != NULL)
1640 {
1641 if (DdeCmpStringHandles(pServer->hszService, hszService) == 0)
1642 {
1643 WDML_BroadcastDDEWindows(WDML_szEventClass, WM_WDML_UNREGISTER,
1644 pServer->atomService, pServer->atomServiceSpec);
1645 /* terminate all conversations for given topic */
1646 for (pConv = pInstance->convs[WDML_SERVER_SIDE]; pConv != NULL; pConv = pConvNext)
1647 {
1648 pConvNext = pConv->next;
1649 if (DdeCmpStringHandles(pConv->hszService, hszService) == 0)
1650 {
1651 WDML_RemoveConv(pConv, WDML_SERVER_SIDE);
1652 /* don't care about return code (whether client window is present or not) */
1653 PostMessageA(pConv->hwndClient, WM_DDE_TERMINATE, (WPARAM)pConv->hwndServer, 0L);
1654 }
1655 }
1656 if (pServer == pInstance->servers)
1657 {
1658 pInstance->servers = pServer->next;
1659 }
1660 else
1661 {
1662 pPrev->next = pServer->next;
1663 }
1664
1665 DestroyWindow(pServer->hwndServer);
1666 WDML_DecHSZ(pInstance, pServer->hszServiceSpec);
1667 WDML_DecHSZ(pInstance, pServer->hszService);
1668
1669 GlobalDeleteAtom(pServer->atomService);
1670 GlobalDeleteAtom(pServer->atomServiceSpec);
1671
1672 HeapFree(GetProcessHeap(), 0, pServer);
1673 break;
1674 }
1675
1676 pPrev = pServer;
1677 pServer = pServer->next;
1678 }
1679 }
1680
1681 /*****************************************************************************
1682 * WDML_FindServer
1683 *
1684 * generic routine to return a pointer to the relevant ServiceNode
1685 * for a given service name, or NULL if the entry does not exist
1686 *
1687 */
1688 WDML_SERVER* WDML_FindServer(WDML_INSTANCE* pInstance, HSZ hszService, HSZ hszTopic)
1689 {
1690 WDML_SERVER* pServer;
1691
1692 for (pServer = pInstance->servers; pServer != NULL; pServer = pServer->next)
1693 {
1694 if (hszService == pServer->hszService)
1695 {
1696 return pServer;
1697 }
1698 }
1699 TRACE("Service name missing\n");
1700 return NULL;
1701 }
1702
1703 /* ================================================================
1704 *
1705 * Conversation management
1706 *
1707 * ================================================================ */
1708
1709 /******************************************************************
1710 * WDML_AddConv
1711 *
1712 *
1713 */
1714 WDML_CONV* WDML_AddConv(WDML_INSTANCE* pInstance, WDML_SIDE side,
1715 HSZ hszService, HSZ hszTopic, HWND hwndClient, HWND hwndServer)
1716 {
1717 WDML_CONV* pConv;
1718
1719 /* no converstation yet, add it */
1720 pConv = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_CONV));
1721 if (!pConv) return NULL;
1722
1723 pConv->instance = pInstance;
1724 WDML_IncHSZ(pInstance, pConv->hszService = hszService);
1725 WDML_IncHSZ(pInstance, pConv->hszTopic = hszTopic);
1726 pConv->hwndServer = hwndServer;
1727 pConv->hwndClient = hwndClient;
1728 pConv->transactions = NULL;
1729 pConv->hUser = 0;
1730 pConv->wStatus = (side == WDML_CLIENT_SIDE) ? ST_CLIENT : 0L;
1731 /* check if both side of the conversation are of the same instance */
1732 if (GetWindowThreadProcessId(hwndClient, NULL) == GetWindowThreadProcessId(hwndServer, NULL) &&
1733 WDML_GetInstanceFromWnd(hwndClient) == WDML_GetInstanceFromWnd(hwndServer))
1734 {
1735 pConv->wStatus |= ST_ISSELF;
1736 }
1737 pConv->wConvst = XST_NULL;
1738
1739 pConv->next = pInstance->convs[side];
1740 pInstance->convs[side] = pConv;
1741
1742 return pConv;
1743 }
1744
1745 /******************************************************************
1746 * WDML_FindConv
1747 *
1748 *
1749 */
1750 WDML_CONV* WDML_FindConv(WDML_INSTANCE* pInstance, WDML_SIDE side,
1751 HSZ hszService, HSZ hszTopic)
1752 {
1753 WDML_CONV* pCurrent = NULL;
1754
1755 for (pCurrent = pInstance->convs[side]; pCurrent != NULL; pCurrent = pCurrent->next)
1756 {
1757 if (DdeCmpStringHandles(pCurrent->hszService, hszService) == 0 &&
1758 DdeCmpStringHandles(pCurrent->hszTopic, hszTopic) == 0)
1759 {
1760 return pCurrent;
1761 }
1762
1763 }
1764 return NULL;
1765 }
1766
1767 /******************************************************************
1768 * WDML_RemoveConv
1769 *
1770 *
1771 */
1772 void WDML_RemoveConv(WDML_CONV* pRef, WDML_SIDE side)
1773 {
1774 WDML_CONV* pPrev = NULL;
1775 WDML_CONV* pCurrent;
1776 WDML_XACT* pXAct;
1777 WDML_XACT* pXActNext;
1778 HWND hWnd;
1779
1780 if (!pRef)
1781 return;
1782
1783 /* remove any pending transaction */
1784 for (pXAct = pRef->transactions; pXAct != NULL; pXAct = pXActNext)
1785 {
1786 pXActNext = pXAct->next;
1787 WDML_FreeTransaction(pRef->instance, pXAct, TRUE);
1788 }
1789
1790 WDML_RemoveAllLinks(pRef->instance, pRef, side);
1791
1792 /* FIXME: should we keep the window around ? it seems so (at least on client side
1793 * to let QueryConvInfo work after conv termination, but also to implement
1794 * DdeReconnect...
1795 */
1796 /* destroy conversation window, but first remove pConv from hWnd.
1797 * this would help the wndProc do appropriate handling upon a WM_DESTROY message
1798 */
1799 hWnd = (side == WDML_CLIENT_SIDE) ? pRef->hwndClient : pRef->hwndServer;
1800 SetWindowLongA(hWnd, GWL_WDML_CONVERSATION, 0);
1801
1802 DestroyWindow((side == WDML_CLIENT_SIDE) ? pRef->hwndClient : pRef->hwndServer);
1803
1804 WDML_DecHSZ(pRef->instance, pRef->hszService);
1805 WDML_DecHSZ(pRef->instance, pRef->hszTopic);
1806
1807 for (pCurrent = pRef->instance->convs[side]; pCurrent != NULL; pCurrent = (pPrev = pCurrent)->next)
1808 {
1809 if (pCurrent == pRef)
1810 {
1811 if (pCurrent == pRef->instance->convs[side])
1812 {
1813 pRef->instance->convs[side] = pCurrent->next;
1814 }
1815 else
1816 {
1817 pPrev->next = pCurrent->next;
1818 }
1819
1820 HeapFree(GetProcessHeap(), 0, pCurrent);
1821 break;
1822 }
1823 }
1824 }
1825
1826 /******************************************************************
1827 * WDML_EnableCallback
1828 */
1829 static BOOL WDML_EnableCallback(WDML_CONV *pConv, UINT wCmd)
1830 {
1831 if (wCmd == EC_DISABLE)
1832 {
1833 FIXME("EC_DISABLE is not implemented\n");
1834 return TRUE;
1835 }
1836
1837 if (wCmd == EC_QUERYWAITING)
1838 return pConv->transactions ? TRUE : FALSE;
1839
1840 if (wCmd != EC_ENABLEALL && wCmd != EC_ENABLEONE)
1841 {
1842 FIXME("Unknown command code %04x\n", wCmd);
1843 return FALSE;
1844 }
1845
1846 while (pConv->transactions)
1847 {
1848 WDML_XACT *pXAct = pConv->transactions;
1849 WDML_UnQueueTransaction(pConv, pXAct);
1850
1851 if (pConv->wStatus & ST_CLIENT)
1852 {
1853 /*WDML_ClientHandle(pConv, pXAct);*/
1854 FIXME("Client delayed transaction queue handling is not supported\n");
1855 }
1856 else
1857 WDML_ServerHandle(pConv, pXAct);
1858
1859 WDML_FreeTransaction(pConv->instance, pXAct, TRUE);
1860
1861 if (wCmd == EC_ENABLEONE) break;
1862 }
1863 return TRUE;
1864 }
1865
1866 /*****************************************************************
1867 * DdeEnableCallback (USER32.@)
1868 */
1869 BOOL WINAPI DdeEnableCallback(DWORD idInst, HCONV hConv, UINT wCmd)
1870 {
1871 BOOL ret = FALSE;
1872 WDML_CONV *pConv;
1873
1874 TRACE("(%ld, %p, %04x)\n", idInst, hConv, wCmd);
1875
1876 EnterCriticalSection(&WDML_CritSect);
1877
1878 pConv = WDML_GetConv(hConv, TRUE);
1879
1880 if (pConv && pConv->instance->instanceID == idInst)
1881 ret = WDML_EnableCallback(pConv, wCmd);
1882
1883 LeaveCriticalSection(&WDML_CritSect);
1884 return ret;
1885 }
1886
1887 /******************************************************************
1888 * WDML_GetConv
1889 *
1890 *
1891 */
1892 WDML_CONV* WDML_GetConv(HCONV hConv, BOOL checkConnected)
1893 {
1894 WDML_CONV* pConv = (WDML_CONV*)hConv;
1895
1896 /* FIXME: should do better checking */
1897 if (pConv == NULL) return NULL;
1898
1899 if (checkConnected && !(pConv->wStatus & ST_CONNECTED))
1900 {
1901 FIXME("found conv but ain't connected\n");
1902 return NULL;
1903 }
1904 if (GetCurrentThreadId() != pConv->instance->threadID)
1905 {
1906 FIXME("wrong thread ID\n");
1907 return NULL;
1908 }
1909
1910 return pConv;
1911 }
1912
1913 /******************************************************************
1914 * WDML_GetConvFromWnd
1915 *
1916 *
1917 */
1918 WDML_CONV* WDML_GetConvFromWnd(HWND hWnd)
1919 {
1920 return (WDML_CONV*)GetWindowLongA(hWnd, GWL_WDML_CONVERSATION);
1921 }
1922
1923 /******************************************************************
1924 * WDML_PostAck
1925 *
1926 *
1927 */
1928 BOOL WDML_PostAck(WDML_CONV* pConv, WDML_SIDE side, WORD appRetCode,
1929 BOOL fBusy, BOOL fAck, UINT pmt, LPARAM lParam, UINT oldMsg)
1930 {
1931 DDEACK ddeAck;
1932 HWND from, to;
1933
1934 if (side == WDML_SERVER_SIDE)
1935 {
1936 from = pConv->hwndServer;
1937 to = pConv->hwndClient;
1938 }
1939 else
1940 {
1941 to = pConv->hwndServer;
1942 from = pConv->hwndClient;
1943 }
1944
1945 ddeAck.bAppReturnCode = appRetCode;
1946 ddeAck.reserved = 0;
1947 ddeAck.fBusy = fBusy;
1948 ddeAck.fAck = fAck;
1949
1950 TRACE("Posting a %s ack\n", ddeAck.fAck ? "positive" : "negative");
1951
1952 lParam = (lParam) ? ReuseDDElParam(lParam, oldMsg, WM_DDE_ACK, *(WORD*)&ddeAck, pmt) :
1953 PackDDElParam(WM_DDE_ACK, *(WORD*)&ddeAck, pmt);
1954 if (!PostMessageA(to, WM_DDE_ACK, (WPARAM)from, lParam))
1955 {
1956 pConv->wStatus &= ~ST_CONNECTED;
1957 FreeDDElParam(WM_DDE_ACK, lParam);
1958 return FALSE;
1959 }
1960 return TRUE;
1961 }
1962
1963 /*****************************************************************
1964 * DdeSetUserHandle (USER32.@)
1965 */
1966 BOOL WINAPI DdeSetUserHandle(HCONV hConv, DWORD id, DWORD hUser)
1967 {
1968 WDML_CONV* pConv;
1969 BOOL ret = TRUE;
1970
1971 TRACE("(%p,%lx,%lx)\n", hConv, id, hUser);
1972
1973 EnterCriticalSection(&WDML_CritSect);
1974
1975 pConv = WDML_GetConv(hConv, FALSE);
1976 if (pConv == NULL)
1977 {
1978 ret = FALSE;
1979 goto theError;
1980 }
1981 if (id == QID_SYNC)
1982 {
1983 pConv->hUser = hUser;
1984 }
1985 else
1986 {
1987 WDML_XACT* pXAct;
1988
1989 pXAct = WDML_FindTransaction(pConv, id);
1990 if (pXAct)
1991 {
1992 pXAct->hUser = hUser;
1993 }
1994 else
1995 {
1996 pConv->instance->lastError = DMLERR_UNFOUND_QUEUE_ID;
1997 ret = FALSE;
1998 }
1999 }
2000 theError:
2001 LeaveCriticalSection(&WDML_CritSect);
2002 return ret;
2003 }
2004
2005 /******************************************************************
2006 * WDML_GetLocalConvInfo
2007 *
2008 *
2009 */
2010 static BOOL WDML_GetLocalConvInfo(WDML_CONV* pConv, CONVINFO* ci, DWORD id)
2011 {
2012 BOOL ret = TRUE;
2013 WDML_LINK* pLink;
2014 WDML_SIDE side;
2015
2016 ci->hConvPartner = (pConv->wStatus & ST_ISLOCAL) ? (HCONV)((DWORD)pConv | 1) : 0;
2017 ci->hszSvcPartner = pConv->hszService;
2018 ci->hszServiceReq = pConv->hszService; /* FIXME: they shouldn't be the same, should they ? */
2019 ci->hszTopic = pConv->hszTopic;
2020 ci->wStatus = pConv->wStatus;
2021
2022 side = (pConv->wStatus & ST_CLIENT) ? WDML_CLIENT_SIDE : WDML_SERVER_SIDE;
2023
2024 for (pLink = pConv->instance->links[side]; pLink != NULL; pLink = pLink->next)
2025 {
2026 if (pLink->hConv == (HCONV)pConv)
2027 {
2028 ci->wStatus |= ST_ADVISE;
2029 break;
2030 }
2031 }
2032
2033 /* FIXME: non handled status flags:
2034 ST_BLOCKED
2035 ST_BLOCKNEXT
2036 ST_INLIST
2037 */
2038
2039 ci->wConvst = pConv->wConvst; /* FIXME */
2040
2041 ci->wLastError = 0; /* FIXME: note it's not the instance last error */
2042 ci->hConvList = 0;
2043 ci->ConvCtxt = pConv->convContext;
2044 if (ci->wStatus & ST_CLIENT)
2045 {
2046 ci->hwnd = pConv->hwndClient;
2047 ci->hwndPartner = pConv->hwndServer;
2048 }
2049 else
2050 {
2051 ci->hwnd = pConv->hwndServer;
2052 ci->hwndPartner = pConv->hwndClient;
2053 }
2054 if (id == QID_SYNC)
2055 {
2056 ci->hUser = pConv->hUser;
2057 ci->hszItem = 0;
2058 ci->wFmt = 0;
2059 ci->wType = 0;
2060 }
2061 else
2062 {
2063 WDML_XACT* pXAct;
2064
2065 pXAct = WDML_FindTransaction(pConv, id);
2066 if (pXAct)
2067 {
2068 ci->hUser = pXAct->hUser;
2069 ci->hszItem = pXAct->hszItem;
2070 ci->wFmt = pXAct->wFmt;
2071 ci->wType = pXAct->wType;
2072 }
2073 else
2074 {
2075 ret = 0;
2076 pConv->instance->lastError = DMLERR_UNFOUND_QUEUE_ID;
2077 }
2078 }
2079 return ret;
2080 }
2081
2082 /******************************************************************
2083 * DdeQueryConvInfo (USER32.@)
2084 *
2085 */
2086 UINT WINAPI DdeQueryConvInfo(HCONV hConv, DWORD id, PCONVINFO lpConvInfo)
2087 {
2088 UINT ret = lpConvInfo->cb;
2089 CONVINFO ci;
2090 WDML_CONV* pConv;
2091
2092 TRACE("(%p,%lx,%p)\n", hConv, id, lpConvInfo);
2093
2094 if (!hConv)
2095 {
2096 FIXME("hConv is NULL\n");
2097 return 0;
2098 }
2099
2100 EnterCriticalSection(&WDML_CritSect);
2101
2102 pConv = WDML_GetConv(hConv, FALSE);
2103 if (pConv != NULL && !WDML_GetLocalConvInfo(pConv, &ci, id))
2104 {
2105 ret = 0;
2106 }
2107 else if ((DWORD)hConv & 1)
2108 {
2109 pConv = WDML_GetConv((HCONV)((DWORD)hConv & ~1), FALSE);
2110 if (pConv != NULL)
2111 {
2112 FIXME("Request on remote conversation information is not implemented yet\n");
2113 ret = 0;
2114 }
2115 }
2116 LeaveCriticalSection(&WDML_CritSect);
2117 if (ret != 0)
2118 memcpy(lpConvInfo, &ci, min((size_t)lpConvInfo->cb, sizeof(ci)));
2119 return ret;
2120 }
2121
2122 /* ================================================================
2123 *
2124 * Link (hot & warm) management
2125 *
2126 * ================================================================ */
2127
2128 /******************************************************************
2129 * WDML_AddLink
2130 *
2131 *
2132 */
2133 void WDML_AddLink(WDML_INSTANCE* pInstance, HCONV hConv, WDML_SIDE side,
2134 UINT wType, HSZ hszItem, UINT wFmt)
2135 {
2136 WDML_LINK* pLink;
2137
2138 pLink = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_LINK));
2139 if (pLink == NULL)
2140 {
2141 ERR("OOM\n");
2142 return;
2143 }
2144
2145 pLink->hConv = hConv;
2146 pLink->transactionType = wType;
2147 WDML_IncHSZ(pInstance, pLink->hszItem = hszItem);
2148 pLink->uFmt = wFmt;
2149 pLink->next = pInstance->links[side];
2150 pInstance->links[side] = pLink;
2151 }
2152
2153 /******************************************************************
2154 * WDML_RemoveLink
2155 *
2156 *
2157 */
2158 void WDML_RemoveLink(WDML_INSTANCE* pInstance, HCONV hConv, WDML_SIDE side,
2159 HSZ hszItem, UINT uFmt)
2160 {
2161 WDML_LINK* pPrev = NULL;
2162 WDML_LINK* pCurrent = NULL;
2163
2164 pCurrent = pInstance->links[side];
2165
2166 while (pCurrent != NULL)
2167 {
2168 if (pCurrent->hConv == hConv &&
2169 DdeCmpStringHandles(pCurrent->hszItem, hszItem) == 0 &&
2170 pCurrent->uFmt == uFmt)
2171 {
2172 if (pCurrent == pInstance->links[side])
2173 {
2174 pInstance->links[side] = pCurrent->next;
2175 }
2176 else
2177 {
2178 pPrev->next = pCurrent->next;
2179 }
2180
2181 WDML_DecHSZ(pInstance, pCurrent->hszItem);
2182 HeapFree(GetProcessHeap(), 0, pCurrent);
2183 break;
2184 }
2185
2186 pPrev = pCurrent;
2187 pCurrent = pCurrent->next;
2188 }
2189 }
2190
2191 /* this function is called to remove all links related to the conv.
2192 It should be called from both client and server when terminating
2193 the conversation.
2194 */
2195 /******************************************************************
2196 * WDML_RemoveAllLinks
2197 *
2198 *
2199 */
2200 void WDML_RemoveAllLinks(WDML_INSTANCE* pInstance, WDML_CONV* pConv, WDML_SIDE side)
2201 {
2202 WDML_LINK* pPrev = NULL;
2203 WDML_LINK* pCurrent = NULL;
2204 WDML_LINK* pNext = NULL;
2205
2206 pCurrent = pInstance->links[side];
2207
2208 while (pCurrent != NULL)
2209 {
2210 if (pCurrent->hConv == (HCONV)pConv)
2211 {
2212 if (pCurrent == pInstance->links[side])
2213 {
2214 pInstance->links[side] = pCurrent->next;
2215 pNext = pCurrent->next;
2216 }
2217 else
2218 {
2219 pPrev->next = pCurrent->next;
2220 pNext = pCurrent->next;
2221 }
2222
2223 WDML_DecHSZ(pInstance, pCurrent->hszItem);
2224
2225 HeapFree(GetProcessHeap(), 0, pCurrent);
2226 pCurrent = NULL;
2227 }
2228
2229 if (pCurrent)
2230 {
2231 pPrev = pCurrent;
2232 pCurrent = pCurrent->next;
2233 }
2234 else
2235 {
2236 pCurrent = pNext;
2237 }
2238 }
2239 }
2240
2241 /******************************************************************
2242 * WDML_FindLink
2243 *
2244 *
2245 */
2246 WDML_LINK* WDML_FindLink(WDML_INSTANCE* pInstance, HCONV hConv, WDML_SIDE side,
2247 HSZ hszItem, BOOL use_fmt, UINT uFmt)
2248 {
2249 WDML_LINK* pCurrent = NULL;
2250
2251 for (pCurrent = pInstance->links[side]; pCurrent != NULL; pCurrent = pCurrent->next)
2252 {
2253 /* we don't need to check for transaction type as it can be altered */
2254
2255 if (pCurrent->hConv == hConv &&
2256 DdeCmpStringHandles(pCurrent->hszItem, hszItem) == 0 &&
2257 (!use_fmt || pCurrent->uFmt == uFmt))
2258 {
2259 break;
2260 }
2261
2262 }
2263
2264 return pCurrent;
2265 }
2266
2267 /* ================================================================
2268 *
2269 * Transaction management
2270 *
2271 * ================================================================ */
2272
2273 /******************************************************************
2274 * WDML_AllocTransaction
2275 *
2276 * Alloc a transaction structure for handling the message ddeMsg
2277 */
2278 WDML_XACT* WDML_AllocTransaction(WDML_INSTANCE* pInstance, UINT ddeMsg,
2279 UINT wFmt, HSZ hszItem)
2280 {
2281 WDML_XACT* pXAct;
2282 static WORD tid = 1; /* FIXME: wrap around */
2283
2284 pXAct = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_XACT));
2285 if (!pXAct)
2286 {
2287 pInstance->lastError = DMLERR_MEMORY_ERROR;
2288 return NULL;
2289 }
2290
2291 pXAct->xActID = tid++;
2292 pXAct->ddeMsg = ddeMsg;
2293 pXAct->hDdeData = 0;
2294 pXAct->hUser = 0;
2295 pXAct->next = NULL;
2296 pXAct->wType = 0;
2297 pXAct->wFmt = wFmt;
2298 if ((pXAct->hszItem = hszItem)) WDML_IncHSZ(pInstance, pXAct->hszItem);
2299 pXAct->atom = 0;
2300 pXAct->hMem = 0;
2301 pXAct->lParam = 0;
2302
2303 return pXAct;
2304 }
2305
2306 /******************************************************************
2307 * WDML_QueueTransaction
2308 *
2309 * Adds a transaction to the list of transaction
2310 */
2311 void WDML_QueueTransaction(WDML_CONV* pConv, WDML_XACT* pXAct)
2312 {
2313 WDML_XACT** pt;
2314
2315 /* advance to last in queue */
2316 for (pt = &pConv->transactions; *pt != NULL; pt = &(*pt)->next);
2317 *pt = pXAct;
2318 }
2319
2320 /******************************************************************
2321 * WDML_UnQueueTransaction
2322 *
2323 *
2324 */
2325 BOOL WDML_UnQueueTransaction(WDML_CONV* pConv, WDML_XACT* pXAct)
2326 {
2327 WDML_XACT** pt;
2328
2329 for (pt = &pConv->transactions; *pt; pt = &(*pt)->next)
2330 {
2331 if (*pt == pXAct)
2332 {
2333 *pt = pXAct->next;
2334 return TRUE;
2335 }
2336 }
2337 return FALSE;
2338 }
2339
2340 /******************************************************************
2341 * WDML_FreeTransaction
2342 *
2343 *
2344 */
2345 void WDML_FreeTransaction(WDML_INSTANCE* pInstance, WDML_XACT* pXAct, BOOL doFreePmt)
2346 {
2347 /* free pmt(s) in pXAct too. check against one for not deleting TRUE return values */
2348 if (doFreePmt && (DWORD)pXAct->hMem > 1)
2349 {
2350 GlobalFree(pXAct->hMem);
2351 }
2352 if (pXAct->hszItem) WDML_DecHSZ(pInstance, pXAct->hszItem);
2353
2354 HeapFree(GetProcessHeap(), 0, pXAct);
2355 }
2356
2357 /******************************************************************
2358 * WDML_FindTransaction
2359 *
2360 *
2361 */
2362 WDML_XACT* WDML_FindTransaction(WDML_CONV* pConv, DWORD tid)
2363 {
2364 WDML_XACT* pXAct;
2365
2366 tid = HIWORD(tid);
2367 for (pXAct = pConv->transactions; pXAct; pXAct = pXAct->next)
2368 {
2369 if (pXAct->xActID == tid)
2370 break;
2371 }
2372 return pXAct;
2373 }
2374
2375 /* ================================================================
2376 *
2377 * Information broadcast across DDEML implementations
2378 *
2379 * ================================================================ */
2380
2381 struct tagWDML_BroadcastPmt
2382 {
2383 LPCSTR clsName;
2384 UINT uMsg;
2385 WPARAM wParam;
2386 LPARAM lParam;
2387 };
2388
2389 /******************************************************************
2390 * WDML_BroadcastEnumProc
2391 *
2392 *
2393 */
2394 static BOOL CALLBACK WDML_BroadcastEnumProc(HWND hWnd, LPARAM lParam)
2395 {
2396 struct tagWDML_BroadcastPmt* s = (struct tagWDML_BroadcastPmt*)lParam;
2397 char buffer[128];
2398
2399 if (GetClassNameA(hWnd, buffer, sizeof(buffer)) > 0 &&
2400 strcmp(buffer, s->clsName) == 0)
2401 {
2402 PostMessageA(hWnd, s->uMsg, s->wParam, s->lParam);
2403 }
2404 return TRUE;
2405 }
2406
2407 /******************************************************************
2408 * WDML_BroadcastDDEWindows
2409 *
2410 *
2411 */
2412 void WDML_BroadcastDDEWindows(const char* clsName, UINT uMsg, WPARAM wParam, LPARAM lParam)
2413 {
2414 struct tagWDML_BroadcastPmt s;
2415
2416 s.clsName = clsName;
2417 s.uMsg = uMsg;
2418 s.wParam = wParam;
2419 s.lParam = lParam;
2420 EnumWindows(WDML_BroadcastEnumProc, (LPARAM)&s);
2421 }