/***************************************************************** * DdeImpersonateClient (USER32.@) */ BOOL WINAPI DdeImpersonateClient(HCONV hConv) { WDML_CONV* pConv; BOOL ret = FALSE; TRACE("(%p)\n", hConv); pConv = WDML_GetConv(hConv, TRUE); if (pConv) { ret = ImpersonateDdeClientWindow(pConv->hwndClient, pConv->hwndServer); } return ret; }
/***************************************************************** * DdeAbandonTransaction (USER32.@) */ BOOL WINAPI DdeAbandonTransaction(DWORD idInst, HCONV hConv, DWORD idTransaction) { WDML_INSTANCE* pInstance; WDML_CONV* pConv; WDML_XACT* pXAct; if ((pInstance = WDML_GetInstance(idInst))) { if (hConv) { if ((pConv = WDML_GetConv(hConv, TRUE)) && pConv->instance == pInstance) { pXAct = pConv->transactions; while (pXAct) { WDML_XACT *nextXAct = pXAct->next; if (pXAct->dwTimeout == TIMEOUT_ASYNC && (idTransaction == 0 || pXAct->xActID == idTransaction)) { WDML_UnQueueTransaction(pConv, pXAct); WDML_FreeTransaction(pInstance, pXAct, TRUE); } pXAct = nextXAct; } } } else { for (pConv = pInstance->convs[WDML_CLIENT_SIDE]; pConv; pConv = pConv->next) { if (!(pConv->wStatus & ST_CONNECTED)) continue; pXAct = pConv->transactions; while (pXAct) { WDML_XACT *nextXAct = pXAct->next; if (pXAct->dwTimeout == TIMEOUT_ASYNC) { WDML_UnQueueTransaction(pConv, pXAct); WDML_FreeTransaction(pInstance, pXAct, TRUE); } pXAct = nextXAct; } } } } return TRUE; }
/***************************************************************** * DdeDisconnect (USER32.@) */ BOOL WINAPI DdeDisconnect(HCONV hConv) { WDML_CONV* pConv = NULL; WDML_XACT* pXAct; DWORD count, i; BOOL ret = FALSE; TRACE("(%p)\n", hConv); if (hConv == 0) { WARN("DdeDisconnect(): hConv = 0\n"); return FALSE; } EnterCriticalSection(&WDML_CritSect); pConv = WDML_GetConv(hConv, TRUE); if (pConv != NULL) { if (pConv->wStatus & ST_CLIENT) { /* FIXME: should abandon all pending transactions */ pXAct = WDML_ClientQueueTerminate(pConv); if (pXAct != NULL) { count = WDML_CritSect.RecursionCount; for (i = 0; i < count; i++) LeaveCriticalSection(&WDML_CritSect); if (PostMessageW(pConv->hwndServer, pXAct->ddeMsg, (WPARAM)pConv->hwndClient, pXAct->lParam)) WDML_SyncWaitTransactionReply(hConv, 10000, pXAct, NULL); for (i = 0; i < count; i++) EnterCriticalSection(&WDML_CritSect); ret = TRUE; WDML_FreeTransaction(pConv->instance, pXAct, TRUE); /* still have to destroy data assosiated with conversation */ WDML_RemoveConv(pConv, WDML_CLIENT_SIDE); } else { FIXME("Not implemented yet for a server side conversation\n"); } } } LeaveCriticalSection(&WDML_CritSect); return ret; }
/***************************************************************** * DdeImpersonateClient (USER32.@) */ BOOL WINAPI DdeImpersonateClient(HCONV hConv) { WDML_CONV* pConv; BOOL ret = FALSE; TRACE("(%p)\n", hConv); EnterCriticalSection(&WDML_CritSect); pConv = WDML_GetConv(hConv, TRUE); if (pConv) { ret = ImpersonateDdeClientWindow(pConv->hwndClient, pConv->hwndServer); } LeaveCriticalSection(&WDML_CritSect); return ret; }
/***************************************************************** * DdeAbandonTransaction (USER32.@) */ BOOL WINAPI DdeAbandonTransaction(DWORD idInst, HCONV hConv, DWORD idTransaction) { WDML_INSTANCE* pInstance; WDML_CONV* pConv; WDML_XACT* pXAct; TRACE("(%08lx,%p,%08lx);\n", idInst, hConv, idTransaction); EnterCriticalSection(&WDML_CritSect); if ((pInstance = WDML_GetInstance(idInst))) { if (hConv) { if ((pConv = WDML_GetConv(hConv, TRUE)) && pConv->instance == pInstance) { for (pXAct = pConv->transactions; pXAct; pXAct = pXAct->next) { if (pXAct->dwTimeout == TIMEOUT_ASYNC && (idTransaction == 0 || pXAct->xActID == idTransaction)) { WDML_UnQueueTransaction(pConv, pXAct); WDML_FreeTransaction(pInstance, pXAct, TRUE); } } } } else { for (pConv = pInstance->convs[WDML_CLIENT_SIDE]; pConv; pConv = pConv->next) { if (!(pConv->wStatus & ST_CONNECTED)) continue; for (pXAct = pConv->transactions; pXAct; pXAct = pXAct->next) { if (pXAct->dwTimeout == TIMEOUT_ASYNC) { WDML_UnQueueTransaction(pConv, pXAct); WDML_FreeTransaction(pInstance, pXAct, TRUE); } } } } } LeaveCriticalSection(&WDML_CritSect); return TRUE; }
/***************************************************************** * DdeDisconnect (USER32.@) */ BOOL WINAPI DdeDisconnect(HCONV hConv) { WDML_CONV* pConv; WDML_XACT* pXAct; BOOL ret = FALSE; TRACE("(%p)\n", hConv); if (hConv == 0) { WARN("DdeDisconnect(): hConv = 0\n"); return FALSE; } pConv = WDML_GetConv(hConv, TRUE); if (pConv != NULL) { if (pConv->wStatus & ST_CLIENT) { /* FIXME: should abandon all pending transactions */ pXAct = WDML_ClientQueueTerminate(pConv); if (pXAct != NULL) { if (PostMessageW(pConv->hwndServer, pXAct->ddeMsg, (WPARAM)pConv->hwndClient, pXAct->lParam)) { WDML_SyncWaitTransactionReply(hConv, 10000, pXAct, NULL); ret = TRUE; } else pConv->instance->lastError = DMLERR_POSTMSG_FAILED; WDML_FreeTransaction(pConv->instance, pXAct, TRUE); /* still have to destroy data associated with conversation */ WDML_RemoveConv(pConv, WDML_CLIENT_SIDE); } else { FIXME("Not implemented yet for a server side conversation\n"); } } } return ret; }
/****************************************************************************** * DdePostAdvise [USER32.@] Send transaction to DDE callback function. * * PARAMS * idInst [I] Instance identifier * hszTopic [I] Handle to topic name string * hszItem [I] Handle to item name string * * RETURNS * Success: TRUE * Failure: FALSE */ BOOL WINAPI DdePostAdvise(DWORD idInst, HSZ hszTopic, HSZ hszItem) { WDML_INSTANCE* pInstance = NULL; WDML_LINK* pLink = NULL; HDDEDATA hDdeData = 0; HGLOBAL hItemData = 0; WDML_CONV* pConv = NULL; ATOM atom = 0; UINT count; TRACE("(%ld,0x%x,0x%x)\n", idInst, hszTopic, hszItem); EnterCriticalSection(&WDML_CritSect); pInstance = WDML_GetInstance(idInst); if (pInstance == NULL || pInstance->links == NULL) { goto theError; } atom = WDML_MakeAtomFromHsz(hszItem); if (!atom) goto theError; /* first compute the number of links which will trigger a message */ count = 0; for (pLink = pInstance->links[WDML_SERVER_SIDE]; pLink != NULL; pLink = pLink->next) { if (DdeCmpStringHandles(hszItem, pLink->hszItem) == 0) { count++; } } if (count >= CADV_LATEACK) { FIXME("too high value for count\n"); count &= 0xFFFF; } for (pLink = pInstance->links[WDML_SERVER_SIDE]; pLink != NULL; pLink = pLink->next) { if (DdeCmpStringHandles(hszItem, pLink->hszItem) == 0) { hDdeData = WDML_InvokeCallback(pInstance, XTYP_ADVREQ, pLink->uFmt, pLink->hConv, hszTopic, hszItem, 0, count--, 0); if (hDdeData == (HDDEDATA)CBR_BLOCK) { /* MS doc is not consistent here */ FIXME("CBR_BLOCK returned for ADVREQ\n"); continue; } if (hDdeData) { if (pLink->transactionType & XTYPF_NODATA) { TRACE("no data\n"); hItemData = 0; } else { TRACE("with data\n"); hItemData = WDML_DataHandle2Global(hDdeData, FALSE, FALSE, FALSE, FALSE); } pConv = WDML_GetConv(pLink->hConv, TRUE); if (pConv == NULL) { if (!WDML_IsAppOwned(hDdeData)) DdeFreeDataHandle(hDdeData); goto theError; } if (!PostMessageA(pConv->hwndClient, WM_DDE_DATA, (WPARAM)pConv->hwndServer, PackDDElParam(WM_DDE_DATA, (UINT)hItemData, atom))) { ERR("post message failed\n"); pConv->wStatus &= ~ST_CONNECTED; if (!WDML_IsAppOwned(hDdeData)) DdeFreeDataHandle(hDdeData); GlobalFree(hItemData); goto theError; } if (!WDML_IsAppOwned(hDdeData)) DdeFreeDataHandle(hDdeData); } } } LeaveCriticalSection(&WDML_CritSect); return TRUE; theError: LeaveCriticalSection(&WDML_CritSect); if (atom) GlobalDeleteAtom(atom); return FALSE; }
/***************************************************************** * DdeReconnect (DDEML.37) * DdeReconnect (USER32.@) */ HCONV WINAPI DdeReconnect(HCONV hConv) { WDML_CONV* pConv; WDML_CONV* pNewConv = NULL; ATOM aSrv = 0, aTpc = 0; TRACE("(%p)\n", hConv); pConv = WDML_GetConv(hConv, FALSE); if (pConv != NULL && (pConv->wStatus & ST_CLIENT)) { BOOL ret; /* to reestablish a connection, we have to make sure that: * 1/ pConv is the conversation attached to the client window (it wouldn't be * if a call to DdeReconnect would have already been done...) * FIXME: is this really an error ??? * 2/ the pConv conversation had really been deconnected */ if (pConv == WDML_GetConvFromWnd(pConv->hwndClient) && (pConv->wStatus & ST_TERMINATED) && !(pConv->wStatus & ST_CONNECTED)) { HWND hwndClient = pConv->hwndClient; HWND hwndServer = pConv->hwndServer; SetWindowLongPtrW(pConv->hwndClient, GWL_WDML_CONVERSATION, 0); aSrv = WDML_MakeAtomFromHsz(pConv->hszService); aTpc = WDML_MakeAtomFromHsz(pConv->hszTopic); if (!aSrv || !aTpc) goto theEnd; /* note: sent messages shall not use packing */ ret = SendMessageW(hwndServer, WM_DDE_INITIATE, (WPARAM)hwndClient, MAKELPARAM(aSrv, aTpc)); pConv = WDML_GetConv(hConv, FALSE); if (pConv == NULL) { FIXME("Should fail reconnection\n"); goto theEnd; } if (ret && (pNewConv = WDML_GetConvFromWnd(pConv->hwndClient)) != NULL) { /* re-establish all links... */ WDML_LINK* pLink; for (pLink = pConv->instance->links[WDML_CLIENT_SIDE]; pLink; pLink = pLink->next) { if (pLink->hConv == hConv) { /* try to reestablish the links... */ DdeClientTransaction(NULL, 0, (HCONV)pNewConv, pLink->hszItem, pLink->uFmt, pLink->transactionType, 1000, NULL); } } } else { /* reset the conversation as it was */ SetWindowLongPtrW(pConv->hwndClient, GWL_WDML_CONVERSATION, (ULONG_PTR)pConv); } } } theEnd: if (aSrv) GlobalDeleteAtom(aSrv); if (aTpc) GlobalDeleteAtom(aTpc); return (HCONV)pNewConv; }
/***************************************************************** * 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; TRACE("(%p,%d,%p,%p,%x,%x,%d,%p)\n", pData, cbData, hConv, hszItem, wFmt, wType, dwTimeout, pdwResult); if (hConv == 0) { WARN("Invalid conversation handle NULL\n"); return 0; } pConv = WDML_GetConv(hConv, TRUE); if (pConv == NULL) { /* cannot set error... cannot get back to DDE instance */ return 0; } switch (wType) { case XTYP_EXECUTE: /* Windows simply ignores hszItem and wFmt in this case */ pXAct = WDML_ClientQueueExecute(pConv, pData, cbData); if (pXAct == NULL) return 0; break; case XTYP_POKE: if (!hszItem) { pConv->instance->lastError = DMLERR_INVALIDPARAMETER; return 0; } pXAct = WDML_ClientQueuePoke(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->instance->lastError = DMLERR_INVALIDPARAMETER; return 0; } pXAct = WDML_ClientQueueAdvise(pConv, wType, wFmt, hszItem); break; case XTYP_ADVSTOP: if (pData) { pConv->instance->lastError = DMLERR_INVALIDPARAMETER; return 0; } pXAct = WDML_ClientQueueUnadvise(pConv, wFmt, hszItem); break; case XTYP_REQUEST: if (pData || !hszItem) { pConv->instance->lastError = DMLERR_INVALIDPARAMETER; return 0; } pXAct = WDML_ClientQueueRequest(pConv, wFmt, hszItem); break; default: FIXME("Unknown transaction type %04x\n", wType); /* unknown transaction type */ pConv->instance->lastError = DMLERR_INVALIDPARAMETER; return 0; } if (pXAct == NULL) { pConv->instance->lastError = DMLERR_MEMORY_ERROR; return 0; } WDML_QueueTransaction(pConv, pXAct); TRACE("pConv->wStatus %04x\n", pConv->wStatus); if (pConv->wStatus & ST_BLOCKED) { TRACE("Transactions are blocked, add to the queue and exit\n"); return (HDDEDATA)1; } hDdeData = WDML_ClientHandle(pConv, pXAct, dwTimeout, pdwResult); if (dwTimeout != TIMEOUT_ASYNC) { WDML_UnQueueTransaction(pConv, pXAct); WDML_FreeTransaction(pConv->instance, pXAct, TRUE); } return hDdeData; }
/****************************************************************** * 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, const WDML_XACT* pXAct, DWORD *ack) { DWORD start, elapsed; DWORD err; WDML_CONV* pConv; TRACE("Starting wait for a timeout of %d ms\n", dwTimeout); start = GetTickCount(); while ((elapsed = GetTickCount() - start) < dwTimeout) { /* we cannot be in the crit sect 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, dwTimeout - elapsed, QS_POSTMESSAGE) == WAIT_OBJECT_0) { MSG msg; while (PeekMessageW(&msg, 0, WM_DDE_FIRST, WM_DDE_LAST, PM_REMOVE)) { HDDEDATA hdd = NULL; pConv = WDML_GetConv(hConv, FALSE); if (pConv == NULL) { /* conversation no longer available... return failure */ return 0; } if (msg.hwnd == pConv->hwndClient) { /* check that either pXAct has been processed or no more xActions are pending */ BOOL ret = (pConv->transactions == pXAct); if (WDML_HandleReply(pConv, &msg, &hdd, ack) == WDML_QS_HANDLED) { TRACE("WDML_HandleReply returned WDML_QS_HANDLED\n"); ret = TRUE; } else ret = (pConv->transactions == NULL || ret); if (ret) { pConv->instance->lastError = hdd ? DMLERR_NO_ERROR : DMLERR_NOTPROCESSED; return hdd; } } else { DispatchMessageW(&msg); } } } } TRACE("Timeout !!\n"); pConv = WDML_GetConv(hConv, FALSE); if (pConv != NULL) { if (pConv->transactions) { 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->instance->lastError = err; } } 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("(%p,%ld,%p,%p,%x,%x,%ld,%p)\n", pData, cbData, hConv, hszItem, wFmt, wType, dwTimeout, pdwResult); if (hConv == 0) { WARN("Invalid conversation handle NULL\n"); return 0; } EnterCriticalSection(&WDML_CritSect); pConv = WDML_GetConv(hConv, TRUE); if (pConv == NULL) { /* cannot set error... cannot get back to DDE instance */ goto theError; } switch (wType) { case XTYP_EXECUTE: /* Windows simply ignores hszItem and wFmt in this case */ pXAct = WDML_ClientQueueExecute(pConv, pData, cbData); break; case XTYP_POKE: pXAct = WDML_ClientQueuePoke(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->instance->lastError = DMLERR_INVALIDPARAMETER; goto theError; } pXAct = WDML_ClientQueueAdvise(pConv, wType, wFmt, hszItem); break; case XTYP_ADVSTOP: if (pData) { pConv->instance->lastError = DMLERR_INVALIDPARAMETER; goto theError; } pXAct = WDML_ClientQueueUnadvise(pConv, wFmt, hszItem); break; case XTYP_REQUEST: if (pData) { pConv->instance->lastError = DMLERR_INVALIDPARAMETER; goto theError; } pXAct = WDML_ClientQueueRequest(pConv, wFmt, hszItem); break; default: FIXME("Unknown transation\n"); /* unknown transaction type */ pConv->instance->lastError = DMLERR_INVALIDPARAMETER; goto theError; } if (pXAct == NULL) { pConv->instance->lastError = DMLERR_MEMORY_ERROR; goto theError; } WDML_QueueTransaction(pConv, pXAct); if (!PostMessageW(pConv->hwndServer, pXAct->ddeMsg, (WPARAM)pConv->hwndClient, pXAct->lParam)) { WARN("Failed posting message %x to %p (error=0x%lx)\n", pXAct->ddeMsg, pConv->hwndServer, GetLastError()); pConv->wStatus &= ~ST_CONNECTED; WDML_UnQueueTransaction(pConv, pXAct); WDML_FreeTransaction(pConv->instance, pXAct, TRUE); 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; } else { DWORD count, i; count = WDML_CritSect.RecursionCount; for (i = 0; i < count; i++) LeaveCriticalSection(&WDML_CritSect); hDdeData = WDML_SyncWaitTransactionReply((HCONV)pConv, dwTimeout, pXAct, pdwResult); for (i = 0; i < count; i++) EnterCriticalSection(&WDML_CritSect); } LeaveCriticalSection(&WDML_CritSect); return hDdeData; theError: LeaveCriticalSection(&WDML_CritSect); return 0; }