Implement A->W call for GetNamedSecurityInfo.
[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) * sizeof(WCHAR);
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/sizeof(WCHAR));
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     TRACE("hDdeData = %p\n", pXAct->hDdeData);
698     pConv->instance->lastError = (pXAct->hDdeData != 0) ? DMLERR_NO_ERROR : DMLERR_NOTPROCESSED;
699
700     return WDML_QS_HANDLED;
701 }
702
703 /******************************************************************
704  *              WDML_ClientQueuePoke
705  *
706  *
707  */
708 static WDML_XACT*       WDML_ClientQueuePoke(WDML_CONV* pConv, LPCVOID pData, DWORD cbData,
709                                              UINT wFmt, HSZ hszItem)
710 {
711     WDML_XACT*  pXAct;
712     ATOM        atom;
713
714     TRACE("XTYP_POKE transaction\n");
715
716     atom = WDML_MakeAtomFromHsz(hszItem);
717     if (!atom) return NULL;
718
719     pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_POKE, wFmt, hszItem);
720     if (!pXAct)
721     {
722         GlobalDeleteAtom(atom);
723         return NULL;
724     }
725
726     if (cbData == (DWORD)-1)
727     {
728         pXAct->hMem = (HDDEDATA)pData;
729     }
730     else
731     {
732         DDEPOKE*        ddePoke;
733
734         pXAct->hMem = GlobalAlloc(GHND | GMEM_DDESHARE, sizeof(DDEPOKE) + cbData);
735         ddePoke = GlobalLock(pXAct->hMem);
736         if (ddePoke)
737         {
738             memcpy(ddePoke->Value, pData, cbData);
739             ddePoke->fRelease = FALSE; /* FIXME: app owned ? */
740             ddePoke->cfFormat = wFmt;
741             GlobalUnlock(pXAct->hMem);
742         }
743     }
744
745     pXAct->lParam = PackDDElParam(WM_DDE_POKE, (UINT_PTR)pXAct->hMem, atom);
746
747     return pXAct;
748 }
749
750 /******************************************************************
751  *              WDML_HandlePokeReply
752  *
753  *
754  */
755 static WDML_QUEUE_STATE WDML_HandlePokeReply(WDML_CONV* pConv, MSG* msg, WDML_XACT* pXAct)
756 {
757     DDEACK      ddeAck;
758     UINT_PTR    uiLo, uiHi;
759     HSZ         hsz;
760
761     if (msg->message != WM_DDE_ACK && WIN_GetFullHandle((HWND)msg->wParam) != pConv->hwndServer)
762     {
763         return WDML_QS_PASS;
764     }
765
766     UnpackDDElParam(WM_DDE_ACK, msg->lParam, &uiLo, &uiHi);
767     hsz = WDML_MakeHszFromAtom(pConv->instance, uiHi);
768     if (DdeCmpStringHandles(hsz, pXAct->hszItem) != 0)
769     {
770         return WDML_QS_PASS;
771     }
772     FreeDDElParam(WM_DDE_ACK, msg->lParam);
773     GlobalDeleteAtom(uiHi);
774
775     WDML_ExtractAck(uiLo, &ddeAck);
776     GlobalFree(pXAct->hMem);
777
778     pXAct->hDdeData = (HDDEDATA)TRUE;
779     return TRUE;
780 }
781
782 /******************************************************************
783  *              WDML_ClientQueueTerminate
784  *
785  * Creates and queue an WM_DDE_TERMINATE transaction
786  */
787 static WDML_XACT*       WDML_ClientQueueTerminate(WDML_CONV* pConv)
788 {
789     WDML_XACT*          pXAct;
790
791     pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_TERMINATE, 0, 0);
792     if (!pXAct)
793         return NULL;
794
795     pXAct->lParam = 0;
796     pConv->wStatus &= ~ST_CONNECTED;
797
798     return pXAct;
799 }
800
801 /******************************************************************
802  *              WDML_HandleTerminateReply
803  *
804  * handles the reply to a terminate request
805  */
806 static WDML_QUEUE_STATE WDML_HandleTerminateReply(WDML_CONV* pConv, MSG* msg, WDML_XACT* pXAct)
807 {
808     if (msg->message != WM_DDE_TERMINATE)
809     {
810         /* FIXME: should delete data passed here */
811         return WDML_QS_SWALLOWED;
812     }
813
814     if (WIN_GetFullHandle((HWND)msg->wParam) != pConv->hwndServer)
815     {
816         FIXME("hmmm shouldn't happen\n");
817         return WDML_QS_PASS;
818     }
819     if (!pConv->instance->CBFflags & CBF_SKIP_DISCONNECTS)
820     {
821         WDML_InvokeCallback(pConv->instance, XTYP_DISCONNECT, 0, (HCONV)pConv,
822                             0, 0, 0, 0, (pConv->wStatus & ST_ISSELF) ? 1 : 0);
823     }
824     WDML_RemoveConv(pConv, WDML_CLIENT_SIDE);
825     return WDML_QS_HANDLED;
826 }
827
828 /******************************************************************
829  *              WDML_HandleReplyData
830  *
831  *
832  */
833 static WDML_QUEUE_STATE WDML_HandleIncomingData(WDML_CONV* pConv, MSG* msg, HDDEDATA* hdd)
834 {
835     UINT_PTR            uiLo, uiHi;
836     HDDEDATA            hDdeDataIn, hDdeDataOut;
837     WDML_LINK*          pLink;
838     WINE_DDEHEAD        wdh;
839     HSZ                 hsz;
840
841     TRACE("WM_DDE_DATA message received in the Client Proc!\n");
842     /* wParam -- sending window handle  */
843     /* lParam -- hDdeData & item HSZ    */
844
845     UnpackDDElParam(WM_DDE_DATA, msg->lParam, &uiLo, &uiHi);
846     hsz = WDML_MakeHszFromAtom(pConv->instance, uiHi);
847
848     hDdeDataIn = WDML_Global2DataHandle((HGLOBAL)uiLo, &wdh);
849
850     /* billx:
851      *  For hot link, data should be passed to its callback with
852      * XTYP_ADVDATA and callback should return the proper status.
853      */
854     pLink = WDML_FindLink(pConv->instance, (HCONV)pConv, WDML_CLIENT_SIDE, hsz,
855                           uiLo ? TRUE : FALSE, wdh.cfFormat);
856     if (!pLink)
857     {
858         WDML_DecHSZ(pConv->instance, hsz);
859         DdeFreeDataHandle(hDdeDataIn);
860         return WDML_QS_PASS;
861     }
862
863     if (hDdeDataIn != 0 && wdh.fAckReq)
864     {
865         WDML_PostAck(pConv, WDML_CLIENT_SIDE, 0, FALSE, TRUE, uiHi, msg->lParam, WM_DDE_DATA);
866         if (msg->lParam)
867             msg->lParam = 0;
868     }
869     else
870     {
871         GlobalDeleteAtom(uiHi);
872     }
873
874     hDdeDataOut = WDML_InvokeCallback(pConv->instance, XTYP_ADVDATA, pLink->uFmt, pLink->hConv,
875                                       pConv->hszTopic, pLink->hszItem, hDdeDataIn, 0, 0);
876
877     if (hDdeDataOut != (HDDEDATA)DDE_FACK || wdh.fRelease)
878     {
879         if (uiLo) GlobalFree((HANDLE)uiLo);
880     }
881
882     DdeFreeDataHandle(hDdeDataIn);
883
884     WDML_DecHSZ(pConv->instance, hsz);
885     if (msg->lParam)
886         FreeDDElParam(WM_DDE_DATA, msg->lParam);
887
888     return WDML_QS_HANDLED;
889 }
890
891 /******************************************************************
892  *              WDML_HandleIncomingTerminate
893  *
894  *
895  */
896 static WDML_QUEUE_STATE WDML_HandleIncomingTerminate(WDML_CONV* pConv, MSG* msg, HDDEDATA* hdd)
897 {
898     if (pConv->hwndServer != WIN_GetFullHandle((HWND)msg->wParam))
899         return WDML_QS_PASS;
900
901     pConv->wStatus |= ST_TERMINATED;
902     if (!pConv->instance->CBFflags & CBF_SKIP_DISCONNECTS)
903     {
904         WDML_InvokeCallback(pConv->instance, XTYP_DISCONNECT, 0, (HCONV)pConv,
905                             0, 0, 0, 0, (pConv->wStatus & ST_ISSELF) ? 1 : 0);
906     }
907     if (pConv->wStatus & ST_CONNECTED)
908     {
909         /* don't care about result code (if server exists or not) */
910         PostMessageA(pConv->hwndServer, WM_DDE_TERMINATE, (WPARAM)pConv->hwndClient, 0L);
911         pConv->wStatus &= ~ST_CONNECTED;
912     }
913     /* have to keep connection around to allow reconnection */
914     return WDML_QS_HANDLED;
915 }
916
917 /******************************************************************
918  *              WDML_HandleReply
919  *
920  * handles any incoming reply, and try to match to an already sent request
921  */
922 static WDML_QUEUE_STATE WDML_HandleReply(WDML_CONV* pConv, MSG* msg, HDDEDATA* hdd)
923 {
924     WDML_XACT*          pXAct = pConv->transactions;
925     WDML_QUEUE_STATE    qs;
926
927     if (pConv->transactions)
928     {
929         /* first check message against a pending transaction, if any */
930         switch (pXAct->ddeMsg)
931         {
932         case WM_DDE_ADVISE:
933             qs = WDML_HandleAdviseReply(pConv, msg, pXAct);
934             break;
935         case WM_DDE_UNADVISE:
936             qs = WDML_HandleUnadviseReply(pConv, msg, pXAct);
937             break;
938         case WM_DDE_EXECUTE:
939             qs = WDML_HandleExecuteReply(pConv, msg, pXAct);
940             break;
941         case WM_DDE_REQUEST:
942             qs = WDML_HandleRequestReply(pConv, msg, pXAct);
943             break;
944         case WM_DDE_POKE:
945             qs = WDML_HandlePokeReply(pConv, msg, pXAct);
946             break;
947         case WM_DDE_TERMINATE:
948             qs = WDML_HandleTerminateReply(pConv, msg, pXAct);
949             break;
950         default:
951             qs = WDML_QS_ERROR;
952             FIXME("oooch\n");
953         }
954     }
955     else
956     {
957         qs = WDML_QS_PASS;
958     }
959
960     /* now check the results */
961     switch (qs)
962     {
963     case WDML_QS_ERROR:
964     case WDML_QS_SWALLOWED:
965         *hdd = 0;
966         break;
967     case WDML_QS_HANDLED:
968         /* ok, we have resolved a pending transaction
969          * notify callback if asynchronous, and remove it in any case
970          */
971         WDML_UnQueueTransaction(pConv, pXAct);
972         if (pXAct->dwTimeout == TIMEOUT_ASYNC && pXAct->ddeMsg != WM_DDE_TERMINATE)
973         {
974             WDML_InvokeCallback(pConv->instance, XTYP_XACT_COMPLETE, pXAct->wFmt,
975                                 (HCONV)pConv, pConv->hszTopic, pXAct->hszItem,
976                                 pXAct->hDdeData, MAKELONG(0, pXAct->xActID), 0 /* FIXME */);
977             qs = WDML_QS_PASS;
978         }
979         else
980         {
981             *hdd = pXAct->hDdeData;
982         }
983         WDML_FreeTransaction(pConv->instance, pXAct, TRUE);
984         break;
985     case WDML_QS_PASS:
986         /* no pending transaction found, try a warm/hot link or a termination request */
987         switch (msg->message)
988         {
989         case WM_DDE_DATA:
990             qs = WDML_HandleIncomingData(pConv, msg, hdd);
991             break;
992         case WM_DDE_TERMINATE:
993             qs = WDML_HandleIncomingTerminate(pConv, msg, hdd);
994             break;
995         }
996         break;
997     case WDML_QS_BLOCK:
998         FIXME("shouldn't be used on client side\n");
999         break;
1000     }
1001
1002     return qs;
1003 }
1004
1005 /******************************************************************
1006  *              WDML_SyncWaitTransactionReply
1007  *
1008  * waits until an answer for a sent request is received
1009  * time out is also handled. only used for synchronous transactions
1010  */
1011 static HDDEDATA WDML_SyncWaitTransactionReply(HCONV hConv, DWORD dwTimeout, WDML_XACT* pXAct)
1012 {
1013     DWORD       dwTime;
1014     DWORD       err;
1015     WDML_CONV*  pConv;
1016
1017     TRACE("Starting wait for a timeout of %ld ms\n", dwTimeout);
1018
1019     /* FIXME: time 32 bit wrap around */
1020     dwTimeout += GetCurrentTime();
1021
1022     while ((dwTime = GetCurrentTime()) < dwTimeout)
1023     {
1024         /* we cannot be in the crit sect all the time because when client and server run in a
1025          * single process they need to share the access to the internal data
1026          */
1027         if (MsgWaitForMultipleObjects(0, NULL, FALSE,
1028                                       dwTimeout - dwTime, QS_POSTMESSAGE) == WAIT_OBJECT_0)
1029         {
1030             BOOL        ret = FALSE;
1031             MSG         msg;
1032             WDML_CONV*  pConv;
1033             HDDEDATA    hdd;
1034
1035             EnterCriticalSection(&WDML_CritSect);
1036
1037             pConv = WDML_GetConv(hConv, FALSE);
1038             if (pConv == NULL)
1039             {
1040                 LeaveCriticalSection(&WDML_CritSect);
1041                 /* conversation no longer available... return failure */
1042                 break;
1043             }
1044             while (PeekMessageA(&msg, pConv->hwndClient, WM_DDE_FIRST, WM_DDE_LAST, PM_REMOVE))
1045             {
1046                 /* check that either pXAct has been processed or no more xActions are pending */
1047                 ret = (pConv->transactions == pXAct);
1048                 ret = WDML_HandleReply(pConv, &msg, &hdd) == WDML_QS_HANDLED &&
1049                     (pConv->transactions == NULL || ret);
1050                 if (ret) break;
1051             }
1052             LeaveCriticalSection(&WDML_CritSect);
1053             if (ret)
1054             {
1055                 return hdd;
1056             }
1057         }
1058     }
1059
1060     TRACE("Timeout !!\n");
1061
1062     EnterCriticalSection(&WDML_CritSect);
1063
1064     pConv = WDML_GetConv(hConv, FALSE);
1065     if (pConv != NULL)
1066     {
1067         if (pConv->transactions)
1068         {
1069             switch (pConv->transactions->ddeMsg)
1070             {
1071             case WM_DDE_ADVISE:         err = DMLERR_ADVACKTIMEOUT;     break;
1072             case WM_DDE_REQUEST:        err = DMLERR_DATAACKTIMEOUT;    break;
1073             case WM_DDE_EXECUTE:        err = DMLERR_EXECACKTIMEOUT;    break;
1074             case WM_DDE_POKE:           err = DMLERR_POKEACKTIMEOUT;    break;
1075             case WM_DDE_UNADVISE:       err = DMLERR_UNADVACKTIMEOUT;   break;
1076             default:                    err = DMLERR_INVALIDPARAMETER;  break;
1077             }
1078
1079             pConv->instance->lastError = err;
1080         }
1081     }
1082     LeaveCriticalSection(&WDML_CritSect);
1083
1084     return 0;
1085 }
1086
1087 /*****************************************************************
1088  *            DdeClientTransaction  (USER32.@)
1089  */
1090 HDDEDATA WINAPI DdeClientTransaction(LPBYTE pData, DWORD cbData, HCONV hConv, HSZ hszItem, UINT wFmt,
1091                                      UINT wType, DWORD dwTimeout, LPDWORD pdwResult)
1092 {
1093     WDML_CONV*          pConv;
1094     WDML_XACT*          pXAct;
1095     HDDEDATA            hDdeData = 0;
1096
1097     TRACE("(%p,%ld,%p,%p,%x,%x,%ld,%p)\n",
1098           pData, cbData, hConv, hszItem, wFmt, wType, dwTimeout, pdwResult);
1099
1100     if (hConv == 0)
1101     {
1102         ERR("Invalid conversation handle\n");
1103         return 0;
1104     }
1105
1106     EnterCriticalSection(&WDML_CritSect);
1107
1108     pConv = WDML_GetConv(hConv, TRUE);
1109     if (pConv == NULL)
1110     {
1111         /* cannot set error... cannot get back to DDE instance */
1112         goto theError;
1113     }
1114
1115     switch (wType)
1116     {
1117     case XTYP_EXECUTE:
1118         if (hszItem != 0 || wFmt != 0)
1119         {
1120             pConv->instance->lastError = DMLERR_INVALIDPARAMETER;
1121             goto theError;
1122         }
1123         pXAct = WDML_ClientQueueExecute(pConv, pData, cbData);
1124         break;
1125     case XTYP_POKE:
1126         pXAct = WDML_ClientQueuePoke(pConv, pData, cbData, wFmt, hszItem);
1127         break;
1128     case XTYP_ADVSTART|XTYPF_NODATA:
1129     case XTYP_ADVSTART|XTYPF_NODATA|XTYPF_ACKREQ:
1130     case XTYP_ADVSTART:
1131     case XTYP_ADVSTART|XTYPF_ACKREQ:
1132         if (pData)
1133         {
1134             pConv->instance->lastError = DMLERR_INVALIDPARAMETER;
1135             goto theError;
1136         }
1137         pXAct = WDML_ClientQueueAdvise(pConv, wType, wFmt, hszItem);
1138         break;
1139     case XTYP_ADVSTOP:
1140         if (pData)
1141         {
1142             pConv->instance->lastError = DMLERR_INVALIDPARAMETER;
1143             goto theError;
1144         }
1145         pXAct = WDML_ClientQueueUnadvise(pConv, wFmt, hszItem);
1146         break;
1147     case XTYP_REQUEST:
1148         if (pData)
1149         {
1150             pConv->instance->lastError = DMLERR_INVALIDPARAMETER;
1151             goto theError;
1152         }
1153         pXAct = WDML_ClientQueueRequest(pConv, wFmt, hszItem);
1154         break;
1155     default:
1156         FIXME("Unknown transation\n");
1157         /* unknown transaction type */
1158         pConv->instance->lastError = DMLERR_INVALIDPARAMETER;
1159         goto theError;
1160     }
1161
1162     if (pXAct == NULL)
1163     {
1164         pConv->instance->lastError = DMLERR_MEMORY_ERROR;
1165         goto theError;
1166     }
1167
1168     WDML_QueueTransaction(pConv, pXAct);
1169
1170     if (!PostMessageA(pConv->hwndServer, pXAct->ddeMsg, (WPARAM)pConv->hwndClient, pXAct->lParam))
1171     {
1172         WARN("Failed posting message %x to %p (error=0x%lx)\n",
1173               pXAct->ddeMsg, pConv->hwndServer, GetLastError());
1174         pConv->wStatus &= ~ST_CONNECTED;
1175         WDML_UnQueueTransaction(pConv, pXAct);
1176         WDML_FreeTransaction(pConv->instance, pXAct, TRUE);
1177         goto theError;
1178     }
1179     pXAct->dwTimeout = dwTimeout;
1180     /* FIXME: should set the app bits on *pdwResult */
1181
1182     if (dwTimeout == TIMEOUT_ASYNC)
1183     {
1184         if (pdwResult)
1185         {
1186             *pdwResult = MAKELONG(0, pXAct->xActID);
1187         }
1188         hDdeData = (HDDEDATA)1;
1189     }
1190     else
1191     {
1192         DWORD   count, i;
1193
1194         if (pdwResult)
1195         {
1196             *pdwResult = 0L;
1197         }
1198         count = WDML_CritSect.RecursionCount;
1199         for (i = 0; i < count; i++)
1200             LeaveCriticalSection(&WDML_CritSect);
1201         hDdeData = WDML_SyncWaitTransactionReply((HCONV)pConv, dwTimeout, pXAct);
1202         for (i = 0; i < count; i++)
1203             EnterCriticalSection(&WDML_CritSect);
1204     }
1205     LeaveCriticalSection(&WDML_CritSect);
1206
1207     return hDdeData;
1208  theError:
1209     LeaveCriticalSection(&WDML_CritSect);
1210     return 0;
1211 }
1212
1213 /*****************************************************************
1214  *            DdeAbandonTransaction (USER32.@)
1215  */
1216 BOOL WINAPI DdeAbandonTransaction(DWORD idInst, HCONV hConv, DWORD idTransaction)
1217 {
1218     WDML_INSTANCE*      pInstance;
1219     WDML_CONV*          pConv;
1220     WDML_XACT*          pXAct;
1221
1222     TRACE("(%08lx,%p,%08ld);\n", idInst, hConv, idTransaction);
1223
1224     EnterCriticalSection(&WDML_CritSect);
1225     if ((pInstance = WDML_GetInstance(idInst)))
1226     {
1227         if (hConv)
1228         {
1229             if ((pConv = WDML_GetConv(hConv, TRUE)) && pConv->instance == pInstance)
1230             {
1231                 for (pXAct = pConv->transactions; pXAct; pXAct = pXAct->next)
1232                 {
1233                     if (pXAct->dwTimeout == TIMEOUT_ASYNC &&
1234                         (idTransaction == 0 || pXAct->xActID == idTransaction))
1235                     {
1236                         WDML_UnQueueTransaction(pConv, pXAct);
1237                         WDML_FreeTransaction(pInstance, pXAct, TRUE);
1238                     }
1239                 }
1240             }
1241         }
1242         else
1243         {
1244             for (pConv = pInstance->convs[WDML_CLIENT_SIDE]; pConv; pConv = pConv->next)
1245             {
1246                 if (!(pConv->wStatus & ST_CONNECTED)) continue;
1247                 for (pXAct = pConv->transactions; pXAct; pXAct = pXAct->next)
1248                 {
1249                     if (pXAct->dwTimeout == TIMEOUT_ASYNC)
1250                     {
1251                         WDML_UnQueueTransaction(pConv, pXAct);
1252                         WDML_FreeTransaction(pInstance, pXAct, TRUE);
1253                     }
1254                 }
1255             }
1256         }
1257     }
1258     LeaveCriticalSection(&WDML_CritSect);
1259
1260     return TRUE;
1261 }
1262
1263 /******************************************************************
1264  *              WDML_ClientProc
1265  *
1266  * Window Proc created on client side for each conversation
1267  */
1268 static LRESULT CALLBACK WDML_ClientProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
1269 {
1270     UINT        uiLo, uiHi;
1271     WDML_CONV*  pConv = NULL;
1272     HSZ         hszSrv, hszTpc;
1273
1274     if (iMsg == WM_DDE_ACK &&
1275         /* in the initial WM_INITIATE sendmessage */
1276         ((pConv = WDML_GetConvFromWnd(hwnd)) == NULL || pConv->wStatus == XST_INIT1))
1277     {
1278         /* In response to WM_DDE_INITIATE, save server window  */
1279         char            buf[256];
1280         WDML_INSTANCE*  pInstance;
1281
1282         /* note: sent messages do not need packing */
1283         uiLo = LOWORD(lParam);
1284         uiHi = HIWORD(lParam);
1285
1286         /* FIXME: convlist should be handled here */
1287         if (pConv)
1288         {
1289             /* we already have started the conv with a server, drop other replies */
1290             GlobalDeleteAtom(uiLo);
1291             GlobalDeleteAtom(uiHi);
1292             PostMessageA((HWND)wParam, WM_DDE_TERMINATE, (WPARAM)hwnd, 0);
1293             return 0;
1294         }
1295
1296         pInstance = WDML_GetInstanceFromWnd(hwnd);
1297
1298         hszSrv = WDML_MakeHszFromAtom(pInstance, uiLo);
1299         hszTpc = WDML_MakeHszFromAtom(pInstance, uiHi);
1300
1301         pConv = WDML_AddConv(pInstance, WDML_CLIENT_SIDE, hszSrv, hszTpc, hwnd, (HWND)wParam);
1302
1303         SetWindowLongA(hwnd, GWL_WDML_CONVERSATION, (DWORD)pConv);
1304         pConv->wStatus |= ST_CONNECTED;
1305         pConv->wConvst = XST_INIT1;
1306
1307         /* check if server is handled by DDEML */
1308         if ((GetClassNameA((HWND)wParam, buf, sizeof(buf)) &&
1309              strcmp(buf, WDML_szServerConvClassA) == 0) ||
1310             (GetClassNameW((HWND)wParam, (LPWSTR)buf, sizeof(buf)/sizeof(WCHAR)) &&
1311              lstrcmpW((LPWSTR)buf, WDML_szServerConvClassW) == 0))
1312         {
1313             pConv->wStatus |= ST_ISLOCAL;
1314         }
1315
1316         WDML_BroadcastDDEWindows(WDML_szEventClass, WM_WDML_CONNECT_CONFIRM, (WPARAM)hwnd, wParam);
1317
1318         GlobalDeleteAtom(uiLo);
1319         GlobalDeleteAtom(uiHi);
1320
1321         /* accept conversation */
1322         return 1;
1323     }
1324
1325     if (iMsg >= WM_DDE_FIRST && iMsg <= WM_DDE_LAST)
1326     {
1327         EnterCriticalSection(&WDML_CritSect);
1328
1329         pConv = WDML_GetConvFromWnd(hwnd);
1330
1331         if (pConv)
1332         {
1333             MSG         msg;
1334             HDDEDATA    hdd;
1335
1336             msg.hwnd = hwnd;
1337             msg.message = iMsg;
1338             msg.wParam = wParam;
1339             msg.lParam = lParam;
1340
1341             WDML_HandleReply(pConv, &msg, &hdd);
1342         }
1343
1344         LeaveCriticalSection(&WDML_CritSect);
1345         return 0;
1346     }
1347
1348     return (IsWindowUnicode(hwnd)) ?
1349         DefWindowProcW(hwnd, iMsg, wParam, lParam) : DefWindowProcA(hwnd, iMsg, wParam, lParam);
1350 }
1351
1352 /*****************************************************************
1353  *            DdeDisconnect   (USER32.@)
1354  */
1355 BOOL WINAPI DdeDisconnect(HCONV hConv)
1356 {
1357     WDML_CONV*  pConv = NULL;
1358     WDML_XACT*  pXAct;
1359     DWORD       count, i;
1360     BOOL        ret = FALSE;
1361
1362     TRACE("(%p)\n", hConv);
1363
1364     if (hConv == 0)
1365     {
1366         ERR("DdeDisconnect(): hConv = 0\n");
1367         return FALSE;
1368     }
1369
1370     EnterCriticalSection(&WDML_CritSect);
1371     pConv = WDML_GetConv(hConv, TRUE);
1372     if (pConv != NULL)
1373     {
1374         if (pConv->wStatus & ST_CLIENT)
1375         {
1376             /* FIXME: should abandon all pending transactions */
1377             pXAct = WDML_ClientQueueTerminate(pConv);
1378             if (pXAct != NULL)
1379             {
1380                 count = WDML_CritSect.RecursionCount;
1381                 for (i = 0; i < count; i++)
1382                     LeaveCriticalSection(&WDML_CritSect);
1383                 if (PostMessageA(pConv->hwndServer, pXAct->ddeMsg,
1384                                  (WPARAM)pConv->hwndClient, pXAct->lParam))
1385                     WDML_SyncWaitTransactionReply(hConv, 10000, pXAct);
1386                 for (i = 0; i < count; i++)
1387                     EnterCriticalSection(&WDML_CritSect);
1388                 ret = TRUE;
1389                 WDML_FreeTransaction(pConv->instance, pXAct, TRUE);
1390                 /* still have to destroy data assosiated with conversation */
1391                 WDML_RemoveConv(pConv, WDML_CLIENT_SIDE);
1392             }
1393             else
1394             {
1395                 FIXME("Not implemented yet for a server side conversation\n");
1396             }
1397         }
1398     }
1399     LeaveCriticalSection(&WDML_CritSect);
1400
1401     return ret;
1402 }
1403
1404 /*****************************************************************
1405  *            DdeImpersonateClient (USER32.@)
1406  */
1407 BOOL WINAPI DdeImpersonateClient(HCONV hConv)
1408 {
1409     WDML_CONV*  pConv;
1410     BOOL        ret = FALSE;
1411
1412     TRACE("(%p)\n", hConv);
1413
1414     EnterCriticalSection(&WDML_CritSect);
1415     pConv = WDML_GetConv(hConv, TRUE);
1416     if (pConv)
1417     {
1418         ret = ImpersonateDdeClientWindow(pConv->hwndClient, pConv->hwndServer);
1419     }
1420     LeaveCriticalSection(&WDML_CritSect);
1421     return ret;
1422 }