blob: 3f80f78533ce37b3fd3367aa71b5003cc3f8b8f9 [file] [log] [blame]
/* -*- 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);
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);
}