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