926 lines
25 KiB
C
926 lines
25 KiB
C
/* -*- tab-width: 8; c-basic-offset: 4 -*- */
|
|
|
|
/*
|
|
* DDEML library
|
|
*
|
|
* Copyright 1997 Alexandre Julliard
|
|
* Copyright 1997 Len White
|
|
* Copyright 1999 Keith Matthews
|
|
* Copyright 2000 Corel
|
|
* Copyright 2001 Eric Pouech
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include "winbase.h"
|
|
#include "windef.h"
|
|
#include "wingdi.h"
|
|
#include "winuser.h"
|
|
#include "winerror.h"
|
|
#include "dde.h"
|
|
#include "ddeml.h"
|
|
#include "debugtools.h"
|
|
#include "dde/dde_private.h"
|
|
|
|
DEFAULT_DEBUG_CHANNEL(ddeml);
|
|
|
|
static 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;
|
|
}
|