- implemented correctly the HSZ as local atoms and added the needed
[wine] / dlls / user / dde / client.c
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2
3 /*
4  * DDEML library
5  *
6  * Copyright 1997 Alexandre Julliard
7  * Copyright 1997 Len White
8  * Copyright 1999 Keith Matthews
9  * Copyright 2000 Corel
10  * Copyright 2001 Eric Pouech
11  */
12
13 #include <string.h>
14 #include "winbase.h"
15 #include "windef.h"
16 #include "wingdi.h"
17 #include "winuser.h"
18 #include "winerror.h"
19 #include "winnls.h"
20 #include "dde.h"
21 #include "ddeml.h"
22 #include "debugtools.h"
23 #include "dde/dde_private.h"
24
25 DEFAULT_DEBUG_CHANNEL(ddeml);
26
27 static LRESULT CALLBACK WDML_ClientProc(HWND, UINT, WPARAM, LPARAM);    /* only for one client, not conv list */
28 const char  WDML_szClientConvClassA[] = "DdeClientAnsi";
29 const WCHAR WDML_szClientConvClassW[] = {'D','d','e','C','l','i','e','n','t','U','n','i','c','o','d','e',0};
30
31 /******************************************************************************
32  * DdeConnectList [USER32.@]  Establishes conversation with DDE servers
33  *
34  * PARAMS
35  *    idInst     [I] Instance identifier
36  *    hszService [I] Handle to service name string
37  *    hszTopic   [I] Handle to topic name string
38  *    hConvList  [I] Handle to conversation list
39  *    pCC        [I] Pointer to structure with context data
40  *
41  * RETURNS
42  *    Success: Handle to new conversation list
43  *    Failure: 0
44  */
45 HCONVLIST WINAPI DdeConnectList(DWORD idInst, HSZ hszService, HSZ hszTopic,
46                                 HCONVLIST hConvList, LPCONVCONTEXT pCC)
47 {
48     FIXME("(%ld,%d,%d,%d,%p): stub\n", idInst, hszService, hszTopic,
49           hConvList, pCC);
50     return (HCONVLIST)1;
51 }
52
53 /*****************************************************************
54  * DdeQueryNextServer [USER32.@]
55  */
56 HCONV WINAPI DdeQueryNextServer(HCONVLIST hConvList, HCONV hConvPrev)
57 {
58     FIXME("(%d,%d): stub\n", hConvList, hConvPrev);
59     return 0;
60 }
61
62 /******************************************************************************
63  * DdeDisconnectList [USER32.@]  Destroys list and terminates conversations
64  *
65  *
66  * PARAMS
67  *    hConvList  [I] Handle to conversation list
68  *
69  * RETURNS
70  *    Success: TRUE
71  *    Failure: FALSE
72  */
73 BOOL WINAPI DdeDisconnectList(HCONVLIST hConvList)
74 {
75     FIXME("(%d): stub\n", hConvList);
76     return TRUE;
77 }
78
79 /*****************************************************************
80  *            DdeConnect   (USER32.@)
81  */
82 HCONV WINAPI DdeConnect(DWORD idInst, HSZ hszService, HSZ hszTopic,
83                         LPCONVCONTEXT pCC)
84 {
85     HWND                hwndClient;
86     LPARAM              lParam = 0;
87     WDML_INSTANCE*      pInstance;
88     WDML_CONV*          pConv = NULL;
89     ATOM                aSrv = 0, aTpc = 0;
90     
91     TRACE("(0x%lx,0x%x,0x%x,%p)\n", idInst, hszService, hszTopic, pCC);
92
93     EnterCriticalSection(&WDML_CritSect);
94
95     pInstance = WDML_GetInstance(idInst);
96     if (!pInstance)
97     {
98         goto theEnd;
99     }
100     
101     /* make sure this conv is never created */
102     pConv = WDML_FindConv(pInstance, WDML_CLIENT_SIDE, hszService, hszTopic);
103     if (pConv != NULL)
104     {
105         ERR("This Conv already exists: (0x%lx)\n", (DWORD)pConv);
106         goto theEnd;
107     }
108     
109     /* we need to establish a conversation with
110        server, so create a window for it       */
111     
112     if (pInstance->unicode)
113     {
114         WNDCLASSEXW     wndclass;
115
116         wndclass.cbSize        = sizeof(wndclass);
117         wndclass.style         = 0;
118         wndclass.lpfnWndProc   = WDML_ClientProc;
119         wndclass.cbClsExtra    = 0;
120         wndclass.cbWndExtra    = 2 * sizeof(DWORD);
121         wndclass.hInstance     = 0;
122         wndclass.hIcon         = 0;
123         wndclass.hCursor       = 0;
124         wndclass.hbrBackground = 0;
125         wndclass.lpszMenuName  = NULL;
126         wndclass.lpszClassName = WDML_szClientConvClassW;
127         wndclass.hIconSm       = 0;
128         
129         RegisterClassExW(&wndclass);
130     
131         hwndClient = CreateWindowW(WDML_szClientConvClassW, NULL, WS_POPUP, 0, 0, 0, 0, 0, 0, 0, 0);
132     }
133     else
134     {
135         WNDCLASSEXA     wndclass;
136
137         wndclass.cbSize        = sizeof(wndclass);
138         wndclass.style         = 0;
139         wndclass.lpfnWndProc   = WDML_ClientProc;
140         wndclass.cbClsExtra    = 0;
141         wndclass.cbWndExtra    = 2 * sizeof(DWORD);
142         wndclass.hInstance     = 0;
143         wndclass.hIcon         = 0;
144         wndclass.hCursor       = 0;
145         wndclass.hbrBackground = 0;
146         wndclass.lpszMenuName  = NULL;
147         wndclass.lpszClassName = WDML_szClientConvClassA;
148         wndclass.hIconSm       = 0;
149         
150         RegisterClassExA(&wndclass);
151     
152         hwndClient = CreateWindowA(WDML_szClientConvClassA, NULL, WS_POPUP, 0, 0, 0, 0, 0, 0, 0, 0);
153     }
154
155     SetWindowLongA(hwndClient, GWL_WDML_INSTANCE, (DWORD)pInstance);
156     
157     if (hszService)
158     {
159         aSrv = WDML_MakeAtomFromHsz(hszService);
160         if (!aSrv) goto theEnd;
161     }
162     if (hszTopic)
163     {
164         aTpc = WDML_MakeAtomFromHsz(hszTopic);
165         if (!aTpc) goto theEnd;
166     }
167
168     LeaveCriticalSection(&WDML_CritSect);
169
170     lParam = PackDDElParam(WM_DDE_INITIATE, aSrv, aTpc);
171     SendMessageA(HWND_BROADCAST, WM_DDE_INITIATE, (WPARAM)hwndClient, lParam);
172     FreeDDElParam(WM_DDE_INITIATE, lParam);
173     
174     EnterCriticalSection(&WDML_CritSect);
175
176     pInstance = WDML_GetInstance(idInst);
177     if (!pInstance)
178     {
179         goto theEnd;
180     }
181     
182     TRACE("WM_DDE_INITIATE was processed\n");
183     /* At this point, Client WM_DDE_ACK should have saved hwndServer
184        for this instance id and hwndClient if server responds.
185        So get HCONV and return it. And add it to conv list */
186     pConv = WDML_GetConvFromWnd(hwndClient);
187     if (pConv == NULL || pConv->hwndServer == 0)
188     {
189         ERR(".. but no Server window available\n");
190         pConv = NULL;
191         goto theEnd;
192     }
193     pConv->wConvst = XST_CONNECTED;
194
195     /* finish init of pConv */
196     if (pCC != NULL)
197     {
198         pConv->convContext = *pCC;
199     }
200     else
201     {
202         memset(&pConv->convContext, 0, sizeof(pConv->convContext));
203         pConv->convContext.cb = sizeof(pConv->convContext);
204         pConv->convContext.iCodePage = (pInstance->unicode) ? CP_WINUNICODE : CP_WINANSI;
205     }
206
207  theEnd:
208     LeaveCriticalSection(&WDML_CritSect);
209
210     if (aSrv) GlobalDeleteAtom(aSrv);
211     if (aTpc) GlobalDeleteAtom(aTpc);
212     return (HCONV)pConv;
213 }
214
215 /*****************************************************************
216  *            DdeReconnect   (DDEML.37)
217  *            DdeReconnect   (USER32.@)
218  */
219 HCONV WINAPI DdeReconnect(HCONV hConv)
220 {
221     WDML_CONV*  pConv;
222     WDML_CONV*  pNewConv = NULL;
223     ATOM        aSrv = 0, aTpc = 0;
224
225     EnterCriticalSection(&WDML_CritSect);
226     pConv = WDML_GetConv(hConv, FALSE);
227     if (pConv != NULL && (pConv->wStatus & ST_CLIENT))
228     {
229         LPARAM  lParam;
230         BOOL    ret;
231
232         /* to reestablist a connection, we have to make sure that:
233          * 1/ pConv is the converstation attached to the client window (it wouldn't be
234          *    if a call to DdeReconnect would have already been done...) 
235          *    FIXME: is this really an error ???
236          * 2/ the pConv conversation had really been deconnected
237          */
238         if (pConv == WDML_GetConvFromWnd(pConv->hwndClient) &&
239             (pConv->wStatus & ST_TERMINATED) && !(pConv->wStatus & ST_CONNECTED))
240         {
241             HWND        hwndClient = pConv->hwndClient;
242             HWND        hwndServer = pConv->hwndServer;
243             ATOM        aSrv, aTpc;
244
245             SetWindowLongA(pConv->hwndClient, GWL_WDML_CONVERSATION, 0);
246
247             aSrv = WDML_MakeAtomFromHsz(pConv->hszService);
248             aTpc = WDML_MakeAtomFromHsz(pConv->hszTopic);
249             if (!aSrv || !aTpc) goto theEnd;
250
251             LeaveCriticalSection(&WDML_CritSect);
252
253             lParam = PackDDElParam(WM_DDE_INITIATE, aSrv, aTpc);
254             ret = SendMessageA(hwndServer, WM_DDE_INITIATE, (WPARAM)hwndClient, lParam);
255             FreeDDElParam(WM_DDE_INITIATE, lParam);
256             
257             EnterCriticalSection(&WDML_CritSect);
258
259             pConv = WDML_GetConv(hConv, FALSE);
260             if (pConv == NULL)
261             {
262                 FIXME("Should fail reconnection\n");
263                 goto theEnd;
264             }
265
266             if (ret && (pNewConv = WDML_GetConvFromWnd(pConv->hwndClient)) != NULL)
267             {
268                 /* re-establish all links... */
269                 WDML_LINK* pLink;
270
271                 for (pLink = pConv->instance->links[WDML_CLIENT_SIDE]; pLink; pLink = pLink->next)
272                 {
273                     if (pLink->hConv == hConv)
274                     {
275                         /* try to reestablish the links... */
276                         DdeClientTransaction(NULL, 0, (HCONV)pNewConv, pLink->hszItem, pLink->uFmt, 
277                                              pLink->transactionType, 1000, NULL);
278                     }
279                 }
280             }
281             else
282             {
283                 /* reset the conversation as it was */
284                 SetWindowLongA(pConv->hwndClient, GWL_WDML_CONVERSATION, (DWORD)pConv);
285             }
286         }
287     }
288
289  theEnd:
290     LeaveCriticalSection(&WDML_CritSect);
291
292     if (aSrv) GlobalDeleteAtom(aSrv);
293     if (aTpc) GlobalDeleteAtom(aTpc);
294     return (HCONV)pNewConv;
295 }
296
297 /******************************************************************
298  *              WDML_ClientQueueAdvise
299  *
300  * Creates and queue an WM_DDE_ADVISE transaction
301  */
302 static WDML_XACT*       WDML_ClientQueueAdvise(WDML_CONV* pConv, UINT wType, UINT wFmt, HSZ hszItem)
303 {
304     DDEADVISE*          pDdeAdvise;
305     WDML_XACT*          pXAct;
306     ATOM                atom;
307
308     TRACE("XTYP_ADVSTART (with%s data) transaction\n", (wType & XTYPF_NODATA) ? "out" : "");
309
310     atom = WDML_MakeAtomFromHsz(hszItem);
311     if (!atom) return NULL;
312      
313     pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_ADVISE, wFmt, hszItem);
314     if (!pXAct)
315     {
316         GlobalDeleteAtom(atom);
317         return NULL;
318     }
319
320     pXAct->wType = wType & ~0x0F;
321     pXAct->hMem = GlobalAlloc(GHND | GMEM_DDESHARE, sizeof(DDEADVISE));
322     
323     /* pack DdeAdvise   */
324     pDdeAdvise = (DDEADVISE*)GlobalLock(pXAct->hMem);
325     pDdeAdvise->fAckReq   = (wType & XTYPF_ACKREQ) ? TRUE : FALSE;
326     pDdeAdvise->fDeferUpd = (wType & XTYPF_NODATA) ? TRUE : FALSE;
327     pDdeAdvise->cfFormat  = wFmt;
328     GlobalUnlock(pXAct->hMem);
329
330     pXAct->lParam = PackDDElParam(WM_DDE_ADVISE, (UINT)pXAct->hMem, atom);
331
332     return pXAct;
333 }
334
335 /******************************************************************
336  *              WDML_HandleAdviseReply
337  *
338  * handles the reply to an advise request
339  */
340 static WDML_QUEUE_STATE WDML_HandleAdviseReply(WDML_CONV* pConv, MSG* msg, WDML_XACT* pXAct)
341 {
342     DDEACK              ddeAck;
343     UINT                uiLo, uiHi;
344     HSZ                 hsz;
345
346     if (msg->message != WM_DDE_ACK || msg->wParam != pConv->hwndServer)
347     {
348         return WDML_QS_PASS;
349     }
350
351     UnpackDDElParam(WM_DDE_ACK, msg->lParam, &uiLo, &uiHi);
352     hsz = WDML_MakeHszFromAtom(pConv->instance, uiHi);
353
354     if (DdeCmpStringHandles(hsz, pXAct->hszItem) != 0)
355         return WDML_QS_PASS;
356
357     GlobalDeleteAtom(uiHi);
358     FreeDDElParam(WM_DDE_ACK, msg->lParam);
359
360     WDML_ExtractAck(uiLo, &ddeAck);
361             
362     if (ddeAck.fAck)
363     {
364         WDML_LINK*      pLink;
365         
366         /* billx: first to see if the link is already created. */
367         pLink = WDML_FindLink(pConv->instance, (HCONV)pConv, WDML_CLIENT_SIDE, 
368                               pXAct->hszItem, pXAct->wFmt);
369         if (pLink != NULL)      
370         {       
371             /* we found a link, and only need to modify it in case it changes */
372             pLink->transactionType = pXAct->wType;
373         }
374         else
375         {
376             WDML_AddLink(pConv->instance, (HCONV)pConv, WDML_CLIENT_SIDE, 
377                          pXAct->wType, pXAct->hszItem, pXAct->wFmt);
378         }
379     }
380     else
381     {
382         TRACE("Returning TRUE on XTYP_ADVSTART - fAck was FALSE\n");
383         GlobalFree(pXAct->hMem);
384     }
385
386     pXAct->hDdeData = (HDDEDATA)1;
387     return WDML_QS_HANDLED;
388 }
389
390 /******************************************************************
391  *              WDML_ClientQueueUnadvise
392  *
393  * queues an unadvise transaction
394  */
395 static WDML_XACT*       WDML_ClientQueueUnadvise(WDML_CONV* pConv, UINT wFmt, HSZ hszItem)
396 {
397     WDML_XACT*  pXAct;
398     ATOM        atom;
399     
400     TRACE("XTYP_ADVSTOP transaction\n");
401
402     atom = WDML_MakeAtomFromHsz(hszItem);
403     if (!atom) return NULL;
404
405     pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_UNADVISE, wFmt, hszItem);
406     if (!pXAct)
407     {
408         GlobalDeleteAtom(atom);
409         return NULL;
410     }
411
412     /* end advise loop: post WM_DDE_UNADVISE to server to terminate link
413      * on the specified item. 
414      */
415     pXAct->lParam = PackDDElParam(WM_DDE_UNADVISE, wFmt, atom);
416     return pXAct;
417 }
418     
419 /******************************************************************
420  *              WDML_HandleUnadviseReply
421  *
422  *
423  */
424 static WDML_QUEUE_STATE WDML_HandleUnadviseReply(WDML_CONV* pConv, MSG* msg, WDML_XACT* pXAct)
425 {
426     DDEACK      ddeAck;
427     UINT        uiLo, uiHi;
428     HSZ         hsz;
429
430     if (msg->message != WM_DDE_ACK || msg->wParam != pConv->hwndServer)
431     {
432         return WDML_QS_PASS;
433     }
434
435     UnpackDDElParam(WM_DDE_ACK, msg->lParam, &uiLo, &uiHi);
436     hsz = WDML_MakeHszFromAtom(pConv->instance, uiHi);
437
438     if (DdeCmpStringHandles(hsz, pXAct->hszItem) != 0)
439         return WDML_QS_PASS;
440
441     FreeDDElParam(WM_DDE_ACK, msg->lParam);
442     GlobalDeleteAtom(uiHi);
443
444     WDML_ExtractAck(uiLo, &ddeAck);
445                     
446     TRACE("WM_DDE_ACK received while waiting for a timeout\n");
447             
448     if (!ddeAck.fAck)
449     {
450         TRACE("Returning TRUE on XTYP_ADVSTOP - fAck was FALSE\n");
451     }
452     else
453     {
454         /* billx: remove the link */
455         WDML_RemoveLink(pConv->instance, (HCONV)pConv, WDML_CLIENT_SIDE, 
456                         pXAct->hszItem, pXAct->wFmt);
457     }
458     pXAct->hDdeData = (HDDEDATA)1;
459     return WDML_QS_HANDLED;
460 }
461
462 /******************************************************************
463  *              WDML_ClientQueueRequest
464  *
465  *
466  */
467 static WDML_XACT*       WDML_ClientQueueRequest(WDML_CONV* pConv, UINT wFmt, HSZ hszItem)
468 {
469     WDML_XACT*  pXAct;
470     ATOM        atom;
471
472     TRACE("XTYP_REQUEST transaction\n");
473
474     atom = WDML_MakeAtomFromHsz(hszItem);
475     if (!atom) return NULL;
476
477     pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_REQUEST, wFmt, hszItem);
478     if (!pXAct)
479     {
480         GlobalDeleteAtom(atom);
481         return NULL;
482     }
483
484     pXAct->lParam = PackDDElParam(WM_DDE_REQUEST, wFmt, atom);
485
486     return pXAct;
487 }
488
489 /******************************************************************
490  *              WDML_HandleRequestReply
491  *
492  *
493  */
494 static WDML_QUEUE_STATE WDML_HandleRequestReply(WDML_CONV* pConv, MSG* msg, WDML_XACT* pXAct)
495 {
496     DDEACK              ddeAck;
497     WINE_DDEHEAD        wdh;
498     UINT                uiLo, uiHi;
499     HSZ                 hsz;
500
501     if (msg->wParam != pConv->hwndServer)
502         return WDML_QS_PASS;
503     UnpackDDElParam(WM_DDE_ACK, msg->lParam, &uiLo, &uiHi);
504
505     switch (msg->message)
506     {
507     case WM_DDE_ACK:
508         GlobalDeleteAtom(uiHi);
509         WDML_ExtractAck(uiLo, &ddeAck);
510         pXAct->hDdeData = 0;
511         if (ddeAck.fAck)
512             ERR("Positive answer should appear in NACK for a request, assuming negative\n");
513         TRACE("Negative answer...\n");
514         break;
515
516     case WM_DDE_DATA:
517         TRACE("Got the result (%08lx)\n", (DWORD)uiLo);
518
519         hsz = WDML_MakeHszFromAtom(pConv->instance, uiHi);
520
521         if (DdeCmpStringHandles(hsz, pXAct->hszItem) != 0)
522             return WDML_QS_PASS;
523
524         /* FIXME: memory clean up ? */
525         pXAct->hDdeData = WDML_Global2DataHandle((HGLOBAL)uiLo, &wdh);
526         if (wdh.fRelease)
527         {
528             GlobalFree((HGLOBAL)uiLo);
529         }
530         if (wdh.fAckReq)
531         {
532             WDML_PostAck(pConv, WDML_CLIENT_SIDE, 0, FALSE, TRUE, (HSZ)uiHi, msg->lParam, 
533                          WM_DDE_DATA);
534         }
535         else
536         {
537             GlobalDeleteAtom(uiHi);
538         }
539         break;
540
541     default:
542         return WDML_QS_PASS;
543     }
544
545     return WDML_QS_HANDLED;
546 }       
547
548 /******************************************************************
549  *              WDML_BuildExecuteCommand
550  *
551  * Creates a DDE block suitable for sending in WM_DDE_COMMAND
552  * It also takes care of string conversion between the two window procedures
553  */
554 static  HGLOBAL WDML_BuildExecuteCommand(WDML_CONV* pConv, LPCVOID pData, DWORD cbData)
555 {
556     HGLOBAL     hMem;
557     BOOL        clientUnicode, serverUnicode;
558     DWORD       memSize;
559
560     clientUnicode = IsWindowUnicode(pConv->hwndClient);
561     serverUnicode = IsWindowUnicode(pConv->hwndServer);
562
563     if (clientUnicode == serverUnicode)
564     {
565         memSize = cbData;
566     }
567     else
568     {
569         if (clientUnicode)
570         {
571             memSize = WideCharToMultiByte( CP_ACP, 0, pData, cbData, NULL, 0, NULL, NULL);
572         }
573         else
574         {
575             memSize = MultiByteToWideChar( CP_ACP, 0, pData, cbData, NULL, 0);
576         }
577     }
578
579     hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, memSize);
580
581     if (hMem)
582     {
583         LPBYTE  pDst;
584             
585         pDst = GlobalLock(hMem);
586         if (pDst)
587         {
588             if (clientUnicode == serverUnicode)
589             {
590                 memcpy(pDst, pData, cbData);
591             }
592             else
593             {
594                 if (clientUnicode)
595                 {
596                     WideCharToMultiByte( CP_ACP, 0, pData, cbData, pDst, memSize, NULL, NULL);
597                 }
598                 else
599                 {
600                     MultiByteToWideChar( CP_ACP, 0, pData, cbData, (LPWSTR)pDst, memSize);
601                 }
602             }
603
604             GlobalUnlock(hMem);
605         }
606         else
607         {
608             GlobalFree(hMem);
609             hMem = 0;
610         }
611     }
612     return hMem;
613 }
614
615 /******************************************************************
616  *              WDML_ClientQueueExecute
617  *
618  *
619  */
620 static WDML_XACT*       WDML_ClientQueueExecute(WDML_CONV* pConv, LPCVOID pData, DWORD cbData)
621 {
622     WDML_XACT*  pXAct;
623
624     TRACE("XTYP_EXECUTE transaction\n");
625         
626     pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_EXECUTE, 0, 0);
627     if (!pXAct)
628         return NULL;
629
630     if (cbData == (DWORD)-1)
631     {
632         HDDEDATA        hDdeData = (HDDEDATA)pData;
633     
634         pData = DdeAccessData(hDdeData, &cbData);
635         if (pData)
636         {
637             pXAct->hMem = WDML_BuildExecuteCommand(pConv, pData, cbData);
638             DdeUnaccessData(hDdeData);
639         }
640     }
641     else
642     {
643         pXAct->hMem = WDML_BuildExecuteCommand(pConv, pData, cbData);
644     }
645
646     pXAct->lParam = pXAct->hMem;
647
648     return pXAct;
649 }
650
651 /******************************************************************
652  *              WDML_HandleExecuteReply
653  *
654  *
655  */
656 static WDML_QUEUE_STATE WDML_HandleExecuteReply(WDML_CONV* pConv, MSG* msg, WDML_XACT* pXAct)
657 {
658     DDEACK      ddeAck;
659     UINT        uiLo, uiHi;
660
661     if (msg->message != WM_DDE_ACK || msg->wParam != pConv->hwndServer)
662     {
663         return WDML_QS_PASS;
664     }
665
666     UnpackDDElParam(WM_DDE_ACK, msg->lParam, &uiLo, &uiHi);
667     FreeDDElParam(WM_DDE_ACK, msg->lParam);
668
669     if (uiHi != pXAct->hMem)
670     {
671         return WDML_QS_PASS;
672     }
673
674     WDML_ExtractAck(uiLo, &ddeAck);
675     if (!ddeAck.fAck)
676     {
677         GlobalFree(pXAct->hMem);
678     }
679     pXAct->hDdeData = (HDDEDATA)1;
680     return WDML_QS_HANDLED;
681 }
682
683 /******************************************************************
684  *              WDML_ClientQueuePoke
685  *
686  *
687  */
688 static WDML_XACT*       WDML_ClientQueuePoke(WDML_CONV* pConv, LPCVOID pData, DWORD cbData, 
689                                              UINT wFmt, HSZ hszItem)
690 {
691     WDML_XACT*  pXAct;
692     ATOM        atom;
693
694     TRACE("XTYP_POKE transaction\n");
695         
696     atom = WDML_MakeAtomFromHsz(hszItem);
697     if (!atom) return NULL;
698      
699     pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_POKE, wFmt, hszItem);
700     if (!pXAct)
701     {
702         GlobalDeleteAtom(atom);
703         return NULL;
704     }
705
706     if (cbData == (DWORD)-1)
707     {
708         pXAct->hMem = (HDDEDATA)pData;
709     }
710     else
711     {
712         DDEPOKE*        ddePoke;
713
714         pXAct->hMem = GlobalAlloc(GHND | GMEM_DDESHARE, sizeof(DDEPOKE) + cbData);
715         ddePoke = GlobalLock(pXAct->hMem);
716         if (ddePoke) 
717         {
718             memcpy(ddePoke->Value, pData, cbData);
719             ddePoke->fRelease = FALSE; /* FIXME: app owned ? */
720             ddePoke->cfFormat = wFmt;
721             GlobalUnlock(pXAct->hMem);
722         }
723     }
724
725     pXAct->lParam = PackDDElParam(WM_DDE_POKE, pXAct->hMem, atom);
726
727     return pXAct;
728 }
729
730 /******************************************************************
731  *              WDML_HandlePokeReply
732  *
733  *
734  */
735 static WDML_QUEUE_STATE WDML_HandlePokeReply(WDML_CONV* pConv, MSG* msg, WDML_XACT* pXAct)
736 {
737     DDEACK      ddeAck;
738     UINT        uiLo, uiHi;
739     HSZ         hsz;
740
741     if (msg->message != WM_DDE_ACK && msg->wParam != pConv->hwndServer)
742     {
743         return WDML_QS_PASS;
744     }
745
746     UnpackDDElParam(WM_DDE_ACK, msg->lParam, &uiLo, &uiHi);
747     hsz = WDML_MakeHszFromAtom(pConv->instance, uiHi);
748     if (DdeCmpStringHandles(hsz, pXAct->hszItem) != 0)
749     {
750         return WDML_QS_PASS;
751     }
752     FreeDDElParam(WM_DDE_ACK, msg->lParam);
753     GlobalDeleteAtom(uiHi);
754
755     WDML_ExtractAck(uiLo, &ddeAck);
756     GlobalFree(pXAct->hMem);
757
758     pXAct->hDdeData = (HDDEDATA)TRUE;
759     return TRUE;
760 }
761
762 /******************************************************************
763  *              WDML_ClientQueueTerminate
764  *
765  * Creates and queue an WM_DDE_TERMINATE transaction
766  */
767 static WDML_XACT*       WDML_ClientQueueTerminate(WDML_CONV* pConv)
768 {
769     WDML_XACT*          pXAct;
770
771     pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_TERMINATE, 0, 0);
772     if (!pXAct)
773         return NULL;
774
775     pXAct->lParam = 0;
776     pConv->wStatus &= ~ST_CONNECTED;
777
778     return pXAct;
779 }
780
781 /******************************************************************
782  *              WDML_HandleTerminateReply
783  *
784  * handles the reply to a terminate request
785  */
786 static WDML_QUEUE_STATE WDML_HandleTerminateReply(WDML_CONV* pConv, MSG* msg, WDML_XACT* pXAct)
787 {
788     if (msg->message != WM_DDE_TERMINATE)
789     {
790         /* FIXME: should delete data passed here */
791         return WDML_QS_SWALLOWED;
792     }
793
794     if (msg->wParam != pConv->hwndServer)
795     {
796         FIXME("hmmm shouldn't happen\n");
797         return WDML_QS_PASS;
798     }
799     if (!pConv->instance->CBFflags & CBF_SKIP_DISCONNECTS)
800     {
801         WDML_InvokeCallback(pConv->instance, XTYP_DISCONNECT, 0, (HCONV)pConv, 
802                             0, 0, 0, 0, (pConv->wStatus & ST_ISSELF) ? 1 : 0);
803     }
804     WDML_RemoveConv(pConv, WDML_CLIENT_SIDE);
805     return WDML_QS_HANDLED;
806 }
807
808 /******************************************************************
809  *              WDML_HandleReplyData
810  *
811  *
812  */
813 static WDML_QUEUE_STATE WDML_HandleIncomingData(WDML_CONV* pConv, MSG* msg, HDDEDATA* hdd)
814 {
815     UINT                uiLo, uiHi;
816     HDDEDATA            hDdeDataIn, hDdeDataOut;
817     WDML_LINK*          pLink;
818     WINE_DDEHEAD        wdh;
819     HSZ                 hsz;
820
821     TRACE("WM_DDE_DATA message received in the Client Proc!\n");
822     /* wParam -- sending window handle  */
823     /* lParam -- hDdeData & item HSZ    */
824         
825     UnpackDDElParam(WM_DDE_DATA, msg->lParam, &uiLo, &uiHi);
826     hsz = WDML_MakeHszFromAtom(pConv->instance, uiHi);
827
828     hDdeDataIn = WDML_Global2DataHandle((HGLOBAL)uiLo, &wdh);
829
830     /* billx: 
831      *  For hot link, data should be passed to its callback with
832      * XTYP_ADVDATA and callback should return the proper status.
833      */
834     pLink = WDML_FindLink(pConv->instance, (HCONV)pConv, WDML_CLIENT_SIDE, hsz, wdh.cfFormat);
835     if (!pLink) 
836     {
837         WDML_DecHSZ(pConv->instance, hsz);
838         return WDML_QS_PASS;
839     }
840         
841     if (hDdeDataIn != 0 && wdh.fAckReq)
842     {
843         WDML_PostAck(pConv, WDML_CLIENT_SIDE, 0, FALSE, TRUE, uiHi, 
844                      msg->lParam, WM_DDE_DATA);
845         if (msg->lParam)
846             msg->lParam = 0;
847     }
848     else
849     {
850         GlobalDeleteAtom(uiHi);
851     }
852         
853     hDdeDataOut = WDML_InvokeCallback(pConv->instance, XTYP_ADVDATA, pLink->uFmt, pLink->hConv, 
854                                       pConv->hszTopic, pLink->hszItem, hDdeDataIn, 0, 0);
855     if (hDdeDataOut == (HDDEDATA)DDE_FACK)
856     {
857         pLink->hDdeData = hDdeDataIn;
858     }
859     if (wdh.fRelease) 
860     {
861         DdeFreeDataHandle(hDdeDataIn);
862     }
863     
864     WDML_DecHSZ(pConv->instance, hsz);
865     if (msg->lParam)
866         FreeDDElParam(WM_DDE_DATA, msg->lParam);
867         
868     return WDML_QS_HANDLED;
869 }
870
871 /******************************************************************
872  *              WDML_HandleIncomingTerminate
873  *
874  *
875  */
876 static WDML_QUEUE_STATE WDML_HandleIncomingTerminate(WDML_CONV* pConv, MSG* msg, HDDEDATA* hdd)
877 {
878     if (pConv->hwndServer != (HWND)msg->wParam)
879         return WDML_QS_PASS;
880     
881     pConv->wStatus |= ST_TERMINATED;
882     if (!pConv->instance->CBFflags & CBF_SKIP_DISCONNECTS)
883     {
884         WDML_InvokeCallback(pConv->instance, XTYP_DISCONNECT, 0, (HCONV)pConv, 
885                             0, 0, 0, 0, (pConv->wStatus & ST_ISSELF) ? 1 : 0);
886     }
887     if (pConv->wStatus & ST_CONNECTED)
888     {
889         /* don't care about result code (if server exists or not) */
890         PostMessageA(pConv->hwndServer, WM_DDE_TERMINATE, (WPARAM)pConv->hwndClient, 0L);
891         pConv->wStatus &= ~ST_CONNECTED;
892     }
893     /* have to keep connection around to allow reconnection */
894     return WDML_QS_HANDLED;
895 }
896
897 /******************************************************************
898  *              WDML_HandleReply
899  *
900  * handles any incoming reply, and try to match to an already sent request
901  */
902 static WDML_QUEUE_STATE WDML_HandleReply(WDML_CONV* pConv, MSG* msg, HDDEDATA* hdd)
903 {
904     WDML_XACT*          pXAct = pConv->transactions;
905     WDML_QUEUE_STATE    qs;
906
907     if (pConv->transactions) 
908     {
909         /* first check message against a pending transaction, if any */
910         switch (pXAct->ddeMsg)
911         {
912         case WM_DDE_ADVISE:
913             qs = WDML_HandleAdviseReply(pConv, msg, pXAct);
914             break;
915         case WM_DDE_UNADVISE:
916             qs = WDML_HandleUnadviseReply(pConv, msg, pXAct);
917             break;
918         case WM_DDE_EXECUTE:
919             qs = WDML_HandleExecuteReply(pConv, msg, pXAct);
920             break;
921         case WM_DDE_REQUEST:
922             qs = WDML_HandleRequestReply(pConv, msg, pXAct);
923             break;
924         case WM_DDE_POKE:
925             qs = WDML_HandlePokeReply(pConv, msg, pXAct);
926             break;
927         case WM_DDE_TERMINATE:
928             qs = WDML_HandleTerminateReply(pConv, msg, pXAct);
929             break;
930         default:
931             qs = WDML_QS_ERROR;
932             FIXME("oooch\n");
933         }
934     }
935     else
936     {
937         qs = WDML_QS_PASS;
938     }
939
940     /* now check the results */
941     switch (qs) 
942     {
943     case WDML_QS_ERROR:
944     case WDML_QS_SWALLOWED:
945         *hdd = 0;
946         break;
947     case WDML_QS_HANDLED:
948         /* ok, we have resolved a pending transaction
949          * notify callback if asynchronous, and remove it in any case
950          */
951         WDML_UnQueueTransaction(pConv, pXAct);
952         if (pXAct->dwTimeout == TIMEOUT_ASYNC && pXAct->ddeMsg != WM_DDE_TERMINATE)
953         {
954             WDML_InvokeCallback(pConv->instance, XTYP_XACT_COMPLETE, pXAct->wFmt,
955                                 (HCONV)pConv, pConv->hszTopic, pXAct->hszItem,
956                                 pXAct->hDdeData, MAKELONG(0, pXAct->xActID), 0 /* FIXME */);
957             qs = WDML_QS_PASS;
958         }       
959         else
960         {
961             *hdd = pXAct->hDdeData;
962         }
963         WDML_FreeTransaction(pConv->instance, pXAct, FALSE); /* FIXME: should we free intermediate pmts ? */
964         break;
965     case WDML_QS_PASS:
966         /* no pending transaction found, try a warm link or a termination request */
967         switch (msg->message)
968         {
969         case WM_DDE_DATA:
970             qs = WDML_HandleIncomingData(pConv, msg, hdd);
971             break;
972         case WM_DDE_TERMINATE:
973             qs = WDML_HandleIncomingTerminate(pConv, msg, hdd);
974             break;
975         }
976         break;
977     case WDML_QS_BLOCK:
978         FIXME("shouldn't be used on client side\n");
979         break;
980     }
981
982     return qs;
983 }
984
985 /******************************************************************
986  *              WDML_SyncWaitTransactionReply
987  *
988  * waits until an answer for a sent request is received
989  * time out is also handled. only used for synchronous transactions
990  */
991 static HDDEDATA WDML_SyncWaitTransactionReply(HCONV hConv, DWORD dwTimeout, WDML_XACT* pXAct)
992 {
993     DWORD       dwTime;
994     DWORD       err;
995     WDML_CONV*  pConv;
996
997     TRACE("Starting wait for a timeout of %ld ms\n", dwTimeout);
998
999     /* FIXME: time 32 bit wrap around */
1000     dwTimeout += GetCurrentTime();
1001             
1002     while ((dwTime = GetCurrentTime()) < dwTimeout)
1003     {
1004         /* we cannot be in the crit sect all the time because when client and server run in a
1005          * single process they need to share the access to the internal data
1006          */
1007         if (MsgWaitForMultipleObjects(0, NULL, FALSE, 
1008                                       dwTime - dwTimeout, QS_POSTMESSAGE) == WAIT_OBJECT_0)
1009         {
1010             BOOL        ret = FALSE;
1011             MSG         msg;
1012             WDML_CONV*  pConv;
1013             HDDEDATA    hdd;
1014             
1015             EnterCriticalSection(&WDML_CritSect);
1016
1017             pConv = WDML_GetConv(hConv, FALSE);
1018             if (pConv == NULL)
1019             {
1020                 LeaveCriticalSection(&WDML_CritSect);
1021                 /* conversation no longer available... return failure */
1022                 break;
1023             }
1024             while (PeekMessageA(&msg, pConv->hwndClient, WM_DDE_FIRST, WM_DDE_LAST, PM_REMOVE))
1025             {
1026                 /* check that either pXAct has been processed or no more xActions are pending */
1027                 ret = (pConv->transactions == pXAct);
1028                 ret = WDML_HandleReply(pConv, &msg, &hdd) == WDML_QS_HANDLED && 
1029                     (pConv->transactions == NULL || ret);
1030                 if (ret) break;
1031             }
1032             LeaveCriticalSection(&WDML_CritSect);
1033             if (ret)
1034             {
1035                 return hdd;
1036             }
1037         }
1038     }
1039
1040     TRACE("Timeout !!\n");
1041
1042     EnterCriticalSection(&WDML_CritSect);
1043
1044     pConv = WDML_GetConv(hConv, FALSE);
1045     if (pConv != NULL)
1046     {
1047         if (pConv->transactions)
1048         {
1049             switch (pConv->transactions->ddeMsg)
1050             {
1051             case WM_DDE_ADVISE:         err = DMLERR_ADVACKTIMEOUT;     break;
1052             case WM_DDE_REQUEST:        err = DMLERR_DATAACKTIMEOUT;    break;
1053             case WM_DDE_EXECUTE:        err = DMLERR_EXECACKTIMEOUT;    break;
1054             case WM_DDE_POKE:           err = DMLERR_POKEACKTIMEOUT;    break;
1055             case WM_DDE_UNADVISE:       err = DMLERR_UNADVACKTIMEOUT;   break;
1056             default:                    err = DMLERR_INVALIDPARAMETER;  break;
1057             }
1058
1059             pConv->instance->lastError = err;
1060         }
1061     }
1062     LeaveCriticalSection(&WDML_CritSect);
1063
1064     return 0;
1065 }
1066
1067 /*****************************************************************
1068  *            DdeClientTransaction  (USER32.@)
1069  */
1070 HDDEDATA WINAPI DdeClientTransaction(LPBYTE pData, DWORD cbData, HCONV hConv, HSZ hszItem, UINT wFmt,
1071                                      UINT wType, DWORD dwTimeout, LPDWORD pdwResult)
1072 {
1073     WDML_CONV*          pConv;
1074     WDML_XACT*          pXAct;
1075     HDDEDATA            hDdeData = 0;
1076     
1077     TRACE("(%p,%ld,0x%lx,0x%x,%d,%d,%ld,%p)\n",
1078           pData, cbData, (DWORD)hConv, hszItem, wFmt, wType, dwTimeout, pdwResult);
1079     
1080     if (hConv == 0)
1081     {
1082         ERR("Invalid conversation handle\n");
1083         return 0;
1084     }
1085     
1086     EnterCriticalSection(&WDML_CritSect);
1087
1088     pConv = WDML_GetConv(hConv, TRUE);
1089     if (pConv == NULL)
1090     {
1091         /* cannot set error... cannot get back to DDE instance */
1092         goto theError;
1093     }
1094
1095     switch (wType)
1096     {
1097     case XTYP_EXECUTE:
1098         if (hszItem != 0 || wFmt != 0)
1099         {
1100             pConv->instance->lastError = DMLERR_INVALIDPARAMETER;
1101             goto theError;
1102         }
1103         pXAct = WDML_ClientQueueExecute(pConv, pData, cbData);
1104         break;
1105     case XTYP_POKE:
1106         pXAct = WDML_ClientQueuePoke(pConv, pData, cbData, wFmt, hszItem);
1107         break;
1108     case XTYP_ADVSTART|XTYPF_NODATA:
1109     case XTYP_ADVSTART|XTYPF_NODATA|XTYPF_ACKREQ:
1110     case XTYP_ADVSTART:
1111     case XTYP_ADVSTART|XTYPF_ACKREQ:
1112         if (pData)
1113         {
1114             pConv->instance->lastError = DMLERR_INVALIDPARAMETER;
1115             goto theError;
1116         }
1117         pXAct = WDML_ClientQueueAdvise(pConv, wType, wFmt, hszItem);
1118         break;
1119     case XTYP_ADVSTOP:
1120         if (pData)
1121         {
1122             pConv->instance->lastError = DMLERR_INVALIDPARAMETER;
1123             goto theError;
1124         }
1125         pXAct = WDML_ClientQueueUnadvise(pConv, wFmt, hszItem);
1126         break;
1127     case XTYP_REQUEST:
1128         if (pData)
1129         {
1130             pConv->instance->lastError = DMLERR_INVALIDPARAMETER;
1131             goto theError;
1132         }
1133         pXAct = WDML_ClientQueueRequest(pConv, wFmt, hszItem);
1134         break;
1135     default:
1136         FIXME("Unknown transation\n");
1137         /* unknown transaction type */
1138         pConv->instance->lastError = DMLERR_INVALIDPARAMETER;
1139         goto theError;
1140     }
1141
1142     if (pXAct == NULL)
1143     {
1144         pConv->instance->lastError = DMLERR_MEMORY_ERROR;
1145         goto theError;
1146     }
1147
1148     WDML_QueueTransaction(pConv, pXAct);
1149
1150     if (!PostMessageA(pConv->hwndServer, pXAct->ddeMsg, (WPARAM)pConv->hwndClient, pXAct->lParam))
1151     {
1152         TRACE("Failed posting message %d to 0x%04x (error=0x%lx)\n", 
1153               pXAct->ddeMsg, pConv->hwndServer, GetLastError());
1154         pConv->wStatus &= ~ST_CONNECTED;
1155         WDML_UnQueueTransaction(pConv, pXAct);
1156         WDML_FreeTransaction(pConv->instance, pXAct, TRUE);
1157         goto theError;
1158
1159     }
1160     pXAct->dwTimeout = dwTimeout;
1161     /* FIXME: should set the app bits on *pdwResult */
1162     
1163     if (dwTimeout == TIMEOUT_ASYNC)
1164     {
1165         if (pdwResult)
1166         {
1167             *pdwResult = MAKELONG(0, pXAct->xActID);
1168         }
1169         hDdeData = (HDDEDATA)1;
1170     }
1171     else
1172     {
1173         DWORD   count, i;
1174
1175         if (pdwResult)
1176         {
1177             *pdwResult = 0L;
1178         }
1179         count = WDML_CritSect.RecursionCount;
1180         for (i = 0; i < count; i++)
1181             LeaveCriticalSection(&WDML_CritSect);
1182         hDdeData = WDML_SyncWaitTransactionReply((HCONV)pConv, dwTimeout, pXAct);
1183         for (i = 0; i < count; i++)
1184             EnterCriticalSection(&WDML_CritSect);
1185     }
1186     LeaveCriticalSection(&WDML_CritSect);
1187
1188     return hDdeData;
1189  theError:
1190     LeaveCriticalSection(&WDML_CritSect);
1191     return 0;
1192 }
1193
1194 /******************************************************************
1195  *              WDML_ClientProc
1196  *
1197  * Window Proc created on client side for each conversation
1198  */
1199 static LRESULT CALLBACK WDML_ClientProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
1200 {
1201     UINT        uiLo, uiHi;
1202     WDML_CONV*  pConv = NULL;
1203     HSZ         hszSrv, hszTpc;
1204
1205     if (iMsg == WM_DDE_ACK &&
1206         UnpackDDElParam(WM_DDE_ACK, lParam, &uiLo, &uiHi) &&
1207         /* in the initial WM_INITIATE sendmessage */
1208         ((pConv = WDML_GetConvFromWnd(hwnd)) == NULL || pConv->wStatus == XST_INIT1))
1209     {
1210         /* In response to WM_DDE_INITIATE, save server window  */
1211         char            buf[256];
1212         WDML_INSTANCE*  pInstance;
1213
1214         FreeDDElParam(WM_DDE_ACK, lParam);
1215
1216         /* FIXME: convlist should be handled here */
1217         if (pConv)
1218         {
1219             /* we already have started the conv with a server, drop other replies */
1220             GlobalDeleteAtom(uiLo);
1221             GlobalDeleteAtom(uiHi);
1222             return 0;
1223         }
1224
1225         pInstance = WDML_GetInstanceFromWnd(hwnd);
1226
1227         hszSrv = WDML_MakeHszFromAtom(pInstance, uiLo);
1228         hszTpc = WDML_MakeHszFromAtom(pInstance, uiHi);
1229
1230         pConv = WDML_AddConv(pInstance, WDML_CLIENT_SIDE, hszSrv, hszTpc, 
1231                              hwnd, (HWND)wParam);
1232
1233         SetWindowLongA(hwnd, GWL_WDML_CONVERSATION, (DWORD)pConv);
1234         pConv->wStatus |= ST_CONNECTED;
1235         pConv->wConvst = XST_INIT1;
1236
1237         /* check if server is handled by DDEML */
1238         if ((GetClassNameA((HWND)wParam, buf, sizeof(buf)) && 
1239              strcmp(buf, WDML_szServerConvClassA) == 0) ||
1240             (GetClassNameW((HWND)wParam, (LPWSTR)buf, sizeof(buf)/sizeof(WCHAR)) && 
1241              lstrcmpW((LPWSTR)buf, WDML_szServerConvClassW) == 0))
1242         {
1243             pConv->wStatus |= ST_ISLOCAL;
1244         }
1245
1246         WDML_BroadcastDDEWindows(WDML_szEventClass, WM_WDML_CONNECT_CONFIRM, hwnd, (HWND)wParam);
1247
1248         GlobalDeleteAtom(uiLo);
1249         GlobalDeleteAtom(uiHi);
1250
1251         /* accept conversation */
1252         return 1;
1253     }
1254
1255     if (iMsg >= WM_DDE_FIRST && iMsg <= WM_DDE_LAST)
1256     {
1257         EnterCriticalSection(&WDML_CritSect);
1258
1259         pConv = WDML_GetConvFromWnd(hwnd);
1260
1261         if (pConv) 
1262         {
1263             MSG         msg;
1264             HDDEDATA    hdd;
1265
1266             msg.hwnd = hwnd;
1267             msg.message = iMsg;
1268             msg.wParam = wParam;
1269             msg.lParam = lParam;
1270
1271             WDML_HandleReply(pConv, &msg, &hdd);
1272         }
1273
1274         LeaveCriticalSection(&WDML_CritSect);
1275         return 0;
1276     }
1277     
1278     return (IsWindowUnicode(hwnd)) ? 
1279         DefWindowProcA(hwnd, iMsg, wParam, lParam) : DefWindowProcW(hwnd, iMsg, wParam, lParam);
1280 }
1281
1282 /*****************************************************************
1283  *            DdeAbandonTransaction (USER32.@)
1284  */
1285 BOOL WINAPI DdeAbandonTransaction(DWORD idInst, HCONV hConv, DWORD idTransaction)
1286 {
1287     FIXME("empty stub\n");
1288     return TRUE;
1289 }
1290
1291 /*****************************************************************
1292  *            DdeDisconnect   (USER32.@)
1293  */
1294 BOOL WINAPI DdeDisconnect(HCONV hConv)
1295 {
1296     WDML_CONV*  pConv = NULL;
1297     WDML_XACT*  pXAct;
1298     DWORD       count, i;
1299     BOOL        ret = FALSE;
1300
1301     TRACE("(%ld)\n", (DWORD)hConv);
1302     
1303     if (hConv == 0)
1304     {
1305         ERR("DdeDisconnect(): hConv = 0\n");
1306         return FALSE;
1307     }
1308     
1309     EnterCriticalSection(&WDML_CritSect);
1310     pConv = WDML_GetConv(hConv, FALSE);
1311     if (pConv != NULL)
1312     {
1313         if (pConv->wStatus & ST_CONNECTED)
1314         {
1315             if (pConv->wStatus & ST_CLIENT)
1316             {
1317                 /* FIXME: should abandon all pending transactions */
1318                 pXAct = WDML_ClientQueueTerminate(pConv);
1319                 if (pXAct != NULL)
1320                 {
1321                     count = WDML_CritSect.RecursionCount;
1322                     for (i = 0; i < count; i++)
1323                         LeaveCriticalSection(&WDML_CritSect);
1324                     WDML_SyncWaitTransactionReply(hConv, 10000, pXAct);
1325                     for (i = 0; i < count; i++)
1326                         EnterCriticalSection(&WDML_CritSect);
1327                     ret = TRUE;
1328                 }
1329                 else
1330                 {
1331                     FIXME("Not implemented yet for a server side conversation\n");
1332                 }
1333             }
1334         }
1335         /* still have to destroy data assosiated with conversation */
1336         WDML_RemoveConv(pConv, WDML_CLIENT_SIDE);
1337     }
1338     LeaveCriticalSection(&WDML_CritSect);
1339
1340     return ret;
1341 }
1342
1343 /*****************************************************************
1344  *            DdeImpersonateClient (USER32.@)
1345  */
1346 BOOL WINAPI DdeImpersonateClient(HCONV hConv)
1347 {
1348     WDML_CONV*  pConv;
1349     BOOL        ret = FALSE;
1350     
1351     EnterCriticalSection(&WDML_CritSect);
1352     pConv = WDML_GetConv(hConv, TRUE);
1353     if (pConv)
1354     {
1355         ret = ImpersonateDdeClientWindow(pConv->hwndClient, pConv->hwndServer);
1356     }
1357     LeaveCriticalSection(&WDML_CritSect);
1358     return ret;
1359 }