[NTDLL_WINETEST]
authorAmine Khaldi <amine.khaldi@reactos.org>
Sun, 20 Apr 2014 13:44:55 +0000 (13:44 +0000)
committerAmine Khaldi <amine.khaldi@reactos.org>
Sun, 20 Apr 2014 13:44:55 +0000 (13:44 +0000)
* Sync with Wine 1.7.17.
CORE-8080

svn path=/trunk/; revision=62842

19 files changed:
rostests/winetests/ntdll/CMakeLists.txt
rostests/winetests/ntdll/atom.c
rostests/winetests/ntdll/change.c
rostests/winetests/ntdll/directory.c
rostests/winetests/ntdll/error.c
rostests/winetests/ntdll/exception.c
rostests/winetests/ntdll/file.c
rostests/winetests/ntdll/generated.c
rostests/winetests/ntdll/info.c
rostests/winetests/ntdll/om.c
rostests/winetests/ntdll/path.c
rostests/winetests/ntdll/pipe.c
rostests/winetests/ntdll/port.c
rostests/winetests/ntdll/reg.c
rostests/winetests/ntdll/rtl.c
rostests/winetests/ntdll/rtlstr.c
rostests/winetests/ntdll/string.c
rostests/winetests/ntdll/testlist.c
rostests/winetests/ntdll/time.c

index e9020f4..872874c 100644 (file)
@@ -1,7 +1,7 @@
 
 remove_definitions(-DWINVER=0x502 -D_WIN32_IE=0x600 -D_WIN32_WINNT=0x502)
 
-add_definitions(-D__ROS_LONG64__)
+add_definitions(-D__WINESRC__)
 
 list(APPEND SOURCE
     atom.c
index 1741f55..0fed630 100755 (executable)
@@ -25,7 +25,7 @@
 #include <stdarg.h>
 
 #include "ntstatus.h"
-/* Define WIN32_NO_STATUS so MSVC does not give us duplicate macro
+/* Define WIN32_NO_STATUS so MSVC does not give us duplicate macro 
  * definition errors when we get to winnt.h
  */
 #define WIN32_NO_STATUS
@@ -432,7 +432,7 @@ static void test_Global(void)
 {
     NTSTATUS    res;
     RTL_ATOM    atom;
-    ULONG       ptr[(sizeof(ATOM_BASIC_INFORMATION) + 255 * sizeof(WCHAR) + sizeof(ULONG)) / sizeof(ULONG)];
+    char        ptr[sizeof(ATOM_BASIC_INFORMATION) + 255 * sizeof(WCHAR)];
     ATOM_BASIC_INFORMATION*     abi = (ATOM_BASIC_INFORMATION*)ptr;
     ULONG       ptr_size = sizeof(ATOM_BASIC_INFORMATION) + 255 * sizeof(WCHAR);
 
@@ -445,7 +445,7 @@ static void test_Global(void)
 
     memset( ptr, 0xcc, sizeof(ptr) );
     res = pNtQueryInformationAtom( atom, AtomBasicInformation, (void*)ptr, ptr_size, NULL );
-    ok(!res, "atom lookup failed with status = 0x%x\n", res);
+    ok(!res, "atom lookup\n");
     ok(!lstrcmpW(abi->Name, testAtom1), "ok strings\n");
     ok(abi->NameLength == lstrlenW(testAtom1) * sizeof(WCHAR), "wrong string length\n");
     ok(abi->Name[lstrlenW(testAtom1)] == 0, "wrong string termination %x\n", abi->Name[lstrlenW(testAtom1)]);
index 297af9e..84e281c 100644 (file)
@@ -69,7 +69,7 @@ static void test_ntncdf(void)
                         OPEN_EXISTING, fflags, NULL);
     ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
 
-    hEvent = CreateEvent( NULL, 0, 0, NULL );
+    hEvent = CreateEventA( NULL, 0, 0, NULL );
 
     r = pNtNotifyChangeDirectoryFile(hdir,NULL,NULL,NULL,&iosb,NULL,0,0,0);
     ok(r==STATUS_INVALID_PARAMETER, "should return invalid parameter\n");
@@ -197,7 +197,7 @@ static void test_ntncdf_async(void)
                         OPEN_EXISTING, fflags, NULL);
     ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
 
-    hEvent = CreateEvent( NULL, 0, 0, NULL );
+    hEvent = CreateEventA( NULL, 0, 0, NULL );
 
     filter = FILE_NOTIFY_CHANGE_FILE_NAME;
     filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
@@ -312,7 +312,7 @@ static void test_ntncdf_async(void)
 
 START_TEST(change)
 {
-    HMODULE hntdll = GetModuleHandle("ntdll");
+    HMODULE hntdll = GetModuleHandleA("ntdll");
     if (!hntdll)
     {
         win_skip("not running on NT, skipping test\n");
index f495fb3..f190ff4 100644 (file)
@@ -120,11 +120,11 @@ static void tear_down_attribute_test(const char *testdirA)
             continue;
         sprintf(buf, "%s\\%s", testdirA, testfiles[i].name);
         if (testfiles[i].attr & FILE_ATTRIBUTE_DIRECTORY) {
-            ret = RemoveDirectory(buf);
+            ret = RemoveDirectoryA(buf);
             ok(ret || (GetLastError() == ERROR_PATH_NOT_FOUND),
                "Failed to rmdir %s, error %d\n", buf, GetLastError());
         } else {
-            ret = DeleteFile(buf);
+            ret = DeleteFileA(buf);
             ok(ret || (GetLastError() == ERROR_PATH_NOT_FOUND),
                "Failed to rm %s, error %d\n", buf, GetLastError());
         }
index d9c83b3..862f0a9 100755 (executable)
@@ -50,7 +50,7 @@
 static ULONG (WINAPI *pRtlNtStatusToDosError)(NTSTATUS Status);
 static int strict;
 
-static int prepare_test(void)
+static BOOL prepare_test(void)
 {
     HMODULE ntdll;
     int argc;
@@ -61,12 +61,12 @@ static int prepare_test(void)
     if (!pRtlNtStatusToDosError)
     {
         win_skip("RtlNtStatusToDosError is not available\n");
-        return 0;
+        return FALSE;
     }
 
     argc = winetest_get_mainargs(&argv);
     strict=(argc >= 3 && strcmp(argv[2],"strict")==0);
-    return 1;
+    return TRUE;
 }
 
 static void cmp_call(NTSTATUS win_nt, ULONG win32, const char* message)
index ac13805..1e45d82 100644 (file)
@@ -51,6 +51,13 @@ static NTSTATUS  (WINAPI *pNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS,
 static NTSTATUS  (WINAPI *pNtSetInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG);
 static BOOL      (WINAPI *pIsWow64Process)(HANDLE, PBOOL);
 
+#if defined(__x86_64__)
+static BOOLEAN   (CDECL *pRtlAddFunctionTable)(RUNTIME_FUNCTION*, DWORD, DWORD64);
+static BOOLEAN   (CDECL *pRtlDeleteFunctionTable)(RUNTIME_FUNCTION*);
+static BOOLEAN   (CDECL *pRtlInstallFunctionTableCallback)(DWORD64, DWORD64, DWORD, PGET_RUNTIME_FUNCTION_CALLBACK, PVOID, PCWSTR);
+static PRUNTIME_FUNCTION (WINAPI *pRtlLookupFunctionEntry)(ULONG64, ULONG64*, UNWIND_HISTORY_TABLE*);
+#endif
+
 #ifdef __i386__
 
 #ifndef __WINE_WINTRNL_H
@@ -314,7 +321,7 @@ static DWORD rtlraiseexception_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTR
         return ExceptionContinueSearch;
 
     /* Eip in context is decreased by 1
-     * Increase it again, else execution will continue in the middle of a instruction */
+     * Increase it again, else execution will continue in the middle of an instruction */
     if(rec->ExceptionCode == EXCEPTION_BREAKPOINT && (context->Eip == (DWORD)code_mem + 0xa))
         context->Eip += 1;
     return ExceptionContinueExecution;
@@ -731,7 +738,7 @@ static void test_debugger(void)
 {
     char cmdline[MAX_PATH];
     PROCESS_INFORMATION pi;
-    STARTUPINFO si = { 0 };
+    STARTUPINFOA si = { 0 };
     DEBUG_EVENT de;
     DWORD continuestatus;
     PVOID code_mem_address = NULL;
@@ -748,7 +755,7 @@ static void test_debugger(void)
     }
 
     sprintf(cmdline, "%s %s %s %p", my_argv[0], my_argv[1], "debuggee", &test_stage);
-    ret = CreateProcess(NULL, cmdline, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &si, &pi);
+    ret = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &si, &pi);
     ok(ret, "could not create child process error: %u\n", GetLastError());
     if (!ret)
         return;
@@ -796,7 +803,7 @@ static void test_debugger(void)
 
             if (counter > 100)
             {
-                ok(FALSE, "got way too many exceptions, probably caught in a infinite loop, terminating child\n");
+                ok(FALSE, "got way too many exceptions, probably caught in an infinite loop, terminating child\n");
                 pNtTerminateProcess(pi.hProcess, 1);
             }
             else if (counter >= 2) /* skip startup breakpoint */
@@ -1057,7 +1064,7 @@ static DWORD dpe_exception_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION
 
 static void test_dpe_exceptions(void)
 {
-    static char single_ret[] = {0xC3};
+    static const BYTE single_ret[] = {0xC3};
     struct dpe_exception_info info;
     NTSTATUS stat;
     BOOL has_hw_support;
@@ -1065,7 +1072,7 @@ static void test_dpe_exceptions(void)
     DWORD val;
     ULONG len;
 
-    /* Query DEP with len to small */
+    /* Query DEP with len too small */
     stat = pNtQueryInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val - 1, &len);
     if(stat == STATUS_INVALID_INFO_CLASS)
     {
@@ -1447,6 +1454,122 @@ static void test_virtual_unwind(void)
         call_virtual_unwind( i, &tests[i] );
 }
 
+static RUNTIME_FUNCTION* CALLBACK dynamic_unwind_callback( DWORD64 pc, PVOID context )
+{
+    static const int code_offset = 1024;
+    static RUNTIME_FUNCTION runtime_func;
+    (*(DWORD *)context)++;
+
+    runtime_func.BeginAddress = code_offset + 16;
+    runtime_func.EndAddress   = code_offset + 32;
+    runtime_func.UnwindData   = 0;
+    return &runtime_func;
+}
+
+static void test_dynamic_unwind(void)
+{
+    static const int code_offset = 1024;
+    char buf[sizeof(RUNTIME_FUNCTION) + 4];
+    RUNTIME_FUNCTION *runtime_func, *func;
+    ULONG_PTR table, base;
+    DWORD count;
+
+    /* Test RtlAddFunctionTable with aligned RUNTIME_FUNCTION pointer */
+    runtime_func = (RUNTIME_FUNCTION *)buf;
+    runtime_func->BeginAddress = code_offset;
+    runtime_func->EndAddress   = code_offset + 16;
+    runtime_func->UnwindData   = 0;
+    ok( pRtlAddFunctionTable( runtime_func, 1, (ULONG_PTR)code_mem ),
+        "RtlAddFunctionTable failed for runtime_func = %p (aligned)\n", runtime_func );
+
+    /* Lookup function outside of any function table */
+    base = 0xdeadbeef;
+    func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 16, &base, NULL );
+    ok( func == NULL,
+        "RtlLookupFunctionEntry returned unexpected function, expected: NULL, got: %p\n", func );
+    ok( base == 0xdeadbeef,
+        "RtlLookupFunctionEntry modified base address, expected: 0xdeadbeef, got: %lx\n", base );
+
+    /* Test with pointer inside of our function */
+    base = 0xdeadbeef;
+    func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 8, &base, NULL );
+    ok( func == runtime_func,
+        "RtlLookupFunctionEntry didn't return expected function, expected: %p, got: %p\n", runtime_func, func );
+    ok( base == (ULONG_PTR)code_mem,
+        "RtlLookupFunctionEntry returned invalid base, expected: %lx, got: %lx\n", (ULONG_PTR)code_mem, base );
+
+    /* Test RtlDeleteFunctionTable */
+    ok( pRtlDeleteFunctionTable( runtime_func ),
+        "RtlDeleteFunctionTable failed for runtime_func = %p (aligned)\n", runtime_func );
+    ok( !pRtlDeleteFunctionTable( runtime_func ),
+        "RtlDeleteFunctionTable returned success for nonexistent table runtime_func = %p\n", runtime_func );
+
+    /* Unaligned RUNTIME_FUNCTION pointer */
+    runtime_func = (RUNTIME_FUNCTION *)((ULONG_PTR)buf | 0x3);
+    runtime_func->BeginAddress = code_offset;
+    runtime_func->EndAddress   = code_offset + 16;
+    runtime_func->UnwindData   = 0;
+    ok( pRtlAddFunctionTable( runtime_func, 1, (ULONG_PTR)code_mem ),
+        "RtlAddFunctionTable failed for runtime_func = %p (unaligned)\n", runtime_func );
+    ok( pRtlDeleteFunctionTable( runtime_func ),
+        "RtlDeleteFunctionTable failed for runtime_func = %p (unaligned)\n", runtime_func );
+
+    /* Attempt to insert the same entry twice */
+    runtime_func = (RUNTIME_FUNCTION *)buf;
+    runtime_func->BeginAddress = code_offset;
+    runtime_func->EndAddress   = code_offset + 16;
+    runtime_func->UnwindData   = 0;
+    ok( pRtlAddFunctionTable( runtime_func, 1, (ULONG_PTR)code_mem ),
+        "RtlAddFunctionTable failed for runtime_func = %p (first attempt)\n", runtime_func );
+    ok( pRtlAddFunctionTable( runtime_func, 1, (ULONG_PTR)code_mem ),
+        "RtlAddFunctionTable failed for runtime_func = %p (second attempt)\n", runtime_func );
+    ok( pRtlDeleteFunctionTable( runtime_func ),
+        "RtlDeleteFunctionTable failed for runtime_func = %p (first attempt)\n", runtime_func );
+    ok( pRtlDeleteFunctionTable( runtime_func ),
+        "RtlDeleteFunctionTable failed for runtime_func = %p (second attempt)\n", runtime_func );
+    ok( !pRtlDeleteFunctionTable( runtime_func ),
+        "RtlDeleteFunctionTable returned success for nonexistent table runtime_func = %p\n", runtime_func );
+
+    /* Test RtlInstallFunctionTableCallback with both low bits unset */
+    table = (ULONG_PTR)code_mem;
+    ok( !pRtlInstallFunctionTableCallback( table, (ULONG_PTR)code_mem, code_offset + 32, &dynamic_unwind_callback, (PVOID*)&count, NULL ),
+        "RtlInstallFunctionTableCallback returned success for table = %lx\n", table );
+
+    /* Test RtlInstallFunctionTableCallback with both low bits set */
+    table = (ULONG_PTR)code_mem | 0x3;
+    ok( pRtlInstallFunctionTableCallback( table, (ULONG_PTR)code_mem, code_offset + 32, &dynamic_unwind_callback, (PVOID*)&count, NULL ),
+        "RtlInstallFunctionTableCallback failed for table = %lx\n", table );
+
+    /* Lookup function outside of any function table */
+    count = 0;
+    base = 0xdeadbeef;
+    func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 32, &base, NULL );
+    ok( func == NULL,
+        "RtlLookupFunctionEntry returned unexpected function, expected: NULL, got: %p\n", func );
+    ok( base == 0xdeadbeef,
+        "RtlLookupFunctionEntry modified base address, expected: 0xdeadbeef, got: %lx\n", base );
+    ok( !count,
+        "RtlLookupFunctionEntry issued %d unexpected calls to dynamic_unwind_callback\n", count );
+
+    /* Test with pointer inside of our function */
+    count = 0;
+    base = 0xdeadbeef;
+    func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 24, &base, NULL );
+    ok( func != NULL && func->BeginAddress == code_offset + 16 && func->EndAddress == code_offset + 32,
+        "RtlLookupFunctionEntry didn't return expected function, got: %p\n", func );
+    ok( base == (ULONG_PTR)code_mem,
+        "RtlLookupFunctionEntry returned invalid base, expected: %lx, got: %lx\n", (ULONG_PTR)code_mem, base );
+    ok( count == 1,
+        "RtlLookupFunctionEntry issued %d calls to dynamic_unwind_callback, expected: 1\n", count );
+
+    /* Clean up again */
+    ok( pRtlDeleteFunctionTable( (PRUNTIME_FUNCTION)table ),
+        "RtlDeleteFunctionTable failed for table = %p\n", (PVOID)table );
+    ok( !pRtlDeleteFunctionTable( (PRUNTIME_FUNCTION)table ),
+        "RtlDeleteFunctionTable returned success for nonexistent table = %p\n", (PVOID)table );
+
+}
+
 #endif  /* __x86_64__ */
 
 START_TEST(exception)
@@ -1473,7 +1596,7 @@ START_TEST(exception)
                                                                  "NtQueryInformationProcess" );
     pNtSetInformationProcess           = (void*)GetProcAddress( hntdll,
                                                                  "NtSetInformationProcess" );
-    pIsWow64Process = (void *)GetProcAddress(GetModuleHandle("kernel32.dll"), "IsWow64Process");
+    pIsWow64Process = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "IsWow64Process");
 
 #ifdef __i386__
     if (!pNtCurrentTeb)
@@ -1534,9 +1657,22 @@ START_TEST(exception)
     test_dpe_exceptions();
 
 #elif defined(__x86_64__)
+    pRtlAddFunctionTable               = (void *)GetProcAddress( hntdll,
+                                                                 "RtlAddFunctionTable" );
+    pRtlDeleteFunctionTable            = (void *)GetProcAddress( hntdll,
+                                                                 "RtlDeleteFunctionTable" );
+    pRtlInstallFunctionTableCallback   = (void *)GetProcAddress( hntdll,
+                                                                 "RtlInstallFunctionTableCallback" );
+    pRtlLookupFunctionEntry            = (void *)GetProcAddress( hntdll,
+                                                                 "RtlLookupFunctionEntry" );
 
     test_virtual_unwind();
 
+    if (pRtlAddFunctionTable && pRtlDeleteFunctionTable && pRtlInstallFunctionTableCallback && pRtlLookupFunctionEntry)
+      test_dynamic_unwind();
+    else
+      skip( "Dynamic unwind functions not found\n" );
+
 #endif
 
     VirtualFree(code_mem, 0, MEM_FREE);
