Revert "winex11.drv: Optimise getting the bits of a DIB after calling SetDIBits."
[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         /* can't reinitialise if we have initialised nothing !! */
519         reference_inst = WDML_InstanceList;
520         /* must first check if we have been given a valid instance to re-initialise !!  how do we do that ? */
521         /*
522          *      MS allows initialisation without specifying a callback, should we allow addition of the
523          *      callback by a later call to initialise ? - if so this lot will have to change
524          */
525         while (reference_inst->next != NULL)
526         {
527             if (*pidInst == reference_inst->instanceID && pfnCallback == reference_inst->callback)
528             {
529                 /* Check 1 - cannot change client-only mode if set via APPCMD_CLIENTONLY */
530
531                 if (reference_inst->clientOnly)
532                 {
533                     if  ((reference_inst->CBFflags & CBF_FAIL_ALLSVRXACTIONS) != CBF_FAIL_ALLSVRXACTIONS)
534                     {
535                                 /* i.e. Was set to Client-only and through APPCMD_CLIENTONLY */
536
537                         if (!(afCmd & APPCMD_CLIENTONLY))
538                         {
539                             ret = DMLERR_INVALIDPARAMETER;
540                             goto theError;
541                         }
542                     }
543                 }
544                 /* Check 2 - cannot change monitor modes */
545
546                 if (pInstance->monitor != reference_inst->monitor)
547                 {
548                     ret = DMLERR_INVALIDPARAMETER;
549                     goto theError;
550                 }
551
552                 /* Check 3 - trying to set Client-only via APPCMD when not set so previously */
553
554                 if ((afCmd&APPCMD_CLIENTONLY) && !reference_inst->clientOnly)
555                 {
556                     ret = DMLERR_INVALIDPARAMETER;
557                     goto theError;
558                 }
559                 break;
560             }
561             reference_inst = reference_inst->next;
562         }
563         if (reference_inst->next == NULL)
564         {
565             ret = DMLERR_INVALIDPARAMETER;
566             goto theError;
567         }
568         /* All checked - change relevant flags */
569
570         reference_inst->CBFflags = pInstance->CBFflags;
571         reference_inst->clientOnly = pInstance->clientOnly;
572         reference_inst->monitorFlags = pInstance->monitorFlags;
573
574         HeapFree(GetProcessHeap(), 0, pInstance); /* finished - release heap space used as work store */
575
576         LeaveCriticalSection(&WDML_CritSect);
577     }
578
579     return DMLERR_NO_ERROR;
580  theError:
581     HeapFree(GetProcessHeap(), 0, pInstance);
582     LeaveCriticalSection(&WDML_CritSect);
583     return ret;
584 }
585
586 /******************************************************************************
587  *            DdeInitializeA   (USER32.@)
588  *
589  * See DdeInitializeW.
590  */
591 UINT WINAPI DdeInitializeA(LPDWORD pidInst, PFNCALLBACK pfnCallback,
592                            DWORD afCmd, DWORD ulRes)
593 {
594     return WDML_Initialize(pidInst, pfnCallback, afCmd, ulRes, FALSE, FALSE);
595 }
596
597 /******************************************************************************
598  * DdeInitializeW [USER32.@]
599  * Registers an application with the DDEML
600  *
601  * PARAMS
602  *    pidInst     [I] Pointer to instance identifier
603  *    pfnCallback [I] Pointer to callback function
604  *    afCmd       [I] Set of command and filter flags
605  *    ulRes       [I] Reserved
606  *
607  * RETURNS
608  *    Success: DMLERR_NO_ERROR
609  *    Failure: DMLERR_DLL_USAGE, DMLERR_INVALIDPARAMETER, DMLERR_SYS_ERROR
610  */
611 UINT WINAPI DdeInitializeW(LPDWORD pidInst, PFNCALLBACK pfnCallback,
612                            DWORD afCmd, DWORD ulRes)
613 {
614     return WDML_Initialize(pidInst, pfnCallback, afCmd, ulRes, TRUE, FALSE);
615 }
616
617 /*****************************************************************
618  * DdeUninitialize [USER32.@]  Frees DDEML resources
619  *
620  * PARAMS
621  *    idInst [I] Instance identifier
622  *
623  * RETURNS
624  *    Success: TRUE
625  *    Failure: FALSE
626  */
627
628 BOOL WINAPI DdeUninitialize(DWORD idInst)
629 {
630     /*  Stage one - check if we have a handle for this instance
631      */
632     WDML_INSTANCE*              pInstance;
633     WDML_CONV*                  pConv;
634     WDML_CONV*                  pConvNext;
635
636     TRACE("(%d)\n", idInst);
637
638     /*  First check instance
639      */
640     pInstance = WDML_GetInstance(idInst);
641     if (pInstance == NULL)
642     {
643         /*
644          *      Needs something here to record NOT_INITIALIZED ready for DdeGetLastError
645          */
646         return FALSE;
647     }
648
649     /* first terminate all conversations client side
650      * this shall close existing links...
651      */
652     for (pConv = pInstance->convs[WDML_CLIENT_SIDE]; pConv != NULL; pConv = pConvNext)
653     {
654         pConvNext = pConv->next;
655         DdeDisconnect((HCONV)pConv);
656     }
657     if (pInstance->convs[WDML_CLIENT_SIDE])
658         FIXME("still pending conversations\n");
659
660     /* then unregister all known service names */
661     DdeNameService(idInst, 0, 0, DNS_UNREGISTER);
662
663     /* Free the nodes that were not freed by this instance
664      * and remove the nodes from the list of HSZ nodes.
665      */
666     WDML_FreeAllHSZ(pInstance);
667
668     DestroyWindow(pInstance->hwndEvent);
669
670     /* OK now delete the instance handle itself */
671
672     if (WDML_InstanceList == pInstance)
673     {
674         /* special case - the first/only entry */
675         WDML_InstanceList = pInstance->next;
676     }
677     else
678     {
679         /* general case, remove entry */
680         WDML_INSTANCE*  inst;
681
682         for (inst = WDML_InstanceList; inst->next != pInstance; inst = inst->next);
683         inst->next = pInstance->next;
684     }
685     /* release the heap entry
686      */
687     HeapFree(GetProcessHeap(), 0, pInstance);
688
689     return TRUE;
690 }
691
692 /******************************************************************
693  *              WDML_NotifyThreadExit
694  *
695  *
696  */
697 void WDML_NotifyThreadDetach(void)
698 {
699     WDML_INSTANCE*      pInstance;
700     WDML_INSTANCE*      next;
701     DWORD               tid = GetCurrentThreadId();
702
703     EnterCriticalSection(&WDML_CritSect);
704     for (pInstance = WDML_InstanceList; pInstance != NULL; pInstance = next)
705     {
706         next = pInstance->next;
707         if (pInstance->threadID == tid)
708         {
709             LeaveCriticalSection(&WDML_CritSect);
710             DdeUninitialize(pInstance->instanceID);
711             EnterCriticalSection(&WDML_CritSect);
712         }
713     }
714     LeaveCriticalSection(&WDML_CritSect);
715 }
716
717 /******************************************************************
718  *              WDML_InvokeCallback
719  *
720  *
721  */
722 HDDEDATA        WDML_InvokeCallback(WDML_INSTANCE* pInstance, UINT uType, UINT uFmt, HCONV hConv,
723                                     HSZ hsz1, HSZ hsz2, HDDEDATA hdata,
724                                     ULONG_PTR dwData1, ULONG_PTR dwData2)
725 {
726     HDDEDATA    ret;
727
728     if (pInstance == NULL)
729         return NULL;
730
731     TRACE("invoking CB%d[%p] (%x %x %p %p %p %p %lx %lx)\n",
732           pInstance->win16 ? 16 : 32, pInstance->callback, uType, uFmt,
733           hConv, hsz1, hsz2, hdata, dwData1, dwData2);
734     if (pInstance->win16)
735     {
736         ret = WDML_InvokeCallback16(pInstance->callback, uType, uFmt, hConv,
737                                     hsz1, hsz2, hdata, dwData1, dwData2);
738     }
739     else
740     {
741         ret = pInstance->callback(uType, uFmt, hConv, hsz1, hsz2, hdata, dwData1, dwData2);
742     }
743     TRACE("done => %p\n", ret);
744     return ret;
745 }
746
747 /*****************************************************************************
748  *      WDML_GetInstance
749  *
750  *      generic routine to return a pointer to the relevant DDE_HANDLE_ENTRY
751  *      for an instance Id, or NULL if the entry does not exist
752  *
753  */
754 WDML_INSTANCE*  WDML_GetInstance(DWORD instId)
755 {
756     WDML_INSTANCE*      pInstance;
757
758     EnterCriticalSection(&WDML_CritSect);
759
760     for (pInstance = WDML_InstanceList; pInstance != NULL; pInstance = pInstance->next)
761     {
762         if (pInstance->instanceID == instId)
763         {
764             if (GetCurrentThreadId() != pInstance->threadID)
765             {
766                 FIXME("Tried to get instance from wrong thread\n");
767                 continue;
768             }
769             break;
770         }
771     }
772
773     LeaveCriticalSection(&WDML_CritSect);
774
775     if (!pInstance)
776         WARN("Instance entry missing for id %04x\n", instId);
777     return pInstance;
778 }
779
780 /******************************************************************
781  *              WDML_GetInstanceFromWnd
782  *
783  *
784  */
785 WDML_INSTANCE*  WDML_GetInstanceFromWnd(HWND hWnd)
786 {
787     return (WDML_INSTANCE*)GetWindowLongPtrW(hWnd, GWL_WDML_INSTANCE);
788 }
789
790 /******************************************************************************
791  * DdeGetLastError [USER32.@]  Gets most recent error code
792  *
793  * PARAMS
794  *    idInst [I] Instance identifier
795  *
796  * RETURNS
797  *    Last error code
798  */
799 UINT WINAPI DdeGetLastError(DWORD idInst)
800 {
801     DWORD               error_code;
802     WDML_INSTANCE*      pInstance;
803
804     /*  First check instance
805      */
806     pInstance = WDML_GetInstance(idInst);
807     if  (pInstance == NULL)
808     {
809         error_code = DMLERR_INVALIDPARAMETER;
810     }
811     else
812     {
813         error_code = pInstance->lastError;
814         pInstance->lastError = 0;
815     }
816
817     return error_code;
818 }
819
820 /* ================================================================
821  *
822  *                      String management
823  *
824  * ================================================================ */
825
826
827 /******************************************************************
828  *              WDML_FindNode
829  *
830  *
831  */
832 static HSZNode* WDML_FindNode(WDML_INSTANCE* pInstance, HSZ hsz)
833 {
834     HSZNode*    pNode;
835
836     if (pInstance == NULL) return NULL;
837
838     for (pNode = pInstance->nodeList; pNode != NULL; pNode = pNode->next)
839     {
840         if (pNode->hsz == hsz) break;
841     }
842     if (!pNode) WARN("HSZ %p not found\n", hsz);
843     return pNode;
844 }
845
846 /******************************************************************
847  *              WDML_MakeAtomFromHsz
848  *
849  * Creates a global atom from an existing HSZ
850  * Generally used before sending an HSZ as an atom to a remote app
851  */
852 ATOM    WDML_MakeAtomFromHsz(HSZ hsz)
853 {
854     WCHAR nameBuffer[MAX_BUFFER_LEN];
855
856     if (GetAtomNameW(HSZ2ATOM(hsz), nameBuffer, MAX_BUFFER_LEN))
857         return GlobalAddAtomW(nameBuffer);
858     WARN("HSZ %p not found\n", hsz);
859     return 0;
860 }
861
862 /******************************************************************
863  *              WDML_MakeHszFromAtom
864  *
865  * Creates a HSZ from an existing global atom
866  * Generally used while receiving a global atom and transforming it
867  * into an HSZ
868  */
869 HSZ     WDML_MakeHszFromAtom(const WDML_INSTANCE* pInstance, ATOM atom)
870 {
871     WCHAR nameBuffer[MAX_BUFFER_LEN];
872
873     if (!atom) return NULL;
874
875     if (GlobalGetAtomNameW(atom, nameBuffer, MAX_BUFFER_LEN))
876     {
877         TRACE("%x => %s\n", atom, debugstr_w(nameBuffer));
878         return DdeCreateStringHandleW(pInstance->instanceID, nameBuffer, CP_WINUNICODE);
879     }
880     WARN("ATOM 0x%x not found\n", atom);
881     return 0;
882 }
883
884 /******************************************************************
885  *              WDML_IncHSZ
886  *
887  *
888  */
889 BOOL WDML_IncHSZ(WDML_INSTANCE* pInstance, HSZ hsz)
890 {
891     HSZNode*    pNode;
892
893     pNode = WDML_FindNode(pInstance, hsz);
894     if (!pNode) return FALSE;
895
896     pNode->refCount++;
897     return TRUE;
898 }
899
900 /******************************************************************************
901  *           WDML_DecHSZ    (INTERNAL)
902  *
903  * Decrease the ref count of an HSZ. If it reaches 0, the node is removed from the list
904  * of HSZ nodes
905  * Returns -1 is the HSZ isn't found, otherwise it's the current (after --) of the ref count
906  */
907 BOOL WDML_DecHSZ(WDML_INSTANCE* pInstance, HSZ hsz)
908 {
909     HSZNode*    pPrev = NULL;
910     HSZNode*    pCurrent;
911
912     for (pCurrent = pInstance->nodeList; pCurrent != NULL; pCurrent = (pPrev = pCurrent)->next)
913     {
914         /* If we found the node we were looking for and its ref count is one,
915          * we can remove it
916          */
917         if (pCurrent->hsz == hsz)
918         {
919             if (--pCurrent->refCount == 0)
920             {
921                 if (pCurrent == pInstance->nodeList)
922                 {
923                     pInstance->nodeList = pCurrent->next;
924                 }
925                 else
926                 {
927                     pPrev->next = pCurrent->next;
928                 }
929                 HeapFree(GetProcessHeap(), 0, pCurrent);
930                 DeleteAtom(HSZ2ATOM(hsz));
931             }
932             return TRUE;
933         }
934     }
935     WARN("HSZ %p not found\n", hsz);
936
937     return FALSE;
938 }
939
940 /******************************************************************************
941  *            WDML_FreeAllHSZ    (INTERNAL)
942  *
943  * Frees up all the strings still allocated in the list and
944  * remove all the nodes from the list of HSZ nodes.
945  */
946 void WDML_FreeAllHSZ(WDML_INSTANCE* pInstance)
947 {
948     /* Free any strings created in this instance.
949      */
950     while (pInstance->nodeList != NULL)
951     {
952         DdeFreeStringHandle(pInstance->instanceID, pInstance->nodeList->hsz);
953     }
954 }
955
956 /******************************************************************************
957  *            InsertHSZNode    (INTERNAL)
958  *
959  * Insert a node to the head of the list.
960  */
961 static void WDML_InsertHSZNode(WDML_INSTANCE* pInstance, HSZ hsz)
962 {
963     if (hsz != 0)
964     {
965         HSZNode* pNew = NULL;
966         /* Create a new node for this HSZ.
967          */
968         pNew = HeapAlloc(GetProcessHeap(), 0, sizeof(HSZNode));
969         if (pNew != NULL)
970         {
971             pNew->hsz      = hsz;
972             pNew->next     = pInstance->nodeList;
973             pNew->refCount = 1;
974             pInstance->nodeList = pNew;
975         }
976         else
977         {
978             ERR("Primary HSZ Node allocation failed - out of memory\n");
979         }
980     }
981 }
982
983 /******************************************************************
984  *              WDML_QueryString
985  *
986  *
987  */
988 static int      WDML_QueryString(WDML_INSTANCE* pInstance, HSZ hsz, LPVOID ptr, DWORD cchMax,
989                                  int codepage)
990 {
991     WCHAR       pString[MAX_BUFFER_LEN];
992     int         ret;
993     /* If psz is null, we have to return only the length
994      * of the string.
995      */
996     if (ptr == NULL)
997     {
998         ptr = pString;
999         cchMax = MAX_BUFFER_LEN;
1000     }
1001
1002     switch (codepage)
1003     {
1004     case CP_WINANSI:
1005         ret = GetAtomNameA(HSZ2ATOM(hsz), ptr, cchMax);
1006         break;
1007     case CP_WINUNICODE:
1008         ret = GetAtomNameW(HSZ2ATOM(hsz), ptr, cchMax);
1009         break;
1010     default:
1011         ERR("Unknown code page %d\n", codepage);
1012         ret = 0;
1013     }
1014     return ret;
1015 }
1016
1017 /*****************************************************************
1018  * DdeQueryStringA [USER32.@]
1019  */
1020 DWORD WINAPI DdeQueryStringA(DWORD idInst, HSZ hsz, LPSTR psz, DWORD cchMax, INT iCodePage)
1021 {
1022     DWORD               ret = 0;
1023     WDML_INSTANCE*      pInstance;
1024
1025     TRACE("(%d, %p, %p, %d, %d)\n", idInst, hsz, psz, cchMax, iCodePage);
1026
1027     /*  First check instance
1028      */
1029     pInstance = WDML_GetInstance(idInst);
1030     if (pInstance != NULL)
1031     {
1032         if (iCodePage == 0) iCodePage = CP_WINANSI;
1033         ret = WDML_QueryString(pInstance, hsz, psz, cchMax, iCodePage);
1034     }
1035
1036     TRACE("returning %d (%s)\n", ret, debugstr_a(psz));
1037     return ret;
1038 }
1039
1040 /*****************************************************************
1041  * DdeQueryStringW [USER32.@]
1042  */
1043
1044 DWORD WINAPI DdeQueryStringW(DWORD idInst, HSZ hsz, LPWSTR psz, DWORD cchMax, INT iCodePage)
1045 {
1046     DWORD               ret = 0;
1047     WDML_INSTANCE*      pInstance;
1048
1049     TRACE("(%d, %p, %p, %d, %d)\n", idInst, hsz, psz, cchMax, iCodePage);
1050
1051     /*  First check instance
1052      */
1053     pInstance = WDML_GetInstance(idInst);
1054     if (pInstance != NULL)
1055     {
1056         if (iCodePage == 0) iCodePage = CP_WINUNICODE;
1057         ret = WDML_QueryString(pInstance, hsz, psz, cchMax, iCodePage);
1058     }
1059
1060     TRACE("returning %d (%s)\n", ret, debugstr_w(psz));
1061     return ret;
1062 }
1063
1064 /******************************************************************
1065  *              DML_CreateString
1066  *
1067  *
1068  */
1069 static  HSZ     WDML_CreateString(WDML_INSTANCE* pInstance, LPCVOID ptr, int codepage)
1070 {
1071     HSZ         hsz;
1072
1073     switch (codepage)
1074     {
1075     case CP_WINANSI:
1076         hsz = ATOM2HSZ(AddAtomA(ptr));
1077         TRACE("added atom %s with HSZ %p,\n", debugstr_a(ptr), hsz);
1078         break;
1079     case CP_WINUNICODE:
1080         hsz = ATOM2HSZ(AddAtomW(ptr));
1081         TRACE("added atom %s with HSZ %p,\n", debugstr_w(ptr), hsz);
1082         break;
1083     default:
1084         ERR("Unknown code page %d\n", codepage);
1085         return 0;
1086     }
1087     WDML_InsertHSZNode(pInstance, hsz);
1088     return hsz;
1089 }
1090
1091 /*****************************************************************
1092  * DdeCreateStringHandleA [USER32.@]
1093  *
1094  * See DdeCreateStringHandleW.
1095  */
1096 HSZ WINAPI DdeCreateStringHandleA(DWORD idInst, LPCSTR psz, INT codepage)
1097 {
1098     HSZ                 hsz = 0;
1099     WDML_INSTANCE*      pInstance;
1100
1101     TRACE("(%d,%s,%d)\n", idInst, debugstr_a(psz), codepage);
1102
1103     pInstance = WDML_GetInstance(idInst);
1104     if (pInstance)
1105     {
1106         if (codepage == 0) codepage = CP_WINANSI;
1107         hsz = WDML_CreateString(pInstance, psz, codepage);
1108     }
1109
1110     return hsz;
1111 }
1112
1113
1114 /******************************************************************************
1115  * DdeCreateStringHandleW [USER32.@]  Creates handle to identify string
1116  *
1117  * PARAMS
1118  *      idInst   [I] Instance identifier
1119  *      psz      [I] Pointer to string
1120  *      codepage [I] Code page identifier
1121  * RETURNS
1122  *    Success: String handle
1123  *    Failure: 0
1124  */
1125 HSZ WINAPI DdeCreateStringHandleW(DWORD idInst, LPCWSTR psz, INT codepage)
1126 {
1127     WDML_INSTANCE*      pInstance;
1128     HSZ                 hsz = 0;
1129
1130     pInstance = WDML_GetInstance(idInst);
1131     if (pInstance)
1132     {
1133         if (codepage == 0) codepage = CP_WINUNICODE;
1134         hsz = WDML_CreateString(pInstance, psz, codepage);
1135     }
1136
1137     return hsz;
1138 }
1139
1140 /*****************************************************************
1141  *            DdeFreeStringHandle   (USER32.@)
1142  * RETURNS
1143  *  success: nonzero
1144  *  fail:    zero
1145  */
1146 BOOL WINAPI DdeFreeStringHandle(DWORD idInst, HSZ hsz)
1147 {
1148     WDML_INSTANCE*      pInstance;
1149     BOOL                ret = FALSE;
1150
1151     TRACE("(%d,%p):\n", idInst, hsz);
1152
1153     /*  First check instance
1154      */
1155     pInstance = WDML_GetInstance(idInst);
1156     if (pInstance)
1157         ret = WDML_DecHSZ(pInstance, hsz);
1158
1159     return ret;
1160 }
1161
1162 /*****************************************************************
1163  *            DdeKeepStringHandle  (USER32.@)
1164  *
1165  * RETURNS
1166  *  success: nonzero
1167  *  fail:    zero
1168  */
1169 BOOL WINAPI DdeKeepStringHandle(DWORD idInst, HSZ hsz)
1170 {
1171     WDML_INSTANCE*      pInstance;
1172     BOOL                ret = FALSE;
1173
1174     TRACE("(%d,%p):\n", idInst, hsz);
1175
1176     /*  First check instance
1177      */
1178     pInstance = WDML_GetInstance(idInst);
1179     if (pInstance)
1180         ret = WDML_IncHSZ(pInstance, hsz);
1181
1182     return ret;
1183 }
1184
1185 /*****************************************************************
1186  *            DdeCmpStringHandles (USER32.@)
1187  *
1188  * Compares the value of two string handles.  This comparison is
1189  * not case sensitive.
1190  *
1191  * PARAMS
1192  *  hsz1    [I] Handle to the first string
1193  *  hsz2    [I] Handle to the second string
1194  *
1195  * RETURNS
1196  *  -1 The value of hsz1 is zero or less than hsz2
1197  *  0  The values of hsz 1 and 2 are the same or both zero.
1198  *  1  The value of hsz2 is zero of less than hsz1
1199  */
1200 INT WINAPI DdeCmpStringHandles(HSZ hsz1, HSZ hsz2)
1201 {
1202     WCHAR       psz1[MAX_BUFFER_LEN];
1203     WCHAR       psz2[MAX_BUFFER_LEN];
1204     int         ret = 0;
1205     int         ret1, ret2;
1206
1207     ret1 = GetAtomNameW(HSZ2ATOM(hsz1), psz1, MAX_BUFFER_LEN);
1208     ret2 = GetAtomNameW(HSZ2ATOM(hsz2), psz2, MAX_BUFFER_LEN);
1209
1210     TRACE("(%p<%s> %p<%s>);\n", hsz1, debugstr_w(psz1), hsz2, debugstr_w(psz2));
1211
1212     /* Make sure we found both strings. */
1213     if (ret1 == 0 && ret2 == 0)
1214     {
1215         /* If both are not found, return both  "zero strings". */
1216         ret = 0;
1217     }
1218     else if (ret1 == 0)
1219     {
1220         /* If hsz1 is a not found, return hsz1 is "zero string". */
1221         ret = -1;
1222     }
1223     else if (ret2 == 0)
1224     {
1225         /* If hsz2 is a not found, return hsz2 is "zero string". */
1226         ret = 1;
1227     }
1228     else
1229     {
1230         /* Compare the two strings we got (case insensitive). */
1231         ret = lstrcmpiW(psz1, psz2);
1232         /* Since strcmp returns any number smaller than
1233          * 0 when the first string is found to be less than
1234          * the second one we must make sure we are returning
1235          * the proper values.
1236          */
1237         if (ret < 0)
1238         {
1239             ret = -1;
1240         }
1241         else if (ret > 0)
1242         {
1243             ret = 1;
1244         }
1245     }
1246
1247     return ret;
1248 }
1249
1250 /* ================================================================
1251  *
1252  *                      Data handle management
1253  *
1254  * ================================================================ */
1255
1256 /*****************************************************************
1257  *            DdeCreateDataHandle (USER32.@)
1258  */
1259 HDDEDATA WINAPI DdeCreateDataHandle(DWORD idInst, LPBYTE pSrc, DWORD cb, DWORD cbOff,
1260                                     HSZ hszItem, UINT wFmt, UINT afCmd)
1261 {
1262     /* For now, we ignore idInst, hszItem.
1263      * The purpose of these arguments still need to be investigated.
1264      */
1265
1266     HGLOBAL                     hMem;
1267     LPBYTE                      pByte;
1268     DDE_DATAHANDLE_HEAD*        pDdh;
1269     WCHAR psz[MAX_BUFFER_LEN];
1270
1271     if (!GetAtomNameW(HSZ2ATOM(hszItem), psz, MAX_BUFFER_LEN))
1272     {
1273         psz[0] = HSZ2ATOM(hszItem);
1274         psz[1] = 0;
1275     }
1276
1277     TRACE("(%d,%p,cb %d, cbOff %d,%p <%s>,fmt %04x,%x)\n",
1278           idInst, pSrc, cb, cbOff, hszItem, debugstr_w(psz), wFmt, afCmd);
1279
1280     if (afCmd != 0 && afCmd != HDATA_APPOWNED)
1281         return 0;
1282
1283     /* we use the first 4 bytes to store the size */
1284     if (!(hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, cb + cbOff + sizeof(DDE_DATAHANDLE_HEAD))))
1285     {
1286         ERR("GlobalAlloc failed\n");
1287         return 0;
1288     }
1289
1290     pDdh = (DDE_DATAHANDLE_HEAD*)GlobalLock(hMem);
1291     if (!pDdh)
1292     {
1293         GlobalFree(hMem);
1294         return 0;
1295     }
1296
1297     pDdh->cfFormat = wFmt;
1298     pDdh->bAppOwned = (afCmd == HDATA_APPOWNED);
1299
1300     pByte = (LPBYTE)(pDdh + 1);
1301     if (pSrc)
1302     {
1303         memcpy(pByte, pSrc + cbOff, cb);
1304     }
1305     GlobalUnlock(hMem);
1306
1307     TRACE("=> %p\n", hMem);
1308     return (HDDEDATA)hMem;
1309 }
1310
1311 /*****************************************************************
1312  *
1313  *            DdeAddData (USER32.@)
1314  */
1315 HDDEDATA WINAPI DdeAddData(HDDEDATA hData, LPBYTE pSrc, DWORD cb, DWORD cbOff)
1316 {
1317     DWORD       old_sz, new_sz;
1318     LPBYTE      pDst;
1319
1320     TRACE("(%p,%p,cb %d, cbOff %d)\n", hData, pSrc, cb, cbOff);
1321
1322     pDst = DdeAccessData(hData, &old_sz);
1323     if (!pDst) return 0;
1324
1325     new_sz = cb + cbOff;
1326     if (new_sz > old_sz)
1327     {
1328         DdeUnaccessData(hData);
1329         hData = GlobalReAlloc(hData, new_sz + sizeof(DDE_DATAHANDLE_HEAD),
1330                               GMEM_MOVEABLE | GMEM_DDESHARE);
1331         pDst = DdeAccessData(hData, &old_sz);
1332     }
1333
1334     if (!pDst) return 0;
1335
1336     memcpy(pDst + cbOff, pSrc, cb);
1337     DdeUnaccessData(hData);
1338     return hData;
1339 }
1340
1341 /******************************************************************************
1342  * DdeGetData [USER32.@]  Copies data from DDE object to local buffer
1343  *
1344  *
1345  * PARAMS
1346  * hData        [I] Handle to DDE object
1347  * pDst         [I] Pointer to destination buffer
1348  * cbMax        [I] Amount of data to copy
1349  * cbOff        [I] Offset to beginning of data
1350  *
1351  * RETURNS
1352  *    Size of memory object associated with handle
1353  */
1354 DWORD WINAPI DdeGetData(HDDEDATA hData, LPBYTE pDst, DWORD cbMax, DWORD cbOff)
1355 {
1356     DWORD   dwSize, dwRet;
1357     LPBYTE  pByte;
1358
1359     TRACE("(%p,%p,%d,%d)\n", hData, pDst, cbMax, cbOff);
1360
1361     pByte = DdeAccessData(hData, &dwSize);
1362
1363     if (pByte)
1364     {
1365         if (!pDst)
1366         {
1367             dwRet = dwSize;
1368         }
1369         else if (cbOff + cbMax < dwSize)
1370         {
1371             dwRet = cbMax;
1372         }
1373         else if (cbOff < dwSize)
1374         {
1375             dwRet = dwSize - cbOff;
1376         }
1377         else
1378         {
1379             dwRet = 0;
1380         }
1381         if (pDst && dwRet != 0)
1382         {
1383             memcpy(pDst, pByte + cbOff, dwRet);
1384         }
1385         DdeUnaccessData(hData);
1386     }
1387     else
1388     {
1389         dwRet = 0;
1390     }
1391     return dwRet;
1392 }
1393
1394 /*****************************************************************
1395  *            DdeAccessData (USER32.@)
1396  */
1397 LPBYTE WINAPI DdeAccessData(HDDEDATA hData, LPDWORD pcbDataSize)
1398 {
1399     HGLOBAL                     hMem = hData;
1400     DDE_DATAHANDLE_HEAD*        pDdh;
1401
1402     TRACE("(%p,%p)\n", hData, pcbDataSize);
1403
1404     pDdh = (DDE_DATAHANDLE_HEAD*)GlobalLock(hMem);
1405     if (pDdh == NULL)
1406     {
1407         ERR("Failed on GlobalLock(%p)\n", hMem);
1408         return 0;
1409     }
1410
1411     if (pcbDataSize != NULL)
1412     {
1413         *pcbDataSize = GlobalSize(hMem) - sizeof(DDE_DATAHANDLE_HEAD);
1414     }
1415     TRACE("=> %p (%lu) fmt %04x\n", pDdh + 1, GlobalSize(hMem) - sizeof(DDE_DATAHANDLE_HEAD), pDdh->cfFormat);
1416     return (LPBYTE)(pDdh + 1);
1417 }
1418
1419 /*****************************************************************
1420  *            DdeUnaccessData (USER32.@)
1421  */
1422 BOOL WINAPI DdeUnaccessData(HDDEDATA hData)
1423 {
1424     HGLOBAL hMem = hData;
1425
1426     TRACE("(%p)\n", hData);
1427
1428     GlobalUnlock(hMem);
1429
1430     return TRUE;
1431 }
1432
1433 /*****************************************************************
1434  *            DdeFreeDataHandle   (USER32.@)
1435  */
1436 BOOL WINAPI DdeFreeDataHandle(HDDEDATA hData)
1437 {
1438     TRACE("(%p)\n", hData);
1439     return GlobalFree(hData) == 0;
1440 }
1441
1442 /******************************************************************
1443  *              WDML_IsAppOwned
1444  *
1445  *
1446  */
1447 BOOL WDML_IsAppOwned(HDDEDATA hData)
1448 {
1449     DDE_DATAHANDLE_HEAD*        pDdh;
1450     BOOL                        ret = FALSE;
1451
1452     pDdh = (DDE_DATAHANDLE_HEAD*)GlobalLock(hData);
1453     if (pDdh != NULL)
1454     {
1455         ret = pDdh->bAppOwned;
1456         GlobalUnlock(hData);
1457     }
1458     return ret;
1459 }
1460
1461 /* ================================================================
1462  *
1463  *                  Global <=> Data handle management
1464  *
1465  * ================================================================ */
1466
1467 /* Note: we use a DDEDATA, but layout of DDEDATA, DDEADVISE and DDEPOKE structures is similar:
1468  *    offset      size
1469  *    (bytes)    (bits) comment
1470  *      0          16   bit fields for options (release, ackreq, response...)
1471  *      2          16   clipboard format
1472  *      4          ?    data to be used
1473  */
1474 HDDEDATA        WDML_Global2DataHandle(HGLOBAL hMem, WINE_DDEHEAD* p)
1475 {
1476     DDEDATA*    pDd;
1477     HDDEDATA    ret = 0;
1478     DWORD       size;
1479
1480     if (hMem)
1481     {
1482         pDd = GlobalLock(hMem);
1483         size = GlobalSize(hMem) - sizeof(WINE_DDEHEAD);
1484         if (pDd)
1485         {
1486             if (p) memcpy(p, pDd, sizeof(WINE_DDEHEAD));
1487             switch (pDd->cfFormat)
1488             {
1489             default:
1490                 FIXME("Unsupported format (%04x) for data %p, passing raw information\n",
1491                       pDd->cfFormat, hMem);
1492                 /* fall thru */
1493             case 0:
1494             case CF_TEXT:
1495                 ret = DdeCreateDataHandle(0, pDd->Value, size, 0, 0, pDd->cfFormat, 0);
1496                 break;
1497             case CF_BITMAP:
1498                 if (size >= sizeof(BITMAP))
1499                 {
1500                     BITMAP*     bmp = (BITMAP*)pDd->Value;
1501                     int         count = bmp->bmWidthBytes * bmp->bmHeight * bmp->bmPlanes;
1502                     if (size >= sizeof(BITMAP) + count)
1503                     {
1504                         HBITMAP hbmp;
1505
1506                         if ((hbmp = CreateBitmap(bmp->bmWidth, bmp->bmHeight,
1507                                                  bmp->bmPlanes, bmp->bmBitsPixel,
1508                                                  pDd->Value + sizeof(BITMAP))))
1509                         {
1510                             ret = DdeCreateDataHandle(0, (LPBYTE)&hbmp, sizeof(hbmp),
1511                                                       0, 0, CF_BITMAP, 0);
1512                         }
1513                         else ERR("Can't create bmp\n");
1514                     }
1515                     else
1516                     {
1517                         ERR("Wrong count: %u / %d\n", size, count);
1518                     }
1519                 } else ERR("No bitmap header\n");
1520                 break;
1521             }
1522             GlobalUnlock(hMem);
1523         }
1524     }
1525     return ret;
1526 }
1527
1528 /******************************************************************
1529  *              WDML_DataHandle2Global
1530  *
1531  *
1532  */
1533 HGLOBAL WDML_DataHandle2Global(HDDEDATA hDdeData, BOOL fResponse, BOOL fRelease,
1534                                BOOL fDeferUpd, BOOL fAckReq)
1535 {
1536     DDE_DATAHANDLE_HEAD*        pDdh;
1537     DWORD                       dwSize;
1538     HGLOBAL                     hMem = 0;
1539
1540     dwSize = GlobalSize((HGLOBAL)hDdeData) - sizeof(DDE_DATAHANDLE_HEAD);
1541     pDdh = (DDE_DATAHANDLE_HEAD*)GlobalLock((HGLOBAL)hDdeData);
1542     if (dwSize && pDdh)
1543     {
1544         WINE_DDEHEAD*    wdh = NULL;
1545
1546         switch (pDdh->cfFormat)
1547         {
1548         default:
1549             FIXME("Unsupported format (%04x) for data %p, passing raw information\n",
1550                    pDdh->cfFormat, hDdeData);
1551             /* fall thru */
1552         case 0:
1553         case CF_TEXT:
1554             hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, sizeof(WINE_DDEHEAD) + dwSize);
1555             if (hMem && (wdh = GlobalLock(hMem)))
1556             {
1557                 memcpy(wdh + 1, pDdh + 1, dwSize);
1558             }
1559             break;
1560         case CF_BITMAP:
1561             if (dwSize >= sizeof(HBITMAP))
1562             {
1563                 BITMAP  bmp;
1564                 DWORD   count;
1565                 HBITMAP hbmp = *(HBITMAP*)(pDdh + 1);
1566
1567                 if (GetObjectW(hbmp, sizeof(bmp), &bmp))
1568                 {
1569                     count = bmp.bmWidthBytes * bmp.bmHeight;
1570                     hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE,
1571                                        sizeof(WINE_DDEHEAD) + sizeof(bmp) + count);
1572                     if (hMem && (wdh = GlobalLock(hMem)))
1573                     {
1574                         memcpy(wdh + 1, &bmp, sizeof(bmp));
1575                         GetBitmapBits(hbmp, count, ((char*)(wdh + 1)) + sizeof(bmp));
1576                     }
1577                 }
1578             }
1579             break;
1580         }
1581         if (wdh)
1582         {
1583             wdh->unused = 0;
1584             wdh->fResponse = fResponse;
1585             wdh->fRelease = fRelease;
1586             wdh->fDeferUpd = fDeferUpd;
1587             wdh->fAckReq = fAckReq;
1588             wdh->cfFormat = pDdh->cfFormat;
1589             GlobalUnlock(hMem);
1590         }
1591         GlobalUnlock((HGLOBAL)hDdeData);
1592     }
1593
1594     return hMem;
1595 }
1596
1597 /* ================================================================
1598  *
1599  *                      Server management
1600  *
1601  * ================================================================ */
1602
1603 /******************************************************************
1604  *              WDML_AddServer
1605  *
1606  *
1607  */
1608 WDML_SERVER*    WDML_AddServer(WDML_INSTANCE* pInstance, HSZ hszService, HSZ hszTopic)
1609 {
1610     static const WCHAR fmtW[] = {'%','s','(','0','x','%','0','8','l','x',')',0};
1611     WDML_SERVER*        pServer;
1612     WCHAR               buf1[256];
1613     WCHAR               buf2[256];
1614
1615     pServer = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_SERVER));
1616     if (pServer == NULL) return NULL;
1617
1618     pServer->hszService = hszService;
1619     WDML_IncHSZ(pInstance, hszService);
1620
1621     DdeQueryStringW(pInstance->instanceID, hszService, buf1, 256, CP_WINUNICODE);
1622     snprintfW(buf2, 256, fmtW, buf1, GetCurrentProcessId());
1623     pServer->hszServiceSpec = DdeCreateStringHandleW(pInstance->instanceID, buf2, CP_WINUNICODE);
1624
1625     pServer->atomService = WDML_MakeAtomFromHsz(pServer->hszService);
1626     pServer->atomServiceSpec = WDML_MakeAtomFromHsz(pServer->hszServiceSpec);
1627
1628     pServer->filterOn = TRUE;
1629
1630     pServer->next = pInstance->servers;
1631     pInstance->servers = pServer;
1632     return pServer;
1633 }
1634
1635 /******************************************************************
1636  *              WDML_RemoveServer
1637  *
1638  *
1639  */
1640 void WDML_RemoveServer(WDML_INSTANCE* pInstance, HSZ hszService, HSZ hszTopic)
1641 {
1642     WDML_SERVER*        pPrev = NULL;
1643     WDML_SERVER*        pServer = NULL;
1644     WDML_CONV*          pConv;
1645     WDML_CONV*          pConvNext;
1646
1647     pServer = pInstance->servers;
1648
1649     while (pServer != NULL)
1650     {
1651         if (DdeCmpStringHandles(pServer->hszService, hszService) == 0)
1652         {
1653             WDML_BroadcastDDEWindows(WDML_szEventClass, WM_WDML_UNREGISTER,
1654                                      pServer->atomService, pServer->atomServiceSpec);
1655             /* terminate all conversations for given topic */
1656             for (pConv = pInstance->convs[WDML_SERVER_SIDE]; pConv != NULL; pConv = pConvNext)
1657             {
1658                 pConvNext = pConv->next;
1659                 if (DdeCmpStringHandles(pConv->hszService, hszService) == 0)
1660                 {
1661                     WDML_RemoveConv(pConv, WDML_SERVER_SIDE);
1662                     /* don't care about return code (whether client window is present or not) */
1663                     PostMessageW(pConv->hwndClient, WM_DDE_TERMINATE, (WPARAM)pConv->hwndServer, 0);
1664                 }
1665             }
1666             if (pServer == pInstance->servers)
1667             {
1668                 pInstance->servers = pServer->next;
1669             }
1670             else
1671             {
1672                 pPrev->next = pServer->next;
1673             }
1674
1675             DestroyWindow(pServer->hwndServer);
1676             WDML_DecHSZ(pInstance, pServer->hszServiceSpec);
1677             WDML_DecHSZ(pInstance, pServer->hszService);
1678
1679             GlobalDeleteAtom(pServer->atomService);
1680             GlobalDeleteAtom(pServer->atomServiceSpec);
1681
1682             HeapFree(GetProcessHeap(), 0, pServer);
1683             break;
1684         }
1685
1686         pPrev = pServer;
1687         pServer = pServer->next;
1688     }
1689 }
1690
1691 /*****************************************************************************
1692  *      WDML_FindServer
1693  *
1694  *      generic routine to return a pointer to the relevant ServiceNode
1695  *      for a given service name, or NULL if the entry does not exist
1696  *
1697  */
1698 WDML_SERVER*    WDML_FindServer(WDML_INSTANCE* pInstance, HSZ hszService, HSZ hszTopic)
1699 {
1700     WDML_SERVER*        pServer;
1701
1702     for (pServer = pInstance->servers; pServer != NULL; pServer = pServer->next)
1703     {
1704         if (hszService == pServer->hszService)
1705         {
1706             return pServer;
1707         }
1708     }
1709     TRACE("Service name missing\n");
1710     return NULL;
1711 }
1712
1713 /* ================================================================
1714  *
1715  *              Conversation management
1716  *
1717  * ================================================================ */
1718
1719 /******************************************************************
1720  *              WDML_AddConv
1721  *
1722  *
1723  */
1724 WDML_CONV*      WDML_AddConv(WDML_INSTANCE* pInstance, WDML_SIDE side,
1725                              HSZ hszService, HSZ hszTopic, HWND hwndClient, HWND hwndServer)
1726 {
1727     WDML_CONV*  pConv;
1728
1729     /* no conversation yet, add it */
1730     pConv = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_CONV));
1731     if (!pConv) return NULL;
1732
1733     pConv->instance = pInstance;
1734     WDML_IncHSZ(pInstance, pConv->hszService = hszService);
1735     WDML_IncHSZ(pInstance, pConv->hszTopic = hszTopic);
1736     pConv->magic = WDML_CONV_MAGIC;
1737     pConv->hwndServer = hwndServer;
1738     pConv->hwndClient = hwndClient;
1739     pConv->transactions = NULL;
1740     pConv->hUser = 0;
1741     pConv->wStatus = (side == WDML_CLIENT_SIDE) ? ST_CLIENT : 0L;
1742     pConv->wStatus |= pInstance->wStatus;
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     TRACE("pConv->wStatus %04x\n", pConv->wStatus);
1755
1756     return pConv;
1757 }
1758
1759 /******************************************************************
1760  *              WDML_FindConv
1761  *
1762  *
1763  */
1764 WDML_CONV*      WDML_FindConv(WDML_INSTANCE* pInstance, WDML_SIDE side,
1765                               HSZ hszService, HSZ hszTopic)
1766 {
1767     WDML_CONV*  pCurrent = NULL;
1768
1769     for (pCurrent = pInstance->convs[side]; pCurrent != NULL; pCurrent = pCurrent->next)
1770     {
1771         if (DdeCmpStringHandles(pCurrent->hszService, hszService) == 0 &&
1772             DdeCmpStringHandles(pCurrent->hszTopic, hszTopic) == 0)
1773         {
1774             return pCurrent;
1775         }
1776
1777     }
1778     return NULL;
1779 }
1780
1781 /******************************************************************
1782  *              WDML_RemoveConv
1783  *
1784  *
1785  */
1786 void WDML_RemoveConv(WDML_CONV* pRef, WDML_SIDE side)
1787 {
1788     WDML_CONV*  pPrev = NULL;
1789     WDML_CONV*  pCurrent;
1790     WDML_XACT*  pXAct;
1791     WDML_XACT*  pXActNext;
1792     HWND        hWnd;
1793
1794     if (!pRef)
1795         return;
1796
1797     /* remove any pending transaction */
1798     for (pXAct = pRef->transactions; pXAct != NULL; pXAct = pXActNext)
1799     {
1800         pXActNext = pXAct->next;
1801         WDML_FreeTransaction(pRef->instance, pXAct, TRUE);
1802     }
1803
1804     WDML_RemoveAllLinks(pRef->instance, pRef, side);
1805
1806     /* FIXME: should we keep the window around ? it seems so (at least on client side
1807      * to let QueryConvInfo work after conv termination, but also to implement
1808      * DdeReconnect...
1809      */
1810     /* destroy conversation window, but first remove pConv from hWnd.
1811      * this would help the wndProc do appropriate handling upon a WM_DESTROY message
1812      */
1813     hWnd = (side == WDML_CLIENT_SIDE) ? pRef->hwndClient : pRef->hwndServer;
1814     SetWindowLongPtrW(hWnd, GWL_WDML_CONVERSATION, 0);
1815
1816     DestroyWindow((side == WDML_CLIENT_SIDE) ? pRef->hwndClient : pRef->hwndServer);
1817
1818     WDML_DecHSZ(pRef->instance, pRef->hszService);
1819     WDML_DecHSZ(pRef->instance, pRef->hszTopic);
1820
1821     for (pCurrent = pRef->instance->convs[side]; pCurrent != NULL; pCurrent = (pPrev = pCurrent)->next)
1822     {
1823         if (pCurrent == pRef)
1824         {
1825             if (pCurrent == pRef->instance->convs[side])
1826             {
1827                 pRef->instance->convs[side] = pCurrent->next;
1828             }
1829             else
1830             {
1831                 pPrev->next = pCurrent->next;
1832             }
1833             pCurrent->magic = 0;
1834             HeapFree(GetProcessHeap(), 0, pCurrent);
1835             break;
1836         }
1837     }
1838 }
1839
1840 /******************************************************************
1841  *              WDML_EnableCallback
1842  */
1843 static BOOL WDML_EnableCallback(WDML_CONV *pConv, UINT wCmd)
1844 {
1845     if (wCmd == EC_DISABLE)
1846     {
1847         pConv->wStatus |= ST_BLOCKED;
1848         TRACE("EC_DISABLE: conv %p status flags %04x\n", pConv, pConv->wStatus);
1849         return TRUE;
1850     }
1851
1852     if (wCmd == EC_QUERYWAITING)
1853         return pConv->transactions ? TRUE : FALSE;
1854
1855     if (wCmd != EC_ENABLEALL && wCmd != EC_ENABLEONE)
1856     {
1857         FIXME("Unknown command code %04x\n", wCmd);
1858         return FALSE;
1859     }
1860
1861     if (wCmd == EC_ENABLEALL)
1862     {
1863         pConv->wStatus &= ~ST_BLOCKED;
1864         TRACE("EC_ENABLEALL: conv %p status flags %04x\n", pConv, pConv->wStatus);
1865     }
1866
1867     while (pConv->transactions)
1868     {
1869         WDML_XACT *pXAct = pConv->transactions;
1870
1871         if (pConv->wStatus & ST_CLIENT)
1872         {
1873             /* transaction should be in the queue until handled */
1874             WDML_ClientHandle(pConv, pXAct, 0, NULL);
1875             WDML_UnQueueTransaction(pConv, pXAct);
1876         }
1877         else
1878         {
1879             /* transaction should be removed from the queue before handling */
1880             WDML_UnQueueTransaction(pConv, pXAct);
1881             WDML_ServerHandle(pConv, pXAct);
1882         }
1883
1884         WDML_FreeTransaction(pConv->instance, pXAct, TRUE);
1885
1886         if (wCmd == EC_ENABLEONE) break;
1887     }
1888     return TRUE;
1889 }
1890
1891 /*****************************************************************
1892  *            DdeEnableCallback (USER32.@)
1893  */
1894 BOOL WINAPI DdeEnableCallback(DWORD idInst, HCONV hConv, UINT wCmd)
1895 {
1896     BOOL ret = FALSE;
1897     WDML_CONV *pConv;
1898
1899     TRACE("(%d, %p, %04x)\n", idInst, hConv, wCmd);
1900
1901     if (hConv)
1902     {
1903         pConv = WDML_GetConv(hConv, TRUE);
1904
1905         if (pConv && pConv->instance->instanceID == idInst)
1906             ret = WDML_EnableCallback(pConv, wCmd);
1907     }
1908     else
1909     {
1910         WDML_INSTANCE *pInstance = WDML_GetInstance(idInst);
1911
1912         if (!pInstance)
1913             return FALSE;
1914
1915         TRACE("adding flags %04x to instance %p\n", wCmd, pInstance);
1916         pInstance->wStatus |= wCmd;
1917
1918         if (wCmd == EC_DISABLE)
1919         {
1920             pInstance->wStatus |= ST_BLOCKED;
1921             TRACE("EC_DISABLE: inst %p status flags %04x\n", pInstance, pInstance->wStatus);
1922         }
1923         else if (wCmd == EC_ENABLEALL)
1924         {
1925             pInstance->wStatus &= ~ST_BLOCKED;
1926             TRACE("EC_ENABLEALL: inst %p status flags %04x\n", pInstance, pInstance->wStatus);
1927         }
1928
1929         ret = TRUE;
1930
1931         for (pConv = pInstance->convs[WDML_CLIENT_SIDE]; pConv != NULL; pConv = pConv->next)
1932         {
1933             ret = WDML_EnableCallback(pConv, wCmd);
1934             if (ret && wCmd == EC_QUERYWAITING) break;
1935         }
1936     }
1937
1938     return ret;
1939 }
1940
1941 /******************************************************************
1942  *              WDML_GetConv
1943  *
1944  *
1945  */
1946 WDML_CONV*      WDML_GetConv(HCONV hConv, BOOL checkConnected)
1947 {
1948     WDML_CONV*  pConv = (WDML_CONV*)hConv;
1949
1950     /* FIXME: should do better checking */
1951     if (pConv == NULL || pConv->magic != WDML_CONV_MAGIC) return NULL;
1952
1953     if (!pConv->instance || pConv->instance->threadID != GetCurrentThreadId())
1954     {
1955         WARN("wrong thread ID\n");
1956         pConv->instance->lastError = DMLERR_INVALIDPARAMETER; /* FIXME: check */
1957         return NULL;
1958     }
1959
1960     if (checkConnected && !(pConv->wStatus & ST_CONNECTED))
1961     {
1962         WARN("found conv but ain't connected\n");
1963         pConv->instance->lastError = DMLERR_NO_CONV_ESTABLISHED;
1964         return NULL;
1965     }
1966
1967     return pConv;
1968 }
1969
1970 /******************************************************************
1971  *              WDML_GetConvFromWnd
1972  *
1973  *
1974  */
1975 WDML_CONV*      WDML_GetConvFromWnd(HWND hWnd)
1976 {
1977     return (WDML_CONV*)GetWindowLongPtrW(hWnd, GWL_WDML_CONVERSATION);
1978 }
1979
1980 /******************************************************************
1981  *              WDML_PostAck
1982  *
1983  *
1984  */
1985 BOOL            WDML_PostAck(WDML_CONV* pConv, WDML_SIDE side, WORD appRetCode,
1986                              BOOL fBusy, BOOL fAck, UINT_PTR pmt, LPARAM lParam, UINT oldMsg)
1987 {
1988     DDEACK      ddeAck;
1989     HWND        from, to;
1990
1991     if (side == WDML_SERVER_SIDE)
1992     {
1993         from = pConv->hwndServer;
1994         to   = pConv->hwndClient;
1995     }
1996     else
1997     {
1998         to   = pConv->hwndServer;
1999         from = pConv->hwndClient;
2000     }
2001
2002     ddeAck.bAppReturnCode = appRetCode;
2003     ddeAck.reserved       = 0;
2004     ddeAck.fBusy          = fBusy;
2005     ddeAck.fAck           = fAck;
2006
2007     TRACE("Posting a %s ack\n", ddeAck.fAck ? "positive" : "negative");
2008
2009     lParam = (lParam) ? ReuseDDElParam(lParam, oldMsg, WM_DDE_ACK, *(WORD*)&ddeAck, pmt) :
2010         PackDDElParam(WM_DDE_ACK, *(WORD*)&ddeAck, pmt);
2011     if (!PostMessageW(to, WM_DDE_ACK, (WPARAM)from, lParam))
2012     {
2013         pConv->wStatus &= ~ST_CONNECTED;
2014         pConv->instance->lastError = DMLERR_POSTMSG_FAILED;
2015         FreeDDElParam(WM_DDE_ACK, lParam);
2016         return FALSE;
2017     }
2018     return TRUE;
2019 }
2020
2021 /*****************************************************************
2022  *            DdeSetUserHandle (USER32.@)
2023  */
2024 BOOL WINAPI DdeSetUserHandle(HCONV hConv, DWORD id, DWORD hUser)
2025 {
2026     WDML_CONV*  pConv;
2027
2028     pConv = WDML_GetConv(hConv, FALSE);
2029     if (pConv == NULL)
2030         return FALSE;
2031
2032     if (id == QID_SYNC)
2033     {
2034         pConv->hUser = hUser;
2035     }
2036     else
2037     {
2038         WDML_XACT*      pXAct;
2039
2040         pXAct = WDML_FindTransaction(pConv, id);
2041         if (pXAct)
2042         {
2043             pXAct->hUser = hUser;
2044         }
2045         else
2046         {
2047             pConv->instance->lastError = DMLERR_UNFOUND_QUEUE_ID;
2048             return  FALSE;
2049         }
2050     }
2051     return TRUE;
2052 }
2053
2054 /******************************************************************
2055  *              WDML_GetLocalConvInfo
2056  *
2057  *
2058  */
2059 static  BOOL    WDML_GetLocalConvInfo(WDML_CONV* pConv, CONVINFO* ci, DWORD id)
2060 {
2061     BOOL        ret = TRUE;
2062     WDML_LINK*  pLink;
2063     WDML_SIDE   side;
2064
2065     ci->hConvPartner = (pConv->wStatus & ST_ISLOCAL) ? (HCONV)((ULONG_PTR)pConv | 1) : 0;
2066     ci->hszSvcPartner = pConv->hszService;
2067     ci->hszServiceReq = pConv->hszService; /* FIXME: they shouldn't be the same, should they ? */
2068     ci->hszTopic = pConv->hszTopic;
2069     ci->wStatus = pConv->wStatus;
2070
2071     side = (pConv->wStatus & ST_CLIENT) ? WDML_CLIENT_SIDE : WDML_SERVER_SIDE;
2072
2073     for (pLink = pConv->instance->links[side]; pLink != NULL; pLink = pLink->next)
2074     {
2075         if (pLink->hConv == (HCONV)pConv)
2076         {
2077             ci->wStatus |= ST_ADVISE;
2078             break;
2079         }
2080     }
2081
2082     /* FIXME: non handled status flags:
2083        ST_BLOCKED
2084        ST_BLOCKNEXT
2085        ST_INLIST
2086     */
2087
2088     ci->wConvst = pConv->wConvst; /* FIXME */
2089
2090     ci->wLastError = 0; /* FIXME: note it's not the instance last error */
2091     ci->hConvList = 0;
2092     ci->ConvCtxt = pConv->convContext;
2093     if (ci->wStatus & ST_CLIENT)
2094     {
2095         ci->hwnd = pConv->hwndClient;
2096         ci->hwndPartner = pConv->hwndServer;
2097     }
2098     else
2099     {
2100         ci->hwnd = pConv->hwndServer;
2101         ci->hwndPartner = pConv->hwndClient;
2102     }
2103     if (id == QID_SYNC)
2104     {
2105         ci->hUser = pConv->hUser;
2106         ci->hszItem = 0;
2107         ci->wFmt = 0;
2108         ci->wType = 0;
2109     }
2110     else
2111     {
2112         WDML_XACT*      pXAct;
2113
2114         pXAct = WDML_FindTransaction(pConv, id);
2115         if (pXAct)
2116         {
2117             ci->hUser = pXAct->hUser;
2118             ci->hszItem = pXAct->hszItem;
2119             ci->wFmt = pXAct->wFmt;
2120             ci->wType = pXAct->wType;
2121         }
2122         else
2123         {
2124             ret = 0;
2125             pConv->instance->lastError = DMLERR_UNFOUND_QUEUE_ID;
2126         }
2127     }
2128     return ret;
2129 }
2130
2131 /******************************************************************
2132  *              DdeQueryConvInfo (USER32.@)
2133  *
2134  * FIXME: Set last DDE error on failure.
2135  */
2136 UINT WINAPI DdeQueryConvInfo(HCONV hConv, DWORD id, PCONVINFO lpConvInfo)
2137 {
2138     UINT        ret = lpConvInfo->cb;
2139     CONVINFO    ci;
2140     WDML_CONV*  pConv;
2141
2142     TRACE("(%p,%x,%p)\n", hConv, id, lpConvInfo);
2143
2144     if (!hConv)
2145     {
2146         FIXME("hConv is NULL\n");
2147         return 0;
2148     }
2149
2150     pConv = WDML_GetConv(hConv, FALSE);
2151     if (pConv != NULL)
2152     {
2153         if (!WDML_GetLocalConvInfo(pConv, &ci, id))
2154             ret = 0;
2155     }
2156     else
2157     {
2158         if ((ULONG_PTR)hConv & 1)
2159         {
2160             pConv = WDML_GetConv((HCONV)((ULONG_PTR)hConv & ~1), FALSE);
2161             if (pConv != NULL)
2162                 FIXME("Request on remote conversation information is not implemented yet\n");
2163         }
2164         ret = 0;
2165     }
2166
2167     if (ret != 0)
2168         memcpy(lpConvInfo, &ci, min((size_t)lpConvInfo->cb, sizeof(ci)));
2169     return ret;
2170 }
2171
2172 /* ================================================================
2173  *
2174  *                      Link (hot & warm) management
2175  *
2176  * ================================================================ */
2177
2178 /******************************************************************
2179  *              WDML_AddLink
2180  *
2181  *
2182  */
2183 void WDML_AddLink(WDML_INSTANCE* pInstance, HCONV hConv, WDML_SIDE side,
2184                   UINT wType, HSZ hszItem, UINT wFmt)
2185 {
2186     WDML_LINK*  pLink;
2187
2188     pLink = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_LINK));
2189     if (pLink == NULL)
2190     {
2191         ERR("OOM\n");
2192         return;
2193     }
2194
2195     pLink->hConv = hConv;
2196     pLink->transactionType = wType;
2197     WDML_IncHSZ(pInstance, pLink->hszItem = hszItem);
2198     pLink->uFmt = wFmt;
2199     pLink->next = pInstance->links[side];
2200     pInstance->links[side] = pLink;
2201 }
2202
2203 /******************************************************************
2204  *              WDML_RemoveLink
2205  *
2206  *
2207  */
2208 void WDML_RemoveLink(WDML_INSTANCE* pInstance, HCONV hConv, WDML_SIDE side,
2209                      HSZ hszItem, UINT uFmt)
2210 {
2211     WDML_LINK* pPrev = NULL;
2212     WDML_LINK* pCurrent = NULL;
2213
2214     pCurrent = pInstance->links[side];
2215
2216     while (pCurrent != NULL)
2217     {
2218         if (pCurrent->hConv == hConv &&
2219             DdeCmpStringHandles(pCurrent->hszItem, hszItem) == 0 &&
2220             pCurrent->uFmt == uFmt)
2221         {
2222             if (pCurrent == pInstance->links[side])
2223             {
2224                 pInstance->links[side] = pCurrent->next;
2225             }
2226             else
2227             {
2228                 pPrev->next = pCurrent->next;
2229             }
2230
2231             WDML_DecHSZ(pInstance, pCurrent->hszItem);
2232             HeapFree(GetProcessHeap(), 0, pCurrent);
2233             break;
2234         }
2235
2236         pPrev = pCurrent;
2237         pCurrent = pCurrent->next;
2238     }
2239 }
2240
2241 /* this function is called to remove all links related to the conv.
2242    It should be called from both client and server when terminating
2243    the conversation.
2244 */
2245 /******************************************************************
2246  *              WDML_RemoveAllLinks
2247  *
2248  *
2249  */
2250 void WDML_RemoveAllLinks(WDML_INSTANCE* pInstance, WDML_CONV* pConv, WDML_SIDE side)
2251 {
2252     WDML_LINK* pPrev = NULL;
2253     WDML_LINK* pCurrent = NULL;
2254     WDML_LINK* pNext = NULL;
2255
2256     pCurrent = pInstance->links[side];
2257
2258     while (pCurrent != NULL)
2259     {
2260         if (pCurrent->hConv == (HCONV)pConv)
2261         {
2262             if (pCurrent == pInstance->links[side])
2263             {
2264                 pInstance->links[side] = pCurrent->next;
2265                 pNext = pCurrent->next;
2266             }
2267             else
2268             {
2269                 pPrev->next = pCurrent->next;
2270                 pNext = pCurrent->next;
2271             }
2272
2273             WDML_DecHSZ(pInstance, pCurrent->hszItem);
2274
2275             HeapFree(GetProcessHeap(), 0, pCurrent);
2276             pCurrent = NULL;
2277         }
2278
2279         if (pCurrent)
2280         {
2281             pPrev = pCurrent;
2282             pCurrent = pCurrent->next;
2283         }
2284         else
2285         {
2286             pCurrent = pNext;
2287         }
2288     }
2289 }
2290
2291 /******************************************************************
2292  *              WDML_FindLink
2293  *
2294  *
2295  */
2296 WDML_LINK*      WDML_FindLink(WDML_INSTANCE* pInstance, HCONV hConv, WDML_SIDE side,
2297                               HSZ hszItem, BOOL use_fmt, UINT uFmt)
2298 {
2299     WDML_LINK*  pCurrent = NULL;
2300
2301     for (pCurrent = pInstance->links[side]; pCurrent != NULL; pCurrent = pCurrent->next)
2302     {
2303         /* we don't need to check for transaction type as it can be altered */
2304
2305         if (pCurrent->hConv == hConv &&
2306             DdeCmpStringHandles(pCurrent->hszItem, hszItem) == 0 &&
2307             (!use_fmt || pCurrent->uFmt == uFmt))
2308         {
2309             break;
2310         }
2311
2312     }
2313
2314     return pCurrent;
2315 }
2316
2317 /* ================================================================
2318  *
2319  *                      Transaction management
2320  *
2321  * ================================================================ */
2322
2323 /******************************************************************
2324  *              WDML_AllocTransaction
2325  *
2326  * Alloc a transaction structure for handling the message ddeMsg
2327  */
2328 WDML_XACT*      WDML_AllocTransaction(WDML_INSTANCE* pInstance, UINT ddeMsg,
2329                                       UINT wFmt, HSZ hszItem)
2330 {
2331     WDML_XACT*          pXAct;
2332     static WORD         tid = 1;        /* FIXME: wrap around */
2333
2334     pXAct = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_XACT));
2335     if (!pXAct)
2336     {
2337         pInstance->lastError = DMLERR_MEMORY_ERROR;
2338         return NULL;
2339     }
2340
2341     pXAct->xActID = tid++;
2342     pXAct->ddeMsg = ddeMsg;
2343     pXAct->hDdeData = 0;
2344     pXAct->hUser = 0;
2345     pXAct->next = NULL;
2346     pXAct->wType = 0;
2347     pXAct->wFmt = wFmt;
2348     if ((pXAct->hszItem = hszItem)) WDML_IncHSZ(pInstance, pXAct->hszItem);
2349     pXAct->atom = 0;
2350     pXAct->hMem = 0;
2351     pXAct->lParam = 0;
2352
2353     return pXAct;
2354 }
2355
2356 /******************************************************************
2357  *              WDML_QueueTransaction
2358  *
2359  * Adds a transaction to the list of transaction
2360  */
2361 void    WDML_QueueTransaction(WDML_CONV* pConv, WDML_XACT* pXAct)
2362 {
2363     WDML_XACT** pt;
2364
2365     /* advance to last in queue */
2366     for (pt = &pConv->transactions; *pt != NULL; pt = &(*pt)->next);
2367     *pt = pXAct;
2368 }
2369
2370 /******************************************************************
2371  *              WDML_UnQueueTransaction
2372  *
2373  *
2374  */
2375 BOOL    WDML_UnQueueTransaction(WDML_CONV* pConv, WDML_XACT*  pXAct)
2376 {
2377     WDML_XACT** pt;
2378
2379     for (pt = &pConv->transactions; *pt; pt = &(*pt)->next)
2380     {
2381         if (*pt == pXAct)
2382         {
2383             *pt = pXAct->next;
2384             return TRUE;
2385         }
2386     }
2387     return FALSE;
2388 }
2389
2390 /******************************************************************
2391  *              WDML_FreeTransaction
2392  *
2393  *
2394  */
2395 void    WDML_FreeTransaction(WDML_INSTANCE* pInstance, WDML_XACT* pXAct, BOOL doFreePmt)
2396 {
2397     /* free pmt(s) in pXAct too. check against one for not deleting TRUE return values */
2398     if (doFreePmt && (ULONG_PTR)pXAct->hMem > 1)
2399     {
2400         GlobalFree(pXAct->hMem);
2401     }
2402     if (pXAct->hszItem) WDML_DecHSZ(pInstance, pXAct->hszItem);
2403
2404     HeapFree(GetProcessHeap(), 0, pXAct);
2405 }
2406
2407 /******************************************************************
2408  *              WDML_FindTransaction
2409  *
2410  *
2411  */
2412 WDML_XACT*      WDML_FindTransaction(WDML_CONV* pConv, DWORD tid)
2413 {
2414     WDML_XACT* pXAct;
2415
2416     tid = HIWORD(tid);
2417     for (pXAct = pConv->transactions; pXAct; pXAct = pXAct->next)
2418     {
2419         if (pXAct->xActID == tid)
2420             break;
2421     }
2422     return pXAct;
2423 }
2424
2425 /* ================================================================
2426  *
2427  *         Information broadcast across DDEML implementations
2428  *
2429  * ================================================================ */
2430
2431 struct tagWDML_BroadcastPmt
2432 {
2433     LPCWSTR     clsName;
2434     UINT        uMsg;
2435     WPARAM      wParam;
2436     LPARAM      lParam;
2437 };
2438
2439 /******************************************************************
2440  *              WDML_BroadcastEnumProc
2441  *
2442  *
2443  */
2444 static  BOOL CALLBACK WDML_BroadcastEnumProc(HWND hWnd, LPARAM lParam)
2445 {
2446     struct tagWDML_BroadcastPmt*        s = (struct tagWDML_BroadcastPmt*)lParam;
2447     WCHAR                               buffer[128];
2448
2449     if (GetClassNameW(hWnd, buffer, 128) > 0 &&
2450         lstrcmpiW(buffer, s->clsName) == 0)
2451     {
2452         PostMessageW(hWnd, s->uMsg, s->wParam, s->lParam);
2453     }
2454     return TRUE;
2455 }
2456
2457 /******************************************************************
2458  *              WDML_BroadcastDDEWindows
2459  *
2460  *
2461  */
2462 void WDML_BroadcastDDEWindows(LPCWSTR clsName, UINT uMsg, WPARAM wParam, LPARAM lParam)
2463 {
2464     struct tagWDML_BroadcastPmt s;
2465
2466     s.clsName = clsName;
2467     s.uMsg    = uMsg;
2468     s.wParam  = wParam;
2469     s.lParam  = lParam;
2470     EnumWindows(WDML_BroadcastEnumProc, (LPARAM)&s);
2471 }