When including 'wine/port.h', include it first.
[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\n");
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\n");
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         
387         /* Check for other way of setting Client-only !! */
388         
389         pInstance->clientOnly = 
390             (pInstance->CBFflags & CBF_FAIL_ALLSVRXACTIONS) == CBF_FAIL_ALLSVRXACTIONS;
391     }
392     
393     TRACE("instance created - checking validity \n");
394     
395     if (*pidInst == 0) 
396     {
397         /*  Initialisation of new Instance Identifier */
398         TRACE("new instance, callback %p flags %lX\n",pfnCallback,afCmd);
399
400         EnterCriticalSection(&WDML_CritSect);
401         
402         if (WDML_InstanceList == NULL) 
403         {
404             /* can't be another instance in this case, assign to the base pointer */
405             WDML_InstanceList = pInstance;
406             
407             /* since first must force filter of XTYP_CONNECT and XTYP_WILDCONNECT for
408              *          present 
409              *  -------------------------------      NOTE NOTE NOTE    --------------------------
410              *          
411              *  the manual is not clear if this condition
412              *  applies to the first call to DdeInitialize from an application, or the 
413              *  first call for a given callback !!!
414              */
415             
416             pInstance->CBFflags = pInstance->CBFflags|APPCMD_FILTERINITS;
417             TRACE("First application instance detected OK\n");
418             /*  allocate new instance ID */
419             WDML_IncrementInstanceId(pInstance);
420         } 
421         else 
422         {
423             /* really need to chain the new one in to the latest here, but after checking conditions
424              *  such as trying to start a conversation from an application trying to monitor */
425             reference_inst = WDML_InstanceList;
426             TRACE("Subsequent application instance - starting checks\n");
427             while (reference_inst->next != NULL) 
428             {
429                 /*
430                  *      This set of tests will work if application uses same instance Id
431                  *      at application level once allocated - which is what manual implies
432                  *      should happen. If someone tries to be 
433                  *      clever (lazy ?) it will fail to pick up that later calls are for
434                  *      the same application - should we trust them ?
435                  */
436                 if (pInstance->instanceID == reference_inst->instanceID) 
437                 {
438                     /* Check 1 - must be same Client-only state */
439                     
440                     if (pInstance->clientOnly != reference_inst->clientOnly)
441                     {
442                         ret = DMLERR_DLL_USAGE;
443                         goto theError;
444                     }
445                     
446                     /* Check 2 - cannot use 'Monitor' with any non-monitor modes */
447                     
448                     if (pInstance->monitor != reference_inst->monitor) 
449                     {
450                         ret = DMLERR_INVALIDPARAMETER;
451                         goto theError;
452                     }
453                     
454                     /* Check 3 - must supply different callback address */
455                     
456                     if (pInstance->callback == reference_inst->callback)
457                     {
458                         ret = DMLERR_DLL_USAGE;
459                         goto theError;
460                     }
461                 }
462                 reference_inst = reference_inst->next;
463             }
464             /*  All cleared, add to chain */
465             
466             TRACE("Application Instance checks finished\n");
467             WDML_IncrementInstanceId(pInstance);
468             reference_inst->next = pInstance;
469         }
470         LeaveCriticalSection(&WDML_CritSect);
471
472         *pidInst = pInstance->instanceID;
473
474         /* for deadlock issues, windows must always be created when outside the critical section */
475         wndclass.cbSize        = sizeof(wndclass);
476         wndclass.style         = 0;
477         wndclass.lpfnWndProc   = WDML_EventProc;
478         wndclass.cbClsExtra    = 0;
479         wndclass.cbWndExtra    = sizeof(DWORD);
480         wndclass.hInstance     = 0;
481         wndclass.hIcon         = 0;
482         wndclass.hCursor       = 0;
483         wndclass.hbrBackground = 0;
484         wndclass.lpszMenuName  = NULL;
485         wndclass.lpszClassName = WDML_szEventClass;
486         wndclass.hIconSm       = 0;
487         
488         RegisterClassExA(&wndclass);
489         
490         pInstance->hwndEvent = CreateWindowA(WDML_szEventClass, NULL,
491                                                 WS_POPUP, 0, 0, 0, 0,
492                                                 0, 0, 0, 0);
493         
494         SetWindowLongA(pInstance->hwndEvent, GWL_WDML_INSTANCE, (DWORD)pInstance);
495
496         TRACE("New application instance processing finished OK\n");
497     } 
498     else 
499     {
500         /* Reinitialisation situation   --- FIX  */
501         TRACE("reinitialisation of (%p,%p,0x%lx,%ld): stub\n", pidInst, pfnCallback, afCmd, ulRes);
502         
503         EnterCriticalSection(&WDML_CritSect);
504         
505         if (WDML_InstanceList == NULL) 
506         {
507             ret = DMLERR_DLL_USAGE;
508             goto theError;
509         }
510         HeapFree(GetProcessHeap(), 0, pInstance); /* finished - release heap space used as work store */
511         /* can't reinitialise if we have initialised nothing !! */
512         reference_inst = WDML_InstanceList;
513         /* must first check if we have been given a valid instance to re-initialise !!  how do we do that ? */
514         /*
515          *      MS allows initialisation without specifying a callback, should we allow addition of the
516          *      callback by a later call to initialise ? - if so this lot will have to change
517          */
518         while (reference_inst->next != NULL)
519         {
520             if (*pidInst == reference_inst->instanceID && pfnCallback == reference_inst->callback)
521             {
522                 /* Check 1 - cannot change client-only mode if set via APPCMD_CLIENTONLY */
523                 
524                 if (reference_inst->clientOnly)
525                 {
526                     if  ((reference_inst->CBFflags & CBF_FAIL_ALLSVRXACTIONS) != CBF_FAIL_ALLSVRXACTIONS) 
527                     {
528                                 /* i.e. Was set to Client-only and through APPCMD_CLIENTONLY */
529                         
530                         if (!(afCmd & APPCMD_CLIENTONLY))
531                         {
532                             ret = DMLERR_DLL_USAGE;
533                             goto theError;
534                         }
535                     }
536                 }
537                 /* Check 2 - cannot change monitor modes */
538                 
539                 if (pInstance->monitor != reference_inst->monitor) 
540                 {
541                     ret = DMLERR_DLL_USAGE;
542                     goto theError;
543                 }
544                 
545                 /* Check 3 - trying to set Client-only via APPCMD when not set so previously */
546                 
547                 if ((afCmd&APPCMD_CLIENTONLY) && !reference_inst->clientOnly)
548                 {
549                     ret = DMLERR_DLL_USAGE;
550                     goto theError;
551                 }
552                 break;
553             }
554             reference_inst = reference_inst->next;
555         }
556         if (reference_inst->next == NULL)
557         {
558             /* Crazy situation - trying to re-initialize something that has not beeen initialized !! 
559              *  
560              *  Manual does not say what we do, cannot return DMLERR_NOT_INITIALIZED so what ?
561              */
562             ret = DMLERR_INVALIDPARAMETER;
563             goto theError;
564         }
565         /* All checked - change relevant flags */
566         
567         reference_inst->CBFflags = pInstance->CBFflags;
568         reference_inst->clientOnly = pInstance->clientOnly;
569         reference_inst->monitorFlags = pInstance->monitorFlags;
570         LeaveCriticalSection(&WDML_CritSect);
571     }
572     
573     return DMLERR_NO_ERROR;
574  theError:
575     HeapFree(GetProcessHeap(), 0, pInstance);
576     LeaveCriticalSection(&WDML_CritSect);
577     return ret;
578 }
579
580 /******************************************************************************
581  *            DdeInitializeA   (USER32.@)
582  */
583 UINT WINAPI DdeInitializeA(LPDWORD pidInst, PFNCALLBACK pfnCallback,
584                            DWORD afCmd, DWORD ulRes)
585 {
586     return WDML_Initialize(pidInst, pfnCallback, afCmd, ulRes, FALSE, FALSE);
587 }
588
589 /******************************************************************************
590  * DdeInitializeW [USER32.@]
591  * Registers an application with the DDEML
592  *
593  * PARAMS
594  *    pidInst     [I] Pointer to instance identifier
595  *    pfnCallback [I] Pointer to callback function
596  *    afCmd       [I] Set of command and filter flags
597  *    ulRes       [I] Reserved
598  *
599  * RETURNS
600  *    Success: DMLERR_NO_ERROR
601  *    Failure: DMLERR_DLL_USAGE, DMLERR_INVALIDPARAMETER, DMLERR_SYS_ERROR
602  */
603 UINT WINAPI DdeInitializeW(LPDWORD pidInst, PFNCALLBACK pfnCallback,
604                            DWORD afCmd, DWORD ulRes)
605 {
606     return WDML_Initialize(pidInst, pfnCallback, afCmd, ulRes, TRUE, FALSE);
607 }
608
609 /*****************************************************************
610  * DdeUninitialize [USER32.@]  Frees DDEML resources
611  *
612  * PARAMS
613  *    idInst [I] Instance identifier
614  *
615  * RETURNS
616  *    Success: TRUE
617  *    Failure: FALSE
618  */
619
620 BOOL WINAPI DdeUninitialize(DWORD idInst)
621 {
622     /*  Stage one - check if we have a handle for this instance
623      */
624     WDML_INSTANCE*              pInstance;
625     WDML_INSTANCE*              reference_inst;
626     WDML_CONV*                  pConv;
627     WDML_CONV*                  pConvNext;
628
629     EnterCriticalSection(&WDML_CritSect);
630
631     /*  First check instance 
632      */
633     pInstance = WDML_GetInstance(idInst);
634     if (pInstance == NULL)
635     {
636         LeaveCriticalSection(&WDML_CritSect);
637         /*
638          *      Needs something here to record NOT_INITIALIZED ready for DdeGetLastError
639          */
640         return FALSE;
641     }
642
643     /* first terminate all conversations client side
644      * this shall close existing links...
645      */
646     for (pConv = pInstance->convs[WDML_CLIENT_SIDE]; pConv != NULL; pConv = pConvNext)
647     {
648         pConvNext = pConv->next;
649         DdeDisconnect((HCONV)pConv);
650     }
651     if (pInstance->convs[WDML_CLIENT_SIDE])
652         FIXME("still pending conversations\n");
653
654     /* then unregister all known service names */
655     DdeNameService(idInst, 0, 0, DNS_UNREGISTER);
656     
657     /* Free the nodes that were not freed by this instance
658      * and remove the nodes from the list of HSZ nodes.
659      */
660     WDML_FreeAllHSZ(pInstance);
661
662     DestroyWindow(pInstance->hwndEvent);
663     
664     /* OK now delete the instance handle itself */
665     
666     if (WDML_InstanceList == pInstance)
667     {
668         /* special case - the first/only entry
669          */
670         WDML_InstanceList = pInstance->next;
671     }
672     else
673     {
674         /* general case
675          */
676         reference_inst = WDML_InstanceList;
677         while (reference_inst->next != pInstance)
678         {
679             reference_inst = pInstance->next;
680         }
681         reference_inst->next = pInstance->next;
682     }
683     /* leave crit sect and release the heap entry
684      */
685     HeapFree(GetProcessHeap(), 0, pInstance);
686     LeaveCriticalSection(&WDML_CritSect);
687     return TRUE;
688 }
689
690 /******************************************************************
691  *              WDML_NotifyThreadExit
692  *
693  *
694  */
695 void WDML_NotifyThreadDetach(void)
696 {
697     WDML_INSTANCE*      pInstance;
698     WDML_INSTANCE*      next;
699     DWORD               tid = GetCurrentThreadId();
700
701     EnterCriticalSection(&WDML_CritSect);
702     for (pInstance = WDML_InstanceList; pInstance != NULL; pInstance = next)
703     {
704         next = pInstance->next;
705         if (pInstance->threadID == tid)
706         {
707             DdeUninitialize(pInstance->instanceID);
708         }
709     }
710     LeaveCriticalSection(&WDML_CritSect);
711 }
712
713 /******************************************************************
714  *              WDML_InvokeCallback
715  *
716  *
717  */
718 HDDEDATA        WDML_InvokeCallback(WDML_INSTANCE* pInstance, UINT uType, UINT uFmt, HCONV hConv,
719                                     HSZ hsz1, HSZ hsz2, HDDEDATA hdata, 
720                                     DWORD dwData1, DWORD dwData2)
721 {
722     HDDEDATA    ret;
723
724     if (pInstance == NULL)
725         return (HDDEDATA)0;
726     TRACE("invoking CB%d[%08lx] (%u %u %08lx 0x%x 0x%x %u %lu %lu)\n",
727           pInstance->win16 ? 16 : 32, (DWORD)pInstance->callback, uType, uFmt, 
728           (DWORD)hConv, hsz1, hsz2, hdata, dwData1, dwData2);
729     if (pInstance->win16)
730     {
731         ret = WDML_InvokeCallback16(pInstance->callback, uType, uFmt, hConv, 
732                                     hsz1, hsz2, hdata, dwData1, dwData2);
733     }
734     else
735     {
736         ret = pInstance->callback(uType, uFmt, hConv, hsz1, hsz2, hdata, dwData1, dwData2);
737     }
738     TRACE("done => %08lx\n", (DWORD)ret);
739     return ret;
740 }
741
742 /*****************************************************************************
743  *      WDML_GetInstance
744  *
745  *      generic routine to return a pointer to the relevant DDE_HANDLE_ENTRY
746  *      for an instance Id, or NULL if the entry does not exist
747  *
748  */
749 WDML_INSTANCE*  WDML_GetInstance(DWORD instId)
750 {
751     WDML_INSTANCE*      pInstance;
752     
753     for (pInstance = WDML_InstanceList; pInstance != NULL; pInstance = pInstance->next)
754     {
755         if (pInstance->instanceID == instId)
756         {
757             if (GetCurrentThreadId() != pInstance->threadID)
758             {
759                 FIXME("Tried to get instance from wrong thread\n");
760                 continue;
761             }
762             return pInstance;
763         }
764     }
765     TRACE("Instance entry missing\n");
766     return NULL;
767 }
768
769 /******************************************************************
770  *              WDML_GetInstanceFromWnd
771  *
772  *
773  */
774 WDML_INSTANCE*  WDML_GetInstanceFromWnd(HWND hWnd)
775 {
776     return (WDML_INSTANCE*)GetWindowLongA(hWnd, GWL_WDML_INSTANCE);
777 }
778
779 /******************************************************************************
780  * DdeGetLastError [USER32.@]  Gets most recent error code
781  *
782  * PARAMS
783  *    idInst [I] Instance identifier
784  *
785  * RETURNS
786  *    Last error code
787  */
788 UINT WINAPI DdeGetLastError(DWORD idInst)
789 {
790     DWORD               error_code;
791     WDML_INSTANCE*      pInstance;
792     
793     FIXME("(%ld): error reporting is weakly implemented\n", idInst);
794     
795     EnterCriticalSection(&WDML_CritSect);
796     
797     /*  First check instance
798      */
799     pInstance = WDML_GetInstance(idInst);
800     if  (pInstance == NULL) 
801     {
802         error_code = DMLERR_DLL_NOT_INITIALIZED;
803     }
804     else
805     {
806         error_code = pInstance->lastError;
807         pInstance->lastError = 0;
808     }
809     
810     LeaveCriticalSection(&WDML_CritSect);
811     return error_code;
812 }
813
814 /* ================================================================
815  *
816  *                      String management
817  *
818  * ================================================================ */
819
820
821 /******************************************************************
822  *              WDML_FindNode
823  *
824  *
825  */
826 static HSZNode* WDML_FindNode(WDML_INSTANCE* pInstance, HSZ hsz)
827 {
828     HSZNode*    pNode;
829
830     if (pInstance == NULL) return NULL;
831
832     for (pNode = pInstance->nodeList; pNode != NULL; pNode = pNode->next)
833     {
834         if (pNode->hsz == hsz) break;
835     }
836     if (!pNode) WARN("HSZ 0x%x not found\n", hsz);
837     return pNode;
838 }
839
840 /******************************************************************
841  *              WDML_MakeAtomFromHsz
842  *
843  * Creates a global atom from an existing HSZ
844  * Generally used before sending an HSZ as an atom to a remote app
845  */
846 ATOM    WDML_MakeAtomFromHsz(HSZ hsz)
847 {
848     WCHAR nameBuffer[MAX_BUFFER_LEN];
849
850     if (GetAtomNameW((ATOM)hsz, nameBuffer, MAX_BUFFER_LEN))
851         return GlobalAddAtomW(nameBuffer);
852     WARN("HSZ 0x%xnot found\n", hsz);
853     return 0;
854 }
855
856 /******************************************************************
857  *              WDML_MakeHszFromAtom
858  *
859  * Creates a HSZ from an existing global atom
860  * Generally used while receiving a global atom and transforming it
861  * into an HSZ
862  */
863 HSZ     WDML_MakeHszFromAtom(WDML_INSTANCE* pInstance, ATOM atom)
864 {
865     WCHAR nameBuffer[MAX_BUFFER_LEN];
866
867     GlobalGetAtomNameW(atom, nameBuffer, MAX_BUFFER_LEN);
868     return DdeCreateStringHandleW(pInstance->instanceID, nameBuffer, CP_WINUNICODE);
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(hsz);
918             }
919             return TRUE;
920         }
921     }
922     WARN("HSZ 0x%xnot 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(hsz, ptr, cchMax);
993         break;
994     case CP_WINUNICODE:
995         ret = GetAtomNameW(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",
1039           idInst, hsz, psz, cchMax, iCodePage);
1040     
1041     EnterCriticalSection(&WDML_CritSect);
1042     
1043     /*  First check instance 
1044      */
1045     pInstance = WDML_GetInstance(idInst);
1046     if (pInstance != NULL)
1047     {
1048         if (iCodePage == 0) iCodePage = CP_WINUNICODE;
1049         ret = WDML_QueryString(pInstance, hsz, psz, cchMax, iCodePage);
1050     }
1051     LeaveCriticalSection(&WDML_CritSect);
1052
1053     TRACE("returning %s\n", debugstr_w(psz)); 
1054     return ret;
1055 }
1056
1057 /******************************************************************
1058  *              DML_CreateString
1059  *
1060  *
1061  */
1062 static  HSZ     WDML_CreateString(WDML_INSTANCE* pInstance, LPCVOID ptr, int codepage)
1063 {
1064     HSZ         hsz;
1065
1066     switch (codepage)
1067     {
1068     case CP_WINANSI:
1069         hsz = AddAtomA(ptr);
1070         TRACE("added atom %s with HSZ 0x%x, \n", debugstr_a(ptr), hsz);
1071         break;
1072     case CP_WINUNICODE:
1073         hsz = AddAtomW(ptr);
1074         TRACE("added atom %s with HSZ 0x%x, \n", debugstr_w(ptr), hsz);
1075         break;
1076     default:
1077         ERR("Unknown code page %d\n", codepage);
1078         return 0;
1079     }
1080     WDML_InsertHSZNode(pInstance, hsz);
1081     return hsz;
1082 }
1083
1084 /*****************************************************************
1085  * DdeCreateStringHandleA [USER32.@]
1086  *
1087  * RETURNS
1088  *    Success: String handle
1089  *    Failure: 0
1090  */
1091 HSZ WINAPI DdeCreateStringHandleA(DWORD idInst, LPCSTR psz, INT codepage)
1092 {
1093     HSZ                 hsz = 0;
1094     WDML_INSTANCE*      pInstance;
1095     
1096     TRACE("(%ld,%p,%d)\n", idInst, psz, codepage);
1097     
1098     EnterCriticalSection(&WDML_CritSect);
1099     
1100     pInstance = WDML_GetInstance(idInst);
1101     if (pInstance)
1102     {
1103         if (codepage == 0) codepage = CP_WINANSI;
1104         hsz = WDML_CreateString(pInstance, psz, codepage);
1105     } 
1106
1107     LeaveCriticalSection(&WDML_CritSect);
1108     return hsz;  
1109 }
1110
1111
1112 /******************************************************************************
1113  * DdeCreateStringHandleW [USER32.@]  Creates handle to identify string
1114  *
1115  * PARAMS
1116  *      idInst   [I] Instance identifier
1117  *      psz      [I] Pointer to string
1118  *      codepage [I] Code page identifier
1119  * RETURNS
1120  *    Success: String handle
1121  *    Failure: 0
1122  */
1123 HSZ WINAPI DdeCreateStringHandleW(DWORD idInst, LPCWSTR psz, INT codepage)
1124 {
1125     WDML_INSTANCE*      pInstance;
1126     HSZ                 hsz = 0;
1127     
1128     TRACE("(%ld,%p,%d)\n", idInst, psz, codepage);
1129     
1130     EnterCriticalSection(&WDML_CritSect);
1131     
1132     pInstance = WDML_GetInstance(idInst);
1133     if (pInstance)
1134     {
1135         if (codepage == 0) codepage = CP_WINUNICODE;
1136         hsz = WDML_CreateString(pInstance, psz, codepage);
1137     } 
1138     LeaveCriticalSection(&WDML_CritSect);
1139
1140     return hsz;
1141 }
1142
1143 /*****************************************************************
1144  *            DdeFreeStringHandle   (USER32.@)
1145  * RETURNS: success: nonzero
1146  *          fail:    zero
1147  */
1148 BOOL WINAPI DdeFreeStringHandle(DWORD idInst, HSZ hsz)
1149 {
1150     WDML_INSTANCE*      pInstance;
1151     BOOL                ret = FALSE;
1152
1153     TRACE("(%ld,0x%x): \n", idInst, hsz);
1154     
1155     EnterCriticalSection(&WDML_CritSect);
1156     
1157     /*  First check instance 
1158      */
1159     pInstance = WDML_GetInstance(idInst);
1160     if (pInstance)
1161         ret = WDML_DecHSZ(pInstance, hsz);
1162
1163     LeaveCriticalSection(&WDML_CritSect);
1164
1165     return ret;
1166 }
1167
1168 /*****************************************************************
1169  *            DdeKeepStringHandle  (USER32.@)
1170  *
1171  * RETURNS: success: nonzero
1172  *          fail:    zero
1173  */
1174 BOOL WINAPI DdeKeepStringHandle(DWORD idInst, HSZ hsz)
1175 {
1176     WDML_INSTANCE*      pInstance;
1177     BOOL                ret = FALSE;
1178
1179     TRACE("(%ld,0x%x): \n", idInst, hsz);
1180     
1181     EnterCriticalSection(&WDML_CritSect);
1182     
1183     /*  First check instance
1184      */
1185     pInstance = WDML_GetInstance(idInst);
1186     if (pInstance)
1187         ret = WDML_IncHSZ(pInstance, hsz);
1188
1189     LeaveCriticalSection(&WDML_CritSect);
1190     return ret;
1191 }
1192
1193 /*****************************************************************
1194  *            DdeCmpStringHandles (USER32.@)
1195  *
1196  * Compares the value of two string handles.  This comparison is
1197  * not case sensitive.
1198  *
1199  * Returns:
1200  * -1 The value of hsz1 is zero or less than hsz2
1201  * 0  The values of hsz 1 and 2 are the same or both zero.
1202  * 1  The value of hsz2 is zero of less than hsz1
1203  */
1204 INT WINAPI DdeCmpStringHandles(HSZ hsz1, HSZ hsz2)
1205 {
1206     WCHAR       psz1[MAX_BUFFER_LEN];
1207     WCHAR       psz2[MAX_BUFFER_LEN];
1208     int         ret = 0;
1209     int         ret1, ret2;
1210     
1211     ret1 = GetAtomNameW(hsz1, psz1, MAX_BUFFER_LEN);
1212     ret2 = GetAtomNameW(hsz2, psz2, MAX_BUFFER_LEN);
1213
1214     TRACE("(%x<%s> %x<%s>);\n", hsz1, debugstr_w(psz1), hsz2, debugstr_w(psz2));
1215     
1216     /* Make sure we found both strings. */
1217     if (ret1 == 0 && ret2 == 0)
1218     {
1219         /* If both are not found, return both  "zero strings". */
1220         ret = 0;
1221     }
1222     else if (ret1 == 0)
1223     {
1224         /* If hsz1 is a not found, return hsz1 is "zero string". */
1225         ret = -1;
1226     }
1227     else if (ret2 == 0)
1228     {
1229         /* If hsz2 is a not found, return hsz2 is "zero string". */
1230         ret = 1;
1231     }
1232     else
1233     {
1234         /* Compare the two strings we got (case insensitive). */
1235         ret = lstrcmpiW(psz1, psz2);
1236         /* Since strcmp returns any number smaller than 
1237          * 0 when the first string is found to be less than
1238          * the second one we must make sure we are returning
1239          * the proper values.
1240          */
1241         if (ret < 0)
1242         {
1243             ret = -1;
1244         }
1245         else if (ret > 0)
1246         {
1247             ret = 1;
1248         }
1249     }
1250     
1251     return ret;
1252 }
1253
1254 /* ================================================================
1255  *
1256  *                      Data handle management
1257  *
1258  * ================================================================ */
1259
1260 /*****************************************************************
1261  *            DdeCreateDataHandle (USER32.@)
1262  */
1263 HDDEDATA WINAPI DdeCreateDataHandle(DWORD idInst, LPBYTE pSrc, DWORD cb, 
1264                                     DWORD cbOff, HSZ hszItem, UINT wFmt, 
1265                                     UINT afCmd)
1266 {
1267     /*
1268       For now, we ignore idInst, hszItem, wFmt, and afCmd.
1269       The purpose of these arguments still need to be investigated.
1270     */
1271     
1272     HGLOBAL                     hMem;
1273     LPBYTE                      pByte;
1274     DDE_DATAHANDLE_HEAD*        pDdh;
1275     
1276     TRACE("(%ld,%p,%ld,%ld,0x%lx,%d,%d): semi-stub\n",
1277           idInst,pSrc,cb,cbOff,(DWORD)hszItem,wFmt,afCmd);
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     pDdh->cfFormat = wFmt;
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 (cbOff + cbMax < dwSize)
1352         {
1353             dwRet = cbMax;
1354         }
1355         else if (cbOff < dwSize)
1356         {
1357             dwRet = dwSize - cbOff;
1358         }
1359         else
1360         {
1361             dwRet = 0;
1362         }
1363         if (pDst && dwRet != 0)
1364         {
1365             memcpy(pDst, pByte + cbOff, dwRet);
1366         }
1367         DdeUnaccessData(hData);
1368     }
1369     else
1370     {
1371         dwRet = 0;
1372     }
1373     return dwRet;
1374 }
1375
1376 /*****************************************************************
1377  *            DdeAccessData (USER32.@)
1378  */
1379 LPBYTE WINAPI DdeAccessData(HDDEDATA hData, LPDWORD pcbDataSize)
1380 {
1381     HGLOBAL                     hMem = (HGLOBAL)hData;
1382     DDE_DATAHANDLE_HEAD*        pDdh;
1383     
1384     TRACE("(%08lx,%p)\n", (DWORD)hData, pcbDataSize);
1385     
1386     pDdh = (DDE_DATAHANDLE_HEAD*)GlobalLock(hMem);
1387     if (pDdh == NULL)
1388     {
1389         ERR("Failed on GlobalLock(%04x)\n", hMem);
1390         return 0;
1391     }
1392     
1393     if (pcbDataSize != NULL)
1394     {
1395         *pcbDataSize = GlobalSize(hMem) - sizeof(DDE_DATAHANDLE_HEAD);
1396     }
1397     
1398     return (LPBYTE)(pDdh + 1);
1399 }
1400
1401 /*****************************************************************
1402  *            DdeUnaccessData (USER32.@)
1403  */
1404 BOOL WINAPI DdeUnaccessData(HDDEDATA hData)
1405 {
1406     HGLOBAL hMem = (HGLOBAL)hData;
1407     
1408     TRACE("(0x%lx)\n", (DWORD)hData);
1409     
1410     GlobalUnlock(hMem);
1411     
1412     return TRUE;
1413 }
1414
1415 /*****************************************************************
1416  *            DdeFreeDataHandle   (USER32.@)
1417  */
1418 BOOL WINAPI DdeFreeDataHandle(HDDEDATA hData)
1419 {       
1420     return GlobalFree((HGLOBAL)hData) == 0;
1421 }
1422
1423 /* ================================================================
1424  *
1425  *                  Global <=> Data handle management
1426  *
1427  * ================================================================ */
1428  
1429 /* Note: we use a DDEDATA, but layout of DDEDATA, DDEADVISE and DDEPOKE structures is similar:
1430  *    offset      size 
1431  *    (bytes)    (bits) comment
1432  *      0          16   bit fields for options (release, ackreq, response...)
1433  *      2          16   clipboard format
1434  *      4          ?    data to be used
1435  */
1436 HDDEDATA        WDML_Global2DataHandle(HGLOBAL hMem, WINE_DDEHEAD* p)
1437 {
1438     DDEDATA*    pDd;
1439     HDDEDATA    ret = 0;
1440
1441     if (hMem)
1442     {
1443         pDd = GlobalLock(hMem);
1444         if (pDd)
1445         {
1446             if (p) memcpy(p, pDd, sizeof(WINE_DDEHEAD));
1447             ret = DdeCreateDataHandle(0, pDd->Value,
1448                                       GlobalSize(hMem) - sizeof(WINE_DDEHEAD),
1449                                       0, 0, pDd->cfFormat, 0);
1450             GlobalUnlock(hMem);
1451         }
1452     }
1453     return ret;
1454 }
1455  
1456 /******************************************************************
1457  *              WDML_DataHandle2Global
1458  *
1459  *
1460  */
1461 HGLOBAL WDML_DataHandle2Global(HDDEDATA hDdeData, BOOL fResponse, BOOL fRelease, 
1462                                BOOL fDeferUpd, BOOL fAckReq)
1463 {
1464     DDE_DATAHANDLE_HEAD*        pDdh;
1465     DWORD                       dwSize;
1466     HGLOBAL                     hMem = 0;
1467  
1468     dwSize = GlobalSize(hDdeData) - sizeof(DDE_DATAHANDLE_HEAD);
1469     pDdh = (DDE_DATAHANDLE_HEAD*)GlobalLock(hDdeData);
1470     if (dwSize && pDdh)
1471     {
1472         hMem = GlobalAlloc(sizeof(WINE_DDEHEAD) + dwSize, GMEM_MOVEABLE | GMEM_DDESHARE);
1473         if (hMem)
1474         {
1475             WINE_DDEHEAD*    wdh;
1476  
1477             wdh = GlobalLock(hMem);
1478             if (wdh)
1479             {
1480                 wdh->fResponse = fResponse;
1481                 wdh->fRelease = fRelease;
1482                 wdh->fDeferUpd = fDeferUpd;
1483                 wdh->fAckReq = fAckReq;
1484                 wdh->cfFormat = pDdh->cfFormat;
1485                 memcpy(wdh + 1, pDdh + 1, dwSize);
1486                 GlobalUnlock(hMem);
1487             }
1488         }
1489         GlobalUnlock(hDdeData);
1490     }
1491  
1492     return hMem;
1493 }
1494
1495 /* ================================================================
1496  *
1497  *                      Server management
1498  *
1499  * ================================================================ */
1500
1501 /******************************************************************
1502  *              WDML_AddServer
1503  *
1504  *
1505  */
1506 WDML_SERVER*    WDML_AddServer(WDML_INSTANCE* pInstance, HSZ hszService, HSZ hszTopic)
1507 {
1508     WDML_SERVER*        pServer;
1509     char                buf1[256];
1510     char                buf2[256];
1511     
1512     pServer = (WDML_SERVER*)HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_SERVER));
1513     if (pServer == NULL) return NULL;
1514     
1515     WDML_IncHSZ(pInstance, pServer->hszService = hszService);
1516
1517     DdeQueryStringA(pInstance->instanceID, hszService, buf1, sizeof(buf1), CP_WINANSI);
1518     snprintf(buf2, sizeof(buf2), "%s(0x%08lx)", buf1, GetCurrentProcessId());
1519     pServer->hszServiceSpec = DdeCreateStringHandleA(pInstance->instanceID, buf2, CP_WINANSI);
1520
1521     pServer->atomService = WDML_MakeAtomFromHsz(pServer->hszService);
1522     pServer->atomServiceSpec = WDML_MakeAtomFromHsz(pServer->hszServiceSpec);
1523
1524     pServer->filterOn = TRUE;
1525     
1526     pServer->next = pInstance->servers;
1527     pInstance->servers = pServer;
1528     return pServer;
1529 }
1530
1531 /******************************************************************
1532  *              WDML_RemoveServer
1533  *
1534  *
1535  */
1536 void WDML_RemoveServer(WDML_INSTANCE* pInstance, HSZ hszService, HSZ hszTopic)
1537 {
1538     WDML_SERVER*        pPrev = NULL;
1539     WDML_SERVER*        pServer = NULL;
1540     WDML_CONV*          pConv;
1541     WDML_CONV*          pConvNext;
1542
1543     pServer = pInstance->servers;
1544     
1545     while (pServer != NULL)
1546     {
1547         if (DdeCmpStringHandles(pServer->hszService, hszService) == 0)
1548         {
1549             WDML_BroadcastDDEWindows(WDML_szEventClass, WM_WDML_UNREGISTER, 
1550                                      pServer->atomService, pServer->atomServiceSpec);
1551             /* terminate all conversations for given topic */
1552             for (pConv = pInstance->convs[WDML_SERVER_SIDE]; pConv != NULL; pConv = pConvNext)
1553             {
1554                 pConvNext = pConv->next;
1555                 if (DdeCmpStringHandles(pConv->hszService, hszService) == 0)
1556                 {
1557                     WDML_RemoveConv(pConv, WDML_SERVER_SIDE);
1558                     /* don't care about return code (whether client window is present or not) */
1559                     PostMessageA(pConv->hwndClient, WM_DDE_TERMINATE, (WPARAM)pConv->hwndServer, 0L);
1560                 }
1561             }
1562             if (pServer == pInstance->servers)
1563             {
1564                 pInstance->servers = pServer->next;
1565             }
1566             else
1567             {
1568                 pPrev->next = pServer->next;
1569             }
1570             
1571             DestroyWindow(pServer->hwndServer);
1572             WDML_DecHSZ(pInstance, pServer->hszServiceSpec);
1573             WDML_DecHSZ(pInstance, pServer->hszService);
1574
1575             GlobalDeleteAtom(pServer->atomService);
1576             GlobalDeleteAtom(pServer->atomServiceSpec);
1577
1578             HeapFree(GetProcessHeap(), 0, pServer);
1579             break;
1580         }
1581         
1582         pPrev = pServer;
1583         pServer = pServer->next;
1584     }
1585 }
1586
1587 /*****************************************************************************
1588  *      WDML_FindServer
1589  *
1590  *      generic routine to return a pointer to the relevant ServiceNode
1591  *      for a given service name, or NULL if the entry does not exist
1592  *
1593  */
1594 WDML_SERVER*    WDML_FindServer(WDML_INSTANCE* pInstance, HSZ hszService, HSZ hszTopic)
1595 {
1596     WDML_SERVER*        pServer;
1597     
1598     for (pServer = pInstance->servers; pServer != NULL; pServer = pServer->next)
1599     {
1600         if (hszService == pServer->hszService)
1601         {
1602             return pServer;
1603         }
1604     }
1605     TRACE("Service name missing\n");
1606     return NULL;
1607 }
1608
1609 /* ================================================================
1610  *
1611  *              Conversation management
1612  *
1613  * ================================================================ */
1614
1615 /******************************************************************
1616  *              WDML_AddConv
1617  *
1618  *
1619  */
1620 WDML_CONV*      WDML_AddConv(WDML_INSTANCE* pInstance, WDML_SIDE side,
1621                              HSZ hszService, HSZ hszTopic, HWND hwndClient, HWND hwndServer)
1622 {
1623     WDML_CONV*  pConv;
1624     
1625     /* no converstation yet, add it */
1626     pConv = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_CONV));
1627     if (!pConv) return NULL;
1628
1629     pConv->instance = pInstance;
1630     WDML_IncHSZ(pInstance, pConv->hszService = hszService);
1631     WDML_IncHSZ(pInstance, pConv->hszTopic = hszTopic);
1632     pConv->hwndServer = hwndServer;
1633     pConv->hwndClient = hwndClient;
1634     pConv->transactions = NULL;
1635     pConv->hUser = 0;
1636     pConv->wStatus = (side == WDML_CLIENT_SIDE) ? ST_CLIENT : 0L;
1637     /* check if both side of the conversation are of the same instance */
1638     if (GetWindowThreadProcessId(hwndClient, NULL) == GetWindowThreadProcessId(hwndServer, NULL) &&
1639         WDML_GetInstanceFromWnd(hwndClient) == WDML_GetInstanceFromWnd(hwndServer))
1640     {
1641         pConv->wStatus |= ST_ISSELF;
1642     }
1643     pConv->wConvst = XST_NULL;
1644
1645     pConv->next = pInstance->convs[side];
1646     pInstance->convs[side] = pConv;
1647     
1648     return pConv;
1649 }
1650
1651 /******************************************************************
1652  *              WDML_FindConv
1653  *
1654  *
1655  */
1656 WDML_CONV*      WDML_FindConv(WDML_INSTANCE* pInstance, WDML_SIDE side, 
1657                               HSZ hszService, HSZ hszTopic)
1658 {
1659     WDML_CONV*  pCurrent = NULL;
1660     
1661     for (pCurrent = pInstance->convs[side]; pCurrent != NULL; pCurrent = pCurrent->next)
1662     {
1663         if (DdeCmpStringHandles(pCurrent->hszService, hszService) == 0 &&
1664             DdeCmpStringHandles(pCurrent->hszTopic, hszTopic) == 0)
1665         {
1666             return pCurrent;
1667         }
1668         
1669     }
1670     return NULL;
1671 }
1672
1673 /******************************************************************
1674  *              WDML_RemoveConv
1675  *
1676  *
1677  */
1678 void WDML_RemoveConv(WDML_CONV* pRef, WDML_SIDE side)
1679 {
1680     WDML_CONV*  pPrev = NULL;
1681     WDML_CONV*  pCurrent;
1682     WDML_XACT*  pXAct;
1683     WDML_XACT*  pXActNext;
1684     HWND        hWnd;
1685
1686     if (!pRef)
1687         return;
1688
1689     /* remove any pending transaction */
1690     for (pXAct = pRef->transactions; pXAct != NULL; pXAct = pXActNext)
1691     {
1692         pXActNext = pXAct->next;
1693         WDML_FreeTransaction(pRef->instance, pXAct, TRUE);
1694     }
1695
1696     WDML_RemoveAllLinks(pRef->instance, pRef, side);
1697
1698     /* FIXME: should we keep the window around ? it seems so (at least on client side
1699      * to let QueryConvInfo work after conv termination, but also to implement
1700      * DdeReconnect...
1701      */
1702     /* destroy conversation window, but first remove pConv from hWnd.
1703      * this would help the wndProc do appropriate handling upon a WM_DESTROY message
1704      */
1705     hWnd = (side == WDML_CLIENT_SIDE) ? pRef->hwndClient : pRef->hwndServer;
1706     SetWindowLongA(hWnd, GWL_WDML_CONVERSATION, 0);
1707
1708     DestroyWindow((side == WDML_CLIENT_SIDE) ? pRef->hwndClient : pRef->hwndServer);
1709
1710     WDML_DecHSZ(pRef->instance, pRef->hszService);
1711     WDML_DecHSZ(pRef->instance, pRef->hszTopic);
1712
1713     for (pCurrent = pRef->instance->convs[side]; pCurrent != NULL; pCurrent = (pPrev = pCurrent)->next)
1714     {
1715         if (pCurrent == pRef)
1716         {
1717             if (pCurrent == pRef->instance->convs[side])
1718             {
1719                 pRef->instance->convs[side] = pCurrent->next;
1720             }
1721             else
1722             {
1723                 pPrev->next = pCurrent->next;
1724             }
1725             
1726             HeapFree(GetProcessHeap(), 0, pCurrent);
1727             break;
1728         }
1729     }
1730 }
1731
1732 /*****************************************************************
1733  *            DdeEnableCallback (USER32.@)
1734  */
1735 BOOL WINAPI DdeEnableCallback(DWORD idInst, HCONV hConv, UINT wCmd)
1736 {
1737     FIXME("(%ld, 0x%x, %d) stub\n", idInst, hConv, wCmd);
1738     
1739     return 0;
1740 }
1741
1742 /******************************************************************
1743  *              WDML_GetConv
1744  *
1745  * 
1746  */
1747 WDML_CONV*      WDML_GetConv(HCONV hConv, BOOL checkConnected)
1748 {
1749     WDML_CONV*  pConv = (WDML_CONV*)hConv;
1750
1751     /* FIXME: should do better checking */
1752
1753     if (checkConnected && !(pConv->wStatus & ST_CONNECTED))
1754     {
1755         FIXME("found conv but ain't connected\n");
1756         return NULL;
1757     }
1758     if (GetCurrentThreadId() != pConv->instance->threadID)
1759     {
1760         FIXME("wrong thread ID\n");
1761         return NULL;
1762     }
1763
1764     return pConv;
1765 }
1766
1767 /******************************************************************
1768  *              WDML_GetConvFromWnd
1769  *
1770  *
1771  */
1772 WDML_CONV*      WDML_GetConvFromWnd(HWND hWnd)
1773 {
1774     return (WDML_CONV*)GetWindowLongA(hWnd, GWL_WDML_CONVERSATION);
1775 }
1776
1777 /******************************************************************
1778  *              WDML_PostAck
1779  *
1780  *
1781  */
1782 LPARAM          WDML_PostAck(WDML_CONV* pConv, WDML_SIDE side, WORD appRetCode, 
1783                              BOOL fBusy, BOOL fAck, ATOM atom, LPARAM lParam, UINT oldMsg)
1784 {
1785     DDEACK      ddeAck;
1786     HWND        from, to;
1787
1788     if (side == WDML_SERVER_SIDE)
1789     {
1790         from = pConv->hwndServer;
1791         to   = pConv->hwndClient;
1792     }
1793     else
1794     {
1795         to   = pConv->hwndServer;
1796         from = pConv->hwndClient;
1797     }
1798
1799     ddeAck.bAppReturnCode = appRetCode;
1800     ddeAck.reserved       = 0;
1801     ddeAck.fBusy          = fBusy;
1802     ddeAck.fAck           = fAck;
1803
1804     TRACE("Posting a %s ack\n", ddeAck.fAck ? "positive" : "negative");
1805                     
1806     if (lParam) {
1807         PostMessageA(to, WM_DDE_ACK, (WPARAM)from, 
1808                      ReuseDDElParam(lParam, oldMsg, WM_DDE_ACK, *(WORD*)&ddeAck, atom));
1809     }
1810     else 
1811     {
1812         lParam = PackDDElParam(WM_DDE_ACK, *(WORD*)&ddeAck, atom);
1813         PostMessageA(to, WM_DDE_ACK, (WPARAM)from, lParam);
1814     }
1815     return lParam;
1816 }
1817
1818 /*****************************************************************
1819  *            DdeSetUserHandle (USER32.@)
1820  */
1821 BOOL WINAPI DdeSetUserHandle(HCONV hConv, DWORD id, DWORD hUser)
1822 {
1823     WDML_CONV*  pConv;
1824     BOOL        ret = TRUE;
1825
1826     EnterCriticalSection(&WDML_CritSect);
1827
1828     pConv = WDML_GetConv(hConv, FALSE);
1829     if (pConv == NULL)
1830     {
1831         ret = FALSE;
1832         goto theError;
1833     }
1834     if (id == QID_SYNC) 
1835     {
1836         pConv->hUser = hUser;
1837     }
1838     else
1839     {
1840         WDML_XACT*      pXAct;
1841
1842         pXAct = WDML_FindTransaction(pConv, id);
1843         if (pXAct)
1844         {
1845             pXAct->hUser = hUser;
1846         }
1847         else
1848         {
1849             pConv->instance->lastError = DMLERR_UNFOUND_QUEUE_ID;
1850             ret = FALSE;
1851         }
1852     }
1853  theError:
1854     LeaveCriticalSection(&WDML_CritSect);
1855     return ret;
1856 }
1857
1858 /******************************************************************
1859  *              WDML_GetLocalConvInfo
1860  *
1861  *
1862  */
1863 static  BOOL    WDML_GetLocalConvInfo(WDML_CONV* pConv, CONVINFO* ci, DWORD id)
1864 {
1865     BOOL        ret = TRUE;
1866     WDML_LINK*  pLink;
1867     WDML_SIDE   side;
1868
1869     ci->hConvPartner = (pConv->wStatus & ST_ISLOCAL) ? (HCONV)((DWORD)pConv | 1) : 0;
1870     ci->hszSvcPartner = pConv->hszService;
1871     ci->hszServiceReq = pConv->hszService; /* FIXME: they shouldn't be the same, should they ? */
1872     ci->hszTopic = pConv->hszTopic;
1873     ci->wStatus = pConv->wStatus;
1874
1875     side = (pConv->wStatus & ST_CLIENT) ? WDML_CLIENT_SIDE : WDML_SERVER_SIDE;
1876
1877     for (pLink = pConv->instance->links[side]; pLink != NULL; pLink = pLink->next)
1878     {
1879         if (pLink->hConv == (HCONV)pConv)
1880         {
1881             ci->wStatus |= ST_ADVISE;
1882             break;
1883         }
1884     }
1885
1886     /* FIXME: non handled status flags:
1887        ST_BLOCKED
1888        ST_BLOCKNEXT
1889        ST_INLIST
1890     */
1891
1892     ci->wConvst = pConv->wConvst; /* FIXME */
1893
1894     ci->wLastError = 0; /* FIXME: note it's not the instance last error */
1895     ci->hConvList = 0;
1896     ci->ConvCtxt = pConv->convContext;
1897     if (ci->wStatus & ST_CLIENT)
1898     {
1899         ci->hwnd = pConv->hwndClient;
1900         ci->hwndPartner = pConv->hwndServer;
1901     }
1902     else
1903     {
1904         ci->hwnd = pConv->hwndServer;
1905         ci->hwndPartner = pConv->hwndClient;
1906     }
1907     if (id == QID_SYNC)
1908     {
1909         ci->hUser = pConv->hUser;
1910         ci->hszItem = 0;
1911         ci->wFmt = 0;
1912         ci->wType = 0;
1913     }
1914     else
1915     {
1916         WDML_XACT*      pXAct;
1917
1918         pXAct = WDML_FindTransaction(pConv, id);
1919         if (pXAct)
1920         {
1921             ci->hUser = pXAct->hUser;
1922             ci->hszItem = pXAct->hszItem;
1923             ci->wFmt = pXAct->wFmt;
1924             ci->wType = pXAct->wType;
1925         }
1926         else
1927         {
1928             ret = 0;
1929             pConv->instance->lastError = DMLERR_UNFOUND_QUEUE_ID;
1930         }
1931     }
1932     return ret;
1933 }
1934
1935 /******************************************************************
1936  *              DdeQueryConvInfo (USER32.@)
1937  *
1938  */
1939 UINT WINAPI DdeQueryConvInfo(HCONV hConv, DWORD id, LPCONVINFO lpConvInfo)
1940 {
1941     UINT        ret = lpConvInfo->cb;
1942     CONVINFO    ci;
1943     WDML_CONV*  pConv;
1944
1945     EnterCriticalSection(&WDML_CritSect);
1946
1947     pConv = WDML_GetConv(hConv, FALSE);
1948     if (pConv != NULL && !WDML_GetLocalConvInfo(pConv, &ci, id))
1949     {
1950         ret = 0;
1951     }
1952     else if (hConv & 1)
1953     {
1954         pConv = WDML_GetConv(hConv & ~1, FALSE);
1955         if (pConv != NULL)
1956         {
1957             FIXME("Request on remote conversation information is not implemented yet\n");
1958             ret = 0;
1959         }
1960     }
1961     LeaveCriticalSection(&WDML_CritSect);
1962     if (ret != 0)
1963         memcpy(lpConvInfo, &ci, min(lpConvInfo->cb, sizeof(ci)));
1964     return ret;
1965 }
1966
1967 /* ================================================================
1968  *
1969  *                      Link (hot & warm) management
1970  *
1971  * ================================================================ */
1972
1973 /******************************************************************
1974  *              WDML_AddLink
1975  *
1976  *
1977  */
1978 void WDML_AddLink(WDML_INSTANCE* pInstance, HCONV hConv, WDML_SIDE side, 
1979                   UINT wType, HSZ hszItem, UINT wFmt)
1980 {
1981     WDML_LINK*  pLink;
1982     
1983     pLink = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_LINK));
1984     if (pLink == NULL)
1985     {
1986         ERR("OOM\n");
1987         return;
1988     }
1989
1990     pLink->hConv = hConv;
1991     pLink->transactionType = wType;
1992     WDML_IncHSZ(pInstance, pLink->hszItem = hszItem);
1993     pLink->uFmt = wFmt;
1994     pLink->hDdeData = 0;
1995     pLink->next = pInstance->links[side];
1996     pInstance->links[side] = pLink;
1997 }
1998
1999 /******************************************************************
2000  *              WDML_RemoveLink
2001  *
2002  *
2003  */
2004 void WDML_RemoveLink(WDML_INSTANCE* pInstance, HCONV hConv, WDML_SIDE side, 
2005                      HSZ hszItem, UINT uFmt)
2006 {
2007     WDML_LINK* pPrev = NULL;
2008     WDML_LINK* pCurrent = NULL;
2009     
2010     pCurrent = pInstance->links[side];
2011     
2012     while (pCurrent != NULL)
2013     {
2014         if (pCurrent->hConv == hConv &&
2015             DdeCmpStringHandles(pCurrent->hszItem, hszItem) == 0 &&
2016             pCurrent->uFmt == uFmt)
2017         {
2018             if (pCurrent == pInstance->links[side])
2019             {
2020                 pInstance->links[side] = pCurrent->next;
2021             }
2022             else
2023             {
2024                 pPrev->next = pCurrent->next;
2025             }
2026             
2027             if (pCurrent->hDdeData)
2028             {
2029                 DdeFreeDataHandle(pCurrent->hDdeData);
2030             }
2031
2032             WDML_DecHSZ(pInstance, pCurrent->hszItem);
2033             HeapFree(GetProcessHeap(), 0, pCurrent);
2034             break;
2035         }
2036         
2037         pPrev = pCurrent;
2038         pCurrent = pCurrent->next;
2039     }
2040 }
2041
2042 /* this function is called to remove all links related to the conv.
2043    It should be called from both client and server when terminating 
2044    the conversation.
2045 */
2046 /******************************************************************
2047  *              WDML_RemoveAllLinks
2048  *
2049  *
2050  */
2051 void WDML_RemoveAllLinks(WDML_INSTANCE* pInstance, WDML_CONV* pConv, WDML_SIDE side)
2052 {
2053     WDML_LINK* pPrev = NULL;
2054     WDML_LINK* pCurrent = NULL;
2055     WDML_LINK* pNext = NULL;
2056     
2057     pCurrent = pInstance->links[side];
2058     
2059     while (pCurrent != NULL)
2060     {
2061         if (pCurrent->hConv == (HCONV)pConv)
2062         {
2063             if (pCurrent == pInstance->links[side])
2064             {
2065                 pInstance->links[side] = pCurrent->next;
2066                 pNext = pCurrent->next;
2067             }
2068             else
2069             {
2070                 pPrev->next = pCurrent->next;
2071                 pNext = pCurrent->next;
2072             }
2073             
2074             if (pCurrent->hDdeData)
2075             {
2076                 DdeFreeDataHandle(pCurrent->hDdeData);
2077             }
2078             WDML_DecHSZ(pInstance, pCurrent->hszItem);
2079             
2080             HeapFree(GetProcessHeap(), 0, pCurrent);
2081             pCurrent = NULL;
2082         }
2083         
2084         if (pCurrent)
2085         {
2086             pPrev = pCurrent;
2087             pCurrent = pCurrent->next;
2088         }
2089         else
2090         {
2091             pCurrent = pNext;
2092         }
2093     }
2094 }
2095
2096 /******************************************************************
2097  *              WDML_FindLink
2098  *
2099  *
2100  */
2101 WDML_LINK*      WDML_FindLink(WDML_INSTANCE* pInstance, HCONV hConv, WDML_SIDE side, 
2102                               HSZ hszItem, UINT uFmt)
2103 {
2104     WDML_LINK*  pCurrent = NULL;
2105     
2106     for (pCurrent = pInstance->links[side]; pCurrent != NULL; pCurrent = pCurrent->next)
2107     {
2108         /* we don't need to check for transaction type as it can be altered */
2109         
2110         if (pCurrent->hConv == hConv &&
2111             DdeCmpStringHandles(pCurrent->hszItem, hszItem) == 0 &&
2112             pCurrent->uFmt == uFmt)
2113         {
2114             break;
2115         }
2116         
2117     }
2118     
2119     return pCurrent;
2120 }
2121
2122 /* ================================================================
2123  *
2124  *                      Transaction management
2125  *
2126  * ================================================================ */
2127
2128 /******************************************************************
2129  *              WDML_AllocTransaction
2130  *
2131  * Alloc a transaction structure for handling the message ddeMsg
2132  */
2133 WDML_XACT*      WDML_AllocTransaction(WDML_INSTANCE* pInstance, UINT ddeMsg,
2134                                       UINT wFmt, HSZ hszItem)
2135 {
2136     WDML_XACT*          pXAct;
2137     static WORD         tid = 1;        /* FIXME: wrap around */
2138
2139     pXAct = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_XACT));
2140     if (!pXAct) 
2141     {
2142         pInstance->lastError = DMLERR_MEMORY_ERROR;
2143         return NULL;
2144     }
2145
2146     pXAct->xActID = tid++;
2147     pXAct->ddeMsg = ddeMsg;
2148     pXAct->hDdeData = 0;
2149     pXAct->hUser = 0;
2150     pXAct->next = NULL;
2151     pXAct->wType = 0;
2152     pXAct->wFmt = wFmt;
2153     WDML_IncHSZ(pInstance, pXAct->hszItem = hszItem);
2154     pXAct->atom = 0;
2155     pXAct->hMem = 0;
2156     pXAct->lParam = 0;
2157
2158     return pXAct;
2159 }
2160
2161 /******************************************************************
2162  *              WDML_QueueTransaction
2163  *
2164  * Adds a transaction to the list of transaction
2165  */
2166 void    WDML_QueueTransaction(WDML_CONV* pConv, WDML_XACT* pXAct)
2167 {
2168     WDML_XACT** pt;
2169     
2170     /* advance to last in queue */
2171     for (pt = &pConv->transactions; *pt != NULL; pt = &(*pt)->next);
2172     *pt = pXAct;
2173 }
2174
2175 /******************************************************************
2176  *              WDML_UnQueueTransaction
2177  *
2178  *
2179  */
2180 BOOL    WDML_UnQueueTransaction(WDML_CONV* pConv, WDML_XACT*  pXAct)
2181 {
2182     WDML_XACT** pt;
2183
2184     for (pt = &pConv->transactions; *pt; pt = &(*pt)->next)
2185     {
2186         if (*pt == pXAct)
2187         {
2188             *pt = pXAct->next;
2189             return TRUE;
2190         }
2191     }
2192     return FALSE;
2193 }
2194
2195 /******************************************************************
2196  *              WDML_FreeTransaction
2197  *
2198  *
2199  */
2200 void    WDML_FreeTransaction(WDML_INSTANCE* pInstance, WDML_XACT* pXAct, BOOL doFreePmt)
2201 {
2202     /* free pmt(s) in pXAct too */
2203     if (doFreePmt && pXAct->hMem)
2204         GlobalFree(pXAct->hMem);
2205     WDML_DecHSZ(pInstance, pXAct->hszItem);
2206
2207     HeapFree(GetProcessHeap(), 0, pXAct);
2208 }
2209
2210 /******************************************************************
2211  *              WDML_FindTransaction
2212  *
2213  *
2214  */
2215 WDML_XACT*      WDML_FindTransaction(WDML_CONV* pConv, DWORD tid)
2216 {
2217     WDML_XACT* pXAct;
2218     
2219     tid = HIWORD(tid);
2220     for (pXAct = pConv->transactions; pXAct; pXAct = pXAct->next)
2221     {
2222         if (pXAct->xActID == tid)
2223             break;
2224     }
2225     return pXAct;
2226 }
2227
2228 /* ================================================================
2229  *
2230  *         Information broadcast across DDEML implementations
2231  *
2232  * ================================================================ */
2233
2234 struct tagWDML_BroadcastPmt
2235 {
2236     LPCSTR      clsName;
2237     UINT        uMsg;
2238     WPARAM      wParam;
2239     LPARAM      lParam;
2240 };
2241
2242 /******************************************************************
2243  *              WDML_BroadcastEnumProc
2244  *
2245  *
2246  */
2247 static  BOOL CALLBACK WDML_BroadcastEnumProc(HWND hWnd, LPARAM lParam)
2248 {
2249     struct tagWDML_BroadcastPmt*        s = (struct tagWDML_BroadcastPmt*)lParam;
2250     char                                buffer[128];
2251
2252     if (GetClassNameA(hWnd, buffer, sizeof(buffer)) > 0 &&
2253         strcmp(buffer, s->clsName) == 0)
2254     {
2255         PostMessageA(hWnd, s->uMsg, s->wParam, s->lParam);
2256     }
2257     return TRUE;
2258 }
2259
2260 /******************************************************************
2261  *              WDML_BroadcastDDEWindows
2262  *
2263  *
2264  */
2265 void WDML_BroadcastDDEWindows(const char* clsName, UINT uMsg, WPARAM wParam, LPARAM lParam)
2266 {
2267     struct tagWDML_BroadcastPmt s;
2268
2269     s.clsName = clsName;
2270     s.uMsg    = uMsg;
2271     s.wParam  = wParam;
2272     s.lParam  = lParam;
2273     EnumWindows(WDML_BroadcastEnumProc, (LPARAM)&s);
2274 }