index ea152d1..de64a85 100644 (file)
@@ -89,7 +89,7 @@ static inline BOOL is_signaled( HANDLE obj )
 
 static BOOL create_pipe( HANDLE *read, HANDLE *write, ULONG flags, ULONG size )
 {
-    *read = CreateNamedPipe(PIPENAME, PIPE_ACCESS_INBOUND | flags, PIPE_TYPE_BYTE | PIPE_WAIT,
+    *read = CreateNamedPipeA(PIPENAME, PIPE_ACCESS_INBOUND | flags, PIPE_TYPE_BYTE | PIPE_WAIT,
                             1, size, size, NMPWAIT_USE_DEFAULT_WAIT, NULL);
     ok(*read != INVALID_HANDLE_VALUE, "CreateNamedPipe failed\n");
 
@@ -782,8 +782,7 @@ static void read_file_test(void)
     iosb.Information = 0xdeadbeef;
     offset.QuadPart = strlen(text) + 2;
     status = pNtReadFile( handle, event, apc, &apc_count, &iosb, buffer, 2, &offset, NULL );
-todo_wine
-    ok(status == STATUS_PENDING || broken(status == STATUS_END_OF_FILE) /* before Vista */, "expected STATUS_PENDING, got %#x\n", status);
+    ok(status == STATUS_PENDING || status == STATUS_END_OF_FILE /* before Vista */, "expected STATUS_PENDING or STATUS_END_OF_FILE, got %#x\n", status);
     if (status == STATUS_PENDING)  /* vista */
     {
         WaitForSingleObject( event, 1000 );
@@ -837,9 +836,9 @@ todo_wine
     ResetEvent( event );
     status = pNtReadFile( handle, event, apc, &apc_count, &iosb, buffer, 2, &offset, NULL );
     ok( status == STATUS_END_OF_FILE, "wrong status %x\n", status );
-    todo_wine ok( U(iosb).Status == STATUS_END_OF_FILE, "wrong status %x\n", U(iosb).Status );
-    todo_wine ok( iosb.Information == 0, "wrong info %lu\n", iosb.Information );
-    todo_wine ok( is_signaled( event ), "event is not signaled\n" );
+    ok( U(iosb).Status == STATUS_END_OF_FILE, "wrong status %x\n", U(iosb).Status );
+    ok( iosb.Information == 0, "wrong info %lu\n", iosb.Information );
+    ok( is_signaled( event ), "event is not signaled\n" );
     ok( !apc_count, "apc was called\n" );
     SleepEx( 1, TRUE ); /* alertable sleep */
     ok( !apc_count, "apc was called\n" );
@@ -851,43 +850,93 @@ todo_wine
 
 static void append_file_test(void)
 {
-    const char text[] = "foobar";
+    static const char text[6] = "foobar";
     HANDLE handle;
     NTSTATUS status;
     IO_STATUS_BLOCK iosb;
-    DWORD written;
-    char path[MAX_PATH], buffer[MAX_PATH];
+    LARGE_INTEGER offset;
+    char path[MAX_PATH], buffer[MAX_PATH], buf[16];
+    DWORD ret;
 
     GetTempPathA( MAX_PATH, path );
     GetTempFileNameA( path, "foo", 0, buffer );
+
+    handle = CreateFileA(buffer, FILE_WRITE_DATA, 0, NULL, CREATE_ALWAYS, 0, 0);
+    ok(handle != INVALID_HANDLE_VALUE, "CreateFile error %d\n", GetLastError());
+
+    U(iosb).Status = -1;
+    iosb.Information = -1;
+    status = pNtWriteFile(handle, NULL, NULL, NULL, &iosb, text, 2, NULL, NULL);
+    ok(status == STATUS_SUCCESS, "NtWriteFile error %#x\n", status);
+    ok(U(iosb).Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", U(iosb).Status);
+    ok(iosb.Information == 2, "expected 2, got %lu\n", iosb.Information);
+
+    CloseHandle(handle);
+
     /* It is possible to open a file with only FILE_APPEND_DATA access flags.
        It matches the O_WRONLY|O_APPEND open() posix behavior */
-    handle = CreateFileA(buffer, FILE_APPEND_DATA, 0, NULL, CREATE_ALWAYS,
-                         FILE_FLAG_DELETE_ON_CLOSE, 0);
-    ok( handle != INVALID_HANDLE_VALUE, "Failed to create a temp file in FILE_APPEND_DATA mode.\n" );
-    if(handle == INVALID_HANDLE_VALUE)
-    {
-        skip("Couldn't create a temporary file, skipping FILE_APPEND_DATA test\n");
-        return;
-    }
+    handle = CreateFileA(buffer, FILE_APPEND_DATA, 0, NULL, OPEN_EXISTING, 0, 0);
+    ok(handle != INVALID_HANDLE_VALUE, "CreateFile error %d\n", GetLastError());
 
-    U(iosb).Status = STATUS_PENDING;
-    iosb.Information = 0;
+    U(iosb).Status = -1;
+    iosb.Information = -1;
+    offset.QuadPart = 1;
+    status = pNtWriteFile(handle, NULL, NULL, NULL, &iosb, text + 2, 2, &offset, NULL);
+    ok(status == STATUS_SUCCESS, "NtWriteFile error %#x\n", status);
+    ok(U(iosb).Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", U(iosb).Status);
+    ok(iosb.Information == 2, "expected 2, got %lu\n", iosb.Information);
 
-    status = pNtWriteFile(handle, NULL, NULL, NULL, &iosb,
-                          text, sizeof(text), NULL, NULL);
+    ret = SetFilePointer(handle, 0, NULL, FILE_CURRENT);
+    ok(ret == 4, "expected 4, got %u\n", ret);
 
-    if (status == STATUS_PENDING)
-    {
-        WaitForSingleObject( handle, 1000 );
-        status = U(iosb).Status;
-    }
-    written = iosb.Information;
+    U(iosb).Status = -1;
+    iosb.Information = -1;
+    offset.QuadPart = 3;
+    status = pNtWriteFile(handle, NULL, NULL, NULL, &iosb, text + 4, 2, &offset, NULL);
+    ok(status == STATUS_SUCCESS, "NtWriteFile error %#x\n", status);
+    ok(U(iosb).Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", U(iosb).Status);
+    ok(iosb.Information == 2, "expected 2, got %lu\n", iosb.Information);
 
-    todo_wine
-    ok(status == STATUS_SUCCESS && written == sizeof(text), "FILE_APPEND_DATA NtWriteFile failed\n");
+    ret = SetFilePointer(handle, 0, NULL, FILE_CURRENT);
+    ok(ret == 6, "expected 6, got %u\n", ret);
+
+    CloseHandle(handle);
+
+    handle = CreateFileA(buffer, FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA, 0, NULL, OPEN_EXISTING, 0, 0);
+    ok(handle != INVALID_HANDLE_VALUE, "CreateFile error %d\n", GetLastError());
+
+    memset(buf, 0, sizeof(buf));
+    U(iosb).Status = -1;
+    iosb.Information = -1;
+    offset.QuadPart = 0;
+    status = pNtReadFile(handle, 0, NULL, NULL, &iosb, buf, sizeof(buf), &offset, NULL);
+    ok(status == STATUS_SUCCESS, "NtReadFile error %#x\n", status);
+    ok(U(iosb).Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", U(iosb).Status);
+    ok(iosb.Information == 6, "expected 6, got %lu\n", iosb.Information);
+    buf[6] = 0;
+    ok(memcmp(buf, text, 6) == 0, "wrong file contents: %s\n", buf);
+
+    U(iosb).Status = -1;
+    iosb.Information = -1;
+    offset.QuadPart = 0;
+    status = pNtWriteFile(handle, NULL, NULL, NULL, &iosb, text + 3, 3, &offset, NULL);
+    ok(status == STATUS_SUCCESS, "NtWriteFile error %#x\n", status);
+    ok(U(iosb).Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", U(iosb).Status);
+    ok(iosb.Information == 3, "expected 3, got %lu\n", iosb.Information);
+
+    memset(buf, 0, sizeof(buf));
+    U(iosb).Status = -1;
+    iosb.Information = -1;
+    offset.QuadPart = 0;
+    status = pNtReadFile(handle, 0, NULL, NULL, &iosb, buf, sizeof(buf), &offset, NULL);
+    ok(status == STATUS_SUCCESS, "NtReadFile error %#x\n", status);
+    ok(U(iosb).Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", U(iosb).Status);
+    ok(iosb.Information == 6, "expected 6, got %lu\n", iosb.Information);
+    buf[6] = 0;
+    ok(memcmp(buf, "barbar", 6) == 0, "wrong file contents: %s\n", buf);
 
     CloseHandle(handle);
+    DeleteFileA(buffer);
 }
 
 static void nt_mailslot_test(void)
@@ -1302,7 +1351,7 @@ static void test_file_both_information(void)
 
 static void test_file_disposition_information(void)
 {
-    char buffer[MAX_PATH + 16];
+    char tmp_path[MAX_PATH], buffer[MAX_PATH + 16];
     DWORD dirpos;
     HANDLE handle, handle2;
     NTSTATUS res;
@@ -1310,8 +1359,10 @@ static void test_file_disposition_information(void)
     FILE_DISPOSITION_INFORMATION fdi;
     BOOL fileDeleted;
 
+    GetTempPathA( MAX_PATH, tmp_path );
+
     /* cannot set disposition on file not opened with delete access */
-    GetTempFileNameA( ".", "dis", 0, buffer );
+    GetTempFileNameA( tmp_path, "dis", 0, buffer );
     handle = CreateFileA(buffer, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
     ok( handle != INVALID_HANDLE_VALUE, "failed to create temp file\n" );
     res = pNtQueryInformationFile( handle, &io, &fdi, sizeof fdi, FileDispositionInformation );
@@ -1326,7 +1377,7 @@ static void test_file_disposition_information(void)
     DeleteFileA( buffer );
 
     /* can set disposition on file opened with proper access */
-    GetTempFileNameA( ".", "dis", 0, buffer );
+    GetTempFileNameA( tmp_path, "dis", 0, buffer );
     handle = CreateFileA(buffer, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0);
     ok( handle != INVALID_HANDLE_VALUE, "failed to create temp file\n" );
     fdi.DoDeleteFile = TRUE;
@@ -1340,7 +1391,7 @@ static void test_file_disposition_information(void)
     DeleteFileA( buffer );
 
     /* cannot set disposition on readonly file */
-    GetTempFileNameA( ".", "dis", 0, buffer );
+    GetTempFileNameA( tmp_path, "dis", 0, buffer );
     handle = CreateFileA(buffer, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_READONLY, 0);
     ok( handle != INVALID_HANDLE_VALUE, "failed to create temp file\n" );
     fdi.DoDeleteFile = TRUE;
@@ -1354,7 +1405,7 @@ static void test_file_disposition_information(void)
     DeleteFileA( buffer );
 
     /* can set disposition on file and then reset it */
-    GetTempFileNameA( ".", "dis", 0, buffer );
+    GetTempFileNameA( tmp_path, "dis", 0, buffer );
     handle = CreateFileA(buffer, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0);
     ok( handle != INVALID_HANDLE_VALUE, "failed to create temp file\n" );
     fdi.DoDeleteFile = TRUE;
@@ -1371,7 +1422,7 @@ static void test_file_disposition_information(void)
     DeleteFileA( buffer );
 
     /* Delete-on-close flag doesn't change file disposition until a handle is closed */
-    GetTempFileNameA( ".", "dis", 0, buffer );
+    GetTempFileNameA( tmp_path, "dis", 0, buffer );
     handle = CreateFileA(buffer, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, 0);
     ok( handle != INVALID_HANDLE_VALUE, "failed to create temp file\n" );
     fdi.DoDeleteFile = FALSE;
@@ -1384,7 +1435,7 @@ static void test_file_disposition_information(void)
     DeleteFileA( buffer );
 
     /* Delete-on-close flag sets disposition when a handle is closed and then it could be changed back */
-    GetTempFileNameA( ".", "dis", 0, buffer );
+    GetTempFileNameA( tmp_path, "dis", 0, buffer );
     handle = CreateFileA(buffer, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, 0);
     ok( handle != INVALID_HANDLE_VALUE, "failed to create temp file\n" );
     ok( DuplicateHandle( GetCurrentProcess(), handle, GetCurrentProcess(), &handle2, 0, FALSE, DUPLICATE_SAME_ACCESS ), "DuplicateHandle failed\n" );
@@ -1399,7 +1450,7 @@ static void test_file_disposition_information(void)
     DeleteFileA( buffer );
 
     /* can set disposition on a directory opened with proper access */
-    GetTempFileNameA( ".", "dis", 0, buffer );
+    GetTempFileNameA( tmp_path, "dis", 0, buffer );
     DeleteFileA( buffer );
     ok( CreateDirectoryA( buffer, NULL ), "CreateDirectory failed\n" );
     handle = CreateFileA(buffer, DELETE, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
@@ -1415,7 +1466,7 @@ static void test_file_disposition_information(void)
     RemoveDirectoryA( buffer );
 
     /* RemoveDirectory sets directory disposition and it can be undone */
-    GetTempFileNameA( ".", "dis", 0, buffer );
+    GetTempFileNameA( tmp_path, "dis", 0, buffer );
     DeleteFileA( buffer );
     ok( CreateDirectoryA( buffer, NULL ), "CreateDirectory failed\n" );
     handle = CreateFileA(buffer, DELETE, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
@@ -1431,7 +1482,7 @@ static void test_file_disposition_information(void)
     RemoveDirectoryA( buffer );
 
     /* cannot set disposition on a non-empty directory */
-    GetTempFileNameA( ".", "dis", 0, buffer );
+    GetTempFileNameA( tmp_path, "dis", 0, buffer );
     DeleteFileA( buffer );
     ok( CreateDirectoryA( buffer, NULL ), "CreateDirectory failed\n" );
     handle = CreateFileA(buffer, DELETE, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
@@ -1921,53 +1972,81 @@ static void test_NtCreateFile(void)
 
 static void test_read_write(void)
 {
-    static const char contents[] = "1234567890abcd";
+    static const char contents[14] = "1234567890abcd";
     char buf[256];
     HANDLE hfile;
     OVERLAPPED ovl;
     IO_STATUS_BLOCK iob;
-    DWORD ret, bytes, status;
+    DWORD ret, bytes, status, off;
     LARGE_INTEGER offset;
+    LONG i;
 
-    iob.Status = -1;
+    U(iob).Status = -1;
     iob.Information = -1;
     offset.QuadPart = 0;
     status = pNtReadFile(INVALID_HANDLE_VALUE, 0, NULL, NULL, &iob, buf, sizeof(buf), &offset, NULL);
     ok(status == STATUS_OBJECT_TYPE_MISMATCH || status == STATUS_INVALID_HANDLE, "expected STATUS_OBJECT_TYPE_MISMATCH, got %#x\n", status);
-    ok(iob.Status == -1, "expected -1, got %#x\n", iob.Status);
+    ok(U(iob).Status == -1, "expected -1, got %#x\n", U(iob).Status);
     ok(iob.Information == -1, "expected -1, got %lu\n", iob.Information);
 
-    iob.Status = -1;
+    U(iob).Status = -1;
     iob.Information = -1;
     offset.QuadPart = 0;
     status = pNtWriteFile(INVALID_HANDLE_VALUE, 0, NULL, NULL, &iob, buf, sizeof(buf), &offset, NULL);
     ok(status == STATUS_OBJECT_TYPE_MISMATCH || status == STATUS_INVALID_HANDLE, "expected STATUS_OBJECT_TYPE_MISMATCH, got %#x\n", status);
-    ok(iob.Status == -1, "expected -1, got %#x\n", iob.Status);
+    ok(U(iob).Status == -1, "expected -1, got %#x\n", U(iob).Status);
     ok(iob.Information == -1, "expected -1, got %lu\n", iob.Information);
 
     hfile = create_temp_file(0);
     if (!hfile) return;
 
-    iob.Status = -1;
+    U(iob).Status = -1;
     iob.Information = -1;
     status = pNtWriteFile(hfile, 0, NULL, NULL, &iob, NULL, sizeof(contents), NULL, NULL);
     ok(status == STATUS_INVALID_USER_BUFFER, "expected STATUS_INVALID_USER_BUFFER, got %#x\n", status);
-    ok(iob.Status == -1, "expected -1, got %#x\n", iob.Status);
+    ok(U(iob).Status == -1, "expected -1, got %#x\n", U(iob).Status);
     ok(iob.Information == -1, "expected -1, got %lu\n", iob.Information);
 
-    iob.Status = -1;
+    U(iob).Status = -1;
     iob.Information = -1;
     status = pNtReadFile(hfile, 0, NULL, NULL, &iob, NULL, sizeof(contents), NULL, NULL);
     ok(status == STATUS_ACCESS_VIOLATION, "expected STATUS_ACCESS_VIOLATION, got %#x\n", status);
-    ok(iob.Status == -1, "expected -1, got %#x\n", iob.Status);
+    ok(U(iob).Status == -1, "expected -1, got %#x\n", U(iob).Status);
     ok(iob.Information == -1, "expected -1, got %lu\n", iob.Information);
 
-    iob.Status = -1;
+    U(iob).Status = -1;
     iob.Information = -1;
-    status = pNtWriteFile(hfile, 0, NULL, NULL, &iob, contents, sizeof(contents), NULL, NULL);
+    status = pNtWriteFile(hfile, 0, NULL, NULL, &iob, contents, 7, NULL, NULL);
     ok(status == STATUS_SUCCESS, "NtWriteFile error %#x\n", status);
-    ok(iob.Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", iob.Status);
-    ok(iob.Information == sizeof(contents), "expected sizeof(contents), got %lu\n", iob.Information);
+    ok(U(iob).Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", U(iob).Status);
+    ok(iob.Information == 7, "expected 7, got %lu\n", iob.Information);
+
+    SetFilePointer(hfile, 0, NULL, FILE_BEGIN);
+
+    U(iob).Status = -1;
+    iob.Information = -1;
+    offset.QuadPart = (LONGLONG)-1 /* FILE_WRITE_TO_END_OF_FILE */;
+    status = pNtWriteFile(hfile, 0, NULL, NULL, &iob, contents + 7, sizeof(contents) - 7, &offset, NULL);
+    ok(status == STATUS_SUCCESS, "NtWriteFile error %#x\n", status);
+    ok(U(iob).Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", U(iob).Status);
+    ok(iob.Information == sizeof(contents) - 7, "expected sizeof(contents)-7, got %lu\n", iob.Information);
+
+    off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT);
+    ok(off == sizeof(contents), "expected sizeof(contents), got %u\n", off);
+
+    bytes = 0xdeadbeef;
+    SetLastError(0xdeadbeef);
+    ret = ReadFile(INVALID_HANDLE_VALUE, buf, 0, &bytes, NULL);
+    ok(!ret, "ReadFile should fail\n");
+    ok(GetLastError() == ERROR_INVALID_HANDLE, "expected ERROR_INVALID_HANDLE, got %d\n", GetLastError());
+    ok(bytes == 0, "bytes %u\n", bytes);
+
+    bytes = 0xdeadbeef;
+    SetLastError(0xdeadbeef);
+    ret = ReadFile(hfile, buf, 0, &bytes, NULL);
+    ok(ret, "ReadFile error %d\n", GetLastError());
+    ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %d\n", GetLastError());
+    ok(bytes == 0, "bytes %u\n", bytes);
 
     bytes = 0xdeadbeef;
     SetLastError(0xdeadbeef);
@@ -1985,16 +2064,39 @@ static void test_read_write(void)
     ok(bytes == sizeof(contents), "bytes %u\n", bytes);
     ok(!memcmp(contents, buf, sizeof(contents)), "file contents mismatch\n");
 
+    for (i = -20; i < -1; i++)
+    {
+        if (i == -2) continue;
+
+        U(iob).Status = -1;
+        iob.Information = -1;
+        offset.QuadPart = (LONGLONG)i;
+        status = pNtWriteFile(hfile, 0, NULL, NULL, &iob, contents, sizeof(contents), &offset, NULL);
+        ok(status == STATUS_INVALID_PARAMETER, "%d: expected STATUS_INVALID_PARAMETER, got %#x\n", i, status);
+        ok(U(iob).Status == -1, "expected -1, got %#x\n", U(iob).Status);
+        ok(iob.Information == -1, "expected -1, got %ld\n", iob.Information);
+    }
+
     SetFilePointer(hfile, sizeof(contents) - 4, NULL, FILE_BEGIN);
 
-    iob.Status = -1;
+    U(iob).Status = -1;
     iob.Information = -1;
     offset.QuadPart = (LONGLONG)-2 /* FILE_USE_FILE_POINTER_POSITION */;
     status = pNtWriteFile(hfile, 0, NULL, NULL, &iob, "DCBA", 4, &offset, NULL);
     ok(status == STATUS_SUCCESS, "NtWriteFile error %#x\n", status);
-    ok(iob.Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", iob.Status);
+    ok(U(iob).Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", U(iob).Status);
     ok(iob.Information == 4, "expected 4, got %lu\n", iob.Information);
 
+    off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT);
+    ok(off == sizeof(contents), "expected sizeof(contents), got %u\n", off);
+
+    U(iob).Status = -1;
+    iob.Information = -1;
+    status = pNtReadFile(hfile, 0, NULL, NULL, &iob, buf, sizeof(buf), NULL, NULL);
+    ok(status == STATUS_END_OF_FILE, "expected STATUS_END_OF_FILE, got %#x\n", status);
+    ok(U(iob).Status == STATUS_END_OF_FILE, "expected STATUS_END_OF_FILE, got %#x\n", U(iob).Status);
+    ok(iob.Information == 0, "expected 0, got %lu\n", iob.Information);
+
     SetFilePointer(hfile, 0, NULL, FILE_BEGIN);
 
     bytes = 0;
@@ -2005,6 +2107,9 @@ static void test_read_write(void)
     ok(!memcmp(contents, buf, sizeof(contents) - 4), "file contents mismatch\n");
     ok(!memcmp(buf + sizeof(contents) - 4, "DCBA", 4), "file contents mismatch\n");
 
+    off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT);
+    ok(off == sizeof(contents), "expected sizeof(contents), got %u\n", off);
+
     SetFilePointer(hfile, 0, NULL, FILE_BEGIN);
 
     bytes = 0;
@@ -2013,25 +2118,118 @@ static void test_read_write(void)
     ok(ret, "WriteFile error %d\n", GetLastError());
     ok(bytes == sizeof(contents), "bytes %u\n", bytes);
 
-    iob.Status = -1;
+    off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT);
+    ok(off == sizeof(contents), "expected sizeof(contents), got %u\n", off);
+
+    /* test reading beyond EOF */
+    bytes = -1;
+    SetLastError(0xdeadbeef);
+    ret = ReadFile(hfile, buf, sizeof(buf), &bytes, NULL);
+    ok(ret, "ReadFile error %d\n", GetLastError());
+    ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %d\n", GetLastError());
+    ok(bytes == 0, "bytes %u\n", bytes);
+
+    bytes = -1;
+    SetLastError(0xdeadbeef);
+    ret = ReadFile(hfile, buf, 0, &bytes, NULL);
+    ok(ret, "ReadFile error %d\n", GetLastError());
+    ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %d\n", GetLastError());
+    ok(bytes == 0, "bytes %u\n", bytes);
+
+    bytes = -1;
+    SetLastError(0xdeadbeef);
+    ret = ReadFile(hfile, NULL, 0, &bytes, NULL);
+    ok(ret, "ReadFile error %d\n", GetLastError());
+    ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %d\n", GetLastError());
+    ok(bytes == 0, "bytes %u\n", bytes);
+
+    S(U(ovl)).Offset = sizeof(contents);
+    S(U(ovl)).OffsetHigh = 0;
+    ovl.Internal = -1;
+    ovl.InternalHigh = -1;
+    ovl.hEvent = 0;
+    bytes = -1;
+    SetLastError(0xdeadbeef);
+    ret = ReadFile(hfile, buf, sizeof(buf), &bytes, &ovl);
+    ok(!ret, "ReadFile should fail\n");
+    ok(GetLastError() == ERROR_HANDLE_EOF, "expected ERROR_HANDLE_EOF, got %d\n", GetLastError());
+    ok(bytes == 0, "bytes %u\n", bytes);
+    ok((NTSTATUS)ovl.Internal == STATUS_END_OF_FILE, "expected STATUS_END_OF_FILE, got %#lx\n", ovl.Internal);
+    ok(ovl.InternalHigh == 0, "expected 0, got %lu\n", ovl.InternalHigh);
+
+    S(U(ovl)).Offset = sizeof(contents);
+    S(U(ovl)).OffsetHigh = 0;
+    ovl.Internal = -1;
+    ovl.InternalHigh = -1;
+    ovl.hEvent = 0;
+    bytes = -1;
+    SetLastError(0xdeadbeef);
+    ret = ReadFile(hfile, buf, 0, &bytes, &ovl);
+    ok(ret, "ReadFile error %d\n", GetLastError());
+    ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %d\n", GetLastError());
+    ok(bytes == 0, "bytes %u\n", bytes);
+    ok((NTSTATUS)ovl.Internal == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx\n", ovl.Internal);
+    ok(ovl.InternalHigh == 0, "expected 0, got %lu\n", ovl.InternalHigh);
+
+    U(iob).Status = -1;
     iob.Information = -1;
     status = pNtReadFile(hfile, 0, NULL, NULL, &iob, buf, sizeof(buf), NULL, NULL);
     ok(status == STATUS_END_OF_FILE, "expected STATUS_END_OF_FILE, got %#x\n", status);
-todo_wine
-    ok(iob.Status == STATUS_END_OF_FILE, "expected STATUS_END_OF_FILE, got %#x\n", iob.Status);
-todo_wine
+    ok(U(iob).Status == STATUS_END_OF_FILE, "expected STATUS_END_OF_FILE, got %#x\n", U(iob).Status);
     ok(iob.Information == 0, "expected 0, got %lu\n", iob.Information);
 
-    iob.Status = -1;
+    U(iob).Status = -1;
+    iob.Information = -1;
+    status = pNtReadFile(hfile, 0, NULL, NULL, &iob, buf, 0, NULL, NULL);
+    ok(status == STATUS_SUCCESS, "NtReadFile error %#x\n", status);
+    ok(U(iob).Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", U(iob).Status);
+    ok(iob.Information == 0, "expected 0, got %lu\n", iob.Information);
+
+    U(iob).Status = -1;
+    iob.Information = -1;
+    offset.QuadPart = sizeof(contents);
+    status = pNtReadFile(hfile, 0, NULL, NULL, &iob, buf, sizeof(buf), &offset, NULL);
+    ok(status == STATUS_END_OF_FILE, "expected STATUS_END_OF_FILE, got %#x\n", status);
+    ok(U(iob).Status == STATUS_END_OF_FILE, "expected STATUS_END_OF_FILE, got %#x\n", U(iob).Status);
+    ok(iob.Information == 0, "expected 0, got %lu\n", iob.Information);
+
+    U(iob).Status = -1;
+    iob.Information = -1;
+    offset.QuadPart = sizeof(contents);
+    status = pNtReadFile(hfile, 0, NULL, NULL, &iob, buf, 0, &offset, NULL);
+    ok(status == STATUS_SUCCESS, "NtReadFile error %#x\n", status);
+    ok(U(iob).Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", U(iob).Status);
+    ok(iob.Information == 0, "expected 0, got %lu\n", iob.Information);
+
+    U(iob).Status = -1;
     iob.Information = -1;
     offset.QuadPart = (LONGLONG)-2 /* FILE_USE_FILE_POINTER_POSITION */;
     status = pNtReadFile(hfile, 0, NULL, NULL, &iob, buf, sizeof(buf), &offset, NULL);
     ok(status == STATUS_END_OF_FILE, "expected STATUS_END_OF_FILE, got %#x\n", status);
-todo_wine
-    ok(iob.Status == STATUS_END_OF_FILE, "expected STATUS_END_OF_FILE, got %#x\n", iob.Status);
-todo_wine
+    ok(U(iob).Status == STATUS_END_OF_FILE, "expected STATUS_END_OF_FILE, got %#x\n", U(iob).Status);
     ok(iob.Information == 0, "expected 0, got %lu\n", iob.Information);
 
+    U(iob).Status = -1;
+    iob.Information = -1;
+    offset.QuadPart = (LONGLONG)-2 /* FILE_USE_FILE_POINTER_POSITION */;
+    status = pNtReadFile(hfile, 0, NULL, NULL, &iob, buf, 0, &offset, NULL);
+    ok(status == STATUS_SUCCESS, "NtReadFile error %#x\n", status);
+    ok(U(iob).Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", U(iob).Status);
+    ok(iob.Information == 0, "expected 0, got %lu\n", iob.Information);
+
+    for (i = -20; i < 0; i++)
+    {
+        if (i == -2) continue;
+
+        U(iob).Status = -1;
+        iob.Information = -1;
+        offset.QuadPart = (LONGLONG)i;
+        status = pNtReadFile(hfile, 0, NULL, NULL, &iob, buf, sizeof(buf), &offset, NULL);
+        ok(status == STATUS_INVALID_PARAMETER, "%d: expected STATUS_INVALID_PARAMETER, got %#x\n", i, status);
+        ok(U(iob).Status == -1, "expected -1, got %#x\n", U(iob).Status);
+        ok(iob.Information == -1, "expected -1, got %ld\n", iob.Information);
+    }
+
     SetFilePointer(hfile, 0, NULL, FILE_BEGIN);
 
     bytes = 0;
@@ -2041,42 +2239,45 @@ todo_wine
     ok(bytes == sizeof(contents), "bytes %u\n", bytes);
     ok(!memcmp(contents, buf, sizeof(contents)), "file contents mismatch\n");
 
-    iob.Status = -1;
-    iob.Information = -1;
-    status = pNtReadFile(hfile, 0, NULL, NULL, &iob, buf, sizeof(buf), NULL, NULL);
-    ok(status == STATUS_END_OF_FILE, "expected STATUS_END_OF_FILE, got %#x\n", status);
-todo_wine
-    ok(iob.Status == STATUS_END_OF_FILE, "expected STATUS_END_OF_FILE, got %#x\n", iob.Status);
-todo_wine
-    ok(iob.Information == 0, "expected 0, got %lu\n", iob.Information);
+    off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT);
+    ok(off == sizeof(contents), "expected sizeof(contents), got %u\n", off);
 
-    iob.Status = -1;
+    U(iob).Status = -1;
     iob.Information = -1;
     offset.QuadPart = 0;
     status = pNtReadFile(hfile, 0, NULL, NULL, &iob, buf, sizeof(buf), &offset, NULL);
     ok(status == STATUS_SUCCESS, "NtReadFile error %#x\n", status);
-    ok(iob.Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", iob.Status);
+    ok(U(iob).Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", U(iob).Status);
     ok(iob.Information == sizeof(contents), "expected sizeof(contents), got %lu\n", iob.Information);
     ok(!memcmp(contents, buf, sizeof(contents)), "file contents mismatch\n");
 
-    iob.Status = -1;
+    off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT);
+    ok(off == sizeof(contents), "expected sizeof(contents), got %u\n", off);
+
+    U(iob).Status = -1;
     iob.Information = -1;
     offset.QuadPart = sizeof(contents) - 4;
     status = pNtWriteFile(hfile, 0, NULL, NULL, &iob, "DCBA", 4, &offset, NULL);
     ok(status == STATUS_SUCCESS, "NtWriteFile error %#x\n", status);
-    ok(iob.Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", iob.Status);
+    ok(U(iob).Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", U(iob).Status);
     ok(iob.Information == 4, "expected 4, got %lu\n", iob.Information);
 
-    iob.Status = -1;
+    off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT);
+    ok(off == sizeof(contents), "expected sizeof(contents), got %u\n", off);
+
+    U(iob).Status = -1;
     iob.Information = -1;
     offset.QuadPart = 0;
     status = pNtReadFile(hfile, 0, NULL, NULL, &iob, buf, sizeof(buf), &offset, NULL);
     ok(status == STATUS_SUCCESS, "NtReadFile error %#x\n", status);
-    ok(iob.Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", iob.Status);
+    ok(U(iob).Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", U(iob).Status);
     ok(iob.Information == sizeof(contents), "expected sizeof(contents), got %lu\n", iob.Information);
     ok(!memcmp(contents, buf, sizeof(contents) - 4), "file contents mismatch\n");
     ok(!memcmp(buf + sizeof(contents) - 4, "DCBA", 4), "file contents mismatch\n");
 
+    off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT);
+    ok(off == sizeof(contents), "expected sizeof(contents), got %u\n", off);
+
     S(U(ovl)).Offset = sizeof(contents) - 4;
     S(U(ovl)).OffsetHigh = 0;
     ovl.hEvent = 0;
@@ -2086,6 +2287,9 @@ todo_wine
     ok(ret, "WriteFile error %d\n", GetLastError());
     ok(bytes == 4, "bytes %u\n", bytes);
 
+    off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT);
+    ok(off == sizeof(contents), "expected sizeof(contents), got %u\n", off);
+
     S(U(ovl)).Offset = 0;
     S(U(ovl)).OffsetHigh = 0;
     ovl.Internal = -1;
@@ -2101,65 +2305,74 @@ todo_wine
     ok(!memcmp(contents, buf, sizeof(contents) - 4), "file contents mismatch\n");
     ok(!memcmp(buf + sizeof(contents) - 4, "ABCD", 4), "file contents mismatch\n");
 
+    off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT);
+    ok(off == sizeof(contents), "expected sizeof(contents), got %u\n", off);
+
     CloseHandle(hfile);
 
     hfile = create_temp_file(FILE_FLAG_OVERLAPPED);
     if (!hfile) return;
 
+    bytes = 0xdeadbeef;
+    SetLastError(0xdeadbeef);
+    ret = ReadFile(INVALID_HANDLE_VALUE, buf, 0, &bytes, NULL);
+    ok(!ret, "ReadFile should fail\n");
+    ok(GetLastError() == ERROR_INVALID_HANDLE, "expected ERROR_INVALID_HANDLE, got %d\n", GetLastError());
+    ok(bytes == 0, "bytes %u\n", bytes);
+
+    S(U(ovl)).Offset = 0;
+    S(U(ovl)).OffsetHigh = 0;
+    ovl.Internal = -1;
+    ovl.InternalHigh = -1;
+    ovl.hEvent = 0;
+    bytes = 0xdeadbeef;
+    SetLastError(0xdeadbeef);
+    /* ReadFile return value depends on Windows version and testing it is not practical */
+    ReadFile(hfile, buf, 0, &bytes, &ovl);
+    ok(bytes == 0, "bytes %u\n", bytes);
+    ok((NTSTATUS)ovl.Internal == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx\n", ovl.Internal);
+    ok(ovl.InternalHigh == 0, "expected 0, got %lu\n", ovl.InternalHigh);
+
     bytes = 0xdeadbeef;
     SetLastError(0xdeadbeef);
     ret = WriteFile(hfile, contents, sizeof(contents), &bytes, NULL);
-todo_wine
     ok(!ret, "WriteFile should fail\n");
-todo_wine
     ok(GetLastError() == ERROR_INVALID_PARAMETER, "expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
-todo_wine
     ok(bytes == 0, "bytes %u\n", bytes);
 
-    iob.Status = -1;
+    U(iob).Status = -1;
     iob.Information = -1;
     status = pNtWriteFile(hfile, 0, NULL, NULL, &iob, contents, sizeof(contents), NULL, NULL);
-todo_wine
     ok(status == STATUS_INVALID_PARAMETER, "expected STATUS_INVALID_PARAMETER, got %#x\n", status);
-todo_wine
-    ok(iob.Status == -1, "expected -1, got %#x\n", iob.Status);
-todo_wine
+    ok(U(iob).Status == -1, "expected -1, got %#x\n", U(iob).Status);
     ok(iob.Information == -1, "expected -1, got %ld\n", iob.Information);
 
-    iob.Status = -1;
-    iob.Information = -1;
-    offset.QuadPart = (LONGLONG)-2 /* FILE_USE_FILE_POINTER_POSITION */;
-    status = pNtWriteFile(hfile, 0, NULL, NULL, &iob, contents, sizeof(contents), &offset, NULL);
-todo_wine
-    ok(status == STATUS_INVALID_PARAMETER, "expected STATUS_INVALID_PARAMETER, got %#x\n", status);
-todo_wine
-    ok(iob.Status == -1, "expected -1, got %#x\n", iob.Status);
-todo_wine
-    ok(iob.Information == -1, "expected -1, got %ld\n", iob.Information);
+    for (i = -20; i < -1; i++)
+    {
+        U(iob).Status = -1;
+        iob.Information = -1;
+        offset.QuadPart = (LONGLONG)i;
+        status = pNtWriteFile(hfile, 0, NULL, NULL, &iob, contents, sizeof(contents), &offset, NULL);
+        ok(status == STATUS_INVALID_PARAMETER, "%d: expected STATUS_INVALID_PARAMETER, got %#x\n", i, status);
+        ok(U(iob).Status == -1, "expected -1, got %#x\n", U(iob).Status);
+        ok(iob.Information == -1, "expected -1, got %ld\n", iob.Information);
+    }
 
-    iob.Status = -1;
+    U(iob).Status = -1;
     iob.Information = -1;
     offset.QuadPart = 0;
     status = pNtWriteFile(hfile, 0, NULL, NULL, &iob, contents, sizeof(contents), &offset, NULL);
-todo_wine
-    ok(status == STATUS_PENDING || broken(status == STATUS_SUCCESS) /* see below */, "expected STATUS_PENDING, got %#x\n", status);
-    ok(iob.Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", iob.Status);
-    ok(iob.Information == sizeof(contents), "expected sizeof(contents), got %lu\n", iob.Information);
-    /* even fully updated XP passes this test, but it looks like some VMs
-     * in a testbot get never updated, so overlapped IO is broken. Instead
-     * of fighting with broken tests and adding a bunch of broken() statements
-     * it's better to skip further tests completely.
-     */
-    if (status != STATUS_PENDING)
+    ok(status == STATUS_PENDING || status == STATUS_SUCCESS /* before Vista */, "expected STATUS_PENDING or STATUS_SUCCESS, got %#x\n", status);
+    if (status == STATUS_PENDING)
     {
-todo_wine
-        win_skip("broken overlapped IO implementation, update your OS\n");
-        CloseHandle(hfile);
-        return;
+        ret = WaitForSingleObject(hfile, 3000);
+        ok(ret == WAIT_OBJECT_0, "WaitForSingleObject error %d\n", ret);
     }
+    ok(U(iob).Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", U(iob).Status);
+    ok(iob.Information == sizeof(contents), "expected sizeof(contents), got %lu\n", iob.Information);
 
-    ret = WaitForSingleObject(hfile, 3000);
-    ok(ret == WAIT_OBJECT_0, "WaitForSingleObject error %d\n", ret);
+    off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT);
+    ok(off == 0, "expected 0, got %u\n", off);
 
     bytes = 0xdeadbeef;
     SetLastError(0xdeadbeef);
@@ -2168,21 +2381,28 @@ todo_wine
     ok(GetLastError() == ERROR_INVALID_PARAMETER, "expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
     ok(bytes == 0, "bytes %u\n", bytes);
 
-    iob.Status = -1;
+    U(iob).Status = -1;
     iob.Information = -1;
     status = pNtReadFile(hfile, 0, NULL, NULL, &iob, buf, sizeof(buf), NULL, NULL);
     ok(status == STATUS_INVALID_PARAMETER, "expected STATUS_INVALID_PARAMETER, got %#x\n", status);
-    ok(iob.Status == -1, "expected -1, got %#x\n", iob.Status);
+    ok(U(iob).Status == -1, "expected -1, got %#x\n", U(iob).Status);
     ok(iob.Information == -1, "expected -1, got %ld\n", iob.Information);
 
-    iob.Status = -1;
-    iob.Information = -1;
-    offset.QuadPart = (LONGLONG)-2 /* FILE_USE_FILE_POINTER_POSITION */;
-    status = pNtReadFile(hfile, 0, NULL, NULL, &iob, buf, sizeof(buf), &offset, NULL);
-    ok(status == STATUS_INVALID_PARAMETER, "expected STATUS_INVALID_PARAMETER, got %#x\n", status);
-    ok(iob.Status == -1, "expected -1, got %#x\n", iob.Status);
-    ok(iob.Information == -1, "expected -1, got %ld\n", iob.Information);
+    for (i = -20; i < 0; i++)
+    {
+        U(iob).Status = -1;
+        iob.Information = -1;
+        offset.QuadPart = (LONGLONG)i;
+        status = pNtReadFile(hfile, 0, NULL, NULL, &iob, buf, sizeof(buf), &offset, NULL);
+        ok(status == STATUS_INVALID_PARAMETER, "%d: expected STATUS_INVALID_PARAMETER, got %#x\n", i, status);
+        ok(U(iob).Status == -1, "expected -1, got %#x\n", U(iob).Status);
+        ok(iob.Information == -1, "expected -1, got %ld\n", iob.Information);
+    }
+
+    off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT);
+    ok(off == 0, "expected 0, got %u\n", off);
 
+    /* test reading beyond EOF */
     offset.QuadPart = sizeof(contents);
     S(U(ovl)).Offset = offset.u.LowPart;
     S(U(ovl)).OffsetHigh = offset.u.HighPart;
@@ -2193,52 +2413,131 @@ todo_wine
     SetLastError(0xdeadbeef);
     ret = ReadFile(hfile, buf, sizeof(buf), &bytes, &ovl);
     ok(!ret, "ReadFile should fail\n");
-    ok(GetLastError() == ERROR_IO_PENDING || broken(GetLastError() == ERROR_HANDLE_EOF), "expected ERROR_IO_PENDING, got %d\n", GetLastError());
-    /* even fully updated XP passes this test, but it looks like some VMs
-     * in a testbot get never updated, so overlapped IO is broken. Instead
-     * of fighting with broken tests and adding a bunch of broken() statements
-     * it's better to skip further tests completely.
-     */
-    if (GetLastError() != ERROR_IO_PENDING)
+    ret = GetLastError();
+    ok(ret == ERROR_IO_PENDING || ret == ERROR_HANDLE_EOF /* before Vista */, "expected ERROR_IO_PENDING or ERROR_HANDLE_EOF, got %d\n", ret);
+    ok(bytes == 0, "bytes %u\n", bytes);
+
+    off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT);
+    ok(off == 0, "expected 0, got %u\n", off);
+
+    if (ret == ERROR_IO_PENDING)
     {
-        win_skip("broken overlapped IO implementation, update your OS\n");
-        CloseHandle(hfile);
-        return;
+        bytes = 0xdeadbeef;
+        SetLastError(0xdeadbeef);
+        ret = GetOverlappedResult(hfile, &ovl, &bytes, TRUE);
+        ok(!ret, "GetOverlappedResult should report FALSE\n");
+        ok(GetLastError() == ERROR_HANDLE_EOF, "expected ERROR_HANDLE_EOF, got %d\n", GetLastError());
+        ok(bytes == 0, "expected 0, read %u\n", bytes);
+        ok((NTSTATUS)ovl.Internal == STATUS_END_OF_FILE, "expected STATUS_END_OF_FILE, got %#lx\n", ovl.Internal);
+        ok(ovl.InternalHigh == 0, "expected 0, got %lu\n", ovl.InternalHigh);
     }
-    ok(bytes == 0, "bytes %u\n", bytes);
-    ok((NTSTATUS)ovl.Internal == STATUS_END_OF_FILE, "expected STATUS_END_OF_FILE, got %#lx\n", ovl.Internal);
-    ok(ovl.InternalHigh == 0, "expected 0, got %lu\n", ovl.InternalHigh);
 
-    bytes = 0xdeadbeef;
-    ret = GetOverlappedResult(hfile, &ovl, &bytes, TRUE);
-    ok(!ret, "GetOverlappedResult should report FALSE\n");
-    ok(GetLastError() == ERROR_HANDLE_EOF, "expected ERROR_HANDLE_EOF, got %d\n", GetLastError());
-    ok(bytes == 0, "expected 0, read %u\n", bytes);
-    ok((NTSTATUS)ovl.Internal == STATUS_END_OF_FILE, "expected STATUS_END_OF_FILE, got %#lx\n", ovl.Internal);
-    ok(ovl.InternalHigh == 0, "expected 0, got %lu\n", ovl.InternalHigh);
+    off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT);
+    ok(off == 0, "expected 0, got %u\n", off);
 
-    iob.Status = -1;
-    iob.Information = -1;
     offset.QuadPart = sizeof(contents);
-    status = pNtReadFile(hfile, 0, NULL, NULL, &iob, buf, sizeof(buf), &offset, NULL);
-    ok(status == STATUS_PENDING, "expected STATUS_PENDING, got %#x\n", status);
-    ok(iob.Status == STATUS_END_OF_FILE, "expected STATUS_END_OF_FILE, got %#x\n", iob.Status);
-    ok(iob.Information == 0, "expected 0, got %lu\n", iob.Information);
+    S(U(ovl)).Offset = offset.u.LowPart;
+    S(U(ovl)).OffsetHigh = offset.u.HighPart;
+    ovl.Internal = -1;
+    ovl.InternalHigh = -1;
+    ovl.hEvent = 0;
+    bytes = 0xdeadbeef;
+    SetLastError(0xdeadbeef);
+    ret = ReadFile(hfile, buf, 0, &bytes, &ovl);
+    /* ReadFile return value depends on Windows version and testing it is not practical */
+    if (!ret)
+todo_wine
+        ok(GetLastError() == ERROR_IO_PENDING, "expected ERROR_IO_PENDING, got %d\n", GetLastError());
+    ret = GetLastError();
+    ok(bytes == 0, "bytes %u\n", bytes);
+
+    off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT);
+    ok(off == 0, "expected 0, got %u\n", off);
 
+    if (ret == ERROR_IO_PENDING)
+    {
+        bytes = 0xdeadbeef;
+        SetLastError(0xdeadbeef);
+        ret = GetOverlappedResult(hfile, &ovl, &bytes, TRUE);
+        ok(ret, "GetOverlappedResult should report TRUE\n");
+        ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %d\n", GetLastError());
+        ok(bytes == 0, "expected 0, read %u\n", bytes);
+        ok((NTSTATUS)ovl.Internal == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx\n", ovl.Internal);
+        ok(ovl.InternalHigh == 0, "expected 0, got %lu\n", ovl.InternalHigh);
+    }
+
+    offset.QuadPart = sizeof(contents);
     S(U(ovl)).Offset = offset.u.LowPart;
     S(U(ovl)).OffsetHigh = offset.u.HighPart;
-    ovl.Internal = iob.Status;
-    ovl.InternalHigh = iob.Information;
+    ovl.Internal = -1;
+    ovl.InternalHigh = -1;
     ovl.hEvent = 0;
     bytes = 0xdeadbeef;
-    ret = GetOverlappedResult(hfile, &ovl, &bytes, TRUE);
-    ok(!ret, "GetOverlappedResult should report FALSE\n");
-    ok(GetLastError() == ERROR_HANDLE_EOF, "expected ERROR_HANDLE_EOF, got %d\n", GetLastError());
-    ok(bytes == 0, "expected 0, read %u\n", bytes);
-    ok((NTSTATUS)ovl.Internal == STATUS_END_OF_FILE, "expected STATUS_END_OF_FILE, got %#lx\n", ovl.Internal);
-    ok(ovl.InternalHigh == 0, "expected 0, got %lu\n", ovl.InternalHigh);
+    SetLastError(0xdeadbeef);
+    ret = ReadFile(hfile, NULL, 0, &bytes, &ovl);
+    /* ReadFile return value depends on Windows version and testing it is not practical */
+    if (!ret)
+todo_wine
+        ok(GetLastError() == ERROR_IO_PENDING, "expected ERROR_IO_PENDING, got %d\n", GetLastError());
+    ret = GetLastError();
+    ok(bytes == 0, "bytes %u\n", bytes);
 
-    SetFilePointer(hfile, 0, NULL, FILE_BEGIN);
+    off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT);
+    ok(off == 0, "expected 0, got %u\n", off);
+
+    if (ret == ERROR_IO_PENDING)
+    {
+        bytes = 0xdeadbeef;
+        SetLastError(0xdeadbeef);
+        ret = GetOverlappedResult(hfile, &ovl, &bytes, TRUE);
+        ok(ret, "GetOverlappedResult should report TRUE\n");
+        ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %d\n", GetLastError());
+        ok(bytes == 0, "expected 0, read %u\n", bytes);
+        ok((NTSTATUS)ovl.Internal == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx\n", ovl.Internal);
+        ok(ovl.InternalHigh == 0, "expected 0, got %lu\n", ovl.InternalHigh);
+    }
+
+    U(iob).Status = -1;
+    iob.Information = -1;
+    offset.QuadPart = sizeof(contents);
+    status = pNtReadFile(hfile, 0, NULL, NULL, &iob, buf, sizeof(buf), &offset, NULL);
+    if (status == STATUS_PENDING)
+    {
+        ret = WaitForSingleObject(hfile, 3000);
+        ok(ret == WAIT_OBJECT_0, "WaitForSingleObject error %d\n", ret);
+        ok(U(iob).Status == STATUS_END_OF_FILE, "expected STATUS_END_OF_FILE, got %#x\n", U(iob).Status);
+        ok(iob.Information == 0, "expected 0, got %lu\n", iob.Information);
+    }
+    else
+    {
+        ok(status == STATUS_END_OF_FILE, "expected STATUS_END_OF_FILE, got %#x\n", status);
+        ok(U(iob).Status == -1, "expected -1, got %#x\n", U(iob).Status);
+        ok(iob.Information == -1, "expected -1, got %lu\n", iob.Information);
+    }
+
+    off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT);
+    ok(off == 0, "expected 0, got %u\n", off);
+
+    U(iob).Status = -1;
+    iob.Information = -1;
+    offset.QuadPart = sizeof(contents);
+    status = pNtReadFile(hfile, 0, NULL, NULL, &iob, buf, 0, &offset, NULL);
+    if (status == STATUS_PENDING)
+    {
+        ret = WaitForSingleObject(hfile, 3000);
+        ok(ret == WAIT_OBJECT_0, "WaitForSingleObject error %d\n", ret);
+        ok(U(iob).Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", U(iob).Status);
+        ok(iob.Information == 0, "expected 0, got %lu\n", iob.Information);
+    }
+    else
+    {
+        ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", status);
+        ok(U(iob).Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", U(iob).Status);
+        ok(iob.Information == 0, "expected 0, got %lu\n", iob.Information);
+    }
+
+    off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT);
+    ok(off == 0, "expected 0, got %u\n", off);
 
     S(U(ovl)).Offset = 0;
     S(U(ovl)).OffsetHigh = 0;
@@ -2248,12 +2547,19 @@ todo_wine
     bytes = 0;
     SetLastError(0xdeadbeef);
     ret = ReadFile(hfile, buf, sizeof(buf), &bytes, &ovl);
-    ok(!ret, "ReadFile should fail\n");
-    ok(GetLastError() == ERROR_IO_PENDING, "expected ERROR_IO_PENDING, got %d\n", GetLastError());
-    ok(bytes == 0, "bytes %u\n", bytes);
+    /* ReadFile return value depends on Windows version and testing it is not practical */
+    if (!ret)
+    {
+        ok(GetLastError() == ERROR_IO_PENDING, "expected ERROR_IO_PENDING, got %d\n", GetLastError());
+        ok(bytes == 0, "bytes %u\n", bytes);
+    }
+    else ok(bytes == 14, "bytes %u\n", bytes);
     ok((NTSTATUS)ovl.Internal == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx\n", ovl.Internal);
     ok(ovl.InternalHigh == sizeof(contents), "expected sizeof(contents), got %lu\n", ovl.InternalHigh);
 
+    off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT);
+    ok(off == 0, "expected 0, got %u\n", off);
+
     bytes = 0xdeadbeef;
     ret = GetOverlappedResult(hfile, &ovl, &bytes, TRUE);
     ok(ret, "GetOverlappedResult error %d\n", GetLastError());
@@ -2262,30 +2568,51 @@ todo_wine
     ok(ovl.InternalHigh == sizeof(contents), "expected sizeof(contents), got %lu\n", ovl.InternalHigh);
     ok(!memcmp(contents, buf, sizeof(contents)), "file contents mismatch\n");
 
-    iob.Status = -1;
+    off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT);
+    ok(off == 0, "expected 0, got %u\n", off);
+
+    SetFilePointer(hfile, sizeof(contents) - 4, NULL, FILE_BEGIN);
+    SetEndOfFile(hfile);
+    SetFilePointer(hfile, 0, NULL, FILE_BEGIN);
+
+    U(iob).Status = -1;
     iob.Information = -1;
-    offset.QuadPart = sizeof(contents) - 4;
+    offset.QuadPart = (LONGLONG)-1 /* FILE_WRITE_TO_END_OF_FILE */;
     status = pNtWriteFile(hfile, 0, NULL, NULL, &iob, "DCBA", 4, &offset, NULL);
-    ok(status == STATUS_PENDING || broken(status == STATUS_SUCCESS) /* before Vista */, "expected STATUS_PENDING, got %#x\n", status);
-    ok(iob.Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", iob.Status);
+    ok(status == STATUS_PENDING || status == STATUS_SUCCESS /* before Vista */, "expected STATUS_PENDING or STATUS_SUCCESS, got %#x\n", status);
+    if (status == STATUS_PENDING)
+    {
+        ret = WaitForSingleObject(hfile, 3000);
+        ok(ret == WAIT_OBJECT_0, "WaitForSingleObject error %d\n", ret);
+    }
+    ok(U(iob).Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", U(iob).Status);
     ok(iob.Information == 4, "expected 4, got %lu\n", iob.Information);
 
-    ret = WaitForSingleObject(hfile, 3000);
-    ok(ret == WAIT_OBJECT_0, "WaitForSingleObject error %d\n", ret);
+    off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT);
+    ok(off == 0, "expected 0, got %u\n", off);
 
-    iob.Status = -1;
+    U(iob).Status = -1;
     iob.Information = -1;
     offset.QuadPart = 0;
     status = pNtReadFile(hfile, 0, NULL, NULL, &iob, buf, sizeof(buf), &offset, NULL);
-    ok(status == STATUS_PENDING, "expected STATUS_PENDING, got %#x\n", status);
-    ok(iob.Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", iob.Status);
+    ok(status == STATUS_PENDING || status == STATUS_SUCCESS, "expected STATUS_PENDING or STATUS_SUCCESS, got %#x\n", status);
+    if (status == STATUS_PENDING)
+    {
+        ret = WaitForSingleObject(hfile, 3000);
+        ok(ret == WAIT_OBJECT_0, "WaitForSingleObject error %d\n", ret);
+    }
+    ok(U(iob).Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", U(iob).Status);
     ok(iob.Information == sizeof(contents), "expected sizeof(contents), got %lu\n", iob.Information);
 
-    ret = WaitForSingleObject(hfile, 3000);
-    ok(ret == WAIT_OBJECT_0, "WaitForSingleObject error %d\n", ret);
+    off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT);
+    ok(off == 0, "expected 0, got %u\n", off);
+
     ok(!memcmp(contents, buf, sizeof(contents) - 4), "file contents mismatch\n");
     ok(!memcmp(buf + sizeof(contents) - 4, "DCBA", 4), "file contents mismatch\n");
 
+    off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT);
+    ok(off == 0, "expected 0, got %u\n", off);
+
     S(U(ovl)).Offset = sizeof(contents) - 4;
     S(U(ovl)).OffsetHigh = 0;
     ovl.Internal = -1;
@@ -2294,12 +2621,19 @@ todo_wine
     bytes = 0;
     SetLastError(0xdeadbeef);
     ret = WriteFile(hfile, "ABCD", 4, &bytes, &ovl);
-    ok(!ret || broken(ret) /* before Vista */, "WriteFile should fail\n");
-    ok(GetLastError() == ERROR_IO_PENDING || broken(GetLastError() == 0xdeadbeef) /* before Vista */, "expected ERROR_IO_PENDING, got %d\n", GetLastError());
-    ok(bytes == 0 || broken(bytes == 4) /* before Vista */, "bytes %u\n", bytes);
+    /* WriteFile return value depends on Windows version and testing it is not practical */
+    if (!ret)
+    {
+        ok(GetLastError() == ERROR_IO_PENDING, "expected ERROR_IO_PENDING, got %d\n", GetLastError());
+        ok(bytes == 0, "bytes %u\n", bytes);
+    }
+    else ok(bytes == 4, "bytes %u\n", bytes);
     ok((NTSTATUS)ovl.Internal == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx\n", ovl.Internal);
     ok(ovl.InternalHigh == 4, "expected 4, got %lu\n", ovl.InternalHigh);
 
+    off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT);
+    ok(off == 0, "expected 0, got %u\n", off);
+
     bytes = 0xdeadbeef;
     ret = GetOverlappedResult(hfile, &ovl, &bytes, TRUE);
     ok(ret, "GetOverlappedResult error %d\n", GetLastError());
@@ -2307,6 +2641,9 @@ todo_wine
     ok((NTSTATUS)ovl.Internal == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx\n", ovl.Internal);
     ok(ovl.InternalHigh == 4, "expected 4, got %lu\n", ovl.InternalHigh);
 
+    off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT);
+    ok(off == 0, "expected 0, got %u\n", off);
+
     S(U(ovl)).Offset = 0;
     S(U(ovl)).OffsetHigh = 0;
     ovl.Internal = -1;
@@ -2315,12 +2652,19 @@ todo_wine
     bytes = 0;
     SetLastError(0xdeadbeef);
     ret = ReadFile(hfile, buf, sizeof(buf), &bytes, &ovl);
-    ok(!ret, "ReadFile should fail\n");
-    ok(GetLastError() == ERROR_IO_PENDING, "expected ERROR_IO_PENDING, got %d\n", GetLastError());
-    ok(bytes == 0, "bytes %u\n", bytes);
+    /* ReadFile return value depends on Windows version and testing it is not practical */
+    if (!ret)
+    {
+        ok(GetLastError() == ERROR_IO_PENDING, "expected ERROR_IO_PENDING, got %d\n", GetLastError());
+        ok(bytes == 0, "bytes %u\n", bytes);
+    }
+    else ok(bytes == 14, "bytes %u\n", bytes);
     ok((NTSTATUS)ovl.Internal == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx\n", ovl.Internal);
     ok(ovl.InternalHigh == sizeof(contents), "expected sizeof(contents), got %lu\n", ovl.InternalHigh);
 
+    off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT);
+    ok(off == 0, "expected 0, got %u\n", off);
+
     bytes = 0xdeadbeef;
     ret = GetOverlappedResult(hfile, &ovl, &bytes, TRUE);
     ok(ret, "GetOverlappedResult error %d\n", GetLastError());
@@ -2330,6 +2674,9 @@ todo_wine
     ok(!memcmp(contents, buf, sizeof(contents) - 4), "file contents mismatch\n");
     ok(!memcmp(buf + sizeof(contents) - 4, "ABCD", 4), "file contents mismatch\n");
 
+    off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT);
+    ok(off == 0, "expected 0, got %u\n", off);
+
     CloseHandle(hfile);
 }
 
index 9e9fc31..f8ef346 100755 (executable)
@@ -1903,13 +1903,6 @@ static void test_pack_PCSTR(void)
     TEST_TARGET_ALIGN(PCSTR, 1)
 }
 
-static void test_pack_PCTSTR(void)
-{
-    /* PCTSTR */
-    TEST_TYPE_SIZE   (PCTSTR, 8)
-    TEST_TYPE_ALIGN  (PCTSTR, 8)
-}
-
 static void test_pack_PCWCH(void)
 {
     /* PCWCH */
@@ -2574,13 +2567,6 @@ static void test_pack_PTOKEN_USER(void)
     TEST_TARGET_ALIGN(PTOKEN_USER, 8)
 }
 
-static void test_pack_PTSTR(void)
-{
-    /* PTSTR */
-    TEST_TYPE_SIZE   (PTSTR, 8)
-    TEST_TYPE_ALIGN  (PTSTR, 8)
-}
-
 static void test_pack_PULARGE_INTEGER(void)
 {
     /* PULARGE_INTEGER */
@@ -2935,13 +2921,6 @@ static void test_pack_SYSTEM_AUDIT_ACE(void)
     TEST_FIELD_OFFSET(SYSTEM_AUDIT_ACE, SidStart, 8)
 }
 
-static void test_pack_TCHAR(void)
-{
-    /* TCHAR */
-    TEST_TYPE_SIZE   (TCHAR, 1)
-    TEST_TYPE_ALIGN  (TCHAR, 1)
-}
-
 static void test_pack_TOKEN_DEFAULT_DACL(void)
 {
     /* TOKEN_DEFAULT_DACL */
@@ -5271,13 +5250,6 @@ static void test_pack_PCSTR(void)
     TEST_TARGET_ALIGN(PCSTR, 1)
 }
 
-static void test_pack_PCTSTR(void)
-{
-    /* PCTSTR */
-    TEST_TYPE_SIZE   (PCTSTR, 4)
-    TEST_TYPE_ALIGN  (PCTSTR, 4)
-}
-
 static void test_pack_PCWCH(void)
 {
     /* PCWCH */
@@ -5940,13 +5912,6 @@ static void test_pack_PTOKEN_USER(void)
     TEST_TARGET_ALIGN(PTOKEN_USER, 4)
 }
 
-static void test_pack_PTSTR(void)
-{
-    /* PTSTR */
-    TEST_TYPE_SIZE   (PTSTR, 4)
-    TEST_TYPE_ALIGN  (PTSTR, 4)
-}
-
 static void test_pack_PULARGE_INTEGER(void)
 {
     /* PULARGE_INTEGER */
@@ -6301,13 +6266,6 @@ static void test_pack_SYSTEM_AUDIT_ACE(void)
     TEST_FIELD_OFFSET(SYSTEM_AUDIT_ACE, SidStart, 8)
 }
 
-static void test_pack_TCHAR(void)
-{
-    /* TCHAR */
-    TEST_TYPE_SIZE   (TCHAR, 1)
-    TEST_TYPE_ALIGN  (TCHAR, 1)
-}
-
 static void test_pack_TOKEN_DEFAULT_DACL(void)
 {
     /* TOKEN_DEFAULT_DACL */
@@ -6935,7 +6893,6 @@ static void test_pack(void)
     test_pack_PCCH();
     test_pack_PCH();
     test_pack_PCSTR();
-    test_pack_PCTSTR();
     test_pack_PCWCH();
     test_pack_PCWSTR();
     test_pack_PEXCEPTION_POINTERS();
@@ -7022,7 +6979,6 @@ static void test_pack(void)
     test_pack_PTOKEN_GROUPS();
     test_pack_PTOKEN_PRIVILEGES();
     test_pack_PTOKEN_USER();
-    test_pack_PTSTR();
     test_pack_PULARGE_INTEGER();
     test_pack_PVECTORED_EXCEPTION_HANDLER();
     test_pack_PVOID();
@@ -7052,7 +7008,6 @@ static void test_pack(void)
     test_pack_SSIZE_T();
     test_pack_SYSTEM_ALARM_ACE();
     test_pack_SYSTEM_AUDIT_ACE();
-    test_pack_TCHAR();
     test_pack_TOKEN_DEFAULT_DACL();
     test_pack_TOKEN_GROUPS();
     test_pack_TOKEN_OWNER();
index 87f3c9c..af00401 100755 (executable)
@@ -55,7 +55,7 @@ static DWORD one_before_last_pid = 0;
 static BOOL InitFunctionPtrs(void)
 {
     /* All needed functions are NT based, so using GetModuleHandle is a good check */
-    HMODULE hntdll = GetModuleHandle("ntdll");
+    HMODULE hntdll = GetModuleHandleA("ntdll");
     if (!hntdll)
     {
         win_skip("Not running on NT\n");
@@ -78,7 +78,7 @@ static BOOL InitFunctionPtrs(void)
     /* not present before XP */
     pNtGetCurrentProcessorNumber = (void *) GetProcAddress(hntdll, "NtGetCurrentProcessorNumber");
 
-    pIsWow64Process = (void *)GetProcAddress(GetModuleHandle("kernel32.dll"), "IsWow64Process");
+    pIsWow64Process = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "IsWow64Process");
     if (!pIsWow64Process || !pIsWow64Process( GetCurrentProcess(), &is_wow64 )) is_wow64 = FALSE;
     return TRUE;
 }
@@ -110,7 +110,7 @@ static void test_query_basic(void)
     ok( status == STATUS_ACCESS_VIOLATION || status == STATUS_INVALID_PARAMETER /* vista */,
         "Expected STATUS_ACCESS_VIOLATION or STATUS_INVALID_PARAMETER, got %08x\n", status);
 
-    /* Use a existing class, correct length, a pointer to a buffer but no ReturnLength pointer */
+    /* Use an existing class, correct length, a pointer to a buffer but no ReturnLength pointer */
     trace("Check no ReturnLength pointer\n");
     status = pNtQuerySystemInformation(SystemBasicInformation, &sbi, sizeof(sbi), NULL);
     ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status);
@@ -259,7 +259,7 @@ static void test_query_process(void)
     DWORD last_pid;
     ULONG ReturnLength;
     int i = 0, k = 0;
-    int is_nt = 0;
+    BOOL is_nt = FALSE;
     SYSTEM_BASIC_INFORMATION sbi;
 
     /* Copy of our winternl.h structure turned into a private one */
@@ -946,14 +946,14 @@ static void test_query_process_debug_port(int argc, char **argv)
     DWORD_PTR debug_port = 0xdeadbeef;
     char cmdline[MAX_PATH];
     PROCESS_INFORMATION pi;
-    STARTUPINFO si = { 0 };
+    STARTUPINFOA si = { 0 };
     NTSTATUS status;
     BOOL ret;
 
     sprintf(cmdline, "%s %s %s", argv[0], argv[1], "debuggee");
 
     si.cb = sizeof(si);
-    ret = CreateProcess(NULL, cmdline, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &si, &pi);
+    ret = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &si, &pi);
     ok(ret, "CreateProcess failed, last error %#x.\n", GetLastError());
     if (!ret) return;
 
@@ -1099,7 +1099,7 @@ static void test_query_process_image_file_name(void)
 static void test_query_process_debug_object_handle(int argc, char **argv)
 {
     char cmdline[MAX_PATH];
-    STARTUPINFO si = {0};
+    STARTUPINFOA si = {0};
     PROCESS_INFORMATION pi;
     BOOL ret;
     HANDLE debug_object;
@@ -1108,7 +1108,7 @@ static void test_query_process_debug_object_handle(int argc, char **argv)
     sprintf(cmdline, "%s %s %s", argv[0], argv[1], "debuggee");
 
     si.cb = sizeof(si);
-    ret = CreateProcess(NULL, cmdline, NULL, NULL, FALSE, DEBUG_PROCESS, NULL,
+    ret = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, DEBUG_PROCESS, NULL,
                         NULL, &si, &pi);
     ok(ret, "CreateProcess failed with last error %u\n", GetLastError());
     if (!ret) return;
@@ -1198,14 +1198,14 @@ static void test_query_process_debug_flags(int argc, char **argv)
     DWORD debug_flags = 0xdeadbeef;
     char cmdline[MAX_PATH];
     PROCESS_INFORMATION pi;
-    STARTUPINFO si = { 0 };
+    STARTUPINFOA si = { 0 };
     NTSTATUS status;
     BOOL ret;
 
     sprintf(cmdline, "%s %s %s", argv[0], argv[1], "debuggee");
 
     si.cb = sizeof(si);
-    ret = CreateProcess(NULL, cmdline, NULL, NULL, FALSE, DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS, NULL, NULL, &si, &pi);
+    ret = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS, NULL, NULL, &si, &pi);
     ok(ret, "CreateProcess failed, last error %#x.\n", GetLastError());
     if (!ret) return;
 
@@ -1359,12 +1359,17 @@ static void test_mapprotection(void)
     addr = NULL;
     status = pNtMapViewOfSection ( h, GetCurrentProcess(), &addr, 0, 0, &offset, &count, ViewShare, 0, PAGE_READWRITE);
     ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status);
+
 #if defined(__x86_64__) || defined(__i386__)
-    memset (addr, 0xc3, 1); /* lret ... in both i386 and x86_64 */
+    *(unsigned char*)addr = 0xc3;       /* lret ... in both i386 and x86_64 */
+#elif defined(__arm__)
+    *(unsigned long*)addr = 0xe12fff1e; /* bx lr */
+#else
+    ok(0, "Add a return opcode for your architecture or expect a crash in this test\n");
+#endif
     trace("trying to execute code in the readwrite only mapped anon file...\n");
     f = addr;f();
     trace("...done.\n");
-#endif
 
     status = pNtQueryVirtualMemory( GetCurrentProcess(), addr, MemoryBasicInformation, &info, sizeof(info), &retlen );
     ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status);
@@ -1390,7 +1395,7 @@ static void test_queryvirtualmemory(void)
     char stackbuf[42];
     HMODULE module;
 
-    module = GetModuleHandle( "ntdll.dll" );
+    module = GetModuleHandleA( "ntdll.dll" );
     trace("Check flags of the PE header of NTDLL.DLL at %p\n", module);
     status = pNtQueryVirtualMemory(NtCurrentProcess(), module, MemoryBasicInformation, &mbi, sizeof(MEMORY_BASIC_INFORMATION), &readcount);
     ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status);
@@ -1402,7 +1407,7 @@ static void test_queryvirtualmemory(void)
     ok (mbi.Type == MEM_IMAGE, "mbi.Type is 0x%x, expected 0x%x\n", mbi.Type, MEM_IMAGE);
 
     trace("Check flags of a function entry in NTDLL.DLL at %p\n", pNtQueryVirtualMemory);
-    module = GetModuleHandle( "ntdll.dll" );
+    module = GetModuleHandleA( "ntdll.dll" );
     status = pNtQueryVirtualMemory(NtCurrentProcess(), pNtQueryVirtualMemory, MemoryBasicInformation, &mbi, sizeof(MEMORY_BASIC_INFORMATION), &readcount);
     ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status);
     ok( readcount == sizeof(MEMORY_BASIC_INFORMATION), "Expected to read %d bytes, got %ld\n",(int)sizeof(MEMORY_BASIC_INFORMATION),readcount);
@@ -1430,7 +1435,7 @@ static void test_queryvirtualmemory(void)
     ok (mbi.Protect == PAGE_READWRITE, "mbi.Protect is 0x%x, expected 0x%x\n", mbi.Protect, PAGE_READWRITE);
 
     trace("Check flags of read-only data at %p\n", teststring);
-    module = GetModuleHandle( NULL );
+    module = GetModuleHandleA( NULL );
     status = pNtQueryVirtualMemory(NtCurrentProcess(), teststring, MemoryBasicInformation, &mbi, sizeof(MEMORY_BASIC_INFORMATION), &readcount);
     ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status);
     ok( readcount == sizeof(MEMORY_BASIC_INFORMATION), "Expected to read %d bytes, got %ld\n",(int)sizeof(MEMORY_BASIC_INFORMATION),readcount);
@@ -1475,7 +1480,7 @@ static void test_affinity(void)
     GetSystemInfo(&si);
     status = pNtQueryInformationProcess( GetCurrentProcess(), ProcessBasicInformation, &pbi, sizeof(pbi), NULL );
     ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status);
-    proc_affinity = (DWORD_PTR)pbi.Reserved2[0];
+    proc_affinity = pbi.AffinityMask;
     ok( proc_affinity == (1 << si.dwNumberOfProcessors) - 1, "Unexpected process affinity\n" );
     proc_affinity = 1 << si.dwNumberOfProcessors;
     status = pNtSetInformationProcess( GetCurrentProcess(), ProcessAffinityMask, &proc_affinity, sizeof(proc_affinity) );
@@ -1534,7 +1539,7 @@ static void test_affinity(void)
     ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status);
     status = pNtQueryInformationProcess( GetCurrentProcess(), ProcessBasicInformation, &pbi, sizeof(pbi), NULL );
     ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status);
-    proc_affinity = (DWORD_PTR)pbi.Reserved2[0];
+    proc_affinity = pbi.AffinityMask;
     ok( proc_affinity == 2, "Unexpected process affinity\n" );
     /* Setting the process affinity changes the thread affinity to match */
     status = pNtQueryInformationThread( GetCurrentThread(), ThreadBasicInformation, &tbi, sizeof(tbi), NULL );
@@ -1578,7 +1583,7 @@ static void test_NtGetCurrentProcessorNumber(void)
     trace("dwNumberOfProcessors: %d, current processor: %d\n", si.dwNumberOfProcessors, current_cpu);
 
     status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessBasicInformation, &pbi, sizeof(pbi), NULL);
-    old_process_mask = (DWORD_PTR)pbi.Reserved2[0];
+    old_process_mask = pbi.AffinityMask;
     ok(status == STATUS_SUCCESS, "got 0x%x (expected STATUS_SUCCESS)\n", status);
 
     status = pNtQueryInformationThread(GetCurrentThread(), ThreadBasicInformation, &tbi, sizeof(tbi), NULL);
@@ -1721,7 +1726,7 @@ START_TEST(info)
     trace("Starting test_process_debug_flags()\n");
     test_query_process_debug_flags(argc, argv);
 
-    /* belongs into it's own file */
+    /* belongs to its own file */
     trace("Starting test_readvirtualmemory()\n");
     test_readvirtualmemory();
 
index a5ffda7..cca05c9 100644 (file)
@@ -26,7 +26,7 @@
 #include "stdlib.h"
 
 static HANDLE   (WINAPI *pCreateWaitableTimerA)(SECURITY_ATTRIBUTES*, BOOL, LPCSTR);
-static NTSTATUS (WINAPI *pRtlCreateUnicodeStringFromAsciiz)(PUNICODE_STRING, LPCSTR);
+static BOOLEAN  (WINAPI *pRtlCreateUnicodeStringFromAsciiz)(PUNICODE_STRING, LPCSTR);
 static VOID     (WINAPI *pRtlInitUnicodeString)( PUNICODE_STRING, LPCWSTR );
 static VOID     (WINAPI *pRtlFreeUnicodeString)(PUNICODE_STRING);
 static NTSTATUS (WINAPI *pNtCreateEvent) ( PHANDLE, ACCESS_MASK, const POBJECT_ATTRIBUTES, BOOLEAN, BOOLEAN);
index 29a6583..673bc93 100755 (executable)
@@ -30,7 +30,7 @@ static BOOLEAN (WINAPI *pRtlIsNameLegalDOS8Dot3)(const UNICODE_STRING*,POEM_STRI
 static DWORD (WINAPI *pRtlGetFullPathName_U)(const WCHAR*,ULONG,WCHAR*,WCHAR**);
 
 
-static void test_RtlDetermineDosPathNameType(void)
+static void test_RtlDetermineDosPathNameType_U(void)
 {
     struct test
     {
@@ -75,6 +75,12 @@ static void test_RtlDetermineDosPathNameType(void)
     WCHAR buffer[MAX_PATH];
     UINT ret;
 
+    if (!pRtlDetermineDosPathNameType_U)
+    {
+        win_skip("RtlDetermineDosPathNameType_U is not available\n");
+        return;
+    }
+
     for (test = tests; test->path; test++)
     {
         pRtlMultiByteToUnicodeN( buffer, sizeof(buffer), NULL, test->path, strlen(test->path)+1 );
@@ -84,7 +90,7 @@ static void test_RtlDetermineDosPathNameType(void)
 }
 
 
-static void test_RtlIsDosDeviceName(void)
+static void test_RtlIsDosDeviceName_U(void)
 {
     struct test
     {
@@ -96,8 +102,8 @@ static void test_RtlIsDosDeviceName(void)
 
     static const struct test tests[] =
     {
-        { "\\\\.\\CON",    8, 6 },
-        { "\\\\.\\con",    8, 6 },
+        { "\\\\.\\CON",    8, 6, TRUE },  /* fails on win8 */
+        { "\\\\.\\con",    8, 6, TRUE },  /* fails on win8 */
         { "\\\\.\\CON2",   0, 0 },
         { "",              0, 0 },
         { "\\\\foo\\nul",  0, 0 },
@@ -144,6 +150,12 @@ static void test_RtlIsDosDeviceName(void)
     WCHAR buffer[2000];
     ULONG ret;
 
+    if (!pRtlIsDosDeviceName_U)
+    {
+        win_skip("RtlIsDosDeviceName_U is not available\n");
+        return;
+    }
+
     for (test = tests; test->path; test++)
     {
         pRtlMultiByteToUnicodeN( buffer, sizeof(buffer), NULL, test->path, strlen(test->path)+1 );
@@ -197,6 +209,12 @@ static void test_RtlIsNameLegalDOS8Dot3(void)
     char buff2[12];
     BOOLEAN ret, spaces;
 
+    if (!pRtlIsNameLegalDOS8Dot3)
+    {
+        win_skip("RtlIsNameLegalDOS8Dot3 is not available\n");
+        return;
+    }
+
     ustr.MaximumLength = sizeof(buffer);
     ustr.Buffer = buffer;
     for (test = tests; test->path; test++)
@@ -274,6 +292,12 @@ static void test_RtlGetFullPathName_U(void)
     DWORD reslen;
     UINT len;
 
+    if (!pRtlGetFullPathName_U)
+    {
+        win_skip("RtlGetFullPathName_U is not available\n");
+        return;
+    }
+
     file_part = (WCHAR *)0xdeadbeef;
     lstrcpyW(rbufferW, deadbeefW);
     ret = pRtlGetFullPathName_U(NULL, MAX_PATH, rbufferW, &file_part);
@@ -336,12 +360,9 @@ START_TEST(path)
     pRtlOemStringToUnicodeString = (void *)GetProcAddress(mod,"RtlOemStringToUnicodeString");
     pRtlIsNameLegalDOS8Dot3 = (void *)GetProcAddress(mod,"RtlIsNameLegalDOS8Dot3");
     pRtlGetFullPathName_U = (void *)GetProcAddress(mod,"RtlGetFullPathName_U");
-    if (pRtlDetermineDosPathNameType_U)
-        test_RtlDetermineDosPathNameType();
-    if (pRtlIsDosDeviceName_U)
-        test_RtlIsDosDeviceName();
-    if (pRtlIsNameLegalDOS8Dot3)
-        test_RtlIsNameLegalDOS8Dot3();
-    if (pRtlGetFullPathName_U && pRtlMultiByteToUnicodeN)
-        test_RtlGetFullPathName_U();
+
+    test_RtlDetermineDosPathNameType_U();
+    test_RtlIsDosDeviceName_U();
+    test_RtlIsNameLegalDOS8Dot3();
+    test_RtlGetFullPathName_U();
 }
index 3ab4d07..c275e3d 100644 (file)
@@ -77,7 +77,7 @@ static DWORD (WINAPI *pQueueUserAPC)(PAPCFUNC pfnAPC, HANDLE hThread, ULONG_PTR
 
 static BOOL init_func_ptrs(void)
 {
-    HMODULE module = GetModuleHandle("ntdll.dll");
+    HMODULE module = GetModuleHandleA("ntdll.dll");
 
 #define loadfunc(name)  if (!(p##name = (void *)GetProcAddress(module, #name))) { \
                             trace("GetProcAddress(%s) failed\n", #name); \
@@ -91,7 +91,7 @@ static BOOL init_func_ptrs(void)
     loadfunc(RtlInitUnicodeString)
 
     /* not fatal */
-    module = GetModuleHandle("kernel32.dll");
+    module = GetModuleHandleA("kernel32.dll");
     pOpenThread = (void *)GetProcAddress(module, "OpenThread");
     pQueueUserAPC = (void *)GetProcAddress(module, "QueueUserAPC");
     return TRUE;
index 20e5831..e01316b 100644 (file)
@@ -160,7 +160,7 @@ static BOOL init_function_ptrs(void)
         return FALSE;
     }
 
-    pIsWow64Process = (void *)GetProcAddress(GetModuleHandle("kernel32.dll"), "IsWow64Process");
+    pIsWow64Process = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "IsWow64Process");
     if (!pIsWow64Process || !pIsWow64Process( GetCurrentProcess(), &is_wow64 )) is_wow64 = FALSE;
     return TRUE;
 }
@@ -197,30 +197,30 @@ static void ProcessLpcRequest(HANDLE PortHandle, union lpc_message *LpcMessage)
     {
         ok(LpcMessage->msg64.MessageType == LPC_REQUEST,
            "Expected LPC_REQUEST, got %d\n", LpcMessage->msg64.MessageType);
-        ok(!lstrcmp((LPSTR)LpcMessage->msg64.Data, REQUEST2),
+        ok(!strcmp((LPSTR)LpcMessage->msg64.Data, REQUEST2),
            "Expected %s, got %s\n", REQUEST2, LpcMessage->msg64.Data);
-        lstrcpy((LPSTR)LpcMessage->msg64.Data, REPLY);
+        strcpy((LPSTR)LpcMessage->msg64.Data, REPLY);
 
         status = pNtReplyPort(PortHandle, &LpcMessage->msg);
         ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %x\n", status);
         ok(LpcMessage->msg64.MessageType == LPC_REQUEST,
            "Expected LPC_REQUEST, got %d\n", LpcMessage->msg64.MessageType);
-        ok(!lstrcmp((LPSTR)LpcMessage->msg64.Data, REPLY),
+        ok(!strcmp((LPSTR)LpcMessage->msg64.Data, REPLY),
            "Expected %s, got %s\n", REPLY, LpcMessage->msg64.Data);
     }
     else
     {
         ok(LpcMessage->msg.MessageType == LPC_REQUEST,
            "Expected LPC_REQUEST, got %d\n", LpcMessage->msg.MessageType);
-        ok(!lstrcmp((LPSTR)LpcMessage->msg.Data, REQUEST2),
+        ok(!strcmp((LPSTR)LpcMessage->msg.Data, REQUEST2),
            "Expected %s, got %s\n", REQUEST2, LpcMessage->msg.Data);
-        lstrcpy((LPSTR)LpcMessage->msg.Data, REPLY);
+        strcpy((LPSTR)LpcMessage->msg.Data, REPLY);
 
         status = pNtReplyPort(PortHandle, &LpcMessage->msg);
         ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %x\n", status);
         ok(LpcMessage->msg.MessageType == LPC_REQUEST,
            "Expected LPC_REQUEST, got %d\n", LpcMessage->msg.MessageType);
-        ok(!lstrcmp((LPSTR)LpcMessage->msg.Data, REPLY),
+        ok(!strcmp((LPSTR)LpcMessage->msg.Data, REPLY),
            "Expected %s, got %s\n", REPLY, LpcMessage->msg.Data);
     }
 }
@@ -251,26 +251,26 @@ static DWORD WINAPI test_ports_client(LPVOID arg)
         LpcMessage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
         out = HeapAlloc(GetProcessHeap(), 0, size);
 
-        LpcMessage->msg64.DataSize = lstrlen(REQUEST1) + 1;
+        LpcMessage->msg64.DataSize = strlen(REQUEST1) + 1;
         LpcMessage->msg64.MessageSize = FIELD_OFFSET(LPC_MESSAGE64, Data[LpcMessage->msg64.DataSize]);
-        lstrcpy((LPSTR)LpcMessage->msg64.Data, REQUEST1);
+        strcpy((LPSTR)LpcMessage->msg64.Data, REQUEST1);
 
         status = pNtRequestPort(PortHandle, &LpcMessage->msg);
         ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %x\n", status);
         ok(LpcMessage->msg64.MessageType == 0, "Expected 0, got %d\n", LpcMessage->msg64.MessageType);
-        ok(!lstrcmp((LPSTR)LpcMessage->msg64.Data, REQUEST1),
+        ok(!strcmp((LPSTR)LpcMessage->msg64.Data, REQUEST1),
            "Expected %s, got %s\n", REQUEST1, LpcMessage->msg64.Data);
 
         /* Fill in the message */
         memset(LpcMessage, 0, size);
-        LpcMessage->msg64.DataSize = lstrlen(REQUEST2) + 1;
+        LpcMessage->msg64.DataSize = strlen(REQUEST2) + 1;
         LpcMessage->msg64.MessageSize = FIELD_OFFSET(LPC_MESSAGE64, Data[LpcMessage->msg64.DataSize]);
-        lstrcpy((LPSTR)LpcMessage->msg64.Data, REQUEST2);
+        strcpy((LPSTR)LpcMessage->msg64.Data, REQUEST2);
 
         /* Send the message and wait for the reply */
         status = pNtRequestWaitReplyPort(PortHandle, &LpcMessage->msg, &out->msg);
         ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %x\n", status);
-        ok(!lstrcmp((LPSTR)out->msg64.Data, REPLY), "Expected %s, got %s\n", REPLY, out->msg64.Data);
+        ok(!strcmp((LPSTR)out->msg64.Data, REPLY), "Expected %s, got %s\n", REPLY, out->msg64.Data);
         ok(out->msg64.MessageType == LPC_REPLY, "Expected LPC_REPLY, got %d\n", out->msg64.MessageType);
     }
     else
@@ -279,26 +279,26 @@ static DWORD WINAPI test_ports_client(LPVOID arg)
         LpcMessage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
         out = HeapAlloc(GetProcessHeap(), 0, size);
 
-        LpcMessage->msg.DataSize = lstrlen(REQUEST1) + 1;
+        LpcMessage->msg.DataSize = strlen(REQUEST1) + 1;
         LpcMessage->msg.MessageSize = FIELD_OFFSET(LPC_MESSAGE, Data[LpcMessage->msg.DataSize]);
-        lstrcpy((LPSTR)LpcMessage->msg.Data, REQUEST1);
+        strcpy((LPSTR)LpcMessage->msg.Data, REQUEST1);
 
         status = pNtRequestPort(PortHandle, &LpcMessage->msg);
         ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %x\n", status);
         ok(LpcMessage->msg.MessageType == 0, "Expected 0, got %d\n", LpcMessage->msg.MessageType);
-        ok(!lstrcmp((LPSTR)LpcMessage->msg.Data, REQUEST1),
+        ok(!strcmp((LPSTR)LpcMessage->msg.Data, REQUEST1),
            "Expected %s, got %s\n", REQUEST1, LpcMessage->msg.Data);
 
         /* Fill in the message */
         memset(LpcMessage, 0, size);
-        LpcMessage->msg.DataSize = lstrlen(REQUEST2) + 1;
+        LpcMessage->msg.DataSize = strlen(REQUEST2) + 1;
         LpcMessage->msg.MessageSize = FIELD_OFFSET(LPC_MESSAGE, Data[LpcMessage->msg.DataSize]);
-        lstrcpy((LPSTR)LpcMessage->msg.Data, REQUEST2);
+        strcpy((LPSTR)LpcMessage->msg.Data, REQUEST2);
 
         /* Send the message and wait for the reply */
         status = pNtRequestWaitReplyPort(PortHandle, &LpcMessage->msg, &out->msg);
         ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %x\n", status);
-        ok(!lstrcmp((LPSTR)out->msg.Data, REPLY), "Expected %s, got %s\n", REPLY, out->msg.Data);
+        ok(!strcmp((LPSTR)out->msg.Data, REPLY), "Expected %s, got %s\n", REPLY, out->msg.Data);
         ok(out->msg.MessageType == LPC_REPLY, "Expected LPC_REPLY, got %d\n", out->msg.MessageType);
     }
 
@@ -345,10 +345,10 @@ static void test_ports_server( HANDLE PortHandle )
 
             case LPC_DATAGRAM:
                 if (is_wow64)
-                    ok(!lstrcmp((LPSTR)LpcMessage->msg64.Data, REQUEST1),
+                    ok(!strcmp((LPSTR)LpcMessage->msg64.Data, REQUEST1),
                        "Expected %s, got %s\n", REQUEST1, LpcMessage->msg64.Data);
                 else
-                    ok(!lstrcmp((LPSTR)LpcMessage->msg.Data, REQUEST1),
+                    ok(!strcmp((LPSTR)LpcMessage->msg.Data, REQUEST1),
                        "Expected %s, got %s\n", REQUEST1, LpcMessage->msg.Data);
                 break;
 
index 076948e..1594665 100755 (executable)
@@ -115,7 +115,7 @@ typedef enum _KEY_VALUE_INFORMATION_CLASS {
 
 #endif
 
-static NTSTATUS (WINAPI * pRtlCreateUnicodeStringFromAsciiz)(PUNICODE_STRING, LPCSTR);
+static BOOLEAN  (WINAPI * pRtlCreateUnicodeStringFromAsciiz)(PUNICODE_STRING, LPCSTR);
 static void     (WINAPI * pRtlInitUnicodeString)(PUNICODE_STRING,PCWSTR);
 static NTSTATUS (WINAPI * pRtlFreeUnicodeString)(PUNICODE_STRING);
 static NTSTATUS (WINAPI * pNtDeleteValueKey)(IN HANDLE, IN PUNICODE_STRING);
@@ -134,7 +134,7 @@ static NTSTATUS (WINAPI * pNtSetValueKey)(HANDLE, const PUNICODE_STRING, ULONG,
                                ULONG, const void*, ULONG  );
 static NTSTATUS (WINAPI * pNtQueryInformationProcess)(HANDLE,PROCESSINFOCLASS,PVOID,ULONG,PULONG);
 static NTSTATUS (WINAPI * pRtlFormatCurrentUserKeyPath)(PUNICODE_STRING);
-static NTSTATUS (WINAPI * pRtlCreateUnicodeString)( PUNICODE_STRING, LPCWSTR);
+static BOOLEAN  (WINAPI * pRtlCreateUnicodeString)(PUNICODE_STRING, LPCWSTR);
 static LPVOID   (WINAPI * pRtlReAllocateHeap)(IN PVOID, IN ULONG, IN PVOID, IN ULONG);
 static NTSTATUS (WINAPI * pRtlAppendUnicodeToString)(PUNICODE_STRING, PCWSTR);
 static NTSTATUS (WINAPI * pRtlUnicodeStringToAnsiString)(PSTRING, PUNICODE_STRING, BOOL);
index 38909a3..6eb6de3 100755 (executable)
@@ -89,6 +89,7 @@ static IMAGE_BASE_RELOCATION *(WINAPI *pLdrProcessRelocationBlock)(void*,UINT,US
 static CHAR *    (WINAPI *pRtlIpv4AddressToStringA)(const IN_ADDR *, LPSTR);
 static NTSTATUS  (WINAPI *pRtlIpv4AddressToStringExA)(const IN_ADDR *, USHORT, LPSTR, PULONG);
 static NTSTATUS  (WINAPI *pRtlIpv4StringToAddressA)(PCSTR, BOOLEAN, PCSTR *, IN_ADDR *);
+static NTSTATUS  (WINAPI *pLdrAddRefDll)(ULONG, HMODULE);
 
 static HMODULE hkernel32 = 0;
 static BOOL      (WINAPI *pIsWow64Process)(HANDLE, PBOOL);
@@ -133,6 +134,7 @@ static void InitFunctionPtrs(void)
         pRtlIpv4AddressToStringA = (void *)GetProcAddress(hntdll, "RtlIpv4AddressToStringA");
         pRtlIpv4AddressToStringExA = (void *)GetProcAddress(hntdll, "RtlIpv4AddressToStringExA");
         pRtlIpv4StringToAddressA = (void *)GetProcAddress(hntdll, "RtlIpv4StringToAddressA");
+        pLdrAddRefDll = (void *)GetProcAddress(hntdll, "LdrAddRefDll");
     }
     hkernel32 = LoadLibraryA("kernel32.dll");
     ok(hkernel32 != 0, "LoadLibrary failed\n");
@@ -1486,6 +1488,61 @@ static void test_RtlIpv4StringToAddress(void)
     }
 }
 
+static void test_LdrAddRefDll(void)
+{
+    HMODULE mod, mod2;
+    NTSTATUS status;
+    BOOL ret;
+
+    if (!pLdrAddRefDll)
+    {
+        win_skip( "LdrAddRefDll not supported\n" );
+        return;
+    }
+
+    mod = LoadLibraryA("comctl32.dll");
+    ok(mod != NULL, "got %p\n", mod);
+    ret = FreeLibrary(mod);
+    ok(ret, "got %d\n", ret);
+
+    mod2 = GetModuleHandleA("comctl32.dll");
+    ok(mod2 == NULL, "got %p\n", mod2);
+
+    /* load, addref and release 2 times */
+    mod = LoadLibraryA("comctl32.dll");
+    ok(mod != NULL, "got %p\n", mod);
+    status = pLdrAddRefDll(0, mod);
+    ok(status == STATUS_SUCCESS, "got 0x%08x\n", status);
+    ret = FreeLibrary(mod);
+    ok(ret, "got %d\n", ret);
+
+    mod2 = GetModuleHandleA("comctl32.dll");
+    ok(mod2 != NULL, "got %p\n", mod2);
+    ret = FreeLibrary(mod);
+    ok(ret, "got %d\n", ret);
+
+    mod2 = GetModuleHandleA("comctl32.dll");
+    ok(mod2 == NULL, "got %p\n", mod2);
+
+    /* pin refcount */
+    mod = LoadLibraryA("comctl32.dll");
+    ok(mod != NULL, "got %p\n", mod);
+    status = pLdrAddRefDll(LDR_ADDREF_DLL_PIN, mod);
+    ok(status == STATUS_SUCCESS, "got 0x%08x\n", status);
+
+    ret = FreeLibrary(mod);
+    ok(ret, "got %d\n", ret);
+    ret = FreeLibrary(mod);
+    ok(ret, "got %d\n", ret);
+    ret = FreeLibrary(mod);
+    ok(ret, "got %d\n", ret);
+    ret = FreeLibrary(mod);
+    ok(ret, "got %d\n", ret);
+
+    mod2 = GetModuleHandleA("comctl32.dll");
+    ok(mod2 != NULL, "got %p\n", mod2);
+}
+
 START_TEST(rtl)
 {
     InitFunctionPtrs();
@@ -1510,4 +1567,5 @@ START_TEST(rtl)
     test_RtlIpv4AddressToString();
     test_RtlIpv4AddressToStringEx();
     test_RtlIpv4StringToAddress();
+    test_LdrAddRefDll();
 }
index 240fe61..dc27c3e 100755 (executable)
@@ -68,6 +68,8 @@ static NTSTATUS (WINAPI *pRtlGUIDFromString)(const UNICODE_STRING*,GUID*);
 static NTSTATUS (WINAPI *pRtlStringFromGUID)(const GUID*, UNICODE_STRING*);
 static BOOLEAN (WINAPI *pRtlIsTextUnicode)(LPVOID, INT, INT *);
 static NTSTATUS (WINAPI *pRtlHashUnicodeString)(PCUNICODE_STRING,BOOLEAN,ULONG,ULONG*);
+static NTSTATUS (WINAPI *pRtlUnicodeToUTF8N)(CHAR *, ULONG, ULONG *, const WCHAR *, ULONG);
+static NTSTATUS (WINAPI *pRtlUTF8ToUnicodeN)(WCHAR *, ULONG, ULONG *, const CHAR *, ULONG);
 
 /*static VOID (WINAPI *pRtlFreeOemString)(PSTRING);*/
 /*static VOID (WINAPI *pRtlCopyUnicodeString)(UNICODE_STRING *, const UNICODE_STRING *);*/
@@ -137,6 +139,8 @@ static void InitFunctionPtrs(void)
        pRtlStringFromGUID = (void *)GetProcAddress(hntdll, "RtlStringFromGUID");
        pRtlIsTextUnicode = (void *)GetProcAddress(hntdll, "RtlIsTextUnicode");
         pRtlHashUnicodeString = (void*)GetProcAddress(hntdll, "RtlHashUnicodeString");
+        pRtlUnicodeToUTF8N = (void*)GetProcAddress(hntdll, "RtlUnicodeToUTF8N");
+        pRtlUTF8ToUnicodeN = (void*)GetProcAddress(hntdll, "RtlUTF8ToUnicodeN");
     }
 }
 
@@ -1971,6 +1975,527 @@ static void test_RtlHashUnicodeString(void)
     }
 }
 
+struct unicode_to_utf8_test {
+    WCHAR unicode[128];
+    const char *expected;
+    NTSTATUS status;
+};
+
+static const struct unicode_to_utf8_test unicode_to_utf8[] = {
+    { { 0 }, "", STATUS_SUCCESS },
+    { { '-',0 }, "-", STATUS_SUCCESS },
+    { { 'h','e','l','l','o',0 }, "hello", STATUS_SUCCESS },
+    { { '-',0x7f,'-',0x80,'-',0xff,'-',0x100,'-',0 }, "-\x7F-\xC2\x80-\xC3\xBF-\xC4\x80-", STATUS_SUCCESS },
+    { { '-',0x7ff,'-',0x800,'-',0 }, "-\xDF\xBF-\xE0\xA0\x80-", STATUS_SUCCESS },
+    { { '-',0xd7ff,'-',0xe000,'-',0 }, "-\xED\x9F\xBF-\xEE\x80\x80-", STATUS_SUCCESS },
+                       /* 0x10000 */
+    { { '-',0xffff,'-',0xd800,0xdc00,'-',0 }, "-\xEF\xBF\xBF-\xF0\x90\x80\x80-", STATUS_SUCCESS },
+            /* 0x103ff */     /* 0x10400 */
+    { { '-',0xd800,0xdfff,'-',0xd801,0xdc00,'-',0 }, "-\xF0\x90\x8F\xBF-\xF0\x90\x90\x80-", STATUS_SUCCESS },
+            /* 0x10ffff */
+    { { '-',0xdbff,0xdfff,'-',0 }, "-\xF4\x8F\xBF\xBF-", STATUS_SUCCESS },
+    /* standalone lead surrogates become 0xFFFD */
+    { { '-',0xd800,'-',0xdbff,'-',0 }, "-\xEF\xBF\xBD-\xEF\xBF\xBD-", STATUS_SOME_NOT_MAPPED },
+    /* standalone trail surrogates become 0xFFFD */
+    { { '-',0xdc00,'-',0xdfff,'-',0 }, "-\xEF\xBF\xBD-\xEF\xBF\xBD-", STATUS_SOME_NOT_MAPPED },
+    /* reverse surrogate pair */
+    { { '-',0xdfff,0xdbff,'-',0 }, "-\xEF\xBF\xBD\xEF\xBF\xBD-", STATUS_SOME_NOT_MAPPED },
+    /* byte order marks */
+    { { '-',0xfeff,'-',0xfffe,'-',0 }, "-\xEF\xBB\xBF-\xEF\xBF\xBE-", STATUS_SUCCESS },
+    { { 0xfeff,'-',0 }, "\xEF\xBB\xBF-", STATUS_SUCCESS },
+    { { 0xfffe,'-',0 }, "\xEF\xBF\xBE-", STATUS_SUCCESS },
+    /* invalid code point */
+    { { 0xffff,'-',0 }, "\xEF\xBF\xBF-", STATUS_SUCCESS },
+    /* canonically equivalent representations -- no normalization should happen */
+    { { '-',0x1e09,'-',0 }, "-\xE1\xB8\x89-", STATUS_SUCCESS },
+    { { '-',0x0107,0x0327,'-',0 }, "-\xC4\x87\xCC\xA7-", STATUS_SUCCESS },
+    { { '-',0x00e7,0x0301,'-',0 }, "-\xC3\xA7\xCC\x81-", STATUS_SUCCESS },
+    { { '-',0x0063,0x0327,0x0301,'-',0 }, "-\x63\xCC\xA7\xCC\x81-", STATUS_SUCCESS },
+    { { '-',0x0063,0x0301,0x0327,'-',0 }, "-\x63\xCC\x81\xCC\xA7-", STATUS_SUCCESS },
+};
+
+static void utf8_expect_(const unsigned char *out_string, ULONG buflen, ULONG out_bytes,
+                         const WCHAR *in_string, ULONG in_bytes,
+                         NTSTATUS expect_status, int line)
+{
+    NTSTATUS status;
+    ULONG bytes_out;
+    char buffer[128];
+    unsigned char *buf = (unsigned char *)buffer;
+    unsigned int i;
+
+    if (buflen == (ULONG)-1)
+        buflen = sizeof(buffer);
+    bytes_out = 0x55555555;
+    memset(buffer, 0x55, sizeof(buffer));
+    status = pRtlUnicodeToUTF8N(
+        out_string ? buffer : NULL, buflen, &bytes_out,
+        in_string, in_bytes);
+    ok_(__FILE__, line)(status == expect_status, "status = 0x%x\n", status);
+    ok_(__FILE__, line)(bytes_out == out_bytes, "bytes_out = %u\n", bytes_out);
+    if (out_string)
+    {
+        for (i = 0; i < bytes_out; i++)
+            ok_(__FILE__, line)(buf[i] == out_string[i],
+                                "buffer[%d] = 0x%x, expected 0x%x\n",
+                                i, buf[i], out_string[i]);
+        for (; i < sizeof(buffer); i++)
+            ok_(__FILE__, line)(buf[i] == 0x55,
+                                "buffer[%d] = 0x%x, expected 0x55\n",
+                                i, buf[i]);
+    }
+}
+#define utf8_expect(out_string, buflen, out_bytes, in_string, in_bytes, expect_status) \
+        utf8_expect_(out_string, buflen, out_bytes, in_string, in_bytes, expect_status, __LINE__)
+
+static void test_RtlUnicodeToUTF8N(void)
+{
+    NTSTATUS status;
+    ULONG bytes_out;
+    ULONG bytes_out_array[2];
+    void * const invalid_pointer = (void *)0x8;
+    char buffer[128];
+    const WCHAR empty_string[] = { 0 };
+    const WCHAR test_string[] = { 'A',0,'a','b','c','d','e','f','g',0 };
+    const WCHAR special_string[] = { 'X',0x80,0xd800,0 };
+    const unsigned char special_expected[] = { 'X',0xc2,0x80,0xef,0xbf,0xbd,0 };
+    unsigned int input_len;
+    const unsigned int test_count = sizeof(unicode_to_utf8) / sizeof(unicode_to_utf8[0]);
+    unsigned int i;
+
+    if (!pRtlUnicodeToUTF8N)
+    {
+        skip("RtlUnicodeToUTF8N unavailable\n");
+        return;
+    }
+
+    /* show that bytes_out is really ULONG */
+    memset(bytes_out_array, 0x55, sizeof(bytes_out_array));
+    status = pRtlUnicodeToUTF8N(NULL, 0, bytes_out_array, empty_string, 0);
+    ok(status == STATUS_SUCCESS, "status = 0x%x\n", status);
+    ok(bytes_out_array[0] == 0x00000000, "Got 0x%x\n", bytes_out_array[0]);
+    ok(bytes_out_array[1] == 0x55555555, "Got 0x%x\n", bytes_out_array[1]);
+
+    /* parameter checks */
+    status = pRtlUnicodeToUTF8N(NULL, 0, NULL, NULL, 0);
+    ok(status == STATUS_INVALID_PARAMETER_4, "status = 0x%x\n", status);
+
+    status = pRtlUnicodeToUTF8N(NULL, 0, NULL, empty_string, 0);
+    ok(status == STATUS_INVALID_PARAMETER, "status = 0x%x\n", status);
+
+    bytes_out = 0x55555555;
+    status = pRtlUnicodeToUTF8N(NULL, 0, &bytes_out, NULL, 0);
+    ok(status == STATUS_INVALID_PARAMETER_4, "status = 0x%x\n", status);
+    ok(bytes_out == 0x55555555, "bytes_out = 0x%x\n", bytes_out);
+
+    bytes_out = 0x55555555;
+    status = pRtlUnicodeToUTF8N(NULL, 0, &bytes_out, invalid_pointer, 0);
+    ok(status == STATUS_SUCCESS, "status = 0x%x\n", status);
+    ok(bytes_out == 0, "bytes_out = 0x%x\n", bytes_out);
+
+    bytes_out = 0x55555555;
+    status = pRtlUnicodeToUTF8N(NULL, 0, &bytes_out, empty_string, 0);
+    ok(status == STATUS_SUCCESS, "status = 0x%x\n", status);
+    ok(bytes_out == 0, "bytes_out = 0x%x\n", bytes_out);
+
+    bytes_out = 0x55555555;
+    status = pRtlUnicodeToUTF8N(NULL, 0, &bytes_out, test_string, 0);
+    ok(status == STATUS_SUCCESS, "status = 0x%x\n", status);
+    ok(bytes_out == 0, "bytes_out = 0x%x\n", bytes_out);
+
+    bytes_out = 0x55555555;
+    status = pRtlUnicodeToUTF8N(NULL, 0, &bytes_out, empty_string, 1);
+    ok(status == STATUS_SUCCESS, "status = 0x%x\n", status);
+    ok(bytes_out == 0, "bytes_out = 0x%x\n", bytes_out);
+
+    bytes_out = 0x55555555;
+    status = pRtlUnicodeToUTF8N(invalid_pointer, 0, &bytes_out, empty_string, 1);
+    ok(status == STATUS_INVALID_PARAMETER_5, "status = 0x%x\n", status);
+    ok(bytes_out == 0x55555555, "bytes_out = 0x%x\n", bytes_out);
+
+    bytes_out = 0x55555555;
+    status = pRtlUnicodeToUTF8N(invalid_pointer, 8, &bytes_out, empty_string, 1);
+    ok(status == STATUS_INVALID_PARAMETER_5, "status = 0x%x\n", status);
+    ok(bytes_out == 0x55555555, "bytes_out = 0x%x\n", bytes_out);
+
+    /* length output with special chars */
+#define length_expect(in_chars, out_bytes, expect_status) \
+        utf8_expect_(NULL, 0, out_bytes, \
+                     special_string, in_chars * sizeof(WCHAR), \
+                     expect_status, __LINE__)
+
+    length_expect(0, 0, STATUS_SUCCESS);
+    length_expect(1, 1, STATUS_SUCCESS);
+    length_expect(2, 3, STATUS_SUCCESS);
+    length_expect(3, 6, STATUS_SOME_NOT_MAPPED);
+    length_expect(4, 7, STATUS_SOME_NOT_MAPPED);
+#undef length_expect
+
+    /* output truncation */
+#define truncate_expect(buflen, out_bytes, expect_status) \
+        utf8_expect_(special_expected, buflen, out_bytes, \
+                     special_string, sizeof(special_string), \
+                     expect_status, __LINE__)
+
+    truncate_expect(0, 0, STATUS_BUFFER_TOO_SMALL);
+    truncate_expect(1, 1, STATUS_BUFFER_TOO_SMALL);
+    truncate_expect(2, 1, STATUS_BUFFER_TOO_SMALL);
+    truncate_expect(3, 3, STATUS_BUFFER_TOO_SMALL);
+    truncate_expect(4, 3, STATUS_BUFFER_TOO_SMALL);
+    truncate_expect(5, 3, STATUS_BUFFER_TOO_SMALL);
+    truncate_expect(6, 6, STATUS_BUFFER_TOO_SMALL);
+    truncate_expect(7, 7, STATUS_SOME_NOT_MAPPED);
+#undef truncate_expect
+
+    /* conversion behavior with varying input length */
+    for (input_len = 0; input_len <= sizeof(test_string); input_len++) {
+        /* no output buffer, just length */
+        utf8_expect(NULL, 0, input_len / sizeof(WCHAR),
+                    test_string, input_len, STATUS_SUCCESS);
+
+        /* write output */
+        bytes_out = 0x55555555;
+        memset(buffer, 0x55, sizeof(buffer));
+        status = pRtlUnicodeToUTF8N(
+            buffer, sizeof(buffer), &bytes_out,
+            test_string, input_len);
+        if (input_len % sizeof(WCHAR) == 0) {
+            ok(status == STATUS_SUCCESS,
+               "(len %u): status = 0x%x\n", input_len, status);
+            ok(bytes_out == input_len / sizeof(WCHAR),
+               "(len %u): bytes_out = 0x%x\n", input_len, bytes_out);
+            for (i = 0; i < bytes_out; i++) {
+                ok(buffer[i] == test_string[i],
+                   "(len %u): buffer[%d] = 0x%x, expected 0x%x\n",
+                   input_len, i, buffer[i], test_string[i]);
+            }
+            for (; i < sizeof(buffer); i++) {
+                ok(buffer[i] == 0x55,
+                   "(len %u): buffer[%d] = 0x%x\n", input_len, i, buffer[i]);
+            }
+        } else {
+            ok(status == STATUS_INVALID_PARAMETER_5,
+               "(len %u): status = 0x%x\n", input_len, status);
+            ok(bytes_out == 0x55555555,
+               "(len %u): bytes_out = 0x%x\n", input_len, bytes_out);
+            for (i = 0; i < sizeof(buffer); i++) {
+                ok(buffer[i] == 0x55,
+                   "(len %u): buffer[%d] = 0x%x\n", input_len, i, buffer[i]);
+            }
+        }
+    }
+
+    /* test cases for special characters */
+    for (i = 0; i < test_count; i++) {
+        bytes_out = 0x55555555;
+        memset(buffer, 0x55, sizeof(buffer));
+        status = pRtlUnicodeToUTF8N(
+            buffer, sizeof(buffer), &bytes_out,
+            unicode_to_utf8[i].unicode, lstrlenW(unicode_to_utf8[i].unicode) * sizeof(WCHAR));
+        ok(status == unicode_to_utf8[i].status,
+           "(test %d): status is 0x%x, expected 0x%x\n",
+           i, status, unicode_to_utf8[i].status);
+        ok(bytes_out == strlen(unicode_to_utf8[i].expected),
+           "(test %d): bytes_out is %u, expected %u\n",
+           i, bytes_out, lstrlenA(unicode_to_utf8[i].expected));
+        ok(!memcmp(buffer, unicode_to_utf8[i].expected, bytes_out),
+           "(test %d): got \"%.*s\", expected \"%s\"\n",
+           i, bytes_out, buffer, unicode_to_utf8[i].expected);
+        ok(buffer[bytes_out] == 0x55,
+           "(test %d): behind string: 0x%x\n", i, buffer[bytes_out]);
+
+        /* same test but include the null terminator */
+        bytes_out = 0x55555555;
+        memset(buffer, 0x55, sizeof(buffer));
+        status = pRtlUnicodeToUTF8N(
+            buffer, sizeof(buffer), &bytes_out,
+            unicode_to_utf8[i].unicode, (lstrlenW(unicode_to_utf8[i].unicode) + 1) * sizeof(WCHAR));
+        ok(status == unicode_to_utf8[i].status,
+           "(test %d): status is 0x%x, expected 0x%x\n",
+           i, status, unicode_to_utf8[i].status);
+        ok(bytes_out == strlen(unicode_to_utf8[i].expected) + 1,
+           "(test %d): bytes_out is %u, expected %u\n",
+           i, bytes_out, lstrlenA(unicode_to_utf8[i].expected) + 1);
+        ok(!memcmp(buffer, unicode_to_utf8[i].expected, bytes_out),
+           "(test %d): got \"%.*s\", expected \"%s\"\n",
+           i, bytes_out, buffer, unicode_to_utf8[i].expected);
+        ok(buffer[bytes_out] == 0x55,
+           "(test %d): behind string: 0x%x\n", i, buffer[bytes_out]);
+    }
+}
+
+struct utf8_to_unicode_test {
+    const char *utf8;
+    WCHAR expected[128];
+    NTSTATUS status;
+};
+
+static const struct utf8_to_unicode_test utf8_to_unicode[] = {
+    { "", { 0 }, STATUS_SUCCESS },
+    { "-", { '-',0 }, STATUS_SUCCESS },
+    { "hello", { 'h','e','l','l','o',0 }, STATUS_SUCCESS },
+    /* first and last of each range */
+    { "-\x7F-\xC2\x80-\xC3\xBF-\xC4\x80-", { '-',0x7f,'-',0x80,'-',0xff,'-',0x100,'-',0 }, STATUS_SUCCESS },
+    { "-\xDF\xBF-\xE0\xA0\x80-", { '-',0x7ff,'-',0x800,'-',0 }, STATUS_SUCCESS },
+    { "-\xED\x9F\xBF-\xEE\x80\x80-", { '-',0xd7ff,'-',0xe000,'-',0 }, STATUS_SUCCESS },
+                     /*   0x10000  */
+    { "-\xEF\xBF\xBF-\xF0\x90\x80\x80-", { '-',0xffff,'-',0xd800,0xdc00,'-',0 }, STATUS_SUCCESS },
+        /*   0x103ff  */ /*   0x10400  */
+    { "-\xF0\x90\x8F\xBF-\xF0\x90\x90\x80-", { '-',0xd800,0xdfff,'-',0xd801,0xdc00,'-',0 }, STATUS_SUCCESS },
+        /*  0x10ffff  */
+    { "-\xF4\x8F\xBF\xBF-", { '-',0xdbff,0xdfff,'-',0 }, STATUS_SUCCESS },
+    /* standalone surrogate code points */
+        /* 0xd800 */ /* 0xdbff */
+    { "-\xED\xA0\x80-\xED\xAF\xBF-", { '-',0xfffd,0xfffd,'-',0xfffd,0xfffd,'-',0 }, STATUS_SOME_NOT_MAPPED },
+        /* 0xdc00 */ /* 0xdfff */
+    { "-\xED\xB0\x80-\xED\xBF\xBF-", { '-',0xfffd,0xfffd,'-',0xfffd,0xfffd,'-',0 }, STATUS_SOME_NOT_MAPPED },
+    /* UTF-8 encoded surrogate pair */
+        /* 0xdbff *//* 0xdfff */
+    { "-\xED\xAF\xBF\xED\xBF\xBF-", { '-',0xfffd,0xfffd,0xfffd,0xfffd,'-',0 }, STATUS_SOME_NOT_MAPPED },
+    /* reverse surrogate pair */
+        /* 0xdfff *//* 0xdbff */
+    { "-\xED\xBF\xBF\xED\xAF\xBF-", { '-',0xfffd,0xfffd,0xfffd,0xfffd,'-',0 }, STATUS_SOME_NOT_MAPPED },
+    /* code points outside the UTF-16 range */
+        /*  0x110000  */
+    { "-\xF4\x90\x80\x80-", { '-',0xfffd,0xfffd,0xfffd,'-',0 }, STATUS_SOME_NOT_MAPPED },
+        /*  0x1fffff  */
+    { "-\xF7\xBF\xBF\xBF-", { '-',0xfffd,0xfffd,0xfffd,0xfffd,'-',0 }, STATUS_SOME_NOT_MAPPED },
+        /*     0x200000   */
+    { "-\xFA\x80\x80\x80\x80-", { '-',0xfffd,0xfffd,0xfffd,0xfffd,0xfffd,'-',0 }, STATUS_SOME_NOT_MAPPED },
+        /*    0x3ffffff   */
+    { "-\xFB\xBF\xBF\xBF\xBF-", { '-',0xfffd,0xfffd,0xfffd,0xfffd,0xfffd,'-',0 }, STATUS_SOME_NOT_MAPPED },
+        /*      0x4000000     */
+    { "-\xFC\x84\x80\x80\x80\x80-", { '-',0xfffd,0xfffd,0xfffd,0xfffd,0xfffd,0xfffd,'-',0 }, STATUS_SOME_NOT_MAPPED },
+        /*     0x7fffffff     */
+    { "-\xFD\xBF\xBF\xBF\xBF\xBF-", { '-',0xfffd,0xfffd,0xfffd,0xfffd,0xfffd,0xfffd,'-',0 }, STATUS_SOME_NOT_MAPPED },
+    /* overlong encodings of each length for -, NUL, and the highest possible value */
+    { "-\xC0\xAD-\xC0\x80-\xC1\xBF-", { '-',0xfffd,0xfffd,'-',0xfffd,0xfffd,'-',0xfffd,0xfffd,'-',0 }, STATUS_SOME_NOT_MAPPED },
+    { "-\xE0\x80\xAD-\xE0\x80\x80-\xE0\x9F\xBF-", { '-',0xfffd,0xfffd,'-',0xfffd,0xfffd,'-',0xfffd,0xfffd,'-',0 }, STATUS_SOME_NOT_MAPPED },
+    { "-\xF0\x80\x80\xAD-", { '-',0xfffd,0xfffd,0xfffd,'-',0 }, STATUS_SOME_NOT_MAPPED },
+    { "-\xF0\x80\x80\x80-", { '-',0xfffd,0xfffd,0xfffd,'-',0 }, STATUS_SOME_NOT_MAPPED },
+    { "-\xF0\x8F\xBF\xBF-", { '-',0xfffd,0xfffd,0xfffd,'-',0 }, STATUS_SOME_NOT_MAPPED },
+    { "-\xF8\x80\x80\x80\xAD-", { '-',0xfffd,0xfffd,0xfffd,0xfffd,0xfffd,'-',0 }, STATUS_SOME_NOT_MAPPED },
+    { "-\xF8\x80\x80\x80\x80-", { '-',0xfffd,0xfffd,0xfffd,0xfffd,0xfffd,'-',0 }, STATUS_SOME_NOT_MAPPED },
+    { "-\xF8\x87\xBF\xBF\xBF-", { '-',0xfffd,0xfffd,0xfffd,0xfffd,0xfffd,'-',0 }, STATUS_SOME_NOT_MAPPED },
+    { "-\xFC\x80\x80\x80\x80\xAD-", { '-',0xfffd,0xfffd,0xfffd,0xfffd,0xfffd,0xfffd,'-',0 }, STATUS_SOME_NOT_MAPPED },
+    { "-\xFC\x80\x80\x80\x80\x80-", { '-',0xfffd,0xfffd,0xfffd,0xfffd,0xfffd,0xfffd,'-',0 }, STATUS_SOME_NOT_MAPPED },
+    { "-\xFC\x83\xBF\xBF\xBF\xBF-", { '-',0xfffd,0xfffd,0xfffd,0xfffd,0xfffd,0xfffd,'-',0 }, STATUS_SOME_NOT_MAPPED },
+    /* invalid bytes */
+    { "\xFE", { 0xfffd,0 }, STATUS_SOME_NOT_MAPPED },
+    { "\xFF", { 0xfffd,0 }, STATUS_SOME_NOT_MAPPED },
+    { "\xFE\xBF\xBF\xBF\xBF\xBF\xBF\xBF\xBF", { 0xfffd,0xfffd,0xfffd,0xfffd,0xfffd,0xfffd,0xfffd,0xfffd,0xfffd,0 }, STATUS_SOME_NOT_MAPPED },
+    { "\xFF\xBF\xBF\xBF\xBF\xBF\xBF\xBF\xBF", { 0xfffd,0xfffd,0xfffd,0xfffd,0xfffd,0xfffd,0xfffd,0xfffd,0xfffd,0 }, STATUS_SOME_NOT_MAPPED },
+    { "\xFF\x80\x80\x80\x80\x80\x80\x80\x80", { 0xfffd,0xfffd,0xfffd,0xfffd,0xfffd,0xfffd,0xfffd,0xfffd,0xfffd,0 }, STATUS_SOME_NOT_MAPPED },
+    { "\xFF\x40\x80\x80\x80\x80\x80\x80\x80", { 0xfffd,0x40,0xfffd,0xfffd,0xfffd,0xfffd,0xfffd,0xfffd,0xfffd,0 }, STATUS_SOME_NOT_MAPPED },
+    /* lone continuation bytes */
+    { "\x80", { 0xfffd,0 }, STATUS_SOME_NOT_MAPPED },
+    { "\x80\x80", { 0xfffd,0xfffd,0 }, STATUS_SOME_NOT_MAPPED },
+    { "\xBF", { 0xfffd,0 }, STATUS_SOME_NOT_MAPPED },
+    { "\xBF\xBF", { 0xfffd,0xfffd,0 }, STATUS_SOME_NOT_MAPPED },
+    /* incomplete sequences */
+    { "\xC2-", { 0xfffd,'-',0 }, STATUS_SOME_NOT_MAPPED },
+    { "\xE0\xA0-", { 0xfffd,'-',0 }, STATUS_SOME_NOT_MAPPED },
+    { "\xF0\x90\x80-", { 0xfffd,'-',0 }, STATUS_SOME_NOT_MAPPED },
+    { "\xF4\x8F\xBF-", { 0xfffd,'-',0 }, STATUS_SOME_NOT_MAPPED },
+    { "\xFA\x80\x80\x80-", { 0xfffd,0xfffd,0xfffd,0xfffd,'-',0 }, STATUS_SOME_NOT_MAPPED },
+    { "\xFC\x84\x80\x80\x80-", { 0xfffd,0xfffd,0xfffd,0xfffd,0xfffd,'-',0 }, STATUS_SOME_NOT_MAPPED },
+    /* multibyte sequence followed by lone continuation byte */
+    { "\xE0\xA0\x80\x80-", { 0x800,0xfffd,'-',0 }, STATUS_SOME_NOT_MAPPED },
+    /* byte order marks */
+    { "-\xEF\xBB\xBF-\xEF\xBF\xBE-", { '-',0xfeff,'-',0xfffe,'-',0 }, STATUS_SUCCESS },
+    { "\xEF\xBB\xBF-", { 0xfeff,'-',0 }, STATUS_SUCCESS },
+    { "\xEF\xBF\xBE-", { 0xfffe,'-',0 }, STATUS_SUCCESS },
+    /* invalid code point */
+       /* 0xffff */
+    { "\xEF\xBF\xBF-", { 0xffff,'-',0 }, STATUS_SUCCESS },
+    /* canonically equivalent representations -- no normalization should happen */
+    { "-\xE1\xB8\x89-", { '-',0x1e09,'-',0 }, STATUS_SUCCESS },
+    { "-\xC4\x87\xCC\xA7-", { '-',0x0107,0x0327,'-',0 }, STATUS_SUCCESS },
+    { "-\xC3\xA7\xCC\x81-", { '-',0x00e7,0x0301,'-',0 }, STATUS_SUCCESS },
+    { "-\x63\xCC\xA7\xCC\x81-", { '-',0x0063,0x0327,0x0301,'-',0 }, STATUS_SUCCESS },
+    { "-\x63\xCC\x81\xCC\xA7-", { '-',0x0063,0x0301,0x0327,'-',0 }, STATUS_SUCCESS },
+};
+
+static void unicode_expect_(const WCHAR *out_string, ULONG buflen, ULONG out_chars,
+                            const char *in_string, ULONG in_chars,
+                            NTSTATUS expect_status, int line)
+{
+    NTSTATUS status;
+    ULONG bytes_out;
+    WCHAR buffer[128];
+    unsigned int i;
+
+    if (buflen == (ULONG)-1)
+        buflen = sizeof(buffer);
+    bytes_out = 0x55555555;
+    memset(buffer, 0x55, sizeof(buffer));
+    status = pRtlUTF8ToUnicodeN(
+        out_string ? buffer : NULL, buflen, &bytes_out,
+        in_string, in_chars);
+    ok_(__FILE__, line)(status == expect_status, "status = 0x%x\n", status);
+    ok_(__FILE__, line)(bytes_out == out_chars * sizeof(WCHAR),
+                        "bytes_out = %u, expected %u\n", bytes_out, out_chars * (ULONG)sizeof(WCHAR));
+    if (out_string)
+    {
+        for (i = 0; i < bytes_out / sizeof(WCHAR); i++)
+            ok_(__FILE__, line)(buffer[i] == out_string[i],
+                                "buffer[%d] = 0x%x, expected 0x%x\n",
+                                i, buffer[i], out_string[i]);
+        for (; i < sizeof(buffer) / sizeof(WCHAR); i++)
+            ok_(__FILE__, line)(buffer[i] == 0x5555,
+                                "buffer[%d] = 0x%x, expected 0x5555\n",
+                                i, buffer[i]);
+    }
+}
+#define unicode_expect(out_string, buflen, out_chars, in_string, in_chars, expect_status) \
+        unicode_expect_(out_string, buflen, out_chars, in_string, in_chars, expect_status, __LINE__)
+
+static void test_RtlUTF8ToUnicodeN(void)
+{
+    NTSTATUS status;
+    ULONG bytes_out;
+    ULONG bytes_out_array[2];
+    void * const invalid_pointer = (void *)0x8;
+    WCHAR buffer[128];
+    const char empty_string[] = "";
+    const char test_string[] = "A\0abcdefg";
+    const WCHAR test_stringW[] = {'A',0,'a','b','c','d','e','f','g',0 };
+    const char special_string[] = { 'X',0xc2,0x80,0xF0,0x90,0x80,0x80,0 };
+    const WCHAR special_expected[] = { 'X',0x80,0xd800,0xdc00,0 };
+    unsigned int input_len;
+    const unsigned int test_count = sizeof(utf8_to_unicode) / sizeof(utf8_to_unicode[0]);
+    unsigned int i;
+
+    if (!pRtlUTF8ToUnicodeN)
+    {
+        skip("RtlUTF8ToUnicodeN unavailable\n");
+        return;
+    }
+
+    /* show that bytes_out is really ULONG */
+    memset(bytes_out_array, 0x55, sizeof(bytes_out_array));
+    status = pRtlUTF8ToUnicodeN(NULL, 0, bytes_out_array, empty_string, 0);
+    ok(status == STATUS_SUCCESS, "status = 0x%x\n", status);
+    ok(bytes_out_array[0] == 0x00000000, "Got 0x%x\n", bytes_out_array[0]);
+    ok(bytes_out_array[1] == 0x55555555, "Got 0x%x\n", bytes_out_array[1]);
+
+    /* parameter checks */
+    status = pRtlUTF8ToUnicodeN(NULL, 0, NULL, NULL, 0);
+    ok(status == STATUS_INVALID_PARAMETER_4, "status = 0x%x\n", status);
+
+    status = pRtlUTF8ToUnicodeN(NULL, 0, NULL, empty_string, 0);
+    ok(status == STATUS_INVALID_PARAMETER, "status = 0x%x\n", status);
+
+    bytes_out = 0x55555555;
+    status = pRtlUTF8ToUnicodeN(NULL, 0, &bytes_out, NULL, 0);
+    ok(status == STATUS_INVALID_PARAMETER_4, "status = 0x%x\n", status);
+    ok(bytes_out == 0x55555555, "bytes_out = 0x%x\n", bytes_out);
+
+    bytes_out = 0x55555555;
+    status = pRtlUTF8ToUnicodeN(NULL, 0, &bytes_out, invalid_pointer, 0);
+    ok(status == STATUS_SUCCESS, "status = 0x%x\n", status);
+    ok(bytes_out == 0, "bytes_out = 0x%x\n", bytes_out);
+
+    bytes_out = 0x55555555;
+    status = pRtlUTF8ToUnicodeN(NULL, 0, &bytes_out, empty_string, 0);
+    ok(status == STATUS_SUCCESS, "status = 0x%x\n", status);
+    ok(bytes_out == 0, "bytes_out = 0x%x\n", bytes_out);
+
+    bytes_out = 0x55555555;
+    status = pRtlUTF8ToUnicodeN(NULL, 0, &bytes_out, test_string, 0);
+    ok(status == STATUS_SUCCESS, "status = 0x%x\n", status);
+    ok(bytes_out == 0, "bytes_out = 0x%x\n", bytes_out);
+
+    bytes_out = 0x55555555;
+    status = pRtlUTF8ToUnicodeN(NULL, 0, &bytes_out, empty_string, 1);
+    ok(status == STATUS_SUCCESS, "status = 0x%x\n", status);
+    ok(bytes_out == sizeof(WCHAR), "bytes_out = 0x%x\n", bytes_out);
+
+    /* length output with special chars */
+#define length_expect(in_chars, out_chars, expect_status) \
+        unicode_expect_(NULL, 0, out_chars, special_string, in_chars, \
+                        expect_status, __LINE__)
+
+    length_expect(0, 0, STATUS_SUCCESS);
+    length_expect(1, 1, STATUS_SUCCESS);
+    length_expect(2, 2, STATUS_SOME_NOT_MAPPED);
+    length_expect(3, 2, STATUS_SUCCESS);
+    length_expect(4, 3, STATUS_SOME_NOT_MAPPED);
+    length_expect(5, 3, STATUS_SOME_NOT_MAPPED);
+    length_expect(6, 3, STATUS_SOME_NOT_MAPPED);
+    length_expect(7, 4, STATUS_SUCCESS);
+    length_expect(8, 5, STATUS_SUCCESS);
+#undef length_expect
+
+    /* output truncation */
+#define truncate_expect(buflen, out_chars, expect_status) \
+        unicode_expect_(special_expected, buflen, out_chars, \
+                        special_string, sizeof(special_string), \
+                        expect_status, __LINE__)
+
+    truncate_expect( 0, 0, STATUS_BUFFER_TOO_SMALL);
+    truncate_expect( 1, 0, STATUS_BUFFER_TOO_SMALL);
+    truncate_expect( 2, 1, STATUS_BUFFER_TOO_SMALL);
+    truncate_expect( 3, 1, STATUS_BUFFER_TOO_SMALL);
+    truncate_expect( 4, 2, STATUS_BUFFER_TOO_SMALL);
+    truncate_expect( 5, 2, STATUS_BUFFER_TOO_SMALL);
+    truncate_expect( 6, 3, STATUS_BUFFER_TOO_SMALL);
+    truncate_expect( 7, 3, STATUS_BUFFER_TOO_SMALL);
+    truncate_expect( 8, 4, STATUS_BUFFER_TOO_SMALL);
+    truncate_expect( 9, 4, STATUS_BUFFER_TOO_SMALL);
+    truncate_expect(10, 5, STATUS_SUCCESS);
+#undef truncate_expect
+
+    /* conversion behavior with varying input length */
+    for (input_len = 0; input_len <= sizeof(test_string); input_len++) {
+        /* no output buffer, just length */
+        unicode_expect(NULL, 0, input_len,
+                       test_string, input_len, STATUS_SUCCESS);
+
+        /* write output */
+        unicode_expect(test_stringW, -1, input_len,
+                       test_string, input_len, STATUS_SUCCESS);
+    }
+
+    /* test cases for special characters */
+    for (i = 0; i < test_count; i++) {
+        bytes_out = 0x55555555;
+        memset(buffer, 0x55, sizeof(buffer));
+        status = pRtlUTF8ToUnicodeN(
+            buffer, sizeof(buffer), &bytes_out,
+            utf8_to_unicode[i].utf8, strlen(utf8_to_unicode[i].utf8));
+        ok(status == utf8_to_unicode[i].status,
+           "(test %d): status is 0x%x, expected 0x%x\n",
+           i, status, utf8_to_unicode[i].status);
+        ok(bytes_out == lstrlenW(utf8_to_unicode[i].expected) * sizeof(WCHAR),
+           "(test %d): bytes_out is %u, expected %u\n",
+           i, bytes_out, lstrlenW(utf8_to_unicode[i].expected) * (ULONG)sizeof(WCHAR));
+        ok(!memcmp(buffer, utf8_to_unicode[i].expected, bytes_out),
+           "(test %d): got %s, expected %s\n",
+           i, wine_dbgstr_wn(buffer, bytes_out / sizeof(WCHAR)), wine_dbgstr_w(utf8_to_unicode[i].expected));
+        ok(buffer[bytes_out] == 0x5555,
+           "(test %d): behind string: 0x%x\n", i, buffer[bytes_out]);
+
+        /* same test but include the null terminator */
+        bytes_out = 0x55555555;
+        memset(buffer, 0x55, sizeof(buffer));
+        status = pRtlUTF8ToUnicodeN(
+            buffer, sizeof(buffer), &bytes_out,
+            utf8_to_unicode[i].utf8, strlen(utf8_to_unicode[i].utf8) + 1);
+        ok(status == utf8_to_unicode[i].status,
+           "(test %d): status is 0x%x, expected 0x%x\n",
+           i, status, utf8_to_unicode[i].status);
+        ok(bytes_out == (lstrlenW(utf8_to_unicode[i].expected) + 1) * sizeof(WCHAR),
+           "(test %d): bytes_out is %u, expected %u\n",
+           i, bytes_out, (lstrlenW(utf8_to_unicode[i].expected) + 1) * (ULONG)sizeof(WCHAR));
+        ok(!memcmp(buffer, utf8_to_unicode[i].expected, bytes_out),
+           "(test %d): got %s, expected %s\n",
+           i, wine_dbgstr_wn(buffer, bytes_out / sizeof(WCHAR)), wine_dbgstr_w(utf8_to_unicode[i].expected));
+        ok(buffer[bytes_out] == 0x5555,
+           "(test %d): behind string: 0x%x\n", i, buffer[bytes_out]);
+    }
+}
+
 START_TEST(rtlstr)
 {
     InitFunctionPtrs();
@@ -2004,4 +2529,6 @@ START_TEST(rtlstr)
        test_RtlDowncaseUnicodeString();
     }
     test_RtlHashUnicodeString();
+    test_RtlUnicodeToUTF8N();
+    test_RtlUTF8ToUnicodeN();
 }
index 9349f5d..56b7584 100755 (executable)
@@ -59,6 +59,7 @@ static LPWSTR   (WINAPIV *p_wcsrchr)(LPCWSTR, WCHAR);
 
 static void     (__cdecl *p_qsort)(void *,size_t,size_t, int(__cdecl *compar)(const void *, const void *) );
 static void*    (__cdecl *p_bsearch)(void *,void*,size_t,size_t, int(__cdecl *compar)(const void *, const void *) );
+static int      (__cdecl *p__snprintf)(char *, size_t, const char *, ...);
 
 
 static void InitFunctionPtrs(void)
@@ -96,6 +97,8 @@ static void InitFunctionPtrs(void)
        p_wcsrchr= (void *)GetProcAddress(hntdll, "wcsrchr");
        p_qsort= (void *)GetProcAddress(hntdll, "qsort");
        p_bsearch= (void *)GetProcAddress(hntdll, "bsearch");
+
+        p__snprintf = (void *)GetProcAddress(hntdll, "_snprintf");
     } /* if */
 }
 
@@ -1270,6 +1273,37 @@ static void test_bsearch(void)
     }
 }
 
+static void test__snprintf(void)
+{
+    const char *origstring = "XXXXXXXXXXXX";
+    const char *teststring = "hello world";
+    char buffer[32];
+    int res;
+
+    res = p__snprintf(NULL, 0, teststring);
+    ok(res == lstrlenA(teststring) || broken(res == -1) /* <= w2k */,
+       "_snprintf returned %d, expected %d.\n", res, lstrlenA(teststring));
+
+    if (res != -1)
+    {
+        res = p__snprintf(NULL, 1, teststring);
+        ok(res == lstrlenA(teststring) /* WinXP */ || res < 0 /* Vista and greater */,
+           "_snprintf returned %d, expected %d or < 0.\n", res, lstrlenA(teststring));
+    }
+    res = p__snprintf(buffer, strlen(teststring) - 1, teststring);
+    ok(res < 0, "_snprintf returned %d, expected < 0.\n", res);
+
+    strcpy(buffer,  origstring);
+    res = p__snprintf(buffer, strlen(teststring), teststring);
+    ok(res == lstrlenA(teststring), "_snprintf returned %d, expected %d.\n", res, lstrlenA(teststring));
+    ok(!strcmp(buffer, "hello worldX"), "_snprintf returned buffer '%s', expected 'hello worldX'.\n", buffer);
+
+    strcpy(buffer, origstring);
+    res = p__snprintf(buffer, strlen(teststring) + 1, teststring);
+    ok(res == lstrlenA(teststring), "_snprintf returned %d, expected %d.\n", res, lstrlenA(teststring));
+    ok(!strcmp(buffer, teststring), "_snprintf returned buffer '%s', expected '%s'.\n", buffer, teststring);
+}
+
 START_TEST(string)
 {
     InitFunctionPtrs();
@@ -1304,4 +1338,6 @@ START_TEST(string)
         test_qsort();
     if (p_bsearch)
         test_bsearch();
+    if (p__snprintf)
+        test__snprintf();
 }
index f956062..ec81eed 100644 (file)
@@ -1,10 +1,7 @@
 /* Automatically generated file; DO NOT EDIT!! */
 
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-
 #define STANDALONE
-#include "wine/test.h"
+#include <wine/test.h>
 
 extern void func_atom(void);
 extern void func_change(void);
index ff80b84..48f4640 100755 (executable)
@@ -33,9 +33,9 @@ static const int MonthLengths[2][12] =
        { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
 };
 
-static inline int IsLeapYear(int Year)
+static inline BOOL IsLeapYear(int Year)
 {
-       return Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0) ? 1 : 0;
+    return Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0);
 }
 
 /* start time of the tests */