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