diff --git a/dlls/advapi32/registry.c b/dlls/advapi32/registry.c
index 3d3e4d5bc7c5b1f6ed356aded95b2d9d7216f178..fddba709d727badc745657fca082ac0bae9ad833 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 466c7e543a1fef3021b3a8b49b05fc2ff85e0628..845aaf5a1a3a182048d7393093dacbcf8752e768 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 0b38b73e3ac2404ca5707e4b5a7811c3f515b644..77a1eca4c236e5ee22784af64a5dde33a753f7b7 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 00dbc54f6ce6db0c83cca08728896f9fe4aed3aa..9df429355351a27e4ca0e450138295c52a886fb8 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 d2f57bc3c09592fa2da76c78d81593acd1311bb2..711f5845e23a52f37685c7c7d00b9237b2e57d32 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 1b15d85d64641cc9f9daf66fda956dd3d58cd18b..056902c0a7e3c738d81449684912dfacde562573 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 792750cdb30adf11936e8a37fbb4dda4613a54bb..77ddf9f5d4d01101740867afa94e78f3afa2ccfd 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 f5fd97d4d4138c5f06b1f381d19a042a26816c41..213da9883dc64d2d3fcf3cfccebe3420eb7a8b09 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 368cca2773472b2ee1a12a5300694ba5e316b336..6412212972274a8a7adcc8e35855574d3094d9c9 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",