Added a close_handle method to the object operations, and use it to
implement registry notifications and the strange behavior of
CloseHandle on winstation/desktop handles.

diff --git a/server/atom.c b/server/atom.c
index 52797ca..b1291d3 100644
--- a/server/atom.c
+++ b/server/atom.c
@@ -76,6 +76,7 @@
     NULL,                         /* satisfied */
     no_signal,                    /* signal */
     no_get_fd,                    /* get_fd */
+    no_close_handle,              /* close_handle */
     atom_table_destroy            /* destroy */
 };
 
diff --git a/server/change.c b/server/change.c
index 51867dd..5ccdfb8 100644
--- a/server/change.c
+++ b/server/change.c
@@ -73,6 +73,7 @@
     no_satisfied,             /* satisfied */
     no_signal,                /* signal */
     no_get_fd,                /* get_fd */
+    no_close_handle,          /* close_handle */
     change_destroy            /* destroy */
 };
 
diff --git a/server/console.c b/server/console.c
index cc57418..871a66e 100644
--- a/server/console.c
+++ b/server/console.c
@@ -48,6 +48,7 @@
     no_satisfied,                     /* satisfied */
     no_signal,                        /* signal */
     no_get_fd,                        /* get_fd */
+    no_close_handle,                  /* close_handle */
     console_input_destroy             /* destroy */
 };
 
@@ -73,6 +74,7 @@
     no_satisfied,                     /* satisfied */
     no_signal,                        /* signal */
     no_get_fd,                        /* get_fd */
+    no_close_handle,                  /* close_handle */
     console_input_events_destroy      /* destroy */
 };
 
@@ -109,6 +111,7 @@
     NULL,                             /* satisfied */
     no_signal,                        /* signal */
     no_get_fd,                        /* get_fd */
+    no_close_handle,                  /* close_handle */
     screen_buffer_destroy             /* destroy */
 };
 
diff --git a/server/debugger.c b/server/debugger.c
index b1ba9b1..e8fbb0c 100644
--- a/server/debugger.c
+++ b/server/debugger.c
@@ -74,6 +74,7 @@
     no_satisfied,                  /* satisfied */
     no_signal,                     /* signal */
     no_get_fd,                     /* get_fd */
+    no_close_handle,               /* close_handle */
     debug_event_destroy            /* destroy */
 };
 
@@ -91,6 +92,7 @@
     no_satisfied,                  /* satisfied */
     no_signal,                     /* signal */
     no_get_fd,                     /* get_fd */
+    no_close_handle,               /* close_handle */
     debug_ctx_destroy              /* destroy */
 };
 
diff --git a/server/event.c b/server/event.c
index fa1dbbf..3445185 100644
--- a/server/event.c
+++ b/server/event.c
@@ -53,6 +53,7 @@
     event_satisfied,           /* satisfied */
     event_signal,              /* signal */
     no_get_fd,                 /* get_fd */
+    no_close_handle,           /* close_handle */
     no_destroy                 /* destroy */
 };
 
diff --git a/server/fd.c b/server/fd.c
index 797f77f..d3143cd 100644
--- a/server/fd.c
+++ b/server/fd.c
@@ -163,6 +163,7 @@
     NULL,                     /* satisfied */
     no_signal,                /* signal */
     no_get_fd,                /* get_fd */
+    no_close_handle,          /* close_handle */
     fd_destroy                /* destroy */
 };
 
@@ -193,6 +194,7 @@
     NULL,                     /* satisfied */
     no_signal,                /* signal */
     no_get_fd,                /* get_fd */
+    no_close_handle,          /* close_handle */
     inode_destroy             /* destroy */
 };
 
@@ -224,6 +226,7 @@
     no_satisfied,               /* satisfied */
     no_signal,                  /* signal */
     no_get_fd,                  /* get_fd */
+    no_close_handle,            /* close_handle */
     no_destroy                  /* destroy */
 };
 
diff --git a/server/file.c b/server/file.c
index dd38e19..9000b61 100644
--- a/server/file.c
+++ b/server/file.c
@@ -80,6 +80,7 @@
     no_satisfied,                 /* satisfied */
     no_signal,                    /* signal */
     file_get_fd,                  /* get_fd */
