diff --git a/dlls/user/tests/winstation.c b/dlls/user/tests/winstation.c index 5ce2d72dd0..9f2358673c 100644 --- a/dlls/user/tests/winstation.c +++ b/dlls/user/tests/winstation.c @@ -64,12 +64,9 @@ static DWORD CALLBACK thread( LPVOID arg ) 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 c3a0244895..0830450bce 100644 --- a/server/hook.c +++ b/server/hook.c @@ -125,6 +125,7 @@ static struct hook *add_hook( struct thread *thread, int index, int global ) 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 @@ static void free_hook( struct hook *hook ) { 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 819fd9f78b..46d1e6ed20 100644 --- a/server/thread.c +++ b/server/thread.c @@ -140,6 +140,7 @@ inline static void init_thread_structure( struct thread *thread ) 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 749b277b4e..4a175e796e 100644 --- a/server/thread.h +++ b/server/thread.h @@ -83,6 +83,7 @@ struct thread 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 20775f2745..9904c39fcd 100644 --- a/server/window.c +++ b/server/window.c @@ -311,6 +311,8 @@ static void destroy_window( struct window *win ) 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 @@ static struct window *create_window( struct window *parent, struct window *owner /* 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 @@ DECL_HANDLER(create_window) 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 d42ed665e6..ed61df9642 100644 --- a/server/winstation.c +++ b/server/winstation.c @@ -175,6 +175,13 @@ static WCHAR *build_desktop_name( const WCHAR *name, size_t len, 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 @@ DECL_HANDLER(get_thread_desktop) /* 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 ); }