/* -*- tab-width: 8; c-basic-offset: 4 -*- */ /* * DDEML library * * Copyright 1997 Alexandre Julliard * Copyright 1997 Len White * Copyright 1999 Keith Matthews * Copyright 2000 Corel * Copyright 2001 Eric Pouech */ #include #include "winbase.h" #include "windef.h" #include "wingdi.h" #include "winuser.h" #include "winerror.h" #include "dde.h" #include "ddeml.h" #include "debugtools.h" #include "dde/dde_private.h" DEFAULT_DEBUG_CHANNEL(ddeml); static LRESULT CALLBACK WDML_ClientProc(HWND, UINT, WPARAM, LPARAM); /* only for one client, not conv list */ static const char szClientClassA[] = "DdeClientAnsi"; /****************************************************************************** * DdeConnectList [USER32.@] Establishes conversation with DDE servers * * PARAMS * idInst [I] Instance identifier * hszService [I] Handle to service name string * hszTopic [I] Handle to topic name string * hConvList [I] Handle to conversation list * pCC [I] Pointer to structure with context data * * RETURNS * Success: Handle to new conversation list * Failure: 0 */ HCONVLIST WINAPI DdeConnectList(DWORD idInst, HSZ hszService, HSZ hszTopic, HCONVLIST hConvList, LPCONVCONTEXT pCC) { FIXME("(%ld,%d,%d,%d,%p): stub\n", idInst, hszService, hszTopic, hConvList,pCC); return (HCONVLIST)1; } /***************************************************************** * DdeQueryNextServer [USER32.@] */ HCONV WINAPI DdeQueryNextServer(HCONVLIST hConvList, HCONV hConvPrev) { FIXME("(%d,%d): stub\n",hConvList,hConvPrev); return 0; } /****************************************************************************** * DdeDisconnectList [USER32.@] Destroys list and terminates conversations * * RETURNS * Success: TRUE * Failure: FALSE */ BOOL WINAPI DdeDisconnectList( HCONVLIST hConvList) /* [in] Handle to conversation list */ { FIXME("(%d): stub\n", hConvList); return TRUE; } /***************************************************************** * DdeConnect (USER32.@) */ HCONV WINAPI DdeConnect(DWORD idInst, HSZ hszService, HSZ hszTopic, LPCONVCONTEXT pCC) { HWND hwndClient; LPARAM lParam = 0; UINT uiLow, uiHi; WNDCLASSEXA wndclass; WDML_INSTANCE* thisInstance; WDML_CONV* pConv; TRACE("(0x%lx,%d,%d,%p)\n",idInst,hszService,hszTopic,pCC); thisInstance = WDML_FindInstance(idInst); if (!thisInstance) { return 0; } /* make sure this conv is never created */ pConv = WDML_FindConv(thisInstance, WDML_CLIENT_SIDE, hszService, hszTopic); if (pConv != NULL) { ERR("This Conv already exists: (0x%lx)\n", (DWORD)pConv); return (HCONV)pConv; } /* we need to establish a conversation with server, so create a window for it */ wndclass.cbSize = sizeof(wndclass); wndclass.style = 0; wndclass.lpfnWndProc = WDML_ClientProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 2 * sizeof(DWORD); wndclass.hInstance = 0; wndclass.hIcon = 0; wndclass.hCursor = 0; wndclass.hbrBackground = 0; wndclass.lpszMenuName = NULL; wndclass.lpszClassName = szClientClassA; wndclass.hIconSm = 0; RegisterClassExA(&wndclass); hwndClient = CreateWindowA(szClientClassA, NULL, WS_POPUP, 0, 0, 0, 0, 0, 0, 0, 0); SetWindowLongA(hwndClient, 0, (DWORD)thisInstance); SendMessageA(HWND_BROADCAST, WM_DDE_INITIATE, (WPARAM)hwndClient, PackDDElParam(WM_DDE_INITIATE, (UINT)hszService, (UINT)hszTopic)); if (UnpackDDElParam(WM_DDE_INITIATE, lParam, &uiLow, &uiHi)) FreeDDElParam(WM_DDE_INITIATE, lParam); TRACE("WM_DDE_INITIATE was processed\n"); /* At this point, Client WM_DDE_ACK should have saved hwndServer for this instance id and hwndClient if server responds. So get HCONV and return it. And add it to conv list */ pConv = (WDML_CONV*)GetWindowLongA(hwndClient, 4); if (pConv == NULL || pConv->hwndServer == 0) { ERR(".. but no Server window available\n"); return 0; } /* finish init of pConv */ if (pCC != NULL) { pConv->convContext = *pCC; } return (HCONV)pConv; } /***************************************************************** * DdeDisconnect (USER32.@) */ BOOL WINAPI DdeDisconnect(HCONV hConv) { WDML_CONV* pConv = NULL; TRACE("(%ld)\n", (DWORD)hConv); if (hConv == 0) { ERR("DdeDisconnect(): hConv = 0\n"); return 0; } pConv = WDML_GetConv(hConv); if (pConv == NULL) { return FALSE; } if (!PostMessageA(pConv->hwndServer, WM_DDE_TERMINATE, (WPARAM)pConv->hwndClient, (LPARAM)hConv)) { ERR("DdeDisconnect(): PostMessage returned 0\n"); return 0; } return TRUE; } /***************************************************************** * DdeReconnect (DDEML.37) * DdeReconnect (USER32.@) */ HCONV WINAPI DdeReconnect(HCONV hConv) { FIXME("empty stub\n"); return 0; } typedef enum { WDML_QS_ERROR, WDML_QS_HANDLED, WDML_QS_PASS } WDML_QUEUE_STATE; /****************************************************************** * WDML_QueueAdvise * * Creates and queue an WM_DDE_ADVISE transaction */ static WDML_XACT* WDML_QueueAdvise(WDML_CONV* pConv, UINT wType, UINT wFmt, HSZ hszItem) { DDEADVISE* pDdeAdvise; WDML_XACT* pXAct; TRACE("XTYP_ADVSTART (with%s data) transaction\n", (wType & XTYPF_NODATA) ? "out" : ""); pXAct = WDML_AllocTransaction(pConv->thisInstance, WM_DDE_ADVISE); if (!pXAct) return NULL; pXAct->u.advise.wType = wType & ~0x0F; pXAct->u.advise.wFmt = wFmt; pXAct->u.advise.hszItem = hszItem; pXAct->u.advise.hDdeAdvise = GlobalAlloc(GHND | GMEM_DDESHARE, sizeof(DDEADVISE)); /* pack DdeAdvise */ pDdeAdvise = (DDEADVISE*)GlobalLock(pXAct->u.advise.hDdeAdvise); pDdeAdvise->fAckReq = (wType & XTYPF_ACKREQ) ? TRUE : FALSE; pDdeAdvise->fDeferUpd = (wType & XTYPF_NODATA) ? TRUE : FALSE; pDdeAdvise->cfFormat = wFmt; GlobalUnlock(pXAct->u.advise.hDdeAdvise); WDML_QueueTransaction(pConv, pXAct); if (!PostMessageA(pConv->hwndServer, WM_DDE_ADVISE, (WPARAM)pConv->hwndClient, PackDDElParam(WM_DDE_ADVISE, (UINT)pXAct->u.advise.hDdeAdvise, (UINT)hszItem))) { GlobalFree(pXAct->u.advise.hDdeAdvise); WDML_UnQueueTransaction(pConv, pXAct); WDML_FreeTransaction(pXAct); return NULL; } return pXAct; } /****************************************************************** * WDML_HandleAdviseReply * * handles the reply to an advise request */ static WDML_QUEUE_STATE WDML_HandleAdviseReply(WDML_CONV* pConv, MSG* msg, WDML_XACT* pXAct) { DDEACK ddeAck; UINT uiLo, uiHi; WORD wStatus; if (msg->message != WM_DDE_ACK || msg->wParam != pConv->hwndServer) { return WDML_QS_PASS; } UnpackDDElParam(WM_DDE_ACK, msg->lParam, &uiLo, &uiHi); if (DdeCmpStringHandles(uiHi, pXAct->u.advise.hszItem) != 0) return WDML_QS_PASS; GlobalDeleteAtom(uiHi); wStatus = uiLo; ddeAck = *((DDEACK*)&wStatus); if (ddeAck.fAck) { WDML_LINK* pLink; /* billx: first to see if the link is already created. */ pLink = WDML_FindLink(pConv->thisInstance, (HCONV)pConv, WDML_CLIENT_SIDE, pXAct->u.advise.hszItem, pXAct->u.advise.wFmt); if (pLink != NULL) { /* we found a link, and only need to modify it in case it changes */ pLink->transactionType = pXAct->u.advise.wType; } else { TRACE("Adding Link with hConv = 0x%lx\n", (DWORD)pConv); WDML_AddLink(pConv->thisInstance, (HCONV)pConv, WDML_CLIENT_SIDE, pXAct->u.advise.wType, pXAct->u.advise.hszItem, pXAct->u.advise.wFmt); } } else { TRACE("Returning TRUE on XTYP_ADVSTART - fAck was FALSE\n"); GlobalFree(pXAct->u.advise.hDdeAdvise); } pXAct->hDdeData = (HDDEDATA)1; return WDML_QS_HANDLED; } /****************************************************************** * WDML_QueueUnadvise * * queues an unadvise transaction */ static WDML_XACT* WDML_QueueUnadvise(WDML_CONV* pConv, UINT wFmt, HSZ hszItem) { WDML_XACT* pXAct; TRACE("XTYP_ADVSTOP transaction\n"); pXAct = WDML_AllocTransaction(pConv->thisInstance, WM_DDE_UNADVISE); if (!pXAct) return NULL; pXAct->u.unadvise.wFmt = wFmt; pXAct->u.unadvise.hszItem = hszItem; WDML_QueueTransaction(pConv, pXAct); /* end advise loop: post WM_DDE_UNADVISE to server to terminate link on the specified item. */ if (!PostMessageA(pConv->hwndServer, WM_DDE_UNADVISE, (WPARAM)pConv->hwndClient, PackDDElParam(WM_DDE_UNADVISE, wFmt, (UINT)hszItem))) { WDML_UnQueueTransaction(pConv, pXAct); WDML_FreeTransaction(pXAct); return NULL; } return pXAct; } /****************************************************************** * WDML_HandleUnadviseReply * * */ static WDML_QUEUE_STATE WDML_HandleUnadviseReply(WDML_CONV* pConv, MSG* msg, WDML_XACT* pXAct) { DDEACK ddeAck; UINT uiLo, uiHi; WORD wStatus; if (msg->message != WM_DDE_ACK || msg->wParam != pConv->hwndServer) { return WDML_QS_PASS; } UnpackDDElParam(WM_DDE_ACK, msg->lParam, &uiLo, &uiHi); if (DdeCmpStringHandles(uiHi, pXAct->u.unadvise.hszItem) != 0) return WDML_QS_PASS; GlobalDeleteAtom(uiHi); wStatus = uiLo; ddeAck = *((DDEACK*)&wStatus); TRACE("WM_DDE_ACK received while waiting for a timeout\n"); if (!ddeAck.fAck) { TRACE("Returning TRUE on XTYP_ADVSTOP - fAck was FALSE\n"); } else { /* billx: remove the link */ WDML_RemoveLink(pConv->thisInstance, (HCONV)pConv, WDML_CLIENT_SIDE, pXAct->u.unadvise.hszItem, pXAct->u.unadvise.wFmt); } pXAct->hDdeData = (HDDEDATA)1; return WDML_QS_HANDLED; } /****************************************************************** * WDML_QueueRequest * * */ static WDML_XACT* WDML_QueueRequest(WDML_CONV* pConv, UINT wFmt, HSZ hszItem) { WDML_XACT* pXAct; TRACE("XTYP_REQUEST transaction\n"); pXAct = WDML_AllocTransaction(pConv->thisInstance, WM_DDE_REQUEST); if (!pXAct) return NULL; pXAct->u.request.hszItem = hszItem; WDML_QueueTransaction(pConv, pXAct); /* end advise loop: post WM_DDE_UNADVISE to server to terminate link * on the specified item. */ if (!PostMessageA(pConv->hwndServer, WM_DDE_REQUEST, (WPARAM)pConv->hwndClient, PackDDElParam(WM_DDE_REQUEST, wFmt, (UINT)hszItem))) { WDML_UnQueueTransaction(pConv, pXAct); WDML_FreeTransaction(pXAct); return NULL; } return pXAct; } /****************************************************************** * WDML_HandleRequestReply * * */ static WDML_QUEUE_STATE WDML_HandleRequestReply(WDML_CONV* pConv, MSG* msg, WDML_XACT* pXAct) { DDEACK ddeAck; UINT uiLo, uiHi; WORD wStatus; switch (msg->message) { case WM_DDE_ACK: if (msg->wParam != pConv->hwndServer) return WDML_QS_PASS; UnpackDDElParam(WM_DDE_ACK, msg->lParam, &uiLo, &uiHi); wStatus = uiLo; ddeAck = *((DDEACK*)&wStatus); pXAct->hDdeData = 0; TRACE("Negative answer...\n"); /* FIXME: billx: we should return 0 and post a negatve WM_DDE_ACK. */ break; case WM_DDE_DATA: if (msg->wParam != pConv->hwndServer) return WDML_QS_PASS; UnpackDDElParam(WM_DDE_DATA, msg->lParam, &uiLo, &uiHi); TRACE("Got the result (%08lx)\n", (DWORD)uiLo); if (DdeCmpStringHandles(uiHi, pXAct->u.request.hszItem) != 0) return WDML_QS_PASS; /* FIXME: memory clean up ? */ pXAct->hDdeData = WDML_Global2DataHandle((HGLOBAL)uiLo); break; default: return WDML_QS_PASS; } return WDML_QS_HANDLED; } /****************************************************************** * WDML_QueueExecute * * */ static WDML_XACT* WDML_QueueExecute(WDML_CONV* pConv, LPCVOID pData, DWORD cbData) { WDML_XACT* pXAct; TRACE("XTYP_EXECUTE transaction\n"); pXAct = WDML_AllocTransaction(pConv->thisInstance, WM_DDE_EXECUTE); if (!pXAct) return NULL; if (cbData == (DWORD)-1) { HDDEDATA hDdeData = (HDDEDATA)pData; DWORD dwSize; pData = DdeAccessData(hDdeData, &dwSize); if (pData) { pXAct->u.execute.hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, dwSize); if (pXAct->u.execute.hMem) { LPBYTE pDst; pDst = GlobalLock(pXAct->u.execute.hMem); if (pDst) { memcpy(pDst, pData, dwSize); GlobalUnlock(pXAct->u.execute.hMem); } else { GlobalFree(pXAct->u.execute.hMem); pXAct->u.execute.hMem = 0; } } DdeUnaccessData(hDdeData); } else { pXAct->u.execute.hMem = 0; } } else { LPSTR ptr; pXAct->u.execute.hMem = GlobalAlloc(GHND | GMEM_DDESHARE, cbData); ptr = GlobalLock(pXAct->u.execute.hMem); if (ptr) { memcpy(ptr, pData, cbData); GlobalUnlock(pXAct->u.execute.hMem); } } WDML_QueueTransaction(pConv, pXAct); if (!PostMessageA(pConv->hwndServer, WM_DDE_EXECUTE, (WPARAM)pConv->hwndClient, pXAct->u.execute.hMem)) { GlobalFree(pXAct->u.execute.hMem); WDML_UnQueueTransaction(pConv, pXAct); WDML_FreeTransaction(pXAct); TRACE("Returning FALSE on XTYP_EXECUTE - PostMessage returned FALSE\n"); return NULL; } return pXAct; } /****************************************************************** * WDML_HandleExecuteReply * * */ static WDML_QUEUE_STATE WDML_HandleExecuteReply(WDML_CONV* pConv, MSG* msg, WDML_XACT* pXAct) { DDEACK ddeAck; UINT uiLo, uiHi; WORD wStatus; if (msg->message != WM_DDE_ACK || msg->wParam != pConv->hwndServer) { return WDML_QS_PASS; } UnpackDDElParam(WM_DDE_ACK, msg->lParam, &uiLo, &uiHi); FreeDDElParam(WM_DDE_ACK, msg->lParam); if (uiHi != pXAct->u.execute.hMem) { return WDML_QS_PASS; } wStatus = uiLo; ddeAck = *((DDEACK*)&wStatus); if (!ddeAck.fAck) { GlobalFree(pXAct->u.execute.hMem); } pXAct->hDdeData = (HDDEDATA)1; return WDML_QS_HANDLED; } /****************************************************************** * WDML_QueuePoke * * */ static WDML_XACT* WDML_QueuePoke(WDML_CONV* pConv, LPCVOID pData, DWORD cbData, UINT wFmt, HSZ hszItem) { WDML_XACT* pXAct; TRACE("XTYP_POKE transaction\n"); pXAct = WDML_AllocTransaction(pConv->thisInstance, WM_DDE_POKE); if (!pXAct) return NULL; if (cbData == (DWORD)-1) { pXAct->u.poke.hMem = (HDDEDATA)pData; } else { DDEPOKE* ddePoke; pXAct->u.poke.hMem = GlobalAlloc(GHND | GMEM_DDESHARE, sizeof(DDEPOKE) + cbData); ddePoke = GlobalLock(pXAct->u.poke.hMem); if (ddePoke) { memcpy(ddePoke->Value, pData, cbData); ddePoke->fRelease = FALSE; /* FIXME: app owned ? */ ddePoke->cfFormat = wFmt; GlobalUnlock(pXAct->u.poke.hMem); } } pXAct->u.poke.hszItem = hszItem; WDML_QueueTransaction(pConv, pXAct); if (!PostMessageA(pConv->hwndServer, WM_DDE_POKE, (WPARAM)pConv->hwndClient, PackDDElParam(WM_DDE_POKE, pXAct->u.execute.hMem, hszItem))) { GlobalFree(pXAct->u.execute.hMem); WDML_UnQueueTransaction(pConv, pXAct); WDML_FreeTransaction(pXAct); TRACE("Returning FALSE on XTYP_POKE - PostMessage returned FALSE\n"); return NULL; } return pXAct; } /****************************************************************** * WDML_HandlePokeReply * * */ static WDML_QUEUE_STATE WDML_HandlePokeReply(WDML_CONV* pConv, MSG* msg, WDML_XACT* pXAct) { DDEACK ddeAck; UINT uiLo, uiHi; WORD wStatus; if (msg->message != WM_DDE_ACK && msg->wParam != pConv->hwndServer) { return WDML_QS_PASS; } UnpackDDElParam(WM_DDE_ACK, msg->lParam, &uiLo, &uiHi); if (uiHi != pXAct->u.poke.hszItem) { return WDML_QS_PASS; } FreeDDElParam(WM_DDE_ACK, msg->lParam); wStatus = uiLo; ddeAck = *((DDEACK*)&wStatus); if (!ddeAck.fAck) { GlobalFree(pXAct->u.poke.hMem); } pXAct->hDdeData = (HDDEDATA)TRUE; return TRUE; } /****************************************************************** * WDML_HandleReplyData * * */ static WDML_QUEUE_STATE WDML_HandleReplyData(WDML_CONV* pConv, MSG* msg, HDDEDATA* hdd) { UINT uiLo, uiHi; HDDEDATA hDdeDataIn, hDdeDataOut; WDML_LINK* pLink; TRACE("WM_DDE_DATA message received in the Client Proc!\n"); /* wParam -- sending window handle */ /* lParam -- hDdeData & item HSZ */ UnpackDDElParam(WM_DDE_DATA, msg->lParam, &uiLo, &uiHi); hDdeDataIn = WDML_Global2DataHandle((HGLOBAL)uiLo); /* billx: * For hot link, data should be passed to its callback with * XTYP_ADVDATA and callback should return the proper status. */ for (pLink = pConv->thisInstance->links[WDML_CLIENT_SIDE]; pLink != NULL; pLink = pLink->next) { if (DdeCmpStringHandles((HSZ)uiHi, pLink->hszItem) == 0) { BOOL fRelease = FALSE; BOOL fAckReq = FALSE; DDEDATA* pDdeData; /* item in the advise loop */ pConv = WDML_GetConv(pLink->hConv); if (pConv == NULL) { continue; } if ((pDdeData = GlobalLock(uiLo)) != NULL) { fRelease = pDdeData->fRelease; fAckReq = pDdeData->fAckReq; } if (hDdeDataIn != 0) { if (fAckReq) { DDEACK ddeAck; ddeAck.bAppReturnCode = 0; ddeAck.reserved = 0; ddeAck.fBusy = FALSE; ddeAck.fAck = TRUE; if (msg->lParam) { PostMessageA(pConv->hwndServer, WM_DDE_ACK, pConv->hwndClient, ReuseDDElParam(msg->lParam, WM_DDE_DATA, WM_DDE_ACK, *(WORD*)&ddeAck, (UINT)pLink->hszItem)); msg->lParam = 0L; } else { PostMessageA(pConv->hwndServer, WM_DDE_ACK, pConv->hwndClient, PackDDElParam(WM_DDE_ACK, *(WORD*)&ddeAck, (UINT)pLink->hszItem)); } } } hDdeDataOut = 0; if (pConv->thisInstance->callback != NULL /*&& thisInstance->processID == GetCurrentProcessId() */) { TRACE("Calling the callback, type = XTYP_ADVDATA, CB = 0x%lx, hConv = 0x%lx\n", (DWORD)pConv->thisInstance->callback, (DWORD)pLink->hConv); hDdeDataOut = (pConv->thisInstance->callback)(XTYP_ADVDATA, pLink->uFmt, pLink->hConv, pConv->hszTopic, pLink->hszItem, hDdeDataIn, 0, 0); if (hDdeDataOut == (HDDEDATA)DDE_FACK) { pLink->hDdeData = hDdeDataIn; } } #if 0 if (fRelease) { DdeFreeDataHandle(hDdeDataIn); } #endif break; } } if (msg->lParam) FreeDDElParam(WM_DDE_DATA, msg->lParam); return WDML_QS_HANDLED; } /****************************************************************** * WDML_HandleReplyTerminate * * */ static WDML_QUEUE_STATE WDML_HandleReplyTerminate(WDML_CONV* pConv, MSG* msg, HDDEDATA* hdd) { if ((LPARAM)pConv != msg->lParam) return WDML_QS_PASS; /* billx: clean up the conv and associated links */ WDML_RemoveAllLinks(pConv->thisInstance, (HCONV)pConv, WDML_CLIENT_SIDE); WDML_RemoveConv(pConv->thisInstance, WDML_CLIENT_SIDE, (HCONV)pConv); DestroyWindow(msg->hwnd); return WDML_QS_HANDLED; } /****************************************************************** * WDML_HandleReply * * handles any incoming reply, and try to match to an already sent request */ static WDML_QUEUE_STATE WDML_HandleReply(WDML_CONV* pConv, MSG* msg, HDDEDATA* hdd) { WDML_XACT* pXAct = pConv->transactions; WDML_QUEUE_STATE qs; if (pConv->transactions) { /* first check message against a pending transaction, if any */ switch (pXAct->ddeMsg) { case WM_DDE_ADVISE: qs = WDML_HandleAdviseReply(pConv, msg, pXAct); break; case WM_DDE_UNADVISE: qs = WDML_HandleUnadviseReply(pConv, msg, pXAct); break; case WM_DDE_EXECUTE: qs = WDML_HandleExecuteReply(pConv, msg, pXAct); break; case WM_DDE_REQUEST: qs = WDML_HandleRequestReply(pConv, msg, pXAct); break; case WM_DDE_POKE: qs = WDML_HandlePokeReply(pConv, msg, pXAct); break; default: qs = WDML_QS_ERROR; FIXME("oooch\n"); } } else { qs = WDML_QS_PASS; } /* now check the results */ switch (qs) { case WDML_QS_ERROR: *hdd = 0; break; case WDML_QS_HANDLED: /* ok, we have resolved a pending transaction * notify callback if asynchronous, and remove it in any case */ WDML_UnQueueTransaction(pConv, pXAct); if (pXAct->dwTimeout == TIMEOUT_ASYNC) { if (pConv->thisInstance->callback != NULL /*&& thisInstance->processID == GetCurrentProcessId() */) { TRACE("Calling the callback, type = XTYP_XACT_COMPLETE, CB = 0x%lx, hConv = 0x%lx\n", (DWORD)pConv->thisInstance->callback, (DWORD)pConv); (pConv->thisInstance->callback)(XTYP_XACT_COMPLETE, 0 /* FIXME */, (HCONV)pConv, pConv->hszTopic, 0 /* FIXME */, pXAct->hDdeData, MAKELONG(0, pXAct->xActID), 0 /* FIXME */); qs = WDML_QS_PASS; } } else { *hdd = pXAct->hDdeData; } WDML_FreeTransaction(pXAct); break; case WDML_QS_PASS: /* no pending transaction found, try a warm link or a termination request */ switch (msg->message) { case WM_DDE_DATA: qs = WDML_HandleReplyData(pConv, msg, hdd); break; case WM_DDE_TERMINATE: qs = WDML_HandleReplyTerminate(pConv, msg, hdd); break; } break; } return qs; } /****************************************************************** * WDML_SyncWaitTransactionReply * * waits until an answer for a sent request is received * time out is also handled. only used for synchronous transactions */ static HDDEDATA WDML_SyncWaitTransactionReply(HCONV hConv, DWORD dwTimeout, WDML_XACT* pXAct) { DWORD dwTime; TRACE("Starting wait for a timeout of %ld ms\n", dwTimeout); /* FIXME: time 32 bit wrap around */ dwTimeout += GetCurrentTime(); while ((dwTime = GetCurrentTime()) < dwTimeout) { /* we cannot hold the mutex all the time because when client and server run in a * single process they need to share the access to the internal data */ if (MsgWaitForMultipleObjects(0, NULL, FALSE, dwTime - dwTimeout, QS_POSTMESSAGE) == WAIT_OBJECT_0 && WDML_WaitForMutex(handle_mutex)) { BOOL ret = FALSE; MSG msg; WDML_CONV* pConv; HDDEDATA hdd; pConv = WDML_GetConv(hConv); if (pConv == NULL) { /* conversation no longer available... return failure */ break; } while (PeekMessageA(&msg, pConv->hwndClient, WM_DDE_FIRST, WM_DDE_LAST, PM_REMOVE)) { /* check that either pXAct has been processed or no more xActions are pending */ ret = (pConv->transactions == pXAct); ret = WDML_HandleReply(pConv, &msg, &hdd) == WDML_QS_HANDLED && (pConv->transactions == NULL || ret); if (ret) break; } WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE); if (ret) { return hdd; } } } TRACE("Timeout !!\n"); if (WDML_WaitForMutex(handle_mutex)) { DWORD err; WDML_CONV* pConv; pConv = WDML_GetConv(hConv); if (pConv == NULL) { return 0; } switch (pConv->transactions->ddeMsg) { case WM_DDE_ADVISE: err = DMLERR_ADVACKTIMEOUT; break; case WM_DDE_REQUEST: err = DMLERR_DATAACKTIMEOUT; break; case WM_DDE_EXECUTE: err = DMLERR_EXECACKTIMEOUT; break; case WM_DDE_POKE: err = DMLERR_POKEACKTIMEOUT; break; case WM_DDE_UNADVISE: err = DMLERR_UNADVACKTIMEOUT; break; default: err = DMLERR_INVALIDPARAMETER; break; } pConv->thisInstance->lastError = err; WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE); } return 0; } /***************************************************************** * DdeClientTransaction (USER32.@) */ HDDEDATA WINAPI DdeClientTransaction(LPBYTE pData, DWORD cbData, HCONV hConv, HSZ hszItem, UINT wFmt, UINT wType, DWORD dwTimeout, LPDWORD pdwResult) { WDML_CONV* pConv; WDML_XACT* pXAct; HDDEDATA hDdeData = 0; TRACE("(0x%lx,%ld,0x%lx,0x%lx,%d,%d,%ld,0x%lx)\n", (ULONG)pData,cbData,(DWORD)hConv,(DWORD)hszItem,wFmt,wType, dwTimeout,(ULONG)pdwResult); if (hConv == 0) { ERR("Invalid conversation handle\n"); return 0; } if (!WDML_WaitForMutex(handle_mutex)) { return FALSE; } pConv = WDML_GetConv(hConv); if (pConv == NULL) { /* cannot set error... cannot get back to DDE instance */ goto theError; } switch (wType) { case XTYP_EXECUTE: if (hszItem != 0 || wFmt != 0) { pConv->thisInstance->lastError = DMLERR_INVALIDPARAMETER; goto theError; } pXAct = WDML_QueueExecute(pConv, pData, cbData); break; case XTYP_POKE: pXAct = WDML_QueuePoke(pConv, pData, cbData, wFmt, hszItem); break; case XTYP_ADVSTART|XTYPF_NODATA: case XTYP_ADVSTART|XTYPF_NODATA|XTYPF_ACKREQ: case XTYP_ADVSTART: case XTYP_ADVSTART|XTYPF_ACKREQ: if (pData) { pConv->thisInstance->lastError = DMLERR_INVALIDPARAMETER; goto theError; } pXAct = WDML_QueueAdvise(pConv, wType, wFmt, hszItem); break; case XTYP_ADVSTOP: if (pData) { pConv->thisInstance->lastError = DMLERR_INVALIDPARAMETER; goto theError; } pXAct = WDML_QueueUnadvise(pConv, wFmt, hszItem); break; case XTYP_REQUEST: if (pData) { pConv->thisInstance->lastError = DMLERR_INVALIDPARAMETER; goto theError; } pXAct = WDML_QueueRequest(pConv, wFmt, hszItem); break; default: FIXME("Unknown transation\n"); /* unknown transaction type */ pConv->thisInstance->lastError = DMLERR_INVALIDPARAMETER; goto theError; } pXAct->dwTimeout = dwTimeout; /* FIXME: should set the app bits on *pdwResult */ if (dwTimeout == TIMEOUT_ASYNC) { if (pdwResult) { *pdwResult = MAKELONG(0, pXAct->xActID); } hDdeData = (HDDEDATA)1; } WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE); if (dwTimeout != TIMEOUT_ASYNC) { DWORD count = 0; if (pdwResult) { *pdwResult = 0L; } while (ReleaseMutex(handle_mutex)) count++; hDdeData = WDML_SyncWaitTransactionReply((HCONV)pConv, dwTimeout, pXAct); while (count-- != 0) WDML_WaitForMutex(handle_mutex); } return hDdeData; theError: WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE); return 0; } /****************************************************************** * WDML_ClientProc * * Window Proc created on client side for each conversation */ static LRESULT CALLBACK WDML_ClientProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) { UINT uiLow, uiHi; if (iMsg == WM_DDE_ACK && /* In response to WM_DDE_INITIATE, save server window */ UnpackDDElParam(WM_DDE_ACK, lParam, &uiLow, &uiHi) && (WDML_CONV*)GetWindowLongA(hwnd, 4) == NULL) { WDML_INSTANCE* thisInstance = NULL; WDML_CONV* pConv = NULL; FreeDDElParam(WM_DDE_ACK, lParam); /* no converstation yet, add it */ thisInstance = (WDML_INSTANCE*)GetWindowLongA(hwnd, 0); pConv = WDML_AddConv(thisInstance, WDML_CLIENT_SIDE, (HSZ)uiLow, (HSZ)uiHi, hwnd, (HWND)wParam); SetWindowLongA(hwnd, 4, (DWORD)pConv); /* FIXME: so far we only use the first window in the list... */ return 0; } if ((iMsg >= WM_DDE_FIRST && iMsg <= WM_DDE_LAST) && WDML_WaitForMutex(handle_mutex)) { WDML_CONV* pConv = (WDML_CONV*)GetWindowLongA(hwnd, 4); if (pConv) { MSG msg; HDDEDATA hdd; msg.hwnd = hwnd; msg.message = iMsg; msg.wParam = wParam; msg.lParam = lParam; WDML_HandleReply(pConv, &msg, &hdd); } WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE); return 0; } return DefWindowProcA(hwnd, iMsg, wParam, lParam); } /***************************************************************** * DdeAbandonTransaction (USER32.@) */ BOOL WINAPI DdeAbandonTransaction(DWORD idInst, HCONV hConv, DWORD idTransaction) { FIXME("empty stub\n"); return TRUE; } /***************************************************************** * DdeImpersonateClient (USER32.@) */ BOOL WINAPI DdeImpersonateClient(HCONV hConv) { WDML_CONV* pConv; BOOL ret = FALSE; if (!WDML_WaitForMutex(handle_mutex)) { return FALSE; } pConv = WDML_GetConv(hConv); if (pConv) { ret = ImpersonateDdeClientWindow(pConv->hwndClient, pConv->hwndServer); } WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE); return ret; }