+    no_close_handle,              /* close_handle */
     file_destroy                  /* destroy */
 };
 
diff --git a/server/handle.c b/server/handle.c
index efcbe27..4a70843 100644
--- a/server/handle.c
+++ b/server/handle.c
@@ -108,6 +108,7 @@
     NULL,                            /* satisfied */
     no_signal,                       /* signal */
     no_get_fd,                       /* get_fd */
+    no_close_handle,                 /* close_handle */
     handle_table_destroy             /* destroy */
 };
 
@@ -323,6 +324,11 @@
         return 0;
     }
     obj = entry->ptr;
+    if (!obj->ops->close_handle( obj, process, handle ))
+    {
+        set_error( STATUS_INVALID_HANDLE );
+        return 0;
+    }
     entry->ptr = NULL;
     if (fd) *fd = entry->fd;
     else if (entry->fd != -1) return 1;  /* silently ignore close attempt if we cannot close the fd */
@@ -330,8 +336,6 @@
     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/hook.c b/server/hook.c
index 125f346..c3a0244 100644
--- a/server/hook.c
+++ b/server/hook.c
@@ -79,6 +79,7 @@
     NULL,                         /* satisfied */
     no_signal,                    /* signal */
     no_get_fd,                    /* get_fd */
+    no_close_handle,              /* close_handle */
     hook_table_destroy            /* destroy */
 };
 
diff --git a/server/mailslot.c b/server/mailslot.c
index 6f24126..347d19f 100644
--- a/server/mailslot.c
+++ b/server/mailslot.c
@@ -76,6 +76,7 @@
     no_satisfied,              /* satisfied */
     no_signal,                 /* signal */
     mailslot_get_fd,           /* get_fd */
+    no_close_handle,           /* close_handle */
     mailslot_destroy           /* destroy */
 };
 
@@ -118,6 +119,7 @@
     NULL,                       /* satisfied */
     no_signal,                  /* signal */
     mail_writer_get_fd,         /* get_fd */
+    no_close_handle,            /* close_handle */
     mail_writer_destroy         /* destroy */
 };
 
diff --git a/server/mapping.c b/server/mapping.c
index 046bc0b..886edc6 100644
--- a/server/mapping.c
+++ b/server/mapping.c
@@ -64,6 +64,7 @@
     NULL,                        /* satisfied */
     no_signal,                   /* signal */
     mapping_get_fd,              /* get_fd */
+    no_close_handle,             /* close_handle */
     mapping_destroy              /* destroy */
 };
 
diff --git a/server/mutex.c b/server/mutex.c
index c1b59bb..c9bee83 100644
--- a/server/mutex.c
+++ b/server/mutex.c
@@ -56,6 +56,7 @@
     mutex_satisfied,           /* satisfied */
     mutex_signal,              /* signal */
     no_get_fd,                 /* get_fd */
+    no_close_handle,           /* close_handle */
     mutex_destroy              /* destroy */
 };
 
diff --git a/server/named_pipe.c b/server/named_pipe.c
index bbf011e..ab4a39d 100644
--- a/server/named_pipe.c
+++ b/server/named_pipe.c
@@ -112,6 +112,7 @@
     NULL,                         /* satisfied */
     no_signal,                    /* signal */
     no_get_fd,                    /* get_fd */
+    no_close_handle,              /* close_handle */
     named_pipe_destroy            /* destroy */
 };
 
@@ -132,6 +133,7 @@
     no_satisfied,                 /* satisfied */
     no_signal,                    /* signal */
     pipe_server_get_fd,           /* get_fd */
+    no_close_handle,              /* close_handle */
     pipe_server_destroy           /* destroy */
 };
 
@@ -162,6 +164,7 @@
     no_satisfied,                 /* satisfied */
     no_signal,                    /* signal */
     pipe_client_get_fd,           /* get_fd */
+    no_close_handle,              /* close_handle */
     pipe_client_destroy           /* destroy */
 };
 
diff --git a/server/object.c b/server/object.c
index 90aa35c..1a629ec 100644
--- a/server/object.c
+++ b/server/object.c
@@ -294,6 +294,11 @@
     return NULL;
 }
 
