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