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