explorer: Call user driver through a new CreateDesktop callback.
[wine.git] / dlls / win32u / winstation.c
blob466b2e6262ca97c9fd77bd8b475609662749f452
1 /*
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
21 #if 0
22 #pragma makedep unix
23 #endif
25 #include "ntstatus.h"
26 #define WIN32_NO_STATUS
27 #include <stdarg.h>
28 #include "windef.h"
29 #include "winbase.h"
30 #include "ntuser.h"
31 #include "ddk/wdm.h"
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 )
49 HANDLE ret;
51 if (attr->ObjectName->Length >= MAX_PATH * sizeof(WCHAR))
53 RtlSetLastWin32Error( ERROR_FILENAME_EXCED_RANGE );
54 return 0;
57 SERVER_START_REQ( create_winstation )
59 req->flags = 0;
60 req->access = access;
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 );
67 SERVER_END_REQ;
68 return ret;
71 /******************************************************************************
72 * NtUserOpenWindowStation (win32u.@)
74 HWINSTA WINAPI NtUserOpenWindowStation( OBJECT_ATTRIBUTES *attr, ACCESS_MASK access )
76 HANDLE ret = 0;
78 SERVER_START_REQ( open_winstation )
80 req->access = access;
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 );
86 SERVER_END_REQ;
87 return ret;
90 /***********************************************************************
91 * NtUserCloseWindowStation (win32u.@)
93 BOOL WINAPI NtUserCloseWindowStation( HWINSTA handle )
95 BOOL ret;
96 SERVER_START_REQ( close_winstation )
98 req->handle = wine_server_obj_handle( handle );
99 ret = !wine_server_call_err( req );
101 SERVER_END_REQ;
102 return ret;
105 /***********************************************************************
106 * NtUSerGetProcessWindowStation (win32u.@)
108 HWINSTA WINAPI NtUserGetProcessWindowStation(void)
110 HWINSTA ret = 0;
112 SERVER_START_REQ( get_process_winstation )
114 if (!wine_server_call_err( req ))
115 ret = wine_server_ptr_handle( reply->handle );
117 SERVER_END_REQ;
118 return ret;
121 /***********************************************************************
122 * NtUserSetProcessWindowStation (win32u.@)
124 BOOL WINAPI NtUserSetProcessWindowStation( HWINSTA handle )
126 BOOL ret;
128 SERVER_START_REQ( set_process_winstation )
130 req->handle = wine_server_obj_handle( handle );
131 ret = !wine_server_call_err( req );
133 SERVER_END_REQ;
134 return ret;
137 /***********************************************************************
138 * NtUserCreateDesktopEx (win32u.@)
140 HDESK WINAPI NtUserCreateDesktopEx( OBJECT_ATTRIBUTES *attr, UNICODE_STRING *device,
141 DEVMODEW *devmode, DWORD flags, ACCESS_MASK access,
142 ULONG heap_size )
144 WCHAR buffer[MAX_PATH];
145 HANDLE ret;
147 if ((device && device->Length) || (devmode && !(flags & DF_WINE_CREATE_DESKTOP)))
149 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
150 return 0;
152 if (attr->ObjectName->Length >= MAX_PATH * sizeof(WCHAR))
154 RtlSetLastWin32Error( ERROR_FILENAME_EXCED_RANGE );
155 return 0;
157 SERVER_START_REQ( create_desktop )
159 req->flags = flags;
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 );
166 SERVER_END_REQ;
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 );
173 return 0;
176 return ret;
179 /***********************************************************************
180 * NtUserOpenDesktop (win32u.@)
182 HDESK WINAPI NtUserOpenDesktop( OBJECT_ATTRIBUTES *attr, DWORD flags, ACCESS_MASK access )
184 HANDLE ret = 0;
185 if (attr->ObjectName->Length >= MAX_PATH * sizeof(WCHAR))
187 RtlSetLastWin32Error( ERROR_FILENAME_EXCED_RANGE );
188 return 0;
190 SERVER_START_REQ( open_desktop )
192 req->winsta = wine_server_obj_handle( attr->RootDirectory );
193 req->flags = flags;
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 );
199 SERVER_END_REQ;
200 return ret;
203 /***********************************************************************
204 * NtUserCloseDesktop (win32u.@)
206 BOOL WINAPI NtUserCloseDesktop( HDESK handle )
208 BOOL ret;
209 SERVER_START_REQ( close_desktop )
211 req->handle = wine_server_obj_handle( handle );
212 ret = !wine_server_call_err( req );
214 SERVER_END_REQ;
215 return ret;
218 /***********************************************************************
219 * NtUserGetThreadDesktop (win32u.@)
221 HDESK WINAPI NtUserGetThreadDesktop( DWORD thread )
223 HDESK ret = 0;
225 SERVER_START_REQ( get_thread_desktop )
227 req->tid = thread;
228 if (!wine_server_call_err( req )) ret = wine_server_ptr_handle( reply->handle );
230 SERVER_END_REQ;
231 return ret;
234 /***********************************************************************
235 * NtUserSetThreadDesktop (win32u.@)
237 BOOL WINAPI NtUserSetThreadDesktop( HDESK handle )
239 BOOL ret;
241 SERVER_START_REQ( set_thread_desktop )
243 req->handle = wine_server_obj_handle( handle );
244 ret = !wine_server_call_err( req );
246 SERVER_END_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;
256 return ret;
259 /***********************************************************************
260 * NtUserOpenInputDesktop (win32u.@)
262 HDESK WINAPI NtUserOpenInputDesktop( DWORD flags, BOOL inherit, ACCESS_MASK access )
264 HANDLE ret = 0;
266 TRACE( "(%x,%i,%x)\n", (int)flags, inherit, (int)access );
268 if (flags)
269 FIXME( "partial stub flags %08x\n", (int)flags );
271 SERVER_START_REQ( open_input_desktop )
273 req->flags = flags;
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 );
278 SERVER_END_REQ;
280 return ret;
283 /***********************************************************************
284 * NtUserGetObjectInformation (win32u.@)
286 BOOL WINAPI NtUserGetObjectInformation( HANDLE handle, INT index, void *info,
287 DWORD len, DWORD *needed )
289 BOOL ret;
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};
294 switch(index)
296 case UOI_FLAGS:
298 USEROBJECTFLAGS *obj_flags = info;
299 if (needed) *needed = sizeof(*obj_flags);
300 if (len < sizeof(*obj_flags))
302 RtlSetLastWin32Error( ERROR_BUFFER_OVERFLOW );
303 return FALSE;
305 SERVER_START_REQ( set_user_object_info )
307 req->handle = wine_server_obj_handle( handle );
308 req->flags = 0;
309 ret = !wine_server_call_err( req );
310 if (ret)
312 /* FIXME: inherit flag */
313 obj_flags->dwFlags = reply->old_obj_flags;
316 SERVER_END_REQ;
318 return ret;
320 case UOI_TYPE:
321 SERVER_START_REQ( set_user_object_info )
323 req->handle = wine_server_obj_handle( handle );
324 req->flags = 0;
325 ret = !wine_server_call_err( req );
326 if (ret)
328 size_t size = reply->is_desktop ? sizeof(desktopW) : sizeof(window_stationW);
329 if (needed) *needed = size;
330 if (len < size)
332 RtlSetLastWin32Error( ERROR_INSUFFICIENT_BUFFER );
333 ret = FALSE;
335 else memcpy( info, reply->is_desktop ? desktopW : window_stationW, size );
338 SERVER_END_REQ;
339 return ret;
341 case UOI_NAME:
343 WCHAR buffer[MAX_PATH];
344 SERVER_START_REQ( set_user_object_info )
346 req->handle = wine_server_obj_handle( handle );
347 req->flags = 0;
348 wine_server_set_reply( req, buffer, sizeof(buffer) - sizeof(WCHAR) );
349 ret = !wine_server_call_err( req );
350 if (ret)
352 size_t size = wine_server_reply_size( reply );
353 buffer[size / sizeof(WCHAR)] = 0;
354 size += sizeof(WCHAR);
355 if (needed) *needed = size;
356 if (len < size)
358 RtlSetLastWin32Error( ERROR_INSUFFICIENT_BUFFER );
359 ret = FALSE;
361 else memcpy( info, buffer, size );
364 SERVER_END_REQ;
366 return ret;
368 case UOI_USER_SID:
369 FIXME( "not supported index %d\n", index );
370 /* fall through */
371 default:
372 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
373 return FALSE;
377 /***********************************************************************
378 * NtUserSetObjectInformation (win32u.@)
380 BOOL WINAPI NtUserSetObjectInformation( HANDLE handle, INT index, void *info, DWORD len )
382 BOOL ret;
383 const USEROBJECTFLAGS *obj_flags = info;
385 if (index != UOI_FLAGS || !info || len < sizeof(*obj_flags))
387 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
388 return FALSE;
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 );
398 SERVER_END_REQ;
399 return ret;
402 #ifdef _WIN64
403 static inline TEB64 *NtCurrentTeb64(void) { return NULL; }
404 #else
405 static inline TEB64 *NtCurrentTeb64(void) { return (TEB64 *)NtCurrentTeb()->GdiBatchCount; }
406 #endif
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 )
417 req->force = 0;
418 if (!wine_server_call( req ))
420 thread_info->top_window = reply->top_window;
421 thread_info->msg_window = reply->msg_window;
424 SERVER_END_REQ;
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;
441 unsigned int status;
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) );
454 else
455 desktop[0] = 0;
457 SERVER_END_REQ;
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( &params.CurrentDirectory.DosPath, system_dir );
464 RtlInitUnicodeString( &params.ImagePathName, appnameW + 4 );
465 RtlInitUnicodeString( &params.CommandLine, cmdlineW );
466 RtlInitUnicodeString( &params.WindowTitle, appnameW + 4 );
467 RtlInitUnicodeString( &params.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, &params,
480 &create_info, &ps_attr );
481 NtCurrentTeb64()->TlsSlots[WOW64_TLS_FILESYSREDIR] = FALSE;
483 else
484 status = NtCreateUserProcess( &process, &thread, PROCESS_ALL_ACCESS, THREAD_ALL_ACCESS,
485 NULL, NULL, 0, THREAD_CREATE_FLAGS_CREATE_SUSPENDED, &params,
486 &create_info, &ps_attr );
487 if (!status)
489 NtResumeThread( thread, NULL );
490 TRACE_(win)( "started explorer\n" );
491 NtUserWaitForInputIdle( process, 10000, FALSE );
492 NtClose( thread );
493 NtClose( process );
495 else ERR_(win)( "failed to start explorer %x\n", status );
497 SERVER_START_REQ( get_desktop_window )
499 req->force = 1;
500 if (!wine_server_call( req ))
502 thread_info->top_window = reply->top_window;
503 thread_info->msg_window = reply->msg_window;
506 SERVER_END_REQ;
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)
518 char bufferA[64];
519 WCHAR buffer[64];
520 UNICODE_STRING str;
521 OBJECT_ATTRIBUTES attr;
522 NTSTATUS status;
523 HANDLE dir;
525 sprintf( bufferA, "\\Sessions\\%u\\Windows\\WindowStations", (int)NtCurrentTeb()->Peb->SessionId );
526 str.Buffer = buffer;
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;
542 WCHAR *buffer = buf;
543 HKEY tmpkey, appkey;
544 DWORD len;
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) );
559 NtClose( tmpkey );
560 if (appkey)
562 len = query_reg_ascii_value( appkey, "Desktop", info, buf_size );
563 NtClose( appkey );
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 );
572 NtClose( appkey );
573 if (len) return (const WCHAR *)info->Data;
576 return defaultW;
579 /***********************************************************************
580 * winstation_init
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;
590 UNICODE_STRING str;
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, '\\' )))
601 *desktop++ = 0;
602 winstation = 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,
614 dir, NULL );
616 handle = NtUserCreateWindowStation( &attr, STANDARD_RIGHTS_REQUIRED | WINSTA_ALL_ACCESS, 0, 0, 0, 0, 0 );
617 if (handle)
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() ))
633 char buffer[4096];
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,
638 dir, NULL );
640 handle = NtUserCreateDesktopEx( &attr, NULL, NULL, 0, STANDARD_RIGHTS_REQUIRED | DESKTOP_ALL_ACCESS, 0 );
641 if (handle) NtUserSetThreadDesktop( handle );
643 NtClose( dir );
644 free( buffer );