/* * Unit tests for window stations and desktops * * Copyright 2002 Alexandre Julliard * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include "wine/test.h" #include "winbase.h" #include "wingdi.h" #include "winuser.h" #include "winnls.h" #include "winternl.h" static NTSTATUS (WINAPI *pNtQueryObject)(HANDLE, OBJECT_INFORMATION_CLASS, PVOID, ULONG, PULONG); #define DESKTOP_ALL_ACCESS 0x01ff static void print_object( HANDLE obj ) { char buffer[100]; DWORD size; strcpy( buffer, "foobar" ); if (!GetUserObjectInformationA( obj, UOI_NAME, buffer, sizeof(buffer), &size )) trace( "could not get info for %p\n", obj ); else trace( "obj %p name '%s'\n", obj, buffer ); strcpy( buffer, "foobar" ); if (!GetUserObjectInformationA( obj, UOI_TYPE, buffer, sizeof(buffer), &size )) trace( "could not get type for %p\n", obj ); else trace( "obj %p type '%s'\n", obj, buffer ); } static void register_class(void) { WNDCLASSA cls; cls.style = CS_DBLCLKS; /* Windows < Vista apparently checks lpfnWndProc against the address of * DefWindowProcA(), and for some reason fails to change the thread desktop * after creating and destroying a window if it doesn't match. Using an IAT * (as is default) or a wrapper triggers this, so use GetProcAddress() as * a workaround. */ cls.lpfnWndProc = (void *)GetProcAddress(GetModuleHandleA("user32"), "DefWindowProcA"); cls.cbClsExtra = 0; cls.cbWndExtra = 0; cls.hInstance = GetModuleHandleA(0); cls.hIcon = 0; cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW); cls.hbrBackground = GetStockObject(WHITE_BRUSH); cls.lpszMenuName = NULL; cls.lpszClassName = "WinStationClass"; RegisterClassA(&cls); } static HDESK initial_desktop; static DWORD CALLBACK thread( LPVOID arg ) { HDESK d1, d2; HWND hwnd = CreateWindowExA(0,"WinStationClass","test",WS_POPUP,0,0,100,100,GetDesktopWindow(),0,0,0); ok( hwnd != 0, "CreateWindow failed\n" ); d1 = GetThreadDesktop(GetCurrentThreadId()); trace( "thread %p desktop: %p\n", arg, d1 ); ok( d1 == initial_desktop, "thread %p doesn't use initial desktop\n", arg ); SetLastError( 0xdeadbeef ); ok( !CloseHandle( d1 ), "CloseHandle succeeded\n" ); ok( GetLastError() == ERROR_INVALID_HANDLE, "bad last error %d\n", GetLastError() ); SetLastError( 0xdeadbeef ); ok( !CloseDesktop( d1 ), "CloseDesktop succeeded\n" ); ok( GetLastError() == ERROR_BUSY || broken(GetLastError() == 0xdeadbeef), /* wow64 */ "bad last error %d\n", GetLastError() ); print_object( d1 ); d2 = CreateDesktopA( "foobar2", NULL, NULL, 0, DESKTOP_ALL_ACCESS, NULL ); trace( "created desktop %p\n", d2 ); ok( d2 != 0, "CreateDesktop failed\n" ); SetLastError( 0xdeadbeef ); ok( !SetThreadDesktop( d2 ), "set thread desktop succeeded with existing window\n" ); ok( GetLastError() == ERROR_BUSY || broken(GetLastError() == 0xdeadbeef), /* wow64 */ "bad last error %d\n", GetLastError() ); DestroyWindow( hwnd ); ok( SetThreadDesktop( d2 ), "set thread desktop failed\n" ); d1 = GetThreadDesktop(GetCurrentThreadId()); ok( d1 == d2, "GetThreadDesktop did not return set desktop %p/%p\n", d1, d2 ); print_object( d2 ); if (arg < (LPVOID)5) { HANDLE hthread = CreateThread( NULL, 0, thread, (char *)arg + 1, 0, NULL ); Sleep(1000); WaitForSingleObject( hthread, INFINITE ); CloseHandle( hthread ); } return 0; } static void test_handles(void) { HWINSTA w1, w2, w3; HDESK d1, d2, d3; HANDLE hthread; DWORD id, flags, le; ATOM atom; char buffer[20]; DWORD size; BOOL ret; /* win stations */ w1 = GetProcessWindowStation(); ok( GetProcessWindowStation() == w1, "GetProcessWindowStation returned different handles\n" ); ok( !CloseWindowStation(w1), "closing process win station succeeded\n" ); SetLastError( 0xdeadbeef ); ok( !CloseHandle(w1), "closing process win station handle succeeded\n" ); ok( GetLastError() == ERROR_INVALID_HANDLE, "bad last error %d\n", GetLastError() ); print_object( w1 ); flags = 0; ok( GetHandleInformation( w1, &flags ), "GetHandleInformation failed\n" ); ok( !(flags & HANDLE_FLAG_PROTECT_FROM_CLOSE) || broken(flags & HANDLE_FLAG_PROTECT_FROM_CLOSE), /* set on nt4 */ "handle %p PROTECT_FROM_CLOSE set\n", w1 ); ok( DuplicateHandle( GetCurrentProcess(), w1, GetCurrentProcess(), (PHANDLE)&w2, 0, TRUE, DUPLICATE_SAME_ACCESS ), "DuplicateHandle failed\n" ); ok( CloseWindowStation(w2), "closing dup win station failed\n" ); ok( DuplicateHandle( GetCurrentProcess(), w1, GetCurrentProcess(), (PHANDLE)&w2, 0, TRUE, DUPLICATE_SAME_ACCESS ), "DuplicateHandle failed\n" ); ok( CloseHandle(w2), "closing dup win station handle failed\n" ); w2 = CreateWindowStationA("WinSta0", 0, WINSTA_ALL_ACCESS, NULL ); le = GetLastError(); ok( w2 != 0 || le == ERROR_ACCESS_DENIED, "CreateWindowStation failed (%u)\n", le ); if (w2 != 0) { ok( w2 != w1, "CreateWindowStation returned default handle\n" ); SetLastError( 0xdeadbeef ); ok( !CloseDesktop( (HDESK)w2 ), "CloseDesktop succeeded on win station\n" ); ok( GetLastError() == ERROR_INVALID_HANDLE || broken(GetLastError() == 0xdeadbeef), /* wow64 */ "bad last error %d\n", GetLastError() ); ok( CloseWindowStation( w2 ), "CloseWindowStation failed\n" ); w2 = CreateWindowStationA("WinSta0", 0, WINSTA_ALL_ACCESS, NULL ); ok( CloseHandle( w2 ), "CloseHandle failed\n" ); } else if (le == ERROR_ACCESS_DENIED) win_skip( "Not enough privileges for CreateWindowStation\n" ); w2 = OpenWindowStationA("winsta0", TRUE, WINSTA_ALL_ACCESS ); ok( w2 != 0, "OpenWindowStation failed\n" ); ok( w2 != w1, "OpenWindowStation returned default handle\n" ); ok( CloseWindowStation( w2 ), "CloseWindowStation failed\n" ); w2 = OpenWindowStationA("dummy name", TRUE, WINSTA_ALL_ACCESS ); ok( !w2, "open dummy win station succeeded\n" ); CreateMutexA( NULL, 0, "foobar" ); w2 = CreateWindowStationA("foobar", 0, WINSTA_ALL_ACCESS, NULL ); le = GetLastError(); ok( w2 != 0 || le == ERROR_ACCESS_DENIED, "create foobar station failed (%u)\n", le ); if (w2 != 0) { w3 = OpenWindowStationA("foobar", TRUE, WINSTA_ALL_ACCESS ); ok( w3 != 0, "open foobar station failed\n" ); ok( w3 != w2, "open foobar station returned same handle\n" ); ok( CloseWindowStation( w2 ), "CloseWindowStation failed\n" ); ok( CloseWindowStation( w3 ), "CloseWindowStation failed\n" ); w3 = OpenWindowStationA("foobar", TRUE, WINSTA_ALL_ACCESS ); ok( !w3, "open foobar station succeeded\n" ); w2 = CreateWindowStationA("foobar1", 0, WINSTA_ALL_ACCESS, NULL ); ok( w2 != 0, "create foobar station failed\n" ); w3 = CreateWindowStationA("foobar2", 0, WINSTA_ALL_ACCESS, NULL ); ok( w3 != 0, "create foobar station failed\n" ); ok( GetHandleInformation( w2, &flags ), "GetHandleInformation failed\n" ); ok( GetHandleInformation( w3, &flags ), "GetHandleInformation failed\n" ); SetProcessWindowStation( w2 ); atom = GlobalAddAtomA("foo"); ok( GlobalGetAtomNameA( atom, buffer, sizeof(buffer) ) == 3, "GlobalGetAtomName failed\n" ); ok( !lstrcmpiA( buffer, "foo" ), "bad atom value %s\n", buffer ); ok( !CloseWindowStation( w2 ), "CloseWindowStation succeeded\n" ); ok( GetHandleInformation( w2, &flags ), "GetHandleInformation failed\n" ); SetProcessWindowStation( w3 ); ok( GetHandleInformation( w2, &flags ), "GetHandleInformation failed\n" ); ok( CloseWindowStation( w2 ), "CloseWindowStation failed\n" ); ok( GlobalGetAtomNameA( atom, buffer, sizeof(buffer) ) == 3, "GlobalGetAtomName failed\n" ); ok( !lstrcmpiA( buffer, "foo" ), "bad atom value %s\n", buffer ); } else if (le == ERROR_ACCESS_DENIED) win_skip( "Not enough privileges for CreateWindowStation\n" ); SetLastError( 0xdeadbeef ); w2 = OpenWindowStationA( "", TRUE, WINSTA_ALL_ACCESS ); ok( !w2, "open station succeeded\n" ); todo_wine ok( GetLastError() == ERROR_FILE_NOT_FOUND, "wrong error %u\n", GetLastError() ); SetLastError( 0xdeadbeef ); w2 = CreateWindowStationA( "", 0, WINSTA_ALL_ACCESS, NULL ); ok( w2 != 0, "create station failed err %u\n", GetLastError() ); memset( buffer, 0, sizeof(buffer) ); ret = GetUserObjectInformationA( w2, UOI_NAME, buffer, sizeof(buffer), &size ); ok( ret, "GetUserObjectInformationA failed with error %u\n", GetLastError() ); todo_wine ok( !memcmp(buffer, "Service-0x0-", 12), "unexpected window station name '%s'\n", buffer ); todo_wine ok( buffer[strlen(buffer) - 1] == '$', "unexpected window station name '%s'\n", buffer ); SetLastError( 0xdeadbeef ); w3 = OpenWindowStationA( "", TRUE, WINSTA_ALL_ACCESS ); todo_wine ok( w3 != 0, "open station failed err %u\n", GetLastError() ); CloseWindowStation( w3 ); CloseWindowStation( w2 ); SetLastError( 0xdeadbeef ); w2 = CreateWindowStationA( "foo\\bar", 0, WINSTA_ALL_ACCESS, NULL ); ok( !w2, "create station succeeded\n" ); ok( GetLastError() == ERROR_PATH_NOT_FOUND || GetLastError() == ERROR_ACCESS_DENIED, "wrong error %u\n", GetLastError() ); SetLastError( 0xdeadbeef ); w2 = OpenWindowStationA( "foo\\bar", TRUE, WINSTA_ALL_ACCESS ); ok( !w2, "create station succeeded\n" ); ok( GetLastError() == ERROR_PATH_NOT_FOUND, "wrong error %u\n", GetLastError() ); /* desktops */ d1 = GetThreadDesktop(GetCurrentThreadId()); initial_desktop = d1; ok( GetThreadDesktop(GetCurrentThreadId()) == d1, "GetThreadDesktop returned different handles\n" ); flags = 0; ok( GetHandleInformation( d1, &flags ), "GetHandleInformation failed\n" ); ok( !(flags & HANDLE_FLAG_PROTECT_FROM_CLOSE), "handle %p PROTECT_FROM_CLOSE set\n", d1 ); SetLastError( 0xdeadbeef ); ok( !CloseDesktop(d1), "closing thread desktop succeeded\n" ); ok( GetLastError() == ERROR_BUSY || broken(GetLastError() == 0xdeadbeef), /* wow64 */ "bad last error %d\n", GetLastError() ); SetLastError( 0xdeadbeef ); if (CloseHandle( d1 )) /* succeeds on nt4 */ { win_skip( "NT4 desktop handle management is completely different\n" ); return; } ok( GetLastError() == ERROR_INVALID_HANDLE, "bad last error %d\n", GetLastError() ); ok( DuplicateHandle( GetCurrentProcess(), d1, GetCurrentProcess(), (PHANDLE)&d2, 0, TRUE, DUPLICATE_SAME_ACCESS ), "DuplicateHandle failed\n" ); ok( CloseDesktop(d2), "closing dup desktop failed\n" ); ok( DuplicateHandle( GetCurrentProcess(), d1, GetCurrentProcess(), (PHANDLE)&d2, 0, TRUE, DUPLICATE_SAME_ACCESS ), "DuplicateHandle failed\n" ); ok( CloseHandle(d2), "closing dup desktop handle failed\n" ); d2 = OpenDesktopA( "dummy name", 0, TRUE, DESKTOP_ALL_ACCESS ); ok( !d2, "open dummy desktop succeeded\n" ); SetLastError( 0xdeadbeef ); d2 = CreateDesktopA( "", NULL, NULL, 0, DESKTOP_ALL_ACCESS, NULL ); todo_wine ok( !d2, "create empty desktop succeeded\n" ); todo_wine ok( GetLastError() == ERROR_INVALID_HANDLE, "wrong error %u\n", GetLastError() ); SetLastError( 0xdeadbeef ); d2 = OpenDesktopA( "", 0, TRUE, DESKTOP_ALL_ACCESS ); ok( !d2, "open empty desktop succeeded\n" ); ok( GetLastError() == ERROR_INVALID_HANDLE, "wrong error %u\n", GetLastError() ); SetLastError( 0xdeadbeef ); d2 = CreateDesktopA( "foo\\bar", NULL, NULL, 0, DESKTOP_ALL_ACCESS, NULL ); ok( !d2, "create desktop succeeded\n" ); ok( GetLastError() == ERROR_BAD_PATHNAME, "wrong error %u\n", GetLastError() ); SetLastError( 0xdeadbeef ); d2 = OpenDesktopA( "foo\\bar", 0, TRUE, DESKTOP_ALL_ACCESS ); ok( !d2, "open desktop succeeded\n" ); ok( GetLastError() == ERROR_BAD_PATHNAME, "wrong error %u\n", GetLastError() ); d2 = CreateDesktopA( "foobar", NULL, NULL, 0, DESKTOP_ALL_ACCESS, NULL ); ok( d2 != 0, "create foobar desktop failed\n" ); SetLastError( 0xdeadbeef ); ok( !CloseWindowStation( (HWINSTA)d2 ), "CloseWindowStation succeeded on desktop\n" ); ok( GetLastError() == ERROR_INVALID_HANDLE || broken(GetLastError() == 0xdeadbeef), /* wow64 */ "bad last error %d\n", GetLastError() ); SetLastError( 0xdeadbeef ); d3 = CreateDesktopA( "foobar", NULL, NULL, 0, DESKTOP_ALL_ACCESS, NULL ); ok( d3 != 0, "create foobar desktop again failed\n" ); ok( GetLastError() == 0xdeadbeef, "bad last error %d\n", GetLastError() ); ok( CloseDesktop( d3 ), "CloseDesktop failed\n" ); d3 = OpenDesktopA( "foobar", 0, TRUE, DESKTOP_ALL_ACCESS ); ok( d3 != 0, "open foobar desktop failed\n" ); ok( d3 != d2, "open foobar desktop returned same handle\n" ); ok( CloseDesktop( d2 ), "CloseDesktop failed\n" ); ok( CloseDesktop( d3 ), "CloseDesktop failed\n" ); d3 = OpenDesktopA( "foobar", 0, TRUE, DESKTOP_ALL_ACCESS ); ok( !d3, "open foobar desktop succeeded\n" ); ok( !CloseHandle(d1), "closing thread desktop handle succeeded\n" ); d2 = GetThreadDesktop(GetCurrentThreadId()); ok( d1 == d2, "got different handles after close\n" ); register_class(); trace( "thread 1 desktop: %p\n", d1 ); print_object( d1 ); hthread = CreateThread( NULL, 0, thread, (LPVOID)2, 0, &id ); Sleep(1000); trace( "get other thread desktop: %p\n", GetThreadDesktop(id) ); WaitForSingleObject( hthread, INFINITE ); CloseHandle( hthread ); /* clean side effect */ SetProcessWindowStation( w1 ); } /* Enumeration tests */ static BOOL CALLBACK window_station_callbackA(LPSTR winsta, LPARAM lp) { trace("window_station_callbackA called with argument %s\n", winsta); return lp; } static BOOL CALLBACK open_window_station_callbackA(LPSTR winsta, LPARAM lp) { HWINSTA hwinsta; trace("open_window_station_callbackA called with argument %s\n", winsta); hwinsta = OpenWindowStationA(winsta, FALSE, WINSTA_ENUMERATE); ok(hwinsta != NULL, "Could not open desktop %s!\n", winsta); if (hwinsta) CloseWindowStation(hwinsta); return lp; } static void test_enumstations(void) { DWORD ret; HWINSTA hwinsta; if (0) /* Crashes instead */ { SetLastError(0xbabefeed); ret = EnumWindowStationsA(NULL, 0); ok(!ret, "EnumWindowStationsA returned successfully!\n"); ok(GetLastError() == ERROR_INVALID_PARAMETER, "LastError is set to %08x\n", GetLastError()); } hwinsta = CreateWindowStationA("winsta_test", 0, WINSTA_ALL_ACCESS, NULL); ret = GetLastError(); ok(hwinsta != NULL || ret == ERROR_ACCESS_DENIED, "CreateWindowStation failed (%u)\n", ret); if (!hwinsta) { win_skip("Not enough privileges for CreateWindowStation\n"); return; } SetLastError(0xdeadbeef); ret = EnumWindowStationsA(open_window_station_callbackA, 0x12345); ok(ret == 0x12345, "EnumWindowStationsA returned %x\n", ret); ok(GetLastError() == 0xdeadbeef, "LastError is set to %08x\n", GetLastError()); SetLastError(0xdeadbeef); ret = EnumWindowStationsA(window_station_callbackA, 0); ok(!ret, "EnumWindowStationsA returned %x\n", ret); ok(GetLastError() == 0xdeadbeef, "LastError is set to %08x\n", GetLastError()); } static BOOL CALLBACK desktop_callbackA(LPSTR desktop, LPARAM lp) { trace("desktop_callbackA called with argument %s\n", desktop); return lp; } static BOOL CALLBACK open_desktop_callbackA(LPSTR desktop, LPARAM lp) { HDESK hdesk; static int once; trace("open_desktop_callbackA called with argument %s\n", desktop); /* Only try to open one desktop */ if (once++) return lp; hdesk = OpenDesktopA(desktop, 0, FALSE, DESKTOP_ENUMERATE); ok(hdesk != NULL, "Could not open desktop %s!\n", desktop); if (hdesk) CloseDesktop(hdesk); return lp; } static void test_enumdesktops(void) { BOOL ret; if (0) /* Crashes instead */ { SetLastError(0xbabefeed); ret = EnumDesktopsA(GetProcessWindowStation(), NULL, 0); ok(!ret, "EnumDesktopsA returned successfully!\n"); ok(GetLastError() == ERROR_INVALID_PARAMETER, "LastError is set to %08x\n", GetLastError()); } SetLastError(0xdeadbeef); ret = EnumDesktopsA(NULL, desktop_callbackA, 0x12345); ok(ret == 0x12345, "EnumDesktopsA returned %x\n", ret); ok(GetLastError() == 0xdeadbeef, "LastError is set to %08x\n", GetLastError()); SetLastError(0xdeadbeef); ret = EnumDesktopsA(GetProcessWindowStation(), open_desktop_callbackA, 0x12345); ok(ret == 0x12345, "EnumDesktopsA returned %x\n", ret); ok(GetLastError() == 0xdeadbeef, "LastError is set to %08x\n", GetLastError()); SetLastError(0xdeadbeef); ret = EnumDesktopsA(INVALID_HANDLE_VALUE, desktop_callbackA, 0x12345); ok(!ret, "EnumDesktopsA returned %x\n", ret); ok(GetLastError() == ERROR_INVALID_HANDLE, "LastError is set to %08x\n", GetLastError()); SetLastError(0xdeadbeef); ret = EnumDesktopsA(GetProcessWindowStation(), desktop_callbackA, 0); ok(!ret, "EnumDesktopsA returned %x\n", ret); ok(GetLastError() == 0xdeadbeef, "LastError is set to %08x\n", GetLastError()); } /* Miscellaneous tests */ static void test_getuserobjectinformation(void) { WCHAR foobarTestW[] = {'\\','f','o','o','b','a','r','T','e','s','t',0}; WCHAR DesktopW[] = {'D','e','s','k','t','o','p',0}; OBJECT_NAME_INFORMATION *name_info; WCHAR bufferW[20]; char buffer[64]; NTSTATUS status; DWORD size; HDESK desk; BOOL ret; desk = CreateDesktopA("foobarTest", NULL, NULL, 0, DESKTOP_ALL_ACCESS, NULL); ok(desk != 0, "open foobarTest desktop failed\n"); strcpy(buffer, "blahblah"); /** Tests for UOI_NAME **/ /* Get size, test size and return value/error code */ SetLastError(0xdeadbeef); size = 0xdeadbeef; ret = GetUserObjectInformationA(desk, UOI_NAME, NULL, 0, &size); ok(!ret, "GetUserObjectInformationA returned %x\n", ret); ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "LastError is set to %08x\n", GetLastError()); ok(size == 22, "size is set to %d\n", size); /* Windows returns Unicode length (11*2) */ /* Get string */ SetLastError(0xdeadbeef); size = 0xdeadbeef; ret = GetUserObjectInformationA(desk, UOI_NAME, buffer, sizeof(buffer), &size); ok(ret, "GetUserObjectInformationA returned %x\n", ret); ok(GetLastError() == 0xdeadbeef, "LastError is set to %08x\n", GetLastError()); ok(strcmp(buffer, "foobarTest") == 0, "Buffer is set to '%s'\n", buffer); ok(size == 11, "size is set to %d\n", size); /* 11 bytes in 'foobarTest\0' */ /* Get size, test size and return value/error code (Unicode) */ SetLastError(0xdeadbeef); size = 0xdeadbeef; ret = GetUserObjectInformationW(desk, UOI_NAME, NULL, 0, &size); ok(!ret, "GetUserObjectInformationW returned %x\n", ret); ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "LastError is set to %08x\n", GetLastError()); ok(size == 22, "size is set to %d\n", size); /* 22 bytes in 'foobarTest\0' in Unicode */ /* Get string (Unicode) */ SetLastError(0xdeadbeef); size = 0xdeadbeef; ret = GetUserObjectInformationW(desk, UOI_NAME, bufferW, sizeof(bufferW), &size); ok(ret, "GetUserObjectInformationW returned %x\n", ret); ok(GetLastError() == 0xdeadbeef, "LastError is set to %08x\n", GetLastError()); ok(lstrcmpW(bufferW, foobarTestW + 1) == 0, "Buffer is not set to 'foobarTest'\n"); ok(size == 22, "size is set to %d\n", size); /* 22 bytes in 'foobarTest\0' in Unicode */ /* ObjectNameInformation does not return the full desktop name */ name_info = (OBJECT_NAME_INFORMATION *)buffer; status = pNtQueryObject(desk, ObjectNameInformation, name_info, sizeof(buffer), NULL); ok(!status, "expected STATUS_SUCCESS, got %08x\n", status); ok(lstrcmpW(name_info->Name.Buffer, foobarTestW) == 0, "expected '\\foobarTest', got %s\n", wine_dbgstr_w(name_info->Name.Buffer)); /** Tests for UOI_TYPE **/ /* Get size, test size and return value/error code */ SetLastError(0xdeadbeef); size = 0xdeadbeef; ret = GetUserObjectInformationA(desk, UOI_TYPE, NULL, 0, &size); ok(!ret, "GetUserObjectInformationA returned %x\n", ret); ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "LastError is set to %08x\n", GetLastError()); ok(size == 16, "size is set to %d\n", size); /* Windows returns Unicode length (8*2) */ /* Get string */ SetLastError(0xdeadbeef); size = 0xdeadbeef; ret = GetUserObjectInformationA(desk, UOI_TYPE, buffer, sizeof(buffer), &size); ok(ret, "GetUserObjectInformationA returned %x\n", ret); ok(GetLastError() == 0xdeadbeef, "LastError is set to %08x\n", GetLastError()); ok(strcmp(buffer, "Desktop") == 0, "Buffer is set to '%s'\n", buffer); ok(size == 8, "size is set to %d\n", size); /* 8 bytes in 'Desktop\0' */ /* Get size, test size and return value/error code (Unicode) */ size = 0xdeadbeef; SetLastError(0xdeadbeef); ret = GetUserObjectInformationW(desk, UOI_TYPE, NULL, 0, &size); ok(!ret, "GetUserObjectInformationW returned %x\n", ret); ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "LastError is set to %08x\n", GetLastError()); ok(size == 16, "size is set to %d\n", size); /* 16 bytes in 'Desktop\0' in Unicode */ /* Get string (Unicode) */ SetLastError(0xdeadbeef); size = 0xdeadbeef; ret = GetUserObjectInformationW(desk, UOI_TYPE, bufferW, sizeof(bufferW), &size); ok(ret, "GetUserObjectInformationW returned %x\n", ret); ok(GetLastError() == 0xdeadbeef, "LastError is set to %08x\n", GetLastError()); ok(lstrcmpW(bufferW, DesktopW) == 0, "Buffer is not set to 'Desktop'\n"); ok(size == 16, "size is set to %d\n", size); /* 16 bytes in 'Desktop\0' in Unicode */ ok(CloseDesktop(desk), "CloseDesktop failed\n"); } static void test_inputdesktop(void) { HDESK input_desk, old_input_desk, thread_desk, old_thread_desk, new_desk; DWORD ret; CHAR name[1024]; INPUT inputs[1]; inputs[0].type = INPUT_KEYBOARD; U(inputs[0]).ki.wVk = 0; U(inputs[0]).ki.wScan = 0x3c0; U(inputs[0]).ki.dwFlags = KEYEVENTF_UNICODE; /* OpenInputDesktop creates new handles for each calls */ old_input_desk = OpenInputDesktop(0, FALSE, DESKTOP_ALL_ACCESS); ok(old_input_desk != NULL, "OpenInputDesktop failed!\n"); memset(name, 0, sizeof(name)); ret = GetUserObjectInformationA(old_input_desk, UOI_NAME, name, 1024, NULL); ok(ret, "GetUserObjectInformation failed!\n"); ok(!strcmp(name, "Default"), "unexpected desktop %s\n", name); input_desk = OpenInputDesktop(0, FALSE, DESKTOP_ALL_ACCESS); ok(input_desk != NULL, "OpenInputDesktop failed!\n"); memset(name, 0, sizeof(name)); ret = GetUserObjectInformationA(input_desk, UOI_NAME, name, 1024, NULL); ok(ret, "GetUserObjectInformation failed!\n"); ok(!strcmp(name, "Default"), "unexpected desktop %s\n", name); ok(old_input_desk != input_desk, "returned the same handle!\n"); ret = CloseDesktop(input_desk); ok(ret, "CloseDesktop failed!\n"); /* by default, GetThreadDesktop is the input desktop, SendInput should succeed. */ old_thread_desk = GetThreadDesktop(GetCurrentThreadId()); ok(old_thread_desk != NULL, "GetThreadDesktop failed!\n"); memset(name, 0, sizeof(name)); ret = GetUserObjectInformationA(old_thread_desk, UOI_NAME, name, 1024, NULL); ok(!strcmp(name, "Default"), "unexpected desktop %s\n", name); SetLastError(0xdeadbeef); ret = SendInput(1, inputs, sizeof(INPUT)); ok(GetLastError() == 0xdeadbeef, "unexpected last error %08x\n", GetLastError()); ok(ret == 1, "unexpected return count %d\n", ret); /* Set thread desktop to the new desktop, SendInput should fail. */ new_desk = CreateDesktopA("new_desk", NULL, NULL, 0, DESKTOP_ALL_ACCESS, NULL); ok(new_desk != NULL, "CreateDesktop failed!\n"); ret = SetThreadDesktop(new_desk); ok(ret, "SetThreadDesktop failed!\n"); thread_desk = GetThreadDesktop(GetCurrentThreadId()); ok(thread_desk == new_desk, "thread desktop doesn't match!\n"); memset(name, 0, sizeof(name)); ret = GetUserObjectInformationA(thread_desk, UOI_NAME, name, 1024, NULL); ok(!strcmp(name, "new_desk"), "unexpected desktop %s\n", name); SetLastError(0xdeadbeef); ret = SendInput(1, inputs, sizeof(INPUT)); if(broken(GetLastError() == 0xdeadbeef)) { SetThreadDesktop(old_thread_desk); CloseDesktop(old_input_desk); CloseDesktop(input_desk); CloseDesktop(new_desk); win_skip("Skip tests on NT4\n"); return; } todo_wine ok(GetLastError() == ERROR_ACCESS_DENIED, "unexpected last error %08x\n", GetLastError()); ok(ret == 1 || broken(ret == 0) /* Win64 */, "unexpected return count %d\n", ret); /* Set thread desktop back to the old thread desktop, SendInput should success. */ ret = SetThreadDesktop(old_thread_desk); ok(ret, "SetThreadDesktop failed!\n"); thread_desk = GetThreadDesktop(GetCurrentThreadId()); ok(thread_desk == old_thread_desk, "thread desktop doesn't match!\n"); memset(name, 0, sizeof(name)); ret = GetUserObjectInformationA(thread_desk, UOI_NAME, name, 1024, NULL); ok(!strcmp(name, "Default"), "unexpected desktop %s\n", name); SetLastError(0xdeadbeef); ret = SendInput(1, inputs, sizeof(INPUT)); ok(GetLastError() == 0xdeadbeef, "unexpected last error %08x\n", GetLastError()); ok(ret == 1, "unexpected return count %d\n", ret); /* Set thread desktop to the input desktop, SendInput should success. */ ret = SetThreadDesktop(old_input_desk); ok(ret, "SetThreadDesktop failed!\n"); thread_desk = GetThreadDesktop(GetCurrentThreadId()); ok(thread_desk == old_input_desk, "thread desktop doesn't match!\n"); memset(name, 0, sizeof(name)); ret = GetUserObjectInformationA(thread_desk, UOI_NAME, name, 1024, NULL); ok(!strcmp(name, "Default"), "unexpected desktop %s\n", name); SetLastError(0xdeadbeef); ret = SendInput(1, inputs, sizeof(INPUT)); ok(GetLastError() == 0xdeadbeef, "unexpected last error %08x\n", GetLastError()); ok(ret == 1, "unexpected return count %d\n", ret); /* Switch input desktop to the new desktop, SendInput should fail. */ ret = SwitchDesktop(new_desk); ok(ret, "SwitchDesktop failed!\n"); input_desk = OpenInputDesktop(0, FALSE, DESKTOP_ALL_ACCESS); ok(input_desk != NULL, "OpenInputDesktop failed!\n"); ok(input_desk != new_desk, "returned the same handle!\n"); memset(name, 0, sizeof(name)); ret = GetUserObjectInformationA(input_desk, UOI_NAME, name, 1024, NULL); ok(ret, "GetUserObjectInformation failed!\n"); todo_wine ok(!strcmp(name, "new_desk"), "unexpected desktop %s\n", name); ret = CloseDesktop(input_desk); ok(ret, "CloseDesktop failed!\n"); SetLastError(0xdeadbeef); ret = SendInput(1, inputs, sizeof(INPUT)); todo_wine ok(GetLastError() == ERROR_ACCESS_DENIED, "unexpected last error %08x\n", GetLastError()); ok(ret == 1 || broken(ret == 0) /* Win64 */, "unexpected return count %d\n", ret); /* Set thread desktop to the new desktop, SendInput should success. */ ret = SetThreadDesktop(new_desk); ok(ret, "SetThreadDesktop failed!\n"); thread_desk = GetThreadDesktop(GetCurrentThreadId()); ok(thread_desk == new_desk, "thread desktop doesn't match!\n"); memset(name, 0, sizeof(name)); ret = GetUserObjectInformationA(thread_desk, UOI_NAME, name, 1024, NULL); ok(!strcmp(name, "new_desk"), "unexpected desktop %s\n", name); SetLastError(0xdeadbeef); ret = SendInput(1, inputs, sizeof(INPUT)); ok(GetLastError() == 0xdeadbeef, "unexpected last error %08x\n", GetLastError()); ok(ret == 1, "unexpected return count %d\n", ret); /* Switch input desktop to the old input desktop, set thread desktop to the old * thread desktop, clean side effects. SendInput should success. */ ret = SwitchDesktop(old_input_desk); input_desk = OpenInputDesktop(0, FALSE, DESKTOP_ALL_ACCESS); ok(input_desk != NULL, "OpenInputDesktop failed!\n"); ok(input_desk != old_input_desk, "returned the same handle!\n"); memset(name, 0, sizeof(name)); ret = GetUserObjectInformationA(input_desk, UOI_NAME, name, 1024, NULL); ok(ret, "GetUserObjectInformation failed!\n"); ok(!strcmp(name, "Default"), "unexpected desktop %s\n", name); ret = SetThreadDesktop(old_thread_desk); ok(ret, "SetThreadDesktop failed!\n"); thread_desk = GetThreadDesktop(GetCurrentThreadId()); ok(thread_desk == old_thread_desk, "thread desktop doesn't match!\n"); memset(name, 0, sizeof(name)); ret = GetUserObjectInformationA(thread_desk, UOI_NAME, name, 1024, NULL); ok(!strcmp(name, "Default"), "unexpected desktop %s\n", name); SetLastError(0xdeadbeef); ret = SendInput(1, inputs, sizeof(INPUT)); ok(GetLastError() == 0xdeadbeef, "unexpected last error %08x\n", GetLastError()); ok(ret == 1, "unexpected return count %d\n", ret); /* free resources */ ret = CloseDesktop(input_desk); ok(ret, "CloseDesktop failed!\n"); ret = CloseDesktop(old_input_desk); ok(ret, "CloseDesktop failed!\n"); ret = CloseDesktop(new_desk); ok(ret, "CloseDesktop failed!\n"); } static void test_inputdesktop2(void) { HWINSTA w1, w2; HDESK thread_desk, new_desk, input_desk, hdesk; DWORD ret; thread_desk = GetThreadDesktop(GetCurrentThreadId()); ok(thread_desk != NULL, "GetThreadDesktop failed!\n"); w1 = GetProcessWindowStation(); ok(w1 != NULL, "GetProcessWindowStation failed!\n"); SetLastError(0xdeadbeef); w2 = CreateWindowStationA("winsta_test", 0, WINSTA_ALL_ACCESS, NULL); ret = GetLastError(); ok(w2 != NULL || ret == ERROR_ACCESS_DENIED, "CreateWindowStation failed (%u)\n", ret); if (!w2) { win_skip("Not enough privileges for CreateWindowStation\n"); return; } ret = EnumDesktopsA(GetProcessWindowStation(), desktop_callbackA, 0); ok(!ret, "EnumDesktopsA failed!\n"); input_desk = OpenInputDesktop(0, FALSE, DESKTOP_ALL_ACCESS); ok(input_desk != NULL, "OpenInputDesktop failed!\n"); ret = CloseDesktop(input_desk); ok(ret, "CloseDesktop failed!\n"); ret = SetProcessWindowStation(w2); ok(ret, "SetProcessWindowStation failed!\n"); hdesk = GetThreadDesktop(GetCurrentThreadId()); ok(hdesk != NULL, "GetThreadDesktop failed!\n"); ok(hdesk == thread_desk, "thread desktop should not change after winstation changed!\n"); ret = EnumDesktopsA(GetProcessWindowStation(), desktop_callbackA, 0); new_desk = CreateDesktopA("desk_test", NULL, NULL, 0, DESKTOP_ALL_ACCESS, NULL); ok(new_desk != NULL, "CreateDesktop failed!\n"); ret = EnumDesktopsA(GetProcessWindowStation(), desktop_callbackA, 0); ok(!ret, "EnumDesktopsA failed!\n"); SetLastError(0xdeadbeef); input_desk = OpenInputDesktop(0, FALSE, DESKTOP_ALL_ACCESS); ok(input_desk == NULL, "OpenInputDesktop should fail on non default winstation!\n"); ok(GetLastError() == ERROR_INVALID_FUNCTION || broken(GetLastError() == 0xdeadbeef), "last error %08x\n", GetLastError()); hdesk = OpenDesktopA("desk_test", 0, TRUE, DESKTOP_ALL_ACCESS); ok(hdesk != NULL, "OpenDesktop failed!\n"); SetLastError(0xdeadbeef); ret = SwitchDesktop(hdesk); todo_wine ok(!ret, "Switch to desktop belong to non default winstation should fail!\n"); todo_wine ok(GetLastError() == ERROR_ACCESS_DENIED || broken(GetLastError() == 0xdeadbeef), "last error %08x\n", GetLastError()); ret = SetThreadDesktop(hdesk); ok(ret, "SetThreadDesktop failed!\n"); /* clean side effect */ ret = SetThreadDesktop(thread_desk); todo_wine ok(ret, "SetThreadDesktop should success even desktop is not belong to process winstation!\n"); ret = SetProcessWindowStation(w1); ok(ret, "SetProcessWindowStation failed!\n"); ret = SetThreadDesktop(thread_desk); ok(ret, "SetThreadDesktop failed!\n"); ret = CloseWindowStation(w2); ok(ret, "CloseWindowStation failed!\n"); ret = CloseDesktop(new_desk); ok(ret, "CloseDesktop failed!\n"); ret = CloseDesktop(hdesk); ok(ret, "CloseDesktop failed!\n"); } static LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { if (msg == WM_DESTROY) { trace("destroying hwnd %p\n", hWnd); PostQuitMessage(0); return 0; } return DefWindowProcA( hWnd, msg, wParam, lParam ); } typedef struct tag_wnd_param { const char *wnd_name; HWND hwnd; HDESK hdesk; HANDLE hevent; } wnd_param; static DWORD WINAPI create_window(LPVOID param) { wnd_param *param1 = param; DWORD ret; MSG msg; ret = SetThreadDesktop(param1->hdesk); ok(ret, "SetThreadDesktop failed!\n"); param1->hwnd = CreateWindowA("test_class", param1->wnd_name, WS_POPUP, 0, 0, 100, 100, NULL, NULL, NULL, NULL); ok(param1->hwnd != 0, "CreateWindowA failed!\n"); ret = SetEvent(param1->hevent); ok(ret, "SetEvent failed!\n"); while (GetMessageA(&msg, 0, 0, 0)) { TranslateMessage(&msg); DispatchMessageA(&msg); } return 0; } static DWORD set_foreground(HWND hwnd) { HWND hwnd_fore; DWORD set_id, fore_id, ret; char win_text[1024]; hwnd_fore = GetForegroundWindow(); GetWindowTextA(hwnd_fore, win_text, 1024); set_id = GetWindowThreadProcessId(hwnd, NULL); fore_id = GetWindowThreadProcessId(hwnd_fore, NULL); trace("\"%s\" %p %08x hwnd %p %08x\n", win_text, hwnd_fore, fore_id, hwnd, set_id); ret = AttachThreadInput(set_id, fore_id, TRUE); trace("AttachThreadInput returned %08x\n", ret); ret = ShowWindow(hwnd, SW_SHOWNORMAL); trace("ShowWindow returned %08x\n", ret); ret = SetWindowPos(hwnd, HWND_TOPMOST, 0,0,0,0, SWP_NOSIZE|SWP_NOMOVE); trace("set topmost returned %08x\n", ret); ret = SetWindowPos(hwnd, HWND_NOTOPMOST, 0,0,0,0, SWP_NOSIZE|SWP_NOMOVE); trace("set notopmost returned %08x\n", ret); ret = SetForegroundWindow(hwnd); trace("SetForegroundWindow returned %08x\n", ret); Sleep(250); AttachThreadInput(set_id, fore_id, FALSE); return ret; } static void test_foregroundwindow(void) { HWND hwnd, hwnd_test, partners[2], hwnds[2]; HDESK hdesks[2]; int thread_desk_id, input_desk_id, hwnd_id; WNDCLASSA wclass; wnd_param param; DWORD ret, timeout, timeout_old; char win_text[1024]; #define DESKTOPS 2 memset( &wclass, 0, sizeof(wclass) ); wclass.lpszClassName = "test_class"; wclass.lpfnWndProc = WndProc; RegisterClassA(&wclass); param.wnd_name = "win_name"; hdesks[0] = GetThreadDesktop(GetCurrentThreadId()); ok(hdesks[0] != NULL, "OpenDesktop failed!\n"); SetLastError(0xdeadbeef); hdesks[1] = CreateDesktopA("desk2", NULL, NULL, 0, DESKTOP_ALL_ACCESS, NULL); ret = GetLastError(); ok(hdesks[1] != NULL || ret == ERROR_ACCESS_DENIED, "CreateDesktop failed (%u)\n", ret); if(!hdesks[1]) { win_skip("Not enough privileges for CreateDesktop\n"); return; } ret = SystemParametersInfoA(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, &timeout_old, 0); if(!ret) { win_skip("Skip tests on NT4\n"); CloseDesktop(hdesks[1]); return; } trace("old timeout %d\n", timeout_old); timeout = 0; ret = SystemParametersInfoA(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, 0, SPIF_SENDCHANGE | SPIF_UPDATEINIFILE); ok(ret, "set foreground lock timeout failed!\n"); ret = SystemParametersInfoA(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, &timeout, 0); ok(ret, "get foreground lock timeout failed!\n"); ok(timeout == 0, "unexpected timeout %d\n", timeout); for (thread_desk_id = 0; thread_desk_id < DESKTOPS; thread_desk_id++) { param.hdesk = hdesks[thread_desk_id]; param.hevent = CreateEventA(NULL, TRUE, FALSE, NULL); CreateThread(NULL, 0, create_window, ¶m, 0, NULL); ret = WaitForSingleObject(param.hevent, INFINITE); ok(ret == WAIT_OBJECT_0, "wait failed!\n"); hwnds[thread_desk_id] = param.hwnd; } for (thread_desk_id = 0; thread_desk_id < DESKTOPS; thread_desk_id++) { param.hdesk = hdesks[thread_desk_id]; param.hevent = CreateEventA(NULL, TRUE, FALSE, NULL); CreateThread(NULL, 0, create_window, ¶m, 0, NULL); ret = WaitForSingleObject(param.hevent, INFINITE); ok(ret == WAIT_OBJECT_0, "wait failed!\n"); partners[thread_desk_id] = param.hwnd; } trace("hwnd0 %p hwnd1 %p partner0 %p partner1 %p\n", hwnds[0], hwnds[1], partners[0], partners[1]); for (hwnd_id = 0; hwnd_id < DESKTOPS; hwnd_id++) for (thread_desk_id = 0; thread_desk_id < DESKTOPS; thread_desk_id++) for (input_desk_id = 0; input_desk_id < DESKTOPS; input_desk_id++) { trace("testing thread_desk %d input_desk %d hwnd %d\n", thread_desk_id, input_desk_id, hwnd_id); hwnd_test = hwnds[hwnd_id]; ret = SetThreadDesktop(hdesks[thread_desk_id]); ok(ret, "set thread desktop failed!\n"); ret = SwitchDesktop(hdesks[input_desk_id]); ok(ret, "switch desktop failed!\n"); set_foreground(partners[0]); set_foreground(partners[1]); hwnd = GetForegroundWindow(); ok(hwnd != hwnd_test, "unexpected foreground window %p\n", hwnd); ret = set_foreground(hwnd_test); hwnd = GetForegroundWindow(); GetWindowTextA(hwnd, win_text, 1024); trace("hwnd %p name %s\n", hwnd, win_text); if (input_desk_id == hwnd_id) { if (input_desk_id == thread_desk_id) { ok(ret, "SetForegroundWindow failed!\n"); todo_wine_if (!hwnd) ok(hwnd == hwnd_test , "unexpected foreground window %p\n", hwnd); } else { todo_wine ok(ret, "SetForegroundWindow failed!\n"); todo_wine ok(hwnd == 0, "unexpected foreground window %p\n", hwnd); } } else { if (input_desk_id == thread_desk_id) { ok(!ret, "SetForegroundWindow should fail!\n"); todo_wine_if (!hwnd) ok(hwnd == partners[input_desk_id] , "unexpected foreground window %p\n", hwnd); } else { todo_wine ok(!ret, "SetForegroundWindow should fail!\n"); todo_wine_if (hwnd) ok(hwnd == 0, "unexpected foreground window %p\n", hwnd); } } } /* Clean up */ for (thread_desk_id = DESKTOPS - 1; thread_desk_id >= 0; thread_desk_id--) { ret = SetThreadDesktop(hdesks[thread_desk_id]); ok(ret, "set thread desktop failed!\n"); SendMessageA(hwnds[thread_desk_id], WM_DESTROY, 0, 0); SendMessageA(partners[thread_desk_id], WM_DESTROY, 0, 0); } ret = SwitchDesktop(hdesks[0]); ok(ret, "switch desktop failed!\n"); CloseDesktop(hdesks[1]); ret = SystemParametersInfoA(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, UlongToPtr(timeout_old), SPIF_SENDCHANGE | SPIF_UPDATEINIFILE); ok(ret, "set foreground lock timeout failed!\n"); ret = SystemParametersInfoA(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, &timeout, 0); ok(ret, "get foreground lock timeout failed!\n"); ok(timeout == timeout_old, "unexpected timeout %d\n", timeout); } START_TEST(winstation) { HMODULE hntdll = GetModuleHandleA("ntdll.dll"); pNtQueryObject = (void *)GetProcAddress(hntdll, "NtQueryObject"); /* Check whether this platform supports WindowStation calls */ SetLastError( 0xdeadbeef ); GetProcessWindowStation(); if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) { win_skip("WindowStation calls not supported on this platform\n"); return; } test_inputdesktop(); test_inputdesktop2(); test_enumstations(); test_enumdesktops(); test_handles(); test_getuserobjectinformation(); test_foregroundwindow(); }