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