From edc3faede310ffc2f18624df890ca8ab88e50a2b Mon Sep 17 00:00:00 2001 From: Keith Matthews Date: Sat, 12 Jun 1999 06:33:14 +0000 Subject: [PATCH] 1st cut implementation of DdeNameService (omits monitor callback) 1st cut implementation of DdeKeepStringHandle (probably complete) DdeInitializeW - minor changes to initialise service names structure DdeGetLastError - start of proper last error retrieval DdeCreateStringHandle16 - added code for default codepage where not supplied. Various documentary/comment corrections. --- documentation/status/dde | 11 +- misc/ddeml.c | 428 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 423 insertions(+), 16 deletions(-) diff --git a/documentation/status/dde b/documentation/status/dde index 3585c1005e9..67450dd95f5 100644 --- a/documentation/status/dde +++ b/documentation/status/dde @@ -14,9 +14,10 @@ The initialisation was tackled first, then the routines produced by Corel/Macada were modified to cope with multiple instances of dde client/server and a first cut at the un-initialise routine. -Once these are satisfactory then DdeNameService must be tackled, followed by the main -message reception routine and the invoking of the callback. Serious testing from this -point may well depend on Wine thread handling settling down. +DdeNameService has now been produced as a first-cut routine, but the monitor notification +must be tackled, followed by the main message reception routine and the invoking of the +callback. Serious testing from this point may well depend on Wine thread handling +settling down. The strategy so far has been to tackle the Unicode versions first. Once they have been accepted as close to complete then the 32-bit ASCII routines then the 16-bit routines. @@ -32,13 +33,11 @@ TO DO Complete initialisation, some of the 2nd call validation conditions have not been worked out yet and the code for enabling the message loop needs writing. -Complete unitialize. +Complete initialize and unitialize, also add the callback handling to DdeNameService. Add true Unicode handling to CreateStringHandle, QueryString, CmpStringHandles. Also find out why Initialize needs ASCII and Unicode variants. -Add reference count handling to CreateStringHAndle, FreeStringHandle etc. - Add DdeGetLAstError and related handling in all other modules. Also find out what to do about storing errors that result in the instance failing to initialise in some way. diff --git a/misc/ddeml.c b/misc/ddeml.c index 359c73ce48c..e98ba54dc69 100644 --- a/misc/ddeml.c +++ b/misc/ddeml.c @@ -51,6 +51,13 @@ struct tagHSZNode }; +typedef struct tagServiceNode ServiceNode; +struct tagServiceNode +{ + ServiceNode* next; + HSZ hsz; + BOOL16 FilterOn; +}; typedef struct DDE_HANDLE_ENTRY { BOOL16 Monitor; /* have these two as full Booleans cos they'll be tested frequently */ BOOL16 Client_only; /* bit wasteful of space but it will be faster */ @@ -64,6 +71,7 @@ typedef struct DDE_HANDLE_ENTRY { DWORD Monitor_flags; UINT Txn_count; /* count transactions open to simplify closure */ DWORD Last_Error; + ServiceNode* ServiceNames; } DDE_HANDLE_ENTRY; static DDE_HANDLE_ENTRY *DDE_Handle_Table_Base = NULL; @@ -184,6 +192,7 @@ static void FreeAndRemoveHSZNodes( DWORD idInst ) * * 1.0 Dec 1998 Corel/Macadamian Initial version * 1.1 Mar 1999 Keith Matthews Added instance handling + * 1.2 Jun 1999 Keith Matthews Added Usage count handling * */ static void InsertHSZNode( HSZ hsz ) @@ -202,6 +211,7 @@ static void InsertHSZNode( HSZ hsz ) /* Attach the node to the head of the list. i.e most recently added is first */ pNew->next = reference_inst->Node_list; + /* The new node is now at the head of the list * so set the global list pointer to it. */ @@ -243,6 +253,39 @@ static void InsertHSZNode( HSZ hsz ) return NULL; } +/***************************************************************************** + * Find_Service_Name + * + * generic routine to return a pointer to the relevant ServiceNode + * for a given service name, or NULL if the entry does not exist + * + * ASSUMES the mutex protecting the handle entry list is reserved before calling + * + ****************************************************************************** + * + * Change History + * + * Vn Date Author Comment + * + * 1.0 May 1999 Keith Matthews 1st implementation + */ + ServiceNode *Find_Service_Name (HSZ Service_Name, DDE_HANDLE_ENTRY* this_instance) +{ + ServiceNode * reference_name= this_instance->ServiceNames; + while ( reference_name != NULL ) + { + if ( reference_name->hsz == Service_Name ) + { + TRACE(ddeml,"Service Name found\n"); + return reference_name; + } + reference_name = reference_name->next; + } + TRACE(ddeml,"Service name missing\n"); + return NULL; +} + + /****************************************************************************** * Release_reserved_mutex * @@ -321,6 +364,97 @@ DWORD IncrementInstanceId() return DMLERR_NO_ERROR; } +/****************************************************************************** + * FindNotifyMonitorCallbacks + * + * Routine to find instances that need to be notified via their callback + * of some event they are monitoring + * + ****************************************************************************** + * + * Change History + * + * Vn Date Author Comment + * + * 1.0 May 1999 Keith Matthews Initial Version + * + */ + +void FindNotifyMonitorCallbacks(DWORD ThisInstance, DWORD DdeEvent ) +{ + DDE_HANDLE_ENTRY *InstanceHandle; + InstanceHandle = DDE_Handle_Table_Base; + while ( InstanceHandle != NULL ) + { + if ( (InstanceHandle->Monitor ) && InstanceHandle->Instance_id == ThisInstance ) + { + /* Found an instance registered as monitor and is not ourselves + * use callback to notify where appropriate + */ + } + InstanceHandle = InstanceHandle->Next_Entry; + } +} + +/****************************************************************************** + * DdeReserveAtom + * + * Routine to make an extra Add on an atom to reserve it a bit longer + * + ****************************************************************************** + * + * Change History + * + * Vn Date Author Comment + * + * 1.0 Jun 1999 Keith Matthews Initial Version + * + */ + +DdeReserveAtom( DDE_HANDLE_ENTRY * reference_inst,HSZ hsz) +{ + CHAR SNameBuffer[MAX_BUFFER_LEN]; + UINT rcode; + if ( reference_inst->Unicode) + { + rcode=GlobalGetAtomNameW(hsz,(LPWSTR)&SNameBuffer,MAX_ATOM_LEN); + GlobalAddAtomW((LPWSTR)SNameBuffer); + } else { + rcode=GlobalGetAtomNameA(hsz,SNameBuffer,MAX_ATOM_LEN); + GlobalAddAtomA(SNameBuffer); + } +} + + +/****************************************************************************** + * DdeReleaseAtom + * + * Routine to make a delete on an atom to release it a bit sooner + * + ****************************************************************************** + * + * Change History + * + * Vn Date Author Comment + * + * 1.0 Jun 1999 Keith Matthews Initial Version + * + */ + +DdeReleaseAtom( DDE_HANDLE_ENTRY * reference_inst,HSZ hsz) +{ + CHAR SNameBuffer[MAX_BUFFER_LEN]; + UINT rcode; + if ( reference_inst->Unicode) + { + rcode=GlobalGetAtomNameW(hsz,(LPWSTR)&SNameBuffer,MAX_ATOM_LEN); + GlobalAddAtomW((LPWSTR)SNameBuffer); + } else { + rcode=GlobalGetAtomNameA(hsz,SNameBuffer,MAX_ATOM_LEN); + GlobalAddAtomA(SNameBuffer); + } +} + /****************************************************************************** * DdeInitialize16 (DDEML.2) */ @@ -420,6 +554,7 @@ UINT WINAPI DdeInitializeW( LPDWORD pidInst, PFNCALLBACK pfnCallback, this_instance->Win16 = FALSE; this_instance->Node_list = NULL; /* node will be added later */ this_instance->Monitor_flags = afCmd & MF_MASK; + this_instance->ServiceNames = NULL; /* isolate CBF flags in one go, expect this will go the way of all attempts to be clever !! */ @@ -683,6 +818,10 @@ BOOL WINAPI DdeUninitialize( DWORD idInst ) } FIXME(ddeml, "(%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. */ @@ -1037,10 +1176,26 @@ HCONV WINAPI DdeReconnect( HCONV hConv ) /***************************************************************** * DdeCreateStringHandle16 (DDEML.21) + * + ***************************************************************** + * + * Change History + * + * Vn Date Author Comment + * + * 1.0 ? ? basic stub + * 1.1 June 1999 Keith Matthews amended onward call to supply default + * code page if none supplied by caller */ HSZ WINAPI DdeCreateStringHandle16( DWORD idInst, LPCSTR str, INT16 codepage ) { + if ( codepage ) + { return DdeCreateStringHandleA( idInst, str, codepage ); + } else { + TRACE(ddeml,"Default codepage supplied\n"); + return DdeCreateStringHandleA( idInst, str, CP_WINANSI); + } } @@ -1049,7 +1204,7 @@ HSZ WINAPI DdeCreateStringHandle16( DWORD idInst, LPCSTR str, INT16 codepage ) * * RETURNS * Success: String handle - * Failure: 1 + * Failure: 0 * ***************************************************************** * @@ -1069,8 +1224,8 @@ HSZ WINAPI DdeCreateStringHandleA( DWORD idInst, LPCSTR psz, INT codepage ) if ( DDE_Max_Assigned_Instance == 0 ) { - /* Nothing has been initialised - exit now ! can return TRUE since effect is the same */ - return TRUE; + /* Nothing has been initialised - exit now ! can return FALSE since effect is the same */ + return FALSE; } WaitForSingleObject(handle_mutex,1000); if ( (err_no=GetLastError()) != 0 ) @@ -1100,6 +1255,7 @@ HSZ WINAPI DdeCreateStringHandleA( DWORD idInst, LPCSTR psz, INT codepage ) /* Save the handle so we know to clean it when * uninitialize is called. */ + TRACE(ddeml,"added atom %s with HSZ 0x%lx, \n",debugstr_a(psz),hsz); InsertHSZNode( hsz ); if ( Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE)) { @@ -1145,8 +1301,8 @@ HSZ WINAPI DdeCreateStringHandleW( if ( DDE_Max_Assigned_Instance == 0 ) { - /* Nothing has been initialised - exit now ! can return TRUE since effect is the same */ - return TRUE; + /* Nothing has been initialised - exit now ! can return FALSE since effect is the same */ + return FALSE; } WaitForSingleObject(handle_mutex,1000); if ( (err_no=GetLastError()) != 0 ) @@ -1298,10 +1454,56 @@ BOOL16 WINAPI DdeKeepStringHandle16( DWORD idInst, HSZ hsz ) /***************************************************************** * DdeKeepStringHandle (USER32.108) + * + * RETURNS: success: nonzero + * fail: zero + * + ***************************************************************** + * + * Change History + * + * Vn Date Author Comment + * + * 1.0 ? ? Stub only + * 1.1 Jun 1999 Keith Matthews First cut implementation + * */ BOOL WINAPI DdeKeepStringHandle( DWORD idInst, HSZ hsz ) { - FIXME( ddeml, "empty stub\n" ); + + TRACE(ddeml, "(%ld,%ld): \n",idInst,hsz); + if ( DDE_Max_Assigned_Instance == 0 ) + { + /* Nothing has been initialised - exit now ! can return FALSE since effect is the same */ + return FALSE; + } + if ( ( prev_err = GetLastError()) != 0 ) + { + /* something earlier failed !! */ + ERR(ddeml,"Error %li before WaitForSingleObject - trying to continue\n",prev_err); + } + WaitForSingleObject(handle_mutex,1000); + if ( ((err_no=GetLastError()) != 0 ) && (err_no != prev_err )) + { + /* FIXME - needs refinement with popup for timeout, also is timeout interval OK */ + + ERR(ddeml,"WaitForSingleObject failed - handle list %li\n",err_no); + return FALSE; + } + TRACE(ddeml,"Handle Mutex created/reserved\n"); + + /* First check instance + */ + reference_inst = Find_Instance_Entry(idInst); + if ( (reference_inst == NULL) || (reference_inst->Node_list == NULL)) + { + if ( Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE)) return FALSE; + /* Nothing has been initialised - exit now ! can return FALSE since effect is the same */ + return FALSE; + return FALSE; + } + DdeReserveAtom(reference_inst,hsz); + Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE); return TRUE; } @@ -1593,11 +1795,18 @@ HDDEDATA WINAPI DdeNameService16( DWORD idInst, HSZ hsz1, HSZ hsz2, * 1.0 ? ? Stub * 1.1 Apr 1999 Keith Matthews Added trap for non-existent instance (uninitialised instance 0 * used by some MS programs for unfathomable reasons) + * 1.2 May 1999 Keith Matthews Added parameter validation and basic service name handling. + * Still needs callback parts * */ HDDEDATA WINAPI DdeNameService( DWORD idInst, HSZ hsz1, HSZ hsz2, UINT afCmd ) { + UINT Cmd_flags = afCmd; + ServiceNode* this_service, *reference_service ; + CHAR SNameBuffer[MAX_BUFFER_LEN]; + UINT rcode; + this_service = NULL; FIXME(ddeml, "(%ld,%ld,%ld,%d): stub\n",idInst,hsz1,hsz2,afCmd); if ( DDE_Max_Assigned_Instance == 0 ) { @@ -1605,7 +1814,204 @@ HDDEDATA WINAPI DdeNameService( DWORD idInst, HSZ hsz1, HSZ hsz2, * needs something for DdeGetLastError */ return 0L; } - return 1; + WaitForSingleObject(handle_mutex,1000); + if ( ((err_no=GetLastError()) != 0 ) && (err_no != prev_err )) + { + /* FIXME - needs refinement with popup for timeout, also is timeout interval OK */ + + ERR(ddeml,"WaitForSingleObject failed - handle list %li\n",err_no); + return DMLERR_SYS_ERROR; +} + TRACE(ddeml,"Handle Mutex created/reserved\n"); + + /* First check instance + */ + reference_inst = Find_Instance_Entry(idInst); + this_instance = reference_inst; + if (reference_inst == NULL) + { + TRACE(ddeml,"Instance not found as initialised\n"); + if ( Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE)) return TRUE; + /* Nothing has been initialised - exit now ! can return TRUE since effect is the same */ + return FALSE; + + } + + if ( hsz2 != 0L ) + { + /* Illegal, reserved parameter + */ + reference_inst->Last_Error = DMLERR_INVALIDPARAMETER; + Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE); + FIXME(ddeml,"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(ddeml,"General unregister unexpected flags\n"); + reference_inst->Last_Error = DMLERR_DLL_USAGE; + Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE); + return FALSE; + } + /* Loop to find all registered service and de-register them + */ + if ( reference_inst->ServiceNames == NULL ) + { + /* None to unregister !! + */ + TRACE(ddeml,"General de-register - nothing registered\n"); + reference_inst->Last_Error = DMLERR_DLL_USAGE; + Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE); + return FALSE; + } else + { + this_service = reference_inst->ServiceNames; + while ( this_service->next != NULL) + { + reference_service = this_service; + this_service = this_service->next; + DdeReleaseAtom(reference_inst,reference_service->hsz); + HeapFree(SystemHeap, 0, this_service); /* finished - release heap space used as work store */ + } + HeapFree(SystemHeap, 0, this_service); /* finished - release heap space used as work store */ + TRACE(ddeml,"General de-register - finished\n"); + } + Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE); + return TRUE; + } + TRACE(ddeml,"Specific name action detected\n"); + reference_service = Find_Service_Name(hsz1,reference_inst); + if (( Cmd_flags && DNS_REGISTER ) == DNS_REGISTER ) + { + /* Register new service name + */ + + rcode=GlobalGetAtomNameA(hsz1,SNameBuffer,MAX_ATOM_LEN); + Cmd_flags = Cmd_flags ^ DNS_REGISTER; + this_service= (ServiceNode*)HeapAlloc( SystemHeap, 0, sizeof(ServiceNode) ); + this_service->next = NULL; + this_service->hsz = hsz1; + this_service->FilterOn = TRUE; + if ( reference_inst->ServiceNames == NULL ) + { + /* easy one - nothing else there + */ + TRACE(ddeml,"Adding 1st service name\n"); + reference_inst->ServiceNames = this_service; + GlobalAddAtomA(SNameBuffer); + } else + { + /* more difficult - may have also been registered + */ + if (reference_service != NULL ) + { + /* Service name already registered !! + * what do we do ? + */ + HeapFree(SystemHeap, 0, this_service); /* finished - release heap space used as work store */ + FIXME(ddeml,"Trying to register already registered service !!\n"); + } else + { + /* Add this one into the chain + */ + TRACE(ddeml,"Adding subsequent service name\n"); + this_service->next = reference_inst->ServiceNames; + reference_inst->ServiceNames = this_service; + GlobalAddAtomA(SNameBuffer); + } + } + } + if ( (Cmd_flags && DNS_UNREGISTER ) == DNS_UNREGISTER ) + { + /* De-register service name + */ + Cmd_flags = Cmd_flags ^ DNS_UNREGISTER; + if ( reference_inst->ServiceNames == NULL ) + { + /* easy one - already done + */ + } else + { + /* more difficult - must hook out of sequence + */ + this_instance = reference_inst; + if (this_service == NULL ) + { + /* Service name not registered !! + * what do we do ? + */ + FIXME(ddeml,"Trying to de-register unregistered service !!\n"); + } else + { + /* Delete this one from the chain + */ + if ( reference_inst->ServiceNames == this_service ) + { + /* special case - the first/only entry + */ + reference_inst->ServiceNames = this_service->next; + } else + { + /* general case + */ + reference_service->next= reference_inst->ServiceNames; + while ( reference_service->next!= this_service ) + { + reference_service = reference_service->next; + } + reference_service->next= this_service->next; + } + DdeReleaseAtom(reference_inst,this_service->hsz); + HeapFree(SystemHeap, 0, this_service); /* finished - release heap space */ + } + } + } + if ( ( Cmd_flags && DNS_FILTEROFF ) != DNS_FILTEROFF ) + { + /* Set filter flags on to hold notifications of connection + * + * test coded this way as this is the default setting + */ + Cmd_flags = Cmd_flags ^ DNS_FILTERON; + if ( ( reference_inst->ServiceNames == NULL ) || ( this_service == NULL) ) + { + /* trying to filter where no service names !! + */ + reference_inst->Last_Error = DMLERR_DLL_USAGE; + Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE); + return FALSE; + } else + { + this_service->FilterOn = TRUE; + } + } + if ( ( Cmd_flags && DNS_FILTEROFF ) == DNS_FILTEROFF ) + { + /* Set filter flags on to hold notifications of connection + */ + Cmd_flags = Cmd_flags ^ DNS_FILTEROFF; + if ( ( reference_inst->ServiceNames == NULL ) || ( this_service == NULL) ) + { + /* trying to filter where no service names !! + */ + reference_inst->Last_Error = DMLERR_DLL_USAGE; + Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE); + return FALSE; + } else + { + this_service->FilterOn = FALSE; + } + } + Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE); + return TRUE; } @@ -1636,10 +2042,12 @@ UINT16 WINAPI DdeGetLastError16( DWORD idInst ) * 1.0 ? ? Stub * 1.1 Apr 1999 Keith Matthews Added response for non-existent instance (uninitialised instance 0 * used by some MS programs for unfathomable reasons) + * 1.2 May 1999 Keith Matthews Added interrogation of Last_Error for instance handle where found. * */ UINT WINAPI DdeGetLastError( DWORD idInst ) { + DWORD error_code; FIXME(ddeml, "(%ld): stub\n",idInst); if ( DDE_Max_Assigned_Instance == 0 ) { @@ -1671,10 +2079,10 @@ UINT WINAPI DdeGetLastError( DWORD idInst ) return DMLERR_DLL_NOT_INITIALIZED; } - - + error_code = this_instance->Last_Error; + this_instance->Last_Error = 0; Release_reserved_mutex(handle_mutex,"handle_mutex",FALSE,FALSE); - return 0; + return error_code; }