Sweden-Number/dlls/user/dde/client.c

1114 lines
28 KiB
C
Raw Normal View History

/* -*- 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 <string.h>
#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_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;
}