1114 lines
28 KiB
C
1114 lines
28 KiB
C
/* -*- 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/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;
|
|
}
|