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 /***********************************************************************
44 * NtUserCreateWindowStation (win32u.@)
46 HWINSTA WINAPI
NtUserCreateWindowStation( OBJECT_ATTRIBUTES
*attr
, ACCESS_MASK access
, ULONG arg3
,
47 ULONG arg4
, ULONG arg5
, ULONG arg6
, ULONG arg7
)
51 if (attr
->ObjectName
->Length
>= MAX_PATH
* sizeof(WCHAR
))
53 RtlSetLastWin32Error( ERROR_FILENAME_EXCED_RANGE
);
57 SERVER_START_REQ( create_winstation
)
61 req
->attributes
= attr
->Attributes
;
62 req
->rootdir
= wine_server_obj_handle( attr
->RootDirectory
);
63 wine_server_add_data( req
, attr
->ObjectName
->Buffer
, attr
->ObjectName
->Length
);
64 wine_server_call_err( req
);
65 ret
= wine_server_ptr_handle( reply
->handle
);
71 /******************************************************************************
72 * NtUserOpenWindowStation (win32u.@)
74 HWINSTA WINAPI
NtUserOpenWindowStation( OBJECT_ATTRIBUTES
*attr
, ACCESS_MASK access
)
78 SERVER_START_REQ( open_winstation
)
81 req
->attributes
= attr
->Attributes
;
82 req
->rootdir
= wine_server_obj_handle( attr
->RootDirectory
);
83 wine_server_add_data( req
, attr
->ObjectName
->Buffer
, attr
->ObjectName
->Length
);
84 if (!wine_server_call_err( req
)) ret
= wine_server_ptr_handle( reply
->handle
);
90 /***********************************************************************
91 * NtUserCloseWindowStation (win32u.@)
93 BOOL WINAPI
NtUserCloseWindowStation( HWINSTA handle
)
96 SERVER_START_REQ( close_winstation
)
98 req
->handle
= wine_server_obj_handle( handle
);
99 ret
= !wine_server_call_err( req
);
105 /***********************************************************************
106 * NtUSerGetProcessWindowStation (win32u.@)
108 HWINSTA WINAPI
NtUserGetProcessWindowStation(void)
112 SERVER_START_REQ( get_process_winstation
)
114 if (!wine_server_call_err( req
))
115 ret
= wine_server_ptr_handle( reply
->handle
);
121 /***********************************************************************
122 * NtUserSetProcessWindowStation (win32u.@)
124 BOOL WINAPI
NtUserSetProcessWindowStation( HWINSTA handle
)
128 SERVER_START_REQ( set_process_winstation
)
130 req
->handle
= wine_server_obj_handle( handle
);
131 ret
= !wine_server_call_err( req
);
137 /***********************************************************************
138 * NtUserCreateDesktopEx (win32u.@)
140 HDESK WINAPI
NtUserCreateDesktopEx( OBJECT_ATTRIBUTES
*attr
, UNICODE_STRING
*device
,
141 DEVMODEW
*devmode
, DWORD flags
, ACCESS_MASK access
,
144 WCHAR buffer
[MAX_PATH
];
147 if ((device
&& device
->Length
) || (devmode
&& !(flags
& DF_WINE_CREATE_DESKTOP
)))
149 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER
);
152 if (attr
->ObjectName
->Length
>= MAX_PATH
* sizeof(WCHAR
))
154 RtlSetLastWin32Error( ERROR_FILENAME_EXCED_RANGE
);
157 SERVER_START_REQ( create_desktop
)
160 req
->access
= access
;
161 req
->attributes
= attr
->Attributes
;
162 wine_server_add_data( req
, attr
->ObjectName
->Buffer
, attr
->ObjectName
->Length
);
163 wine_server_call_err( req
);
164 ret
= wine_server_ptr_handle( reply
->handle
);
167 if (!devmode
) return ret
;
169 lstrcpynW( buffer
, attr
->ObjectName
->Buffer
, attr
->ObjectName
->Length
/ sizeof(WCHAR
) + 1 );
170 if (!user_driver
->pCreateDesktop( buffer
, devmode
->dmPelsWidth
, devmode
->dmPelsHeight
))
172 NtUserCloseDesktop( ret
);
179 /***********************************************************************
180 * NtUserOpenDesktop (win32u.@)
182 HDESK WINAPI
NtUserOpenDesktop( OBJECT_ATTRIBUTES
*attr
, DWORD flags
, ACCESS_MASK access
)
185 if (attr
->ObjectName
->Length
>= MAX_PATH
* sizeof(WCHAR
))
187 RtlSetLastWin32Error( ERROR_FILENAME_EXCED_RANGE
);
190 SERVER_START_REQ( open_desktop
)
192 req
->winsta
= wine_server_obj_handle( attr
->RootDirectory
);
194 req
->access
= access
;
195 req
->attributes
= attr
->Attributes
;
196 wine_server_add_data( req
, attr
->ObjectName
->Buffer
, attr
->ObjectName
->Length
);
197 if (!wine_server_call_err( req
)) ret
= wine_server_ptr_handle( reply
->handle
);
203 /***********************************************************************
204 * NtUserCloseDesktop (win32u.@)
206 BOOL WINAPI
NtUserCloseDesktop( HDESK handle
)
209 SERVER_START_REQ( close_desktop
)
211 req
->handle
= wine_server_obj_handle( handle
);
212 ret
= !wine_server_call_err( req
);
218 /***********************************************************************
219 * NtUserGetThreadDesktop (win32u.@)
221 HDESK WINAPI
NtUserGetThreadDesktop( DWORD thread
)
225 SERVER_START_REQ( get_thread_desktop
)
228 if (!wine_server_call_err( req
)) ret
= wine_server_ptr_handle( reply
->handle
);
234 /***********************************************************************
235 * NtUserSetThreadDesktop (win32u.@)
237 BOOL WINAPI
NtUserSetThreadDesktop( HDESK handle
)
241 SERVER_START_REQ( set_thread_desktop
)
243 req
->handle
= wine_server_obj_handle( handle
);
244 ret
= !wine_server_call_err( req
);
248 if (ret
) /* reset the desktop windows */
250 struct user_thread_info
*thread_info
= get_user_thread_info();
251 struct user_key_state_info
*key_state_info
= thread_info
->key_state
;
252 thread_info
->client_info
.top_window
= 0;
253 thread_info
->client_info
.msg_window
= 0;
254 if (key_state_info
) key_state_info
->time
= 0;
259 /***********************************************************************
260 * NtUserOpenInputDesktop (win32u.@)
262 HDESK WINAPI
NtUserOpenInputDesktop( DWORD flags
, BOOL inherit
, ACCESS_MASK access
)
266 TRACE( "(%x,%i,%x)\n", (int)flags
, inherit
, (int)access
);
269 FIXME( "partial stub flags %08x\n", (int)flags
);
271 SERVER_START_REQ( open_input_desktop
)
274 req
->access
= access
;
275 req
->attributes
= inherit
? OBJ_INHERIT
: 0;
276 if (!wine_server_call_err( req
)) ret
= wine_server_ptr_handle( reply
->handle
);
283 /***********************************************************************
284 * NtUserGetObjectInformation (win32u.@)
286 BOOL WINAPI
NtUserGetObjectInformation( HANDLE handle
, INT index
, void *info
,
287 DWORD len
, DWORD
*needed
)
291 static const WCHAR desktopW
[] = {'D','e','s','k','t','o','p',0};
292 static const WCHAR window_stationW
[] = {'W','i','n','d','o','w','S','t','a','t','i','o','n',0};
298 USEROBJECTFLAGS
*obj_flags
= info
;
299 if (needed
) *needed
= sizeof(*obj_flags
);
300 if (len
< sizeof(*obj_flags
))
302 RtlSetLastWin32Error( ERROR_BUFFER_OVERFLOW
);
305 SERVER_START_REQ( set_user_object_info
)
307 req
->handle
= wine_server_obj_handle( handle
);
309 ret
= !wine_server_call_err( req
);
312 /* FIXME: inherit flag */
313 obj_flags
->dwFlags
= reply
->old_obj_flags
;
321 SERVER_START_REQ( set_user_object_info
)
323 req
->handle
= wine_server_obj_handle( handle
);
325 ret
= !wine_server_call_err( req
);
328 size_t size
= reply
->is_desktop
? sizeof(desktopW
) : sizeof(window_stationW
);
329 if (needed
) *needed
= size
;
332 RtlSetLastWin32Error( ERROR_INSUFFICIENT_BUFFER
);
335 else memcpy( info
, reply
->is_desktop
? desktopW
: window_stationW
, size
);
343 WCHAR buffer
[MAX_PATH
];
344 SERVER_START_REQ( set_user_object_info
)
346 req
->handle
= wine_server_obj_handle( handle
);
348 wine_server_set_reply( req
, buffer
, sizeof(buffer
) - sizeof(WCHAR
) );
349 ret
= !wine_server_call_err( req
);
352 size_t size
= wine_server_reply_size( reply
);
353 buffer
[size
/ sizeof(WCHAR
)] = 0;
354 size
+= sizeof(WCHAR
);
355 if (needed
) *needed
= size
;
358 RtlSetLastWin32Error( ERROR_INSUFFICIENT_BUFFER
);
361 else memcpy( info
, buffer
, size
);
369 FIXME( "not supported index %d\n", index
);
372 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER
);
377 /***********************************************************************
378 * NtUserSetObjectInformation (win32u.@)
380 BOOL WINAPI
NtUserSetObjectInformation( HANDLE handle
, INT index
, void *info
, DWORD len
)
383 const USEROBJECTFLAGS
*obj_flags
= info
;
385 if (index
!= UOI_FLAGS
|| !info
|| len
< sizeof(*obj_flags
))
387 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER
);
390 /* FIXME: inherit flag */
391 SERVER_START_REQ( set_user_object_info
)
393 req
->handle
= wine_server_obj_handle( handle
);
394 req
->flags
= SET_USER_OBJECT_SET_FLAGS
;
395 req
->obj_flags
= obj_flags
->dwFlags
;
396 ret
= !wine_server_call_err( req
);
403 static inline TEB64
*NtCurrentTeb64(void) { return NULL
; }
405 static inline TEB64
*NtCurrentTeb64(void) { return (TEB64
*)NtCurrentTeb()->GdiBatchCount
; }
408 HWND
get_desktop_window(void)
410 struct ntuser_thread_info
*thread_info
= NtUserGetThreadInfo();
412 if (thread_info
->top_window
) return UlongToHandle( thread_info
->top_window
);
415 SERVER_START_REQ( get_desktop_window
)
418 if (!wine_server_call( req
))
420 thread_info
->top_window
= reply
->top_window
;
421 thread_info
->msg_window
= reply
->msg_window
;
426 if (!thread_info
->top_window
)
428 static const WCHAR appnameW
[] = {'\\','?','?','\\','C',':','\\','w','i','n','d','o','w','s',
429 '\\','s','y','s','t','e','m','3','2','\\','e','x','p','l','o','r','e','r','.','e','x','e',0};
430 static const WCHAR cmdlineW
[] = {'"','C',':','\\','w','i','n','d','o','w','s','\\',
431 's','y','s','t','e','m','3','2','\\','e','x','p','l','o','r','e','r','.','e','x','e','"',
432 ' ','/','d','e','s','k','t','o','p',0};
433 static const WCHAR system_dir
[] = {'C',':','\\','w','i','n','d','o','w','s','\\',
434 's','y','s','t','e','m','3','2','\\',0};
435 RTL_USER_PROCESS_PARAMETERS params
= { sizeof(params
), sizeof(params
) };
436 PS_ATTRIBUTE_LIST ps_attr
;
437 PS_CREATE_INFO create_info
;
438 WCHAR desktop
[MAX_PATH
];
439 PEB
*peb
= NtCurrentTeb()->Peb
;
440 HANDLE process
, thread
;
443 SERVER_START_REQ( set_user_object_info
)
445 req
->handle
= wine_server_obj_handle( NtUserGetThreadDesktop(GetCurrentThreadId()) );
446 req
->flags
= SET_USER_OBJECT_GET_FULL_NAME
;
447 wine_server_set_reply( req
, desktop
, sizeof(desktop
) - sizeof(WCHAR
) );
448 if (!wine_server_call( req
))
450 size_t size
= wine_server_reply_size( reply
);
451 desktop
[size
/ sizeof(WCHAR
)] = 0;
452 TRACE( "starting explorer for desktop %s\n", debugstr_w(desktop
) );
459 params
.Flags
= PROCESS_PARAMS_FLAG_NORMALIZED
;
460 params
.Environment
= peb
->ProcessParameters
->Environment
;
461 params
.EnvironmentSize
= peb
->ProcessParameters
->EnvironmentSize
;
462 params
.hStdError
= peb
->ProcessParameters
->hStdError
;
463 RtlInitUnicodeString( ¶ms
.CurrentDirectory
.DosPath
, system_dir
);
464 RtlInitUnicodeString( ¶ms
.ImagePathName
, appnameW
+ 4 );
465 RtlInitUnicodeString( ¶ms
.CommandLine
, cmdlineW
);
466 RtlInitUnicodeString( ¶ms
.WindowTitle
, appnameW
+ 4 );
467 RtlInitUnicodeString( ¶ms
.Desktop
, desktop
);
469 ps_attr
.TotalLength
= sizeof(ps_attr
);
470 ps_attr
.Attributes
[0].Attribute
= PS_ATTRIBUTE_IMAGE_NAME
;
471 ps_attr
.Attributes
[0].Size
= sizeof(appnameW
) - sizeof(WCHAR
);
472 ps_attr
.Attributes
[0].ValuePtr
= (WCHAR
*)appnameW
;
473 ps_attr
.Attributes
[0].ReturnLength
= NULL
;
475 if (NtCurrentTeb64() && !NtCurrentTeb64()->TlsSlots
[WOW64_TLS_FILESYSREDIR
])
477 NtCurrentTeb64()->TlsSlots
[WOW64_TLS_FILESYSREDIR
] = TRUE
;
478 status
= NtCreateUserProcess( &process
, &thread
, PROCESS_ALL_ACCESS
, THREAD_ALL_ACCESS
,
479 NULL
, NULL
, 0, THREAD_CREATE_FLAGS_CREATE_SUSPENDED
, ¶ms
,
480 &create_info
, &ps_attr
);
481 NtCurrentTeb64()->TlsSlots
[WOW64_TLS_FILESYSREDIR
] = FALSE
;
484 status
= NtCreateUserProcess( &process
, &thread
, PROCESS_ALL_ACCESS
, THREAD_ALL_ACCESS
,
485 NULL
, NULL
, 0, THREAD_CREATE_FLAGS_CREATE_SUSPENDED
, ¶ms
,
486 &create_info
, &ps_attr
);
489 NtResumeThread( thread
, NULL
);
490 TRACE_(win
)( "started explorer\n" );
491 NtUserWaitForInputIdle( process
, 10000, FALSE
);
495 else ERR_(win
)( "failed to start explorer %x\n", status
);
497 SERVER_START_REQ( get_desktop_window
)
500 if (!wine_server_call( req
))
502 thread_info
->top_window
= reply
->top_window
;
503 thread_info
->msg_window
= reply
->msg_window
;
509 if (!thread_info
->top_window
) ERR_(win
)( "failed to create desktop window\n" );
510 else user_driver
->pSetDesktopWindow( UlongToHandle( thread_info
->top_window
));
512 register_builtin_classes();
513 return UlongToHandle( thread_info
->top_window
);
516 static HANDLE
get_winstations_dir_handle(void)
521 OBJECT_ATTRIBUTES attr
;
525 sprintf( bufferA
, "\\Sessions\\%u\\Windows\\WindowStations", (int)NtCurrentTeb()->Peb
->SessionId
);
527 str
.Length
= str
.MaximumLength
= asciiz_to_unicode( buffer
, bufferA
) - sizeof(WCHAR
);
528 InitializeObjectAttributes( &attr
, &str
, 0, 0, NULL
);
529 status
= NtOpenDirectoryObject( &dir
, DIRECTORY_CREATE_OBJECT
| DIRECTORY_TRAVERSE
, &attr
);
530 return status
? 0 : dir
;
533 /***********************************************************************
534 * get_default_desktop
536 * Get the name of the desktop to use for this app if not specified explicitly.
538 static const WCHAR
*get_default_desktop( void *buf
, size_t buf_size
)
540 const WCHAR
*p
, *appname
= NtCurrentTeb()->Peb
->ProcessParameters
->ImagePathName
.Buffer
;
541 KEY_VALUE_PARTIAL_INFORMATION
*info
= buf
;
546 static const WCHAR defaultW
[] = {'D','e','f','a','u','l','t',0};
548 if ((p
= wcsrchr( appname
, '/' ))) appname
= p
+ 1;
549 if ((p
= wcsrchr( appname
, '\\' ))) appname
= p
+ 1;
550 len
= lstrlenW(appname
);
551 if (len
> MAX_PATH
) return defaultW
;
552 memcpy( buffer
, appname
, len
* sizeof(WCHAR
) );
553 asciiz_to_unicode( buffer
+ len
, "\\Explorer" );
555 /* @@ Wine registry key: HKCU\Software\Wine\AppDefaults\app.exe\Explorer */
556 if ((tmpkey
= reg_open_hkcu_key( "Software\\Wine\\AppDefaults" )))
558 appkey
= reg_open_key( tmpkey
, buffer
, lstrlenW(buffer
) * sizeof(WCHAR
) );
562 len
= query_reg_ascii_value( appkey
, "Desktop", info
, buf_size
);
564 if (len
) return (const WCHAR
*)info
->Data
;
568 /* @@ Wine registry key: HKCU\Software\Wine\Explorer */
569 if ((appkey
= reg_open_hkcu_key( "Software\\Wine\\Explorer" )))
571 len
= query_reg_ascii_value( appkey
, "Desktop", info
, buf_size
);
573 if (len
) return (const WCHAR
*)info
->Data
;
579 /***********************************************************************
582 * Connect to the process window station and desktop.
584 void winstation_init(void)
586 RTL_USER_PROCESS_PARAMETERS
*params
= NtCurrentTeb()->Peb
->ProcessParameters
;
587 WCHAR
*winstation
= NULL
, *desktop
= NULL
, *buffer
= NULL
;
588 HANDLE handle
, dir
= NULL
;
589 OBJECT_ATTRIBUTES attr
;
592 static const WCHAR winsta0
[] = {'W','i','n','S','t','a','0',0};
594 if (params
->Desktop
.Length
)
596 buffer
= malloc( params
->Desktop
.Length
+ sizeof(WCHAR
) );
597 memcpy( buffer
, params
->Desktop
.Buffer
, params
->Desktop
.Length
);
598 buffer
[params
->Desktop
.Length
/ sizeof(WCHAR
)] = 0;
599 if ((desktop
= wcschr( buffer
, '\\' )))
604 else desktop
= buffer
;
607 /* set winstation if explicitly specified, or if we don't have one yet */
608 if (buffer
|| !NtUserGetProcessWindowStation())
610 str
.Buffer
= (WCHAR
*)(winstation
? winstation
: winsta0
);
611 str
.Length
= str
.MaximumLength
= lstrlenW( str
.Buffer
) * sizeof(WCHAR
);
612 dir
= get_winstations_dir_handle();
613 InitializeObjectAttributes( &attr
, &str
, OBJ_CASE_INSENSITIVE
| OBJ_OPENIF
,
616 handle
= NtUserCreateWindowStation( &attr
, STANDARD_RIGHTS_REQUIRED
| WINSTA_ALL_ACCESS
, 0, 0, 0, 0, 0 );
619 NtUserSetProcessWindowStation( handle
);
620 /* only WinSta0 is visible */
621 if (!winstation
|| !wcsicmp( winstation
, winsta0
))
623 USEROBJECTFLAGS flags
;
624 flags
.fInherit
= FALSE
;
625 flags
.fReserved
= FALSE
;
626 flags
.dwFlags
= WSF_VISIBLE
;
627 NtUserSetObjectInformation( handle
, UOI_FLAGS
, &flags
, sizeof(flags
) );
631 if (buffer
|| !NtUserGetThreadDesktop( GetCurrentThreadId() ))
634 str
.Buffer
= (WCHAR
*)(desktop
? desktop
: get_default_desktop( buffer
, sizeof(buffer
) ));
635 str
.Length
= str
.MaximumLength
= lstrlenW( str
.Buffer
) * sizeof(WCHAR
);
636 if (!dir
) dir
= get_winstations_dir_handle();
637 InitializeObjectAttributes( &attr
, &str
, OBJ_CASE_INSENSITIVE
| OBJ_OPENIF
,
640 handle
= NtUserCreateDesktopEx( &attr
, NULL
, NULL
, 0, STANDARD_RIGHTS_REQUIRED
| DESKTOP_ALL_ACCESS
, 0 );
641 if (handle
) NtUserSetThreadDesktop( handle
);