From 11f4b44451400987cac12e8f9287ab5f33805b9d Mon Sep 17 00:00:00 2001 From: Mike McCormack <mike@codeweavers.com> Date: Mon, 25 Nov 2002 02:47:32 +0000 Subject: [PATCH] Implemented registry change notifications. --- dlls/advapi32/registry.c | 27 ++++++- include/wine/server_protocol.h | 19 ++++- include/winnt.h | 5 +- server/handle.c | 2 + server/object.h | 1 + server/protocol.def | 8 ++ server/registry.c | 142 ++++++++++++++++++++++++++++++++- server/request.h | 2 + server/trace.c | 11 +++ 9 files changed, 209 insertions(+), 8 deletions(-) diff --git a/dlls/advapi32/registry.c b/dlls/advapi32/registry.c index 3d3e4d5bc7c..fddba709d72 100644 --- a/dlls/advapi32/registry.c +++ b/dlls/advapi32/registry.c @@ -1841,7 +1841,30 @@ LONG WINAPI RegNotifyChangeKeyValue( HKEY hkey, BOOL fWatchSubTree, DWORD fdwNotifyFilter, HANDLE hEvent, BOOL fAsync ) { - FIXME("(%p,%i,%ld,%p,%i): stub\n",hkey,fWatchSubTree,fdwNotifyFilter, + LONG ret; + + TRACE("(%p,%i,%ld,%p,%i)\n",hkey,fWatchSubTree,fdwNotifyFilter, hEvent,fAsync); - return ERROR_SUCCESS; + + if( !fAsync ) + hEvent = CreateEventA(NULL, 0, 0, NULL); + + SERVER_START_REQ( set_registry_notification ) + { + req->hkey = hkey; + req->event = hEvent; + req->subtree = fWatchSubTree; + req->filter = fdwNotifyFilter; + ret = RtlNtStatusToDosError( wine_server_call(req) ); + } + SERVER_END_REQ; + + if( !fAsync ) + { + if( ret == ERROR_SUCCESS ) + WaitForSingleObject( hEvent, INFINITE ); + CloseHandle( hEvent ); + } + + return ret; } diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index 466c7e543a1..845aaf5a1a3 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -1893,6 +1893,20 @@ struct set_registry_levels_reply }; +struct set_registry_notification_request +{ + struct request_header __header; + obj_handle_t hkey; + obj_handle_t event; + int subtree; + unsigned int filter; +}; +struct set_registry_notification_reply +{ + struct reply_header __header; +}; + + struct create_timer_request { @@ -3044,6 +3058,7 @@ enum request REQ_save_registry, REQ_save_registry_atexit, REQ_set_registry_levels, + REQ_set_registry_notification, REQ_create_timer, REQ_open_timer, REQ_set_timer, @@ -3220,6 +3235,7 @@ union generic_request struct save_registry_request save_registry_request; struct save_registry_atexit_request save_registry_atexit_request; struct set_registry_levels_request set_registry_levels_request; + struct set_registry_notification_request set_registry_notification_request; struct create_timer_request create_timer_request; struct open_timer_request open_timer_request; struct set_timer_request set_timer_request; @@ -3394,6 +3410,7 @@ union generic_reply struct save_registry_reply save_registry_reply; struct save_registry_atexit_reply save_registry_atexit_reply; struct set_registry_levels_reply set_registry_levels_reply; + struct set_registry_notification_reply set_registry_notification_reply; struct create_timer_reply create_timer_reply; struct open_timer_reply open_timer_reply; struct set_timer_reply set_timer_reply; @@ -3462,6 +3479,6 @@ union generic_reply struct get_next_hook_reply get_next_hook_reply; }; -#define SERVER_PROTOCOL_VERSION 91 +#define SERVER_PROTOCOL_VERSION 92 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */ diff --git a/include/winnt.h b/include/winnt.h index 0b38b73e3ac..77a1eca4c23 100644 --- a/include/winnt.h +++ b/include/winnt.h @@ -3360,7 +3360,10 @@ typedef enum tagSID_NAME_USE { #define REG_OPENED_EXISTING_KEY 0x00000002 /* For RegNotifyChangeKeyValue */ -#define REG_NOTIFY_CHANGE_NAME 0x1 +#define REG_NOTIFY_CHANGE_NAME 0x01 +#define REG_NOTIFY_CHANGE_ATTRIBUTES 0x02 +#define REG_NOTIFY_CHANGE_LAST_SET 0x04 +#define REG_NOTIFY_CHANGE_SECURITY 0x08 #define KEY_QUERY_VALUE 0x00000001 #define KEY_SET_VALUE 0x00000002 diff --git a/server/handle.c b/server/handle.c index 00dbc54f6ce..9df42935535 100644 --- a/server/handle.c +++ b/server/handle.c @@ -332,6 +332,8 @@ int close_handle( struct process *process, obj_handle_t handle, int *fd ) table = handle_is_global(handle) ? global_table : process->handles; if (entry < table->entries + table->free) table->free = entry - table->entries; if (entry == table->entries + table->last) shrink_handle_table( table ); + /* hack: windows seems to treat registry handles differently */ + registry_close_handle( obj, handle ); release_object( obj ); return 1; } diff --git a/server/object.h b/server/object.h index d2f57bc3c09..711f5845e23 100644 --- a/server/object.h +++ b/server/object.h @@ -195,6 +195,7 @@ extern int get_page_size(void); extern void init_registry(void); extern void flush_registry(void); extern void close_registry(void); +extern void registry_close_handle( struct object *obj, obj_handle_t hkey ); /* atom functions */ diff --git a/server/protocol.def b/server/protocol.def index 1b15d85d646..056902c0a7e 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1358,6 +1358,14 @@ enum char_info_mode @END +@REQ(set_registry_notification) + obj_handle_t hkey; /* key to watch for changes */ + obj_handle_t event; /* event to set */ + int subtree; /* should we watch the whole subtree? */ + unsigned int filter; /* things to watch */ +@END + + /* Create a waitable timer */ @REQ(create_timer) int inherit; /* inherit flag */ diff --git a/server/registry.c b/server/registry.c index 792750cdb30..77ddf9f5d4d 100644 --- a/server/registry.c +++ b/server/registry.c @@ -48,6 +48,16 @@ #include "winternl.h" #include "wine/library.h" +struct notify +{ + struct event *event; /* event to set when changing this key */ + int subtree; /* true if subtree notification */ + unsigned int filter; /* which events to notify on */ + obj_handle_t hkey; /* hkey associated with this notification */ + struct notify *next; /* list of notifications */ + struct notify *prev; /* list of notifications */ +}; + /* a registry key */ struct key { @@ -64,6 +74,8 @@ struct key short flags; /* flags */ short level; /* saving level */ time_t modif; /* last modification time */ + struct notify *first_notify; /* list of notifications */ + struct notify *last_notify; /* list of notifications */ }; /* key flags */ @@ -284,6 +296,53 @@ static void key_dump( struct object *obj, int verbose ) fprintf( stderr, "\n" ); } +/* notify waiter and maybe delete the notification */ +static void do_notification( struct key *key, struct notify *notify, int del ) +{ + if( notify->event ) + { + set_event( notify->event ); + release_object( notify->event ); + notify->event = NULL; + } + + if ( !del ) + return; + if( notify->next ) + notify->next->prev = notify->prev; + else + key->last_notify = notify->prev; + if( notify->prev ) + notify->prev->next = notify->next; + else + key->first_notify = notify->next; + free( notify ); +} + +static struct notify *find_notify( struct key *key, obj_handle_t hkey) +{ + struct notify *n; + + for( n=key->first_notify; n; n = n->next) + if( n->hkey == hkey ) + break; + return n; +} + +/* close the notification associated with a handle */ +void registry_close_handle( struct object *obj, obj_handle_t hkey ) +{ + struct key * key = (struct key *) obj; + struct notify *notify; + + if( obj->ops != &key_ops ) + return; + notify = find_notify( key, hkey ); + if( !notify ) + return; + do_notification( key, notify, 1 ); +} + static void key_destroy( struct object *obj ) { int i; @@ -302,6 +361,9 @@ static void key_destroy( struct object *obj ) key->subkeys[i]->parent = NULL; release_object( key->subkeys[i] ); } + /* unconditionally notify everything waiting on this key */ + while ( key->first_notify ) + do_notification( key, key->first_notify, 1 ); } /* duplicate a key path */ @@ -389,6 +451,8 @@ static struct key *alloc_key( const WCHAR *name, time_t modif ) key->level = current_level; key->modif = modif; key->parent = NULL; + key->first_notify = NULL; + key->last_notify = NULL; if (!(key->name = strdupW( name ))) { release_object( key ); @@ -420,12 +484,32 @@ static void make_clean( struct key *key ) for (i = 0; i <= key->last_subkey; i++) make_clean( key->subkeys[i] ); } +/* go through all the notifications and send them if necessary */ +void check_notify( struct key *key, unsigned int change, int not_subtree ) +{ + struct notify *n = key->first_notify; + while (n) + { + struct notify *next = n->next; + if ( ( not_subtree || n->subtree ) && ( change & n->filter ) ) + do_notification( key, n, 0 ); + n = next; + } +} + /* update key modification time */ -static void touch_key( struct key *key ) +static void touch_key( struct key *key, unsigned int change ) { + struct key *k; + key->modif = time(NULL); key->level = max( key->level, current_level ); make_dirty( key ); + + /* do notifications */ + check_notify( key, change, 1 ); + for ( k = key->parent; k; k = k->parent ) + check_notify( k, change & ~REG_NOTIFY_CHANGE_LAST_SET, 0 ); } /* try to grow the array of subkeys; return 1 if OK, 0 on error */ @@ -583,6 +667,7 @@ static struct key *create_key( struct key *key, WCHAR *name, WCHAR *class, if (!*path) goto done; *created = 1; + touch_key( key, REG_NOTIFY_CHANGE_NAME ); /* FIXME: is this right? */ if (flags & KEY_DIRTY) make_dirty( key ); base = key; base_idx = index; @@ -718,7 +803,7 @@ static void delete_key( struct key *key ) } if (debug_level > 1) dump_operation( key, NULL, "Delete" ); free_subkey( parent, index ); - touch_key( parent ); + touch_key( parent, REG_NOTIFY_CHANGE_NAME ); } /* try to grow the array of values; return 1 if OK, 0 on error */ @@ -821,7 +906,7 @@ static void set_value( struct key *key, WCHAR *name, int type, const void *data, value->type = type; value->len = len; value->data = ptr; - touch_key( key ); + touch_key( key, REG_NOTIFY_CHANGE_LAST_SET ); if (debug_level > 1) dump_operation( key, value, "Set" ); } @@ -912,7 +997,7 @@ static void delete_value( struct key *key, const WCHAR *name ) if (value->data) free( value->data ); for (i = index; i < key->last_value; i++) key->values[i] = key->values[i + 1]; key->last_value--; - touch_key( key ); + touch_key( key, REG_NOTIFY_CHANGE_LAST_SET ); /* try to shrink the array */ nb_values = key->nb_values; @@ -1817,3 +1902,52 @@ DECL_HANDLER(save_registry_atexit) release_object( key ); } } + +/* add a registry key change notification */ +DECL_HANDLER(set_registry_notification) +{ + struct key *key; + struct event *event; + struct notify *notify; + + key = get_hkey_obj( req->hkey, KEY_NOTIFY ); + if( key ) + { + event = get_event_obj( current->process, req->event, SYNCHRONIZE ); + if( event ) + { + notify = find_notify( key, req->hkey ); + if( notify ) + { + release_object( notify->event ); + grab_object( event ); + notify->event = event; + } + else + { + notify = (struct notify *) malloc (sizeof *notify); + if( notify ) + { + grab_object( event ); + notify->event = event; + notify->subtree = req->subtree; + notify->filter = req->filter; + notify->hkey = req->hkey; + + /* add to linked list */ + notify->prev = NULL; + notify->next = key->first_notify; + if ( notify->next ) + notify->next->prev = notify; + else + key->last_notify = notify; + key->first_notify = notify; + } + else + set_error( STATUS_NO_MEMORY ); + } + release_object( event ); + } + release_object( key ); + } +} diff --git a/server/request.h b/server/request.h index f5fd97d4d41..213da9883dc 100644 --- a/server/request.h +++ b/server/request.h @@ -206,6 +206,7 @@ DECL_HANDLER(load_registry); DECL_HANDLER(save_registry); DECL_HANDLER(save_registry_atexit); DECL_HANDLER(set_registry_levels); +DECL_HANDLER(set_registry_notification); DECL_HANDLER(create_timer); DECL_HANDLER(open_timer); DECL_HANDLER(set_timer); @@ -381,6 +382,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = (req_handler)req_save_registry, (req_handler)req_save_registry_atexit, (req_handler)req_set_registry_levels, + (req_handler)req_set_registry_notification, (req_handler)req_create_timer, (req_handler)req_open_timer, (req_handler)req_set_timer, diff --git a/server/trace.c b/server/trace.c index 368cca27734..64122129722 100644 --- a/server/trace.c +++ b/server/trace.c @@ -1569,6 +1569,14 @@ static void dump_set_registry_levels_request( const struct set_registry_levels_r fprintf( stderr, " period=%d", req->period ); } +static void dump_set_registry_notification_request( const struct set_registry_notification_request *req ) +{ + fprintf( stderr, " hkey=%p,", req->hkey ); + fprintf( stderr, " event=%p,", req->event ); + fprintf( stderr, " subtree=%d,", req->subtree ); + fprintf( stderr, " filter=%08x", req->filter ); +} + static void dump_create_timer_request( const struct create_timer_request *req ) { fprintf( stderr, " inherit=%d,", req->inherit ); @@ -2445,6 +2453,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_save_registry_request, (dump_func)dump_save_registry_atexit_request, (dump_func)dump_set_registry_levels_request, + (dump_func)dump_set_registry_notification_request, (dump_func)dump_create_timer_request, (dump_func)dump_open_timer_request, (dump_func)dump_set_timer_request, @@ -2617,6 +2626,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { (dump_func)0, (dump_func)0, (dump_func)0, + (dump_func)0, (dump_func)dump_create_timer_reply, (dump_func)dump_open_timer_reply, (dump_func)0, @@ -2789,6 +2799,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = { "save_registry", "save_registry_atexit", "set_registry_levels", + "set_registry_notification", "create_timer", "open_timer", "set_timer", -- GitLab