1 /*
2  * Unit tests for window stations and desktops
3  *
4  * Copyright 2002 Alexandre Julliard
5  *
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.
10  *
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.
15  *
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
19  */
20 
21 #include "wine/test.h"
22 #include "winbase.h"
23 #include "wingdi.h"
24 #include "winuser.h"
25 #include "winnls.h"
26 #include "wine/winternl.h"
27 
28 static NTSTATUS (WINAPI *pNtQueryObject)(HANDLE, OBJECT_INFORMATION_CLASS, PVOID, ULONG, PULONG);
29 
30 #define DESKTOP_ALL_ACCESS 0x01ff
31 
32 static void print_object( HANDLE obj )
33 {
34     char buffer[100];
35     DWORD size;
36 
37     strcpy( buffer, "foobar" );
38     if (!GetUserObjectInformationA( obj, UOI_NAME, buffer, sizeof(buffer), &size ))
39         trace( "could not get info for %p\n", obj );
40     else
41         trace( "obj %p name '%s'\n", obj, buffer );
42     strcpy( buffer, "foobar" );
43     if (!GetUserObjectInformationA( obj, UOI_TYPE, buffer, sizeof(buffer), &size ))
44         trace( "could not get type for %p\n", obj );
45     else
46         trace( "obj %p type '%s'\n", obj, buffer );
47 }
48 
49 static void register_class(void)
50 {
51     WNDCLASSA cls;
52 
53     cls.style = CS_DBLCLKS;
54     cls.lpfnWndProc = DefWindowProcA;
55     cls.cbClsExtra = 0;
56     cls.cbWndExtra = 0;
57     cls.hInstance = GetModuleHandleA(0);
58     cls.hIcon = 0;
59     cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
60     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
61     cls.lpszMenuName = NULL;
62     cls.lpszClassName = "WinStationClass";
63     RegisterClassA(&cls);
64 }
65 
66 static HDESK initial_desktop;
67 
68 static DWORD CALLBACK thread( LPVOID arg )
69 {
70     HDESK d1, d2;
71     HWND hwnd = CreateWindowExA(0,"WinStationClass","test",WS_POPUP,0,0,100,100,GetDesktopWindow(),0,0,0);
72     ok( hwnd != 0, "CreateWindow failed\n" );
73     d1 = GetThreadDesktop(GetCurrentThreadId());
74     trace( "thread %p desktop: %p\n", arg, d1 );
75     ok( d1 == initial_desktop, "thread %p doesn't use initial desktop\n", arg );
76 
77     SetLastError( 0xdeadbeef );
78     ok( !CloseHandle( d1 ), "CloseHandle succeeded\n" );
79     ok( GetLastError() == ERROR_INVALID_HANDLE, "bad last error %d\n", GetLastError() );
80     SetLastError( 0xdeadbeef );
81     ok( !CloseDesktop( d1 ), "CloseDesktop succeeded\n" );
82     ok( GetLastError() == ERROR_BUSY || broken(GetLastError() == 0xdeadbeef), /* wow64 */
83         "bad last error %d\n", GetLastError() );
84     print_object( d1 );
85     d2 = CreateDesktopA( "foobar2", NULL, NULL, 0, DESKTOP_ALL_ACCESS, NULL );
86     trace( "created desktop %p\n", d2 );
87     ok( d2 != 0, "CreateDesktop failed\n" );
88 
89     SetLastError( 0xdeadbeef );
90     ok( !SetThreadDesktop( d2 ), "set thread desktop succeeded with existing window\n" );
91     ok( GetLastError() == ERROR_BUSY || broken(GetLastError() == 0xdeadbeef), /* wow64 */
92         "bad last error %d\n", GetLastError() );
93 
94     DestroyWindow( hwnd );
95     ok( SetThreadDesktop( d2 ), "set thread desktop failed\n" );
96     d1 = GetThreadDesktop(GetCurrentThreadId());
97     ok( d1 == d2, "GetThreadDesktop did not return set desktop %p/%p\n", d1, d2 );
98     print_object( d2 );
99     if (arg < (LPVOID)5)
100     {
101         HANDLE hthread = CreateThread( NULL, 0, thread, (char *)arg + 1, 0, NULL );
102         Sleep(1000);
103         WaitForSingleObject( hthread, INFINITE );
104         CloseHandle( hthread );
105     }
106     return 0;
107 }
108 
109 static void test_handles(void)
110 {
111     HWINSTA w1, w2, w3;
112     HDESK d1, d2, d3;
113     HANDLE hthread;
114     DWORD id, flags, le;
115     ATOM atom;
116     char buffer[20];
117     DWORD size;
118     BOOL ret;
119 
120     /* win stations */
121 
122     w1 = GetProcessWindowStation();
123     ok( GetProcessWindowStation() == w1, "GetProcessWindowStation returned different handles\n" );
124     ok( !CloseWindowStation(w1), "closing process win station succeeded\n" );
125     SetLastError( 0xdeadbeef );
126     ok( !CloseHandle(w1), "closing process win station handle succeeded\n" );
127     ok( GetLastError() == ERROR_INVALID_HANDLE, "bad last error %d\n", GetLastError() );
128     print_object( w1 );
129 
130     flags = 0;
131     ok( GetHandleInformation( w1, &flags ), "GetHandleInformation failed\n" );
132     ok( !(flags & HANDLE_FLAG_PROTECT_FROM_CLOSE) ||
133         broken(flags & HANDLE_FLAG_PROTECT_FROM_CLOSE), /* set on nt4 */
134         "handle %p PROTECT_FROM_CLOSE set\n", w1 );
135 
136     ok( DuplicateHandle( GetCurrentProcess(), w1, GetCurrentProcess(), (PHANDLE)&w2, 0,
137                          TRUE, DUPLICATE_SAME_ACCESS ), "DuplicateHandle failed\n" );
138     ok( CloseWindowStation(w2), "closing dup win station failed\n" );
139 
140     ok( DuplicateHandle( GetCurrentProcess(), w1, GetCurrentProcess(), (PHANDLE)&w2, 0,
141                          TRUE, DUPLICATE_SAME_ACCESS ), "DuplicateHandle failed\n" );
142     ok( CloseHandle(w2), "closing dup win station handle failed\n" );
143 
144     w2 = CreateWindowStationA("WinSta0", 0, WINSTA_ALL_ACCESS, NULL );
145     le = GetLastError();
146     ok( w2 != 0 || le == ERROR_ACCESS_DENIED, "CreateWindowStation failed (%u)\n", le );
147     if (w2 != 0)
148     {
149         ok( w2 != w1, "CreateWindowStation returned default handle\n" );
150         SetLastError( 0xdeadbeef );
151         ok( !CloseDesktop( (HDESK)w2 ), "CloseDesktop succeeded on win station\n" );
152         ok( GetLastError() == ERROR_INVALID_HANDLE || broken(GetLastError() == 0xdeadbeef), /* wow64 */
153             "bad last error %d\n", GetLastError() );
154         ok( CloseWindowStation( w2 ), "CloseWindowStation failed\n" );
155 
156         w2 = CreateWindowStationA("WinSta0", 0, WINSTA_ALL_ACCESS, NULL );
157         ok( CloseHandle( w2 ), "CloseHandle failed\n" );
158     }
159     else if (le == ERROR_ACCESS_DENIED)
160         win_skip( "Not enough privileges for CreateWindowStation\n" );
161 
162     w2 = OpenWindowStationA("winsta0", TRUE, WINSTA_ALL_ACCESS );
163     ok( w2 != 0, "OpenWindowStation failed\n" );
164     ok( w2 != w1, "OpenWindowStation returned default handle\n" );
165     ok( CloseWindowStation( w2 ), "CloseWindowStation failed\n" );
166 
167     w2 = OpenWindowStationA("dummy name", TRUE, WINSTA_ALL_ACCESS );
168     ok( !w2, "open dummy win station succeeded\n" );
169 
170     CreateMutexA( NULL, 0, "foobar" );
171     w2 = CreateWindowStationA("foobar", 0, WINSTA_ALL_ACCESS, NULL );
172     le = GetLastError();
173     ok( w2 != 0 || le == ERROR_ACCESS_DENIED, "create foobar station failed (%u)\n", le );
174 
175     if (w2 != 0)
176     {
177         w3 = OpenWindowStationA("foobar", TRUE, WINSTA_ALL_ACCESS );
178         ok( w3 != 0, "open foobar station failed\n" );
179         ok( w3 != w2, "open foobar station returned same handle\n" );
180         ok( CloseWindowStation( w2 ), "CloseWindowStation failed\n" );
181         ok( CloseWindowStation( w3 ), "CloseWindowStation failed\n" );
182 
183         w3 = OpenWindowStationA("foobar", TRUE, WINSTA_ALL_ACCESS );
184         ok( !w3, "open foobar station succeeded\n" );
185 
186         w2 = CreateWindowStationA("foobar1", 0, WINSTA_ALL_ACCESS, NULL );
187         ok( w2 != 0, "create foobar station failed\n" );
188         w3 = CreateWindowStationA("foobar2", 0, WINSTA_ALL_ACCESS, NULL );
189         ok( w3 != 0, "create foobar station failed\n" );
190         ok( GetHandleInformation( w2, &flags ), "GetHandleInformation failed\n" );
191         ok( GetHandleInformation( w3, &flags ), "GetHandleInformation failed\n" );
192 
193         SetProcessWindowStation( w2 );
194         atom = GlobalAddAtomA("foo");
195         ok( GlobalGetAtomNameA( atom, buffer, sizeof(buffer) ) == 3, "GlobalGetAtomName failed\n" );
196         ok( !lstrcmpiA( buffer, "foo" ), "bad atom value %s\n", buffer );
197 
198         ok( !CloseWindowStation( w2 ), "CloseWindowStation succeeded\n" );
199         ok( GetHandleInformation( w2, &flags ), "GetHandleInformation failed\n" );
200 
201         SetProcessWindowStation( w3 );
202         ok( GetHandleInformation( w2, &flags ), "GetHandleInformation failed\n" );
203         ok( CloseWindowStation( w2 ), "CloseWindowStation failed\n" );
204         ok( GlobalGetAtomNameA( atom, buffer, sizeof(buffer) ) == 3, "GlobalGetAtomName failed\n" );
205         ok( !lstrcmpiA( buffer, "foo" ), "bad atom value %s\n", buffer );
206     }
207     else if (le == ERROR_ACCESS_DENIED)
208         win_skip( "Not enough privileges for CreateWindowStation\n" );
209 
210     SetLastError( 0xdeadbeef );
211     w2 = OpenWindowStationA( "", TRUE, WINSTA_ALL_ACCESS );
212     ok( !w2, "open station succeeded\n" );
213     todo_wine
214     ok( GetLastError() == ERROR_FILE_NOT_FOUND, "wrong error %u\n", GetLastError() );
215 
216     SetLastError( 0xdeadbeef );
217     w2 = CreateWindowStationA( "", 0, WINSTA_ALL_ACCESS, NULL );
218     ok( w2 != 0, "create station failed err %u\n", GetLastError() );
219 
220     memset( buffer, 0, sizeof(buffer) );
221     ret = GetUserObjectInformationA( w2, UOI_NAME, buffer, sizeof(buffer), &size );
222     ok( ret, "GetUserObjectInformationA failed with error %u\n", GetLastError() );
223     ok( !memcmp(buffer, "Service-0x0-", 12), "unexpected window station name '%s'\n", buffer );
224     ok( buffer[strlen(buffer) - 1] == '$', "unexpected window station name '%s'\n", buffer );
225 
226     SetLastError( 0xdeadbeef );
227     w3 = OpenWindowStationA( "", TRUE, WINSTA_ALL_ACCESS );
228     todo_wine
229     ok( w3 != 0, "open station failed err %u\n", GetLastError() );
230     CloseWindowStation( w3 );
231     CloseWindowStation( w2 );
232 
233     SetLastError( 0xdeadbeef );
234     w2 = CreateWindowStationA( "foo\\bar", 0, WINSTA_ALL_ACCESS, NULL );
235     ok( !w2, "create station succeeded\n" );
236     ok( GetLastError() == ERROR_PATH_NOT_FOUND || GetLastError() == ERROR_ACCESS_DENIED,
237         "wrong error %u\n", GetLastError() );
238 
239     SetLastError( 0xdeadbeef );
240     w2 = OpenWindowStationA( "foo\\bar", TRUE, WINSTA_ALL_ACCESS );
241     ok( !w2, "create station succeeded\n" );
242     ok( GetLastError() == ERROR_PATH_NOT_FOUND, "wrong error %u\n", GetLastError() );
243 
244     /* desktops */
245     d1 = GetThreadDesktop(GetCurrentThreadId());
246     initial_desktop = d1;
247     ok( GetThreadDesktop(GetCurrentThreadId()) == d1,
248         "GetThreadDesktop returned different handles\n" );
249 
250     flags = 0;
251     ok( GetHandleInformation( d1, &flags ), "GetHandleInformation failed\n" );
252     ok( !(flags & HANDLE_FLAG_PROTECT_FROM_CLOSE), "handle %p PROTECT_FROM_CLOSE set\n", d1 );
253 
254     SetLastError( 0xdeadbeef );
255     ok( !CloseDesktop(d1), "closing thread desktop succeeded\n" );
256     ok( GetLastError() == ERROR_BUSY || broken(GetLastError() == 0xdeadbeef), /* wow64 */
257         "bad last error %d\n", GetLastError() );
258 
259     SetLastError( 0xdeadbeef );
260     if (CloseHandle( d1 ))  /* succeeds on nt4 */
261     {
262         win_skip( "NT4 desktop handle management is completely different\n" );
263         return;
264     }
265     ok( GetLastError() == ERROR_INVALID_HANDLE, "bad last error %d\n", GetLastError() );
266 
267     ok( DuplicateHandle( GetCurrentProcess(), d1, GetCurrentProcess(), (PHANDLE)&d2, 0,
268                          TRUE, DUPLICATE_SAME_ACCESS ), "DuplicateHandle failed\n" );
269     ok( CloseDesktop(d2), "closing dup desktop failed\n" );
270 
271     ok( DuplicateHandle( GetCurrentProcess(), d1, GetCurrentProcess(), (PHANDLE)&d2, 0,
272                          TRUE, DUPLICATE_SAME_ACCESS ), "DuplicateHandle failed\n" );
273     ok( CloseHandle(d2), "closing dup desktop handle failed\n" );
274 
275     d2 = OpenDesktopA( "dummy name", 0, TRUE, DESKTOP_ALL_ACCESS );
276     ok( !d2, "open dummy desktop succeeded\n" );
277 
278     SetLastError( 0xdeadbeef );
279     d2 = CreateDesktopA( "", NULL, NULL, 0, DESKTOP_ALL_ACCESS, NULL );
280     todo_wine
281     ok( !d2, "create empty desktop succeeded\n" );
282     todo_wine
283     ok( GetLastError() == ERROR_INVALID_HANDLE, "wrong error %u\n", GetLastError() );
284 
285     SetLastError( 0xdeadbeef );
286     d2 = OpenDesktopA( "", 0, TRUE, DESKTOP_ALL_ACCESS );
287     ok( !d2, "open empty desktop succeeded\n" );
288     ok( GetLastError() == ERROR_INVALID_HANDLE, "wrong error %u\n", GetLastError() );
289 
290     SetLastError( 0xdeadbeef );
291     d2 = CreateDesktopA( "foo\\bar", NULL, NULL, 0, DESKTOP_ALL_ACCESS, NULL );
292     ok( !d2, "create desktop succeeded\n" );
293     ok( GetLastError() == ERROR_BAD_PATHNAME, "wrong error %u\n", GetLastError() );
294 
295     SetLastError( 0xdeadbeef );
296     d2 = OpenDesktopA( "foo\\bar", 0, TRUE, DESKTOP_ALL_ACCESS );
297     ok( !d2, "open desktop succeeded\n" );
298     ok( GetLastError() == ERROR_BAD_PATHNAME, "wrong error %u\n", GetLastError() );
299 
300     d2 = CreateDesktopA( "foobar", NULL, NULL, 0, DESKTOP_ALL_ACCESS, NULL );
301     ok( d2 != 0, "create foobar desktop failed\n" );
302     SetLastError( 0xdeadbeef );
303     ok( !CloseWindowStation( (HWINSTA)d2 ), "CloseWindowStation succeeded on desktop\n" );
304     ok( GetLastError() == ERROR_INVALID_HANDLE || broken(GetLastError() == 0xdeadbeef), /* wow64 */
305         "bad last error %d\n", GetLastError() );
306 
307     SetLastError( 0xdeadbeef );
308     d3 = CreateDesktopA( "foobar", NULL, NULL, 0, DESKTOP_ALL_ACCESS, NULL );
309     ok( d3 != 0, "create foobar desktop again failed\n" );
310     ok( GetLastError() == 0xdeadbeef, "bad last error %d\n", GetLastError() );
311     ok( CloseDesktop( d3 ), "CloseDesktop failed\n" );
312 
313     d3 = OpenDesktopA( "foobar", 0, TRUE, DESKTOP_ALL_ACCESS );
314     ok( d3 != 0, "open foobar desktop failed\n" );
315     ok( d3 != d2, "open foobar desktop returned same handle\n" );
316     ok( CloseDesktop( d2 ), "CloseDesktop failed\n" );
317     ok( CloseDesktop( d3 ), "CloseDesktop failed\n" );
318 
319     d3 = OpenDesktopA( "foobar", 0, TRUE, DESKTOP_ALL_ACCESS );
320     ok( !d3, "open foobar desktop succeeded\n" );
321 
322     ok( !CloseHandle(d1), "closing thread desktop handle succeeded\n" );
323     d2 = GetThreadDesktop(GetCurrentThreadId());
324     ok( d1 == d2, "got different handles after close\n" );
325 
326     register_class();
327     trace( "thread 1 desktop: %p\n", d1 );
328     print_object( d1 );
329     hthread = CreateThread( NULL, 0, thread, (LPVOID)2, 0, &id );
330     Sleep(1000);
331     trace( "get other thread desktop: %p\n", GetThreadDesktop(id) );
332     WaitForSingleObject( hthread, INFINITE );
333     CloseHandle( hthread );
334 
335     /* clean side effect */
336     SetProcessWindowStation( w1 );
337 }
338 
339 /* Enumeration tests */
340 
341 static BOOL CALLBACK window_station_callbackA(LPSTR winsta, LPARAM lp)
342 {
343     trace("window_station_callbackA called with argument %s\n", winsta);
344     return lp;
345 }
346 
347 static BOOL CALLBACK open_window_station_callbackA(LPSTR winsta, LPARAM lp)
348 {
349     HWINSTA hwinsta;
350 
351     trace("open_window_station_callbackA called with argument %s\n", winsta);
352     hwinsta = OpenWindowStationA(winsta, FALSE, WINSTA_ENUMERATE);
353     ok(hwinsta != NULL, "Could not open desktop %s!\n", winsta);
354     if (hwinsta)
355         CloseWindowStation(hwinsta);
356     return lp;
357 }
358 
359 static void test_enumstations(void)
360 {
361     DWORD ret;
362     HWINSTA hwinsta;
363 
364     if (0) /* Crashes instead */
365     {
366         SetLastError(0xbabefeed);
367         ret = EnumWindowStationsA(NULL, 0);
368         ok(!ret, "EnumWindowStationsA returned successfully!\n");
369         ok(GetLastError() == ERROR_INVALID_PARAMETER, "LastError is set to %08x\n", GetLastError());
370     }
371 
372     hwinsta = CreateWindowStationA("winsta_test", 0, WINSTA_ALL_ACCESS, NULL);
373     ret = GetLastError();
374     ok(hwinsta != NULL || ret == ERROR_ACCESS_DENIED, "CreateWindowStation failed (%u)\n", ret);
375     if (!hwinsta)
376     {
377         win_skip("Not enough privileges for CreateWindowStation\n");
378         return;
379     }
380 
381     SetLastError(0xdeadbeef);
382     ret = EnumWindowStationsA(open_window_station_callbackA, 0x12345);
383     ok(ret == 0x12345, "EnumWindowStationsA returned %x\n", ret);
384     ok(GetLastError() == 0xdeadbeef, "LastError is set to %08x\n", GetLastError());
385 
386     SetLastError(0xdeadbeef);
387     ret = EnumWindowStationsA(window_station_callbackA, 0);
388     ok(!ret, "EnumWindowStationsA returned %x\n", ret);
389     ok(GetLastError() == 0xdeadbeef, "LastError is set to %08x\n", GetLastError());
390 }
391 
392 static BOOL CALLBACK desktop_callbackA(LPSTR desktop, LPARAM lp)
393 {
394     trace("desktop_callbackA called with argument %s\n", desktop);
395     return lp;
396 }
397 
398 static BOOL CALLBACK open_desktop_callbackA(LPSTR desktop, LPARAM lp)
399 {
400     HDESK hdesk;
401     static int once;
402 
403     trace("open_desktop_callbackA called with argument %s\n", desktop);
404     /* Only try to open one desktop */
405     if (once++)
406         return lp;
407 
408     hdesk = OpenDesktopA(desktop, 0, FALSE, DESKTOP_ENUMERATE);
409     ok(hdesk != NULL, "Could not open desktop %s!\n", desktop);
410     if (hdesk)
411         CloseDesktop(hdesk);
412     return lp;
413 }
414 
415 static void test_enumdesktops(void)
416 {
417     BOOL ret;
418 
419     if (0)  /* Crashes instead */
420     {
421         SetLastError(0xbabefeed);
422         ret = EnumDesktopsA(GetProcessWindowStation(), NULL, 0);
423         ok(!ret, "EnumDesktopsA returned successfully!\n");
424         ok(GetLastError() == ERROR_INVALID_PARAMETER, "LastError is set to %08x\n", GetLastError());
425     }
426 
427     SetLastError(0xdeadbeef);
428     ret = EnumDesktopsA(NULL, desktop_callbackA, 0x12345);
429     ok(ret == 0x12345, "EnumDesktopsA returned %x\n", ret);
430     ok(GetLastError() == 0xdeadbeef, "LastError is set to %08x\n", GetLastError());
431 
432     SetLastError(0xdeadbeef);
433     ret = EnumDesktopsA(GetProcessWindowStation(), open_desktop_callbackA, 0x12345);
434     ok(ret == 0x12345, "EnumDesktopsA returned %x\n", ret);
435     ok(GetLastError() == 0xdeadbeef, "LastError is set to %08x\n", GetLastError());
436 
437     SetLastError(0xdeadbeef);
438     ret = EnumDesktopsA(INVALID_HANDLE_VALUE, desktop_callbackA, 0x12345);
439     ok(!ret, "EnumDesktopsA returned %x\n", ret);
440     ok(GetLastError() == ERROR_INVALID_HANDLE, "LastError is set to %08x\n", GetLastError());
441 
442     SetLastError(0xdeadbeef);
443     ret = EnumDesktopsA(GetProcessWindowStation(), desktop_callbackA, 0);
444     ok(!ret, "EnumDesktopsA returned %x\n", ret);
445     ok(GetLastError() == 0xdeadbeef, "LastError is set to %08x\n", GetLastError());
446 }
447 
448 /* Miscellaneous tests */
449 
450 static void test_getuserobjectinformation(void)
451 {
452     WCHAR foobarTestW[] = {'\\','f','o','o','b','a','r','T','e','s','t',0};
453     WCHAR DesktopW[] = {'D','e','s','k','t','o','p',0};
454     OBJECT_NAME_INFORMATION *name_info;
455     WCHAR bufferW[20];
456     char buffer[64];
457     NTSTATUS status;
458     DWORD size;
459     HDESK desk;
460     BOOL ret;
461 
462     desk = CreateDesktopA("foobarTest", NULL, NULL, 0, DESKTOP_ALL_ACCESS, NULL);
463     ok(desk != 0, "open foobarTest desktop failed\n");
464 
465     strcpy(buffer, "blahblah");
466 
467     /** Tests for UOI_NAME **/
468 
469     /* Get size, test size and return value/error code */
470     SetLastError(0xdeadbeef);
471     size = 0xdeadbeef;
472     ret = GetUserObjectInformationA(desk, UOI_NAME, NULL, 0, &size);
473 
474     ok(!ret, "GetUserObjectInformationA returned %x\n", ret);
475     ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "LastError is set to %08x\n", GetLastError());
476     ok(size == 22, "size is set to %d\n", size); /* Windows returns Unicode length (11*2) */
477 
478     /* Get string */
479     SetLastError(0xdeadbeef);
480     size = 0xdeadbeef;
481     ret = GetUserObjectInformationA(desk, UOI_NAME, buffer, sizeof(buffer), &size);
482 
483     ok(ret, "GetUserObjectInformationA returned %x\n", ret);
484     ok(GetLastError() == 0xdeadbeef, "LastError is set to %08x\n", GetLastError());
485 
486     ok(strcmp(buffer, "foobarTest") == 0, "Buffer is set to '%s'\n", buffer);
487     ok(size == 11, "size is set to %d\n", size); /* 11 bytes in 'foobarTest\0' */
488 
489     /* Get size, test size and return value/error code (Unicode) */
490     SetLastError(0xdeadbeef);
491     size = 0xdeadbeef;
492     ret = GetUserObjectInformationW(desk, UOI_NAME, NULL, 0, &size);
493 
494     ok(!ret, "GetUserObjectInformationW returned %x\n", ret);
495     ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "LastError is set to %08x\n", GetLastError());
496     ok(size == 22, "size is set to %d\n", size);  /* 22 bytes in 'foobarTest\0' in Unicode */
497 
498     /* Get string (Unicode) */
499     SetLastError(0xdeadbeef);
500     size = 0xdeadbeef;
501     ret = GetUserObjectInformationW(desk, UOI_NAME, bufferW, sizeof(bufferW), &size);
502 
503     ok(ret, "GetUserObjectInformationW returned %x\n", ret);
504     ok(GetLastError() == 0xdeadbeef, "LastError is set to %08x\n", GetLastError());
505 
506     ok(lstrcmpW(bufferW, foobarTestW + 1) == 0, "Buffer is not set to 'foobarTest'\n");
507     ok(size == 22, "size is set to %d\n", size);  /* 22 bytes in 'foobarTest\0' in Unicode */
508 
509     /* ObjectNameInformation does not return the full desktop name */
510     name_info = (OBJECT_NAME_INFORMATION *)buffer;
511     status = pNtQueryObject(desk, ObjectNameInformation, name_info, sizeof(buffer), NULL);
512     ok(!status, "expected STATUS_SUCCESS, got %08x\n", status);
513     ok(lstrcmpW(name_info->Name.Buffer, foobarTestW) == 0,
514        "expected '\\foobarTest', got %s\n", wine_dbgstr_w(name_info->Name.Buffer));
515 
516     /** Tests for UOI_TYPE **/
517 
518     /* Get size, test size and return value/error code */
519     SetLastError(0xdeadbeef);
520     size = 0xdeadbeef;
521     ret = GetUserObjectInformationA(desk, UOI_TYPE, NULL, 0, &size);
522 
523     ok(!ret, "GetUserObjectInformationA returned %x\n", ret);
524     ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "LastError is set to %08x\n", GetLastError());
525     ok(size == 16, "size is set to %d\n", size); /* Windows returns Unicode length (8*2) */
526 
527     /* Get string */
528     SetLastError(0xdeadbeef);
529     size = 0xdeadbeef;
530     ret = GetUserObjectInformationA(desk, UOI_TYPE, buffer, sizeof(buffer), &size);
531 
532     ok(ret, "GetUserObjectInformationA returned %x\n", ret);
533     ok(GetLastError() == 0xdeadbeef, "LastError is set to %08x\n", GetLastError());
534 
535     ok(strcmp(buffer, "Desktop") == 0, "Buffer is set to '%s'\n", buffer);
536     ok(size == 8, "size is set to %d\n", size); /* 8 bytes in 'Desktop\0' */
537 
538     /* Get size, test size and return value/error code (Unicode) */
539     size = 0xdeadbeef;
540     SetLastError(0xdeadbeef);
541     ret = GetUserObjectInformationW(desk, UOI_TYPE, NULL, 0, &size);
542 
543     ok(!ret, "GetUserObjectInformationW returned %x\n", ret);
544     ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "LastError is set to %08x\n", GetLastError());
545     ok(size == 16, "size is set to %d\n", size);  /* 16 bytes in 'Desktop\0' in Unicode */
546 
547     /* Get string (Unicode) */
548     SetLastError(0xdeadbeef);
549     size = 0xdeadbeef;
550     ret = GetUserObjectInformationW(desk, UOI_TYPE, bufferW, sizeof(bufferW), &size);
551 
552     ok(ret, "GetUserObjectInformationW returned %x\n", ret);
553     ok(GetLastError() == 0xdeadbeef, "LastError is set to %08x\n", GetLastError());
554 
555     ok(lstrcmpW(bufferW, DesktopW) == 0, "Buffer is not set to 'Desktop'\n");
556     ok(size == 16, "size is set to %d\n", size);  /* 16 bytes in 'Desktop\0' in Unicode */
557 
558     ok(CloseDesktop(desk), "CloseDesktop failed\n");
559 }
560 
561 static void test_inputdesktop(void)
562 {
563     HDESK input_desk, old_input_desk, thread_desk, old_thread_desk, new_desk;
564     DWORD ret;
565     CHAR name[1024];
566     INPUT inputs[1];
567 
568     inputs[0].type = INPUT_KEYBOARD;
569     U(inputs[0]).ki.wVk = 0;
570     U(inputs[0]).ki.wScan = 0x3c0;
571     U(inputs[0]).ki.dwFlags = KEYEVENTF_UNICODE;
572 
573     /* OpenInputDesktop creates new handles for each calls */
574     old_input_desk = OpenInputDesktop(0, FALSE, DESKTOP_ALL_ACCESS);
575     ok(old_input_desk != NULL, "OpenInputDesktop failed!\n");
576     memset(name, 0, sizeof(name));
577     ret = GetUserObjectInformationA(old_input_desk, UOI_NAME, name, 1024, NULL);
578     ok(ret, "GetUserObjectInformation failed!\n");
579     ok(!strcmp(name, "Default"), "unexpected desktop %s\n", name);
580 
581     input_desk = OpenInputDesktop(0, FALSE, DESKTOP_ALL_ACCESS);
582     ok(input_desk != NULL, "OpenInputDesktop failed!\n");
583     memset(name, 0, sizeof(name));
584     ret = GetUserObjectInformationA(input_desk, UOI_NAME, name, 1024, NULL);
585     ok(ret, "GetUserObjectInformation failed!\n");
586     ok(!strcmp(name, "Default"), "unexpected desktop %s\n", name);
587 
588     ok(old_input_desk != input_desk, "returned the same handle!\n");
589     ret = CloseDesktop(input_desk);
590     ok(ret, "CloseDesktop failed!\n");
591 
592     /* by default, GetThreadDesktop is the input desktop, SendInput should succeed. */
593     old_thread_desk = GetThreadDesktop(GetCurrentThreadId());
594     ok(old_thread_desk != NULL, "GetThreadDesktop faile!\n");
595     memset(name, 0, sizeof(name));
596     ret = GetUserObjectInformationA(old_thread_desk, UOI_NAME, name, 1024, NULL);
597     ok(!strcmp(name, "Default"), "unexpected desktop %s\n", name);
598 
599     SetLastError(0xdeadbeef);
600     ret = SendInput(1, inputs, sizeof(INPUT));
601     ok(GetLastError() == 0xdeadbeef, "unexpected last error %08x\n", GetLastError());
602     ok(ret == 1, "unexpected return count %d\n", ret);
603 
604     /* Set thread desktop to the new desktop, SendInput should fail. */
605     new_desk = CreateDesktopA("new_desk", NULL, NULL, 0, DESKTOP_ALL_ACCESS, NULL);
606     ok(new_desk != NULL, "CreateDesktop failed!\n");
607     ret = SetThreadDesktop(new_desk);
608     ok(ret, "SetThreadDesktop failed!\n");
609     thread_desk = GetThreadDesktop(GetCurrentThreadId());
610     ok(thread_desk == new_desk, "thread desktop doesn't match!\n");
611     memset(name, 0, sizeof(name));
612     ret = GetUserObjectInformationA(thread_desk, UOI_NAME, name, 1024, NULL);
613     ok(!strcmp(name, "new_desk"), "unexpected desktop %s\n", name);
614 
615     SetLastError(0xdeadbeef);
616     ret = SendInput(1, inputs, sizeof(INPUT));
617     if(broken(GetLastError() == 0xdeadbeef))
618     {
619         SetThreadDesktop(old_thread_desk);
620         CloseDesktop(old_input_desk);
621         CloseDesktop(input_desk);
622         CloseDesktop(new_desk);
623         win_skip("Skip tests on NT4\n");
624         return;
625     }
626 todo_wine
627     ok(GetLastError() == ERROR_ACCESS_DENIED, "unexpected last error %08x\n", GetLastError());
628     ok(ret == 1 || broken(ret == 0) /* Win64 */, "unexpected return count %d\n", ret);
629 
630     /* Set thread desktop back to the old thread desktop, SendInput should success. */
631     ret = SetThreadDesktop(old_thread_desk);
632     ok(ret, "SetThreadDesktop failed!\n");
633     thread_desk = GetThreadDesktop(GetCurrentThreadId());
634     ok(thread_desk == old_thread_desk, "thread desktop doesn't match!\n");
635     memset(name, 0, sizeof(name));
636     ret = GetUserObjectInformationA(thread_desk, UOI_NAME, name, 1024, NULL);
637     ok(!strcmp(name, "Default"), "unexpected desktop %s\n", name);
638 
639     SetLastError(0xdeadbeef);
640     ret = SendInput(1, inputs, sizeof(INPUT));
641     ok(GetLastError() == 0xdeadbeef, "unexpected last error %08x\n", GetLastError());
642     ok(ret == 1, "unexpected return count %d\n", ret);
643 
644     /* Set thread desktop to the input desktop, SendInput should success. */
645     ret = SetThreadDesktop(old_input_desk);
646     ok(ret, "SetThreadDesktop failed!\n");
647     thread_desk = GetThreadDesktop(GetCurrentThreadId());
648     ok(thread_desk == old_input_desk, "thread desktop doesn't match!\n");
649     memset(name, 0, sizeof(name));
650     ret = GetUserObjectInformationA(thread_desk, UOI_NAME, name, 1024, NULL);
651     ok(!strcmp(name, "Default"), "unexpected desktop %s\n", name);
652 
653     SetLastError(0xdeadbeef);
654     ret = SendInput(1, inputs, sizeof(INPUT));
655     ok(GetLastError() == 0xdeadbeef, "unexpected last error %08x\n", GetLastError());
656     ok(ret == 1, "unexpected return count %d\n", ret);
657 
658     /* Switch input desktop to the new desktop, SendInput should fail. */
659     ret = SwitchDesktop(new_desk);
660     ok(ret, "SwitchDesktop failed!\n");
661     input_desk = OpenInputDesktop(0, FALSE, DESKTOP_ALL_ACCESS);
662     ok(input_desk != NULL, "OpenInputDesktop failed!\n");
663     ok(input_desk != new_desk, "returned the same handle!\n");
664     memset(name, 0, sizeof(name));
665     ret = GetUserObjectInformationA(input_desk, UOI_NAME, name, 1024, NULL);
666     ok(ret, "GetUserObjectInformation failed!\n");
667 todo_wine
668     ok(!strcmp(name, "new_desk"), "unexpected desktop %s\n", name);
669     ret = CloseDesktop(input_desk);
670     ok(ret, "CloseDesktop failed!\n");
671 
672     SetLastError(0xdeadbeef);
673     ret = SendInput(1, inputs, sizeof(INPUT));
674 todo_wine
675     ok(GetLastError() == ERROR_ACCESS_DENIED, "unexpected last error %08x\n", GetLastError());
676     ok(ret == 1 || broken(ret == 0) /* Win64 */, "unexpected return count %d\n", ret);
677 
678     /* Set thread desktop to the new desktop, SendInput should success. */
679     ret = SetThreadDesktop(new_desk);
680     ok(ret, "SetThreadDesktop failed!\n");
681     thread_desk = GetThreadDesktop(GetCurrentThreadId());
682     ok(thread_desk == new_desk, "thread desktop doesn't match!\n");
683     memset(name, 0, sizeof(name));
684     ret = GetUserObjectInformationA(thread_desk, UOI_NAME, name, 1024, NULL);
685     ok(!strcmp(name, "new_desk"), "unexpected desktop %s\n", name);
686 
687     SetLastError(0xdeadbeef);
688     ret = SendInput(1, inputs, sizeof(INPUT));
689     ok(GetLastError() == 0xdeadbeef, "unexpected last error %08x\n", GetLastError());
690     ok(ret == 1, "unexpected return count %d\n", ret);
691 
692     /* Switch input desktop to the old input desktop, set thread desktop to the old
693      * thread desktop, clean side effects. SendInput should success. */
694     ret = SwitchDesktop(old_input_desk);
695     input_desk = OpenInputDesktop(0, FALSE, DESKTOP_ALL_ACCESS);
696     ok(input_desk != NULL, "OpenInputDesktop failed!\n");
697     ok(input_desk != old_input_desk, "returned the same handle!\n");
698     memset(name, 0, sizeof(name));
699     ret = GetUserObjectInformationA(input_desk, UOI_NAME, name, 1024, NULL);
700     ok(ret, "GetUserObjectInformation failed!\n");
701     ok(!strcmp(name, "Default"), "unexpected desktop %s\n", name);
702 
703     ret = SetThreadDesktop(old_thread_desk);
704     ok(ret, "SetThreadDesktop failed!\n");
705     thread_desk = GetThreadDesktop(GetCurrentThreadId());
706     ok(thread_desk == old_thread_desk, "thread desktop doesn't match!\n");
707     memset(name, 0, sizeof(name));
708     ret = GetUserObjectInformationA(thread_desk, UOI_NAME, name, 1024, NULL);
709     ok(!strcmp(name, "Default"), "unexpected desktop %s\n", name);
710 
711     SetLastError(0xdeadbeef);
712     ret = SendInput(1, inputs, sizeof(INPUT));
713     ok(GetLastError() == 0xdeadbeef, "unexpected last error %08x\n", GetLastError());
714     ok(ret == 1, "unexpected return count %d\n", ret);
715 
716     /* free resources */
717     ret = CloseDesktop(input_desk);
718     ok(ret, "CloseDesktop failed!\n");
719     ret = CloseDesktop(old_input_desk);
720     ok(ret, "CloseDesktop failed!\n");
721     ret = CloseDesktop(new_desk);
722     ok(ret, "CloseDesktop failed!\n");
723 }
724 
725 static void test_inputdesktop2(void)
726 {
727     HWINSTA w1, w2;
728     HDESK thread_desk, new_desk, input_desk, hdesk;
729     DWORD ret;
730 
731     thread_desk = GetThreadDesktop(GetCurrentThreadId());
732     ok(thread_desk != NULL, "GetThreadDesktop failed!\n");
733     w1 = GetProcessWindowStation();
734     ok(w1 != NULL, "GetProcessWindowStation failed!\n");
735     SetLastError(0xdeadbeef);
736     w2 = CreateWindowStationA("winsta_test", 0, WINSTA_ALL_ACCESS, NULL);
737     ret = GetLastError();
738     ok(w2 != NULL || ret == ERROR_ACCESS_DENIED, "CreateWindowStation failed (%u)\n", ret);
739     if (!w2)
740     {
741         win_skip("Not enough privileges for CreateWindowStation\n");
742         return;
743     }
744 
745     ret = EnumDesktopsA(GetProcessWindowStation(), desktop_callbackA, 0);
746     ok(!ret, "EnumDesktopsA failed!\n");
747     input_desk = OpenInputDesktop(0, FALSE, DESKTOP_ALL_ACCESS);
748     ok(input_desk != NULL, "OpenInputDesktop failed!\n");
749     ret = CloseDesktop(input_desk);
750     ok(ret, "CloseDesktop failed!\n");
751 
752     ret = SetProcessWindowStation(w2);
753     ok(ret, "SetProcessWindowStation failed!\n");
754     hdesk = GetThreadDesktop(GetCurrentThreadId());
755     ok(hdesk != NULL, "GetThreadDesktop failed!\n");
756     ok(hdesk == thread_desk, "thread desktop should not change after winstation changed!\n");
757     ret = EnumDesktopsA(GetProcessWindowStation(), desktop_callbackA, 0);
758 
759     new_desk = CreateDesktopA("desk_test", NULL, NULL, 0, DESKTOP_ALL_ACCESS, NULL);
760     ok(new_desk != NULL, "CreateDesktop failed!\n");
761     ret = EnumDesktopsA(GetProcessWindowStation(), desktop_callbackA, 0);
762     ok(!ret, "EnumDesktopsA failed!\n");
763     SetLastError(0xdeadbeef);
764     input_desk = OpenInputDesktop(0, FALSE, DESKTOP_ALL_ACCESS);
765     ok(input_desk == NULL, "OpenInputDesktop should fail on non default winstation!\n");
766     ok(GetLastError() == ERROR_INVALID_FUNCTION || broken(GetLastError() == 0xdeadbeef), "last error %08x\n", GetLastError());
767 
768     hdesk = OpenDesktopA("desk_test", 0, TRUE, DESKTOP_ALL_ACCESS);
769     ok(hdesk != NULL, "OpenDesktop failed!\n");
770     SetLastError(0xdeadbeef);
771     ret = SwitchDesktop(hdesk);
772 todo_wine
773     ok(!ret, "Switch to desktop belong to non default winstation should fail!\n");
774 todo_wine
775     ok(GetLastError() == ERROR_ACCESS_DENIED || broken(GetLastError() == 0xdeadbeef), "last error %08x\n", GetLastError());
776     ret = SetThreadDesktop(hdesk);
777     ok(ret, "SetThreadDesktop failed!\n");
778 
779     /* clean side effect */
780     ret = SetThreadDesktop(thread_desk);
781 todo_wine
782     ok(ret, "SetThreadDesktop should success even desktop is not belong to process winstation!\n");
783     ret = SetProcessWindowStation(w1);
784     ok(ret, "SetProcessWindowStation failed!\n");
785     ret = SetThreadDesktop(thread_desk);
786     ok(ret, "SetThreadDesktop failed!\n");
787     ret = CloseWindowStation(w2);
788     ok(ret, "CloseWindowStation failed!\n");
789     ret = CloseDesktop(new_desk);
790     ok(ret, "CloseDesktop failed!\n");
791     ret = CloseDesktop(hdesk);
792     ok(ret, "CloseDesktop failed!\n");
793 }
794 
795 static LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
796 {
797     if (msg == WM_DESTROY)
798     {
799         trace("destroying hwnd %p\n", hWnd);
800         PostQuitMessage(0);
801         return 0;
802     }
803     return DefWindowProcA( hWnd, msg, wParam, lParam );
804 }
805 
806 typedef struct tag_wnd_param
807 {
808     const char *wnd_name;
809     HWND hwnd;
810     HDESK hdesk;
811     HANDLE hevent;
812 } wnd_param;
813 
814 static DWORD WINAPI create_window(LPVOID param)
815 {
816     wnd_param *param1 = param;
817     DWORD ret;
818     MSG msg;
819 
820     ret = SetThreadDesktop(param1->hdesk);
821     ok(ret, "SetThreadDesktop failed!\n");
822     param1->hwnd = CreateWindowA("test_class", param1->wnd_name, WS_POPUP, 0, 0, 100, 100, NULL, NULL, NULL, NULL);
823     ok(param1->hwnd != 0, "CreateWindowA failed!\n");
824     ret = SetEvent(param1->hevent);
825     ok(ret, "SetEvent failed!\n");
826 
827     while (GetMessageA(&msg, 0, 0, 0))
828     {
829         TranslateMessage(&msg);
830         DispatchMessageA(&msg);
831     }
832 
833     return 0;
834 }
835 
836 static DWORD set_foreground(HWND hwnd)
837 {
838     HWND hwnd_fore;
839     DWORD set_id, fore_id, ret;
840     char win_text[1024];
841 
842     hwnd_fore = GetForegroundWindow();
843     GetWindowTextA(hwnd_fore, win_text, 1024);
844     set_id = GetWindowThreadProcessId(hwnd, NULL);
845     fore_id = GetWindowThreadProcessId(hwnd_fore, NULL);
846     trace("\"%s\" %p %08x hwnd %p %08x\n", win_text, hwnd_fore, fore_id, hwnd, set_id);
847     ret = AttachThreadInput(set_id, fore_id, TRUE);
848     trace("AttachThreadInput returned %08x\n", ret);
849     ret = ShowWindow(hwnd, SW_SHOWNORMAL);
850     trace("ShowWindow returned %08x\n", ret);
851     ret = SetWindowPos(hwnd, HWND_TOPMOST, 0,0,0,0, SWP_NOSIZE|SWP_NOMOVE);
852     trace("set topmost returned %08x\n", ret);
853     ret = SetWindowPos(hwnd, HWND_NOTOPMOST, 0,0,0,0, SWP_NOSIZE|SWP_NOMOVE);
854     trace("set notopmost returned %08x\n", ret);
855     ret = SetForegroundWindow(hwnd);
856     trace("SetForegroundWindow returned %08x\n", ret);
857     Sleep(250);
858     AttachThreadInput(set_id, fore_id, FALSE);
859     return ret;
860 }
861 
862 static void test_foregroundwindow(void)
863 {
864     HWND hwnd, hwnd_test, partners[2], hwnds[2];
865     HDESK hdesks[2];
866     int thread_desk_id, input_desk_id, hwnd_id;
867     WNDCLASSA wclass;
868     wnd_param param;
869     DWORD ret, timeout, timeout_old;
870     char win_text[1024];
871 
872 #define DESKTOPS 2
873 
874     memset( &wclass, 0, sizeof(wclass) );
875     wclass.lpszClassName = "test_class";
876     wclass.lpfnWndProc   = WndProc;
877     RegisterClassA(&wclass);
878     param.wnd_name = "win_name";
879 
880     hdesks[0] = GetThreadDesktop(GetCurrentThreadId());
881     ok(hdesks[0] != NULL, "OpenDesktop failed!\n");
882     SetLastError(0xdeadbeef);
883     hdesks[1] = CreateDesktopA("desk2", NULL, NULL, 0, DESKTOP_ALL_ACCESS, NULL);
884     ret = GetLastError();
885     ok(hdesks[1] != NULL || ret == ERROR_ACCESS_DENIED, "CreateDesktop failed (%u)\n", ret);
886     if(!hdesks[1])
887     {
888         win_skip("Not enough privileges for CreateDesktop\n");
889         return;
890     }
891 
892     ret = SystemParametersInfoA(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, &timeout_old, 0);
893     if(!ret)
894     {
895         win_skip("Skip tests on NT4\n");
896         CloseDesktop(hdesks[1]);
897         return;
898     }
899     trace("old timeout %d\n", timeout_old);
900     timeout = 0;
901     ret = SystemParametersInfoA(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, 0, SPIF_SENDCHANGE | SPIF_UPDATEINIFILE);
902     ok(ret, "set foreground lock timeout failed!\n");
903     ret = SystemParametersInfoA(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, &timeout, 0);
904     ok(ret, "get foreground lock timeout failed!\n");
905     ok(timeout == 0, "unexpected timeout %d\n", timeout);
906 
907     for (thread_desk_id = 0; thread_desk_id < DESKTOPS; thread_desk_id++)
908     {
909         param.hdesk = hdesks[thread_desk_id];
910         param.hevent = CreateEventA(NULL, TRUE, FALSE, NULL);
911         CreateThread(NULL, 0, create_window, &param, 0, NULL);
912         ret = WaitForSingleObject(param.hevent, INFINITE);
913         ok(ret == WAIT_OBJECT_0, "wait failed!\n");
914         hwnds[thread_desk_id] = param.hwnd;
915     }
916 
917     for (thread_desk_id = 0; thread_desk_id < DESKTOPS; thread_desk_id++)
918     {
919         param.hdesk = hdesks[thread_desk_id];
920         param.hevent = CreateEventA(NULL, TRUE, FALSE, NULL);
921         CreateThread(NULL, 0, create_window, &param, 0, NULL);
922         ret = WaitForSingleObject(param.hevent, INFINITE);
923         ok(ret == WAIT_OBJECT_0, "wait failed!\n");
924         partners[thread_desk_id] = param.hwnd;
925     }
926 
927     trace("hwnd0 %p hwnd1 %p partner0 %p partner1 %p\n", hwnds[0], hwnds[1], partners[0], partners[1]);
928 
929     for (hwnd_id = 0; hwnd_id < DESKTOPS; hwnd_id++)
930         for (thread_desk_id = 0; thread_desk_id < DESKTOPS; thread_desk_id++)
931             for (input_desk_id = 0; input_desk_id < DESKTOPS; input_desk_id++)
932             {
933                 trace("testing thread_desk %d input_desk %d hwnd %d\n",
934                         thread_desk_id, input_desk_id, hwnd_id);
935                 hwnd_test = hwnds[hwnd_id];
936                 ret = SetThreadDesktop(hdesks[thread_desk_id]);
937                 ok(ret, "set thread desktop failed!\n");
938                 ret = SwitchDesktop(hdesks[input_desk_id]);
939                 ok(ret, "switch desktop failed!\n");
940                 set_foreground(partners[0]);
941                 set_foreground(partners[1]);
942                 hwnd = GetForegroundWindow();
943                 ok(hwnd != hwnd_test, "unexpected foreground window %p\n", hwnd);
944                 ret = set_foreground(hwnd_test);
945                 hwnd = GetForegroundWindow();
946                 GetWindowTextA(hwnd, win_text, 1024);
947                 trace("hwnd %p name %s\n", hwnd, win_text);
948                 if (input_desk_id == hwnd_id)
949                 {
950                     if (input_desk_id == thread_desk_id)
951                     {
952                         ok(ret, "SetForegroundWindow failed!\n");
953                         todo_wine_if (!hwnd)
954                             ok(hwnd == hwnd_test , "unexpected foreground window %p\n", hwnd);
955                     }
956                     else
957                     {
958                         todo_wine ok(ret, "SetForegroundWindow failed!\n");
959                         todo_wine ok(hwnd == 0, "unexpected foreground window %p\n", hwnd);
960                     }
961                 }
962                 else
963                 {
964                     if (input_desk_id == thread_desk_id)
965                     {
966                         ok(!ret, "SetForegroundWindow should fail!\n");
967                         todo_wine_if (!hwnd)
968                             ok(hwnd == partners[input_desk_id] , "unexpected foreground window %p\n", hwnd);
969                     }
970                     else
971                     {
972                         todo_wine ok(!ret, "SetForegroundWindow should fail!\n");
973                         todo_wine_if (hwnd)
974                             ok(hwnd == 0, "unexpected foreground window %p\n", hwnd);
975                     }
976                 }
977             }
978 
979     /* Clean up */
980 
981     for (thread_desk_id = DESKTOPS - 1; thread_desk_id >= 0; thread_desk_id--)
982     {
983         ret = SetThreadDesktop(hdesks[thread_desk_id]);
984         ok(ret, "set thread desktop failed!\n");
985         SendMessageA(hwnds[thread_desk_id], WM_DESTROY, 0, 0);
986         SendMessageA(partners[thread_desk_id], WM_DESTROY, 0, 0);
987     }
988 
989     ret = SwitchDesktop(hdesks[0]);
990     ok(ret, "switch desktop failed!\n");
991     CloseDesktop(hdesks[1]);
992 
993     ret = SystemParametersInfoA(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, UlongToPtr(timeout_old), SPIF_SENDCHANGE | SPIF_UPDATEINIFILE);
994     ok(ret, "set foreground lock timeout failed!\n");
995     ret = SystemParametersInfoA(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, &timeout, 0);
996     ok(ret, "get foreground lock timeout failed!\n");
997     ok(timeout == timeout_old, "unexpected timeout %d\n", timeout);
998 }
999 
1000 START_TEST(winstation)
1001 {
1002     HMODULE hntdll = GetModuleHandleA("ntdll.dll");
1003     pNtQueryObject = (void *)GetProcAddress(hntdll, "NtQueryObject");
1004 
1005     /* Check whether this platform supports WindowStation calls */
1006 
1007     SetLastError( 0xdeadbeef );
1008     GetProcessWindowStation();
1009     if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
1010     {
1011         win_skip("WindowStation calls not supported on this platform\n");
1012         return;
1013     }
1014 
1015     test_inputdesktop();
1016     test_inputdesktop2();
1017     test_enumstations();
1018     test_enumdesktops();
1019     test_handles();
1020     test_getuserobjectinformation();
1021     test_foregroundwindow();
1022 }
1023