2 * Window stations and desktops
4 * Copyright 2002 Alexandre Julliard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 #define WIN32_NO_STATUS
32 #include "ntgdi_private.h"
33 #include "ntuser_private.h"
34 #include "wine/server.h"
35 #include "wine/debug.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(winstation
);
38 WINE_DECLARE_DEBUG_CHANNEL(win
);
41 #define DESKTOP_ALL_ACCESS 0x01ff
43 BOOL
is_virtual_desktop(void)
45 HANDLE desktop
= NtUserGetThreadDesktop( GetCurrentThreadId() );
46 USEROBJECTFLAGS flags
= {0};
49 if (!NtUserGetObjectInformation( desktop
, UOI_FLAGS
, &flags
, sizeof(flags
), &len
)) return FALSE
;
50 return !!(flags
.dwFlags
& DF_WINE_CREATE_DESKTOP
);
53 /***********************************************************************
54 * NtUserCreateWindowStation (win32u.@)
56 HWINSTA WINAPI
NtUserCreateWindowStation( OBJECT_ATTRIBUTES
*attr
, ACCESS_MASK access
, ULONG arg3
,
57 ULONG arg4
, ULONG arg5
, ULONG arg6
, ULONG arg7
)
61 if (attr
->ObjectName
->Length
>= MAX_PATH
* sizeof(WCHAR
))
63 RtlSetLastWin32Error( ERROR_FILENAME_EXCED_RANGE
);
67 SERVER_START_REQ( create_winstation
)
71 req
->attributes
= attr
->Attributes
;
72 req
->rootdir
= wine_server_obj_handle( attr
->RootDirectory
);
73 wine_server_add_data( req
, attr
->ObjectName
->Buffer
, attr
->ObjectName
->Length
);
74 wine_server_call_err( req
);
75 ret
= wine_server_ptr_handle( reply
->handle
);
81 /******************************************************************************
82 * NtUserOpenWindowStation (win32u.@)
84 HWINSTA WINAPI
NtUserOpenWindowStation( OBJECT_ATTRIBUTES
*attr
, ACCESS_MASK access
)
88 SERVER_START_REQ( open_winstation
)
91 req
->attributes
= attr
->Attributes
;
92 req
->rootdir
= wine_server_obj_handle( attr
->RootDirectory
);
93 wine_server_add_data( req
, attr
->ObjectName
->Buffer
, attr
->ObjectName
->Length
);
94 if (!wine_server_call_err( req
)) ret
= wine_server_ptr_handle( reply
->handle
);
100 /***********************************************************************
101 * NtUserCloseWindowStation (win32u.@)
103 BOOL WINAPI
NtUserCloseWindowStation( HWINSTA handle
)
106 SERVER_START_REQ( close_winstation
)
108 req
->handle
= wine_server_obj_handle( handle
);
109 ret
= !wine_server_call_err( req
);
115 /***********************************************************************
116 * NtUSerGetProcessWindowStation (win32u.@)
118 HWINSTA WINAPI
NtUserGetProcessWindowStation(void)
122 SERVER_START_REQ( get_process_winstation
)
124 if (!wine_server_call_err( req
))
125 ret
= wine_server_ptr_handle( reply
->handle
);
131 /***********************************************************************
132 * NtUserSetProcessWindowStation (win32u.@)
134 BOOL WINAPI
NtUserSetProcessWindowStation( HWINSTA handle
)
138 SERVER_START_REQ( set_process_winstation
)
140 req
->handle
= wine_server_obj_handle( handle
);
141 ret
= !wine_server_call_err( req
);
147 /***********************************************************************
148 * NtUserCreateDesktopEx (win32u.@)
150 HDESK WINAPI
NtUserCreateDesktopEx( OBJECT_ATTRIBUTES
*attr
, UNICODE_STRING
*device
,
151 DEVMODEW
*devmode
, DWORD flags
, ACCESS_MASK access
,
154 WCHAR buffer
[MAX_PATH
];
157 if ((device
&& device
->Length
) || (devmode
&& !(flags
& DF_WINE_CREATE_DESKTOP
)))
159 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER
);
162 if (attr
->ObjectName
->Length
>= MAX_PATH
* sizeof(WCHAR
))
164 RtlSetLastWin32Error( ERROR_FILENAME_EXCED_RANGE
);
167 SERVER_START_REQ( create_desktop
)
170 req
->access
= access
;
171 req
->attributes
= attr
->Attributes
;
172 wine_server_add_data( req
, attr
->ObjectName
->Buffer
, attr
->ObjectName
->Length
);
173 wine_server_call_err( req
);
174 ret
= wine_server_ptr_handle( reply
->handle
);
177 if (!devmode
) return ret
;
179 lstrcpynW( buffer
, attr
->ObjectName
->Buffer
, attr
->ObjectName
->Length
/ sizeof(WCHAR
) + 1 );
180 if (!user_driver
->pCreateDesktop( buffer
, devmode
->dmPelsWidth
, devmode
->dmPelsHeight
))
182 NtUserCloseDesktop( ret
);
186 /* force update display cache to use virtual desktop display settings */
187 if (flags
& DF_WINE_CREATE_DESKTOP
) update_display_cache( TRUE
);
191 /***********************************************************************
192 * NtUserOpenDesktop (win32u.@)
194 HDESK WINAPI
NtUserOpenDesktop( OBJECT_ATTRIBUTES
*attr
, DWORD flags
, ACCESS_MASK access
)
197 if (attr
->ObjectName
->Length
>= MAX_PATH
* sizeof(WCHAR
))
199 RtlSetLastWin32Error( ERROR_FILENAME_EXCED_RANGE
);
202 SERVER_START_REQ( open_desktop
)
204 req
->winsta
= wine_server_obj_handle( attr
->RootDirectory
);
206 req
->access
= access
;
207 req
->attributes
= attr
->Attributes
;
208 wine_server_add_data( req
, attr
->ObjectName
->Buffer
, attr
->ObjectName
->Length
);
209 if (!wine_server_call_err( req
)) ret
= wine_server_ptr_handle( reply
->handle
);
215 /***********************************************************************
216 * NtUserCloseDesktop (win32u.@)
218 BOOL WINAPI
NtUserCloseDesktop( HDESK handle
)
221 SERVER_START_REQ( close_desktop
)
223 req
->handle
= wine_server_obj_handle( handle
);
224 ret
= !wine_server_call_err( req
);
230 /***********************************************************************
231 * NtUserGetThreadDesktop (win32u.@)
233 HDESK WINAPI
NtUserGetThreadDesktop( DWORD thread
)
237 SERVER_START_REQ( get_thread_desktop
)
240 if (!wine_server_call_err( req
)) ret
= wine_server_ptr_handle( reply
->handle
);
246 /***********************************************************************
247 * NtUserSetThreadDesktop (win32u.@)
249 BOOL WINAPI
NtUserSetThreadDesktop( HDESK handle
)
251 BOOL ret
, was_virtual_desktop
= is_virtual_desktop();
253 SERVER_START_REQ( set_thread_desktop
)
255 req
->handle
= wine_server_obj_handle( handle
);
256 ret
= !wine_server_call_err( req
);
260 if (ret
) /* reset the desktop windows */
262 struct user_thread_info
*thread_info
= get_user_thread_info();
263 struct user_key_state_info
*key_state_info
= thread_info
->key_state
;
264 thread_info
->client_info
.top_window
= 0;
265 thread_info
->client_info
.msg_window
= 0;
266 if (key_state_info
) key_state_info
->time
= 0;
267 if (was_virtual_desktop
!= is_virtual_desktop()) update_display_cache( TRUE
);
272 /***********************************************************************
273 * NtUserOpenInputDesktop (win32u.@)
275 HDESK WINAPI
NtUserOpenInputDesktop( DWORD flags
, BOOL inherit
, ACCESS_MASK access
)
279 TRACE( "(%x,%i,%x)\n", (int)flags
, inherit
, (int)access
);
282 FIXME( "partial stub flags %08x\n", (int)flags
);
284 SERVER_START_REQ( open_input_desktop
)
287 req
->access
= access
;
288 req
->attributes
= inherit
? OBJ_INHERIT
: 0;
289 if (!wine_server_call_err( req
)) ret
= wine_server_ptr_handle( reply
->handle
);
296 /***********************************************************************
297 * NtUserGetObjectInformation (win32u.@)
299 BOOL WINAPI
NtUserGetObjectInformation( HANDLE handle
, INT index
, void *info
,
300 DWORD len
, DWORD
*needed
)
304 static const WCHAR desktopW
[] = {'D','e','s','k','t','o','p',0};
305 static const WCHAR window_stationW
[] = {'W','i','n','d','o','w','S','t','a','t','i','o','n',0};
311 USEROBJECTFLAGS
*obj_flags
= info
;
312 if (needed
) *needed
= sizeof(*obj_flags
);
313 if (len
< sizeof(*obj_flags
))
315 RtlSetLastWin32Error( ERROR_BUFFER_OVERFLOW
);
318 SERVER_START_REQ( set_user_object_info
)
320 req
->handle
= wine_server_obj_handle( handle
);
322 ret
= !wine_server_call_err( req
);
325 /* FIXME: inherit flag */
326 obj_flags
->dwFlags
= reply
->old_obj_flags
;
334 SERVER_START_REQ( set_user_object_info
)
336 req
->handle
= wine_server_obj_handle( handle
);
338 ret
= !wine_server_call_err( req
);
341 size_t size
= reply
->is_desktop
? sizeof(desktopW
) : sizeof(window_stationW
);
342 if (needed
) *needed
= size
;
345 RtlSetLastWin32Error( ERROR_INSUFFICIENT_BUFFER
);
348 else memcpy( info
, reply
->is_desktop
? desktopW
: window_stationW
, size
);
356 WCHAR buffer
[MAX_PATH
];
357 SERVER_START_REQ( set_user_object_info
)
359 req
->handle
= wine_server_obj_handle( handle
);
361 wine_server_set_reply( req
, buffer
, sizeof(buffer
) - sizeof(WCHAR
) );
362 ret
= !wine_server_call_err( req
);
365 size_t size
= wine_server_reply_size( reply
);
366 buffer
[size
/ sizeof(WCHAR
)] = 0;
367 size
+= sizeof(WCHAR
);
368 if (needed
) *needed
= size
;
371 RtlSetLastWin32Error( ERROR_INSUFFICIENT_BUFFER
);
374 else memcpy( info
, buffer
, size
);
382 FIXME( "not supported index %d\n", index
);
385 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER
);
390 /***********************************************************************
391 * NtUserSetObjectInformation (win32u.@)
393 BOOL WINAPI
NtUserSetObjectInformation( HANDLE handle
, INT index
, void *info
, DWORD len
)
396 const USEROBJECTFLAGS
*obj_flags
= info
;
398 if (index
!= UOI_FLAGS
|| !info
|| len
< sizeof(*obj_flags
))
400 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER
);
403 /* FIXME: inherit flag */
404 SERVER_START_REQ( set_user_object_info
)
406 req
->handle
= wine_server_obj_handle( handle
);
407 req
->flags
= SET_USER_OBJECT_SET_FLAGS
;
408 req
->obj_flags
= obj_flags
->dwFlags
;
409 ret
= !wine_server_call_err( req
);
416 static inline TEB64
*NtCurrentTeb64(void) { return NULL
; }
418 static inline TEB64
*NtCurrentTeb64(void) { return (TEB64
*)NtCurrentTeb()->GdiBatchCount
; }
421 HWND
get_desktop_window(void)
423 static const WCHAR wine_service_station_name
[] =
424 {'_','_','w','i','n','e','s','e','r','v','i','c','e','_','w','i','n','s','t','a','t','i','o','n',0};
425 struct ntuser_thread_info
*thread_info
= NtUserGetThreadInfo();
426 WCHAR name
[MAX_PATH
];
429 if (thread_info
->top_window
) return UlongToHandle( thread_info
->top_window
);
431 /* don't create an actual explorer desktop window for services */
432 if (NtUserGetObjectInformation( NtUserGetProcessWindowStation(), UOI_NAME
, name
, sizeof(name
), NULL
)
433 && !wcscmp( name
, wine_service_station_name
))
438 SERVER_START_REQ( get_desktop_window
)
440 req
->force
= is_service
;
441 if (!wine_server_call( req
))
443 thread_info
->top_window
= reply
->top_window
;
444 thread_info
->msg_window
= reply
->msg_window
;
449 if (!thread_info
->top_window
)
451 static const WCHAR appnameW
[] = {'\\','?','?','\\','C',':','\\','w','i','n','d','o','w','s',
452 '\\','s','y','s','t','e','m','3','2','\\','e','x','p','l','o','r','e','r','.','e','x','e',0};
453 static const WCHAR cmdlineW
[] = {'"','C',':','\\','w','i','n','d','o','w','s','\\',
454 's','y','s','t','e','m','3','2','\\','e','x','p','l','o','r','e','r','.','e','x','e','"',
455 ' ','/','d','e','s','k','t','o','p',0};
456 static const WCHAR system_dir
[] = {'C',':','\\','w','i','n','d','o','w','s','\\',
457 's','y','s','t','e','m','3','2','\\',0};
458 RTL_USER_PROCESS_PARAMETERS params
= { sizeof(params
), sizeof(params
) };
459 ULONG_PTR buffer
[offsetof( PS_ATTRIBUTE_LIST
, Attributes
[2] ) / sizeof(ULONG_PTR
)];
460 PS_ATTRIBUTE_LIST
*ps_attr
= (PS_ATTRIBUTE_LIST
*)buffer
;
461 PS_CREATE_INFO create_info
;
462 WCHAR desktop
[MAX_PATH
];
463 PEB
*peb
= NtCurrentTeb()->Peb
;
464 HANDLE process
, thread
;
467 SERVER_START_REQ( set_user_object_info
)
469 req
->handle
= wine_server_obj_handle( NtUserGetThreadDesktop(GetCurrentThreadId()) );
470 req
->flags
= SET_USER_OBJECT_GET_FULL_NAME
;
471 wine_server_set_reply( req
, desktop
, sizeof(desktop
) - sizeof(WCHAR
) );
472 if (!wine_server_call( req
))
474 size_t size
= wine_server_reply_size( reply
);
475 desktop
[size
/ sizeof(WCHAR
)] = 0;
476 TRACE( "starting explorer for desktop %s\n", debugstr_w(desktop
) );
483 params
.Flags
= PROCESS_PARAMS_FLAG_NORMALIZED
;
484 params
.Environment
= peb
->ProcessParameters
->Environment
;
485 params
.EnvironmentSize
= peb
->ProcessParameters
->EnvironmentSize
;
486 params
.hStdError
= peb
->ProcessParameters
->hStdError
;
487 RtlInitUnicodeString( ¶ms
.CurrentDirectory
.DosPath
, system_dir
);
488 RtlInitUnicodeString( ¶ms
.ImagePathName
, appnameW
+ 4 );
489 RtlInitUnicodeString( ¶ms
.CommandLine
, cmdlineW
);
490 RtlInitUnicodeString( ¶ms
.WindowTitle
, appnameW
+ 4 );
491 RtlInitUnicodeString( ¶ms
.Desktop
, desktop
);
493 ps_attr
->Attributes
[0].Attribute
= PS_ATTRIBUTE_IMAGE_NAME
;
494 ps_attr
->Attributes
[0].Size
= sizeof(appnameW
) - sizeof(WCHAR
);
495 ps_attr
->Attributes
[0].ValuePtr
= (WCHAR
*)appnameW
;
496 ps_attr
->Attributes
[0].ReturnLength
= NULL
;
498 ps_attr
->Attributes
[1].Attribute
= PS_ATTRIBUTE_TOKEN
;
499 ps_attr
->Attributes
[1].Size
= sizeof(HANDLE
);
500 ps_attr
->Attributes
[1].ValuePtr
= GetCurrentThreadEffectiveToken();
501 ps_attr
->Attributes
[1].ReturnLength
= NULL
;
503 ps_attr
->TotalLength
= offsetof( PS_ATTRIBUTE_LIST
, Attributes
[2] );
505 if (NtCurrentTeb64() && !NtCurrentTeb64()->TlsSlots
[WOW64_TLS_FILESYSREDIR
])
507 NtCurrentTeb64()->TlsSlots
[WOW64_TLS_FILESYSREDIR
] = TRUE
;
508 status
= NtCreateUserProcess( &process
, &thread
, PROCESS_ALL_ACCESS
, THREAD_ALL_ACCESS
,
509 NULL
, NULL
, 0, THREAD_CREATE_FLAGS_CREATE_SUSPENDED
, ¶ms
,
510 &create_info
, ps_attr
);
511 NtCurrentTeb64()->TlsSlots
[WOW64_TLS_FILESYSREDIR
] = FALSE
;
514 status
= NtCreateUserProcess( &process
, &thread
, PROCESS_ALL_ACCESS
, THREAD_ALL_ACCESS
,
515 NULL
, NULL
, 0, THREAD_CREATE_FLAGS_CREATE_SUSPENDED
, ¶ms
,
516 &create_info
, ps_attr
);
519 NtResumeThread( thread
, NULL
);
520 TRACE_(win
)( "started explorer\n" );
521 NtUserWaitForInputIdle( process
, 10000, FALSE
);
525 else ERR_(win
)( "failed to start explorer %x\n", status
);
527 SERVER_START_REQ( get_desktop_window
)
530 if (!wine_server_call( req
))
532 thread_info
->top_window
= reply
->top_window
;
533 thread_info
->msg_window
= reply
->msg_window
;
539 if (!thread_info
->top_window
) ERR_(win
)( "failed to create desktop window\n" );
540 else user_driver
->pSetDesktopWindow( UlongToHandle( thread_info
->top_window
));
542 register_builtin_classes();
543 return UlongToHandle( thread_info
->top_window
);
546 static HANDLE
get_winstations_dir_handle(void)
551 OBJECT_ATTRIBUTES attr
;
555 sprintf( bufferA
, "\\Sessions\\%u\\Windows\\WindowStations", (int)NtCurrentTeb()->Peb
->SessionId
);
557 str
.Length
= str
.MaximumLength
= asciiz_to_unicode( buffer
, bufferA
) - sizeof(WCHAR
);
558 InitializeObjectAttributes( &attr
, &str
, 0, 0, NULL
);
559 status
= NtOpenDirectoryObject( &dir
, DIRECTORY_CREATE_OBJECT
| DIRECTORY_TRAVERSE
, &attr
);
560 return status
? 0 : dir
;
563 /***********************************************************************
564 * get_default_desktop
566 * Get the name of the desktop to use for this app if not specified explicitly.
568 static const WCHAR
*get_default_desktop( void *buf
, size_t buf_size
)
570 const WCHAR
*p
, *appname
= NtCurrentTeb()->Peb
->ProcessParameters
->ImagePathName
.Buffer
;
571 KEY_VALUE_PARTIAL_INFORMATION
*info
= buf
;
576 static const WCHAR defaultW
[] = {'D','e','f','a','u','l','t',0};
578 if ((p
= wcsrchr( appname
, '/' ))) appname
= p
+ 1;
579 if ((p
= wcsrchr( appname
, '\\' ))) appname
= p
+ 1;
580 len
= lstrlenW(appname
);
581 if (len
> MAX_PATH
) return defaultW
;
582 memcpy( buffer
, appname
, len
* sizeof(WCHAR
) );
583 asciiz_to_unicode( buffer
+ len
, "\\Explorer" );
585 /* @@ Wine registry key: HKCU\Software\Wine\AppDefaults\app.exe\Explorer */
586 if ((tmpkey
= reg_open_hkcu_key( "Software\\Wine\\AppDefaults" )))
588 appkey
= reg_open_key( tmpkey
, buffer
, lstrlenW(buffer
) * sizeof(WCHAR
) );
592 len
= query_reg_ascii_value( appkey
, "Desktop", info
, buf_size
);
594 if (len
) return (const WCHAR
*)info
->Data
;
598 /* @@ Wine registry key: HKCU\Software\Wine\Explorer */
599 if ((appkey
= reg_open_hkcu_key( "Software\\Wine\\Explorer" )))
601 len
= query_reg_ascii_value( appkey
, "Desktop", info
, buf_size
);
603 if (len
) return (const WCHAR
*)info
->Data
;
609 /***********************************************************************
612 * Connect to the process window station and desktop.
614 void winstation_init(void)
616 RTL_USER_PROCESS_PARAMETERS
*params
= NtCurrentTeb()->Peb
->ProcessParameters
;
617 WCHAR
*winstation
= NULL
, *desktop
= NULL
, *buffer
= NULL
;
618 HANDLE handle
, dir
= NULL
;
619 OBJECT_ATTRIBUTES attr
;
622 static const WCHAR winsta0
[] = {'W','i','n','S','t','a','0',0};
624 if (params
->Desktop
.Length
)
626 buffer
= malloc( params
->Desktop
.Length
+ sizeof(WCHAR
) );
627 memcpy( buffer
, params
->Desktop
.Buffer
, params
->Desktop
.Length
);
628 buffer
[params
->Desktop
.Length
/ sizeof(WCHAR
)] = 0;
629 if ((desktop
= wcschr( buffer
, '\\' )))
634 else desktop
= buffer
;
637 /* set winstation if explicitly specified, or if we don't have one yet */
638 if (buffer
|| !NtUserGetProcessWindowStation())
640 str
.Buffer
= (WCHAR
*)(winstation
? winstation
: winsta0
);
641 str
.Length
= str
.MaximumLength
= lstrlenW( str
.Buffer
) * sizeof(WCHAR
);
642 dir
= get_winstations_dir_handle();
643 InitializeObjectAttributes( &attr
, &str
, OBJ_CASE_INSENSITIVE
| OBJ_OPENIF
,
646 handle
= NtUserCreateWindowStation( &attr
, STANDARD_RIGHTS_REQUIRED
| WINSTA_ALL_ACCESS
, 0, 0, 0, 0, 0 );
649 NtUserSetProcessWindowStation( handle
);
650 /* only WinSta0 is visible */
651 if (!winstation
|| !wcsicmp( winstation
, winsta0
))
653 USEROBJECTFLAGS flags
;
654 flags
.fInherit
= FALSE
;
655 flags
.fReserved
= FALSE
;
656 flags
.dwFlags
= WSF_VISIBLE
;
657 NtUserSetObjectInformation( handle
, UOI_FLAGS
, &flags
, sizeof(flags
) );
661 if (buffer
|| !NtUserGetThreadDesktop( GetCurrentThreadId() ))
664 str
.Buffer
= (WCHAR
*)(desktop
? desktop
: get_default_desktop( buffer
, sizeof(buffer
) ));
665 str
.Length
= str
.MaximumLength
= lstrlenW( str
.Buffer
) * sizeof(WCHAR
);
666 if (!dir
) dir
= get_winstations_dir_handle();
667 InitializeObjectAttributes( &attr
, &str
, OBJ_CASE_INSENSITIVE
| OBJ_OPENIF
,
670 handle
= NtUserCreateDesktopEx( &attr
, NULL
, NULL
, 0, STANDARD_RIGHTS_REQUIRED
| DESKTOP_ALL_ACCESS
, 0 );
671 if (handle
) NtUserSetThreadDesktop( handle
);