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
}