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