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