+int no_close_handle( struct object *obj, struct process *process, obj_handle_t handle )
+{
+    return 1;  /* ok to close */
+}
+
 void no_destroy( struct object *obj )
 {
 }
diff --git a/server/object.h b/server/object.h
index 9fd6581..6551cf8 100644
--- a/server/object.h
+++ b/server/object.h
@@ -63,6 +63,8 @@
     int  (*signal)(struct object *, unsigned int);
     /* return an fd object that can be used to read/write from the object */
     struct fd *(*get_fd)(struct object *);
+    /* close a handle to this object */
+    int (*close_handle)(struct object *,struct process *,obj_handle_t);
     /* destroy on refcount == 0 */
     void (*destroy)(struct object *);
 };
@@ -102,6 +104,7 @@
 extern int no_satisfied( struct object *obj, struct thread *thread );
 extern int no_signal( struct object *obj, unsigned int access );
 extern struct fd *no_get_fd( struct object *obj );
+extern int no_close_handle( struct object *obj, struct process *process, obj_handle_t handle );
 extern void no_destroy( struct object *obj );
 #ifdef DEBUG_OBJECTS
 extern void dump_objects(void);
@@ -146,7 +149,6 @@
 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 );
 
 /* signal functions */
 
diff --git a/server/process.c b/server/process.c
index f2b4012..32d3be0 100644
--- a/server/process.c
+++ b/server/process.c
@@ -70,8 +70,9 @@
     remove_queue,                /* remove_queue */
     process_signaled,            /* signaled */
     no_satisfied,                /* satisfied */
-    no_signal,                    /* signal */
+    no_signal,                   /* signal */
     no_get_fd,                   /* get_fd */
+    no_close_handle,             /* close_handle */
     process_destroy              /* destroy */
 };
 
@@ -119,6 +120,7 @@
     no_satisfied,                  /* satisfied */
     no_signal,                     /* signal */
     no_get_fd,                     /* get_fd */
+    no_close_handle,               /* close_handle */
     startup_info_destroy           /* destroy */
 };
 
diff --git a/server/queue.c b/server/queue.c
index 38e5d67..d827c0f 100644
--- a/server/queue.c
+++ b/server/queue.c
@@ -148,6 +148,7 @@
     msg_queue_satisfied,       /* satisfied */
     no_signal,                 /* signal */
     no_get_fd,                 /* get_fd */
+    no_close_handle,           /* close_handle */
     msg_queue_destroy          /* destroy */
 };
 
@@ -162,6 +163,7 @@
     NULL,                         /* satisfied */
     no_signal,                    /* signal */
     no_get_fd,                    /* get_fd */
+    no_close_handle,              /* close_handle */
     thread_input_destroy          /* destroy */
 };
 
diff --git a/server/registry.c b/server/registry.c
index 5d2d0b9..a38682d 100644
--- a/server/registry.c
+++ b/server/registry.c
@@ -129,6 +129,7 @@
 
 
 static void key_dump( struct object *obj, int verbose );
+static int key_close_handle( struct object *obj, struct process *process, obj_handle_t handle );
 static void key_destroy( struct object *obj );
 
 static const struct object_ops key_ops =
@@ -141,6 +142,7 @@
     NULL,                    /* satisfied */
     no_signal,               /* signal */
     no_get_fd,               /* get_fd */
+    key_close_handle,        /* close_handle */
     key_destroy              /* destroy */
 };
 
@@ -293,17 +295,12 @@
 }
 
 /* close the notification associated with a handle */
-void registry_close_handle( struct object *obj, obj_handle_t hkey )
+static int key_close_handle( struct object *obj, struct process *process, obj_handle_t handle )
 {
     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 );
+    struct notify *notify = find_notify( key, handle );
+    if (notify) do_notification( key, notify, 1 );
+    return 1;  /* ok to close */
 }
 
 static void key_destroy( struct object *obj )
diff --git a/server/request.c b/server/request.c
index aa5c09d..4a2c80a 100644
--- a/server/request.c
+++ b/server/request.c
@@ -95,6 +95,7 @@
     NULL,                          /* satisfied */
     no_signal,                     /* signal */
     no_get_fd,                     /* get_fd */
