diff --git a/rostests/winetests/kernel32/CMakeLists.txt b/rostests/winetests/kernel32/CMakeLists.txt index 1b98b38a49d..3b67cd9ecfe 100644 --- a/rostests/winetests/kernel32/CMakeLists.txt +++ b/rostests/winetests/kernel32/CMakeLists.txt @@ -8,6 +8,7 @@ list(APPEND SOURCE codepage.c comm.c console.c + cpu.c debugger.c directory.c drive.c @@ -37,6 +38,11 @@ list(APPEND SOURCE testlist.c) add_executable(kernel32_winetest ${SOURCE} resource.rc) + +if(NOT MSVC) + add_target_compile_flags(kernel32_winetest "-Wno-format") +endif() + set_module_type(kernel32_winetest win32cui) add_importlibs(kernel32_winetest user32 advapi32 msvcrt kernel32 ntdll) add_cd_file(TARGET kernel32_winetest DESTINATION reactos/bin FOR all) diff --git a/rostests/winetests/kernel32/actctx.c b/rostests/winetests/kernel32/actctx.c index 5d4035a4aff..e3bc0ebf7b5 100644 --- a/rostests/winetests/kernel32/actctx.c +++ b/rostests/winetests/kernel32/actctx.c @@ -97,6 +97,7 @@ static const char manifest3[] = "" "" +"" "" "wndClass" " "; +static const char manifest5[] = +"" +"" +"" +"" +" " +" " +" " +"" +""; + static const char testdep_manifest1[] = "" "" @@ -304,8 +316,6 @@ static const WCHAR wndClass2W[] = {'w','n','d','C','l','a','s','s','2',0}; static const WCHAR wndClass3W[] = {'w','n','d','C','l','a','s','s','3',0}; -static const WCHAR acr_manifest[] = - {'a','c','r','.','m','a','n','i','f','e','s','t',0}; static WCHAR app_dir[MAX_PATH], exe_path[MAX_PATH], work_dir[MAX_PATH], work_dir_subdir[MAX_PATH]; static WCHAR app_manifest_path[MAX_PATH], manifest_path[MAX_PATH], depmanifest_path[MAX_PATH]; @@ -1729,6 +1739,29 @@ static void test_typelib_section(void) pReleaseActCtx(handle); } +static void test_allowDelayedBinding(void) +{ + HANDLE handle; + + if (!create_manifest_file("test5.manifest", manifest5, -1, NULL, NULL)) { + skip("Could not create manifest file\n"); + return; + } + + handle = test_create("test5.manifest"); + if (handle == INVALID_HANDLE_VALUE) { + win_skip("allowDelayedBinding attribute is not supported.\n"); + return; + } + + DeleteFileA("test5.manifest"); + DeleteFileA("testdep.manifest"); + if (handle != INVALID_HANDLE_VALUE) { + test_basic_info(handle, __LINE__); + pReleaseActCtx(handle); + } +} + static void test_actctx(void) { ULONG_PTR cookie; @@ -1994,6 +2027,7 @@ static void test_actctx(void) test_wndclass_section(); test_dllredirect_section(); test_typelib_section(); + test_allowDelayedBinding(); } static void test_app_manifest(void) @@ -2055,7 +2089,6 @@ static void run_child_process(void) static void init_paths(void) { LPWSTR ptr; - WCHAR last; static const WCHAR dot_manifest[] = {'.','M','a','n','i','f','e','s','t',0}; static const WCHAR backslash[] = {'\\',0}; @@ -2067,8 +2100,8 @@ static void init_paths(void) ptr[1] = 0; GetCurrentDirectoryW(MAX_PATH, work_dir); - last = work_dir[lstrlenW(work_dir) - 1]; - if (last != '\\' && last != '/') + ptr = work_dir + lstrlenW( work_dir ) - 1; + if (*ptr != '\\' && *ptr != '/') lstrcatW(work_dir, backslash); lstrcpyW(work_dir_subdir, work_dir); lstrcatW(work_dir_subdir, subdir); diff --git a/rostests/winetests/kernel32/change.c b/rostests/winetests/kernel32/change.c index 92269508d98..19e55bbbbc3 100755 --- a/rostests/winetests/kernel32/change.c +++ b/rostests/winetests/kernel32/change.c @@ -472,7 +472,7 @@ static void test_readdirectorychanges(void) r = WaitForSingleObject( ov.hEvent, 1000 ); ok( r == WAIT_OBJECT_0, "event should be ready\n" ); - ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n"); + ok( (NTSTATUS)ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n"); ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n"); pfni = (PFILE_NOTIFY_INFORMATION) buffer; @@ -502,7 +502,7 @@ static void test_readdirectorychanges(void) r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL); ok(r==TRUE, "should return true\n"); - ok( ov.Internal == STATUS_PENDING, "ov.Internal wrong\n"); + ok( (NTSTATUS)ov.Internal == STATUS_PENDING, "ov.Internal wrong\n"); ok( ov.InternalHigh == 1, "ov.InternalHigh wrong\n"); r = WaitForSingleObject( ov.hEvent, 0 ); @@ -514,10 +514,10 @@ static void test_readdirectorychanges(void) r = WaitForSingleObject( ov.hEvent, 1000 ); ok( r == WAIT_OBJECT_0, "should be ready\n" ); - ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n"); + ok( (NTSTATUS)ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n"); ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n"); - if (ov.Internal == STATUS_SUCCESS) + if ((NTSTATUS)ov.Internal == STATUS_SUCCESS) { r = GetOverlappedResult( hdir, &ov, &dwCount, TRUE ); ok( r == TRUE, "getoverlappedresult failed\n"); @@ -540,7 +540,7 @@ static void test_readdirectorychanges(void) r = WaitForSingleObject( ov.hEvent, 1000 ); ok( r == WAIT_OBJECT_0, "should be ready\n" ); - ok( ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n"); + ok( (NTSTATUS)ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n"); ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n"); /* test the recursive watch */ @@ -553,10 +553,19 @@ static void test_readdirectorychanges(void) r = WaitForSingleObject( ov.hEvent, 1000 ); ok( r == WAIT_OBJECT_0, "should be ready\n" ); - ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n"); - ok( ov.InternalHigh == 0x18, "ov.InternalHigh wrong\n"); + ok( (NTSTATUS)ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n"); + ok( ov.InternalHigh == 0x18 || ov.InternalHigh == 0x12 + 0x18, + "ov.InternalHigh wrong %lx\n", ov.InternalHigh); pfni = (PFILE_NOTIFY_INFORMATION) buffer; + if (pfni->NextEntryOffset) /* we may get a modified event on the parent dir */ + { + ok( pfni->NextEntryOffset == 0x12, "offset wrong %x\n", pfni->NextEntryOffset ); + ok( pfni->Action == FILE_ACTION_MODIFIED, "action wrong %d\n", pfni->Action ); + ok( pfni->FileNameLength == 3*sizeof(WCHAR), "len wrong\n" ); + ok( !memcmp(pfni->FileName,&szGa[1],3*sizeof(WCHAR)), "name wrong\n"); + pfni = (PFILE_NOTIFY_INFORMATION)((char *)pfni + pfni->NextEntryOffset); + } ok( pfni->NextEntryOffset == 0, "offset wrong\n" ); ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" ); ok( pfni->FileNameLength == 6*sizeof(WCHAR), "len wrong\n" ); @@ -589,7 +598,7 @@ static void test_readdirectorychanges(void) ok( pfni->FileNameLength == 6*sizeof(WCHAR), "len wrong %u\n", pfni->FileNameLength ); ok( !memcmp(pfni->FileName,&szGa[1],6*sizeof(WCHAR)), "name wrong\n" ); - ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n"); + ok( (NTSTATUS)ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n"); dwCount = (char *)&pfni->FileName[pfni->FileNameLength/sizeof(WCHAR)] - buffer; ok( ov.InternalHigh == dwCount, "ov.InternalHigh wrong %lu/%u\n",ov.InternalHigh, dwCount ); @@ -666,7 +675,7 @@ static void test_readdirectorychanges_null(void) r = WaitForSingleObject( ov.hEvent, 0 ); ok( r == WAIT_OBJECT_0, "event should be ready\n" ); - ok( ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n"); + ok( (NTSTATUS)ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n"); ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n"); ov.Internal = 0; @@ -687,7 +696,7 @@ static void test_readdirectorychanges_null(void) r = WaitForSingleObject( ov.hEvent, 1000 ); ok( r == WAIT_OBJECT_0, "should be ready\n" ); - ok( ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n"); + ok( (NTSTATUS)ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n"); ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n"); pfni = (PFILE_NOTIFY_INFORMATION) buffer; @@ -763,7 +772,7 @@ static void test_readdirectorychanges_filedir(void) r = WaitForSingleObject( ov.hEvent, 1000 ); ok( r == WAIT_OBJECT_0, "event should be ready\n" ); - ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n"); + ok( (NTSTATUS)ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n"); ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n"); pfni = (PFILE_NOTIFY_INFORMATION) buffer; diff --git a/rostests/winetests/kernel32/codepage.c b/rostests/winetests/kernel32/codepage.c index 8423c75b89a..ffac1ace743 100755 --- a/rostests/winetests/kernel32/codepage.c +++ b/rostests/winetests/kernel32/codepage.c @@ -20,6 +20,7 @@ */ #include +#include #include #include "wine/test.h" @@ -412,6 +413,521 @@ static void test_string_conversion(LPBOOL bUsedDefaultChar) ok(GetLastError() == 0xdeadbeef, "GetLastError() is %u\n", GetLastError()); } +static void test_utf7_encoding(void) +{ + WCHAR input[16]; + char output[16], expected[16]; + int i, len, expected_len; + + static const BOOL directly_encodable_table[] = + { + 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0x00 - 0x0F */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1F */ + 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* 0x20 - 0x2F */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 0x30 - 0x3F */ + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4F */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x50 - 0x5F */ + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6F */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 /* 0x70 - 0x7F */ + }; + static const char base64_encoding_table[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + const struct + { + /* inputs */ + WCHAR src[16]; + int srclen; + char *dst; + int dstlen; + /* expected outputs */ + char expected_dst[16]; + int chars_written; + int len; + } + tests[] = + { + /* tests string conversion with srclen=-1 */ + { + {0x4F60,0x597D,0x5417,0}, -1, output, sizeof(output) - 1, + "+T2BZfVQX-", 11, 11 + }, + /* tests string conversion with srclen=-2 */ + { + {0x4F60,0x597D,0x5417,0}, -2, output, sizeof(output) - 1, + "+T2BZfVQX-", 11, 11 + }, + /* tests string conversion with dstlen=strlen(expected_dst) */ + { + {0x4F60,0x597D,0x5417,0}, -1, output, 10, + "+T2BZfVQX-", 10, 0 + }, + /* tests string conversion with dstlen=strlen(expected_dst)+1 */ + { + {0x4F60,0x597D,0x5417,0}, -1, output, 11, + "+T2BZfVQX-", 11, 11 + }, + /* tests string conversion with dstlen=strlen(expected_dst)+2 */ + { + {0x4F60,0x597D,0x5417,0}, -1, output, 12, + "+T2BZfVQX-", 11, 11 + }, + /* tests dry run with dst=NULL and dstlen=0 */ + { + {0x4F60,0x597D,0x5417,0}, -1, NULL, 0, + {0}, 0, 11 + }, + /* tests dry run with dst!=NULL and dstlen=0 */ + { + {0x4F60,0x597D,0x5417,0}, -1, output, 0, + {0}, 0, 11 + }, + /* tests srclen < strlenW(src) with directly encodable chars */ + { + {'h','e','l','l','o',0}, 2, output, sizeof(output) - 1, + "he", 2, 2 + }, + /* tests srclen < strlenW(src) with non-directly encodable chars */ + { + {0x4F60,0x597D,0x5417,0}, 2, output, sizeof(output) - 1, + "+T2BZfQ-", 8, 8 + }, + /* tests a single null char */ + { + {0}, -1, output, sizeof(output) - 1, + "", 1, 1 + }, + /* tests a buffer that runs out while not encoding a UTF-7 sequence */ + { + {'h','e','l','l','o',0}, -1, output, 2, + "he", 2, 0 + }, + /* tests a buffer that runs out after writing 1 base64 character */ + { + {0x4F60,0x0001,0}, -1, output, 2, + "+T", 2, 0 + }, + /* tests a buffer that runs out after writing 2 base64 characters */ + { + {0x4F60,0x0001,0}, -1, output, 3, + "+T2", 3, 0 + }, + /* tests a buffer that runs out after writing 3 base64 characters */ + { + {0x4F60,0x0001,0}, -1, output, 4, + "+T2A", 4, 0 + }, + /* tests a buffer that runs out just after writing the + sign */ + { + {0x4F60,0}, -1, output, 1, + "+", 1, 0 + }, + /* tests a buffer that runs out just before writing the - sign + * the number of bits to encode here is evenly divisible by 6 */ + { + {0x4F60,0x597D,0x5417,0}, -1, output, 9, + "+T2BZfVQX", 9, 0 + }, + /* tests a buffer that runs out just before writing the - sign + * the number of bits to encode here is NOT evenly divisible by 6 */ + { + {0x4F60,0}, -1, output, 4, + "+T2", 3, 0 + }, + /* tests a buffer that runs out in the middle of escaping a + sign */ + { + {'+',0}, -1, output, 1, + "+", 1, 0 + } + }; + + /* test which characters are encoded if surrounded by non-encoded characters */ + for (i = 0; i <= 0xFFFF; i++) + { + input[0] = ' '; + input[1] = i; + input[2] = ' '; + input[3] = 0; + + memset(output, '#', sizeof(output) - 1); + output[sizeof(output) - 1] = 0; + + len = WideCharToMultiByte(CP_UTF7, 0, input, 4, output, sizeof(output) - 1, NULL, NULL); + + if (i == '+') + { + /* '+' is a special case and is encoded as "+-" */ + expected_len = 5; + strcpy(expected, " +- "); + } + else if (i <= 0x7F && directly_encodable_table[i]) + { + /* encodes directly */ + expected_len = 4; + sprintf(expected, " %c ", i); + } + else + { + /* base64-encodes */ + expected_len = 8; + sprintf(expected, " +%c%c%c- ", + base64_encoding_table[(i & 0xFC00) >> 10], + base64_encoding_table[(i & 0x03F0) >> 4], + base64_encoding_table[(i & 0x000F) << 2]); + } + + ok(len == expected_len, "i=0x%04x: expected len=%i, got len=%i\n", i, expected_len, len); + ok(memcmp(output, expected, expected_len) == 0, + "i=0x%04x: expected output='%s', got output='%s'\n", i, expected, output); + ok(output[expected_len] == '#', "i=0x%04x: expected output[%i]='#', got output[%i]=%i\n", + i, expected_len, expected_len, output[expected_len]); + } + + /* test which one-byte characters are absorbed into surrounding base64 blocks + * (Windows always ends the base64 block when it encounters a directly encodable character) */ + for (i = 0; i <= 0xFFFF; i++) + { + input[0] = 0x2672; + input[1] = i; + input[2] = 0x2672; + input[3] = 0; + + memset(output, '#', sizeof(output) - 1); + output[sizeof(output) - 1] = 0; + + len = WideCharToMultiByte(CP_UTF7, 0, input, 4, output, sizeof(output) - 1, NULL, NULL); + + if (i == '+') + { + /* '+' is a special case and is encoded as "+-" */ + expected_len = 13; + strcpy(expected, "+JnI-+-+JnI-"); + } + else if (i <= 0x7F && directly_encodable_table[i]) + { + /* encodes directly */ + expected_len = 12; + sprintf(expected, "+JnI-%c+JnI-", i); + } + else + { + /* base64-encodes */ + expected_len = 11; + sprintf(expected, "+Jn%c%c%c%cZy-", + base64_encoding_table[8 | ((i & 0xC000) >> 14)], + base64_encoding_table[(i & 0x3F00) >> 8], + base64_encoding_table[(i & 0x00FC) >> 2], + base64_encoding_table[((i & 0x0003) << 4) | 2]); + } + + ok(len == expected_len, "i=0x%04x: expected len=%i, got len=%i\n", i, expected_len, len); + ok(memcmp(output, expected, expected_len) == 0, + "i=0x%04x: expected output='%s', got output='%s'\n", i, expected, output); + ok(output[expected_len] == '#', "i=0x%04x: expected output[%i]='#', got output[%i]=%i\n", + i, expected_len, expected_len, output[expected_len]); + } + + for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) + { + memset(output, '#', sizeof(output) - 1); + output[sizeof(output) - 1] = 0; + SetLastError(0xdeadbeef); + + len = WideCharToMultiByte(CP_UTF7, 0, tests[i].src, tests[i].srclen, + tests[i].dst, tests[i].dstlen, NULL, NULL); + + if (!tests[i].len) + { + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, + "tests[%i]: expected error=0x%x, got error=0x%x\n", + i, ERROR_INSUFFICIENT_BUFFER, GetLastError()); + } + ok(len == tests[i].len, "tests[%i]: expected len=%i, got len=%i\n", i, tests[i].len, len); + + if (tests[i].dst) + { + ok(memcmp(tests[i].dst, tests[i].expected_dst, tests[i].chars_written) == 0, + "tests[%i]: expected dst='%s', got dst='%s'\n", + i, tests[i].expected_dst, tests[i].dst); + ok(tests[i].dst[tests[i].chars_written] == '#', + "tests[%i]: expected dst[%i]='#', got dst[%i]=%i\n", + i, tests[i].chars_written, tests[i].chars_written, tests[i].dst[tests[i].chars_written]); + } + } +} + +static void test_utf7_decoding(void) +{ + char input[32]; + WCHAR output[32], expected[32]; + int i, len, expected_len; + + static const signed char base64_decoding_table[] = + { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00-0x0F */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10-0x1F */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20-0x2F */ + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30-0x3F */ + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40-0x4F */ + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50-0x5F */ + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60-0x6F */ + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 /* 0x70-0x7F */ + }; + + struct + { + /* inputs */ + char src[32]; + int srclen; + WCHAR *dst; + int dstlen; + /* expected outputs */ + WCHAR expected_dst[32]; + int chars_written; + int len; + } + tests[] = + { + /* tests string conversion with srclen=-1 */ + { + "+T2BZfQ-", -1, output, sizeof(output) / sizeof(WCHAR) - 1, + {0x4F60,0x597D,0}, 3, 3 + }, + /* tests string conversion with srclen=-2 */ + { + "+T2BZfQ-", -2, output, sizeof(output) / sizeof(WCHAR) - 1, + {0x4F60,0x597D,0}, 3, 3 + }, + /* tests string conversion with dstlen=strlen(expected_dst) */ + { + "+T2BZfQ-", -1, output, 2, + {0x4F60,0x597D}, 2, 0 + }, + /* tests string conversion with dstlen=strlen(expected_dst)+1 */ + { + "+T2BZfQ-", -1, output, 3, + {0x4F60,0x597D,0}, 3, 3 + }, + /* tests string conversion with dstlen=strlen(expected_dst)+2 */ + { + "+T2BZfQ-", -1, output, 4, + {0x4F60,0x597D,0}, 3, 3 + }, + /* tests dry run with dst=NULL and dstlen=0 */ + { + "+T2BZfQ-", -1, NULL, 0, + {0}, 0, 3 + }, + /* tests dry run with dst!=NULL and dstlen=0 */ + { + "+T2BZfQ-", -1, output, 0, + {0}, 0, 3 + }, + /* tests ill-formed UTF-7: 6 bits, not enough for a byte pair */ + { + "+T-+T-+T-hello", -1, output, sizeof(output) / sizeof(WCHAR) - 1, + {'h','e','l','l','o',0}, 6, 6 + }, + /* tests ill-formed UTF-7: 12 bits, not enough for a byte pair */ + { + "+T2-+T2-+T2-hello", -1, output, sizeof(output) / sizeof(WCHAR) - 1, + {'h','e','l','l','o',0}, 6, 6 + }, + /* tests ill-formed UTF-7: 18 bits, not a multiple of 16 and the last bit is a 1 */ + { + "+T2B-+T2B-+T2B-hello", -1, output, sizeof(output) / sizeof(WCHAR) - 1, + {0x4F60,0x4F60,0x4F60,'h','e','l','l','o',0}, 9, 9 + }, + /* tests ill-formed UTF-7: 24 bits, a multiple of 8 but not a multiple of 16 */ + { + "+T2BZ-+T2BZ-+T2BZ-hello", -1, output, sizeof(output) / sizeof(WCHAR) - 1, + {0x4F60,0x4F60,0x4F60,'h','e','l','l','o',0}, 9, 9 + }, + /* tests UTF-7 followed by characters that should be encoded but aren't */ + { + "+T2BZ-\x82\xFE", -1, output, sizeof(output) / sizeof(WCHAR) - 1, + {0x4F60,0x0082,0x00FE,0}, 4, 4 + }, + /* tests srclen > strlen(src) */ + { + "a\0b", 4, output, sizeof(output) / sizeof(WCHAR) - 1, + {'a',0,'b',0}, 4, 4 + }, + /* tests srclen < strlen(src) outside of a UTF-7 sequence */ + { + "hello", 2, output, sizeof(output) / sizeof(WCHAR) - 1, + {'h','e'}, 2, 2 + }, + /* tests srclen < strlen(src) inside of a UTF-7 sequence */ + { + "+T2BZfQ-", 4, output, sizeof(output) / sizeof(WCHAR) - 1, + {0x4F60}, 1, 1 + }, + /* tests srclen < strlen(src) right at the beginning of a UTF-7 sequence */ + { + "hi+T2A-", 3, output, sizeof(output) / sizeof(WCHAR) - 1, + {'h','i'}, 2, 2 + }, + /* tests srclen < strlen(src) right at the end of a UTF-7 sequence */ + { + "+T2A-hi", 5, output, sizeof(output) / sizeof(WCHAR) - 1, + {0x4F60}, 1, 1 + }, + /* tests srclen < strlen(src) at the beginning of an escaped + sign */ + { + "hi+-", 3, output, sizeof(output) / sizeof(WCHAR) - 1, + {'h','i'}, 2, 2 + }, + /* tests srclen < strlen(src) at the end of an escaped + sign */ + { + "+-hi", 2, output, sizeof(output) / sizeof(WCHAR) - 1, + {'+'}, 1, 1 + }, + /* tests len=0 but no error */ + { + "+", 1, output, sizeof(output) / sizeof(WCHAR) - 1, + {0}, 0, 0 + }, + /* tests a single null char */ + { + "", -1, output, sizeof(output) / sizeof(WCHAR) - 1, + {0}, 1, 1 + }, + /* tests a buffer that runs out while not decoding a UTF-7 sequence */ + { + "hello", -1, output, 2, + {'h','e'}, 2, 0 + }, + /* tests a buffer that runs out in the middle of decoding a UTF-7 sequence */ + { + "+T2BZfQ-", -1, output, 1, + {0x4F60}, 1, 0 + } + }; + + /* test which one-byte characters remove stray + signs */ + for (i = 0; i < 256; i++) + { + sprintf(input, "+%c+AAA", i); + + memset(output, 0x23, sizeof(output) - sizeof(WCHAR)); + output[sizeof(output) / sizeof(WCHAR) - 1] = 0; + + len = MultiByteToWideChar(CP_UTF7, 0, input, 7, output, sizeof(output) / sizeof(WCHAR) - 1); + + if (i == '-') + { + /* removes the - sign */ + expected_len = 3; + expected[0] = 0x002B; + expected[1] = 0; + expected[2] = 0; + } + else if (i <= 0x7F && base64_decoding_table[i] != -1) + { + /* absorbs the character into the base64 sequence */ + expected_len = 2; + expected[0] = (base64_decoding_table[i] << 10) | 0x03E0; + expected[1] = 0; + } + else + { + /* removes the + sign */ + expected_len = 3; + expected[0] = i; + expected[1] = 0; + expected[2] = 0; + } + expected[expected_len] = 0x2323; + + ok(len == expected_len, "i=0x%02x: expected len=%i, got len=%i\n", i, expected_len, len); + ok(memcmp(output, expected, (expected_len + 1) * sizeof(WCHAR)) == 0, + "i=0x%02x: expected output=%s, got output=%s\n", + i, wine_dbgstr_wn(expected, expected_len + 1), wine_dbgstr_wn(output, expected_len + 1)); + } + + /* test which one-byte characters terminate a sequence + * also test whether the unfinished byte pair is discarded or not */ + for (i = 0; i < 256; i++) + { + sprintf(input, "+B%c+AAA", i); + + memset(output, 0x23, sizeof(output) - sizeof(WCHAR)); + output[sizeof(output) / sizeof(WCHAR) - 1] = 0; + + len = MultiByteToWideChar(CP_UTF7, 0, input, 8, output, sizeof(output) / sizeof(WCHAR) - 1); + + if (i == '-') + { + /* explicitly terminates */ + expected_len = 2; + expected[0] = 0; + expected[1] = 0; + } + else if (i <= 0x7F) + { + if (base64_decoding_table[i] != -1) + { + /* absorbs the character into the base64 sequence */ + expected_len = 3; + expected[0] = 0x0400 | (base64_decoding_table[i] << 4) | 0x000F; + expected[1] = 0x8000; + expected[2] = 0; + } + else + { + /* implicitly terminates and discards the unfinished byte pair */ + expected_len = 3; + expected[0] = i; + expected[1] = 0; + expected[2] = 0; + } + } + else + { + /* implicitly terminates but does not the discard unfinished byte pair */ + expected_len = 3; + expected[0] = i; + expected[1] = 0x0400; + expected[2] = 0; + } + expected[expected_len] = 0x2323; + + ok(len == expected_len, "i=0x%02x: expected len=%i, got len=%i\n", i, expected_len, len); + ok(memcmp(output, expected, (expected_len + 1) * sizeof(WCHAR)) == 0, + "i=0x%02x: expected output=%s, got output=%s\n", + i, wine_dbgstr_wn(expected, expected_len + 1), wine_dbgstr_wn(output, expected_len + 1)); + } + + for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) + { + memset(output, 0x23, sizeof(output) - sizeof(WCHAR)); + output[sizeof(output) / sizeof(WCHAR) - 1] = 0; + SetLastError(0xdeadbeef); + + len = MultiByteToWideChar(CP_UTF7, 0, tests[i].src, tests[i].srclen, + tests[i].dst, tests[i].dstlen); + + tests[i].expected_dst[tests[i].chars_written] = 0x2323; + + if (!tests[i].len && tests[i].chars_written) + { + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, + "tests[%i]: expected error=0x%x, got error=0x%x\n", + i, ERROR_INSUFFICIENT_BUFFER, GetLastError()); + } + ok(len == tests[i].len, "tests[%i]: expected len=%i, got len=%i\n", i, tests[i].len, len); + + if (tests[i].dst) + { + ok(memcmp(tests[i].dst, tests[i].expected_dst, (tests[i].chars_written + 1) * sizeof(WCHAR)) == 0, + "tests[%i]: expected dst=%s, got dst=%s\n", + i, wine_dbgstr_wn(tests[i].expected_dst, tests[i].chars_written + 1), + wine_dbgstr_wn(tests[i].dst, tests[i].chars_written + 1)); + } + } +} + static void test_undefined_byte_char(void) { static const struct tag_testset { @@ -618,6 +1134,9 @@ START_TEST(codepage) test_string_conversion(NULL); test_string_conversion(&bUsedDefaultChar); + test_utf7_encoding(); + test_utf7_decoding(); + test_undefined_byte_char(); test_threadcp(); } diff --git a/rostests/winetests/kernel32/cpu.c b/rostests/winetests/kernel32/cpu.c new file mode 100644 index 00000000000..405d990a069 --- /dev/null +++ b/rostests/winetests/kernel32/cpu.c @@ -0,0 +1,77 @@ +/* + * Unit test suite for cpu functions + * + * Copyright 2014 Michael Müller + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "wine/test.h" +#include "winbase.h" +#include "winnls.h" + +static BOOL (WINAPI *pGetNumaProcessorNode)(UCHAR, PUCHAR); + +static void InitFunctionPointers(void) +{ + HMODULE hkernel32 = GetModuleHandleA("kernel32"); + + pGetNumaProcessorNode = (void *)GetProcAddress(hkernel32, "GetNumaProcessorNode"); +} + +static void test_GetNumaProcessorNode(void) +{ + SYSTEM_INFO si; + UCHAR node; + BOOL ret; + int i; + + if (!pGetNumaProcessorNode) + { + win_skip("GetNumaProcessorNode() is missing\n"); + return; + } + + GetSystemInfo(&si); + + for (i = 0; i < 256; i++) + { + ret = pGetNumaProcessorNode(i, &node); + if (i < si.dwNumberOfProcessors) + { + ok(ret, "expected TRUE, got FALSE for processor %d\n", i); + ok(node != 0xFF, "expected node != 0xFF, but got 0xFF\n"); + } + else + { + ok(!ret, "expected FALSE, got TRUE for processor %d\n", i); + ok(node == 0xFF, "expected node == 0xFF, but got %x\n", node); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); + } + } + + /* crashes on windows */ + if (0) + { + ok(!pGetNumaProcessorNode(0, NULL), "expected return value FALSE, got TRUE\n"); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); + } +} + +START_TEST(cpu) +{ + InitFunctionPointers(); + test_GetNumaProcessorNode(); +} diff --git a/rostests/winetests/kernel32/file.c b/rostests/winetests/kernel32/file.c index 2e4871ce7b0..051e19cf401 100755 --- a/rostests/winetests/kernel32/file.c +++ b/rostests/winetests/kernel32/file.c @@ -46,10 +46,10 @@ static HANDLE (WINAPI *pOpenFileById)(HANDLE, LPFILE_ID_DESCRIPTOR, DWORD, DWORD static BOOL (WINAPI *pSetFileValidData)(HANDLE, LONGLONG); static HRESULT (WINAPI *pCopyFile2)(PCWSTR,PCWSTR,COPYFILE2_EXTENDED_PARAMETERS*); static HANDLE (WINAPI *pCreateFile2)(LPCWSTR, DWORD, DWORD, DWORD, CREATEFILE2_EXTENDED_PARAMETERS*); +static DWORD (WINAPI* pGetFinalPathNameByHandleA)(HANDLE, LPSTR, DWORD, DWORD); +static DWORD (WINAPI* pGetFinalPathNameByHandleW)(HANDLE, LPWSTR, DWORD, DWORD); -/* keep filename and filenameW the same */ static const char filename[] = "testfile.xxx"; -static const WCHAR filenameW[] = { 't','e','s','t','f','i','l','e','.','x','x','x',0 }; static const char sillytext[] = "en larvig liten text dx \033 gx hej 84 hej 4484 ! \001\033 bla bl\na.. bla bla." "1234 43 4kljf lf &%%%&&&&&& 34 4 34 3############# 33 3 3 3 # 3## 3" @@ -85,6 +85,8 @@ static void InitFunctionPointers(void) pSetFileValidData = (void *) GetProcAddress(hkernel32, "SetFileValidData"); pCopyFile2 = (void *) GetProcAddress(hkernel32, "CopyFile2"); pCreateFile2 = (void *) GetProcAddress(hkernel32, "CreateFile2"); + pGetFinalPathNameByHandleA = (void *) GetProcAddress(hkernel32, "GetFinalPathNameByHandleA"); + pGetFinalPathNameByHandleW = (void *) GetProcAddress(hkernel32, "GetFinalPathNameByHandleW"); } static void test__hread( void ) @@ -2562,7 +2564,7 @@ static void test_FindNextFileA(void) ok ( err == ERROR_NO_MORE_FILES, "GetLastError should return ERROR_NO_MORE_FILES\n"); } -static void test_FindFirstFileExA(FINDEX_SEARCH_OPS search_ops) +static void test_FindFirstFileExA(FINDEX_INFO_LEVELS level, FINDEX_SEARCH_OPS search_ops, DWORD flags) { WIN32_FIND_DATAA search_results; HANDLE handle; @@ -2574,27 +2576,45 @@ static void test_FindFirstFileExA(FINDEX_SEARCH_OPS search_ops) return; } + trace("Running FindFirstFileExA tests with level=%d, search_ops=%d, flags=%u\n", + level, search_ops, flags); + CreateDirectoryA("test-dir", NULL); _lclose(_lcreat("test-dir\\file1", 0)); _lclose(_lcreat("test-dir\\file2", 0)); CreateDirectoryA("test-dir\\dir1", NULL); SetLastError(0xdeadbeef); - handle = pFindFirstFileExA("test-dir\\*", FindExInfoStandard, &search_results, search_ops, NULL, 0); + handle = pFindFirstFileExA("test-dir\\*", level, &search_results, search_ops, NULL, flags); if (handle == INVALID_HANDLE_VALUE && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) { win_skip("FindFirstFileExA is not implemented\n"); goto cleanup; } - ok(handle != INVALID_HANDLE_VALUE, "FindFirstFile failed (err=%u)\n", GetLastError()); - ok(strcmp(search_results.cFileName, ".") == 0, "First entry should be '.', is %s\n", search_results.cFileName); + if ((flags & FIND_FIRST_EX_LARGE_FETCH) && handle == INVALID_HANDLE_VALUE && GetLastError() == ERROR_INVALID_PARAMETER) + { + win_skip("FindFirstFileExA flag FIND_FIRST_EX_LARGE_FETCH not supported, skipping test\n"); + goto cleanup; + } + if ((level == FindExInfoBasic) && handle == INVALID_HANDLE_VALUE && GetLastError() == ERROR_INVALID_PARAMETER) + { + win_skip("FindFirstFileExA level FindExInfoBasic not supported, skipping test\n"); + goto cleanup; + } #define CHECK_NAME(fn) (strcmp((fn), "file1") == 0 || strcmp((fn), "file2") == 0 || strcmp((fn), "dir1") == 0) +#define CHECK_LEVEL(fn) (level != FindExInfoBasic || !(fn)[0]) + + ok(handle != INVALID_HANDLE_VALUE, "FindFirstFile failed (err=%u)\n", GetLastError()); + ok(strcmp(search_results.cFileName, ".") == 0, "First entry should be '.', is %s\n", search_results.cFileName); + ok(CHECK_LEVEL(search_results.cAlternateFileName), "FindFirstFile unexpectedly returned an alternate filename\n"); ok(FindNextFileA(handle, &search_results), "Fetching second file failed\n"); ok(strcmp(search_results.cFileName, "..") == 0, "Second entry should be '..' is %s\n", search_results.cFileName); + ok(CHECK_LEVEL(search_results.cAlternateFileName), "FindFirstFile unexpectedly returned an alternate filename\n"); ok(FindNextFileA(handle, &search_results), "Fetching third file failed\n"); ok(CHECK_NAME(search_results.cFileName), "Invalid third entry - %s\n", search_results.cFileName); + ok(CHECK_LEVEL(search_results.cAlternateFileName), "FindFirstFile unexpectedly returned an alternate filename\n"); SetLastError(0xdeadbeef); ret = FindNextFileA(handle, &search_results); @@ -2603,22 +2623,43 @@ static void test_FindFirstFileExA(FINDEX_SEARCH_OPS search_ops) skip("File system supports directory filtering\n"); /* Results from the previous call are not cleared */ ok(strcmp(search_results.cFileName, "dir1") == 0, "Third entry should be 'dir1' is %s\n", search_results.cFileName); - FindClose( handle ); - goto cleanup; + ok(CHECK_LEVEL(search_results.cAlternateFileName), "FindFirstFile unexpectedly returned an alternate filename\n"); + + } + else + { + ok(ret, "Fetching fourth file failed\n"); + ok(CHECK_NAME(search_results.cFileName), "Invalid fourth entry - %s\n", search_results.cFileName); + ok(CHECK_LEVEL(search_results.cAlternateFileName), "FindFirstFile unexpectedly returned an alternate filename\n"); + + ok(FindNextFileA(handle, &search_results), "Fetching fifth file failed\n"); + ok(CHECK_NAME(search_results.cFileName), "Invalid fifth entry - %s\n", search_results.cFileName); + ok(CHECK_LEVEL(search_results.cAlternateFileName), "FindFirstFile unexpectedly returned an alternate filename\n"); + + ok(FindNextFileA(handle, &search_results) == FALSE, "Fetching sixth file should fail\n"); } - ok(ret, "Fetching fourth file failed\n"); - ok(CHECK_NAME(search_results.cFileName), "Invalid fourth entry - %s\n", search_results.cFileName); - - ok(FindNextFileA(handle, &search_results), "Fetching fifth file failed\n"); - ok(CHECK_NAME(search_results.cFileName), "Invalid fifth entry - %s\n", search_results.cFileName); - #undef CHECK_NAME - - ok(FindNextFileA(handle, &search_results) == FALSE, "Fetching sixth file should fail\n"); +#undef CHECK_LEVEL FindClose( handle ); + /* Most Windows systems seem to ignore the FIND_FIRST_EX_CASE_SENSITIVE flag. Unofficial documentation + * suggests that there are registry keys and that it might depend on the used filesystem. */ + SetLastError(0xdeadbeef); + handle = pFindFirstFileExA("TEST-DIR\\*", level, &search_results, search_ops, NULL, flags); + if (flags & FIND_FIRST_EX_CASE_SENSITIVE) + { + ok(handle != INVALID_HANDLE_VALUE || GetLastError() == ERROR_PATH_NOT_FOUND, + "Unexpected error %x, expected valid handle or ERROR_PATH_NOT_FOUND\n", GetLastError()); + trace("FindFirstFileExA flag FIND_FIRST_EX_CASE_SENSITIVE is %signored\n", + (handle == INVALID_HANDLE_VALUE) ? "not " : ""); + } + else + ok(handle != INVALID_HANDLE_VALUE, "Unexpected error %x, expected valid handle\n", GetLastError()); + if (handle != INVALID_HANDLE_VALUE) + FindClose( handle ); + cleanup: DeleteFileA("test-dir\\file1"); DeleteFileA("test-dir\\file2"); @@ -4145,6 +4186,203 @@ todo_wine } } + +static void test_GetFinalPathNameByHandleA(void) +{ + static char prefix[] = "GetFinalPathNameByHandleA"; + static char dos_prefix[] = "\\\\?\\"; + char temp_path[MAX_PATH], test_path[MAX_PATH]; + char long_path[MAX_PATH], result_path[MAX_PATH]; + char dos_path[sizeof(dos_prefix) + MAX_PATH]; + HANDLE hFile; + DWORD count; + UINT ret; + + if (!pGetFinalPathNameByHandleA) + { + win_skip("GetFinalPathNameByHandleA is missing\n"); + return; + } + + /* Test calling with INVALID_HANDLE_VALUE */ + SetLastError(0xdeadbeaf); + count = pGetFinalPathNameByHandleA(INVALID_HANDLE_VALUE, result_path, MAX_PATH, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); + ok(count == 0, "Expected length 0, got %d\n", count); + ok(GetLastError() == ERROR_INVALID_HANDLE, "Expected ERROR_INVALID_HANDLE, got %x\n", GetLastError()); + + count = GetTempPathA(MAX_PATH, temp_path); + ok(count, "Failed to get temp path, error %x\n", GetLastError()); + if (!count) return; + + ret = GetTempFileNameA(temp_path, prefix, 0, test_path); + ok(ret != 0, "GetTempFileNameA error %x\n", GetLastError()); + if (!ret) return; + + ret = GetLongPathNameA(test_path, long_path, MAX_PATH); + ok(ret != 0, "GetLongPathNameA error %x\n", GetLastError()); + if (!ret) return; + + hFile = CreateFileA(test_path, GENERIC_READ | GENERIC_WRITE, 0, NULL, + CREATE_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, 0); + ok(hFile != INVALID_HANDLE_VALUE, "CreateFileA error %x\n", GetLastError()); + if (hFile == INVALID_HANDLE_VALUE) return; + + dos_path[0] = 0; + strcat(dos_path, dos_prefix); + strcat(dos_path, long_path); + + /* Test VOLUME_NAME_DOS with sufficient buffer size */ + memset(result_path, 0x11, sizeof(result_path)); + count = pGetFinalPathNameByHandleA(hFile, result_path, MAX_PATH, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); + ok(count == strlen(dos_path), "Expected length %u, got %u\n", (DWORD)strlen(dos_path), count); + if (count && count <= MAX_PATH) + ok(lstrcmpiA(dos_path, result_path) == 0, "Expected %s, got %s\n", dos_path, result_path); + + /* Test VOLUME_NAME_DOS with insufficient buffer size */ + memset(result_path, 0x11, sizeof(result_path)); + count = pGetFinalPathNameByHandleA(hFile, result_path, strlen(dos_path)-2, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); + ok(count == strlen(dos_path), "Expected length %u, got %u\n", (DWORD)strlen(dos_path), count); + ok(result_path[0] == 0x11, "Result path was modified\n"); + + memset(result_path, 0x11, sizeof(result_path)); + count = pGetFinalPathNameByHandleA(hFile, result_path, strlen(dos_path)-1, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); + ok(count == strlen(dos_path), "Expected length %u, got %u\n", (DWORD)strlen(dos_path), count); + ok(result_path[0] == 0x11, "Result path was modified\n"); + + memset(result_path, 0x11, sizeof(result_path)); + count = pGetFinalPathNameByHandleA(hFile, result_path, strlen(dos_path), FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); + ok(count == strlen(dos_path), "Expected length %u, got %u\n", (DWORD)strlen(dos_path), count); + ok(result_path[0] == 0x11, "Result path was modified\n"); + + memset(result_path, 0x11, sizeof(result_path)); + count = pGetFinalPathNameByHandleA(hFile, result_path, strlen(dos_path)+1, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); + ok(count == strlen(dos_path), "Expected length %u, got %u\n", (DWORD)strlen(dos_path), count); + ok(result_path[0] != 0x11, "Result path was not modified\n"); + ok(result_path[strlen(dos_path)+1] == 0x11, "Buffer overflow\n"); + + CloseHandle(hFile); +} + +static void test_GetFinalPathNameByHandleW(void) +{ + static WCHAR prefix[] = {'G','e','t','F','i','n','a','l','P','a','t','h','N','a','m','e','B','y','H','a','n','d','l','e','W','\0'}; + static WCHAR dos_prefix[] = {'\\','\\','?','\\','\0'}; + WCHAR temp_path[MAX_PATH], test_path[MAX_PATH]; + WCHAR long_path[MAX_PATH], result_path[MAX_PATH]; + WCHAR dos_path[MAX_PATH + sizeof(dos_prefix)]; + WCHAR drive_part[MAX_PATH]; + WCHAR *file_part; + WCHAR volume_path[MAX_PATH+50]; + WCHAR nt_path[2*MAX_PATH]; + HANDLE hFile; + DWORD count; + UINT ret; + + if (!pGetFinalPathNameByHandleW) + { + win_skip("GetFinalPathNameByHandleW is missing\n"); + return; + } + + /* Test calling with INVALID_HANDLE_VALUE */ + SetLastError(0xdeadbeaf); + count = pGetFinalPathNameByHandleW(INVALID_HANDLE_VALUE, result_path, MAX_PATH, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); + ok(count == 0, "Expected length 0, got %d\n", count); + ok(GetLastError() == ERROR_INVALID_HANDLE, "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError()); + + count = GetTempPathW(MAX_PATH, temp_path); + ok(count, "Failed to get temp path, error %d\n", GetLastError()); + if (!count) return; + + ret = GetTempFileNameW(temp_path, prefix, 0, test_path); + ok(ret != 0, "GetTempFileNameW error %d\n", GetLastError()); + if (!ret) return; + + ret = GetLongPathNameW(test_path, long_path, MAX_PATH); + ok(ret != 0, "GetLongPathNameW error %d\n", GetLastError()); + if (!ret) return; + + hFile = CreateFileW(test_path, GENERIC_READ | GENERIC_WRITE, 0, NULL, + CREATE_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, 0); + ok(hFile != INVALID_HANDLE_VALUE, "CreateFileW error %d\n", GetLastError()); + if (hFile == INVALID_HANDLE_VALUE) return; + + dos_path[0] = 0; + lstrcatW(dos_path, dos_prefix); + lstrcatW(dos_path, long_path); + + /* Test VOLUME_NAME_DOS with sufficient buffer size */ + memset(result_path, 0x11, sizeof(result_path)); + count = pGetFinalPathNameByHandleW(hFile, result_path, MAX_PATH, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); + ok(count == lstrlenW(dos_path), "Expected length %d, got %d\n", lstrlenW(dos_path), count); + if (count && count <= MAX_PATH) + ok(lstrcmpiW(dos_path, result_path) == 0, "Expected %s, got %s\n", wine_dbgstr_w(dos_path), wine_dbgstr_w(result_path)); + + /* Test VOLUME_NAME_DOS with insufficient buffer size */ + memset(result_path, 0x11, sizeof(result_path)); + count = pGetFinalPathNameByHandleW(hFile, result_path, lstrlenW(dos_path)-1, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); + ok(count == lstrlenW(dos_path) + 1, "Expected length %d, got %d\n", lstrlenW(dos_path) + 1, count); + ok(result_path[0] == 0x1111, "Result path was modified\n"); + + memset(result_path, 0x11, sizeof(result_path)); + count = pGetFinalPathNameByHandleW(hFile, result_path, lstrlenW(dos_path), FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); + ok(count == lstrlenW(dos_path) + 1, "Expected length %d, got %d\n", lstrlenW(dos_path) + 1, count); + ok(result_path[0] == 0x1111, "Result path was modified\n"); + + memset(result_path, 0x11, sizeof(result_path)); + count = pGetFinalPathNameByHandleW(hFile, result_path, lstrlenW(dos_path)+1, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); + ok(count == lstrlenW(dos_path), "Expected length %d, got %d\n", lstrlenW(dos_path), count); + ok(result_path[0] != 0x1111, "Result path was not modified\n"); + ok(result_path[lstrlenW(dos_path)+1] == 0x1111, "Buffer overflow\n"); + + if (!GetVolumePathNameW(long_path, drive_part, MAX_PATH)) + { + ok(0, "Failed to get drive part, error: %d\n", GetLastError()); + CloseHandle(hFile); + return; + } + + if (!GetVolumeNameForVolumeMountPointW(drive_part, volume_path, sizeof(volume_path) / sizeof(WCHAR))) + ok(0, "GetVolumeNameForVolumeMountPointW failed, error: %d\n", GetLastError()); + else + { + /* Test for VOLUME_NAME_GUID */ + lstrcatW(volume_path, long_path + lstrlenW(drive_part)); + memset(result_path, 0x11, sizeof(result_path)); + count = pGetFinalPathNameByHandleW(hFile, result_path, MAX_PATH, FILE_NAME_NORMALIZED | VOLUME_NAME_GUID); + ok(count == lstrlenW(volume_path), "Expected length %d, got %d\n", lstrlenW(volume_path), count); + if (count && count <= MAX_PATH) + ok(lstrcmpiW(volume_path, result_path) == 0, "Expected %s, got %s\n", + wine_dbgstr_w(volume_path), wine_dbgstr_w(result_path)); + } + + /* Test for VOLUME_NAME_NONE */ + file_part = long_path + lstrlenW(drive_part) - 1; + memset(result_path, 0x11, sizeof(result_path)); + count = pGetFinalPathNameByHandleW(hFile, result_path, MAX_PATH, FILE_NAME_NORMALIZED | VOLUME_NAME_NONE); + ok(count == lstrlenW(file_part), "Expected length %d, got %d\n", lstrlenW(file_part), count); + if (count && count <= MAX_PATH) + ok(lstrcmpiW(file_part, result_path) == 0, "Expected %s, got %s\n", + wine_dbgstr_w(file_part), wine_dbgstr_w(result_path)); + + drive_part[lstrlenW(drive_part)-1] = 0; + if (!QueryDosDeviceW(drive_part, nt_path, sizeof(nt_path) / sizeof(WCHAR))) + ok(0, "QueryDosDeviceW failed, error: %d\n", GetLastError()); + else + { + /* Test for VOLUME_NAME_NT */ + lstrcatW(nt_path, file_part); + memset(result_path, 0x11, sizeof(result_path)); + count = pGetFinalPathNameByHandleW(hFile, result_path, MAX_PATH, FILE_NAME_NORMALIZED | VOLUME_NAME_NT); + ok(count == lstrlenW(nt_path), "Expected length %d, got %d\n", lstrlenW(nt_path), count); + if (count && count <= MAX_PATH) + ok(lstrcmpiW(nt_path, result_path) == 0, "Expected %s, got %s\n", + wine_dbgstr_w(nt_path), wine_dbgstr_w(result_path)); + } + + CloseHandle(hFile); +} + START_TEST(file) { InitFunctionPointers(); @@ -4171,9 +4409,15 @@ START_TEST(file) test_MoveFileW(); test_FindFirstFileA(); test_FindNextFileA(); - test_FindFirstFileExA(0); + test_FindFirstFileExA(FindExInfoStandard, 0, 0); + test_FindFirstFileExA(FindExInfoStandard, 0, FIND_FIRST_EX_CASE_SENSITIVE); + test_FindFirstFileExA(FindExInfoStandard, 0, FIND_FIRST_EX_LARGE_FETCH); + test_FindFirstFileExA(FindExInfoBasic, 0, 0); /* FindExLimitToDirectories is ignored if the file system doesn't support directory filtering */ - test_FindFirstFileExA(FindExSearchLimitToDirectories); + test_FindFirstFileExA(FindExInfoStandard, FindExSearchLimitToDirectories, 0); + test_FindFirstFileExA(FindExInfoStandard, FindExSearchLimitToDirectories, FIND_FIRST_EX_CASE_SENSITIVE); + test_FindFirstFileExA(FindExInfoStandard, FindExSearchLimitToDirectories, FIND_FIRST_EX_LARGE_FETCH); + test_FindFirstFileExA(FindExInfoBasic, FindExSearchLimitToDirectories, 0); test_LockFile(); test_file_sharing(); test_offset_in_overlapped_structure(); @@ -4191,4 +4435,6 @@ START_TEST(file) test_SetFileValidData(); test_WriteFileGather(); test_file_access(); + test_GetFinalPathNameByHandleA(); + test_GetFinalPathNameByHandleW(); } diff --git a/rostests/winetests/kernel32/format_msg.c b/rostests/winetests/kernel32/format_msg.c index e98e08372d1..4a1ae7d0429 100755 --- a/rostests/winetests/kernel32/format_msg.c +++ b/rostests/winetests/kernel32/format_msg.c @@ -1552,6 +1552,11 @@ static void test_message_from_hmodule(void) MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), out, sizeof(out)/sizeof(CHAR), NULL); ok(ret != 0, "FormatMessageA returned 0\n"); + /* Test HRESULT. It's not documented but in practice _com_error::ErrorMessage relies on this. */ + ret = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_FROM_HMODULE, h, 0x80070005 /* E_ACCESSDENIED */, + MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), out, sizeof(out)/sizeof(CHAR), NULL); + ok(ret != 0, "FormatMessageA returned 0\n"); + /* Test a message string with an insertion without passing any variadic arguments. */ ret = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_FROM_HMODULE, h, 193 /* ERROR_BAD_EXE_FORMAT */, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), out, sizeof(out)/sizeof(CHAR), NULL); diff --git a/rostests/winetests/kernel32/loader.c b/rostests/winetests/kernel32/loader.c index af437796939..492203586f9 100644 --- a/rostests/winetests/kernel32/loader.c +++ b/rostests/winetests/kernel32/loader.c @@ -78,7 +78,7 @@ static PVOID RVAToAddr(DWORD_PTR rva, HMODULE module) static IMAGE_DOS_HEADER dos_header; -static IMAGE_NT_HEADERS nt_header = +static const IMAGE_NT_HEADERS nt_header_template = { IMAGE_NT_SIGNATURE, /* Signature */ { @@ -123,8 +123,8 @@ static IMAGE_NT_HEADERS nt_header = 4, /* MajorSubsystemVersion */ 0, /* MinorSubsystemVersion */ 0, /* Win32VersionValue */ - sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER) + 0x1000, /* SizeOfImage */ - sizeof(dos_header) + sizeof(nt_header), /* SizeOfHeaders */ + sizeof(dos_header) + sizeof(nt_header_template) + sizeof(IMAGE_SECTION_HEADER) + 0x1000, /* SizeOfImage */ + sizeof(dos_header) + sizeof(nt_header_template), /* SizeOfHeaders */ 0, /* CheckSum */ IMAGE_SUBSYSTEM_WINDOWS_CUI, /* Subsystem */ 0, /* DllCharacteristics */ @@ -266,14 +266,14 @@ static void test_Loader(void) }, { sizeof(dos_header), 1, sizeof(IMAGE_OPTIONAL_HEADER), 0x1000, 0x1000, - sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER) + 0xe00, - sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER), + sizeof(dos_header) + sizeof(nt_header_template) + sizeof(IMAGE_SECTION_HEADER) + 0xe00, + sizeof(dos_header) + sizeof(nt_header_template) + sizeof(IMAGE_SECTION_HEADER), { ERROR_BAD_EXE_FORMAT } /* XP doesn't like too small image size */ }, { sizeof(dos_header), 1, sizeof(IMAGE_OPTIONAL_HEADER), 0x1000, 0x1000, - sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER) + 0x1000, - sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER), + sizeof(dos_header) + sizeof(nt_header_template) + sizeof(IMAGE_SECTION_HEADER) + 0x1000, + sizeof(dos_header) + sizeof(nt_header_template) + sizeof(IMAGE_SECTION_HEADER), { ERROR_SUCCESS } }, { sizeof(dos_header), @@ -284,25 +284,25 @@ static void test_Loader(void) }, { sizeof(dos_header), 1, sizeof(IMAGE_OPTIONAL_HEADER), 0x200, 0x200, - sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER) + 0x200, - sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER), + sizeof(dos_header) + sizeof(nt_header_template) + sizeof(IMAGE_SECTION_HEADER) + 0x200, + sizeof(dos_header) + sizeof(nt_header_template) + sizeof(IMAGE_SECTION_HEADER), { ERROR_SUCCESS, ERROR_INVALID_ADDRESS } /* vista is more strict */ }, { sizeof(dos_header), 1, sizeof(IMAGE_OPTIONAL_HEADER), 0x200, 0x1000, - sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER) + 0x1000, - sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER), + sizeof(dos_header) + sizeof(nt_header_template) + sizeof(IMAGE_SECTION_HEADER) + 0x1000, + sizeof(dos_header) + sizeof(nt_header_template) + sizeof(IMAGE_SECTION_HEADER), { ERROR_BAD_EXE_FORMAT } /* XP doesn't like alignments */ }, { sizeof(dos_header), 1, sizeof(IMAGE_OPTIONAL_HEADER), 0x1000, 0x200, - sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER) + 0x1000, - sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER), + sizeof(dos_header) + sizeof(nt_header_template) + sizeof(IMAGE_SECTION_HEADER) + 0x1000, + sizeof(dos_header) + sizeof(nt_header_template) + sizeof(IMAGE_SECTION_HEADER), { ERROR_SUCCESS } }, { sizeof(dos_header), 1, sizeof(IMAGE_OPTIONAL_HEADER), 0x1000, 0x200, - sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER) + 0x1000, + sizeof(dos_header) + sizeof(nt_header_template) + sizeof(IMAGE_SECTION_HEADER) + 0x1000, 0x200, { ERROR_SUCCESS } }, @@ -363,7 +363,7 @@ static void test_Loader(void) /* the following data mimics the PE image which upack creates */ { 0x10, 1, 0x148, 0x1000, 0x200, - sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER) + 0x1000, + sizeof(dos_header) + sizeof(nt_header_template) + sizeof(IMAGE_SECTION_HEADER) + 0x1000, 0x200, { ERROR_SUCCESS } }, @@ -384,7 +384,8 @@ static void test_Loader(void) SIZE_T size; BOOL ret; NTSTATUS status; - WORD orig_machine = nt_header.FileHeader.Machine; + WORD orig_machine = nt_header_template.FileHeader.Machine; + IMAGE_NT_HEADERS nt_header; /* prevent displaying of the "Unable to load this DLL" message box */ SetErrorMode(SEM_FAILCRITICALERRORS); @@ -395,6 +396,7 @@ static void test_Loader(void) { GetTempFileNameA(temp_path, "ldr", 0, dll_name); + nt_header = nt_header_template; nt_header.FileHeader.NumberOfSections = td[i].number_of_sections; nt_header.FileHeader.SizeOfOptionalHeader = td[i].size_of_optional_header; @@ -589,13 +591,14 @@ todo_wine ok(ret, "DeleteFile error %d\n", GetLastError()); } + nt_header = nt_header_template; nt_header.FileHeader.NumberOfSections = 1; nt_header.FileHeader.SizeOfOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER); nt_header.OptionalHeader.SectionAlignment = page_size; nt_header.OptionalHeader.FileAlignment = page_size; nt_header.OptionalHeader.SizeOfHeaders = sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER); - nt_header.OptionalHeader.SizeOfImage = nt_header.OptionalHeader.SizeOfImage + page_size; + nt_header.OptionalHeader.SizeOfImage = sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER) + page_size; status = map_image_section( &nt_header ); ok( status == STATUS_SUCCESS, "NtCreateSection error %08x\n", status ); @@ -1032,8 +1035,6 @@ static void test_section_access(void) { IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_EXECUTE, PAGE_EXECUTE_WRITECOPY, PAGE_EXECUTE_READWRITE }, { IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_EXECUTE, PAGE_EXECUTE_WRITECOPY, PAGE_EXECUTE_READWRITE } }; - static const char filler[0x1000]; - static const char section_data[0x10] = "section data"; char buf[256]; int i; DWORD dummy, file_align; @@ -1054,6 +1055,8 @@ static void test_section_access(void) for (i = 0; i < sizeof(td)/sizeof(td[0]); i++) { + IMAGE_NT_HEADERS nt_header; + GetTempFileNameA(temp_path, "ldr", 0, dll_name); /*trace("creating %s\n", dll_name);*/ @@ -1068,6 +1071,7 @@ static void test_section_access(void) ret = WriteFile(hfile, &dos_header, sizeof(dos_header), &dummy, NULL); ok(ret, "WriteFile error %d\n", GetLastError()); + nt_header = nt_header_template; nt_header.FileHeader.NumberOfSections = 1; nt_header.FileHeader.SizeOfOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER); nt_header.FileHeader.Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DLL | IMAGE_FILE_RELOCS_STRIPPED; @@ -1235,11 +1239,13 @@ static void test_import_resolution(void) for (test = 0; test < 3; test++) { #define DATA_RVA(ptr) (page_size + ((char *)(ptr) - (char *)&data)) - nt = nt_header; + nt = nt_header_template; nt.FileHeader.NumberOfSections = 1; nt.FileHeader.SizeOfOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER); nt.FileHeader.Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_32BIT_MACHINE | IMAGE_FILE_RELOCS_STRIPPED; if (test != 2) nt.FileHeader.Characteristics |= IMAGE_FILE_DLL; + nt.OptionalHeader.SectionAlignment = page_size; + nt.OptionalHeader.FileAlignment = 0x200; nt.OptionalHeader.ImageBase = 0x12340000; nt.OptionalHeader.SizeOfImage = 2 * page_size; nt.OptionalHeader.SizeOfHeaders = nt.OptionalHeader.FileAlignment; @@ -2015,6 +2021,7 @@ static void test_ExitProcess(void) void *addr; LARGE_INTEGER offset; SIZE_T size; + IMAGE_NT_HEADERS nt_header; #if !defined(__i386__) && !defined(__x86_64__) skip("x86 specific ExitProcess test\n"); @@ -2055,6 +2062,7 @@ static void test_ExitProcess(void) ret = WriteFile(file, &dos_header, sizeof(dos_header), &dummy, NULL); ok(ret, "WriteFile error %d\n", GetLastError()); + nt_header = nt_header_template; nt_header.FileHeader.NumberOfSections = 1; nt_header.FileHeader.SizeOfOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER); nt_header.FileHeader.Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DLL | IMAGE_FILE_RELOCS_STRIPPED; @@ -2510,6 +2518,8 @@ static void test_ResolveDelayLoadedAPI(void) DWORD dummy, file_size, i; WORD hint = 0; BOOL ret; + IMAGE_NT_HEADERS nt_header; + static const struct test_data { BOOL func; @@ -2536,7 +2546,7 @@ static void test_ResolveDelayLoadedAPI(void) if (!pResolveDelayLoadedAPI) { - todo_wine win_skip("ResolveDelayLoadedAPI is not available\n"); + win_skip("ResolveDelayLoadedAPI is not available\n"); return; } @@ -2569,6 +2579,7 @@ static void test_ResolveDelayLoadedAPI(void) ret = WriteFile(hfile, &dos_header, sizeof(dos_header), &dummy, NULL); ok(ret, "WriteFile error %d\n", GetLastError()); + nt_header = nt_header_template; nt_header.FileHeader.NumberOfSections = 2; nt_header.FileHeader.SizeOfOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER); diff --git a/rostests/winetests/kernel32/locale.c b/rostests/winetests/kernel32/locale.c index 50f6105b309..efd9e67ebf4 100755 --- a/rostests/winetests/kernel32/locale.c +++ b/rostests/winetests/kernel32/locale.c @@ -42,7 +42,9 @@ 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 localeW[] = {'e','n','-','U','S',0}; static const WCHAR fooW[] = {'f','o','o',0}; +static const WCHAR emptyW[] = {0}; static inline unsigned int strlenW( const WCHAR *str ) { @@ -75,6 +77,8 @@ static inline BOOL isdigitW( WCHAR wc ) static HMODULE hKernel32; static WORD enumCount; +static INT (WINAPI *pGetTimeFormatEx)(LPCWSTR, DWORD, const SYSTEMTIME *, LPCWSTR, LPWSTR, INT); +static INT (WINAPI *pGetDateFormatEx)(LPCWSTR, DWORD, const SYSTEMTIME *, LPCWSTR, LPWSTR, INT, LPCWSTR); static BOOL (WINAPI *pEnumSystemLanguageGroupsA)(LANGUAGEGROUP_ENUMPROCA, DWORD, LONG_PTR); static BOOL (WINAPI *pEnumLanguageGroupLocalesA)(LANGGROUPLOCALE_ENUMPROCA, LGRPID, DWORD, LONG_PTR); static BOOL (WINAPI *pEnumUILanguagesA)(UILANGUAGE_ENUMPROCA, DWORD, LONG_PTR); @@ -102,6 +106,8 @@ static void InitFunctionPointers(void) hKernel32 = GetModuleHandleA("kernel32"); #define X(f) p##f = (void*)GetProcAddress(hKernel32, #f) + X(GetTimeFormatEx); + X(GetDateFormatEx); X(EnumSystemLanguageGroupsA); X(EnumLanguageGroupLocalesA); X(LocaleNameToLCID); @@ -615,6 +621,202 @@ static void test_GetTimeFormatA(void) EXPECT_LENA; EXPECT_EQA; } +static void test_GetTimeFormatEx(void) +{ + int ret; + SYSTEMTIME curtime; + WCHAR buffer[BUFFER_SIZE], input[BUFFER_SIZE], Expected[BUFFER_SIZE]; + + if (!pGetTimeFormatEx) + { + win_skip("GetTimeFormatEx not supported\n"); + return; + } + + memset(&curtime, 2, sizeof(SYSTEMTIME)); + STRINGSW("tt HH':'mm'@'ss", ""); /* Invalid time */ + SetLastError(0xdeadbeef); + ret = pGetTimeFormatEx(localeW, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, COUNTOF(buffer)); + ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); + + curtime.wHour = 8; + curtime.wMinute = 56; + curtime.wSecond = 13; + curtime.wMilliseconds = 22; + STRINGSW("tt HH':'mm'@'ss", "AM 08:56@13"); /* Valid time */ + SetLastError(0xdeadbeef); + ret = pGetTimeFormatEx(localeW, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, COUNTOF(buffer)); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + /* MSDN: LOCALE_NOUSEROVERRIDE can't be specified with a format string */ + SetLastError(0xdeadbeef); + ret = pGetTimeFormatEx(localeW, NUO|TIME_FORCE24HOURFORMAT, &curtime, input, buffer, COUNTOF(buffer)); + ok(!ret && GetLastError() == ERROR_INVALID_FLAGS, + "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError()); + + STRINGSW("tt HH':'mm'@'ss", "A"); /* Insufficient buffer */ + SetLastError(0xdeadbeef); + ret = pGetTimeFormatEx(localeW, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, 2); + ok( !ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, + "Expected ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError()); + + STRINGSW("tt HH':'mm'@'ss", "AM 08:56@13"); /* Calculate length only */ + ret = pGetTimeFormatEx(localeW, TIME_FORCE24HOURFORMAT, &curtime, input, NULL, 0); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; + + STRINGSW("", "8 AM"); /* TIME_NOMINUTESORSECONDS, default format */ + ret = pGetTimeFormatEx(localeW, NUO|TIME_NOMINUTESORSECONDS, &curtime, NULL, buffer, COUNTOF(buffer)); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + STRINGSW("m1s2m3s4", ""); /* TIME_NOMINUTESORSECONDS/complex format */ + ret = pGetTimeFormatEx(localeW, TIME_NOMINUTESORSECONDS, &curtime, input, buffer, COUNTOF(buffer)); + ok(ret == strlenW(buffer)+1, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + STRINGSW("", "8:56 AM"); /* TIME_NOSECONDS/Default format */ + ret = pGetTimeFormatEx(localeW, NUO|TIME_NOSECONDS, &curtime, NULL, buffer, COUNTOF(buffer)); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + STRINGSW("h:m:s tt", "8:56 AM"); /* TIME_NOSECONDS */ + ret = pGetTimeFormatEx(localeW, TIME_NOSECONDS, &curtime, input, buffer, COUNTOF(buffer)); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + STRINGSW("h.@:m.@:s.@:tt", "8.@:56AM"); /* Multiple delimiters */ + ret = pGetTimeFormatEx(localeW, TIME_NOSECONDS, &curtime, input, buffer, COUNTOF(buffer)); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + STRINGSW("s1s2s3", ""); /* Duplicate tokens */ + ret = pGetTimeFormatEx(localeW, TIME_NOSECONDS, &curtime, input, buffer, COUNTOF(buffer)); + ok(ret == strlenW(buffer)+1, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + STRINGSW("t/tt", "A/AM"); /* AM time marker */ + ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, COUNTOF(buffer)); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + curtime.wHour = 13; + STRINGSW("t/tt", "P/PM"); /* PM time marker */ + ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, COUNTOF(buffer)); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + STRINGSW("h1t2tt3m", "156"); /* TIME_NOTIMEMARKER: removes text around time marker token */ + ret = pGetTimeFormatEx(localeW, TIME_NOTIMEMARKER, &curtime, input, buffer, COUNTOF(buffer)); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + STRINGSW("h:m:s tt", "13:56:13 PM"); /* TIME_FORCE24HOURFORMAT */ + ret = pGetTimeFormatEx(localeW, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, COUNTOF(buffer)); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + STRINGSW("h:m:s", "13:56:13"); /* TIME_FORCE24HOURFORMAT doesn't add time marker */ + ret = pGetTimeFormatEx(localeW, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, COUNTOF(buffer)); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + curtime.wHour = 14; /* change this to 14 or 2pm */ + curtime.wMinute = 5; + curtime.wSecond = 3; + STRINGSW("h hh H HH m mm s ss t tt", "2 02 14 14 5 05 3 03 P PM"); /* 24 hrs, leading 0 */ + ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, COUNTOF(buffer)); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + curtime.wHour = 0; + STRINGSW("h/H/hh/HH", "12/0/12/00"); /* "hh" and "HH" */ + ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, COUNTOF(buffer)); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + STRINGSW("h:m:s tt", "12:5:3 AM"); /* non-zero flags should fail with format, doesn't */ + ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, COUNTOF(buffer)); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + /* try to convert formatting strings with more than two letters + * "h:hh:hhh:H:HH:HHH:m:mm:mmm:M:MM:MMM:s:ss:sss:S:SS:SSS" + * NOTE: We expect any letter for which there is an upper case value + * we should see a replacement. For letters that DO NOT have + * upper case values we should see NO REPLACEMENT. + */ + curtime.wHour = 8; + curtime.wMinute = 56; + curtime.wSecond = 13; + curtime.wMilliseconds = 22; + STRINGSW("h:hh:hhh H:HH:HHH m:mm:mmm M:MM:MMM s:ss:sss S:SS:SSS", + "8:08:08 8:08:08 56:56:56 M:MM:MMM 13:13:13 S:SS:SSS"); + ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, COUNTOF(buffer)); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + STRINGSW("h", "text"); /* Don't write to buffer if len is 0 */ + lstrcpyW(buffer, Expected); + ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, 0); + ok(ret == 2, "Expected ret == 2, got %d, error %d\n", ret, GetLastError()); + EXPECT_EQW; + + STRINGSW("h 'h' H 'H' HH 'HH' m 'm' s 's' t 't' tt 'tt'", + "8 h 8 H 08 HH 56 m 13 s A t AM tt"); /* "'" preserves tokens */ + ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, COUNTOF(buffer)); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + STRINGSW("'''", "'"); /* invalid quoted string */ + ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, COUNTOF(buffer)); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + /* test that msdn suggested single quotation usage works as expected */ + STRINGSW("''''", "'"); /* single quote mark */ + ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, COUNTOF(buffer)); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + STRINGSW("''HHHHHH", "08"); /* Normal use */ + ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, COUNTOF(buffer)); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + /* and test for normal use of the single quotation mark */ + STRINGSW("'''HHHHHH'", "'HHHHHH"); /* Normal use */ + ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, COUNTOF(buffer)); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + STRINGSW("'''HHHHHH", "'HHHHHH"); /* Odd use */ + ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, COUNTOF(buffer)); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + STRINGSW("'123'tt", ""); /* TIME_NOTIMEMARKER drops literals too */ + ret = pGetTimeFormatEx(localeW, TIME_NOTIMEMARKER, &curtime, input, buffer, COUNTOF(buffer)); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + curtime.wHour = 25; + STRINGSW("'123'tt", ""); /* Invalid time */ + SetLastError(0xdeadbeef); + ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, COUNTOF(buffer)); + ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); + + curtime.wHour = 12; + curtime.wMonth = 60; /* Invalid */ + STRINGSW("h:m:s", "12:56:13"); /* Invalid date */ + ret = pGetTimeFormatEx(localeW, 0, &curtime, input, buffer, COUNTOF(buffer)); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; +} + static void test_GetDateFormatA(void) { int ret; @@ -782,6 +984,102 @@ static void test_GetDateFormatA(void) "Expected '%s', got '%s'\n", Expected, buffer); } +static void test_GetDateFormatEx(void) +{ + int ret; + SYSTEMTIME curtime; + WCHAR buffer[BUFFER_SIZE], input[BUFFER_SIZE], Expected[BUFFER_SIZE]; + + if (!pGetDateFormatEx) + { + win_skip("GetDateFormatEx not supported\n"); + return; + } + + STRINGSW("",""); /* If flags are set, then format must be NULL */ + SetLastError(0xdeadbeef); + ret = pGetDateFormatEx(localeW, DATE_LONGDATE, NULL, + input, buffer, COUNTOF(buffer), NULL); + ok(!ret && GetLastError() == ERROR_INVALID_FLAGS, + "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError()); + EXPECT_EQW; + + STRINGSW("",""); /* NULL buffer, len > 0 */ + SetLastError(0xdeadbeef); + ret = pGetDateFormatEx(localeW, 0, NULL, input, NULL, COUNTOF(buffer), NULL); + ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); + + STRINGSW("",""); /* NULL buffer, len == 0 */ + ret = pGetDateFormatEx(localeW, 0, NULL, input, NULL, 0, NULL); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + STRINGSW("",""); /* Invalid flag combination */ + SetLastError(0xdeadbeef); + ret = pGetDateFormatEx(localeW, DATE_LONGDATE|DATE_SHORTDATE, NULL, + input, NULL, 0, NULL); + ok(!ret && GetLastError() == ERROR_INVALID_FLAGS, + "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError()); + EXPECT_EQW; + + curtime.wYear = 2002; + curtime.wMonth = 10; + curtime.wDay = 23; + curtime.wDayOfWeek = 45612; /* Should be 3 - Wednesday */ + curtime.wHour = 65432; /* Invalid */ + curtime.wMinute = 34512; /* Invalid */ + curtime.wSecond = 65535; /* Invalid */ + curtime.wMilliseconds = 12345; + STRINGSW("dddd d MMMM yyyy","Wednesday 23 October 2002"); /* Incorrect DOW and time */ + ret = pGetDateFormatEx(localeW, 0, &curtime, input, buffer, COUNTOF(buffer), NULL); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + curtime.wYear = 2002; + curtime.wMonth = 10; + curtime.wDay = 23; + curtime.wDayOfWeek = 45612; /* Should be 3 - Wednesday */ + curtime.wHour = 65432; /* Invalid */ + curtime.wMinute = 34512; /* Invalid */ + curtime.wSecond = 65535; /* Invalid */ + curtime.wMilliseconds = 12345; + STRINGSW("dddd d MMMM yyyy","Wednesday 23 October 2002"); + ret = pGetDateFormatEx(localeW, 0, &curtime, input, buffer, COUNTOF(buffer), emptyW); /* Use reserved arg */ + ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); + + /* Limit tests */ + + curtime.wYear = 1601; + curtime.wMonth = 1; + curtime.wDay = 1; + curtime.wDayOfWeek = 0; /* Irrelevant */ + curtime.wHour = 0; + curtime.wMinute = 0; + curtime.wSecond = 0; + curtime.wMilliseconds = 0; + STRINGSW("dddd d MMMM yyyy","Monday 1 January 1601"); + SetLastError(0xdeadbeef); + ret = pGetDateFormatEx(localeW, 0, &curtime, input, buffer, COUNTOF(buffer), NULL); + ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError()); + EXPECT_LENW; EXPECT_EQW; + + curtime.wYear = 1600; + curtime.wMonth = 12; + curtime.wDay = 31; + curtime.wDayOfWeek = 0; /* Irrelevant */ + curtime.wHour = 23; + curtime.wMinute = 59; + curtime.wSecond = 59; + curtime.wMilliseconds = 999; + STRINGSW("dddd d MMMM yyyy","Friday 31 December 1600"); + SetLastError(0xdeadbeef); + ret = pGetDateFormatEx(localeW, 0, &curtime, input, buffer, COUNTOF(buffer), NULL); + ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); +} + static void test_GetDateFormatW(void) { int ret; @@ -1464,13 +1762,13 @@ static void test_CompareStringA(void) 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); + ok(ret == CSTR_LESS_THAN, "\"#\" vs \".\" expected CSTR_LESS_THAN, got %d\n", ret); ret = CompareStringA(lcid, NORM_IGNORECASE, "_", -1, ".", -1); - todo_wine ok(ret == CSTR_GREATER_THAN, "\"_\" vs \".\" expected CSTR_GREATER_THAN, got %d\n", ret); + ok(ret == CSTR_GREATER_THAN, "\"_\" vs \".\" expected CSTR_GREATER_THAN, got %d\n", ret); ret = lstrcmpiA("#", "."); - todo_wine ok(ret == -1, "\"#\" vs \".\" expected -1, got %d\n", ret); + ok(ret == -1, "\"#\" vs \".\" expected -1, got %d\n", ret); lcid = MAKELCID(MAKELANGID(LANG_POLISH, SUBLANG_DEFAULT), SORT_DEFAULT); @@ -3284,6 +3582,7 @@ static void test_GetStringTypeW(void) WORD types[20]; WCHAR ch; + BOOL res; int i; memset(types,0,sizeof(types)); @@ -3341,6 +3640,15 @@ static void test_GetStringTypeW(void) for (i = 0; i < 3; i++) ok(types[i] & C1_SPACE || broken(types[i] == C1_CNTRL) || broken(types[i] == 0), "incorrect types returned for %x -> (%x does not have %x)\n",space_special[i], types[i], C1_SPACE ); + for (i = -1; i < 3; i++) + { + SetLastError(0xdeadbeef); + memset(types, 0, sizeof(types)); + res = GetStringTypeW(CT_CTYPE1, NULL, i, types); + ok(!res, "GetStringTypeW unexpectedly succeeded\n"); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "wrong error, got %u\n", GetLastError()); + } + /* surrogate pairs */ ch = 0xd800; memset(types, 0, sizeof(types)); @@ -3974,7 +4282,7 @@ static void test_EnumSystemGeoID(void) ret = pEnumSystemGeoID(GEOCLASS_NATION, 0, test_geoid_enumproc); ok(ret, "got %d\n", ret); - /* only first level is enumerated, not the whole hierarchy */ + /* only the first level is enumerated, not the whole hierarchy */ geoidenum_count = 0; ret = pEnumSystemGeoID(GEOCLASS_NATION, 39070, test_geoid_enumproc2); if (ret == 0) @@ -4006,7 +4314,9 @@ START_TEST(locale) test_GetLocaleInfoW(); test_GetLocaleInfoEx(); test_GetTimeFormatA(); + test_GetTimeFormatEx(); test_GetDateFormatA(); + test_GetDateFormatEx(); test_GetDateFormatW(); test_GetCurrencyFormatA(); /* Also tests the W version */ test_GetNumberFormatA(); /* Also tests the W version */ diff --git a/rostests/winetests/kernel32/module.c b/rostests/winetests/kernel32/module.c index 0d140a497b5..bb22c24316c 100755 --- a/rostests/winetests/kernel32/module.c +++ b/rostests/winetests/kernel32/module.c @@ -20,12 +20,15 @@ #include "wine/test.h" #include +#include static DWORD (WINAPI *pGetDllDirectoryA)(DWORD,LPSTR); static DWORD (WINAPI *pGetDllDirectoryW)(DWORD,LPWSTR); static BOOL (WINAPI *pSetDllDirectoryA)(LPCSTR); static BOOL (WINAPI *pGetModuleHandleExA)(DWORD,LPCSTR,HMODULE*); static BOOL (WINAPI *pGetModuleHandleExW)(DWORD,LPCWSTR,HMODULE*); +static BOOL (WINAPI *pK32GetModuleInformation)(HANDLE process, HMODULE module, + MODULEINFO *modinfo, DWORD cb); static BOOL is_unicode_enabled = TRUE; @@ -514,7 +517,20 @@ static void init_pointers(void) MAKEFUNC(SetDllDirectoryA); MAKEFUNC(GetModuleHandleExA); MAKEFUNC(GetModuleHandleExW); + MAKEFUNC(K32GetModuleInformation); #undef MAKEFUNC + + /* not all Windows versions export this in kernel32 */ + if (!pK32GetModuleInformation) + { + HMODULE hPsapi = LoadLibraryA("psapi.dll"); + if (hPsapi) + { + pK32GetModuleInformation = (void *)GetProcAddress(hPsapi, "GetModuleInformation"); + if (!pK32GetModuleInformation) FreeLibrary(hPsapi); + } + } + } static void testGetModuleHandleEx(void) @@ -696,6 +712,33 @@ static void testGetModuleHandleEx(void) FreeLibrary( mod_kernel32 ); } +static void testK32GetModuleInformation(void) +{ + MODULEINFO info; + HMODULE mod; + BOOL ret; + + if (!pK32GetModuleInformation) + { + win_skip("K32GetModuleInformation not available\n"); + return; + } + + mod = GetModuleHandleA(NULL); + memset(&info, 0xAA, sizeof(info)); + ret = pK32GetModuleInformation(GetCurrentProcess(), mod, &info, sizeof(info)); + ok(ret, "K32GetModuleInformation failed for main module\n"); + ok(info.lpBaseOfDll == mod, "Wrong info.lpBaseOfDll = %p, expected %p\n", info.lpBaseOfDll, mod); + ok(info.EntryPoint != NULL, "Expected nonzero entrypoint\n"); + + mod = GetModuleHandleA("kernel32.dll"); + memset(&info, 0xAA, sizeof(info)); + ret = pK32GetModuleInformation(GetCurrentProcess(), mod, &info, sizeof(info)); + ok(ret, "K32GetModuleInformation failed for kernel32 module\n"); + ok(info.lpBaseOfDll == mod, "Wrong info.lpBaseOfDll = %p, expected %p\n", info.lpBaseOfDll, mod); + ok(info.EntryPoint != NULL, "Expected nonzero entrypoint\n"); +} + START_TEST(module) { WCHAR filenameW[MAX_PATH]; @@ -724,4 +767,5 @@ START_TEST(module) testGetProcAddress_Wrong(); testLoadLibraryEx(); testGetModuleHandleEx(); + testK32GetModuleInformation(); } diff --git a/rostests/winetests/kernel32/path.c b/rostests/winetests/kernel32/path.c index 0f07ea6fb01..3600abecce0 100755 --- a/rostests/winetests/kernel32/path.c +++ b/rostests/winetests/kernel32/path.c @@ -1002,8 +1002,10 @@ static void test_GetTempPath(void) { char save_TMP[MAX_PATH]; char windir[MAX_PATH]; + char origdir[MAX_PATH]; char buf[MAX_PATH]; + GetCurrentDirectoryA(sizeof(origdir), origdir); if (!GetEnvironmentVariableA("TMP", save_TMP, sizeof(save_TMP))) save_TMP[0] = 0; /* test default configuration */ @@ -1048,6 +1050,7 @@ static void test_GetTempPath(void) test_GetTempPathW(windir); SetEnvironmentVariableA("TMP", save_TMP); + SetCurrentDirectoryA(origdir); } static void test_GetLongPathNameA(void) @@ -1644,10 +1647,11 @@ static void test_SearchPathA(void) static const CHAR testdeprelA[] = "./testdep.dll"; static const CHAR kernel32A[] = "kernel32.dll"; static const CHAR fileA[] = ""; - CHAR pathA[MAX_PATH], buffA[MAX_PATH], path2A[MAX_PATH]; - CHAR *ptrA = NULL; + CHAR pathA[MAX_PATH], buffA[MAX_PATH], path2A[MAX_PATH], path3A[MAX_PATH], curdirA[MAX_PATH]; + CHAR tmpdirA[MAX_PATH], *ptrA = NULL; ULONG_PTR cookie; HANDLE handle; + BOOL bret; DWORD ret; if (!pSearchPathA) @@ -1721,6 +1725,28 @@ static void test_SearchPathA(void) ret = pDeactivateActCtx(0, cookie); ok(ret, "failed to deactivate context, %u\n", GetLastError()); pReleaseActCtx(handle); + + /* test the search path priority of the working directory */ + GetTempPathA(sizeof(tmpdirA), tmpdirA); + ret = GetCurrentDirectoryA(MAX_PATH, curdirA); + ok(ret, "failed to obtain working directory.\n"); + sprintf(pathA, "%s\\%s", tmpdirA, kernel32A); + ret = pSearchPathA(NULL, kernel32A, NULL, sizeof(path2A)/sizeof(CHAR), path2A, NULL); + ok(ret && ret == strlen(path2A), "got %d\n", ret); + bret = CopyFileA(path2A, pathA, FALSE); + ok(bret != 0, "failed to copy test executable to temp directory, %u\n", GetLastError()); + sprintf(path3A, "%s%s%s", curdirA, curdirA[strlen(curdirA)-1] != '\\' ? "\\" : "", kernel32A); + bret = CopyFileA(path2A, path3A, FALSE); + ok(bret != 0, "failed to copy test executable to launch directory, %u\n", GetLastError()); + bret = SetCurrentDirectoryA(tmpdirA); + ok(bret, "failed to change working directory\n"); + ret = pSearchPathA(NULL, kernel32A, ".exe", sizeof(buffA), buffA, NULL); + ok(ret && ret == strlen(buffA), "got %d\n", ret); + ok(strcmp(buffA, path3A) == 0, "expected %s, got %s\n", path3A, buffA); + bret = SetCurrentDirectoryA(curdirA); + ok(bret, "failed to reset working directory\n"); + DeleteFileA(path3A); + DeleteFileA(pathA); } static void test_SearchPathW(void) @@ -1828,6 +1854,7 @@ static void test_GetFullPathNameA(void) char output[MAX_PATH], *filepart; DWORD ret; int i; + UINT acp; const struct { @@ -1864,6 +1891,27 @@ static void test_GetFullPathNameA(void) "[%d] Expected GetLastError() to return 0xdeadbeef, got %u\n", i, GetLastError()); } + + acp = GetACP(); + if (acp != 932) + skip("Skipping DBCS(Japanese) GetFullPathNameA test in this codepage (%d)\n", acp); + else { + const struct dbcs_case { + const char *input; + const char *expected; + } testset[] = { + { "c:\\a\\\x95\x5c\x97\xa0.txt", "\x95\x5c\x97\xa0.txt" }, + { "c:\\\x83\x8f\x83\x43\x83\x93\\wine.c", "wine.c" }, + { "c:\\demo\\\x97\xa0\x95\x5c", "\x97\xa0\x95\x5c" } + }; + for (i = 0; i < sizeof(testset)/sizeof(testset[0]); i++) { + ret = GetFullPathNameA(testset[i].input, sizeof(output), + output, &filepart); + ok(ret, "[%d] GetFullPathName error %u\n", i, GetLastError()); + ok(!lstrcmpA(filepart, testset[i].expected), + "[%d] expected %s got %s\n", i, testset[i].expected, filepart); + } + } } static void test_GetFullPathNameW(void) diff --git a/rostests/winetests/kernel32/pipe.c b/rostests/winetests/kernel32/pipe.c index 3f89086f655..980c2a4e5d3 100755 --- a/rostests/winetests/kernel32/pipe.c +++ b/rostests/winetests/kernel32/pipe.c @@ -29,6 +29,7 @@ #include "wine/test.h" #define PIPENAME "\\\\.\\PiPe\\tests_pipe.c" +#define PIPENAME_SPECIAL "\\\\.\\PiPe\\tests->pipe.c" #define NB_SERVER_LOOPS 8 @@ -46,7 +47,9 @@ static void CALLBACK user_apc(ULONG_PTR param) enum rpcThreadOp { - RPC_READFILE + RPC_READFILE, + RPC_WRITEFILE, + RPC_PEEKNAMEDPIPE }; struct rpcThreadArgs @@ -54,7 +57,7 @@ struct rpcThreadArgs ULONG_PTR returnValue; DWORD lastError; enum rpcThreadOp op; - ULONG_PTR args[5]; + ULONG_PTR args[6]; }; static DWORD CALLBACK rpcThreadMain(LPVOID arg) @@ -73,6 +76,23 @@ static DWORD CALLBACK rpcThreadMain(LPVOID arg) (LPOVERLAPPED)rpcargs->args[4] ); /* overlapped */ break; + case RPC_WRITEFILE: + rpcargs->returnValue = (ULONG_PTR)WriteFile( (HANDLE)rpcargs->args[0], /* hFile */ + (LPCVOID)rpcargs->args[1], /* buffer */ + (DWORD)rpcargs->args[2], /* bytesToWrite */ + (LPDWORD)rpcargs->args[3], /* bytesWritten */ + (LPOVERLAPPED)rpcargs->args[4] ); /* overlapped */ + break; + + case RPC_PEEKNAMEDPIPE: + rpcargs->returnValue = (ULONG_PTR)PeekNamedPipe( (HANDLE)rpcargs->args[0], /* hPipe */ + (LPVOID)rpcargs->args[1], /* lpvBuffer */ + (DWORD)rpcargs->args[2], /* cbBuffer */ + (LPDWORD)rpcargs->args[3], /* lpcbRead */ + (LPDWORD)rpcargs->args[4], /* lpcbAvail */ + (LPDWORD)rpcargs->args[5] ); /* lpcbMessage */ + break; + default: SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); rpcargs->returnValue = 0; @@ -89,7 +109,7 @@ static BOOL RpcReadFile(HANDLE hFile, LPVOID buffer, DWORD bytesToRead, LPDWORD { struct rpcThreadArgs rpcargs; HANDLE thread; - DWORD threadId; + DWORD threadId, ret; rpcargs.returnValue = 0; rpcargs.lastError = GetLastError(); @@ -102,7 +122,35 @@ static BOOL RpcReadFile(HANDLE hFile, LPVOID buffer, DWORD bytesToRead, LPDWORD thread = CreateThread(NULL, 0, rpcThreadMain, (void *)&rpcargs, 0, &threadId); ok(thread != NULL, "CreateThread failed. %d\n", GetLastError()); - ok(WaitForSingleObject(thread, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed with %d.\n", GetLastError()); + ret = WaitForSingleObject(thread, INFINITE); + ok(ret == WAIT_OBJECT_0, "WaitForSingleObject failed with %d.\n", GetLastError()); + CloseHandle(thread); + + SetLastError(rpcargs.lastError); + return (BOOL)rpcargs.returnValue; +} + +/* Runs PeekNamedPipe(...) from a different thread */ +static BOOL RpcPeekNamedPipe(HANDLE hPipe, LPVOID lpvBuffer, DWORD cbBuffer, + LPDWORD lpcbRead, LPDWORD lpcbAvail, LPDWORD lpcbMessage) +{ + struct rpcThreadArgs rpcargs; + HANDLE thread; + DWORD threadId; + + rpcargs.returnValue = 0; + rpcargs.lastError = GetLastError(); + rpcargs.op = RPC_PEEKNAMEDPIPE; + rpcargs.args[0] = (ULONG_PTR)hPipe; + rpcargs.args[1] = (ULONG_PTR)lpvBuffer; + rpcargs.args[2] = (ULONG_PTR)cbBuffer; + rpcargs.args[3] = (ULONG_PTR)lpcbRead; + rpcargs.args[4] = (ULONG_PTR)lpcbAvail; + rpcargs.args[5] = (ULONG_PTR)lpcbMessage; + + thread = CreateThread(NULL, 0, rpcThreadMain, (void *)&rpcargs, 0, &threadId); + ok(thread != NULL, "CreateThread failed. %d\n", GetLastError()); + ok(WaitForSingleObject(thread, INFINITE) == WAIT_OBJECT_0,"WaitForSingleObject failed with %d.\n", GetLastError()); CloseHandle(thread); SetLastError(rpcargs.lastError); @@ -118,6 +166,7 @@ static void test_CreateNamedPipe(int pipemode) char ibuf[32], *pbuf; DWORD written; DWORD readden; + DWORD leftmsg; DWORD avail; DWORD lpmode; BOOL ret; @@ -127,9 +176,9 @@ static void test_CreateNamedPipe(int pipemode) else trace("test_CreateNamedPipe starting in message mode\n"); - /* Wait for non existing pipe */ + /* Wait for nonexistent pipe */ ret = WaitNamedPipeA(PIPENAME, 2000); - ok(ret == 0, "WaitNamedPipe returned %d for non existing pipe\n", ret); + ok(ret == 0, "WaitNamedPipe returned %d for nonexistent pipe\n", ret); ok(GetLastError() == ERROR_FILE_NOT_FOUND, "wrong error %u\n", GetLastError()); /* Bad parameter checks */ @@ -232,9 +281,20 @@ static void test_CreateNamedPipe(int pipemode) ok(written == sizeof(obuf2), "write file len\n"); ok(ReadFile(hFile, ibuf, 4, &readden, NULL), "ReadFile\n"); ok(readden == 4, "read got %d bytes\n", readden); + readden = leftmsg = -1; + ok(PeekNamedPipe(hFile, NULL, 0, NULL, &readden, &leftmsg), "PeekNamedPipe\n"); + ok(readden == sizeof(obuf2) - 4, "peek got %d bytes total\n", readden); + if (pipemode == PIPE_TYPE_BYTE) + ok(leftmsg == 0, "peek got %d bytes left in message\n", leftmsg); + else + ok(leftmsg == sizeof(obuf2) - 4, "peek got %d bytes left in message\n", leftmsg); ok(ReadFile(hFile, ibuf + 4, sizeof(ibuf) - 4, &readden, NULL), "ReadFile\n"); ok(readden == sizeof(obuf2) - 4, "read got %d bytes\n", readden); ok(memcmp(obuf2, ibuf, written) == 0, "content check\n"); + readden = leftmsg = -1; + ok(PeekNamedPipe(hFile, NULL, 0, NULL, &readden, &leftmsg), "PeekNamedPipe\n"); + ok(readden == 0, "peek got %d bytes total\n", readden); + ok(leftmsg == 0, "peek got %d bytes left in message\n", leftmsg); memset(ibuf, 0, sizeof(ibuf)); ok(WriteFile(hFile, obuf, sizeof(obuf), &written, NULL), "WriteFile\n"); @@ -246,9 +306,7 @@ static void test_CreateNamedPipe(int pipemode) else { SetLastError(0xdeadbeef); - todo_wine ok(!ReadFile(hnp, ibuf, 4, &readden, NULL), "ReadFile\n"); - todo_wine ok(GetLastError() == ERROR_MORE_DATA, "wrong error\n"); } ok(readden == 4, "read got %d bytes\n", readden); @@ -269,15 +327,11 @@ static void test_CreateNamedPipe(int pipemode) else { SetLastError(0xdeadbeef); - todo_wine ok(!ReadFile(hnp, ibuf, 4, &readden, NULL), "ReadFile\n"); - todo_wine ok(GetLastError() == ERROR_MORE_DATA, "wrong error\n"); ok(readden == 4, "read got %d bytes\n", readden); SetLastError(0xdeadbeef); - todo_wine ok(!ReadFile(hnp, ibuf + 4, 4, &readden, NULL), "ReadFile\n"); - todo_wine ok(GetLastError() == ERROR_MORE_DATA, "wrong error\n"); } ok(readden == 4, "read got %d bytes\n", readden); @@ -285,6 +339,146 @@ static void test_CreateNamedPipe(int pipemode) ok(readden == sizeof(obuf2) - 8, "read got %d bytes\n", readden); ok(memcmp(obuf2, ibuf, written) == 0, "content check\n"); + /* Tests for sending empty messages */ + memset(ibuf, 0, sizeof(ibuf)); + ok(WriteFile(hnp, obuf, 0, &written, NULL), "WriteFile\n"); + ok(written == 0, "write file len\n"); + if (pipemode != PIPE_TYPE_BYTE) + { + ok(ReadFile(hFile, ibuf, sizeof(ibuf), &readden, NULL), "ReadFile\n"); + ok(readden == 0, "read got %d bytes\n", readden); + } + + memset(ibuf, 0, sizeof(ibuf)); + ok(WriteFile(hFile, obuf, 0, &written, NULL), "WriteFile\n"); + ok(written == 0, "write file len\n"); + if (pipemode != PIPE_TYPE_BYTE) + { + ok(ReadFile(hnp, ibuf, sizeof(ibuf), &readden, NULL), "ReadFile\n"); + ok(readden == 0, "read got %d bytes\n", readden); + } + + /* similar to above, but with an additional call to PeekNamedPipe inbetween */ + memset(ibuf, 0, sizeof(ibuf)); + ok(WriteFile(hnp, obuf, 0, &written, NULL), "WriteFile\n"); + ok(written == 0, "write file len\n"); + ok(PeekNamedPipe(hFile, NULL, 0, NULL, &readden, NULL), "Peek\n"); + ok(readden == 0, "peek got %d bytes\n", readden); + if (pipemode != PIPE_TYPE_BYTE) + { + struct rpcThreadArgs rpcargs; + HANDLE thread; + DWORD threadId; + + rpcargs.returnValue = 0; + rpcargs.lastError = GetLastError(); + rpcargs.op = RPC_READFILE; + rpcargs.args[0] = (ULONG_PTR)hFile; + rpcargs.args[1] = (ULONG_PTR)ibuf; + rpcargs.args[2] = (ULONG_PTR)sizeof(ibuf); + rpcargs.args[3] = (ULONG_PTR)&readden; + rpcargs.args[4] = (ULONG_PTR)NULL; + + thread = CreateThread(NULL, 0, rpcThreadMain, (void *)&rpcargs, 0, &threadId); + ok(thread != NULL, "CreateThread failed. %d\n", GetLastError()); + ret = WaitForSingleObject(thread, 200); + todo_wine + ok(ret == WAIT_OBJECT_0, "WaitForSingleObject returned %d instead of %d.\n", ret, WAIT_OBJECT_0); + if (ret == WAIT_TIMEOUT) + { + ok(WriteFile(hnp, obuf, 0, &written, NULL), "WriteFile\n"); + ok(written == 0, "write file len\n"); + ret = WaitForSingleObject(thread, 200); + ok(ret == WAIT_OBJECT_0, "WaitForSingleObject returned %d instead of %d.\n", ret, WAIT_OBJECT_0); + } + CloseHandle(thread); + ok((BOOL)rpcargs.returnValue, "ReadFile\n"); + ok(readden == 0, "read got %d bytes\n", readden); + } + + memset(ibuf, 0, sizeof(ibuf)); + ok(WriteFile(hFile, obuf, 0, &written, NULL), "WriteFile\n"); + ok(written == 0, "write file len\n"); + ok(PeekNamedPipe(hnp, NULL, 0, NULL, &readden, NULL), "Peek\n"); + ok(readden == 0, "peek got %d bytes\n", readden); + if (pipemode != PIPE_TYPE_BYTE) + { + struct rpcThreadArgs rpcargs; + HANDLE thread; + DWORD threadId; + + rpcargs.returnValue = 0; + rpcargs.lastError = GetLastError(); + rpcargs.op = RPC_READFILE; + rpcargs.args[0] = (ULONG_PTR)hnp; + rpcargs.args[1] = (ULONG_PTR)ibuf; + rpcargs.args[2] = (ULONG_PTR)sizeof(ibuf); + rpcargs.args[3] = (ULONG_PTR)&readden; + rpcargs.args[4] = (ULONG_PTR)NULL; + + thread = CreateThread(NULL, 0, rpcThreadMain, (void *)&rpcargs, 0, &threadId); + ok(thread != NULL, "CreateThread failed. %d\n", GetLastError()); + ret = WaitForSingleObject(thread, 200); + todo_wine + ok(ret == WAIT_OBJECT_0, "WaitForSingleObject returned %d instead of %d.\n", ret, WAIT_OBJECT_0); + if (ret == WAIT_TIMEOUT) + { + ok(WriteFile(hFile, obuf, 0, &written, NULL), "WriteFile\n"); + ok(written == 0, "write file len\n"); + ret = WaitForSingleObject(thread, 200); + ok(ret == WAIT_OBJECT_0, "WaitForSingleObject returned %d instead of %d.\n", ret, WAIT_OBJECT_0); + } + CloseHandle(thread); + ok((BOOL)rpcargs.returnValue, "ReadFile\n"); + ok(readden == 0, "read got %d bytes\n", readden); + } + + /* similar to above, but now with PeekNamedPipe and multiple messages */ + memset(ibuf, 0, sizeof(ibuf)); + ok(WriteFile(hnp, obuf, 0, &written, NULL), "WriteFile\n"); + ok(written == 0, "write file len\n"); + ok(WriteFile(hnp, obuf, sizeof(obuf), &written, NULL), "WriteFile\n"); + ok(written == sizeof(obuf), "write file len\n"); + ok(PeekNamedPipe(hFile, NULL, 0, NULL, &readden, &leftmsg), "Peek\n"); + ok(readden == sizeof(obuf), "peek got %d bytes\n", readden); + ok(leftmsg == 0, "peek got %d bytes left in msg\n", leftmsg); + ok(PeekNamedPipe(hFile, NULL, 0, NULL, &readden, &leftmsg), "Peek\n"); + ok(readden == sizeof(obuf), "peek got %d bytes\n", readden); + if (pipemode != PIPE_TYPE_BYTE) + todo_wine + ok(leftmsg == 0, "peek got %d bytes left in msg\n", leftmsg); + else + ok(leftmsg == 0, "peek got %d bytes left in msg\n", leftmsg); + ok(ReadFile(hFile, ibuf, sizeof(ibuf), &readden, NULL), "ReadFile\n"); + ok(readden == sizeof(obuf), "read got %d bytes\n", readden); + ok(memcmp(obuf, ibuf, sizeof(obuf)) == 0, "content check\n"); + + memset(ibuf, 0, sizeof(ibuf)); + ok(WriteFile(hFile, obuf2, 0, &written, NULL), "WriteFile\n"); + ok(written == 0, "write file len\n"); + ok(WriteFile(hFile, obuf2, sizeof(obuf2), &written, NULL), "WriteFile\n"); + ok(written == sizeof(obuf2), "write file len\n"); + ok(PeekNamedPipe(hnp, NULL, 0, NULL, &readden, &leftmsg), "Peek\n"); + ok(readden == sizeof(obuf2), "peek got %d bytes\n", readden); + ok(leftmsg == 0, "peek got %d bytes left in msg\n", leftmsg); + ok(PeekNamedPipe(hnp, NULL, 0, NULL, &readden, &leftmsg), "Peek\n"); + ok(readden == sizeof(obuf2), "peek got %d bytes\n", readden); + if (pipemode != PIPE_TYPE_BYTE) + todo_wine + ok(leftmsg == 0, "peek got %d bytes left in msg\n", leftmsg); + else + ok(leftmsg == 0, "peek got %d bytes left in msg\n", leftmsg); + ok(ReadFile(hnp, ibuf, sizeof(ibuf), &readden, NULL), "ReadFile\n"); + if (pipemode != PIPE_TYPE_BYTE) + { + todo_wine + ok(readden == 0, "read got %d bytes\n", readden); + if (readden == 0) + ok(ReadFile(hnp, ibuf, sizeof(ibuf), &readden, NULL), "ReadFile\n"); + } + ok(readden == sizeof(obuf2), "read got %d bytes\n", readden); + ok(memcmp(obuf2, ibuf, sizeof(obuf2)) == 0, "content check\n"); + /* Test reading of multiple writes */ memset(ibuf, 0, sizeof(ibuf)); ok(WriteFile(hnp, obuf, sizeof(obuf), &written, NULL), "WriteFile3a\n"); @@ -339,9 +533,7 @@ static void test_CreateNamedPipe(int pipemode) ok(readden == sizeof(obuf) + sizeof(obuf2), "read 4 got %d bytes\n", readden); } else { - todo_wine { - ok(readden == sizeof(obuf), "read 4 got %d bytes\n", readden); - } + ok(readden == sizeof(obuf), "read 4 got %d bytes\n", readden); } pbuf = ibuf; ok(memcmp(obuf, pbuf, sizeof(obuf)) == 0, "content 4a check\n"); @@ -371,9 +563,7 @@ static void test_CreateNamedPipe(int pipemode) pbuf = ibuf; ok(memcmp(obuf, pbuf, sizeof(obuf)) == 0, "content 5a check\n"); ok(ReadFile(hFile, ibuf, sizeof(ibuf), &readden, NULL), "ReadFile\n"); - todo_wine { - ok(readden == sizeof(obuf), "read 5 got %d bytes\n", readden); - } + ok(readden == sizeof(obuf), "read 5 got %d bytes\n", readden); pbuf = ibuf; ok(memcmp(obuf, pbuf, sizeof(obuf)) == 0, "content 5a check\n"); if (readden <= sizeof(obuf)) @@ -382,10 +572,8 @@ static void test_CreateNamedPipe(int pipemode) /* Multiple writes in the reverse direction */ /* the write of obuf2 from write4 should still be in the buffer */ ok(PeekNamedPipe(hnp, ibuf, sizeof(ibuf), &readden, &avail, NULL), "Peek6a\n"); - todo_wine { - ok(readden == sizeof(obuf2), "peek6a got %d bytes\n", readden); - ok(avail == sizeof(obuf2), "peek6a got %d bytes available\n", avail); - } + ok(readden == sizeof(obuf2), "peek6a got %d bytes\n", readden); + ok(avail == sizeof(obuf2), "peek6a got %d bytes available\n", avail); if (avail > 0) { ok(ReadFile(hnp, ibuf, sizeof(ibuf), &readden, NULL), "ReadFile\n"); ok(readden == sizeof(obuf2), "read 6a got %d bytes\n", readden); @@ -403,22 +591,145 @@ static void test_CreateNamedPipe(int pipemode) pbuf = ibuf; ok(memcmp(obuf, pbuf, sizeof(obuf)) == 0, "content 6a check\n"); ok(ReadFile(hnp, ibuf, sizeof(ibuf), &readden, NULL), "ReadFile\n"); - todo_wine { - ok(readden == sizeof(obuf), "read 6b got %d bytes\n", readden); - } + ok(readden == sizeof(obuf), "read 6b got %d bytes\n", readden); pbuf = ibuf; ok(memcmp(obuf, pbuf, sizeof(obuf)) == 0, "content 6a check\n"); if (readden <= sizeof(obuf)) ok(ReadFile(hnp, ibuf, sizeof(ibuf), &readden, NULL), "ReadFile\n"); + /* Tests for sending empty messages */ + memset(ibuf, 0, sizeof(ibuf)); + ok(WriteFile(hnp, obuf, 0, &written, NULL), "WriteFile\n"); + ok(written == 0, "write file len\n"); + ok(ReadFile(hFile, ibuf, sizeof(ibuf), &readden, NULL), "ReadFile\n"); + ok(readden == 0, "read got %d bytes\n", readden); + + memset(ibuf, 0, sizeof(ibuf)); + ok(WriteFile(hFile, obuf, 0, &written, NULL), "WriteFile\n"); + ok(written == 0, "write file len\n"); + ok(ReadFile(hnp, ibuf, sizeof(ibuf), &readden, NULL), "ReadFile\n"); + ok(readden == 0, "read got %d bytes\n", readden); + + /* similar to above, but with an additional call to PeekNamedPipe inbetween */ + memset(ibuf, 0, sizeof(ibuf)); + ok(WriteFile(hnp, obuf, 0, &written, NULL), "WriteFile\n"); + ok(written == 0, "write file len\n"); + ok(PeekNamedPipe(hFile, NULL, 0, NULL, &readden, NULL), "Peek\n"); + ok(readden == 0, "peek got %d bytes\n", readden); + { + struct rpcThreadArgs rpcargs; + HANDLE thread; + DWORD threadId; + + rpcargs.returnValue = 0; + rpcargs.lastError = GetLastError(); + rpcargs.op = RPC_READFILE; + rpcargs.args[0] = (ULONG_PTR)hFile; + rpcargs.args[1] = (ULONG_PTR)ibuf; + rpcargs.args[2] = (ULONG_PTR)sizeof(ibuf); + rpcargs.args[3] = (ULONG_PTR)&readden; + rpcargs.args[4] = (ULONG_PTR)NULL; + + thread = CreateThread(NULL, 0, rpcThreadMain, (void *)&rpcargs, 0, &threadId); + ok(thread != NULL, "CreateThread failed. %d\n", GetLastError()); + ret = WaitForSingleObject(thread, 200); + todo_wine + ok(ret == WAIT_OBJECT_0, "WaitForSingleObject returned %d instead of %d.\n", ret, WAIT_OBJECT_0); + if (ret == WAIT_TIMEOUT) + { + ok(WriteFile(hnp, obuf, 0, &written, NULL), "WriteFile\n"); + ok(written == 0, "write file len\n"); + ret = WaitForSingleObject(thread, 200); + ok(ret == WAIT_OBJECT_0, "WaitForSingleObject returned %d instead of %d.\n", ret, WAIT_OBJECT_0); + } + CloseHandle(thread); + ok((BOOL)rpcargs.returnValue, "ReadFile\n"); + ok(readden == 0, "read got %d bytes\n", readden); + } + + memset(ibuf, 0, sizeof(ibuf)); + ok(WriteFile(hFile, obuf, 0, &written, NULL), "WriteFile\n"); + ok(written == 0, "write file len\n"); + ok(PeekNamedPipe(hnp, NULL, 0, NULL, &readden, NULL), "Peek\n"); + ok(readden == 0, "peek got %d bytes\n", readden); + { + struct rpcThreadArgs rpcargs; + HANDLE thread; + DWORD threadId; + + rpcargs.returnValue = 0; + rpcargs.lastError = GetLastError(); + rpcargs.op = RPC_READFILE; + rpcargs.args[0] = (ULONG_PTR)hnp; + rpcargs.args[1] = (ULONG_PTR)ibuf; + rpcargs.args[2] = (ULONG_PTR)sizeof(ibuf); + rpcargs.args[3] = (ULONG_PTR)&readden; + rpcargs.args[4] = (ULONG_PTR)NULL; + + thread = CreateThread(NULL, 0, rpcThreadMain, (void *)&rpcargs, 0, &threadId); + ok(thread != NULL, "CreateThread failed. %d\n", GetLastError()); + ret = WaitForSingleObject(thread, 200); + todo_wine + ok(ret == WAIT_OBJECT_0, "WaitForSingleObject returned %d instead of %d.\n", ret, WAIT_OBJECT_0); + if (ret == WAIT_TIMEOUT) + { + ok(WriteFile(hFile, obuf, 0, &written, NULL), "WriteFile\n"); + ok(written == 0, "write file len\n"); + ret = WaitForSingleObject(thread, 200); + ok(ret == WAIT_OBJECT_0, "WaitForSingleObject returned %d instead of %d.\n", ret, WAIT_OBJECT_0); + } + CloseHandle(thread); + ok((BOOL)rpcargs.returnValue, "ReadFile\n"); + ok(readden == 0, "read got %d bytes\n", readden); + } + + /* similar to above, but now with PeekNamedPipe and multiple messages */ + memset(ibuf, 0, sizeof(ibuf)); + ok(WriteFile(hnp, obuf, 0, &written, NULL), "WriteFile\n"); + ok(written == 0, "write file len\n"); + ok(WriteFile(hnp, obuf, sizeof(obuf), &written, NULL), "WriteFile\n"); + ok(written == sizeof(obuf), "write file len\n"); + ok(PeekNamedPipe(hFile, NULL, 0, NULL, &readden, &leftmsg), "Peek\n"); + ok(readden == sizeof(obuf), "peek got %d bytes\n", readden); + ok(leftmsg == 0, "peek got %d bytes left in msg\n", leftmsg); + ok(PeekNamedPipe(hFile, NULL, 0, NULL, &readden, &leftmsg), "Peek\n"); + ok(readden == sizeof(obuf), "peek got %d bytes\n", readden); + todo_wine + ok(leftmsg == 0, "peek got %d bytes left in msg\n", leftmsg); + ok(ReadFile(hFile, ibuf, sizeof(ibuf), &readden, NULL), "ReadFile\n"); + todo_wine + ok(readden == 0, "read got %d bytes\n", readden); + if (readden == 0) + ok(ReadFile(hFile, ibuf, sizeof(ibuf), &readden, NULL), "ReadFile\n"); + ok(readden == sizeof(obuf), "read got %d bytes\n", readden); + ok(memcmp(obuf, ibuf, sizeof(obuf)) == 0, "content check\n"); + + memset(ibuf, 0, sizeof(ibuf)); + ok(WriteFile(hFile, obuf2, 0, &written, NULL), "WriteFile\n"); + ok(written == 0, "write file len\n"); + ok(WriteFile(hFile, obuf2, sizeof(obuf2), &written, NULL), "WriteFile\n"); + ok(written == sizeof(obuf2), "write file len\n"); + ok(PeekNamedPipe(hnp, NULL, 0, NULL, &readden, &leftmsg), "Peek\n"); + ok(readden == sizeof(obuf2), "peek got %d bytes\n", readden); + ok(leftmsg == 0, "peek got %d bytes left in msg\n", leftmsg); + ok(PeekNamedPipe(hnp, NULL, 0, NULL, &readden, &leftmsg), "Peek\n"); + ok(readden == sizeof(obuf2), "peek got %d bytes\n", readden); + todo_wine + ok(leftmsg == 0, "peek got %d bytes left in msg\n", leftmsg); + ok(ReadFile(hnp, ibuf, sizeof(ibuf), &readden, NULL), "ReadFile\n"); + todo_wine + ok(readden == 0, "read got %d bytes\n", readden); + if (readden == 0) + ok(ReadFile(hnp, ibuf, sizeof(ibuf), &readden, NULL), "ReadFile\n"); + ok(readden == sizeof(obuf2), "read got %d bytes\n", readden); + ok(memcmp(obuf2, ibuf, sizeof(obuf2)) == 0, "content check\n"); + /* Test how ReadFile behaves when the buffer is not big enough for the whole message */ memset(ibuf, 0, sizeof(ibuf)); ok(WriteFile(hnp, obuf2, sizeof(obuf2), &written, NULL), "WriteFile 7\n"); ok(written == sizeof(obuf2), "write file len 7\n"); SetLastError(0xdeadbeef); - todo_wine ok(!ReadFile(hFile, ibuf, 4, &readden, NULL), "ReadFile 7\n"); - todo_wine ok(GetLastError() == ERROR_MORE_DATA, "wrong error 7\n"); ok(readden == 4, "read got %d bytes 7\n", readden); ok(ReadFile(hFile, ibuf + 4, sizeof(ibuf) - 4, &readden, NULL), "ReadFile 7\n"); @@ -429,9 +740,7 @@ static void test_CreateNamedPipe(int pipemode) ok(WriteFile(hFile, obuf, sizeof(obuf), &written, NULL), "WriteFile 8\n"); ok(written == sizeof(obuf), "write file len 8\n"); SetLastError(0xdeadbeef); - todo_wine ok(!ReadFile(hnp, ibuf, 4, &readden, NULL), "ReadFile 8\n"); - todo_wine ok(GetLastError() == ERROR_MORE_DATA, "wrong error 8\n"); ok(readden == 4, "read got %d bytes 8\n", readden); ok(ReadFile(hnp, ibuf + 4, sizeof(ibuf) - 4, &readden, NULL), "ReadFile 8\n"); @@ -447,44 +756,76 @@ static void test_CreateNamedPipe(int pipemode) ok(written == sizeof(obuf), "write file len 9\n"); ok(WriteFile(hnp, obuf2, sizeof(obuf2), &written, NULL), "WriteFile 9\n"); ok(written == sizeof(obuf2), "write file len 9\n"); + readden = leftmsg = -1; + ok(PeekNamedPipe(hFile, NULL, 0, NULL, &readden, &leftmsg), "PeekNamedPipe 9\n"); + ok(readden == sizeof(obuf) + sizeof(obuf2), "peek got %d bytes total 9\n", readden); + ok(leftmsg == sizeof(obuf), "peek got %d bytes left in message 9\n", leftmsg); + readden = leftmsg = -1; + ok(RpcPeekNamedPipe(hFile, NULL, 0, NULL, &readden, &leftmsg), "RpcPeekNamedPipe 9\n"); + ok(readden == sizeof(obuf) + sizeof(obuf2), "peek got %d bytes total 9\n", readden); + ok(leftmsg == sizeof(obuf), "peek got %d bytes left in message 9\n", leftmsg); SetLastError(0xdeadbeef); - todo_wine ok(!ReadFile(hFile, ibuf, 4, &readden, NULL), "ReadFile 9\n"); - todo_wine ok(GetLastError() == ERROR_MORE_DATA, "wrong error 9\n"); ok(readden == 4, "read got %d bytes 9\n", readden); SetLastError(0xdeadbeef); ret = RpcReadFile(hFile, ibuf + 4, 4, &readden, NULL); - todo_wine ok(!ret, "RpcReadFile 9\n"); - todo_wine ok(GetLastError() == ERROR_MORE_DATA, "wrong error 9\n"); ok(readden == 4, "read got %d bytes 9\n", readden); + readden = leftmsg = -1; + ok(PeekNamedPipe(hFile, NULL, 0, NULL, &readden, &leftmsg), "PeekNamedPipe 9\n"); + ok(readden == sizeof(obuf) - 8 + sizeof(obuf2), "peek got %d bytes total 9\n", readden); + ok(leftmsg == sizeof(obuf) - 8, "peek got %d bytes left in message 9\n", leftmsg); + readden = leftmsg = -1; + ok(RpcPeekNamedPipe(hFile, NULL, 0, NULL, &readden, &leftmsg), "RpcPeekNamedPipe 9\n"); + ok(readden == sizeof(obuf) - 8 + sizeof(obuf2), "peek got %d bytes total 9\n", readden); + ok(leftmsg == sizeof(obuf) - 8, "peek got %d bytes left in message 9\n", leftmsg); ret = RpcReadFile(hFile, ibuf + 8, sizeof(ibuf), &readden, NULL); ok(ret, "RpcReadFile 9\n"); - todo_wine ok(readden == sizeof(obuf) - 8, "read got %d bytes 9\n", readden); ok(memcmp(obuf, ibuf, sizeof(obuf)) == 0, "content check 9\n"); if (readden <= sizeof(obuf) - 8) /* blocks forever if second part was already received */ { memset(ibuf, 0, sizeof(ibuf)); + readden = leftmsg = -1; + ok(PeekNamedPipe(hFile, NULL, 0, NULL, &readden, &leftmsg), "PeekNamedPipe 9\n"); + ok(readden == sizeof(obuf2), "peek got %d bytes total 9\n", readden); + ok(leftmsg == sizeof(obuf2), "peek got %d bytes left in message 9\n", leftmsg); + readden = leftmsg = -1; + ok(RpcPeekNamedPipe(hFile, NULL, 0, NULL, &readden, &leftmsg), "RpcPeekNamedPipe 9\n"); + ok(readden == sizeof(obuf2), "peek got %d bytes total 9\n", readden); + ok(leftmsg == sizeof(obuf2), "peek got %d bytes left in message 9\n", leftmsg); SetLastError(0xdeadbeef); ret = RpcReadFile(hFile, ibuf, 4, &readden, NULL); ok(!ret, "RpcReadFile 9\n"); - todo_wine ok(GetLastError() == ERROR_MORE_DATA, "wrong error 9\n"); ok(readden == 4, "read got %d bytes 9\n", readden); SetLastError(0xdeadbeef); - todo_wine ok(!ReadFile(hFile, ibuf + 4, 4, &readden, NULL), "ReadFile 9\n"); - todo_wine ok(GetLastError() == ERROR_MORE_DATA, "wrong error 9\n"); ok(readden == 4, "read got %d bytes 9\n", readden); + readden = leftmsg = -1; + ok(PeekNamedPipe(hFile, NULL, 0, NULL, &readden, &leftmsg), "PeekNamedPipe 9\n"); + ok(readden == sizeof(obuf2) - 8, "peek got %d bytes total 9\n", readden); + ok(leftmsg == sizeof(obuf2) - 8, "peek got %d bytes left in message 9\n", leftmsg); + readden = leftmsg = -1; + ok(RpcPeekNamedPipe(hFile, NULL, 0, NULL, &readden, &leftmsg), "RpcPeekNamedPipe 9\n"); + ok(readden == sizeof(obuf2) - 8, "peek got %d bytes total 9\n", readden); + ok(leftmsg == sizeof(obuf2) - 8, "peek got %d bytes left in message 9\n", leftmsg); ret = RpcReadFile(hFile, ibuf + 8, sizeof(ibuf), &readden, NULL); ok(ret, "RpcReadFile 9\n"); ok(readden == sizeof(obuf2) - 8, "read got %d bytes 9\n", readden); ok(memcmp(obuf2, ibuf, sizeof(obuf2)) == 0, "content check 9\n"); } + readden = leftmsg = -1; + ok(PeekNamedPipe(hFile, NULL, 0, NULL, &readden, &leftmsg), "PeekNamedPipe 9\n"); + ok(readden == 0, "peek got %d bytes total 9\n", readden); + ok(leftmsg == 0, "peek got %d bytes left in message 9\n", leftmsg); + readden = leftmsg = -1; + ok(RpcPeekNamedPipe(hFile, NULL, 0, NULL, &readden, &leftmsg), "RpcPeekNamedPipe 9\n"); + ok(readden == 0, "peek got %d bytes total 9\n", readden); + ok(leftmsg == 0, "peek got %d bytes left in message 9\n", leftmsg); /* Now the reverse direction */ memset(ibuf, 0, sizeof(ibuf)); @@ -492,47 +833,190 @@ static void test_CreateNamedPipe(int pipemode) ok(written == sizeof(obuf2), "write file len 10\n"); ok(WriteFile(hFile, obuf, sizeof(obuf), &written, NULL), "WriteFile 10\n"); ok(written == sizeof(obuf), "write file len 10\n"); + readden = leftmsg = -1; + ok(PeekNamedPipe(hnp, NULL, 0, NULL, &readden, &leftmsg), "PeekNamedPipe 10\n"); + ok(readden == sizeof(obuf) + sizeof(obuf2), "peek got %d bytes total 10\n", readden); + ok(leftmsg == sizeof(obuf2), "peek got %d bytes left in message 10\n", leftmsg); + readden = leftmsg = -1; + ok(RpcPeekNamedPipe(hnp, NULL, 0, NULL, &readden, &leftmsg), "RpcPeekNamedPipe 10\n"); + ok(readden == sizeof(obuf) + sizeof(obuf2), "peek got %d bytes total 10\n", readden); + ok(leftmsg == sizeof(obuf2), "peek got %d bytes left in message 10\n", leftmsg); SetLastError(0xdeadbeef); - todo_wine ok(!ReadFile(hnp, ibuf, 4, &readden, NULL), "ReadFile 10\n"); - todo_wine ok(GetLastError() == ERROR_MORE_DATA, "wrong error 10\n"); ok(readden == 4, "read got %d bytes 10\n", readden); SetLastError(0xdeadbeef); ret = RpcReadFile(hnp, ibuf + 4, 4, &readden, NULL); - todo_wine ok(!ret, "RpcReadFile 10\n"); - todo_wine ok(GetLastError() == ERROR_MORE_DATA, "wrong error 10\n"); ok(readden == 4, "read got %d bytes 10\n", readden); + readden = leftmsg = -1; + ok(PeekNamedPipe(hnp, NULL, 0, NULL, &readden, &leftmsg), "PeekNamedPipe 10\n"); + ok(readden == sizeof(obuf2) - 8 + sizeof(obuf), "peek got %d bytes total 10\n", readden); + ok(leftmsg == sizeof(obuf2) - 8, "peek got %d bytes left in message 10\n", leftmsg); + readden = leftmsg = -1; + ok(RpcPeekNamedPipe(hnp, NULL, 0, NULL, &readden, &leftmsg), "RpcPeekNamedPipe 10\n"); + ok(readden == sizeof(obuf2) - 8 + sizeof(obuf), "peek got %d bytes total 10\n", readden); + ok(leftmsg == sizeof(obuf2) - 8, "peek got %d bytes left in message 10\n", leftmsg); ret = RpcReadFile(hnp, ibuf + 8, sizeof(ibuf), &readden, NULL); ok(ret, "RpcReadFile 10\n"); - todo_wine ok(readden == sizeof(obuf2) - 8, "read got %d bytes 10\n", readden); ok(memcmp(obuf2, ibuf, sizeof(obuf2)) == 0, "content check 10\n"); if (readden <= sizeof(obuf2) - 8) /* blocks forever if second part was already received */ { memset(ibuf, 0, sizeof(ibuf)); + readden = leftmsg = -1; + ok(PeekNamedPipe(hnp, NULL, 0, NULL, &readden, &leftmsg), "PeekNamedPipe 10\n"); + ok(readden == sizeof(obuf), "peek got %d bytes total 10\n", readden); + ok(leftmsg == sizeof(obuf), "peek got %d bytes left in message 10\n", leftmsg); + readden = leftmsg = -1; + ok(RpcPeekNamedPipe(hnp, NULL, 0, NULL, &readden, &leftmsg), "RpcPeekNamedPipe 10\n"); + ok(readden == sizeof(obuf), "peek got %d bytes total 10\n", readden); + ok(leftmsg == sizeof(obuf), "peek got %d bytes left in message 10\n", leftmsg); SetLastError(0xdeadbeef); ret = RpcReadFile(hnp, ibuf, 4, &readden, NULL); ok(!ret, "RpcReadFile 10\n"); - todo_wine ok(GetLastError() == ERROR_MORE_DATA, "wrong error 10\n"); ok(readden == 4, "read got %d bytes 10\n", readden); SetLastError(0xdeadbeef); - todo_wine ok(!ReadFile(hnp, ibuf + 4, 4, &readden, NULL), "ReadFile 10\n"); - todo_wine ok(GetLastError() == ERROR_MORE_DATA, "wrong error 10\n"); ok(readden == 4, "read got %d bytes 10\n", readden); + readden = leftmsg = -1; + ok(PeekNamedPipe(hnp, NULL, 0, NULL, &readden, &leftmsg), "PeekNamedPipe 10\n"); + ok(readden == sizeof(obuf) - 8, "peek got %d bytes total 10\n", readden); + ok(leftmsg == sizeof(obuf) - 8, "peek got %d bytes left in message 10\n", leftmsg); + readden = leftmsg = -1; + ok(RpcPeekNamedPipe(hnp, NULL, 0, NULL, &readden, &leftmsg), "RpcPeekNamedPipe 10\n"); + ok(readden == sizeof(obuf) - 8, "peek got %d bytes total 10\n", readden); + ok(leftmsg == sizeof(obuf) - 8, "peek got %d bytes left in message 10\n", leftmsg); ret = RpcReadFile(hnp, ibuf + 8, sizeof(ibuf), &readden, NULL); ok(ret, "RpcReadFile 10\n"); ok(readden == sizeof(obuf) - 8, "read got %d bytes 10\n", readden); ok(memcmp(obuf, ibuf, sizeof(obuf)) == 0, "content check 10\n"); } + readden = leftmsg = -1; + ok(PeekNamedPipe(hnp, NULL, 0, NULL, &readden, &leftmsg), "PeekNamedPipe 10\n"); + ok(readden == 0, "peek got %d bytes total 10\n", readden); + ok(leftmsg == 0, "peek got %d bytes left in message 10\n", leftmsg); + readden = leftmsg = -1; + ok(RpcPeekNamedPipe(hnp, NULL, 0, NULL, &readden, &leftmsg), "RpcPeekNamedPipe 10\n"); + ok(readden == 0, "peek got %d bytes total 10\n", readden); + ok(leftmsg == 0, "peek got %d bytes left in message 10\n", leftmsg); } + /* Test behaviour for very huge messages (which don't fit completely in the buffer) */ + { + static char big_obuf[512 * 1024]; + static char big_ibuf[512 * 1024]; + struct rpcThreadArgs rpcargs; + HANDLE thread; + DWORD threadId; + memset(big_obuf, 0xAA, sizeof(big_obuf)); + + /* Ensure that both pipes are empty before we continue with the next test */ + while (PeekNamedPipe(hFile, NULL, 0, NULL, &readden, NULL) && readden > 0) + ok(ReadFile(hFile, big_ibuf, sizeof(big_ibuf), &readden, NULL) || + GetLastError() == ERROR_MORE_DATA, "ReadFile\n"); + + while (PeekNamedPipe(hnp, NULL, 0, NULL, &readden, NULL) && readden > 0) + ok(ReadFile(hnp, big_ibuf, sizeof(big_ibuf), &readden, NULL) || + GetLastError() == ERROR_MORE_DATA, "ReadFile\n"); + + readden = leftmsg = -1; + ok(PeekNamedPipe(hFile, NULL, 0, NULL, &readden, &leftmsg), "PeekNamedPipe\n"); + ok(readden == 0, "peek got %d bytes total\n", readden); + ok(leftmsg == 0, "peek got %d bytes left in message\n", leftmsg); + + /* transmit big message, receive with buffer of equal size */ + memset(big_ibuf, 0, sizeof(big_ibuf)); + rpcargs.returnValue = 0; + rpcargs.lastError = GetLastError(); + rpcargs.op = RPC_WRITEFILE; + rpcargs.args[0] = (ULONG_PTR)hnp; + rpcargs.args[1] = (ULONG_PTR)big_obuf; + rpcargs.args[2] = (ULONG_PTR)sizeof(big_obuf); + rpcargs.args[3] = (ULONG_PTR)&written; + rpcargs.args[4] = (ULONG_PTR)NULL; + + thread = CreateThread(NULL, 0, rpcThreadMain, (void *)&rpcargs, 0, &threadId); + ok(thread != NULL, "CreateThread failed. %d\n", GetLastError()); + ret = WaitForSingleObject(thread, 200); + ok(ret == WAIT_TIMEOUT, "WaitForSingleObject returned %d instead of %d.\n", ret, WAIT_TIMEOUT); + ok(ReadFile(hFile, big_ibuf, sizeof(big_ibuf), &readden, NULL), "ReadFile\n"); + todo_wine + ok(readden == sizeof(big_obuf), "read got %d bytes\n", readden); + todo_wine + ok(memcmp(big_ibuf, big_obuf, sizeof(big_obuf)) == 0, "content check\n"); + do + { + ret = WaitForSingleObject(thread, 1); + while (PeekNamedPipe(hFile, NULL, 0, NULL, &readden, NULL) && readden > 0) + ok(ReadFile(hFile, big_ibuf, sizeof(big_ibuf), &readden, NULL) || + GetLastError() == ERROR_MORE_DATA, "ReadFile\n"); + } + while (ret == WAIT_TIMEOUT); + ok(WaitForSingleObject(thread, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed with %d.\n", GetLastError()); + ok((BOOL)rpcargs.returnValue, "WriteFile\n"); + ok(written == sizeof(big_obuf), "write file len\n"); + CloseHandle(thread); + + readden = leftmsg = -1; + ok(PeekNamedPipe(hFile, NULL, 0, NULL, &readden, &leftmsg), "PeekNamedPipe\n"); + ok(readden == 0, "peek got %d bytes total\n", readden); + ok(leftmsg == 0, "peek got %d bytes left in message\n", leftmsg); + + /* same as above, but receive as multiple parts */ + memset(big_ibuf, 0, sizeof(big_ibuf)); + rpcargs.returnValue = 0; + rpcargs.lastError = GetLastError(); + + thread = CreateThread(NULL, 0, rpcThreadMain, (void *)&rpcargs, 0, &threadId); + ok(thread != NULL, "CreateThread failed. %d\n", GetLastError()); + ret = WaitForSingleObject(thread, 200); + ok(ret == WAIT_TIMEOUT, "WaitForSingleObject returned %d instead of %d.\n", ret, WAIT_TIMEOUT); + if (pipemode == PIPE_TYPE_BYTE) + { + ok(ReadFile(hFile, big_ibuf, 32, &readden, NULL), "ReadFile\n"); + ok(readden == 32, "read got %d bytes\n", readden); + ok(ReadFile(hFile, big_ibuf + 32, 32, &readden, NULL), "ReadFile\n"); + } + else + { + SetLastError(0xdeadbeef); + ok(!ReadFile(hFile, big_ibuf, 32, &readden, NULL), "ReadFile\n"); + ok(GetLastError() == ERROR_MORE_DATA, "wrong error\n"); + ok(readden == 32, "read got %d bytes\n", readden); + SetLastError(0xdeadbeef); + ok(!ReadFile(hFile, big_ibuf + 32, 32, &readden, NULL), "ReadFile\n"); + ok(GetLastError() == ERROR_MORE_DATA, "wrong error\n"); + } + ok(readden == 32, "read got %d bytes\n", readden); + ok(ReadFile(hFile, big_ibuf + 64, sizeof(big_ibuf) - 64, &readden, NULL), "ReadFile\n"); + todo_wine + ok(readden == sizeof(big_obuf) - 64, "read got %d bytes\n", readden); + todo_wine + ok(memcmp(big_ibuf, big_obuf, sizeof(big_obuf)) == 0, "content check\n"); + do + { + ret = WaitForSingleObject(thread, 1); + while (PeekNamedPipe(hFile, NULL, 0, NULL, &readden, NULL) && readden > 0) + ok(ReadFile(hFile, big_ibuf, sizeof(big_ibuf), &readden, NULL) || + GetLastError() == ERROR_MORE_DATA, "ReadFile\n"); + } + while (ret == WAIT_TIMEOUT); + ok(WaitForSingleObject(thread, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed with %d.\n", GetLastError()); + ok((BOOL)rpcargs.returnValue, "WriteFile\n"); + ok(written == sizeof(big_obuf), "write file len\n"); + CloseHandle(thread); + + readden = leftmsg = -1; + ok(PeekNamedPipe(hFile, NULL, 0, NULL, &readden, &leftmsg), "PeekNamedPipe\n"); + ok(readden == 0, "peek got %d bytes total\n", readden); + ok(leftmsg == 0, "peek got %d bytes left in message\n", leftmsg); + } + /* Picky conformance tests */ /* Verify that you can't connect to pipe again @@ -573,6 +1057,15 @@ static void test_CreateNamedPipe(int pipemode) ok(CloseHandle(hnp), "CloseHandle\n"); + hnp = CreateNamedPipeA(PIPENAME_SPECIAL, PIPE_ACCESS_DUPLEX, pipemode | PIPE_WAIT, + /* nMaxInstances */ 1, + /* nOutBufSize */ 1024, + /* nInBufSize */ 1024, + /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT, + /* lpSecurityAttrib */ NULL); + ok(hnp != INVALID_HANDLE_VALUE, "CreateNamedPipe with special characters failed\n"); + ok(CloseHandle(hnp), "CloseHandle\n"); + trace("test_CreateNamedPipe returning\n"); } @@ -661,6 +1154,167 @@ static void test_CreateNamedPipe_instances_must_match(void) ok(CloseHandle(hnp2), "CloseHandle\n"); } +static void test_CloseNamedPipe(void) +{ + HANDLE hnp; + HANDLE hFile; + static const char obuf[] = "Bit Bucket"; + char ibuf[32]; + DWORD written; + DWORD readden; + + hnp = CreateNamedPipeA(PIPENAME, PIPE_ACCESS_DUPLEX, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, + /* nMaxInstances */ 1, + /* nOutBufSize */ 1024, + /* nInBufSize */ 1024, + /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT, + /* lpSecurityAttrib */ NULL); + ok(hnp != INVALID_HANDLE_VALUE, "CreateNamedPipe failed\n"); + + hFile = CreateFileA(PIPENAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0); + ok(hFile != INVALID_HANDLE_VALUE, "CreateFile failed (%d)\n", GetLastError()); + + /* don't try to do i/o if one side couldn't be opened, as it hangs */ + if (hFile != INVALID_HANDLE_VALUE) + { + /* Make sure we can read and write a few bytes in both directions */ + memset(ibuf, 0, sizeof(ibuf)); + ok(WriteFile(hnp, obuf, sizeof(obuf), &written, NULL), "WriteFile\n"); + ok(written == sizeof(obuf), "write file len 1\n"); + ok(PeekNamedPipe(hFile, NULL, 0, NULL, &readden, NULL), "Peek\n"); + ok(readden == sizeof(obuf), "got %d bytes\n", readden); + + /* close server end without disconnecting */ + ok(CloseHandle(hnp), "CloseHandle() failed: %08x\n", GetLastError()); + + ok(ReadFile(hFile, ibuf, 0, &readden, NULL), "ReadFile() failed: %08x\n", GetLastError()); + ok(readden == 0, "got %d bytes\n", readden); + + memset(ibuf, 0, sizeof(ibuf)); + ok(ReadFile(hFile, ibuf, sizeof(ibuf), &readden, NULL), "ReadFile() failed: %08x\n", GetLastError()); + ok(readden == sizeof(obuf), "got %d bytes\n", readden); + /* pipe is empty now */ + + SetLastError(0xdeadbeef); + ok(!ReadFile(hFile, ibuf, 0, &readden, NULL), "ReadFile() succeeded\n"); + ok(GetLastError() == ERROR_BROKEN_PIPE, "GetLastError() returned %08x, expected ERROR_BROKEN_PIPE\n", GetLastError()); + SetLastError(0); + + CloseHandle(hFile); + } + + hnp = CreateNamedPipeA(PIPENAME, PIPE_ACCESS_DUPLEX, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, + /* nMaxInstances */ 1, + /* nOutBufSize */ 1024, + /* nInBufSize */ 1024, + /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT, + /* lpSecurityAttrib */ NULL); + ok(hnp != INVALID_HANDLE_VALUE, "CreateNamedPipe failed\n"); + + hFile = CreateFileA(PIPENAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0); + ok(hFile != INVALID_HANDLE_VALUE, "CreateFile failed (%d)\n", GetLastError()); + + /* don't try to do i/o if one side couldn't be opened, as it hangs */ + if (hFile != INVALID_HANDLE_VALUE) + { + ok(WriteFile(hnp, obuf, 0, &written, NULL), "WriteFile\n"); + ok(written == 0, "write file len 1\n"); + + /* close server end without disconnecting */ + ok(CloseHandle(hnp), "CloseHandle() failed: %08x\n", GetLastError()); + + todo_wine + ok(ReadFile(hFile, ibuf, sizeof(ibuf), &readden, NULL), "ReadFile() failed: %08x\n", GetLastError()); + ok(readden == 0, "got %d bytes\n", readden); + /* pipe is empty now */ + + SetLastError(0xdeadbeef); + ok(!ReadFile(hFile, ibuf, sizeof(ibuf), &readden, NULL), "ReadFile() succeeded\n"); + todo_wine + ok(GetLastError() == ERROR_BROKEN_PIPE, "GetLastError() returned %08x, expected ERROR_BROKEN_PIPE\n", GetLastError()); + SetLastError(0); + + CloseHandle(hFile); + } + + hnp = CreateNamedPipeA(PIPENAME, PIPE_ACCESS_DUPLEX, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, + /* nMaxInstances */ 1, + /* nOutBufSize */ 1024, + /* nInBufSize */ 1024, + /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT, + /* lpSecurityAttrib */ NULL); + ok(hnp != INVALID_HANDLE_VALUE, "CreateNamedPipe failed\n"); + + hFile = CreateFileA(PIPENAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0); + ok(hFile != INVALID_HANDLE_VALUE, "CreateFile failed (%d)\n", GetLastError()); + + if (hFile != INVALID_HANDLE_VALUE) + { + /* Make sure we can read and write a few bytes in both directions */ + memset(ibuf, 0, sizeof(ibuf)); + ok(WriteFile(hFile, obuf, sizeof(obuf), &written, NULL), "WriteFile\n"); + ok(written == sizeof(obuf), "write file len 1\n"); + ok(PeekNamedPipe(hnp, NULL, 0, NULL, &readden, NULL), "Peek\n"); + ok(readden == sizeof(obuf), "got %d bytes\n", readden); + + /* close client end without disconnecting */ + ok(CloseHandle(hFile), "CloseHandle() failed: %08x\n", GetLastError()); + + /* you'd think ERROR_MORE_DATA, but no */ + ok(ReadFile(hnp, ibuf, 0, &readden, NULL), "ReadFile() failed: %08x\n", GetLastError()); + ok(readden == 0, "got %d bytes\n", readden); + + memset(ibuf, 0, sizeof(ibuf)); + ok(ReadFile(hnp, ibuf, sizeof(ibuf), &readden, NULL), "ReadFile() failed: %08x\n", GetLastError()); + ok(readden == sizeof(obuf), "got %d bytes\n", readden); + /* pipe is empty now */ + + SetLastError(0xdeadbeef); + ok(!ReadFile(hnp, ibuf, 0, &readden, NULL), "ReadFile() succeeded\n"); + ok(GetLastError() == ERROR_BROKEN_PIPE, "GetLastError() returned %08x, expected ERROR_BROKEN_PIPE\n", GetLastError()); + SetLastError(0); + + CloseHandle(hnp); + } + + hnp = CreateNamedPipeA(PIPENAME, PIPE_ACCESS_DUPLEX, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, + /* nMaxInstances */ 1, + /* nOutBufSize */ 1024, + /* nInBufSize */ 1024, + /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT, + /* lpSecurityAttrib */ NULL); + ok(hnp != INVALID_HANDLE_VALUE, "CreateNamedPipe failed\n"); + + hFile = CreateFileA(PIPENAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0); + ok(hFile != INVALID_HANDLE_VALUE, "CreateFile failed (%d)\n", GetLastError()); + + /* don't try to do i/o if one side couldn't be opened, as it hangs */ + if (hFile != INVALID_HANDLE_VALUE) + { + ok(WriteFile(hFile, obuf, 0, &written, NULL), "WriteFile\n"); + ok(written == 0, "write file len 1\n"); + + /* close server end without disconnecting */ + ok(CloseHandle(hFile), "CloseHandle() failed: %08x\n", GetLastError()); + + todo_wine + ok(ReadFile(hnp, ibuf, sizeof(ibuf), &readden, NULL), "ReadFile() failed: %08x\n", GetLastError()); + ok(readden == 0, "got %d bytes\n", readden); + /* pipe is empty now */ + + SetLastError(0xdeadbeef); + ok(!ReadFile(hnp, ibuf, sizeof(ibuf), &readden, NULL), "ReadFile() succeeded\n"); + ok(GetLastError() == ERROR_BROKEN_PIPE, "GetLastError() returned %08x, expected ERROR_BROKEN_PIPE\n", GetLastError()); + SetLastError(0); + + CloseHandle(hnp); + } +} + /** implementation of alarm() */ static DWORD CALLBACK alarmThreadMain(LPVOID arg) { @@ -1884,6 +2538,121 @@ static void test_overlapped(void) CloseHandle(thread); } +static void test_nowait(int pipemode) +{ + HANDLE hnp; + HANDLE hFile; + static const char obuf[] = "Bit Bucket"; + char ibuf[32]; + DWORD written; + DWORD readden; + DWORD lpmode; + + hnp = CreateNamedPipeA(PIPENAME, PIPE_ACCESS_DUPLEX, + pipemode | PIPE_NOWAIT, + /* nMaxInstances */ 1, + /* nOutBufSize */ 1024, + /* nInBufSize */ 1024, + /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT, + /* lpSecurityAttrib */ NULL); + ok(hnp != INVALID_HANDLE_VALUE, "CreateNamedPipe failed\n"); + + hFile = CreateFileA(PIPENAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0); + ok(hFile != INVALID_HANDLE_VALUE, "CreateFile failed (%d)\n", GetLastError()); + + /* don't try to do i/o if one side couldn't be opened, as it hangs */ + if (hFile != INVALID_HANDLE_VALUE) + { + /* send message from client to server */ + ok(WriteFile(hFile, obuf, sizeof(obuf), &written, NULL), "WriteFile\n"); + ok(written == sizeof(obuf), "write file len\n"); + ok(PeekNamedPipe(hnp, NULL, 0, NULL, &readden, NULL), "Peek\n"); + ok(readden == sizeof(obuf), "got %d bytes\n", readden); + + memset(ibuf, 0, sizeof(ibuf)); + ok(ReadFile(hnp, ibuf, sizeof(ibuf), &readden, NULL), "ReadFile() failed: %08x\n", GetLastError()); + ok(readden == sizeof(obuf), "got %d bytes\n", readden); + ok(memcmp(obuf, ibuf, sizeof(obuf)) == 0, "content check\n"); + + readden = 0xdeadbeef; + SetLastError(0xdeadbeef); + ok(!ReadFile(hnp, ibuf, sizeof(ibuf), &readden, NULL), "ReadFile() succeeded\n"); + ok(readden == 0, "got %d bytes\n", readden); + ok(GetLastError() == ERROR_NO_DATA, "GetLastError() returned %08x, expected ERROR_NO_DATA\n", GetLastError()); + + lpmode = (pipemode & PIPE_READMODE_MESSAGE) | PIPE_NOWAIT; + ok(SetNamedPipeHandleState(hFile, &lpmode, NULL, NULL), "Change mode\n"); + + /* send message from server to client */ + ok(WriteFile(hnp, obuf, sizeof(obuf), &written, NULL), "WriteFile\n"); + ok(written == sizeof(obuf), "write file len\n"); + ok(PeekNamedPipe(hFile, NULL, 0, NULL, &readden, NULL), "Peek\n"); + ok(readden == sizeof(obuf), "got %d bytes\n", readden); + + memset(ibuf, 0, sizeof(ibuf)); + ok(ReadFile(hFile, ibuf, sizeof(ibuf), &readden, NULL), "ReadFile() failed: %08x\n", GetLastError()); + ok(readden == sizeof(obuf), "got %d bytes\n", readden); + ok(memcmp(obuf, ibuf, sizeof(obuf)) == 0, "content check\n"); + + readden = 0xdeadbeef; + SetLastError(0xdeadbeef); + ok(!ReadFile(hFile, ibuf, sizeof(ibuf), &readden, NULL), "ReadFile() succeeded\n"); + ok(readden == 0, "got %d bytes\n", readden); + ok(GetLastError() == ERROR_NO_DATA, "GetLastError() returned %08x, expected ERROR_NO_DATA\n", GetLastError()); + + /* now again the bad zero byte message test */ + ok(WriteFile(hFile, obuf, 0, &written, NULL), "WriteFile\n"); + ok(written == 0, "write file len\n"); + + if (pipemode != PIPE_TYPE_BYTE) + { + ok(ReadFile(hnp, ibuf, sizeof(ibuf), &readden, NULL), "ReadFile() failed: %08x\n", GetLastError()); + ok(readden == 0, "got %d bytes\n", readden); + } + else + { + SetLastError(0xdeadbeef); + ok(!ReadFile(hnp, ibuf, sizeof(ibuf), &readden, NULL), "ReadFile() succeeded\n"); + ok(readden == 0, "got %d bytes\n", readden); + ok(GetLastError() == ERROR_NO_DATA, "GetLastError() returned %08x, expected ERROR_NO_DATA\n", GetLastError()); + } + + readden = 0xdeadbeef; + SetLastError(0xdeadbeef); + ok(!ReadFile(hnp, ibuf, sizeof(ibuf), &readden, NULL), "ReadFile() succeeded\n"); + ok(readden == 0, "got %d bytes\n", readden); + ok(GetLastError() == ERROR_NO_DATA, "GetLastError() returned %08x, expected ERROR_NO_DATA\n", GetLastError()); + + /* and the same for the reverse direction */ + ok(WriteFile(hnp, obuf, 0, &written, NULL), "WriteFile\n"); + ok(written == 0, "write file len\n"); + + if (pipemode != PIPE_TYPE_BYTE) + { + ok(ReadFile(hFile, ibuf, sizeof(ibuf), &readden, NULL), "ReadFile() failed: %08x\n", GetLastError()); + ok(readden == 0, "got %d bytes\n", readden); + } + else + { + SetLastError(0xdeadbeef); + ok(!ReadFile(hFile, ibuf, sizeof(ibuf), &readden, NULL), "ReadFile() succeeded\n"); + ok(readden == 0, "got %d bytes\n", readden); + ok(GetLastError() == ERROR_NO_DATA, "GetLastError() returned %08x, expected ERROR_NO_DATA\n", GetLastError()); + } + + readden = 0xdeadbeef; + SetLastError(0xdeadbeef); + ok(!ReadFile(hFile, ibuf, sizeof(ibuf), &readden, NULL), "ReadFile() succeeded\n"); + ok(readden == 0, "got %d bytes\n", readden); + ok(GetLastError() == ERROR_NO_DATA, "GetLastError() returned %08x, expected ERROR_NO_DATA\n", GetLastError()); + + ok(CloseHandle(hFile), "CloseHandle\n"); + } + + ok(CloseHandle(hnp), "CloseHandle\n"); + +} + static void test_NamedPipeHandleState(void) { HANDLE server, client; @@ -1989,10 +2758,11 @@ static void test_readfileex_pending(void) { HANDLE server, client, event; BOOL ret; - DWORD err, wait, num_bytes; + DWORD err, wait, num_bytes, lpmode; OVERLAPPED overlapped; char read_buf[1024]; char write_buf[1024]; + const char long_test_string[] = "12test3456ab"; const char test_string[] = "test"; int i; @@ -2032,6 +2802,9 @@ static void test_readfileex_pending(void) 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"); + wait = WaitForSingleObject(event, 100); + ok(wait == WAIT_TIMEOUT, "WaitForSingleObjectEx returned %x\n", wait); + ok(completion_called == 0, "completion routine called before WriteFile started\n"); ret = WriteFile(client, test_string, strlen(test_string), &num_bytes, NULL); ok(ret == TRUE, "WriteFile failed\n"); @@ -2113,7 +2886,6 @@ static void test_readfileex_pending(void) num_bytes = 0xdeadbeef; SetLastError(0xdeadbeef); ret = ReadFile(server, read_buf, 0, &num_bytes, &overlapped); -todo_wine ok(GetLastError() == ERROR_IO_PENDING, "expected ERROR_IO_PENDING, got %d\n", GetLastError()); ok(num_bytes == 0, "bytes %u\n", num_bytes); ok((NTSTATUS)overlapped.Internal == STATUS_PENDING, "expected STATUS_PENDING, got %#lx\n", overlapped.Internal); @@ -2129,11 +2901,9 @@ todo_wine ok(num_bytes == 1, "bytes %u\n", num_bytes); wait = WaitForSingleObject(event, 100); -todo_wine ok(wait == WAIT_OBJECT_0, "WaitForSingleObject returned %x\n", wait); ok(num_bytes == 1, "bytes %u\n", num_bytes); -todo_wine ok((NTSTATUS)overlapped.Internal == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx\n", overlapped.Internal); ok(overlapped.InternalHigh == 0, "expected 0, got %lu\n", overlapped.InternalHigh); @@ -2143,6 +2913,440 @@ todo_wine ok(ret, "ReadFile failed\n"); ok(num_bytes == 1, "bytes %u\n", num_bytes); + CloseHandle(client); + CloseHandle(server); + + /* On Windows versions > 2000 it is not possible to add PIPE_NOWAIT to a byte-mode + * PIPE after creating. Create a new pipe for the following tests. */ + server = CreateNamedPipeA(PIPENAME, FILE_FLAG_OVERLAPPED | PIPE_ACCESS_DUPLEX, + /* dwOpenMode */ PIPE_TYPE_BYTE | PIPE_NOWAIT, + /* nMaxInstances */ 1, + /* nOutBufSize */ 1024, + /* nInBufSize */ 1024, + /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT, + /* lpSecurityAttrib */ NULL); + ok(server != INVALID_HANDLE_VALUE, "cf failed\n"); + + client = CreateFileA(PIPENAME, GENERIC_READ|GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, 0, NULL); + ok(client != INVALID_HANDLE_VALUE, "cf failed\n"); + + memset(&overlapped, 0, sizeof(overlapped)); + overlapped.hEvent = event; + + /* Initial check with empty pipe */ + memset(read_buf, 0, sizeof(read_buf)); + completion_called = 0; + ResetEvent(event); + ret = ReadFileEx(server, read_buf, 4, &overlapped, completion_routine); + ok(ret == FALSE, "ReadFileEx succeded\n"); + ok(completion_called == 0, "completion routine called before ReadFileEx returned\n"); + ok(GetLastError() == ERROR_NO_DATA, "expected ERROR_NO_DATA, got %d\n", GetLastError()); + wait = WaitForSingleObjectEx(event, 0, TRUE); + todo_wine + ok(wait == WAIT_TIMEOUT, "WaitForSingleObjectEx returned %x\n", wait); + ok(completion_called == 0, "completion routine called before writing to file\n"); + + /* Call ReadFileEx after writing content to the pipe */ + num_bytes = 0xdeadbeef; + ret = WriteFile(client, long_test_string, strlen(long_test_string), &num_bytes, NULL); + ok(ret, "WriteFile failed, err=%i\n", GetLastError()); + ok(num_bytes == strlen(long_test_string), "bytes %u\n", num_bytes); + + memset(read_buf, 0, sizeof(read_buf)); + completion_called = 0; + ResetEvent(event); + ret = ReadFileEx(server, read_buf, 4, &overlapped, completion_routine); + ok(ret == TRUE, "ReadFileEx failed, err=%i\n", GetLastError()); + ok(completion_called == 0, "completion routine called before ReadFileEx returned\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 == 4, "ReadFileEx returned only %d bytes\n", completion_num_bytes); + ok(completion_lpoverlapped == &overlapped, "completion called with wrong overlapped pointer\n"); + + ret = ReadFile(server, read_buf + 4, sizeof(read_buf) - 4, &num_bytes, NULL); + ok(ret == TRUE, "ReadFile succeeded\n"); + ok(num_bytes == strlen(long_test_string)-4, "ReadFile returned only %d bytes\n", num_bytes); + ok(!memcmp(long_test_string, read_buf, strlen(long_test_string)), "ReadFile read wrong bytes\n"); + + /* Same again, but read as a single part */ + num_bytes = 0xdeadbeef; + ret = WriteFile(client, long_test_string, strlen(long_test_string), &num_bytes, NULL); + ok(ret, "WriteFile failed, err=%i\n", GetLastError()); + ok(num_bytes == strlen(long_test_string), "bytes %u\n", num_bytes); + + memset(read_buf, 0, sizeof(read_buf)); + 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"); + 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(long_test_string), "ReadFileEx returned only %d bytes\n", completion_num_bytes); + ok(completion_lpoverlapped == &overlapped, "completion called with wrong overlapped pointer\n"); + ok(!memcmp(long_test_string, read_buf, strlen(long_test_string)), "ReadFile read wrong bytes\n"); + + /* Check content of overlapped structure */ + memset(read_buf, 0, sizeof(read_buf)); + S(U(overlapped)).Offset = 0; + S(U(overlapped)).OffsetHigh = 0; + overlapped.Internal = -1; + overlapped.InternalHigh = -1; + overlapped.hEvent = event; + num_bytes = 0xdeadbeef; + SetLastError(0xdeadbeef); + ret = ReadFile(server, read_buf, 4, &num_bytes, &overlapped); + ok(ret == FALSE, "ReadFile succeeded\n"); + ok(GetLastError() == ERROR_NO_DATA, "expected ERROR_NO_DATA, got %d\n", GetLastError()); + ok(num_bytes == 0, "bytes %u\n", num_bytes); + ok((NTSTATUS)overlapped.Internal == STATUS_PENDING, "expected STATUS_PENDING, got %#lx\n", overlapped.Internal); + todo_wine + ok(overlapped.InternalHigh == -1, "expected -1, got %lu\n", overlapped.InternalHigh); + wait = WaitForSingleObject(event, 100); + ok(wait == WAIT_TIMEOUT, "WaitForSingleObjectEx returned %x\n", wait); + ok((NTSTATUS)overlapped.Internal == STATUS_PENDING, "expected STATUS_PENDING, got %#lx\n", overlapped.Internal); + todo_wine + ok(overlapped.InternalHigh == -1, "expected -1, got %lu\n", overlapped.InternalHigh); + + /* Call ReadFile after writing to the pipe */ + num_bytes = 0xdeadbeef; + ret = WriteFile(client, long_test_string, strlen(long_test_string), &num_bytes, NULL); + ok(ret, "WriteFile failed, err=%i\n", GetLastError()); + ok(num_bytes == strlen(long_test_string), "bytes %u\n", num_bytes); + + memset(read_buf, 0, sizeof(read_buf)); + S(U(overlapped)).Offset = 0; + S(U(overlapped)).OffsetHigh = 0; + overlapped.Internal = -1; + overlapped.InternalHigh = -1; + overlapped.hEvent = event; + num_bytes = 0xdeadbeef; + SetLastError(0xdeadbeef); + ret = ReadFile(server, read_buf, 4, &num_bytes, &overlapped); + ok(ret == TRUE, "ReadFile failed, err=%i\n", GetLastError()); + ok(num_bytes == 4, "bytes %u\n", num_bytes); + ok((NTSTATUS)overlapped.Internal == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx\n", overlapped.Internal); + ok(overlapped.InternalHigh == 4, "expected 4, got %lu\n", overlapped.InternalHigh); + wait = WaitForSingleObject(event, 100); + ok(wait == WAIT_IO_COMPLETION || wait == WAIT_OBJECT_0, "WaitForSingleObject returned %x\n", wait); + ok((NTSTATUS)overlapped.Internal == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx\n", overlapped.Internal); + ok(overlapped.InternalHigh == 4, "expected 4, got %lu\n", overlapped.InternalHigh); + + ret = ReadFile(server, read_buf + 4, sizeof(read_buf) - 4, &num_bytes, NULL); + ok(ret == TRUE, "ReadFile failed\n"); + ok(num_bytes == strlen(long_test_string)-4, "ReadFile returned only %d bytes\n", num_bytes); + ok(!memcmp(long_test_string, read_buf, strlen(long_test_string)), "ReadFile read wrong bytes\n"); + + /* Same again, but read as a single part */ + num_bytes = 0xdeadbeef; + ret = WriteFile(client, long_test_string, strlen(long_test_string), &num_bytes, NULL); + ok(ret, "WriteFile failed, err=%i\n", GetLastError()); + ok(num_bytes == strlen(long_test_string), "bytes %u\n", num_bytes); + + memset(read_buf, 0, sizeof(read_buf)); + S(U(overlapped)).Offset = 0; + S(U(overlapped)).OffsetHigh = 0; + overlapped.Internal = -1; + overlapped.InternalHigh = -1; + overlapped.hEvent = event; + num_bytes = 0xdeadbeef; + SetLastError(0xdeadbeef); + ret = ReadFile(server, read_buf, sizeof(read_buf), &num_bytes, &overlapped); + ok(ret == TRUE, "ReadFile failed, err=%i\n", GetLastError()); + ok(num_bytes == strlen(long_test_string), "bytes %u\n", num_bytes); + ok((NTSTATUS)overlapped.Internal == 0, "expected 0, got %#lx\n", overlapped.Internal); + ok(overlapped.InternalHigh == strlen(long_test_string), "expected %u, got %lu\n", (DWORD)strlen(long_test_string), overlapped.InternalHigh); + wait = WaitForSingleObject(event, 100); + ok(wait == WAIT_IO_COMPLETION || wait == WAIT_OBJECT_0, "WaitForSingleObject returned %x\n", wait); + ok((NTSTATUS)overlapped.Internal == 0, "expected 0, got %#lx\n", overlapped.Internal); + ok(overlapped.InternalHigh == strlen(long_test_string), "expected %u, got %lu\n", (DWORD)strlen(long_test_string), overlapped.InternalHigh); + ok(!memcmp(long_test_string, read_buf, strlen(long_test_string)), "ReadFile read wrong bytes\n"); + + CloseHandle(client); + CloseHandle(server); + + server = CreateNamedPipeA(PIPENAME, FILE_FLAG_OVERLAPPED | PIPE_ACCESS_DUPLEX, + /* dwOpenMode */ PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, + /* nMaxInstances */ 1, + /* nOutBufSize */ 1024, + /* nInBufSize */ 1024, + /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT, + /* lpSecurityAttrib */ NULL); + ok(server != INVALID_HANDLE_VALUE, "cf failed\n"); + + client = CreateFileA(PIPENAME, GENERIC_READ|GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, 0, NULL); + ok(client != INVALID_HANDLE_VALUE, "cf failed\n"); + + memset(&overlapped, 0, sizeof(overlapped)); + overlapped.hEvent = event; + + /* Start a call to ReadFileEx which cannot complete immediately */ + memset(read_buf, 0, sizeof(read_buf)); + completion_called = 0; + ResetEvent(event); + ret = ReadFileEx(server, read_buf, 4, &overlapped, completion_routine); + ok(ret == TRUE, "ReadFileEx failed, err=%i\n", GetLastError()); + ok(completion_called == 0, "completion routine called before ReadFileEx returned\n"); + wait = WaitForSingleObject(event, 100); + ok(wait == WAIT_TIMEOUT, "WaitForSingleObject returned %x\n", wait); + ok(completion_called == 0, "completion routine called before WriteFile started\n"); + + ret = WriteFile(client, long_test_string, strlen(long_test_string), &num_bytes, NULL); + ok(ret == TRUE, "WriteFile failed\n"); + ok(num_bytes == strlen(long_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 == 4, "ReadFileEx returned only %d bytes\n", completion_num_bytes); + ok(completion_lpoverlapped == &overlapped, "completion called with wrong overlapped pointer\n"); + ok(!memcmp(long_test_string, read_buf, 4), "ReadFileEx read wrong bytes\n"); + + ret = ReadFile(server, read_buf + 4, 4, &num_bytes, NULL); + ok(ret == FALSE, "ReadFile succeeded\n"); + ok(num_bytes == 4, "ReadFile returned only %d bytes\n", num_bytes); + ok(GetLastError() == ERROR_MORE_DATA, "wrong error\n"); + ret = ReadFile(server, read_buf + 8, sizeof(read_buf) - 8, &num_bytes, NULL); + ok(ret == TRUE, "ReadFile failed\n"); + ok(num_bytes == strlen(long_test_string)-8, "ReadFile returned only %d bytes\n", num_bytes); + ok(!memcmp(long_test_string, read_buf, strlen(long_test_string)), "ReadFile read wrong bytes\n"); + + /* Call ReadFileEx when there is already some content in the pipe */ + ret = WriteFile(client, long_test_string, strlen(long_test_string), &num_bytes, NULL); + ok(ret == TRUE, "WriteFile failed\n"); + ok(num_bytes == strlen(long_test_string), "only %i bytes written\n", num_bytes); + + memset(read_buf, 0, sizeof(read_buf)); + completion_called = 0; + ResetEvent(event); + ret = ReadFileEx(server, read_buf, 4, &overlapped, completion_routine); + ok(ret == TRUE, "ReadFileEx failed, err=%i\n", GetLastError()); + ok(completion_called == 0, "completion routine called before ReadFileEx returned\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 == 4, "ReadFileEx returned only %d bytes\n", completion_num_bytes); + ok(completion_lpoverlapped == &overlapped, "completion called with wrong overlapped pointer\n"); + ok(!memcmp(long_test_string, read_buf, 4), "ReadFileEx read wrong bytes\n"); + + ret = ReadFile(server, read_buf + 4, sizeof(read_buf) - 4, &num_bytes, NULL); + ok(ret == TRUE, "ReadFile failed\n"); + ok(num_bytes == strlen(long_test_string)-4, "ReadFile returned only %d bytes\n", num_bytes); + ok(!memcmp(long_test_string, read_buf, strlen(long_test_string)), "ReadFile read wrong bytes\n"); + + /* Check content of overlapped structure */ + memset(read_buf, 0, sizeof(read_buf)); + S(U(overlapped)).Offset = 0; + S(U(overlapped)).OffsetHigh = 0; + overlapped.Internal = -1; + overlapped.InternalHigh = -1; + overlapped.hEvent = event; + num_bytes = 0xdeadbeef; + SetLastError(0xdeadbeef); + ret = ReadFile(server, read_buf, 4, &num_bytes, &overlapped); + ok(GetLastError() == ERROR_IO_PENDING, "expected ERROR_IO_PENDING, got %d\n", GetLastError()); + ok(num_bytes == 0, "bytes %u\n", num_bytes); + ok((NTSTATUS)overlapped.Internal == STATUS_PENDING, "expected STATUS_PENDING, got %#lx\n", overlapped.Internal); + todo_wine + ok(overlapped.InternalHigh == -1, "expected -1, got %lu\n", overlapped.InternalHigh); + wait = WaitForSingleObject(event, 100); + ok(wait == WAIT_TIMEOUT, "WaitForSingleObject returned %x\n", wait); + ok((NTSTATUS)overlapped.Internal == STATUS_PENDING, "expected STATUS_PENDING, got %#lx\n", overlapped.Internal); + + num_bytes = 0xdeadbeef; + ret = WriteFile(client, long_test_string, strlen(long_test_string), &num_bytes, NULL); + ok(ret, "WriteFile failed\n"); + ok(num_bytes == strlen(long_test_string), "bytes %u\n", num_bytes); + wait = WaitForSingleObject(event, 100); + ok(wait == WAIT_OBJECT_0, "WaitForSingleObject returned %x\n", wait); + ok(num_bytes == strlen(long_test_string), "bytes %u\n", num_bytes); + ok((NTSTATUS)overlapped.Internal == STATUS_BUFFER_OVERFLOW, "expected STATUS_BUFFER_OVERFLOW, got %#lx\n", overlapped.Internal); + ok(overlapped.InternalHigh == 4, "expected 4, got %lu\n", overlapped.InternalHigh); + + ret = ReadFile(server, read_buf + 4, sizeof(read_buf) - 4, &num_bytes, NULL); + ok(ret == TRUE, "ReadFile failed\n"); + ok(num_bytes == strlen(long_test_string)-4, "ReadFile returned only %d bytes\n", num_bytes); + ok(!memcmp(long_test_string, read_buf, strlen(long_test_string)), "ReadFile read wrong bytes\n"); + + /* Call ReadFile when there is already some content in the pipe */ + num_bytes = 0xdeadbeef; + ret = WriteFile(client, long_test_string, strlen(long_test_string), &num_bytes, NULL); + ok(ret, "WriteFile failed\n"); + ok(num_bytes == strlen(long_test_string), "bytes %u\n", num_bytes); + + memset(read_buf, 0, sizeof(read_buf)); + S(U(overlapped)).Offset = 0; + S(U(overlapped)).OffsetHigh = 0; + overlapped.Internal = -1; + overlapped.InternalHigh = -1; + overlapped.hEvent = event; + num_bytes = 0xdeadbeef; + SetLastError(0xdeadbeef); + ret = ReadFile(server, read_buf, 4, &num_bytes, &overlapped); + ok(ret == FALSE, "ReadFile succeeded\n"); + ok(GetLastError() == ERROR_MORE_DATA, "expected ERROR_MORE_DATA, got %d\n", GetLastError()); + todo_wine + ok(num_bytes == 0, "ReadFile returned %d bytes\n", num_bytes); + wait = WaitForSingleObjectEx(event, 0, TRUE); + ok(wait == WAIT_OBJECT_0, "WaitForSingleObjectEx returned %x\n", wait); + todo_wine + ok(num_bytes == 0, "bytes %u\n", num_bytes); + ok((NTSTATUS)overlapped.Internal == STATUS_BUFFER_OVERFLOW, "expected STATUS_BUFFER_OVERFLOW, got %#lx\n", overlapped.Internal); + ok(overlapped.InternalHigh == 4, "expected 4, got %lu\n", overlapped.InternalHigh); + + ret = ReadFile(server, read_buf + 4, sizeof(read_buf) - 4, &num_bytes, NULL); + ok(ret == TRUE, "ReadFile failed\n"); + ok(num_bytes == strlen(long_test_string)-4, "ReadFile returned only %d bytes\n", num_bytes); + ok(!memcmp(long_test_string, read_buf, strlen(long_test_string)), "ReadFile read wrong bytes\n"); + + /* Tests for PIPE_NOWAIT in message mode */ + lpmode = PIPE_READMODE_MESSAGE | PIPE_NOWAIT; + ok(SetNamedPipeHandleState(server, &lpmode, NULL, NULL), "Change mode\n"); + + /* Initial check with empty pipe */ + memset(read_buf, 0, sizeof(read_buf)); + completion_called = 0; + ResetEvent(event); + ret = ReadFileEx(server, read_buf, 4, &overlapped, completion_routine); + ok(ret == FALSE, "ReadFileEx succeded\n"); + ok(completion_called == 0, "completion routine called before ReadFileEx returned\n"); + ok(GetLastError() == ERROR_NO_DATA, "expected ERROR_NO_DATA, got %d\n", GetLastError()); + wait = WaitForSingleObjectEx(event, 0, TRUE); + todo_wine + ok(wait == WAIT_TIMEOUT, "WaitForSingleObjectEx returned %x\n", wait); + ok(completion_called == 0, "completion routine called before writing to file\n"); + + /* Call ReadFileEx after writing content to the pipe */ + num_bytes = 0xdeadbeef; + ret = WriteFile(client, long_test_string, strlen(long_test_string), &num_bytes, NULL); + ok(ret, "WriteFile failed, err=%i\n", GetLastError()); + ok(num_bytes == strlen(long_test_string), "bytes %u\n", num_bytes); + + memset(read_buf, 0, sizeof(read_buf)); + completion_called = 0; + ResetEvent(event); + ret = ReadFileEx(server, read_buf, 4, &overlapped, completion_routine); + ok(ret == TRUE, "ReadFileEx failed, err=%i\n", GetLastError()); + ok(completion_called == 0, "completion routine called before ReadFileEx returned\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 == 4, "ReadFileEx returned only %d bytes\n", completion_num_bytes); + ok(completion_lpoverlapped == &overlapped, "completion called with wrong overlapped pointer\n"); + + ret = ReadFile(server, read_buf + 4, sizeof(read_buf) - 4, &num_bytes, NULL); + ok(ret == TRUE, "ReadFile succeeded\n"); + ok(num_bytes == strlen(long_test_string)-4, "ReadFile returned only %d bytes\n", num_bytes); + ok(!memcmp(long_test_string, read_buf, strlen(long_test_string)), "ReadFile read wrong bytes\n"); + + /* Same again, but read as a single part */ + num_bytes = 0xdeadbeef; + ret = WriteFile(client, long_test_string, strlen(long_test_string), &num_bytes, NULL); + ok(ret, "WriteFile failed, err=%i\n", GetLastError()); + ok(num_bytes == strlen(long_test_string), "bytes %u\n", num_bytes); + + memset(read_buf, 0, sizeof(read_buf)); + 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"); + 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(long_test_string), "ReadFileEx returned only %d bytes\n", completion_num_bytes); + ok(completion_lpoverlapped == &overlapped, "completion called with wrong overlapped pointer\n"); + ok(!memcmp(long_test_string, read_buf, strlen(long_test_string)), "ReadFile read wrong bytes\n"); + + /* Check content of overlapped structure */ + memset(read_buf, 0, sizeof(read_buf)); + S(U(overlapped)).Offset = 0; + S(U(overlapped)).OffsetHigh = 0; + overlapped.Internal = -1; + overlapped.InternalHigh = -1; + overlapped.hEvent = event; + num_bytes = 0xdeadbeef; + SetLastError(0xdeadbeef); + ret = ReadFile(server, read_buf, 4, &num_bytes, &overlapped); + ok(ret == FALSE, "ReadFile succeeded\n"); + ok(GetLastError() == ERROR_NO_DATA, "expected ERROR_NO_DATA, got %d\n", GetLastError()); + ok(num_bytes == 0, "bytes %u\n", num_bytes); + ok((NTSTATUS)overlapped.Internal == STATUS_PENDING, "expected STATUS_PENDING, got %#lx\n", overlapped.Internal); + todo_wine + ok(overlapped.InternalHigh == -1, "expected -1, got %lu\n", overlapped.InternalHigh); + wait = WaitForSingleObject(event, 100); + ok(wait == WAIT_TIMEOUT, "WaitForSingleObjectEx returned %x\n", wait); + ok((NTSTATUS)overlapped.Internal == STATUS_PENDING, "expected STATUS_PENDING, got %#lx\n", overlapped.Internal); + todo_wine + ok(overlapped.InternalHigh == -1, "expected -1, got %lu\n", overlapped.InternalHigh); + + /* Call ReadFile after writing to the pipe */ + num_bytes = 0xdeadbeef; + ret = WriteFile(client, long_test_string, strlen(long_test_string), &num_bytes, NULL); + ok(ret, "WriteFile failed, err=%i\n", GetLastError()); + ok(num_bytes == strlen(long_test_string), "bytes %u\n", num_bytes); + + memset(read_buf, 0, sizeof(read_buf)); + S(U(overlapped)).Offset = 0; + S(U(overlapped)).OffsetHigh = 0; + overlapped.Internal = -1; + overlapped.InternalHigh = -1; + overlapped.hEvent = event; + num_bytes = 0xdeadbeef; + SetLastError(0xdeadbeef); + ret = ReadFile(server, read_buf, 4, &num_bytes, &overlapped); + ok(ret == FALSE, "ReadFile succeeded\n"); + ok(GetLastError() == ERROR_MORE_DATA, "expected ERROR_MORE_DATA, got %d\n", GetLastError()); + todo_wine + ok(num_bytes == 0, "bytes %u\n", num_bytes); + ok((NTSTATUS)overlapped.Internal == STATUS_BUFFER_OVERFLOW, "expected STATUS_BUFFER_OVERFLOW, got %#lx\n", overlapped.Internal); + ok(overlapped.InternalHigh == 4, "expected 4, got %lu\n", overlapped.InternalHigh); + wait = WaitForSingleObject(event, 100); + ok(wait == WAIT_IO_COMPLETION || wait == WAIT_OBJECT_0, "WaitForSingleObject returned %x\n", wait); + ok((NTSTATUS)overlapped.Internal == STATUS_BUFFER_OVERFLOW, "expected STATUS_BUFFER_OVERFLOW, got %#lx\n", overlapped.Internal); + ok(overlapped.InternalHigh == 4, "expected 4, got %lu\n", overlapped.InternalHigh); + + ret = ReadFile(server, read_buf + 4, sizeof(read_buf) - 4, &num_bytes, NULL); + ok(ret == TRUE, "ReadFile failed\n"); + ok(num_bytes == strlen(long_test_string)-4, "ReadFile returned only %d bytes\n", num_bytes); + ok(!memcmp(long_test_string, read_buf, strlen(long_test_string)), "ReadFile read wrong bytes\n"); + + /* Same again, but read as a single part */ + num_bytes = 0xdeadbeef; + ret = WriteFile(client, long_test_string, strlen(long_test_string), &num_bytes, NULL); + ok(ret, "WriteFile failed, err=%i\n", GetLastError()); + ok(num_bytes == strlen(long_test_string), "bytes %u\n", num_bytes); + + memset(read_buf, 0, sizeof(read_buf)); + S(U(overlapped)).Offset = 0; + S(U(overlapped)).OffsetHigh = 0; + overlapped.Internal = -1; + overlapped.InternalHigh = -1; + overlapped.hEvent = event; + num_bytes = 0xdeadbeef; + SetLastError(0xdeadbeef); + ret = ReadFile(server, read_buf, sizeof(read_buf), &num_bytes, &overlapped); + ok(ret == TRUE, "ReadFile failed, err=%i\n", GetLastError()); + ok(num_bytes == strlen(long_test_string), "bytes %u\n", num_bytes); + ok((NTSTATUS)overlapped.Internal == 0, "expected 0, got %#lx\n", overlapped.Internal); + ok(overlapped.InternalHigh == strlen(long_test_string), "expected %u, got %lu\n", (DWORD)strlen(long_test_string), overlapped.InternalHigh); + wait = WaitForSingleObject(event, 100); + ok(wait == WAIT_IO_COMPLETION || wait == WAIT_OBJECT_0, "WaitForSingleObject returned %x\n", wait); + ok((NTSTATUS)overlapped.Internal == 0, "expected 0, got %#lx\n", overlapped.Internal); + ok(overlapped.InternalHigh == strlen(long_test_string), "expected %u, got %lu\n", (DWORD)strlen(long_test_string), overlapped.InternalHigh); + ok(!memcmp(long_test_string, read_buf, strlen(long_test_string)), "ReadFile read wrong bytes\n"); + CloseHandle(client); CloseHandle(server); CloseHandle(event); @@ -2163,9 +3367,12 @@ START_TEST(pipe) test_NamedPipe_2(); test_CreateNamedPipe(PIPE_TYPE_BYTE); test_CreateNamedPipe(PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE); + test_CloseNamedPipe(); test_CreatePipe(); test_impersonation(); test_overlapped(); + test_nowait(PIPE_TYPE_BYTE); + test_nowait(PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE); test_NamedPipeHandleState(); test_readfileex_pending(); } diff --git a/rostests/winetests/kernel32/process.c b/rostests/winetests/kernel32/process.c index aa6658da6c3..ff33b965f9f 100755 --- a/rostests/winetests/kernel32/process.c +++ b/rostests/winetests/kernel32/process.c @@ -57,7 +57,7 @@ wine_dbgstr_w(expected), wine_dbgstr_w(value)); \ } while (0) -static HINSTANCE hkernel32; +static HINSTANCE hkernel32, hntdll; static void (WINAPI *pGetNativeSystemInfo)(LPSYSTEM_INFO); static BOOL (WINAPI *pGetSystemRegistryQuota)(PDWORD, PDWORD); static BOOL (WINAPI *pIsWow64Process)(HANDLE,PBOOL); @@ -66,6 +66,7 @@ static BOOL (WINAPI *pVirtualFreeEx)(HANDLE, LPVOID, SIZE_T, DWORD); static BOOL (WINAPI *pQueryFullProcessImageNameA)(HANDLE hProcess, DWORD dwFlags, LPSTR lpExeName, PDWORD lpdwSize); static BOOL (WINAPI *pQueryFullProcessImageNameW)(HANDLE hProcess, DWORD dwFlags, LPWSTR lpExeName, PDWORD lpdwSize); static DWORD (WINAPI *pK32GetProcessImageFileNameA)(HANDLE,LPSTR,DWORD); +static struct _TEB * (WINAPI *pNtCurrentTeb)(void); /* ############################### */ static char base[MAX_PATH]; @@ -201,6 +202,8 @@ static BOOL init(void) if ((p = strrchr(exename, '/')) != NULL) exename = p + 1; hkernel32 = GetModuleHandleA("kernel32"); + hntdll = GetModuleHandleA("ntdll.dll"); + pGetNativeSystemInfo = (void *) GetProcAddress(hkernel32, "GetNativeSystemInfo"); pGetSystemRegistryQuota = (void *) GetProcAddress(hkernel32, "GetSystemRegistryQuota"); pIsWow64Process = (void *) GetProcAddress(hkernel32, "IsWow64Process"); @@ -209,6 +212,7 @@ static BOOL init(void) pQueryFullProcessImageNameA = (void *) GetProcAddress(hkernel32, "QueryFullProcessImageNameA"); pQueryFullProcessImageNameW = (void *) GetProcAddress(hkernel32, "QueryFullProcessImageNameW"); pK32GetProcessImageFileNameA = (void *) GetProcAddress(hkernel32, "K32GetProcessImageFileNameA"); + pNtCurrentTeb = (void *)GetProcAddress( hntdll, "NtCurrentTeb" ); return TRUE; } @@ -277,6 +281,16 @@ static void doChild(const char* file, const char* option) siA.dwFlags, siA.wShowWindow, (DWORD_PTR)siA.hStdInput, (DWORD_PTR)siA.hStdOutput, (DWORD_PTR)siA.hStdError); + if (pNtCurrentTeb) + { + RTL_USER_PROCESS_PARAMETERS *params = pNtCurrentTeb()->Peb->ProcessParameters; + + /* check the console handles in the TEB */ + childPrintf(hFile, "[TEB]\nhStdInput=%lu\nhStdOutput=%lu\nhStdError=%lu\n\n", + (DWORD_PTR)params->hStdInput, (DWORD_PTR)params->hStdOutput, + (DWORD_PTR)params->hStdError); + } + /* since GetStartupInfoW is only implemented in win2k, * zero out before calling so we can notice the difference */ @@ -1863,6 +1877,62 @@ static void test_Handles(void) SetStdHandle( STD_ERROR_HANDLE, handle ); } +static void test_IsWow64Process(void) +{ + PROCESS_INFORMATION pi; + STARTUPINFOA si; + DWORD ret; + BOOL is_wow64; + static char cmdline[] = "C:\\Program Files\\Internet Explorer\\iexplore.exe"; + static char cmdline_wow64[] = "C:\\Program Files (x86)\\Internet Explorer\\iexplore.exe"; + + if (!pIsWow64Process) + { + skip("IsWow64Process is not available\n"); + return; + } + + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + si.dwFlags = STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; + ret = CreateProcessA(NULL, cmdline_wow64, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); + if (ret) + { + trace("Created process %s\n", cmdline_wow64); + is_wow64 = FALSE; + ret = pIsWow64Process(pi.hProcess, &is_wow64); + ok(ret, "IsWow64Process failed.\n"); + ok(is_wow64, "is_wow64 returned FALSE.\n"); + + ret = TerminateProcess(pi.hProcess, 0); + ok(ret, "TerminateProcess error\n"); + + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + } + + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + si.dwFlags = STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; + ret = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); + if (ret) + { + trace("Created process %s\n", cmdline); + is_wow64 = TRUE; + ret = pIsWow64Process(pi.hProcess, &is_wow64); + ok(ret, "IsWow64Process failed.\n"); + ok(!is_wow64, "is_wow64 returned TRUE.\n"); + + ret = TerminateProcess(pi.hProcess, 0); + ok(ret, "TerminateProcess error\n"); + + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + } +} + static void test_SystemInfo(void) { SYSTEM_INFO si, nsi; @@ -2063,6 +2133,41 @@ static void test_DuplicateHandle(void) CloseHandle(out); } +void test_StartupNoConsole(void) +{ + char buffer[MAX_PATH]; + PROCESS_INFORMATION info; + STARTUPINFOA startup; + DWORD code; + + if (!pNtCurrentTeb) + { + win_skip( "NtCurrentTeb not supported\n" ); + return; + } + + memset(&startup, 0, sizeof(startup)); + startup.cb = sizeof(startup); + startup.dwFlags = STARTF_USESHOWWINDOW; + startup.wShowWindow = SW_SHOWNORMAL; + get_file_name(resfile); + sprintf(buffer, "\"%s\" tests/process.c \"%s\"", selfname, resfile); + ok(CreateProcessA(NULL, buffer, NULL, NULL, TRUE, DETACHED_PROCESS, NULL, NULL, &startup, + &info), "CreateProcess\n"); + ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n"); + ok(GetExitCodeProcess(info.hProcess, &code), "Getting exit code\n"); + WritePrivateProfileStringA(NULL, NULL, NULL, resfile); + okChildInt("StartupInfoA", "hStdInput", (DWORD_PTR)INVALID_HANDLE_VALUE); + okChildInt("StartupInfoA", "hStdOutput", (DWORD_PTR)INVALID_HANDLE_VALUE); + okChildInt("StartupInfoA", "hStdError", (DWORD_PTR)INVALID_HANDLE_VALUE); + okChildInt("TEB", "hStdInput", (DWORD_PTR)0); + okChildInt("TEB", "hStdOutput", (DWORD_PTR)0); + okChildInt("TEB", "hStdError", (DWORD_PTR)0); + release_memory(); + assert(DeleteFileA(resfile) != 0); + +} + START_TEST(process) { BOOL b = init(); @@ -2089,9 +2194,11 @@ START_TEST(process) test_QueryFullProcessImageNameA(); test_QueryFullProcessImageNameW(); test_Handles(); + test_IsWow64Process(); test_SystemInfo(); test_RegistryQuota(); test_DuplicateHandle(); + test_StartupNoConsole(); /* things that can be tested: * lookup: check the way program to be executed is searched * handles: check the handle inheritance stuff (+sec options) diff --git a/rostests/winetests/kernel32/profile.c b/rostests/winetests/kernel32/profile.c index 2eb90a83ad8..990aa1441d9 100755 --- a/rostests/winetests/kernel32/profile.c +++ b/rostests/winetests/kernel32/profile.c @@ -154,9 +154,7 @@ static void test_profile_string(void) /* works only in unicode, ascii crashes */ ret=GetPrivateProfileStringW(sW, emptyW, emptyW, bufW, sizeof(bufW)/sizeof(bufW[0]), TESTFILE2W); - todo_wine ok(ret == 10, "expected 10, got %u\n", ret); - todo_wine ok(!lstrcmpW(valnokeyW,bufW), "expected %s, got %s\n", wine_dbgstr_w(valnokeyW), wine_dbgstr_w(bufW) ); diff --git a/rostests/winetests/kernel32/resource.c b/rostests/winetests/kernel32/resource.c index 43551fa560c..74066cfba3c 100644 --- a/rostests/winetests/kernel32/resource.c +++ b/rostests/winetests/kernel32/resource.c @@ -39,15 +39,8 @@ static const IMAGE_SECTION_HEADER sh_rodata_1 = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ }; -/* rodata @ [0x1000-0x4000) */ -static const IMAGE_SECTION_HEADER sh_rodata_2 = -{ - ".rodata", {3*page_size}, page_size, 3*page_size, page_size, 0, 0, 0, 0, - IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ -}; - /* rodata @ [0x1000-0x2000) */ -static const IMAGE_SECTION_HEADER sh_rodata_3 = +static const IMAGE_SECTION_HEADER sh_rodata_2 = { ".rodata", {page_size}, page_size, page_size, page_size, 0, 0, 0, 0, IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ @@ -60,50 +53,36 @@ static const IMAGE_SECTION_HEADER sh_rsrc_1 = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ }; -/* rsrc @ [0x4000-0x5000) */ -static const IMAGE_SECTION_HEADER sh_rsrc_2 = -{ - ".rsrc\0\0", {page_size}, 4*page_size, page_size, 4*page_size, 0, 0, 0, 0, - IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ -}; - /* rsrc @ [0x2000-0x4000) */ -static const IMAGE_SECTION_HEADER sh_rsrc_3 = +static const IMAGE_SECTION_HEADER sh_rsrc_2 = { ".rsrc\0\0", {2*page_size}, rva_rsrc_start-page_size, 2*page_size, rva_rsrc_start-page_size, 0, 0, 0, 0, IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ }; /* rsrc @ [0x2000-0x3000) */ -static const IMAGE_SECTION_HEADER sh_rsrc_4 = +static const IMAGE_SECTION_HEADER sh_rsrc_3 = { ".rsrc\0\0", {page_size}, rva_rsrc_start-page_size, page_size, rva_rsrc_start-page_size, 0, 0, 0, 0, IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ }; /* rsrc @ [0x3000-0x6000) */ -static const IMAGE_SECTION_HEADER sh_rsrc_5 = +static const IMAGE_SECTION_HEADER sh_rsrc_4 = { ".rsrc\0\0", {3*page_size}, rva_rsrc_start, 3*page_size, rva_rsrc_start, 0, 0, 0, 0, IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ }; -/* rsrc @ [0x4000-0x7000) */ -static const IMAGE_SECTION_HEADER sh_rsrc_6 = -{ - ".rsrc\0\0", {3*page_size}, 4*page_size, 3*page_size, 4*page_size, 0, 0, 0, 0, - IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ -}; - /* rsrc @ [0x2000-0x5000) */ -static const IMAGE_SECTION_HEADER sh_rsrc_7 = +static const IMAGE_SECTION_HEADER sh_rsrc_5 = { ".rsrc\0\0", {3*page_size}, 2*page_size, 3*page_size, 2*page_size, 0, 0, 0, 0, IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ }; /* rsrc @ [0x3000-0x4000), small SizeOfRawData */ -static const IMAGE_SECTION_HEADER sh_rsrc_8 = +static const IMAGE_SECTION_HEADER sh_rsrc_6 = { ".rsrc\0\0", {page_size}, rva_rsrc_start, 8, rva_rsrc_start, 0, 0, 0, 0, IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ @@ -148,29 +127,17 @@ static const struct _sec_variants {{&sh_rodata_1, &sh_rsrc_1, NULL}, 4*page_size, 1, 0, 0}, {{&sh_rodata_1, &sh_rsrc_1, NULL}, 4*page_size, 1, 0, 0}, {{&sh_rodata_1, &sh_rsrc_1, NULL}, 4*page_size, 1, 0, 1}, - {{&sh_rodata_1, &sh_rsrc_5, NULL}, 6*page_size, 1, 0, 1} + {{&sh_rodata_1, &sh_rsrc_4, NULL}, 6*page_size, 1, 0, 1} }, - /* single .rodata section with compatible characteristics, data directory entry points to section end */ - /* Vista+ - existing section isn't used, new section is created at the end of file */ - /* NT4/2000/2003 - image is broken */ -#if 0 - { - {{&sh_rodata_2, NULL, NULL}}, - {{&sh_rodata_2, &sh_rsrc_2, NULL}, 5*page_size, 1, 0, 0}, - {{&sh_rodata_2, &sh_rsrc_2, NULL}, 5*page_size, 1, 0, 0}, - {{&sh_rodata_2, &sh_rsrc_2, NULL}, 5*page_size, 1, 0, 1}, - {{&sh_rodata_2, &sh_rsrc_6, NULL}, 7*page_size, 1, 0, 1} - }, -#endif /* .rsrc is the last section, data directory entry points to section end */ /* Vista+ - resources are moved to section start (trashing data that could be there), and section is trimmed */ /* NT4/2000/2003 - resources are moved to section start (trashing data that could be there); section isn't trimmed */ { - {{&sh_rodata_3, &sh_rsrc_3, NULL}}, - {{&sh_rodata_3, &sh_rsrc_4, NULL}, 3*page_size, 1, 0, 0}, - {{&sh_rodata_3, &sh_rsrc_4, NULL}, 3*page_size, 1, 0, 0}, - {{&sh_rodata_3, &sh_rsrc_4, NULL}, 3*page_size, 1, 0, 1}, - {{&sh_rodata_3, &sh_rsrc_7, NULL}, 5*page_size, 1, 0, 1} + {{&sh_rodata_2, &sh_rsrc_2, NULL}}, + {{&sh_rodata_2, &sh_rsrc_3, NULL}, 3*page_size, 1, 0, 0}, + {{&sh_rodata_2, &sh_rsrc_3, NULL}, 3*page_size, 1, 0, 0}, + {{&sh_rodata_2, &sh_rsrc_3, NULL}, 3*page_size, 1, 0, 1}, + {{&sh_rodata_2, &sh_rsrc_5, NULL}, 5*page_size, 1, 0, 1} }, /* .rsrc is not the last section */ /* section is reused; sections after .rsrc are shifted to give space to rsrc (in-image offset and RVA!) */ @@ -179,15 +146,15 @@ static const struct _sec_variants {{&sh_rodata_1, &sh_rsrc_1, &sh_junk}, 5*page_size, 1, 0, 0}, {{&sh_rodata_1, &sh_rsrc_1, &sh_junk}, 5*page_size, 1, 0, 0}, {{&sh_rodata_1, &sh_rsrc_1, &sh_junk}, 5*page_size, 1, 0, 1}, - {{&sh_rodata_1, &sh_rsrc_5, &sh_junk_2}, 7*page_size, 1, 0, 1} + {{&sh_rodata_1, &sh_rsrc_4, &sh_junk_2}, 7*page_size, 1, 0, 1} }, /* .rsrc is the last section, data directory entry points to whole section, file size is not aligned on FileAlign */ { - {{&sh_rodata_1, &sh_rsrc_8, NULL}}, + {{&sh_rodata_1, &sh_rsrc_6, NULL}}, {{&sh_rodata_1, &sh_rsrc_1, NULL}, 4*page_size, 1, 0, 0}, {{&sh_rodata_1, &sh_rsrc_1, NULL}, 4*page_size, 1, 0, 0}, {{&sh_rodata_1, &sh_rsrc_1, NULL}, 4*page_size, 1, 0, 1}, - {{&sh_rodata_1, &sh_rsrc_5, NULL}, 6*page_size, 1, 0, 1} + {{&sh_rodata_1, &sh_rsrc_4, NULL}, 6*page_size, 1, 0, 1} } }; diff --git a/rostests/winetests/kernel32/sync.c b/rostests/winetests/kernel32/sync.c index 4ef5084447a..833cc37ada0 100755 --- a/rostests/winetests/kernel32/sync.c +++ b/rostests/winetests/kernel32/sync.c @@ -64,6 +64,7 @@ static void test_signalandwait(void) HMODULE kernel32; DWORD r; HANDLE event[2], semaphore[2], file; + int i; kernel32 = GetModuleHandleA("kernel32.dll"); pSignalObjectAndWait = (void*) GetProcAddress(kernel32, "SignalObjectAndWait"); @@ -96,9 +97,13 @@ static void test_signalandwait(void) r = pSignalObjectAndWait(event[0], event[1], 0, FALSE); ok( r == WAIT_OBJECT_0, "should succeed\n"); - /* event[0] is now signalled */ - r = pSignalObjectAndWait(event[0], event[0], 0, FALSE); - ok( r == WAIT_OBJECT_0, "should succeed\n"); + /* event[0] is now signalled - we repeat this test multiple times + * to ensure that the wineserver handles this situation properly. */ + for (i = 0; i < 10000; i++) + { + r = pSignalObjectAndWait(event[0], event[0], 0, FALSE); + ok( r == WAIT_OBJECT_0, "should succeed\n"); + } /* event[0] is not signalled */ r = WaitForSingleObject(event[0], 0); diff --git a/rostests/winetests/kernel32/testlist.c b/rostests/winetests/kernel32/testlist.c index c5518defe76..05f63de1521 100755 --- a/rostests/winetests/kernel32/testlist.c +++ b/rostests/winetests/kernel32/testlist.c @@ -12,6 +12,7 @@ extern void func_change(void); extern void func_codepage(void); extern void func_comm(void); extern void func_console(void); +extern void func_cpu(void); extern void func_debugger(void); extern void func_directory(void); extern void func_drive(void); diff --git a/rostests/winetests/kernel32/thread.c b/rostests/winetests/kernel32/thread.c index bbfa0d1ec7f..2866a444cd9 100755 --- a/rostests/winetests/kernel32/thread.c +++ b/rostests/winetests/kernel32/thread.c @@ -1625,8 +1625,8 @@ static void test_threadpool(void) int workcalled = 0; if (!pCreateThreadpool) { - todo_wine win_skip("thread pool apis not supported.\n"); - return; + win_skip("thread pool apis not supported.\n"); + return; } work = pCreateThreadpoolWork(threadpool_workcallback, &workcalled, NULL); @@ -1638,7 +1638,7 @@ static void test_threadpool(void) ok (workcalled == 1, "expected work to be called once, got %d\n", workcalled); pool = pCreateThreadpool(NULL); - todo_wine ok (pool != NULL, "CreateThreadpool failed\n"); + ok (pool != NULL, "CreateThreadpool failed\n"); } static void test_reserved_tls(void) diff --git a/rostests/winetests/kernel32/time.c b/rostests/winetests/kernel32/time.c index 55d4be9029f..63ab35ad856 100755 --- a/rostests/winetests/kernel32/time.c +++ b/rostests/winetests/kernel32/time.c @@ -22,9 +22,11 @@ #include "wine/test.h" #include "winbase.h" #include "winnls.h" +#include "winternl.h" static BOOL (WINAPI *pTzSpecificLocalTimeToSystemTime)(LPTIME_ZONE_INFORMATION, LPSYSTEMTIME, LPSYSTEMTIME); static BOOL (WINAPI *pSystemTimeToTzSpecificLocalTime)(LPTIME_ZONE_INFORMATION, LPSYSTEMTIME, LPSYSTEMTIME); +static BOOL (WINAPI *pGetSystemTimes)(LPFILETIME, LPFILETIME, LPFILETIME); static int (WINAPI *pGetCalendarInfoA)(LCID,CALID,CALTYPE,LPSTR,int,LPDWORD); static int (WINAPI *pGetCalendarInfoW)(LCID,CALID,CALTYPE,LPWSTR,int,LPDWORD); @@ -732,12 +734,85 @@ static void test_GetCalendarInfo(void) ok( ret == ret2, "got %d, expected %d\n", ret, ret2 ); } +static void test_GetSystemTimes(void) +{ + + FILETIME idletime, kerneltime, usertime; + int i; + ULARGE_INTEGER ul1, ul2, ul3; + SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *sppi; + SYSTEM_BASIC_INFORMATION sbi; + ULONG ReturnLength; + double total_usertime = 0.0, total_kerneltime = 0.0, total_idletime = 0.0; + + if (!pGetSystemTimes) + { + win_skip("GetSystemTimes not available\n"); + return; + } + + ok( pGetSystemTimes(NULL, NULL, NULL), "GetSystemTimes failed unexpectedly\n" ); + + memset( &idletime, 0x11, sizeof(idletime) ); + memset( &kerneltime, 0x11, sizeof(kerneltime) ); + memset( &usertime, 0x11, sizeof(usertime) ); + ok( pGetSystemTimes(&idletime, &kerneltime , &usertime), + "GetSystemTimes failed unexpectedly\n" ); + + ul1.LowPart = idletime.dwLowDateTime; + ul1.HighPart = idletime.dwHighDateTime; + + trace( "IdleTime: %f seconds\n", (double)ul1.QuadPart/10000000.0 ); + + ul2.LowPart = kerneltime.dwLowDateTime; + ul2.HighPart = kerneltime.dwHighDateTime; + + trace( "KernelTime: %f seconds\n", (double)ul2.QuadPart/10000000.0 ); + + ul3.LowPart = usertime.dwLowDateTime; + ul3.HighPart = usertime.dwHighDateTime; + + trace( "UserTime: %f seconds\n", (double)ul3.QuadPart/10000000.0 ); + + ok( !NtQuerySystemInformation(SystemBasicInformation, &sbi, sizeof(sbi), &ReturnLength), + "NtQuerySystemInformation failed\n" ); + ok( sizeof(sbi) == ReturnLength, "Inconsistent length %d\n", ReturnLength ); + + /* Check if we have some return values */ + trace( "Number of Processors : %d\n", sbi.NumberOfProcessors ); + ok( sbi.NumberOfProcessors > 0, "Expected more than 0 processors, got %d\n", + sbi.NumberOfProcessors ); + + sppi = HeapAlloc( GetProcessHeap(), 0, + sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION) * sbi.NumberOfProcessors); + + ok( !NtQuerySystemInformation( SystemProcessorPerformanceInformation, sppi, + sizeof(*sppi), &ReturnLength), + "NtQuerySystemInformation failed\n" ); + + for (i = 0; i < sbi.NumberOfProcessors; i++) + { + total_usertime += (double)(sppi[i].UserTime.QuadPart)/10000000.0; + total_kerneltime += (double)(sppi[i].KernelTime.QuadPart)/10000000.0; + total_idletime += (double)(sppi[i].IdleTime.QuadPart)/10000000.0; + } + + trace( "total_idletime %f total_kerneltime %f total_usertime %f \n", total_idletime, + total_kerneltime, total_usertime ); + + ok( (total_idletime - (double)ul1.QuadPart/10000000.0) < 1.0, "test idletime failed\n" ); + ok( (total_kerneltime - (double)ul2.QuadPart/10000000.0) < 1.0, "test kerneltime failed\n" ); + ok( (total_usertime - (double)ul3.QuadPart/10000000.0) < 1.0, "test usertime failed\n" ); + + HeapFree(GetProcessHeap(), 0, sppi); +} START_TEST(time) { HMODULE hKernel = GetModuleHandleA("kernel32"); pTzSpecificLocalTimeToSystemTime = (void *)GetProcAddress(hKernel, "TzSpecificLocalTimeToSystemTime"); pSystemTimeToTzSpecificLocalTime = (void *)GetProcAddress( hKernel, "SystemTimeToTzSpecificLocalTime"); + pGetSystemTimes = (void *)GetProcAddress( hKernel, "GetSystemTimes"); pGetCalendarInfoA = (void *)GetProcAddress(hKernel, "GetCalendarInfoA"); pGetCalendarInfoW = (void *)GetProcAddress(hKernel, "GetCalendarInfoW"); @@ -747,6 +822,7 @@ START_TEST(time) test_FileTimeToSystemTime(); test_FileTimeToLocalFileTime(); test_TzSpecificLocalTimeToSystemTime(); + test_GetSystemTimes(); test_FileTimeToDosDateTime(); test_GetCalendarInfo(); } diff --git a/rostests/winetests/kernel32/version.c b/rostests/winetests/kernel32/version.c index e27ea4c7984..4d071764b0a 100644 --- a/rostests/winetests/kernel32/version.c +++ b/rostests/winetests/kernel32/version.c @@ -23,23 +23,29 @@ #include "wine/test.h" #include "winbase.h" +#include "winternl.h" static BOOL (WINAPI * pGetProductInfo)(DWORD, DWORD, DWORD, DWORD, DWORD *); static BOOL (WINAPI * pVerifyVersionInfoA)(LPOSVERSIONINFOEXA, DWORD, DWORDLONG); static ULONGLONG (WINAPI * pVerSetConditionMask)(ULONGLONG, DWORD, BYTE); +static NTSTATUS (WINAPI * pRtlGetVersion)(RTL_OSVERSIONINFOEXW *); -#define KERNEL32_GET_PROC(func) \ - p##func = (void *)GetProcAddress(hKernel32, #func); +#define GET_PROC(func) \ + p##func = (void *)GetProcAddress(hmod, #func); static void init_function_pointers(void) { - HMODULE hKernel32; + HMODULE hmod; - hKernel32 = GetModuleHandleA("kernel32.dll"); + hmod = GetModuleHandleA("kernel32.dll"); - KERNEL32_GET_PROC(GetProductInfo); - KERNEL32_GET_PROC(VerifyVersionInfoA); - KERNEL32_GET_PROC(VerSetConditionMask); + GET_PROC(GetProductInfo); + GET_PROC(VerifyVersionInfoA); + GET_PROC(VerSetConditionMask); + + hmod = GetModuleHandleA("ntdll.dll"); + + GET_PROC(RtlGetVersion); } static void test_GetProductInfo(void) @@ -170,6 +176,21 @@ static void test_VerifyVersionInfo(void) info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA); GetVersionExA((OSVERSIONINFOA *)&info); servicepack = info.wServicePackMajor; + + /* Win8.1+ returns Win8 version in GetVersionEx when there's no app manifest targeting 8.1 */ + if (info.dwMajorVersion == 6 && info.dwMinorVersion == 2) + { + RTL_OSVERSIONINFOEXW rtlinfo; + rtlinfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW); + ok(SUCCEEDED(pRtlGetVersion(&rtlinfo)), "RtlGetVersion failed\n"); + + if (rtlinfo.dwMajorVersion != 6 || rtlinfo.dwMinorVersion != 2) + { + win_skip("GetVersionEx and VerifyVersionInfo are faking values\n"); + return; + } + } + memset(&info, 0, sizeof(info)); ret = pVerifyVersionInfoA(&info, VER_MAJORVERSION | VER_MINORVERSION, @@ -332,6 +353,337 @@ static void test_VerifyVersionInfo(void) pVerSetConditionMask(0, VER_MINORVERSION, VER_GREATER_EQUAL)); ok(ret || broken(!ret) /* some win2k */, "VerifyVersionInfoA failed with error %d\n", GetLastError()); + /* systematically test behaviour of condition mask (tests sorted by condition mask value) */ + + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA); + GetVersionExA((OSVERSIONINFOA *)&info); + info.dwMinorVersion++; + ret = pVerifyVersionInfoA(&info, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, + pVerSetConditionMask(pVerSetConditionMask(0, VER_MAJORVERSION, VER_EQUAL), VER_MINORVERSION, VER_LESS)); + ok(ret, "VerifyVersionInfoA failed with error %d\n", GetLastError()); + + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA); + GetVersionExA((OSVERSIONINFOA *)&info); + info.dwMinorVersion++; + SetLastError(0xdeadbeef); + ret = pVerifyVersionInfoA(&info, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, + pVerSetConditionMask(pVerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL), VER_MINORVERSION, VER_LESS)); + error = GetLastError(); + ok(!ret, "VerifyVersionInfoA succeeded\n"); + ok(error == ERROR_OLD_WIN_VERSION || broken(error == ERROR_BAD_ARGUMENTS) /* some win2k */, + "VerifyVersionInfoA should have failed with ERROR_OLD_WIN_VERSION instead of %d\n", error); + + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA); + GetVersionExA((OSVERSIONINFOA *)&info); + ret = pVerifyVersionInfoA(&info, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, + pVerSetConditionMask(pVerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL), VER_MINORVERSION, VER_LESS)); + ok(ret, "VerifyVersionInfoA failed with error %d\n", GetLastError()); + + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA); + GetVersionExA((OSVERSIONINFOA *)&info); + ret = pVerifyVersionInfoA(&info, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, + pVerSetConditionMask(pVerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL), VER_MINORVERSION, VER_AND)); + ok(ret, "VerifyVersionInfoA failed with error %d\n", GetLastError()); + + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA); + GetVersionExA((OSVERSIONINFOA *)&info); + info.dwMinorVersion++; + ret = pVerifyVersionInfoA(&info, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, + pVerSetConditionMask(pVerSetConditionMask(0, VER_MAJORVERSION, VER_LESS_EQUAL), VER_MINORVERSION, VER_LESS)); + ok(ret, "VerifyVersionInfoA failed with error %d\n", GetLastError()); + + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA); + GetVersionExA((OSVERSIONINFOA *)&info); + info.dwMinorVersion++; + SetLastError(0xdeadbeef); + ret = pVerifyVersionInfoA(&info, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, + pVerSetConditionMask(pVerSetConditionMask(0, VER_MAJORVERSION, VER_AND), VER_MINORVERSION, VER_LESS)); + error = GetLastError(); + ok(!ret, "VerifyVersionInfoA succeeded\n"); + ok(error == ERROR_OLD_WIN_VERSION || broken(error == ERROR_BAD_ARGUMENTS) /* some win2k */, + "VerifyVersionInfoA should have failed with ERROR_OLD_WIN_VERSION instead of %d\n", error); + + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA); + GetVersionExA((OSVERSIONINFOA *)&info); + info.dwMinorVersion++; + SetLastError(0xdeadbeef); + ret = pVerifyVersionInfoA(&info, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, + pVerSetConditionMask(pVerSetConditionMask(0, VER_MAJORVERSION, VER_OR), VER_MINORVERSION, VER_LESS)); + error = GetLastError(); + ok(!ret, "VerifyVersionInfoA succeeded\n"); + ok(error == ERROR_OLD_WIN_VERSION || broken(error == ERROR_BAD_ARGUMENTS) /* some win2k */, + "VerifyVersionInfoA should have failed with ERROR_OLD_WIN_VERSION instead of %d\n", error); + + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA); + GetVersionExA((OSVERSIONINFOA *)&info); + info.wServicePackMinor++; + SetLastError(0xdeadbeef); + ret = pVerifyVersionInfoA(&info, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, + pVerSetConditionMask(pVerSetConditionMask(0, VER_MINORVERSION, VER_EQUAL), VER_SERVICEPACKMINOR, VER_LESS)); + error = GetLastError(); + ok(!ret, "VerifyVersionInfoA succeeded\n"); + ok(error == ERROR_OLD_WIN_VERSION || broken(error == ERROR_BAD_ARGUMENTS) /* some win2k */, + "VerifyVersionInfoA should have failed with ERROR_OLD_WIN_VERSION instead of %d\n", error); + + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA); + GetVersionExA((OSVERSIONINFOA *)&info); + info.wServicePackMinor++; + SetLastError(0xdeadbeef); + ret = pVerifyVersionInfoA(&info, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, + pVerSetConditionMask(pVerSetConditionMask(0, VER_MAJORVERSION, VER_EQUAL), VER_SERVICEPACKMINOR, VER_LESS)); + error = GetLastError(); + ok(!ret, "VerifyVersionInfoA succeeded\n"); + ok(error == ERROR_OLD_WIN_VERSION || broken(error == ERROR_BAD_ARGUMENTS) /* some win2k */, + "VerifyVersionInfoA should have failed with ERROR_OLD_WIN_VERSION instead of %d\n", error); + + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA); + GetVersionExA((OSVERSIONINFOA *)&info); + info.wServicePackMajor++; + SetLastError(0xdeadbeef); + ret = pVerifyVersionInfoA(&info, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, + pVerSetConditionMask(pVerSetConditionMask(0, VER_MAJORVERSION, VER_EQUAL), VER_SERVICEPACKMAJOR, VER_EQUAL)); + error = GetLastError(); + ok(!ret, "VerifyVersionInfoA succeeded\n"); + ok(error == ERROR_OLD_WIN_VERSION || broken(error == ERROR_BAD_ARGUMENTS) /* some win2k */, + "VerifyVersionInfoA should have failed with ERROR_OLD_WIN_VERSION instead of %d\n", error); + + if (servicepack) + { + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA); + GetVersionExA((OSVERSIONINFOA *)&info); + info.dwMajorVersion++; + info.wServicePackMajor--; + ret = pVerifyVersionInfoA(&info, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, + pVerSetConditionMask(pVerSetConditionMask(0, VER_MAJORVERSION, VER_LESS), VER_SERVICEPACKMAJOR, VER_EQUAL)); + ok(ret, "VerifyVersionInfoA failed with error %d\n", GetLastError()); + } + + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA); + GetVersionExA((OSVERSIONINFOA *)&info); + info.wServicePackMinor++; + ret = pVerifyVersionInfoA(&info, VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, + pVerSetConditionMask(pVerSetConditionMask(0, VER_SERVICEPACKMAJOR, VER_EQUAL), VER_SERVICEPACKMINOR, VER_LESS)); + ok(ret, "VerifyVersionInfoA failed with error %d\n", GetLastError()); + + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA); + GetVersionExA((OSVERSIONINFOA *)&info); + info.wServicePackMinor++; + SetLastError(0xdeadbeef); + ret = pVerifyVersionInfoA(&info, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, + pVerSetConditionMask(pVerSetConditionMask(0, VER_SERVICEPACKMAJOR, VER_EQUAL), VER_SERVICEPACKMINOR, VER_LESS)); + error = GetLastError(); + ok(!ret, "VerifyVersionInfoA succeeded\n"); + ok(error == ERROR_OLD_WIN_VERSION || broken(error == ERROR_BAD_ARGUMENTS) /* some win2k */, + "VerifyVersionInfoA should have failed with ERROR_OLD_WIN_VERSION instead of %d\n", error); + + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA); + GetVersionExA((OSVERSIONINFOA *)&info); + info.wServicePackMinor++; + ret = pVerifyVersionInfoA(&info, VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, + pVerSetConditionMask(pVerSetConditionMask(pVerSetConditionMask(0, VER_MINORVERSION, VER_EQUAL), + VER_SERVICEPACKMAJOR, VER_EQUAL), VER_SERVICEPACKMINOR, VER_LESS)); + ok(ret, "VerifyVersionInfoA failed with error %d\n", GetLastError()); + + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA); + GetVersionExA((OSVERSIONINFOA *)&info); + info.wServicePackMinor++; + SetLastError(0xdeadbeef); + ret = pVerifyVersionInfoA(&info, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, + pVerSetConditionMask(pVerSetConditionMask(pVerSetConditionMask(0, VER_MINORVERSION, VER_EQUAL), + VER_SERVICEPACKMAJOR, VER_EQUAL), VER_SERVICEPACKMINOR, VER_LESS)); + error = GetLastError(); + ok(!ret, "VerifyVersionInfoA succeeded\n"); + ok(error == ERROR_OLD_WIN_VERSION || broken(error == ERROR_BAD_ARGUMENTS) /* some win2k */, + "VerifyVersionInfoA should have failed with ERROR_OLD_WIN_VERSION instead of %d\n", error); + + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA); + GetVersionExA((OSVERSIONINFOA *)&info); + info.wServicePackMinor++; + ret = pVerifyVersionInfoA(&info, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, + pVerSetConditionMask(pVerSetConditionMask(pVerSetConditionMask(pVerSetConditionMask(0, VER_MAJORVERSION, VER_EQUAL), + VER_MINORVERSION, VER_EQUAL), VER_SERVICEPACKMAJOR, VER_EQUAL), VER_SERVICEPACKMINOR, VER_LESS)); + ok(ret, "VerifyVersionInfoA failed with error %d\n", GetLastError()); + + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA); + GetVersionExA((OSVERSIONINFOA *)&info); + info.wServicePackMinor++; + SetLastError(0xdeadbeef); + ret = pVerifyVersionInfoA(&info, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, + pVerSetConditionMask(pVerSetConditionMask(pVerSetConditionMask(pVerSetConditionMask(0, VER_MAJORVERSION, VER_EQUAL), + VER_MINORVERSION, VER_GREATER_EQUAL), VER_SERVICEPACKMAJOR, VER_EQUAL), VER_SERVICEPACKMINOR, VER_LESS)); + error = GetLastError(); + ok(!ret, "VerifyVersionInfoA succeeded\n"); + ok(error == ERROR_OLD_WIN_VERSION || broken(error == ERROR_BAD_ARGUMENTS) /* some win2k */, + "VerifyVersionInfoA should have failed with ERROR_OLD_WIN_VERSION instead of %d\n", error); + + if (servicepack) + { + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA); + GetVersionExA((OSVERSIONINFOA *)&info); + info.wServicePackMajor--; + SetLastError(0xdeadbeef); + ret = pVerifyVersionInfoA(&info, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, + pVerSetConditionMask(pVerSetConditionMask(0, VER_MAJORVERSION, VER_EQUAL), VER_SERVICEPACKMAJOR, VER_GREATER)); + error = GetLastError(); + ok(!ret, "VerifyVersionInfoA succeeded\n"); + ok(error == ERROR_OLD_WIN_VERSION || broken(error == ERROR_BAD_ARGUMENTS) /* some win2k */, + "VerifyVersionInfoA should have failed with ERROR_OLD_WIN_VERSION instead of %d\n", error); + + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA); + GetVersionExA((OSVERSIONINFOA *)&info); + info.wServicePackMajor--; + ret = pVerifyVersionInfoA(&info, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, + pVerSetConditionMask(pVerSetConditionMask(pVerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL), + VER_MINORVERSION, VER_EQUAL), VER_SERVICEPACKMAJOR, VER_GREATER)); + ok(ret, "VerifyVersionInfoA failed with error %d\n", GetLastError()); + + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA); + GetVersionExA((OSVERSIONINFOA *)&info); + info.wServicePackMajor--; + ret = pVerifyVersionInfoA(&info, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, + pVerSetConditionMask(pVerSetConditionMask(pVerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL), + VER_MINORVERSION, VER_LESS_EQUAL), VER_SERVICEPACKMAJOR, VER_GREATER)); + ok(ret, "VerifyVersionInfoA failed with error %d\n", GetLastError()); + + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA); + GetVersionExA((OSVERSIONINFOA *)&info); + info.wServicePackMajor--; + ret = pVerifyVersionInfoA(&info, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, + pVerSetConditionMask(pVerSetConditionMask(pVerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL), + VER_MINORVERSION, VER_AND), VER_SERVICEPACKMAJOR, VER_GREATER)); + ok(ret, "VerifyVersionInfoA failed with error %d\n", GetLastError()); + } + + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA); + GetVersionExA((OSVERSIONINFOA *)&info); + info.wServicePackMajor++; + ret = pVerifyVersionInfoA(&info, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, + pVerSetConditionMask(pVerSetConditionMask(0, VER_MAJORVERSION, VER_LESS_EQUAL), VER_SERVICEPACKMAJOR, VER_GREATER)); + ok(ret, "VerifyVersionInfoA failed with error %d\n", GetLastError()); + + if (servicepack) + { + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA); + GetVersionExA((OSVERSIONINFOA *)&info); + info.wServicePackMajor--; + SetLastError(0xdeadbeef); + ret = pVerifyVersionInfoA(&info, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, + pVerSetConditionMask(pVerSetConditionMask(0, VER_SERVICEPACKMAJOR, VER_GREATER), VER_SERVICEPACKMINOR, VER_EQUAL)); + error = GetLastError(); + ok(!ret, "VerifyVersionInfoA succeeded\n"); + ok(error == ERROR_OLD_WIN_VERSION || broken(error == ERROR_BAD_ARGUMENTS) /* some win2k */, + "VerifyVersionInfoA should have failed with ERROR_OLD_WIN_VERSION instead of %d\n", error); + } + + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA); + GetVersionExA((OSVERSIONINFOA *)&info); + info.wServicePackMajor++; + ret = pVerifyVersionInfoA(&info, VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, + pVerSetConditionMask(pVerSetConditionMask(0, VER_MINORVERSION, VER_EQUAL), VER_SERVICEPACKMAJOR, VER_LESS)); + ok(ret, "VerifyVersionInfoA failed with error %d\n", GetLastError()); + + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA); + GetVersionExA((OSVERSIONINFOA *)&info); + info.wServicePackMajor++; + SetLastError(0xdeadbeef); + ret = pVerifyVersionInfoA(&info, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, + pVerSetConditionMask(pVerSetConditionMask(0, VER_MINORVERSION, VER_EQUAL), VER_SERVICEPACKMAJOR, VER_LESS)); + error = GetLastError(); + ok(!ret, "VerifyVersionInfoA succeeded\n"); + ok(error == ERROR_OLD_WIN_VERSION || broken(error == ERROR_BAD_ARGUMENTS) /* some win2k */, + "VerifyVersionInfoA should have failed with ERROR_OLD_WIN_VERSION instead of %d\n", error); + + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA); + GetVersionExA((OSVERSIONINFOA *)&info); + info.wServicePackMajor++; + ret = pVerifyVersionInfoA(&info, VER_MAJORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, + pVerSetConditionMask(pVerSetConditionMask(0, VER_MAJORVERSION, VER_EQUAL), VER_SERVICEPACKMAJOR, VER_LESS)); + ok(ret, "VerifyVersionInfoA failed with error %d\n", GetLastError()); + + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA); + GetVersionExA((OSVERSIONINFOA *)&info); + info.wServicePackMajor++; + SetLastError(0xdeadbeef); + ret = pVerifyVersionInfoA(&info, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, + pVerSetConditionMask(pVerSetConditionMask(0, VER_MAJORVERSION, VER_EQUAL), VER_SERVICEPACKMAJOR, VER_LESS)); + error = GetLastError(); + ok(!ret, "VerifyVersionInfoA succeeded\n"); + ok(error == ERROR_OLD_WIN_VERSION || broken(error == ERROR_BAD_ARGUMENTS) /* some win2k */, + "VerifyVersionInfoA should have failed with ERROR_OLD_WIN_VERSION instead of %d\n", error); + + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA); + GetVersionExA((OSVERSIONINFOA *)&info); + info.wServicePackMajor++; + ret = pVerifyVersionInfoA(&info, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, + pVerSetConditionMask(pVerSetConditionMask(pVerSetConditionMask(0, VER_MAJORVERSION, VER_EQUAL), + VER_MINORVERSION, VER_EQUAL), VER_SERVICEPACKMAJOR, VER_LESS)); + ok(ret, "VerifyVersionInfoA failed with error %d\n", GetLastError()); + + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA); + GetVersionExA((OSVERSIONINFOA *)&info); + info.wServicePackMajor++; + SetLastError(0xdeadbeef); + ret = pVerifyVersionInfoA(&info, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, + pVerSetConditionMask(pVerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL), VER_SERVICEPACKMAJOR, VER_LESS)); + error = GetLastError(); + ok(!ret, "VerifyVersionInfoA succeeded\n"); + ok(error == ERROR_OLD_WIN_VERSION || broken(error == ERROR_BAD_ARGUMENTS) /* some win2k */, + "VerifyVersionInfoA should have failed with ERROR_OLD_WIN_VERSION instead of %d\n", error); + + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA); + GetVersionExA((OSVERSIONINFOA *)&info); + info.dwMajorVersion--; + ret = pVerifyVersionInfoA(&info, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, + pVerSetConditionMask(pVerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL), VER_SERVICEPACKMAJOR, VER_LESS)); + ok(ret, "VerifyVersionInfoA failed with error %d\n", GetLastError()); + + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA); + GetVersionExA((OSVERSIONINFOA *)&info); + ret = pVerifyVersionInfoA(&info, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, + pVerSetConditionMask(pVerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL), VER_SERVICEPACKMAJOR, VER_LESS)); + ok(ret, "VerifyVersionInfoA failed with error %d\n", GetLastError()); + + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA); + GetVersionExA((OSVERSIONINFOA *)&info); + info.wServicePackMajor++; + SetLastError(0xdeadbeef); + ret = pVerifyVersionInfoA(&info, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, + pVerSetConditionMask(pVerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL), VER_SERVICEPACKMAJOR, VER_LESS)); + error = GetLastError(); + ok(!ret, "VerifyVersionInfoA succeeded\n"); + ok(error == ERROR_OLD_WIN_VERSION || broken(error == ERROR_BAD_ARGUMENTS) /* some win2k */, + "VerifyVersionInfoA should have failed with ERROR_OLD_WIN_VERSION instead of %d\n", error); + + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA); + GetVersionExA((OSVERSIONINFOA *)&info); + info.wServicePackMajor++; + SetLastError(0xdeadbeef); + ret = pVerifyVersionInfoA(&info, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, + pVerSetConditionMask(pVerSetConditionMask(pVerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL), + VER_MINORVERSION, VER_EQUAL), VER_SERVICEPACKMAJOR, VER_LESS)); + error = GetLastError(); + ok(!ret, "VerifyVersionInfoA succeeded\n"); + ok(error == ERROR_OLD_WIN_VERSION || broken(error == ERROR_BAD_ARGUMENTS) /* some win2k */, + "VerifyVersionInfoA should have failed with ERROR_OLD_WIN_VERSION instead of %d\n", error); + + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA); + GetVersionExA((OSVERSIONINFOA *)&info); + info.wServicePackMajor++; + SetLastError(0xdeadbeef); + ret = pVerifyVersionInfoA(&info, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, + pVerSetConditionMask(pVerSetConditionMask(pVerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL), + VER_MINORVERSION, VER_GREATER_EQUAL), VER_SERVICEPACKMAJOR, VER_LESS_EQUAL)); + error = GetLastError(); + ok(!ret, "VerifyVersionInfoA succeeded\n"); + ok(error == ERROR_OLD_WIN_VERSION || broken(error == ERROR_BAD_ARGUMENTS) /* some win2k */, + "VerifyVersionInfoA should have failed with ERROR_OLD_WIN_VERSION instead of %d\n", error); + + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA); + GetVersionExA((OSVERSIONINFOA *)&info); + ret = pVerifyVersionInfoA(&info, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, + pVerSetConditionMask(pVerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL), VER_SERVICEPACKMAJOR, VER_AND)); + ok(ret, "VerifyVersionInfoA failed with error %d\n", GetLastError()); + /* test bad dwOSVersionInfoSize */ info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA); GetVersionExA((OSVERSIONINFOA *)&info); diff --git a/rostests/winetests/kernel32/virtual.c b/rostests/winetests/kernel32/virtual.c index 2caaa6cb6bb..731b531ef6e 100755 --- a/rostests/winetests/kernel32/virtual.c +++ b/rostests/winetests/kernel32/virtual.c @@ -25,14 +25,17 @@ #define WIN32_NO_STATUS #include "windef.h" #include "winbase.h" +#include "winnt.h" #include "winternl.h" #include "winerror.h" +#include "winuser.h" +#include "wine/exception.h" #include "wine/test.h" #define NUM_THREADS 4 #define MAPPING_SIZE 0x100000 -static HINSTANCE hkernel32; +static HINSTANCE hkernel32, hntdll; static LPVOID (WINAPI *pVirtualAllocEx)(HANDLE, LPVOID, SIZE_T, DWORD, DWORD); static BOOL (WINAPI *pVirtualFreeEx)(HANDLE, LPVOID, SIZE_T, DWORD); static UINT (WINAPI *pGetWriteWatch)(DWORD,LPVOID,SIZE_T,LPVOID*,ULONG_PTR*,ULONG*); @@ -40,9 +43,31 @@ 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); +static struct _TEB * (WINAPI *pNtCurrentTeb)(void); +static PVOID (WINAPI *pRtlAddVectoredExceptionHandler)(ULONG, PVECTORED_EXCEPTION_HANDLER); +static ULONG (WINAPI *pRtlRemoveVectoredExceptionHandler)(PVOID); +static BOOL (WINAPI *pGetProcessDEPPolicy)(HANDLE, LPDWORD, PBOOL); +static NTSTATUS (WINAPI *pNtQuerySection)(HANDLE, int, PVOID, ULONG, PULONG); /* ############################### */ +static UINT_PTR page_mask = 0xfff; +#define ROUND_SIZE(addr,size) \ + (((SIZE_T)(size) + ((UINT_PTR)(addr) & page_mask) + page_mask) & ~page_mask) + +static PIMAGE_NT_HEADERS image_nt_header(HMODULE module) +{ + IMAGE_NT_HEADERS *ret = NULL; + IMAGE_DOS_HEADER *dos = (IMAGE_DOS_HEADER *)module; + + if (dos->e_magic == IMAGE_DOS_SIGNATURE) + { + ret = (IMAGE_NT_HEADERS *)((char *)dos + dos->e_lfanew); + if (ret->Signature != IMAGE_NT_SIGNATURE) ret = NULL; + } + return ret; +} + static HANDLE create_target_process(const char *arg) { char **argv; @@ -1543,6 +1568,890 @@ static void test_write_watch(void) VirtualFree( base, 0, MEM_FREE ); } +#ifdef __i386__ + +static DWORD num_guard_page_calls; + +static DWORD guard_page_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame, + CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher ) +{ + trace( "exception: %08x flags:%x addr:%p\n", + rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress ); + + ok( rec->NumberParameters == 2, "NumberParameters is %d instead of 2\n", rec->NumberParameters ); + ok( rec->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION, "ExceptionCode is %08x instead of %08x\n", + rec->ExceptionCode, STATUS_GUARD_PAGE_VIOLATION ); + + num_guard_page_calls++; + *(int *)rec->ExceptionInformation[1] += 0x100; + + return ExceptionContinueExecution; +} + +static void test_guard_page(void) +{ + EXCEPTION_REGISTRATION_RECORD frame; + MEMORY_BASIC_INFORMATION info; + DWORD ret, size, old_prot; + int *value, old_value; + void *results[64]; + ULONG_PTR count; + ULONG pagesize; + BOOL success; + char *base; + + if (!pNtCurrentTeb) + { + win_skip( "NtCurrentTeb not supported\n" ); + return; + } + + size = 0x1000; + base = VirtualAlloc( 0, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE | PAGE_GUARD ); + ok( base != NULL, "VirtualAlloc failed %u\n", GetLastError() ); + value = (int *)base; + + /* verify info structure */ + ret = VirtualQuery( base, &info, sizeof(info) ); + ok( ret, "VirtualQuery failed %u\n", GetLastError()); + ok( info.BaseAddress == base, "BaseAddress %p instead of %p\n", info.BaseAddress, base ); + ok( info.AllocationProtect == (PAGE_READWRITE | PAGE_GUARD), "wrong AllocationProtect %x\n", info.AllocationProtect ); + ok( info.RegionSize == size, "wrong RegionSize 0x%lx\n", info.RegionSize ); + ok( info.State == MEM_COMMIT, "wrong State 0x%x\n", info.State ); + ok( info.Protect == (PAGE_READWRITE | PAGE_GUARD), "wrong Protect 0x%x\n", info.Protect ); + ok( info.Type == MEM_PRIVATE, "wrong Type 0x%x\n", info.Type ); + + /* put some initial value into the memory */ + success = VirtualProtect( base, size, PAGE_READWRITE, &old_prot ); + ok( success, "VirtualProtect failed %u\n", GetLastError() ); + ok( old_prot == (PAGE_READWRITE | PAGE_GUARD), "wrong old prot %x\n", old_prot ); + + *value = 1; + *(value + 1) = 2; + + success = VirtualProtect( base, size, PAGE_READWRITE | PAGE_GUARD, &old_prot ); + ok( success, "VirtualProtect failed %u\n", GetLastError() ); + ok( old_prot == PAGE_READWRITE, "wrong old prot %x\n", old_prot ); + + /* test behaviour of VirtualLock - first attempt should fail */ + SetLastError( 0xdeadbeef ); + success = VirtualLock( base, size ); + ok( !success, "VirtualLock unexpectedly succeeded\n" ); + todo_wine + ok( GetLastError() == STATUS_GUARD_PAGE_VIOLATION, "wrong error %u\n", GetLastError() ); + + success = VirtualLock( base, size ); + todo_wine + ok( success, "VirtualLock failed %u\n", GetLastError() ); + if (success) + { + ok( *value == 1, "memory block contains wrong value, expected 1, got 0x%x\n", *value ); + success = VirtualUnlock( base, size ); + ok( success, "VirtualUnlock failed %u\n", GetLastError() ); + } + + /* check info structure again, PAGE_GUARD should be removed now */ + ret = VirtualQuery( base, &info, sizeof(info) ); + ok( ret, "VirtualQuery failed %u\n", GetLastError()); + ok( info.BaseAddress == base, "BaseAddress %p instead of %p\n", info.BaseAddress, base ); + ok( info.AllocationProtect == (PAGE_READWRITE | PAGE_GUARD), "wrong AllocationProtect %x\n", info.AllocationProtect ); + ok( info.RegionSize == size, "wrong RegionSize 0x%lx\n", info.RegionSize ); + ok( info.State == MEM_COMMIT, "wrong State 0x%x\n", info.State ); + todo_wine + ok( info.Protect == PAGE_READWRITE, "wrong Protect 0x%x\n", info.Protect ); + ok( info.Type == MEM_PRIVATE, "wrong Type 0x%x\n", info.Type ); + + success = VirtualProtect( base, size, PAGE_READWRITE | PAGE_GUARD, &old_prot ); + ok( success, "VirtualProtect failed %u\n", GetLastError() ); + todo_wine + ok( old_prot == PAGE_READWRITE, "wrong old prot %x\n", old_prot ); + + /* test directly accessing the memory - we need to setup an exception handler first */ + frame.Handler = guard_page_handler; + frame.Prev = pNtCurrentTeb()->Tib.ExceptionList; + pNtCurrentTeb()->Tib.ExceptionList = &frame; + + num_guard_page_calls = 0; + old_value = *value; /* exception handler increments value by 0x100 */ + *value = 2; + ok( old_value == 0x101, "memory block contains wrong value, expected 0x101, got 0x%x\n", old_value ); + ok( num_guard_page_calls == 1, "expected one callback of guard page handler, got %d calls\n", num_guard_page_calls ); + + pNtCurrentTeb()->Tib.ExceptionList = frame.Prev; + + /* check info structure again, PAGE_GUARD should be removed now */ + ret = VirtualQuery( base, &info, sizeof(info) ); + ok( ret, "VirtualQuery failed %u\n", GetLastError()); + ok( info.Protect == PAGE_READWRITE, "wrong Protect 0x%x\n", info.Protect ); + + success = VirtualProtect( base, size, PAGE_READWRITE | PAGE_GUARD, &old_prot ); + ok( success, "VirtualProtect failed %u\n", GetLastError() ); + ok( old_prot == PAGE_READWRITE, "wrong old prot %x\n", old_prot ); + + /* test accessing second integer in memory */ + frame.Handler = guard_page_handler; + frame.Prev = pNtCurrentTeb()->Tib.ExceptionList; + pNtCurrentTeb()->Tib.ExceptionList = &frame; + + num_guard_page_calls = 0; + old_value = *(value + 1); + ok( old_value == 0x102, "memory block contains wrong value, expected 0x102, got 0x%x\n", old_value ); + ok( *value == 2, "memory block contains wrong value, expected 2, got 0x%x\n", *value ); + ok( num_guard_page_calls == 1, "expected one callback of guard page handler, got %d calls\n", num_guard_page_calls ); + + pNtCurrentTeb()->Tib.ExceptionList = frame.Prev; + + success = VirtualLock( base, size ); + ok( success, "VirtualLock failed %u\n", GetLastError() ); + if (success) + { + ok( *value == 2, "memory block contains wrong value, expected 2, got 0x%x\n", *value ); + success = VirtualUnlock( base, size ); + ok( success, "VirtualUnlock failed %u\n", GetLastError() ); + } + + VirtualFree( base, 0, MEM_FREE ); + + /* combined guard page / write watch tests */ + if (!pGetWriteWatch || !pResetWriteWatch) + { + win_skip( "GetWriteWatch not supported, skipping combined guard page / write watch tests\n" ); + return; + } + + base = VirtualAlloc( 0, size, MEM_RESERVE | MEM_COMMIT | MEM_WRITE_WATCH, PAGE_READWRITE | PAGE_GUARD ); + if (!base && (GetLastError() == ERROR_INVALID_PARAMETER || GetLastError() == ERROR_NOT_SUPPORTED)) + { + win_skip( "MEM_WRITE_WATCH not supported\n" ); + return; + } + ok( base != NULL, "VirtualAlloc failed %u\n", GetLastError() ); + value = (int *)base; + + ret = VirtualQuery( base, &info, sizeof(info) ); + ok( ret, "VirtualQuery failed %u\n", GetLastError() ); + ok( info.BaseAddress == base, "BaseAddress %p instead of %p\n", info.BaseAddress, base ); + ok( info.AllocationProtect == (PAGE_READWRITE | PAGE_GUARD), "wrong AllocationProtect %x\n", info.AllocationProtect ); + ok( info.RegionSize == size, "wrong RegionSize 0x%lx\n", info.RegionSize ); + ok( info.State == MEM_COMMIT, "wrong State 0x%x\n", info.State ); + ok( info.Protect == (PAGE_READWRITE | PAGE_GUARD), "wrong Protect 0x%x\n", info.Protect ); + ok( info.Type == MEM_PRIVATE, "wrong Type 0x%x\n", info.Type ); + + count = 64; + ret = pGetWriteWatch( 0, base, size, results, &count, &pagesize ); + ok( !ret, "GetWriteWatch failed %u\n", GetLastError() ); + ok( count == 0, "wrong count %lu\n", count ); + + /* writing to a page should trigger should trigger guard page, even if write watch is set */ + frame.Handler = guard_page_handler; + frame.Prev = pNtCurrentTeb()->Tib.ExceptionList; + pNtCurrentTeb()->Tib.ExceptionList = &frame; + + num_guard_page_calls = 0; + *value = 1; + *(value + 1) = 2; + ok( num_guard_page_calls == 1, "expected one callback of guard page handler, got %d calls\n", num_guard_page_calls ); + + pNtCurrentTeb()->Tib.ExceptionList = frame.Prev; + + count = 64; + ret = pGetWriteWatch( WRITE_WATCH_FLAG_RESET, base, size, results, &count, &pagesize ); + ok( !ret, "GetWriteWatch failed %u\n", GetLastError() ); + ok( count == 1, "wrong count %lu\n", count ); + ok( results[0] == base, "wrong result %p\n", results[0] ); + + success = VirtualProtect( base, size, PAGE_READWRITE | PAGE_GUARD, &old_prot ); + ok( success, "VirtualProtect failed %u\n", GetLastError() ); + + /* write watch is triggered from inside of the guard page handler */ + frame.Handler = guard_page_handler; + frame.Prev = pNtCurrentTeb()->Tib.ExceptionList; + pNtCurrentTeb()->Tib.ExceptionList = &frame; + + num_guard_page_calls = 0; + old_value = *(value + 1); /* doesn't trigger write watch */ + ok( old_value == 0x102, "memory block contains wrong value, expected 0x102, got 0x%x\n", old_value ); + ok( *value == 1, "memory block contains wrong value, expected 1, got 0x%x\n", *value ); + ok( num_guard_page_calls == 1, "expected one callback of guard page handler, got %d calls\n", num_guard_page_calls ); + + pNtCurrentTeb()->Tib.ExceptionList = frame.Prev; + + count = 64; + ret = pGetWriteWatch( WRITE_WATCH_FLAG_RESET, base, size, results, &count, &pagesize ); + ok( !ret, "GetWriteWatch failed %u\n", GetLastError() ); + ok( count == 1, "wrong count %lu\n", count ); + ok( results[0] == base, "wrong result %p\n", results[0] ); + + success = VirtualProtect( base, size, PAGE_READWRITE | PAGE_GUARD, &old_prot ); + ok( success, "VirtualProtect failed %u\n", GetLastError() ); + + /* test behaviour of VirtualLock - first attempt should fail without triggering write watches */ + SetLastError( 0xdeadbeef ); + success = VirtualLock( base, size ); + ok( !success, "VirtualLock unexpectedly succeeded\n" ); + todo_wine + ok( GetLastError() == STATUS_GUARD_PAGE_VIOLATION, "wrong error %u\n", GetLastError() ); + + count = 64; + ret = pGetWriteWatch( 0, base, size, results, &count, &pagesize ); + ok( !ret, "GetWriteWatch failed %u\n", GetLastError() ); + ok( count == 0, "wrong count %lu\n", count ); + + success = VirtualLock( base, size ); + todo_wine + ok( success, "VirtualLock failed %u\n", GetLastError() ); + if (success) + { + ok( *value == 1, "memory block contains wrong value, expected 1, got 0x%x\n", *value ); + success = VirtualUnlock( base, size ); + ok( success, "VirtualUnlock failed %u\n", GetLastError() ); + } + + count = 64; + results[0] = (void *)0xdeadbeef; + ret = pGetWriteWatch( WRITE_WATCH_FLAG_RESET, base, size, results, &count, &pagesize ); + ok( !ret, "GetWriteWatch failed %u\n", GetLastError() ); + todo_wine + ok( count == 1 || broken(count == 0) /* Windows 8 */, "wrong count %lu\n", count ); + todo_wine + ok( results[0] == base || broken(results[0] == (void *)0xdeadbeef) /* Windows 8 */, "wrong result %p\n", results[0] ); + + VirtualFree( base, 0, MEM_FREE ); +} + +DWORD num_execute_fault_calls; + +static DWORD execute_fault_seh_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame, + CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher ) +{ + ULONG flags = MEM_EXECUTE_OPTION_ENABLE; + DWORD err; + + trace( "exception: %08x flags:%x addr:%p info[0]:%ld info[1]:%p\n", + rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, + rec->ExceptionInformation[0], (void *)rec->ExceptionInformation[1] ); + + ok( rec->NumberParameters == 2, "NumberParameters is %d instead of 2\n", rec->NumberParameters ); + ok( rec->ExceptionCode == STATUS_ACCESS_VIOLATION || rec->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION, + "ExceptionCode is %08x instead of STATUS_ACCESS_VIOLATION or STATUS_GUARD_PAGE_VIOLATION\n", rec->ExceptionCode ); + + NtQueryInformationProcess( GetCurrentProcess(), ProcessExecuteFlags, &flags, sizeof(flags), NULL ); + + if (rec->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION) + { + + err = IsProcessorFeaturePresent( PF_NX_ENABLED ) ? EXCEPTION_EXECUTE_FAULT : EXCEPTION_READ_FAULT; + ok( rec->ExceptionInformation[0] == err, "ExceptionInformation[0] is %d instead of %d\n", + (DWORD)rec->ExceptionInformation[0], err ); + + num_guard_page_calls++; + } + else if (rec->ExceptionCode == STATUS_ACCESS_VIOLATION) + { + DWORD old_prot; + BOOL success; + + err = (flags & MEM_EXECUTE_OPTION_DISABLE) ? EXCEPTION_EXECUTE_FAULT : EXCEPTION_READ_FAULT; + ok( rec->ExceptionInformation[0] == err, "ExceptionInformation[0] is %d instead of %d\n", + (DWORD)rec->ExceptionInformation[0], err ); + + success = VirtualProtect( (void *)rec->ExceptionInformation[1], 16, PAGE_EXECUTE_READWRITE, &old_prot ); + ok( success, "VirtualProtect failed %u\n", GetLastError() ); + ok( old_prot == PAGE_READWRITE, "wrong old prot %x\n", old_prot ); + + num_execute_fault_calls++; + } + + return ExceptionContinueExecution; +} + +static LONG CALLBACK execute_fault_vec_handler( EXCEPTION_POINTERS *ExceptionInfo ) +{ + PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord; + DWORD old_prot; + BOOL success; + + trace( "exception: %08x flags:%x addr:%p info[0]:%ld info[1]:%p\n", + rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, + rec->ExceptionInformation[0], (void *)rec->ExceptionInformation[1] ); + + ok( rec->NumberParameters == 2, "NumberParameters is %d instead of 2\n", rec->NumberParameters ); + ok( rec->ExceptionCode == STATUS_ACCESS_VIOLATION, + "ExceptionCode is %08x instead of STATUS_ACCESS_VIOLATION\n", rec->ExceptionCode ); + + if (rec->ExceptionCode == STATUS_ACCESS_VIOLATION) + num_execute_fault_calls++; + + if (rec->ExceptionInformation[0] == EXCEPTION_READ_FAULT) + return EXCEPTION_CONTINUE_SEARCH; + + success = VirtualProtect( (void *)rec->ExceptionInformation[1], 16, PAGE_EXECUTE_READWRITE, &old_prot ); + ok( success, "VirtualProtect failed %u\n", GetLastError() ); + ok( old_prot == PAGE_NOACCESS, "wrong old prot %x\n", old_prot ); + + return EXCEPTION_CONTINUE_EXECUTION; +} + +static inline DWORD send_message_excpt( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) +{ + EXCEPTION_REGISTRATION_RECORD frame; + DWORD ret; + + frame.Handler = execute_fault_seh_handler; + frame.Prev = pNtCurrentTeb()->Tib.ExceptionList; + pNtCurrentTeb()->Tib.ExceptionList = &frame; + + num_guard_page_calls = num_execute_fault_calls = 0; + ret = SendMessageA( hWnd, uMsg, wParam, lParam ); + + pNtCurrentTeb()->Tib.ExceptionList = frame.Prev; + + return ret; +} + +static inline DWORD call_proc_excpt( DWORD (CALLBACK *code)(void *), void *arg ) +{ + EXCEPTION_REGISTRATION_RECORD frame; + DWORD ret; + + frame.Handler = execute_fault_seh_handler; + frame.Prev = pNtCurrentTeb()->Tib.ExceptionList; + pNtCurrentTeb()->Tib.ExceptionList = &frame; + + num_guard_page_calls = num_execute_fault_calls = 0; + ret = code( arg ); + + pNtCurrentTeb()->Tib.ExceptionList = frame.Prev; + + return ret; +} + +static LRESULT CALLBACK jmp_test_func( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) +{ + if (uMsg == WM_USER) + return 42; + + return DefWindowProcA( hWnd, uMsg, wParam, lParam ); +} + +static LRESULT CALLBACK atl_test_func( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) +{ + DWORD arg = (DWORD)hWnd; + if (uMsg == WM_USER) + ok( arg == 0x11223344, "arg is 0x%08x instead of 0x11223344\n", arg ); + else + ok( arg != 0x11223344, "arg is unexpectedly 0x11223344\n" ); + return 43; +} + +static DWORD CALLBACK atl5_test_func( void ) +{ + return 44; +} + +static void test_atl_thunk_emulation( ULONG dep_flags ) +{ + static const char code_jmp[] = {0xE9, 0x00, 0x00, 0x00, 0x00}; + static const char code_atl1[] = {0xC7, 0x44, 0x24, 0x04, 0x44, 0x33, 0x22, 0x11, 0xE9, 0x00, 0x00, 0x00, 0x00}; + static const char code_atl2[] = {0xB9, 0x44, 0x33, 0x22, 0x11, 0xE9, 0x00, 0x00, 0x00, 0x00}; + static const char code_atl3[] = {0xBA, 0x44, 0x33, 0x22, 0x11, 0xB9, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE1}; + static const char code_atl4[] = {0xB9, 0x44, 0x33, 0x22, 0x11, 0xB8, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE0}; + static const char code_atl5[] = {0x59, 0x58, 0x51, 0xFF, 0x60, 0x04}; + static const char cls_name[] = "atl_thunk_class"; + DWORD ret, size, old_prot; + ULONG old_flags = MEM_EXECUTE_OPTION_ENABLE; + BOOL success, restore_flags = FALSE; + void *results[64]; + ULONG_PTR count; + ULONG pagesize; + WNDCLASSEXA wc; + char *base; + HWND hWnd; + + if (!pNtCurrentTeb) + { + win_skip( "NtCurrentTeb not supported\n" ); + return; + } + + trace( "Running DEP tests with ProcessExecuteFlags = %d\n", dep_flags ); + + NtQueryInformationProcess( GetCurrentProcess(), ProcessExecuteFlags, &old_flags, sizeof(old_flags), NULL ); + if (old_flags != dep_flags) + { + ret = NtSetInformationProcess( GetCurrentProcess(), ProcessExecuteFlags, &dep_flags, sizeof(dep_flags) ); + if (ret == STATUS_INVALID_INFO_CLASS) /* Windows 2000 */ + { + win_skip( "Skipping DEP tests with ProcessExecuteFlags = %d\n", dep_flags ); + return; + } + ok( !ret, "NtSetInformationProcess failed with status %08x\n", ret ); + restore_flags = TRUE; + } + + size = 0x1000; + base = VirtualAlloc( 0, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE ); + ok( base != NULL, "VirtualAlloc failed %u\n", GetLastError() ); + + /* Check result of GetProcessDEPPolicy */ + if (!pGetProcessDEPPolicy) + win_skip( "GetProcessDEPPolicy not supported\n" ); + else + { + BOOL (WINAPI *get_dep_policy)(HANDLE, LPDWORD, PBOOL) = (void *)base; + BOOL policy_permanent = 0xdeadbeef; + DWORD policy_flags = 0xdeadbeef; + + /* GetProcessDEPPolicy crashes on Windows when a NULL pointer is passed. + * Moreover this function has a bug on Windows 8, which has the effect that + * policy_permanent is set to the content of the CL register instead of 0, + * when the policy is not permanent. To detect that we use an assembler + * wrapper to call the function. */ + + memcpy( base, code_atl2, sizeof(code_atl2) ); + *(DWORD *)(base + 6) = (DWORD_PTR)pGetProcessDEPPolicy - (DWORD_PTR)(base + 10); + + success = VirtualProtect( base, size, PAGE_EXECUTE_READWRITE, &old_prot ); + ok( success, "VirtualProtect failed %u\n", GetLastError() ); + + success = get_dep_policy( GetCurrentProcess(), &policy_flags, &policy_permanent ); + ok( success, "GetProcessDEPPolicy failed %u\n", GetLastError() ); + + ret = 0; + if (dep_flags & MEM_EXECUTE_OPTION_DISABLE) + ret |= PROCESS_DEP_ENABLE; + if (dep_flags & MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION) + ret |= PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION; + + ok( policy_flags == ret, "expected policy flags %d, got %d\n", ret, policy_flags ); + ok( !policy_permanent || broken(policy_permanent == 0x44), + "expected policy permanent FALSE, got %d\n", policy_permanent ); + } + + memcpy( base, code_jmp, sizeof(code_jmp) ); + *(DWORD *)(base + 1) = (DWORD_PTR)jmp_test_func - (DWORD_PTR)(base + 5); + + /* On Windows, the ATL Thunk emulation is only enabled while running WndProc functions, + * whereas in Wine such a limitation doesn't exist yet. We want to test in a scenario + * where it is active, so that application which depend on that still work properly. + * We have no exception handler enabled yet, so give proper EXECUTE permissions to + * prevent crashes while creating the window. */ + + success = VirtualProtect( base, size, PAGE_EXECUTE_READWRITE, &old_prot ); + ok( success, "VirtualProtect failed %u\n", GetLastError() ); + + memset( &wc, 0, sizeof(wc) ); + wc.cbSize = sizeof(wc); + wc.style = CS_VREDRAW | CS_HREDRAW; + wc.hInstance = GetModuleHandleA( 0 ); + wc.hCursor = LoadCursorA( NULL, (LPCSTR)IDC_ARROW ); + wc.hbrBackground = NULL; + wc.lpszClassName = cls_name; + wc.lpfnWndProc = (WNDPROC)base; + success = RegisterClassExA(&wc) != 0; + ok( success, "RegisterClassExA failed %u\n", GetLastError() ); + + hWnd = CreateWindowExA(0, cls_name, "Test", WS_TILEDWINDOW, 0, 0, 640, 480, 0, 0, 0, 0); + ok( hWnd != 0, "CreateWindowExA failed %u\n", GetLastError() ); + + ret = SendMessageA(hWnd, WM_USER, 0, 0); + ok( ret == 42, "SendMessage returned unexpected result %d\n", ret ); + + /* At first try with an instruction which is not recognized as proper ATL thunk + * by the Windows ATL Thunk Emulator. Removing execute permissions will lead to + * STATUS_ACCESS_VIOLATION exceptions when DEP is enabled. */ + + success = VirtualProtect( base, size, PAGE_READWRITE, &old_prot ); + ok( success, "VirtualProtect failed %u\n", GetLastError() ); + + ret = send_message_excpt( hWnd, WM_USER, 0, 0 ); + ok( ret == 42, "call returned wrong result, expected 42, got %d\n", ret ); + ok( num_guard_page_calls == 0, "expected no STATUS_GUARD_PAGE_VIOLATION exception, got %d exceptions\n", num_guard_page_calls ); + if ((dep_flags & MEM_EXECUTE_OPTION_DISABLE) && !IsProcessorFeaturePresent( PF_NX_ENABLED )) + { + trace( "DEP hardware support is not available\n" ); + ok( num_execute_fault_calls == 0, "expected no STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls ); + dep_flags = MEM_EXECUTE_OPTION_ENABLE; + } + else if (dep_flags & MEM_EXECUTE_OPTION_DISABLE) + { + trace( "DEP hardware support is available\n" ); + ok( num_execute_fault_calls == 1, "expected one STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls ); + } + else + ok( num_execute_fault_calls == 0, "expected no STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls ); + + /* Now a bit more complicated, the page containing the code is protected with + * PAGE_GUARD memory protection. */ + + success = VirtualProtect( base, size, PAGE_READWRITE | PAGE_GUARD, &old_prot ); + ok( success, "VirtualProtect failed %u\n", GetLastError() ); + + ret = send_message_excpt( hWnd, WM_USER, 0, 0 ); + ok( ret == 42, "call returned wrong result, expected 42, got %d\n", ret ); + ok( num_guard_page_calls == 1, "expected one STATUS_GUARD_PAGE_VIOLATION exception, got %d exceptions\n", num_guard_page_calls ); + if (dep_flags & MEM_EXECUTE_OPTION_DISABLE) + ok( num_execute_fault_calls == 1, "expected one STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls ); + else + ok( num_execute_fault_calls == 0, "expected no STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls ); + + ret = send_message_excpt( hWnd, WM_USER, 0, 0 ); + ok( ret == 42, "call returned wrong result, expected 42, got %d\n", ret ); + ok( num_guard_page_calls == 0, "expected no STATUS_GUARD_PAGE_VIOLATION exception, got %d exceptions\n", num_guard_page_calls ); + ok( num_execute_fault_calls == 0, "expected no STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls ); + + /* Now test with a proper ATL thunk instruction. */ + + memcpy( base, code_atl1, sizeof(code_atl1) ); + *(DWORD *)(base + 9) = (DWORD_PTR)atl_test_func - (DWORD_PTR)(base + 13); + + success = VirtualProtect( base, size, PAGE_EXECUTE_READWRITE, &old_prot ); + ok( success, "VirtualProtect failed %u\n", GetLastError() ); + + ret = SendMessageA(hWnd, WM_USER, 0, 0); + ok( ret == 43, "SendMessage returned unexpected result %d\n", ret ); + + /* Try executing with PAGE_READWRITE protection. */ + + success = VirtualProtect( base, size, PAGE_READWRITE, &old_prot ); + ok( success, "VirtualProtect failed %u\n", GetLastError() ); + + ret = send_message_excpt( hWnd, WM_USER, 0, 0 ); + ok( ret == 43, "call returned wrong result, expected 43, got %d\n", ret ); + ok( num_guard_page_calls == 0, "expected no STATUS_GUARD_PAGE_VIOLATION exception, got %d exceptions\n", num_guard_page_calls ); + if ((dep_flags & MEM_EXECUTE_OPTION_DISABLE) && (dep_flags & MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION)) + ok( num_execute_fault_calls == 1, "expected one STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls ); + else + ok( num_execute_fault_calls == 0, "expected no STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls ); + + /* Now a bit more complicated, the page containing the code is protected with + * PAGE_GUARD memory protection. */ + + success = VirtualProtect( base, size, PAGE_READWRITE | PAGE_GUARD, &old_prot ); + ok( success, "VirtualProtect failed %u\n", GetLastError() ); + + /* the same, but with PAGE_GUARD set */ + ret = send_message_excpt( hWnd, WM_USER, 0, 0 ); + ok( ret == 43, "call returned wrong result, expected 43, got %d\n", ret ); + ok( num_guard_page_calls == 1, "expected one STATUS_GUARD_PAGE_VIOLATION exception, got %d exceptions\n", num_guard_page_calls ); + if ((dep_flags & MEM_EXECUTE_OPTION_DISABLE) && (dep_flags & MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION)) + ok( num_execute_fault_calls == 1, "expected one STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls ); + else + ok( num_execute_fault_calls == 0, "expected no STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls ); + + ret = send_message_excpt( hWnd, WM_USER, 0, 0 ); + ok( ret == 43, "call returned wrong result, expected 43, got %d\n", ret ); + ok( num_guard_page_calls == 0, "expected no STATUS_GUARD_PAGE_VIOLATION exception, got %d exceptions\n", num_guard_page_calls ); + ok( num_execute_fault_calls == 0, "expected no STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls ); + + /* The following test shows that on Windows, even a vectored exception handler + * cannot intercept internal exceptions thrown by the ATL thunk emulation layer. */ + + if ((dep_flags & MEM_EXECUTE_OPTION_DISABLE) && !(dep_flags & MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION)) + { + if (pRtlAddVectoredExceptionHandler && pRtlRemoveVectoredExceptionHandler) + { + PVOID vectored_handler; + + success = VirtualProtect( base, size, PAGE_NOACCESS, &old_prot ); + ok( success, "VirtualProtect failed %u\n", GetLastError() ); + + vectored_handler = pRtlAddVectoredExceptionHandler( TRUE, &execute_fault_vec_handler ); + ok( vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n" ); + + ret = send_message_excpt( hWnd, WM_USER, 0, 0 ); + + pRtlRemoveVectoredExceptionHandler( vectored_handler ); + + ok( ret == 43, "call returned wrong result, expected 43, got %d\n", ret ); + ok( num_execute_fault_calls == 1, "expected one STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls ); + } + else + win_skip( "RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler not found\n" ); + } + + /* Test alternative ATL thunk instructions. */ + + memcpy( base, code_atl2, sizeof(code_atl2) ); + *(DWORD *)(base + 6) = (DWORD_PTR)atl_test_func - (DWORD_PTR)(base + 10); + + success = VirtualProtect( base, size, PAGE_READWRITE, &old_prot ); + ok( success, "VirtualProtect failed %u\n", GetLastError() ); + + ret = send_message_excpt( hWnd, WM_USER + 1, 0, 0 ); + /* FIXME: we don't check the content of the register ECX yet */ + ok( ret == 43, "call returned wrong result, expected 43, got %d\n", ret ); + ok( num_guard_page_calls == 0, "expected no STATUS_GUARD_PAGE_VIOLATION exception, got %d exceptions\n", num_guard_page_calls ); + if ((dep_flags & MEM_EXECUTE_OPTION_DISABLE) && (dep_flags & MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION)) + ok( num_execute_fault_calls == 1, "expected one STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls ); + else + ok( num_execute_fault_calls == 0, "expected no STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls ); + + memcpy( base, code_atl3, sizeof(code_atl3) ); + *(DWORD *)(base + 6) = (DWORD_PTR)atl_test_func; + + success = VirtualProtect( base, size, PAGE_READWRITE, &old_prot ); + ok( success, "VirtualProtect failed %u\n", GetLastError() ); + + ret = send_message_excpt( hWnd, WM_USER + 1, 0, 0 ); + /* FIXME: we don't check the content of the registers ECX/EDX yet */ + ok( ret == 43, "call returned wrong result, expected 43, got %d\n", ret ); + ok( num_guard_page_calls == 0, "expected no STATUS_GUARD_PAGE_VIOLATION exception, got %d exceptions\n", num_guard_page_calls ); + if ((dep_flags & MEM_EXECUTE_OPTION_DISABLE) && (dep_flags & MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION)) + ok( num_execute_fault_calls == 1, "expected one STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls ); + else + ok( num_execute_fault_calls == 0, "expected no STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls ); + + memcpy( base, code_atl4, sizeof(code_atl4) ); + *(DWORD *)(base + 6) = (DWORD_PTR)atl_test_func; + + success = VirtualProtect( base, size, PAGE_READWRITE, &old_prot ); + ok( success, "VirtualProtect failed %u\n", GetLastError() ); + + ret = send_message_excpt( hWnd, WM_USER + 1, 0, 0 ); + /* FIXME: We don't check the content of the registers EAX/ECX yet */ + ok( ret == 43, "call returned wrong result, expected 43, got %d\n", ret ); + ok( num_guard_page_calls == 0, "expected no STATUS_GUARD_PAGE_VIOLATION exception, got %d exceptions\n", num_guard_page_calls ); + if ((dep_flags & MEM_EXECUTE_OPTION_DISABLE) && (dep_flags & MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION)) + ok( num_execute_fault_calls == 1, "expected one STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls ); + else if (dep_flags & MEM_EXECUTE_OPTION_DISABLE) + ok( num_execute_fault_calls == 0 || broken(num_execute_fault_calls == 1) /* Windows XP */, + "expected no STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls ); + else + ok( num_execute_fault_calls == 0, "expected no STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls ); + + memcpy( base, code_atl5, sizeof(code_atl5) ); + + success = VirtualProtect( base, size, PAGE_READWRITE, &old_prot ); + ok( success, "VirtualProtect failed %u\n", GetLastError() ); + + ret = (DWORD_PTR)atl5_test_func; + ret = call_proc_excpt( (void *)base, &ret - 1 ); + /* FIXME: We don't check the content of the registers EAX/ECX yet */ + ok( ret == 44, "call returned wrong result, expected 44, got %d\n", ret ); + ok( num_guard_page_calls == 0, "expected no STATUS_GUARD_PAGE_VIOLATION exception, got %d exceptions\n", num_guard_page_calls ); + if ((dep_flags & MEM_EXECUTE_OPTION_DISABLE) && (dep_flags & MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION)) + ok( num_execute_fault_calls == 1, "expected one STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls ); + else if (dep_flags & MEM_EXECUTE_OPTION_DISABLE) + ok( num_execute_fault_calls == 0 || broken(num_execute_fault_calls == 1) /* Windows XP */, + "expected no STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls ); + else + ok( num_execute_fault_calls == 0, "expected no STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls ); + + /* Restore the JMP instruction, set to executable, and then destroy the Window */ + + memcpy( base, code_jmp, sizeof(code_jmp) ); + *(DWORD *)(base + 1) = (DWORD_PTR)jmp_test_func - (DWORD_PTR)(base + 5); + + success = VirtualProtect( base, size, PAGE_EXECUTE_READWRITE, &old_prot ); + ok( success, "VirtualProtect failed %u\n", GetLastError() ); + + DestroyWindow( hWnd ); + + success = UnregisterClassA( cls_name, GetModuleHandleA(0) ); + ok( success, "UnregisterClass failed %u\n", GetLastError() ); + + VirtualFree( base, 0, MEM_FREE ); + + /* Repeat the tests from above with MEM_WRITE_WATCH protected memory. */ + + base = VirtualAlloc( 0, size, MEM_RESERVE | MEM_COMMIT | MEM_WRITE_WATCH, PAGE_READWRITE ); + if (!base && (GetLastError() == ERROR_INVALID_PARAMETER || GetLastError() == ERROR_NOT_SUPPORTED)) + { + win_skip( "MEM_WRITE_WATCH not supported\n" ); + goto out; + } + ok( base != NULL, "VirtualAlloc failed %u\n", GetLastError() ); + + count = 64; + ret = pGetWriteWatch( WRITE_WATCH_FLAG_RESET, base, size, results, &count, &pagesize ); + ok( !ret, "GetWriteWatch failed %u\n", GetLastError() ); + ok( count == 0, "wrong count %lu\n", count ); + + memcpy( base, code_jmp, sizeof(code_jmp) ); + *(DWORD *)(base + 1) = (DWORD_PTR)jmp_test_func - (DWORD_PTR)(base + 5); + + count = 64; + ret = pGetWriteWatch( WRITE_WATCH_FLAG_RESET, base, size, results, &count, &pagesize ); + ok( !ret, "GetWriteWatch failed %u\n", GetLastError() ); + ok( count == 1, "wrong count %lu\n", count ); + ok( results[0] == base, "wrong result %p\n", results[0] ); + + /* Create a new window class and associcated Window (see above) */ + + success = VirtualProtect( base, size, PAGE_EXECUTE_READWRITE, &old_prot ); + ok( success, "VirtualProtect failed %u\n", GetLastError() ); + + memset( &wc, 0, sizeof(wc) ); + wc.cbSize = sizeof(wc); + wc.style = CS_VREDRAW | CS_HREDRAW; + wc.hInstance = GetModuleHandleA( 0 ); + wc.hCursor = LoadCursorA( NULL, (LPCSTR)IDC_ARROW ); + wc.hbrBackground = NULL; + wc.lpszClassName = cls_name; + wc.lpfnWndProc = (WNDPROC)base; + success = RegisterClassExA(&wc) != 0; + ok( success, "RegisterClassExA failed %u\n", GetLastError() ); + + hWnd = CreateWindowExA(0, cls_name, "Test", WS_TILEDWINDOW, 0, 0, 640, 480, 0, 0, 0, 0); + ok( hWnd != 0, "CreateWindowExA failed %u\n", GetLastError() ); + + ret = SendMessageA(hWnd, WM_USER, 0, 0); + ok( ret == 42, "SendMessage returned unexpected result %d\n", ret ); + + count = 64; + ret = pGetWriteWatch( WRITE_WATCH_FLAG_RESET, base, size, results, &count, &pagesize ); + ok( !ret, "GetWriteWatch failed %u\n", GetLastError() ); + ok( count == 0, "wrong count %lu\n", count ); + + /* At first try with an instruction which is not recognized as proper ATL thunk + * by the Windows ATL Thunk Emulator. Removing execute permissions will lead to + * STATUS_ACCESS_VIOLATION exceptions when DEP is enabled. */ + + success = VirtualProtect( base, size, PAGE_READWRITE, &old_prot ); + ok( success, "VirtualProtect failed %u\n", GetLastError() ); + + ret = send_message_excpt( hWnd, WM_USER, 0, 0 ); + ok( ret == 42, "call returned wrong result, expected 42, got %d\n", ret ); + ok( num_guard_page_calls == 0, "expected no STATUS_GUARD_PAGE_VIOLATION exception, got %d exceptions\n", num_guard_page_calls ); + if (dep_flags & MEM_EXECUTE_OPTION_DISABLE) + ok( num_execute_fault_calls == 1, "expected one STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls ); + else + ok( num_execute_fault_calls == 0, "expected no STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls ); + + count = 64; + ret = pGetWriteWatch( WRITE_WATCH_FLAG_RESET, base, size, results, &count, &pagesize ); + ok( !ret, "GetWriteWatch failed %u\n", GetLastError() ); + ok( count == 0, "wrong count %lu\n", count ); + + ret = send_message_excpt( hWnd, WM_USER, 0, 0 ); + ok( ret == 42, "call returned wrong result, expected 42, got %d\n", ret ); + ok( num_guard_page_calls == 0, "expected no STATUS_GUARD_PAGE_VIOLATION exception, got %d exceptions\n", num_guard_page_calls ); + ok( num_execute_fault_calls == 0, "expected no STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls ); + + /* Now a bit more complicated, the page containing the code is protected with + * PAGE_GUARD memory protection. */ + + success = VirtualProtect( base, size, PAGE_READWRITE | PAGE_GUARD, &old_prot ); + ok( success, "VirtualProtect failed %u\n", GetLastError() ); + + ret = send_message_excpt( hWnd, WM_USER, 0, 0 ); + ok( ret == 42, "call returned wrong result, expected 42, got %d\n", ret ); + ok( num_guard_page_calls == 1, "expected one STATUS_GUARD_PAGE_VIOLATION exception, got %d exceptions\n", num_guard_page_calls ); + if (dep_flags & MEM_EXECUTE_OPTION_DISABLE) + ok( num_execute_fault_calls == 1, "expected one STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls ); + else + ok( num_execute_fault_calls == 0, "expected no STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls ); + + ret = send_message_excpt( hWnd, WM_USER, 0, 0 ); + ok( ret == 42, "call returned wrong result, expected 42, got %d\n", ret ); + ok( num_guard_page_calls == 0, "expected no STATUS_GUARD_PAGE_VIOLATION exception, got %d exceptions\n", num_guard_page_calls ); + ok( num_execute_fault_calls == 0, "expected no STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls ); + + count = 64; + ret = pGetWriteWatch( WRITE_WATCH_FLAG_RESET, base, size, results, &count, &pagesize ); + ok( !ret, "GetWriteWatch failed %u\n", GetLastError() ); + ok( count == 0 || broken(count == 1) /* Windows 8 */, "wrong count %lu\n", count ); + + /* Now test with a proper ATL thunk instruction. */ + + memcpy( base, code_atl1, sizeof(code_atl1) ); + *(DWORD *)(base + 9) = (DWORD_PTR)atl_test_func - (DWORD_PTR)(base + 13); + + count = 64; + ret = pGetWriteWatch( WRITE_WATCH_FLAG_RESET, base, size, results, &count, &pagesize ); + ok( !ret, "GetWriteWatch failed %u\n", GetLastError() ); + ok( count == 1, "wrong count %lu\n", count ); + ok( results[0] == base, "wrong result %p\n", results[0] ); + + success = VirtualProtect( base, size, PAGE_EXECUTE_READWRITE, &old_prot ); + ok( success, "VirtualProtect failed %u\n", GetLastError() ); + + ret = SendMessageA(hWnd, WM_USER, 0, 0); + ok( ret == 43, "SendMessage returned unexpected result %d\n", ret ); + + /* Try executing with PAGE_READWRITE protection. */ + + success = VirtualProtect( base, size, PAGE_READWRITE, &old_prot ); + ok( success, "VirtualProtect failed %u\n", GetLastError() ); + + ret = send_message_excpt( hWnd, WM_USER, 0, 0 ); + ok( ret == 43, "call returned wrong result, expected 43, got %d\n", ret ); + ok( num_guard_page_calls == 0, "expected no STATUS_GUARD_PAGE_VIOLATION exception, got %d exceptions\n", num_guard_page_calls ); + if ((dep_flags & MEM_EXECUTE_OPTION_DISABLE) && (dep_flags & MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION)) + ok( num_execute_fault_calls == 1, "expected one STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls ); + else + ok( num_execute_fault_calls == 0, "expected no STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls ); + + count = 64; + ret = pGetWriteWatch( WRITE_WATCH_FLAG_RESET, base, size, results, &count, &pagesize ); + ok( !ret, "GetWriteWatch failed %u\n", GetLastError() ); + ok( count == 0, "wrong count %lu\n", count ); + + ret = send_message_excpt( hWnd, WM_USER, 0, 0 ); + ok( ret == 43, "call returned wrong result, expected 43, got %d\n", ret ); + ok( num_guard_page_calls == 0, "expected no STATUS_GUARD_PAGE_VIOLATION exception, got %d exceptions\n", num_guard_page_calls ); + if ((dep_flags & MEM_EXECUTE_OPTION_DISABLE) && (dep_flags & MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION)) + ok( num_execute_fault_calls == 0, "expected no STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls ); + else + ok( num_execute_fault_calls == 0, "expected no STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls ); + + /* Now a bit more complicated, the page containing the code is protected with + * PAGE_GUARD memory protection. */ + + success = VirtualProtect( base, size, PAGE_READWRITE | PAGE_GUARD, &old_prot ); + ok( success, "VirtualProtect failed %u\n", GetLastError() ); + + /* the same, but with PAGE_GUARD set */ + ret = send_message_excpt( hWnd, WM_USER, 0, 0 ); + ok( ret == 43, "call returned wrong result, expected 43, got %d\n", ret ); + ok( num_guard_page_calls == 1, "expected one STATUS_GUARD_PAGE_VIOLATION exception, got %d exceptions\n", num_guard_page_calls ); + if ((dep_flags & MEM_EXECUTE_OPTION_DISABLE) && (dep_flags & MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION)) + ok( num_execute_fault_calls == 1, "expected one STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls ); + else + ok( num_execute_fault_calls == 0, "expected no STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls ); + + ret = send_message_excpt( hWnd, WM_USER, 0, 0 ); + ok( ret == 43, "call returned wrong result, expected 43, got %d\n", ret ); + ok( num_guard_page_calls == 0, "expected no STATUS_GUARD_PAGE_VIOLATION exception, got %d exceptions\n", num_guard_page_calls ); + ok( num_execute_fault_calls == 0, "expected no STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls ); + + count = 64; + ret = pGetWriteWatch( WRITE_WATCH_FLAG_RESET, base, size, results, &count, &pagesize ); + ok( !ret, "GetWriteWatch failed %u\n", GetLastError() ); + ok( count == 0 || broken(count == 1) /* Windows 8 */, "wrong count %lu\n", count ); + + /* Restore the JMP instruction, set to executable, and then destroy the Window */ + + memcpy( base, code_jmp, sizeof(code_jmp) ); + *(DWORD *)(base + 1) = (DWORD_PTR)jmp_test_func - (DWORD_PTR)(base + 5); + + count = 64; + ret = pGetWriteWatch( WRITE_WATCH_FLAG_RESET, base, size, results, &count, &pagesize ); + ok( !ret, "GetWriteWatch failed %u\n", GetLastError() ); + ok( count == 1, "wrong count %lu\n", count ); + ok( results[0] == base, "wrong result %p\n", results[0] ); + + success = VirtualProtect( base, size, PAGE_EXECUTE_READWRITE, &old_prot ); + ok( success, "VirtualProtect failed %u\n", GetLastError() ); + + DestroyWindow( hWnd ); + + success = UnregisterClassA( cls_name, GetModuleHandleA(0) ); + ok( success, "UnregisterClass failed %u\n", GetLastError() ); + + VirtualFree( base, 0, MEM_FREE ); + +out: + if (restore_flags) + { + ret = NtSetInformationProcess( GetCurrentProcess(), ProcessExecuteFlags, &old_flags, sizeof(old_flags) ); + ok( !ret, "NtSetInformationProcess failed with status %08x\n", ret ); + } +} + +#endif /* __i386__ */ + static void test_VirtualProtect(void) { static const struct test_data @@ -2038,11 +2947,7 @@ todo_wine 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); + ok(info.Protect == td[i].prot_after_write, "%d: got %#x != expected %#x\n", i, info.Protect, td[i].prot_after_write); } } else @@ -2056,11 +2961,7 @@ todo_wine 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); + ok(old_prot == td[i].prot_after_write, "%d: got %#x != expected %#x\n", i, old_prot, td[i].prot_after_write); } UnmapViewOfFile(base); @@ -2534,6 +3435,244 @@ static void test_shared_memory_ro(BOOL is_child, DWORD child_access) CloseHandle(mapping); } +static void test_NtQuerySection(void) +{ + char path[MAX_PATH]; + HANDLE file, mapping; + void *p; + NTSTATUS status; + union + { + SECTION_BASIC_INFORMATION basic; + SECTION_IMAGE_INFORMATION image; + char buf[1024]; + } info; + IMAGE_NT_HEADERS *nt; + ULONG ret; + SIZE_T fsize, image_size; + SYSTEM_INFO si; + + if (!pNtQuerySection) + { + win_skip("NtQuerySection is not available\n"); + return; + } + + GetSystemInfo(&si); + page_mask = si.dwPageSize - 1; + + GetSystemDirectoryA(path, sizeof(path)); + strcat(path, "\\kernel32.dll"); + + SetLastError(0xdeadbef); + file = CreateFileA(path, GENERIC_READ|GENERIC_EXECUTE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0); + ok(file != INVALID_HANDLE_VALUE, "CreateFile error %u\n", GetLastError()); + + fsize = GetFileSize(file, NULL); + + SetLastError(0xdeadbef); + mapping = CreateFileMappingA(file, NULL, PAGE_EXECUTE_READ, 0, 0, NULL); + /* NT4 and win2k don't support EXEC on file mappings */ + if (!mapping) + mapping = CreateFileMappingA(file, NULL, PAGE_READONLY, 0, 0, NULL); + ok(mapping != 0, "CreateFileMapping error %u\n", GetLastError()); + + status = pNtQuerySection(mapping, SectionBasicInformation, NULL, sizeof(info), &ret); + ok(status == STATUS_ACCESS_VIOLATION, "expected STATUS_ACCESS_VIOLATION, got %#x\n", status); + + status = pNtQuerySection(mapping, SectionBasicInformation, &info, 0, NULL); + ok(status == STATUS_INFO_LENGTH_MISMATCH, "expected STATUS_INFO_LENGTH_MISMATCH, got %#x\n", status); + + status = pNtQuerySection(mapping, SectionBasicInformation, &info, 0, &ret); + ok(status == STATUS_INFO_LENGTH_MISMATCH, "expected STATUS_INFO_LENGTH_MISMATCH, got %#x\n", status); + + memset(&info, 0x55, sizeof(info)); + ret = 0xdeadbeef; + status = pNtQuerySection(mapping, SectionBasicInformation, &info, sizeof(info), &ret); + ok(status == STATUS_SUCCESS, "NtQuerySection error %#x\n", status); + ok(ret == sizeof(info.basic), "wrong returned size %u\n", ret); + ok(info.basic.BaseAddress == NULL, "expected NULL, got %p\n", info.basic.BaseAddress); +todo_wine + ok(info.basic.Attributes == SEC_FILE, "expected SEC_FILE, got %#x\n", info.basic.Attributes); +todo_wine + ok(info.basic.Size.QuadPart == fsize, "expected %#lx, got %#x/%08x\n", fsize, info.basic.Size.HighPart, info.basic.Size.LowPart); + + status = pNtQuerySection(mapping, SectionImageInformation, &info, sizeof(info.basic), &ret); + ok(status == STATUS_INFO_LENGTH_MISMATCH, "expected STATUS_INFO_LENGTH_MISMATCH, got %#x\n", status); + + status = pNtQuerySection(mapping, SectionImageInformation, &info, sizeof(info), &ret); + ok(status == STATUS_SECTION_NOT_IMAGE, "expected STATUS_SECTION_NOT_IMAGE, got %#x\n", status); + + SetLastError(0xdeadbef); + p = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0); + ok(p != NULL, "MapViewOfFile error %u\n", GetLastError()); + + nt = image_nt_header(p); + image_size = ROUND_SIZE(p, nt->OptionalHeader.SizeOfImage); + + memset(&info, 0x55, sizeof(info)); + ret = 0xdeadbeef; + status = pNtQuerySection(mapping, SectionBasicInformation, &info, sizeof(info), &ret); + ok(status == STATUS_SUCCESS, "NtQuerySection error %#x\n", status); + ok(ret == sizeof(info.basic), "wrong returned size %u\n", ret); + ok(info.basic.BaseAddress == NULL, "expected NULL, got %p\n", info.basic.BaseAddress); +todo_wine + ok(info.basic.Attributes == SEC_FILE, "expected SEC_FILE, got %#x\n", info.basic.Attributes); +todo_wine + ok(info.basic.Size.QuadPart == fsize, "expected %#lx, got %#x/%08x\n", fsize, info.basic.Size.HighPart, info.basic.Size.LowPart); + + UnmapViewOfFile(p); + CloseHandle(mapping); + + SetLastError(0xdeadbef); + mapping = CreateFileMappingA(file, NULL, PAGE_EXECUTE_READ|SEC_IMAGE, 0, 0, NULL); + /* NT4 and win2k don't support EXEC on file mappings */ + if (!mapping) + mapping = CreateFileMappingA(file, NULL, PAGE_READONLY|SEC_IMAGE, 0, 0, NULL); + ok(mapping != 0, "CreateFileMapping error %u\n", GetLastError()); + + memset(&info, 0x55, sizeof(info)); + ret = 0xdeadbeef; + status = pNtQuerySection(mapping, SectionBasicInformation, &info, sizeof(info), &ret); + ok(status == STATUS_SUCCESS, "NtQuerySection error %#x\n", status); + ok(ret == sizeof(info.basic), "wrong returned size %u\n", ret); + ok(info.basic.BaseAddress == NULL, "expected NULL, got %p\n", info.basic.BaseAddress); +todo_wine + ok(info.basic.Attributes == (SEC_FILE|SEC_IMAGE), "expected SEC_FILE|SEC_IMAGE, got %#x\n", info.basic.Attributes); + ok(info.basic.Size.QuadPart == image_size, "expected %#lx, got %#x/%08x\n", image_size, info.basic.Size.HighPart, info.basic.Size.LowPart); + + status = pNtQuerySection(mapping, SectionImageInformation, NULL, sizeof(info), &ret); + ok(status == STATUS_ACCESS_VIOLATION, "expected STATUS_ACCESS_VIOLATION, got %#x\n", status); + + status = pNtQuerySection(mapping, SectionImageInformation, &info, 0, NULL); + ok(status == STATUS_INFO_LENGTH_MISMATCH, "expected STATUS_INFO_LENGTH_MISMATCH, got %#x\n", status); + + status = pNtQuerySection(mapping, SectionImageInformation, &info, 0, &ret); + ok(status == STATUS_INFO_LENGTH_MISMATCH, "expected STATUS_INFO_LENGTH_MISMATCH, got %#x\n", status); + + status = pNtQuerySection(mapping, SectionImageInformation, &info, sizeof(info.basic), &ret); + ok(status == STATUS_INFO_LENGTH_MISMATCH, "expected STATUS_INFO_LENGTH_MISMATCH, got %#x\n", status); + + SetLastError(0xdeadbef); + p = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0); + ok(p != NULL, "MapViewOfFile error %u\n", GetLastError()); + + nt = image_nt_header(p); + + memset(&info, 0x55, sizeof(info)); + ret = 0xdeadbeef; + status = pNtQuerySection(mapping, SectionImageInformation, &info, sizeof(info), &ret); + ok(status == STATUS_SUCCESS, "NtQuerySection error %#x\n", status); + ok(ret == sizeof(info.image), "wrong returned size %u\n", ret); + ok((ULONG_PTR)info.image.TransferAddress == nt->OptionalHeader.ImageBase + nt->OptionalHeader.AddressOfEntryPoint, + "expected %#lx, got %p\n", (SIZE_T)(nt->OptionalHeader.ImageBase + nt->OptionalHeader.AddressOfEntryPoint), info.image.TransferAddress); + ok(info.image.ZeroBits == 0, "expected 0, got %#x\n", info.image.ZeroBits); +todo_wine + ok(info.image.MaximumStackSize == nt->OptionalHeader.SizeOfStackReserve, "expected %#lx, got %#lx\n", (SIZE_T)nt->OptionalHeader.SizeOfStackReserve, info.image.MaximumStackSize); +todo_wine + ok(info.image.CommittedStackSize == nt->OptionalHeader.SizeOfStackCommit, "expected %#lx, got %#lx\n", (SIZE_T)nt->OptionalHeader.SizeOfStackCommit, info.image.CommittedStackSize); + ok(info.image.SubSystemType == nt->OptionalHeader.Subsystem, "expected %#x, got %#x\n", nt->OptionalHeader.Subsystem, info.image.SubSystemType); + ok(info.image.SubsystemVersionLow == nt->OptionalHeader.MinorSubsystemVersion, "expected %#x, got %#x\n", nt->OptionalHeader.MinorSubsystemVersion, info.image.SubsystemVersionLow); + ok(info.image.SubsystemVersionHigh == nt->OptionalHeader.MajorSubsystemVersion, "expected %#x, got %#x\n", nt->OptionalHeader.MajorSubsystemVersion, info.image.SubsystemVersionHigh); + ok(info.image.ImageCharacteristics == nt->FileHeader.Characteristics, "expected %#x, got %#x\n", nt->FileHeader.Characteristics, info.image.ImageCharacteristics); + ok(info.image.DllCharacteristics == nt->OptionalHeader.DllCharacteristics, "expected %#x, got %#x\n", nt->OptionalHeader.DllCharacteristics, info.image.DllCharacteristics); + ok(info.image.Machine == nt->FileHeader.Machine, "expected %#x, got %#x\n", nt->FileHeader.Machine, info.image.Machine); + ok(info.image.ImageContainsCode == TRUE, "expected 1, got %#x\n", info.image.ImageContainsCode); + + memset(&info, 0x55, sizeof(info)); + ret = 0xdeadbeef; + status = pNtQuerySection(mapping, SectionBasicInformation, &info, sizeof(info), &ret); + ok(status == STATUS_SUCCESS, "NtQuerySection error %#x\n", status); + ok(ret == sizeof(info.basic), "wrong returned size %u\n", ret); + ok(info.basic.BaseAddress == NULL, "expected NULL, got %p\n", info.basic.BaseAddress); +todo_wine + ok(info.basic.Attributes == (SEC_FILE|SEC_IMAGE), "expected SEC_FILE|SEC_IMAGE, got %#x\n", info.basic.Attributes); + ok(info.basic.Size.QuadPart == image_size, "expected %#lx, got %#x/%08x\n", image_size, info.basic.Size.HighPart, info.basic.Size.LowPart); + + UnmapViewOfFile(p); + CloseHandle(mapping); + + SetLastError(0xdeadbef); + mapping = CreateFileMappingA(file, NULL, PAGE_READONLY|SEC_COMMIT|SEC_NOCACHE, 0, 0, NULL); + ok(mapping != 0, "CreateFileMapping error %u\n", GetLastError()); + + memset(&info, 0x55, sizeof(info)); + ret = 0xdeadbeef; + status = pNtQuerySection(mapping, SectionBasicInformation, &info, sizeof(info), &ret); + ok(status == STATUS_SUCCESS, "NtQuerySection error %#x\n", status); + ok(ret == sizeof(info.basic), "wrong returned size %u\n", ret); + ok(info.basic.BaseAddress == NULL, "expected NULL, got %p\n", info.basic.BaseAddress); +todo_wine + ok(info.basic.Attributes == SEC_FILE, "expected SEC_FILE, got %#x\n", info.basic.Attributes); +todo_wine + ok(info.basic.Size.QuadPart == fsize, "expected %#lx, got %#x/%08x\n", fsize, info.basic.Size.HighPart, info.basic.Size.LowPart); + + CloseHandle(mapping); + + SetLastError(0xdeadbef); + mapping = CreateFileMappingA(file, NULL, PAGE_READONLY|SEC_RESERVE, 0, 0, NULL); +todo_wine + ok(mapping != 0, "CreateFileMapping error %u\n", GetLastError()); + if (!mapping) goto skip1; + + memset(&info, 0x55, sizeof(info)); + ret = 0xdeadbeef; + status = pNtQuerySection(mapping, SectionBasicInformation, &info, sizeof(info), &ret); + ok(status == STATUS_SUCCESS, "NtQuerySection error %#x\n", status); + ok(ret == sizeof(info.basic), "wrong returned size %u\n", ret); + ok(info.basic.BaseAddress == NULL, "expected NULL, got %p\n", info.basic.BaseAddress); + ok(info.basic.Attributes == SEC_FILE, "expected SEC_FILE, got %#x\n", info.basic.Attributes); + ok(info.basic.Size.QuadPart == fsize, "expected %#lx, got %#x/%08x\n", fsize, info.basic.Size.HighPart, info.basic.Size.LowPart); + + CloseHandle(mapping); +skip1: + CloseHandle(file); + + SetLastError(0xdeadbef); + mapping = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE|SEC_COMMIT, 0, 4096, NULL); + ok(mapping != 0, "CreateFileMapping error %u\n", GetLastError()); + + memset(&info, 0x55, sizeof(info)); + ret = 0xdeadbeef; + status = pNtQuerySection(mapping, SectionBasicInformation, &info, sizeof(info), &ret); + ok(status == STATUS_SUCCESS, "NtQuerySection error %#x\n", status); + ok(ret == sizeof(info.basic), "wrong returned size %u\n", ret); + ok(info.basic.BaseAddress == NULL, "expected NULL, got %p\n", info.basic.BaseAddress); + ok(info.basic.Attributes == SEC_COMMIT, "expected SEC_COMMIT, got %#x\n", info.basic.Attributes); + ok(info.basic.Size.QuadPart == 4096, "expected 4096, got %#x/%08x\n", info.basic.Size.HighPart, info.basic.Size.LowPart); + + SetLastError(0xdeadbef); + p = MapViewOfFile(mapping, FILE_MAP_READ|FILE_MAP_WRITE, 0, 0, 0); + ok(p != NULL, "MapViewOfFile error %u\n", GetLastError()); + + memset(&info, 0x55, sizeof(info)); + ret = 0xdeadbeef; + status = pNtQuerySection(mapping, SectionBasicInformation, &info, sizeof(info), &ret); + ok(status == STATUS_SUCCESS, "NtQuerySection error %#x\n", status); + ok(ret == sizeof(info.basic), "wrong returned size %u\n", ret); + ok(info.basic.BaseAddress == NULL, "expected NULL, got %p\n", info.basic.BaseAddress); + ok(info.basic.Attributes == SEC_COMMIT, "expected SEC_COMMIT, got %#x\n", info.basic.Attributes); + ok(info.basic.Size.QuadPart == 4096, "expected 4096, got %#x/%08x\n", info.basic.Size.HighPart, info.basic.Size.LowPart); + + UnmapViewOfFile(p); + CloseHandle(mapping); + + SetLastError(0xdeadbef); + mapping = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READONLY|SEC_RESERVE, 0, 4096, NULL); + ok(mapping != 0, "CreateFileMapping error %u\n", GetLastError()); + + memset(&info, 0x55, sizeof(info)); + ret = 0xdeadbeef; + status = pNtQuerySection(mapping, SectionBasicInformation, &info, sizeof(info), &ret); + ok(status == STATUS_SUCCESS, "NtQuerySection error %#x\n", status); + ok(ret == sizeof(info.basic), "wrong returned size %u\n", ret); + ok(info.basic.BaseAddress == NULL, "expected NULL, got %p\n", info.basic.BaseAddress); + ok(info.basic.Attributes == SEC_RESERVE, "expected SEC_RESERVE, got %#x\n", info.basic.Attributes); + ok(info.basic.Size.QuadPart == 4096, "expected 4096, got %#x/%08x\n", info.basic.Size.HighPart, info.basic.Size.LowPart); + + CloseHandle(mapping); +} + START_TEST(virtual) { int argc; @@ -2580,20 +3719,27 @@ START_TEST(virtual) } hkernel32 = GetModuleHandleA("kernel32.dll"); + hntdll = GetModuleHandleA("ntdll.dll"); + pVirtualAllocEx = (void *) GetProcAddress(hkernel32, "VirtualAllocEx"); pVirtualFreeEx = (void *) GetProcAddress(hkernel32, "VirtualFreeEx"); pGetWriteWatch = (void *) GetProcAddress(hkernel32, "GetWriteWatch"); pResetWriteWatch = (void *) GetProcAddress(hkernel32, "ResetWriteWatch"); - pNtAreMappedFilesTheSame = (void *)GetProcAddress( GetModuleHandleA("ntdll.dll"), - "NtAreMappedFilesTheSame" ); - pNtMapViewOfSection = (void *)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtMapViewOfSection"); - pNtUnmapViewOfSection = (void *)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtUnmapViewOfSection"); + pGetProcessDEPPolicy = (void *)GetProcAddress( hkernel32, "GetProcessDEPPolicy" ); + pNtAreMappedFilesTheSame = (void *)GetProcAddress( hntdll, "NtAreMappedFilesTheSame" ); + pNtMapViewOfSection = (void *)GetProcAddress( hntdll, "NtMapViewOfSection" ); + pNtUnmapViewOfSection = (void *)GetProcAddress( hntdll, "NtUnmapViewOfSection" ); + pNtCurrentTeb = (void *)GetProcAddress( hntdll, "NtCurrentTeb" ); + pRtlAddVectoredExceptionHandler = (void *)GetProcAddress( hntdll, "RtlAddVectoredExceptionHandler" ); + pRtlRemoveVectoredExceptionHandler = (void *)GetProcAddress( hntdll, "RtlRemoveVectoredExceptionHandler" ); + pNtQuerySection = (void *)GetProcAddress( hntdll, "NtQuerySection" ); test_shared_memory(FALSE); test_shared_memory_ro(FALSE, FILE_MAP_READ|FILE_MAP_WRITE); test_shared_memory_ro(FALSE, FILE_MAP_COPY); test_shared_memory_ro(FALSE, FILE_MAP_COPY|FILE_MAP_WRITE); test_mapping(); + test_NtQuerySection(); test_CreateFileMapping_protection(); test_VirtualAlloc_protection(); test_VirtualProtect(); @@ -2607,4 +3753,19 @@ START_TEST(virtual) test_IsBadWritePtr(); test_IsBadCodePtr(); test_write_watch(); +#ifdef __i386__ + if (!winetest_interactive) + { + skip("ROSTESTS-155: Skipping virtual guard page tests due to Mm assertion failure.\n"); + } + else + { + test_guard_page(); + /* The following tests should be executed as a last step, and in exactly this + * order, since ATL thunk emulation cannot be enabled anymore on Windows. */ + test_atl_thunk_emulation( MEM_EXECUTE_OPTION_ENABLE ); + test_atl_thunk_emulation( MEM_EXECUTE_OPTION_DISABLE ); + test_atl_thunk_emulation( MEM_EXECUTE_OPTION_DISABLE | MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION ); + } +#endif }