Implement registry change notification and test
Mike McCormack
mike at codeweavers.com
Sun Nov 17 00:43:22 CST 2002
It seems that registry handles are somehow treated differently from
normal handles, so to make this work I needed to add a hack so that we
could trigger a registry change event when a registry key was closed.
Windows also remembers the change filter on a per hkey basis.
Mike
ChangeLog:
* Implement registry change notification
* add test for registry change notification
-------------- next part --------------
Index: server/handle.c
===================================================================
RCS file: /home/wine/wine/server/handle.c,v
retrieving revision 1.22
diff -u -r1.22 handle.c
--- server/handle.c 2 Oct 2002 23:49:30 -0000 1.22
+++ server/handle.c 17 Nov 2002 06:35:38 -0000
@@ -332,6 +332,8 @@
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;
}
Index: server/protocol.def
===================================================================
RCS file: /home/wine/wine/server/protocol.def,v
retrieving revision 1.49
diff -u -r1.49 protocol.def
--- server/protocol.def 29 Oct 2002 00:41:42 -0000 1.49
+++ server/protocol.def 17 Nov 2002 06:35:40 -0000
@@ -1362,6 +1362,14 @@
@END
+ at 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 */
+ at END
+
+
/* Create a waitable timer */
@REQ(create_timer)
int inherit; /* inherit flag */
Index: server/registry.c
===================================================================
RCS file: /home/wine/wine/server/registry.c,v
retrieving revision 1.43
diff -u -r1.43 registry.c
--- server/registry.c 12 Sep 2002 22:07:06 -0000 1.43
+++ server/registry.c 17 Nov 2002 06:35:42 -0000
@@ -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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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;
@@ -1814,6 +1899,55 @@
if ((key = get_hkey_obj( req->hkey, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS )))
{
register_branch_for_saving( key, get_req_data(), get_req_data_size() );
+ 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 );
}
}
Index: include/winnt.h
===================================================================
RCS file: /home/wine/wine/include/winnt.h,v
retrieving revision 1.138
diff -u -r1.138 winnt.h
--- include/winnt.h 4 Nov 2002 22:43:24 -0000 1.138
+++ include/winnt.h 17 Nov 2002 06:35:46 -0000
@@ -3386,7 +3386,10 @@
#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
Index: dlls/advapi32/registry.c
===================================================================
RCS file: /home/wine/wine/dlls/advapi32/registry.c,v
retrieving revision 1.48
diff -u -r1.48 registry.c
--- dlls/advapi32/registry.c 13 Nov 2002 19:45:27 -0000 1.48
+++ dlls/advapi32/registry.c 17 Nov 2002 06:35:49 -0000
@@ -1841,7 +1841,31 @@
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;
}
+
-------------- next part --------------
Index: dlls/advapi32/tests/Makefile.in
===================================================================
RCS file: /home/wine/wine/dlls/advapi32/tests/Makefile.in,v
retrieving revision 1.1
diff -u -r1.1 Makefile.in
--- dlls/advapi32/tests/Makefile.in 9 Aug 2002 01:22:40 -0000 1.1
+++ dlls/advapi32/tests/Makefile.in 17 Nov 2002 06:36:24 -0000
@@ -6,6 +6,7 @@
IMPORTS = advapi32 kernel32 ntdll
CTESTS = \
+ rchange.c \
registry.c
@MAKE_TEST_RULES@
--- /dev/null Mon Jul 18 08:46:18 1994
+++ dlls/advapi32/tests/rchange.c Sun Nov 17 15:17:27 2002
@@ -0,0 +1,200 @@
+/*
+ * Unit tests for registry change notifications
+ *
+ * Copyright (c) 2002 Mike McCormack
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <assert.h>
+#include "wine/test.h"
+#include "winbase.h"
+#include "winreg.h"
+#include "winerror.h"
+
+#if 0
+#include<windows.h>
+#include<stdio.h>
+
+#define ok(cond, str) if(!(cond)) printf("%d %s\n",__LINE__,str)
+#define START_TEXT(x) int main(int argc, char **argv)
+#endif
+
+START_TEST(rchange)
+/* int main(int argc, char **argv) */
+{
+ HKEY hkey = 0, hkeynew = 0;
+ DWORD r, val = 1234;
+ HANDLE hEvent;
+
+ // cleanup from previous run
+ RegDeleteKey(HKEY_CURRENT_USER, "Software\\test\\new");
+ RegDeleteKey(HKEY_CURRENT_USER, "Software\\test");
+
+ // create an event to play with
+ hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ ok(hEvent!=INVALID_HANDLE_VALUE, "couldn't create event");
+
+ // create a key and a change notification... shouldn't be notified yet
+ r = RegCreateKeyEx(HKEY_CURRENT_USER, "Software\\test", 0,
+ NULL, 0, KEY_ALL_ACCESS, NULL, &hkey, NULL);
+ ok(!r, "reg open key failed(1)");
+ r = RegNotifyChangeKeyValue(hkey, FALSE, REG_NOTIFY_CHANGE_NAME, hEvent, TRUE);
+ ok(!r, "reg notify change failed (1)");
+ r = WaitForSingleObject(hEvent, 0);
+ ok(r!=WAIT_OBJECT_0, "wait succeeded but nothing has happened(1)");
+
+ // create a subkey on the key we're watching... we should be notified
+ r = RegCreateKeyEx(hkey, "new", 0,
+ NULL, 0, KEY_ALL_ACCESS, NULL, &hkeynew, NULL);
+ ok(!r, "failed to create subkey");
+ r = WaitForSingleObject(hEvent, 0);
+ ok(r==WAIT_OBJECT_0, "wait failed but shouldn't have(1)");
+ // close the key so we can reopen it with a different notification
+ RegCloseKey(hkeynew);
+ RegCloseKey(hkey);
+ hkeynew = hkey = 0;
+
+
+ // condition: the key HKEY_CURRENT_USER\\Software\\test\\new exists
+ // but all key handles are closed
+
+ // create a value in the subkey... check for a notification
+ r = RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\test\\new", 0, KEY_ALL_ACCESS, &hkeynew);
+ ok(!r, "key does not exist!");
+ r = RegNotifyChangeKeyValue(hkeynew, TRUE, REG_NOTIFY_CHANGE_LAST_SET, hEvent, TRUE);
+ ok(!r, "reg notify change failed (1a)");
+ r = RegSetValueEx(hkeynew, "blah", 0, REG_DWORD, (LPBYTE) &val, sizeof val);
+ ok(!r, "couldn't set value(1a)\n");
+ r = WaitForSingleObject(hEvent, 0);
+ ok(r==WAIT_OBJECT_0, "wait failed (1a)");
+
+ // delete a value ... check for a notification
+ r = RegNotifyChangeKeyValue(hkeynew, TRUE, REG_NOTIFY_CHANGE_LAST_SET, hEvent, TRUE);
+ ok(!r, "reg notify change failed (1b)");
+ r = RegDeleteValue(hkeynew, "blah");
+ ok(!r, "couldn't delete value(1b)\n");
+ r = WaitForSingleObject(hEvent, 0);
+ ok(r==WAIT_OBJECT_0, "wait failed (1c)");
+
+ // re-establish the wait, then close the key .. check for another notification
+ r = RegNotifyChangeKeyValue(hkeynew, FALSE, REG_NOTIFY_CHANGE_NAME, hEvent, TRUE);
+ ok(!r, "reg notify change failed (2)");
+ r = CloseHandle(hkeynew);
+ ok(r, "close failed!(2)");
+ r = WaitForSingleObject(hEvent, 0);
+ ok(r==WAIT_OBJECT_0, "wait failed but shouldn't have(2)");
+ hkeynew = 0;
+
+
+ // condition: the key HKEY_CURRENT_USER\\Software\\test\\new exists
+ // but all key handles are closed
+
+ // delete the subkey ...check for a notification
+ r = RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\test", 0, KEY_ALL_ACCESS, &hkey);
+ ok(!r, "key does not exist!");
+ r = RegNotifyChangeKeyValue(hkey, FALSE, REG_NOTIFY_CHANGE_NAME, hEvent, TRUE);
+ ok(!r, "reg notify change failed (3)");
+ r = RegDeleteKey(hkey,"new");
+ ok(!r, "delete key failed");
+ r = WaitForSingleObject(hEvent, 0);
+ ok(r==WAIT_OBJECT_0, "wait failed but shouldn't have(3)");
+
+ // create a value ... but wait on the wrong thing
+ r = RegNotifyChangeKeyValue(hkey, FALSE, REG_NOTIFY_CHANGE_NAME, hEvent, TRUE);
+ ok(!r, "reg notify change failed (3a)");
+ r = RegSetValueEx(hkey, "blah", 0, REG_DWORD, (LPBYTE) &val, sizeof val);
+ ok(!r, "couldn't set value\n");
+ r = WaitForSingleObject(hEvent, 0);
+ ok(r!=WAIT_OBJECT_0, "wait succeeded but shouldn't have(3a)");
+
+ // condition: the key HKEY_CURRENT_USER\\Software\\test exists
+ // only hkey is open with a handle to it
+
+ // get rid of the notification from above by creating a key
+ r = RegCreateKeyEx(hkey, "new", 0,
+ NULL, 0, KEY_ALL_ACCESS, NULL, &hkeynew, NULL);
+ ok(!r, "failed to create subkey");
+ r = WaitForSingleObject(hEvent, 0);
+ ok(r==WAIT_OBJECT_0, "wait failed but shouldn't have(1)");
+
+ // condition: the key HKEY_CURRENT_USER\\Software\\test\\new exists
+ // only hkey is open with a handle to it's parent
+
+ // delete a value ... but wait on the wrong thing
+ // ALERT: changing the notification filter does not work until we close the key
+ // so this does *not* work as expected on windows
+ r = RegNotifyChangeKeyValue(hkey, FALSE, REG_NOTIFY_CHANGE_LAST_SET, hEvent, TRUE);
+ ok(!r, "reg notify change failed");
+ r = RegDeleteValue(hkey, "blah");
+ ok(!r, "couldn't delete value\n");
+ r = WaitForSingleObject(hEvent, 0);
+ ok(r!=WAIT_OBJECT_0, "doesn't succeed on (on winnt)");
+
+ // condition: the key HKEY_CURRENT_USER\\Software\\test\\new exists
+ // only hkey is open with a handle to it's parent
+
+ // delete the key Softare\\test\\new to get rid of the notification
+ r = RegDeleteKey(hkey,"new");
+ ok(!r, "delete key failed");
+ r = WaitForSingleObject(hEvent, 0);
+ ok(r==WAIT_OBJECT_0, "wait should have succeeded");
+
+ r = RegCloseKey(hkey);
+ ok(!r, "close key failed");
+
+ // condition: the key HKEY_CURRENT_USER\\Software\\test exists
+ // no keys open
+
+
+ // create a value ... check for a notification
+ r = RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\test", 0, KEY_ALL_ACCESS, &hkey);
+ ok(!r, "key does not exist!");
+ r = RegNotifyChangeKeyValue(hkey, FALSE, REG_NOTIFY_CHANGE_LAST_SET, hEvent, TRUE);
+ ok(!r, "reg notify change failed (3c)");
+ r = RegSetValueEx(hkey, "blah", 0, REG_DWORD, (LPBYTE) &val, sizeof val);
+ ok(!r, "couldn't set value\n");
+ r = WaitForSingleObject(hEvent, 0);
+ ok(r==WAIT_OBJECT_0, "wait should have succeeded(3c)");
+
+ // delete a value ... check for a notification
+ // again, our change to the filter should be ignored
+ r = RegNotifyChangeKeyValue(hkey, FALSE, REG_NOTIFY_CHANGE_NAME, hEvent, TRUE);
+ ok(!r, "reg notify change failed (3d)");
+ r = RegDeleteValue(hkey, "blah");
+ ok(!r, "couldn't delete value\n");
+ r = WaitForSingleObject(hEvent, 0);
+ ok(r==WAIT_OBJECT_0, "wait should have succeeded(3d)");
+
+ // condition: the key HKEY_CURRENT_USER\\Software\\test exists
+ // hkey is a handle to it
+
+ // now close the key ... check for a notification
+ r = RegNotifyChangeKeyValue(hkey, FALSE, REG_NOTIFY_CHANGE_NAME, hEvent, TRUE);
+ ok(!r, "reg notify change failed (4)");
+ r = CloseHandle(hkey);
+ ok(r, "close failed(4)");
+ r = WaitForSingleObject(hEvent, 0);
+ ok(r==WAIT_OBJECT_0, "wait failed but shouldn't have(4)");
+
+ // clean up
+ r = RegDeleteKey(HKEY_CURRENT_USER, "Software\\test");
+ ok(!r, "delete key failed(5)");
+ r = CloseHandle(hEvent);
+ ok(r, "close failed(5)");
+
+ // post condition: Software\\test does not exist any longer
+ // no keys open
+}
More information about the wine-patches
mailing list