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