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