From e32abe8a76537c1bf952a0acf2d0699143001bf8 Mon Sep 17 00:00:00 2001 From: Keith Matthews Date: Thu, 28 Jan 1999 16:26:39 +0000 Subject: [PATCH] 1st cut implementation of DdeInitialize32W and supporting code. --- include/ddeml.h | 132 ++++++++++++++++++ misc/ddeml.c | 358 ++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 482 insertions(+), 8 deletions(-) diff --git a/include/ddeml.h b/include/ddeml.h index dfb08eebf4..cf76a8d023 100644 --- a/include/ddeml.h +++ b/include/ddeml.h @@ -16,11 +16,119 @@ #define CP_WINUNICODE 1200 #define MSGF_DDEMGR 0x8001 +/*************************************************** + + FLAGS Section - copied from Microsoft SDK as must be standard, probably Copyright Microsoft Corporation + +***************************************************/ + +/* + * Callback filter flags for use with standard apps. + */ + +#define CBF_FAIL_SELFCONNECTIONS 0x00001000 +#define CBF_FAIL_CONNECTIONS 0x00002000 +#define CBF_FAIL_ADVISES 0x00004000 +#define CBF_FAIL_EXECUTES 0x00008000 +#define CBF_FAIL_POKES 0x00010000 +#define CBF_FAIL_REQUESTS 0x00020000 +#define CBF_FAIL_ALLSVRXACTIONS 0x0003f000 + +#define CBF_SKIP_CONNECT_CONFIRMS 0x00040000 +#define CBF_SKIP_REGISTRATIONS 0x00080000 +#define CBF_SKIP_UNREGISTRATIONS 0x00100000 +#define CBF_SKIP_DISCONNECTS 0x00200000 +#define CBF_SKIP_ALLNOTIFICATIONS 0x003c0000 + +/* + * Application command flags + */ +#define APPCMD_CLIENTONLY 0x00000010L +#define APPCMD_FILTERINITS 0x00000020L +#define APPCMD_MASK 0x00000FF0L + +/* + * Application classification flags + */ + +#define APPCLASS_STANDARD 0x00000000L +#define APPCLASS_MONITOR 0x00000001L +#define APPCLASS_MASK 0x0000000FL + +/* + * Callback filter flags for use with MONITOR apps - 0 implies no monitor + * callbacks. + */ +#define MF_HSZ_INFO 0x01000000 +#define MF_SENDMSGS 0x02000000 +#define MF_POSTMSGS 0x04000000 +#define MF_CALLBACKS 0x08000000 +#define MF_ERRORS 0x10000000 +#define MF_LINKS 0x20000000 +#define MF_CONV 0x40000000 + +#define MF_MASK 0xFF000000 + +/* + * DdeNameService service name flags + */ + +#define DNS_REGISTER 0x0001 +#define DNS_UNREGISTER 0x0002 +#define DNS_FILTERON 0x0004 +#define DNS_FILTEROFF 0x0008 + + +/**************************************************** + + End of Flags section + +****************************************************/ + +/**************************************************** + + Return Codes section again copied from SDK as must be same + +*****************************************************/ + +#define DMLERR_NO_ERROR 0 /* must be 0 */ + +#define DMLERR_FIRST 0x4000 + +#define DMLERR_ADVACKTIMEOUT 0x4000 +#define DMLERR_BUSY 0x4001 +#define DMLERR_DATAACKTIMEOUT 0x4002 +#define DMLERR_DLL_NOT_INITIALIZED 0x4003 +#define DMLERR_DLL_USAGE 0x4004 +#define DMLERR_EXECACKTIMEOUT 0x4005 +#define DMLERR_INVALIDPARAMETER 0x4006 +#define DMLERR_LOW_MEMORY 0x4007 +#define DMLERR_MEMORY_ERROR 0x4008 +#define DMLERR_NOTPROCESSED 0x4009 +#define DMLERR_NO_CONV_ESTABLISHED 0x400a +#define DMLERR_POKEACKTIMEOUT 0x400b +#define DMLERR_POSTMSG_FAILED 0x400c +#define DMLERR_REENTRANCY 0x400d +#define DMLERR_SERVER_DIED 0x400e +#define DMLERR_SYS_ERROR 0x400f +#define DMLERR_UNADVACKTIMEOUT 0x4010 +#define DMLERR_UNFOUND_QUEUE_ID 0x4011 + +#define DMLERR_LAST 0x4011 + +/***************************************************** + + End of Return Codes and Microsoft section + +******************************************************/ + + typedef DWORD HCONVLIST; typedef DWORD HCONV; typedef DWORD HSZ; typedef DWORD HDDEDATA; +typedef CHAR *LPTSTR; typedef HDDEDATA (CALLBACK *PFNCALLBACK16)(UINT16,UINT16,HCONV,HSZ,HSZ, HDDEDATA,DWORD,DWORD); @@ -28,6 +136,12 @@ typedef HDDEDATA (CALLBACK *PFNCALLBACK32)(UINT32,UINT32,HCONV,HSZ,HSZ, HDDEDATA,DWORD,DWORD); DECL_WINELIB_TYPE(PFNCALLBACK) +/*************************************************** + + Externally visible data structures + +***************************************************/ + typedef struct { UINT16 cb; @@ -48,6 +162,24 @@ typedef struct DWORD dwSecurity; } CONVCONTEXT32, *LPCONVCONTEXT32; +// Internal data structures + + /* entry for handle table */ +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 */ + LPDWORD Instance_id; // needed to track monitor usage + struct DDE_HANDLE_ENTRY *Next_Entry; + PFNCALLBACK32 CallBack; + DWORD CBF_Flags; + DWORD Monitor_flags; + UINT32 Txn_count; // count transactions open to simplify closure +} DDE_HANDLE_ENTRY; + +// Interface Definitions + DECL_WINELIB_TYPE(CONVCONTEXT) DECL_WINELIB_TYPE(LPCONVCONTEXT) diff --git a/misc/ddeml.c b/misc/ddeml.c index dceb722987..ebc7bda323 100644 --- a/misc/ddeml.c +++ b/misc/ddeml.c @@ -3,6 +3,7 @@ * * Copyright 1997 Alexandre Julliard * Copyright 1997 Len White + * Copyright 1999 Keith Matthews */ /* Only empty stubs for now */ @@ -12,10 +13,10 @@ #include "ddeml.h" #include "debug.h" #include "windows.h" +#include "wintypes.h" +#include "winerror.h" #include "heap.h" - -/* FIXME: What are these values? */ -#define DMLERR_NO_ERROR 0 +#include "shm_semaph.h" /* Has defined in atom.c file. */ @@ -25,13 +26,35 @@ */ #define MAX_BUFFER_LEN (MAX_ATOM_LEN + 1) -static LONG DDE_current_handle; + +static DDE_HANDLE_ENTRY *DDE_Handle_Table_Base = NULL; +static LPDWORD DDE_Max_Assigned_Instance = 0; // OK for present, may have to worry about wrap-around later +static const char inst_string[]= "DDEMaxInstance"; +static LPCWSTR DDEInstanceAccess = (LPCWSTR)&inst_string; +static const char handle_string[] = "DDEHandleAccess"; +static LPCWSTR DDEHandleAccess = (LPCWSTR)&handle_string; +static HANDLE32 inst_count_mutex = 0; +static HANDLE32 handle_mutex = 0; + DDE_HANDLE_ENTRY *this_instance; + SECURITY_ATTRIBUTES *s_att= NULL; + DWORD err_no = 0; + +/* typedef struct { + DWORD nLength; + LPVOID lpSecurityDescriptor; + BOOL32 bInheritHandle; +} SECURITY_ATTRIBUTES; */ + +#define TRUE 1 +#define FALSE 0 + + /* 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 witch instance + * 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. */ @@ -51,6 +74,15 @@ static HSZNode* pHSZNodes = NULL; * 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 + * */ static void RemoveHSZNode( DWORD idInst, HSZ hsz ) { @@ -103,6 +135,15 @@ static void RemoveHSZNode( DWORD idInst, HSZ hsz ) * * 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 + * */ static void FreeAndRemoveHSZNodes( DWORD idInst ) { @@ -118,6 +159,15 @@ static void FreeAndRemoveHSZNodes( DWORD idInst ) * 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 + * */ static void InsertHSZNode( DWORD idInst, HSZ hsz ) { @@ -143,6 +193,81 @@ static void InsertHSZNode( DWORD idInst, HSZ hsz ) } } +/****************************************************************************** + * Release_reserved_mutex + * + * generic routine to release a reserved mutex + * + * + ****************************************************************************** + * + * Change History + * + * Vn Date Author Comment + * + * 1.0 Jan 1999 Keith Matthews Initial version + * + */ + DWORD Release_reserved_mutex (HANDLE32 mutex, LPTSTR mutex_name, BOOL32 release_handle_m, BOOL32 release_this_i ) +{ + ReleaseMutex(mutex); + if ( (err_no=GetLastError()) != 0 ) + { + ERR(ddeml,"ReleaseMutex failed - %s mutex %li\n",mutex_name,err_no); + 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; +} + +/****************************************************************************** + * 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() +{ + 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 = CreateMutex32W(&s_attrib,1,DDEInstanceAccess); // 1st time through + } else { + WaitForSingleObject(inst_count_mutex,1000); // subsequent calls + /* FIXME - needs refinement with popup for timeout, also is timeout interval OK */ + } + if ( (err_no=GetLastError()) == ERROR_INVALID_HANDLE ) + { + ERR(ddeml,"CreateMutex failed - inst_count %li\n",err_no); + err_no=Release_reserved_mutex (handle_mutex,"handle_mutex",0,1); + return DMLERR_SYS_ERROR; + } + DDE_Max_Assigned_Instance++; + this_instance->Instance_id = DDE_Max_Assigned_Instance; + if (Release_reserved_mutex(inst_count_mutex,"instance_count",1,0)) return DMLERR_SYS_ERROR; + return DMLERR_NO_ERROR; +} /****************************************************************************** * DdeInitialize16 (DDEML.2) @@ -178,17 +303,234 @@ UINT32 WINAPI DdeInitialize32A( LPDWORD pidInst, PFNCALLBACK32 pfnCallback, * 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 + * */ UINT32 WINAPI DdeInitialize32W( LPDWORD pidInst, PFNCALLBACK32 pfnCallback, DWORD afCmd, DWORD ulRes ) { - FIXME(ddeml, "(%p,%p,0x%lx,%ld): stub\n",pidInst,pfnCallback,afCmd,ulRes); + DDE_HANDLE_ENTRY *reference_inst; + SECURITY_ATTRIBUTES s_attrib; + s_att = &s_attrib; - if(pidInst) - *pidInst = 0; +// probably not really capable of handling mutliple processes, but should handle +// multiple instances within one process if( ulRes ) + { ERR(dde, "Reserved value not zero? What does this mean?\n"); + FIXME(ddeml, "(%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 ) + { + /* can't set up the instance with nothing to act as a callback */ + TRACE(ddeml,"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( SystemHeap, 0, sizeof(DDE_HANDLE_ENTRY) ); + if ( this_instance == NULL ) + { + // catastrophe !! warn user & abort + ERR (ddeml,"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->Monitor_flags = afCmd & MF_MASK; + + // 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(ddeml,"instance created - checking validity \n"); + + if( *pidInst == 0 ) { + /* Initialisation of new Instance Identifier */ + TRACE(ddeml,"new instance, callback %p flags %lX\n",pfnCallback,afCmd); + /* 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 = CreateMutex32W(s_att,1,DDEHandleAccess); + if ( (err_no=GetLastError()) == ERROR_INVALID_HANDLE ) + { + ERR(ddeml,"CreateMutex failed - handle list %li\n",err_no); + HeapFree(GetProcessHeap(), 0, this_instance); + return DMLERR_SYS_ERROR; + } + TRACE(ddeml,"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(ddeml,"First application instance detected OK\n"); + // allocate new instance ID + if ((err_no = IncrementInstanceId()) ) 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(ddeml,"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)) + 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)) + 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)) + return DMLERR_SYS_ERROR; + return DMLERR_DLL_USAGE; + } + } + reference_inst = reference_inst->Next_Entry; + } + // All cleared, add to chain + + TRACE(ddeml,"Application Instance checks finished\n"); + if ((err_no = IncrementInstanceId())) return err_no; + if ( Release_reserved_mutex(handle_mutex,"handle_mutex",0,0)) return DMLERR_SYS_ERROR; + reference_inst->Next_Entry = this_instance; + } + pidInst = (LPDWORD)this_instance->Instance_id; + TRACE(ddeml,"New application instance processing finished OK\n"); + } else { + /* Reinitialisation situation --- FIX */ + TRACE(ddeml,"reinitialisation of (%p,%p,0x%lx,%ld): stub\n",pidInst,pfnCallback,afCmd,ulRes); + WaitForSingleObject(handle_mutex,1000); + if ( (err_no=GetLastError()) != 0 ) + { + + /* FIXME - needs refinement with popup for timeout, also is timeout interval OK */ + + ERR(ddeml,"WaitForSingleObject failed - handle list %li\n",err_no); + 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)) 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 ? */ + 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)) + 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)) + 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)) + return DMLERR_SYS_ERROR; + return DMLERR_DLL_USAGE; + } + } + } + // 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)) + return DMLERR_SYS_ERROR; + } return DMLERR_NO_ERROR; } -- 2.32.0.93.g670b81a890