4 * Copyright 1997 Alexandre Julliard
5 * Copyright 1997 Len White
6 * Copyright 1999 Keith Matthews
8 * Copyright 2001 Eric Pouech
9 * Copyright 2004, 2005 Dmitry Timoshkov
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
37 #include "wine/debug.h"
38 #include "dde_private.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(ddeml);
42 static LRESULT CALLBACK WDML_ClientProc(HWND, UINT, WPARAM, LPARAM); /* only for one client, not conv list */
43 const WCHAR WDML_szClientConvClass[] = {'W','i','n','e','D','d','e','C','l','i','e','n','t',0};
45 /******************************************************************************
46 * DdeConnectList [USER32.@] Establishes conversation with DDE servers
49 * idInst [I] Instance identifier
50 * hszService [I] Handle to service name string
51 * hszTopic [I] Handle to topic name string
52 * hConvList [I] Handle to conversation list
53 * pCC [I] Pointer to structure with context data
56 * Success: Handle to new conversation list
59 HCONVLIST WINAPI DdeConnectList(DWORD idInst, HSZ hszService, HSZ hszTopic,
60 HCONVLIST hConvList, PCONVCONTEXT pCC)
62 FIXME("(%ld,%p,%p,%p,%p): stub\n", idInst, hszService, hszTopic, hConvList, pCC);
66 /*****************************************************************
67 * DdeQueryNextServer [USER32.@]
69 HCONV WINAPI DdeQueryNextServer(HCONVLIST hConvList, HCONV hConvPrev)
71 FIXME("(%p,%p): stub\n", hConvList, hConvPrev);
75 /******************************************************************************
76 * DdeDisconnectList [USER32.@] Destroys list and terminates conversations
80 * hConvList [I] Handle to conversation list
86 BOOL WINAPI DdeDisconnectList(HCONVLIST hConvList)
88 FIXME("(%p): stub\n", hConvList);
92 /*****************************************************************
93 * DdeConnect (USER32.@)
95 HCONV WINAPI DdeConnect(DWORD idInst, HSZ hszService, HSZ hszTopic,
99 WDML_INSTANCE* pInstance;
100 WDML_CONV* pConv = NULL;
101 ATOM aSrv = 0, aTpc = 0;
102 WNDCLASSEXW wndclass;
104 TRACE("(0x%lx,%p,%p,%p)\n", idInst, hszService, hszTopic, pCC);
106 EnterCriticalSection(&WDML_CritSect);
108 pInstance = WDML_GetInstance(idInst);
114 /* make sure this conv is never created */
115 pConv = WDML_FindConv(pInstance, WDML_CLIENT_SIDE, hszService, hszTopic);
118 ERR("This Conv already exists: (%p)\n", pConv);
122 /* we need to establish a conversation with
123 server, so create a window for it */
125 wndclass.cbSize = sizeof(wndclass);
127 wndclass.lpfnWndProc = WDML_ClientProc;
128 wndclass.cbClsExtra = 0;
129 wndclass.cbWndExtra = 2 * sizeof(ULONG_PTR);
130 wndclass.hInstance = 0;
132 wndclass.hCursor = 0;
133 wndclass.hbrBackground = 0;
134 wndclass.lpszMenuName = NULL;
135 wndclass.lpszClassName = WDML_szClientConvClass;
136 wndclass.hIconSm = 0;
138 RegisterClassExW(&wndclass);
140 hwndClient = CreateWindowW(WDML_szClientConvClass, NULL, WS_POPUP, 0, 0, 0, 0, 0, 0, 0, 0);
142 SetWindowLongPtrW(hwndClient, GWL_WDML_INSTANCE, (ULONG_PTR)pInstance);
146 aSrv = WDML_MakeAtomFromHsz(hszService);
147 if (!aSrv) goto theEnd;
151 aTpc = WDML_MakeAtomFromHsz(hszTopic);
152 if (!aTpc) goto theEnd;
155 LeaveCriticalSection(&WDML_CritSect);
157 /* note: sent messages shall not use packing */
158 SendMessageTimeoutW( HWND_BROADCAST, WM_DDE_INITIATE, (WPARAM)hwndClient, MAKELPARAM(aSrv, aTpc),
159 SMTO_ABORTIFHUNG, 2000, NULL );
161 EnterCriticalSection(&WDML_CritSect);
163 pInstance = WDML_GetInstance(idInst);
169 /* At this point, Client WM_DDE_ACK should have saved hwndServer
170 for this instance id and hwndClient if server responds.
171 So get HCONV and return it. And add it to conv list */
172 pConv = WDML_GetConvFromWnd(hwndClient);
173 if (pConv == NULL || pConv->hwndServer == 0)
175 ERR("Done with INITIATE, but no Server window available\n");
179 TRACE("Connected to Server window (%p)\n", pConv->hwndServer);
180 pConv->wConvst = XST_CONNECTED;
182 /* finish init of pConv */
185 pConv->convContext = *pCC;
189 memset(&pConv->convContext, 0, sizeof(pConv->convContext));
190 pConv->convContext.cb = sizeof(pConv->convContext);
191 pConv->convContext.iCodePage = CP_WINUNICODE;
195 LeaveCriticalSection(&WDML_CritSect);
197 if (aSrv) GlobalDeleteAtom(aSrv);
198 if (aTpc) GlobalDeleteAtom(aTpc);
202 /*****************************************************************
203 * DdeReconnect (DDEML.37)
204 * DdeReconnect (USER32.@)
206 HCONV WINAPI DdeReconnect(HCONV hConv)
209 WDML_CONV* pNewConv = NULL;
210 ATOM aSrv = 0, aTpc = 0;
212 TRACE("(%p)\n", hConv);
214 EnterCriticalSection(&WDML_CritSect);
215 pConv = WDML_GetConv(hConv, FALSE);
216 if (pConv != NULL && (pConv->wStatus & ST_CLIENT))
220 /* to reestablist a connection, we have to make sure that:
221 * 1/ pConv is the converstation attached to the client window (it wouldn't be
222 * if a call to DdeReconnect would have already been done...)
223 * FIXME: is this really an error ???
224 * 2/ the pConv conversation had really been deconnected
226 if (pConv == WDML_GetConvFromWnd(pConv->hwndClient) &&
227 (pConv->wStatus & ST_TERMINATED) && !(pConv->wStatus & ST_CONNECTED))
229 HWND hwndClient = pConv->hwndClient;
230 HWND hwndServer = pConv->hwndServer;
233 SetWindowLongPtrW(pConv->hwndClient, GWL_WDML_CONVERSATION, 0);
235 aSrv = WDML_MakeAtomFromHsz(pConv->hszService);
236 aTpc = WDML_MakeAtomFromHsz(pConv->hszTopic);
237 if (!aSrv || !aTpc) goto theEnd;
239 LeaveCriticalSection(&WDML_CritSect);
241 /* note: sent messages shall not use packing */
242 ret = SendMessageW(hwndServer, WM_DDE_INITIATE, (WPARAM)hwndClient,
243 MAKELPARAM(aSrv, aTpc));
245 EnterCriticalSection(&WDML_CritSect);
247 pConv = WDML_GetConv(hConv, FALSE);
250 FIXME("Should fail reconnection\n");
254 if (ret && (pNewConv = WDML_GetConvFromWnd(pConv->hwndClient)) != NULL)
256 /* re-establish all links... */
259 for (pLink = pConv->instance->links[WDML_CLIENT_SIDE]; pLink; pLink = pLink->next)
261 if (pLink->hConv == hConv)
263 /* try to reestablish the links... */
264 DdeClientTransaction(NULL, 0, (HCONV)pNewConv, pLink->hszItem, pLink->uFmt,
265 pLink->transactionType, 1000, NULL);
271 /* reset the conversation as it was */
272 SetWindowLongPtrW(pConv->hwndClient, GWL_WDML_CONVERSATION, (ULONG_PTR)pConv);
278 LeaveCriticalSection(&WDML_CritSect);
280 if (aSrv) GlobalDeleteAtom(aSrv);
281 if (aTpc) GlobalDeleteAtom(aTpc);
282 return (HCONV)pNewConv;
285 /******************************************************************
286 * WDML_ClientQueueAdvise
288 * Creates and queue an WM_DDE_ADVISE transaction
290 static WDML_XACT* WDML_ClientQueueAdvise(WDML_CONV* pConv, UINT wType, UINT wFmt, HSZ hszItem)
292 DDEADVISE* pDdeAdvise;
296 TRACE("XTYP_ADVSTART (with%s data) transaction\n", (wType & XTYPF_NODATA) ? "out" : "");
298 atom = WDML_MakeAtomFromHsz(hszItem);
299 if (!atom) return NULL;
301 pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_ADVISE, wFmt, hszItem);
304 GlobalDeleteAtom(atom);
308 pXAct->wType = wType & ~0x0F;
309 pXAct->hMem = GlobalAlloc(GHND | GMEM_DDESHARE, sizeof(DDEADVISE));
310 /* FIXME: hMem is unfreed for now... should be deleted in server */
313 pDdeAdvise = (DDEADVISE*)GlobalLock(pXAct->hMem);
314 pDdeAdvise->fAckReq = (wType & XTYPF_ACKREQ) ? TRUE : FALSE;
315 pDdeAdvise->fDeferUpd = (wType & XTYPF_NODATA) ? TRUE : FALSE;
316 pDdeAdvise->cfFormat = wFmt;
317 GlobalUnlock(pXAct->hMem);
319 pXAct->lParam = PackDDElParam(WM_DDE_ADVISE, (UINT_PTR)pXAct->hMem, atom);
324 /******************************************************************
325 * WDML_HandleAdviseReply
327 * handles the reply to an advise request
329 static WDML_QUEUE_STATE WDML_HandleAdviseReply(WDML_CONV* pConv, MSG* msg, WDML_XACT* pXAct)
335 if (msg->message != WM_DDE_ACK || WIN_GetFullHandle((HWND)msg->wParam) != pConv->hwndServer)
340 UnpackDDElParam(WM_DDE_ACK, msg->lParam, &uiLo, &uiHi);
341 hsz = WDML_MakeHszFromAtom(pConv->instance, uiHi);
343 if (DdeCmpStringHandles(hsz, pXAct->hszItem) != 0)
346 GlobalDeleteAtom(uiHi);
347 FreeDDElParam(WM_DDE_ACK, msg->lParam);
349 WDML_ExtractAck(uiLo, &ddeAck);
355 /* billx: first to see if the link is already created. */
356 pLink = WDML_FindLink(pConv->instance, (HCONV)pConv, WDML_CLIENT_SIDE,
357 pXAct->hszItem, TRUE, pXAct->wFmt);
360 /* we found a link, and only need to modify it in case it changes */
361 pLink->transactionType = pXAct->wType;
365 WDML_AddLink(pConv->instance, (HCONV)pConv, WDML_CLIENT_SIDE,
366 pXAct->wType, pXAct->hszItem, pXAct->wFmt);
368 pXAct->hDdeData = (HDDEDATA)1;
372 TRACE("Returning FALSE on XTYP_ADVSTART - fAck was FALSE\n");
373 GlobalFree(pXAct->hMem);
374 pXAct->hDdeData = NULL;
377 return WDML_QS_HANDLED;
380 /******************************************************************
381 * WDML_ClientQueueUnadvise
383 * queues an unadvise transaction
385 static WDML_XACT* WDML_ClientQueueUnadvise(WDML_CONV* pConv, UINT wFmt, HSZ hszItem)
390 TRACE("XTYP_ADVSTOP transaction\n");
392 atom = WDML_MakeAtomFromHsz(hszItem);
393 if (!atom) return NULL;
395 pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_UNADVISE, wFmt, hszItem);
398 GlobalDeleteAtom(atom);
402 /* end advise loop: post WM_DDE_UNADVISE to server to terminate link
403 * on the specified item.
405 pXAct->lParam = PackDDElParam(WM_DDE_UNADVISE, wFmt, atom);
409 /******************************************************************
410 * WDML_HandleUnadviseReply
414 static WDML_QUEUE_STATE WDML_HandleUnadviseReply(WDML_CONV* pConv, MSG* msg, WDML_XACT* pXAct)
420 if (msg->message != WM_DDE_ACK || WIN_GetFullHandle((HWND)msg->wParam) != pConv->hwndServer)
425 UnpackDDElParam(WM_DDE_ACK, msg->lParam, &uiLo, &uiHi);
426 hsz = WDML_MakeHszFromAtom(pConv->instance, uiHi);
428 if (DdeCmpStringHandles(hsz, pXAct->hszItem) != 0)
431 FreeDDElParam(WM_DDE_ACK, msg->lParam);
432 GlobalDeleteAtom(uiHi);
434 WDML_ExtractAck(uiLo, &ddeAck);
436 TRACE("WM_DDE_ACK received while waiting for a timeout\n");
440 TRACE("Returning FALSE on XTYP_ADVSTOP - fAck was FALSE\n");
441 pXAct->hDdeData = NULL;
445 /* billx: remove the link */
446 WDML_RemoveLink(pConv->instance, (HCONV)pConv, WDML_CLIENT_SIDE,
447 pXAct->hszItem, pXAct->wFmt);
448 pXAct->hDdeData = (HDDEDATA)1;
450 return WDML_QS_HANDLED;
453 /******************************************************************
454 * WDML_ClientQueueRequest
458 static WDML_XACT* WDML_ClientQueueRequest(WDML_CONV* pConv, UINT wFmt, HSZ hszItem)
463 TRACE("XTYP_REQUEST transaction\n");
465 atom = WDML_MakeAtomFromHsz(hszItem);
466 if (!atom) return NULL;
468 pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_REQUEST, wFmt, hszItem);
471 GlobalDeleteAtom(atom);
475 pXAct->lParam = PackDDElParam(WM_DDE_REQUEST, wFmt, atom);
480 /******************************************************************
481 * WDML_HandleRequestReply
485 static WDML_QUEUE_STATE WDML_HandleRequestReply(WDML_CONV* pConv, MSG* msg, WDML_XACT* pXAct)
492 if (WIN_GetFullHandle((HWND)msg->wParam) != pConv->hwndServer)
495 switch (msg->message)
498 UnpackDDElParam(WM_DDE_ACK, msg->lParam, &uiLo, &uiHi);
499 FreeDDElParam(WM_DDE_ACK, msg->lParam);
500 GlobalDeleteAtom(uiHi);
501 WDML_ExtractAck(uiLo, &ddeAck);
504 ERR("Positive answer should appear in NACK for a request, assuming negative\n");
505 TRACE("Negative answer...\n");
509 UnpackDDElParam(WM_DDE_DATA, msg->lParam, &uiLo, &uiHi);
510 TRACE("Got the result (%08x)\n", uiLo);
512 hsz = WDML_MakeHszFromAtom(pConv->instance, uiHi);
514 if (DdeCmpStringHandles(hsz, pXAct->hszItem) != 0)
517 pXAct->hDdeData = WDML_Global2DataHandle((HGLOBAL)uiLo, &wdh);
520 GlobalFree((HGLOBAL)uiLo);
524 WDML_PostAck(pConv, WDML_CLIENT_SIDE, 0, FALSE, TRUE, uiHi, msg->lParam, WM_DDE_DATA);
528 GlobalDeleteAtom(uiHi);
529 FreeDDElParam(WM_DDE_ACK, msg->lParam);
534 FreeDDElParam(msg->message, msg->lParam);
538 return WDML_QS_HANDLED;
541 /******************************************************************
542 * WDML_BuildExecuteCommand
544 * Creates a DDE block suitable for sending in WM_DDE_COMMAND
545 * It also takes care of string conversion between the two window procedures
547 static HGLOBAL WDML_BuildExecuteCommand(WDML_CONV* pConv, LPCVOID pData, DWORD cbData)
550 BOOL clientUnicode, serverUnicode;
553 clientUnicode = IsWindowUnicode(pConv->hwndClient);
554 serverUnicode = IsWindowUnicode(pConv->hwndServer);
556 if (clientUnicode == serverUnicode)
564 memSize = WideCharToMultiByte( CP_ACP, 0, pData, cbData, NULL, 0, NULL, NULL);
568 memSize = MultiByteToWideChar( CP_ACP, 0, pData, cbData, NULL, 0) * sizeof(WCHAR);
572 hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, memSize);
578 pDst = GlobalLock(hMem);
581 if (clientUnicode == serverUnicode)
583 memcpy(pDst, pData, cbData);
589 WideCharToMultiByte( CP_ACP, 0, pData, cbData, pDst, memSize, NULL, NULL);
593 MultiByteToWideChar( CP_ACP, 0, pData, cbData, (LPWSTR)pDst, memSize/sizeof(WCHAR));
608 /******************************************************************
609 * WDML_ClientQueueExecute
613 static WDML_XACT* WDML_ClientQueueExecute(WDML_CONV* pConv, LPCVOID pData, DWORD cbData)
617 TRACE("XTYP_EXECUTE transaction\n");
619 pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_EXECUTE, 0, 0);
623 if (cbData == (DWORD)-1)
625 HDDEDATA hDdeData = (HDDEDATA)pData;
627 pData = DdeAccessData(hDdeData, &cbData);
630 pXAct->hMem = WDML_BuildExecuteCommand(pConv, pData, cbData);
631 DdeUnaccessData(hDdeData);
636 pXAct->hMem = WDML_BuildExecuteCommand(pConv, pData, cbData);
639 pXAct->lParam = (LPARAM)pXAct->hMem;
644 /******************************************************************
645 * WDML_HandleExecuteReply
649 static WDML_QUEUE_STATE WDML_HandleExecuteReply(WDML_CONV* pConv, MSG* msg, WDML_XACT* pXAct)
654 if (msg->message != WM_DDE_ACK || WIN_GetFullHandle((HWND)msg->wParam) != pConv->hwndServer)
659 UnpackDDElParam(WM_DDE_ACK, msg->lParam, &uiLo, &uiHi);
660 FreeDDElParam(WM_DDE_ACK, msg->lParam);
662 if ((HANDLE)uiHi != pXAct->hMem)
667 WDML_ExtractAck(uiLo, &ddeAck);
668 pXAct->hDdeData = (HDDEDATA)(UINT_PTR)ddeAck.fAck;
670 TRACE("hDdeData = %p\n", pXAct->hDdeData);
671 pConv->instance->lastError = (pXAct->hDdeData != 0) ? DMLERR_NO_ERROR : DMLERR_NOTPROCESSED;
673 return WDML_QS_HANDLED;
676 /******************************************************************
677 * WDML_ClientQueuePoke
681 static WDML_XACT* WDML_ClientQueuePoke(WDML_CONV* pConv, LPCVOID pData, DWORD cbData,
682 UINT wFmt, HSZ hszItem)
687 TRACE("XTYP_POKE transaction\n");
689 atom = WDML_MakeAtomFromHsz(hszItem);
690 if (!atom) return NULL;
692 pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_POKE, wFmt, hszItem);
695 GlobalDeleteAtom(atom);
699 if (cbData == (DWORD)-1)
701 pXAct->hMem = (HDDEDATA)pData;
707 pXAct->hMem = GlobalAlloc(GHND | GMEM_DDESHARE, sizeof(DDEPOKE) + cbData);
708 ddePoke = GlobalLock(pXAct->hMem);
711 memcpy(ddePoke->Value, pData, cbData);
712 ddePoke->fRelease = FALSE; /* FIXME: app owned ? */
713 ddePoke->cfFormat = wFmt;
714 GlobalUnlock(pXAct->hMem);
718 pXAct->lParam = PackDDElParam(WM_DDE_POKE, (UINT_PTR)pXAct->hMem, atom);
723 /******************************************************************
724 * WDML_HandlePokeReply
728 static WDML_QUEUE_STATE WDML_HandlePokeReply(WDML_CONV* pConv, MSG* msg, WDML_XACT* pXAct)
734 if (msg->message != WM_DDE_ACK && WIN_GetFullHandle((HWND)msg->wParam) != pConv->hwndServer)
739 UnpackDDElParam(WM_DDE_ACK, msg->lParam, &uiLo, &uiHi);
740 hsz = WDML_MakeHszFromAtom(pConv->instance, uiHi);
741 if (DdeCmpStringHandles(hsz, pXAct->hszItem) != 0)
745 FreeDDElParam(WM_DDE_ACK, msg->lParam);
746 GlobalDeleteAtom(uiHi);
748 WDML_ExtractAck(uiLo, &ddeAck);
749 GlobalFree(pXAct->hMem);
751 pXAct->hDdeData = (HDDEDATA)TRUE;
755 /******************************************************************
756 * WDML_ClientQueueTerminate
758 * Creates and queue an WM_DDE_TERMINATE transaction
760 static WDML_XACT* WDML_ClientQueueTerminate(WDML_CONV* pConv)
764 pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_TERMINATE, 0, 0);
769 pConv->wStatus &= ~ST_CONNECTED;
774 /******************************************************************
775 * WDML_HandleTerminateReply
777 * handles the reply to a terminate request
779 static WDML_QUEUE_STATE WDML_HandleTerminateReply(WDML_CONV* pConv, MSG* msg, WDML_XACT* pXAct)
781 if (msg->message != WM_DDE_TERMINATE)
783 /* FIXME: should delete data passed here */
784 return WDML_QS_SWALLOWED;
787 if (WIN_GetFullHandle((HWND)msg->wParam) != pConv->hwndServer)
789 FIXME("hmmm shouldn't happen\n");
792 if (!pConv->instance->CBFflags & CBF_SKIP_DISCONNECTS)
794 WDML_InvokeCallback(pConv->instance, XTYP_DISCONNECT, 0, (HCONV)pConv,
795 0, 0, 0, 0, (pConv->wStatus & ST_ISSELF) ? 1 : 0);
797 WDML_RemoveConv(pConv, WDML_CLIENT_SIDE);
798 return WDML_QS_HANDLED;
801 /******************************************************************
802 * WDML_HandleReplyData
806 static WDML_QUEUE_STATE WDML_HandleIncomingData(WDML_CONV* pConv, MSG* msg, HDDEDATA* hdd)
809 HDDEDATA hDdeDataIn, hDdeDataOut;
814 TRACE("WM_DDE_DATA message received in the Client Proc!\n");
815 /* wParam -- sending window handle */
816 /* lParam -- hDdeData & item HSZ */
818 UnpackDDElParam(WM_DDE_DATA, msg->lParam, &uiLo, &uiHi);
819 hsz = WDML_MakeHszFromAtom(pConv->instance, uiHi);
821 hDdeDataIn = WDML_Global2DataHandle((HGLOBAL)uiLo, &wdh);
824 * For hot link, data should be passed to its callback with
825 * XTYP_ADVDATA and callback should return the proper status.
827 pLink = WDML_FindLink(pConv->instance, (HCONV)pConv, WDML_CLIENT_SIDE, hsz,
828 uiLo ? TRUE : FALSE, wdh.cfFormat);
831 WDML_DecHSZ(pConv->instance, hsz);
832 DdeFreeDataHandle(hDdeDataIn);
836 if (hDdeDataIn != 0 && wdh.fAckReq)
838 WDML_PostAck(pConv, WDML_CLIENT_SIDE, 0, FALSE, TRUE, uiHi, msg->lParam, WM_DDE_DATA);
844 GlobalDeleteAtom(uiHi);
847 hDdeDataOut = WDML_InvokeCallback(pConv->instance, XTYP_ADVDATA, pLink->uFmt, pLink->hConv,
848 pConv->hszTopic, pLink->hszItem, hDdeDataIn, 0, 0);
850 if (hDdeDataOut != (HDDEDATA)DDE_FACK || wdh.fRelease)
852 if (uiLo) GlobalFree((HANDLE)uiLo);
855 DdeFreeDataHandle(hDdeDataIn);
857 WDML_DecHSZ(pConv->instance, hsz);
859 FreeDDElParam(WM_DDE_DATA, msg->lParam);
861 return WDML_QS_HANDLED;
864 /******************************************************************
865 * WDML_HandleIncomingTerminate
869 static WDML_QUEUE_STATE WDML_HandleIncomingTerminate(WDML_CONV* pConv, MSG* msg, HDDEDATA* hdd)
871 if (pConv->hwndServer != WIN_GetFullHandle((HWND)msg->wParam))
874 pConv->wStatus |= ST_TERMINATED;
875 if (!pConv->instance->CBFflags & CBF_SKIP_DISCONNECTS)
877 WDML_InvokeCallback(pConv->instance, XTYP_DISCONNECT, 0, (HCONV)pConv,
878 0, 0, 0, 0, (pConv->wStatus & ST_ISSELF) ? 1 : 0);
880 if (pConv->wStatus & ST_CONNECTED)
882 /* don't care about result code (if server exists or not) */
883 PostMessageW(pConv->hwndServer, WM_DDE_TERMINATE, (WPARAM)pConv->hwndClient, 0L);
884 pConv->wStatus &= ~ST_CONNECTED;
886 /* have to keep connection around to allow reconnection */
887 return WDML_QS_HANDLED;
890 /******************************************************************
893 * handles any incoming reply, and try to match to an already sent request
895 static WDML_QUEUE_STATE WDML_HandleReply(WDML_CONV* pConv, MSG* msg, HDDEDATA* hdd)
897 WDML_XACT* pXAct = pConv->transactions;
900 if (pConv->transactions)
902 /* first check message against a pending transaction, if any */
903 switch (pXAct->ddeMsg)
906 qs = WDML_HandleAdviseReply(pConv, msg, pXAct);
908 case WM_DDE_UNADVISE:
909 qs = WDML_HandleUnadviseReply(pConv, msg, pXAct);
912 qs = WDML_HandleExecuteReply(pConv, msg, pXAct);
915 qs = WDML_HandleRequestReply(pConv, msg, pXAct);
918 qs = WDML_HandlePokeReply(pConv, msg, pXAct);
920 case WM_DDE_TERMINATE:
921 qs = WDML_HandleTerminateReply(pConv, msg, pXAct);
933 /* now check the results */
937 case WDML_QS_SWALLOWED:
940 case WDML_QS_HANDLED:
941 /* ok, we have resolved a pending transaction
942 * notify callback if asynchronous, and remove it in any case
944 WDML_UnQueueTransaction(pConv, pXAct);
945 if (pXAct->dwTimeout == TIMEOUT_ASYNC && pXAct->ddeMsg != WM_DDE_TERMINATE)
947 WDML_InvokeCallback(pConv->instance, XTYP_XACT_COMPLETE, pXAct->wFmt,
948 (HCONV)pConv, pConv->hszTopic, pXAct->hszItem,
949 pXAct->hDdeData, MAKELONG(0, pXAct->xActID), 0 /* FIXME */);
954 *hdd = pXAct->hDdeData;
956 WDML_FreeTransaction(pConv->instance, pXAct, TRUE);
959 /* no pending transaction found, try a warm/hot link or a termination request */
960 switch (msg->message)
963 qs = WDML_HandleIncomingData(pConv, msg, hdd);
965 case WM_DDE_TERMINATE:
966 qs = WDML_HandleIncomingTerminate(pConv, msg, hdd);
971 FIXME("shouldn't be used on client side\n");
978 /******************************************************************
979 * WDML_SyncWaitTransactionReply
981 * waits until an answer for a sent request is received
982 * time out is also handled. only used for synchronous transactions
984 static HDDEDATA WDML_SyncWaitTransactionReply(HCONV hConv, DWORD dwTimeout, WDML_XACT* pXAct)
990 TRACE("Starting wait for a timeout of %ld ms\n", dwTimeout);
992 /* FIXME: time 32 bit wrap around */
993 dwTimeout += GetCurrentTime();
995 while ((dwTime = GetCurrentTime()) < dwTimeout)
997 /* we cannot be in the crit sect all the time because when client and server run in a
998 * single process they need to share the access to the internal data
1000 if (MsgWaitForMultipleObjects(0, NULL, FALSE,
1001 dwTimeout - dwTime, QS_POSTMESSAGE) == WAIT_OBJECT_0)
1008 EnterCriticalSection(&WDML_CritSect);
1010 pConv = WDML_GetConv(hConv, FALSE);
1013 LeaveCriticalSection(&WDML_CritSect);
1014 /* conversation no longer available... return failure */
1017 while (PeekMessageW(&msg, pConv->hwndClient, WM_DDE_FIRST, WM_DDE_LAST, PM_REMOVE))
1019 /* check that either pXAct has been processed or no more xActions are pending */
1020 ret = (pConv->transactions == pXAct);
1021 ret = WDML_HandleReply(pConv, &msg, &hdd) == WDML_QS_HANDLED &&
1022 (pConv->transactions == NULL || ret);
1025 LeaveCriticalSection(&WDML_CritSect);
1033 TRACE("Timeout !!\n");
1035 EnterCriticalSection(&WDML_CritSect);
1037 pConv = WDML_GetConv(hConv, FALSE);
1040 if (pConv->transactions)
1042 switch (pConv->transactions->ddeMsg)
1044 case WM_DDE_ADVISE: err = DMLERR_ADVACKTIMEOUT; break;
1045 case WM_DDE_REQUEST: err = DMLERR_DATAACKTIMEOUT; break;
1046 case WM_DDE_EXECUTE: err = DMLERR_EXECACKTIMEOUT; break;
1047 case WM_DDE_POKE: err = DMLERR_POKEACKTIMEOUT; break;
1048 case WM_DDE_UNADVISE: err = DMLERR_UNADVACKTIMEOUT; break;
1049 default: err = DMLERR_INVALIDPARAMETER; break;
1052 pConv->instance->lastError = err;
1055 LeaveCriticalSection(&WDML_CritSect);
1060 /*****************************************************************
1061 * DdeClientTransaction (USER32.@)
1063 HDDEDATA WINAPI DdeClientTransaction(LPBYTE pData, DWORD cbData, HCONV hConv, HSZ hszItem, UINT wFmt,
1064 UINT wType, DWORD dwTimeout, LPDWORD pdwResult)
1068 HDDEDATA hDdeData = 0;
1070 TRACE("(%p,%ld,%p,%p,%x,%x,%ld,%p)\n",
1071 pData, cbData, hConv, hszItem, wFmt, wType, dwTimeout, pdwResult);
1075 ERR("Invalid conversation handle\n");
1079 EnterCriticalSection(&WDML_CritSect);
1081 pConv = WDML_GetConv(hConv, TRUE);
1084 /* cannot set error... cannot get back to DDE instance */
1091 if (hszItem != 0 || wFmt != 0)
1093 pConv->instance->lastError = DMLERR_INVALIDPARAMETER;
1096 pXAct = WDML_ClientQueueExecute(pConv, pData, cbData);
1099 pXAct = WDML_ClientQueuePoke(pConv, pData, cbData, wFmt, hszItem);
1101 case XTYP_ADVSTART|XTYPF_NODATA:
1102 case XTYP_ADVSTART|XTYPF_NODATA|XTYPF_ACKREQ:
1104 case XTYP_ADVSTART|XTYPF_ACKREQ:
1107 pConv->instance->lastError = DMLERR_INVALIDPARAMETER;
1110 pXAct = WDML_ClientQueueAdvise(pConv, wType, wFmt, hszItem);
1115 pConv->instance->lastError = DMLERR_INVALIDPARAMETER;
1118 pXAct = WDML_ClientQueueUnadvise(pConv, wFmt, hszItem);
1123 pConv->instance->lastError = DMLERR_INVALIDPARAMETER;
1126 pXAct = WDML_ClientQueueRequest(pConv, wFmt, hszItem);
1129 FIXME("Unknown transation\n");
1130 /* unknown transaction type */
1131 pConv->instance->lastError = DMLERR_INVALIDPARAMETER;
1137 pConv->instance->lastError = DMLERR_MEMORY_ERROR;
1141 WDML_QueueTransaction(pConv, pXAct);
1143 if (!PostMessageW(pConv->hwndServer, pXAct->ddeMsg, (WPARAM)pConv->hwndClient, pXAct->lParam))
1145 WARN("Failed posting message %x to %p (error=0x%lx)\n",
1146 pXAct->ddeMsg, pConv->hwndServer, GetLastError());
1147 pConv->wStatus &= ~ST_CONNECTED;
1148 WDML_UnQueueTransaction(pConv, pXAct);
1149 WDML_FreeTransaction(pConv->instance, pXAct, TRUE);
1152 pXAct->dwTimeout = dwTimeout;
1153 /* FIXME: should set the app bits on *pdwResult */
1155 if (dwTimeout == TIMEOUT_ASYNC)
1159 *pdwResult = MAKELONG(0, pXAct->xActID);
1161 hDdeData = (HDDEDATA)1;
1171 count = WDML_CritSect.RecursionCount;
1172 for (i = 0; i < count; i++)
1173 LeaveCriticalSection(&WDML_CritSect);
1174 hDdeData = WDML_SyncWaitTransactionReply((HCONV)pConv, dwTimeout, pXAct);
1175 for (i = 0; i < count; i++)
1176 EnterCriticalSection(&WDML_CritSect);
1178 LeaveCriticalSection(&WDML_CritSect);
1182 LeaveCriticalSection(&WDML_CritSect);
1186 /*****************************************************************
1187 * DdeAbandonTransaction (USER32.@)
1189 BOOL WINAPI DdeAbandonTransaction(DWORD idInst, HCONV hConv, DWORD idTransaction)
1191 WDML_INSTANCE* pInstance;
1195 TRACE("(%08lx,%p,%08ld);\n", idInst, hConv, idTransaction);
1197 EnterCriticalSection(&WDML_CritSect);
1198 if ((pInstance = WDML_GetInstance(idInst)))
1202 if ((pConv = WDML_GetConv(hConv, TRUE)) && pConv->instance == pInstance)
1204 for (pXAct = pConv->transactions; pXAct; pXAct = pXAct->next)
1206 if (pXAct->dwTimeout == TIMEOUT_ASYNC &&
1207 (idTransaction == 0 || pXAct->xActID == idTransaction))
1209 WDML_UnQueueTransaction(pConv, pXAct);
1210 WDML_FreeTransaction(pInstance, pXAct, TRUE);
1217 for (pConv = pInstance->convs[WDML_CLIENT_SIDE]; pConv; pConv = pConv->next)
1219 if (!(pConv->wStatus & ST_CONNECTED)) continue;
1220 for (pXAct = pConv->transactions; pXAct; pXAct = pXAct->next)
1222 if (pXAct->dwTimeout == TIMEOUT_ASYNC)
1224 WDML_UnQueueTransaction(pConv, pXAct);
1225 WDML_FreeTransaction(pInstance, pXAct, TRUE);
1231 LeaveCriticalSection(&WDML_CritSect);
1236 /******************************************************************
1239 * Window Proc created on client side for each conversation
1241 static LRESULT CALLBACK WDML_ClientProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
1244 WDML_CONV* pConv = NULL;
1247 TRACE("%p %04x %08x %08lx\n", hwnd, iMsg, wParam , lParam);
1249 if (iMsg == WM_DDE_ACK &&
1250 /* in the initial WM_INITIATE sendmessage */
1251 ((pConv = WDML_GetConvFromWnd(hwnd)) == NULL || pConv->wStatus == XST_INIT1))
1253 /* In response to WM_DDE_INITIATE, save server window */
1255 WDML_INSTANCE* pInstance;
1257 /* note: sent messages do not need packing */
1258 uiLo = LOWORD(lParam);
1259 uiHi = HIWORD(lParam);
1261 /* FIXME: convlist should be handled here */
1264 /* we already have started the conv with a server, drop other replies */
1265 GlobalDeleteAtom(uiLo);
1266 GlobalDeleteAtom(uiHi);
1267 PostMessageW((HWND)wParam, WM_DDE_TERMINATE, (WPARAM)hwnd, 0);
1271 pInstance = WDML_GetInstanceFromWnd(hwnd);
1273 hszSrv = WDML_MakeHszFromAtom(pInstance, uiLo);
1274 hszTpc = WDML_MakeHszFromAtom(pInstance, uiHi);
1276 pConv = WDML_AddConv(pInstance, WDML_CLIENT_SIDE, hszSrv, hszTpc, hwnd, (HWND)wParam);
1278 SetWindowLongPtrW(hwnd, GWL_WDML_CONVERSATION, (ULONG_PTR)pConv);
1279 pConv->wStatus |= ST_CONNECTED;
1280 pConv->wConvst = XST_INIT1;
1282 /* check if server is handled by DDEML */
1283 if (GetClassNameW((HWND)wParam, buf, sizeof(buf)/sizeof(WCHAR)) &&
1284 lstrcmpiW(buf, WDML_szServerConvClass) == 0)
1286 pConv->wStatus |= ST_ISLOCAL;
1289 WDML_BroadcastDDEWindows(WDML_szEventClass, WM_WDML_CONNECT_CONFIRM, (WPARAM)hwnd, wParam);
1291 GlobalDeleteAtom(uiLo);
1292 GlobalDeleteAtom(uiHi);
1294 /* accept conversation */
1298 if (iMsg >= WM_DDE_FIRST && iMsg <= WM_DDE_LAST)
1300 EnterCriticalSection(&WDML_CritSect);
1302 pConv = WDML_GetConvFromWnd(hwnd);
1311 msg.wParam = wParam;
1312 msg.lParam = lParam;
1314 WDML_HandleReply(pConv, &msg, &hdd);
1317 LeaveCriticalSection(&WDML_CritSect);
1321 return DefWindowProcW(hwnd, iMsg, wParam, lParam);
1324 /*****************************************************************
1325 * DdeDisconnect (USER32.@)
1327 BOOL WINAPI DdeDisconnect(HCONV hConv)
1329 WDML_CONV* pConv = NULL;
1334 TRACE("(%p)\n", hConv);
1338 ERR("DdeDisconnect(): hConv = 0\n");
1342 EnterCriticalSection(&WDML_CritSect);
1343 pConv = WDML_GetConv(hConv, TRUE);
1346 if (pConv->wStatus & ST_CLIENT)
1348 /* FIXME: should abandon all pending transactions */
1349 pXAct = WDML_ClientQueueTerminate(pConv);
1352 count = WDML_CritSect.RecursionCount;
1353 for (i = 0; i < count; i++)
1354 LeaveCriticalSection(&WDML_CritSect);
1355 if (PostMessageW(pConv->hwndServer, pXAct->ddeMsg,
1356 (WPARAM)pConv->hwndClient, pXAct->lParam))
1357 WDML_SyncWaitTransactionReply(hConv, 10000, pXAct);
1358 for (i = 0; i < count; i++)
1359 EnterCriticalSection(&WDML_CritSect);
1361 WDML_FreeTransaction(pConv->instance, pXAct, TRUE);
1362 /* still have to destroy data assosiated with conversation */
1363 WDML_RemoveConv(pConv, WDML_CLIENT_SIDE);
1367 FIXME("Not implemented yet for a server side conversation\n");
1371 LeaveCriticalSection(&WDML_CritSect);
1376 /*****************************************************************
1377 * DdeImpersonateClient (USER32.@)
1379 BOOL WINAPI DdeImpersonateClient(HCONV hConv)
1384 TRACE("(%p)\n", hConv);
1386 EnterCriticalSection(&WDML_CritSect);
1387 pConv = WDML_GetConv(hConv, TRUE);
1390 ret = ImpersonateDdeClientWindow(pConv->hwndClient, pConv->hwndServer);
1392 LeaveCriticalSection(&WDML_CritSect);