1 /*
2  * PROJECT:         ReactOS api tests
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * PURPOSE:         Test for desktop objects
5  * PROGRAMMERS:     Giannis Adamopoulos
6  *                  Thomas Faber
7  */
8 
9 #include "precomp.h"
10 
11 #include <ndk/obfuncs.h>
12 
13 struct test_info {
14     WCHAR* ExpectedWinsta;
15     WCHAR* ExpectedDesktp;
16 };
17 
18 struct test_info TestResults[] =
19 {
20     {L"WinSta0",L"Default"},
21     {L"WinSta0",L"Default"},
22     {NULL, NULL},
23     {NULL, NULL},
24     {L"WinSta0",L"Default"},
25     {NULL, NULL},
26     {NULL, NULL},
27     {NULL, NULL},
28     {NULL, NULL},
29     {L"WinSta0",L"Default"},
30     {NULL, NULL},
31     {NULL, NULL},
32     {NULL, NULL},
33     {L"TestWinsta", L"TestDesktop"},
34     {NULL, NULL},
35     {L"WinSta0",L"Default"},
36     {NULL, NULL}
37 };
38 
39 void do_InitialDesktop_child(int i)
40 {
41     HDESK hdesktop;
42     HWINSTA hwinsta;
43     WCHAR buffer[100];
44     DWORD size;
45     BOOL ret;
46 
47     if (TestResults[i].ExpectedWinsta == NULL)
48         ok(FALSE, "%d: Process should have failed to initialize\n", i);
49 
50     IsGUIThread(TRUE);
51 
52     hdesktop = GetThreadDesktop(GetCurrentThreadId());
53     hwinsta = GetProcessWindowStation();
54 
55     ret = GetUserObjectInformationW( hwinsta, UOI_NAME, buffer, sizeof(buffer), &size );
56     ok(ret == TRUE, "ret = %d\n", ret);
57     if (TestResults[i].ExpectedWinsta)
58         ok(wcscmp(buffer, TestResults[i].ExpectedWinsta) == 0, "%d: Wrong winsta %S instead of %S\n", i, buffer, TestResults[i].ExpectedWinsta);
59     trace("%d: We run on winstation %S\n", i, buffer);
60 
61     ret = GetUserObjectInformationW( hdesktop, UOI_NAME, buffer, sizeof(buffer), &size );
62     ok(ret == TRUE, "ret = %d\n", ret);
63     if (TestResults[i].ExpectedDesktp)
64         ok(wcscmp(buffer, TestResults[i].ExpectedDesktp) == 0, "%d: Wrong desktop %S instead of %S\n", i, buffer, TestResults[i].ExpectedDesktp);
65     trace("%d: We run on desktop %S\n", i, buffer);
66 }
67 
68 void test_CreateProcessWithDesktop(int i, char *argv0, char* Desktop, DWORD expectedExitCode)
69 {
70     STARTUPINFOA startup;
71     char path[MAX_PATH];
72     BOOL ret;
73     DWORD ExitCode;
74     PROCESS_INFORMATION pi;
75 
76     memset( &startup, 0, sizeof(startup) );
77     startup.cb = sizeof(startup);
78     startup.dwFlags = STARTF_USESHOWWINDOW;
79     startup.wShowWindow = SW_SHOWNORMAL;
80     startup.lpDesktop = Desktop;
81 
82     sprintf( path, "%s desktop %u", argv0, i );
83     ret = CreateProcessA( NULL, path, NULL, NULL, TRUE, 0, NULL, NULL, &startup, &pi );
84     ok( ret, "%d: CreateProcess '%s' failed err %d.\n", i, path, (int)GetLastError() );
85     WaitForSingleObject (pi.hProcess, INFINITE);
86     ret = GetExitCodeProcess(pi.hProcess, &ExitCode);
87     ok(ret > 0 , "%d: GetExitCodeProcess failed\n", i);
88 
89     /* the exit code varies from version to version */
90     /* xp returns error 128 and 7 returns STATUS_DLL_INIT_FAILED */
91     if (ExitCode == 128) ExitCode = STATUS_DLL_INIT_FAILED;
92 
93     ok(ExitCode == expectedExitCode, "%d: expected error 0x%x in child process got 0x%x\n", i, (int)expectedExitCode, (int)ExitCode);
94 
95     CloseHandle(pi.hProcess);
96 }
97 
98 HWINSTA CreateInheritableWinsta(WCHAR* name, ACCESS_MASK dwDesiredAccess, BOOL inheritable, DWORD *error)
99 {
100     HWINSTA hwinsta;
101     SECURITY_ATTRIBUTES sa;
102     sa.nLength = sizeof(sa);
103     sa.lpSecurityDescriptor = NULL;
104     sa.bInheritHandle = inheritable;
105     SetLastError(0xfeedf00d);
106     hwinsta = CreateWindowStationW(name, 0, dwDesiredAccess, &sa);
107     *error = GetLastError();
108     return hwinsta;
109 }
110 
111 HDESK CreateInheritableDesktop(WCHAR* name, ACCESS_MASK dwDesiredAccess, BOOL inheritable, DWORD *error)
112 {
113     HDESK hdesk;
114     SECURITY_ATTRIBUTES sa;
115     sa.nLength = sizeof(sa);
116     sa.lpSecurityDescriptor = NULL;
117     sa.bInheritHandle = inheritable;
118     SetLastError(0xfeedf00d);
119     hdesk = CreateDesktopW(name, NULL, NULL, 0, dwDesiredAccess, &sa);
120     *error = GetLastError();
121     return hdesk;
122 }
123 
124 void Test_InitialDesktop(char *argv0)
125 {
126     HWINSTA hwinsta = NULL, hwinstaInitial;
127     HDESK hdesktop = NULL;
128     BOOL ret;
129     DWORD error;
130 
131     hwinstaInitial = GetProcessWindowStation();
132 
133     /* Use the default (interactive) window station */
134     test_CreateProcessWithDesktop(0, argv0, NULL, 0);
135     test_CreateProcessWithDesktop(1, argv0, "Default", 0);
136     test_CreateProcessWithDesktop(2, argv0, "WinSta0\\", STATUS_DLL_INIT_FAILED);
137     test_CreateProcessWithDesktop(3, argv0, "\\Default", STATUS_DLL_INIT_FAILED);
138     test_CreateProcessWithDesktop(4, argv0, "WinSta0\\Default", 0);
139     test_CreateProcessWithDesktop(5, argv0, "Winlogon", STATUS_DLL_INIT_FAILED);
140     test_CreateProcessWithDesktop(6, argv0, "WinSta0/Default", STATUS_DLL_INIT_FAILED);
141     test_CreateProcessWithDesktop(7, argv0, "NonExistantDesktop", STATUS_DLL_INIT_FAILED);
142     test_CreateProcessWithDesktop(8, argv0, "NonExistantWinsta\\NonExistantDesktop", STATUS_DLL_INIT_FAILED);
143 
144     /* Test on an (non-interactive) window station */
145     hwinsta = CreateInheritableWinsta(L"TestWinsta", WINSTA_ALL_ACCESS, TRUE, &error);
146     ok(hwinsta != NULL && error == NO_ERROR, "CreateWindowStation failed, got 0x%p, 0x%lx\n", hwinsta, error);
147     ret = SetProcessWindowStation(hwinsta);
148     ok(ret != FALSE, "SetProcessWindowStation failed\n");
149     hdesktop = CreateInheritableDesktop(L"TestDesktop", DESKTOP_ALL_ACCESS, TRUE, &error);
150     ok(hdesktop != NULL && error == 0xfeedf00d, "CreateDesktop failed, got 0x%p, 0x%lx\n", hdesktop, error);
151 
152     test_CreateProcessWithDesktop(9, argv0, NULL, 0);
153     test_CreateProcessWithDesktop(10, argv0, "TestDesktop", STATUS_DLL_INIT_FAILED);
154     test_CreateProcessWithDesktop(11, argv0, "TestWinsta\\", STATUS_DLL_INIT_FAILED);
155     test_CreateProcessWithDesktop(12, argv0, "\\TestDesktop", STATUS_DLL_INIT_FAILED);
156     test_CreateProcessWithDesktop(13, argv0, "TestWinsta\\TestDesktop", 0);
157     test_CreateProcessWithDesktop(14, argv0, "NonExistantWinsta\\NonExistantDesktop", STATUS_DLL_INIT_FAILED);
158 
159     ret = SetProcessWindowStation(hwinstaInitial);
160     ok(ret != FALSE, "SetProcessWindowStation failed\n");
161 
162     ret = CloseDesktop(hdesktop);
163     ok(ret != FALSE, "CloseDesktop failed\n");
164 
165     ret = CloseWindowStation(hwinsta);
166     ok(ret != FALSE, "CloseWindowStation failed\n");
167 
168 #if 0
169     /* Test on an non-interactive Service-0xXXXX-YYYY$ window station */
170     hwinsta = CreateInheritableWinsta(NULL, WINSTA_ALL_ACCESS, TRUE, &error);
171     ok(hwinsta != NULL && error == NO_ERROR, "CreateWindowStation failed, got 0x%p, 0x%lx\n", hwinsta, error);
172     ret = SetProcessWindowStation(hwinsta);
173     ok(ret != FALSE, "SetProcessWindowStation failed\n");
174     hdesktop = CreateInheritableDesktop(L"TestDesktop", DESKTOP_ALL_ACCESS, TRUE, &error);
175     ok(hdesktop != NULL && error == 0xfeedf00d, "CreateDesktop failed, got 0x%p, 0x%lx\n", hdesktop, error);
176 
177     test_CreateProcessWithDesktop(15, argv0, NULL, 0);
178     test_CreateProcessWithDesktop(16, "TestDesktop", NULL, 0 /*ERROR_ACCESS_DENIED*/);
179 
180     ret = SetProcessWindowStation(hwinstaInitial);
181     ok(ret != FALSE, "SetProcessWindowStation failed\n");
182 
183     ret = CloseDesktop(hdesktop);
184     ok(ret != FALSE, "CloseDesktop failed\n");
185 
186     ret = CloseWindowStation(hwinsta);
187     ok(ret != FALSE, "CloseWindowStation failed\n");
188 #endif
189 }
190 
191 void Test_OpenInputDesktop()
192 {
193     HDESK hDeskInput ,hDeskInput2;
194     HDESK hDeskInitial;
195     BOOL ret;
196     HWINSTA hwinsta = NULL, hwinstaInitial;
197     DWORD err;
198 
199     hDeskInput = OpenInputDesktop(0, FALSE, DESKTOP_ALL_ACCESS);
200     ok(hDeskInput != NULL, "OpenInputDesktop failed\n");
201     hDeskInitial = GetThreadDesktop( GetCurrentThreadId() );
202     ok(hDeskInitial != NULL, "GetThreadDesktop failed\n");
203     ok(hDeskInput != hDeskInitial, "OpenInputDesktop returned thread desktop\n");
204 
205     hDeskInput2 = OpenInputDesktop(0, FALSE, DESKTOP_ALL_ACCESS);
206     ok(hDeskInput2 != NULL, "Second call to OpenInputDesktop failed\n");
207     ok(hDeskInput2 != hDeskInput, "Second call to OpenInputDesktop returned same handle\n");
208 
209     ok(CloseDesktop(hDeskInput2) != 0, "CloseDesktop failed\n");
210 
211     ret = SetThreadDesktop(hDeskInput);
212     ok(ret == TRUE, "SetThreadDesktop for input desktop failed\n");
213 
214     ret = SetThreadDesktop(hDeskInitial);
215     ok(ret == TRUE, "SetThreadDesktop for initial desktop failed\n");
216 
217     ok(CloseDesktop(hDeskInput) != 0, "CloseDesktop failed\n");
218 
219     /* Try calling OpenInputDesktop after switching to a new winsta */
220     hwinstaInitial = GetProcessWindowStation();
221     ok(hwinstaInitial != 0, "GetProcessWindowStation failed\n");
222 
223     hwinsta = CreateWindowStationW(L"TestWinsta", 0, WINSTA_ALL_ACCESS, NULL);
224     ok(hwinsta != 0, "CreateWindowStationW failed\n");
225 
226     ret = SetProcessWindowStation(hwinsta);
227     ok(ret != FALSE, "SetProcessWindowStation failed\n");
228 
229     hDeskInput = OpenInputDesktop(0, FALSE, DESKTOP_ALL_ACCESS);
230     ok(hDeskInput == 0, "OpenInputDesktop should fail\n");
231 
232     err = GetLastError();
233     ok(err == ERROR_INVALID_FUNCTION, "Got last error: %lu\n", err);
234 
235     ret = SetProcessWindowStation(hwinstaInitial);
236     ok(ret != FALSE, "SetProcessWindowStation failed\n");
237 
238     ret = CloseWindowStation(hwinsta);
239     ok(ret != FALSE, "CloseWindowStation failed\n");
240 
241 }
242 
243 static HWINSTA open_winsta(PCWSTR winstaName, DWORD *error)
244 {
245     HWINSTA hwinsta;
246     SetLastError(0xfeedf00d);
247     hwinsta = OpenWindowStationW(winstaName, FALSE, WINSTA_ALL_ACCESS);
248     *error = GetLastError();
249     return hwinsta;
250 }
251 
252 static HWINSTA create_winsta(PCWSTR winstaName, DWORD *error)
253 {
254     HWINSTA hwinsta;
255     SetLastError(0xfeedf00d);
256     hwinsta = CreateWindowStationW(winstaName, 0, WINSTA_ALL_ACCESS, NULL);
257     *error = GetLastError();
258     return hwinsta;
259 }
260 
261 static HDESK open_desk(PCWSTR deskName, DWORD *error)
262 {
263     HDESK hdesk;
264     SetLastError(0xfeedf00d);
265     hdesk = OpenDesktopW(deskName, 0, FALSE, DESKTOP_ALL_ACCESS);
266     *error = GetLastError();
267     return hdesk;
268 }
269 
270 static HDESK create_desk(PCWSTR deskName, DWORD *error)
271 {
272     HDESK hdesk;
273     SetLastError(0xfeedf00d);
274     hdesk = CreateDesktopW(deskName, NULL, NULL, 0, DESKTOP_ALL_ACCESS, NULL);
275     *error = GetLastError();
276     return hdesk;
277 }
278 
279 static void Test_References(void)
280 {
281     PCWSTR winstaName = L"RefTestWinsta";
282     PCWSTR deskName = L"RefTestDesktop";
283     HWINSTA hwinsta;
284     HWINSTA hwinsta2;
285     HWINSTA hwinstaProcess;
286     DWORD error;
287     NTSTATUS status;
288     OBJECT_BASIC_INFORMATION objectInfo = { 0 };
289     HDESK hdesk;
290     HDESK hdesk1;
291     BOOL ret;
292     ULONG baseRefs;
293 
294 #define check_ref(handle, hdlcnt, ptrcnt) \
295     status = NtQueryObject(handle, ObjectBasicInformation, &objectInfo, sizeof(objectInfo), NULL);  \
296     ok(status == STATUS_SUCCESS, "status = 0x%lx\n", status);                                       \
297     ok(objectInfo.HandleCount == (hdlcnt), "HandleCount = %lu, expected %lu\n", objectInfo.HandleCount, (ULONG)(hdlcnt));  \
298     ok(objectInfo.PointerCount == (ptrcnt), "PointerCount = %lu, expected %lu\n", objectInfo.PointerCount, (ULONG)(ptrcnt));
299 
300     /* Winsta shouldn't exist */
301     hwinsta = open_winsta(winstaName, &error);
302     ok(hwinsta == NULL && error == ERROR_FILE_NOT_FOUND, "Got 0x%p, 0x%lx\n", hwinsta, error);
303 
304     /* Create it -- we get 1/4 instead of 1/3 because Winstas are kept in a list */
305     hwinsta = create_winsta(winstaName, &error);
306     ok(hwinsta != NULL && error == NO_ERROR, "Got 0x%p, 0x%lx\n", hwinsta, error);
307     check_ref(hwinsta, 1, 4);
308     baseRefs = objectInfo.PointerCount;
309     ok(baseRefs == 4, "Window station initially has %lu references, expected 4\n", baseRefs);
310     check_ref(hwinsta, 1, baseRefs);
311 
312     /* Open a second handle */
313     hwinsta2 = open_winsta(winstaName, &error);
314     ok(hwinsta2 != NULL && error == 0xfeedf00d, "Got 0x%p, 0x%lx\n", hwinsta, error);
315     check_ref(hwinsta, 2, baseRefs + 1);
316 
317     /* Close second handle -- back to 1/4 */
318     ret = CloseHandle(hwinsta2);
319     ok(ret == TRUE, "ret = %d\n", ret);
320     check_ref(hwinsta, 1, baseRefs);
321 
322     /* Same game but using CloseWindowStation */
323     hwinsta2 = open_winsta(winstaName, &error);
324     ok(hwinsta2 != NULL && error == 0xfeedf00d, "Got 0x%p, 0x%lx\n", hwinsta, error);
325     check_ref(hwinsta, 2, baseRefs + 1);
326     ret = CloseWindowStation(hwinsta2);
327     ok(ret == TRUE, "ret = %d\n", ret);
328     check_ref(hwinsta, 1, baseRefs);
329 
330     /* Set it as the process Winsta */
331     hwinstaProcess = GetProcessWindowStation();
332     SetProcessWindowStation(hwinsta);
333     check_ref(hwinsta, 2, baseRefs + 2);
334 
335     /* Create a desktop. It takes a reference */
336     hdesk = create_desk(deskName, &error);
337     ok(hdesk != NULL && error == 0xfeedf00d, "Got 0x%p, 0x%lx\n", hdesk, error);
338     check_ref(hwinsta, 2, baseRefs + 3);
339 
340     /* CloseHandle fails, must use CloseDesktop */
341     ret = CloseHandle(hdesk);
342     ok(ret == FALSE, "ret = %d\n", ret);
343     check_ref(hwinsta, 2, baseRefs + 3);
344     ret = CloseDesktop(hdesk);
345     ok(ret == TRUE, "ret = %d\n", ret);
346     check_ref(hwinsta, 2, baseRefs + 2); // 2/7 on Win7?
347 
348     /* Desktop no longer exists */
349     hdesk = open_desk(deskName, &error);
350     ok(hdesk == NULL && error == ERROR_FILE_NOT_FOUND, "Got 0x%p, 0x%lx\n", hdesk, error);
351     check_ref(hwinsta, 2, baseRefs + 2);
352 
353     /* Restore the original process Winsta */
354     SetProcessWindowStation(hwinstaProcess);
355     check_ref(hwinsta, 1, baseRefs);
356 
357     /* Close our last handle */
358     ret = CloseHandle(hwinsta);
359     ok(ret == TRUE, "ret = %d\n", ret);
360 
361     /* Winsta no longer exists */
362     hwinsta = open_winsta(winstaName, &error);
363     ok(hwinsta == NULL && error == ERROR_FILE_NOT_FOUND, "Got 0x%p, 0x%lx\n", hwinsta, error);
364 
365     /* Create the Winsta again, and close it while there's still a desktop */
366     hwinsta = create_winsta(winstaName, &error);
367     ok(hwinsta != NULL && error == NO_ERROR, "Got 0x%p, 0x%lx\n", hwinsta, error);
368     check_ref(hwinsta, 1, baseRefs);
369     hwinstaProcess = GetProcessWindowStation();
370     SetProcessWindowStation(hwinsta);
371     check_ref(hwinsta, 2, baseRefs + 2);
372 
373     hdesk = create_desk(deskName, &error);
374     ok(hdesk != NULL && error == 0xfeedf00d, "Got 0x%p, 0x%lx\n", hdesk, error);
375     check_ref(hwinsta, 2, baseRefs + 3);
376 
377     /* The reference from the desktop is still there, hence 1/5 */
378     SetProcessWindowStation(hwinstaProcess);
379     check_ref(hwinsta, 1, baseRefs + 1);
380     ret = CloseHandle(hwinsta);
381     ok(ret == TRUE, "ret = %d\n", ret);
382     hwinsta = open_winsta(winstaName, &error);
383     ok(hwinsta == NULL && error == ERROR_FILE_NOT_FOUND, "Got 0x%p, 0x%lx\n", hwinsta, error);
384 
385     /* Test references by SetThreadDesktop */
386     hdesk1 = GetThreadDesktop(GetCurrentThreadId());
387     ok (hdesk1 != hdesk, "Expected the new desktop not to be the thread desktop\n");
388 
389     check_ref(hdesk, 1, 8);
390     baseRefs = objectInfo.PointerCount;
391     ok(baseRefs == 8, "Desktop initially has %lu references, expected 8\n", baseRefs);
392     check_ref(hdesk, 1, baseRefs);
393 
394     SetThreadDesktop(hdesk);
395     check_ref(hdesk, 1, baseRefs + 1);
396     ok (GetThreadDesktop(GetCurrentThreadId()) == hdesk, "Expected GetThreadDesktop to return hdesk\n");
397 
398     SetThreadDesktop(hdesk1);
399     check_ref(hdesk, 1, baseRefs);
400     ok (GetThreadDesktop(GetCurrentThreadId()) == hdesk1, "Expected GetThreadDesktop to return hdesk1\n");
401 }
402 
403 START_TEST(desktop)
404 {
405     char **test_argv;
406     int argc = winetest_get_mainargs( &test_argv );
407 
408     /* this program tests some cases that a child application fails to initialize */
409     /* to test this behaviour properly we have to disable error messages */
410     //SetErrorMode( SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX );
411 
412     if (argc >= 3)
413     {
414         unsigned int arg;
415         /* Child process. */
416         sscanf (test_argv[2], "%d", (unsigned int *) &arg);
417         do_InitialDesktop_child( arg );
418         return;
419     }
420 
421     Test_InitialDesktop(test_argv[0]);
422     Test_OpenInputDesktop();
423     Test_References();
424 }
425