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