sync to trunk (44770)
[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 return GlobalFree(hData) == 0;
1460 }
1461
1462 /******************************************************************
1463 * WDML_IsAppOwned
1464 *
1465 *
1466 */
1467 BOOL WDML_IsAppOwned(HDDEDATA hData)
1468 {
1469 DDE_DATAHANDLE_HEAD* pDdh;
1470 BOOL ret = FALSE;
1471
1472 pDdh = GlobalLock(hData);
1473 if (pDdh != NULL)
1474 {
1475 ret = pDdh->bAppOwned;
1476 GlobalUnlock(hData);
1477 }
1478 return ret;
1479 }
1480
1481 /* ================================================================
1482 *
1483 * Global <=> Data handle management
1484 *
1485 * ================================================================ */
1486
1487 /* Note: we use a DDEDATA, but layout of DDEDATA, DDEADVISE and DDEPOKE structures is similar:
1488 * offset size
1489 * (bytes) (bits) comment
1490 * 0 16 bit fields for options (release, ackreq, response...)
1491 * 2 16 clipboard format
1492 * 4 ? data to be used
1493 */
1494 HDDEDATA WDML_Global2DataHandle(WDML_CONV* pConv, HGLOBAL hMem, WINE_DDEHEAD* p)
1495 {
1496 DDEDATA* pDd;
1497 HDDEDATA ret = 0;
1498 DWORD size;
1499
1500 if (hMem)
1501 {
1502 pDd = GlobalLock(hMem);
1503 size = GlobalSize(hMem) - sizeof(WINE_DDEHEAD);
1504 if (pDd)
1505 {
1506 if (p) memcpy(p, pDd, sizeof(WINE_DDEHEAD));
1507 switch (pDd->cfFormat)
1508 {
1509 default:
1510 FIXME("Unsupported format (%04x) for data %p, passing raw information\n",
1511 pDd->cfFormat, hMem);
1512 /* fall thru */
1513 case 0:
1514 case CF_TEXT:
1515 ret = DdeCreateDataHandle(pConv->instance->instanceID, pDd->Value, size, 0, 0, pDd->cfFormat, 0);
1516 break;
1517 case CF_BITMAP:
1518 if (size >= sizeof(BITMAP))
1519 {
1520 BITMAP* bmp = (BITMAP*)pDd->Value;
1521 int count = bmp->bmWidthBytes * bmp->bmHeight * bmp->bmPlanes;
1522 if (size >= sizeof(BITMAP) + count)
1523 {
1524 HBITMAP hbmp;
1525
1526 if ((hbmp = CreateBitmap(bmp->bmWidth, bmp->bmHeight,
1527 bmp->bmPlanes, bmp->bmBitsPixel,
1528 pDd->Value + sizeof(BITMAP))))
1529 {
1530 ret = DdeCreateDataHandle(pConv->instance->instanceID, (LPBYTE)&hbmp, sizeof(hbmp),
1531 0, 0, CF_BITMAP, 0);
1532 }
1533 else ERR("Can't create bmp\n");
1534 }
1535 else
1536 {
1537 ERR("Wrong count: %u / %d\n", size, count);
1538 }
1539 } else ERR("No bitmap header\n");
1540 break;
1541 }
1542 GlobalUnlock(hMem);
1543 }
1544 }
1545 return ret;
1546 }
1547
1548 /******************************************************************
1549 * WDML_DataHandle2Global
1550 *
1551 *
1552 */
1553 HGLOBAL WDML_DataHandle2Global(HDDEDATA hDdeData, BOOL fResponse, BOOL fRelease,
1554 BOOL fDeferUpd, BOOL fAckReq)
1555 {
1556 DDE_DATAHANDLE_HEAD* pDdh;
1557 DWORD dwSize;
1558 HGLOBAL hMem = 0;
1559
1560 dwSize = GlobalSize(hDdeData) - sizeof(DDE_DATAHANDLE_HEAD);
1561 pDdh = GlobalLock(hDdeData);
1562 if (dwSize && pDdh)
1563 {
1564 WINE_DDEHEAD* wdh = NULL;
1565
1566 switch (pDdh->cfFormat)
1567 {
1568 default:
1569 FIXME("Unsupported format (%04x) for data %p, passing raw information\n",
1570 pDdh->cfFormat, hDdeData);
1571 /* fall thru */
1572 case 0:
1573 case CF_TEXT:
1574 hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, sizeof(WINE_DDEHEAD) + dwSize);
1575 if (hMem && (wdh = GlobalLock(hMem)))
1576 {
1577 memcpy(wdh + 1, pDdh + 1, dwSize);
1578 }
1579 break;
1580 case CF_BITMAP:
1581 if (dwSize >= sizeof(HBITMAP))
1582 {
1583 BITMAP bmp;
1584 DWORD count;
1585 HBITMAP hbmp = *(HBITMAP*)(pDdh + 1);
1586
1587 if (GetObjectW(hbmp, sizeof(bmp), &bmp))
1588 {
1589 count = bmp.bmWidthBytes * bmp.bmHeight;
1590 hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE,
1591 sizeof(WINE_DDEHEAD) + sizeof(bmp) + count);
1592 if (hMem && (wdh = GlobalLock(hMem)))
1593 {
1594 memcpy(wdh + 1, &bmp, sizeof(bmp));
1595 GetBitmapBits(hbmp, count, ((char*)(wdh + 1)) + sizeof(bmp));
1596 }
1597 }
1598 }
1599 break;
1600 }
1601 if (wdh)
1602 {
1603 wdh->unused = 0;
1604 wdh->fResponse = fResponse;
1605 wdh->fRelease = fRelease;
1606 wdh->fDeferUpd = fDeferUpd;
1607 wdh->fAckReq = fAckReq;
1608 wdh->cfFormat = pDdh->cfFormat;
1609 GlobalUnlock(hMem);
1610 }
1611 GlobalUnlock(hDdeData);
1612 }
1613
1614 return hMem;
1615 }
1616
1617 /* ================================================================
1618 *
1619 * Server management
1620 *
1621 * ================================================================ */
1622
1623 /******************************************************************
1624 * WDML_AddServer
1625 *
1626 *
1627 */
1628 WDML_SERVER* WDML_AddServer(WDML_INSTANCE* pInstance, HSZ hszService, HSZ hszTopic)
1629 {
1630 static const WCHAR fmtW[] = {'%','s','(','0','x','%','*','x',')',0};
1631 WDML_SERVER* pServer;
1632 WCHAR buf1[256];
1633 WCHAR buf2[256];
1634
1635 pServer = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_SERVER));
1636 if (pServer == NULL) return NULL;
1637
1638 pServer->hszService = hszService;
1639 WDML_IncHSZ(pInstance, hszService);
1640
1641 DdeQueryStringW(pInstance->instanceID, hszService, buf1, 256, CP_WINUNICODE);
1642 snprintfW(buf2, 256, fmtW, buf1, 2*sizeof(ULONG_PTR), GetCurrentProcessId());
1643 pServer->hszServiceSpec = DdeCreateStringHandleW(pInstance->instanceID, buf2, CP_WINUNICODE);
1644
1645 pServer->atomService = WDML_MakeAtomFromHsz(pServer->hszService);
1646 pServer->atomServiceSpec = WDML_MakeAtomFromHsz(pServer->hszServiceSpec);
1647
1648 pServer->filterOn = TRUE;
1649
1650 pServer->next = pInstance->servers;
1651 pInstance->servers = pServer;
1652 return pServer;
1653 }
1654
1655 /******************************************************************
1656 * WDML_RemoveServer
1657 *
1658 *
1659 */
1660 void WDML_RemoveServer(WDML_INSTANCE* pInstance, HSZ hszService, HSZ hszTopic)
1661 {
1662 WDML_SERVER* pPrev = NULL;
1663 WDML_SERVER* pServer = NULL;
1664 WDML_CONV* pConv;
1665 WDML_CONV* pConvNext;
1666
1667 pServer = pInstance->servers;
1668
1669 while (pServer != NULL)
1670 {
1671 if (DdeCmpStringHandles(pServer->hszService, hszService) == 0)
1672 {
1673 WDML_BroadcastDDEWindows(WDML_szEventClass, WM_WDML_UNREGISTER,
1674 pServer->atomService, pServer->atomServiceSpec);
1675 /* terminate all conversations for given topic */
1676 for (pConv = pInstance->convs[WDML_SERVER_SIDE]; pConv != NULL; pConv = pConvNext)
1677 {
1678 pConvNext = pConv->next;
1679 if (DdeCmpStringHandles(pConv->hszService, hszService) == 0)
1680 {
1681 HWND client = pConv->hwndClient, server = pConv->hwndServer;
1682 WDML_RemoveConv(pConv, WDML_SERVER_SIDE);
1683 /* don't care about return code (whether client window is present or not) */
1684 PostMessageW(client, WM_DDE_TERMINATE, (WPARAM)server, 0);
1685 }
1686 }
1687 if (pServer == pInstance->servers)
1688 {
1689 pInstance->servers = pServer->next;
1690 }
1691 else
1692 {
1693 pPrev->next = pServer->next;
1694 }
1695
1696 DestroyWindow(pServer->hwndServer);
1697 WDML_DecHSZ(pInstance, pServer->hszServiceSpec);
1698 WDML_DecHSZ(pInstance, pServer->hszService);
1699
1700 GlobalDeleteAtom(pServer->atomService);
1701 GlobalDeleteAtom(pServer->atomServiceSpec);
1702
1703 HeapFree(GetProcessHeap(), 0, pServer);
1704 break;
1705 }
1706
1707 pPrev = pServer;
1708 pServer = pServer->next;
1709 }
1710 }
1711
1712 /*****************************************************************************
1713 * WDML_FindServer
1714 *
1715 * generic routine to return a pointer to the relevant ServiceNode
1716 * for a given service name, or NULL if the entry does not exist
1717 *
1718 */
1719 WDML_SERVER* WDML_FindServer(WDML_INSTANCE* pInstance, HSZ hszService, HSZ hszTopic)
1720 {
1721 WDML_SERVER* pServer;
1722
1723 for (pServer = pInstance->servers; pServer != NULL; pServer = pServer->next)
1724 {
1725 if (hszService == pServer->hszService)
1726 {
1727 return pServer;
1728 }
1729 }
1730 TRACE("Service name missing\n");
1731 return NULL;
1732 }
1733
1734 /* ================================================================
1735 *
1736 * Conversation management
1737 *
1738 * ================================================================ */
1739
1740 /******************************************************************
1741 * WDML_AddConv
1742 *
1743 *
1744 */
1745 WDML_CONV* WDML_AddConv(WDML_INSTANCE* pInstance, WDML_SIDE side,
1746 HSZ hszService, HSZ hszTopic, HWND hwndClient, HWND hwndServer)
1747 {
1748 WDML_CONV* pConv;
1749
1750 /* no conversation yet, add it */
1751 pConv = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_CONV));
1752 if (!pConv) return NULL;
1753
1754 pConv->instance = pInstance;
1755 WDML_IncHSZ(pInstance, pConv->hszService = hszService);
1756 WDML_IncHSZ(pInstance, pConv->hszTopic = hszTopic);
1757 pConv->magic = WDML_CONV_MAGIC;
1758 pConv->hwndServer = hwndServer;
1759 pConv->hwndClient = hwndClient;
1760 pConv->transactions = NULL;
1761 pConv->hUser = 0;
1762 pConv->wStatus = (side == WDML_CLIENT_SIDE) ? ST_CLIENT : 0L;
1763 pConv->wStatus |= pInstance->wStatus;
1764 /* check if both side of the conversation are of the same instance */
1765 if (GetWindowThreadProcessId(hwndClient, NULL) == GetWindowThreadProcessId(hwndServer, NULL) &&
1766 WDML_GetInstanceFromWnd(hwndClient) == WDML_GetInstanceFromWnd(hwndServer))
1767 {
1768 pConv->wStatus |= ST_ISSELF;
1769 }
1770 pConv->wConvst = XST_NULL;
1771
1772 pConv->next = pInstance->convs[side];
1773 pInstance->convs[side] = pConv;
1774
1775 TRACE("pConv->wStatus %04x pInstance(%p)\n", pConv->wStatus, pInstance);
1776
1777 return pConv;
1778 }
1779
1780 /******************************************************************
1781 * WDML_FindConv
1782 *
1783 *
1784 */
1785 WDML_CONV* WDML_FindConv(WDML_INSTANCE* pInstance, WDML_SIDE side,
1786 HSZ hszService, HSZ hszTopic)
1787 {
1788 WDML_CONV* pCurrent = NULL;
1789
1790 for (pCurrent = pInstance->convs[side]; pCurrent != NULL; pCurrent = pCurrent->next)
1791 {
1792 if (DdeCmpStringHandles(pCurrent->hszService, hszService) == 0 &&
1793 DdeCmpStringHandles(pCurrent->hszTopic, hszTopic) == 0)
1794 {
1795 return pCurrent;
1796 }
1797
1798 }
1799 return NULL;
1800 }
1801
1802 /******************************************************************
1803 * WDML_RemoveConv
1804 *
1805 *
1806 */
1807 void WDML_RemoveConv(WDML_CONV* pRef, WDML_SIDE side)
1808 {
1809 WDML_CONV* pPrev = NULL;
1810 WDML_CONV* pCurrent;
1811 WDML_XACT* pXAct;
1812 WDML_XACT* pXActNext;
1813 HWND hWnd;
1814
1815 if (!pRef)
1816 return;
1817
1818 /* remove any pending transaction */
1819 for (pXAct = pRef->transactions; pXAct != NULL; pXAct = pXActNext)
1820 {
1821 pXActNext = pXAct->next;
1822 WDML_FreeTransaction(pRef->instance, pXAct, TRUE);
1823 }
1824
1825 WDML_RemoveAllLinks(pRef->instance, pRef, side);
1826
1827 /* FIXME: should we keep the window around ? it seems so (at least on client side
1828 * to let QueryConvInfo work after conv termination, but also to implement
1829 * DdeReconnect...
1830 */
1831 /* destroy conversation window, but first remove pConv from hWnd.
1832 * this would help the wndProc do appropriate handling upon a WM_DESTROY message
1833 */
1834 hWnd = (side == WDML_CLIENT_SIDE) ? pRef->hwndClient : pRef->hwndServer;
1835 SetWindowLongPtrW(hWnd, GWL_WDML_CONVERSATION, 0);
1836
1837 DestroyWindow((side == WDML_CLIENT_SIDE) ? pRef->hwndClient : pRef->hwndServer);
1838
1839 WDML_DecHSZ(pRef->instance, pRef->hszService);
1840 WDML_DecHSZ(pRef->instance, pRef->hszTopic);
1841
1842 for (pCurrent = pRef->instance->convs[side]; pCurrent != NULL; pCurrent = (pPrev = pCurrent)->next)
1843 {
1844 if (pCurrent == pRef)
1845 {
1846 if (pCurrent == pRef->instance->convs[side])
1847 {
1848 pRef->instance->convs[side] = pCurrent->next;
1849 }
1850 else
1851 {
1852 pPrev->next = pCurrent->next;
1853 }
1854 pCurrent->magic = 0;
1855 HeapFree(GetProcessHeap(), 0, pCurrent);
1856 break;
1857 }
1858 }
1859 }
1860
1861 /******************************************************************
1862 * WDML_EnableCallback
1863 */
1864 static BOOL WDML_EnableCallback(WDML_CONV *pConv, UINT wCmd)
1865 {
1866 if (wCmd == EC_DISABLE)
1867 {
1868 pConv->wStatus |= ST_BLOCKED;
1869 TRACE("EC_DISABLE: conv %p status flags %04x\n", pConv, pConv->wStatus);
1870 return TRUE;
1871 }
1872
1873 if (wCmd == EC_QUERYWAITING)
1874 return pConv->transactions ? TRUE : FALSE;
1875
1876 if (wCmd != EC_ENABLEALL && wCmd != EC_ENABLEONE)
1877 {
1878 FIXME("Unknown command code %04x\n", wCmd);
1879 return FALSE;
1880 }
1881
1882 if (wCmd == EC_ENABLEALL)
1883 {
1884 pConv->wStatus &= ~ST_BLOCKED;
1885 TRACE("EC_ENABLEALL: conv %p status flags %04x\n", pConv, pConv->wStatus);
1886 }
1887
1888 while (pConv->transactions)
1889 {
1890 WDML_XACT *pXAct = pConv->transactions;
1891
1892 if (pConv->wStatus & ST_CLIENT)
1893 {
1894 /* transaction should be in the queue until handled */
1895 WDML_ClientHandle(pConv, pXAct, 0, NULL);
1896 WDML_UnQueueTransaction(pConv, pXAct);
1897 }
1898 else
1899 {
1900 /* transaction should be removed from the queue before handling */
1901 WDML_UnQueueTransaction(pConv, pXAct);
1902 WDML_ServerHandle(pConv, pXAct);
1903 }
1904
1905 WDML_FreeTransaction(pConv->instance, pXAct, TRUE);
1906
1907 if (wCmd == EC_ENABLEONE) break;
1908 }
1909 return TRUE;
1910 }
1911
1912 /*****************************************************************
1913 * DdeEnableCallback (USER32.@)
1914 */
1915 BOOL WINAPI DdeEnableCallback(DWORD idInst, HCONV hConv, UINT wCmd)
1916 {
1917 BOOL ret = FALSE;
1918 WDML_CONV *pConv;
1919
1920 TRACE("(%d, %p, %04x)\n", idInst, hConv, wCmd);
1921
1922 if (hConv)
1923 {
1924 pConv = WDML_GetConv(hConv, TRUE);
1925
1926 if (pConv && pConv->instance->instanceID == idInst)
1927 ret = WDML_EnableCallback(pConv, wCmd);
1928 }
1929 else
1930 {
1931 WDML_INSTANCE *pInstance = WDML_GetInstance(idInst);
1932
1933 if (!pInstance)
1934 return FALSE;
1935
1936 TRACE("adding flags %04x to instance %p\n", wCmd, pInstance);
1937 pInstance->wStatus |= wCmd;
1938
1939 if (wCmd == EC_DISABLE)
1940 {
1941 pInstance->wStatus |= ST_BLOCKED;
1942 TRACE("EC_DISABLE: inst %p status flags %04x\n", pInstance, pInstance->wStatus);
1943 }
1944 else if (wCmd == EC_ENABLEALL)
1945 {
1946 pInstance->wStatus &= ~ST_BLOCKED;
1947 TRACE("EC_ENABLEALL: inst %p status flags %04x\n", pInstance, pInstance->wStatus);
1948 }
1949
1950 ret = TRUE;
1951
1952 for (pConv = pInstance->convs[WDML_CLIENT_SIDE]; pConv != NULL; pConv = pConv->next)
1953 {
1954 ret = WDML_EnableCallback(pConv, wCmd);
1955 if (ret && wCmd == EC_QUERYWAITING) break;
1956 }
1957 }
1958
1959 return ret;
1960 }
1961
1962 /******************************************************************
1963 * WDML_GetConv
1964 *
1965 *
1966 */
1967 WDML_CONV* WDML_GetConv(HCONV hConv, BOOL checkConnected)
1968 {
1969 WDML_CONV* pConv = (WDML_CONV*)hConv;
1970
1971 /* FIXME: should do better checking */
1972 if (pConv == NULL || pConv->magic != WDML_CONV_MAGIC) return NULL;
1973
1974 if (!pConv->instance)
1975 {
1976 WARN("wrong thread ID, no instance\n");
1977 return NULL;
1978 }
1979
1980 if (pConv->instance->threadID != GetCurrentThreadId())
1981 {
1982 WARN("wrong thread ID\n");
1983 pConv->instance->lastError = DMLERR_INVALIDPARAMETER; /* FIXME: check */
1984 return NULL;
1985 }
1986
1987 if (checkConnected && !(pConv->wStatus & ST_CONNECTED))
1988 {
1989 WARN("found conv but ain't connected\n");
1990 pConv->instance->lastError = DMLERR_NO_CONV_ESTABLISHED;
1991 return NULL;
1992 }
1993
1994 return pConv;
1995 }
1996
1997 /******************************************************************
1998 * WDML_GetConvFromWnd
1999 *
2000 *
2001 */
2002 WDML_CONV* WDML_GetConvFromWnd(HWND hWnd)
2003 {
2004 return (WDML_CONV*)GetWindowLongPtrW(hWnd, GWL_WDML_CONVERSATION);
2005 }
2006
2007 /******************************************************************
2008 * WDML_PostAck
2009 *
2010 *
2011 */
2012 BOOL WDML_PostAck(WDML_CONV* pConv, WDML_SIDE side, WORD appRetCode,
2013 BOOL fBusy, BOOL fAck, UINT_PTR pmt, LPARAM lParam, UINT oldMsg)
2014 {
2015 DDEACK ddeAck;
2016 HWND from, to;
2017
2018 if (side == WDML_SERVER_SIDE)
2019 {
2020 from = pConv->hwndServer;
2021 to = pConv->hwndClient;
2022 }
2023 else
2024 {
2025 to = pConv->hwndServer;
2026 from = pConv->hwndClient;
2027 }
2028
2029 ddeAck.bAppReturnCode = appRetCode;
2030 ddeAck.reserved = 0;
2031 ddeAck.fBusy = fBusy;
2032 ddeAck.fAck = fAck;
2033
2034 TRACE("Posting a %s ack\n", ddeAck.fAck ? "positive" : "negative");
2035
2036 lParam = (lParam) ? ReuseDDElParam(lParam, oldMsg, WM_DDE_ACK, *(WORD*)&ddeAck, pmt) :
2037 PackDDElParam(WM_DDE_ACK, *(WORD*)&ddeAck, pmt);
2038 if (!PostMessageW(to, WM_DDE_ACK, (WPARAM)from, lParam))
2039 {
2040 pConv->wStatus &= ~ST_CONNECTED;
2041 pConv->instance->lastError = DMLERR_POSTMSG_FAILED;
2042 FreeDDElParam(WM_DDE_ACK, lParam);
2043 return FALSE;
2044 }
2045 return TRUE;
2046 }
2047
2048 /*****************************************************************
2049 * DdeSetUserHandle (USER32.@)
2050 */
2051 BOOL WINAPI DdeSetUserHandle(HCONV hConv, DWORD id, DWORD hUser)
2052 {
2053 WDML_CONV* pConv;
2054
2055 pConv = WDML_GetConv(hConv, FALSE);
2056 if (pConv == NULL)
2057 return FALSE;
2058
2059 if (id == QID_SYNC)
2060 {
2061 pConv->hUser = hUser;
2062 }
2063 else
2064 {
2065 WDML_XACT* pXAct;
2066
2067 pXAct = WDML_FindTransaction(pConv, id);
2068 if (pXAct)
2069 {
2070 pXAct->hUser = hUser;
2071 }
2072 else
2073 {
2074 pConv->instance->lastError = DMLERR_UNFOUND_QUEUE_ID;
2075 return FALSE;
2076 }
2077 }
2078 return TRUE;
2079 }
2080
2081 /******************************************************************
2082 * WDML_GetLocalConvInfo
2083 *
2084 *
2085 */
2086 static BOOL WDML_GetLocalConvInfo(WDML_CONV* pConv, CONVINFO* ci, DWORD id)
2087 {
2088 BOOL ret = TRUE;
2089 WDML_LINK* pLink;
2090 WDML_SIDE side;
2091
2092 ci->hConvPartner = (pConv->wStatus & ST_ISLOCAL) ? (HCONV)((ULONG_PTR)pConv | 1) : 0;
2093 ci->hszSvcPartner = pConv->hszService;
2094 ci->hszServiceReq = pConv->hszService; /* FIXME: they shouldn't be the same, should they ? */
2095 ci->hszTopic = pConv->hszTopic;
2096 ci->wStatus = pConv->wStatus;
2097
2098 side = (pConv->wStatus & ST_CLIENT) ? WDML_CLIENT_SIDE : WDML_SERVER_SIDE;
2099
2100 for (pLink = pConv->instance->links[side]; pLink != NULL; pLink = pLink->next)
2101 {
2102 if (pLink->hConv == (HCONV)pConv)
2103 {
2104 ci->wStatus |= ST_ADVISE;
2105 break;
2106 }
2107 }
2108
2109 /* FIXME: non handled status flags:
2110 ST_BLOCKED
2111 ST_BLOCKNEXT
2112 ST_INLIST
2113 */
2114
2115 ci->wConvst = pConv->wConvst; /* FIXME */
2116
2117 ci->wLastError = 0; /* FIXME: note it's not the instance last error */
2118 ci->hConvList = 0;
2119 ci->ConvCtxt = pConv->convContext;
2120 if (ci->wStatus & ST_CLIENT)
2121 {
2122 ci->hwnd = pConv->hwndClient;
2123 ci->hwndPartner = pConv->hwndServer;
2124 }
2125 else
2126 {
2127 ci->hwnd = pConv->hwndServer;
2128 ci->hwndPartner = pConv->hwndClient;
2129 }
2130 if (id == QID_SYNC)
2131 {
2132 ci->hUser = pConv->hUser;
2133 ci->hszItem = 0;
2134 ci->wFmt = 0;
2135 ci->wType = 0;
2136 }
2137 else
2138 {
2139 WDML_XACT* pXAct;
2140
2141 pXAct = WDML_FindTransaction(pConv, id);
2142 if (pXAct)
2143 {
2144 ci->hUser = pXAct->hUser;
2145 ci->hszItem = pXAct->hszItem;
2146 ci->wFmt = pXAct->wFmt;
2147 ci->wType = pXAct->wType;
2148 }
2149 else
2150 {
2151 ret = 0;
2152 pConv->instance->lastError = DMLERR_UNFOUND_QUEUE_ID;
2153 }
2154 }
2155 return ret;
2156 }
2157
2158 /******************************************************************
2159 * DdeQueryConvInfo (USER32.@)
2160 *
2161 * FIXME: Set last DDE error on failure.
2162 */
2163 UINT WINAPI DdeQueryConvInfo(HCONV hConv, DWORD id, PCONVINFO lpConvInfo)
2164 {
2165 UINT ret = lpConvInfo->cb;
2166 CONVINFO ci;
2167 WDML_CONV* pConv;
2168
2169 TRACE("(%p,%x,%p)\n", hConv, id, lpConvInfo);
2170
2171 if (!hConv)
2172 {
2173 FIXME("hConv is NULL\n");
2174 return 0;
2175 }
2176
2177 pConv = WDML_GetConv(hConv, FALSE);
2178 if (pConv != NULL)
2179 {
2180 if (!WDML_GetLocalConvInfo(pConv, &ci, id))
2181 ret = 0;
2182 }
2183 else
2184 {
2185 if ((ULONG_PTR)hConv & 1)
2186 {
2187 pConv = WDML_GetConv((HCONV)((ULONG_PTR)hConv & ~1), FALSE);
2188 if (pConv != NULL)
2189 FIXME("Request on remote conversation information is not implemented yet\n");
2190 }
2191 ret = 0;
2192 }
2193
2194 if (ret != 0)
2195 memcpy(lpConvInfo, &ci, min((size_t)lpConvInfo->cb, sizeof(ci)));
2196 return ret;
2197 }
2198
2199 /* ================================================================
2200 *
2201 * Link (hot & warm) management
2202 *
2203 * ================================================================ */
2204
2205 /******************************************************************
2206 * WDML_AddLink
2207 *
2208 *
2209 */
2210 void WDML_AddLink(WDML_INSTANCE* pInstance, HCONV hConv, WDML_SIDE side,
2211 UINT wType, HSZ hszItem, UINT wFmt)
2212 {
2213 WDML_LINK* pLink;
2214
2215 pLink = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_LINK));
2216 if (pLink == NULL)
2217 {
2218 ERR("OOM\n");
2219 return;
2220 }
2221
2222 pLink->hConv = hConv;
2223 pLink->transactionType = wType;
2224 WDML_IncHSZ(pInstance, pLink->hszItem = hszItem);
2225 pLink->uFmt = wFmt;
2226 pLink->next = pInstance->links[side];
2227 pInstance->links[side] = pLink;
2228 }
2229
2230 /******************************************************************
2231 * WDML_RemoveLink
2232 *
2233 *
2234 */
2235 void WDML_RemoveLink(WDML_INSTANCE* pInstance, HCONV hConv, WDML_SIDE side,
2236 HSZ hszItem, UINT uFmt)
2237 {
2238 WDML_LINK* pPrev = NULL;
2239 WDML_LINK* pCurrent = NULL;
2240
2241 pCurrent = pInstance->links[side];
2242
2243 while (pCurrent != NULL)
2244 {
2245 if (pCurrent->hConv == hConv &&
2246 DdeCmpStringHandles(pCurrent->hszItem, hszItem) == 0 &&
2247 pCurrent->uFmt == uFmt)
2248 {
2249 if (pCurrent == pInstance->links[side])
2250 {
2251 pInstance->links[side] = pCurrent->next;
2252 }
2253 else
2254 {
2255 pPrev->next = pCurrent->next;
2256 }
2257
2258 WDML_DecHSZ(pInstance, pCurrent->hszItem);
2259 HeapFree(GetProcessHeap(), 0, pCurrent);
2260 break;
2261 }
2262
2263 pPrev = pCurrent;
2264 pCurrent = pCurrent->next;
2265 }
2266 }
2267
2268 /* this function is called to remove all links related to the conv.
2269 It should be called from both client and server when terminating
2270 the conversation.
2271 */
2272 /******************************************************************
2273 * WDML_RemoveAllLinks
2274 *
2275 *
2276 */
2277 void WDML_RemoveAllLinks(WDML_INSTANCE* pInstance, WDML_CONV* pConv, WDML_SIDE side)
2278 {
2279 WDML_LINK* pPrev = NULL;
2280 WDML_LINK* pCurrent = NULL;
2281 WDML_LINK* pNext = NULL;
2282
2283 pCurrent = pInstance->links[side];
2284
2285 while (pCurrent != NULL)
2286 {
2287 if (pCurrent->hConv == (HCONV)pConv)
2288 {
2289 if (pCurrent == pInstance->links[side])
2290 {
2291 pInstance->links[side] = pCurrent->next;
2292 pNext = pCurrent->next;
2293 }
2294 else
2295 {
2296 pPrev->next = pCurrent->next;
2297 pNext = pCurrent->next;
2298 }
2299
2300 WDML_DecHSZ(pInstance, pCurrent->hszItem);
2301
2302 HeapFree(GetProcessHeap(), 0, pCurrent);
2303 pCurrent = NULL;
2304 }
2305
2306 if (pCurrent)
2307 {
2308 pPrev = pCurrent;
2309 pCurrent = pCurrent->next;
2310 }
2311 else
2312 {
2313 pCurrent = pNext;
2314 }
2315 }
2316 }
2317
2318 /******************************************************************
2319 * WDML_FindLink
2320 *
2321 *
2322 */
2323 WDML_LINK* WDML_FindLink(WDML_INSTANCE* pInstance, HCONV hConv, WDML_SIDE side,
2324 HSZ hszItem, BOOL use_fmt, UINT uFmt)
2325 {
2326 WDML_LINK* pCurrent = NULL;
2327
2328 for (pCurrent = pInstance->links[side]; pCurrent != NULL; pCurrent = pCurrent->next)
2329 {
2330 /* we don't need to check for transaction type as it can be altered */
2331
2332 if (pCurrent->hConv == hConv &&
2333 DdeCmpStringHandles(pCurrent->hszItem, hszItem) == 0 &&
2334 (!use_fmt || pCurrent->uFmt == uFmt))
2335 {
2336 break;
2337 }
2338
2339 }
2340
2341 return pCurrent;
2342 }
2343
2344 /* ================================================================
2345 *
2346 * Transaction management
2347 *
2348 * ================================================================ */
2349
2350 /******************************************************************
2351 * WDML_AllocTransaction
2352 *
2353 * Alloc a transaction structure for handling the message ddeMsg
2354 */
2355 WDML_XACT* WDML_AllocTransaction(WDML_INSTANCE* pInstance, UINT ddeMsg,
2356 UINT wFmt, HSZ hszItem)
2357 {
2358 WDML_XACT* pXAct;
2359 static WORD tid = 1; /* FIXME: wrap around */
2360
2361 pXAct = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_XACT));
2362 if (!pXAct)
2363 {
2364 pInstance->lastError = DMLERR_MEMORY_ERROR;
2365 return NULL;
2366 }
2367
2368 pXAct->xActID = tid++;
2369 pXAct->ddeMsg = ddeMsg;
2370 pXAct->hDdeData = 0;
2371 pXAct->hUser = 0;
2372 pXAct->next = NULL;
2373 pXAct->wType = 0;
2374 pXAct->wFmt = wFmt;
2375 if ((pXAct->hszItem = hszItem)) WDML_IncHSZ(pInstance, pXAct->hszItem);
2376 pXAct->atom = 0;
2377 pXAct->hMem = 0;
2378 pXAct->lParam = 0;
2379
2380 return pXAct;
2381 }
2382
2383 /******************************************************************
2384 * WDML_QueueTransaction
2385 *
2386 * Adds a transaction to the list of transaction
2387 */
2388 void WDML_QueueTransaction(WDML_CONV* pConv, WDML_XACT* pXAct)
2389 {
2390 WDML_XACT** pt;
2391
2392 /* advance to last in queue */
2393 for (pt = &pConv->transactions; *pt != NULL; pt = &(*pt)->next);
2394 *pt = pXAct;
2395 }
2396
2397 /******************************************************************
2398 * WDML_UnQueueTransaction
2399 *
2400 *
2401 */
2402 BOOL WDML_UnQueueTransaction(WDML_CONV* pConv, WDML_XACT* pXAct)
2403 {
2404 WDML_XACT** pt;
2405
2406 for (pt = &pConv->transactions; *pt; pt = &(*pt)->next)
2407 {
2408 if (*pt == pXAct)
2409 {
2410 *pt = pXAct->next;
2411 return TRUE;
2412 }
2413 }
2414 return FALSE;
2415 }
2416
2417 /******************************************************************
2418 * WDML_FreeTransaction
2419 *
2420 *
2421 */
2422 void WDML_FreeTransaction(WDML_INSTANCE* pInstance, WDML_XACT* pXAct, BOOL doFreePmt)
2423 {
2424 /* free pmt(s) in pXAct too. check against one for not deleting TRUE return values */
2425 if (doFreePmt && (ULONG_PTR)pXAct->hMem > 1)
2426 {
2427 GlobalFree(pXAct->hMem);
2428 }
2429 if (pXAct->hszItem) WDML_DecHSZ(pInstance, pXAct->hszItem);
2430
2431 HeapFree(GetProcessHeap(), 0, pXAct);
2432 }
2433
2434 /******************************************************************
2435 * WDML_FindTransaction
2436 *
2437 *
2438 */
2439 WDML_XACT* WDML_FindTransaction(WDML_CONV* pConv, DWORD tid)
2440 {
2441 WDML_XACT* pXAct;
2442
2443 tid = HIWORD(tid);
2444 for (pXAct = pConv->transactions; pXAct; pXAct = pXAct->next)
2445 {
2446 if (pXAct->xActID == tid)
2447 break;
2448 }
2449 return pXAct;
2450 }
2451
2452 /* ================================================================
2453 *
2454 * Information broadcast across DDEML implementations
2455 *
2456 * ================================================================ */
2457
2458 struct tagWDML_BroadcastPmt
2459 {
2460 LPCWSTR clsName;
2461 UINT uMsg;
2462 WPARAM wParam;
2463 LPARAM lParam;
2464 };
2465
2466 /******************************************************************
2467 * WDML_BroadcastEnumProc
2468 *
2469 *
2470 */
2471 static BOOL CALLBACK WDML_BroadcastEnumProc(HWND hWnd, LPARAM lParam)
2472 {
2473 struct tagWDML_BroadcastPmt* s = (struct tagWDML_BroadcastPmt*)lParam;
2474 WCHAR buffer[128];
2475
2476 if (GetClassNameW(hWnd, buffer, 128) > 0 &&
2477 lstrcmpiW(buffer, s->clsName) == 0)
2478 {
2479 PostMessageW(hWnd, s->uMsg, s->wParam, s->lParam);
2480 }
2481 return TRUE;
2482 }
2483
2484 /******************************************************************
2485 * WDML_BroadcastDDEWindows
2486 *
2487 *
2488 */
2489 void WDML_BroadcastDDEWindows(LPCWSTR clsName, UINT uMsg, WPARAM wParam, LPARAM lParam)
2490 {
2491 struct tagWDML_BroadcastPmt s;
2492
2493 s.clsName = clsName;
2494 s.uMsg = uMsg;
2495 s.wParam = wParam;
2496 s.lParam = lParam;
2497 EnumWindows(WDML_BroadcastEnumProc, (LPARAM)&s);
2498 }