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