Documentation fixes.
[wine] / dlls / user / dde / client.c
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2
3 /*
4  * DDEML library
5  *
6  * Copyright 1997 Alexandre Julliard
7  * Copyright 1997 Len White
8  * Copyright 1999 Keith Matthews
9  * Copyright 2000 Corel
10  * Copyright 2001 Eric Pouech
11  */
12
13 #include <string.h>
14 #include "winbase.h"
15 #include "windef.h"
16 #include "wingdi.h"
17 #include "winuser.h"
18 #include "winerror.h"
19 #include "dde.h"
20 #include "ddeml.h"
21 #include "debugtools.h"
22 #include "dde/dde_private.h"
23
24 DEFAULT_DEBUG_CHANNEL(ddeml);
25
26 static LRESULT CALLBACK WDML_ClientProc(HWND, UINT, WPARAM, LPARAM);    /* only for one client, not conv list */
27 static const char szClientClassA[] = "DdeClientAnsi";
28
29 /******************************************************************************
30  * DdeConnectList [USER32.@]  Establishes conversation with DDE servers
31  *
32  * PARAMS
33  *    idInst     [I] Instance identifier
34  *    hszService [I] Handle to service name string
35  *    hszTopic   [I] Handle to topic name string
36  *    hConvList  [I] Handle to conversation list
37  *    pCC        [I] Pointer to structure with context data
38  *
39  * RETURNS
40  *    Success: Handle to new conversation list
41  *    Failure: 0
42  */
43 HCONVLIST WINAPI DdeConnectList(DWORD idInst, HSZ hszService, HSZ hszTopic,
44                                 HCONVLIST hConvList, LPCONVCONTEXT pCC)
45 {
46     FIXME("(%ld,%d,%d,%d,%p): stub\n", idInst, hszService, hszTopic,
47           hConvList,pCC);
48     return (HCONVLIST)1;
49 }
50
51 /*****************************************************************
52  * DdeQueryNextServer [USER32.@]
53  */
54 HCONV WINAPI DdeQueryNextServer(HCONVLIST hConvList, HCONV hConvPrev)
55 {
56     FIXME("(%d,%d): stub\n",hConvList,hConvPrev);
57     return 0;
58 }
59
60 /******************************************************************************
61  * DdeDisconnectList [USER32.@]  Destroys list and terminates conversations
62  *
63  * RETURNS
64  *    Success: TRUE
65  *    Failure: FALSE
66  */
67 BOOL WINAPI DdeDisconnectList(
68     HCONVLIST hConvList) /* [in] Handle to conversation list */
69 {
70     FIXME("(%d): stub\n", hConvList);
71     return TRUE;
72 }
73
74 /*****************************************************************
75  *            DdeConnect   (USER32.@)
76  */
77 HCONV WINAPI DdeConnect(DWORD idInst, HSZ hszService, HSZ hszTopic,
78                         LPCONVCONTEXT pCC)
79 {
80     HWND                hwndClient;
81     LPARAM              lParam = 0;
82     UINT                uiLow, uiHi;
83     WNDCLASSEXA wndclass;
84     WDML_INSTANCE*      thisInstance;
85     WDML_CONV*          pConv;
86     
87     TRACE("(0x%lx,%d,%d,%p)\n",idInst,hszService,hszTopic,pCC);
88     
89     thisInstance = WDML_FindInstance(idInst);
90     if (!thisInstance)
91     {
92         return 0;
93     }
94     
95     /* make sure this conv is never created */
96     pConv = WDML_FindConv(thisInstance, WDML_CLIENT_SIDE, hszService, hszTopic);
97     if (pConv != NULL)
98     {
99         ERR("This Conv already exists: (0x%lx)\n", (DWORD)pConv);
100         return (HCONV)pConv;
101     }
102     
103     /* we need to establish a conversation with
104        server, so create a window for it       */
105     
106     wndclass.cbSize        = sizeof(wndclass);
107     wndclass.style         = 0;
108     wndclass.lpfnWndProc   = WDML_ClientProc;
109     wndclass.cbClsExtra    = 0;
110     wndclass.cbWndExtra    = 2 * sizeof(DWORD);
111     wndclass.hInstance     = 0;
112     wndclass.hIcon         = 0;
113     wndclass.hCursor       = 0;
114     wndclass.hbrBackground = 0;
115     wndclass.lpszMenuName  = NULL;
116     wndclass.lpszClassName = szClientClassA;
117     wndclass.hIconSm       = 0;
118     
119     RegisterClassExA(&wndclass);
120     
121     hwndClient = CreateWindowA(szClientClassA, NULL, WS_POPUP, 0, 0, 0, 0, 0, 0, 0, 0);
122     
123     SetWindowLongA(hwndClient, 0, (DWORD)thisInstance);
124     
125     SendMessageA(HWND_BROADCAST, WM_DDE_INITIATE, (WPARAM)hwndClient,
126                  PackDDElParam(WM_DDE_INITIATE, (UINT)hszService, (UINT)hszTopic));
127     
128     if (UnpackDDElParam(WM_DDE_INITIATE, lParam, &uiLow, &uiHi))
129         FreeDDElParam(WM_DDE_INITIATE, lParam);
130     
131     TRACE("WM_DDE_INITIATE was processed\n");
132     /* At this point, Client WM_DDE_ACK should have saved hwndServer
133        for this instance id and hwndClient if server responds.
134        So get HCONV and return it. And add it to conv list */
135     pConv = (WDML_CONV*)GetWindowLongA(hwndClient, 4);
136     if (pConv == NULL || pConv->hwndServer == 0)
137     {
138         ERR(".. but no Server window available\n");
139         return 0;
140     }
141     /* finish init of pConv */
142     if (pCC != NULL)
143     {
144         pConv->convContext = *pCC;
145     }
146     
147     return (HCONV)pConv;
148 }
149
150 /*****************************************************************
151  *            DdeDisconnect   (USER32.@)
152  */
153 BOOL WINAPI DdeDisconnect(HCONV hConv)
154 {
155     WDML_CONV*  pConv = NULL;
156     
157     TRACE("(%ld)\n", (DWORD)hConv);
158     
159     if (hConv == 0)
160     {
161         ERR("DdeDisconnect(): hConv = 0\n");
162         return 0;
163     }
164     
165     pConv = WDML_GetConv(hConv);
166     if (pConv == NULL)
167     {
168         return FALSE;
169     }
170     if (!PostMessageA(pConv->hwndServer, WM_DDE_TERMINATE,
171                       (WPARAM)pConv->hwndClient, (LPARAM)hConv))
172     {
173         ERR("DdeDisconnect(): PostMessage returned 0\n");
174         return 0;
175     }
176     return TRUE;
177 }
178
179
180 /*****************************************************************
181  *            DdeReconnect   (DDEML.37)
182  *            DdeReconnect   (USER32.@)
183  */
184 HCONV WINAPI DdeReconnect(HCONV hConv)
185 {
186     FIXME("empty stub\n");
187     return 0;
188 }
189
190 typedef enum {
191     WDML_QS_ERROR, WDML_QS_HANDLED, WDML_QS_PASS
192 } WDML_QUEUE_STATE;
193
194 /******************************************************************
195  *              WDML_QueueAdvise
196  *
197  * Creates and queue an WM_DDE_ADVISE transaction
198  */
199 static WDML_XACT*       WDML_QueueAdvise(WDML_CONV* pConv, UINT wType, UINT wFmt, HSZ hszItem)
200 {
201     DDEADVISE*          pDdeAdvise;
202     WDML_XACT*          pXAct;
203
204     TRACE("XTYP_ADVSTART (with%s data) transaction\n", (wType & XTYPF_NODATA) ? "out" : "");
205
206     pXAct = WDML_AllocTransaction(pConv->thisInstance, WM_DDE_ADVISE);
207     if (!pXAct)
208         return NULL;
209
210     pXAct->u.advise.wType = wType & ~0x0F;
211     pXAct->u.advise.wFmt = wFmt;
212     pXAct->u.advise.hszItem = hszItem;
213     pXAct->u.advise.hDdeAdvise = GlobalAlloc(GHND | GMEM_DDESHARE, sizeof(DDEADVISE));
214     
215     /* pack DdeAdvise   */
216     pDdeAdvise = (DDEADVISE*)GlobalLock(pXAct->u.advise.hDdeAdvise);
217     pDdeAdvise->fAckReq   = (wType & XTYPF_ACKREQ) ? TRUE : FALSE;
218     pDdeAdvise->fDeferUpd = (wType & XTYPF_NODATA) ? TRUE : FALSE;
219     pDdeAdvise->cfFormat  = wFmt;
220     GlobalUnlock(pXAct->u.advise.hDdeAdvise);
221
222     WDML_QueueTransaction(pConv, pXAct);
223
224     if (!PostMessageA(pConv->hwndServer, WM_DDE_ADVISE, (WPARAM)pConv->hwndClient,
225                       PackDDElParam(WM_DDE_ADVISE, (UINT)pXAct->u.advise.hDdeAdvise, (UINT)hszItem)))
226     {
227         GlobalFree(pXAct->u.advise.hDdeAdvise);
228         WDML_UnQueueTransaction(pConv, pXAct);
229         WDML_FreeTransaction(pXAct);
230         return NULL;
231     }
232
233     return pXAct;
234 }
235
236 /******************************************************************
237  *              WDML_HandleAdviseReply
238  *
239  * handles the reply to an advise request
240  */
241 static WDML_QUEUE_STATE WDML_HandleAdviseReply(WDML_CONV* pConv, MSG* msg, WDML_XACT* pXAct)
242 {
243     DDEACK              ddeAck;
244     UINT                uiLo, uiHi;
245     WORD                wStatus;
246     
247     if (msg->message != WM_DDE_ACK || msg->wParam != pConv->hwndServer)
248     {
249         return WDML_QS_PASS;
250     }
251
252     UnpackDDElParam(WM_DDE_ACK, msg->lParam, &uiLo, &uiHi);
253
254     if (DdeCmpStringHandles(uiHi, pXAct->u.advise.hszItem) != 0)
255         return WDML_QS_PASS;
256
257     GlobalDeleteAtom(uiHi);
258
259     wStatus = uiLo;
260     ddeAck = *((DDEACK*)&wStatus);
261             
262     if (ddeAck.fAck)
263     {
264         WDML_LINK*      pLink;
265         
266         /* billx: first to see if the link is already created. */
267         pLink = WDML_FindLink(pConv->thisInstance, (HCONV)pConv, WDML_CLIENT_SIDE, 
268                               pXAct->u.advise.hszItem, pXAct->u.advise.wFmt);
269         if (pLink != NULL)      
270         {       
271             /* we found a link, and only need to modify it in case it changes */
272             pLink->transactionType = pXAct->u.advise.wType;
273         }
274         else
275         {
276             TRACE("Adding Link with hConv = 0x%lx\n", (DWORD)pConv);
277             WDML_AddLink(pConv->thisInstance, (HCONV)pConv, WDML_CLIENT_SIDE, 
278                          pXAct->u.advise.wType, pXAct->u.advise.hszItem, 
279                          pXAct->u.advise.wFmt);
280         }
281     }
282     else
283     {
284         TRACE("Returning TRUE on XTYP_ADVSTART - fAck was FALSE\n");
285         GlobalFree(pXAct->u.advise.hDdeAdvise);
286     }
287     pXAct->hDdeData = (HDDEDATA)1;
288     return WDML_QS_HANDLED;
289 }
290
291 /******************************************************************
292  *              WDML_QueueUnadvise
293  *
294  * queues an unadvise transaction
295  */
296 static WDML_XACT*       WDML_QueueUnadvise(WDML_CONV* pConv, UINT wFmt, HSZ hszItem)
297 {
298     WDML_XACT*  pXAct;
299     
300     TRACE("XTYP_ADVSTOP transaction\n");
301
302     pXAct = WDML_AllocTransaction(pConv->thisInstance, WM_DDE_UNADVISE);
303     if (!pXAct)
304         return NULL;
305
306     pXAct->u.unadvise.wFmt = wFmt;
307     pXAct->u.unadvise.hszItem = hszItem;
308
309     WDML_QueueTransaction(pConv, pXAct);
310
311    /* end advise loop: post WM_DDE_UNADVISE to server to terminate link
312        on the specified item. */
313             
314     if (!PostMessageA(pConv->hwndServer, WM_DDE_UNADVISE, (WPARAM)pConv->hwndClient,
315                       PackDDElParam(WM_DDE_UNADVISE, wFmt, (UINT)hszItem)))
316     {
317         WDML_UnQueueTransaction(pConv, pXAct);
318         WDML_FreeTransaction(pXAct);
319         return NULL;
320     }
321     return pXAct;
322 }
323     
324 /******************************************************************
325  *              WDML_HandleUnadviseReply
326  *
327  *
328  */
329 static WDML_QUEUE_STATE WDML_HandleUnadviseReply(WDML_CONV* pConv, MSG* msg, WDML_XACT* pXAct)
330 {
331     DDEACK      ddeAck;
332     UINT        uiLo, uiHi;
333     WORD        wStatus;
334
335     if (msg->message != WM_DDE_ACK || msg->wParam != pConv->hwndServer)
336     {
337         return WDML_QS_PASS;
338     }
339
340     UnpackDDElParam(WM_DDE_ACK, msg->lParam, &uiLo, &uiHi);
341
342     if (DdeCmpStringHandles(uiHi, pXAct->u.unadvise.hszItem) != 0)
343         return WDML_QS_PASS;
344
345     GlobalDeleteAtom(uiHi);
346                     
347     wStatus = uiLo;
348     ddeAck = *((DDEACK*)&wStatus);
349                     
350     TRACE("WM_DDE_ACK received while waiting for a timeout\n");
351             
352     if (!ddeAck.fAck)
353     {
354         TRACE("Returning TRUE on XTYP_ADVSTOP - fAck was FALSE\n");
355     }
356     else
357     {
358         /* billx: remove the link */
359         WDML_RemoveLink(pConv->thisInstance, (HCONV)pConv, WDML_CLIENT_SIDE, 
360                         pXAct->u.unadvise.hszItem, pXAct->u.unadvise.wFmt);
361     }
362     pXAct->hDdeData = (HDDEDATA)1;
363     return WDML_QS_HANDLED;
364 }
365
366 /******************************************************************
367  *              WDML_QueueRequest
368  *
369  *
370  */
371 static WDML_XACT*       WDML_QueueRequest(WDML_CONV* pConv, UINT wFmt, HSZ hszItem)
372 {
373     WDML_XACT*  pXAct;
374     
375     TRACE("XTYP_REQUEST transaction\n");
376
377     pXAct = WDML_AllocTransaction(pConv->thisInstance, WM_DDE_REQUEST);
378     if (!pXAct)
379         return NULL;
380
381     pXAct->u.request.hszItem = hszItem;
382
383     WDML_QueueTransaction(pConv, pXAct);
384
385    /* end advise loop: post WM_DDE_UNADVISE to server to terminate link
386     * on the specified item. 
387     */
388             
389     if (!PostMessageA(pConv->hwndServer, WM_DDE_REQUEST, (WPARAM)pConv->hwndClient,
390                       PackDDElParam(WM_DDE_REQUEST, wFmt, (UINT)hszItem)))
391     {
392         WDML_UnQueueTransaction(pConv, pXAct);
393         WDML_FreeTransaction(pXAct);
394         return NULL;
395     }
396     return pXAct;
397 }
398
399 /******************************************************************
400  *              WDML_HandleRequestReply
401  *
402  *
403  */
404 static WDML_QUEUE_STATE WDML_HandleRequestReply(WDML_CONV* pConv, MSG* msg, WDML_XACT* pXAct)
405 {
406     DDEACK      ddeAck;
407     UINT        uiLo, uiHi;
408     WORD        wStatus;
409
410     switch (msg->message)
411     {
412     case WM_DDE_ACK:
413         if (msg->wParam != pConv->hwndServer)
414             return WDML_QS_PASS;
415         UnpackDDElParam(WM_DDE_ACK, msg->lParam, &uiLo, &uiHi);
416         wStatus = uiLo;
417         ddeAck = *((DDEACK*)&wStatus);
418         pXAct->hDdeData = 0;
419         TRACE("Negative answer...\n");
420                 
421         /* FIXME: billx: we should return 0 and post a negatve WM_DDE_ACK. */
422         break;
423
424     case WM_DDE_DATA:
425         if (msg->wParam != pConv->hwndServer)
426             return WDML_QS_PASS;
427         UnpackDDElParam(WM_DDE_DATA, msg->lParam, &uiLo, &uiHi);
428         TRACE("Got the result (%08lx)\n", (DWORD)uiLo);
429         if (DdeCmpStringHandles(uiHi, pXAct->u.request.hszItem) != 0)
430             return WDML_QS_PASS;
431         /* FIXME: memory clean up ? */
432         pXAct->hDdeData = WDML_Global2DataHandle((HGLOBAL)uiLo);
433         break;
434
435     default:
436         return WDML_QS_PASS;
437     }
438
439     return WDML_QS_HANDLED;
440 }       
441
442 /******************************************************************
443  *              WDML_QueueExecute
444  *
445  *
446  */
447 static WDML_XACT*       WDML_QueueExecute(WDML_CONV* pConv, LPCVOID pData, DWORD cbData)
448 {
449     WDML_XACT*  pXAct;
450
451     TRACE("XTYP_EXECUTE transaction\n");
452         
453     pXAct = WDML_AllocTransaction(pConv->thisInstance, WM_DDE_EXECUTE);
454     if (!pXAct)
455         return NULL;
456
457     if (cbData == (DWORD)-1)
458     {
459         HDDEDATA                hDdeData = (HDDEDATA)pData;
460         DWORD                   dwSize;
461     
462         pData = DdeAccessData(hDdeData, &dwSize);
463         if (pData)
464         {
465             pXAct->u.execute.hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, dwSize);
466             if (pXAct->u.execute.hMem)
467             {
468                 LPBYTE  pDst;
469             
470                 pDst = GlobalLock(pXAct->u.execute.hMem);
471                 if (pDst)
472                 {
473                     memcpy(pDst, pData, dwSize);
474                     GlobalUnlock(pXAct->u.execute.hMem);
475                 }
476                 else
477                 {
478                     GlobalFree(pXAct->u.execute.hMem);
479                     pXAct->u.execute.hMem = 0;
480                 }
481             }
482             DdeUnaccessData(hDdeData);
483         }
484         else
485         {
486             pXAct->u.execute.hMem = 0;
487         }
488     }
489     else
490     {
491         LPSTR   ptr;
492
493         pXAct->u.execute.hMem = GlobalAlloc(GHND | GMEM_DDESHARE, cbData);
494         ptr = GlobalLock(pXAct->u.execute.hMem);
495         if (ptr) 
496         {
497             memcpy(ptr, pData, cbData);
498             GlobalUnlock(pXAct->u.execute.hMem);
499         }
500     }
501
502     WDML_QueueTransaction(pConv, pXAct);
503         
504     if (!PostMessageA(pConv->hwndServer, WM_DDE_EXECUTE, (WPARAM)pConv->hwndClient, 
505                       pXAct->u.execute.hMem))
506     {
507         GlobalFree(pXAct->u.execute.hMem);
508         WDML_UnQueueTransaction(pConv, pXAct);
509         WDML_FreeTransaction(pXAct);
510         TRACE("Returning FALSE on XTYP_EXECUTE - PostMessage returned FALSE\n");
511         return NULL;
512     }
513     return pXAct;
514 }
515
516 /******************************************************************
517  *              WDML_HandleExecuteReply
518  *
519  *
520  */
521 static WDML_QUEUE_STATE WDML_HandleExecuteReply(WDML_CONV* pConv, MSG* msg, WDML_XACT* pXAct)
522 {
523     DDEACK      ddeAck;
524     UINT        uiLo, uiHi;
525     WORD        wStatus;
526
527     if (msg->message != WM_DDE_ACK || msg->wParam != pConv->hwndServer)
528     {
529         return WDML_QS_PASS;
530     }
531
532     UnpackDDElParam(WM_DDE_ACK, msg->lParam, &uiLo, &uiHi);
533     FreeDDElParam(WM_DDE_ACK, msg->lParam);
534
535     if (uiHi != pXAct->u.execute.hMem)
536     {
537         return WDML_QS_PASS;
538     }
539
540     wStatus = uiLo;
541     ddeAck = *((DDEACK*)&wStatus);
542     if (!ddeAck.fAck)
543     {
544         GlobalFree(pXAct->u.execute.hMem);
545     }
546     pXAct->hDdeData = (HDDEDATA)1;
547     return WDML_QS_HANDLED;
548 }
549
550 /******************************************************************
551  *              WDML_QueuePoke
552  *
553  *
554  */
555 static WDML_XACT*       WDML_QueuePoke(WDML_CONV* pConv, LPCVOID pData, DWORD cbData, 
556                                        UINT wFmt, HSZ hszItem)
557 {
558     WDML_XACT*  pXAct;
559
560     TRACE("XTYP_POKE transaction\n");
561         
562     pXAct = WDML_AllocTransaction(pConv->thisInstance, WM_DDE_POKE);
563     if (!pXAct)
564         return NULL;
565
566     if (cbData == (DWORD)-1)
567     {
568         pXAct->u.poke.hMem = (HDDEDATA)pData;
569     }
570     else
571     {
572         DDEPOKE*        ddePoke;
573
574         pXAct->u.poke.hMem = GlobalAlloc(GHND | GMEM_DDESHARE, sizeof(DDEPOKE) + cbData);
575         ddePoke = GlobalLock(pXAct->u.poke.hMem);
576         if (ddePoke) 
577         {
578             memcpy(ddePoke->Value, pData, cbData);
579             ddePoke->fRelease = FALSE; /* FIXME: app owned ? */
580             ddePoke->cfFormat = wFmt;
581             GlobalUnlock(pXAct->u.poke.hMem);
582         }
583     }
584
585     pXAct->u.poke.hszItem = hszItem;
586
587     WDML_QueueTransaction(pConv, pXAct);
588         
589     if (!PostMessageA(pConv->hwndServer, WM_DDE_POKE, (WPARAM)pConv->hwndClient, 
590                       PackDDElParam(WM_DDE_POKE, pXAct->u.execute.hMem, hszItem)))
591     {
592         GlobalFree(pXAct->u.execute.hMem);
593         WDML_UnQueueTransaction(pConv, pXAct);
594         WDML_FreeTransaction(pXAct);
595         TRACE("Returning FALSE on XTYP_POKE - PostMessage returned FALSE\n");
596         return NULL;
597     }
598     return pXAct;
599 }
600
601 /******************************************************************
602  *              WDML_HandlePokeReply
603  *
604  *
605  */
606 static WDML_QUEUE_STATE WDML_HandlePokeReply(WDML_CONV* pConv, MSG* msg, WDML_XACT* pXAct)
607 {
608     DDEACK      ddeAck;
609     UINT        uiLo, uiHi;
610     WORD        wStatus;
611
612     if (msg->message != WM_DDE_ACK && msg->wParam != pConv->hwndServer)
613     {
614         return WDML_QS_PASS;
615     }
616
617     UnpackDDElParam(WM_DDE_ACK, msg->lParam, &uiLo, &uiHi);
618     if (uiHi != pXAct->u.poke.hszItem)
619     {
620         return WDML_QS_PASS;
621     }
622     FreeDDElParam(WM_DDE_ACK, msg->lParam);
623
624     wStatus = uiLo;
625     ddeAck = *((DDEACK*)&wStatus);
626     if (!ddeAck.fAck)
627     {
628         GlobalFree(pXAct->u.poke.hMem);
629     }
630     pXAct->hDdeData = (HDDEDATA)TRUE;
631     return TRUE;
632 }
633
634 /******************************************************************
635  *              WDML_HandleReplyData
636  *
637  *
638  */
639 static WDML_QUEUE_STATE WDML_HandleReplyData(WDML_CONV* pConv, MSG* msg, HDDEDATA* hdd)
640 {
641     UINT        uiLo, uiHi;
642     HDDEDATA    hDdeDataIn, hDdeDataOut;
643     WDML_LINK*  pLink;
644
645     TRACE("WM_DDE_DATA message received in the Client Proc!\n");
646     /* wParam -- sending window handle  */
647     /* lParam -- hDdeData & item HSZ    */
648         
649     UnpackDDElParam(WM_DDE_DATA, msg->lParam, &uiLo, &uiHi);
650         
651     hDdeDataIn = WDML_Global2DataHandle((HGLOBAL)uiLo);
652
653     /* billx: 
654      *  For hot link, data should be passed to its callback with
655      * XTYP_ADVDATA and callback should return the proper status.
656      */
657         
658     for (pLink = pConv->thisInstance->links[WDML_CLIENT_SIDE]; pLink != NULL; pLink = pLink->next)
659     {
660         if (DdeCmpStringHandles((HSZ)uiHi, pLink->hszItem) == 0)
661         {
662             BOOL        fRelease = FALSE;
663             BOOL        fAckReq = FALSE;
664             DDEDATA*    pDdeData;
665
666             /* item in the advise loop */
667             pConv = WDML_GetConv(pLink->hConv);
668             if (pConv == NULL)
669             {
670                 continue;
671             }
672             if ((pDdeData = GlobalLock(uiLo)) != NULL)
673             {
674                 fRelease = pDdeData->fRelease;
675                 fAckReq = pDdeData->fAckReq;
676             }
677
678             if (hDdeDataIn != 0)
679             {
680                 if (fAckReq)
681                 {
682                     DDEACK      ddeAck;
683                     
684                     ddeAck.bAppReturnCode = 0;
685                     ddeAck.reserved       = 0;
686                     ddeAck.fBusy          = FALSE;
687                     ddeAck.fAck           = TRUE;
688                     
689                     if (msg->lParam) {
690                         PostMessageA(pConv->hwndServer, WM_DDE_ACK, pConv->hwndClient, 
691                                      ReuseDDElParam(msg->lParam, WM_DDE_DATA, WM_DDE_ACK, 
692                                                     *(WORD*)&ddeAck, (UINT)pLink->hszItem));
693                         msg->lParam = 0L;
694                     }
695                     else 
696                     {
697                         PostMessageA(pConv->hwndServer, WM_DDE_ACK, pConv->hwndClient, 
698                                      PackDDElParam(WM_DDE_ACK, *(WORD*)&ddeAck, (UINT)pLink->hszItem));
699                     }   
700                 }
701             }
702             hDdeDataOut = 0;
703             if (pConv->thisInstance->callback != NULL /*&& thisInstance->processID == GetCurrentProcessId() */)
704             {
705                 TRACE("Calling the callback, type = XTYP_ADVDATA, CB = 0x%lx, hConv = 0x%lx\n", 
706                       (DWORD)pConv->thisInstance->callback, (DWORD)pLink->hConv);
707                 hDdeDataOut = (pConv->thisInstance->callback)(XTYP_ADVDATA,
708                                                               pLink->uFmt,
709                                                               pLink->hConv,
710                                                               pConv->hszTopic,
711                                                               pLink->hszItem,
712                                                               hDdeDataIn, 
713                                                               0, 0);
714                 if (hDdeDataOut == (HDDEDATA)DDE_FACK)
715                 {
716                     pLink->hDdeData = hDdeDataIn;
717                 }
718             }
719 #if 0
720             if (fRelease) 
721             {
722                 DdeFreeDataHandle(hDdeDataIn);
723             }
724 #endif
725             break;
726         }
727     }
728     
729     if (msg->lParam)
730         FreeDDElParam(WM_DDE_DATA, msg->lParam);
731         
732     return WDML_QS_HANDLED;
733 }
734
735 /******************************************************************
736  *              WDML_HandleReplyTerminate
737  *
738  *
739  */
740 static WDML_QUEUE_STATE WDML_HandleReplyTerminate(WDML_CONV* pConv, MSG* msg, HDDEDATA* hdd)
741 {
742     if ((LPARAM)pConv != msg->lParam)
743         return WDML_QS_PASS;
744
745     /* billx: clean up the conv and associated links */
746     WDML_RemoveAllLinks(pConv->thisInstance, (HCONV)pConv, WDML_CLIENT_SIDE);
747     WDML_RemoveConv(pConv->thisInstance, WDML_CLIENT_SIDE, (HCONV)pConv);
748     DestroyWindow(msg->hwnd);
749     return WDML_QS_HANDLED;
750 }
751
752 /******************************************************************
753  *              WDML_HandleReply
754  *
755  * handles any incoming reply, and try to match to an already sent request
756  */
757 static WDML_QUEUE_STATE WDML_HandleReply(WDML_CONV* pConv, MSG* msg, HDDEDATA* hdd)
758 {
759     WDML_XACT*  pXAct = pConv->transactions;
760     WDML_QUEUE_STATE    qs;
761
762     if (pConv->transactions) 
763     {
764         /* first check message against a pending transaction, if any */
765         switch (pXAct->ddeMsg)
766         {
767         case WM_DDE_ADVISE:
768             qs = WDML_HandleAdviseReply(pConv, msg, pXAct);
769             break;
770         case WM_DDE_UNADVISE:
771             qs = WDML_HandleUnadviseReply(pConv, msg, pXAct);
772             break;
773         case WM_DDE_EXECUTE:
774             qs = WDML_HandleExecuteReply(pConv, msg, pXAct);
775             break;
776         case WM_DDE_REQUEST:
777             qs = WDML_HandleRequestReply(pConv, msg, pXAct);
778             break;
779         case WM_DDE_POKE:
780             qs = WDML_HandlePokeReply(pConv, msg, pXAct);
781             break;
782         default:
783             qs = WDML_QS_ERROR;
784             FIXME("oooch\n");
785         }
786     }
787     else
788     {
789         qs = WDML_QS_PASS;
790     }
791
792     /* now check the results */
793     switch (qs) 
794     {
795     case WDML_QS_ERROR:
796         *hdd = 0;
797         break;
798     case WDML_QS_HANDLED:
799         /* ok, we have resolved a pending transaction
800          * notify callback if asynchronous, and remove it in any case
801          */
802         WDML_UnQueueTransaction(pConv, pXAct);
803         if (pXAct->dwTimeout == TIMEOUT_ASYNC)
804         {
805             if (pConv->thisInstance->callback != NULL /*&& thisInstance->processID == GetCurrentProcessId() */)
806             {
807                 TRACE("Calling the callback, type = XTYP_XACT_COMPLETE, CB = 0x%lx, hConv = 0x%lx\n",
808                       (DWORD)pConv->thisInstance->callback, (DWORD)pConv);
809                 (pConv->thisInstance->callback)(XTYP_XACT_COMPLETE, 0 /* FIXME */,
810                                                 (HCONV)pConv,
811                                                 pConv->hszTopic, 0 /* FIXME */,
812                                                 pXAct->hDdeData, 
813                                                 MAKELONG(0, pXAct->xActID), 
814                                                 0 /* FIXME */);
815                 qs = WDML_QS_PASS;
816             }
817         }       
818         else
819         {
820             *hdd = pXAct->hDdeData;
821         }
822         WDML_FreeTransaction(pXAct);
823         break;
824     case WDML_QS_PASS:
825         /* no pending transaction found, try a warm link or a termination request */
826         switch (msg->message)
827         {
828         case WM_DDE_DATA:
829             qs = WDML_HandleReplyData(pConv, msg, hdd);
830             break;
831         case WM_DDE_TERMINATE:
832             qs = WDML_HandleReplyTerminate(pConv, msg, hdd);
833             break;
834         }
835         break;
836     }
837
838     return qs;
839 }
840
841 /******************************************************************
842  *              WDML_SyncWaitTransactionReply
843  *
844  * waits until an answer for a sent request is received
845  * time out is also handled. only used for synchronous transactions
846  */
847 static HDDEDATA WDML_SyncWaitTransactionReply(HCONV hConv, DWORD dwTimeout, WDML_XACT* pXAct)
848 {
849     DWORD               dwTime;
850
851     TRACE("Starting wait for a timeout of %ld ms\n", dwTimeout);
852
853     /* FIXME: time 32 bit wrap around */
854     dwTimeout += GetCurrentTime();
855             
856     while ((dwTime = GetCurrentTime()) < dwTimeout)
857     {
858         /* we cannot hold the mutex all the time because when client and server run in a
859          * single process they need to share the access to the internal data
860          */
861         if (MsgWaitForMultipleObjects(0, NULL, FALSE, 
862                                       dwTime - dwTimeout, QS_POSTMESSAGE) == WAIT_OBJECT_0 &&
863             WDML_WaitForMutex(handle_mutex))
864         {
865             BOOL        ret = FALSE;
866             MSG         msg;
867             WDML_CONV*  pConv;
868             HDDEDATA    hdd;
869             
870             pConv = WDML_GetConv(hConv);
871             if (pConv == NULL)
872             {
873                 /* conversation no longer available... return failure */
874                 break;
875             }
876             while (PeekMessageA(&msg, pConv->hwndClient, WM_DDE_FIRST, WM_DDE_LAST, PM_REMOVE))
877             {
878                 /* check that either pXAct has been processed or no more xActions are pending */
879                 ret = (pConv->transactions == pXAct);
880                 ret = WDML_HandleReply(pConv, &msg, &hdd) == WDML_QS_HANDLED && 
881                     (pConv->transactions == NULL || ret);
882                 if (ret) break;
883             }
884             WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
885             if (ret)
886             {
887                 return hdd;
888             }
889         }
890     }
891
892     TRACE("Timeout !!\n");
893     if (WDML_WaitForMutex(handle_mutex))
894     {
895         DWORD           err;
896         WDML_CONV*      pConv;
897
898         pConv = WDML_GetConv(hConv);
899         if (pConv == NULL)
900         {
901             return 0;
902         } 
903         switch (pConv->transactions->ddeMsg)
904         {
905         case WM_DDE_ADVISE:     err = DMLERR_ADVACKTIMEOUT;     break;
906         case WM_DDE_REQUEST:    err = DMLERR_DATAACKTIMEOUT;    break;
907         case WM_DDE_EXECUTE:    err = DMLERR_EXECACKTIMEOUT;    break;
908         case WM_DDE_POKE:       err = DMLERR_POKEACKTIMEOUT;    break;
909         case WM_DDE_UNADVISE:   err = DMLERR_UNADVACKTIMEOUT;   break;
910         default:                err = DMLERR_INVALIDPARAMETER;  break;
911         }
912
913         pConv->thisInstance->lastError = err;
914         WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
915     }
916     return 0;
917 }
918
919 /*****************************************************************
920  *            DdeClientTransaction  (USER32.@)
921  */
922 HDDEDATA WINAPI DdeClientTransaction(LPBYTE pData, DWORD cbData, HCONV hConv, HSZ hszItem, UINT wFmt,
923                                      UINT wType, DWORD dwTimeout, LPDWORD pdwResult)
924 {
925     WDML_CONV*          pConv;
926     WDML_XACT*          pXAct;
927     HDDEDATA            hDdeData = 0;
928     
929     TRACE("(0x%lx,%ld,0x%lx,0x%lx,%d,%d,%ld,0x%lx)\n",
930           (ULONG)pData,cbData,(DWORD)hConv,(DWORD)hszItem,wFmt,wType,
931           dwTimeout,(ULONG)pdwResult);
932     
933     if (hConv == 0)
934     {
935         ERR("Invalid conversation handle\n");
936         return 0;
937     }
938     
939     if (!WDML_WaitForMutex(handle_mutex))
940     {
941         return FALSE;
942     }
943
944     pConv = WDML_GetConv(hConv);
945     if (pConv == NULL)
946     {
947         /* cannot set error... cannot get back to DDE instance */
948         goto theError;
949     }
950
951     switch (wType)
952     {
953     case XTYP_EXECUTE:
954         if (hszItem != 0 || wFmt != 0)
955         {
956             pConv->thisInstance->lastError = DMLERR_INVALIDPARAMETER;
957             goto theError;
958         }
959         pXAct = WDML_QueueExecute(pConv, pData, cbData);
960         break;
961     case XTYP_POKE:
962         pXAct = WDML_QueuePoke(pConv, pData, cbData, wFmt, hszItem);
963         break;
964     case XTYP_ADVSTART|XTYPF_NODATA:
965     case XTYP_ADVSTART|XTYPF_NODATA|XTYPF_ACKREQ:
966     case XTYP_ADVSTART:
967     case XTYP_ADVSTART|XTYPF_ACKREQ:
968         if (pData)
969         {
970             pConv->thisInstance->lastError = DMLERR_INVALIDPARAMETER;
971             goto theError;
972         }
973         pXAct = WDML_QueueAdvise(pConv, wType, wFmt, hszItem);
974         break;
975     case XTYP_ADVSTOP:
976         if (pData)
977         {
978             pConv->thisInstance->lastError = DMLERR_INVALIDPARAMETER;
979             goto theError;
980         }
981         pXAct = WDML_QueueUnadvise(pConv, wFmt, hszItem);
982         break;
983     case XTYP_REQUEST:
984         if (pData)
985         {
986             pConv->thisInstance->lastError = DMLERR_INVALIDPARAMETER;
987             goto theError;
988         }
989         pXAct = WDML_QueueRequest(pConv, wFmt, hszItem);
990         break;
991     default:
992         FIXME("Unknown transation\n");
993         /* unknown transaction type */
994         pConv->thisInstance->lastError = DMLERR_INVALIDPARAMETER;
995         goto theError;
996     }
997
998     pXAct->dwTimeout = dwTimeout;
999     /* FIXME: should set the app bits on *pdwResult */
1000     
1001     if (dwTimeout == TIMEOUT_ASYNC)
1002     {
1003         if (pdwResult)
1004         {
1005             *pdwResult = MAKELONG(0, pXAct->xActID);
1006         }
1007         hDdeData = (HDDEDATA)1;
1008     } 
1009
1010     WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
1011
1012     if (dwTimeout != TIMEOUT_ASYNC)
1013     {
1014         DWORD   count = 0;
1015
1016         if (pdwResult)
1017         {
1018             *pdwResult = 0L;
1019         }
1020         while (ReleaseMutex(handle_mutex))
1021             count++;
1022         hDdeData = WDML_SyncWaitTransactionReply((HCONV)pConv, dwTimeout, pXAct);
1023         while (count-- != 0)
1024             WDML_WaitForMutex(handle_mutex);
1025     }
1026     return hDdeData;
1027  theError:
1028     WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
1029     return 0;
1030 }
1031
1032 /******************************************************************
1033  *              WDML_ClientProc
1034  *
1035  * Window Proc created on client side for each conversation
1036  */
1037 static LRESULT CALLBACK WDML_ClientProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
1038 {
1039     UINT                uiLow, uiHi;
1040     
1041     if (iMsg == WM_DDE_ACK &&
1042         /* In response to WM_DDE_INITIATE, save server window  */
1043         UnpackDDElParam(WM_DDE_ACK, lParam, &uiLow, &uiHi) &&
1044         (WDML_CONV*)GetWindowLongA(hwnd, 4) == NULL)
1045     {
1046         WDML_INSTANCE*  thisInstance = NULL;
1047         WDML_CONV*      pConv = NULL;
1048
1049         FreeDDElParam(WM_DDE_ACK, lParam);
1050         /* no converstation yet, add it */
1051         thisInstance = (WDML_INSTANCE*)GetWindowLongA(hwnd, 0);
1052         pConv = WDML_AddConv(thisInstance, WDML_CLIENT_SIDE, (HSZ)uiLow, (HSZ)uiHi, 
1053                              hwnd, (HWND)wParam);
1054         SetWindowLongA(hwnd, 4, (DWORD)pConv);
1055         /* FIXME: so far we only use the first window in the list... */
1056         return 0;
1057     }
1058         
1059     if ((iMsg >= WM_DDE_FIRST && iMsg <= WM_DDE_LAST) && WDML_WaitForMutex(handle_mutex))
1060     {
1061         WDML_CONV*      pConv = (WDML_CONV*)GetWindowLongA(hwnd, 4);
1062
1063         if (pConv) 
1064         {
1065             MSG         msg;
1066             HDDEDATA    hdd;
1067
1068             msg.hwnd = hwnd;
1069             msg.message = iMsg;
1070             msg.wParam = wParam;
1071             msg.lParam = lParam;
1072
1073             WDML_HandleReply(pConv, &msg, &hdd);
1074         }
1075
1076         WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
1077         return 0;
1078     }
1079         
1080     return DefWindowProcA(hwnd, iMsg, wParam, lParam);
1081 }
1082
1083
1084 /*****************************************************************
1085  *            DdeAbandonTransaction (USER32.@)
1086  */
1087 BOOL WINAPI DdeAbandonTransaction(DWORD idInst, HCONV hConv, DWORD idTransaction)
1088 {
1089     FIXME("empty stub\n");
1090     return TRUE;
1091 }
1092
1093
1094 /*****************************************************************
1095  *            DdeImpersonateClient (USER32.@)
1096  */
1097 BOOL WINAPI DdeImpersonateClient(HCONV hConv)
1098 {
1099     WDML_CONV*  pConv;
1100     BOOL        ret = FALSE;
1101     
1102     if (!WDML_WaitForMutex(handle_mutex))
1103     {
1104         return FALSE;
1105     }
1106     pConv = WDML_GetConv(hConv);
1107     if (pConv)
1108     {
1109         ret = ImpersonateDdeClientWindow(pConv->hwndClient, pConv->hwndServer);
1110     }
1111     WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
1112     return ret;
1113 }