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