From: Amine Khaldi Date: Sun, 9 Dec 2012 20:39:13 +0000 (+0000) Subject: [KERNEL32_WINETEST]: Sync with Wine 1.5.19. X-Git-Tag: backups/ros-csrss@60644~107^2~11 X-Git-Url: https://git.reactos.org/?p=reactos.git;a=commitdiff_plain;h=541bbc00b6315b936f33c645f6b3cf235bfaae57 [KERNEL32_WINETEST]: Sync with Wine 1.5.19. svn path=/trunk/; revision=57853 --- diff --git a/rostests/winetests/kernel32/codepage.c b/rostests/winetests/kernel32/codepage.c index 61bcf547dba..c98034800ef 100755 --- a/rostests/winetests/kernel32/codepage.c +++ b/rostests/winetests/kernel32/codepage.c @@ -189,6 +189,74 @@ static void test_negative_dest_length(void) } +static void test_other_invalid_parameters(void) +{ + char c_string[] = "Hello World"; + size_t c_string_len = sizeof(c_string); + WCHAR w_string[] = {'H','e','l','l','o',' ','W','o','r','l','d',0}; + size_t w_string_len = sizeof(w_string) / sizeof(WCHAR); + BOOL used; + INT len; + + /* srclen=0 => ERROR_INVALID_PARAMETER */ + SetLastError(0xdeadbeef); + len = WideCharToMultiByte(CP_ACP, 0, w_string, 0, c_string, c_string_len, NULL, NULL); + ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%x\n", len, GetLastError()); + + SetLastError(0xdeadbeef); + len = MultiByteToWideChar(CP_ACP, 0, c_string, 0, w_string, w_string_len); + ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%x\n", len, GetLastError()); + + + /* dst=NULL but dstlen not 0 => ERROR_INVALID_PARAMETER */ + SetLastError(0xdeadbeef); + len = WideCharToMultiByte(CP_ACP, 0, w_string, w_string_len, NULL, c_string_len, NULL, NULL); + ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%x\n", len, GetLastError()); + + SetLastError(0xdeadbeef); + len = MultiByteToWideChar(CP_ACP, 0, c_string, c_string_len, NULL, w_string_len); + ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%x\n", len, GetLastError()); + + + /* CP_UTF7, CP_UTF8, or CP_SYMBOL and defchar not NULL => ERROR_INVALID_PARAMETER */ + /* CP_SYMBOL's behavior here is undocumented */ + SetLastError(0xdeadbeef); + len = WideCharToMultiByte(CP_UTF7, 0, w_string, w_string_len, c_string, c_string_len, c_string, NULL); + ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%x\n", len, GetLastError()); + + SetLastError(0xdeadbeef); + len = WideCharToMultiByte(CP_UTF8, 0, w_string, w_string_len, c_string, c_string_len, c_string, NULL); + ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%x\n", len, GetLastError()); + + SetLastError(0xdeadbeef); + len = WideCharToMultiByte(CP_SYMBOL, 0, w_string, w_string_len, c_string, c_string_len, c_string, NULL); + ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%x\n", len, GetLastError()); + + + /* CP_UTF7, CP_UTF8, or CP_SYMBOL and used not NULL => ERROR_INVALID_PARAMETER */ + /* CP_SYMBOL's behavior here is undocumented */ + SetLastError(0xdeadbeef); + len = WideCharToMultiByte(CP_UTF7, 0, w_string, w_string_len, c_string, c_string_len, NULL, &used); + ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%x\n", len, GetLastError()); + + SetLastError(0xdeadbeef); + len = WideCharToMultiByte(CP_UTF8, 0, w_string, w_string_len, c_string, c_string_len, NULL, &used); + ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%x\n", len, GetLastError()); + + SetLastError(0xdeadbeef); + len = WideCharToMultiByte(CP_SYMBOL, 0, w_string, w_string_len, c_string, c_string_len, NULL, &used); + ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%x\n", len, GetLastError()); + + + /* CP_UTF7, flags not 0 and used not NULL => ERROR_INVALID_PARAMETER */ + /* (tests precedence of ERROR_INVALID_PARAMETER over ERROR_INVALID_FLAGS) */ + /* The same test with CP_SYMBOL instead of CP_UTF7 gives ERROR_INVALID_FLAGS + instead except on Windows NT4 */ + SetLastError(0xdeadbeef); + len = WideCharToMultiByte(CP_UTF7, 1, w_string, w_string_len, c_string, c_string_len, NULL, &used); + ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%x\n", len, GetLastError()); +} + static void test_overlapped_buffers(void) { static const WCHAR strW[] = {'j','u','s','t',' ','a',' ','t','e','s','t',0}; @@ -402,6 +470,7 @@ START_TEST(codepage) test_null_source(); test_negative_source_length(); test_negative_dest_length(); + test_other_invalid_parameters(); test_overlapped_buffers(); /* WideCharToMultiByte has two code paths, test both here */ diff --git a/rostests/winetests/kernel32/console.c b/rostests/winetests/kernel32/console.c index d0c58f80edf..b1907d7dc0e 100755 --- a/rostests/winetests/kernel32/console.c +++ b/rostests/winetests/kernel32/console.c @@ -1031,7 +1031,7 @@ static void test_OpenCON(void) h = CreateFileW(conW, GENERIC_READ, 0, NULL, accesses[i], 0, NULL); /* Windows versions differ here: * MSDN states in CreateFile that TRUNCATE_EXISTING requires GENERIC_WRITE - * NT, XP, Vista comply, but Win7 doesn't and allows to open CON with TRUNCATE_EXISTING + * NT, XP, Vista comply, but Win7 doesn't and allows opening CON with TRUNCATE_EXISTING * So don't test when disposition is TRUNCATE_EXISTING */ if (accesses[i] != TRUNCATE_EXISTING) @@ -1354,12 +1354,12 @@ static void test_WriteConsoleInputA(HANDLE input_handle) LPDWORD written; DWORD expected_count; DWORD last_error; - int win7_crash; + int win_crash; } invalid_table[] = { {NULL, NULL, 0, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1}, {NULL, NULL, 0, &count, 0, ERROR_INVALID_HANDLE}, - {NULL, NULL, 1, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS}, + {NULL, NULL, 1, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1}, {NULL, NULL, 1, &count, 0xdeadbeef, ERROR_INVALID_ACCESS}, {NULL, &event, 0, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1}, {NULL, &event, 0, &count, 0, ERROR_INVALID_HANDLE}, @@ -1367,14 +1367,14 @@ static void test_WriteConsoleInputA(HANDLE input_handle) {NULL, &event, 1, &count, 0, ERROR_INVALID_HANDLE}, {INVALID_HANDLE_VALUE, NULL, 0, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1}, {INVALID_HANDLE_VALUE, NULL, 0, &count, 0, ERROR_INVALID_HANDLE}, - {INVALID_HANDLE_VALUE, NULL, 1, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS}, + {INVALID_HANDLE_VALUE, NULL, 1, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1}, {INVALID_HANDLE_VALUE, NULL, 1, &count, 0xdeadbeef, ERROR_INVALID_ACCESS}, {INVALID_HANDLE_VALUE, &event, 0, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1}, {INVALID_HANDLE_VALUE, &event, 0, &count, 0, ERROR_INVALID_HANDLE}, {INVALID_HANDLE_VALUE, &event, 1, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1}, {INVALID_HANDLE_VALUE, &event, 1, &count, 0, ERROR_INVALID_HANDLE}, {input_handle, NULL, 0, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1}, - {input_handle, NULL, 1, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS}, + {input_handle, NULL, 1, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1}, {input_handle, NULL, 1, &count, 0xdeadbeef, ERROR_INVALID_ACCESS}, {input_handle, &event, 0, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1}, {input_handle, &event, 1, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1}, @@ -1406,7 +1406,7 @@ static void test_WriteConsoleInputA(HANDLE input_handle) for (i = 0; i < sizeof(invalid_table)/sizeof(invalid_table[0]); i++) { - if (invalid_table[i].win7_crash) + if (invalid_table[i].win_crash) continue; SetLastError(0xdeadbeef); @@ -1597,12 +1597,12 @@ static void test_WriteConsoleInputW(HANDLE input_handle) LPDWORD written; DWORD expected_count; DWORD last_error; - int win7_crash; + int win_crash; } invalid_table[] = { {NULL, NULL, 0, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1}, {NULL, NULL, 0, &count, 0, ERROR_INVALID_HANDLE}, - {NULL, NULL, 1, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS}, + {NULL, NULL, 1, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1}, {NULL, NULL, 1, &count, 0xdeadbeef, ERROR_INVALID_ACCESS}, {NULL, &event, 0, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1}, {NULL, &event, 0, &count, 0, ERROR_INVALID_HANDLE}, @@ -1610,14 +1610,14 @@ static void test_WriteConsoleInputW(HANDLE input_handle) {NULL, &event, 1, &count, 0, ERROR_INVALID_HANDLE}, {INVALID_HANDLE_VALUE, NULL, 0, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1}, {INVALID_HANDLE_VALUE, NULL, 0, &count, 0, ERROR_INVALID_HANDLE}, - {INVALID_HANDLE_VALUE, NULL, 1, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS}, + {INVALID_HANDLE_VALUE, NULL, 1, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1}, {INVALID_HANDLE_VALUE, NULL, 1, &count, 0xdeadbeef, ERROR_INVALID_ACCESS}, {INVALID_HANDLE_VALUE, &event, 0, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1}, {INVALID_HANDLE_VALUE, &event, 0, &count, 0, ERROR_INVALID_HANDLE}, {INVALID_HANDLE_VALUE, &event, 1, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1}, {INVALID_HANDLE_VALUE, &event, 1, &count, 0, ERROR_INVALID_HANDLE}, {input_handle, NULL, 0, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1}, - {input_handle, NULL, 1, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS}, + {input_handle, NULL, 1, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1}, {input_handle, NULL, 1, &count, 0xdeadbeef, ERROR_INVALID_ACCESS}, {input_handle, &event, 0, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1}, {input_handle, &event, 1, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1}, @@ -1649,7 +1649,7 @@ static void test_WriteConsoleInputW(HANDLE input_handle) for (i = 0; i < sizeof(invalid_table)/sizeof(invalid_table[0]); i++) { - if (invalid_table[i].win7_crash) + if (invalid_table[i].win_crash) continue; SetLastError(0xdeadbeef); diff --git a/rostests/winetests/kernel32/debugger.c b/rostests/winetests/kernel32/debugger.c index 2094190456b..22f7190445b 100644 --- a/rostests/winetests/kernel32/debugger.c +++ b/rostests/winetests/kernel32/debugger.c @@ -287,6 +287,13 @@ static void crash_and_debug(HKEY hkey, const char* argv0, const char* dbgtasks) } ret=RegSetValueExA(hkey, "auto", 0, REG_SZ, (BYTE*)"1", 2); + if (ret == ERROR_ACCESS_DENIED) + { + skip_crash_and_debug = TRUE; + skip("No write access to change the debugger\n"); + return; + } + ok(ret == ERROR_SUCCESS, "unable to set AeDebug/auto: ret=%d\n", ret); get_file_name(dbglog); diff --git a/rostests/winetests/kernel32/file.c b/rostests/winetests/kernel32/file.c index 9cfb664f79d..d35c38fe197 100755 --- a/rostests/winetests/kernel32/file.c +++ b/rostests/winetests/kernel32/file.c @@ -21,7 +21,7 @@ */ /* ReplaceFile requires Windows 2000 or newer */ -#define _WIN32_WINNT 0x0500 +#define _WIN32_WINNT 0x0600 #include #include @@ -32,6 +32,7 @@ #include "windef.h" #include "winbase.h" #include "winerror.h" +#include "winnls.h" static HANDLE (WINAPI *pFindFirstFileExA)(LPCSTR,FINDEX_INFO_LEVELS,LPVOID,FINDEX_SEARCH_OPS,LPVOID,DWORD); static BOOL (WINAPI *pReplaceFileA)(LPCSTR, LPCSTR, LPCSTR, DWORD, LPVOID, LPVOID); @@ -39,6 +40,9 @@ static BOOL (WINAPI *pReplaceFileW)(LPCWSTR, LPCWSTR, LPCWSTR, DWORD, LPVOID, LP static UINT (WINAPI *pGetSystemWindowsDirectoryA)(LPSTR, UINT); static BOOL (WINAPI *pGetVolumeNameForVolumeMountPointA)(LPCSTR, LPSTR, DWORD); static DWORD (WINAPI *pQueueUserAPC)(PAPCFUNC pfnAPC, HANDLE hThread, ULONG_PTR dwData); +static BOOL (WINAPI *pGetFileInformationByHandleEx)(HANDLE, FILE_INFO_BY_HANDLE_CLASS, LPVOID, DWORD); +static HANDLE (WINAPI *pOpenFileById)(HANDLE, LPFILE_ID_DESCRIPTOR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD); +static BOOL (WINAPI *pSetFileValidData)(HANDLE, LONGLONG); /* keep filename and filenameW the same */ static const char filename[] = "testfile.xxx"; @@ -73,6 +77,9 @@ static void InitFunctionPointers(void) pGetSystemWindowsDirectoryA=(void*)GetProcAddress(hkernel32, "GetSystemWindowsDirectoryA"); pGetVolumeNameForVolumeMountPointA = (void *) GetProcAddress(hkernel32, "GetVolumeNameForVolumeMountPointA"); pQueueUserAPC = (void *) GetProcAddress(hkernel32, "QueueUserAPC"); + pGetFileInformationByHandleEx = (void *) GetProcAddress(hkernel32, "GetFileInformationByHandleEx"); + pOpenFileById = (void *) GetProcAddress(hkernel32, "OpenFileById"); + pSetFileValidData = (void *) GetProcAddress(hkernel32, "SetFileValidData"); } static void test__hread( void ) @@ -796,7 +803,7 @@ static void dumpmem(unsigned char *mem, int len) p = hex; c = txt; do { - p += sprintf(p, "%02hhx ", mem[x]); + p += sprintf(p, "%02x ", mem[x]); *c++ = (mem[x] >= 32 && mem[x] <= 127) ? mem[x] : '.'; } while (++x % 16 && x < len); *c = '\0'; @@ -1291,6 +1298,8 @@ static void test_GetTempFileNameA(void) static void test_DeleteFileA( void ) { BOOL ret; + char temp_path[MAX_PATH], temp_file[MAX_PATH]; + HANDLE hfile; ret = DeleteFileA(NULL); ok(!ret && (GetLastError() == ERROR_INVALID_PARAMETER || @@ -1308,6 +1317,25 @@ static void test_DeleteFileA( void ) GetLastError() == ERROR_ACCESS_DENIED || GetLastError() == ERROR_INVALID_FUNCTION), "DeleteFileA(\"nul\") returned ret=%d error=%d\n",ret,GetLastError()); + + GetTempPathA(MAX_PATH, temp_path); + GetTempFileName(temp_path, "tst", 0, temp_file); + + SetLastError(0xdeadbeef); + hfile = CreateFile(temp_file, GENERIC_READ, FILE_SHARE_DELETE | FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0); + ok(hfile != INVALID_HANDLE_VALUE, "CreateFile error %d\n", GetLastError()); + + SetLastError(0xdeadbeef); + ret = DeleteFile(temp_file); +todo_wine + ok(ret, "DeleteFile error %d\n", GetLastError()); + + SetLastError(0xdeadbeef); + ret = CloseHandle(hfile); + ok(ret, "CloseHandle error %d\n", GetLastError()); + ret = DeleteFile(temp_file); +todo_wine + ok(!ret, "DeleteFile should fail\n"); } static void test_DeleteFileW( void ) @@ -1758,7 +1786,7 @@ static BOOL create_fake_dll( LPCSTR filename ) #elif defined __sparc__ nt->FileHeader.Machine = IMAGE_FILE_MACHINE_SPARC; #elif defined __arm__ - nt->FileHeader.Machine = IMAGE_FILE_MACHINE_ARMV7; + nt->FileHeader.Machine = IMAGE_FILE_MACHINE_ARMNT; #else # error You must specify the machine type #endif @@ -3237,6 +3265,314 @@ static void test_CreatFile(void) DeleteFile(file_name); } +static void test_GetFileInformationByHandleEx(void) +{ + int i; + char tempPath[MAX_PATH], tempFileName[MAX_PATH], buffer[1024]; + BOOL ret; + DWORD ret2; + HANDLE directory; + FILE_ID_BOTH_DIR_INFO *bothDirInfo; + struct { + FILE_INFO_BY_HANDLE_CLASS handleClass; + void *ptr; + DWORD size; + DWORD errorCode; + } checks[] = { + {0xdeadbeef, NULL, 0, ERROR_INVALID_PARAMETER}, + {FileIdBothDirectoryInfo, NULL, 0, ERROR_BAD_LENGTH}, + {FileIdBothDirectoryInfo, NULL, sizeof(buffer), ERROR_NOACCESS}, + {FileIdBothDirectoryInfo, buffer, 0, ERROR_BAD_LENGTH}}; + + if (!pGetFileInformationByHandleEx) + { + win_skip("GetFileInformationByHandleEx is missing.\n"); + return; + } + + ret2 = GetTempPathA(sizeof(tempPath), tempPath); + ok(ret2, "GetFileInformationByHandleEx: GetTempPathA failed, got error %u.\n", GetLastError()); + + /* ensure the existence of a file in the temp folder */ + ret2 = GetTempFileNameA(tempPath, "abc", 0, tempFileName); + ok(ret2, "GetFileInformationByHandleEx: GetTempFileNameA failed, got error %u.\n", GetLastError()); + ok(GetFileAttributesA(tempFileName) != INVALID_FILE_ATTRIBUTES, "GetFileInformationByHandleEx: " + "GetFileAttributesA failed to find the temp file, got error %u.\n", GetLastError()); + + directory = CreateFileA(tempPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + ok(directory != INVALID_HANDLE_VALUE, "GetFileInformationByHandleEx: failed to open the temp folder, " + "got error %u.\n", GetLastError()); + + for (i = 0; i < sizeof(checks) / sizeof(checks[0]); i += 1) + { + SetLastError(0xdeadbeef); + ret = pGetFileInformationByHandleEx(directory, checks[i].handleClass, checks[i].ptr, checks[i].size); + ok(!ret && GetLastError() == checks[i].errorCode, "GetFileInformationByHandleEx: expected error %u, " + "got %u.\n", checks[i].errorCode, GetLastError()); + } + + while (TRUE) + { + memset(buffer, 0xff, sizeof(buffer)); + ret = pGetFileInformationByHandleEx(directory, FileIdBothDirectoryInfo, buffer, sizeof(buffer)); + if (!ret && GetLastError() == ERROR_NO_MORE_FILES) + break; + ok(ret, "GetFileInformationByHandleEx: failed to query for FileIdBothDirectoryInfo, got error %u.\n", GetLastError()); + if (!ret) + break; + bothDirInfo = (FILE_ID_BOTH_DIR_INFO *)buffer; + while (TRUE) + { + ok(bothDirInfo->FileAttributes != 0xffffffff, "GetFileInformationByHandleEx: returned invalid file attributes.\n"); + ok(bothDirInfo->FileId.u.LowPart != 0xffffffff, "GetFileInformationByHandleEx: returned invalid file id.\n"); + ok(bothDirInfo->FileNameLength != 0xffffffff, "GetFileInformationByHandleEx: returned invalid file name length.\n"); + if (!bothDirInfo->NextEntryOffset) + break; + bothDirInfo = (FILE_ID_BOTH_DIR_INFO *)(((char *)bothDirInfo) + bothDirInfo->NextEntryOffset); + } + } + + CloseHandle(directory); + DeleteFile(tempFileName); +} + +static void test_OpenFileById(void) +{ + char tempPath[MAX_PATH], tempFileName[MAX_PATH], buffer[256], tickCount[256]; + WCHAR tempFileNameW[MAX_PATH]; + BOOL ret, found; + DWORD ret2, count, tempFileNameLen; + HANDLE directory, handle, tempFile; + FILE_ID_BOTH_DIR_INFO *bothDirInfo; + FILE_ID_DESCRIPTOR fileIdDescr; + + if (!pGetFileInformationByHandleEx || !pOpenFileById) + { + win_skip("GetFileInformationByHandleEx or OpenFileById is missing.\n"); + return; + } + + ret2 = GetTempPathA(sizeof(tempPath), tempPath); + ok(ret2, "OpenFileById: GetTempPath failed, got error %u.\n", GetLastError()); + + /* ensure the existence of a file in the temp folder */ + ret2 = GetTempFileNameA(tempPath, "abc", 0, tempFileName); + ok(ret2, "OpenFileById: GetTempFileNameA failed, got error %u.\n", GetLastError()); + ok(GetFileAttributesA(tempFileName) != INVALID_FILE_ATTRIBUTES, + "OpenFileById: GetFileAttributesA failed to find the temp file, got error %u\n", GetLastError()); + + ret2 = MultiByteToWideChar(CP_ACP, 0, tempFileName + strlen(tempPath), -1, tempFileNameW, sizeof(tempFileNameW)/sizeof(tempFileNameW[0])); + ok(ret2, "OpenFileById: MultiByteToWideChar failed to convert tempFileName, got error %u.\n", GetLastError()); + tempFileNameLen = ret2 - 1; + + tempFile = CreateFileA(tempFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + ok(tempFile != INVALID_HANDLE_VALUE, "OpenFileById: failed to create a temp file, " + "got error %u.\n", GetLastError()); + ret2 = sprintf(tickCount, "%u", GetTickCount()); + ret = WriteFile(tempFile, tickCount, ret2, &count, NULL); + ok(ret, "OpenFileById: WriteFile failed, got error %u.\n", GetLastError()); + CloseHandle(tempFile); + + directory = CreateFileA(tempPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + ok(directory != INVALID_HANDLE_VALUE, "OpenFileById: failed to open the temp folder, " + "got error %u.\n", GetLastError()); + + /* get info about the temp folder itself */ + bothDirInfo = (FILE_ID_BOTH_DIR_INFO *)buffer; + ret = pGetFileInformationByHandleEx(directory, FileIdBothDirectoryInfo, buffer, sizeof(buffer)); + ok(ret, "OpenFileById: failed to query for FileIdBothDirectoryInfo, got error %u.\n", GetLastError()); + ok(bothDirInfo->FileNameLength == sizeof(WCHAR) && bothDirInfo->FileName[0] == '.', + "OpenFileById: failed to return the temp folder at the first entry, got error %u.\n", GetLastError()); + + /* open the temp folder itself */ + fileIdDescr.dwSize = sizeof(fileIdDescr); + fileIdDescr.Type = FileIdType; + U(fileIdDescr).FileId = bothDirInfo->FileId; + handle = pOpenFileById(directory, &fileIdDescr, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, 0); + todo_wine + ok(handle != INVALID_HANDLE_VALUE, "OpenFileById: failed to open the temp folder itself, got error %u.\n", GetLastError()); + CloseHandle(handle); + + /* find the temp file in the temp folder */ + found = FALSE; + while (!found) + { + ret = pGetFileInformationByHandleEx(directory, FileIdBothDirectoryInfo, buffer, sizeof(buffer)); + ok(ret, "OpenFileById: failed to query for FileIdBothDirectoryInfo, got error %u.\n", GetLastError()); + if (!ret) + break; + bothDirInfo = (FILE_ID_BOTH_DIR_INFO *)buffer; + while (TRUE) + { + if (tempFileNameLen == bothDirInfo->FileNameLength / sizeof(WCHAR) && + memcmp(tempFileNameW, bothDirInfo->FileName, bothDirInfo->FileNameLength) == 0) + { + found = TRUE; + break; + } + if (!bothDirInfo->NextEntryOffset) + break; + bothDirInfo = (FILE_ID_BOTH_DIR_INFO *)(((char *)bothDirInfo) + bothDirInfo->NextEntryOffset); + } + } + ok(found, "OpenFileById: failed to find the temp file in the temp folder.\n"); + + SetLastError(0xdeadbeef); + handle = pOpenFileById(directory, NULL, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, 0); + ok(handle == INVALID_HANDLE_VALUE && GetLastError() == ERROR_INVALID_PARAMETER, + "OpenFileById: expected ERROR_INVALID_PARAMETER, got error %u.\n", GetLastError()); + + fileIdDescr.dwSize = sizeof(fileIdDescr); + fileIdDescr.Type = FileIdType; + U(fileIdDescr).FileId = bothDirInfo->FileId; + handle = pOpenFileById(directory, &fileIdDescr, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, 0); + ok(handle != INVALID_HANDLE_VALUE, "OpenFileById: failed to open the file, got error %u.\n", GetLastError()); + + ret = ReadFile(handle, buffer, sizeof(buffer), &count, NULL); + buffer[count] = 0; + ok(ret, "OpenFileById: ReadFile failed, got error %u.\n", GetLastError()); + ok(strcmp(tickCount, buffer) == 0, "OpenFileById: invalid contents of the temp file.\n"); + + CloseHandle(handle); + CloseHandle(directory); + DeleteFile(tempFileName); +} + +static void test_SetFileValidData(void) +{ + BOOL ret; + HANDLE handle; + DWORD error, count; + char path[MAX_PATH], filename[MAX_PATH]; + TOKEN_PRIVILEGES privs; + HANDLE token = NULL; + + if (!pSetFileValidData) + { + win_skip("SetFileValidData is missing\n"); + return; + } + GetTempPathA(sizeof(path), path); + GetTempFileNameA(path, "tst", 0, filename); + handle = CreateFileA(filename, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + WriteFile(handle, "test", sizeof("test") - 1, &count, NULL); + CloseHandle(handle); + + SetLastError(0xdeadbeef); + ret = pSetFileValidData(INVALID_HANDLE_VALUE, 0); + error = GetLastError(); + ok(!ret, "SetFileValidData succeeded\n"); + ok(error == ERROR_INVALID_HANDLE, "got %u\n", error); + + SetLastError(0xdeadbeef); + ret = pSetFileValidData(INVALID_HANDLE_VALUE, -1); + error = GetLastError(); + ok(!ret, "SetFileValidData succeeded\n"); + ok(error == ERROR_INVALID_HANDLE, "got %u\n", error); + + /* file opened for reading */ + handle = CreateFileA(filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL); + + SetLastError(0xdeadbeef); + ret = pSetFileValidData(handle, 0); + ok(!ret, "SetFileValidData succeeded\n"); + error = GetLastError(); + ok(error == ERROR_ACCESS_DENIED, "got %u\n", error); + + SetLastError(0xdeadbeef); + ret = pSetFileValidData(handle, -1); + error = GetLastError(); + ok(!ret, "SetFileValidData succeeded\n"); + ok(error == ERROR_ACCESS_DENIED, "got %u\n", error); + CloseHandle(handle); + + handle = CreateFileA(filename, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + + SetLastError(0xdeadbeef); + ret = pSetFileValidData(handle, 0); + error = GetLastError(); + ok(!ret, "SetFileValidData succeeded\n"); + todo_wine ok(error == ERROR_PRIVILEGE_NOT_HELD, "got %u\n", error); + CloseHandle(handle); + + privs.PrivilegeCount = 1; + privs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token) || + !LookupPrivilegeValue(NULL, SE_MANAGE_VOLUME_NAME, &privs.Privileges[0].Luid) || + !AdjustTokenPrivileges(token, FALSE, &privs, sizeof(privs), NULL, NULL)) + { + win_skip("cannot enable SE_MANAGE_VOLUME_NAME privilege\n"); + CloseHandle(token); + return; + } + handle = CreateFileA(filename, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + + SetLastError(0xdeadbeef); + ret = pSetFileValidData(handle, 0); + error = GetLastError(); + ok(!ret, "SetFileValidData succeeded\n"); + ok(error == ERROR_INVALID_PARAMETER, "got %u\n", error); + + SetLastError(0xdeadbeef); + ret = pSetFileValidData(handle, -1); + error = GetLastError(); + ok(!ret, "SetFileValidData succeeded\n"); + ok(error == ERROR_INVALID_PARAMETER, "got %u\n", error); + + SetLastError(0xdeadbeef); + ret = pSetFileValidData(handle, 2); + error = GetLastError(); + todo_wine ok(!ret, "SetFileValidData succeeded\n"); + todo_wine ok(error == ERROR_INVALID_PARAMETER, "got %u\n", error); + + ret = pSetFileValidData(handle, 4); + ok(ret, "SetFileValidData failed %u\n", GetLastError()); + + SetLastError(0xdeadbeef); + ret = pSetFileValidData(handle, 8); + error = GetLastError(); + ok(!ret, "SetFileValidData succeeded\n"); + ok(error == ERROR_INVALID_PARAMETER, "got %u\n", error); + + count = SetFilePointer(handle, 1024, NULL, FILE_END); + ok(count != INVALID_SET_FILE_POINTER, "SetFilePointer failed %u\n", GetLastError()); + ret = SetEndOfFile(handle); + ok(ret, "SetEndOfFile failed %u\n", GetLastError()); + + SetLastError(0xdeadbeef); + ret = pSetFileValidData(handle, 2); + error = GetLastError(); + todo_wine ok(!ret, "SetFileValidData succeeded\n"); + todo_wine ok(error == ERROR_INVALID_PARAMETER, "got %u\n", error); + + ret = pSetFileValidData(handle, 4); + ok(ret, "SetFileValidData failed %u\n", GetLastError()); + + ret = pSetFileValidData(handle, 8); + ok(ret, "SetFileValidData failed %u\n", GetLastError()); + + ret = pSetFileValidData(handle, 4); + error = GetLastError(); + todo_wine ok(!ret, "SetFileValidData succeeded\n"); + todo_wine ok(error == ERROR_INVALID_PARAMETER, "got %u\n", error); + + ret = pSetFileValidData(handle, 1024); + ok(ret, "SetFileValidData failed %u\n", GetLastError()); + + ret = pSetFileValidData(handle, 2048); + error = GetLastError(); + ok(!ret, "SetFileValidData succeeded\n"); + ok(error == ERROR_INVALID_PARAMETER, "got %u\n", error); + + privs.Privileges[0].Attributes = 0; + AdjustTokenPrivileges(token, FALSE, &privs, sizeof(privs), NULL, NULL); + CloseHandle(token); + DeleteFile(filename); +} + START_TEST(file) { InitFunctionPointers(); @@ -3276,4 +3612,7 @@ START_TEST(file) test_RemoveDirectory(); test_ReplaceFileA(); test_ReplaceFileW(); + test_GetFileInformationByHandleEx(); + test_OpenFileById(); + test_SetFileValidData(); } diff --git a/rostests/winetests/kernel32/format_msg.c b/rostests/winetests/kernel32/format_msg.c index 1f56440f60e..005e6c4a66c 100755 --- a/rostests/winetests/kernel32/format_msg.c +++ b/rostests/winetests/kernel32/format_msg.c @@ -997,6 +997,195 @@ static void test_message_ignore_inserts_wide(void) ok(!lstrcmpW(s_2sp, out), "Expected output string \" \", got %s\n", wine_dbgstr_w(out)); } +static void test_message_wrap(void) +{ + DWORD ret; + int i; + CHAR in[300], out[300], ref[300]; + + /* No need for wrapping */ + ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 20, + "short long line", 0, 0, out, sizeof(out), NULL); + ok(ret == 15, "Expected FormatMessageW to return 15, got %d\n", ret); + ok(!strcmp("short long line", out),"failed out=[%s]\n",out); + + /* Wrap the last word */ + ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 11, + "short long line", 0, 0, out, sizeof(out), NULL); + ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret); + ok(!strcmp("short long\r\nline", out),"failed out=[%s]\n",out); + + /* Wrap the very last word */ + ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 20, + "short long long line", 0, 0, out, sizeof(out), NULL); + ok(ret == 21, "Expected FormatMessageW to return 21, got %d\n", ret); + ok(!strcmp("short long long\r\nline", out),"failed out=[%s]\n",out); + + /* Strictly less than 10 characters per line! */ + ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 10, + "short long line", 0, 0, out, sizeof(out), NULL); + ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret); + ok(!strcmp("short\r\nlong line", out),"failed out=[%s]\n",out); + + /* Handling of duplicate spaces */ + ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 16, + "short long line", 0, 0, out, sizeof(out), NULL); + ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret); + ok(!strcmp("short long\r\nline", out),"failed out=[%s]\n",out); + + ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 16, + "short long wordlongerthanaline", 0, 0, out, sizeof(out), NULL); + ok(ret == 33, "Expected FormatMessageW to return 33, got %d\n", ret); + ok(!strcmp("short long\r\nwordlongerthanal\r\nine", out),"failed out=[%s]\n",out); + + /* Breaking in the middle of spaces */ + ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 12, + "short long line", 0, 0, out, sizeof(out), NULL); + ok(ret == 18, "Expected FormatMessageW to return 18, got %d\n", ret); + ok(!strcmp("short long\r\n line", out),"failed out=[%s]\n",out); + + ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 12, + "short long wordlongerthanaline", 0, 0, out, sizeof(out), NULL); + ok(ret == 35, "Expected FormatMessageW to return 35, got %d\n", ret); + ok(!strcmp("short long\r\n\r\nwordlongerth\r\nanaline", out),"failed out=[%s]\n",out); + + /* Handling of start-of-string spaces */ + ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 15, + " short line", 0, 0, out, sizeof(out), NULL); + ok(ret == 13, "Expected FormatMessageW to return 13, got %d\n", ret); + ok(!strcmp(" short line", out),"failed out=[%s]\n",out); + + ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 11, + " shortlong line", 0, 0, out, sizeof(out), NULL); + ok(ret == 17, "Expected FormatMessageW to return 17, got %d\n", ret); + ok(!strcmp("\r\nshortlong\r\nline", out),"failed out=[%s]\n",out); + + /* Handling of start-of-line spaces */ + ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 11, + "l1%n shortlong line", 0, 0, out, sizeof(out), NULL); + ok(ret == 21, "Expected FormatMessageW to return 21, got %d\n", ret); + ok(!strcmp("l1\r\n\r\nshortlong\r\nline", out),"failed out=[%s]\n",out); + + /* Pure space wrapping */ + ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 5, + " ", 0, 0, out, sizeof(out), NULL); + ok(ret == 7, "Expected FormatMessageW to return 7, got %d\n", ret); + ok(!strcmp("\r\n\r\n\r\n ", out),"failed out=[%s]\n",out); + + /* Handling of trailing spaces */ + ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 5, + "l1 ", 0, 0, out, sizeof(out), NULL); + ok(ret == 10, "Expected FormatMessageW to return 10, got %d\n", ret); + ok(!strcmp("l1\r\n\r\n\r\n ", out),"failed out=[%s]\n",out); + + /* Word that just fills the line */ + ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 8, + "shortlon", 0, 0, out, sizeof(out), NULL); + ok(ret == 10, "Expected FormatMessageW to return 10, got %d\n", ret); + ok(!strcmp("shortlon\r\n", out),"failed out=[%s]\n",out); + + /* Word longer than the line */ + ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 8, + "shortlongline", 0, 0, out, sizeof(out), NULL); + ok(ret == 15, "Expected FormatMessageW to return 15, got %d\n", ret); + ok(!strcmp("shortlon\r\ngline", out),"failed out=[%s]\n",out); + + /* Wrap the line multiple times */ + ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 7, + "short long line", 0, 0, out, sizeof(out), NULL); + ok(ret == 17, "Expected FormatMessageW to return 17, got %d\n", ret); + ok(!strcmp("short\r\nlong\r\nline", out),"failed out=[%s]\n",out); + + /* '\n's in the source are ignored */ + ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 11, + "short\nlong line", 0, 0, out, sizeof(out), NULL); + ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret); + ok(!strcmp("short long\r\nline", out),"failed out=[%s]\n",out); + + /* Wrap even before a '%n' */ + ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 8, + "shortlon%n", 0, 0, out, sizeof(out), NULL); + ok(ret == 12, "Expected FormatMessageW to return 12, got %d\n", ret); + ok(!strcmp("shortlon\r\n\r\n", out),"failed out=[%s]\n",out); + + /* '%n's count as starting a new line and combine with line wrapping */ + ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 10, + "short%nlong line", 0, 0, out, sizeof(out), NULL); + ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret); + ok(!strcmp("short\r\nlong line", out),"failed out=[%s]\n",out); + + ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 8, + "short%nlong line", 0, 0, out, sizeof(out), NULL); + ok(ret == 17, "Expected FormatMessageW to return 17, got %d\n", ret); + ok(!strcmp("short\r\nlong\r\nline", out),"failed out=[%s]\n",out); + + /* '%r's also count as starting a new line and all */ + ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 10, + "short%rlong line", 0, 0, out, sizeof(out), NULL); + ok(ret == 15, "Expected FormatMessageW to return 15, got %d\n", ret); + ok(!strcmp("short\rlong line", out),"failed out=[%s]\n",out); + + ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 8, + "short%rlong line", 0, 0, out, sizeof(out), NULL); + ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret); + ok(!strcmp("short\rlong\r\nline", out),"failed out=[%s]\n",out); + + /* IGNORE_INSERTS does not prevent line wrapping or disable '%n' */ + ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_IGNORE_INSERTS | 8, + "short%nlong line%1", 0, 0, out, sizeof(out), NULL); + ok(ret == 19, "Expected FormatMessageW to return 19, got %d\n", ret); + ok(!strcmp("short\r\nlong\r\nline%1", out),"failed out=[%s]\n",out); + + /* MAX_WIDTH_MASK is the same as specifying an infinite line width */ + strcpy(in, "first line%n"); + strcpy(ref, "first line\r\n"); + for (i=0; i < 26; i++) + { + strcat(in, "123456789 "); + strcat(ref, "123456789 "); + } + ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_MAX_WIDTH_MASK, + in, 0, 0, out, sizeof(out), NULL); + ok(ret == 272, "Expected FormatMessageW to return 272, got %d\n", ret); + ok(!strcmp(ref, out),"failed out=[%s]\n",out); + + /* Wrapping and non-space characters */ + ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 11, + "short long\tline", 0, 0, out, sizeof(out), NULL); + ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret); + ok(!strcmp("short\r\nlong\tline", out),"failed out=[%s]\n",out); + + ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 11, + "short long-line", 0, 0, out, sizeof(out), NULL); + ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret); + ok(!strcmp("short\r\nlong-line", out),"failed out=[%s]\n",out); + + ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 11, + "short long_line", 0, 0, out, sizeof(out), NULL); + ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret); + ok(!strcmp("short\r\nlong_line", out),"failed out=[%s]\n",out); + + ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 11, + "short long.line", 0, 0, out, sizeof(out), NULL); + ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret); + ok(!strcmp("short\r\nlong.line", out),"failed out=[%s]\n",out); + + ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 11, + "short long,line", 0, 0, out, sizeof(out), NULL); + ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret); + ok(!strcmp("short\r\nlong,line", out),"failed out=[%s]\n",out); + + ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 11, + "short long!line", 0, 0, out, sizeof(out), NULL); + ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret); + ok(!strcmp("short\r\nlong!line", out),"failed out=[%s]\n",out); + + ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 11, + "short long?line", 0, 0, out, sizeof(out), NULL); + ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret); + ok(!strcmp("short\r\nlong?line", out),"failed out=[%s]\n",out); +} + static void test_message_insufficient_buffer(void) { static const char init_buf[] = {'x', 'x', 'x', 'x', 'x'}; @@ -1593,6 +1782,7 @@ START_TEST(format_msg) test_message_from_string(); test_message_ignore_inserts(); + test_message_wrap(); test_message_insufficient_buffer(); test_message_null_buffer(); test_message_allocate_buffer(); diff --git a/rostests/winetests/kernel32/loader.c b/rostests/winetests/kernel32/loader.c index e98ebc1bde3..e503171c9ad 100644 --- a/rostests/winetests/kernel32/loader.c +++ b/rostests/winetests/kernel32/loader.c @@ -63,7 +63,7 @@ static IMAGE_NT_HEADERS nt_header = #elif defined __sparc__ IMAGE_FILE_MACHINE_SPARC, /* Machine */ #elif defined __arm__ - IMAGE_FILE_MACHINE_ARMV7, /* Machine */ + IMAGE_FILE_MACHINE_ARMNT, /* Machine */ #else # error You must specify the machine type #endif diff --git a/rostests/winetests/kernel32/locale.c b/rostests/winetests/kernel32/locale.c index 1cc66105f77..8800d73f9e3 100755 --- a/rostests/winetests/kernel32/locale.c +++ b/rostests/winetests/kernel32/locale.c @@ -39,6 +39,11 @@ #include "winerror.h" #include "winnls.h" +static const WCHAR upper_case[] = {'\t','J','U','S','T','!',' ','A',',',' ','T','E','S','T',';',' ','S','T','R','I','N','G',' ','1','/','*','+','-','.','\r','\n',0}; +static const WCHAR lower_case[] = {'\t','j','u','s','t','!',' ','a',',',' ','t','e','s','t',';',' ','s','t','r','i','n','g',' ','1','/','*','+','-','.','\r','\n',0}; +static const WCHAR symbols_stripped[] = {'j','u','s','t','a','t','e','s','t','s','t','r','i','n','g','1',0}; +static const WCHAR fooW[] = {'f','o','o',0}; + static inline unsigned int strlenW( const WCHAR *str ) { const WCHAR *s = str; @@ -74,6 +79,7 @@ static BOOL (WINAPI *pEnumSystemLanguageGroupsA)(LANGUAGEGROUP_ENUMPROC, DWORD, static BOOL (WINAPI *pEnumLanguageGroupLocalesA)(LANGGROUPLOCALE_ENUMPROC, LGRPID, DWORD, LONG_PTR); static BOOL (WINAPI *pEnumUILanguagesA)(UILANGUAGE_ENUMPROC, DWORD, LONG_PTR); static BOOL (WINAPI *pEnumSystemLocalesEx)(LOCALE_ENUMPROCEX, DWORD, LPARAM, LPVOID); +static INT (WINAPI *pLCMapStringEx)(LPCWSTR, DWORD, LPCWSTR, INT, LPWSTR, INT, LPNLSVERSIONINFO, LPVOID, LPARAM); static LCID (WINAPI *pLocaleNameToLCID)(LPCWSTR, DWORD); static INT (WINAPI *pLCIDToLocaleName)(LCID, LPWSTR, INT, DWORD); static INT (WINAPI *pFoldStringA)(DWORD, LPCSTR, INT, LPSTR, INT); @@ -82,6 +88,9 @@ static BOOL (WINAPI *pIsValidLanguageGroup)(LGRPID, DWORD); static INT (WINAPI *pIdnToNameprepUnicode)(DWORD, LPCWSTR, INT, LPWSTR, INT); static INT (WINAPI *pIdnToAscii)(DWORD, LPCWSTR, INT, LPWSTR, INT); static INT (WINAPI *pIdnToUnicode)(DWORD, LPCWSTR, INT, LPWSTR, INT); +static INT (WINAPI *pGetLocaleInfoEx)(LPCWSTR, LCTYPE, LPWSTR, INT); +static BOOL (WINAPI *pIsValidLocaleName)(LPCWSTR); +static INT (WINAPI *pCompareStringOrdinal)(const WCHAR *, INT, const WCHAR *, INT, BOOL); static void InitFunctionPointers(void) { @@ -90,6 +99,7 @@ static void InitFunctionPointers(void) pEnumLanguageGroupLocalesA = (void*)GetProcAddress(hKernel32, "EnumLanguageGroupLocalesA"); pLocaleNameToLCID = (void*)GetProcAddress(hKernel32, "LocaleNameToLCID"); pLCIDToLocaleName = (void*)GetProcAddress(hKernel32, "LCIDToLocaleName"); + pLCMapStringEx = (void*)GetProcAddress(hKernel32, "LCMapStringEx"); pFoldStringA = (void*)GetProcAddress(hKernel32, "FoldStringA"); pFoldStringW = (void*)GetProcAddress(hKernel32, "FoldStringW"); pIsValidLanguageGroup = (void*)GetProcAddress(hKernel32, "IsValidLanguageGroup"); @@ -98,6 +108,9 @@ static void InitFunctionPointers(void) pIdnToNameprepUnicode = (void*)GetProcAddress(hKernel32, "IdnToNameprepUnicode"); pIdnToAscii = (void*)GetProcAddress(hKernel32, "IdnToAscii"); pIdnToUnicode = (void*)GetProcAddress(hKernel32, "IdnToUnicode"); + pGetLocaleInfoEx = (void*)GetProcAddress(hKernel32, "GetLocaleInfoEx"); + pIsValidLocaleName = (void*)GetProcAddress(hKernel32, "IsValidLocaleName"); + pCompareStringOrdinal = (void*)GetProcAddress(hKernel32, "CompareStringOrdinal"); } #define eq(received, expected, label, type) \ @@ -127,9 +140,14 @@ static void test_GetLocaleInfoA(void) LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT); char buffer[BUFFER_SIZE]; char expected[BUFFER_SIZE]; + DWORD val; ok(lcid == 0x409, "wrong LCID calculated - %d\n", lcid); + ret = GetLocaleInfoA(lcid, LOCALE_ILANGUAGE|LOCALE_RETURN_NUMBER, (char*)&val, sizeof(val)); + ok(ret, "got %d\n", ret); + ok(val == lcid, "got 0x%08x\n", val); + /* en and ar use SUBLANG_NEUTRAL, but GetLocaleInfo assume SUBLANG_DEFAULT Same is true for zh on pre-Vista, but on Vista and higher GetLocaleInfo assumes SUBLANG_NEUTRAL for zh */ @@ -188,12 +206,57 @@ static void test_GetLocaleInfoA(void) ok(!strcmp(buffer, "Monday"), "Expected 'Monday', got '%s'\n", buffer); } +struct neutralsublang_name2_t { + WCHAR name[3]; + WCHAR sname[15]; + LCID lcid; + LCID lcid_broken; + WCHAR sname_broken[15]; + int todo; +}; + +static const struct neutralsublang_name2_t neutralsublang_names2[] = { + { {'a','r',0}, {'a','r','-','S','A',0}, + MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_SAUDI_ARABIA), SORT_DEFAULT) }, + { {'a','z',0}, {'a','z','-','L','a','t','n','-','A','Z',0}, + MAKELCID(MAKELANGID(LANG_AZERI, SUBLANG_AZERI_LATIN), SORT_DEFAULT) }, + { {'d','e',0}, {'d','e','-','D','E',0}, + MAKELCID(MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN), SORT_DEFAULT) }, + { {'e','n',0}, {'e','n','-','U','S',0}, + MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT) }, + { {'e','s',0}, {'e','s','-','E','S',0}, + MAKELCID(MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_MODERN), SORT_DEFAULT), + MAKELCID(MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH), SORT_DEFAULT) /* vista */, + {'e','s','-','E','S','_','t','r','a','d','n','l',0}, 0x1 }, + { {'g','a',0}, {'g','a','-','I','E',0}, + MAKELCID(MAKELANGID(LANG_IRISH, SUBLANG_IRISH_IRELAND), SORT_DEFAULT), 0, {0}, 0x3 }, + { {'i','t',0}, {'i','t','-','I','T',0}, + MAKELCID(MAKELANGID(LANG_ITALIAN, SUBLANG_ITALIAN), SORT_DEFAULT) }, + { {'m','s',0}, {'m','s','-','M','Y',0}, + MAKELCID(MAKELANGID(LANG_MALAY, SUBLANG_MALAY_MALAYSIA), SORT_DEFAULT) }, + { {'n','l',0}, {'n','l','-','N','L',0}, + MAKELCID(MAKELANGID(LANG_DUTCH, SUBLANG_DUTCH), SORT_DEFAULT) }, + { {'p','t',0}, {'p','t','-','B','R',0}, + MAKELCID(MAKELANGID(LANG_PORTUGUESE, SUBLANG_PORTUGUESE_BRAZILIAN), SORT_DEFAULT) }, + { {'s','r',0}, {'h','r','-','H','R',0}, + MAKELCID(MAKELANGID(LANG_SERBIAN, SUBLANG_SERBIAN_CROATIA), SORT_DEFAULT) }, + { {'s','v',0}, {'s','v','-','S','E',0}, + MAKELCID(MAKELANGID(LANG_SWEDISH, SUBLANG_SWEDISH), SORT_DEFAULT) }, + { {'u','z',0}, {'u','z','-','L','a','t','n','-','U','Z',0}, + MAKELCID(MAKELANGID(LANG_UZBEK, SUBLANG_UZBEK_LATIN), SORT_DEFAULT) }, + { {'z','h',0}, {'z','h','-','C','N',0}, + MAKELCID(MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED), SORT_DEFAULT), 0, {0}, 0x3 }, + { {0} } +}; + static void test_GetLocaleInfoW(void) { LCID lcid_en = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT); LCID lcid_ru = MAKELCID(MAKELANGID(LANG_RUSSIAN, SUBLANG_NEUTRAL), SORT_DEFAULT); + LCID lcid_en_neut = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_NEUTRAL), SORT_DEFAULT); WCHAR bufferW[80], buffer2W[80]; CHAR bufferA[80]; + DWORD val; DWORD ret; INT i; @@ -202,6 +265,68 @@ static void test_GetLocaleInfoW(void) win_skip("GetLocaleInfoW() isn't implemented\n"); return; } + + ret = GetLocaleInfoW(lcid_en, LOCALE_ILANGUAGE|LOCALE_RETURN_NUMBER, (WCHAR*)&val, sizeof(val)/sizeof(WCHAR)); + ok(ret, "got %d\n", ret); + ok(val == lcid_en, "got 0x%08x\n", val); + + ret = GetLocaleInfoW(lcid_en_neut, LOCALE_SNAME, bufferW, COUNTOF(bufferW)); + if (ret) + { + static const WCHAR slangW[] = {'E','n','g','l','i','s','h',' ','(','U','n','i','t','e','d',' ', + 'S','t','a','t','e','s',')',0}; + static const WCHAR statesW[] = {'U','n','i','t','e','d',' ','S','t','a','t','e','s',0}; + static const WCHAR enW[] = {'e','n','-','U','S',0}; + const struct neutralsublang_name2_t *ptr = neutralsublang_names2; + + ok(!lstrcmpW(bufferW, enW), "got wrong name %s\n", wine_dbgstr_w(bufferW)); + + ret = GetLocaleInfoW(lcid_en_neut, LOCALE_SCOUNTRY, bufferW, COUNTOF(bufferW)); + ok(ret, "got %d\n", ret); + ok(!lstrcmpW(statesW, bufferW), "got wrong name %s\n", wine_dbgstr_w(bufferW)); + + ret = GetLocaleInfoW(lcid_en_neut, LOCALE_SLANGUAGE, bufferW, COUNTOF(bufferW)); + ok(ret, "got %d\n", ret); + ok(!lstrcmpW(slangW, bufferW), "got wrong name %s\n", wine_dbgstr_w(bufferW)); + + while (*ptr->name) + { + LANGID langid; + LCID lcid; + + /* make neutral lcid */ + langid = MAKELANGID(PRIMARYLANGID(LANGIDFROMLCID(ptr->lcid)), SUBLANG_NEUTRAL); + lcid = MAKELCID(langid, SORT_DEFAULT); + + val = 0; + GetLocaleInfoW(lcid, LOCALE_ILANGUAGE|LOCALE_RETURN_NUMBER, (WCHAR*)&val, sizeof(val)/sizeof(WCHAR)); + if (ptr->todo & 0x1) + { + todo_wine + ok(val == ptr->lcid || (val && broken(val == ptr->lcid_broken)), "%s: got wrong lcid 0x%04x, expected 0x%04x\n", + wine_dbgstr_w(ptr->name), val, ptr->lcid); + } + else + ok(val == ptr->lcid || (val && broken(val == ptr->lcid_broken)), "%s: got wrong lcid 0x%04x, expected 0x%04x\n", + wine_dbgstr_w(ptr->name), val, ptr->lcid); + + /* now check LOCALE_SNAME */ + GetLocaleInfoW(lcid, LOCALE_SNAME, bufferW, COUNTOF(bufferW)); + if (ptr->todo & 0x2) + todo_wine + ok(!lstrcmpW(bufferW, ptr->sname) || + (*ptr->sname_broken && broken(!lstrcmpW(bufferW, ptr->sname_broken))), + "%s: got %s\n", wine_dbgstr_w(ptr->name), wine_dbgstr_w(bufferW)); + else + ok(!lstrcmpW(bufferW, ptr->sname) || + (*ptr->sname_broken && broken(!lstrcmpW(bufferW, ptr->sname_broken))), + "%s: got %s\n", wine_dbgstr_w(ptr->name), wine_dbgstr_w(bufferW)); + ptr++; + } + } + else + win_skip("English neutral locale not supported\n"); + ret = GetLocaleInfoW(lcid_ru, LOCALE_SMONTHNAME1, bufferW, COUNTOF(bufferW)); if (!ret) { win_skip("LANG_RUSSIAN locale data unavailable\n"); @@ -1206,27 +1331,27 @@ static void test_CompareStringA(void) } ret = CompareStringA(lcid, NORM_IGNORECASE, "Salut", -1, "Salute", -1); - ok (ret== 1, "(Salut/Salute) Expected 1, got %d\n", ret); + ok (ret == CSTR_LESS_THAN, "(Salut/Salute) Expected CSTR_LESS_THAN, got %d\n", ret); ret = CompareStringA(lcid, NORM_IGNORECASE, "Salut", -1, "SaLuT", -1); - ok (ret== 2, "(Salut/SaLuT) Expected 2, got %d\n", ret); + ok (ret == CSTR_EQUAL, "(Salut/SaLuT) Expected CSTR_EQUAL, got %d\n", ret); ret = CompareStringA(lcid, NORM_IGNORECASE, "Salut", -1, "hola", -1); - ok (ret== 3, "(Salut/hola) Expected 3, got %d\n", ret); + ok (ret == CSTR_GREATER_THAN, "(Salut/hola) Expected CSTR_GREATER_THAN, got %d\n", ret); ret = CompareStringA(lcid, NORM_IGNORECASE, "haha", -1, "hoho", -1); - ok (ret== 1, "(haha/hoho) Expected 1, got %d\n", ret); + ok (ret == CSTR_LESS_THAN, "(haha/hoho) Expected CSTR_LESS_THAN, got %d\n", ret); lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT); ret = CompareStringA(lcid, NORM_IGNORECASE, "haha", -1, "hoho", -1); - ok (ret== 1, "(haha/hoho) Expected 1, got %d\n", ret); + ok (ret == CSTR_LESS_THAN, "(haha/hoho) Expected CSTR_LESS_THAN, got %d\n", ret); ret = CompareStringA(lcid, NORM_IGNORECASE, "haha", -1, "hoho", 0); - ok (ret== 3, "(haha/hoho) Expected 3, got %d\n", ret); + ok (ret == CSTR_GREATER_THAN, "(haha/hoho) Expected CSTR_GREATER_THAN, got %d\n", ret); ret = CompareStringA(lcid, NORM_IGNORECASE, "Salut", 5, "saLuT", -1); - ok (ret == 2, "(Salut/saLuT) Expected 2, got %d\n", ret); + ok (ret == CSTR_EQUAL, "(Salut/saLuT) Expected CSTR_EQUAL, got %d\n", ret); /* test for CompareStringA flags */ SetLastError(0xdeadbeef); @@ -1256,52 +1381,52 @@ static void test_CompareStringA(void) if (0) { /* this requires collation table patch to make it MS compatible */ ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "'o", -1, "-o", -1 ); - ok(ret == 1, "'o vs -o expected 1, got %d\n", ret); + ok(ret == CSTR_LESS_THAN, "'o vs -o expected CSTR_LESS_THAN, got %d\n", ret); ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "'o", -1, "-o", -1 ); - ok(ret == 1, "'o vs -o expected 1, got %d\n", ret); + ok(ret == CSTR_LESS_THAN, "'o vs -o expected CSTR_LESS_THAN, got %d\n", ret); ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "'", -1, "-", -1 ); - ok(ret == 1, "' vs - expected 1, got %d\n", ret); + ok(ret == CSTR_LESS_THAN, "' vs - expected CSTR_LESS_THAN, got %d\n", ret); ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "'", -1, "-", -1 ); - ok(ret == 1, "' vs - expected 1, got %d\n", ret); + ok(ret == CSTR_LESS_THAN, "' vs - expected CSTR_LESS_THAN, got %d\n", ret); ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "`o", -1, "/m", -1 ); - ok(ret == 3, "`o vs /m expected 3, got %d\n", ret); + ok(ret == CSTR_GREATER_THAN, "`o vs /m CSTR_GREATER_THAN got %d\n", ret); ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "/m", -1, "`o", -1 ); - ok(ret == 1, "/m vs `o expected 1, got %d\n", ret); + ok(ret == CSTR_LESS_THAN, "/m vs `o expected CSTR_LESS_THAN, got %d\n", ret); ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "`o", -1, "/m", -1 ); - ok(ret == 3, "`o vs /m expected 3, got %d\n", ret); + ok(ret == CSTR_GREATER_THAN, "`o vs /m CSTR_GREATER_THAN got %d\n", ret); ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "/m", -1, "`o", -1 ); - ok(ret == 1, "/m vs `o expected 1, got %d\n", ret); + ok(ret == CSTR_LESS_THAN, "/m vs `o expected CSTR_LESS_THAN, got %d\n", ret); ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "`o", -1, "-m", -1 ); - ok(ret == 1, "`o vs -m expected 1, got %d\n", ret); + ok(ret == CSTR_LESS_THAN, "`o vs -m expected CSTR_LESS_THAN, got %d\n", ret); ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "-m", -1, "`o", -1 ); - ok(ret == 3, "-m vs `o expected 3, got %d\n", ret); + ok(ret == CSTR_GREATER_THAN, "-m vs `o CSTR_GREATER_THAN got %d\n", ret); ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "`o", -1, "-m", -1 ); - ok(ret == 3, "`o vs -m expected 3, got %d\n", ret); + ok(ret == CSTR_GREATER_THAN, "`o vs -m CSTR_GREATER_THAN got %d\n", ret); ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "-m", -1, "`o", -1 ); - ok(ret == 1, "-m vs `o expected 1, got %d\n", ret); + ok(ret == CSTR_LESS_THAN, "-m vs `o expected CSTR_LESS_THAN, got %d\n", ret); } /* WinXP handles embedded NULLs differently than earlier versions */ ret = CompareStringA(LOCALE_USER_DEFAULT, 0, "aLuZkUtZ", 8, "aLuZkUtZ\0A", 10); - ok(ret == 1 || ret == 2, "aLuZkUtZ vs aLuZkUtZ\\0A expected 1 or 2, got %d\n", ret); + ok(ret == CSTR_LESS_THAN || ret == CSTR_EQUAL, "aLuZkUtZ vs aLuZkUtZ\\0A expected CSTR_LESS_THAN or CSTR_EQUAL, got %d\n", ret); ret = CompareStringA(LOCALE_USER_DEFAULT, 0, "aLu\0ZkUtZ", 8, "aLu\0ZkUtZ\0A", 10); - ok(ret == 1 || ret == 2, "aLu\\0ZkUtZ vs aLu\\0ZkUtZ\\0A expected 1 or 2, got %d\n", ret); + ok(ret == CSTR_LESS_THAN || ret == CSTR_EQUAL, "aLu\\0ZkUtZ vs aLu\\0ZkUtZ\\0A expected CSTR_LESS_THAN or CSTR_EQUAL, got %d\n", ret); ret = CompareStringA(lcid, 0, "a\0b", -1, "a", -1); - ok(ret == 2, "a vs a expected 2, got %d\n", ret); + ok(ret == CSTR_EQUAL, "a vs a expected CSTR_EQUAL, got %d\n", ret); ret = CompareStringA(lcid, 0, "a\0b", 4, "a", 2); ok(ret == CSTR_EQUAL || /* win2k */ @@ -1309,7 +1434,7 @@ static void test_CompareStringA(void) "a\\0b vs a expected CSTR_EQUAL or CSTR_GREATER_THAN, got %d\n", ret); ret = CompareStringA(lcid, 0, "\2", 2, "\1", 2); - todo_wine ok(ret != 2, "\\2 vs \\1 expected unequal\n"); + todo_wine ok(ret != CSTR_EQUAL, "\\2 vs \\1 expected unequal\n"); ret = CompareStringA(lcid, NORM_IGNORECASE | LOCALE_USE_CP_ACP, "#", -1, ".", -1); todo_wine ok(ret == CSTR_LESS_THAN, "\"#\" vs \".\" expected CSTR_LESS_THAN, got %d\n", ret); @@ -1493,168 +1618,253 @@ static void test_LCMapStringA(void) "unexpected error code %d\n", GetLastError()); } -static void test_LCMapStringW(void) +typedef INT (*lcmapstring_wrapper)(DWORD, LPCWSTR, INT, LPWSTR, INT); + +static void test_lcmapstring_unicode(lcmapstring_wrapper func_ptr, const char *func_name) { int ret, ret2; WCHAR buf[256], buf2[256]; char *p_buf = (char *)buf, *p_buf2 = (char *)buf2; - static const WCHAR upper_case[] = {'\t','J','U','S','T','!',' ','A',',',' ','T','E','S','T',';',' ','S','T','R','I','N','G',' ','1','/','*','+','-','.','\r','\n',0}; - static const WCHAR lower_case[] = {'\t','j','u','s','t','!',' ','a',',',' ','t','e','s','t',';',' ','s','t','r','i','n','g',' ','1','/','*','+','-','.','\r','\n',0}; - static const WCHAR symbols_stripped[] = {'j','u','s','t','a','t','e','s','t','s','t','r','i','n','g','1',0}; - static const WCHAR fooW[] = {'f','o','o',0}; - ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE | LCMAP_UPPERCASE, + ret = func_ptr(LCMAP_LOWERCASE | LCMAP_UPPERCASE, upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR)); - if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) - { - win_skip("LCMapStringW is not implemented\n"); - return; - } if (broken(ret)) ok(lstrcmpW(buf, upper_case) == 0, "Expected upper case string\n"); else { - ok(!ret, "LCMAP_LOWERCASE and LCMAP_UPPERCASE are mutually exclusive\n"); - ok(GetLastError() == ERROR_INVALID_FLAGS, - "unexpected error code %d\n", GetLastError()); + ok(!ret, "%s LCMAP_LOWERCASE and LCMAP_UPPERCASE are mutually exclusive\n", func_name); + ok(GetLastError() == ERROR_INVALID_FLAGS, "%s unexpected error code %d\n", + func_name, GetLastError()); } - ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_HIRAGANA | LCMAP_KATAKANA, + ret = func_ptr(LCMAP_HIRAGANA | LCMAP_KATAKANA, upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR)); - ok(!ret, "LCMAP_HIRAGANA and LCMAP_KATAKANA are mutually exclusive\n"); - ok(GetLastError() == ERROR_INVALID_FLAGS, - "unexpected error code %d\n", GetLastError()); + ok(!ret, "%s LCMAP_HIRAGANA and LCMAP_KATAKANA are mutually exclusive\n", func_name); + ok(GetLastError() == ERROR_INVALID_FLAGS, "%s unexpected error code %d\n", + func_name, GetLastError()); - ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_HALFWIDTH | LCMAP_FULLWIDTH, + ret = func_ptr(LCMAP_HALFWIDTH | LCMAP_FULLWIDTH, upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR)); - ok(!ret, "LCMAP_HALFWIDTH | LCMAP_FULLWIDTH are mutually exclusive\n"); - ok(GetLastError() == ERROR_INVALID_FLAGS, - "unexpected error code %d\n", GetLastError()); + ok(!ret, "%s LCMAP_HALFWIDTH | LCMAP_FULLWIDTH are mutually exclusive\n", func_name); + ok(GetLastError() == ERROR_INVALID_FLAGS, "%s unexpected error code %d\n", + func_name, GetLastError()); - ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE, + ret = func_ptr(LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE, upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR)); - ok(!ret, "LCMAP_TRADITIONAL_CHINESE and LCMAP_SIMPLIFIED_CHINESE are mutually exclusive\n"); - ok(GetLastError() == ERROR_INVALID_FLAGS, - "unexpected error code %d\n", GetLastError()); + ok(!ret, "%s LCMAP_TRADITIONAL_CHINESE and LCMAP_SIMPLIFIED_CHINESE are mutually exclusive\n", + func_name); + ok(GetLastError() == ERROR_INVALID_FLAGS, "%s unexpected error code %d\n", + func_name, GetLastError()); /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */ SetLastError(0xdeadbeef); - ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE | SORT_STRINGSORT, + ret = func_ptr(LCMAP_LOWERCASE | SORT_STRINGSORT, upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR)); - ok(GetLastError() == ERROR_INVALID_FLAGS, "expected ERROR_INVALID_FLAGS, got %d\n", GetLastError()); - ok(!ret, "SORT_STRINGSORT without LCMAP_SORTKEY must fail\n"); + ok(GetLastError() == ERROR_INVALID_FLAGS, "%s expected ERROR_INVALID_FLAGS, got %d\n", + func_name, GetLastError()); + ok(!ret, "%s SORT_STRINGSORT without LCMAP_SORTKEY must fail\n", func_name); /* test LCMAP_LOWERCASE */ - ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE, + ret = func_ptr(LCMAP_LOWERCASE, upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR)); - ok(ret == lstrlenW(upper_case) + 1, - "ret %d, error %d, expected value %d\n", + ok(ret == lstrlenW(upper_case) + 1, "%s ret %d, error %d, expected value %d\n", func_name, ret, GetLastError(), lstrlenW(upper_case) + 1); - ok(!lstrcmpW(buf, lower_case), "string compare mismatch\n"); + ok(!lstrcmpW(buf, lower_case), "%s string compare mismatch\n", func_name); /* test LCMAP_UPPERCASE */ - ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_UPPERCASE, + ret = func_ptr(LCMAP_UPPERCASE, lower_case, -1, buf, sizeof(buf)/sizeof(WCHAR)); - ok(ret == lstrlenW(lower_case) + 1, - "ret %d, error %d, expected value %d\n", + ok(ret == lstrlenW(lower_case) + 1, "%s ret %d, error %d, expected value %d\n", func_name, ret, GetLastError(), lstrlenW(lower_case) + 1); - ok(!lstrcmpW(buf, upper_case), "string compare mismatch\n"); + ok(!lstrcmpW(buf, upper_case), "%s string compare mismatch\n", func_name); /* test buffer overflow */ SetLastError(0xdeadbeef); - ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_UPPERCASE, + ret = func_ptr(LCMAP_UPPERCASE, lower_case, -1, buf, 4); ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, - "should return 0 and ERROR_INSUFFICIENT_BUFFER, got %d\n", ret); + "%s should return 0 and ERROR_INSUFFICIENT_BUFFER, got %d\n", func_name, ret); /* LCMAP_UPPERCASE or LCMAP_LOWERCASE should accept src == dst */ lstrcpyW(buf, lower_case); - ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_UPPERCASE, + ret = func_ptr(LCMAP_UPPERCASE, buf, -1, buf, sizeof(buf)/sizeof(WCHAR)); - ok(ret == lstrlenW(lower_case) + 1, - "ret %d, error %d, expected value %d\n", + ok(ret == lstrlenW(lower_case) + 1, "%s ret %d, error %d, expected value %d\n", func_name, ret, GetLastError(), lstrlenW(lower_case) + 1); - ok(!lstrcmpW(buf, upper_case), "string compare mismatch\n"); + ok(!lstrcmpW(buf, upper_case), "%s string compare mismatch\n", func_name); lstrcpyW(buf, upper_case); - ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE, + ret = func_ptr(LCMAP_LOWERCASE, buf, -1, buf, sizeof(buf)/sizeof(WCHAR)); - ok(ret == lstrlenW(upper_case) + 1, - "ret %d, error %d, expected value %d\n", + ok(ret == lstrlenW(upper_case) + 1, "%s ret %d, error %d, expected value %d\n", func_name, ret, GetLastError(), lstrlenW(lower_case) + 1); - ok(!lstrcmpW(buf, lower_case), "string compare mismatch\n"); + ok(!lstrcmpW(buf, lower_case), "%s string compare mismatch\n", func_name); /* otherwise src == dst should fail */ SetLastError(0xdeadbeef); - ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_SORTKEY | LCMAP_UPPERCASE, + ret = func_ptr(LCMAP_SORTKEY | LCMAP_UPPERCASE, buf, 10, buf, sizeof(buf)); ok(GetLastError() == ERROR_INVALID_FLAGS /* NT */ || - GetLastError() == ERROR_INVALID_PARAMETER /* Win9x */, - "unexpected error code %d\n", GetLastError()); - ok(!ret, "src == dst without LCMAP_UPPERCASE or LCMAP_LOWERCASE must fail\n"); + GetLastError() == ERROR_INVALID_PARAMETER /* Win7+ */, + "%s unexpected error code %d\n", func_name, GetLastError()); + ok(!ret, "%s src == dst without LCMAP_UPPERCASE or LCMAP_LOWERCASE must fail\n", func_name); /* test whether '\0' is always appended */ - ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_SORTKEY, + ret = func_ptr(LCMAP_SORTKEY, upper_case, -1, buf, sizeof(buf)); - ok(ret, "LCMapStringW must succeed\n"); - ret2 = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_SORTKEY, + ok(ret, "%s func_ptr must succeed\n", func_name); + ret2 = func_ptr(LCMAP_SORTKEY, upper_case, lstrlenW(upper_case), buf2, sizeof(buf2)); - ok(ret, "LCMapStringW must succeed\n"); - ok(ret == ret2, "lengths of sort keys must be equal\n"); - ok(!lstrcmpA(p_buf, p_buf2), "sort keys must be equal\n"); + ok(ret, "%s func_ptr must succeed\n", func_name); + ok(ret == ret2, "%s lengths of sort keys must be equal\n", func_name); + ok(!lstrcmpA(p_buf, p_buf2), "%s sort keys must be equal\n", func_name); /* test LCMAP_SORTKEY | NORM_IGNORECASE */ - ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_SORTKEY | NORM_IGNORECASE, + ret = func_ptr(LCMAP_SORTKEY | NORM_IGNORECASE, upper_case, -1, buf, sizeof(buf)); - ok(ret, "LCMapStringW must succeed\n"); - ret2 = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_SORTKEY, + ok(ret, "%s func_ptr must succeed\n", func_name); + ret2 = func_ptr(LCMAP_SORTKEY, lower_case, -1, buf2, sizeof(buf2)); - ok(ret2, "LCMapStringW must succeed\n"); - ok(ret == ret2, "lengths of sort keys must be equal\n"); - ok(!lstrcmpA(p_buf, p_buf2), "sort keys must be equal\n"); + ok(ret2, "%s func_ptr must succeed\n", func_name); + ok(ret == ret2, "%s lengths of sort keys must be equal\n", func_name); + ok(!lstrcmpA(p_buf, p_buf2), "%s sort keys must be equal\n", func_name); /* Don't test LCMAP_SORTKEY | NORM_IGNORENONSPACE, produces different results from plain LCMAP_SORTKEY on Vista */ /* test LCMAP_SORTKEY | NORM_IGNORESYMBOLS */ - ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_SORTKEY | NORM_IGNORESYMBOLS, + ret = func_ptr(LCMAP_SORTKEY | NORM_IGNORESYMBOLS, lower_case, -1, buf, sizeof(buf)); - ok(ret, "LCMapStringW must succeed\n"); - ret2 = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_SORTKEY, + ok(ret, "%s func_ptr must succeed\n", func_name); + ret2 = func_ptr(LCMAP_SORTKEY, symbols_stripped, -1, buf2, sizeof(buf2)); - ok(ret2, "LCMapStringW must succeed\n"); - ok(ret == ret2, "lengths of sort keys must be equal\n"); - ok(!lstrcmpA(p_buf, p_buf2), "sort keys must be equal\n"); + ok(ret2, "%s func_ptr must succeed\n", func_name); + ok(ret == ret2, "%s lengths of sort keys must be equal\n", func_name); + ok(!lstrcmpA(p_buf, p_buf2), "%s sort keys must be equal\n", func_name); /* test NORM_IGNORENONSPACE */ lstrcpyW(buf, fooW); - ret = LCMapStringW(LOCALE_USER_DEFAULT, NORM_IGNORENONSPACE, + ret = func_ptr(NORM_IGNORENONSPACE, lower_case, -1, buf, sizeof(buf)/sizeof(WCHAR)); - ok(ret == lstrlenW(lower_case) + 1, "LCMapStringW should return %d, ret = %d\n", - lstrlenW(lower_case) + 1, ret); - ok(!lstrcmpW(buf, lower_case), "string comparison mismatch\n"); + ok(ret == lstrlenW(lower_case) + 1, "%s func_ptr should return %d, ret = %d\n", func_name, + lstrlenW(lower_case) + 1, ret); + ok(!lstrcmpW(buf, lower_case), "%s string comparison mismatch\n", func_name); /* test NORM_IGNORESYMBOLS */ lstrcpyW(buf, fooW); - ret = LCMapStringW(LOCALE_USER_DEFAULT, NORM_IGNORESYMBOLS, + ret = func_ptr(NORM_IGNORESYMBOLS, lower_case, -1, buf, sizeof(buf)/sizeof(WCHAR)); - ok(ret == lstrlenW(symbols_stripped) + 1, "LCMapStringW should return %d, ret = %d\n", - lstrlenW(symbols_stripped) + 1, ret); - ok(!lstrcmpW(buf, symbols_stripped), "string comparison mismatch\n"); + ok(ret == lstrlenW(symbols_stripped) + 1, "%s func_ptr should return %d, ret = %d\n", func_name, + lstrlenW(symbols_stripped) + 1, ret); + ok(!lstrcmpW(buf, symbols_stripped), "%s string comparison mismatch\n", func_name); /* test srclen = 0 */ SetLastError(0xdeadbeef); - ret = LCMapStringW(LOCALE_USER_DEFAULT, 0, upper_case, 0, buf, sizeof(buf)/sizeof(WCHAR)); - ok(!ret, "LCMapStringW should fail with srclen = 0\n"); + ret = func_ptr(0, upper_case, 0, buf, sizeof(buf)/sizeof(WCHAR)); + ok(!ret, "%s func_ptr should fail with srclen = 0\n", func_name); ok(GetLastError() == ERROR_INVALID_PARAMETER, - "unexpected error code %d\n", GetLastError()); + "%s unexpected error code %d\n", func_name, GetLastError()); +} + +static INT LCMapStringW_wrapper(DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen) +{ + return LCMapStringW(LOCALE_USER_DEFAULT, flags, src, srclen, dst, dstlen); +} + +static void test_LCMapStringW(void) +{ + int ret; + WCHAR buf[256]; + + trace("testing LCMapStringW\n"); + + SetLastError(0xdeadbeef); + ret = LCMapStringW((LCID)-1, LCMAP_LOWERCASE, upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR)); + todo_wine { + ok(!ret, "LCMapStringW should fail with bad lcid\n"); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "unexpected error code %d\n", GetLastError()); + } + + test_lcmapstring_unicode(LCMapStringW_wrapper, "LCMapStringW:"); +} + +static INT LCMapStringEx_wrapper(DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen) +{ + return pLCMapStringEx(LOCALE_NAME_USER_DEFAULT, flags, src, srclen, dst, dstlen, NULL, NULL, 0); } -static void test_LocaleNames(void) +static void test_LCMapStringEx(void) +{ + int ret; + WCHAR buf[256]; + + if (!pLCMapStringEx) + { + win_skip( "LCMapStringEx not available\n" ); + return; + } + + trace("testing LCMapStringEx\n"); + + SetLastError(0xdeadbeef); + ret = pLCMapStringEx(fooW, LCMAP_LOWERCASE, + upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR), NULL, NULL, 0); + todo_wine { + ok(!ret, "LCMapStringEx should fail with bad locale name\n"); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "unexpected error code %d\n", GetLastError()); + } + + /* test reserved parameters */ + ret = pLCMapStringEx(LOCALE_NAME_USER_DEFAULT, LCMAP_LOWERCASE, + upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR), NULL, NULL, 1); + ok(ret == lstrlenW(upper_case) + 1, "ret %d, error %d, expected value %d\n", + ret, GetLastError(), lstrlenW(upper_case) + 1); + ok(!lstrcmpW(buf, lower_case), "string compare mismatch\n"); + + ret = pLCMapStringEx(LOCALE_NAME_USER_DEFAULT, LCMAP_LOWERCASE, + upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR), NULL, (void*)1, 0); + ok(ret == lstrlenW(upper_case) + 1, "ret %d, error %d, expected value %d\n", + ret, GetLastError(), lstrlenW(upper_case) + 1); + ok(!lstrcmpW(buf, lower_case), "string compare mismatch\n"); + + /* crashes on native */ + if(0) + ret = pLCMapStringEx(LOCALE_NAME_USER_DEFAULT, LCMAP_LOWERCASE, + upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR), (void*)1, NULL, 0); + + test_lcmapstring_unicode(LCMapStringEx_wrapper, "LCMapStringEx:"); +} + +struct neutralsublang_name_t { + WCHAR name[3]; + LCID lcid; + int todo; +}; + +static const struct neutralsublang_name_t neutralsublang_names[] = { + { {'a','r',0}, MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_SAUDI_ARABIA), SORT_DEFAULT) }, + { {'a','z',0}, MAKELCID(MAKELANGID(LANG_AZERI, SUBLANG_AZERI_LATIN), SORT_DEFAULT) }, + { {'d','e',0}, MAKELCID(MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN), SORT_DEFAULT) }, + { {'e','n',0}, MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT) }, + { {'e','s',0}, MAKELCID(MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_MODERN), SORT_DEFAULT), 1 }, + { {'g','a',0}, MAKELCID(MAKELANGID(LANG_IRISH, SUBLANG_IRISH_IRELAND), SORT_DEFAULT), 1 }, + { {'i','t',0}, MAKELCID(MAKELANGID(LANG_ITALIAN, SUBLANG_ITALIAN), SORT_DEFAULT) }, + { {'m','s',0}, MAKELCID(MAKELANGID(LANG_MALAY, SUBLANG_MALAY_MALAYSIA), SORT_DEFAULT) }, + { {'n','l',0}, MAKELCID(MAKELANGID(LANG_DUTCH, SUBLANG_DUTCH), SORT_DEFAULT) }, + { {'p','t',0}, MAKELCID(MAKELANGID(LANG_PORTUGUESE, SUBLANG_PORTUGUESE_BRAZILIAN), SORT_DEFAULT) }, + { {'s','r',0}, MAKELCID(MAKELANGID(LANG_SERBIAN, SUBLANG_SERBIAN_SERBIA_LATIN), SORT_DEFAULT), 1 }, + { {'s','v',0}, MAKELCID(MAKELANGID(LANG_SWEDISH, SUBLANG_SWEDISH), SORT_DEFAULT) }, + { {'u','z',0}, MAKELCID(MAKELANGID(LANG_UZBEK, SUBLANG_UZBEK_LATIN), SORT_DEFAULT) }, + { {'z','h',0}, MAKELCID(MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED), SORT_DEFAULT), 1 }, + { {0} } +}; + +static void test_LocaleNameToLCID(void) { LCID lcid; INT ret; WCHAR buffer[LOCALE_NAME_MAX_LENGTH]; + static const WCHAR enW[] = {'e','n',0}; if (!pLocaleNameToLCID) { @@ -1664,27 +1874,65 @@ static void test_LocaleNames(void) /* special cases */ buffer[0] = 0; + SetLastError(0xdeadbeef); lcid = pLocaleNameToLCID(LOCALE_NAME_USER_DEFAULT, 0); ok(lcid == GetUserDefaultLCID() || broken(GetLastError() == ERROR_INVALID_PARAMETER /* Vista */), - "Expected lcid == %08x, got %08x, error %d\n", lcid, GetUserDefaultLCID(), GetLastError()); + "Expected lcid == %08x, got %08x, error %d\n", GetUserDefaultLCID(), lcid, GetLastError()); ret = pLCIDToLocaleName(lcid, buffer, LOCALE_NAME_MAX_LENGTH, 0); ok(ret > 0, "Expected ret > 0, got %d, error %d\n", ret, GetLastError()); trace("%08x, %s\n", lcid, wine_dbgstr_w(buffer)); buffer[0] = 0; + SetLastError(0xdeadbeef); lcid = pLocaleNameToLCID(LOCALE_NAME_SYSTEM_DEFAULT, 0); - todo_wine ok(!lcid && GetLastError() == ERROR_INVALID_PARAMETER, - "Expected lcid != 0, got %08x, error %d\n", lcid, GetLastError()); + ok(!lcid && GetLastError() == ERROR_INVALID_PARAMETER, + "Expected lcid == 0, got %08x, error %d\n", lcid, GetLastError()); ret = pLCIDToLocaleName(lcid, buffer, LOCALE_NAME_MAX_LENGTH, 0); ok(ret > 0, "Expected ret > 0, got %d, error %d\n", ret, GetLastError()); trace("%08x, %s\n", lcid, wine_dbgstr_w(buffer)); buffer[0] = 0; + SetLastError(0xdeadbeef); lcid = pLocaleNameToLCID(LOCALE_NAME_INVARIANT, 0); todo_wine ok(lcid == 0x7F, "Expected lcid = 0x7F, got %08x, error %d\n", lcid, GetLastError()); ret = pLCIDToLocaleName(lcid, buffer, LOCALE_NAME_MAX_LENGTH, 0); ok(ret > 0, "Expected ret > 0, got %d, error %d\n", ret, GetLastError()); trace("%08x, %s\n", lcid, wine_dbgstr_w(buffer)); + + /* bad name */ + SetLastError(0xdeadbeef); + lcid = pLocaleNameToLCID(fooW, 0); + ok(!lcid && GetLastError() == ERROR_INVALID_PARAMETER, + "Expected lcid == 0, got got %08x, error %d\n", lcid, GetLastError()); + + /* english neutral name */ + lcid = pLocaleNameToLCID(enW, 0); + ok(lcid == MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT) || + broken(lcid == 0) /* Vista */, "got 0x%04x\n", lcid); + if (lcid) + { + const struct neutralsublang_name_t *ptr = neutralsublang_names; + + while (*ptr->name) + { + lcid = pLocaleNameToLCID(ptr->name, 0); + if (ptr->todo) + todo_wine + ok(lcid == ptr->lcid, "%s: got wrong lcid 0x%04x, expected 0x%04x\n", + wine_dbgstr_w(ptr->name), lcid, ptr->lcid); + else + ok(lcid == ptr->lcid, "%s: got wrong lcid 0x%04x, expected 0x%04x\n", + wine_dbgstr_w(ptr->name), lcid, ptr->lcid); + + *buffer = 0; + ret = pLCIDToLocaleName(lcid, buffer, sizeof(buffer)/sizeof(WCHAR), 0); + ok(ret > 0, "%s: got %d\n", wine_dbgstr_w(ptr->name), ret); + ok(lstrcmpW(ptr->name, buffer), "%s: got wrong locale name %s\n", + wine_dbgstr_w(ptr->name), wine_dbgstr_w(buffer)); + + ptr++; + } + } } /* this requires collation table patch to make it MS compatible */ @@ -3140,6 +3388,173 @@ static void test_IdnToUnicode(void) } } +static void test_GetLocaleInfoEx(void) +{ + static const WCHAR enW[] = {'e','n',0}; + WCHAR bufferW[80]; + INT ret; + + if (!pGetLocaleInfoEx) + { + win_skip("GetLocaleInfoEx not supported\n"); + return; + } + + ret = pGetLocaleInfoEx(enW, LOCALE_SNAME, bufferW, sizeof(bufferW)/sizeof(WCHAR)); + ok(ret || broken(ret == 0) /* Vista */, "got %d\n", ret); + if (ret) + { + static const WCHAR statesW[] = {'U','n','i','t','e','d',' ','S','t','a','t','e','s',0}; + static const WCHAR dummyW[] = {'d','u','m','m','y',0}; + static const WCHAR enusW[] = {'e','n','-','U','S',0}; + static const WCHAR usaW[] = {'U','S','A',0}; + static const WCHAR enuW[] = {'E','N','U',0}; + const struct neutralsublang_name_t *ptr = neutralsublang_names; + DWORD val; + + ok(ret == lstrlenW(bufferW)+1, "got %d\n", ret); + ok(!lstrcmpW(bufferW, enW), "got %s\n", wine_dbgstr_w(bufferW)); + + SetLastError(0xdeadbeef); + ret = pGetLocaleInfoEx(enW, LOCALE_SNAME, bufferW, 2); + ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "got %d, %d\n", ret, GetLastError()); + + SetLastError(0xdeadbeef); + ret = pGetLocaleInfoEx(enW, LOCALE_SNAME, NULL, 0); + ok(ret == 3 && GetLastError() == 0xdeadbeef, "got %d, %d\n", ret, GetLastError()); + + ret = pGetLocaleInfoEx(enusW, LOCALE_SNAME, bufferW, sizeof(bufferW)/sizeof(WCHAR)); + ok(ret == lstrlenW(bufferW)+1, "got %d\n", ret); + ok(!lstrcmpW(bufferW, enusW), "got %s\n", wine_dbgstr_w(bufferW)); + + ret = pGetLocaleInfoEx(enW, LOCALE_SABBREVCTRYNAME, bufferW, sizeof(bufferW)/sizeof(WCHAR)); + ok(ret == lstrlenW(bufferW)+1, "got %d\n", ret); + ok(!lstrcmpW(bufferW, usaW), "got %s\n", wine_dbgstr_w(bufferW)); + + ret = pGetLocaleInfoEx(enW, LOCALE_SABBREVLANGNAME, bufferW, sizeof(bufferW)/sizeof(WCHAR)); + ok(ret == lstrlenW(bufferW)+1, "got %d\n", ret); + ok(!lstrcmpW(bufferW, enuW), "got %s\n", wine_dbgstr_w(bufferW)); + + ret = pGetLocaleInfoEx(enW, LOCALE_SCOUNTRY, bufferW, sizeof(bufferW)/sizeof(WCHAR)); + ok(ret == lstrlenW(bufferW)+1, "got %d\n", ret); + ok(!lstrcmpW(bufferW, statesW), "got %s\n", wine_dbgstr_w(bufferW)); + + bufferW[0] = 0; + SetLastError(0xdeadbeef); + ret = pGetLocaleInfoEx(dummyW, LOCALE_SNAME, bufferW, sizeof(bufferW)/sizeof(WCHAR)); + ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER, "got %d, error %d\n", ret, GetLastError()); + + while (*ptr->name) + { + val = 0; + pGetLocaleInfoEx(ptr->name, LOCALE_ILANGUAGE|LOCALE_RETURN_NUMBER, (WCHAR*)&val, sizeof(val)/sizeof(WCHAR)); + if (ptr->todo) + todo_wine + ok(val == ptr->lcid, "%s: got wrong lcid 0x%04x, expected 0x%04x\n", wine_dbgstr_w(ptr->name), val, ptr->lcid); + else + ok(val == ptr->lcid, "%s: got wrong lcid 0x%04x, expected 0x%04x\n", wine_dbgstr_w(ptr->name), val, ptr->lcid); + bufferW[0] = 0; + ret = pGetLocaleInfoEx(ptr->name, LOCALE_SNAME, bufferW, sizeof(bufferW)/sizeof(WCHAR)); + ok(ret == lstrlenW(bufferW)+1, "%s: got ret value %d\n", wine_dbgstr_w(ptr->name), ret); + ok(!lstrcmpW(bufferW, ptr->name), "%s: got wrong LOCALE_SNAME %s\n", wine_dbgstr_w(ptr->name), wine_dbgstr_w(bufferW)); + ptr++; + } + } +} + +static void test_IsValidLocaleName(void) +{ + static const WCHAR enusW[] = {'e','n','-','U','S',0}; + static const WCHAR zzW[] = {'z','z',0}; + static const WCHAR zzzzW[] = {'z','z','-','Z','Z',0}; + BOOL ret; + + if (!pIsValidLocaleName) + { + win_skip("IsValidLocaleName not supported\n"); + return; + } + + ret = pIsValidLocaleName(enusW); + ok(ret, "IsValidLocaleName failed\n"); + ret = pIsValidLocaleName(zzW); + ok(!ret, "IsValidLocaleName should have failed\n"); + ret = pIsValidLocaleName(zzzzW); + ok(!ret, "IsValidLocaleName should have failed\n"); +} + +static void test_CompareStringOrdinal(void) +{ + INT ret; + WCHAR test1[] = { 't','e','s','t',0 }; + WCHAR test2[] = { 'T','e','S','t',0 }; + WCHAR test3[] = { 't','e','s','t','3',0 }; + WCHAR null1[] = { 'a',0,'a',0 }; + WCHAR null2[] = { 'a',0,'b',0 }; + WCHAR bills1[] = { 'b','i','l','l','\'','s',0 }; + WCHAR bills2[] = { 'b','i','l','l','s',0 }; + WCHAR coop1[] = { 'c','o','-','o','p',0 }; + WCHAR coop2[] = { 'c','o','o','p',0 }; + WCHAR nonascii1[] = { 0x0102,0 }; + WCHAR nonascii2[] = { 0x0201,0 }; + + if (!pCompareStringOrdinal) + { + win_skip("CompareStringOrdinal not supported\n"); + return; + } + + /* Check errors */ + SetLastError(0xdeadbeef); + ret = pCompareStringOrdinal(NULL, 0, NULL, 0, FALSE); + ok(!ret, "Got %u, expected 0\n", ret); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got %x, expected %x\n", GetLastError(), ERROR_INVALID_PARAMETER); + SetLastError(0xdeadbeef); + ret = pCompareStringOrdinal(test1, -1, NULL, 0, FALSE); + ok(!ret, "Got %u, expected 0\n", ret); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got %x, expected %x\n", GetLastError(), ERROR_INVALID_PARAMETER); + SetLastError(0xdeadbeef); + ret = pCompareStringOrdinal(NULL, 0, test1, -1, FALSE); + ok(!ret, "Got %u, expected 0\n", ret); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got %x, expected %x\n", GetLastError(), ERROR_INVALID_PARAMETER); + + /* Check case */ + ret = pCompareStringOrdinal(test1, -1, test1, -1, FALSE); + ok(ret == CSTR_EQUAL, "Got %u, expected %u\n", ret, CSTR_EQUAL); + ret = pCompareStringOrdinal(test1, -1, test2, -1, FALSE); + ok(ret == CSTR_GREATER_THAN, "Got %u, expected %u\n", ret, CSTR_GREATER_THAN); + ret = pCompareStringOrdinal(test2, -1, test1, -1, FALSE); + ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN); + ret = pCompareStringOrdinal(test1, -1, test2, -1, TRUE); + ok(ret == CSTR_EQUAL, "Got %u, expected %u\n", ret, CSTR_EQUAL); + + /* Check different sizes */ + ret = pCompareStringOrdinal(test1, 3, test2, -1, TRUE); + ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN); + ret = pCompareStringOrdinal(test1, -1, test2, 3, TRUE); + ok(ret == CSTR_GREATER_THAN, "Got %u, expected %u\n", ret, CSTR_GREATER_THAN); + + /* Check null character */ + ret = pCompareStringOrdinal(null1, 3, null2, 3, FALSE); + ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN); + ret = pCompareStringOrdinal(null1, 3, null2, 3, TRUE); + ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN); + ret = pCompareStringOrdinal(test1, 5, test3, 5, FALSE); + ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN); + ret = pCompareStringOrdinal(test1, 4, test1, 5, FALSE); + ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN); + + /* Check ordinal behaviour */ + ret = pCompareStringOrdinal(bills1, -1, bills2, -1, FALSE); + ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN); + ret = pCompareStringOrdinal(coop2, -1, coop1, -1, FALSE); + ok(ret == CSTR_GREATER_THAN, "Got %u, expected %u\n", ret, CSTR_GREATER_THAN); + ret = pCompareStringOrdinal(nonascii1, -1, nonascii2, -1, FALSE); + ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN); + ret = pCompareStringOrdinal(nonascii1, -1, nonascii2, -1, TRUE); + ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN); +} + START_TEST(locale) { InitFunctionPointers(); @@ -3148,6 +3563,7 @@ START_TEST(locale) test_EnumDateFormatsA(); test_GetLocaleInfoA(); test_GetLocaleInfoW(); + test_GetLocaleInfoEx(); test_GetTimeFormatA(); test_GetDateFormatA(); test_GetDateFormatW(); @@ -3156,7 +3572,8 @@ START_TEST(locale) test_CompareStringA(); test_LCMapStringA(); test_LCMapStringW(); - test_LocaleNames(); + test_LCMapStringEx(); + test_LocaleNameToLCID(); test_FoldStringA(); test_FoldStringW(); test_ConvertDefaultLocale(); @@ -3170,6 +3587,8 @@ START_TEST(locale) test_IdnToNameprepUnicode(); test_IdnToAscii(); test_IdnToUnicode(); + test_IsValidLocaleName(); + test_CompareStringOrdinal(); /* this requires collation table patch to make it MS compatible */ if (0) test_sorting(); } diff --git a/rostests/winetests/kernel32/module.c b/rostests/winetests/kernel32/module.c index f88c8cbcdbf..e8d51e16e79 100755 --- a/rostests/winetests/kernel32/module.c +++ b/rostests/winetests/kernel32/module.c @@ -476,12 +476,16 @@ static void testGetDllDirectory(void) ok(bufferW[0] == 0 || /* XP, 2003 */ broken(bufferW[0] == 'A'), "i=%d, Buffer overflow\n", i); - /* no buffer, but too short length */ - ret = pGetDllDirectoryA(length, NULL); - ok(ret == length + 1, "i=%d, Expected %u, got %u\n", i, length + 1, ret); + if (0) + { + /* crashes on win8 */ + /* no buffer, but too short length */ + ret = pGetDllDirectoryA(length, NULL); + ok(ret == length + 1, "i=%d, Expected %u, got %u\n", i, length + 1, ret); - ret = pGetDllDirectoryW(length, NULL); - ok(ret == length + 1, "i=%d, Expected %u, got %u\n", i, length + 1, ret); + ret = pGetDllDirectoryW(length, NULL); + ok(ret == length + 1, "i=%d, Expected %u, got %u\n", i, length + 1, ret); + } } /* unset whatever we did so following tests won't be affected */ diff --git a/rostests/winetests/kernel32/pipe.c b/rostests/winetests/kernel32/pipe.c index 3484c62c791..923c0e7adc3 100755 --- a/rostests/winetests/kernel32/pipe.c +++ b/rostests/winetests/kernel32/pipe.c @@ -798,6 +798,113 @@ static DWORD CALLBACK serverThreadMain4(LPVOID arg) return 0; } +static int completion_called; +static DWORD completion_errorcode; +static DWORD completion_num_bytes; +static LPOVERLAPPED completion_lpoverlapped; + +static VOID WINAPI completion_routine(DWORD errorcode, DWORD num_bytes, LPOVERLAPPED lpoverlapped) +{ + completion_called++; + completion_errorcode = errorcode; + completion_num_bytes = num_bytes; + completion_lpoverlapped = lpoverlapped; + SetEvent(lpoverlapped->hEvent); +} + +/** Trivial byte echo server - uses ReadFileEx/WriteFileEx */ +static DWORD CALLBACK serverThreadMain5(LPVOID arg) +{ + int i; + HANDLE hEvent; + + trace("serverThreadMain5\n"); + /* Set up a simple echo server */ + hnp = CreateNamedPipe(PIPENAME "serverThreadMain5", PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_BYTE | PIPE_WAIT, + /* nMaxInstances */ 1, + /* nOutBufSize */ 1024, + /* nInBufSize */ 1024, + /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT, + /* lpSecurityAttrib */ NULL); + ok(hnp != INVALID_HANDLE_VALUE, "CreateNamedPipe failed\n"); + + hEvent = CreateEvent(NULL, /* security attribute */ + TRUE, /* manual reset event */ + FALSE, /* initial state */ + NULL); /* name */ + ok(hEvent != NULL, "CreateEvent\n"); + + for (i = 0; i < NB_SERVER_LOOPS; i++) { + char buf[512]; + DWORD readden; + DWORD success; + OVERLAPPED oOverlap; + DWORD err; + + memset(&oOverlap, 0, sizeof(oOverlap)); + oOverlap.hEvent = hEvent; + + /* Wait for client to connect */ + trace("Server calling ConnectNamedPipe...\n"); + success = ConnectNamedPipe(hnp, NULL); + err = GetLastError(); + ok(success || (err == ERROR_PIPE_CONNECTED), "ConnectNamedPipe failed: %d\n", err); + trace("ConnectNamedPipe operation complete.\n"); + + /* Echo bytes once */ + memset(buf, 0, sizeof(buf)); + + trace("Server reading...\n"); + completion_called = 0; + ResetEvent(hEvent); + success = ReadFileEx(hnp, buf, sizeof(buf), &oOverlap, completion_routine); + trace("Server ReadFileEx returned...\n"); + ok(success, "ReadFileEx failed, err=%i\n", GetLastError()); + ok(completion_called == 0, "completion routine called before ReadFileEx return\n"); + trace("ReadFileEx returned.\n"); + if (success) { + DWORD ret; + do { + ret = WaitForSingleObjectEx(hEvent, INFINITE, TRUE); + } while (ret == WAIT_IO_COMPLETION); + ok(ret == 0, "wait ReadFileEx returned %x\n", ret); + } + ok(completion_called == 1, "completion routine called %i times\n", completion_called); + ok(completion_errorcode == ERROR_SUCCESS, "completion routine got error %d\n", completion_errorcode); + ok(completion_num_bytes != 0, "read 0 bytes\n"); + ok(completion_lpoverlapped == &oOverlap, "got wrong overlapped pointer %p\n", completion_lpoverlapped); + readden = completion_num_bytes; + trace("Server done reading.\n"); + + trace("Server writing...\n"); + completion_called = 0; + ResetEvent(hEvent); + success = WriteFileEx(hnp, buf, readden, &oOverlap, completion_routine); + trace("Server WriteFileEx returned...\n"); + ok(success, "WriteFileEx failed, err=%i\n", GetLastError()); + ok(completion_called == 0, "completion routine called before ReadFileEx return\n"); + trace("overlapped WriteFile returned.\n"); + if (success) { + DWORD ret; + do { + ret = WaitForSingleObjectEx(hEvent, INFINITE, TRUE); + } while (ret == WAIT_IO_COMPLETION); + ok(ret == 0, "wait WriteFileEx returned %x\n", ret); + } + trace("Server done writing.\n"); + ok(completion_called == 1, "completion routine called %i times\n", completion_called); + ok(completion_errorcode == ERROR_SUCCESS, "completion routine got error %d\n", completion_errorcode); + ok(completion_num_bytes == readden, "read %i bytes wrote %i\n", readden, completion_num_bytes); + ok(completion_lpoverlapped == &oOverlap, "got wrong overlapped pointer %p\n", completion_lpoverlapped); + + /* finish this connection, wait for next one */ + ok(FlushFileBuffers(hnp), "FlushFileBuffers\n"); + ok(DisconnectNamedPipe(hnp), "DisconnectNamedPipe\n"); + } + return 0; +} + static void exercizeServer(const char *pipename, HANDLE serverThread) { int i; @@ -891,6 +998,12 @@ static void test_NamedPipe_2(void) ok(serverThread != NULL, "CreateThread failed: %d\n", GetLastError()); exercizeServer(PIPENAME "serverThreadMain4", serverThread); + /* Try server #5 */ + SetLastError(0xdeadbeef); + serverThread = CreateThread(NULL, 0, serverThreadMain5, 0, 0, &serverThreadId); + ok(serverThread != NULL, "CreateThread failed: %d\n", GetLastError()); + exercizeServer(PIPENAME "serverThreadMain5", serverThread); + ok(SetEvent( alarm_event ), "SetEvent\n"); CloseHandle( alarm_event ); trace("test_NamedPipe_2 returning\n"); @@ -1637,6 +1750,116 @@ static void test_NamedPipeHandleState(void) CloseHandle(server); } +static void test_readfileex_pending(void) +{ + HANDLE server, client, event; + BOOL ret; + DWORD err, wait, num_bytes; + OVERLAPPED overlapped; + char read_buf[1024]; + char write_buf[1024]; + const char test_string[] = "test"; + int i; + + server = CreateNamedPipe(PIPENAME, FILE_FLAG_OVERLAPPED | PIPE_ACCESS_DUPLEX, + /* dwOpenMode */ PIPE_TYPE_BYTE | PIPE_WAIT, + /* nMaxInstances */ 1, + /* nOutBufSize */ 1024, + /* nInBufSize */ 1024, + /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT, + /* lpSecurityAttrib */ NULL); + ok(server != INVALID_HANDLE_VALUE, "cf failed\n"); + + event = CreateEventA(NULL, TRUE, FALSE, NULL); + ok(event != NULL, "CreateEventA failed\n"); + + memset(&overlapped, 0, sizeof(overlapped)); + overlapped.hEvent = event; + + ret = ConnectNamedPipe(server, &overlapped); + err = GetLastError(); + ok(ret == FALSE, "ConnectNamedPipe succeeded\n"); + ok(err == ERROR_IO_PENDING, "ConnectNamedPipe set error %i\n", err); + + wait = WaitForSingleObject(event, 0); + ok(wait == WAIT_TIMEOUT, "WaitForSingleObject returned %x\n", wait); + + client = CreateFileA(PIPENAME, GENERIC_READ|GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, 0, NULL); + ok(client != INVALID_HANDLE_VALUE, "cf failed\n"); + + wait = WaitForSingleObject(event, 0); + ok(wait == WAIT_OBJECT_0, "WaitForSingleObject returned %x\n", wait); + + /* Start a read that can't complete immediately. */ + completion_called = 0; + ResetEvent(event); + ret = ReadFileEx(server, read_buf, sizeof(read_buf), &overlapped, completion_routine); + ok(ret == TRUE, "ReadFileEx failed, err=%i\n", GetLastError()); + ok(completion_called == 0, "completion routine called before ReadFileEx returned\n"); + + ret = WriteFile(client, test_string, strlen(test_string), &num_bytes, NULL); + ok(ret == TRUE, "WriteFile failed\n"); + ok(num_bytes == strlen(test_string), "only %i bytes written\n", num_bytes); + + ok(completion_called == 0, "completion routine called during WriteFile\n"); + + wait = WaitForSingleObjectEx(event, 0, TRUE); + ok(wait == WAIT_IO_COMPLETION || wait == WAIT_OBJECT_0, "WaitForSingleObjectEx returned %x\n", wait); + + ok(completion_called == 1, "completion not called after writing pipe\n"); + ok(completion_errorcode == 0, "completion called with error %x\n", completion_errorcode); + ok(completion_num_bytes == strlen(test_string), "ReadFileEx returned only %d bytes\n", completion_num_bytes); + ok(completion_lpoverlapped == &overlapped, "completion called with wrong overlapped pointer\n"); + ok(!memcmp(test_string, read_buf, strlen(test_string)), "ReadFileEx read wrong bytes\n"); + + /* Make writes until the pipe is full and the write fails */ + memset(write_buf, 0xaa, sizeof(write_buf)); + for (i=0; i<256; i++) + { + completion_called = 0; + ResetEvent(event); + ret = WriteFileEx(server, write_buf, sizeof(write_buf), &overlapped, completion_routine); + err = GetLastError(); + + ok(completion_called == 0, "completion routine called during WriteFileEx\n"); + + wait = WaitForSingleObjectEx(event, 0, TRUE); + + if (wait == WAIT_TIMEOUT) + /* write couldn't complete immediately, presumably the pipe is full */ + break; + + ok(wait == WAIT_IO_COMPLETION || wait == WAIT_OBJECT_0, "WaitForSingleObject returned %x\n", wait); + + ok(ret == TRUE, "WriteFileEx failed, err=%i\n", err); + ok(completion_errorcode == 0, "completion called with error %x\n", completion_errorcode); + ok(completion_lpoverlapped == &overlapped, "completion called with wrong overlapped pointer\n"); + } + + ok(ret == TRUE, "WriteFileEx failed, err=%i\n", err); + ok(completion_called == 0, "completion routine called but wait timed out\n"); + ok(completion_errorcode == 0, "completion called with error %x\n", completion_errorcode); + ok(completion_lpoverlapped == &overlapped, "completion called with wrong overlapped pointer\n"); + + /* free up some space in the pipe */ + ret = ReadFile(client, read_buf, sizeof(read_buf), &num_bytes, NULL); + ok(ret == TRUE, "ReadFile failed\n"); + + ok(completion_called == 0, "completion routine called during ReadFile\n"); + + wait = WaitForSingleObjectEx(event, 0, TRUE); + ok(wait == WAIT_IO_COMPLETION || wait == WAIT_OBJECT_0, "WaitForSingleObject returned %x\n", wait); + + ok(completion_called == 1, "completion routine not called\n"); + ok(completion_errorcode == 0, "completion called with error %x\n", completion_errorcode); + ok(completion_lpoverlapped == &overlapped, "completion called with wrong overlapped pointer\n"); + + CloseHandle(client); + CloseHandle(server); + CloseHandle(event); +} + START_TEST(pipe) { HMODULE hmod; @@ -1656,4 +1879,5 @@ START_TEST(pipe) test_impersonation(); test_overlapped(); test_NamedPipeHandleState(); + test_readfileex_pending(); } diff --git a/rostests/winetests/kernel32/process.c b/rostests/winetests/kernel32/process.c index 8aadf2a6e5f..be0e948c03d 100755 --- a/rostests/winetests/kernel32/process.c +++ b/rostests/winetests/kernel32/process.c @@ -298,18 +298,6 @@ static void doChild(const char* file, const char* option) childPrintf(hFile, "argvA%d=%s\n", i, encodeA(myARGV[i])); } childPrintf(hFile, "CommandLineA=%s\n", encodeA(GetCommandLineA())); - -#if 0 - int argcW; - WCHAR** argvW; - - /* this is part of shell32... and should be tested there */ - argvW = CommandLineToArgvW(GetCommandLineW(), &argcW); - for (i = 0; i < argcW; i++) - { - childPrintf(hFile, "argvW%d=%s\n", i, encodeW(argvW[i])); - } -#endif childPrintf(hFile, "CommandLineW=%s\n\n", encodeW(GetCommandLineW())); /* output of environment (Ansi) */ diff --git a/rostests/winetests/kernel32/sync.c b/rostests/winetests/kernel32/sync.c index e875c795059..5b68c8c5324 100755 --- a/rostests/winetests/kernel32/sync.c +++ b/rostests/winetests/kernel32/sync.c @@ -18,7 +18,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ -#define _WIN32_WINNT 0x502 +#define _WIN32_WINNT 0x600 #include #include #include @@ -37,6 +37,15 @@ static BOOL (WINAPI *pDeleteTimerQueueTimer)(HANDLE, HANDLE, HANDLE); static HANDLE (WINAPI *pOpenWaitableTimerA)(DWORD,BOOL,LPCSTR); static HANDLE (WINAPI *pCreateMemoryResourceNotification)(MEMORY_RESOURCE_NOTIFICATION_TYPE); static BOOL (WINAPI *pQueryMemoryResourceNotification)(HANDLE, PBOOL); +static VOID (WINAPI *pInitOnceInitialize)(PINIT_ONCE); +static BOOL (WINAPI *pInitOnceExecuteOnce)(PINIT_ONCE,PINIT_ONCE_FN,PVOID,LPVOID*); +static BOOL (WINAPI *pInitOnceBeginInitialize)(PINIT_ONCE,DWORD,BOOL*,LPVOID*); +static BOOL (WINAPI *pInitOnceComplete)(PINIT_ONCE,DWORD,LPVOID); + +static VOID (WINAPI *pInitializeConditionVariable)(PCONDITION_VARIABLE); +static BOOL (WINAPI *pSleepConditionVariableCS)(PCONDITION_VARIABLE,PCRITICAL_SECTION,DWORD); +static VOID (WINAPI *pWakeAllConditionVariable)(PCONDITION_VARIABLE); +static VOID (WINAPI *pWakeConditionVariable)(PCONDITION_VARIABLE); static void test_signalandwait(void) { @@ -704,11 +713,11 @@ static void CALLBACK timer_queue_cb6(PVOID p, BOOLEAN timedOut) static void test_timer_queue(void) { - HANDLE q, t1, t2, t3, t4, t5; - int n1, n2, n3, n4, n5; + HANDLE q, t0, t1, t2, t3, t4, t5; + int n0, n1, n2, n3, n4, n5; struct timer_queue_data1 d1, d2, d3, d4; HANDLE e, et1, et2; - BOOL ret; + BOOL ret, ret0; if (!pChangeTimerQueueTimer || !pCreateTimerQueue || !pCreateTimerQueueTimer || !pDeleteTimerQueueEx || !pDeleteTimerQueueTimer) @@ -731,6 +740,18 @@ static void test_timer_queue(void) q = pCreateTimerQueue(); ok(q != NULL, "CreateTimerQueue\n"); + /* Not called. */ + t0 = NULL; + n0 = 0; + ret = pCreateTimerQueueTimer(&t0, q, timer_queue_cb1, &n0, 0, + 300, 0); + ok(ret, "CreateTimerQueueTimer\n"); + ok(t0 != NULL, "CreateTimerQueueTimer\n"); + ret0 = pDeleteTimerQueueTimer(q, t0, NULL); + ok((!ret0 && GetLastError() == ERROR_IO_PENDING) || + broken(ret0), /* Win 2000 & XP & 2003 */ + "DeleteTimerQueueTimer ret=%d le=%u\n", ret0, GetLastError()); + /* Called once. */ t1 = NULL; n1 = 0; @@ -784,10 +805,12 @@ static void test_timer_queue(void) ret = pDeleteTimerQueueEx(q, INVALID_HANDLE_VALUE); ok(ret, "DeleteTimerQueueEx\n"); - ok(n1 == 1, "Timer callback 1\n"); - ok(n2 < n3, "Timer callback 2 should be much slower than 3\n"); - ok(n4 == 0, "Timer callback 4\n"); - ok(n5 == 1, "Timer callback 5\n"); + todo_wine + ok(n0 == 1 || broken(ret0 && n0 == 0), "Timer callback 0 expected 1 got %d\n", n0); + ok(n1 == 1, "Timer callback 1 expected 1 got %d\n", n1); + ok(n2 < n3, "Timer callback 2 & 3 expected %d < %d\n", n2, n3); + ok(n4 == 0, "Timer callback 4 expected 0 got %d\n", n4); + ok(n5 == 1, "Timer callback 5 expected 1 got %d\n", n5); /* Test synchronous deletion of the timer/queue with event trigger. */ e = CreateEvent(NULL, TRUE, FALSE, NULL); @@ -1121,6 +1144,211 @@ static void test_WaitForMultipleObjects(void) if (maxevents[i]) CloseHandle(maxevents[i]); } +static BOOL g_initcallback_ret, g_initcallback_called; +static void *g_initctxt; + +static BOOL CALLBACK initonce_callback(INIT_ONCE *initonce, void *parameter, void **ctxt) +{ + g_initcallback_called = TRUE; + /* zero bit set means here that initialization is taking place - initialization locked */ + ok(g_initctxt == *ctxt, "got wrong context value %p, expected %p\n", *ctxt, g_initctxt); + ok(initonce->Ptr == (void*)0x1, "got %p\n", initonce->Ptr); + ok(parameter == (void*)0xdeadbeef, "got wrong parameter\n"); + return g_initcallback_ret; +} + +static void test_initonce(void) +{ + INIT_ONCE initonce; + BOOL ret, pending; + + if (!pInitOnceInitialize || !pInitOnceExecuteOnce) + { + skip("one-time initialization API not supported\n"); + return; + } + + /* blocking initialization with callback */ + initonce.Ptr = (void*)0xdeadbeef; + pInitOnceInitialize(&initonce); + ok(initonce.Ptr == NULL, "got %p\n", initonce.Ptr); + + /* initialisation completed successfully */ + g_initcallback_ret = TRUE; + g_initctxt = NULL; + ret = pInitOnceExecuteOnce(&initonce, initonce_callback, (void*)0xdeadbeef, &g_initctxt); + ok(ret, "got wrong ret value %d\n", ret); + ok(initonce.Ptr == (void*)0x2, "got %p\n", initonce.Ptr); + ok(g_initctxt == NULL, "got %p\n", g_initctxt); + ok(g_initcallback_called, "got %d\n", g_initcallback_called); + + /* so it's been called already so won't be called again */ + g_initctxt = NULL; + g_initcallback_called = FALSE; + ret = pInitOnceExecuteOnce(&initonce, initonce_callback, (void*)0xdeadbeef, &g_initctxt); + ok(ret, "got wrong ret value %d\n", ret); + ok(initonce.Ptr == (void*)0x2, "got %p\n", initonce.Ptr); + ok(g_initctxt == NULL, "got %p\n", g_initctxt); + ok(!g_initcallback_called, "got %d\n", g_initcallback_called); + + pInitOnceInitialize(&initonce); + g_initcallback_called = FALSE; + /* 2 lower order bits should never be used, you'll get a crash in result */ + g_initctxt = (void*)0xFFFFFFF0; + ret = pInitOnceExecuteOnce(&initonce, initonce_callback, (void*)0xdeadbeef, &g_initctxt); + ok(ret, "got wrong ret value %d\n", ret); + ok(initonce.Ptr == (void*)0xFFFFFFF2, "got %p\n", initonce.Ptr); + ok(g_initctxt == (void*)0xFFFFFFF0, "got %p\n", g_initctxt); + ok(g_initcallback_called, "got %d\n", g_initcallback_called); + + /* callback failed */ + g_initcallback_ret = FALSE; + g_initcallback_called = FALSE; + g_initctxt = NULL; + pInitOnceInitialize(&initonce); + ret = pInitOnceExecuteOnce(&initonce, initonce_callback, (void*)0xdeadbeef, &g_initctxt); + ok(!ret, "got wrong ret value %d\n", ret); + ok(initonce.Ptr == NULL, "got %p\n", initonce.Ptr); + ok(g_initctxt == NULL, "got %p\n", g_initctxt); + ok(g_initcallback_called, "got %d\n", g_initcallback_called); + + /* blocking initialzation without a callback */ + pInitOnceInitialize(&initonce); + g_initctxt = NULL; + pending = FALSE; + ret = pInitOnceBeginInitialize(&initonce, 0, &pending, &g_initctxt); + ok(ret, "got wrong ret value %d\n", ret); + ok(pending, "got %d\n", pending); + ok(initonce.Ptr == (void*)1, "got %p\n", initonce.Ptr); + ok(g_initctxt == NULL, "got %p\n", g_initctxt); + /* another attempt to begin initialization with block a single thread */ + + g_initctxt = NULL; + pending = 0xf; + ret = pInitOnceBeginInitialize(&initonce, INIT_ONCE_CHECK_ONLY, &pending, &g_initctxt); + ok(!ret, "got wrong ret value %d\n", ret); + ok(pending == 0xf, "got %d\n", pending); + ok(initonce.Ptr == (void*)1, "got %p\n", initonce.Ptr); + ok(g_initctxt == NULL, "got %p\n", g_initctxt); + + g_initctxt = (void*)0xdeadbee0; + ret = pInitOnceComplete(&initonce, INIT_ONCE_INIT_FAILED, g_initctxt); + ok(!ret, "got wrong ret value %d\n", ret); + ok(initonce.Ptr == (void*)1, "got %p\n", initonce.Ptr); + + /* once failed already */ + g_initctxt = (void*)0xdeadbee0; + ret = pInitOnceComplete(&initonce, 0, g_initctxt); + ok(ret, "got wrong ret value %d\n", ret); + ok(initonce.Ptr == (void*)0xdeadbee2, "got %p\n", initonce.Ptr); +} + +static CONDITION_VARIABLE buffernotempty,buffernotfull; +static CRITICAL_SECTION buffercrit; +static BOOL condvar_stop = FALSE, condvar_sleeperr = FALSE; +static LONG bufferlen,totalproduced,totalconsumed; +static LONG condvar_producer_sleepcnt,condvar_consumer_sleepcnt; + +#define BUFFER_SIZE 10 + +static DWORD WINAPI condvar_producer(LPVOID x) { + while (1) { + Sleep(rand() % 10); + + EnterCriticalSection(&buffercrit); + while ((bufferlen == BUFFER_SIZE) && !condvar_stop) { + condvar_producer_sleepcnt++; + if (!pSleepConditionVariableCS(&buffernotfull, &buffercrit, 2000)) + condvar_sleeperr = TRUE; + } + if (condvar_stop) { + LeaveCriticalSection(&buffercrit); + break; + } + bufferlen++; + totalproduced++; + LeaveCriticalSection(&buffercrit); + pWakeConditionVariable(&buffernotempty); + } + return 0; +} + +static DWORD WINAPI condvar_consumer(LPVOID x) { + DWORD *cnt = (DWORD*)x; + + while (1) { + EnterCriticalSection(&buffercrit); + while ((bufferlen == 0) && !condvar_stop) { + condvar_consumer_sleepcnt++; + if (!pSleepConditionVariableCS (&buffernotempty, &buffercrit, 2000)) + condvar_sleeperr = TRUE; + } + if (condvar_stop && (bufferlen == 0)) { + LeaveCriticalSection(&buffercrit); + break; + } + bufferlen--; + totalconsumed++; + (*cnt)++; + LeaveCriticalSection(&buffercrit); + pWakeConditionVariable(&buffernotfull); + Sleep(rand() % 10); + } + return 0; +} + +static void test_condvars(void) +{ + HANDLE hp1,hp2,hc1,hc2; + DWORD dummy; + DWORD cnt1,cnt2; + + if (!pInitializeConditionVariable) { + /* function is not yet in XP, only in newer Windows */ + /* and not yet implemented in Wine for some days/weeks */ + todo_wine win_skip("no condition variable support.\n"); + return; + } + + /* Implement a producer / consumer scheme with non-full / non-empty triggers */ + pInitializeConditionVariable(&buffernotfull); + pInitializeConditionVariable(&buffernotempty); + InitializeCriticalSection(&buffercrit); + bufferlen = totalproduced = totalconsumed = cnt1 = cnt2 = 0; + + hp1 = CreateThread(NULL, 0, condvar_producer, NULL, 0, &dummy); + hp2 = CreateThread(NULL, 0, condvar_producer, NULL, 0, &dummy); + hc1 = CreateThread(NULL, 0, condvar_consumer, (PVOID)&cnt1, 0, &dummy); + hc2 = CreateThread(NULL, 0, condvar_consumer, (PVOID)&cnt2, 0, &dummy); + + /* Limit run to 0.5 seconds. */ + Sleep(500); + + /* tear down start */ + condvar_stop = TRUE; + + /* final wake up call */ + pWakeAllConditionVariable (&buffernotfull); + pWakeAllConditionVariable (&buffernotempty); + + WaitForSingleObject(hp1, 1000); + WaitForSingleObject(hp2, 1000); + WaitForSingleObject(hc1, 1000); + WaitForSingleObject(hc2, 1000); + + ok(totalconsumed == totalproduced, + "consumed %d != produced %d\n", totalconsumed, totalproduced); + ok (!condvar_sleeperr, "error occurred during SleepConditionVariableCS\n"); + + /* Checking cnt1 - cnt2 for non-0 would be not good, the case where + * one consumer does not get anything to do is possible. */ + trace("produced %d, c1 %d, c2 %d\n", totalproduced, cnt1, cnt2); + /* The sleeps of the producer or consumer should not go above 100* produced count, + * otherwise the implementation does not sleep correctly. But yet again, this is + * not hard defined. */ + trace("producer sleep %d, consumer sleep %d\n", condvar_producer_sleepcnt, condvar_consumer_sleepcnt); +} + START_TEST(sync) { HMODULE hdll = GetModuleHandle("kernel32"); @@ -1133,6 +1361,14 @@ START_TEST(sync) pOpenWaitableTimerA = (void*)GetProcAddress(hdll, "OpenWaitableTimerA"); pCreateMemoryResourceNotification = (void *)GetProcAddress(hdll, "CreateMemoryResourceNotification"); pQueryMemoryResourceNotification = (void *)GetProcAddress(hdll, "QueryMemoryResourceNotification"); + pInitOnceInitialize = (void *)GetProcAddress(hdll, "InitOnceInitialize"); + pInitOnceExecuteOnce = (void *)GetProcAddress(hdll, "InitOnceExecuteOnce"); + pInitOnceBeginInitialize = (void *)GetProcAddress(hdll, "InitOnceBeginInitialize"); + pInitOnceComplete = (void *)GetProcAddress(hdll, "InitOnceComplete"); + pInitializeConditionVariable = (void *)GetProcAddress(hdll, "InitializeConditionVariable"); + pSleepConditionVariableCS = (void *)GetProcAddress(hdll, "SleepConditionVariableCS"); + pWakeAllConditionVariable = (void *)GetProcAddress(hdll, "WakeAllConditionVariable"); + pWakeConditionVariable = (void *)GetProcAddress(hdll, "WakeConditionVariable"); test_signalandwait(); test_mutex(); @@ -1144,4 +1380,6 @@ START_TEST(sync) test_timer_queue(); test_WaitForSingleObject(); test_WaitForMultipleObjects(); + test_initonce(); + test_condvars(); } diff --git a/rostests/winetests/kernel32/thread.c b/rostests/winetests/kernel32/thread.c index 19726af54b2..76a7a33184f 100755 --- a/rostests/winetests/kernel32/thread.c +++ b/rostests/winetests/kernel32/thread.c @@ -961,16 +961,19 @@ static void test_SetThreadContext(void) ctx.ContextFlags = CONTEXT_FULL; SetLastError(0xdeadbeef); ret = GetThreadContext( thread, &ctx ); - ok( !ret, "GetThreadContext succeeded\n" ); - ok( GetLastError() == ERROR_GEN_FAILURE || broken(GetLastError() == ERROR_INVALID_HANDLE), /* win2k */ - "wrong error %u\n", GetLastError() ); + ok( (!ret && (GetLastError() == ERROR_GEN_FAILURE)) || + (!ret && broken(GetLastError() == ERROR_INVALID_HANDLE)) || /* win2k */ + broken(ret), /* 32bit application on NT 5.x 64bit */ + "got %d with %u (expected FALSE with ERROR_GEN_FAILURE)\n", + ret, GetLastError() ); SetLastError(0xdeadbeef); ret = SetThreadContext( thread, &ctx ); - ok( !ret, "SetThreadContext succeeded\n" ); - ok( GetLastError() == ERROR_GEN_FAILURE || GetLastError() == ERROR_ACCESS_DENIED || - broken(GetLastError() == ERROR_INVALID_HANDLE), /* win2k */ - "wrong error %u\n", GetLastError() ); + ok( (!ret && ((GetLastError() == ERROR_GEN_FAILURE) || (GetLastError() == ERROR_ACCESS_DENIED))) || + (!ret && broken(GetLastError() == ERROR_INVALID_HANDLE)) || /* win2k */ + broken(ret), /* 32bit application on NT 5.x 64bit */ + "got %d with %u (expected FALSE with ERROR_GEN_FAILURE or ERROR_ACCESS_DENIED)\n", + ret, GetLastError() ); CloseHandle( thread ); } diff --git a/rostests/winetests/kernel32/timer.c b/rostests/winetests/kernel32/timer.c index 8c233f0d449..a06217a1886 100755 --- a/rostests/winetests/kernel32/timer.c +++ b/rostests/winetests/kernel32/timer.c @@ -23,27 +23,24 @@ #include "wine/test.h" #include "winbase.h" -typedef HANDLE (WINAPI *fnCreateWaitableTimerA)( SECURITY_ATTRIBUTES*, BOOL, LPSTR ); -typedef BOOL (WINAPI *fnSetWaitableTimer)(HANDLE, LARGE_INTEGER*, LONG, PTIMERAPCROUTINE, LPVOID, BOOL); - static void test_timer(void) { + HANDLE (WINAPI *pCreateWaitableTimerA)( SECURITY_ATTRIBUTES*, BOOL, LPSTR ); + BOOL (WINAPI *pSetWaitableTimer)(HANDLE, LARGE_INTEGER*, LONG, PTIMERAPCROUTINE, LPVOID, BOOL); HMODULE hker = GetModuleHandle("kernel32"); - fnCreateWaitableTimerA pCreateWaitableTimerA; - fnSetWaitableTimer pSetWaitableTimer; HANDLE handle; BOOL r; LARGE_INTEGER due; - pCreateWaitableTimerA = (fnCreateWaitableTimerA) GetProcAddress( hker, "CreateWaitableTimerA"); + pCreateWaitableTimerA = (void*)GetProcAddress( hker, "CreateWaitableTimerA"); if( !pCreateWaitableTimerA ) { win_skip("CreateWaitableTimerA is not available\n"); return; } - pSetWaitableTimer = (fnSetWaitableTimer) GetProcAddress( hker, "SetWaitableTimer"); + pSetWaitableTimer = (void*)GetProcAddress( hker, "SetWaitableTimer"); if( !pSetWaitableTimer ) { win_skip("SetWaitableTimer is not available\n"); diff --git a/rostests/winetests/kernel32/version.c b/rostests/winetests/kernel32/version.c index 9f9b72e564e..4b9a4f30241 100644 --- a/rostests/winetests/kernel32/version.c +++ b/rostests/winetests/kernel32/version.c @@ -18,31 +18,88 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +/* Needed for PRODUCT_* defines and GetProductInfo() */ +#define _WIN32_WINNT 0x0600 + #include #include "wine/test.h" #include "winbase.h" +static BOOL (WINAPI * pGetProductInfo)(DWORD, DWORD, DWORD, DWORD, DWORD *); static BOOL (WINAPI * pVerifyVersionInfoA)(LPOSVERSIONINFOEXA, DWORD, DWORDLONG); static ULONGLONG (WINAPI * pVerSetConditionMask)(ULONGLONG, DWORD, BYTE); #define KERNEL32_GET_PROC(func) \ - p##func = (void *)GetProcAddress(hKernel32, #func); \ - if(!p##func) trace("GetProcAddress(hKernel32, '%s') failed\n", #func); + p##func = (void *)GetProcAddress(hKernel32, #func); static void init_function_pointers(void) { HMODULE hKernel32; - pVerifyVersionInfoA = NULL; - pVerSetConditionMask = NULL; - hKernel32 = GetModuleHandleA("kernel32.dll"); - assert(hKernel32); + + KERNEL32_GET_PROC(GetProductInfo); KERNEL32_GET_PROC(VerifyVersionInfoA); KERNEL32_GET_PROC(VerSetConditionMask); } +static void test_GetProductInfo(void) +{ + DWORD product; + DWORD res; + DWORD table[] = {9,8,7,6, + 7,0,0,0, + 6,2,0,0, + 6,1,2,0, + 6,1,1,0, + 6,1,0,2, + 6,1,0,0, + 6,0,3,0, + 6,0,2,0, + 6,0,1,5, + 6,0,1,0, + 6,0,0,0, + 5,3,0,0, + 5,2,0,0, + 5,1,0,0, + 5,0,0,0, + 0}; + + DWORD *entry = table; + + if (!pGetProductInfo) + { + /* Not present before Vista */ + win_skip("GetProductInfo() not available\n"); + return; + } + + while (*entry) + { + /* SetLastError() / GetLastError(): value is untouched */ + product = 0xdeadbeef; + SetLastError(0xdeadbeef); + res = pGetProductInfo(entry[0], entry[1], entry[2], entry[3], &product); + + if (entry[0] >= 6) + ok(res && (product > PRODUCT_UNDEFINED) && (product <= PRODUCT_PROFESSIONAL_WMC), + "got %d and 0x%x (expected TRUE and a valid PRODUCT_* value)\n", res, product); + else + ok(!res && !product && (GetLastError() == 0xdeadbeef), + "got %d and 0x%x with 0x%x (expected FALSE and PRODUCT_UNDEFINED with LastError untouched)\n", + res, product, GetLastError()); + + entry+= 4; + } + + /* NULL pointer is not a problem */ + SetLastError(0xdeadbeef); + res = pGetProductInfo(6, 1, 0, 0, NULL); + ok( (!res) && (GetLastError() == 0xdeadbeef), + "got %d with 0x%x (expected FALSE with LastError untouched\n", res, GetLastError()); +} + static void test_GetVersionEx(void) { OSVERSIONINFOA infoA; @@ -95,8 +152,6 @@ static void test_GetVersionEx(void) ok(ret || broken(ret == 0), /* win95 */ "Expected GetVersionExA to succeed\n"); - ok(GetLastError() == 0xdeadbeef, - "Expected 0xdeadbeef, got %d\n", GetLastError()); } static void test_VerifyVersionInfo(void) @@ -292,6 +347,7 @@ START_TEST(version) { init_function_pointers(); + test_GetProductInfo(); test_GetVersionEx(); test_VerifyVersionInfo(); } diff --git a/rostests/winetests/kernel32/virtual.c b/rostests/winetests/kernel32/virtual.c index e9582712022..63badc6ed5d 100755 --- a/rostests/winetests/kernel32/virtual.c +++ b/rostests/winetests/kernel32/virtual.c @@ -38,6 +38,8 @@ static BOOL (WINAPI *pVirtualFreeEx)(HANDLE, LPVOID, SIZE_T, DWORD); static UINT (WINAPI *pGetWriteWatch)(DWORD,LPVOID,SIZE_T,LPVOID*,ULONG_PTR*,ULONG*); static UINT (WINAPI *pResetWriteWatch)(LPVOID,SIZE_T); static NTSTATUS (WINAPI *pNtAreMappedFilesTheSame)(PVOID,PVOID); +static NTSTATUS (WINAPI *pNtMapViewOfSection)(HANDLE, HANDLE, PVOID *, ULONG, SIZE_T, const LARGE_INTEGER *, SIZE_T *, ULONG, ULONG, ULONG); +static DWORD (WINAPI *pNtUnmapViewOfSection)(HANDLE, PVOID); /* ############################### */ @@ -422,6 +424,7 @@ static void test_MapViewOfFile(void) ret = DuplicateHandle( GetCurrentProcess(), mapping, GetCurrentProcess(), &map2, FILE_MAP_READ, FALSE, 0 ); ok( ret, "DuplicateHandle failed error %u\n", GetLastError()); + SetLastError(0xdeadbeef); ptr = MapViewOfFile( map2, FILE_MAP_WRITE, 0, 0, 4096 ); if (!ptr) { @@ -429,6 +432,7 @@ static void test_MapViewOfFile(void) CloseHandle( map2 ); ret = DuplicateHandle( GetCurrentProcess(), mapping, GetCurrentProcess(), &map2, 0, FALSE, 0 ); ok( ret, "DuplicateHandle failed error %u\n", GetLastError()); + SetLastError(0xdeadbeef); ptr = MapViewOfFile( map2, 0, 0, 0, 4096 ); ok( !ptr, "MapViewOfFile succeeded\n" ); ok( GetLastError() == ERROR_ACCESS_DENIED, "Wrong error %d\n", GetLastError() ); @@ -603,12 +607,14 @@ static void test_MapViewOfFile(void) ptr = MapViewOfFile( mapping, FILE_MAP_WRITE, 0, 0, 0 ); if (!ptr) { + SIZE_T size; ok( GetLastError() == ERROR_ACCESS_DENIED, "Wrong error %d\n", GetLastError() ); SetLastError(0xdeadbeef); ptr = MapViewOfFile( mapping, FILE_MAP_READ, 0, 0, 0 ); ok( ptr != NULL, "MapViewOfFile FILE_MAP_READ error %u\n", GetLastError() ); SetLastError(0xdeadbeef); - ok( VirtualQuery( ptr, &info, sizeof(info) ) == sizeof(info), + size = VirtualQuery( ptr, &info, sizeof(info) ); + ok( size == sizeof(info), "VirtualQuery error %u\n", GetLastError() ); ok( info.BaseAddress == ptr, "%p != %p\n", info.BaseAddress, ptr ); ok( info.AllocationBase == ptr, "%p != %p\n", info.AllocationBase, ptr ); @@ -628,12 +634,14 @@ static void test_MapViewOfFile(void) ptr = MapViewOfFile( mapping, FILE_MAP_READ, 0, 0, 0 ); if (!ptr) { + SIZE_T size; ok( GetLastError() == ERROR_ACCESS_DENIED, "Wrong error %d\n", GetLastError() ); SetLastError(0xdeadbeef); ptr = MapViewOfFile( mapping, FILE_MAP_WRITE, 0, 0, 0 ); ok( ptr != NULL, "MapViewOfFile FILE_MAP_WRITE error %u\n", GetLastError() ); SetLastError(0xdeadbeef); - ok( VirtualQuery( ptr, &info, sizeof(info) ) == sizeof(info), + size = VirtualQuery( ptr, &info, sizeof(info) ); + ok( size == sizeof(info), "VirtualQuery error %u\n", GetLastError() ); ok( info.BaseAddress == ptr, "%p != %p\n", info.BaseAddress, ptr ); ok( info.AllocationBase == ptr, "%p != %p\n", info.AllocationBase, ptr ); @@ -664,7 +672,7 @@ static void test_MapViewOfFile(void) ok(ret, "VirtualQuery failed with error %d\n", GetLastError()); ok(info.BaseAddress == ptr, "BaseAddress should have been %p but was %p instead\n", ptr, info.BaseAddress); ok(info.AllocationBase == ptr, "AllocationBase should have been %p but was %p instead\n", ptr, info.AllocationBase); - ok(info.RegionSize == MAPPING_SIZE, "RegionSize should have been 0x%x but was 0x%x\n", MAPPING_SIZE, (unsigned int)info.RegionSize); + ok(info.RegionSize == MAPPING_SIZE, "RegionSize should have been 0x%x but was 0x%lx\n", MAPPING_SIZE, info.RegionSize); ok(info.State == MEM_RESERVE, "State should have been MEM_RESERVE instead of 0x%x\n", info.State); if (info.Type == MEM_PRIVATE) /* win9x is different for uncommitted mappings */ { @@ -692,7 +700,7 @@ static void test_MapViewOfFile(void) ok(info.AllocationProtect == PAGE_READWRITE, "AllocationProtect should have been PAGE_READWRITE but was 0x%x\n", info.AllocationProtect); ok(info.RegionSize == MAPPING_SIZE, - "RegionSize should have been 0x%x but was 0x%x\n", MAPPING_SIZE, (unsigned int)info.RegionSize); + "RegionSize should have been 0x%x but was 0x%lx\n", MAPPING_SIZE, info.RegionSize); ok(info.State == MEM_RESERVE, "State should have been MEM_RESERVE instead of 0x%x\n", info.State); ok(info.Protect == 0, @@ -708,7 +716,7 @@ static void test_MapViewOfFile(void) ok(ret, "VirtualQuery failed with error %d\n", GetLastError()); ok(info.BaseAddress == ptr, "BaseAddress should have been %p but was %p instead\n", ptr, info.BaseAddress); ok(info.AllocationBase == ptr, "AllocationBase should have been %p but was %p instead\n", ptr, info.AllocationBase); - ok(info.RegionSize == 0x10000, "RegionSize should have been 0x10000 but was 0x%x\n", (unsigned int)info.RegionSize); + ok(info.RegionSize == 0x10000, "RegionSize should have been 0x10000 but was 0x%lx\n", info.RegionSize); ok(info.State == MEM_COMMIT, "State should have been MEM_RESERVE instead of 0x%x\n", info.State); ok(info.Protect == PAGE_READONLY, "Protect should have been 0 instead of 0x%x\n", info.Protect); if (info.Type == MEM_PRIVATE) /* win9x is different for uncommitted mappings */ @@ -737,11 +745,11 @@ static void test_MapViewOfFile(void) ok(info.AllocationProtect == PAGE_READWRITE, "AllocationProtect should have been PAGE_READWRITE but was 0x%x\n", info.AllocationProtect); ok(info.RegionSize == 0x10000, - "RegionSize should have been 0x10000 but was 0x%x\n", (unsigned int)info.RegionSize); + "RegionSize should have been 0x10000 but was 0x%lx\n", info.RegionSize); ok(info.State == MEM_COMMIT, - "State should have been MEM_RESERVE instead of 0x%x\n", info.State); + "State should have been MEM_COMMIT instead of 0x%x\n", info.State); ok(info.Protect == PAGE_READWRITE, - "Protect should have been 0 instead of 0x%x\n", info.Protect); + "Protect should have been PAGE_READWRITE instead of 0x%x\n", info.Protect); ok(info.Type == MEM_MAPPED, "Type should have been MEM_MAPPED instead of 0x%x\n", info.Type); } @@ -782,13 +790,166 @@ else "got %u, expected ERROR_INVALID_ADDRESS\n", GetLastError()); ok( VirtualFree(addr, 0, MEM_RELEASE), "VirtualFree failed\n" ); -} -static DWORD (WINAPI *pNtMapViewOfSection)( HANDLE handle, HANDLE process, PVOID *addr_ptr, - ULONG zero_bits, SIZE_T commit_size, - const LARGE_INTEGER *offset_ptr, SIZE_T *size_ptr, - ULONG inherit, ULONG alloc_type, ULONG protect ); -static DWORD (WINAPI *pNtUnmapViewOfSection)( HANDLE process, PVOID addr ); + /* close named mapping handle without unmapping */ + name = "Foo"; + SetLastError(0xdeadbeef); + mapping = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, MAPPING_SIZE, name); + ok( mapping != 0, "CreateFileMappingA failed with error %d\n", GetLastError() ); + SetLastError(0xdeadbeef); + ptr = MapViewOfFile(mapping, FILE_MAP_WRITE, 0, 0, 0); + ok( ptr != NULL, "MapViewOfFile failed with error %d\n", GetLastError() ); + SetLastError(0xdeadbeef); + map2 = OpenFileMappingA(FILE_MAP_READ, FALSE, name); + ok( map2 != 0, "OpenFileMappingA failed with error %d\n", GetLastError() ); + SetLastError(0xdeadbeef); + ret = CloseHandle(map2); + ok(ret, "CloseHandle error %d\n", GetLastError()); + SetLastError(0xdeadbeef); + ret = CloseHandle(mapping); + ok(ret, "CloseHandle error %d\n", GetLastError()); + + ret = IsBadReadPtr(ptr, MAPPING_SIZE); + ok( !ret, "memory is not accessible\n" ); + + ret = VirtualQuery(ptr, &info, sizeof(info)); + ok(ret, "VirtualQuery error %d\n", GetLastError()); + ok(info.BaseAddress == ptr, "got %p != expected %p\n", info.BaseAddress, ptr); + ok(info.RegionSize == MAPPING_SIZE, "got %#lx != expected %#x\n", info.RegionSize, MAPPING_SIZE); + ok(info.Protect == PAGE_READWRITE, "got %#x != expected PAGE_READWRITE\n", info.Protect); + ok(info.AllocationBase == ptr, "%p != %p\n", info.AllocationBase, ptr); + ok(info.AllocationProtect == PAGE_READWRITE, "%#x != PAGE_READWRITE\n", info.AllocationProtect); + ok(info.State == MEM_COMMIT, "%#x != MEM_COMMIT\n", info.State); + ok(info.Type == MEM_MAPPED, "%#x != MEM_MAPPED\n", info.Type); + + SetLastError(0xdeadbeef); + map2 = OpenFileMappingA(FILE_MAP_READ, FALSE, name); + todo_wine + ok( map2 == 0, "OpenFileMappingA succeeded\n" ); + todo_wine + ok( GetLastError() == ERROR_FILE_NOT_FOUND, "OpenFileMappingA set error %d\n", GetLastError() ); + if (map2) CloseHandle(map2); /* FIXME: remove once Wine is fixed */ + SetLastError(0xdeadbeef); + mapping = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, MAPPING_SIZE, name); + ok( mapping != 0, "CreateFileMappingA failed\n" ); + todo_wine + ok( GetLastError() == ERROR_SUCCESS, "CreateFileMappingA set error %d\n", GetLastError() ); + SetLastError(0xdeadbeef); + ret = CloseHandle(mapping); + ok(ret, "CloseHandle error %d\n", GetLastError()); + + ret = IsBadReadPtr(ptr, MAPPING_SIZE); + ok( !ret, "memory is not accessible\n" ); + + ret = VirtualQuery(ptr, &info, sizeof(info)); + ok(ret, "VirtualQuery error %d\n", GetLastError()); + ok(info.BaseAddress == ptr, "got %p != expected %p\n", info.BaseAddress, ptr); + ok(info.RegionSize == MAPPING_SIZE, "got %#lx != expected %#x\n", info.RegionSize, MAPPING_SIZE); + ok(info.Protect == PAGE_READWRITE, "got %#x != expected PAGE_READWRITE\n", info.Protect); + ok(info.AllocationBase == ptr, "%p != %p\n", info.AllocationBase, ptr); + ok(info.AllocationProtect == PAGE_READWRITE, "%#x != PAGE_READWRITE\n", info.AllocationProtect); + ok(info.State == MEM_COMMIT, "%#x != MEM_COMMIT\n", info.State); + ok(info.Type == MEM_MAPPED, "%#x != MEM_MAPPED\n", info.Type); + + SetLastError(0xdeadbeef); + ret = UnmapViewOfFile(ptr); + ok( ret, "UnmapViewOfFile failed with error %d\n", GetLastError() ); + + ret = IsBadReadPtr(ptr, MAPPING_SIZE); + ok( ret, "memory is accessible\n" ); + + ret = VirtualQuery(ptr, &info, sizeof(info)); + ok(ret, "VirtualQuery error %d\n", GetLastError()); + ok(info.BaseAddress == ptr, "got %p != expected %p\n", info.BaseAddress, ptr); + ok(info.Protect == PAGE_NOACCESS, "got %#x != expected PAGE_NOACCESS\n", info.Protect); + ok(info.AllocationBase == NULL, "%p != NULL\n", info.AllocationBase); + ok(info.AllocationProtect == 0, "%#x != 0\n", info.AllocationProtect); + ok(info.State == MEM_FREE, "%#x != MEM_FREE\n", info.State); + ok(info.Type == 0, "%#x != 0\n", info.Type); + + SetLastError(0xdeadbeef); + file = CreateFileA(testfile, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0); + ok( file != INVALID_HANDLE_VALUE, "CreateFile error %u\n", GetLastError() ); + SetFilePointer(file, 4096, NULL, FILE_BEGIN); + SetEndOfFile(file); + + SetLastError(0xdeadbeef); + mapping = CreateFileMappingA(file, NULL, PAGE_READWRITE, 0, MAPPING_SIZE, name); + ok( mapping != 0, "CreateFileMappingA failed with error %d\n", GetLastError() ); + SetLastError(0xdeadbeef); + ptr = MapViewOfFile(mapping, FILE_MAP_WRITE, 0, 0, 0); + ok( ptr != NULL, "MapViewOfFile failed with error %d\n", GetLastError() ); + SetLastError(0xdeadbeef); + map2 = OpenFileMappingA(FILE_MAP_READ, FALSE, name); + ok( map2 != 0, "OpenFileMappingA failed with error %d\n", GetLastError() ); + SetLastError(0xdeadbeef); + ret = CloseHandle(map2); + ok(ret, "CloseHandle error %d\n", GetLastError()); + SetLastError(0xdeadbeef); + ret = CloseHandle(mapping); + ok(ret, "CloseHandle error %d\n", GetLastError()); + + ret = IsBadReadPtr(ptr, MAPPING_SIZE); + ok( !ret, "memory is not accessible\n" ); + + ret = VirtualQuery(ptr, &info, sizeof(info)); + ok(ret, "VirtualQuery error %d\n", GetLastError()); + ok(info.BaseAddress == ptr, "got %p != expected %p\n", info.BaseAddress, ptr); + ok(info.RegionSize == MAPPING_SIZE, "got %#lx != expected %#x\n", info.RegionSize, MAPPING_SIZE); + ok(info.Protect == PAGE_READWRITE, "got %#x != expected PAGE_READWRITE\n", info.Protect); + ok(info.AllocationBase == ptr, "%p != %p\n", info.AllocationBase, ptr); + ok(info.AllocationProtect == PAGE_READWRITE, "%#x != PAGE_READWRITE\n", info.AllocationProtect); + ok(info.State == MEM_COMMIT, "%#x != MEM_COMMIT\n", info.State); + ok(info.Type == MEM_MAPPED, "%#x != MEM_MAPPED\n", info.Type); + + SetLastError(0xdeadbeef); + map2 = OpenFileMappingA(FILE_MAP_READ, FALSE, name); + todo_wine + ok( map2 == 0, "OpenFileMappingA succeeded\n" ); + todo_wine + ok( GetLastError() == ERROR_FILE_NOT_FOUND, "OpenFileMappingA set error %d\n", GetLastError() ); + CloseHandle(map2); + SetLastError(0xdeadbeef); + mapping = CreateFileMappingA(file, NULL, PAGE_READWRITE, 0, MAPPING_SIZE, name); + ok( mapping != 0, "CreateFileMappingA failed\n" ); + todo_wine + ok( GetLastError() == ERROR_SUCCESS, "CreateFileMappingA set error %d\n", GetLastError() ); + SetLastError(0xdeadbeef); + ret = CloseHandle(mapping); + ok(ret, "CloseHandle error %d\n", GetLastError()); + + ret = IsBadReadPtr(ptr, MAPPING_SIZE); + ok( !ret, "memory is not accessible\n" ); + + ret = VirtualQuery(ptr, &info, sizeof(info)); + ok(ret, "VirtualQuery error %d\n", GetLastError()); + ok(info.BaseAddress == ptr, "got %p != expected %p\n", info.BaseAddress, ptr); + ok(info.RegionSize == MAPPING_SIZE, "got %#lx != expected %#x\n", info.RegionSize, MAPPING_SIZE); + ok(info.Protect == PAGE_READWRITE, "got %#x != expected PAGE_READWRITE\n", info.Protect); + ok(info.AllocationBase == ptr, "%p != %p\n", info.AllocationBase, ptr); + ok(info.AllocationProtect == PAGE_READWRITE, "%#x != PAGE_READWRITE\n", info.AllocationProtect); + ok(info.State == MEM_COMMIT, "%#x != MEM_COMMIT\n", info.State); + ok(info.Type == MEM_MAPPED, "%#x != MEM_MAPPED\n", info.Type); + + SetLastError(0xdeadbeef); + ret = UnmapViewOfFile(ptr); + ok( ret, "UnmapViewOfFile failed with error %d\n", GetLastError() ); + + ret = IsBadReadPtr(ptr, MAPPING_SIZE); + ok( ret, "memory is accessible\n" ); + + ret = VirtualQuery(ptr, &info, sizeof(info)); + ok(ret, "VirtualQuery error %d\n", GetLastError()); + ok(info.BaseAddress == ptr, "got %p != expected %p\n", info.BaseAddress, ptr); + ok(info.Protect == PAGE_NOACCESS, "got %#x != expected PAGE_NOACCESS\n", info.Protect); + ok(info.AllocationBase == NULL, "%p != NULL\n", info.AllocationBase); + ok(info.AllocationProtect == 0, "%#x != 0\n", info.AllocationProtect); + ok(info.State == MEM_FREE, "%#x != MEM_FREE\n", info.State); + ok(info.Type == 0, "%#x != 0\n", info.Type); + + CloseHandle(file); + DeleteFileA(testfile); +} static void test_NtMapViewOfSection(void) { @@ -804,8 +965,6 @@ static void test_NtMapViewOfSection(void) SIZE_T size, result; LARGE_INTEGER offset; - pNtMapViewOfSection = (void *)GetProcAddress( GetModuleHandle("ntdll.dll"), "NtMapViewOfSection" ); - pNtUnmapViewOfSection = (void *)GetProcAddress( GetModuleHandle("ntdll.dll"), "NtUnmapViewOfSection" ); if (!pNtMapViewOfSection || !pNtUnmapViewOfSection) { win_skip( "NtMapViewOfSection not available\n" ); @@ -1392,6 +1551,851 @@ static void test_write_watch(void) VirtualFree( base, 0, MEM_FREE ); } +static void test_VirtualProtect(void) +{ + static const struct test_data + { + DWORD prot_set, prot_get; + } td[] = + { + { 0, 0 }, /* 0x00 */ + { PAGE_NOACCESS, PAGE_NOACCESS }, /* 0x01 */ + { PAGE_READONLY, PAGE_READONLY }, /* 0x02 */ + { PAGE_READONLY | PAGE_NOACCESS, 0 }, /* 0x03 */ + { PAGE_READWRITE, PAGE_READWRITE }, /* 0x04 */ + { PAGE_READWRITE | PAGE_NOACCESS, 0 }, /* 0x05 */ + { PAGE_READWRITE | PAGE_READONLY, 0 }, /* 0x06 */ + { PAGE_READWRITE | PAGE_READONLY | PAGE_NOACCESS, 0 }, /* 0x07 */ + { PAGE_WRITECOPY, 0 }, /* 0x08 */ + { PAGE_WRITECOPY | PAGE_NOACCESS, 0 }, /* 0x09 */ + { PAGE_WRITECOPY | PAGE_READONLY, 0 }, /* 0x0a */ + { PAGE_WRITECOPY | PAGE_NOACCESS | PAGE_READONLY, 0 }, /* 0x0b */ + { PAGE_WRITECOPY | PAGE_READWRITE, 0 }, /* 0x0c */ + { PAGE_WRITECOPY | PAGE_READWRITE | PAGE_NOACCESS, 0 }, /* 0x0d */ + { PAGE_WRITECOPY | PAGE_READWRITE | PAGE_READONLY, 0 }, /* 0x0e */ + { PAGE_WRITECOPY | PAGE_READWRITE | PAGE_READONLY | PAGE_NOACCESS, 0 }, /* 0x0f */ + + { PAGE_EXECUTE, PAGE_EXECUTE }, /* 0x10 */ + { PAGE_EXECUTE_READ, PAGE_EXECUTE_READ }, /* 0x20 */ + { PAGE_EXECUTE_READ | PAGE_EXECUTE, 0 }, /* 0x30 */ + { PAGE_EXECUTE_READWRITE, PAGE_EXECUTE_READWRITE }, /* 0x40 */ + { PAGE_EXECUTE_READWRITE | PAGE_EXECUTE, 0 }, /* 0x50 */ + { PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_READ, 0 }, /* 0x60 */ + { PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_READ | PAGE_EXECUTE, 0 }, /* 0x70 */ + { PAGE_EXECUTE_WRITECOPY, 0 }, /* 0x80 */ + { PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE, 0 }, /* 0x90 */ + { PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE_READ, 0 }, /* 0xa0 */ + { PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE_READ | PAGE_EXECUTE, 0 }, /* 0xb0 */ + { PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE_READWRITE, 0 }, /* 0xc0 */ + { PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE, 0 }, /* 0xd0 */ + { PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_READ, 0 }, /* 0xe0 */ + { PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_READ | PAGE_EXECUTE, 0 } /* 0xf0 */ + }; + char *base; + DWORD ret, old_prot, rw_prot, exec_prot, i, j; + MEMORY_BASIC_INFORMATION info; + SYSTEM_INFO si; + + GetSystemInfo(&si); + trace("system page size %#x\n", si.dwPageSize); + + SetLastError(0xdeadbeef); + base = VirtualAlloc(0, si.dwPageSize, MEM_RESERVE | MEM_COMMIT, PAGE_NOACCESS); + ok(base != NULL, "VirtualAlloc failed %d\n", GetLastError()); + + for (i = 0; i < sizeof(td)/sizeof(td[0]); i++) + { + SetLastError(0xdeadbeef); + ret = VirtualQuery(base, &info, sizeof(info)); + ok(ret, "VirtualQuery failed %d\n", GetLastError()); + ok(info.BaseAddress == base, "%d: got %p != expected %p\n", i, info.BaseAddress, base); + ok(info.RegionSize == si.dwPageSize, "%d: got %#lx != expected %#x\n", i, info.RegionSize, si.dwPageSize); + ok(info.Protect == PAGE_NOACCESS, "%d: got %#x != expected PAGE_NOACCESS\n", i, info.Protect); + ok(info.AllocationBase == base, "%d: %p != %p\n", i, info.AllocationBase, base); + ok(info.AllocationProtect == PAGE_NOACCESS, "%d: %#x != PAGE_NOACCESS\n", i, info.AllocationProtect); + ok(info.State == MEM_COMMIT, "%d: %#x != MEM_COMMIT\n", i, info.State); + ok(info.Type == MEM_PRIVATE, "%d: %#x != MEM_PRIVATE\n", i, info.Type); + + old_prot = 0xdeadbeef; + SetLastError(0xdeadbeef); + ret = VirtualProtect(base, si.dwPageSize, td[i].prot_set, &old_prot); + if (td[i].prot_get) + { + ok(ret, "%d: VirtualProtect error %d\n", i, GetLastError()); + ok(old_prot == PAGE_NOACCESS, "%d: got %#x != expected PAGE_NOACCESS\n", i, old_prot); + + SetLastError(0xdeadbeef); + ret = VirtualQuery(base, &info, sizeof(info)); + ok(ret, "VirtualQuery failed %d\n", GetLastError()); + ok(info.BaseAddress == base, "%d: got %p != expected %p\n", i, info.BaseAddress, base); + ok(info.RegionSize == si.dwPageSize, "%d: got %#lx != expected %#x\n", i, info.RegionSize, si.dwPageSize); + ok(info.Protect == td[i].prot_get, "%d: got %#x != expected %#x\n", i, info.Protect, td[i].prot_get); + ok(info.AllocationBase == base, "%d: %p != %p\n", i, info.AllocationBase, base); + ok(info.AllocationProtect == PAGE_NOACCESS, "%d: %#x != PAGE_NOACCESS\n", i, info.AllocationProtect); + ok(info.State == MEM_COMMIT, "%d: %#x != MEM_COMMIT\n", i, info.State); + ok(info.Type == MEM_PRIVATE, "%d: %#x != MEM_PRIVATE\n", i, info.Type); + } + else + { + ok(!ret, "%d: VirtualProtect should fail\n", i); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "%d: expected ERROR_INVALID_PARAMETER, got %d\n", i, GetLastError()); + } + + old_prot = 0xdeadbeef; + SetLastError(0xdeadbeef); + ret = VirtualProtect(base, si.dwPageSize, PAGE_NOACCESS, &old_prot); + ok(ret, "%d: VirtualProtect error %d\n", i, GetLastError()); + if (td[i].prot_get) + ok(old_prot == td[i].prot_get, "%d: got %#x != expected %#x\n", i, old_prot, td[i].prot_get); + else + ok(old_prot == PAGE_NOACCESS, "%d: got %#x != expected PAGE_NOACCESS\n", i, old_prot); + } + + exec_prot = 0; + + for (i = 0; i <= 4; i++) + { + rw_prot = 0; + + for (j = 0; j <= 4; j++) + { + DWORD prot = exec_prot | rw_prot; + + SetLastError(0xdeadbeef); + ret = VirtualProtect(base, si.dwPageSize, prot, &old_prot); + if ((rw_prot && exec_prot) || (!rw_prot && !exec_prot)) + { + ok(!ret, "VirtualProtect(%02x) should fail\n", prot); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); + } + else + { + if (prot & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY)) + { + ok(!ret, "VirtualProtect(%02x) should fail\n", prot); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); + } + else + ok(ret, "VirtualProtect(%02x) error %d\n", prot, GetLastError()); + } + + rw_prot = 1 << j; + } + + exec_prot = 1 << (i + 4); + } + + VirtualFree(base, 0, MEM_FREE); +} + +static BOOL is_mem_writable(DWORD prot) +{ + switch (prot & 0xff) + { + case PAGE_READWRITE: + case PAGE_WRITECOPY: + case PAGE_EXECUTE_READWRITE: + case PAGE_EXECUTE_WRITECOPY: + return TRUE; + + default: + return FALSE; + } +} + +static void test_VirtualAlloc_protection(void) +{ + static const struct test_data + { + DWORD prot; + BOOL success; + } td[] = + { + { 0, FALSE }, /* 0x00 */ + { PAGE_NOACCESS, TRUE }, /* 0x01 */ + { PAGE_READONLY, TRUE }, /* 0x02 */ + { PAGE_READONLY | PAGE_NOACCESS, FALSE }, /* 0x03 */ + { PAGE_READWRITE, TRUE }, /* 0x04 */ + { PAGE_READWRITE | PAGE_NOACCESS, FALSE }, /* 0x05 */ + { PAGE_READWRITE | PAGE_READONLY, FALSE }, /* 0x06 */ + { PAGE_READWRITE | PAGE_READONLY | PAGE_NOACCESS, FALSE }, /* 0x07 */ + { PAGE_WRITECOPY, FALSE }, /* 0x08 */ + { PAGE_WRITECOPY | PAGE_NOACCESS, FALSE }, /* 0x09 */ + { PAGE_WRITECOPY | PAGE_READONLY, FALSE }, /* 0x0a */ + { PAGE_WRITECOPY | PAGE_NOACCESS | PAGE_READONLY, FALSE }, /* 0x0b */ + { PAGE_WRITECOPY | PAGE_READWRITE, FALSE }, /* 0x0c */ + { PAGE_WRITECOPY | PAGE_READWRITE | PAGE_NOACCESS, FALSE }, /* 0x0d */ + { PAGE_WRITECOPY | PAGE_READWRITE | PAGE_READONLY, FALSE }, /* 0x0e */ + { PAGE_WRITECOPY | PAGE_READWRITE | PAGE_READONLY | PAGE_NOACCESS, FALSE }, /* 0x0f */ + + { PAGE_EXECUTE, TRUE }, /* 0x10 */ + { PAGE_EXECUTE_READ, TRUE }, /* 0x20 */ + { PAGE_EXECUTE_READ | PAGE_EXECUTE, FALSE }, /* 0x30 */ + { PAGE_EXECUTE_READWRITE, TRUE }, /* 0x40 */ + { PAGE_EXECUTE_READWRITE | PAGE_EXECUTE, FALSE }, /* 0x50 */ + { PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_READ, FALSE }, /* 0x60 */ + { PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_READ | PAGE_EXECUTE, FALSE }, /* 0x70 */ + { PAGE_EXECUTE_WRITECOPY, FALSE }, /* 0x80 */ + { PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE, FALSE }, /* 0x90 */ + { PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE_READ, FALSE }, /* 0xa0 */ + { PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE_READ | PAGE_EXECUTE, FALSE }, /* 0xb0 */ + { PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE_READWRITE, FALSE }, /* 0xc0 */ + { PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE, FALSE }, /* 0xd0 */ + { PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_READ, FALSE }, /* 0xe0 */ + { PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_READ | PAGE_EXECUTE, FALSE } /* 0xf0 */ + }; + char *base; + DWORD ret, i; + MEMORY_BASIC_INFORMATION info; + SYSTEM_INFO si; + + GetSystemInfo(&si); + trace("system page size %#x\n", si.dwPageSize); + + for (i = 0; i < sizeof(td)/sizeof(td[0]); i++) + { + SetLastError(0xdeadbeef); + base = VirtualAlloc(0, si.dwPageSize, MEM_COMMIT, td[i].prot); + + if (td[i].success) + { + ok(base != NULL, "%d: VirtualAlloc failed %d\n", i, GetLastError()); + + SetLastError(0xdeadbeef); + ret = VirtualQuery(base, &info, sizeof(info)); + ok(ret, "VirtualQuery failed %d\n", GetLastError()); + ok(info.BaseAddress == base, "%d: got %p != expected %p\n", i, info.BaseAddress, base); + ok(info.RegionSize == si.dwPageSize, "%d: got %#lx != expected %#x\n", i, info.RegionSize, si.dwPageSize); + ok(info.Protect == td[i].prot, "%d: got %#x != expected %#x\n", i, info.Protect, td[i].prot); + ok(info.AllocationBase == base, "%d: %p != %p\n", i, info.AllocationBase, base); + ok(info.AllocationProtect == td[i].prot, "%d: %#x != %#x\n", i, info.AllocationProtect, td[i].prot); + ok(info.State == MEM_COMMIT, "%d: %#x != MEM_COMMIT\n", i, info.State); + ok(info.Type == MEM_PRIVATE, "%d: %#x != MEM_PRIVATE\n", i, info.Type); + + if (is_mem_writable(info.Protect)) + { + base[0] = 0xfe; + + SetLastError(0xdeadbeef); + ret = VirtualQuery(base, &info, sizeof(info)); + ok(ret, "VirtualQuery failed %d\n", GetLastError()); + ok(info.Protect == td[i].prot, "%d: got %#x != expected %#x\n", i, info.Protect, td[i].prot); + } + + VirtualFree(base, 0, MEM_FREE); + } + else + { + ok(!base, "%d: VirtualAlloc should fail\n", i); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "%d: expected ERROR_INVALID_PARAMETER, got %d\n", i, GetLastError()); + } + } +} + +static void test_CreateFileMapping_protection(void) +{ + static const struct test_data + { + DWORD prot; + BOOL success; + DWORD prot_after_write; + } td[] = + { + { 0, FALSE, 0 }, /* 0x00 */ + { PAGE_NOACCESS, FALSE, PAGE_NOACCESS }, /* 0x01 */ + { PAGE_READONLY, TRUE, PAGE_READONLY }, /* 0x02 */ + { PAGE_READONLY | PAGE_NOACCESS, FALSE, PAGE_NOACCESS }, /* 0x03 */ + { PAGE_READWRITE, TRUE, PAGE_READWRITE }, /* 0x04 */ + { PAGE_READWRITE | PAGE_NOACCESS, FALSE, PAGE_NOACCESS }, /* 0x05 */ + { PAGE_READWRITE | PAGE_READONLY, FALSE, PAGE_NOACCESS }, /* 0x06 */ + { PAGE_READWRITE | PAGE_READONLY | PAGE_NOACCESS, FALSE, PAGE_NOACCESS }, /* 0x07 */ + { PAGE_WRITECOPY, TRUE, PAGE_READWRITE }, /* 0x08 */ + { PAGE_WRITECOPY | PAGE_NOACCESS, FALSE, PAGE_NOACCESS }, /* 0x09 */ + { PAGE_WRITECOPY | PAGE_READONLY, FALSE, PAGE_NOACCESS }, /* 0x0a */ + { PAGE_WRITECOPY | PAGE_NOACCESS | PAGE_READONLY, FALSE, PAGE_NOACCESS }, /* 0x0b */ + { PAGE_WRITECOPY | PAGE_READWRITE, FALSE, PAGE_NOACCESS }, /* 0x0c */ + { PAGE_WRITECOPY | PAGE_READWRITE | PAGE_NOACCESS, FALSE, PAGE_NOACCESS }, /* 0x0d */ + { PAGE_WRITECOPY | PAGE_READWRITE | PAGE_READONLY, FALSE, PAGE_NOACCESS }, /* 0x0e */ + { PAGE_WRITECOPY | PAGE_READWRITE | PAGE_READONLY | PAGE_NOACCESS, FALSE, PAGE_NOACCESS }, /* 0x0f */ + + { PAGE_EXECUTE, FALSE, PAGE_EXECUTE }, /* 0x10 */ + { PAGE_EXECUTE_READ, TRUE, PAGE_EXECUTE_READ }, /* 0x20 */ + { PAGE_EXECUTE_READ | PAGE_EXECUTE, FALSE, PAGE_EXECUTE_READ }, /* 0x30 */ + { PAGE_EXECUTE_READWRITE, TRUE, PAGE_EXECUTE_READWRITE }, /* 0x40 */ + { PAGE_EXECUTE_READWRITE | PAGE_EXECUTE, FALSE, PAGE_NOACCESS }, /* 0x50 */ + { PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_READ, FALSE, PAGE_NOACCESS }, /* 0x60 */ + { PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_READ | PAGE_EXECUTE, FALSE, PAGE_NOACCESS }, /* 0x70 */ + { PAGE_EXECUTE_WRITECOPY, TRUE, PAGE_EXECUTE_READWRITE }, /* 0x80 */ + { PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE, FALSE, PAGE_NOACCESS }, /* 0x90 */ + { PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE_READ, FALSE, PAGE_NOACCESS }, /* 0xa0 */ + { PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE_READ | PAGE_EXECUTE, FALSE, PAGE_NOACCESS }, /* 0xb0 */ + { PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE_READWRITE, FALSE, PAGE_NOACCESS }, /* 0xc0 */ + { PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE, FALSE, PAGE_NOACCESS }, /* 0xd0 */ + { PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_READ, FALSE, PAGE_NOACCESS }, /* 0xe0 */ + { PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_READ | PAGE_EXECUTE, FALSE, PAGE_NOACCESS } /* 0xf0 */ + }; + char *base; + DWORD ret, i, alloc_prot, prot, old_prot; + MEMORY_BASIC_INFORMATION info; + SYSTEM_INFO si; + char temp_path[MAX_PATH]; + char file_name[MAX_PATH]; + HANDLE hfile, hmap; + BOOL page_exec_supported = TRUE; + + GetSystemInfo(&si); + trace("system page size %#x\n", si.dwPageSize); + + GetTempPath(MAX_PATH, temp_path); + GetTempFileName(temp_path, "map", 0, file_name); + + SetLastError(0xdeadbeef); + hfile = CreateFile(file_name, GENERIC_READ|GENERIC_WRITE|GENERIC_EXECUTE, 0, NULL, CREATE_ALWAYS, 0, 0); + ok(hfile != INVALID_HANDLE_VALUE, "CreateFile(%s) error %d\n", file_name, GetLastError()); + SetFilePointer(hfile, si.dwPageSize, NULL, FILE_BEGIN); + SetEndOfFile(hfile); + + for (i = 0; i < sizeof(td)/sizeof(td[0]); i++) + { + SetLastError(0xdeadbeef); + hmap = CreateFileMapping(hfile, NULL, td[i].prot | SEC_COMMIT, 0, si.dwPageSize, NULL); + + if (td[i].success) + { + if (!hmap) + { + trace("%d: CreateFileMapping(%04x) failed: %d\n", i, td[i].prot, GetLastError()); + /* NT4 and win2k don't support EXEC on file mappings */ + if (td[i].prot == PAGE_EXECUTE_READ || td[i].prot == PAGE_EXECUTE_READWRITE) + { + page_exec_supported = FALSE; + ok(broken(!hmap), "%d: CreateFileMapping doesn't support PAGE_EXECUTE\n", i); + continue; + } + /* Vista+ supports PAGE_EXECUTE_WRITECOPY, earlier versions don't */ + if (td[i].prot == PAGE_EXECUTE_WRITECOPY) + { + page_exec_supported = FALSE; + ok(broken(!hmap), "%d: CreateFileMapping doesn't support PAGE_EXECUTE_WRITECOPY\n", i); + continue; + } + } + ok(hmap != 0, "%d: CreateFileMapping(%04x) error %d\n", i, td[i].prot, GetLastError()); + + base = MapViewOfFile(hmap, FILE_MAP_READ, 0, 0, 0); + ok(base != NULL, "%d: MapViewOfFile failed %d\n", i, GetLastError()); + + SetLastError(0xdeadbeef); + ret = VirtualQuery(base, &info, sizeof(info)); + ok(ret, "VirtualQuery failed %d\n", GetLastError()); + ok(info.BaseAddress == base, "%d: got %p != expected %p\n", i, info.BaseAddress, base); + ok(info.RegionSize == si.dwPageSize, "%d: got %#lx != expected %#x\n", i, info.RegionSize, si.dwPageSize); + ok(info.Protect == PAGE_READONLY, "%d: got %#x != expected PAGE_READONLY\n", i, info.Protect); + ok(info.AllocationBase == base, "%d: %p != %p\n", i, info.AllocationBase, base); + ok(info.AllocationProtect == PAGE_READONLY, "%d: %#x != PAGE_READONLY\n", i, info.AllocationProtect); + ok(info.State == MEM_COMMIT, "%d: %#x != MEM_COMMIT\n", i, info.State); + ok(info.Type == MEM_MAPPED, "%d: %#x != MEM_MAPPED\n", i, info.Type); + + if (is_mem_writable(info.Protect)) + { + base[0] = 0xfe; + + SetLastError(0xdeadbeef); + ret = VirtualQuery(base, &info, sizeof(info)); + ok(ret, "VirtualQuery failed %d\n", GetLastError()); + ok(info.Protect == td[i].prot, "%d: got %#x != expected %#x\n", i, info.Protect, td[i].prot); + } + + UnmapViewOfFile(base); + CloseHandle(hmap); + } + else + { + ok(!hmap, "%d: CreateFileMapping should fail\n", i); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "%d: expected ERROR_INVALID_PARAMETER, got %d\n", i, GetLastError()); + } + } + + if (page_exec_supported) alloc_prot = PAGE_EXECUTE_READWRITE; + else alloc_prot = PAGE_READWRITE; + SetLastError(0xdeadbeef); + hmap = CreateFileMapping(hfile, NULL, alloc_prot, 0, si.dwPageSize, NULL); + ok(hmap != 0, "%d: CreateFileMapping error %d\n", i, GetLastError()); + + SetLastError(0xdeadbeef); + base = MapViewOfFile(hmap, FILE_MAP_READ | FILE_MAP_WRITE | (page_exec_supported ? FILE_MAP_EXECUTE : 0), 0, 0, 0); + ok(base != NULL, "MapViewOfFile failed %d\n", GetLastError()); + + old_prot = 0xdeadbeef; + SetLastError(0xdeadbeef); + ret = VirtualProtect(base, si.dwPageSize, PAGE_NOACCESS, &old_prot); + ok(ret, "VirtualProtect error %d\n", GetLastError()); + ok(old_prot == alloc_prot, "got %#x != expected %#x\n", old_prot, alloc_prot); + + for (i = 0; i < sizeof(td)/sizeof(td[0]); i++) + { + SetLastError(0xdeadbeef); + ret = VirtualQuery(base, &info, sizeof(info)); + ok(ret, "VirtualQuery failed %d\n", GetLastError()); + ok(info.BaseAddress == base, "%d: got %p != expected %p\n", i, info.BaseAddress, base); + ok(info.RegionSize == si.dwPageSize, "%d: got %#lx != expected %#x\n", i, info.RegionSize, si.dwPageSize); + ok(info.Protect == PAGE_NOACCESS, "%d: got %#x != expected PAGE_NOACCESS\n", i, info.Protect); + ok(info.AllocationBase == base, "%d: %p != %p\n", i, info.AllocationBase, base); + ok(info.AllocationProtect == alloc_prot, "%d: %#x != %#x\n", i, info.AllocationProtect, alloc_prot); + ok(info.State == MEM_COMMIT, "%d: %#x != MEM_COMMIT\n", i, info.State); + ok(info.Type == MEM_MAPPED, "%d: %#x != MEM_MAPPED\n", i, info.Type); + + old_prot = 0xdeadbeef; + SetLastError(0xdeadbeef); + ret = VirtualProtect(base, si.dwPageSize, td[i].prot, &old_prot); + if (td[i].success || td[i].prot == PAGE_NOACCESS || td[i].prot == PAGE_EXECUTE) + { + if (!ret) + { + /* win2k and XP don't support EXEC on file mappings */ + if (td[i].prot == PAGE_EXECUTE) + { + ok(broken(!ret), "%d: VirtualProtect doesn't support PAGE_EXECUTE\n", i); + continue; + } + /* NT4 and win2k don't support EXEC on file mappings */ + if (td[i].prot == PAGE_EXECUTE_READ || td[i].prot == PAGE_EXECUTE_READWRITE) + { + ok(broken(!ret), "%d: VirtualProtect doesn't support PAGE_EXECUTE\n", i); + continue; + } + /* Vista+ supports PAGE_EXECUTE_WRITECOPY, earlier versions don't */ + if (td[i].prot == PAGE_EXECUTE_WRITECOPY) + { + ok(broken(!ret), "%d: VirtualProtect doesn't support PAGE_EXECUTE_WRITECOPY\n", i); + continue; + } + } + + ok(ret, "%d: VirtualProtect error %d\n", i, GetLastError()); + ok(old_prot == PAGE_NOACCESS, "%d: got %#x != expected PAGE_NOACCESS\n", i, old_prot); + + prot = td[i].prot; + /* looks strange but Windows doesn't do this for PAGE_WRITECOPY */ + if (prot == PAGE_EXECUTE_WRITECOPY) prot = PAGE_EXECUTE_READWRITE; + + SetLastError(0xdeadbeef); + ret = VirtualQuery(base, &info, sizeof(info)); + ok(ret, "VirtualQuery failed %d\n", GetLastError()); + ok(info.BaseAddress == base, "%d: got %p != expected %p\n", i, info.BaseAddress, base); + ok(info.RegionSize == si.dwPageSize, "%d: got %#lx != expected %#x\n", i, info.RegionSize, si.dwPageSize); + /* FIXME: remove the condition below once Wine is fixed */ + if (td[i].prot == PAGE_EXECUTE_WRITECOPY) + todo_wine ok(info.Protect == prot, "%d: got %#x != expected %#x\n", i, info.Protect, prot); + else + ok(info.Protect == prot, "%d: got %#x != expected %#x\n", i, info.Protect, prot); + ok(info.AllocationBase == base, "%d: %p != %p\n", i, info.AllocationBase, base); + ok(info.AllocationProtect == alloc_prot, "%d: %#x != %#x\n", i, info.AllocationProtect, alloc_prot); + ok(info.State == MEM_COMMIT, "%d: %#x != MEM_COMMIT\n", i, info.State); + ok(info.Type == MEM_MAPPED, "%d: %#x != MEM_MAPPED\n", i, info.Type); + + if (is_mem_writable(info.Protect)) + { + base[0] = 0xfe; + + SetLastError(0xdeadbeef); + ret = VirtualQuery(base, &info, sizeof(info)); + ok(ret, "VirtualQuery failed %d\n", GetLastError()); + /* FIXME: remove the condition below once Wine is fixed */ + if (td[i].prot == PAGE_WRITECOPY || td[i].prot == PAGE_EXECUTE_WRITECOPY) + todo_wine ok(info.Protect == td[i].prot_after_write, "%d: got %#x != expected %#x\n", i, info.Protect, td[i].prot_after_write); + else + ok(info.Protect == td[i].prot_after_write, "%d: got %#x != expected %#x\n", i, info.Protect, td[i].prot_after_write); + } + } + else + { + ok(!ret, "%d: VirtualProtect should fail\n", i); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "%d: expected ERROR_INVALID_PARAMETER, got %d\n", i, GetLastError()); + continue; + } + + old_prot = 0xdeadbeef; + SetLastError(0xdeadbeef); + ret = VirtualProtect(base, si.dwPageSize, PAGE_NOACCESS, &old_prot); + ok(ret, "%d: VirtualProtect error %d\n", i, GetLastError()); + /* FIXME: remove the condition below once Wine is fixed */ + if (td[i].prot == PAGE_WRITECOPY || td[i].prot == PAGE_EXECUTE_WRITECOPY) + todo_wine ok(old_prot == td[i].prot_after_write, "%d: got %#x != expected %#x\n", i, old_prot, td[i].prot_after_write); + else + ok(old_prot == td[i].prot_after_write, "%d: got %#x != expected %#x\n", i, old_prot, td[i].prot_after_write); + } + + UnmapViewOfFile(base); + CloseHandle(hmap); + + CloseHandle(hfile); + DeleteFile(file_name); +} + +#define ACCESS_READ 0x01 +#define ACCESS_WRITE 0x02 +#define ACCESS_EXECUTE 0x04 +#define ACCESS_WRITECOPY 0x08 + +static DWORD page_prot_to_access(DWORD prot) +{ + switch (prot) + { + case PAGE_READWRITE: + return ACCESS_READ | ACCESS_WRITE; + + case PAGE_EXECUTE: + case PAGE_EXECUTE_READ: + return ACCESS_READ | ACCESS_EXECUTE; + + case PAGE_EXECUTE_READWRITE: + return ACCESS_READ | ACCESS_WRITE | ACCESS_WRITECOPY | ACCESS_EXECUTE; + + case PAGE_EXECUTE_WRITECOPY: + return ACCESS_READ | ACCESS_WRITECOPY | ACCESS_EXECUTE; + + case PAGE_READONLY: + return ACCESS_READ; + + case PAGE_WRITECOPY: + return ACCESS_READ; + + default: + return 0; + } +} + +static BOOL is_compatible_protection(DWORD map_prot, DWORD view_prot, DWORD prot) +{ + DWORD map_access, view_access, prot_access; + + map_access = page_prot_to_access(map_prot); + view_access = page_prot_to_access(view_prot); + prot_access = page_prot_to_access(prot); + + if (view_access == prot_access) return TRUE; + if (!view_access) return FALSE; + + if ((view_access & prot_access) != prot_access) return FALSE; + if ((map_access & prot_access) == prot_access) return TRUE; + + return FALSE; +} + +static DWORD map_prot_to_access(DWORD prot) +{ + switch (prot) + { + case PAGE_READWRITE: + case PAGE_EXECUTE_READWRITE: + return SECTION_MAP_READ | SECTION_MAP_WRITE | SECTION_MAP_EXECUTE | SECTION_MAP_EXECUTE_EXPLICIT | SECTION_QUERY; + case PAGE_READONLY: + case PAGE_WRITECOPY: + case PAGE_EXECUTE: + case PAGE_EXECUTE_READ: + case PAGE_EXECUTE_WRITECOPY: + return SECTION_MAP_READ | SECTION_MAP_EXECUTE | SECTION_MAP_EXECUTE_EXPLICIT | SECTION_QUERY; + default: + return 0; + } +} + +static BOOL is_compatible_access(DWORD map_prot, DWORD view_prot) +{ + DWORD access = map_prot_to_access(map_prot); + if (!view_prot) view_prot = SECTION_MAP_READ; + return (view_prot & access) == view_prot; +} + +static void *map_view_of_file(HANDLE handle, DWORD access) +{ + NTSTATUS status; + LARGE_INTEGER offset; + SIZE_T count; + ULONG protect; + BOOL exec; + void *addr; + + if (!pNtMapViewOfSection) return NULL; + + count = 0; + offset.u.LowPart = 0; + offset.u.HighPart = 0; + + exec = access & FILE_MAP_EXECUTE; + access &= ~FILE_MAP_EXECUTE; + + if (access == FILE_MAP_COPY) + { + if (exec) + protect = PAGE_EXECUTE_WRITECOPY; + else + protect = PAGE_WRITECOPY; + } + else if (access & FILE_MAP_WRITE) + { + if (exec) + protect = PAGE_EXECUTE_READWRITE; + else + protect = PAGE_READWRITE; + } + else if (access & FILE_MAP_READ) + { + if (exec) + protect = PAGE_EXECUTE_READ; + else + protect = PAGE_READONLY; + } + else protect = PAGE_NOACCESS; + + addr = NULL; + status = pNtMapViewOfSection(handle, GetCurrentProcess(), &addr, 0, 0, &offset, + &count, 1 /* ViewShare */, 0, protect); + if (status) + { + /* for simplicity */ + SetLastError(ERROR_ACCESS_DENIED); + addr = NULL; + } + return addr; +} + +static void test_mapping(void) +{ + static const DWORD page_prot[] = + { + PAGE_NOACCESS, PAGE_READONLY, PAGE_READWRITE, PAGE_WRITECOPY, + PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_EXECUTE_WRITECOPY + }; + static const struct + { + DWORD access, prot; + } view[] = + { + { 0, PAGE_NOACCESS }, /* 0x00 */ + { FILE_MAP_COPY, PAGE_WRITECOPY }, /* 0x01 */ + { FILE_MAP_WRITE, PAGE_READWRITE }, /* 0x02 */ + { FILE_MAP_WRITE | FILE_MAP_COPY, PAGE_READWRITE }, /* 0x03 */ + { FILE_MAP_READ, PAGE_READONLY }, /* 0x04 */ + { FILE_MAP_READ | FILE_MAP_COPY, PAGE_READONLY }, /* 0x05 */ + { FILE_MAP_READ | FILE_MAP_WRITE, PAGE_READWRITE }, /* 0x06 */ + { FILE_MAP_READ | FILE_MAP_WRITE | FILE_MAP_COPY, PAGE_READWRITE }, /* 0x07 */ + { SECTION_MAP_EXECUTE, PAGE_NOACCESS }, /* 0x08 */ + { SECTION_MAP_EXECUTE | FILE_MAP_COPY, PAGE_NOACCESS }, /* 0x09 */ + { SECTION_MAP_EXECUTE | FILE_MAP_WRITE, PAGE_READWRITE }, /* 0x0a */ + { SECTION_MAP_EXECUTE | FILE_MAP_WRITE | FILE_MAP_COPY, PAGE_READWRITE }, /* 0x0b */ + { SECTION_MAP_EXECUTE | FILE_MAP_READ, PAGE_READONLY }, /* 0x0c */ + { SECTION_MAP_EXECUTE | FILE_MAP_READ | FILE_MAP_COPY, PAGE_READONLY }, /* 0x0d */ + { SECTION_MAP_EXECUTE | FILE_MAP_READ | FILE_MAP_WRITE, PAGE_READWRITE }, /* 0x0e */ + { SECTION_MAP_EXECUTE | FILE_MAP_READ | FILE_MAP_WRITE | FILE_MAP_COPY, PAGE_READWRITE }, /* 0x0f */ + { FILE_MAP_EXECUTE, PAGE_NOACCESS }, /* 0x20 */ + { FILE_MAP_EXECUTE | FILE_MAP_COPY, PAGE_EXECUTE_WRITECOPY }, /* 0x21 */ + { FILE_MAP_EXECUTE | FILE_MAP_WRITE, PAGE_EXECUTE_READWRITE }, /* 0x22 */ + { FILE_MAP_EXECUTE | FILE_MAP_WRITE | FILE_MAP_COPY, PAGE_EXECUTE_READWRITE }, /* 0x23 */ + { FILE_MAP_EXECUTE | FILE_MAP_READ, PAGE_EXECUTE_READ }, /* 0x24 */ + { FILE_MAP_EXECUTE | FILE_MAP_READ | FILE_MAP_COPY, PAGE_EXECUTE_READ }, /* 0x25 */ + { FILE_MAP_EXECUTE | FILE_MAP_READ | FILE_MAP_WRITE, PAGE_EXECUTE_READWRITE }, /* 0x26 */ + { FILE_MAP_EXECUTE | FILE_MAP_READ | FILE_MAP_WRITE | FILE_MAP_COPY, PAGE_EXECUTE_READWRITE }, /* 0x27 */ + { FILE_MAP_EXECUTE | SECTION_MAP_EXECUTE, PAGE_NOACCESS }, /* 0x28 */ + { FILE_MAP_EXECUTE | SECTION_MAP_EXECUTE | FILE_MAP_COPY, PAGE_NOACCESS }, /* 0x29 */ + { FILE_MAP_EXECUTE | SECTION_MAP_EXECUTE | FILE_MAP_WRITE, PAGE_EXECUTE_READWRITE }, /* 0x2a */ + { FILE_MAP_EXECUTE | SECTION_MAP_EXECUTE | FILE_MAP_WRITE | FILE_MAP_COPY, PAGE_EXECUTE_READWRITE }, /* 0x2b */ + { FILE_MAP_EXECUTE | SECTION_MAP_EXECUTE | FILE_MAP_READ, PAGE_EXECUTE_READ }, /* 0x2c */ + { FILE_MAP_EXECUTE | SECTION_MAP_EXECUTE | FILE_MAP_READ | FILE_MAP_COPY, PAGE_EXECUTE_READ }, /* 0x2d */ + { FILE_MAP_EXECUTE | SECTION_MAP_EXECUTE | FILE_MAP_READ | FILE_MAP_WRITE, PAGE_EXECUTE_READWRITE }, /* 0x2e */ + { FILE_MAP_EXECUTE | SECTION_MAP_EXECUTE | FILE_MAP_READ | FILE_MAP_WRITE | FILE_MAP_COPY, PAGE_EXECUTE_READWRITE } /* 0x2f */ + }; + void *base, *nt_base; + DWORD i, j, k, ret, old_prot, prev_prot; + SYSTEM_INFO si; + char temp_path[MAX_PATH]; + char file_name[MAX_PATH]; + HANDLE hfile, hmap; + MEMORY_BASIC_INFORMATION info, nt_info; + + GetSystemInfo(&si); + trace("system page size %#x\n", si.dwPageSize); + + GetTempPath(MAX_PATH, temp_path); + GetTempFileName(temp_path, "map", 0, file_name); + + SetLastError(0xdeadbeef); + hfile = CreateFile(file_name, GENERIC_READ|GENERIC_WRITE|GENERIC_EXECUTE, 0, NULL, CREATE_ALWAYS, 0, 0); + ok(hfile != INVALID_HANDLE_VALUE, "CreateFile(%s) error %d\n", file_name, GetLastError()); + SetFilePointer(hfile, si.dwPageSize, NULL, FILE_BEGIN); + SetEndOfFile(hfile); + + for (i = 0; i < sizeof(page_prot)/sizeof(page_prot[0]); i++) + { + SetLastError(0xdeadbeef); + hmap = CreateFileMapping(hfile, NULL, page_prot[i] | SEC_COMMIT, 0, si.dwPageSize, NULL); + + if (page_prot[i] == PAGE_NOACCESS) + { + HANDLE hmap2; + + ok(!hmap, "CreateFileMapping(PAGE_NOACCESS) should fail\n"); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); + + /* A trick to create a not accessible mapping */ + SetLastError(0xdeadbeef); + hmap = CreateFileMapping(hfile, NULL, PAGE_READWRITE | SEC_COMMIT, 0, si.dwPageSize, NULL); + ok(hmap != 0, "CreateFileMapping(PAGE_READWRITE) error %d\n", GetLastError()); + SetLastError(0xdeadbeef); + ret = DuplicateHandle(GetCurrentProcess(), hmap, GetCurrentProcess(), &hmap2, 0, FALSE, 0); + ok(ret, "DuplicateHandle error %d\n", GetLastError()); + CloseHandle(hmap); + hmap = hmap2; + } + + if (!hmap) + { + trace("%d: CreateFileMapping(%04x) failed: %d\n", i, page_prot[i], GetLastError()); + + /* NT4 and win2k don't support EXEC on file mappings */ + if (page_prot[i] == PAGE_EXECUTE_READ || page_prot[i] == PAGE_EXECUTE_READWRITE) + { + ok(broken(!hmap), "%d: CreateFileMapping doesn't support PAGE_EXECUTE\n", i); + continue; + } + /* Vista+ supports PAGE_EXECUTE_WRITECOPY, earlier versions don't */ + if (page_prot[i] == PAGE_EXECUTE_WRITECOPY) + { + ok(broken(!hmap), "%d: CreateFileMapping doesn't support PAGE_EXECUTE_WRITECOPY\n", i); + continue; + } + } + + ok(hmap != 0, "%d: CreateFileMapping(%04x) error %d\n", i, page_prot[i], GetLastError()); + + for (j = 0; j < sizeof(view)/sizeof(view[0]); j++) + { + nt_base = map_view_of_file(hmap, view[j].access); + if (nt_base) + { + SetLastError(0xdeadbeef); + ret = VirtualQuery(nt_base, &nt_info, sizeof(nt_info)); + ok(ret, "%d: VirtualQuery failed %d\n", j, GetLastError()); + UnmapViewOfFile(nt_base); + } + + SetLastError(0xdeadbeef); + base = MapViewOfFile(hmap, view[j].access, 0, 0, 0); + + /* Vista+ supports FILE_MAP_EXECUTE properly, earlier versions don't */ + ok(!nt_base == !base || + broken((view[j].access & FILE_MAP_EXECUTE) && !nt_base != !base), + "%d: (%04x/%04x) NT %p kernel %p\n", j, page_prot[i], view[j].access, nt_base, base); + + if (!is_compatible_access(page_prot[i], view[j].access)) + { + ok(!base, "%d: MapViewOfFile(%04x/%04x) should fail\n", j, page_prot[i], view[j].access); + ok(GetLastError() == ERROR_ACCESS_DENIED, "wrong error %d\n", GetLastError()); + continue; + } + + /* Vista+ properly supports FILE_MAP_EXECUTE, earlier versions don't */ + if (!base && (view[j].access & FILE_MAP_EXECUTE)) + { + ok(broken(!base), "%d: MapViewOfFile(%04x/%04x) failed %d\n", j, page_prot[i], view[j].access, GetLastError()); + continue; + } + + ok(base != NULL, "%d: MapViewOfFile(%04x/%04x) failed %d\n", j, page_prot[i], view[j].access, GetLastError()); + + SetLastError(0xdeadbeef); + ret = VirtualQuery(base, &info, sizeof(info)); + ok(ret, "%d: VirtualQuery failed %d\n", j, GetLastError()); + ok(info.BaseAddress == base, "%d: (%04x) got %p, expected %p\n", j, view[j].access, info.BaseAddress, base); + ok(info.RegionSize == si.dwPageSize, "%d: (%04x) got %#lx != expected %#x\n", j, view[j].access, info.RegionSize, si.dwPageSize); + ok(info.Protect == view[j].prot || + broken(view[j].prot == PAGE_EXECUTE_READ && info.Protect == PAGE_READONLY) || /* win2k */ + broken(view[j].prot == PAGE_EXECUTE_READWRITE && info.Protect == PAGE_READWRITE) || /* win2k */ + broken(view[j].prot == PAGE_EXECUTE_WRITECOPY && info.Protect == PAGE_NOACCESS), /* XP */ + "%d: (%04x) got %#x, expected %#x\n", j, view[j].access, info.Protect, view[j].prot); + ok(info.AllocationBase == base, "%d: (%04x) got %p, expected %p\n", j, view[j].access, info.AllocationBase, base); + ok(info.AllocationProtect == info.Protect, "%d: (%04x) got %#x, expected %#x\n", j, view[j].access, info.AllocationProtect, info.Protect); + ok(info.State == MEM_COMMIT, "%d: (%04x) got %#x, expected MEM_COMMIT\n", j, view[j].access, info.State); + ok(info.Type == MEM_MAPPED, "%d: (%04x) got %#x, expected MEM_MAPPED\n", j, view[j].access, info.Type); + + if (nt_base && base) + { + ok(nt_info.RegionSize == info.RegionSize, "%d: (%04x) got %#lx != expected %#lx\n", j, view[j].access, nt_info.RegionSize, info.RegionSize); + ok(nt_info.Protect == info.Protect /* Vista+ */ || + broken(nt_info.AllocationProtect == PAGE_EXECUTE_WRITECOPY && info.Protect == PAGE_NOACCESS), /* XP */ + "%d: (%04x) got %#x, expected %#x\n", j, view[j].access, nt_info.Protect, info.Protect); + ok(nt_info.AllocationProtect == info.AllocationProtect /* Vista+ */ || + broken(nt_info.AllocationProtect == PAGE_EXECUTE_WRITECOPY && info.Protect == PAGE_NOACCESS), /* XP */ + "%d: (%04x) got %#x, expected %#x\n", j, view[j].access, nt_info.AllocationProtect, info.AllocationProtect); + ok(nt_info.State == info.State, "%d: (%04x) got %#x, expected %#x\n", j, view[j].access, nt_info.State, info.State); + ok(nt_info.Type == info.Type, "%d: (%04x) got %#x, expected %#x\n", j, view[j].access, nt_info.Type, info.Type); + } + + prev_prot = info.Protect; + + for (k = 0; k < sizeof(page_prot)/sizeof(page_prot[0]); k++) + { + /*trace("map %#x, view %#x, requested prot %#x\n", page_prot[i], view[j].prot, page_prot[k]);*/ + SetLastError(0xdeadbeef); + old_prot = 0xdeadbeef; + ret = VirtualProtect(base, si.dwPageSize, page_prot[k], &old_prot); + if (is_compatible_protection(page_prot[i], view[j].prot, page_prot[k])) + { + /* win2k and XP don't support EXEC on file mappings */ + if (!ret && page_prot[k] == PAGE_EXECUTE) + { + ok(broken(!ret), "VirtualProtect doesn't support PAGE_EXECUTE\n"); + continue; + } + /* NT4 and win2k don't support EXEC on file mappings */ + if (!ret && (page_prot[k] == PAGE_EXECUTE_READ || page_prot[k] == PAGE_EXECUTE_READWRITE)) + { + ok(broken(!ret), "VirtualProtect doesn't support PAGE_EXECUTE\n"); + continue; + } + /* Vista+ supports PAGE_EXECUTE_WRITECOPY, earlier versions don't */ + if (!ret && page_prot[k] == PAGE_EXECUTE_WRITECOPY) + { + ok(broken(!ret), "VirtualProtect doesn't support PAGE_EXECUTE_WRITECOPY\n"); + continue; + } + /* win2k and XP don't support PAGE_EXECUTE_WRITECOPY views properly */ + if (!ret && view[j].prot == PAGE_EXECUTE_WRITECOPY) + { + ok(broken(!ret), "VirtualProtect doesn't support PAGE_EXECUTE_WRITECOPY view properly\n"); + continue; + } + + ok(ret, "VirtualProtect error %d, map %#x, view %#x, requested prot %#x\n", GetLastError(), page_prot[i], view[j].prot, page_prot[k]); + ok(old_prot == prev_prot, "got %#x, expected %#x\n", old_prot, prev_prot); + prev_prot = page_prot[k]; + } + else + { + /* NT4 doesn't fail on incompatible map and view */ + if (ret) + { + ok(broken(ret), "VirtualProtect should fail, map %#x, view %#x, requested prot %#x\n", page_prot[i], view[j].prot, page_prot[k]); + skip("Incompatible map and view are not properly handled on this platform\n"); + break; /* NT4 won't pass remaining tests */ + } + + ok(!ret, "VirtualProtect should fail, map %#x, view %#x, requested prot %#x\n", page_prot[i], view[j].prot, page_prot[k]); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); + } + } + + UnmapViewOfFile(base); + } + + CloseHandle(hmap); + } + + CloseHandle(hfile); + DeleteFile(file_name); +} + START_TEST(virtual) { int argc; @@ -1427,7 +2431,13 @@ START_TEST(virtual) pResetWriteWatch = (void *) GetProcAddress(hkernel32, "ResetWriteWatch"); pNtAreMappedFilesTheSame = (void *)GetProcAddress( GetModuleHandle("ntdll.dll"), "NtAreMappedFilesTheSame" ); + pNtMapViewOfSection = (void *)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtMapViewOfSection"); + pNtUnmapViewOfSection = (void *)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtUnmapViewOfSection"); + test_mapping(); + test_CreateFileMapping_protection(); + test_VirtualAlloc_protection(); + test_VirtualProtect(); test_VirtualAllocEx(); test_VirtualAlloc(); test_MapViewOfFile(); diff --git a/rostests/winetests/kernel32/volume.c b/rostests/winetests/kernel32/volume.c index 1d027826754..7dd73886cb0 100644 --- a/rostests/winetests/kernel32/volume.c +++ b/rostests/winetests/kernel32/volume.c @@ -22,6 +22,27 @@ #include "winbase.h" #include "winioctl.h" #include +#include "wine/ddk/ntddcdvd.h" + +#include +struct COMPLETE_DVD_LAYER_DESCRIPTOR +{ + DVD_DESCRIPTOR_HEADER Header; + DVD_LAYER_DESCRIPTOR Descriptor; + UCHAR Padding; +}; +#include +C_ASSERT(sizeof(struct COMPLETE_DVD_LAYER_DESCRIPTOR) == 22); + +#include +struct COMPLETE_DVD_MANUFACTURER_DESCRIPTOR +{ + DVD_DESCRIPTOR_HEADER Header; + DVD_MANUFACTURER_DESCRIPTOR Descriptor; + UCHAR Padding; +}; +#include +C_ASSERT(sizeof(struct COMPLETE_DVD_MANUFACTURER_DESCRIPTOR) == 2053); static HINSTANCE hdll; static BOOL (WINAPI * pGetVolumeNameForVolumeMountPointA)(LPCSTR, LPSTR, DWORD); @@ -732,6 +753,175 @@ static void test_GetVolumePathNamesForVolumeNameW(void) ok(error == ERROR_FILE_NOT_FOUND, "expected ERROR_FILE_NOT_FOUND got %u\n", error); } +static void test_dvd_read_structure(HANDLE handle) +{ + int i; + DWORD nbBytes; + BOOL ret; + DVD_READ_STRUCTURE dvdReadStructure; + DVD_LAYER_DESCRIPTOR dvdLayerDescriptor; + struct COMPLETE_DVD_LAYER_DESCRIPTOR completeDvdLayerDescriptor; + DVD_COPYRIGHT_DESCRIPTOR dvdCopyrightDescriptor; + struct COMPLETE_DVD_MANUFACTURER_DESCRIPTOR completeDvdManufacturerDescriptor; + + dvdReadStructure.BlockByteOffset.QuadPart = 0; + dvdReadStructure.SessionId = 0; + dvdReadStructure.LayerNumber = 0; + + + /* DvdPhysicalDescriptor */ + dvdReadStructure.Format = 0; + + SetLastError(0xdeadbeef); + + /* Test whether this ioctl is supported */ + ret = DeviceIoControl(handle, IOCTL_DVD_READ_STRUCTURE, &dvdReadStructure, sizeof(DVD_READ_STRUCTURE), + &completeDvdLayerDescriptor, sizeof(struct COMPLETE_DVD_LAYER_DESCRIPTOR), &nbBytes, NULL); + if ((!ret && GetLastError() == ERROR_INVALID_FUNCTION) + || (!ret && GetLastError() == ERROR_NOT_SUPPORTED)) + { + skip("IOCTL_DVD_READ_STRUCTURE not supported\n"); + return; + } + + ok(ret || broken(GetLastError() == ERROR_NOT_READY) || broken(GetLastError() == ERROR_INVALID_PARAMETER), + "IOCTL_DVD_READ_STRUCTURE (DvdPhysicalDescriptor) failed, last error = %u\n", GetLastError()); + if(!ret) + return; + + /* Confirm there is always a header before the actual data */ + ok( completeDvdLayerDescriptor.Header.Length == 0x0802, "Length is 0x%04x instead of 0x0802\n", completeDvdLayerDescriptor.Header.Length); + ok( completeDvdLayerDescriptor.Header.Reserved[0] == 0, "Reserved[0] is %x instead of 0\n", completeDvdLayerDescriptor.Header.Reserved[0]); + ok( completeDvdLayerDescriptor.Header.Reserved[1] == 0, "Reserved[1] is %x instead of 0\n", completeDvdLayerDescriptor.Header.Reserved[1]); + + /* TODO: Also check completeDvdLayerDescriptor.Descriptor content (via IOCTL_SCSI_PASS_THROUGH_DIRECT ?) */ + + /* Insufficient output buffer */ + for(i=0; i