Commit 6a03ac0e authored by Eric Pouech's avatar Eric Pouech Committed by Alexandre Julliard

Merge from Corel tree. Contributors are:

- Corel (Sean Langley, Michael Abd-El-Malek, Rick Mutzke, Bill Xing) - Macadamian on behalf of Corel (Louis-Philippe Gagnon, Jean-Claude Batista) Also added a few bits: - Complete window model for several instances and conversations. - Support for asynchronous transactions. - Fixed some code (WM_DDE_REQUEST, WM_DDE_ADVISE, WM_DDE_REQUEST) handling. - Support for wild connections.
parent f4b14106
......@@ -10,7 +10,10 @@ IMPORTS = gdi32 kernel32 ntdll
C_SRCS = \
bidi16.c \
cache.c \
ddeml.c \
dde/client.c \
dde/ddeml16.c \
dde/misc.c \
dde/server.c \
display.c \
exticon.c \
lstr.c \
......@@ -42,6 +45,7 @@ SUBDIRS = \
$(TOPOBJDIR)/windows
EXTRASUBDIRS = \
dde \
resources
@MAKE_DLL_RULES@
......
/* -*- 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;
}
/* -*- 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
*/
#ifndef __WINE_DDEML_PRIVATE_H
#define __WINE_DDEML_PRIVATE_H
/* defined in atom.c file.
*/
#define MAX_ATOM_LEN 255
/* Maximum buffer size ( including the '\0' ).
*/
#define MAX_BUFFER_LEN (MAX_ATOM_LEN + 1)
/* This is a simple list to keep track of the strings created
* by DdeCreateStringHandle. The list is used to free
* the strings whenever DdeUninitialize is called.
* This mechanism is not complete and does not handle multiple instances.
* Most of the DDE API use a DWORD parameter indicating which instance
* of a given program is calling them. The API are supposed to
* associate the data to the instance that created it.
*/
/* The internal structures (prefixed by WDML) are used as follows:
* + a WDML_INSTANCE is created for each instance creation (DdeInitialize)
* - a popup windows (InstanceClass) is created for each instance. It will be
* used to receive all the DDEML events (server registration, conversation
* confirmation...)
* + when registring a server (DdeNameService) a WDML_SERVER is created
* - a popup window (ServerNameClass) is created
* + a conversation is represented by two WDML_CONV structures:
* - one on the client side, the other one on the server side
* - this is needed because the address spaces may be different
* - therefore, two lists of links are kept for each instance
* - two windows are created for a conversation:
* o a popup window on client side (ClientConvClass)
* o a child window (of the ServerName) on the server side
* (ServerConvClass)
* - all the exchanges then take place between those two windows
* + a (warm or link) is represented by two WDML_LINK structures:
* - one on client side, the other one on server side
* - therefore, two lists of links are kept for each instance
*
* To help getting back to data, WDML windows store information:
* - offset 0: the DDE instance
* - offset 4: the current conversation (for ClientConv and ServerConv only)
*
*/
typedef struct tagHSZNode
{
struct tagHSZNode* next;
HSZ hsz;
HSZ hsz2;
} HSZNode;
typedef struct tagWDML_SERVER
{
struct tagWDML_SERVER* next;
HSZ hszService;
HSZ hszTopic;
BOOL filterOn;
HWND hwndServer;
} WDML_SERVER;
typedef struct tagWDML_XACT {
struct tagWDML_XACT* next;
DWORD xActID;
UINT ddeMsg;
HDDEDATA hDdeData;
DWORD dwTimeout;
DWORD hUser;
union {
struct {
UINT wType;
UINT wFmt;
HSZ hszItem;
HGLOBAL hDdeAdvise;
} advise;
struct {
UINT wFmt;
HSZ hszItem;
} unadvise;
struct {
HGLOBAL hMem;
} execute;
struct {
HGLOBAL hMem;
HSZ hszItem;
} poke;
struct {
HSZ hszItem;
} request;
} u;
} WDML_XACT;
typedef struct tagWDML_CONV
{
struct tagWDML_CONV* next; /* to link all the conversations */
struct tagWDML_INSTANCE* thisInstance;
HSZ hszService; /* pmt used for connection */
HSZ hszTopic; /* pmt used for connection */
UINT afCmd; /* service name flag */
CONVCONTEXT convContext;
HWND hwndClient; /* source of conversation (ClientConvClass) */
HWND hwndServer; /* destination of conversation (ServerConvClass) */
WDML_XACT* transactions; /* pending transactions (client only) */
DWORD hUser; /* user defined value */
} WDML_CONV;
/* DDE_LINK struct defines hot, warm, and cold links */
typedef struct tagWDML_LINK {
struct tagWDML_LINK* next; /* to link all the active links */
HCONV hConv; /* to get back to the converstaion */
UINT transactionType;/* 0 for no link */
HSZ hszItem; /* item targetted for (hot/warm) link */
UINT uFmt; /* format for data */
HDDEDATA hDdeData; /* data them selves */
} WDML_LINK;
typedef struct tagWDML_INSTANCE
{
struct tagWDML_INSTANCE* next;
DWORD instanceID; /* needed to track monitor usage */
BOOL monitor; /* have these two as full Booleans cos they'll be tested frequently */
BOOL clientOnly; /* bit wasteful of space but it will be faster */
BOOL unicode; /* Flag to indicate Win32 API used to initialise */
BOOL win16; /* flag to indicate Win16 API used to initialize */
HSZNode* nodeList; /* for cleaning upon exit */
PFNCALLBACK callback;
DWORD CBFflags;
DWORD monitorFlags;
UINT txnCount; /* count transactions open to simplify closure */
DWORD lastError;
HWND hwndEvent;
WDML_SERVER* servers; /* list of registered servers */
WDML_CONV* convs[2]; /* active conversations for this instance (client and server) */
WDML_LINK* links[2]; /* active links for this instance (client and server) */
} WDML_INSTANCE;
extern WDML_INSTANCE* WDML_InstanceList; /* list of created instances, a process can create many */
extern DWORD WDML_MaxInstanceID; /* FIXME: OK for present, may have to worry about wrap-around later */
extern HANDLE handle_mutex;
/* header for the DDE Data objects */
typedef struct tagDDE_DATAHANDLE_HEAD
{
short cfFormat;
} DDE_DATAHANDLE_HEAD;
typedef enum tagWDML_SIDE
{
WDML_CLIENT_SIDE = 0, WDML_SERVER_SIDE = 1
} WDML_SIDE;
/* server calls this. */
extern WDML_SERVER* WDML_AddServer(WDML_INSTANCE* thisInstance, HSZ hszService, HSZ hszTopic);
extern void WDML_RemoveServer(WDML_INSTANCE* thisInstance, HSZ hszService, HSZ hszTopic);
extern WDML_SERVER* WDML_FindServer(WDML_INSTANCE* thisInstance, HSZ hszService, HSZ hszTopic);
/* called both in DdeClientTransaction and server side. */
extern WDML_CONV* WDML_AddConv(WDML_INSTANCE* thisInstance, WDML_SIDE side,
HSZ hszService, HSZ hszTopic, HWND hwndClient, HWND hwndServer);
extern void WDML_RemoveConv(WDML_INSTANCE* thisInstance, WDML_SIDE side, HCONV hConv);
extern WDML_CONV* WDML_GetConv(HCONV hConv);
extern WDML_CONV* WDML_FindConv(WDML_INSTANCE* thisInstance, WDML_SIDE side,
HSZ hszService, HSZ hszTopic);
extern void WDML_AddLink(WDML_INSTANCE* thisInstance, HCONV hConv, WDML_SIDE side,
UINT wType, HSZ hszItem, UINT wFmt);
extern WDML_LINK* WDML_FindLink(WDML_INSTANCE* thisInstance, HCONV hConv, WDML_SIDE side,
HSZ hszItem, UINT uFmt);
extern void WDML_RemoveLink(WDML_INSTANCE* thisInstance, HCONV hConv, WDML_SIDE side,
HSZ hszItem, UINT wFmt);
extern void WDML_RemoveAllLinks(WDML_INSTANCE* thisInstance, HCONV hConv, WDML_SIDE side);
/* client calls these */
extern WDML_XACT* WDML_AllocTransaction(WDML_INSTANCE* thisInstance, UINT ddeMsg);
extern void WDML_QueueTransaction(WDML_CONV* pConv, WDML_XACT* pXAct);
extern BOOL WDML_UnQueueTransaction(WDML_CONV* pConv, WDML_XACT* pXAct);
extern void WDML_FreeTransaction(WDML_XACT* pXAct);
extern WDML_XACT* WDML_FindTransaction(WDML_CONV* pConv, DWORD tid);
extern HGLOBAL WDML_DataHandle2Global(HDDEDATA hDdeData, BOOL fResponse, BOOL fRelease,
BOOL fDeferUpd, BOOL dAckReq);
extern HDDEDATA WDML_Global2DataHandle(HGLOBAL hMem);
extern WDML_INSTANCE* WDML_FindInstance(DWORD InstId);
extern BOOL WDML_WaitForMutex(HANDLE mutex);
extern DWORD WDML_ReleaseMutex(HANDLE mutex, LPSTR mutex_name, BOOL release_handle_m);
extern void WDML_FreeAllHSZ(WDML_INSTANCE* thisInstance);
extern void WDML_ReleaseAtom(WDML_INSTANCE* thisInstance, HSZ hsz);
extern void WDML_ReserveAtom(WDML_INSTANCE* thisInstance, HSZ hsz);
/* broadcasting to DDE windows */
extern void WDML_BroadcastDDEWindows(const char* clsName, UINT uMsg,
WPARAM wParam, LPARAM lParam);
extern const char WDML_szEventClass[]; /* class of window for events (aka instance) */
#define WM_WDML_REGISTER (WM_USER + 0x200)
#define WM_WDML_UNREGISTER (WM_USER + 0x201)
#endif /* __WINE_DDEML_PRIVATE_H */
/* -*- tab-width: 8; c-basic-offset: 8 -*- */
/*
* 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 "wine/windef16.h"
#include "wingdi.h"
#include "winuser.h"
#include "winerror.h"
#include "dde.h"
#include "ddeml.h"
#include "debugtools.h"
DEFAULT_DEBUG_CHANNEL(ddeml);
typedef HDDEDATA CALLBACK (*PFNCALLBACK16)(UINT16,UINT16,HCONV,HSZ,HSZ,HDDEDATA,DWORD,DWORD);
typedef struct
{
UINT16 cb;
UINT16 wFlags;
UINT16 wCountryID;
INT16 iCodePage;
DWORD dwLangID;
DWORD dwSecurity;
} CONVCONTEXT16, *LPCONVCONTEXT16;
typedef struct
{
DWORD cb;
DWORD hUser;
HCONV hConvPartner;
HSZ hszSvcPartner;
HSZ hszServiceReq;
HSZ hszTopic;
HSZ hszItem;
UINT16 wFmt;
UINT16 wType;
UINT16 wStatus;
UINT16 wConvst;
UINT16 wLastError;
HCONVLIST hConvList;
CONVCONTEXT16 ConvCtxt;
} CONVINFO16, *LPCONVINFO16;
/******************************************************************************
* DdeInitialize16 (DDEML.2)
*/
UINT16 WINAPI DdeInitialize16(LPDWORD pidInst, PFNCALLBACK16 pfnCallback,
DWORD afCmd, DWORD ulRes)
{
return (UINT16)DdeInitializeA(pidInst,(PFNCALLBACK)pfnCallback,
afCmd, ulRes);
}
/*****************************************************************
* DdeUninitialize16 (DDEML.3)
*/
BOOL16 WINAPI DdeUninitialize16(DWORD idInst)
{
FIXME(" stub calling DdeUninitialize\n");
return (BOOL16)DdeUninitialize(idInst);
}
/*****************************************************************
* DdeConnectList16 [DDEML.4]
*/
HCONVLIST WINAPI DdeConnectList16(DWORD idInst, HSZ hszService, HSZ hszTopic,
HCONVLIST hConvList, LPCONVCONTEXT16 pCC)
{
return DdeConnectList(idInst, hszService, hszTopic, hConvList,
(LPCONVCONTEXT)pCC);
}
/*****************************************************************
* DdeQueryNextServer16 [DDEML.5]
*/
HCONV WINAPI DdeQueryNextServer16(HCONVLIST hConvList, HCONV hConvPrev)
{
return DdeQueryNextServer(hConvList, hConvPrev);
}
/*****************************************************************
* DdeDisconnectList (DDEML.6)
*/
BOOL16 WINAPI DdeDisconnectList16(HCONVLIST hConvList)
{
return (BOOL16)DdeDisconnectList(hConvList);
}
/*****************************************************************
* DdeQueryString16 (DDEML.23)
*/
DWORD WINAPI DdeQueryString16(DWORD idInst, HSZ hsz, LPSTR lpsz, DWORD cchMax, INT16 codepage)
{
FIXME("(%ld, 0x%x, %p, %ld, %d): stub \n",
idInst, hsz, lpsz, cchMax, codepage);
return 0;
}
/*****************************************************************
* DdeConnect16 (DDEML.7)
*/
HCONV WINAPI DdeConnect16(DWORD idInst, HSZ hszService, HSZ hszTopic,
LPCONVCONTEXT16 pCC16)
{
CONVCONTEXT cc;
CONVCONTEXT* pCC = NULL;
if (pCC16) {
pCC = &cc;
cc.cb = sizeof(cc);
cc.wFlags = pCC16->wFlags;
cc.iCodePage = pCC16->iCodePage;
cc.dwLangID = pCC16->dwLangID;
cc.dwSecurity = pCC16->dwSecurity;
}
return DdeConnect(idInst, hszService, hszTopic, pCC);
}
/*****************************************************************
* DdeDisconnect16 (DDEML.8)
*/
BOOL16 WINAPI DdeDisconnect16(HCONV hConv)
{
return (BOOL16)DdeDisconnect(hConv);
}
/*****************************************************************
* DdeSetUserHandle16 (DDEML.10)
*/
BOOL16 WINAPI DdeSetUserHandle16(HCONV hConv, DWORD id, DWORD hUser)
{
FIXME("(%d,%ld,%ld): stub\n",hConv,id, hUser);
return 0;
}
/*****************************************************************
* DdeCreateDataHandle16 (DDEML.14)
*/
HDDEDATA WINAPI DdeCreateDataHandle16(DWORD idInst, LPBYTE pSrc, DWORD cb,
DWORD cbOff, HSZ hszItem, UINT16 wFmt,
UINT16 afCmd)
{
return DdeCreateDataHandle(idInst, pSrc, cb, cbOff, hszItem, wFmt, afCmd);
}
/*****************************************************************
* DdeCreateStringHandle16 (DDEML.21)
*/
HSZ WINAPI DdeCreateStringHandle16(DWORD idInst, LPCSTR str, INT16 codepage)
{
if (codepage)
{
return DdeCreateStringHandleA(idInst, str, codepage);
} else {
TRACE("Default codepage supplied\n");
return DdeCreateStringHandleA(idInst, str, CP_WINANSI);
}
}
/*****************************************************************
* DdeFreeStringHandle16 (DDEML.22)
*/
BOOL16 WINAPI DdeFreeStringHandle16(DWORD idInst, HSZ hsz)
{
FIXME("idInst %ld hsz 0x%x\n",idInst,hsz);
return (BOOL)DdeFreeStringHandle(idInst, hsz);
}
/*****************************************************************
* DdeFreeDataHandle16 (DDEML.19)
*/
BOOL16 WINAPI DdeFreeDataHandle16(HDDEDATA hData)
{
return (BOOL)DdeFreeDataHandle(hData);
}
/*****************************************************************
* DdeKeepStringHandle16 (DDEML.24)
*/
BOOL16 WINAPI DdeKeepStringHandle16(DWORD idInst, HSZ hsz)
{
return (BOOL)DdeKeepStringHandle(idInst, hsz);
}
/*****************************************************************
* DdeClientTransaction16 (DDEML.11)
*/
HDDEDATA WINAPI DdeClientTransaction16(LPVOID pData, DWORD cbData,
HCONV hConv, HSZ hszItem, UINT16 wFmt,
UINT16 wType, DWORD dwTimeout,
LPDWORD pdwResult)
{
return DdeClientTransaction((LPBYTE)pData, cbData, hConv, hszItem,
wFmt, wType, dwTimeout, pdwResult);
}
/*****************************************************************
*
* DdeAbandonTransaction16 (DDEML.12)
*
*/
BOOL16 WINAPI DdeAbandonTransaction16(DWORD idInst, HCONV hConv,
DWORD idTransaction)
{
FIXME("empty stub\n");
return TRUE;
}
/*****************************************************************
* DdePostAdvise16 [DDEML.13]
*/
BOOL16 WINAPI DdePostAdvise16(DWORD idInst, HSZ hszTopic, HSZ hszItem)
{
return (BOOL16)DdePostAdvise(idInst, hszTopic, hszItem);
}
/*****************************************************************
* DdeAddData16 (DDEML.15)
*/
HDDEDATA WINAPI DdeAddData16(HDDEDATA hData, LPBYTE pSrc, DWORD cb,
DWORD cbOff)
{
return DdeAddData(hData, pSrc, cb, cbOff);
}
/*****************************************************************
* DdeGetData16 [DDEML.16]
*/
DWORD WINAPI DdeGetData16(
HDDEDATA hData,
LPBYTE pDst,
DWORD cbMax,
DWORD cbOff)
{
return DdeGetData(hData, pDst, cbMax, cbOff);
}
/*****************************************************************
* DdeAccessData16 (DDEML.17)
*/
LPBYTE WINAPI DdeAccessData16(HDDEDATA hData, LPDWORD pcbDataSize)
{
return DdeAccessData(hData, pcbDataSize);
}
/*****************************************************************
* DdeUnaccessData16 (DDEML.18)
*/
BOOL16 WINAPI DdeUnaccessData16(HDDEDATA hData)
{
return DdeUnaccessData(hData);
}
/*****************************************************************
* DdeEnableCallback16 (DDEML.26)
*/
BOOL16 WINAPI DdeEnableCallback16(DWORD idInst, HCONV hConv, UINT16 wCmd)
{
return DdeEnableCallback(idInst, hConv, wCmd);
}
/*****************************************************************
* DdeNameService16 (DDEML.27)
*/
HDDEDATA WINAPI DdeNameService16(DWORD idInst, HSZ hsz1, HSZ hsz2,
UINT16 afCmd)
{
return DdeNameService(idInst, hsz1, hsz2, afCmd);
}
/*****************************************************************
* DdeGetLastError16 (DDEML.20)
*/
UINT16 WINAPI DdeGetLastError16(DWORD idInst)
{
return (UINT16)DdeGetLastError(idInst);
}
/*****************************************************************
* DdeCmpStringHandles16 (DDEML.36)
*/
INT16 WINAPI DdeCmpStringHandles16(HSZ hsz1, HSZ hsz2)
{
return DdeCmpStringHandles(hsz1, hsz2);
}
/******************************************************************
* DdeQueryConvInfo16 (DDEML.9)
*
*/
UINT16 WINAPI DdeQueryConvInfo16(HCONV hconv, DWORD idTransaction, LPCONVINFO16 lpConvInfo)
{
FIXME("stub.\n");
return 0;
}
/* -*- 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);
WDML_INSTANCE* WDML_InstanceList = NULL;
DWORD WDML_MaxInstanceID = 0; /* OK for present, may have to worry about wrap-around later */
static const char DDEInstanceAccess[] = "DDEMaxInstance";
static const char DDEHandleAccess[] = "DDEHandleAccess";
HANDLE handle_mutex = 0;
const char WDML_szEventClass[] = "DdeEventClass";
/* FIXME
* currently the msg parameter is not used in the packing functions.
* it should be used to identify messages which don't actually require the packing operation
* but would do with the simple DWORD for lParam
*/
static BOOL DDE_RequirePacking(UINT msg)
{
BOOL ret;
switch (msg)
{
case WM_DDE_ACK:
case WM_DDE_ADVISE:
case WM_DDE_DATA:
case WM_DDE_POKE:
ret = TRUE;
break;
case WM_DDE_EXECUTE: /* strange, NT 2000 (at least) really uses packing here... */
case WM_DDE_INITIATE:
case WM_DDE_REQUEST: /* assuming clipboard formats are 16 bit */
case WM_DDE_TERMINATE:
case WM_DDE_UNADVISE: /* assuming clipboard formats are 16 bit */
ret = FALSE;
break;
default:
TRACE("Unknown message %04x\n", msg);
ret = FALSE;
break;
}
return ret;
}
/*****************************************************************
* PackDDElParam (USER32.@)
*
* RETURNS
* the packed lParam
*/
LPARAM WINAPI PackDDElParam(UINT msg, UINT uiLo, UINT uiHi)
{
HGLOBAL hMem;
UINT* params;
if (!DDE_RequirePacking(msg))
return MAKELONG(uiLo, uiHi);
if (!(hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, sizeof(UINT) * 2)))
{
ERR("GlobalAlloc failed\n");
return 0;
}
params = GlobalLock(hMem);
if (params == NULL)
{
ERR("GlobalLock failed\n");
return 0;
}
params[0] = uiLo;
params[1] = uiHi;
GlobalUnlock(hMem);
return (LPARAM)hMem;
}
/*****************************************************************
* UnpackDDElParam (USER32.@)
*
* RETURNS
* success: nonzero
* failure: zero
*/
BOOL WINAPI UnpackDDElParam(UINT msg, LPARAM lParam,
PUINT uiLo, PUINT uiHi)
{
HGLOBAL hMem;
UINT *params;
if (!DDE_RequirePacking(msg))
{
*uiLo = LOWORD(lParam);
*uiHi = HIWORD(lParam);
return TRUE;
}
if (lParam == 0)
{
return FALSE;
}
hMem = (HGLOBAL)lParam;
params = GlobalLock(hMem);
if (params == NULL)
{
ERR("GlobalLock failed\n");
return FALSE;
}
*uiLo = params[0];
*uiHi = params[1];
GlobalUnlock(hMem);
return TRUE;
}
/*****************************************************************
* FreeDDElParam (USER32.@)
*
* RETURNS
* success: nonzero
* failure: zero
*/
BOOL WINAPI FreeDDElParam(UINT msg, LPARAM lParam)
{
HGLOBAL hMem = (HGLOBAL)lParam;
if (!DDE_RequirePacking(msg))
return TRUE;
if (lParam == 0)
{
return FALSE;
}
return GlobalFree(hMem) == (HGLOBAL)NULL;
}
/*****************************************************************
* ReuseDDElParam (USER32.@)
*
* RETURNS
* the packed lParam
*/
LPARAM WINAPI ReuseDDElParam(LPARAM lParam, UINT msgIn, UINT msgOut,
UINT uiLo, UINT uiHi)
{
HGLOBAL hMem;
UINT* params;
BOOL in, out;
in = DDE_RequirePacking(msgIn);
out = DDE_RequirePacking(msgOut);
if (!in)
{
return PackDDElParam(msgOut, uiLo, uiHi);
}
if (lParam == 0)
{
return FALSE;
}
if (!out)
{
FreeDDElParam(msgIn, lParam);
return MAKELONG(uiLo, uiHi);
}
hMem = (HGLOBAL)lParam;
params = GlobalLock(hMem);
if (params == NULL)
{
ERR("GlobalLock failed\n");
return 0;
}
params[0] = uiLo;
params[1] = uiHi;
TRACE("Reusing pack %08x %08x\n", uiLo, uiHi);
GlobalLock(hMem);
return lParam;
}
/*****************************************************************
* ImpersonateDdeClientWindow (USER32.@)
*
*/
BOOL WINAPI ImpersonateDdeClientWindow(
HWND hWndClient, /* [in] handle to DDE client window */
HWND hWndServer /* [in] handle to DDE server window */
)
{
FIXME("(%04x %04x): stub\n", hWndClient, hWndServer);
return FALSE;
}
/*****************************************************************
* DdeSetQualityOfService (USER32.@)
*/
BOOL WINAPI DdeSetQualityOfService(HWND hwndClient, CONST SECURITY_QUALITY_OF_SERVICE *pqosNew,
PSECURITY_QUALITY_OF_SERVICE pqosPrev)
{
FIXME("(%04x %p %p): stub\n", hwndClient, pqosNew, pqosPrev);
return TRUE;
}
/******************************************************************************
* IncrementInstanceId
*
* generic routine to increment the max instance Id and allocate a new application instance
*/
static DWORD WDML_IncrementInstanceId(WDML_INSTANCE* thisInstance)
{
DWORD id = InterlockedIncrement(&WDML_MaxInstanceID);
thisInstance->instanceID = id;
TRACE("New instance id %ld allocated\n", id);
return DMLERR_NO_ERROR;
}
/******************************************************************************
* DdeInitializeA (USER32.@)
*/
UINT WINAPI DdeInitializeA(LPDWORD pidInst, PFNCALLBACK pfnCallback,
DWORD afCmd, DWORD ulRes)
{
UINT ret = DdeInitializeW(pidInst, pfnCallback, afCmd, ulRes);
if (ret == DMLERR_NO_ERROR) {
WDML_INSTANCE* thisInstance = WDML_FindInstance(*pidInst);
if (thisInstance)
thisInstance->unicode = FALSE;
}
return ret;
}
static LRESULT CALLBACK WDML_EventProc(HWND hwndEvent, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
WDML_INSTANCE* thisInstance;
HDDEDATA hDdeData;
switch (uMsg)
{
case WM_WDML_REGISTER:
thisInstance = (WDML_INSTANCE*)GetWindowLongA(hwndEvent, 0);
/* try calling the Callback */
if (thisInstance->callback != NULL /* && thisInstance->Process_id == GetCurrentProcessId()*/)
{
TRACE("Calling the callback, type=XTYP_REGISTER, CB=0x%lx\n",
(DWORD)thisInstance->callback);
hDdeData = (thisInstance->callback)(XTYP_REGISTER, 0, 0,
(HSZ)wParam, (HSZ)lParam, 0, 0, 0);
TRACE("Callback function called - result=%d\n", (INT)hDdeData);
}
break;
case WM_WDML_UNREGISTER:
thisInstance = (WDML_INSTANCE*)GetWindowLongA(hwndEvent, 0);
if (thisInstance && thisInstance->callback != NULL)
{
if (thisInstance->CBFflags & CBF_SKIP_DISCONNECTS)
{
FIXME("skip callback XTYP_UNREGISTER\n");
}
else
{
TRACE("calling callback XTYP_UNREGISTER, idInst=%ld\n",
thisInstance->instanceID);
(thisInstance->callback)(XTYP_UNREGISTER, 0, 0,
(HSZ)wParam, (HSZ)lParam, 0, 0, 0);
}
}
}
return DefWindowProcA(hwndEvent, uMsg, wParam, lParam);
}
/******************************************************************************
* DdeInitializeW [USER32.@]
* Registers an application with the DDEML
*
* PARAMS
* pidInst [I] Pointer to instance identifier
* pfnCallback [I] Pointer to callback function
* afCmd [I] Set of command and filter flags
* ulRes [I] Reserved
*
* RETURNS
* Success: DMLERR_NO_ERROR
* Failure: DMLERR_DLL_USAGE, DMLERR_INVALIDPARAMETER, DMLERR_SYS_ERROR
*/
UINT WINAPI DdeInitializeW(LPDWORD pidInst, PFNCALLBACK pfnCallback,
DWORD afCmd, DWORD ulRes)
{
/* probably not really capable of handling multiple processes, but should handle
* multiple instances within one process */
SECURITY_ATTRIBUTES s_attrib;
DWORD err_no = 0;
WDML_INSTANCE* thisInstance;
WDML_INSTANCE* reference_inst;
UINT ret;
WNDCLASSEXA wndclass;
if (ulRes)
{
ERR("Reserved value not zero? What does this mean?\n");
FIXME("(%p,%p,0x%lx,%ld): stub\n", pidInst, pfnCallback,
afCmd,ulRes);
/* trap this and no more until we know more */
return DMLERR_NO_ERROR;
}
if (!pfnCallback)
{
/* this one may be wrong - MS dll seems to accept the condition,
leave this until we find out more !! */
/* can't set up the instance with nothing to act as a callback */
TRACE("No callback provided\n");
return DMLERR_INVALIDPARAMETER; /* might be DMLERR_DLL_USAGE */
}
/* grab enough heap for one control struct - not really necessary for re-initialise
* but allows us to use same validation routines */
thisInstance = (WDML_INSTANCE*)HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_INSTANCE));
if (thisInstance == NULL)
{
/* catastrophe !! warn user & abort */
ERR("Instance create failed - out of memory\n");
return DMLERR_SYS_ERROR;
}
thisInstance->next = NULL;
thisInstance->monitor = (afCmd | APPCLASS_MONITOR);
/* messy bit, spec implies that 'Client Only' can be set in 2 different ways, catch 1 here */
thisInstance->clientOnly = afCmd & APPCMD_CLIENTONLY;
thisInstance->instanceID = *pidInst; /* May need to add calling proc Id */
thisInstance->callback = *pfnCallback;
thisInstance->txnCount = 0;
thisInstance->unicode = TRUE;
thisInstance->win16 = FALSE;
thisInstance->nodeList = NULL; /* node will be added later */
thisInstance->monitorFlags = afCmd & MF_MASK;
thisInstance->servers = NULL;
thisInstance->convs[0] = NULL;
thisInstance->convs[1] = NULL;
thisInstance->links[0] = NULL;
thisInstance->links[1] = NULL;
wndclass.cbSize = sizeof(wndclass);
wndclass.style = 0;
wndclass.lpfnWndProc = WDML_EventProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = sizeof(DWORD);
wndclass.hInstance = 0;
wndclass.hIcon = 0;
wndclass.hCursor = 0;
wndclass.hbrBackground = 0;
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = WDML_szEventClass;
wndclass.hIconSm = 0;
RegisterClassExA(&wndclass);
thisInstance->hwndEvent = CreateWindowA(WDML_szEventClass, NULL,
WS_POPUP, 0, 0, 0, 0,
0, 0, 0, 0);
SetWindowLongA(thisInstance->hwndEvent, 0, (DWORD)thisInstance);
/* isolate CBF flags in one go, expect this will go the way of all attempts to be clever !! */
thisInstance->CBFflags = afCmd^((afCmd&MF_MASK)|((afCmd&APPCMD_MASK)|(afCmd&APPCLASS_MASK)));
if (!thisInstance->clientOnly)
{
/* Check for other way of setting Client-only !! */
thisInstance->clientOnly =
(thisInstance->CBFflags & CBF_FAIL_ALLSVRXACTIONS) == CBF_FAIL_ALLSVRXACTIONS;
}
TRACE("instance created - checking validity \n");
if (*pidInst == 0)
{
/* Initialisation of new Instance Identifier */
TRACE("new instance, callback %p flags %lX\n",pfnCallback,afCmd);
if (WDML_MaxInstanceID == 0)
{
/* Nothing has been initialised - exit now ! can return TRUE since effect is the same */
/* Need to set up Mutex in case it is not already present */
s_attrib.bInheritHandle = TRUE;
s_attrib.lpSecurityDescriptor = NULL;
s_attrib.nLength = sizeof(s_attrib);
handle_mutex = CreateMutexA(&s_attrib,0,DDEHandleAccess);
if (!handle_mutex)
{
ERR("CreateMutex failed - handle list %li\n",GetLastError());
HeapFree(GetProcessHeap(), 0, thisInstance);
return DMLERR_SYS_ERROR;
}
}
if (!WDML_WaitForMutex(handle_mutex))
{
return DMLERR_SYS_ERROR;
}
if (WDML_InstanceList == NULL)
{
/* can't be another instance in this case, assign to the base pointer */
WDML_InstanceList = thisInstance;
/* since first must force filter of XTYP_CONNECT and XTYP_WILDCONNECT for
* present
* ------------------------------- NOTE NOTE NOTE --------------------------
*
* the manual is not clear if this condition
* applies to the first call to DdeInitialize from an application, or the
* first call for a given callback !!!
*/
thisInstance->CBFflags = thisInstance->CBFflags|APPCMD_FILTERINITS;
TRACE("First application instance detected OK\n");
/* allocate new instance ID */
if ((err_no = WDML_IncrementInstanceId(thisInstance))) return err_no;
}
else
{
/* really need to chain the new one in to the latest here, but after checking conditions
* such as trying to start a conversation from an application trying to monitor */
reference_inst = WDML_InstanceList;
TRACE("Subsequent application instance - starting checks\n");
while (reference_inst->next != NULL)
{
/*
* This set of tests will work if application uses same instance Id
* at application level once allocated - which is what manual implies
* should happen. If someone tries to be
* clever (lazy ?) it will fail to pick up that later calls are for
* the same application - should we trust them ?
*/
if (thisInstance->instanceID == reference_inst->instanceID)
{
/* Check 1 - must be same Client-only state */
if (thisInstance->clientOnly != reference_inst->clientOnly)
{
ret = DMLERR_DLL_USAGE;
goto theError;
}
/* Check 2 - cannot use 'Monitor' with any non-monitor modes */
if (thisInstance->monitor != reference_inst->monitor)
{
ret = DMLERR_INVALIDPARAMETER;
goto theError;
}
/* Check 3 - must supply different callback address */
if (thisInstance->callback == reference_inst->callback)
{
ret = DMLERR_DLL_USAGE;
goto theError;
}
}
reference_inst = reference_inst->next;
}
/* All cleared, add to chain */
TRACE("Application Instance checks finished\n");
if ((err_no = WDML_IncrementInstanceId(thisInstance))) return err_no;
reference_inst->next = thisInstance;
}
if (WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE)) return DMLERR_SYS_ERROR;
*pidInst = thisInstance->instanceID;
TRACE("New application instance processing finished OK\n");
}
else
{
/* Reinitialisation situation --- FIX */
TRACE("reinitialisation of (%p,%p,0x%lx,%ld): stub\n",pidInst,pfnCallback,afCmd,ulRes);
if (!WDML_WaitForMutex(handle_mutex))
{
HeapFree(GetProcessHeap(), 0, thisInstance);
return DMLERR_SYS_ERROR;
}
if (WDML_InstanceList == NULL)
{
ret = DMLERR_DLL_USAGE;
goto theError;
}
HeapFree(GetProcessHeap(), 0, thisInstance); /* finished - release heap space used as work store */
/* can't reinitialise if we have initialised nothing !! */
reference_inst = WDML_InstanceList;
/* must first check if we have been given a valid instance to re-initialise !! how do we do that ? */
/*
* MS allows initialisation without specifying a callback, should we allow addition of the
* callback by a later call to initialise ? - if so this lot will have to change
*/
while (reference_inst->next != NULL)
{
if (*pidInst == reference_inst->instanceID && pfnCallback == reference_inst->callback)
{
/* Check 1 - cannot change client-only mode if set via APPCMD_CLIENTONLY */
if (reference_inst->clientOnly)
{
if ((reference_inst->CBFflags & CBF_FAIL_ALLSVRXACTIONS) != CBF_FAIL_ALLSVRXACTIONS)
{
/* i.e. Was set to Client-only and through APPCMD_CLIENTONLY */
if (!(afCmd & APPCMD_CLIENTONLY))
{
ret = DMLERR_DLL_USAGE;
goto theError;
}
}
}
/* Check 2 - cannot change monitor modes */
if (thisInstance->monitor != reference_inst->monitor)
{
ret = DMLERR_DLL_USAGE;
goto theError;
}
/* Check 3 - trying to set Client-only via APPCMD when not set so previously */
if ((afCmd&APPCMD_CLIENTONLY) && !reference_inst->clientOnly)
{
ret = DMLERR_DLL_USAGE;
goto theError;
}
break;
}
reference_inst = reference_inst->next;
}
if (reference_inst->next == NULL)
{
/* Crazy situation - trying to re-initialize something that has not beeen initialized !!
*
* Manual does not say what we do, cannot return DMLERR_NOT_INITIALIZED so what ?
*/
ret = DMLERR_INVALIDPARAMETER;
goto theError;
}
/* All checked - change relevant flags */
reference_inst->CBFflags = thisInstance->CBFflags;
reference_inst->clientOnly = thisInstance->clientOnly;
reference_inst->monitorFlags = thisInstance->monitorFlags;
if (WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE))
{
HeapFree(GetProcessHeap(), 0, thisInstance);
return DMLERR_SYS_ERROR;
}
}
return DMLERR_NO_ERROR;
theError:
HeapFree(GetProcessHeap(), 0, thisInstance);
if (WDML_ReleaseMutex(handle_mutex, "handle_mutex", 0))
return DMLERR_SYS_ERROR;
return ret;
}
/*****************************************************************
* DdeUninitialize [USER32.@] Frees DDEML resources
*
* PARAMS
* idInst [I] Instance identifier
*
* RETURNS
* Success: TRUE
* Failure: FALSE
*/
BOOL WINAPI DdeUninitialize(DWORD idInst)
{
/* Stage one - check if we have a handle for this instance
*/
WDML_INSTANCE* thisInstance;
WDML_INSTANCE* reference_inst;
if (WDML_MaxInstanceID == 0)
{
/* Nothing has been initialised - exit now ! can return TRUE since effect is the same */
return TRUE;
}
if (!WDML_WaitForMutex(handle_mutex))
{
return DMLERR_SYS_ERROR;
}
/* First check instance
*/
thisInstance = WDML_FindInstance(idInst);
if (thisInstance == NULL)
{
if (WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE)) return FALSE;
/*
* Needs something here to record NOT_INITIALIZED ready for DdeGetLastError
*/
return FALSE;
}
FIXME("(%ld): partial stub\n", idInst);
/* FIXME ++++++++++++++++++++++++++++++++++++++++++
* Needs to de-register all service names
*
*/
/* Free the nodes that were not freed by this instance
* and remove the nodes from the list of HSZ nodes.
*/
WDML_FreeAllHSZ(thisInstance);
DestroyWindow(thisInstance->hwndEvent);
/* OK now delete the instance handle itself */
if (WDML_InstanceList == thisInstance)
{
/* special case - the first/only entry
*/
WDML_InstanceList = thisInstance->next;
}
else
{
/* general case
*/
reference_inst = WDML_InstanceList;
while (reference_inst->next != thisInstance)
{
reference_inst = thisInstance->next;
}
reference_inst->next = thisInstance->next;
}
/* release the mutex and the heap entry
*/
HeapFree(GetProcessHeap(), 0, thisInstance);
if (WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE))
{
/* should record something here, but nothing left to hang it from !!
*/
return FALSE;
}
return TRUE;
}
/******************************************************************************
* RemoveHSZNodes (INTERNAL)
*
* Remove a node from the list of HSZ nodes.
*/
static void WDML_RemoveHSZNode(WDML_INSTANCE* thisInstance, HSZ hsz)
{
HSZNode* pPrev = NULL;
HSZNode* pCurrent = NULL;
/* Set the current node at the start of the list.
*/
pCurrent = thisInstance->nodeList;
/* While we have more nodes.
*/
while (pCurrent != NULL)
{
/* If we found the node we were looking for.
*/
if (pCurrent->hsz == hsz)
{
/* Remove the node.
*/
/* If the first node in the list is to to be removed.
* Set the global list pointer to the next node.
*/
if (pCurrent == thisInstance->nodeList)
{
thisInstance->nodeList = pCurrent->next;
}
/* Just fix the pointers has to skip the current
* node so we can delete it.
*/
else
{
pPrev->next = pCurrent->next;
}
/* Destroy this node.
*/
HeapFree(GetProcessHeap(), 0, pCurrent);
break;
}
/* Save the previous node pointer.
*/
pPrev = pCurrent;
/* Move on to the next node.
*/
pCurrent = pCurrent->next;
}
}
/******************************************************************************
* FreeAndRemoveHSZNodes (INTERNAL)
*
* Frees up all the strings still allocated in the list and
* remove all the nodes from the list of HSZ nodes.
*/
void WDML_FreeAllHSZ(WDML_INSTANCE* thisInstance)
{
/* Free any strings created in this instance.
*/
while (thisInstance->nodeList != NULL)
{
DdeFreeStringHandle(thisInstance->instanceID, thisInstance->nodeList->hsz);
}
}
/******************************************************************************
* GetSecondaryHSZValue (INTERNAL)
*
* Insert a node to the head of the list.
*/
static HSZ WDML_GetSecondaryHSZValue(WDML_INSTANCE* thisInstance, HSZ hsz)
{
HSZ hsz2 = 0;
if (hsz != 0)
{
/* Create and set the Secondary handle */
if (thisInstance->unicode)
{
WCHAR wSecondaryString[MAX_BUFFER_LEN];
WCHAR wUniqueNum[MAX_BUFFER_LEN];
if (DdeQueryStringW(thisInstance->instanceID, hsz,
wSecondaryString,
MAX_BUFFER_LEN, CP_WINUNICODE))
{
wsprintfW(wUniqueNum,"(%ld)",
(DWORD)thisInstance->instanceID);
lstrcatW(wSecondaryString, wUniqueNum);
hsz2 = GlobalAddAtomW(wSecondaryString);
}
}
else
{
CHAR SecondaryString[MAX_BUFFER_LEN];
CHAR UniqueNum[MAX_BUFFER_LEN];
if (DdeQueryStringA(thisInstance->instanceID, hsz,
SecondaryString,
MAX_BUFFER_LEN, CP_WINANSI))
{
wsprintfA(UniqueNum,"(%ld)", thisInstance->instanceID);
lstrcatA(SecondaryString, UniqueNum);
hsz2 = GlobalAddAtomA(SecondaryString);
}
}
}
return hsz2;
}
/******************************************************************************
* InsertHSZNode (INTERNAL)
*
* Insert a node to the head of the list.
*/
static void WDML_InsertHSZNode(WDML_INSTANCE* thisInstance, HSZ hsz)
{
if (hsz != 0)
{
HSZNode* pNew = NULL;
/* Create a new node for this HSZ.
*/
pNew = (HSZNode*)HeapAlloc(GetProcessHeap(), 0, sizeof(HSZNode));
if (pNew != NULL)
{
/* Set the handle value.
*/
pNew->hsz = hsz;
/* Create and set the Secondary handle */
pNew->hsz2 = WDML_GetSecondaryHSZValue(thisInstance, hsz);
/* Attach the node to the head of the list. i.e most recently added is first
*/
pNew->next = thisInstance->nodeList;
/* The new node is now at the head of the list
* so set the global list pointer to it.
*/
thisInstance->nodeList = pNew;
}
else
{
ERR("Primary HSZ Node allocation failed - out of memory\n");
}
}
}
/*****************************************************************************
* Find_Instance_Entry
*
* generic routine to return a pointer to the relevant DDE_HANDLE_ENTRY
* for an instance Id, or NULL if the entry does not exist
*
* ASSUMES the mutex protecting the handle entry list is reserved before calling
*/
WDML_INSTANCE* WDML_FindInstance(DWORD InstId)
{
WDML_INSTANCE* thisInstance;
thisInstance = WDML_InstanceList;
while (thisInstance != NULL)
{
if (thisInstance->instanceID == InstId)
{
return thisInstance;
}
thisInstance = thisInstance->next;
}
TRACE("Instance entry missing\n");
return NULL;
}
/******************************************************************************
* WDML_ReleaseMutex
*
* generic routine to release a reserved mutex
*/
DWORD WDML_ReleaseMutex(HANDLE mutex, LPSTR mutex_name, BOOL release_handle_m)
{
if (!ReleaseMutex(mutex))
{
ERR("ReleaseMutex failed - %s mutex %li\n", mutex_name, GetLastError());
if (release_handle_m)
{
ReleaseMutex(handle_mutex);
}
return DMLERR_SYS_ERROR;
}
return DMLERR_NO_ERROR;
}
/******************************************************************************
* WDML_WaitForMutex
*
* generic routine to wait for the mutex
*/
BOOL WDML_WaitForMutex(HANDLE mutex)
{
DWORD result;
result = WaitForSingleObject(mutex, INFINITE);
/* both errors should never occur */
if (WAIT_TIMEOUT == result)
{
ERR("WaitForSingleObject timed out\n");
return FALSE;
}
if (WAIT_FAILED == result)
{
ERR("WaitForSingleObject failed - error %li\n", GetLastError());
return FALSE;
}
/* TRACE("Handle Mutex created/reserved\n"); */
return TRUE;
}
/******************************************************************************
* WDML_ReserveAtom
*
* Routine to make an extra Add on an atom to reserve it a bit longer
*/
void WDML_ReserveAtom(WDML_INSTANCE* thisInstance, HSZ hsz)
{
if (thisInstance->unicode)
{
WCHAR SNameBuffer[MAX_BUFFER_LEN];
GlobalGetAtomNameW(hsz, SNameBuffer, MAX_BUFFER_LEN);
GlobalAddAtomW(SNameBuffer);
} else {
CHAR SNameBuffer[MAX_BUFFER_LEN];
GlobalGetAtomNameA(hsz, SNameBuffer, MAX_BUFFER_LEN);
GlobalAddAtomA(SNameBuffer);
}
}
/******************************************************************************
* WDML_ReleaseAtom
*
* Routine to make a delete on an atom to release it a bit sooner
*/
void WDML_ReleaseAtom(WDML_INSTANCE* thisInstance, HSZ hsz)
{
GlobalDeleteAtom(hsz);
}
/*****************************************************************
* DdeQueryStringA [USER32.@]
*/
DWORD WINAPI DdeQueryStringA(DWORD idInst, HSZ hsz, LPSTR psz, DWORD cchMax, INT iCodePage)
{
DWORD ret = 0;
CHAR pString[MAX_BUFFER_LEN];
WDML_INSTANCE* thisInstance;
TRACE("(%ld, 0x%x, %p, %ld, %d): partial stub\n",
idInst, hsz, psz, cchMax, iCodePage);
if (WDML_MaxInstanceID == 0)
{
/* Nothing has been initialised - exit now ! */
/* needs something for DdeGetLAstError even if the manual doesn't say so */
return FALSE;
}
if (!WDML_WaitForMutex(handle_mutex))
{
return FALSE;
}
/* First check instance
*/
thisInstance = WDML_FindInstance(idInst);
if (thisInstance == NULL)
{
if (WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE)) return FALSE;
/*
Needs something here to record NOT_INITIALIZED ready for DdeGetLastError
*/
return FALSE;
}
if (iCodePage == CP_WINANSI)
{
/* If psz is null, we have to return only the length
* of the string.
*/
if (psz == NULL)
{
psz = pString;
cchMax = MAX_BUFFER_LEN;
}
ret = GlobalGetAtomNameA(hsz, (LPSTR)psz, cchMax);
}
WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
TRACE("returning pointer\n");
return ret;
}
/*****************************************************************
* DdeQueryStringW [USER32.@]
*/
DWORD WINAPI DdeQueryStringW(DWORD idInst, HSZ hsz, LPWSTR psz, DWORD cchMax, INT iCodePage)
{
DWORD ret = 0;
WCHAR pString[MAX_BUFFER_LEN];
int factor = 1;
TRACE("(%ld, 0x%x, %p, %ld, %d): partial-stub\n",
idInst, hsz, psz, cchMax, iCodePage);
if (iCodePage == CP_WINUNICODE)
{
/* If psz is null, we have to return only the length
* of the string.
*/
if (psz == NULL)
{
psz = pString;
cchMax = MAX_BUFFER_LEN;
/* Note: According to documentation if the psz parameter
* was NULL this API must return the length of the string in bytes.
*/
factor = (int)sizeof(WCHAR)/sizeof(BYTE);
}
ret = GlobalGetAtomNameW(hsz, (LPWSTR)psz, cchMax) * factor;
}
return ret;
}
/*****************************************************************
* DdeCreateStringHandleA [USER32.@]
*
* RETURNS
* Success: String handle
* Failure: 0
*/
HSZ WINAPI DdeCreateStringHandleA(DWORD idInst, LPCSTR psz, INT codepage)
{
HSZ hsz = 0;
WDML_INSTANCE* thisInstance;
TRACE("(%ld,%s,%d): partial stub\n",idInst,debugstr_a(psz),codepage);
if (WDML_MaxInstanceID == 0)
{
/* Nothing has been initialised - exit now ! can return FALSE since effect is the same */
return FALSE;
}
if (!WDML_WaitForMutex(handle_mutex))
{
return DMLERR_SYS_ERROR;
}
/* First check instance
*/
thisInstance = WDML_FindInstance(idInst);
if (thisInstance == NULL)
{
if (WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE)) return 0;
/*
Needs something here to record NOT_INITIALIZED ready for DdeGetLastError
*/
return 0;
}
if (codepage == CP_WINANSI)
{
hsz = GlobalAddAtomA(psz);
/* Save the handle so we know to clean it when
* uninitialize is called.
*/
TRACE("added atom %s with HSZ 0x%x, \n",debugstr_a(psz),hsz);
WDML_InsertHSZNode(thisInstance, hsz);
if (WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE))
{
thisInstance->lastError = DMLERR_SYS_ERROR;
return 0;
}
TRACE("Returning pointer\n");
return hsz;
}
else
{
WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
}
TRACE("Returning error\n");
return 0;
}
/******************************************************************************
* DdeCreateStringHandleW [USER32.@] Creates handle to identify string
*
* RETURNS
* Success: String handle
* Failure: 0
*/
HSZ WINAPI DdeCreateStringHandleW(
DWORD idInst, /* [in] Instance identifier */
LPCWSTR psz, /* [in] Pointer to string */
INT codepage) /* [in] Code page identifier */
{
WDML_INSTANCE* thisInstance;
HSZ hsz = 0;
TRACE("(%ld,%s,%d): partial stub\n",idInst,debugstr_w(psz),codepage);
if (WDML_MaxInstanceID == 0)
{
/* Nothing has been initialised - exit now ! can return FALSE since effect is the same */
return FALSE;
}
if (!WDML_WaitForMutex(handle_mutex))
{
return DMLERR_SYS_ERROR;
}
/* First check instance
*/
thisInstance = WDML_FindInstance(idInst);
if (thisInstance == NULL)
{
if (WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE)) return 0;
/*
Needs something here to record NOT_INITIALIZED ready for DdeGetLastError
*/
return 0;
}
FIXME("(%ld,%s,%d): partial stub\n",idInst,debugstr_w(psz),codepage);
if (codepage == CP_WINUNICODE)
{
/*
* Should we be checking this against the unicode/ascii nature of the call to DdeInitialize ?
*/
hsz = GlobalAddAtomW(psz);
/* Save the handle so we know to clean it when
* uninitialize is called.
*/
WDML_InsertHSZNode(thisInstance, hsz);
if (WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE))
{
thisInstance->lastError = DMLERR_SYS_ERROR;
return 0;
}
TRACE("Returning pointer\n");
return hsz;
}
else
{
WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
}
TRACE("Returning error\n");
return 0;
}
/*****************************************************************
* DdeFreeStringHandle (USER32.@)
* RETURNS: success: nonzero
* fail: zero
*/
BOOL WINAPI DdeFreeStringHandle(DWORD idInst, HSZ hsz)
{
WDML_INSTANCE* thisInstance;
HSZ hsz2;
TRACE("(%ld,%d): \n",idInst,hsz);
if (WDML_MaxInstanceID == 0)
{
/* Nothing has been initialised - exit now ! can return TRUE since effect is the same */
return TRUE;
}
if (!WDML_WaitForMutex(handle_mutex))
{
return DMLERR_SYS_ERROR;
}
/* First check instance
*/
thisInstance = WDML_FindInstance(idInst);
if ((thisInstance == NULL) || (thisInstance->nodeList == NULL))
{
if (WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE)) return TRUE;
/* Nothing has been initialised - exit now ! can return TRUE since effect is the same */
return TRUE;
}
/* Remove the node associated with this HSZ.
*/
hsz2 = thisInstance->nodeList->hsz2; /* save this value first */
WDML_RemoveHSZNode(thisInstance, hsz);
/* Free the string associated with this HSZ.
*/
WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
if (hsz2 != 0)
{
GlobalDeleteAtom(hsz2);
}
return GlobalDeleteAtom(hsz) ? 0 : hsz;
}
/*****************************************************************
* DdeKeepStringHandle (USER32.@)
*
* RETURNS: success: nonzero
* fail: zero
*/
BOOL WINAPI DdeKeepStringHandle(DWORD idInst, HSZ hsz)
{
WDML_INSTANCE* thisInstance;
TRACE("(%ld,%d): \n",idInst,hsz);
if (WDML_MaxInstanceID == 0)
{
/* Nothing has been initialised - exit now ! can return FALSE since effect is the same */
return FALSE;
}
if (!WDML_WaitForMutex(handle_mutex))
{
return FALSE;
}
/* First check instance
*/
thisInstance = WDML_FindInstance(idInst);
if ((thisInstance == NULL) || (thisInstance->nodeList == NULL))
{
if (WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE)) return FALSE;
/* Nothing has been initialised - exit now ! can return FALSE since effect is the same */
return FALSE;
return FALSE;
}
WDML_ReserveAtom(thisInstance, hsz);
WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
return TRUE;
}
/*****************************************************************
* DdeCreateDataHandle (USER32.@)
*/
HDDEDATA WINAPI DdeCreateDataHandle(DWORD idInst, LPBYTE pSrc, DWORD cb,
DWORD cbOff, HSZ hszItem, UINT wFmt,
UINT afCmd)
{
/*
For now, we ignore idInst, hszItem, wFmt, and afCmd.
The purpose of these arguments still need to be investigated.
*/
HGLOBAL hMem;
LPBYTE pByte;
DDE_DATAHANDLE_HEAD* pDdh;
TRACE("(%ld,%p,%ld,%ld,0x%lx,%d,%d): semi-stub\n",
idInst,pSrc,cb,cbOff,(DWORD)hszItem,wFmt,afCmd);
/* we use the first 4 bytes to store the size */
if (!(hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, cb + sizeof(DDE_DATAHANDLE_HEAD))))
{
ERR("GlobalAlloc failed\n");
return 0;
}
pDdh = (DDE_DATAHANDLE_HEAD*)GlobalLock(hMem);
pDdh->cfFormat = wFmt;
pByte = (LPBYTE)(pDdh + 1);
if (pSrc)
{
memcpy(pByte, pSrc + cbOff, cb);
}
GlobalUnlock(hMem);
return (HDDEDATA)hMem;
}
/*****************************************************************
*
* DdeAddData (USER32.@)
*/
HDDEDATA WINAPI DdeAddData(HDDEDATA hData, LPBYTE pSrc, DWORD cb, DWORD cbOff)
{
DWORD old_sz, new_sz;
LPBYTE pDst;
pDst = DdeAccessData(hData, &old_sz);
if (!pDst) return 0;
new_sz = cb + cbOff;
if (new_sz > old_sz)
{
DdeUnaccessData(hData);
hData = GlobalReAlloc((HGLOBAL)hData, new_sz + sizeof(DDE_DATAHANDLE_HEAD),
GMEM_MOVEABLE | GMEM_DDESHARE);
pDst = DdeAccessData(hData, &old_sz);
}
if (!pDst) return 0;
memcpy(pDst + cbOff, pSrc, cb);
DdeUnaccessData(hData);
return hData;
}
/*****************************************************************
* DdeSetUserHandle (USER32.@)
*/
BOOL WINAPI DdeSetUserHandle(HCONV hConv, DWORD id, DWORD hUser)
{
WDML_CONV* pConv;
BOOL ret = TRUE;
WDML_WaitForMutex(handle_mutex);
pConv = WDML_GetConv(hConv);
if (pConv == NULL)
{
ret = FALSE;
goto theError;
}
if (id == QID_SYNC)
{
pConv->hUser = hUser;
}
else
{
WDML_XACT* pXAct;
pXAct = WDML_FindTransaction(pConv, id);
if (pXAct)
{
pXAct->hUser = hUser;
}
else
{
pConv->thisInstance->lastError = DMLERR_UNFOUND_QUEUE_ID;
ret = FALSE;
}
}
theError:
WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
return ret;
}
/******************************************************************************
* DdeGetData [USER32.@] Copies data from DDE object to local buffer
*
* RETURNS
* Size of memory object associated with handle
*/
DWORD WINAPI DdeGetData(
HDDEDATA hData, /* [in] Handle to DDE object */
LPBYTE pDst, /* [in] Pointer to destination buffer */
DWORD cbMax, /* [in] Amount of data to copy */
DWORD cbOff) /* [in] Offset to beginning of data */
{
DWORD dwSize, dwRet;
LPBYTE pByte;
TRACE("(%08lx,%p,%ld,%ld)\n",(DWORD)hData,pDst,cbMax,cbOff);
pByte = DdeAccessData(hData, &dwSize);
if (pByte)
{
if (cbOff + cbMax < dwSize)
{
dwRet = cbMax;
}
else if (cbOff < dwSize)
{
dwRet = dwSize - cbOff;
}
else
{
dwRet = 0;
}
if (pDst && dwRet != 0)
{
memcpy(pDst, pByte + cbOff, dwRet);
}
DdeUnaccessData(hData);
}
else
{
dwRet = 0;
}
return dwRet;
}
/*****************************************************************
* DdeAccessData (USER32.@)
*/
LPBYTE WINAPI DdeAccessData(HDDEDATA hData, LPDWORD pcbDataSize)
{
HGLOBAL hMem = (HGLOBAL)hData;
DDE_DATAHANDLE_HEAD* pDdh;
TRACE("(%08lx,%p)\n", (DWORD)hData, pcbDataSize);
pDdh = (DDE_DATAHANDLE_HEAD*)GlobalLock(hMem);
if (pDdh == NULL)
{
ERR("Failed on GlobalLock(%04x)\n", hMem);
return 0;
}
if (pcbDataSize != NULL)
{
*pcbDataSize = GlobalSize(hMem) - sizeof(DDE_DATAHANDLE_HEAD);
}
return (LPBYTE)(pDdh + 1);
}
/*****************************************************************
* DdeUnaccessData (USER32.@)
*/
BOOL WINAPI DdeUnaccessData(HDDEDATA hData)
{
HGLOBAL hMem = (HGLOBAL)hData;
TRACE("(0x%lx)\n", (DWORD)hData);
GlobalUnlock(hMem);
return TRUE;
}
/*****************************************************************
* DdeFreeDataHandle (USER32.@)
*/
BOOL WINAPI DdeFreeDataHandle(HDDEDATA hData)
{
return GlobalFree((HGLOBAL)hData) == 0;
}
/* ================================================================
*
* Global <=> Data handle management
*
* ================================================================ */
/* Note: we use a DDEDATA, but layout of DDEDATA, DDEADVISE and DDEPOKE structures is similar:
* offset size
* (bytes) (bits) comment
* 0 16 bit fields for options (release, ackreq, response...)
* 2 16 clipboard format
* 4 ? data to be used
*/
HDDEDATA WDML_Global2DataHandle(HGLOBAL hMem)
{
DDEDATA* pDd;
if (hMem)
{
pDd = GlobalLock(hMem);
if (pDd)
{
return DdeCreateDataHandle(0, pDd->Value,
GlobalSize(hMem) - (sizeof(DDEDATA) - 1),
0, 0, pDd->cfFormat, 0);
}
}
return 0;
}
HGLOBAL WDML_DataHandle2Global(HDDEDATA hDdeData, BOOL fResponse, BOOL fRelease,
BOOL fDeferUpd, BOOL fAckReq)
{
DDE_DATAHANDLE_HEAD* pDdh;
DWORD dwSize;
HGLOBAL hMem = 0;
dwSize = GlobalSize(hDdeData) - sizeof(DDE_DATAHANDLE_HEAD);
pDdh = (DDE_DATAHANDLE_HEAD*)GlobalLock(hDdeData);
if (dwSize && pDdh)
{
hMem = GlobalAlloc(sizeof(DDEDATA) - 1 + dwSize,
GMEM_MOVEABLE | GMEM_DDESHARE);
if (hMem)
{
DDEDATA* ddeData;
ddeData = GlobalLock(hMem);
if (ddeData)
{
ddeData->fResponse = fResponse;
ddeData->fRelease = fRelease;
ddeData->reserved /*fDeferUpd*/ = fDeferUpd;
ddeData->fAckReq = fAckReq;
ddeData->cfFormat = pDdh->cfFormat;
memcpy(ddeData->Value, pDdh + 1, dwSize);
GlobalUnlock(hMem);
}
}
GlobalUnlock(hDdeData);
}
return hMem;
}
/*****************************************************************
* DdeEnableCallback (USER32.@)
*/
BOOL WINAPI DdeEnableCallback(DWORD idInst, HCONV hConv, UINT wCmd)
{
FIXME("(%ld, 0x%x, %d) stub\n", idInst, hConv, wCmd);
return 0;
}
/******************************************************************************
* DdeGetLastError [USER32.@] Gets most recent error code
*
* PARAMS
* idInst [I] Instance identifier
*
* RETURNS
* Last error code
*/
UINT WINAPI DdeGetLastError(DWORD idInst)
{
DWORD error_code;
WDML_INSTANCE* thisInstance;
FIXME("(%ld): error reporting is weakly implemented\n",idInst);
if (WDML_MaxInstanceID == 0)
{
/* Nothing has been initialised - exit now ! */
return DMLERR_DLL_NOT_INITIALIZED;
}
if (!WDML_WaitForMutex(handle_mutex))
{
return DMLERR_SYS_ERROR;
}
/* First check instance
*/
thisInstance = WDML_FindInstance(idInst);
if (thisInstance == NULL)
{
error_code = DMLERR_DLL_NOT_INITIALIZED;
}
else
{
error_code = thisInstance->lastError;
thisInstance->lastError = 0;
}
WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
return error_code;
}
/*****************************************************************
* DdeCmpStringHandles (USER32.@)
*
* Compares the value of two string handles. This comparison is
* not case sensitive.
*
* Returns:
* -1 The value of hsz1 is zero or less than hsz2
* 0 The values of hsz 1 and 2 are the same or both zero.
* 1 The value of hsz2 is zero of less than hsz1
*/
INT WINAPI DdeCmpStringHandles(HSZ hsz1, HSZ hsz2)
{
CHAR psz1[MAX_BUFFER_LEN];
CHAR psz2[MAX_BUFFER_LEN];
int ret = 0;
int ret1, ret2;
ret1 = GlobalGetAtomNameA(hsz1, psz1, MAX_BUFFER_LEN);
ret2 = GlobalGetAtomNameA(hsz2, psz2, MAX_BUFFER_LEN);
TRACE("(%04lx<%s> %04lx<%s>);\n", (DWORD)hsz1, psz1, (DWORD)hsz2, psz2);
/* Make sure we found both strings.
*/
if (ret1 == 0 && ret2 == 0)
{
/* If both are not found, return both "zero strings".
*/
ret = 0;
}
else if (ret1 == 0)
{
/* If hsz1 is a not found, return hsz1 is "zero string".
*/
ret = -1;
}
else if (ret2 == 0)
{
/* If hsz2 is a not found, return hsz2 is "zero string".
*/
ret = 1;
}
else
{
/* Compare the two strings we got (case insensitive).
*/
ret = strcasecmp(psz1, psz2);
/* Since strcmp returns any number smaller than
* 0 when the first string is found to be less than
* the second one we must make sure we are returning
* the proper values.
*/
if (ret < 0)
{
ret = -1;
}
else if (ret > 0)
{
ret = 1;
}
}
return ret;
}
/******************************************************************
* DdeQueryConvInfo (USER32.@)
*
*/
UINT WINAPI DdeQueryConvInfo(HCONV hConv, DWORD id, LPCONVINFO lpConvInfo)
{
UINT ret = lpConvInfo->cb;
CONVINFO ci;
WDML_CONV* pConv;
FIXME("semi-stub.\n");
WDML_WaitForMutex(handle_mutex);
pConv = WDML_GetConv(hConv);
if (pConv == NULL)
{
WDML_ReleaseMutex(handle_mutex, "hande_mutex", FALSE);
return 0;
}
ci.hConvPartner = 0; /* FIXME */
ci.hszSvcPartner = pConv->hszService;
ci.hszServiceReq = pConv->hszService; /* FIXME: they shouldn't be the same, should they ? */
ci.hszTopic = pConv->hszTopic;
ci.wStatus = ST_CLIENT /* FIXME */ | ST_CONNECTED;
ci.wConvst = 0; /* FIXME */
ci.wLastError = 0; /* FIXME: note it's not the instance last error */
ci.hConvList = 0;
ci.ConvCtxt = pConv->convContext;
if (ci.wStatus & ST_CLIENT)
{
ci.hwnd = pConv->hwndClient;
ci.hwndPartner = pConv->hwndServer;
}
else
{
ci.hwnd = pConv->hwndServer;
ci.hwndPartner = pConv->hwndClient;
}
if (id == QID_SYNC)
{
ci.hUser = pConv->hUser;
ci.hszItem = 0;
ci.wFmt = 0;
ci.wType = 0;
}
else
{
WDML_XACT* pXAct;
pXAct = WDML_FindTransaction(pConv, id);
if (pXAct)
{
ci.hUser = pXAct->hUser;
ci.hszItem = 0; /* FIXME */
ci.wFmt = 0; /* FIXME */
ci.wType = 0; /* FIXME */
}
else
{
ret = 0;
pConv->thisInstance->lastError = DMLERR_UNFOUND_QUEUE_ID;
}
}
WDML_ReleaseMutex(handle_mutex, "hande_mutex", FALSE);
memcpy(lpConvInfo, &ci, min(lpConvInfo->cb, sizeof(ci)));
return ret;
}
/* ================================================================
*
* Server management
*
* ================================================================ */
/******************************************************************
* WDML_AddServer
*
*
*/
WDML_SERVER* WDML_AddServer(WDML_INSTANCE* thisInstance, HSZ hszService, HSZ hszTopic)
{
WDML_SERVER* pServer;
pServer = (WDML_SERVER*)HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_SERVER));
if (pServer == NULL) return NULL;
pServer->hszService = hszService;
pServer->hszTopic = 0;
pServer->filterOn = TRUE;
pServer->next = thisInstance->servers;
thisInstance->servers = pServer;
return pServer;
}
/******************************************************************
* WDML_RemoveServer
*
*
*/
void WDML_RemoveServer(WDML_INSTANCE* thisInstance, HSZ hszService, HSZ hszTopic)
{
WDML_SERVER* pPrev = NULL;
WDML_SERVER* pCurrent = NULL;
pCurrent = thisInstance->servers;
while (pCurrent != NULL)
{
if (DdeCmpStringHandles(pCurrent->hszService, hszService) == 0)
{
if (pCurrent == thisInstance->servers)
{
thisInstance->servers = pCurrent->next;
}
else
{
pPrev->next = pCurrent->next;
}
DestroyWindow(pCurrent->hwndServer);
HeapFree(GetProcessHeap(), 0, pCurrent);
break;
}
pPrev = pCurrent;
pCurrent = pCurrent->next;
}
}
/*****************************************************************************
* WDML_FindServer
*
* generic routine to return a pointer to the relevant ServiceNode
* for a given service name, or NULL if the entry does not exist
*
* ASSUMES the mutex protecting the handle entry list is reserved before calling
*/
WDML_SERVER* WDML_FindServer(WDML_INSTANCE* thisInstance, HSZ hszService, HSZ hszTopic)
{
WDML_SERVER* pServer;
for (pServer = thisInstance->servers; pServer != NULL; pServer = pServer->next)
{
if (hszService == pServer->hszService)
{
return pServer;
}
}
TRACE("Service name missing\n");
return NULL;
}
/* ================================================================
*
* Conversation management
*
* ================================================================ */
/******************************************************************
* WDML_AddConv
*
*
*/
WDML_CONV* WDML_AddConv(WDML_INSTANCE* thisInstance, WDML_SIDE side,
HSZ hszService, HSZ hszTopic, HWND hwndClient, HWND hwndServer)
{
WDML_CONV* pConv;
/* no converstation yet, add it */
pConv = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_CONV));
if (!pConv) return NULL;
pConv->thisInstance = thisInstance;
pConv->hszService = hszService;
pConv->hszTopic = hszTopic;
pConv->hwndServer = hwndServer;
pConv->hwndClient = hwndClient;
pConv->transactions = NULL;
pConv->hUser = 0;
pConv->next = thisInstance->convs[side];
thisInstance->convs[side] = pConv;
return pConv;
}
/******************************************************************
* WDML_FindConv
*
*
*/
WDML_CONV* WDML_FindConv(WDML_INSTANCE* thisInstance, WDML_SIDE side,
HSZ hszService, HSZ hszTopic)
{
WDML_CONV* pCurrent = NULL;
for (pCurrent = thisInstance->convs[side]; pCurrent != NULL; pCurrent = pCurrent->next)
{
if (DdeCmpStringHandles(pCurrent->hszService, hszService) == 0 &&
DdeCmpStringHandles(pCurrent->hszTopic, hszTopic) == 0)
{
return pCurrent;
}
}
return NULL;
}
/******************************************************************
* WDML_RemoveConv
*
*
*/
void WDML_RemoveConv(WDML_INSTANCE* thisInstance, WDML_SIDE side, HCONV hConv)
{
WDML_CONV* pPrev = NULL;
WDML_CONV* pRef = WDML_GetConv(hConv);
WDML_CONV* pCurrent = NULL;
if (!pRef)
return;
for (pCurrent = thisInstance->convs[side]; pCurrent != NULL; pCurrent = (pPrev = pCurrent)->next)
{
if (pCurrent == pRef)
{
if (pCurrent == thisInstance->convs[side])
{
thisInstance->convs[side] = pCurrent->next;
}
else
{
pPrev->next = pCurrent->next;
}
HeapFree(GetProcessHeap(), 0, pCurrent);
break;
}
}
}
/******************************************************************
* WDML_GetConv
*
*
*/
WDML_CONV* WDML_GetConv(HCONV hConv)
{
/* FIXME: should do better checking */
return (WDML_CONV*)hConv;
}
/* ================================================================
*
* Link (hot & warm) management
*
* ================================================================ */
/******************************************************************
* WDML_AddLink
*
*
*/
void WDML_AddLink(WDML_INSTANCE* thisInstance, HCONV hConv, WDML_SIDE side,
UINT wType, HSZ hszItem, UINT wFmt)
{
WDML_LINK* pLink;
TRACE("AddDdeLink was called...\n");
pLink = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_LINK));
if (pLink == NULL)
{
ERR("OOM\n");
return;
}
pLink->hConv = hConv;
pLink->transactionType = wType;
pLink->hszItem = hszItem;
pLink->uFmt = wFmt;
pLink->hDdeData = 0;
pLink->next = thisInstance->links[side];
thisInstance->links[side] = pLink;
}
/******************************************************************
* WDML_RemoveLink
*
*
*/
void WDML_RemoveLink(WDML_INSTANCE* thisInstance, HCONV hConv, WDML_SIDE side,
HSZ hszItem, UINT uFmt)
{
WDML_LINK* pPrev = NULL;
WDML_LINK* pCurrent = NULL;
pCurrent = thisInstance->links[side];
while (pCurrent != NULL)
{
if (pCurrent->hConv == hConv &&
DdeCmpStringHandles(pCurrent->hszItem, hszItem) == 0 &&
pCurrent->uFmt == uFmt)
{
if (pCurrent == thisInstance->links[side])
{
thisInstance->links[side] = pCurrent->next;
}
else
{
pPrev->next = pCurrent->next;
}
if (pCurrent->hDdeData)
{
DdeFreeDataHandle(pCurrent->hDdeData);
}
HeapFree(GetProcessHeap(), 0, pCurrent);
break;
}
pPrev = pCurrent;
pCurrent = pCurrent->next;
}
}
/* this function is called to remove all links related to the conv.
It should be called from both client and server when terminating
the conversation.
*/
/******************************************************************
* WDML_RemoveAllLinks
*
*
*/
void WDML_RemoveAllLinks(WDML_INSTANCE* thisInstance, HCONV hConv, WDML_SIDE side)
{
WDML_LINK* pPrev = NULL;
WDML_LINK* pCurrent = NULL;
WDML_LINK* pNext = NULL;
pCurrent = thisInstance->links[side];
while (pCurrent != NULL)
{
if (pCurrent->hConv == hConv)
{
if (pCurrent == thisInstance->links[side])
{
thisInstance->links[side] = pCurrent->next;
pNext = pCurrent->next;
}
else
{
pPrev->next = pCurrent->next;
pNext = pCurrent->next;
}
if (pCurrent->hDdeData)
{
DdeFreeDataHandle(pCurrent->hDdeData);
}
HeapFree(GetProcessHeap(), 0, pCurrent);
pCurrent = NULL;
}
if (pCurrent)
{
pPrev = pCurrent;
pCurrent = pCurrent->next;
}
else
{
pCurrent = pNext;
}
}
}
/******************************************************************
* WDML_FindLink
*
*
*/
WDML_LINK* WDML_FindLink(WDML_INSTANCE* thisInstance, HCONV hConv, WDML_SIDE side,
HSZ hszItem, UINT uFmt)
{
WDML_LINK* pCurrent = NULL;
for (pCurrent = thisInstance->links[side]; pCurrent != NULL; pCurrent = pCurrent->next)
{
/* we don't need to check for transaction type as
it can be altered */
if (pCurrent->hConv == hConv &&
DdeCmpStringHandles(pCurrent->hszItem, hszItem) == 0 &&
pCurrent->uFmt == uFmt)
{
break;
}
}
return pCurrent;
}
/* ================================================================
*
* Transaction management
*
* ================================================================ */
/******************************************************************
* WDML_AllocTransaction
*
* Alloc a transaction structure for handling the message ddeMsg
*/
WDML_XACT* WDML_AllocTransaction(WDML_INSTANCE* thisInstance, UINT ddeMsg)
{
WDML_XACT* pXAct;
static WORD tid = 1; /* FIXME: wrap around */
pXAct = HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_XACT));
if (!pXAct)
{
thisInstance->lastError = DMLERR_MEMORY_ERROR;
return NULL;
}
pXAct->xActID = tid++;
pXAct->ddeMsg = ddeMsg;
pXAct->hDdeData = 0;
pXAct->hUser = 0;
pXAct->next = NULL;
return pXAct;
}
/******************************************************************
* WDML_QueueTransaction
*
* Adds a transaction to the list of transaction
*/
void WDML_QueueTransaction(WDML_CONV* pConv, WDML_XACT* pXAct)
{
WDML_XACT** pt;
/* advance to last in queue */
for (pt = &pConv->transactions; *pt != NULL; pt = &(*pt)->next);
*pt = pXAct;
}
/******************************************************************
* WDML_UnQueueTransaction
*
*
*/
BOOL WDML_UnQueueTransaction(WDML_CONV* pConv, WDML_XACT* pXAct)
{
WDML_XACT** pt;
for (pt = &pConv->transactions; *pt; pt = &(*pt)->next)
{
if (*pt == pXAct)
{
*pt = pXAct->next;
return TRUE;
}
}
return FALSE;
}
/******************************************************************
* WDML_FreeTransaction
*
*
*/
void WDML_FreeTransaction(WDML_XACT* pXAct)
{
HeapFree(GetProcessHeap(), 0, pXAct);
}
/******************************************************************
* WDML_FindTransaction
*
*
*/
WDML_XACT* WDML_FindTransaction(WDML_CONV* pConv, DWORD tid)
{
WDML_XACT* pXAct;
tid = HIWORD(tid);
for (pXAct = pConv->transactions; pXAct; pXAct = pXAct->next)
{
if (pXAct->xActID == tid)
break;
}
return pXAct;
}
struct tagWDML_BroadcastPmt
{
LPCSTR clsName;
UINT uMsg;
WPARAM wParam;
LPARAM lParam;
};
/******************************************************************
* WDML_BroadcastEnumProc
*
*
*/
static BOOL CALLBACK WDML_BroadcastEnumProc(HWND hWnd, LPARAM lParam)
{
struct tagWDML_BroadcastPmt* s = (struct tagWDML_BroadcastPmt*)lParam;
char buffer[128];
if (GetClassNameA(hWnd, buffer, sizeof(buffer)) > 0 &&
strcmp(buffer, s->clsName) == 0)
{
PostMessageA(hWnd, s->uMsg, s->wParam, s->lParam);
}
return TRUE;
}
/******************************************************************
* WDML_BroadcastDDEWindows
*
*
*/
void WDML_BroadcastDDEWindows(const char* clsName, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
struct tagWDML_BroadcastPmt s;
s.clsName = clsName;
s.uMsg = uMsg;
s.wParam = wParam;
s.lParam = lParam;
EnumWindows(WDML_BroadcastEnumProc, (LPARAM)&s);
}
/* -*- 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 const char szServerNameClassA[] = "DdeServerNameAnsi";
static const char szServerConvClassA[] = "DdeServerConvAnsi";
static LRESULT CALLBACK WDML_ServerNameProc(HWND, UINT, WPARAM, LPARAM);
static LRESULT CALLBACK WDML_ServerConvProc(HWND, UINT, WPARAM, LPARAM);
/******************************************************************************
* DdePostAdvise [USER32.@] Send transaction to DDE callback function.
*
* RETURNS
* Success: TRUE
* Failure: FALSE
*/
BOOL WINAPI DdePostAdvise(
DWORD idInst, /* [in] Instance identifier */
HSZ hszTopic, /* [in] Handle to topic name string */
HSZ hszItem) /* [in] Handle to item name string */
{
WDML_INSTANCE* thisInstance = NULL;
WDML_LINK* pLink = NULL;
HDDEDATA hDdeData = 0, hItemData = 0;
WDML_CONV* pConv = NULL;
CHAR pszTopic[MAX_BUFFER_LEN];
CHAR pszItem[MAX_BUFFER_LEN];
TRACE("(%ld,%ld,%ld)\n",idInst,(DWORD)hszTopic,(DWORD)hszItem);
if (idInst == 0)
{
return FALSE;
}
thisInstance = WDML_FindInstance(idInst);
if (thisInstance == NULL || thisInstance->links == NULL)
{
return FALSE;
}
GlobalGetAtomNameA(hszTopic, (LPSTR)pszTopic, MAX_BUFFER_LEN);
GlobalGetAtomNameA(hszItem, (LPSTR)pszItem, MAX_BUFFER_LEN);
for (pLink = thisInstance->links[WDML_SERVER_SIDE]; pLink != NULL; pLink = pLink->next)
{
if (DdeCmpStringHandles(hszItem, pLink->hszItem) == 0)
{
hDdeData = 0;
if (thisInstance->callback != NULL /* && thisInstance->Process_id == GetCurrentProcessId()*/)
{
TRACE("Calling the callback, type=XTYP_ADVREQ, CB=0x%lx, hConv=0x%lx, Topic=%s, Item=%s\n",
(DWORD)thisInstance->callback, (DWORD)pLink->hConv, pszTopic, pszItem);
hDdeData = (thisInstance->callback)(XTYP_ADVREQ,
pLink->uFmt,
pLink->hConv,
hszTopic,
hszItem,
0, 0, 0);
TRACE("Callback was called\n");
}
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);
if (pConv == NULL ||
!PostMessageA(pConv->hwndClient, WM_DDE_DATA, (WPARAM)pConv->hwndServer,
PackDDElParam(WM_DDE_DATA, (UINT)hItemData, (DWORD)hszItem)))
{
ERR("post message failed\n");
DdeFreeDataHandle(hDdeData);
return FALSE;
}
}
}
}
return TRUE;
}
/******************************************************************************
* DdeNameService [USER32.@] {Un}registers service name of DDE server
*
* PARAMS
* idInst [I] Instance identifier
* hsz1 [I] Handle to service name string
* hsz2 [I] Reserved
* afCmd [I] Service name flags
*
* RETURNS
* Success: Non-zero
* Failure: 0
*/
HDDEDATA WINAPI DdeNameService(DWORD idInst, HSZ hsz1, HSZ hsz2, UINT afCmd)
{
WDML_SERVER* pServer;
WDML_SERVER* pServerTmp;
WDML_INSTANCE* thisInstance;
HDDEDATA hDdeData;
HSZ hsz2nd = 0;
HWND hwndServer;
WNDCLASSEXA wndclass;
hDdeData = (HDDEDATA)NULL;
TRACE("(%ld,%d,%d,%d): stub\n",idInst,hsz1,hsz2,afCmd);
if (WDML_MaxInstanceID == 0)
{
/* Nothing has been initialised - exit now !
* needs something for DdeGetLastError */
return 0;
}
if (!WDML_WaitForMutex(handle_mutex))
{
/* FIXME: setError DMLERR_SYS_ERROR; */
return 0;
}
/* First check instance
*/
thisInstance = WDML_FindInstance(idInst);
if (thisInstance == NULL)
{
TRACE("Instance not found as initialised\n");
WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
/* Nothing has been initialised - exit now ! can return TRUE since effect is the same */
return FALSE;
}
if (hsz2 != 0L)
{
/* Illegal, reserved parameter
*/
thisInstance->lastError = DMLERR_INVALIDPARAMETER;
WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
FIXME("Reserved parameter no-zero !!\n");
return FALSE;
}
if (hsz1 == 0L)
{
/*
* General unregister situation
*/
if (afCmd != DNS_UNREGISTER)
{
/* don't know if we should check this but it makes sense
* why supply REGISTER or filter flags if de-registering all
*/
TRACE("General unregister unexpected flags\n");
thisInstance->lastError = DMLERR_DLL_USAGE;
WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
return FALSE;
}
/* Loop to find all registered service and de-register them
*/
if (thisInstance->servers == NULL)
{
/* None to unregister !!
*/
TRACE("General de-register - nothing registered\n");
thisInstance->lastError = DMLERR_DLL_USAGE;
WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
return FALSE;
}
else
{
pServer = thisInstance->servers;
while (pServer != NULL)
{
TRACE("general deregister - iteration\n");
pServerTmp = pServer;
pServer = pServer->next;
WDML_ReleaseAtom(thisInstance, pServerTmp->hszService);
/* finished - release heap space used as work store */
HeapFree(GetProcessHeap(), 0, pServerTmp);
}
thisInstance->servers = NULL;
TRACE("General de-register - finished\n");
}
WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
return (HDDEDATA)TRUE;
}
TRACE("Specific name action detected\n");
if (afCmd & DNS_REGISTER)
{
/* Register new service name
*/
pServer = WDML_FindServer(thisInstance, hsz1, 0);
if (pServer)
ERR("Trying to register already registered service!\n");
else
{
TRACE("Adding service name\n");
WDML_ReserveAtom(thisInstance, hsz1);
pServer = WDML_AddServer(thisInstance, hsz1, 0);
WDML_BroadcastDDEWindows(WDML_szEventClass, WM_WDML_REGISTER, hsz1, hsz2nd);
}
wndclass.cbSize = sizeof(wndclass);
wndclass.style = 0;
wndclass.lpfnWndProc = WDML_ServerNameProc;
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 = szServerNameClassA;
wndclass.hIconSm = 0;
RegisterClassExA(&wndclass);
hwndServer = CreateWindowA(szServerNameClassA, NULL,
WS_POPUP, 0, 0, 0, 0,
0, 0, 0, 0);
SetWindowLongA(hwndServer, 0, (DWORD)thisInstance);
TRACE("Created nameServer=%04x for instance=%08lx\n", hwndServer, idInst);
pServer->hwndServer = hwndServer;
}
if (afCmd & DNS_UNREGISTER)
{
TRACE("Broadcasting WM_DDE_TERMINATE message\n");
SendMessageA(HWND_BROADCAST, WM_DDE_TERMINATE, (WPARAM)NULL,
PackDDElParam(WM_DDE_TERMINATE, (UINT)hsz1, (UINT)hsz2));
WDML_RemoveServer(thisInstance, hsz1, hsz2);
}
if (afCmd & DNS_FILTERON)
{
/* Set filter flags on to hold notifications of connection
*
* test coded this way as this is the default setting
*/
pServer = WDML_FindServer(thisInstance, hsz1, 0);
if (!pServer)
{
/* trying to filter where no service names !!
*/
thisInstance->lastError = DMLERR_DLL_USAGE;
WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
return FALSE;
}
else
{
pServer->filterOn = TRUE;
}
}
if (afCmd & DNS_FILTEROFF)
{
/* Set filter flags on to hold notifications of connection
*/
pServer = WDML_FindServer(thisInstance, hsz1, 0);
if (!pServer)
{
/* trying to filter where no service names !!
*/
thisInstance->lastError = DMLERR_DLL_USAGE;
WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
return FALSE;
}
else
{
pServer->filterOn = FALSE;
}
}
WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
return (HDDEDATA)TRUE;
}
/******************************************************************
* WDML_CreateServerConv
*
*
*/
static BOOL WDML_CreateServerConv(WDML_INSTANCE* thisInstance, HWND hwndClient, HWND hwndServerName,
HSZ hszApp, HSZ hszTopic)
{
WNDCLASSEXA wndclass;
HWND hwndServerConv;
WDML_CONV* pConv;
wndclass.cbSize = sizeof(wndclass);
wndclass.style = 0;
wndclass.lpfnWndProc = WDML_ServerConvProc;
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 = szServerConvClassA;
wndclass.hIconSm = 0;
RegisterClassExA(&wndclass);
hwndServerConv = CreateWindowA(szServerConvClassA, 0,
WS_CHILD, 0, 0, 0, 0,
hwndServerName, 0, 0, 0);
TRACE("Created convServer=%04x (nameServer=%04x) for instance=%08lx\n",
hwndServerConv, hwndServerName, thisInstance->instanceID);
pConv = WDML_AddConv(thisInstance, WDML_SERVER_SIDE, hszApp, hszTopic, hwndClient, hwndServerConv);
SetWindowLongA(hwndServerConv, 0, (DWORD)thisInstance);
SetWindowLongA(hwndServerConv, 4, (DWORD)pConv);
/* this should be the only place using SendMessage for WM_DDE_ACK */
SendMessageA(hwndClient, WM_DDE_ACK, (WPARAM)hwndServerConv,
PackDDElParam(WM_DDE_ACK, (UINT)hszApp, (UINT)hszTopic));
#if 0
if (thisInstance && thisInstance->callback != NULL /*&& thisInstance->Process_id == GetCurrentProcessId()*/)
{
/* confirm connection...
* FIXME: a better way would be to check for any incoming message if the conversation
* exists (and/or) has been confirmed...
* Anyway, always pretend we use a connection from a different instance...
*/
(thisInstance->callback)(XTYP_CONNECT_CONFIRM, 0, (HCONV)pConv, hszApp, hszTopic, 0, 0, 0);
}
#endif
return TRUE;
}
/******************************************************************
* WDML_ServerNameProc
*
*
*/
static LRESULT CALLBACK WDML_ServerNameProc(HWND hwndServer, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
HWND hwndClient;
HSZ hszApp, hszTop;
HDDEDATA hDdeData = 0;
WDML_INSTANCE* thisInstance;
UINT uiLow, uiHi;
switch (iMsg)
{
case WM_DDE_INITIATE:
/* wParam -- sending window handle
LOWORD(lParam) -- application atom
HIWORD(lParam) -- topic atom */
TRACE("WM_DDE_INITIATE message received in the Server Proc!\n");
hwndClient = (HWND)wParam;
/* don't free DDEParams, since this is a broadcast */
UnpackDDElParam(WM_DDE_INITIATE, lParam, &uiLow, &uiHi);
hszApp = (HSZ)uiLow;
hszTop = (HSZ)uiHi;
thisInstance = (WDML_INSTANCE*)GetWindowLongA(hwndServer, 0);
TRACE("idInst=%ld, ProcessID=0x%lx\n", thisInstance->instanceID, GetCurrentProcessId());
if (hszApp && hszTop)
{
/* pass on to the callback */
if (thisInstance && thisInstance->callback != NULL /*&& thisInstance->Process_id == GetCurrentProcessId()*/)
{
TRACE("calling the Callback, type = XTYP_CONNECT, CB=0x%lx\n",
(DWORD)thisInstance->callback);
hDdeData = (thisInstance->callback)(XTYP_CONNECT,
0, 0,
hszTop,
hszApp,
0, 0, 0);
if ((UINT)hDdeData)
{
WDML_CreateServerConv(thisInstance, hwndClient, hwndServer, hszApp, hszTop);
}
}
}
else
{
/* pass on to the callback */
if (thisInstance && thisInstance->callback != NULL /*&& thisInstance->Process_id == GetCurrentProcessId()*/)
{
TRACE("calling the Callback, type=XTYP_WILDCONNECT, CB=0x%lx\n",
(DWORD)thisInstance->callback);
hDdeData = (thisInstance->callback)(XTYP_WILDCONNECT,
0, 0,
hszTop,
hszApp,
0, 0, 0);
if ((UINT)hDdeData)
{
HSZPAIR* hszp;
hszp = (HSZPAIR*)DdeAccessData(hDdeData, NULL);
if (hszp)
{
int i;
for (i = 0; hszp[i].hszSvc && hszp[i].hszTopic; i++)
{
WDML_CreateServerConv(thisInstance, hwndClient, hwndServer,
hszp[i].hszSvc, hszp[i].hszTopic);
}
DdeUnaccessData(hDdeData);
}
}
}
}
/*
billx: make a conv and add it to the server list -
this can be delayed when link is created for the conv. NO NEED !!!
*/
return 0;
case WM_DDE_REQUEST:
FIXME("WM_DDE_REQUEST message received!\n");
return 0;
case WM_DDE_ADVISE:
FIXME("WM_DDE_ADVISE message received!\n");
return 0;
case WM_DDE_UNADVISE:
FIXME("WM_DDE_UNADVISE message received!\n");
return 0;
case WM_DDE_EXECUTE:
FIXME("WM_DDE_EXECUTE message received!\n");
return 0;
case WM_DDE_POKE:
FIXME("WM_DDE_POKE message received!\n");
return 0;
case WM_DDE_TERMINATE:
FIXME("WM_DDE_TERMINATE message received!\n");
return 0;
}
return DefWindowProcA(hwndServer, iMsg, wParam, lParam);
}
/******************************************************************
* WDML_ServerHandleRequest
*
*
*/
static LRESULT WDML_ServerHandleRequest(WDML_INSTANCE* thisInstance, WDML_CONV* pConv,
HWND hwndServer, HWND hwndClient, LPARAM lParam)
{
UINT uiLow, uiHi;
HSZ hszItem;
HDDEDATA hDdeData;
TRACE("(%04x %04x %08lx)!\n", hwndServer, hwndClient, lParam);
UnpackDDElParam(WM_DDE_REQUEST, lParam, &uiLow, &uiHi);
hszItem = (HSZ)uiHi;
hDdeData = 0;
if (thisInstance && thisInstance->callback != NULL /* && thisInstance->Process_id == GetCurrentProcessId() */)
{
TRACE("calling the Callback, idInst=%ld, CB=0x%lx, uType=0x%x\n",
thisInstance->instanceID, (DWORD)thisInstance->callback, XTYP_REQUEST);
hDdeData = (thisInstance->callback)(XTYP_REQUEST, uiLow, (HCONV)pConv,
pConv->hszTopic, hszItem, 0, 0, 0);
}
if (hDdeData)
{
HGLOBAL hMem = WDML_DataHandle2Global(hDdeData, FALSE, FALSE, FALSE, FALSE);
if (!PostMessageA(hwndClient, WM_DDE_DATA, (WPARAM)hwndServer,
ReuseDDElParam(lParam, WM_DDE_REQUEST, WM_DDE_DATA, (UINT)hMem, (UINT)hszItem)))
{
DdeFreeDataHandle(hDdeData);
GlobalFree(hMem);
}
}
else
{
DDEACK ddeAck;
ddeAck.bAppReturnCode = 0;
ddeAck.reserved = 0;
ddeAck.fBusy = FALSE;
ddeAck.fAck = FALSE;
TRACE("Posting a %s ack\n", ddeAck.fAck ? "positive" : "negative");
PostMessageA(hwndClient, WM_DDE_ACK, (WPARAM)hwndServer,
ReuseDDElParam(lParam, WM_DDE_REQUEST, WM_DDE_ACK, *((WORD*)&ddeAck), (UINT)hszItem));
}
return 0;
}
/******************************************************************
* WDML_ServerHandleAdvise
*
*
*/
static LRESULT WDML_ServerHandleAdvise(WDML_INSTANCE* thisInstance, WDML_CONV* pConv,
HWND hwndServer, HWND hwndClient, LPARAM lParam)
{
UINT uiLo, uiHi, uType;
HGLOBAL hDdeAdvise;
HSZ hszItem;
WDML_LINK* pLink;
DDEADVISE* pDdeAdvise;
HDDEDATA hDdeData;
DDEACK ddeAck;
/* XTYP_ADVSTART transaction:
establish link and save link info to InstanceInfoTable */
TRACE("(%04x %04x %08lx)!\n", hwndServer, hwndClient, lParam);
UnpackDDElParam(WM_DDE_ADVISE, lParam, &uiLo, &uiHi);
hDdeAdvise = (HGLOBAL)uiLo;
hszItem = (HSZ)uiHi; /* FIXME: it should be a global atom */
if (!pConv)
{
ERR("Got an advise on a not known conversation, dropping request\n");
FreeDDElParam(WM_DDE_ADVISE, lParam);
return 0;
}
pDdeAdvise = (DDEADVISE*)GlobalLock(hDdeAdvise);
uType = XTYP_ADVSTART |
(pDdeAdvise->fDeferUpd ? XTYPF_NODATA : 0) |
(pDdeAdvise->fAckReq ? XTYPF_ACKREQ : 0);
hDdeData = 0;
if (thisInstance && thisInstance->callback != NULL /* && thisInstance->Process_id == GetCurrentProcessId() */)
{
TRACE("calling the Callback, idInst=%ld, CB=0x%lx, uType=0x%x, uFmt=%x\n",
thisInstance->instanceID, (DWORD)thisInstance->callback,
uType, pDdeAdvise->cfFormat);
hDdeData = (thisInstance->callback)(XTYP_ADVSTART, pDdeAdvise->cfFormat, (HCONV)pConv,
pConv->hszTopic, hszItem, 0, 0, 0);
}
ddeAck.bAppReturnCode = 0;
ddeAck.reserved = 0;
ddeAck.fBusy = FALSE;
if ((UINT)hDdeData || TRUE) /* FIXME (from Corel ?) some apps don't return this value */
{
ddeAck.fAck = TRUE;
/* billx: first to see if the link is already created. */
pLink = WDML_FindLink(thisInstance, (HCONV)pConv, WDML_SERVER_SIDE,
hszItem, pDdeAdvise->cfFormat);
if (pLink != NULL)
{
/* we found a link, and only need to modify it in case it changes */
pLink->transactionType = uType;
}
else
{
TRACE("Adding Link with hConv=0x%lx\n", (DWORD)pConv);
WDML_AddLink(thisInstance, (HCONV)pConv, WDML_SERVER_SIDE,
uType, hszItem, pDdeAdvise->cfFormat);
}
}
else
{
TRACE("No data returned from the Callback\n");
ddeAck.fAck = FALSE;
}
GlobalUnlock(hDdeAdvise);
if (ddeAck.fAck)
GlobalFree(hDdeAdvise);
TRACE("Posting a %s ack\n", ddeAck.fAck ? "positive" : "negative");
PostMessageA(hwndClient, WM_DDE_ACK, (WPARAM)hwndServer,
ReuseDDElParam(lParam, WM_DDE_ADVISE, WM_DDE_ACK, *((WORD*)&ddeAck), (UINT)hszItem));
return 0L;
}
/******************************************************************
* WDML_ServerHandleUnadvise
*
*
*/
static LRESULT WDML_ServerHandleUnadvise(WDML_INSTANCE* thisInstance, WDML_CONV* pConv,
HWND hwndServer, HWND hwndClient, LPARAM lParam)
{
UINT uiLo, uiHi;
HSZ hszItem;
WDML_LINK* pLink;
DDEACK ddeAck;
TRACE("(%04x %04x %08lx)!\n", hwndServer, hwndClient, lParam);
/* billx: XTYP_ADVSTOP transaction */
UnpackDDElParam(WM_DDE_UNADVISE, lParam, &uiLo, &uiHi);
/* uiLow: wFmt */
hszItem = (HSZ)uiHi; /* FIXME: it should be a global atom */
if (hszItem == (HSZ)0 || uiLo == 0)
{
ERR("Unsupported yet options (null item or clipboard format\n");
}
pLink = WDML_FindLink(thisInstance, (HCONV)pConv, WDML_SERVER_SIDE, hszItem, uiLo);
if (pLink == NULL)
{
ERR("Couln'd find link for %08lx, dropping request\n", (DWORD)hszItem);
FreeDDElParam(WM_DDE_UNADVISE, lParam);
return 0;
}
/* callback shouldn't be invoked if CBF_FAIL_ADVISES is on. */
if (thisInstance && thisInstance->callback != NULL &&
!(thisInstance->CBFflags & CBF_SKIP_DISCONNECTS) /* && thisInstance->Process_id == GetCurrentProcessId() */)
{
TRACE("calling the Callback, idInst=%ld, CB=0x%lx, uType=0x%x\n",
thisInstance->instanceID, (DWORD)thisInstance->callback, XTYP_ADVSTOP);
(thisInstance->callback)(XTYP_ADVSTOP, uiLo, (HCONV)pConv, pConv->hszTopic,
hszItem, 0, 0, 0);
}
WDML_RemoveLink(thisInstance, (HCONV)pConv, WDML_SERVER_SIDE, hszItem, uiLo);
/* send back ack */
ddeAck.bAppReturnCode = 0;
ddeAck.reserved = 0;
ddeAck.fBusy = FALSE;
ddeAck.fAck = TRUE;
PostMessageA(hwndClient, WM_DDE_ACK, (WPARAM)hwndServer,
ReuseDDElParam(lParam, WM_DDE_UNADVISE, WM_DDE_ACK, *((WORD*)&ddeAck), (UINT)hszItem));
return 0;
}
/******************************************************************
* WDML_ServerHandleExecute
*
*
*/
static LRESULT WDML_ServerHandleExecute(WDML_INSTANCE* thisInstance, WDML_CONV* pConv,
HWND hwndServer, HWND hwndClient, LPARAM lParam)
{
DDEACK ddeAck;
HDDEDATA hDdeData;
TRACE("(%04x %04x %08lx)!\n", hwndServer, hwndClient, lParam);
if (hwndClient != pConv->hwndClient)
WARN("hmmm source window (%04x)\n", hwndClient);
hDdeData = 0;
if (thisInstance && thisInstance->callback != NULL /* && thisInstance->Process_id == GetCurrentProcessId() */)
{
LPVOID ptr = GlobalLock((HGLOBAL)lParam);
if (ptr)
{
hDdeData = DdeCreateDataHandle(0, ptr, GlobalSize((HGLOBAL)lParam),
0, 0, CF_TEXT, 0);
GlobalUnlock((HGLOBAL)lParam);
}
TRACE("calling the Callback, idInst=%ld, CB=0x%lx, uType=0x%x\n",
thisInstance->instanceID, (DWORD)thisInstance->callback, XTYP_EXECUTE);
hDdeData = (thisInstance->callback)(XTYP_EXECUTE, 0, (HCONV)pConv, pConv->hszTopic, 0,
hDdeData, 0L, 0L);
}
ddeAck.bAppReturnCode = 0;
ddeAck.reserved = 0;
ddeAck.fBusy = FALSE;
ddeAck.fAck = FALSE;
switch ((UINT)hDdeData)
{
case DDE_FACK:
ddeAck.fAck = TRUE;
break;
case DDE_FBUSY:
ddeAck.fBusy = TRUE;
break;
default:
WARN("Bad result code\n");
/* fall thru */
case DDE_FNOTPROCESSED:
break;
}
PostMessageA(pConv->hwndClient, WM_DDE_ACK, (WPARAM)hwndServer,
PackDDElParam(WM_DDE_ACK, *((WORD*)&ddeAck), lParam));
return 0;
}
/******************************************************************
* WDML_ServerHandlePoke
*
*
*/
static LRESULT WDML_ServerHandlePoke(WDML_INSTANCE* thisInstance, WDML_CONV* pConv,
HWND hwndServer, HWND hwndClient, LPARAM lParam)
{
UINT uiLo, uiHi;
HSZ hszItem;
DDEACK ddeAck;
DDEPOKE* pDdePoke;
HDDEDATA hDdeData;
TRACE("(%04x %04x %08lx)!\n", hwndServer, hwndClient, lParam);
UnpackDDElParam(WM_DDE_UNADVISE, lParam, &uiLo, &uiHi);
hszItem = (HSZ)uiHi;
pDdePoke = (DDEPOKE*)GlobalLock((HGLOBAL)uiLo);
if (!pDdePoke)
{
return 0;
}
ddeAck.bAppReturnCode = 0;
ddeAck.reserved = 0;
ddeAck.fBusy = FALSE;
ddeAck.fAck = FALSE;
if (thisInstance && thisInstance->callback != NULL)
{
hDdeData = DdeCreateDataHandle(thisInstance->instanceID, pDdePoke->Value,
GlobalSize((HGLOBAL)uiLo) - sizeof(DDEPOKE) + 1,
0, 0, pDdePoke->cfFormat, 0);
if (hDdeData)
{
HDDEDATA hDdeDataOut;
TRACE("calling callback XTYP_POKE, idInst=%ld\n",
thisInstance->instanceID);
hDdeDataOut = (thisInstance->callback)(XTYP_POKE,
pDdePoke->cfFormat, (HCONV)pConv,
pConv->hszTopic, (HSZ)uiHi,
hDdeData, 0, 0);
switch ((UINT)hDdeDataOut)
{
case DDE_FACK:
ddeAck.fAck = TRUE;
break;
case DDE_FBUSY:
ddeAck.fBusy = TRUE;
break;
default:
FIXME("Unsupported returned value %08lx\n", (DWORD)hDdeDataOut);
/* fal thru */
case DDE_FNOTPROCESSED:
break;
}
DdeFreeDataHandle(hDdeData);
}
}
GlobalUnlock((HGLOBAL)uiLo);
if (!ddeAck.fAck)
GlobalFree((HGLOBAL)uiHi);
PostMessageA(hwndClient, WM_DDE_ACK, (WPARAM)hwndServer,
ReuseDDElParam(lParam, WM_DDE_POKE, WM_DDE_ACK, *((WORD*)&ddeAck), (UINT)hszItem));
return 0L;
}
/******************************************************************
* WDML_ServerHandleTerminate
*
*
*/
static LRESULT WDML_ServerHandleTerminate(WDML_INSTANCE* thisInstance, WDML_CONV* pConv,
HWND hwndServer, HWND hwndClient, LPARAM lParam)
{
UINT uiLo, uiHi;
HSZ hszApp, hszTop;
TRACE("(%04x %04x %08lx)!\n", hwndServer, hwndClient, lParam);
TRACE("WM_DDE_TERMINATE!\n");
/* XTYP_DISCONNECT transaction */
/* billx: two things to remove: the conv, and associated links.
callback shouldn't be called if CBF_SKIP_DISCONNECTS is on.
Respond with another WM_DDE_TERMINATE iMsg.*/
/* don't free DDEParams, since this is a broadcast */
UnpackDDElParam(WM_DDE_TERMINATE, lParam, &uiLo, &uiHi);
hszApp = (HSZ)uiLo;
hszTop = (HSZ)uiHi;
WDML_BroadcastDDEWindows(WDML_szEventClass, WM_WDML_UNREGISTER, hszApp, hszTop);
/* PostMessageA(hwndClient, WM_DDE_TERMINATE, (WPARAM)hwndServer, (LPARAM)hConv); */
WDML_RemoveAllLinks(thisInstance, (HCONV)pConv, WDML_SERVER_SIDE);
WDML_RemoveConv(thisInstance, WDML_SERVER_SIDE, (HCONV)pConv);
/* DestroyWindow(hwnd); don't destroy it now, we may still need it. */
return 0;
}
/******************************************************************
* WDML_ServerConvProc
*
*
*/
static LRESULT CALLBACK WDML_ServerConvProc(HWND hwndServer, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
WDML_INSTANCE* thisInstance;
WDML_CONV* pConv;
if (iMsg < WM_DDE_FIRST || iMsg > WM_DDE_LAST)
{
return DefWindowProcA(hwndServer, iMsg, wParam, lParam);
}
TRACE("About to wait... \n");
if (!WDML_WaitForMutex(handle_mutex))
{
return 0;
}
thisInstance = (WDML_INSTANCE*)GetWindowLongA(hwndServer, 0);
pConv = (WDML_CONV*)GetWindowLongA(hwndServer, 4);
switch (iMsg)
{
case WM_DDE_INITIATE:
FIXME("WM_DDE_INITIATE message received in the ServerConv Proc!\n");
break;
case WM_DDE_REQUEST:
WDML_ServerHandleRequest(thisInstance, pConv, hwndServer, (HWND)wParam, lParam);
break;
case WM_DDE_ADVISE:
WDML_ServerHandleAdvise(thisInstance, pConv, hwndServer, (HWND)wParam, lParam);
break;
case WM_DDE_UNADVISE:
WDML_ServerHandleUnadvise(thisInstance, pConv, hwndServer, (HWND)wParam, lParam);
break;
case WM_DDE_EXECUTE:
WDML_ServerHandleExecute(thisInstance, pConv, hwndServer, (HWND)wParam, lParam);
break;
case WM_DDE_POKE:
WDML_ServerHandlePoke(thisInstance, pConv, hwndServer, (HWND)wParam, lParam);
break;
case WM_DDE_TERMINATE:
WDML_ServerHandleTerminate(thisInstance, pConv, hwndServer, (HWND)wParam, lParam);
break;
case WM_DDE_ACK:
WARN("Shouldn't receive a ACK message (never requests them). Ignoring it\n");
break;
default:
FIXME("Unsupported message %d\n", iMsg);
}
WDML_ReleaseMutex(handle_mutex, "handle_mutex", FALSE);
return 0;
}
/*
* DDEML library
*
* Copyright 1997 Alexandre Julliard
* Copyright 1997 Len White
* Copyright 1999 Keith Matthews
*/
/* Only empty stubs for now */
#include <stdlib.h>
#include <string.h>
#include "winbase.h"
#include "windef.h"
#include "wingdi.h"
#include "winuser.h"
#include "ddeml.h"
#include "ddeml16.h"
#include "winerror.h"
#include "heap.h"
#include "debugtools.h"
#include "winnt.h"
DEFAULT_DEBUG_CHANNEL(ddeml);
/* Has defined in atom.c file.
*/
#define MAX_ATOM_LEN 255
/* Maximum buffer size ( including the '\0' ).
*/
#define MAX_BUFFER_LEN (MAX_ATOM_LEN + 1)
/* typedef struct {
DWORD nLength;
LPVOID lpSecurityDescriptor;
BOOL bInheritHandle;
} SECURITY_ATTRIBUTES; */
/* This is a simple list to keep track of the strings created
* by DdeCreateStringHandle. The list is used to free
* the strings whenever DdeUninitialize is called.
* This mechanism is not complete and does not handle multiple instances.
* Most of the DDE API use a DWORD parameter indicating which instance
* of a given program is calling them. The API are supposed to
* associate the data to the instance that created it.
*/
typedef struct tagHSZNode HSZNode;
struct tagHSZNode
{
HSZNode* next;
HSZ hsz;
};
typedef struct tagServiceNode ServiceNode;
struct tagServiceNode
{
ServiceNode* next;
HSZ hsz;
BOOL16 FilterOn;
};
typedef struct DDE_HANDLE_ENTRY {
BOOL16 Monitor; /* have these two as full Booleans cos they'll be tested frequently */
BOOL16 Client_only; /* bit wasteful of space but it will be faster */
BOOL16 Unicode; /* Flag to indicate Win32 API used to initialise */
BOOL16 Win16; /* flag to indicate Win16 API used to initialize */
DWORD Instance_id; /* needed to track monitor usage */
struct DDE_HANDLE_ENTRY *Next_Entry;
HSZNode *Node_list;
PFNCALLBACK CallBack;
DWORD CBF_Flags;
DWORD Monitor_flags;
UINT Txn_count; /* count transactions open to simplify closure */
DWORD Last_Error;
ServiceNode* ServiceNames;
} DDE_HANDLE_ENTRY;
static DDE_HANDLE_ENTRY *DDE_Handle_Table_Base = NULL;
static DWORD DDE_Max_Assigned_Instance = 0; /* OK for present, may have to worry about wrap-around later */
static const char *DDEInstanceAccess = "DDEMaxInstance";
static const char *DDEHandleAccess = "DDEHandleAccess";
static HANDLE inst_count_mutex = 0;
static HANDLE handle_mutex = 0;
#define TRUE 1
#define FALSE 0
/******************************************************************************
* RemoveHSZNodes (INTERNAL)
*
* Remove a node from the list of HSZ nodes.
*
******************************************************************************
*
* Change History
*
* Vn Date Author Comment
*
* 1.0 Dec 1998 Corel/Macadamian Initial version
* 1.1 Mar 1999 Keith Matthews Added multiple instance handling
*
*/
static void RemoveHSZNode( HSZ hsz, DDE_HANDLE_ENTRY * reference_inst )
{
HSZNode* pPrev = NULL;
HSZNode* pCurrent = NULL;
/* Set the current node at the start of the list.
*/
pCurrent = reference_inst->Node_list;
/* While we have more nodes.
*/
while( pCurrent != NULL )
{
/* If we found the node we were looking for.
*/
if( pCurrent->hsz == hsz )
{
/* Remove the node.
*/
/* If the first node in the list is to to be removed.
* Set the global list pointer to the next node.
*/
if( pCurrent == reference_inst->Node_list )
{
reference_inst->Node_list = pCurrent->next;
}
/* Just fix the pointers has to skip the current
* node so we can delete it.
*/
else
{
pPrev->next = pCurrent->next;
}
/* Destroy this node.
*/
free( pCurrent );
break;
}
/* Save the previous node pointer.
*/
pPrev = pCurrent;
/* Move on to the next node.
*/
pCurrent = pCurrent->next;
}
}
/******************************************************************************
* FreeAndRemoveHSZNodes (INTERNAL)
*
* Frees up all the strings still allocated in the list and
* remove all the nodes from the list of HSZ nodes.
*
******************************************************************************
*
* Change History
*
* Vn Date Author Comment
*
* 1.0 Dec 1998 Corel/Macadamian Initial version
* 1.1 Mar 1999 Keith Matthews Added multiple instance handling
*
*/
static void FreeAndRemoveHSZNodes( DWORD idInst, DDE_HANDLE_ENTRY * reference_inst )
{
/* Free any strings created in this instance.
*/
while( reference_inst->Node_list != NULL )
{
DdeFreeStringHandle( idInst, reference_inst->Node_list->hsz );
}
}
/******************************************************************************
* InsertHSZNode (INTERNAL)
*
* Insert a node to the head of the list.
*
******************************************************************************
*
* Change History
*
* Vn Date Author Comment
*
* 1.0 Dec 1998 Corel/Macadamian Initial version
* 1.1 Mar 1999 Keith Matthews Added instance handling
* 1.2 Jun 1999 Keith Matthews Added Usage count handling
*
*/
static void InsertHSZNode( HSZ hsz, DDE_HANDLE_ENTRY * reference_inst )
{
if( hsz != 0 )
{
HSZNode* pNew = NULL;
/* Create a new node for this HSZ.
*/
pNew = (HSZNode*) malloc( sizeof( HSZNode ) );
if( pNew != NULL )
{
/* Set the handle value.
*/
pNew->hsz = hsz;
/* Attach the node to the head of the list. i.e most recently added is first
*/
pNew->next = reference_inst->Node_list;
/* The new node is now at the head of the list
* so set the global list pointer to it.
*/
reference_inst->Node_list = pNew;
TRACE("HSZ node list entry added\n");
}
}
}
/*****************************************************************************
* Find_Instance_Entry
*
* generic routine to return a pointer to the relevant DDE_HANDLE_ENTRY
* for an instance Id, or NULL if the entry does not exist
*
* ASSUMES the mutex protecting the handle entry list is reserved before calling
*
******************************************************************************
*
* Change History
*
* Vn Date Author Comment
*
* 1.0 March 1999 Keith Matthews 1st implementation
*/
DDE_HANDLE_ENTRY *Find_Instance_Entry (DWORD InstId)
{
DDE_HANDLE_ENTRY * reference_inst;
reference_inst = DDE_Handle_Table_Base;
while ( reference_inst != NULL )
{
if ( reference_inst->Instance_id == InstId )
{
TRACE("Instance entry found\n");
return reference_inst;
}
reference_inst = reference_inst->Next_Entry;
}
TRACE("Instance entry missing\n");
return NULL;
}
/*****************************************************************************
* Find_Service_Name
*
* generic routine to return a pointer to the relevant ServiceNode
* for a given service name, or NULL if the entry does not exist
*
* ASSUMES the mutex protecting the handle entry list is reserved before calling
*
******************************************************************************
*
* Change History
*
* Vn Date Author Comment
*
* 1.0 May 1999 Keith Matthews 1st implementation
*/
ServiceNode *Find_Service_Name (HSZ Service_Name, DDE_HANDLE_ENTRY* this_instance)
{
ServiceNode * reference_name= this_instance->ServiceNames;
while ( reference_name != NULL )
{
if ( reference_name->hsz == Service_Name )
{
TRACE("Service Name found\n");
return reference_name;
}
reference_name = reference_name->next;
}
TRACE("Service name missing\n");
return NULL;
}
/******************************************************************************
* Release_reserved_mutex
*
* generic routine to release a reserved mutex
*
*
******************************************************************************
*
* Change History
*
* Vn Date Author Comment
*
* 1.0 Jan 1999 Keith Matthews Initial version
* 1.1 Mar 1999 Keith Matthews Corrected Heap handling. Corrected re-initialisation handling
* 1.2 Aug 1999 Jürgen Schmied Corrected error handling
*
*/
static DWORD Release_reserved_mutex (HANDLE mutex, LPSTR mutex_name, BOOL release_handle_m, BOOL release_this_i ,
DDE_HANDLE_ENTRY *this_instance)
{
if (!ReleaseMutex(mutex))
{
ERR("ReleaseMutex failed - %s mutex %li\n",mutex_name,GetLastError());
HeapFree(GetProcessHeap(), 0, this_instance);
if ( release_handle_m )
{
ReleaseMutex(handle_mutex);
}
return DMLERR_SYS_ERROR;
}
if ( release_this_i )
{
HeapFree(GetProcessHeap(), 0, this_instance);
}
return DMLERR_NO_ERROR;
}
/******************************************************************************
* WaitForMutex
*
* generic routine to wait for the mutex
*
*
******************************************************************************
*
* Change History
*
* Vn Date Author Comment
*
* 1.0 Aug 1999 Juergen Schmied Initial version
*
*/
static BOOL WaitForMutex (HANDLE mutex)
{
DWORD result;
result = WaitForSingleObject(mutex,1000);
/* both errors should never occur */
if (WAIT_TIMEOUT == result)
{
ERR("WaitForSingleObject timed out\n");
return FALSE;
}
if (WAIT_FAILED == result)
{
ERR("WaitForSingleObject failed - error %li\n", GetLastError());
return FALSE;
}
return TRUE;
}
/******************************************************************************
* IncrementInstanceId
*
* generic routine to increment the max instance Id and allocate a new application instance
*
******************************************************************************
*
* Change History
*
* Vn Date Author Comment
*
* 1.0 Jan 1999 Keith Matthews Initial version
*
*/
DWORD IncrementInstanceId( DDE_HANDLE_ENTRY *this_instance)
{
SECURITY_ATTRIBUTES s_attrib;
/* Need to set up Mutex in case it is not already present */
/* increment handle count & get value */
if ( !inst_count_mutex )
{
s_attrib.bInheritHandle = TRUE;
s_attrib.lpSecurityDescriptor = NULL;
s_attrib.nLength = sizeof(s_attrib);
inst_count_mutex = CreateMutexA(&s_attrib,0,DDEInstanceAccess); /* 1st time through */
}
if ( inst_count_mutex )
{
if ( !WaitForMutex(inst_count_mutex) )
{
return DMLERR_SYS_ERROR;
}
} else
{
ERR("CreateMutex failed - inst_count %li\n",GetLastError());
Release_reserved_mutex (handle_mutex,"handle_mutex",0,1,this_instance);
return DMLERR_SYS_ERROR;
}
DDE_Max_Assigned_Instance++;
this_instance->Instance_id = DDE_Max_Assigned_Instance;
TRACE("New instance id %ld allocated\n",DDE_Max_Assigned_Instance);
if (Release_reserved_mutex(inst_count_mutex,"instance_count",1,0,this_instance)) return DMLERR_SYS_ERROR;
return DMLERR_NO_ERROR;
}
/******************************************************************************
* FindNotifyMonitorCallbacks
*
* Routine to find instances that need to be notified via their callback
* of some event they are monitoring
*
******************************************************************************
*
* Change History
*
* Vn Date Author Comment
*
* 1.0 May 1999 Keith Matthews Initial Version
*
*/
void FindNotifyMonitorCallbacks(DWORD ThisInstance, DWORD DdeEvent )
{
DDE_HANDLE_ENTRY *InstanceHandle;
InstanceHandle = DDE_Handle_Table_Base;
while ( InstanceHandle != NULL )
{
if ( (InstanceHandle->Monitor ) && InstanceHandle->Instance_id == ThisInstance )
{
/* Found an instance registered as monitor and is not ourselves
* use callback to notify where appropriate
*/
}
InstanceHandle = InstanceHandle->Next_Entry;
}
}
/******************************************************************************
* DdeReserveAtom
*
* Routine to make an extra Add on an atom to reserve it a bit longer
*
******************************************************************************
*
* Change History
*
* Vn Date Author Comment
*
* 1.0 Jun 1999 Keith Matthews Initial Version
*
*/
static void DdeReserveAtom( DDE_HANDLE_ENTRY * reference_inst,HSZ hsz)
{
if ( reference_inst->Unicode)
{
WCHAR SNameBuffer[MAX_BUFFER_LEN];
GlobalGetAtomNameW(hsz,SNameBuffer,MAX_BUFFER_LEN);
GlobalAddAtomW(SNameBuffer);
} else {
CHAR SNameBuffer[MAX_BUFFER_LEN];
GlobalGetAtomNameA(hsz,SNameBuffer,MAX_BUFFER_LEN);
GlobalAddAtomA(SNameBuffer);
}
}
/******************************************************************************
* DdeReleaseAtom
*
* Routine to make a delete on an atom to release it a bit sooner
*
******************************************************************************
*
* Change History
*
* Vn Date Author Comment
*
* 1.0 Jun 1999 Keith Matthews Initial Version
*
*/
static void DdeReleaseAtom( DDE_HANDLE_ENTRY * reference_inst,HSZ hsz)
{
GlobalDeleteAtom( hsz );
}
/******************************************************************************
* DdeInitialize16 (DDEML.2)
*/
UINT16 WINAPI DdeInitialize16( LPDWORD pidInst, PFNCALLBACK16 pfnCallback,
DWORD afCmd, DWORD ulRes)
{
TRACE("DdeInitialize16 called - calling DdeInitializeA\n");
return (UINT16)DdeInitializeA(pidInst,(PFNCALLBACK)pfnCallback,
afCmd, ulRes);
}
/******************************************************************************
* DdeInitializeA (USER32.@)
*/
UINT WINAPI DdeInitializeA( LPDWORD pidInst, PFNCALLBACK pfnCallback,
DWORD afCmd, DWORD ulRes )
{
TRACE("DdeInitializeA called - calling DdeInitializeW\n");
return DdeInitializeW(pidInst,pfnCallback,afCmd,ulRes);
}
/******************************************************************************
* DdeInitializeW [USER32.@]
* Registers an application with the DDEML
*
* PARAMS
* pidInst [I] Pointer to instance identifier
* pfnCallback [I] Pointer to callback function
* afCmd [I] Set of command and filter flags
* ulRes [I] Reserved
*
* RETURNS
* Success: DMLERR_NO_ERROR
* Failure: DMLERR_DLL_USAGE, DMLERR_INVALIDPARAMETER, DMLERR_SYS_ERROR
*
******************************************************************************
*
* Change History
*
* Vn Date Author Comment
*
* 1.0 Pre 1998 Alexandre/Len Initial Stub
* 1.1 Jan 1999 Keith Matthews Initial (near-)complete version
* 1.2 Mar 1999 Keith Matthews Corrected Heap handling, CreateMutex failure handling
*
*/
UINT WINAPI DdeInitializeW( LPDWORD pidInst, PFNCALLBACK pfnCallback,
DWORD afCmd, DWORD ulRes )
{
/* probably not really capable of handling multiple processes, but should handle
* multiple instances within one process */
SECURITY_ATTRIBUTES *s_att= NULL;
SECURITY_ATTRIBUTES s_attrib;
DWORD err_no = 0;
DDE_HANDLE_ENTRY *this_instance;
DDE_HANDLE_ENTRY *reference_inst;
s_att = &s_attrib;
if( ulRes )
{
ERR("Reserved value not zero? What does this mean?\n");
FIXME("(%p,%p,0x%lx,%ld): stub\n", pidInst, pfnCallback,
afCmd,ulRes);
/* trap this and no more until we know more */
return DMLERR_NO_ERROR;
}
if (!pfnCallback )
{
/* this one may be wrong - MS dll seems to accept the condition,
leave this until we find out more !! */
/* can't set up the instance with nothing to act as a callback */
TRACE("No callback provided\n");
return DMLERR_INVALIDPARAMETER; /* might be DMLERR_DLL_USAGE */
}
/* grab enough heap for one control struct - not really necessary for re-initialise
* but allows us to use same validation routines */
this_instance= (DDE_HANDLE_ENTRY*)HeapAlloc( GetProcessHeap(), 0, sizeof(DDE_HANDLE_ENTRY) );
if ( this_instance == NULL )
{
/* catastrophe !! warn user & abort */
ERR("Instance create failed - out of memory\n");
return DMLERR_SYS_ERROR;
}
this_instance->Next_Entry = NULL;
this_instance->Monitor=(afCmd|APPCLASS_MONITOR);
/* messy bit, spec implies that 'Client Only' can be set in 2 different ways, catch 1 here */
this_instance->Client_only=afCmd&APPCMD_CLIENTONLY;
this_instance->Instance_id = *pidInst; /* May need to add calling proc Id */
this_instance->CallBack=*pfnCallback;
this_instance->Txn_count=0;
this_instance->Unicode = TRUE;
this_instance->Win16 = FALSE;
this_instance->Node_list = NULL; /* node will be added later */
this_instance->Monitor_flags = afCmd & MF_MASK;
this_instance->ServiceNames = NULL;
/* isolate CBF flags in one go, expect this will go the way of all attempts to be clever !! */
this_instance->CBF_Flags=afCmd^((afCmd&MF_MASK)|((afCmd&APPCMD_MASK)|(afCmd&APPCLASS_MASK)));
if ( ! this_instance->Client_only )
{
/* Check for other way of setting Client-only !! */
this_instance->Client_only=(this_instance->CBF_Flags&CBF_FAIL_ALLSVRXACTIONS)
==CBF_FAIL_ALLSVRXACTIONS;
}
TRACE("instance created - checking validity \n");
if( *pidInst == 0 ) {
/* Initialisation of new Instance Identifier */
TRACE("new instance, callback %p flags %lX\n",pfnCallback,afCmd);
if ( DDE_Max_Assigned_Instance == 0 )
{
/* Nothing has been initialised - exit now ! can return TRUE since effect is the same */
/* Need to set up Mutex in case it is not already present */
s_att->bInheritHandle = TRUE;
s_att->lpSecurityDescriptor = NULL;
s_att->nLength = sizeof(s_att);
handle_mutex = CreateMutexA(s_att,0,DDEHandleAccess);
if ( !handle_mutex ) {
ERR("CreateMutex failed - handle list %li\n",GetLastError());
HeapFree(GetProcessHeap(), 0, this_instance);
return DMLERR_SYS_ERROR;
}
}
if ( !WaitForMutex(handle_mutex) )
{
return DMLERR_SYS_ERROR;
}
TRACE("Handle Mutex created/reserved\n");
if (DDE_Handle_Table_Base == NULL )
{
/* can't be another instance in this case, assign to the base pointer */
DDE_Handle_Table_Base= this_instance;
/* since first must force filter of XTYP_CONNECT and XTYP_WILDCONNECT for
* present
* ------------------------------- NOTE NOTE NOTE --------------------------
*
* the manual is not clear if this condition
* applies to the first call to DdeInitialize from an application, or the
* first call for a given callback !!!
*/
this_instance->CBF_Flags=this_instance->CBF_Flags|APPCMD_FILTERINITS;
TRACE("First application instance detected OK\n");
/* allocate new instance ID */
if ((err_no = IncrementInstanceId( this_instance)) ) return err_no;
} else {
/* really need to chain the new one in to the latest here, but after checking conditions
* such as trying to start a conversation from an application trying to monitor */
reference_inst = DDE_Handle_Table_Base;
TRACE("Subsequent application instance - starting checks\n");
while ( reference_inst->Next_Entry != NULL )
{
/*
* This set of tests will work if application uses same instance Id
* at application level once allocated - which is what manual implies
* should happen. If someone tries to be
* clever (lazy ?) it will fail to pick up that later calls are for
* the same application - should we trust them ?
*/
if ( this_instance->Instance_id == reference_inst->Instance_id)
{
/* Check 1 - must be same Client-only state */
if ( this_instance->Client_only != reference_inst->Client_only)
{
if ( Release_reserved_mutex(handle_mutex,"handle_mutex",0,1,this_instance))
return DMLERR_SYS_ERROR;
return DMLERR_DLL_USAGE;
}
/* Check 2 - cannot use 'Monitor' with any non-monitor modes */
if ( this_instance->Monitor != reference_inst->Monitor)
{
if ( Release_reserved_mutex(handle_mutex,"handle_mutex",0,1,this_instance))
return DMLERR_SYS_ERROR;
return DMLERR_INVALIDPARAMETER;
}
/* Check 3 - must supply different callback address */
if ( this_instance->CallBack == reference_inst->CallBack)
{
if ( Release_reserved_mutex(handle_mutex,"handle_mutex",0,1,this_instance))
return DMLERR_SYS_ERROR;
return DMLERR_DLL_USAGE;
}
}
reference_inst = reference_inst->Next_Entry;
}
/* All cleared, add to chain */
TRACE("Application Instance checks finished\n");
if ((err_no = IncrementInstanceId( this_instance)) ) return err_no;
if ( Release_reserved_mutex(handle_mutex,"handle_mutex",0,0,this_instance)) return DMLERR_SYS_ERROR;
reference_inst->Next_Entry = this_instance;
}
*pidInst = this_instance->Instance_id;
TRACE("New application instance processing finished OK\n");
} else {
/* Reinitialisation situation --- FIX */
TRACE("reinitialisation of (%p,%p,0x%lx,%ld): stub\n",pidInst,pfnCallback,afCmd,ulRes);
if ( !WaitForMutex(handle_mutex) )
{
HeapFree(GetProcessHeap(), 0, this_instance);
return DMLERR_SYS_ERROR;
}
if (DDE_Handle_Table_Base == NULL )
{
if ( Release_reserved_mutex(handle_mutex,"handle_mutex",0,1,this_instance)) return DMLERR_SYS_ERROR;
return DMLERR_DLL_USAGE;
}
HeapFree(GetProcessHeap(), 0, this_instance); /* finished - release heap space used as work store */
/* can't reinitialise if we have initialised nothing !! */
reference_inst = DDE_Handle_Table_Base;
/* must first check if we have been given a valid instance to re-initialise !! how do we do that ? */
/*
* MS allows initialisation without specifying a callback, should we allow addition of the
* callback by a later call to initialise ? - if so this lot will have to change
*/
while ( reference_inst->Next_Entry != NULL )
{
if ( *pidInst == reference_inst->Instance_id && pfnCallback == reference_inst->CallBack )
{
/* Check 1 - cannot change client-only mode if set via APPCMD_CLIENTONLY */
if ( reference_inst->Client_only )
{
if ((reference_inst->CBF_Flags & CBF_FAIL_ALLSVRXACTIONS) != CBF_FAIL_ALLSVRXACTIONS)
{
/* i.e. Was set to Client-only and through APPCMD_CLIENTONLY */
if ( ! ( afCmd & APPCMD_CLIENTONLY))
{
if ( Release_reserved_mutex(handle_mutex,"handle_mutex",0,1,this_instance))
return DMLERR_SYS_ERROR;
return DMLERR_DLL_USAGE;
}
}
}
/* Check 2 - cannot change monitor modes */
if ( this_instance->Monitor != reference_inst->Monitor)
{
if ( Release_reserved_mutex(handle_mutex,"handle_mutex",0,1,this_instance))
return DMLERR_SYS_ERROR;
return DMLERR_DLL_USAGE;
}
/* Check 3 - trying to set Client-only via APPCMD when not set so previously */
if (( afCmd&APPCMD_CLIENTONLY) && ! reference_inst->Client_only )
{
if ( Release_reserved_mutex(handle_mutex,"handle_mutex",0,1,this_instance))
return DMLERR_SYS_ERROR;
return DMLERR_DLL_USAGE;
}
break;
}
reference_inst = reference_inst->Next_Entry;
}
if ( reference_inst->Next_Entry == NULL )
{
/* Crazy situation - trying to re-initialize something that has not beeen initialized !!
*
* Manual does not say what we do, cannot return DMLERR_NOT_INITIALIZED so what ?
*/
if ( Release_reserved_mutex(handle_mutex,"handle_mutex",0,1,this_instance))
return DMLERR_SYS_ERROR;
return DMLERR_INVALIDPARAMETER;
}
/* All checked - change relevant flags */
reference_inst->CBF_Flags = this_instance->CBF_Flags;
reference_inst->Client_only = this_instance->Client_only;
reference_inst->Monitor_flags = this_instance->Monitor_flags;
if ( Release_reserved_mutex(handle_mutex,"handle_mutex",0,1,this_instance))
return DMLERR_SYS_ERROR;
}
return DMLERR_NO_ERROR;
}
/*****************************************************************
* DdeUninitialize16 (DDEML.3)
*/
BOOL16 WINAPI DdeUninitialize16( DWORD idInst )
{
FIXME(" stub calling DdeUninitialize\n");
return (BOOL16)DdeUninitialize( idInst );
}
/*****************************************************************
* DdeUninitialize [USER32.@] Frees DDEML resources
*
* PARAMS
* idInst [I] Instance identifier
*
* RETURNS
* Success: TRUE
* Failure: FALSE
*/
BOOL WINAPI DdeUninitialize( DWORD idInst )
{
/* Stage one - check if we have a handle for this instance
*/
SECURITY_ATTRIBUTES *s_att= NULL;
SECURITY_ATTRIBUTES s_attrib;
DDE_HANDLE_ENTRY *this_instance;
DDE_HANDLE_ENTRY *reference_inst;
s_att = &s_attrib;
if ( DDE_Max_Assigned_Instance == 0 )
{
/* Nothing has been initialised - exit now ! can return TRUE since effect is the same */
return TRUE;
}
if ( !WaitForMutex(handle_mutex) )
{
return DMLERR_SYS_ERROR;
}
TRACE("Handle Mutex created/reserved\n");
/* First check instance
*/
this_instance = Find_Instance_Entry(idInst);
if ( this_instance == NULL )
{
if ( Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,this_instance)) return FALSE;
/*
* Needs something here to record NOT_INITIALIZED ready for DdeGetLastError
*/
return FALSE;
}
FIXME("(%ld): partial stub\n", idInst);
/* FIXME ++++++++++++++++++++++++++++++++++++++++++
* Needs to de-register all service names
*
*/
/* Free the nodes that were not freed by this instance
* and remove the nodes from the list of HSZ nodes.
*/
FreeAndRemoveHSZNodes( idInst, this_instance );
/* OK now delete the instance handle itself */
if ( DDE_Handle_Table_Base == this_instance )
{
/* special case - the first/only entry
*/
DDE_Handle_Table_Base = this_instance->Next_Entry;
} else
{
/* general case
*/
reference_inst = DDE_Handle_Table_Base;
while ( reference_inst->Next_Entry != this_instance )
{
reference_inst = this_instance->Next_Entry;
}
reference_inst->Next_Entry = this_instance->Next_Entry;
}
/* release the mutex and the heap entry
*/
if ( Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,TRUE,this_instance))
{
/* should record something here, but nothing left to hang it from !!
*/
return FALSE;
}
return TRUE;
}
/*****************************************************************
* DdeConnectList16 [DDEML.4]
*/
HCONVLIST WINAPI DdeConnectList16( DWORD idInst, HSZ hszService, HSZ hszTopic,
HCONVLIST hConvList, LPCONVCONTEXT16 pCC )
{
return DdeConnectList(idInst, hszService, hszTopic, hConvList,
(LPCONVCONTEXT)pCC);
}
/******************************************************************************
* 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 1;
}
/*****************************************************************
* DdeQueryNextServer16 [DDEML.5]
*/
HCONV WINAPI DdeQueryNextServer16( HCONVLIST hConvList, HCONV hConvPrev )
{
return DdeQueryNextServer(hConvList, hConvPrev);
}
/*****************************************************************
* DdeQueryNextServer [USER32.@]
*/
HCONV WINAPI DdeQueryNextServer( HCONVLIST hConvList, HCONV hConvPrev )
{
FIXME("(%d,%d): stub\n",hConvList,hConvPrev);
return 0;
}
/*****************************************************************
* DdeQueryStringA [USER32.@]
*
*****************************************************************
*
* Change History
*
* Vn Date Author Comment
*
* 1.0 Dec 1998 Corel/Macadamian Initial version
* 1.1 Mar 1999 Keith Matthews Added links to instance table and related processing
*
*/
DWORD WINAPI DdeQueryStringA(DWORD idInst, HSZ hsz, LPSTR psz, DWORD cchMax, INT iCodePage)
{
DWORD ret = 0;
CHAR pString[MAX_BUFFER_LEN];
DDE_HANDLE_ENTRY *reference_inst;
FIXME(
"(%ld, 0x%x, %p, %ld, %d): partial stub\n",
idInst,
hsz,
psz,
cchMax,
iCodePage);
if ( DDE_Max_Assigned_Instance == 0 )
{
/* Nothing has been initialised - exit now ! */
/* needs something for DdeGetLAstError even if the manual doesn't say so */
return FALSE;
}
if ( !WaitForMutex(handle_mutex) )
{
return FALSE;
}
TRACE("Handle Mutex created/reserved\n");
/* First check instance
*/
reference_inst = Find_Instance_Entry(idInst);
if ( reference_inst == NULL )
{
if ( Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,reference_inst)) return FALSE;
/*
Needs something here to record NOT_INITIALIZED ready for DdeGetLastError
*/
return FALSE;
}
if( iCodePage == CP_WINANSI )
{
/* If psz is null, we have to return only the length
* of the string.
*/
if( psz == NULL )
{
psz = pString;
cchMax = MAX_BUFFER_LEN;
}
ret = GlobalGetAtomNameA( hsz, (LPSTR)psz, cchMax );
} else {
Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,reference_inst);
}
TRACE("returning pointer\n");
return ret;
}
/*****************************************************************
* DdeQueryStringW [USER32.@]
*
*****************************************************************
*
* Change History
*
* Vn Date Author Comment
*
* 1.0 Dec 1998 Corel/Macadamian Initial version
*
*/
DWORD WINAPI DdeQueryStringW(DWORD idInst, HSZ hsz, LPWSTR psz, DWORD cchMax, INT iCodePage)
{
DWORD ret = 0;
WCHAR pString[MAX_BUFFER_LEN];
int factor = 1;
FIXME(
"(%ld, 0x%x, %p, %ld, %d): stub\n",
idInst,
hsz,
psz,
cchMax,
iCodePage);
if( iCodePage == CP_WINUNICODE )
{
/* If psz is null, we have to return only the length
* of the string.
*/
if( psz == NULL )
{
psz = pString;
cchMax = MAX_BUFFER_LEN;
/* Note: According to documentation if the psz parameter
* was NULL this API must return the length of the string in bytes.
*/
factor = (int) sizeof(WCHAR)/sizeof(BYTE);
}
ret = GlobalGetAtomNameW( hsz, (LPWSTR)psz, cchMax ) * factor;
}
return ret;
}
/*****************************************************************
*
* DdeQueryString16 (DDEML.23)
*
******************************************************************
*
* Change History
*
* Vn Date Author Comment
*
* 1.0 March 1999 K Matthews stub only
*/
DWORD WINAPI DdeQueryString16(DWORD idInst, HSZ hsz, LPSTR lpsz, DWORD cchMax, INT16 codepage)
{
FIXME("(%ld, 0x%x, %p, %ld, %d): stub \n",
idInst,
hsz,
lpsz,
cchMax,
codepage);
return 0;
}
/*****************************************************************
* DdeDisconnectList (DDEML.6)
*/
BOOL16 WINAPI DdeDisconnectList16( HCONVLIST hConvList )
{
return (BOOL16)DdeDisconnectList(hConvList);
}
/******************************************************************************
* 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;
}
/*****************************************************************
* DdeConnect16 (DDEML.7)
*/
HCONV WINAPI DdeConnect16( DWORD idInst, HSZ hszService, HSZ hszTopic,
LPCONVCONTEXT16 pCC )
{
FIXME("empty stub\n" );
return 0;
}
/*****************************************************************
* DdeConnect (USER32.@)
*/
HCONV WINAPI DdeConnect( DWORD idInst, HSZ hszService, HSZ hszTopic,
LPCONVCONTEXT pCC )
{
FIXME("(0x%lx,%d,%d,%p): stub\n",idInst,hszService,hszTopic,
pCC);
return 0;
}
/*****************************************************************
* DdeDisconnect16 (DDEML.8)
*/
BOOL16 WINAPI DdeDisconnect16( HCONV hConv )
{
return (BOOL16)DdeDisconnect( hConv );
}
/*****************************************************************
* DdeSetUserHandle16 (DDEML.10)
*/
BOOL16 WINAPI DdeSetUserHandle16( HCONV hConv, DWORD id, DWORD hUser )
{
FIXME("(%d,%ld,%ld): stub\n",hConv,id, hUser );
return 0;
}
/*****************************************************************
* DdeCreateDataHandle16 (DDEML.14)
*/
HDDEDATA WINAPI DdeCreateDataHandle16( DWORD idInst, LPBYTE pSrc, DWORD cb,
DWORD cbOff, HSZ hszItem, UINT16 wFmt,
UINT16 afCmd )
{
return DdeCreateDataHandle(idInst,
pSrc,
cb,
cbOff,
hszItem,
wFmt,
afCmd);
}
/*****************************************************************
* DdeCreateDataHandle (USER32.@)
*/
HDDEDATA WINAPI DdeCreateDataHandle( DWORD idInst, LPBYTE pSrc, DWORD cb,
DWORD cbOff, HSZ hszItem, UINT wFmt,
UINT afCmd )
{
FIXME(
"(%ld,%p,%ld,%ld,0x%x,%d,%d): stub\n",
idInst,
pSrc,
cb,
cbOff,
hszItem,
wFmt,
afCmd );
return 0;
}
/*****************************************************************
* DdeDisconnect (USER32.@)
*/
BOOL WINAPI DdeDisconnect( HCONV hConv )
{
FIXME("empty stub\n" );
return 0;
}
/*****************************************************************
* DdeReconnect (DDEML.37)
* DdeReconnect (USER32.@)
*/
HCONV WINAPI DdeReconnect( HCONV hConv )
{
FIXME("empty stub\n" );
return 0;
}
/*****************************************************************
* DdeCreateStringHandle16 (DDEML.21)
*
*****************************************************************
*
* Change History
*
* Vn Date Author Comment
*
* 1.0 ? ? basic stub
* 1.1 June 1999 Keith Matthews amended onward call to supply default
* code page if none supplied by caller
*/
HSZ WINAPI DdeCreateStringHandle16( DWORD idInst, LPCSTR str, INT16 codepage )
{
if ( codepage )
{
return DdeCreateStringHandleA( idInst, str, codepage );
} else {
TRACE("Default codepage supplied\n");
return DdeCreateStringHandleA( idInst, str, CP_WINANSI);
}
}
/*****************************************************************
* DdeCreateStringHandleA [USER32.@]
*
* RETURNS
* Success: String handle
* Failure: 0
*
*****************************************************************
*
* Change History
*
* Vn Date Author Comment
*
* 1.0 Dec 1998 Corel/Macadamian Initial version
* 1.1 Mar 1999 Keith Matthews Added links to instance table and related processing
*
*/
HSZ WINAPI DdeCreateStringHandleA( DWORD idInst, LPCSTR psz, INT codepage )
{
HSZ hsz = 0;
DDE_HANDLE_ENTRY *reference_inst;
TRACE("(%ld,%s,%d): partial stub\n",idInst,debugstr_a(psz),codepage);
if ( DDE_Max_Assigned_Instance == 0 )
{
/* Nothing has been initialised - exit now ! can return FALSE since effect is the same */
return FALSE;
}
if ( !WaitForMutex(handle_mutex) )
{
return DMLERR_SYS_ERROR;
}
TRACE("Handle Mutex created/reserved\n");
/* First check instance
*/
reference_inst = Find_Instance_Entry(idInst);
if ( reference_inst == NULL )
{
if ( Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,reference_inst)) return 0;
/*
Needs something here to record NOT_INITIALIZED ready for DdeGetLastError
*/
return 0;
}
if (codepage==CP_WINANSI)
{
hsz = GlobalAddAtomA (psz);
/* Save the handle so we know to clean it when
* uninitialize is called.
*/
TRACE("added atom %s with HSZ 0x%x, \n",debugstr_a(psz),hsz);
InsertHSZNode( hsz, reference_inst );
if ( Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,reference_inst))
{
reference_inst->Last_Error = DMLERR_SYS_ERROR;
return 0;
}
TRACE("Returning pointer\n");
return hsz;
} else {
Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,reference_inst);
}
TRACE("Returning error\n");
return 0;
}
/******************************************************************************
* DdeCreateStringHandleW [USER32.@] Creates handle to identify string
*
* RETURNS
* Success: String handle
* Failure: 0
*
*****************************************************************
*
* Change History
*
* Vn Date Author Comment
*
* 1.0 Dec 1998 Corel/Macadamian Initial version
* 1.1 Mar 1999 Keith Matthews Added links to instance table and related processing
*
*/
HSZ WINAPI DdeCreateStringHandleW(
DWORD idInst, /* [in] Instance identifier */
LPCWSTR psz, /* [in] Pointer to string */
INT codepage) /* [in] Code page identifier */
{
DDE_HANDLE_ENTRY *reference_inst;
HSZ hsz = 0;
TRACE("(%ld,%s,%d): partial stub\n",idInst,debugstr_w(psz),codepage);
if ( DDE_Max_Assigned_Instance == 0 )
{
/* Nothing has been initialised - exit now ! can return FALSE since effect is the same */
return FALSE;
}
if ( !WaitForMutex(handle_mutex) )
{
return DMLERR_SYS_ERROR;
}
TRACE("CreateString - Handle Mutex created/reserved\n");
/* First check instance
*/
reference_inst = Find_Instance_Entry(idInst);
if ( reference_inst == NULL )
{
if ( Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,reference_inst)) return 0;
/*
Needs something here to record NOT_INITIALIZED ready for DdeGetLastError
*/
return 0;
}
FIXME("(%ld,%s,%d): partial stub\n",idInst,debugstr_w(psz),codepage);
if (codepage==CP_WINUNICODE)
/*
Should we be checking this against the unicode/ascii nature of the call to DdeInitialize ?
*/
{
hsz = GlobalAddAtomW (psz);
/* Save the handle so we know to clean it when
* uninitialize is called.
*/
InsertHSZNode( hsz, reference_inst );
if ( Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,reference_inst))
{
reference_inst->Last_Error = DMLERR_SYS_ERROR;
return 0;
}
TRACE("Returning pointer\n");
return hsz;
} else {
Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,reference_inst);
}
TRACE("Returning error\n");
return 0;
}
/*****************************************************************
* DdeFreeStringHandle16 (DDEML.22)
*/
BOOL16 WINAPI DdeFreeStringHandle16( DWORD idInst, HSZ hsz )
{
FIXME("idInst %ld hsz 0x%x\n",idInst,hsz);
return (BOOL)DdeFreeStringHandle( idInst, hsz );
}
/*****************************************************************
* DdeFreeStringHandle (USER32.@)
* RETURNS: success: nonzero
* fail: zero
*
*****************************************************************
*
* Change History
*
* Vn Date Author Comment
*
* 1.0 Dec 1998 Corel/Macadamian Initial version
* 1.1 Apr 1999 Keith Matthews Added links to instance table and related processing
*
*/
BOOL WINAPI DdeFreeStringHandle( DWORD idInst, HSZ hsz )
{
DDE_HANDLE_ENTRY *reference_inst;
TRACE("(%ld,%d): \n",idInst,hsz);
if ( DDE_Max_Assigned_Instance == 0 )
{
/* Nothing has been initialised - exit now ! can return TRUE since effect is the same */
return TRUE;
}
if ( !WaitForMutex(handle_mutex) )
{
return DMLERR_SYS_ERROR;
}
TRACE("Handle Mutex created/reserved\n");
/* First check instance
*/
reference_inst = Find_Instance_Entry(idInst);
if ( (reference_inst == NULL) || (reference_inst->Node_list == NULL))
{
if ( Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,reference_inst)) return TRUE;
/* Nothing has been initialised - exit now ! can return TRUE since effect is the same */
return TRUE;
}
/* Remove the node associated with this HSZ.
*/
RemoveHSZNode( hsz , reference_inst);
/* Free the string associated with this HSZ.
*/
Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,reference_inst);
return GlobalDeleteAtom (hsz) ? 0 : hsz;
}
/*****************************************************************
* DdeFreeDataHandle16 (DDEML.19)
*/
BOOL16 WINAPI DdeFreeDataHandle16( HDDEDATA hData )
{
return (BOOL)DdeFreeDataHandle( hData );
}
/*****************************************************************
* DdeFreeDataHandle (USER32.@)
*/
BOOL WINAPI DdeFreeDataHandle( HDDEDATA hData )
{
FIXME("empty stub\n" );
return TRUE;
}
/*****************************************************************
* DdeKeepStringHandle16 (DDEML.24)
*/
BOOL16 WINAPI DdeKeepStringHandle16( DWORD idInst, HSZ hsz )
{
return (BOOL)DdeKeepStringHandle( idInst, hsz );
}
/*****************************************************************
* DdeKeepStringHandle (USER32.@)
*
* RETURNS: success: nonzero
* fail: zero
*
*****************************************************************
*
* Change History
*
* Vn Date Author Comment
*
* 1.0 ? ? Stub only
* 1.1 Jun 1999 Keith Matthews First cut implementation
*
*/
BOOL WINAPI DdeKeepStringHandle( DWORD idInst, HSZ hsz )
{
DDE_HANDLE_ENTRY *reference_inst;
TRACE("(%ld,%d): \n",idInst,hsz);
if ( DDE_Max_Assigned_Instance == 0 )
{
/* Nothing has been initialised - exit now ! can return FALSE since effect is the same */
return FALSE;
}
if ( !WaitForMutex(handle_mutex) )
{
return FALSE;
}
TRACE("Handle Mutex created/reserved\n");
/* First check instance
*/
reference_inst = Find_Instance_Entry(idInst);
if ( (reference_inst == NULL) || (reference_inst->Node_list == NULL))
{
if ( Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,reference_inst)) return FALSE;
/* Nothing has been initialised - exit now ! can return FALSE since effect is the same */
return FALSE;
return FALSE;
}
DdeReserveAtom(reference_inst,hsz);
Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,reference_inst);
return TRUE;
}
/*****************************************************************
* DdeClientTransaction16 (DDEML.11)
*/
HDDEDATA WINAPI DdeClientTransaction16( LPVOID pData, DWORD cbData,
HCONV hConv, HSZ hszItem, UINT16 wFmt,
UINT16 wType, DWORD dwTimeout,
LPDWORD pdwResult )
{
return DdeClientTransaction( (LPBYTE)pData, cbData, hConv, hszItem,
wFmt, wType, dwTimeout, pdwResult );
}
/*****************************************************************
* DdeClientTransaction (USER32.@)
*/
HDDEDATA WINAPI DdeClientTransaction( LPBYTE pData, DWORD cbData,
HCONV hConv, HSZ hszItem, UINT wFmt,
UINT wType, DWORD dwTimeout,
LPDWORD pdwResult )
{
FIXME("empty stub\n" );
return 0;
}
/*****************************************************************
*
* DdeAbandonTransaction16 (DDEML.12)
*
*/
BOOL16 WINAPI DdeAbandonTransaction16( DWORD idInst, HCONV hConv,
DWORD idTransaction )
{
FIXME("empty stub\n" );
return TRUE;
}
/*****************************************************************
*
* DdeAbandonTransaction (USER32.@)
*
******************************************************************
*
* Change History
*
* Vn Date Author Comment
*
* 1.0 March 1999 K Matthews stub only
*/
BOOL WINAPI DdeAbandonTransaction( DWORD idInst, HCONV hConv,
DWORD idTransaction )
{
FIXME("empty stub\n" );
return TRUE;
}
/*****************************************************************
* DdePostAdvise16 [DDEML.13]
*/
BOOL16 WINAPI DdePostAdvise16( DWORD idInst, HSZ hszTopic, HSZ hszItem )
{
return (BOOL16)DdePostAdvise(idInst, hszTopic, hszItem);
}
/******************************************************************************
* DdePostAdvise [USER32.@] Send transaction to DDE callback function.
*
* RETURNS
* Success: TRUE
* Failure: FALSE
*/
BOOL WINAPI DdePostAdvise(
DWORD idInst, /* [in] Instance identifier */
HSZ hszTopic, /* [in] Handle to topic name string */
HSZ hszItem) /* [in] Handle to item name string */
{
FIXME("(%ld,%d,%d): stub\n",idInst,hszTopic,hszItem);
return TRUE;
}
/*****************************************************************
* DdeAddData16 (DDEML.15)
*/
HDDEDATA WINAPI DdeAddData16( HDDEDATA hData, LPBYTE pSrc, DWORD cb,
DWORD cbOff )
{
FIXME("empty stub\n" );
return 0;
}
/*****************************************************************
*
* DdeAddData (USER32.@)
*
******************************************************************
*
* Change History
*
* Vn Date Author Comment
*
* 1.0 March 1999 K Matthews stub only
*/
HDDEDATA WINAPI DdeAddData( HDDEDATA hData, LPBYTE pSrc, DWORD cb,
DWORD cbOff )
{
FIXME("empty stub\n" );
return 0;
}
/*****************************************************************
*
* DdeImpersonateClient (USER32.@)
*
******************************************************************
*
* Change History
*
* Vn Date Author Comment
*
* 1.0 March 1999 K Matthews stub only
*/
BOOL WINAPI DdeImpersonateClient( HCONV hConv)
{
FIXME("empty stub\n" );
return TRUE;
}
/*****************************************************************
*
* DdeSetQualityOfService (USER32.@)
*
******************************************************************
*
* Change History
*
* Vn Date Author Comment
*
* 1.0 March 1999 K Matthews stub only
*/
BOOL WINAPI DdeSetQualityOfService( HWND hwndClient, CONST SECURITY_QUALITY_OF_SERVICE *pqosNew,
PSECURITY_QUALITY_OF_SERVICE pqosPrev)
{
FIXME("empty stub\n" );
return TRUE;
}
/*****************************************************************
*
* DdeSetUserHandle (USER32.@)
*
******************************************************************
*
* Change History
*
* Vn Date Author Comment
*
* 1.0 March 1999 K Matthews stub only
*/
BOOL WINAPI DdeSetUserHandle( HCONV hConv, DWORD id, DWORD hUser)
{
FIXME("empty stub\n" );
return TRUE;
}
/******************************************************************************
* DdeGetData [USER32.@] Copies data from DDE object to local buffer
*
* RETURNS
* Size of memory object associated with handle
*/
DWORD WINAPI DdeGetData(
HDDEDATA hData, /* [in] Handle to DDE object */
LPBYTE pDst, /* [in] Pointer to destination buffer */
DWORD cbMax, /* [in] Amount of data to copy */
DWORD cbOff) /* [in] Offset to beginning of data */
{
FIXME("(%d,%p,%ld,%ld): stub\n",hData,pDst,cbMax,cbOff);
return cbMax;
}
/*****************************************************************
* DdeGetData16 [DDEML.16]
*/
DWORD WINAPI DdeGetData16(
HDDEDATA hData,
LPBYTE pDst,
DWORD cbMax,
DWORD cbOff)
{
return DdeGetData(hData, pDst, cbMax, cbOff);
}
/*****************************************************************
* DdeAccessData16 (DDEML.17)
*/
LPBYTE WINAPI DdeAccessData16( HDDEDATA hData, LPDWORD pcbDataSize )
{
return DdeAccessData(hData, pcbDataSize);
}
/*****************************************************************
* DdeAccessData (USER32.@)
*/
LPBYTE WINAPI DdeAccessData( HDDEDATA hData, LPDWORD pcbDataSize )
{
FIXME("(%d,%p): stub\n", hData, pcbDataSize);
return 0;
}
/*****************************************************************
* DdeUnaccessData16 (DDEML.18)
*/
BOOL16 WINAPI DdeUnaccessData16( HDDEDATA hData )
{
return DdeUnaccessData(hData);
}
/*****************************************************************
* DdeUnaccessData (USER32.@)
*/
BOOL WINAPI DdeUnaccessData( HDDEDATA hData )
{
FIXME("(0x%x): stub\n", hData);
return 0;
}
/*****************************************************************
* DdeEnableCallback16 (DDEML.26)
*/
BOOL16 WINAPI DdeEnableCallback16( DWORD idInst, HCONV hConv, UINT16 wCmd )
{
return DdeEnableCallback(idInst, hConv, wCmd);
}
/*****************************************************************
* DdeEnableCallback (USER32.@)
*/
BOOL WINAPI DdeEnableCallback( DWORD idInst, HCONV hConv, UINT wCmd )
{
FIXME("(%ld, 0x%x, %d) stub\n", idInst, hConv, wCmd);
return 0;
}
/*****************************************************************
* DdeNameService16 (DDEML.27)
*/
HDDEDATA WINAPI DdeNameService16( DWORD idInst, HSZ hsz1, HSZ hsz2,
UINT16 afCmd )
{
return DdeNameService( idInst, hsz1, hsz2, afCmd );
}
/******************************************************************************
* DdeNameService [USER32.@] {Un}registers service name of DDE server
*
* PARAMS
* idInst [I] Instance identifier
* hsz1 [I] Handle to service name string
* hsz2 [I] Reserved
* afCmd [I] Service name flags
*
* RETURNS
* Success: Non-zero
* Failure: 0
*
*****************************************************************
*
* Change History
*
* Vn Date Author Comment
*
* 1.0 ? ? Stub
* 1.1 Apr 1999 Keith Matthews Added trap for non-existent instance (uninitialised instance 0
* used by some MS programs for unfathomable reasons)
* 1.2 May 1999 Keith Matthews Added parameter validation and basic service name handling.
* Still needs callback parts
*
*/
HDDEDATA WINAPI DdeNameService( DWORD idInst, HSZ hsz1, HSZ hsz2,
UINT afCmd )
{
ServiceNode* this_service, *reference_service ;
DDE_HANDLE_ENTRY *this_instance;
DDE_HANDLE_ENTRY *reference_inst;
this_service = NULL;
FIXME("(%ld,%d,%d,%d): stub\n",idInst,hsz1,hsz2,afCmd);
if ( DDE_Max_Assigned_Instance == 0 )
{
/* Nothing has been initialised - exit now !
* needs something for DdeGetLastError */
return 0L;
}
if ( !WaitForMutex(handle_mutex) )
{
return DMLERR_SYS_ERROR;
}
TRACE("Handle Mutex created/reserved\n");
/* First check instance
*/
reference_inst = Find_Instance_Entry(idInst);
this_instance = reference_inst;
if (reference_inst == NULL)
{
TRACE("Instance not found as initialised\n");
if ( Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,this_instance)) return TRUE;
/* Nothing has been initialised - exit now ! can return TRUE since effect is the same */
return FALSE;
}
if ( hsz2 != 0L )
{
/* Illegal, reserved parameter
*/
reference_inst->Last_Error = DMLERR_INVALIDPARAMETER;
Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,this_instance);
FIXME("Reserved parameter no-zero !!\n");
return FALSE;
}
if ( hsz1 == 0L )
{
/*
* General unregister situation
*/
if ( afCmd != DNS_UNREGISTER )
{
/* don't know if we should check this but it makes sense
* why supply REGISTER or filter flags if de-registering all
*/
TRACE("General unregister unexpected flags\n");
reference_inst->Last_Error = DMLERR_DLL_USAGE;
Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,this_instance);
return FALSE;
}
/* Loop to find all registered service and de-register them
*/
if ( reference_inst->ServiceNames == NULL )
{
/* None to unregister !!
*/
TRACE("General de-register - nothing registered\n");
reference_inst->Last_Error = DMLERR_DLL_USAGE;
Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,this_instance);
return FALSE;
} else
{
this_service = reference_inst->ServiceNames;
while ( this_service->next != NULL)
{
TRACE("general deregister - iteration\n");
reference_service = this_service;
this_service = this_service->next;
DdeReleaseAtom(reference_inst,reference_service->hsz);
HeapFree(GetProcessHeap(), 0, reference_service); /* finished - release heap space used as work store */
}
DdeReleaseAtom(reference_inst,this_service->hsz);
HeapFree(GetProcessHeap(), 0, this_service); /* finished - release heap space used as work store */
reference_inst->ServiceNames = NULL;
TRACE("General de-register - finished\n");
}
Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,this_instance);
return TRUE;
}
TRACE("Specific name action detected\n");
if ( afCmd & DNS_REGISTER )
{
/* Register new service name
*/
this_service = Find_Service_Name( hsz1, reference_inst );
if ( this_service )
ERR("Trying to register already registered service!\n");
else
{
TRACE("Adding service name\n");
DdeReserveAtom(reference_inst, hsz1);
this_service = (ServiceNode*)HeapAlloc( GetProcessHeap(), 0, sizeof(ServiceNode) );
this_service->hsz = hsz1;
this_service->FilterOn = TRUE;
this_service->next = reference_inst->ServiceNames;
reference_inst->ServiceNames = this_service;
}
}
if ( afCmd & DNS_UNREGISTER )
{
/* De-register service name
*/
ServiceNode **pServiceNode = &reference_inst->ServiceNames;
while ( *pServiceNode && (*pServiceNode)->hsz != hsz1 )
pServiceNode = &(*pServiceNode)->next;
this_service = *pServiceNode;
if ( !this_service )
ERR("Trying to de-register unregistered service!\n");
else
{
*pServiceNode = this_service->next;
DdeReleaseAtom(reference_inst,this_service->hsz);
HeapFree(GetProcessHeap(), 0, this_service);
}
}
if ( afCmd & DNS_FILTERON )
{
/* Set filter flags on to hold notifications of connection
*
* test coded this way as this is the default setting
*/
this_service = Find_Service_Name( hsz1, reference_inst );
if ( !this_service )
{
/* trying to filter where no service names !!
*/
reference_inst->Last_Error = DMLERR_DLL_USAGE;
Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,this_instance);
return FALSE;
} else
{
this_service->FilterOn = TRUE;
}
}
if ( afCmd & DNS_FILTEROFF )
{
/* Set filter flags on to hold notifications of connection
*/
this_service = Find_Service_Name( hsz1, reference_inst );
if ( !this_service )
{
/* trying to filter where no service names !!
*/
reference_inst->Last_Error = DMLERR_DLL_USAGE;
Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,this_instance);
return FALSE;
} else
{
this_service->FilterOn = FALSE;
}
}
Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,this_instance);
return TRUE;
}
/*****************************************************************
* DdeGetLastError16 (DDEML.20)
*/
UINT16 WINAPI DdeGetLastError16( DWORD idInst )
{
return (UINT16)DdeGetLastError( idInst );
}
/******************************************************************************
* DdeGetLastError [USER32.@] Gets most recent error code
*
* PARAMS
* idInst [I] Instance identifier
*
* RETURNS
* Last error code
*
*****************************************************************
*
* Change History
*
* Vn Date Author Comment
*
* 1.0 ? ? Stub
* 1.1 Apr 1999 Keith Matthews Added response for non-existent instance (uninitialised instance 0
* used by some MS programs for unfathomable reasons)
* 1.2 May 1999 Keith Matthews Added interrogation of Last_Error for instance handle where found.
*
*/
UINT WINAPI DdeGetLastError( DWORD idInst )
{
DWORD error_code;
DDE_HANDLE_ENTRY *reference_inst;
FIXME("(%ld): stub\n",idInst);
if ( DDE_Max_Assigned_Instance == 0 )
{
/* Nothing has been initialised - exit now ! */
return DMLERR_DLL_NOT_INITIALIZED;
}
if ( !WaitForMutex(handle_mutex) )
{
return DMLERR_SYS_ERROR;
}
TRACE("Handle Mutex created/reserved\n");
/* First check instance
*/
reference_inst = Find_Instance_Entry(idInst);
if (reference_inst == NULL)
{
if ( Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,reference_inst)) return TRUE;
/* Nothing has been initialised - exit now ! can return TRUE since effect is the same */
return DMLERR_DLL_NOT_INITIALIZED;
}
error_code = reference_inst->Last_Error;
reference_inst->Last_Error = 0;
Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE,reference_inst);
return error_code;
}
/*****************************************************************
* DdeCmpStringHandles16 (DDEML.36)
*/
INT16 WINAPI DdeCmpStringHandles16( HSZ hsz1, HSZ hsz2 )
{
return DdeCmpStringHandles(hsz1, hsz2);
}
/*****************************************************************
* DdeCmpStringHandles (USER32.@)
*
* Compares the value of two string handles. This comparison is
* not case sensitive.
*
* Returns:
* -1 The value of hsz1 is zero or less than hsz2
* 0 The values of hsz 1 and 2 are the same or both zero.
* 1 The value of hsz2 is zero of less than hsz1
*/
INT WINAPI DdeCmpStringHandles( HSZ hsz1, HSZ hsz2 )
{
CHAR psz1[MAX_BUFFER_LEN];
CHAR psz2[MAX_BUFFER_LEN];
int ret = 0;
int ret1, ret2;
TRACE("handle 1, handle 2\n" );
ret1 = GlobalGetAtomNameA( hsz1, psz1, MAX_BUFFER_LEN );
ret2 = GlobalGetAtomNameA( hsz2, psz2, MAX_BUFFER_LEN );
/* Make sure we found both strings.
*/
if( ret1 == 0 && ret2 == 0 )
{
/* If both are not found, return both "zero strings".
*/
ret = 0;
}
else if( ret1 == 0 )
{
/* If hsz1 is a not found, return hsz1 is "zero string".
*/
ret = -1;
}
else if( ret2 == 0 )
{
/* If hsz2 is a not found, return hsz2 is "zero string".
*/
ret = 1;
}
else
{
/* Compare the two strings we got ( case insensitive ).
*/
ret = strcasecmp( psz1, psz2 );
/* Since strcmp returns any number smaller than
* 0 when the first string is found to be less than
* the second one we must make sure we are returning
* the proper values.
*/
if( ret < 0 )
{
ret = -1;
}
else if( ret > 0 )
{
ret = 1;
}
}
return ret;
}
/*****************************************************************
* PackDDElParam (USER32.@)
*
* RETURNS
* the packed lParam
*/
LPARAM WINAPI PackDDElParam(UINT msg, UINT uiLo, UINT uiHi)
{
FIXME("stub.\n");
return 0;
}
/*****************************************************************
* UnpackDDElParam (USER32.@)
*
* RETURNS
* success: nonzero
* failure: zero
*/
BOOL WINAPI UnpackDDElParam(UINT msg, LPARAM lParam,
PUINT uiLo, PUINT uiHi)
{
FIXME("stub.\n");
return 0;
}
/*****************************************************************
* FreeDDElParam (USER32.@)
*
* RETURNS
* success: nonzero
* failure: zero
*/
BOOL WINAPI FreeDDElParam(UINT msg, LPARAM lParam)
{
FIXME("stub.\n");
return 0;
}
/*****************************************************************
* ReuseDDElParam (USER32.@)
*
* RETURNS
* the packed lParam
*/
LPARAM WINAPI ReuseDDElParam(UINT lParam, UINT msgIn, UINT msgOut,
UINT uiLi, UINT uiHi)
{
FIXME("stub.\n");
return 0;
}
/******************************************************************
* DdeQueryConvInfo16 (DDEML.9)
*
*/
UINT16 WINAPI DdeQueryConvInfo16( HCONV hconv, DWORD idTransaction , LPCONVINFO16 lpConvInfo)
{
FIXME("stub.\n");
return 0;
}
/******************************************************************
* DdeQueryConvInfo (USER32.@)
*
*/
UINT WINAPI DdeQueryConvInfo( HCONV hconv, DWORD idTransaction , LPCONVINFO lpConvInfo)
{
FIXME("stub.\n");
return 0;
}
/*
* DDEML 16-bit library definitions
*
* Copyright 1997 Alexandre Julliard
* Copyright 1997 Len White
*/
#ifndef __WINE_WINE_DDEML16_H
#define __WINE_WINE_DDEML16_H
#include "windef.h"
#include "wine/windef16.h"
#define QID_SYNC16 -1L
typedef HDDEDATA CALLBACK (*PFNCALLBACK16)(UINT16,UINT16,HCONV,HSZ,HSZ,HDDEDATA,DWORD,DWORD);
/***************************************************
Externally visible data structures
***************************************************/
typedef struct
{
UINT16 cb;
UINT16 wFlags;
UINT16 wCountryID;
INT16 iCodePage;
DWORD dwLangID;
DWORD dwSecurity;
} CONVCONTEXT16, *LPCONVCONTEXT16;
typedef struct
{
DWORD cb;
DWORD hUser;
HCONV hConvPartner;
HSZ hszSvcPartner;
HSZ hszServiceReq;
HSZ hszTopic;
HSZ hszItem;
UINT16 wFmt;
UINT16 wType;
UINT16 wStatus;
UINT16 wConvst;
UINT16 wLastError;
HCONVLIST hConvList;
CONVCONTEXT16 ConvCtxt;
} CONVINFO16, *LPCONVINFO16;
/* Interface Definitions */
UINT16 WINAPI DdeInitialize16(LPDWORD,PFNCALLBACK16,DWORD,DWORD);
BOOL16 WINAPI DdeUninitialize16(DWORD);
HCONVLIST WINAPI DdeConnectList16(DWORD,HSZ,HSZ,HCONVLIST,LPCONVCONTEXT16);
HCONV WINAPI DdeQueryNextServer16(HCONVLIST, HCONV);
BOOL16 WINAPI DdeDisconnectList16(HCONVLIST);
HCONV WINAPI DdeConnect16(DWORD,HSZ,HSZ,LPCONVCONTEXT16);
BOOL16 WINAPI DdeDisconnect16(HCONV);
BOOL16 WINAPI DdeSetUserHandle16(HCONV,DWORD,DWORD);
HDDEDATA WINAPI DdeCreateDataHandle16(DWORD,LPBYTE,DWORD,DWORD,HSZ,UINT16,UINT16);
HSZ WINAPI DdeCreateStringHandle16(DWORD,LPCSTR,INT16);
BOOL16 WINAPI DdeFreeStringHandle16(DWORD,HSZ);
BOOL16 WINAPI DdeFreeDataHandle16(HDDEDATA);
BOOL16 WINAPI DdeKeepStringHandle16(DWORD,HSZ);
HDDEDATA WINAPI DdeClientTransaction16(LPVOID,DWORD,HCONV,HSZ,UINT16,UINT16,DWORD,LPDWORD);
BOOL16 WINAPI DdeAbandonTransaction16(DWORD,HCONV,DWORD);
BOOL16 WINAPI DdePostAdvise16(DWORD,HSZ,HSZ);
HDDEDATA WINAPI DdeAddData16(HDDEDATA,LPBYTE,DWORD,DWORD);
LPBYTE WINAPI DdeAccessData16(HDDEDATA,LPDWORD);
BOOL16 WINAPI DdeUnaccessData16(HDDEDATA);
BOOL16 WINAPI DdeEnableCallback16(DWORD,HCONV,UINT16);
INT16 WINAPI DdeCmpStringHandles16(HSZ,HSZ);
HDDEDATA WINAPI DdeNameService16(DWORD,HSZ,HSZ,UINT16);
UINT16 WINAPI DdeGetLastError16(DWORD);
UINT16 WINAPI DdeQueryConvInfo16(HCONV,DWORD,LPCONVINFO16);
#endif /* __WINE_WINE_DDEML16_H */
......@@ -8,7 +8,7 @@ import advapi32.dll
import kernel32.dll
import ntdll.dll
debug_channels (accel caret class clipboard combo cursor dc ddeml dialog driver
debug_channels (accel caret class clipboard combo cursor dc dde ddeml dialog driver
edit event graphics hook icon key keyboard listbox local mdi
menu message msg nonclient prop relay resource scroll sendmsg
shell static string syscolor system text timer win win32 wnet)
......@@ -331,7 +331,7 @@ debug_channels (accel caret class clipboard combo cursor dc ddeml dialog driver
@ stdcall GrayStringW(long long ptr long long long long long long) GrayStringW
@ stdcall HideCaret(long) HideCaret
@ stdcall HiliteMenuItem(long long long long) HiliteMenuItem
@ stub ImpersonateDdeClientWindow
@ stdcall ImpersonateDdeClientWindow(long long) ImpersonateDdeClientWindow
@ stdcall InSendMessage() InSendMessage
@ stdcall InSendMessageEx(ptr) InSendMessageEx
@ stdcall InflateRect(ptr long long) InflateRect
......
......@@ -32,7 +32,7 @@ extern "C" {
/* DDEACK: wStatus in WM_DDE_ACK message */
struct tagDDEACK
{
unsigned bAppReturnCode:8, reserved:6, fBusy:1, fAck:1;
unsigned short bAppReturnCode:8, reserved:6, fBusy:1, fAck:1;
};
typedef struct tagDDEACK DDEACK;
......@@ -61,6 +61,11 @@ struct tagDDEPOKE
};
typedef struct tagDDEPOKE DDEPOKE;
BOOL WINAPI DdeSetQualityOfService(HWND hwndClient,
CONST SECURITY_QUALITY_OF_SERVICE *pqosNew,
PSECURITY_QUALITY_OF_SERVICE pqosPrev);
BOOL WINAPI ImpersonateDdeClientWindow(HWND hWndClient, HWND hWndServer);
/* lParam packing/unpacking API */
......
......@@ -32,12 +32,49 @@ extern "C" {
#define EXPENTRY CALLBACK
#define SZDDESYS_TOPIC TEXT("System")
#define SZDDESYS_ITEM_TOPICS TEXT("Topics")
#define SZDDESYS_ITEM_SYSITEMS TEXT("SysItems")
#define SZDDESYS_ITEM_RTNMSG TEXT("ReturnMessage")
#define SZDDESYS_ITEM_STATUS TEXT("Status")
#define SZDDESYS_ITEM_FORMATS TEXT("Formats")
#define SZDDESYS_ITEM_HELP TEXT("Help")
#define SZDDE_ITEM_ITEMLIST TEXT("TopicItemList")
/***************************************************
FLAGS Section - copied from Microsoft SDK as must be standard, probably Copyright Microsoft Corporation
***************************************************/
#define XST_NULL 0
#define XST_INCOMPLETE 1
#define XST_CONNECTED 2
#define XST_INIT1 3
#define XST_INIT2 4
#define XST_REQSENT 5
#define XST_DATARCVD 6
#define XST_POKESENT 7
#define XST_POKEACKRCVD 8
#define XST_EXECSENT 9
#define XST_EXECACKRCVD 10
#define XST_ADVSENT 11
#define XST_UNADVSENT 12
#define XST_ADVACKRCVD 13
#define XST_UNADVACKRCVD 14
#define XST_ADVDATASENT 15
#define XST_ADVDATAACKRCVD 16
#define ST_CONNECTED 0x0001
#define ST_ADVISE 0x0002
#define ST_ISLOCAL 0x0004
#define ST_BLOCKED 0x0008
#define ST_CLIENT 0x0010
#define ST_TERMINATED 0x0020
#define ST_INLIST 0x0040
#define ST_BLOCKNEXT 0x0080
#define ST_ISSELF 0x0100
/*
* Callback filter flags for use with standard apps.
*/
......@@ -138,6 +175,8 @@ extern "C" {
#define XTYP_MASK 0x00F0
#define XTYP_SHIFT 4
#define TIMEOUT_ASYNC 0xFFFFFFFF
/**************************************************
End of Message Types Section
......@@ -159,6 +198,10 @@ extern "C" {
#define DDE_FAPPSTATUS 0x00FF
#define DDE_FNOTPROCESSED 0x0000
#define DDE_FACKRESERVED (~(DDE_FACK | DDE_FBUSY | DDE_FAPPSTATUS))
#define DDE_FADVRESERVED (~(DDE_FACKREQ | DDE_FDEFERUPD))
#define DDE_FDATRESERVED (~(DDE_FACKREQ | DDE_FRELEASE | DDE_FREQUESTED))
#define DDE_FPOKRESERVED (~(DDE_FRELEASE))
/*****************************************************
......@@ -197,6 +240,8 @@ extern "C" {
#define DMLERR_LAST 0x4011
#define HDATA_APPOWNED 0x0001
/*****************************************************
End of Return Codes and Microsoft section
......@@ -205,10 +250,10 @@ extern "C" {
DECLARE_OLD_HANDLE(HCONVLIST);
DECLARE_OLD_HANDLE(HCONV);
DECLARE_OLD_HANDLE(HSZ);
DECLARE_OLD_HANDLE(HDDEDATA);
DECLARE_HANDLE(HCONVLIST);
DECLARE_HANDLE(HCONV);
DECLARE_HANDLE(HSZ);
DECLARE_HANDLE(HDDEDATA);
......@@ -218,8 +263,8 @@ DECLARE_OLD_HANDLE(HDDEDATA);
*******************************************************/
typedef HDDEDATA CALLBACK (*PFNCALLBACK)(UINT,UINT,HCONV,HSZ,HSZ,
HDDEDATA,DWORD,DWORD);
typedef HDDEDATA CALLBACK (*PFNCALLBACK)(UINT, UINT, HCONV, HSZ, HSZ,
HDDEDATA, DWORD, DWORD);
/***************************************************
......@@ -227,7 +272,13 @@ typedef HDDEDATA CALLBACK (*PFNCALLBACK)(UINT,UINT,HCONV,HSZ,HSZ,
***************************************************/
typedef struct
typedef struct tagHSZPAIR
{
HSZ hszSvc;
HSZ hszTopic;
} HSZPAIR, *PHSZPAIR, *LPHSZPAIR;
typedef struct tagCONVCONTEXT
{
UINT cb;
UINT wFlags;
......@@ -237,7 +288,7 @@ typedef struct
DWORD dwSecurity;
} CONVCONTEXT, *LPCONVCONTEXT;
typedef struct
typedef struct tagCONVINFO
{
DWORD cb;
DWORD hUser;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment