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