📄 dde.c
字号:
/*
* DDEML library
*
* Copyright 1997 Alexandre Julliard
* Copyright 1997 Len White
* Copyright 1999 Keith Matthews
* Copyright 2000 Corel
* Copyright 2001 Eric Pouech
* Copyright 2003, 2004, 2005 Dmitry Timoshkov
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <user32.h>
#include <wine/debug.h>
WINE_DEFAULT_DEBUG_CHANNEL(ddeml);
/* convert between ATOM and HSZ avoiding compiler warnings */
#define ATOM2HSZ(atom) ((HSZ) (ULONG_PTR)(atom))
#define HSZ2ATOM(hsz) ((ATOM) (ULONG_PTR)(hsz))
static WDML_INSTANCE* WDML_InstanceList = NULL;
static LONG WDML_MaxInstanceID = 0; /* OK for present, have to worry about wrap-around later */
const WCHAR WDML_szEventClass[] = {'D','d','e','E','v','e','n','t','C','l','a','s','s',0};
static CRITICAL_SECTION_DEBUG critsect_debug =
{
0, 0, &WDML_CritSect,
{ &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
0, 0, { (DWORD_PTR)(__FILE__ ": WDML_CritSect") }
};
CRITICAL_SECTION WDML_CritSect = { &critsect_debug, -1, 0, 0, 0, 0 };
/* ================================================================
*
* Pure DDE (non DDEML) management
*
* ================================================================ */
/*****************************************************************
* PackDDElParam (USER32.@)
*
* RETURNS
* the packed lParam
*/
LPARAM WINAPI PackDDElParam(UINT msg, UINT_PTR uiLo, UINT_PTR uiHi)
{
HGLOBAL hMem;
UINT_PTR *params;
switch (msg)
{
case WM_DDE_ACK:
case WM_DDE_ADVISE:
case WM_DDE_DATA:
case WM_DDE_POKE:
if (!(hMem = GlobalAlloc(GMEM_DDESHARE, sizeof(UINT_PTR) * 2)))
{
ERR("GlobalAlloc failed\n");
return 0;
}
if (!(params = GlobalLock(hMem)))
{
ERR("GlobalLock failed (%p)\n", hMem);
return 0;
}
params[0] = uiLo;
params[1] = uiHi;
GlobalUnlock(hMem);
return (LPARAM)hMem;
case WM_DDE_EXECUTE:
return uiHi;
default:
return MAKELPARAM(uiLo, uiHi);
}
}
/*****************************************************************
* UnpackDDElParam (USER32.@)
*
* RETURNS
* success: nonzero
* failure: zero
*/
BOOL WINAPI UnpackDDElParam(UINT msg, LPARAM lParam,
PUINT_PTR uiLo, PUINT_PTR uiHi)
{
UINT_PTR *params;
switch (msg)
{
case WM_DDE_ACK:
case WM_DDE_ADVISE:
case WM_DDE_DATA:
case WM_DDE_POKE:
if (!lParam) return FALSE;
if (!(params = GlobalLock( (HGLOBAL)lParam )))
{
ERR("GlobalLock failed (%lx)\n", lParam);
return FALSE;
}
TRACE("unpacked: low %08x, high %08x\n", params[0], params[1]);
if (uiLo) *uiLo = params[0];
if (uiHi) *uiHi = params[1];
GlobalUnlock( (HGLOBAL)lParam );
return TRUE;
case WM_DDE_EXECUTE:
if (uiLo) *uiLo = 0;
if (uiHi) *uiHi = lParam;
return TRUE;
default:
if (uiLo) *uiLo = LOWORD(lParam);
if (uiHi) *uiHi = HIWORD(lParam);
return TRUE;
}
}
/*****************************************************************
* FreeDDElParam (USER32.@)
*
* RETURNS
* success: nonzero
* failure: zero
*/
BOOL WINAPI FreeDDElParam(UINT msg, LPARAM lParam)
{
switch (msg)
{
case WM_DDE_ACK:
case WM_DDE_ADVISE:
case WM_DDE_DATA:
case WM_DDE_POKE:
/* first check if it's a global handle */
if (!GlobalHandle( (LPVOID)lParam )) return TRUE;
return !GlobalFree( (HGLOBAL)lParam );
default:
return TRUE;
}
}
/*****************************************************************
* ReuseDDElParam (USER32.@)
*
* RETURNS
* the packed lParam
*/
LPARAM WINAPI ReuseDDElParam(LPARAM lParam, UINT msgIn, UINT msgOut,
UINT_PTR uiLo, UINT_PTR uiHi)
{
UINT_PTR *params;
switch (msgIn)
{
case WM_DDE_ACK:
case WM_DDE_ADVISE:
case WM_DDE_DATA:
case WM_DDE_POKE:
switch(msgOut)
{
case WM_DDE_ACK:
case WM_DDE_ADVISE:
case WM_DDE_DATA:
case WM_DDE_POKE:
if (!lParam) return 0;
if (!(params = GlobalLock( (HGLOBAL)lParam )))
{
ERR("GlobalLock failed\n");
return 0;
}
params[0] = uiLo;
params[1] = uiHi;
TRACE("Reusing pack %08x %08x\n", uiLo, uiHi);
GlobalUnlock( (HGLOBAL)lParam );
return lParam;
case WM_DDE_EXECUTE:
FreeDDElParam( msgIn, lParam );
return uiHi;
default:
FreeDDElParam( msgIn, lParam );
return MAKELPARAM(uiLo, uiHi);
}
default:
return PackDDElParam( msgOut, uiLo, uiHi );
}
}
/*****************************************************************
* ImpersonateDdeClientWindow (USER32.@)
*
* PARAMS
* hWndClient [I] handle to DDE client window
* hWndServer [I] handle to DDE server window
*/
BOOL WINAPI ImpersonateDdeClientWindow(HWND hWndClient, HWND hWndServer)
{
FIXME("(%p %p): 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("(%p %p %p): stub\n", hwndClient, pqosNew, pqosPrev);
return TRUE;
}
/* ================================================================
*
* Instance management
*
* ================================================================ */
/******************************************************************************
* IncrementInstanceId
*
* generic routine to increment the max instance Id and allocate a new application instance
*/
static void WDML_IncrementInstanceId(WDML_INSTANCE* pInstance)
{
DWORD id = InterlockedIncrement(&WDML_MaxInstanceID);
pInstance->instanceID = id;
TRACE("New instance id %ld allocated\n", id);
}
/******************************************************************
* WDML_EventProc
*
*
*/
static LRESULT CALLBACK WDML_EventProc(HWND hwndEvent, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
WDML_INSTANCE* pInstance;
HSZ hsz1, hsz2;
switch (uMsg)
{
case WM_WDML_REGISTER:
pInstance = WDML_GetInstanceFromWnd(hwndEvent);
/* try calling the Callback */
if (pInstance && !(pInstance->CBFflags & CBF_SKIP_REGISTRATIONS))
{
hsz1 = WDML_MakeHszFromAtom(pInstance, wParam);
hsz2 = WDML_MakeHszFromAtom(pInstance, lParam);
WDML_InvokeCallback(pInstance, XTYP_REGISTER, 0, 0, hsz1, hsz2, 0, 0, 0);
WDML_DecHSZ(pInstance, hsz1);
WDML_DecHSZ(pInstance, hsz2);
}
break;
case WM_WDML_UNREGISTER:
pInstance = WDML_GetInstanceFromWnd(hwndEvent);
if (pInstance && !(pInstance->CBFflags & CBF_SKIP_UNREGISTRATIONS))
{
hsz1 = WDML_MakeHszFromAtom(pInstance, wParam);
hsz2 = WDML_MakeHszFromAtom(pInstance, lParam);
WDML_InvokeCallback(pInstance, XTYP_UNREGISTER, 0, 0, hsz1, hsz2, 0, 0, 0);
WDML_DecHSZ(pInstance, hsz1);
WDML_DecHSZ(pInstance, hsz2);
}
break;
case WM_WDML_CONNECT_CONFIRM:
pInstance = WDML_GetInstanceFromWnd(hwndEvent);
if (pInstance && !(pInstance->CBFflags & CBF_SKIP_CONNECT_CONFIRMS))
{
WDML_CONV* pConv;
/* confirm connection...
* lookup for this conv handle
*/
HWND client = (HWND)wParam;
HWND server = (HWND)lParam;
for (pConv = pInstance->convs[WDML_SERVER_SIDE]; pConv != NULL; pConv = pConv->next)
{
if (pConv->hwndClient == client && pConv->hwndServer == server)
break;
}
if (pConv)
{
pConv->wStatus |= ST_ISLOCAL;
WDML_InvokeCallback(pInstance, XTYP_CONNECT_CONFIRM, 0, (HCONV)pConv,
pConv->hszTopic, pConv->hszService, 0, 0,
(pConv->wStatus & ST_ISSELF) ? 1 : 0);
}
}
break;
default:
return DefWindowProcW(hwndEvent, uMsg, wParam, lParam);
}
return 0;
}
/******************************************************************
* WDML_Initialize
*
*
*/
UINT WDML_Initialize(LPDWORD pidInst, PFNCALLBACK pfnCallback,
DWORD afCmd, DWORD ulRes, BOOL bUnicode, BOOL b16)
{
WDML_INSTANCE* pInstance;
WDML_INSTANCE* reference_inst;
UINT ret;
WNDCLASSEXW wndclass;
TRACE("(%p,%p,0x%lx,%ld)\n",
pidInst, pfnCallback, afCmd, ulRes);
if (ulRes)
{
ERR("Reserved value not zero? What does this mean?\n");
/* trap this and no more until we know more */
return DMLERR_NO_ERROR;
}
/* grab enough heap for one control struct - not really necessary for re-initialise
* but allows us to use same validation routines */
pInstance = (WDML_INSTANCE*)HeapAlloc(GetProcessHeap(), 0, sizeof(WDML_INSTANCE));
if (pInstance == NULL)
{
/* catastrophe !! warn user & abort */
ERR("Instance create failed - out of memory\n");
return DMLERR_SYS_ERROR;
}
pInstance->next = NULL;
pInstance->monitor = (afCmd | APPCLASS_MONITOR);
/* messy bit, spec implies that 'Client Only' can be set in 2 different ways, catch 1 here */
pInstance->clientOnly = afCmd & APPCMD_CLIENTONLY;
pInstance->instanceID = *pidInst; /* May need to add calling proc Id */
pInstance->threadID = GetCurrentThreadId();
pInstance->callback = *pfnCallback;
pInstance->unicode = bUnicode;
pInstance->win16 = b16;
pInstance->nodeList = NULL; /* node will be added later */
pInstance->monitorFlags = afCmd & MF_MASK;
pInstance->servers = NULL;
pInstance->convs[0] = NULL;
pInstance->convs[1] = NULL;
pInstance->links[0] = NULL;
pInstance->links[1] = NULL;
/* isolate CBF flags in one go, expect this will go the way of all attempts to be clever !! */
pInstance->CBFflags = afCmd^((afCmd&MF_MASK)|((afCmd&APPCMD_MASK)|(afCmd&APPCLASS_MASK)));
if (!pInstance->clientOnly)
{
/* Check for other way of setting Client-only !! */
pInstance->clientOnly =
(pInstance->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);
EnterCriticalSection(&WDML_CritSect);
if (WDML_InstanceList == NULL)
{
/* can't be another instance in this case, assign to the base pointer */
WDML_InstanceList = pInstance;
/* 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 !!!
*/
pInstance->CBFflags = pInstance->CBFflags|APPCMD_FILTERINITS;
TRACE("First application instance detected OK\n");
/* allocate new instance ID */
WDML_IncrementInstanceId(pInstance);
}
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 (pInstance->instanceID == reference_inst->instanceID)
{
/* Check 1 - must be same Client-only state */
if (pInstance->clientOnly != reference_inst->clientOnly)
{
ret = DMLERR_DLL_USAGE;
goto theError;
}
/* Check 2 - cannot use 'Monitor' with any non-monitor modes */
if (pInstance->monitor != reference_inst->monitor)
{
ret = DMLERR_INVALIDPARAMETER;
goto theError;
}
/* Check 3 - must supply different callback address */
if (pInstance->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");
WDML_IncrementInstanceId(pInstance);
reference_inst->next = pInstance;
}
LeaveCriticalSection(&WDML_CritSect);
*pidInst = pInstance->instanceID;
/* for deadlock issues, windows must always be created when outside the critical section */
wndclass.cbSize = sizeof(wndclass);
wndclass.style = 0;
wndclass.lpfnWndProc = WDML_EventProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = sizeof(ULONG_PTR);
wndclass.hInstance = 0;
wndclass.hIcon = 0;
wndclass.hCursor = 0;
wndclass.hbrBackground = 0;
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = WDML_szEventClass;
wndclass.hIconSm = 0;
RegisterClassExW(&wndclass);
pInstance->hwndEvent = CreateWindowW(WDML_szEventClass, NULL,
WS_POPUP, 0, 0, 0, 0,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -