Keep track of the windows and hooks used by a thread to properly
refuse to change the thread desktop when it's in use.
diff --git a/dlls/user/tests/winstation.c b/dlls/user/tests/winstation.c
index 5ce2d72..9f23586 100644
--- a/dlls/user/tests/winstation.c
+++ b/dlls/user/tests/winstation.c
@@ -64,12 +64,9 @@
trace( "created desktop %p\n", d2 );
ok( d2 != 0, "CreateDesktop failed\n" );
- todo_wine
- {
- SetLastError( 0xdeadbeef );
- ok( !SetThreadDesktop( d2 ), "set thread desktop succeeded with existing window\n" );
- ok( GetLastError() == ERROR_BUSY, "bad last error %ld\n", GetLastError() );
- }
+ SetLastError( 0xdeadbeef );
+ ok( !SetThreadDesktop( d2 ), "set thread desktop succeeded with existing window\n" );
+ ok( GetLastError() == ERROR_BUSY, "bad last error %ld\n", GetLastError() );
DestroyWindow( hwnd );
ok( SetThreadDesktop( d2 ), "set thread desktop failed\n" );
diff --git a/server/hook.c b/server/hook.c
index c3a0244..0830450 100644
--- a/server/hook.c
+++ b/server/hook.c
@@ -125,6 +125,7 @@
hook->thread = thread ? (struct thread *)grab_object( thread ) : NULL;
hook->index = index;
list_add_head( &table->hooks[index], &hook->chain );
+ if (thread) thread->desktop_users++;
return hook;
}
@@ -133,7 +134,12 @@
{
free_user_handle( hook->handle );
if (hook->module) free( hook->module );
- if (hook->thread) release_object( hook->thread );
+ if (hook->thread)
+ {
+ assert( hook->thread->desktop_users > 0 );
+ hook->thread->desktop_users--;
+ release_object( hook->thread );
+ }
if (hook->process) release_object( hook->process );
release_object( hook->owner );
list_remove( &hook->chain );
diff --git a/server/thread.c b/server/thread.c
index 819fd9f..46d1e6e 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -140,6 +140,7 @@
thread->suspend = 0;
thread->creation_time = time(NULL);
thread->exit_time = 0;
+ thread->desktop_users = 0;
list_init( &thread->mutex_list );
list_init( &thread->system_apc );
diff --git a/server/thread.h b/server/thread.h
index 749b277..4a175e7 100644
--- a/server/thread.h
+++ b/server/thread.h
@@ -83,6 +83,7 @@
int affinity; /* affinity mask */
int suspend; /* suspend count */
obj_handle_t desktop; /* desktop handle */
+ int desktop_users; /* number of objects using the thread desktop */
time_t creation_time; /* Thread creation time */
time_t exit_time; /* Thread exit time */
struct token *token; /* security token associated with this thread */
diff --git a/server/window.c b/server/window.c
index 20775f2..9904c39 100644
--- a/server/window.c
+++ b/server/window.c
@@ -311,6 +311,8 @@
if (win == shell_listview) shell_listview = NULL;
if (win == progman_window) progman_window = NULL;
if (win == taskman_window) taskman_window = NULL;
+ assert( win->thread->desktop_users > 0 );
+ win->thread->desktop_users--;
free_user_handle( win->handle );
destroy_properties( win );
list_remove( &win->entry );
@@ -376,6 +378,7 @@
/* put it on parent unlinked list */
if (parent) list_add_head( &parent->unlinked, &win->entry );
+ current->desktop_users++;
return win;
failed:
@@ -1303,6 +1306,7 @@
if (!top_window)
{
if (!(top_window = create_window( NULL, NULL, req->atom, req->instance ))) return;
+ current->desktop_users--;
top_window->thread = NULL; /* no thread owns the desktop */
top_window->style = WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
}
diff --git a/server/winstation.c b/server/winstation.c
index d42ed66..ed61df9 100644
--- a/server/winstation.c
+++ b/server/winstation.c
@@ -175,6 +175,13 @@
return full_name;
}
+/* retrieve a pointer to a desktop object */
+inline static struct desktop *get_desktop_obj( struct process *process, obj_handle_t handle,
+ unsigned int access )
+{
+ return (struct desktop *)get_handle_obj( process, handle, access, &desktop_ops );
+}
+
/* create a desktop object */
static struct desktop *create_desktop( const WCHAR *name, size_t len, unsigned int flags,
struct winstation *winstation )
@@ -426,14 +433,39 @@
/* set the thread current desktop */
DECL_HANDLER(set_thread_desktop)
{
- struct desktop *desktop;
+ struct desktop *old_desktop, *new_desktop;
+ struct winstation *winstation;
- if ((desktop = (struct desktop *)get_handle_obj( current->process, req->handle, 0, &desktop_ops )))
+ if (!(winstation = get_process_winstation( current->process, 0 /* FIXME: access rights? */ )))
+ return;
+
+ if (!(new_desktop = get_desktop_obj( current->process, req->handle, 0 )))
{
- /* FIXME: should we close the old one? */
- current->desktop = req->handle;
- release_object( desktop );
+ release_object( winstation );
+ return;
}
+ if (new_desktop->winstation != winstation)
+ {
+ set_error( STATUS_ACCESS_DENIED );
+ release_object( new_desktop );
+ release_object( winstation );
+ return;
+ }
+
+ /* check if we are changing to a new desktop */
+
+ if (!(old_desktop = get_desktop_obj( current->process, current->desktop, 0)))
+ clear_error(); /* ignore error */
+
+ /* when changing desktop, we can't have any users on the current one */
+ if (old_desktop != new_desktop && current->desktop_users > 0)
+ set_error( STATUS_DEVICE_BUSY );
+ else
+ current->desktop = req->handle; /* FIXME: should we close the old one? */
+
+ if (old_desktop) release_object( old_desktop );
+ release_object( new_desktop );
+ release_object( winstation );
}