+    no_close_handle,               /* close_handle */
     master_socket_destroy          /* destroy */
 };
 
diff --git a/server/semaphore.c b/server/semaphore.c
index 7886842..64c3447 100644
--- a/server/semaphore.c
+++ b/server/semaphore.c
@@ -53,6 +53,7 @@
     semaphore_satisfied,           /* satisfied */
     semaphore_signal,              /* signal */
     no_get_fd,                     /* get_fd */
+    no_close_handle,               /* close_handle */
     no_destroy                     /* destroy */
 };
 
diff --git a/server/serial.c b/server/serial.c
index 014142e..73d73ae 100644
--- a/server/serial.c
+++ b/server/serial.c
@@ -103,6 +103,7 @@
     no_satisfied,                 /* satisfied */
     no_signal,                    /* signal */
     serial_get_fd,                /* get_fd */
+    no_close_handle,              /* close_handle */
     serial_destroy                /* destroy */
 };
 
diff --git a/server/signal.c b/server/signal.c
index f3b0f5f..9ce3640 100644
--- a/server/signal.c
+++ b/server/signal.c
@@ -64,6 +64,7 @@
     NULL,                     /* satisfied */
     no_signal,                /* signal */
     no_get_fd,                /* get_fd */
+    no_close_handle,          /* close_handle */
     handler_destroy           /* destroy */
 };
 
diff --git a/server/snapshot.c b/server/snapshot.c
index 7f1670c..6c71b75 100644
--- a/server/snapshot.c
+++ b/server/snapshot.c
@@ -63,6 +63,7 @@
     NULL,                         /* satisfied */
     no_signal,                    /* signal */
     no_get_fd,                    /* get_fd */
+    no_close_handle,              /* close_handle */
     snapshot_destroy              /* destroy */
 };
 
diff --git a/server/sock.c b/server/sock.c
index 3fa3049..535786e 100644
--- a/server/sock.c
+++ b/server/sock.c
@@ -109,6 +109,7 @@
     no_satisfied,                 /* satisfied */
     no_signal,                    /* signal */
     sock_get_fd,                  /* get_fd */
+    no_close_handle,              /* close_handle */
     sock_destroy                  /* destroy */
 };
 
diff --git a/server/thread.c b/server/thread.c
index 3fb06c5..b021dc0 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -94,6 +94,7 @@
     no_satisfied,               /* satisfied */
     no_signal,                  /* signal */
     no_get_fd,                  /* get_fd */
+    no_close_handle,            /* close_handle */
     destroy_thread              /* destroy */
 };
 
diff --git a/server/timer.c b/server/timer.c
index 2329c6c..c4ab0e8 100644
--- a/server/timer.c
+++ b/server/timer.c
@@ -61,6 +61,7 @@
     timer_satisfied,           /* satisfied */
     no_signal,                 /* signal */
     no_get_fd,                 /* get_fd */
+    no_close_handle,           /* close_handle */
     timer_destroy              /* destroy */
 };
 
diff --git a/server/token.c b/server/token.c
index ca1a7ff..0c11edf 100644
--- a/server/token.c
+++ b/server/token.c
@@ -109,6 +109,7 @@
     NULL,                      /* satisfied */
     no_signal,                 /* signal */
     no_get_fd,                 /* get_fd */
+    no_close_handle,           /* close_handle */
     token_destroy              /* destroy */
 };
 
diff --git a/server/winstation.c b/server/winstation.c
index 7e7adab..d42ed66 100644
--- a/server/winstation.c
+++ b/server/winstation.c
@@ -56,8 +56,10 @@
 static struct namespace *winstation_namespace;
 
 static void winstation_dump( struct object *obj, int verbose );
+static int winstation_close_handle( struct object *obj, struct process *process, obj_handle_t handle );
 static void winstation_destroy( struct object *obj );
 static void desktop_dump( struct object *obj, int verbose );
+static int desktop_close_handle( struct object *obj, struct process *process, obj_handle_t handle );
 static void desktop_destroy( struct object *obj );
 
 static const struct object_ops winstation_ops =
