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