@@ -70,6 +72,7 @@
     NULL,                         /* satisfied */
     no_signal,                    /* signal */
     no_get_fd,                    /* get_fd */
+    winstation_close_handle,      /* close_handle */
     winstation_destroy            /* destroy */
 };
 
@@ -84,6 +87,7 @@
     NULL,                         /* satisfied */
     no_signal,                    /* signal */
     no_get_fd,                    /* get_fd */
+    desktop_close_handle,         /* close_handle */
     desktop_destroy               /* destroy */
 };
 
@@ -125,6 +129,11 @@
     fputc( '\n', stderr );
 }
 
+static int winstation_close_handle( struct object *obj, struct process *process, obj_handle_t handle )
+{
+    return (process->winstation != handle);
+}
+
 static void winstation_destroy( struct object *obj )
 {
     struct winstation *winstation = (struct winstation *)obj;
@@ -199,6 +208,17 @@
     fputc( '\n', stderr );
 }
 
+static int desktop_close_handle( struct object *obj, struct process *process, obj_handle_t handle )
+{
+    struct thread *thread;
+
+    /* check if the handle is currently used by the process or one of its threads */
+    if (process->desktop == handle) return 0;
+    LIST_FOR_EACH_ENTRY( thread, &process->thread_list, struct thread, proc_entry )
+        if (thread->desktop == handle) return 0;
+    return 1;
+}
+
 static void desktop_destroy( struct object *obj )
 {
     struct desktop *desktop = (struct desktop *)obj;
@@ -207,19 +227,6 @@
     release_object( desktop->winstation );
 }
 
-/* check if a desktop handle is currently used by the process */
-static int is_desktop_in_use( struct process *process, obj_handle_t handle )
-{
-    struct thread *thread;
-
-    if (process->desktop == handle) return 1;
-
-    LIST_FOR_EACH_ENTRY( thread, &process->thread_list, struct thread, proc_entry )
-        if (thread->desktop == handle) return 1;
-
-    return 0;
-}
-
 /* connect a process to its window station */
 void connect_process_winstation( struct process *process, const WCHAR *name, size_t len )
 {
@@ -246,13 +253,7 @@
     }
     if (winstation)
     {
-        if ((process->winstation = alloc_handle( process, winstation, WINSTA_ALL_ACCESS, FALSE )))
-        {
-            int fd = -1;
-            /* FIXME: Windows doesn't do it this way */
-            set_handle_info( process, process->winstation, HANDLE_FLAG_PROTECT_FROM_CLOSE,
-                             HANDLE_FLAG_PROTECT_FROM_CLOSE, &fd );
-        }
+        process->winstation = alloc_handle( process, winstation, WINSTA_ALL_ACCESS, FALSE );
         release_object( winstation );
     }
     clear_error();  /* ignore errors */
@@ -291,8 +292,8 @@
     obj_handle_t handle = thread->desktop;
 
     thread->desktop = 0;
-    if (handle && !is_desktop_in_use( thread->process, handle ))
-        close_handle( thread->process, handle, NULL );
+    if (handle) close_handle( thread->process, handle, NULL );
+    clear_error();  /* ignore errors */
 }
 
 
@@ -328,10 +329,7 @@
     if ((winstation = (struct winstation *)get_handle_obj( current->process, req->handle,
                                                            0, &winstation_ops )))
     {
-        if (req->handle != current->process->winstation)
-            close_handle( current->process, req->handle, NULL );
-        else
-            set_error( STATUS_ACCESS_DENIED );
+        if (!close_handle( current->process, req->handle, NULL )) set_error( STATUS_ACCESS_DENIED );
         release_object( winstation );
     }
 }
@@ -408,10 +406,7 @@
     if ((desktop = (struct desktop *)get_handle_obj( current->process, req->handle,
                                                      0, &desktop_ops )))
     {
-        if (!is_desktop_in_use( current->process, req->handle ))
-            close_handle( current->process, req->handle, NULL );
-        else
-            set_error( STATUS_DEVICE_BUSY );
+        if (!close_handle( current->process, req->handle, NULL )) set_error( STATUS_DEVICE_BUSY );
         release_object( desktop );
     }
 }