[NTDLL_WINETEST]
authorChristoph von Wittich <christoph_vw@reactos.org>
Mon, 15 Mar 2010 22:13:19 +0000 (22:13 +0000)
committerChristoph von Wittich <christoph_vw@reactos.org>
Mon, 15 Mar 2010 22:13:19 +0000 (22:13 +0000)
sync ntdll_winetest to wine 1.1.40

svn path=/trunk/; revision=46215

13 files changed:
rostests/winetests/ntdll/atom.c
rostests/winetests/ntdll/change.c
rostests/winetests/ntdll/directory.c [new file with mode: 0644]
rostests/winetests/ntdll/error.c
rostests/winetests/ntdll/exception.c
rostests/winetests/ntdll/file.c
rostests/winetests/ntdll/ntdll.rbuild
rostests/winetests/ntdll/om.c
rostests/winetests/ntdll/reg.c
rostests/winetests/ntdll/rtl.c
rostests/winetests/ntdll/string.c
rostests/winetests/ntdll/testlist.c
rostests/winetests/ntdll/time.c

index 220ea40..8210548 100755 (executable)
@@ -34,6 +34,7 @@
 #include "winbase.h"
 #include "winreg.h"
 #include "winnls.h"
+#include "winuser.h"
 #include "wine/test.h"
 #include "winternl.h"
 
index f395962..ed7f22c 100644 (file)
@@ -295,15 +295,14 @@ static void test_ntncdf_async(void)
     ok(U(iosb).Status == 0x01234567, "status set too soon\n");
     ok(iosb.Information == 0x12345678, "info set too soon\n");
 
-    todo_wine {
     r = pNtCancelIoFile(hdir, &iosb);
     ok( r == STATUS_SUCCESS, "cancel failed\n");
 
     CloseHandle(hdir);
 
     ok(U(iosb).Status == STATUS_SUCCESS, "status wrong\n");
-    ok(U(iosb2).Status == STATUS_CANCELLED, "status wrong\n");
-    }
+    todo_wine ok(U(iosb2).Status == STATUS_CANCELLED, "status wrong\n");
+
     ok(iosb.Information == 0, "info wrong\n");
     ok(iosb2.Information == 0, "info wrong\n");
 
diff --git a/rostests/winetests/ntdll/directory.c b/rostests/winetests/ntdll/directory.c
new file mode 100644 (file)
index 0000000..a77a4b4
--- /dev/null
@@ -0,0 +1,293 @@
+/* Unit test suite for Ntdll directory functions
+ *
+ * Copyright 2007 Jeff Latimer
+ * Copyright 2007 Andrey Turkin
+ * Copyright 2008 Jeff Zaroyko
+ * Copyright 2009 Dan Kegel
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * NOTES
+ * We use function pointers here as there is no import library for NTDLL on
+ * windows.
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "ntstatus.h"
+/* Define WIN32_NO_STATUS so MSVC does not give us duplicate macro
+ * definition errors when we get to winnt.h
+ */
+#define WIN32_NO_STATUS
+
+#include "wine/test.h"
+#include "winternl.h"
+
+static NTSTATUS (WINAPI *pNtClose)( PHANDLE );
+static NTSTATUS (WINAPI *pNtOpenFile)    ( PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PIO_STATUS_BLOCK, ULONG, ULONG );
+static NTSTATUS (WINAPI *pNtQueryDirectoryFile)(HANDLE,HANDLE,PIO_APC_ROUTINE,PVOID,PIO_STATUS_BLOCK,
+                                                PVOID,ULONG,FILE_INFORMATION_CLASS,BOOLEAN,PUNICODE_STRING,BOOLEAN);
+static BOOLEAN  (WINAPI *pRtlCreateUnicodeStringFromAsciiz)(PUNICODE_STRING,LPCSTR);
+static BOOL     (WINAPI *pRtlDosPathNameToNtPathName_U)( LPCWSTR, PUNICODE_STRING, PWSTR*, CURDIR* );
+static VOID     (WINAPI *pRtlInitUnicodeString)( PUNICODE_STRING, LPCWSTR );
+static VOID     (WINAPI *pRtlFreeUnicodeString)( PUNICODE_STRING );
+static NTSTATUS (WINAPI *pRtlMultiByteToUnicodeN)( LPWSTR dst, DWORD dstlen, LPDWORD reslen,
+                                                   LPCSTR src, DWORD srclen );
+static NTSTATUS (WINAPI *pRtlWow64EnableFsRedirection)( BOOLEAN enable );
+static NTSTATUS (WINAPI *pRtlWow64EnableFsRedirectionEx)( ULONG disable, ULONG *old_value );
+
+/* The attribute sets to test */
+struct testfile_s {
+    int todo;                 /* set if it doesn't work on wine yet */
+    const DWORD attr;         /* desired attribute */
+    const char *name;         /* filename to use */
+    const char *target;       /* what to point to (only for reparse pts) */
+    const char *description;  /* for error messages */
+    int nfound;               /* How many were found (expect 1) */
+    WCHAR nameW[20];          /* unicode version of name (filled in later) */
+} testfiles[] = {
+    { 0, FILE_ATTRIBUTE_NORMAL,    "n.tmp", NULL, "normal" },
+    { 1, FILE_ATTRIBUTE_HIDDEN,    "h.tmp", NULL, "hidden" },
+    { 1, FILE_ATTRIBUTE_SYSTEM,    "s.tmp", NULL, "system" },
+    { 0, FILE_ATTRIBUTE_DIRECTORY, "d.tmp", NULL, "directory" },
+    { 0, 0, NULL }
+};
+static const int max_test_dir_size = 20;  /* size of above plus some for .. etc */
+
+/* Create a test directory full of attribute test files, clear counts */
+static void set_up_attribute_test(const char *testdirA)
+{
+    int i;
+
+    ok(CreateDirectoryA(testdirA, NULL),
+       "couldn't create dir '%s', error %d\n", testdirA, GetLastError());
+
+    for (i=0; testfiles[i].name; i++) {
+        char buf[MAX_PATH];
+        pRtlMultiByteToUnicodeN(testfiles[i].nameW, sizeof(testfiles[i].nameW), NULL, testfiles[i].name, strlen(testfiles[i].name)+1);
+
+        sprintf(buf, "%s\\%s", testdirA, testfiles[i].name);
+        testfiles[i].nfound = 0;
+        if (testfiles[i].attr & FILE_ATTRIBUTE_DIRECTORY) {
+            ok(CreateDirectoryA(buf, NULL),
+               "couldn't create dir '%s', error %d\n", buf, GetLastError());
+        } else {
+            HANDLE h = CreateFileA(buf,
+                                   GENERIC_READ|GENERIC_WRITE,
+                                   0, NULL, CREATE_ALWAYS,
+                                   testfiles[i].attr, 0);
+            ok( h != INVALID_HANDLE_VALUE, "failed to create temp file '%s'\n", buf );
+            CloseHandle(h);
+        }
+    }
+}
+
+/* Remove the given test directory and the attribute test files, if any */
+static void tear_down_attribute_test(const char *testdirA)
+{
+    int i;
+
+    for (i=0; testfiles[i].name; i++) {
+        int ret;
+        char buf[MAX_PATH];
+        sprintf(buf, "%s\\%s", testdirA, testfiles[i].name);
+        if (testfiles[i].attr & FILE_ATTRIBUTE_DIRECTORY) {
+            ret = RemoveDirectory(buf);
+            ok(ret || (GetLastError() == ERROR_PATH_NOT_FOUND),
+               "Failed to rmdir %s, error %d\n", buf, GetLastError());
+        } else {
+            ret = DeleteFile(buf);
+            ok(ret || (GetLastError() == ERROR_PATH_NOT_FOUND),
+               "Failed to rm %s, error %d\n", buf, GetLastError());
+        }
+    }
+    RemoveDirectoryA(testdirA);
+}
+
+/* Match one found file against testfiles[], increment count if found */
+static void tally_test_file(FILE_BOTH_DIRECTORY_INFORMATION *dir_info)
+{
+    int i;
+    DWORD attribmask =
+      (FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_REPARSE_POINT);
+    DWORD attrib = dir_info->FileAttributes & attribmask;
+    WCHAR *nameW = dir_info->FileName;
+    int namelen = dir_info->FileNameLength / sizeof(WCHAR);
+
+    if (nameW[0] == '.')
+        return;
+
+    for (i=0; testfiles[i].name; i++) {
+        int len = strlen(testfiles[i].name);
+        if (namelen != len || memcmp(nameW, testfiles[i].nameW, len*sizeof(WCHAR)))
+            continue;
+        if (testfiles[i].todo) {
+            todo_wine
+            ok (attrib == (testfiles[i].attr & attribmask), "file %s: expected %s (%x), got %x (is your linux new enough?)\n", testfiles[i].name, testfiles[i].description, testfiles[i].attr, attrib);
+        } else {
+            ok (attrib == (testfiles[i].attr & attribmask), "file %s: expected %s (%x), got %x (is your linux new enough?)\n", testfiles[i].name, testfiles[i].description, testfiles[i].attr, attrib);
+        }
+        testfiles[i].nfound++;
+        break;
+    }
+    ok(testfiles[i].name != NULL, "unexpected file found\n");
+}
+
+static void test_NtQueryDirectoryFile(void)
+{
+    OBJECT_ATTRIBUTES attr;
+    UNICODE_STRING ntdirname;
+    char testdirA[MAX_PATH];
+    WCHAR testdirW[MAX_PATH];
+    HANDLE dirh;
+    IO_STATUS_BLOCK io;
+    UINT data_pos;
+    UINT data_len;    /* length of dir data */
+    BYTE data[8192];  /* directory data */
+    FILE_BOTH_DIRECTORY_INFORMATION *dir_info;
+    DWORD status;
+    int numfiles;
+    int i;
+
+    /* Clean up from prior aborted run, if any, then set up test files */
+    ok(GetTempPathA(MAX_PATH, testdirA), "couldn't get temp dir\n");
+    strcat(testdirA, "NtQueryDirectoryFile.tmp");
+    tear_down_attribute_test(testdirA);
+    set_up_attribute_test(testdirA);
+
+    /* Read the directory and note which files are found */
+    pRtlMultiByteToUnicodeN(testdirW, sizeof(testdirW), NULL, testdirA, strlen(testdirA)+1);
+    if (!pRtlDosPathNameToNtPathName_U(testdirW, &ntdirname, NULL, NULL))
+    {
+        ok(0,"RtlDosPathNametoNtPathName_U failed\n");
+        goto done;
+    }
+    InitializeObjectAttributes(&attr, &ntdirname, OBJ_CASE_INSENSITIVE, 0, NULL);
+    status = pNtOpenFile( &dirh, SYNCHRONIZE | FILE_LIST_DIRECTORY, &attr, &io,
+                         FILE_OPEN,
+                         FILE_SYNCHRONOUS_IO_NONALERT|FILE_OPEN_FOR_BACKUP_INTENT|FILE_DIRECTORY_FILE);
+    ok (status == STATUS_SUCCESS, "failed to open dir '%s', ret 0x%x, error %d\n", testdirA, status, GetLastError());
+    if (status != STATUS_SUCCESS) {
+       skip("can't test if we can't open the directory\n");
+       goto done;
+    }
+
+    pNtQueryDirectoryFile( dirh, NULL, NULL, NULL, &io, data, sizeof(data),
+                       FileBothDirectoryInformation, FALSE, NULL, TRUE );
+    ok (U(io).Status == STATUS_SUCCESS, "filed to query directory; status %x\n", U(io).Status);
+    data_len = io.Information;
+    ok (data_len >= sizeof(FILE_BOTH_DIRECTORY_INFORMATION), "not enough data in directory\n");
+
+    data_pos = 0;
+    numfiles = 0;
+    while ((data_pos < data_len) && (numfiles < max_test_dir_size)) {
+        dir_info = (FILE_BOTH_DIRECTORY_INFORMATION *)(data + data_pos);
+
+        tally_test_file(dir_info);
+
+        if (dir_info->NextEntryOffset == 0) {
+            pNtQueryDirectoryFile( dirh, 0, NULL, NULL, &io, data, sizeof(data),
+                               FileBothDirectoryInformation, FALSE, NULL, FALSE );
+            if (U(io).Status == STATUS_NO_MORE_FILES)
+                break;
+            ok (U(io).Status == STATUS_SUCCESS, "filed to query directory; status %x\n", U(io).Status);
+            data_len = io.Information;
+            if (data_len < sizeof(FILE_BOTH_DIRECTORY_INFORMATION))
+                break;
+            data_pos = 0;
+        } else {
+            data_pos += dir_info->NextEntryOffset;
+        }
+        numfiles++;
+    }
+    ok(numfiles < max_test_dir_size, "too many loops\n");
+
+    for (i=0; testfiles[i].name; i++)
+        ok(testfiles[i].nfound == 1, "Wrong number %d of %s files found\n",
+          testfiles[i].nfound, testfiles[i].description);
+
+    pNtClose(dirh);
+done:
+    tear_down_attribute_test(testdirA);
+    pRtlFreeUnicodeString(&ntdirname);
+}
+
+static void test_redirection(void)
+{
+    ULONG old, cur;
+    NTSTATUS status;
+
+    if (!pRtlWow64EnableFsRedirection || !pRtlWow64EnableFsRedirectionEx)
+    {
+        skip( "Wow64 redirection not supported\n" );
+        return;
+    }
+    status = pRtlWow64EnableFsRedirectionEx( FALSE, &old );
+    if (status == STATUS_NOT_IMPLEMENTED)
+    {
+        skip( "Wow64 redirection not supported\n" );
+        return;
+    }
+    ok( !status, "RtlWow64EnableFsRedirectionEx failed status %x\n", status );
+
+    status = pRtlWow64EnableFsRedirectionEx( FALSE, &cur );
+    ok( !status, "RtlWow64EnableFsRedirectionEx failed status %x\n", status );
+    ok( !cur, "RtlWow64EnableFsRedirectionEx got %u\n", cur );
+
+    status = pRtlWow64EnableFsRedirectionEx( TRUE, &cur );
+    ok( !status, "RtlWow64EnableFsRedirectionEx failed status %x\n", status );
+    status = pRtlWow64EnableFsRedirectionEx( TRUE, &cur );
+    ok( !status, "RtlWow64EnableFsRedirectionEx failed status %x\n", status );
+    ok( cur == 1, "RtlWow64EnableFsRedirectionEx got %u\n", cur );
+
+    status = pRtlWow64EnableFsRedirection( TRUE );
+    ok( !status, "RtlWow64EnableFsRedirectionEx failed status %x\n", status );
+    status = pRtlWow64EnableFsRedirectionEx( TRUE, &cur );
+    ok( !status, "RtlWow64EnableFsRedirectionEx failed status %x\n", status );
+    ok( !cur, "RtlWow64EnableFsRedirectionEx got %u\n", cur );
+
+    status = pRtlWow64EnableFsRedirection( FALSE );
+    ok( !status, "RtlWow64EnableFsRedirectionEx failed status %x\n", status );
+    status = pRtlWow64EnableFsRedirectionEx( FALSE, &cur );
+    ok( !status, "RtlWow64EnableFsRedirectionEx failed status %x\n", status );
+    ok( cur == 1, "RtlWow64EnableFsRedirectionEx got %u\n", cur );
+
+    pRtlWow64EnableFsRedirectionEx( old, &cur );
+}
+
+START_TEST(directory)
+{
+    HMODULE hntdll = GetModuleHandleA("ntdll.dll");
+    if (!hntdll)
+    {
+        skip("not running on NT, skipping test\n");
+        return;
+    }
+
+    pNtClose                = (void *)GetProcAddress(hntdll, "NtClose");
+    pNtOpenFile             = (void *)GetProcAddress(hntdll, "NtOpenFile");
+    pNtQueryDirectoryFile   = (void *)GetProcAddress(hntdll, "NtQueryDirectoryFile");
+    pRtlCreateUnicodeStringFromAsciiz = (void *)GetProcAddress(hntdll, "RtlCreateUnicodeStringFromAsciiz");
+    pRtlDosPathNameToNtPathName_U = (void *)GetProcAddress(hntdll, "RtlDosPathNameToNtPathName_U");
+    pRtlInitUnicodeString   = (void *)GetProcAddress(hntdll, "RtlInitUnicodeString");
+    pRtlFreeUnicodeString   = (void *)GetProcAddress(hntdll, "RtlFreeUnicodeString");
+    pRtlMultiByteToUnicodeN = (void *)GetProcAddress(hntdll,"RtlMultiByteToUnicodeN");
+    pRtlWow64EnableFsRedirection = (void *)GetProcAddress(hntdll,"RtlWow64EnableFsRedirection");
+    pRtlWow64EnableFsRedirectionEx = (void *)GetProcAddress(hntdll,"RtlWow64EnableFsRedirectionEx");
+
+    test_NtQueryDirectoryFile();
+    test_redirection();
+}
index b65d7d0..ee8b9b2 100755 (executable)
@@ -892,12 +892,6 @@ static void run_error_tests(void)
     cmp2(STATUS_CTX_SHADOW_ENDED_BY_MODE_CHANGE, ERROR_CTX_SHADOW_ENDED_BY_MODE_CHANGE);
     cmp2(STATUS_CTX_SHADOW_NOT_RUNNING,          ERROR_CTX_SHADOW_NOT_RUNNING);
     cmp2(STATUS_LICENSE_VIOLATION,               ERROR_CTX_LICENSE_NOT_AVAILABLE);
-#if 0
-    /* FIXME - unknown STATUS values, see bug 1001 */
-    cmp(STATUS_ENDPOINT_CLOSED,                  ERROR_DEV_NOT_EXIST);
-    cmp(STATUS_DISCONNECTED,                     ERROR_DEV_NOT_EXIST);
-    cmp(STATUS_NONEXISTENT_NET_NAME,             ERROR_DEV_NOT_EXIST);
-#endif
     cmp2(STATUS_NETWORK_SESSION_EXPIRED,         ERROR_NO_USER_SESSION_KEY);
     cmp2(STATUS_FILES_OPEN,                      ERROR_OPEN_FILES);
     cmp2(STATUS_SXS_SECTION_NOT_FOUND,           ERROR_SXS_SECTION_NOT_FOUND);
index 6ed641c..7f215ed 100644 (file)
@@ -25,6 +25,8 @@
 #define _WIN32_WINNT 0x500 /* For NTSTATUS */
 #endif
 
+#define NONAMELESSUNION
+#define NONAMELESSSTRUCT
 #include "ntstatus.h"
 #define WIN32_NO_STATUS
 #include "windef.h"
 #include "wine/exception.h"
 #include "wine/test.h"
 
-#ifdef __i386__
-static int      my_argc;
-static char**   my_argv;
-static int      test_stage;
+static void *code_mem;
 
 static struct _TEB * (WINAPI *pNtCurrentTeb)(void);
 static NTSTATUS  (WINAPI *pNtGetContextThread)(HANDLE,CONTEXT*);
@@ -48,7 +47,21 @@ static PVOID     (WINAPI *pRtlAddVectoredExceptionHandler)(ULONG first, PVECTORE
 static ULONG     (WINAPI *pRtlRemoveVectoredExceptionHandler)(PVOID handler);
 static NTSTATUS  (WINAPI *pNtReadVirtualMemory)(HANDLE, const void*, void*, SIZE_T, SIZE_T*);
 static NTSTATUS  (WINAPI *pNtTerminateProcess)(HANDLE handle, LONG exit_code);
-static void *code_mem;
+static NTSTATUS  (WINAPI *pNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
+static NTSTATUS  (WINAPI *pNtSetInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG);
+
+#ifdef __i386__
+
+#ifndef __WINE_WINTRNL_H
+#define ProcessExecuteFlags 0x22
+#define MEM_EXECUTE_OPTION_DISABLE   0x01
+#define MEM_EXECUTE_OPTION_ENABLE    0x02
+#define MEM_EXECUTE_OPTION_PERMANENT 0x08
+#endif
+
+static int      my_argc;
+static char**   my_argv;
+static int      test_stage;
 
 /* Test various instruction combinations that cause a protection fault on the i386,
  * and check what the resulting exception looks like.
@@ -116,11 +129,11 @@ static const struct exception
     { { 0x0e, 0x17, 0x58, 0xc3 },  /* 18: pushl %cs; popl %ss; popl %eax; ret */
       1, 1, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
 
-    /* 19: test overlong instruction (limit is 16 bytes) */
+    /* 19: test overlong instruction (limit is 15 bytes, 5 on Win7) */
     { { 0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0xfa,0xc3 },
       0, 16, STATUS_ILLEGAL_INSTRUCTION, 0 },
-    { { 0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0xfa,0xc3 },
-      0, 15, STATUS_PRIVILEGED_INSTRUCTION, 0 },
+    { { 0x64,0x64,0x64,0x64,0xfa,0xc3 },
+      0, 5, STATUS_PRIVILEGED_INSTRUCTION, 0 },
 
     /* test invalid interrupt */
     { { 0xcd, 0xff, 0xc3 },   /* 21: int $0xff; ret */
@@ -180,23 +193,30 @@ static int got_exception;
 static BOOL have_vectored_api;
 
 static void run_exception_test(void *handler, const void* context,
-                               const void *code, unsigned int code_size)
+                               const void *code, unsigned int code_size,
+                               DWORD access)
 {
     struct {
         EXCEPTION_REGISTRATION_RECORD frame;
         const void *context;
     } exc_frame;
     void (*func)(void) = code_mem;
+    DWORD oldaccess, oldaccess2;
 
     exc_frame.frame.Handler = handler;
     exc_frame.frame.Prev = pNtCurrentTeb()->Tib.ExceptionList;
     exc_frame.context = context;
 
     memcpy(code_mem, code, code_size);
+    if(access)
+        VirtualProtect(code_mem, code_size, access, &oldaccess);
 
     pNtCurrentTeb()->Tib.ExceptionList = &exc_frame.frame;
     func();
     pNtCurrentTeb()->Tib.ExceptionList = exc_frame.frame.Prev;
+
+    if(access)
+        VirtualProtect(code_mem, code_size, oldaccess, &oldaccess2);
 }
 
 static LONG CALLBACK rtlraiseexception_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo)
@@ -394,7 +414,7 @@ static void test_prot_fault(void)
     {
         got_exception = 0;
         run_exception_test(handler, &exceptions[i], &exceptions[i].code,
-                           sizeof(exceptions[i].code));
+                           sizeof(exceptions[i].code), 0);
         if (!i && !got_exception)
         {
             trace( "No exception, assuming win9x, no point in testing further\n" );
@@ -583,7 +603,7 @@ static void test_exceptions(void)
     }
 
     /* test handling of debug registers */
-    run_exception_test(dreg_handler, NULL, &segfault_code, sizeof(segfault_code));
+    run_exception_test(dreg_handler, NULL, &segfault_code, sizeof(segfault_code), 0);
 
     ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
     res = pNtGetContextThread(GetCurrentThread(), &ctx);
@@ -593,17 +613,17 @@ static void test_exceptions(void)
 
     /* test single stepping behavior */
     got_exception = 0;
-    run_exception_test(single_step_handler, NULL, &single_stepcode, sizeof(single_stepcode));
+    run_exception_test(single_step_handler, NULL, &single_stepcode, sizeof(single_stepcode), 0);
     ok(got_exception == 3, "expected 3 single step exceptions, got %d\n", got_exception);
 
     /* test alignment exceptions */
     got_exception = 0;
-    run_exception_test(align_check_handler, NULL, align_check_code, sizeof(align_check_code));
+    run_exception_test(align_check_handler, NULL, align_check_code, sizeof(align_check_code), 0);
     ok(got_exception == 0, "got %d alignment faults, expected 0\n", got_exception);
 
     /* test direction flag */
     got_exception = 0;
-    run_exception_test(direction_flag_handler, NULL, direction_flag_code, sizeof(direction_flag_code));
+    run_exception_test(direction_flag_handler, NULL, direction_flag_code, sizeof(direction_flag_code), 0);
     ok(got_exception == 1, "got %d exceptions, expected 1\n", got_exception);
 
     /* test single stepping over hardware breakpoint */
@@ -615,11 +635,11 @@ static void test_exceptions(void)
     ok( res == STATUS_SUCCESS, "NtSetContextThread faild with %x\n", res);
 
     got_exception = 0;
-    run_exception_test(bpx_handler, NULL, dummy_code, sizeof(dummy_code));
+    run_exception_test(bpx_handler, NULL, dummy_code, sizeof(dummy_code), 0);
     ok( got_exception == 4,"expected 4 exceptions, got %d\n", got_exception);
 
     /* test int3 handling */
-    run_exception_test(int3_handler, NULL, int3_code, sizeof(int3_code));
+    run_exception_test(int3_handler, NULL, int3_code, sizeof(int3_code), 0);
 }
 
 static void test_debugger(void)
@@ -638,7 +658,7 @@ static void test_debugger(void)
 
     if(!pNtGetContextThread || !pNtSetContextThread || !pNtReadVirtualMemory || !pNtTerminateProcess)
     {
-        skip("NtGetContextThread, NtSetContextThread, NtReadVirtualMemory or NtTerminateProcess not found\n)");
+        skip("NtGetContextThread, NtSetContextThread, NtReadVirtualMemory or NtTerminateProcess not found\n");
         return;
     }
 
@@ -825,7 +845,7 @@ static void test_simd_exceptions(void)
     /* test if CPU & OS can do sse */
     stage = 1;
     got_exception = 0;
-    run_exception_test(simd_fault_handler, &stage, sse_check, sizeof(sse_check));
+    run_exception_test(simd_fault_handler, &stage, sse_check, sizeof(sse_check), 0);
     if(got_exception) {
         skip("system doesn't support SSE\n");
         return;
@@ -835,7 +855,7 @@ static void test_simd_exceptions(void)
     stage = 2;
     got_exception = 0;
     run_exception_test(simd_fault_handler, &stage, simd_exception_test,
-                       sizeof(simd_exception_test));
+                       sizeof(simd_exception_test), 0);
     ok( got_exception == 1, "got exception: %i, should be 1\n", got_exception);
 }
 
@@ -901,27 +921,450 @@ static void test_fpu_exceptions(void)
     struct fpu_exception_info info;
 
     memset(&info, 0, sizeof(info));
-    run_exception_test(fpu_exception_handler, &info, fpu_exception_test_ie, sizeof(fpu_exception_test_ie));
+    run_exception_test(fpu_exception_handler, &info, fpu_exception_test_ie, sizeof(fpu_exception_test_ie), 0);
     ok(info.exception_code == EXCEPTION_FLT_STACK_CHECK,
             "Got exception code %#x, expected EXCEPTION_FLT_STACK_CHECK\n", info.exception_code);
     ok(info.exception_offset == 0x19, "Got exception offset %#x, expected 0x19\n", info.exception_offset);
     ok(info.eip_offset == 0x1b, "Got EIP offset %#x, expected 0x1b\n", info.eip_offset);
 
     memset(&info, 0, sizeof(info));
-    run_exception_test(fpu_exception_handler, &info, fpu_exception_test_de, sizeof(fpu_exception_test_de));
+    run_exception_test(fpu_exception_handler, &info, fpu_exception_test_de, sizeof(fpu_exception_test_de), 0);
     ok(info.exception_code == EXCEPTION_FLT_DIVIDE_BY_ZERO,
             "Got exception code %#x, expected EXCEPTION_FLT_DIVIDE_BY_ZERO\n", info.exception_code);
     ok(info.exception_offset == 0x17, "Got exception offset %#x, expected 0x17\n", info.exception_offset);
     ok(info.eip_offset == 0x19, "Got EIP offset %#x, expected 0x19\n", info.eip_offset);
 }
 
-#endif  /* __i386__ */
+struct dpe_exception_info {
+    BOOL exception_caught;
+    DWORD exception_info;
+};
+
+static DWORD dpe_exception_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
+        CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher)
+{
+    DWORD old_prot;
+    struct dpe_exception_info *info = *(struct dpe_exception_info **)(frame + 1);
+
+    ok(rec->ExceptionCode == EXCEPTION_ACCESS_VIOLATION,
+       "Exception code %08x\n", rec->ExceptionCode);
+    ok(rec->NumberParameters == 2,
+       "Parameter count: %d\n", rec->NumberParameters);
+    ok((LPVOID)rec->ExceptionInformation[1] == code_mem,
+       "Exception address: %p, expected %p\n",
+       (LPVOID)rec->ExceptionInformation[1], code_mem);
+
+    info->exception_info = rec->ExceptionInformation[0];
+    info->exception_caught = TRUE;
+
+    VirtualProtect(code_mem, 1, PAGE_EXECUTE_READWRITE, &old_prot);
+    return ExceptionContinueExecution;
+}
+
+static void test_dpe_exceptions(void)
+{
+    static char single_ret[] = {0xC3};
+    struct dpe_exception_info info;
+    NTSTATUS stat;
+    BOOL has_hw_support;
+    BOOL is_permanent = FALSE, can_test_without = TRUE, can_test_with = TRUE;
+    DWORD val;
+    ULONG len;
+
+    /* Query DEP with len to small */
+    stat = pNtQueryInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val - 1, &len);
+    if(stat == STATUS_INVALID_INFO_CLASS)
+    {
+        skip("This software platform does not support DEP\n");
+        return;
+    }
+    ok(stat == STATUS_INFO_LENGTH_MISMATCH, "buffer too small: %08x\n", stat);
+
+    /* Query DEP */
+    stat = pNtQueryInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val, &len);
+    ok(stat == STATUS_SUCCESS, "querying DEP: status %08x\n", stat);
+    if(stat == STATUS_SUCCESS)
+    {
+        ok(len == sizeof val, "returned length: %d\n", len);
+        if(val & MEM_EXECUTE_OPTION_PERMANENT)
+        {
+            skip("toggling DEP impossible - status locked\n");
+            is_permanent = TRUE;
+            if(val & MEM_EXECUTE_OPTION_DISABLE)
+                can_test_without = FALSE;
+            else
+                can_test_with = FALSE;
+        }
+    }
+
+    if(!is_permanent)
+    {
+        /* Enable DEP */
+        val = MEM_EXECUTE_OPTION_DISABLE;
+        stat = pNtSetInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val);
+        ok(stat == STATUS_SUCCESS, "enabling DEP: status %08x\n", stat);
+    }
+
+    if(can_test_with)
+    {
+        /* Try access to locked page with DEP on*/
+        info.exception_caught = FALSE;
+        run_exception_test(dpe_exception_handler, &info, single_ret, sizeof(single_ret), PAGE_NOACCESS);
+        ok(info.exception_caught == TRUE, "Execution of disabled memory succeeded\n");
+        ok(info.exception_info == EXCEPTION_READ_FAULT ||
+           info.exception_info == EXCEPTION_EXECUTE_FAULT,
+              "Access violation type: %08x\n", (unsigned)info.exception_info);
+        has_hw_support = info.exception_info == EXCEPTION_EXECUTE_FAULT;
+        trace("DEP hardware support: %s\n", has_hw_support?"Yes":"No");
+
+        /* Try execution of data with DEP on*/
+        info.exception_caught = FALSE;
+        run_exception_test(dpe_exception_handler, &info, single_ret, sizeof(single_ret), PAGE_READWRITE);
+        if(has_hw_support)
+        {
+            ok(info.exception_caught == TRUE, "Execution of data memory succeeded\n");
+            ok(info.exception_info == EXCEPTION_EXECUTE_FAULT,
+                  "Access violation type: %08x\n", (unsigned)info.exception_info);
+        }
+        else
+            ok(info.exception_caught == FALSE, "Execution trapped without hardware support\n");
+    }
+    else
+        skip("DEP is in AlwaysOff state\n");
+
+    if(!is_permanent)
+    {
+        /* Disable DEP */
+        val = MEM_EXECUTE_OPTION_ENABLE;
+        stat = pNtSetInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val);
+        ok(stat == STATUS_SUCCESS, "disabling DEP: status %08x\n", stat);
+    }
+
+    /* page is read without exec here */
+    if(can_test_without)
+    {
+        /* Try execution of data with DEP off */
+        info.exception_caught = FALSE;
+        run_exception_test(dpe_exception_handler, &info, single_ret, sizeof(single_ret), PAGE_READWRITE);
+        ok(info.exception_caught == FALSE, "Execution trapped with DEP turned off\n");
+
+        /* Try access to locked page with DEP off - error code is different than
+           with hardware DEP on */
+        info.exception_caught = FALSE;
+        run_exception_test(dpe_exception_handler, &info, single_ret, sizeof(single_ret), PAGE_NOACCESS);
+        ok(info.exception_caught == TRUE, "Execution of disabled memory succeeded\n");
+        ok(info.exception_info == EXCEPTION_READ_FAULT,
+              "Access violation type: %08x\n", (unsigned)info.exception_info);
+    }
+    else
+        skip("DEP is in AlwaysOn state\n");
+
+    if(!is_permanent)
+    {
+        /* Turn off DEP permanently */
+        val = MEM_EXECUTE_OPTION_ENABLE | MEM_EXECUTE_OPTION_PERMANENT;
+        stat = pNtSetInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val);
+        ok(stat == STATUS_SUCCESS, "disabling DEP permanently: status %08x\n", stat);
+    }
+
+    /* Try to turn off DEP */
+    val = MEM_EXECUTE_OPTION_ENABLE;
+    stat = pNtSetInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val);
+    ok(stat == STATUS_ACCESS_DENIED, "disabling DEP while permanent: status %08x\n", stat);
+
+    /* Try to turn on DEP */
+    val = MEM_EXECUTE_OPTION_DISABLE;
+    stat = pNtSetInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val);
+    ok(stat == STATUS_ACCESS_DENIED, "enabling DEP while permanent: status %08x\n", stat);
+}
+
+#elif defined(__x86_64__)
+
+#define UNW_FLAG_NHANDLER  0
+#define UNW_FLAG_EHANDLER  1
+#define UNW_FLAG_UHANDLER  2
+#define UNW_FLAG_CHAININFO 4
+
+#define UWOP_PUSH_NONVOL     0
+#define UWOP_ALLOC_LARGE     1
+#define UWOP_ALLOC_SMALL     2
+#define UWOP_SET_FPREG       3
+#define UWOP_SAVE_NONVOL     4
+#define UWOP_SAVE_NONVOL_FAR 5
+#define UWOP_SAVE_XMM128     8
+#define UWOP_SAVE_XMM128_FAR 9
+#define UWOP_PUSH_MACHFRAME  10
+
+struct results
+{
+    int rip_offset;   /* rip offset from code start */
+    int rbp_offset;   /* rbp offset from stack pointer */
+    int handler;      /* expect handler to be set? */
+    int rip;          /* expected final rip value */
+    int frame;        /* expected frame return value */
+    int regs[8][2];   /* expected values for registers */
+};
+
+struct unwind_test
+{
+    const BYTE *function;
+    size_t function_size;
+    const BYTE *unwind_info;
+    const struct results *results;
+    unsigned int nb_results;
+};
+
+enum regs
+{
+    rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi,
+    r8,  r9,  r10, r11, r12, r13, r14, r15
+};
+
+static const char * const reg_names[16] =
+{
+    "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
+    "r8",  "r9",  "r10", "r11", "r12", "r13", "r14", "r15"
+};
+
+#define UWOP(code,info) (UWOP_##code | ((info) << 4))
+
+static void call_virtual_unwind( int testnum, const struct unwind_test *test )
+{
+    static const int code_offset = 1024;
+    static const int unwind_offset = 2048;
+    void *handler, *data;
+    CONTEXT context;
+    RUNTIME_FUNCTION runtime_func;
+    KNONVOLATILE_CONTEXT_POINTERS ctx_ptr;
+    UINT i, j, k;
+    ULONG64 fake_stack[256];
+    ULONG64 frame, orig_rip, orig_rbp, unset_reg;
+    UINT unwind_size = 4 + 2 * test->unwind_info[2] + 8;
+
+    memcpy( (char *)code_mem + code_offset, test->function, test->function_size );
+    memcpy( (char *)code_mem + unwind_offset, test->unwind_info, unwind_size );
+
+    runtime_func.BeginAddress = code_offset;
+    runtime_func.EndAddress = code_offset + test->function_size;
+    runtime_func.UnwindData = unwind_offset;
+
+    trace( "code: %p stack: %p\n", code_mem, fake_stack );
+
+    for (i = 0; i < test->nb_results; i++)
+    {
+        memset( &ctx_ptr, 0, sizeof(ctx_ptr) );
+        memset( &context, 0x55, sizeof(context) );
+        memset( &unset_reg, 0x55, sizeof(unset_reg) );
+        for (j = 0; j < 256; j++) fake_stack[j] = j * 8;
+
+        context.Rsp = (ULONG_PTR)fake_stack;
+        context.Rbp = (ULONG_PTR)fake_stack + test->results[i].rbp_offset;
+        orig_rbp = context.Rbp;
+        orig_rip = (ULONG64)code_mem + code_offset + test->results[i].rip_offset;
+
+        trace( "%u/%u: rip=%p (%02x) rbp=%p rsp=%p\n", testnum, i,
+               (void *)orig_rip, *(BYTE *)orig_rip, (void *)orig_rbp, (void *)context.Rsp );
+
+        data = (void *)0xdeadbeef;
+        handler = RtlVirtualUnwind( UNW_FLAG_EHANDLER, (ULONG64)code_mem, orig_rip,
+                                    &runtime_func, &context, &data, &frame, &ctx_ptr );
+        if (test->results[i].handler)
+        {
+            ok( (char *)handler == (char *)code_mem + 0x200,
+                "%u/%u: wrong handler %p/%p\n", testnum, i, handler, (char *)code_mem + 0x200 );
+            if (handler) ok( *(DWORD *)data == 0x08070605,
+                             "%u/%u: wrong handler data %p\n", testnum, i, data );
+        }
+        else
+        {
+            ok( handler == NULL, "%u/%u: handler %p instead of NULL\n", testnum, i, handler );
+            ok( data == (void *)0xdeadbeef, "%u/%u: handler data set to %p\n", testnum, i, data );
+        }
+
+        ok( context.Rip == test->results[i].rip, "%u/%u: wrong rip %p/%x\n",
+            testnum, i, (void *)context.Rip, test->results[i].rip );
+        ok( frame == (ULONG64)fake_stack + test->results[i].frame, "%u/%u: wrong frame %p/%p\n",
+            testnum, i, (void *)frame, (char *)fake_stack + test->results[i].frame );
+
+        for (j = 0; j < 16; j++)
+        {
+            static const UINT nb_regs = sizeof(test->results[i].regs) / sizeof(test->results[i].regs[0]);
+
+            for (k = 0; k < nb_regs; k++)
+            {
+                if (test->results[i].regs[k][0] == -1)
+                {
+                    k = nb_regs;
+                    break;
+                }
+                if (test->results[i].regs[k][0] == j) break;
+            }
+
+            if (j == rsp)  /* rsp is special */
+            {
+                ok( !ctx_ptr.u2.IntegerContext[j],
+                    "%u/%u: rsp should not be set in ctx_ptr\n", testnum, i );
+                ok( context.Rsp == (ULONG64)fake_stack + test->results[i].regs[k][1],
+                    "%u/%u: register rsp wrong %p/%p\n",
+                    testnum, i, (void *)context.Rsp, (char *)fake_stack + test->results[i].regs[k][1] );
+                continue;
+            }
+
+            if (ctx_ptr.u2.IntegerContext[j])
+            {
+                ok( k < nb_regs, "%u/%u: register %s should not be set to %lx\n",
+                    testnum, i, reg_names[j], *(&context.Rax + j) );
+                if (k < nb_regs)
+                    ok( *(&context.Rax + j) == test->results[i].regs[k][1],
+                        "%u/%u: register %s wrong %p/%x\n",
+                        testnum, i, reg_names[j], (void *)*(&context.Rax + j), test->results[i].regs[k][1] );
+            }
+            else
+            {
+                ok( k == nb_regs, "%u/%u: register %s should be set\n", testnum, i, reg_names[j] );
+                if (j == rbp)
+                    ok( context.Rbp == orig_rbp, "%u/%u: register rbp wrong %p/unset\n",
+                        testnum, i, (void *)context.Rbp );
+                else
+                    ok( *(&context.Rax + j) == unset_reg,
+                        "%u/%u: register %s wrong %p/unset\n",
+                        testnum, i, reg_names[j], (void *)*(&context.Rax + j));
+            }
+        }
+    }
+}
+
+static void test_virtual_unwind(void)
+{
+    static const BYTE function_0[] =
+    {
+        0xff, 0xf5,                                  /* 00: push %rbp */
+        0x48, 0x81, 0xec, 0x10, 0x01, 0x00, 0x00,    /* 02: sub $0x110,%rsp */
+        0x48, 0x8d, 0x6c, 0x24, 0x30,                /* 09: lea 0x30(%rsp),%rbp */
+        0x48, 0x89, 0x9d, 0xf0, 0x00, 0x00, 0x00,    /* 0e: mov %rbx,0xf0(%rbp) */
+        0x48, 0x89, 0xb5, 0xf8, 0x00, 0x00, 0x00,    /* 15: mov %rsi,0xf8(%rbp) */
+        0x90,                                        /* 1c: nop */
+        0x48, 0x8b, 0x9d, 0xf0, 0x00, 0x00, 0x00,    /* 1d: mov 0xf0(%rbp),%rbx */
+        0x48, 0x8b, 0xb5, 0xf8, 0x00, 0x00, 0x00,    /* 24: mov 0xf8(%rbp),%rsi */
+        0x48, 0x8d, 0xa5, 0xe0, 0x00, 0x00, 0x00,    /* 2b: lea 0xe0(%rbp),%rsp */
+        0x5d,                                        /* 32: pop %rbp */
+        0xc3                                         /* 33: ret */
+    };
+
+    static const BYTE unwind_info_0[] =
+    {
+        1 | (UNW_FLAG_EHANDLER << 3),  /* version + flags */
+        0x1c,                          /* prolog size */
+        8,                             /* opcode count */
+        (0x03 << 4) | rbp,             /* frame reg rbp offset 0x30 */
+
+        0x1c, UWOP(SAVE_NONVOL, rsi), 0x25, 0, /* 1c: mov %rsi,0x128(%rsp) */
+        0x15, UWOP(SAVE_NONVOL, rbx), 0x24, 0, /* 15: mov %rbx,0x120(%rsp) */
+        0x0e, UWOP(SET_FPREG, rbp),            /* 0e: lea 0x30(%rsp),rbp */
+        0x09, UWOP(ALLOC_LARGE, 0), 0x22, 0,   /* 09: sub $0x110,%rsp */
+        0x02, UWOP(PUSH_NONVOL, rbp),          /* 02: push %rbp */
+
+        0x00, 0x02, 0x00, 0x00,  /* handler */
+        0x05, 0x06, 0x07, 0x08,  /* data */
+    };
+
+    static const struct results results_0[] =
+    {
+      /* offset  rbp   handler  rip   frame   registers */
+        { 0x00,  0x40,  FALSE, 0x000, 0x000, { {rsp,0x008}, {-1,-1} }},
+        { 0x02,  0x40,  FALSE, 0x008, 0x000, { {rsp,0x010}, {rbp,0x000}, {-1,-1} }},
+        { 0x09,  0x40,  FALSE, 0x118, 0x000, { {rsp,0x120}, {rbp,0x110}, {-1,-1} }},
+        { 0x0e,  0x40,  FALSE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {-1,-1} }},
+        { 0x15,  0x40,  FALSE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {-1,-1} }},
+        { 0x1c,  0x40,  TRUE,  0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}},
+        { 0x1d,  0x40,  TRUE,  0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}},
+        { 0x24,  0x40,  TRUE,  0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}},
+        { 0x2b,  0x40,  FALSE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {-1,-1}}},
+        { 0x32,  0x40,  FALSE, 0x008, 0x010, { {rsp,0x010}, {rbp,0x000}, {-1,-1}}},
+        { 0x33,  0x40,  FALSE, 0x000, 0x010, { {rsp,0x008}, {-1,-1}}},
+    };
+
+
+    static const BYTE function_1[] =
+    {
+        0x53,                     /* 00: push %rbx */
+        0x55,                     /* 01: push %rbp */
+        0x56,                     /* 02: push %rsi */
+        0x57,                     /* 03: push %rdi */
+        0x41, 0x54,               /* 04: push %r12 */
+        0x48, 0x83, 0xec, 0x30,   /* 06: sub $0x30,%rsp */
+        0x90, 0x90,               /* 0a: nop; nop */
+        0x48, 0x83, 0xc4, 0x30,   /* 0c: add $0x30,%rsp */
+        0x41, 0x5c,               /* 10: pop %r12 */
+        0x5f,                     /* 12: pop %rdi */
+        0x5e,                     /* 13: pop %rsi */
+        0x5d,                     /* 14: pop %rbp */
+        0x5b,                     /* 15: pop %rbx */
+        0xc3                      /* 16: ret */
+     };
+
+    static const BYTE unwind_info_1[] =
+    {
+        1 | (UNW_FLAG_EHANDLER << 3),  /* version + flags */
+        0x0a,                          /* prolog size */
+        6,                             /* opcode count */
+        0,                             /* frame reg */
+
+        0x0a, UWOP(ALLOC_SMALL, 5),   /* 0a: sub $0x30,%rsp */
+        0x06, UWOP(PUSH_NONVOL, r12), /* 06: push %r12 */
+        0x04, UWOP(PUSH_NONVOL, rdi), /* 04: push %rdi */
+        0x03, UWOP(PUSH_NONVOL, rsi), /* 03: push %rsi */
+        0x02, UWOP(PUSH_NONVOL, rbp), /* 02: push %rbp */
+        0x01, UWOP(PUSH_NONVOL, rbx), /* 01: push %rbx */
+
+        0x00, 0x02, 0x00, 0x00,  /* handler */
+        0x05, 0x06, 0x07, 0x08,  /* data */
+    };
+
+    static const struct results results_1[] =
+    {
+      /* offset  rbp   handler  rip   frame   registers */
+        { 0x00,  0x50,  FALSE, 0x000, 0x000, { {rsp,0x008}, {-1,-1} }},
+        { 0x01,  0x50,  FALSE, 0x008, 0x000, { {rsp,0x010}, {rbx,0x000}, {-1,-1} }},
+        { 0x02,  0x50,  FALSE, 0x010, 0x000, { {rsp,0x018}, {rbx,0x008}, {rbp,0x000}, {-1,-1} }},
+        { 0x03,  0x50,  FALSE, 0x018, 0x000, { {rsp,0x020}, {rbx,0x010}, {rbp,0x008}, {rsi,0x000}, {-1,-1} }},
+        { 0x04,  0x50,  FALSE, 0x020, 0x000, { {rsp,0x028}, {rbx,0x018}, {rbp,0x010}, {rsi,0x008}, {rdi,0x000}, {-1,-1} }},
+        { 0x06,  0x50,  FALSE, 0x028, 0x000, { {rsp,0x030}, {rbx,0x020}, {rbp,0x018}, {rsi,0x010}, {rdi,0x008}, {r12,0x000}, {-1,-1} }},
+        { 0x0a,  0x50,  TRUE,  0x058, 0x000, { {rsp,0x060}, {rbx,0x050}, {rbp,0x048}, {rsi,0x040}, {rdi,0x038}, {r12,0x030}, {-1,-1} }},
+        { 0x0c,  0x50,  FALSE, 0x058, 0x000, { {rsp,0x060}, {rbx,0x050}, {rbp,0x048}, {rsi,0x040}, {rdi,0x038}, {r12,0x030}, {-1,-1} }},
+        { 0x10,  0x50,  FALSE, 0x028, 0x000, { {rsp,0x030}, {rbx,0x020}, {rbp,0x018}, {rsi,0x010}, {rdi,0x008}, {r12,0x000}, {-1,-1} }},
+        { 0x12,  0x50,  FALSE, 0x020, 0x000, { {rsp,0x028}, {rbx,0x018}, {rbp,0x010}, {rsi,0x008}, {rdi,0x000}, {-1,-1} }},
+        { 0x13,  0x50,  FALSE, 0x018, 0x000, { {rsp,0x020}, {rbx,0x010}, {rbp,0x008}, {rsi,0x000}, {-1,-1} }},
+        { 0x14,  0x50,  FALSE, 0x010, 0x000, { {rsp,0x018}, {rbx,0x008}, {rbp,0x000}, {-1,-1} }},
+        { 0x15,  0x50,  FALSE, 0x008, 0x000, { {rsp,0x010}, {rbx,0x000}, {-1,-1} }},
+        { 0x16,  0x50,  FALSE, 0x000, 0x000, { {rsp,0x008}, {-1,-1} }},
+    };
+
+    static const struct unwind_test tests[] =
+    {
+        { function_0, sizeof(function_0), unwind_info_0,
+          results_0, sizeof(results_0)/sizeof(results_0[0]) },
+        { function_1, sizeof(function_1), unwind_info_1,
+          results_1, sizeof(results_1)/sizeof(results_1[0]) }
+    };
+    unsigned int i;
+
+    for (i = 0; i < sizeof(tests)/sizeof(tests[0]); i++)
+        call_virtual_unwind( i, &tests[i] );
+}
+
+#endif  /* __x86_64__ */
 
 START_TEST(exception)
 {
-#ifdef __i386__
     HMODULE hntdll = GetModuleHandleA("ntdll.dll");
 
+    code_mem = VirtualAlloc(NULL, 65536, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
+    if(!code_mem) {
+        trace("VirtualAlloc failed\n");
+        return;
+    }
+
     pNtCurrentTeb        = (void *)GetProcAddress( hntdll, "NtCurrentTeb" );
     pNtGetContextThread  = (void *)GetProcAddress( hntdll, "NtGetContextThread" );
     pNtSetContextThread  = (void *)GetProcAddress( hntdll, "NtSetContextThread" );
@@ -932,6 +1375,12 @@ START_TEST(exception)
                                                                  "RtlAddVectoredExceptionHandler" );
     pRtlRemoveVectoredExceptionHandler = (void *)GetProcAddress( hntdll,
                                                                  "RtlRemoveVectoredExceptionHandler" );
+    pNtQueryInformationProcess         = (void*)GetProcAddress( hntdll,
+                                                                 "NtQueryInformationProcess" );
+    pNtSetInformationProcess           = (void*)GetProcAddress( hntdll,
+                                                                 "NtSetInformationProcess" );
+
+#ifdef __i386__
     if (!pNtCurrentTeb)
     {
         skip( "NtCurrentTeb not found\n" );
@@ -943,13 +1392,6 @@ START_TEST(exception)
     else
         skip("RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler not found\n");
 
-    /* 1024 byte should be sufficient */
-    code_mem = VirtualAlloc(NULL, 1024, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
-    if(!code_mem) {
-        trace("VirtualAlloc failed\n");
-        return;
-    }
-
     my_argc = winetest_get_mainargs( &my_argv );
     if (my_argc >= 4)
     {
@@ -993,7 +1435,13 @@ START_TEST(exception)
     test_debugger();
     test_simd_exceptions();
     test_fpu_exceptions();
+    test_dpe_exceptions();
+
+#elif defined(__x86_64__)
+
+    test_virtual_unwind();
 
-    VirtualFree(code_mem, 1024, MEM_RELEASE);
 #endif
+
+    VirtualFree(code_mem, 0, MEM_FREE);
 }
index 164c2da..3d28d80 100644 (file)
 
 #include "wine/test.h"
 #include "winternl.h"
+#include "winuser.h"
 
 #ifndef IO_COMPLETION_ALL_ACCESS
 #define IO_COMPLETION_ALL_ACCESS 0x001F0003
 #endif
 
-static NTSTATUS (WINAPI *pRtlFreeUnicodeString)( PUNICODE_STRING );
+static BOOL     (WINAPI * pGetVolumePathNameW)(LPCWSTR, LPWSTR, DWORD);
+static UINT     (WINAPI *pGetSystemWow64DirectoryW)( LPWSTR, UINT );
+
+static VOID     (WINAPI *pRtlFreeUnicodeString)( PUNICODE_STRING );
 static VOID     (WINAPI *pRtlInitUnicodeString)( PUNICODE_STRING, LPCWSTR );
 static BOOL     (WINAPI *pRtlDosPathNameToNtPathName_U)( LPCWSTR, PUNICODE_STRING, PWSTR*, CURDIR* );
+static NTSTATUS (WINAPI *pRtlWow64EnableFsRedirectionEx)( ULONG, ULONG * );
+
 static NTSTATUS (WINAPI *pNtCreateMailslotFile)( PHANDLE, ULONG, POBJECT_ATTRIBUTES, PIO_STATUS_BLOCK,
                                        ULONG, ULONG, ULONG, PLARGE_INTEGER );
+static NTSTATUS (WINAPI *pNtCreateFile)(PHANDLE,ACCESS_MASK,POBJECT_ATTRIBUTES,PIO_STATUS_BLOCK,PLARGE_INTEGER,ULONG,ULONG,ULONG,ULONG,PVOID,ULONG);
+static NTSTATUS (WINAPI *pNtOpenFile)(PHANDLE,ACCESS_MASK,POBJECT_ATTRIBUTES,PIO_STATUS_BLOCK,ULONG,ULONG);
 static NTSTATUS (WINAPI *pNtDeleteFile)(POBJECT_ATTRIBUTES ObjectAttributes);
 static NTSTATUS (WINAPI *pNtReadFile)(HANDLE hFile, HANDLE hEvent,
                                       PIO_APC_ROUTINE apc, void* apc_user,
@@ -54,6 +62,8 @@ static NTSTATUS (WINAPI *pNtWriteFile)(HANDLE hFile, HANDLE hEvent,
                                        PIO_STATUS_BLOCK io_status,
                                        const void* buffer, ULONG length,
                                        PLARGE_INTEGER offset, PULONG key);
+static NTSTATUS (WINAPI *pNtCancelIoFile)(HANDLE hFile, PIO_STATUS_BLOCK io_status);
+static NTSTATUS (WINAPI *pNtCancelIoFileEx)(HANDLE hFile, PIO_STATUS_BLOCK iosb, PIO_STATUS_BLOCK io_status);
 static NTSTATUS (WINAPI *pNtClose)( PHANDLE );
 
 static NTSTATUS (WINAPI *pNtCreateIoCompletion)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, ULONG);
@@ -62,6 +72,9 @@ static NTSTATUS (WINAPI *pNtQueryIoCompletion)(HANDLE, IO_COMPLETION_INFORMATION
 static NTSTATUS (WINAPI *pNtRemoveIoCompletion)(HANDLE, PULONG_PTR, PULONG_PTR, PIO_STATUS_BLOCK, PLARGE_INTEGER);
 static NTSTATUS (WINAPI *pNtSetIoCompletion)(HANDLE, ULONG_PTR, ULONG_PTR, NTSTATUS, ULONG);
 static NTSTATUS (WINAPI *pNtSetInformationFile)(HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG, FILE_INFORMATION_CLASS);
+static NTSTATUS (WINAPI *pNtQueryInformationFile)(HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG, FILE_INFORMATION_CLASS);
+static NTSTATUS (WINAPI *pNtQueryDirectoryFile)(HANDLE,HANDLE,PIO_APC_ROUTINE,PVOID,PIO_STATUS_BLOCK,
+                                                PVOID,ULONG,FILE_INFORMATION_CLASS,BOOLEAN,PUNICODE_STRING,BOOLEAN);
 
 static inline BOOL is_signaled( HANDLE obj )
 {
@@ -140,6 +153,231 @@ static void WINAPI apc( void *arg, IO_STATUS_BLOCK *iosb, ULONG reserved )
     ok( !reserved, "reserved is not 0: %x\n", reserved );
 }
 
+static void create_file_test(void)
+{
+    static const WCHAR systemrootW[] = {'\\','S','y','s','t','e','m','R','o','o','t',
+                                        '\\','f','a','i','l','i','n','g',0};
+    NTSTATUS status;
+    HANDLE dir;
+    WCHAR path[MAX_PATH];
+    OBJECT_ATTRIBUTES attr;
+    IO_STATUS_BLOCK io;
+    UNICODE_STRING nameW;
+    UINT len;
+
+    len = GetCurrentDirectoryW( MAX_PATH, path );
+    pRtlDosPathNameToNtPathName_U( path, &nameW, NULL, NULL );
+    attr.Length = sizeof(attr);
+    attr.RootDirectory = 0;
+    attr.ObjectName = &nameW;
+    attr.Attributes = OBJ_CASE_INSENSITIVE;
+    attr.SecurityDescriptor = NULL;
+    attr.SecurityQualityOfService = NULL;
+
+    /* try various open modes and options on directories */
+    status = pNtCreateFile( &dir, GENERIC_READ, &attr, &io, NULL, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
+                            FILE_OPEN, FILE_DIRECTORY_FILE, NULL, 0 );
+    ok( !status, "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status );
+    CloseHandle( dir );
+
+    status = pNtCreateFile( &dir, GENERIC_READ, &attr, &io, NULL, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
+                            FILE_CREATE, FILE_DIRECTORY_FILE, NULL, 0 );
+    ok( status == STATUS_OBJECT_NAME_COLLISION || status == STATUS_ACCESS_DENIED,
+        "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status );
+
+    status = pNtCreateFile( &dir, GENERIC_READ, &attr, &io, NULL, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
+                            FILE_OPEN_IF, FILE_DIRECTORY_FILE, NULL, 0 );
+    ok( !status, "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status );
+    CloseHandle( dir );
+
+    status = pNtCreateFile( &dir, GENERIC_READ, &attr, &io, NULL, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
+                            FILE_SUPERSEDE, FILE_DIRECTORY_FILE, NULL, 0 );
+    ok( status == STATUS_INVALID_PARAMETER, "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status );
+
+    status = pNtCreateFile( &dir, GENERIC_READ, &attr, &io, NULL, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
+                            FILE_OVERWRITE, FILE_DIRECTORY_FILE, NULL, 0 );
+    ok( status == STATUS_INVALID_PARAMETER, "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status );
+
+    status = pNtCreateFile( &dir, GENERIC_READ, &attr, &io, NULL, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
+                            FILE_OVERWRITE_IF, FILE_DIRECTORY_FILE, NULL, 0 );
+    ok( status == STATUS_INVALID_PARAMETER, "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status );
+
+    status = pNtCreateFile( &dir, GENERIC_READ, &attr, &io, NULL, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
+                            FILE_OPEN, 0, NULL, 0 );
+    ok( !status, "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status );
+    CloseHandle( dir );
+
+    status = pNtCreateFile( &dir, GENERIC_READ, &attr, &io, NULL, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
+                            FILE_CREATE, 0, NULL, 0 );
+    ok( status == STATUS_OBJECT_NAME_COLLISION || status == STATUS_ACCESS_DENIED,
+        "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status );
+
+    status = pNtCreateFile( &dir, GENERIC_READ, &attr, &io, NULL, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
+                            FILE_OPEN_IF, 0, NULL, 0 );
+    ok( !status, "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status );
+    CloseHandle( dir );
+
+    status = pNtCreateFile( &dir, GENERIC_READ, &attr, &io, NULL, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
+                            FILE_SUPERSEDE, 0, NULL, 0 );
+    ok( status == STATUS_OBJECT_NAME_COLLISION || status == STATUS_ACCESS_DENIED,
+        "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status );
+
+    status = pNtCreateFile( &dir, GENERIC_READ, &attr, &io, NULL, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
+                            FILE_OVERWRITE, 0, NULL, 0 );
+    ok( status == STATUS_OBJECT_NAME_COLLISION || status == STATUS_ACCESS_DENIED,
+        "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status );
+
+    status = pNtCreateFile( &dir, GENERIC_READ, &attr, &io, NULL, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
+                            FILE_OVERWRITE_IF, 0, NULL, 0 );
+    ok( status == STATUS_OBJECT_NAME_COLLISION || status == STATUS_ACCESS_DENIED,
+        "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status );
+
+    pRtlFreeUnicodeString( &nameW );
+
+    pRtlInitUnicodeString( &nameW, systemrootW );
+    attr.Length = sizeof(attr);
+    attr.RootDirectory = NULL;
+    attr.ObjectName = &nameW;
+    attr.Attributes = OBJ_CASE_INSENSITIVE;
+    attr.SecurityDescriptor = NULL;
+    attr.SecurityQualityOfService = NULL;
+    dir = NULL;
+    status = pNtCreateFile( &dir, FILE_APPEND_DATA, &attr, &io, NULL, FILE_ATTRIBUTE_NORMAL, 0,
+                            FILE_OPEN_IF, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 );
+    todo_wine
+    ok( status == STATUS_INVALID_PARAMETER,
+        "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status );
+    pRtlFreeUnicodeString( &nameW );
+}
+
+static void open_file_test(void)
+{
+    NTSTATUS status;
+    HANDLE dir, root, handle;
+    WCHAR path[MAX_PATH];
+    BYTE data[8192];
+    OBJECT_ATTRIBUTES attr;
+    IO_STATUS_BLOCK io;
+    UNICODE_STRING nameW;
+    UINT i, len;
+    BOOL restart = TRUE;
+
+    len = GetWindowsDirectoryW( path, MAX_PATH );
+    pRtlDosPathNameToNtPathName_U( path, &nameW, NULL, NULL );
+    attr.Length = sizeof(attr);
+    attr.RootDirectory = 0;
+    attr.ObjectName = &nameW;
+    attr.Attributes = OBJ_CASE_INSENSITIVE;
+    attr.SecurityDescriptor = NULL;
+    attr.SecurityQualityOfService = NULL;
+    status = pNtOpenFile( &dir, GENERIC_READ, &attr, &io,
+                          FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_DIRECTORY_FILE );
+    ok( !status, "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status );
+    pRtlFreeUnicodeString( &nameW );
+
+    path[3] = 0;  /* root of the drive */
+    pRtlDosPathNameToNtPathName_U( path, &nameW, NULL, NULL );
+    status = pNtOpenFile( &root, GENERIC_READ, &attr, &io,
+                          FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_DIRECTORY_FILE );
+    ok( !status, "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status );
+    pRtlFreeUnicodeString( &nameW );
+
+    /* test opening system dir with RootDirectory set to windows dir */
+    GetSystemDirectoryW( path, MAX_PATH );
+    while (path[len] == '\\') len++;
+    nameW.Buffer = path + len;
+    nameW.Length = lstrlenW(path + len) * sizeof(WCHAR);
+    attr.RootDirectory = dir;
+    status = pNtOpenFile( &handle, GENERIC_READ, &attr, &io,
+                          FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_DIRECTORY_FILE );
+    ok( !status, "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status );
+    CloseHandle( handle );
+
+    /* try uppercase name */
+    for (i = len; path[i]; i++) if (path[i] >= 'a' && path[i] <= 'z') path[i] -= 'a' - 'A';
+    status = pNtOpenFile( &handle, GENERIC_READ, &attr, &io,
+                          FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_DIRECTORY_FILE );
+    ok( !status, "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status );
+    CloseHandle( handle );
+
+    /* try with leading backslash */
+    nameW.Buffer--;
+    nameW.Length += sizeof(WCHAR);
+    status = pNtOpenFile( &handle, GENERIC_READ, &attr, &io,
+                          FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_DIRECTORY_FILE );
+    ok( status == STATUS_INVALID_PARAMETER ||
+        status == STATUS_OBJECT_NAME_INVALID ||
+        status == STATUS_OBJECT_PATH_SYNTAX_BAD,
+        "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status );
+    if (!status) CloseHandle( handle );
+
+    /* try with empty name */
+    nameW.Length = 0;
+    status = pNtOpenFile( &handle, GENERIC_READ, &attr, &io,
+                          FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_DIRECTORY_FILE );
+    ok( !status, "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status );
+    CloseHandle( handle );
+
+    /* try open by file id */
+
+    while (!pNtQueryDirectoryFile( dir, NULL, NULL, NULL, &io, data, sizeof(data),
+                                   FileIdBothDirectoryInformation, FALSE, NULL, restart ))
+    {
+        FILE_ID_BOTH_DIRECTORY_INFORMATION *info = (FILE_ID_BOTH_DIRECTORY_INFORMATION *)data;
+
+        restart = FALSE;
+        for (;;)
+        {
+            if (!info->FileId.QuadPart) goto next;
+            nameW.Buffer = (WCHAR *)&info->FileId;
+            nameW.Length = sizeof(info->FileId);
+            info->FileName[info->FileNameLength/sizeof(WCHAR)] = 0;
+            attr.RootDirectory = dir;
+            status = pNtOpenFile( &handle, GENERIC_READ, &attr, &io,
+                                  FILE_SHARE_READ|FILE_SHARE_WRITE,
+                                  FILE_OPEN_BY_FILE_ID |
+                                  ((info->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? FILE_DIRECTORY_FILE : 0) );
+            ok( status == STATUS_SUCCESS || status == STATUS_ACCESS_DENIED || status == STATUS_NOT_IMPLEMENTED,
+                "open %s failed %x\n", wine_dbgstr_w(info->FileName), status );
+            if (status == STATUS_NOT_IMPLEMENTED)
+            {
+                win_skip( "FILE_OPEN_BY_FILE_ID not supported\n" );
+                break;
+            }
+            if (!status)
+            {
+                FILE_ALL_INFORMATION all_info;
+
+                if (!pNtQueryInformationFile( handle, &io, &all_info, sizeof(all_info), FileAllInformation ))
+                {
+                    /* check that it's the same file */
+                    ok( info->EndOfFile.QuadPart == all_info.StandardInformation.EndOfFile.QuadPart,
+                        "mismatched file size for %s\n", wine_dbgstr_w(info->FileName));
+                    ok( info->LastWriteTime.QuadPart == all_info.BasicInformation.LastWriteTime.QuadPart,
+                        "mismatched write time for %s\n", wine_dbgstr_w(info->FileName));
+                }
+                CloseHandle( handle );
+
+                /* try same thing from drive root */
+                attr.RootDirectory = root;
+                status = pNtOpenFile( &handle, GENERIC_READ, &attr, &io,
+                                      FILE_SHARE_READ|FILE_SHARE_WRITE,
+                                      FILE_OPEN_BY_FILE_ID |
+                                      ((info->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? FILE_DIRECTORY_FILE : 0) );
+                ok( status == STATUS_SUCCESS || status == STATUS_NOT_IMPLEMENTED,
+                    "open %s failed %x\n", wine_dbgstr_w(info->FileName), status );
+                if (!status) CloseHandle( handle );
+            }
+        next:
+            if (!info->NextEntryOffset) break;
+            info = (FILE_ID_BOTH_DIRECTORY_INFORMATION *)((char *)info + info->NextEntryOffset);
+        }
+    }
+
+    CloseHandle( dir );
+    CloseHandle( root );
+}
+
 static void delete_file_test(void)
 {
     NTSTATUS ret;
@@ -207,7 +445,7 @@ static void read_file_test(void)
     const char text[] = "foobar";
     HANDLE handle, read, write;
     NTSTATUS status;
-    IO_STATUS_BLOCK iosb;
+    IO_STATUS_BLOCK iosb, iosb2;
     DWORD written;
     int apc_count = 0;
     char buffer[128];
@@ -355,6 +593,112 @@ static void read_file_test(void)
     ok( apc_count == 1, "apc was not called\n" );
     CloseHandle( read );
 
+    if (!create_pipe( &read, &write, FILE_FLAG_OVERLAPPED, 4096 )) return;
+    ok(DuplicateHandle(GetCurrentProcess(), read, GetCurrentProcess(), &handle, 0, TRUE, DUPLICATE_SAME_ACCESS),
+        "Failed to duplicate handle: %d\n", GetLastError());
+
+    apc_count = 0;
+    U(iosb).Status = 0xdeadbabe;
+    iosb.Information = 0xdeadbeef;
+    status = pNtReadFile( handle, event, apc, &apc_count, &iosb, buffer, 2, NULL, NULL );
+    ok( status == STATUS_PENDING, "wrong status %x\n", status );
+    ok( !is_signaled( event ), "event is signaled\n" );
+    ok( U(iosb).Status == 0xdeadbabe, "wrong status %x\n", U(iosb).Status );
+    ok( iosb.Information == 0xdeadbeef, "wrong info %lu\n", iosb.Information );
+    ok( !apc_count, "apc was called\n" );
+    /* Cancel by other handle */
+    status = pNtCancelIoFile( read, &iosb2 );
+    ok(status == STATUS_SUCCESS, "failed to cancel by different handle: %x\n", status);
+    Sleep(1);  /* FIXME: needed for wine to run the i/o apc  */
+    ok( U(iosb).Status == STATUS_CANCELLED, "wrong status %x\n", U(iosb).Status );
+    ok( iosb.Information == 0, "wrong info %lu\n", iosb.Information );
+    ok( is_signaled( event ), "event is signaled\n" );
+    todo_wine ok( !apc_count, "apc was called\n" );
+    SleepEx( 1, TRUE ); /* alertable sleep */
+    ok( apc_count == 1, "apc was not called\n" );
+
+    apc_count = 0;
+    U(iosb).Status = 0xdeadbabe;
+    iosb.Information = 0xdeadbeef;
+    status = pNtReadFile( read, event, apc, &apc_count, &iosb, buffer, 2, NULL, NULL );
+    ok( status == STATUS_PENDING, "wrong status %x\n", status );
+    ok( !is_signaled( event ), "event is signaled\n" );
+    ok( U(iosb).Status == 0xdeadbabe, "wrong status %x\n", U(iosb).Status );
+    ok( iosb.Information == 0xdeadbeef, "wrong info %lu\n", iosb.Information );
+    ok( !apc_count, "apc was called\n" );
+    /* Close queued handle */
+    CloseHandle( read );
+    SleepEx( 1, TRUE ); /* alertable sleep */
+    ok( U(iosb).Status == 0xdeadbabe, "wrong status %x\n", U(iosb).Status );
+    ok( iosb.Information == 0xdeadbeef, "wrong info %lu\n", iosb.Information );
+    status = pNtCancelIoFile( read, &iosb2 );
+    ok(status == STATUS_INVALID_HANDLE, "cancelled by closed handle?\n");
+    status = pNtCancelIoFile( handle, &iosb2 );
+    ok(status == STATUS_SUCCESS, "failed to cancel: %x\n", status);
+    Sleep(1);  /* FIXME: needed for wine to run the i/o apc  */
+    ok( U(iosb).Status == STATUS_CANCELLED, "wrong status %x\n", U(iosb).Status );
+    ok( iosb.Information == 0, "wrong info %lu\n", iosb.Information );
+    ok( is_signaled( event ), "event is signaled\n" );
+    todo_wine ok( !apc_count, "apc was called\n" );
+    SleepEx( 1, TRUE ); /* alertable sleep */
+    ok( apc_count == 1, "apc was not called\n" );
+    CloseHandle( handle );
+    CloseHandle( write );
+
+    if (pNtCancelIoFileEx)
+    {
+        /* Basic Cancel Ex */
+        if (!create_pipe( &read, &write, FILE_FLAG_OVERLAPPED, 4096 )) return;
+
+        apc_count = 0;
+        U(iosb).Status = 0xdeadbabe;
+        iosb.Information = 0xdeadbeef;
+        status = pNtReadFile( read, event, apc, &apc_count, &iosb, buffer, 2, NULL, NULL );
+        ok( status == STATUS_PENDING, "wrong status %x\n", status );
+        ok( !is_signaled( event ), "event is signaled\n" );
+        ok( U(iosb).Status == 0xdeadbabe, "wrong status %x\n", U(iosb).Status );
+        ok( iosb.Information == 0xdeadbeef, "wrong info %lu\n", iosb.Information );
+        ok( !apc_count, "apc was called\n" );
+        status = pNtCancelIoFileEx( read, &iosb, &iosb2 );
+        ok(status == STATUS_SUCCESS, "Failed to cancel I/O\n");
+        Sleep(1);  /* FIXME: needed for wine to run the i/o apc  */
+        ok( U(iosb).Status == STATUS_CANCELLED, "wrong status %x\n", U(iosb).Status );
+        ok( iosb.Information == 0, "wrong info %lu\n", iosb.Information );
+        ok( is_signaled( event ), "event is signaled\n" );
+        todo_wine ok( !apc_count, "apc was called\n" );
+        SleepEx( 1, TRUE ); /* alertable sleep */
+        ok( apc_count == 1, "apc was not called\n" );
+
+        /* Duplicate iosb */
+        apc_count = 0;
+        U(iosb).Status = 0xdeadbabe;
+        iosb.Information = 0xdeadbeef;
+        status = pNtReadFile( read, event, apc, &apc_count, &iosb, buffer, 2, NULL, NULL );
+        ok( status == STATUS_PENDING, "wrong status %x\n", status );
+        ok( !is_signaled( event ), "event is signaled\n" );
+        ok( U(iosb).Status == 0xdeadbabe, "wrong status %x\n", U(iosb).Status );
+        ok( iosb.Information == 0xdeadbeef, "wrong info %lu\n", iosb.Information );
+        ok( !apc_count, "apc was called\n" );
+        status = pNtReadFile( read, event, apc, &apc_count, &iosb, buffer, 2, NULL, NULL );
+        ok( status == STATUS_PENDING, "wrong status %x\n", status );
+        ok( !is_signaled( event ), "event is signaled\n" );
+        ok( U(iosb).Status == 0xdeadbabe, "wrong status %x\n", U(iosb).Status );
+        ok( iosb.Information == 0xdeadbeef, "wrong info %lu\n", iosb.Information );
+        ok( !apc_count, "apc was called\n" );
+        status = pNtCancelIoFileEx( read, &iosb, &iosb2 );
+        ok(status == STATUS_SUCCESS, "Failed to cancel I/O\n");
+        Sleep(1);  /* FIXME: needed for wine to run the i/o apc  */
+        ok( U(iosb).Status == STATUS_CANCELLED, "wrong status %x\n", U(iosb).Status );
+        ok( iosb.Information == 0, "wrong info %lu\n", iosb.Information );
+        ok( is_signaled( event ), "event is signaled\n" );
+        todo_wine ok( !apc_count, "apc was called\n" );
+        SleepEx( 1, TRUE ); /* alertable sleep */
+        ok( apc_count == 2, "apc was not called\n" );
+
+        CloseHandle( read );
+        CloseHandle( write );
+    }
+
     /* now try a real file */
     if (!(handle = create_temp_file( FILE_FLAG_OVERLAPPED ))) return;
     apc_count = 0;
@@ -362,8 +706,8 @@ static void read_file_test(void)
     iosb.Information = 0xdeadbeef;
     offset.QuadPart = 0;
     ResetEvent( event );
-    pNtWriteFile( handle, event, apc, &apc_count, &iosb, text, strlen(text), &offset, NULL );
-    ok( status == STATUS_PENDING, "wrong status %x\n", status );
+    status = pNtWriteFile( handle, event, apc, &apc_count, &iosb, text, strlen(text), &offset, NULL );
+    ok( status == STATUS_SUCCESS || status == STATUS_PENDING, "wrong status %x\n", status );
     ok( U(iosb).Status == STATUS_SUCCESS, "wrong status %x\n", U(iosb).Status );
     ok( iosb.Information == strlen(text), "wrong info %lu\n", iosb.Information );
     ok( is_signaled( event ), "event is signaled\n" );
@@ -420,8 +764,9 @@ static void read_file_test(void)
     U(iosb).Status = 0xdeadbabe;
     iosb.Information = 0xdeadbeef;
     offset.QuadPart = 0;
-    pNtWriteFile( handle, event, apc, &apc_count, &iosb, text, strlen(text), &offset, NULL );
+    status = pNtWriteFile( handle, event, apc, &apc_count, &iosb, text, strlen(text), &offset, NULL );
     ok( status == STATUS_END_OF_FILE ||
+        status == STATUS_SUCCESS ||
         status == STATUS_PENDING,  /* vista */
         "wrong status %x\n", status );
     ok( U(iosb).Status == STATUS_SUCCESS, "wrong status %x\n", U(iosb).Status );
@@ -678,6 +1023,138 @@ static void test_iocp_fileio(HANDLE h)
     CloseHandle( hPipeClt );
 }
 
+static void test_file_basic_information(void)
+{
+    IO_STATUS_BLOCK io;
+    FILE_BASIC_INFORMATION fbi;
+    HANDLE h;
+    int res;
+    int attrib_mask = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_NORMAL;
+
+    if (!(h = create_temp_file(0))) return;
+
+    /* Check default first */
+    memset(&fbi, 0, sizeof(fbi));
+    res = pNtQueryInformationFile(h, &io, &fbi, sizeof fbi, FileBasicInformation);
+    ok ( res == STATUS_SUCCESS, "can't get attributes, res %x\n", res);
+    ok ( (fbi.FileAttributes & FILE_ATTRIBUTE_ARCHIVE) == FILE_ATTRIBUTE_ARCHIVE,
+         "attribute %x not expected\n", fbi.FileAttributes );
+
+    /* Then SYSTEM */
+    /* Clear fbi to avoid setting times */
+    memset(&fbi, 0, sizeof(fbi));
+    fbi.FileAttributes = FILE_ATTRIBUTE_SYSTEM;
+    res = pNtSetInformationFile(h, &io, &fbi, sizeof fbi, FileBasicInformation);
+    ok ( res == STATUS_SUCCESS, "can't set system attribute\n");
+
+    memset(&fbi, 0, sizeof(fbi));
+    res = pNtQueryInformationFile(h, &io, &fbi, sizeof fbi, FileBasicInformation);
+    ok ( res == STATUS_SUCCESS, "can't get attributes\n");
+    todo_wine ok ( (fbi.FileAttributes & attrib_mask) == FILE_ATTRIBUTE_SYSTEM, "attribute %x not FILE_ATTRIBUTE_SYSTEM\n", fbi.FileAttributes );
+
+    /* Then HIDDEN */
+    memset(&fbi, 0, sizeof(fbi));
+    fbi.FileAttributes = FILE_ATTRIBUTE_HIDDEN;
+    res = pNtSetInformationFile(h, &io, &fbi, sizeof fbi, FileBasicInformation);
+    ok ( res == STATUS_SUCCESS, "can't set system attribute\n");
+
+    memset(&fbi, 0, sizeof(fbi));
+    res = pNtQueryInformationFile(h, &io, &fbi, sizeof fbi, FileBasicInformation);
+    ok ( res == STATUS_SUCCESS, "can't get attributes\n");
+    todo_wine ok ( (fbi.FileAttributes & attrib_mask) == FILE_ATTRIBUTE_HIDDEN, "attribute %x not FILE_ATTRIBUTE_HIDDEN\n", fbi.FileAttributes );
+
+    /* Check NORMAL last of all (to make sure we can clear attributes) */
+    memset(&fbi, 0, sizeof(fbi));
+    fbi.FileAttributes = FILE_ATTRIBUTE_NORMAL;
+    res = pNtSetInformationFile(h, &io, &fbi, sizeof fbi, FileBasicInformation);
+    ok ( res == STATUS_SUCCESS, "can't set normal attribute\n");
+
+    memset(&fbi, 0, sizeof(fbi));
+    res = pNtQueryInformationFile(h, &io, &fbi, sizeof fbi, FileBasicInformation);
+    ok ( res == STATUS_SUCCESS, "can't get attributes\n");
+    todo_wine ok ( (fbi.FileAttributes & attrib_mask) == FILE_ATTRIBUTE_NORMAL, "attribute %x not 0\n", fbi.FileAttributes );
+
+    CloseHandle( h );
+}
+
+static void test_file_all_information(void)
+{
+    IO_STATUS_BLOCK io;
+    /* FileAllInformation, like FileNameInformation, has a variable-length pathname
+     * buffer at the end.  Vista objects with STATUS_BUFFER_OVERFLOW if you
+     * don't leave enough room there.
+     */
+    struct {
+      FILE_ALL_INFORMATION fai;
+      WCHAR buf[256];
+    } fai_buf;
+    HANDLE h;
+    int res;
+    int attrib_mask = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_NORMAL;
+
+    if (!(h = create_temp_file(0))) return;
+
+    /* Check default first */
+    res = pNtQueryInformationFile(h, &io, &fai_buf.fai, sizeof fai_buf, FileAllInformation);
+    ok ( res == STATUS_SUCCESS, "can't get attributes, res %x\n", res);
+    ok ( (fai_buf.fai.BasicInformation.FileAttributes & FILE_ATTRIBUTE_ARCHIVE) == FILE_ATTRIBUTE_ARCHIVE,
+         "attribute %x not expected\n", fai_buf.fai.BasicInformation.FileAttributes );
+
+    /* Then SYSTEM */
+    /* Clear fbi to avoid setting times */
+    memset(&fai_buf.fai.BasicInformation, 0, sizeof(fai_buf.fai.BasicInformation));
+    fai_buf.fai.BasicInformation.FileAttributes = FILE_ATTRIBUTE_SYSTEM;
+    res = pNtSetInformationFile(h, &io, &fai_buf.fai, sizeof fai_buf, FileAllInformation);
+    ok ( res == STATUS_INVALID_INFO_CLASS || res == STATUS_NOT_IMPLEMENTED, "shouldn't be able to set FileAllInformation, res %x\n", res);
+    res = pNtSetInformationFile(h, &io, &fai_buf.fai.BasicInformation, sizeof fai_buf.fai.BasicInformation, FileBasicInformation);
+    ok ( res == STATUS_SUCCESS, "can't set system attribute\n");
+
+    memset(&fai_buf.fai, 0, sizeof(fai_buf.fai));
+    res = pNtQueryInformationFile(h, &io, &fai_buf.fai, sizeof fai_buf, FileAllInformation);
+    ok ( res == STATUS_SUCCESS, "can't get attributes, res %x\n", res);
+    todo_wine ok ( (fai_buf.fai.BasicInformation.FileAttributes & attrib_mask) == FILE_ATTRIBUTE_SYSTEM, "attribute %x not FILE_ATTRIBUTE_SYSTEM\n", fai_buf.fai.BasicInformation.FileAttributes );
+
+    /* Then HIDDEN */
+    memset(&fai_buf.fai.BasicInformation, 0, sizeof(fai_buf.fai.BasicInformation));
+    fai_buf.fai.BasicInformation.FileAttributes = FILE_ATTRIBUTE_HIDDEN;
+    res = pNtSetInformationFile(h, &io, &fai_buf.fai.BasicInformation, sizeof fai_buf.fai.BasicInformation, FileBasicInformation);
+    ok ( res == STATUS_SUCCESS, "can't set system attribute\n");
+
+    memset(&fai_buf.fai, 0, sizeof(fai_buf.fai));
+    res = pNtQueryInformationFile(h, &io, &fai_buf.fai, sizeof fai_buf, FileAllInformation);
+    ok ( res == STATUS_SUCCESS, "can't get attributes\n");
+    todo_wine ok ( (fai_buf.fai.BasicInformation.FileAttributes & attrib_mask) == FILE_ATTRIBUTE_HIDDEN, "attribute %x not FILE_ATTRIBUTE_HIDDEN\n", fai_buf.fai.BasicInformation.FileAttributes );
+
+    /* Check NORMAL last of all (to make sure we can clear attributes) */
+    memset(&fai_buf.fai.BasicInformation, 0, sizeof(fai_buf.fai.BasicInformation));
+    fai_buf.fai.BasicInformation.FileAttributes = FILE_ATTRIBUTE_NORMAL;
+    res = pNtSetInformationFile(h, &io, &fai_buf.fai.BasicInformation, sizeof fai_buf.fai.BasicInformation, FileBasicInformation);
+    ok ( res == STATUS_SUCCESS, "can't set normal attribute\n");
+
+    memset(&fai_buf.fai, 0, sizeof(fai_buf.fai));
+    res = pNtQueryInformationFile(h, &io, &fai_buf.fai, sizeof fai_buf, FileAllInformation);
+    ok ( res == STATUS_SUCCESS, "can't get attributes\n");
+    todo_wine ok ( (fai_buf.fai.BasicInformation.FileAttributes & attrib_mask) == FILE_ATTRIBUTE_NORMAL, "attribute %x not FILE_ATTRIBUTE_NORMAL\n", fai_buf.fai.BasicInformation.FileAttributes );
+
+    CloseHandle( h );
+}
+
+static void test_file_both_information(void)
+{
+    IO_STATUS_BLOCK io;
+    FILE_BOTH_DIR_INFORMATION fbi;
+    HANDLE h;
+    int res;
+
+    if (!(h = create_temp_file(0))) return;
+
+    memset(&fbi, 0, sizeof(fbi));
+    res = pNtQueryInformationFile(h, &io, &fbi, sizeof fbi, FileBothDirectoryInformation);
+    ok ( res == STATUS_INVALID_INFO_CLASS || res == STATUS_NOT_IMPLEMENTED, "shouldn't be able to query FileBothDirectoryInformation, res %x\n", res);
+
+    CloseHandle( h );
+}
+
 static void test_iocompletion(void)
 {
     HANDLE h = INVALID_HANDLE_VALUE;
@@ -696,8 +1173,273 @@ static void test_iocompletion(void)
     }
 }
 
+static void test_file_name_information(void)
+{
+    WCHAR *file_name, *volume_prefix, *expected;
+    FILE_NAME_INFORMATION *info;
+    ULONG old_redir = 1, tmp;
+    UINT file_name_size;
+    IO_STATUS_BLOCK io;
+    UINT info_size;
+    HRESULT hr;
+    HANDLE h;
+    UINT len;
+
+    /* GetVolumePathName is not present before w2k */
+    if (!pGetVolumePathNameW) {
+        win_skip("GetVolumePathNameW not found\n");
+        return;
+    }
+
+    file_name_size = GetSystemDirectoryW( NULL, 0 );
+    file_name = HeapAlloc( GetProcessHeap(), 0, file_name_size * sizeof(*file_name) );
+    volume_prefix = HeapAlloc( GetProcessHeap(), 0, file_name_size * sizeof(*volume_prefix) );
+    expected = HeapAlloc( GetProcessHeap(), 0, file_name_size * sizeof(*volume_prefix) );
+
+    len = GetSystemDirectoryW( file_name, file_name_size );
+    ok(len == file_name_size - 1,
+            "GetSystemDirectoryW returned %u, expected %u.\n",
+            len, file_name_size - 1);
+
+    len = pGetVolumePathNameW( file_name, volume_prefix, file_name_size );
+    ok(len, "GetVolumePathNameW failed.\n");
+
+    len = lstrlenW( volume_prefix );
+    if (len && volume_prefix[len - 1] == '\\') --len;
+    memcpy( expected, file_name + len, (file_name_size - len - 1) * sizeof(WCHAR) );
+    expected[file_name_size - len - 1] = '\0';
+
+    /* A bit more than we actually need, but it keeps the calculation simple. */
+    info_size = sizeof(*info) + (file_name_size * sizeof(WCHAR));
+    info = HeapAlloc( GetProcessHeap(), 0, info_size );
+
+    if (pRtlWow64EnableFsRedirectionEx) pRtlWow64EnableFsRedirectionEx( TRUE, &old_redir );
+    h = CreateFileW( file_name, GENERIC_READ,
+            FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+            NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0 );
+    if (pRtlWow64EnableFsRedirectionEx) pRtlWow64EnableFsRedirectionEx( old_redir, &tmp );
+    ok(h != INVALID_HANDLE_VALUE, "Failed to open file.\n");
+
+    hr = pNtQueryInformationFile( h, &io, info, sizeof(*info) - 1, FileNameInformation );
+    ok(hr == STATUS_INFO_LENGTH_MISMATCH, "NtQueryInformationFile returned %#x.\n", hr);
+
+    memset( info, 0xcc, info_size );
+    hr = pNtQueryInformationFile( h, &io, info, sizeof(*info), FileNameInformation );
+    ok(hr == STATUS_BUFFER_OVERFLOW, "NtQueryInformationFile returned %#x, expected %#x.\n",
+            hr, STATUS_BUFFER_OVERFLOW);
+    ok(U(io).Status == STATUS_BUFFER_OVERFLOW, "io.Status is %#x, expected %#x.\n",
+            U(io).Status, STATUS_BUFFER_OVERFLOW);
+    ok(info->FileNameLength == lstrlenW( expected ) * sizeof(WCHAR), "info->FileNameLength is %u\n", info->FileNameLength);
+    ok(info->FileName[2] == 0xcccc, "info->FileName[2] is %#x, expected 0xcccc.\n", info->FileName[2]);
+    ok(CharLowerW((LPWSTR)(UINT_PTR)info->FileName[1]) == CharLowerW((LPWSTR)(UINT_PTR)expected[1]),
+            "info->FileName[1] is %p, expected %p.\n",
+            CharLowerW((LPWSTR)(UINT_PTR)info->FileName[1]), CharLowerW((LPWSTR)(UINT_PTR)expected[1]));
+    ok(io.Information == sizeof(*info), "io.Information is %lu\n", io.Information);
+
+    memset( info, 0xcc, info_size );
+    hr = pNtQueryInformationFile( h, &io, info, info_size, FileNameInformation );
+    ok(hr == STATUS_SUCCESS, "NtQueryInformationFile returned %#x, expected %#x.\n", hr, STATUS_SUCCESS);
+    ok(U(io).Status == STATUS_SUCCESS, "io.Status is %#x, expected %#x.\n", U(io).Status, STATUS_SUCCESS);
+    ok(info->FileNameLength == lstrlenW( expected ) * sizeof(WCHAR), "info->FileNameLength is %u\n", info->FileNameLength);
+    ok(info->FileName[info->FileNameLength / sizeof(WCHAR)] == 0xcccc, "info->FileName[len] is %#x, expected 0xcccc.\n",
+       info->FileName[info->FileNameLength / sizeof(WCHAR)]);
+    info->FileName[info->FileNameLength / sizeof(WCHAR)] = '\0';
+    ok(!lstrcmpiW( info->FileName, expected ), "info->FileName is %s, expected %s.\n",
+            wine_dbgstr_w( info->FileName ), wine_dbgstr_w( expected ));
+    ok(io.Information == FIELD_OFFSET(FILE_NAME_INFORMATION, FileName) + info->FileNameLength,
+            "io.Information is %lu, expected %u.\n",
+            io.Information, FIELD_OFFSET(FILE_NAME_INFORMATION, FileName) + info->FileNameLength);
+
+    CloseHandle( h );
+    HeapFree( GetProcessHeap(), 0, info );
+    HeapFree( GetProcessHeap(), 0, expected );
+    HeapFree( GetProcessHeap(), 0, volume_prefix );
+
+    if (old_redir || !pGetSystemWow64DirectoryW || !(file_name_size = pGetSystemWow64DirectoryW( NULL, 0 )))
+    {
+        skip("Not running on WoW64, skipping test.\n");
+        HeapFree( GetProcessHeap(), 0, file_name );
+        return;
+    }
+
+    h = CreateFileW( file_name, GENERIC_READ,
+            FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+            NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0 );
+    ok(h != INVALID_HANDLE_VALUE, "Failed to open file.\n");
+    HeapFree( GetProcessHeap(), 0, file_name );
+
+    file_name = HeapAlloc( GetProcessHeap(), 0, file_name_size * sizeof(*file_name) );
+    volume_prefix = HeapAlloc( GetProcessHeap(), 0, file_name_size * sizeof(*volume_prefix) );
+    expected = HeapAlloc( GetProcessHeap(), 0, file_name_size * sizeof(*expected) );
+
+    len = pGetSystemWow64DirectoryW( file_name, file_name_size );
+    ok(len == file_name_size - 1,
+            "GetSystemWow64DirectoryW returned %u, expected %u.\n",
+            len, file_name_size - 1);
+
+    len = pGetVolumePathNameW( file_name, volume_prefix, file_name_size );
+    ok(len, "GetVolumePathNameW failed.\n");
+
+    len = lstrlenW( volume_prefix );
+    if (len && volume_prefix[len - 1] == '\\') --len;
+    memcpy( expected, file_name + len, (file_name_size - len - 1) * sizeof(WCHAR) );
+    expected[file_name_size - len - 1] = '\0';
+
+    info_size = sizeof(*info) + (file_name_size * sizeof(WCHAR));
+    info = HeapAlloc( GetProcessHeap(), 0, info_size );
+
+    memset( info, 0xcc, info_size );
+    hr = pNtQueryInformationFile( h, &io, info, info_size, FileNameInformation );
+    ok(hr == STATUS_SUCCESS, "NtQueryInformationFile returned %#x, expected %#x.\n", hr, STATUS_SUCCESS);
+    info->FileName[info->FileNameLength / sizeof(WCHAR)] = '\0';
+    ok(!lstrcmpiW( info->FileName, expected ), "info->FileName is %s, expected %s.\n",
+            wine_dbgstr_w( info->FileName ), wine_dbgstr_w( expected ));
+
+    CloseHandle( h );
+    HeapFree( GetProcessHeap(), 0, info );
+    HeapFree( GetProcessHeap(), 0, expected );
+    HeapFree( GetProcessHeap(), 0, volume_prefix );
+    HeapFree( GetProcessHeap(), 0, file_name );
+}
+
+static void test_file_all_name_information(void)
+{
+    WCHAR *file_name, *volume_prefix, *expected;
+    FILE_ALL_INFORMATION *info;
+    ULONG old_redir = 1, tmp;
+    UINT file_name_size;
+    IO_STATUS_BLOCK io;
+    UINT info_size;
+    HRESULT hr;
+    HANDLE h;
+    UINT len;
+
+    /* GetVolumePathName is not present before w2k */
+    if (!pGetVolumePathNameW) {
+        win_skip("GetVolumePathNameW not found\n");
+        return;
+    }
+
+    file_name_size = GetSystemDirectoryW( NULL, 0 );
+    file_name = HeapAlloc( GetProcessHeap(), 0, file_name_size * sizeof(*file_name) );
+    volume_prefix = HeapAlloc( GetProcessHeap(), 0, file_name_size * sizeof(*volume_prefix) );
+    expected = HeapAlloc( GetProcessHeap(), 0, file_name_size * sizeof(*volume_prefix) );
+
+    len = GetSystemDirectoryW( file_name, file_name_size );
+    ok(len == file_name_size - 1,
+            "GetSystemDirectoryW returned %u, expected %u.\n",
+            len, file_name_size - 1);
+
+    len = pGetVolumePathNameW( file_name, volume_prefix, file_name_size );
+    ok(len, "GetVolumePathNameW failed.\n");
+
+    len = lstrlenW( volume_prefix );
+    if (len && volume_prefix[len - 1] == '\\') --len;
+    memcpy( expected, file_name + len, (file_name_size - len - 1) * sizeof(WCHAR) );
+    expected[file_name_size - len - 1] = '\0';
+
+    /* A bit more than we actually need, but it keeps the calculation simple. */
+    info_size = sizeof(*info) + (file_name_size * sizeof(WCHAR));
+    info = HeapAlloc( GetProcessHeap(), 0, info_size );
+
+    if (pRtlWow64EnableFsRedirectionEx) pRtlWow64EnableFsRedirectionEx( TRUE, &old_redir );
+    h = CreateFileW( file_name, GENERIC_READ,
+            FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+            NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0 );
+    if (pRtlWow64EnableFsRedirectionEx) pRtlWow64EnableFsRedirectionEx( old_redir, &tmp );
+    ok(h != INVALID_HANDLE_VALUE, "Failed to open file.\n");
+
+    hr = pNtQueryInformationFile( h, &io, info, sizeof(*info) - 1, FileAllInformation );
+    ok(hr == STATUS_INFO_LENGTH_MISMATCH, "NtQueryInformationFile returned %#x, expected %#x.\n",
+            hr, STATUS_INFO_LENGTH_MISMATCH);
+
+    memset( info, 0xcc, info_size );
+    hr = pNtQueryInformationFile( h, &io, info, sizeof(*info), FileAllInformation );
+    ok(hr == STATUS_BUFFER_OVERFLOW, "NtQueryInformationFile returned %#x, expected %#x.\n",
+            hr, STATUS_BUFFER_OVERFLOW);
+    ok(U(io).Status == STATUS_BUFFER_OVERFLOW, "io.Status is %#x, expected %#x.\n",
+            U(io).Status, STATUS_BUFFER_OVERFLOW);
+    ok(info->NameInformation.FileNameLength == lstrlenW( expected ) * sizeof(WCHAR),
+       "info->NameInformation.FileNameLength is %u\n", info->NameInformation.FileNameLength );
+    ok(info->NameInformation.FileName[2] == 0xcccc,
+            "info->NameInformation.FileName[2] is %#x, expected 0xcccc.\n", info->NameInformation.FileName[2]);
+    ok(CharLowerW((LPWSTR)(UINT_PTR)info->NameInformation.FileName[1]) == CharLowerW((LPWSTR)(UINT_PTR)expected[1]),
+            "info->NameInformation.FileName[1] is %p, expected %p.\n",
+            CharLowerW((LPWSTR)(UINT_PTR)info->NameInformation.FileName[1]), CharLowerW((LPWSTR)(UINT_PTR)expected[1]));
+    ok(io.Information == sizeof(*info), "io.Information is %lu\n", io.Information);
+
+    memset( info, 0xcc, info_size );
+    hr = pNtQueryInformationFile( h, &io, info, info_size, FileAllInformation );
+    ok(hr == STATUS_SUCCESS, "NtQueryInformationFile returned %#x, expected %#x.\n", hr, STATUS_SUCCESS);
+    ok(U(io).Status == STATUS_SUCCESS, "io.Status is %#x, expected %#x.\n", U(io).Status, STATUS_SUCCESS);
+    ok(info->NameInformation.FileNameLength == lstrlenW( expected ) * sizeof(WCHAR),
+       "info->NameInformation.FileNameLength is %u\n", info->NameInformation.FileNameLength );
+    ok(info->NameInformation.FileName[info->NameInformation.FileNameLength / sizeof(WCHAR)] == 0xcccc,
+       "info->NameInformation.FileName[len] is %#x, expected 0xcccc.\n",
+       info->NameInformation.FileName[info->NameInformation.FileNameLength / sizeof(WCHAR)]);
+    info->NameInformation.FileName[info->NameInformation.FileNameLength / sizeof(WCHAR)] = '\0';
+    ok(!lstrcmpiW( info->NameInformation.FileName, expected ),
+            "info->NameInformation.FileName is %s, expected %s.\n",
+            wine_dbgstr_w( info->NameInformation.FileName ), wine_dbgstr_w( expected ));
+    ok(io.Information == FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName)
+            + info->NameInformation.FileNameLength,
+            "io.Information is %lu\n", io.Information );
+
+    CloseHandle( h );
+    HeapFree( GetProcessHeap(), 0, info );
+    HeapFree( GetProcessHeap(), 0, expected );
+    HeapFree( GetProcessHeap(), 0, volume_prefix );
+
+    if (old_redir || !pGetSystemWow64DirectoryW || !(file_name_size = pGetSystemWow64DirectoryW( NULL, 0 )))
+    {
+        skip("Not running on WoW64, skipping test.\n");
+        HeapFree( GetProcessHeap(), 0, file_name );
+        return;
+    }
+
+    h = CreateFileW( file_name, GENERIC_READ,
+            FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+            NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0 );
+    ok(h != INVALID_HANDLE_VALUE, "Failed to open file.\n");
+    HeapFree( GetProcessHeap(), 0, file_name );
+
+    file_name = HeapAlloc( GetProcessHeap(), 0, file_name_size * sizeof(*file_name) );
+    volume_prefix = HeapAlloc( GetProcessHeap(), 0, file_name_size * sizeof(*volume_prefix) );
+    expected = HeapAlloc( GetProcessHeap(), 0, file_name_size * sizeof(*expected) );
+
+    len = pGetSystemWow64DirectoryW( file_name, file_name_size );
+    ok(len == file_name_size - 1,
+            "GetSystemWow64DirectoryW returned %u, expected %u.\n",
+            len, file_name_size - 1);
+
+    len = pGetVolumePathNameW( file_name, volume_prefix, file_name_size );
+    ok(len, "GetVolumePathNameW failed.\n");
+
+    len = lstrlenW( volume_prefix );
+    if (len && volume_prefix[len - 1] == '\\') --len;
+    memcpy( expected, file_name + len, (file_name_size - len - 1) * sizeof(WCHAR) );
+    expected[file_name_size - len - 1] = '\0';
+
+    info_size = sizeof(*info) + (file_name_size * sizeof(WCHAR));
+    info = HeapAlloc( GetProcessHeap(), 0, info_size );
+
+    memset( info, 0xcc, info_size );
+    hr = pNtQueryInformationFile( h, &io, info, info_size, FileAllInformation );
+    ok(hr == STATUS_SUCCESS, "NtQueryInformationFile returned %#x, expected %#x.\n", hr, STATUS_SUCCESS);
+    info->NameInformation.FileName[info->NameInformation.FileNameLength / sizeof(WCHAR)] = '\0';
+    ok(!lstrcmpiW( info->NameInformation.FileName, expected ), "info->NameInformation.FileName is %s, expected %s.\n",
+            wine_dbgstr_w( info->NameInformation.FileName ), wine_dbgstr_w( expected ));
+
+    CloseHandle( h );
+    HeapFree( GetProcessHeap(), 0, info );
+    HeapFree( GetProcessHeap(), 0, expected );
+    HeapFree( GetProcessHeap(), 0, volume_prefix );
+    HeapFree( GetProcessHeap(), 0, file_name );
+}
+
 START_TEST(file)
 {
+    HMODULE hkernel32 = GetModuleHandleA("kernel32.dll");
     HMODULE hntdll = GetModuleHandleA("ntdll.dll");
     if (!hntdll)
     {
@@ -705,13 +1447,21 @@ START_TEST(file)
         return;
     }
 
+    pGetVolumePathNameW = (void *)GetProcAddress(hkernel32, "GetVolumePathNameW");
+    pGetSystemWow64DirectoryW = (void *)GetProcAddress(hkernel32, "GetSystemWow64DirectoryW");
+
     pRtlFreeUnicodeString   = (void *)GetProcAddress(hntdll, "RtlFreeUnicodeString");
     pRtlInitUnicodeString   = (void *)GetProcAddress(hntdll, "RtlInitUnicodeString");
     pRtlDosPathNameToNtPathName_U = (void *)GetProcAddress(hntdll, "RtlDosPathNameToNtPathName_U");
+    pRtlWow64EnableFsRedirectionEx = (void *)GetProcAddress(hntdll, "RtlWow64EnableFsRedirectionEx");
     pNtCreateMailslotFile   = (void *)GetProcAddress(hntdll, "NtCreateMailslotFile");
+    pNtCreateFile           = (void *)GetProcAddress(hntdll, "NtCreateFile");
+    pNtOpenFile             = (void *)GetProcAddress(hntdll, "NtOpenFile");
     pNtDeleteFile           = (void *)GetProcAddress(hntdll, "NtDeleteFile");
     pNtReadFile             = (void *)GetProcAddress(hntdll, "NtReadFile");
     pNtWriteFile            = (void *)GetProcAddress(hntdll, "NtWriteFile");
+    pNtCancelIoFile         = (void *)GetProcAddress(hntdll, "NtCancelIoFile");
+    pNtCancelIoFileEx       = (void *)GetProcAddress(hntdll, "NtCancelIoFileEx");
     pNtClose                = (void *)GetProcAddress(hntdll, "NtClose");
     pNtCreateIoCompletion   = (void *)GetProcAddress(hntdll, "NtCreateIoCompletion");
     pNtOpenIoCompletion     = (void *)GetProcAddress(hntdll, "NtOpenIoCompletion");
@@ -719,9 +1469,18 @@ START_TEST(file)
     pNtRemoveIoCompletion   = (void *)GetProcAddress(hntdll, "NtRemoveIoCompletion");
     pNtSetIoCompletion      = (void *)GetProcAddress(hntdll, "NtSetIoCompletion");
     pNtSetInformationFile   = (void *)GetProcAddress(hntdll, "NtSetInformationFile");
+    pNtQueryInformationFile = (void *)GetProcAddress(hntdll, "NtQueryInformationFile");
+    pNtQueryDirectoryFile   = (void *)GetProcAddress(hntdll, "NtQueryDirectoryFile");
 
+    create_file_test();
+    open_file_test();
     delete_file_test();
     read_file_test();
     nt_mailslot_test();
     test_iocompletion();
+    test_file_basic_information();
+    test_file_all_information();
+    test_file_both_information();
+    test_file_name_information();
+    test_file_all_name_information();
 }
index 448d93c..aba50d2 100644 (file)
@@ -4,8 +4,10 @@
        <include base="ntdll_winetest">.</include>
        <define name="__ROS_LONG64__" />
        <library>ntdll</library>
+       <library>user32</library>
        <file>atom.c</file>
        <file>change.c</file>
+       <file>directory.c</file>
        <file>env.c</file>
        <file>error.c</file>
        <file>exception.c</file>
index d5cf158..d052c35 100644 (file)
@@ -44,6 +44,7 @@ static NTSTATUS (WINAPI *pNtOpenDirectoryObject)(PHANDLE, ACCESS_MASK, POBJECT_A
 static NTSTATUS (WINAPI *pNtCreateDirectoryObject)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES);
 static NTSTATUS (WINAPI *pNtOpenSymbolicLinkObject)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES);
 static NTSTATUS (WINAPI *pNtCreateSymbolicLinkObject)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PUNICODE_STRING);
+static NTSTATUS (WINAPI *pNtQueryObject)(HANDLE,OBJECT_INFORMATION_CLASS,PVOID,ULONG,PULONG);
 
 
 static void test_case_sensitive (void)
@@ -617,6 +618,72 @@ static void test_symboliclink(void)
     pNtClose(dir);
 }
 
+static void test_query_object(void)
+{
+    static const WCHAR name[] = {'\\','B','a','s','e','N','a','m','e','d','O','b','j','e','c','t','s',
+                                 '\\','t','e','s','t','_','e','v','e','n','t'};
+    HANDLE handle;
+    char buffer[1024];
+    NTSTATUS status;
+    ULONG len;
+    UNICODE_STRING *str;
+    char dir[MAX_PATH];
+
+    handle = CreateEventA( NULL, FALSE, FALSE, "test_event" );
+
+    len = 0;
+    status = pNtQueryObject( handle, ObjectNameInformation, buffer, 0, &len );
+    ok( status == STATUS_INFO_LENGTH_MISMATCH, "NtQueryObject failed %x\n", status );
+    ok( len >= sizeof(UNICODE_STRING) + sizeof(name) + sizeof(WCHAR), "unexpected len %u\n", len );
+
+    len = 0;
+    status = pNtQueryObject( handle, ObjectNameInformation, buffer, sizeof(UNICODE_STRING), &len );
+    ok( status == STATUS_INFO_LENGTH_MISMATCH, "NtQueryObject failed %x\n", status );
+    ok( len >= sizeof(UNICODE_STRING) + sizeof(name) + sizeof(WCHAR), "unexpected len %u\n", len );
+
+    len = 0;
+    status = pNtQueryObject( handle, ObjectNameInformation, buffer, sizeof(buffer), &len );
+    ok( status == STATUS_SUCCESS, "NtQueryObject failed %x\n", status );
+    ok( len > sizeof(UNICODE_STRING), "unexpected len %u\n", len );
+    str = (UNICODE_STRING *)buffer;
+    ok( sizeof(UNICODE_STRING) + str->Length + sizeof(WCHAR) == len, "unexpected len %u\n", len );
+    ok( str->Length >= sizeof(name), "unexpected len %u\n", str->Length );
+    /* there can be a \\Sessions prefix in the name */
+    ok( !memcmp( str->Buffer + (str->Length - sizeof(name)) / sizeof(WCHAR), name, sizeof(name) ),
+        "wrong name %s\n", wine_dbgstr_w(str->Buffer) );
+
+    len -= sizeof(WCHAR);
+    status = pNtQueryObject( handle, ObjectNameInformation, buffer, len, &len );
+    ok( status == STATUS_INFO_LENGTH_MISMATCH, "NtQueryObject failed %x\n", status );
+    ok( len >= sizeof(UNICODE_STRING) + sizeof(name) + sizeof(WCHAR), "unexpected len %u\n", len );
+
+    pNtClose( handle );
+
+    handle = CreateEventA( NULL, FALSE, FALSE, NULL );
+    len = 0;
+    status = pNtQueryObject( handle, ObjectNameInformation, buffer, sizeof(buffer), &len );
+    ok( status == STATUS_SUCCESS, "NtQueryObject failed %x\n", status );
+    ok( len == sizeof(UNICODE_STRING), "unexpected len %u\n", len );
+    str = (UNICODE_STRING *)buffer;
+    ok( str->Length == 0, "unexpected len %u\n", len );
+    ok( str->Buffer == NULL, "unexpected ptr %p\n", str->Buffer );
+    pNtClose( handle );
+
+    GetWindowsDirectoryA( dir, MAX_PATH );
+    handle = CreateFileA( dir, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
+                          FILE_FLAG_BACKUP_SEMANTICS, 0 );
+    len = 0;
+    status = pNtQueryObject( handle, ObjectNameInformation, buffer, sizeof(buffer), &len );
+    ok( status == STATUS_SUCCESS, "NtQueryObject failed %x\n", status );
+    ok( len > sizeof(UNICODE_STRING), "unexpected len %u\n", len );
+    str = (UNICODE_STRING *)buffer;
+    ok( sizeof(UNICODE_STRING) + str->Length + sizeof(WCHAR) == len ||
+        broken(sizeof(UNICODE_STRING) + str->Length == len), /* NT4 */
+        "unexpected len %u\n", len );
+    trace( "got %s len %u\n", wine_dbgstr_w(str->Buffer), len );
+    pNtClose( handle );
+}
+
 START_TEST(om)
 {
     HMODULE hntdll = GetModuleHandleA("ntdll.dll");
@@ -646,10 +713,12 @@ START_TEST(om)
     pNtCreateSemaphore      =  (void *)GetProcAddress(hntdll, "NtCreateSemaphore");
     pNtCreateTimer          =  (void *)GetProcAddress(hntdll, "NtCreateTimer");
     pNtCreateSection        =  (void *)GetProcAddress(hntdll, "NtCreateSection");
+    pNtQueryObject          =  (void *)GetProcAddress(hntdll, "NtQueryObject");
 
     test_case_sensitive();
     test_namespace_pipe();
     test_name_collisions();
     test_directory();
     test_symboliclink();
+    test_query_object();
 }
index 730f818..1cad29e 100755 (executable)
@@ -116,22 +116,24 @@ typedef enum _KEY_VALUE_INFORMATION_CLASS {
 #endif
 
 static NTSTATUS (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);
 static NTSTATUS (WINAPI * pRtlQueryRegistryValues)(IN ULONG, IN PCWSTR,IN PRTL_QUERY_REGISTRY_TABLE, IN PVOID,IN PVOID);
 static NTSTATUS (WINAPI * pRtlCheckRegistryKey)(IN ULONG,IN PWSTR);
-static NTSTATUS (WINAPI * pRtlOpenCurrentUser)(IN ACCESS_MASK, OUT PHKEY);
+static NTSTATUS (WINAPI * pRtlOpenCurrentUser)(IN ACCESS_MASK, PHANDLE);
 static NTSTATUS (WINAPI * pNtOpenKey)(PHANDLE, IN ACCESS_MASK, IN POBJECT_ATTRIBUTES);
 static NTSTATUS (WINAPI * pNtClose)(IN HANDLE);
 static NTSTATUS (WINAPI * pNtDeleteValueKey)(IN HANDLE, IN PUNICODE_STRING);
-static NTSTATUS (WINAPI * pNtFlushKey)(HKEY);
-static NTSTATUS (WINAPI * pNtDeleteKey)(HKEY);
-static NTSTATUS (WINAPI * pNtCreateKey)( PHKEY retkey, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr,
+static NTSTATUS (WINAPI * pNtFlushKey)(HANDLE);
+static NTSTATUS (WINAPI * pNtDeleteKey)(HANDLE);
+static NTSTATUS (WINAPI * pNtCreateKey)( PHANDLE retkey, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr,
                              ULONG TitleIndex, const UNICODE_STRING *class, ULONG options,
                              PULONG dispos );
 static NTSTATUS (WINAPI * pNtQueryValueKey)(HANDLE,const UNICODE_STRING *,KEY_VALUE_INFORMATION_CLASS,void *,DWORD,DWORD *);
-static NTSTATUS (WINAPI * pNtSetValueKey)( PHKEY, const PUNICODE_STRING, ULONG,
-                               ULONG, const PVOID, ULONG  );
+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 LPVOID   (WINAPI * pRtlReAllocateHeap)(IN PVOID, IN ULONG, IN PVOID, IN ULONG);
@@ -140,7 +142,8 @@ static NTSTATUS (WINAPI * pRtlUnicodeStringToAnsiString)(PSTRING, PUNICODE_STRIN
 static NTSTATUS (WINAPI * pRtlFreeHeap)(PVOID, ULONG, PVOID);
 static LPVOID   (WINAPI * pRtlAllocateHeap)(PVOID,ULONG,ULONG);
 static NTSTATUS (WINAPI * pRtlZeroMemory)(PVOID, ULONG);
-static NTSTATUS (WINAPI * pRtlpNtQueryValueKey)(HANDLE,ULONG*,PBYTE,DWORD*);
+static NTSTATUS (WINAPI * pRtlpNtQueryValueKey)(HANDLE,ULONG*,PBYTE,DWORD*,void *);
+static NTSTATUS (WINAPI * pRtlOpenCurrentUser)(ACCESS_MASK,HANDLE*);
 
 static HMODULE hntdll = 0;
 static int CurrentTest = 0;
@@ -161,6 +164,7 @@ static BOOL InitFunctionPtrs(void)
         trace("Could not load ntdll.dll\n");
         return FALSE;
     }
+    NTDLL_GET_PROC(RtlInitUnicodeString)
     NTDLL_GET_PROC(RtlCreateUnicodeStringFromAsciiz)
     NTDLL_GET_PROC(RtlCreateUnicodeString)
     NTDLL_GET_PROC(RtlFreeUnicodeString)
@@ -174,6 +178,7 @@ static BOOL InitFunctionPtrs(void)
     NTDLL_GET_PROC(NtFlushKey)
     NTDLL_GET_PROC(NtDeleteKey)
     NTDLL_GET_PROC(NtQueryValueKey)
+    NTDLL_GET_PROC(NtQueryInformationProcess)
     NTDLL_GET_PROC(NtSetValueKey)
     NTDLL_GET_PROC(NtOpenKey)
     NTDLL_GET_PROC(RtlFormatCurrentUserKeyPath)
@@ -184,6 +189,7 @@ static BOOL InitFunctionPtrs(void)
     NTDLL_GET_PROC(RtlAllocateHeap)
     NTDLL_GET_PROC(RtlZeroMemory)
     NTDLL_GET_PROC(RtlpNtQueryValueKey)
+    NTDLL_GET_PROC(RtlOpenCurrentUser)
     return TRUE;
 }
 #undef NTDLL_GET_PROC
@@ -336,9 +342,6 @@ static void test_NtOpenKey(void)
     OBJECT_ATTRIBUTES attr;
     ACCESS_MASK am = KEY_READ;
 
-    if (0)
-    {
-    /* Crashes Wine */
     /* All NULL */
     status = pNtOpenKey(NULL, 0, NULL);
     ok(status == STATUS_ACCESS_VIOLATION, "Expected STATUS_ACCESS_VIOLATION, got: 0x%08x\n", status);
@@ -347,29 +350,27 @@ static void test_NtOpenKey(void)
     status = pNtOpenKey(&key, 0, NULL);
     ok(status == STATUS_ACCESS_VIOLATION /* W2K3/XP/W2K */ || status == STATUS_INVALID_PARAMETER /* NT4 */,
         "Expected STATUS_ACCESS_VIOLATION or STATUS_INVALID_PARAMETER(NT4), got: 0x%08x\n", status);
-    }
 
     InitializeObjectAttributes(&attr, &winetestpath, 0, 0, 0);
 
     /* NULL key */
-    status = pNtOpenKey(NULL, 0, &attr);
-    todo_wine
-        ok(status == STATUS_ACCESS_VIOLATION, "Expected STATUS_ACCESS_VIOLATION, got: 0x%08x\n", status);
+    status = pNtOpenKey(NULL, am, &attr);
+    ok(status == STATUS_ACCESS_VIOLATION, "Expected STATUS_ACCESS_VIOLATION, got: 0x%08x\n", status);
 
     /* Length > sizeof(OBJECT_ATTRIBUTES) */
     attr.Length *= 2;
     status = pNtOpenKey(&key, am, &attr);
-    todo_wine
-        ok(status == STATUS_INVALID_PARAMETER, "Expected STATUS_INVALID_PARAMETER, got: 0x%08x\n", status);
+    ok(status == STATUS_INVALID_PARAMETER, "Expected STATUS_INVALID_PARAMETER, got: 0x%08x\n", status);
 }
 
 static void test_NtCreateKey(void)
 {
     /*Create WineTest*/
     OBJECT_ATTRIBUTES attr;
-    HKEY key;
+    HANDLE key, subkey;
     ACCESS_MASK am = GENERIC_ALL;
     NTSTATUS status;
+    UNICODE_STRING str;
 
     /* All NULL */
     status = pNtCreateKey(NULL, 0, NULL, 0, 0, 0, 0);
@@ -397,14 +398,51 @@ static void test_NtCreateKey(void)
     status = pNtCreateKey(NULL, 0, &attr, 0, 0, 0, 0);
     ok(status == STATUS_ACCESS_VIOLATION, "Expected STATUS_ACCESS_VIOLATION, got: 0x%08x\n", status);
 
-    status = pNtCreateKey(&key, am, &attr, 0, 0, 0, 0);
-    ok(status == STATUS_SUCCESS, "NtCreateKey Failed: 0x%08x\n", status);
-
     /* Length > sizeof(OBJECT_ATTRIBUTES) */
     attr.Length *= 2;
     status = pNtCreateKey(&key, am, &attr, 0, 0, 0, 0);
     ok(status == STATUS_INVALID_PARAMETER, "Expected STATUS_INVALID_PARAMETER, got: 0x%08x\n", status);
 
+    attr.Length = sizeof(attr);
+    status = pNtCreateKey(&key, am, &attr, 0, 0, 0, 0);
+    ok(status == STATUS_SUCCESS, "NtCreateKey Failed: 0x%08x\n", status);
+
+    attr.RootDirectory = key;
+    attr.ObjectName = &str;
+
+    pRtlCreateUnicodeStringFromAsciiz( &str, "test\\sub\\key" );
+    status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 );
+    ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtCreateKey failed: 0x%08x\n", status );
+    pRtlFreeUnicodeString( &str );
+
+    pRtlCreateUnicodeStringFromAsciiz( &str, "test\\subkey" );
+    status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 );
+    ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtCreateKey failed: 0x%08x\n", status );
+    pRtlFreeUnicodeString( &str );
+
+    pRtlCreateUnicodeStringFromAsciiz( &str, "test\\subkey\\" );
+    status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 );
+    ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtCreateKey failed: 0x%08x\n", status );
+    pRtlFreeUnicodeString( &str );
+
+    pRtlCreateUnicodeStringFromAsciiz( &str, "test_subkey\\" );
+    status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 );
+    ok( status == STATUS_SUCCESS || broken(status == STATUS_OBJECT_NAME_NOT_FOUND), /* nt4 */
+        "NtCreateKey failed: 0x%08x\n", status );
+    if (status == STATUS_SUCCESS)
+    {
+        pNtDeleteKey( subkey );
+        pNtClose( subkey );
+    }
+    pRtlFreeUnicodeString( &str );
+
+    pRtlCreateUnicodeStringFromAsciiz( &str, "test_subkey" );
+    status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 );
+    ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
+    pRtlFreeUnicodeString( &str );
+    pNtDeleteKey( subkey );
+    pNtClose( subkey );
+
     pNtClose(key);
 }
 
@@ -437,7 +475,7 @@ static void test_NtSetValueKey(void)
 static void test_RtlOpenCurrentUser(void)
 {
     NTSTATUS status;
-    HKEY handle;
+    HANDLE handle;
     status=pRtlOpenCurrentUser(KEY_READ, &handle);
     ok(status == STATUS_SUCCESS, "RtlOpenCurrentUser Failed: 0x%08x\n", status);
     pNtClose(handle);
@@ -482,7 +520,7 @@ static void test_NtQueryValueKey(void)
     KEY_VALUE_BASIC_INFORMATION *basic_info;
     KEY_VALUE_PARTIAL_INFORMATION *partial_info;
     KEY_VALUE_FULL_INFORMATION *full_info;
-    DWORD len;
+    DWORD len, expected;
 
     pRtlCreateUnicodeStringFromAsciiz(&ValName, "deletetest");
 
@@ -556,7 +594,7 @@ static void test_NtQueryValueKey(void)
     pRtlCreateUnicodeStringFromAsciiz(&ValName, "stringtest");
 
     status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, NULL, 0, &len);
-    todo_wine ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryValueKey should have returned STATUS_BUFFER_TOO_SMALL instead of 0x%08x\n", status);
+    ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryValueKey should have returned STATUS_BUFFER_TOO_SMALL instead of 0x%08x\n", status);
     partial_info = HeapAlloc(GetProcessHeap(), 0, len+1);
     memset(partial_info, 0xbd, len+1);
     status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, partial_info, len, &len);
@@ -566,6 +604,21 @@ static void test_NtQueryValueKey(void)
     ok(partial_info->DataLength == STR_TRUNC_SIZE, "NtQueryValueKey returned wrong DataLength %d\n", partial_info->DataLength);
     ok(!memcmp(partial_info->Data, stringW, STR_TRUNC_SIZE), "incorrect Data returned\n");
     ok(*(partial_info->Data+STR_TRUNC_SIZE) == 0xbd, "string overflowed %02x\n", *(partial_info->Data+STR_TRUNC_SIZE));
+
+    expected = len;
+    status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, partial_info, 0, &len);
+    ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryValueKey wrong status 0x%08x\n", status);
+    ok(len == expected, "NtQueryValueKey wrong len %u\n", len);
+    status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, partial_info, 1, &len);
+    ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryValueKey wrong status 0x%08x\n", status);
+    ok(len == expected, "NtQueryValueKey wrong len %u\n", len);
+    status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, partial_info, FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) - 1, &len);
+    ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryValueKey wrong status 0x%08x\n", status);
+    ok(len == expected, "NtQueryValueKey wrong len %u\n", len);
+    status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, partial_info, FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data), &len);
+    ok(status == STATUS_BUFFER_OVERFLOW, "NtQueryValueKey wrong status 0x%08x\n", status);
+    ok(len == expected, "NtQueryValueKey wrong len %u\n", len);
+
     HeapFree(GetProcessHeap(), 0, partial_info);
 
     pRtlFreeUnicodeString(&ValName);
@@ -593,10 +646,597 @@ static void test_RtlpNtQueryValueKey(void)
 {
     NTSTATUS status;
 
-    status = pRtlpNtQueryValueKey(NULL, NULL, NULL, NULL);
+    status = pRtlpNtQueryValueKey(NULL, NULL, NULL, NULL, NULL);
     ok(status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got: 0x%08x\n", status);
 }
 
+static void test_symlinks(void)
+{
+    static const WCHAR linkW[] = {'l','i','n','k',0};
+    static const WCHAR valueW[] = {'v','a','l','u','e',0};
+    static const WCHAR symlinkW[] = {'S','y','m','b','o','l','i','c','L','i','n','k','V','a','l','u','e',0};
+    static const WCHAR targetW[] = {'\\','t','a','r','g','e','t',0};
+    static UNICODE_STRING null_str;
+    char buffer[1024];
+    KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
+    WCHAR *target;
+    UNICODE_STRING symlink_str, link_str, target_str, value_str;
+    HANDLE root, key, link;
+    OBJECT_ATTRIBUTES attr;
+    NTSTATUS status;
+    DWORD target_len, len, dw;
+
+    pRtlInitUnicodeString( &link_str, linkW );
+    pRtlInitUnicodeString( &symlink_str, symlinkW );
+    pRtlInitUnicodeString( &target_str, targetW + 1 );
+    pRtlInitUnicodeString( &value_str, valueW );
+
+    target_len = winetestpath.Length + sizeof(targetW);
+    target = pRtlAllocateHeap( GetProcessHeap(), 0, target_len + sizeof(targetW) /*for loop test*/ );
+    memcpy( target, winetestpath.Buffer, winetestpath.Length );
+    memcpy( target + winetestpath.Length/sizeof(WCHAR), targetW, sizeof(targetW) );
+
+    attr.Length = sizeof(attr);
+    attr.RootDirectory = 0;
+    attr.Attributes = 0;
+    attr.ObjectName = &winetestpath;
+    attr.SecurityDescriptor = NULL;
+    attr.SecurityQualityOfService = NULL;
+
+    status = pNtCreateKey( &root, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
+    ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
+
+    attr.RootDirectory = root;
+    attr.ObjectName = &link_str;
+    status = pNtCreateKey( &link, KEY_ALL_ACCESS, &attr, 0, 0, REG_OPTION_CREATE_LINK, 0 );
+    ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
+
+    /* REG_SZ is not allowed */
+    status = pNtSetValueKey( link, &symlink_str, 0, REG_SZ, target, target_len );
+    ok( status == STATUS_ACCESS_DENIED, "NtSetValueKey wrong status 0x%08x\n", status );
+    status = pNtSetValueKey( link, &symlink_str, 0, REG_LINK, target, target_len - sizeof(WCHAR) );
+    ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status );
+    /* other values are not allowed */
+    status = pNtSetValueKey( link, &link_str, 0, REG_LINK, target, target_len - sizeof(WCHAR) );
+    ok( status == STATUS_ACCESS_DENIED, "NtSetValueKey wrong status 0x%08x\n", status );
+
+    /* try opening the target through the link */
+
+    attr.ObjectName = &link_str;
+    status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
+    ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtOpenKey wrong status 0x%08x\n", status );
+
+    attr.ObjectName = &target_str;
+    status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
+    ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
+
+    dw = 0xbeef;
+    status = pNtSetValueKey( key, &value_str, 0, REG_DWORD, &dw, sizeof(dw) );
+    ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status );
+    pNtClose( key );
+
+    attr.ObjectName = &link_str;
+    status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
+    ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status );
+
+    len = sizeof(buffer);
+    status = pNtQueryValueKey( key, &value_str, KeyValuePartialInformation, info, len, &len );
+    ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
+    ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + sizeof(DWORD), "wrong len %u\n", len );
+
+    status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
+    ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtQueryValueKey failed: 0x%08x\n", status );
+
+    /* REG_LINK can be created in non-link keys */
+    status = pNtSetValueKey( key, &symlink_str, 0, REG_LINK, target, target_len - sizeof(WCHAR) );
+    ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status );
+    len = sizeof(buffer);
+    status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
+    ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
+    ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + target_len - sizeof(WCHAR),
+        "wrong len %u\n", len );
+    status = pNtDeleteValueKey( key, &symlink_str );
+    ok( status == STATUS_SUCCESS, "NtDeleteValueKey failed: 0x%08x\n", status );
+
+    pNtClose( key );
+
+    attr.Attributes = 0;
+    status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
+    ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
+
+    len = sizeof(buffer);
+    status = pNtQueryValueKey( key, &value_str, KeyValuePartialInformation, info, len, &len );
+    ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
+    ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + sizeof(DWORD), "wrong len %u\n", len );
+
+    status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
+    ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtQueryValueKey failed: 0x%08x\n", status );
+    pNtClose( key );
+
+    /* now open the symlink itself */
+
+    attr.RootDirectory = root;
+    attr.Attributes = OBJ_OPENLINK;
+    attr.ObjectName = &link_str;
+    status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
+    ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status );
+
+    len = sizeof(buffer);
+    status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
+    ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
+    ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + target_len - sizeof(WCHAR),
+        "wrong len %u\n", len );
+    pNtClose( key );
+
+    status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
+    ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
+    len = sizeof(buffer);
+    status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
+    ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
+    ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + target_len - sizeof(WCHAR),
+        "wrong len %u\n", len );
+    pNtClose( key );
+
+    if (0)  /* crashes the Windows kernel on some Vista systems */
+    {
+        /* reopen the link from itself */
+
+        attr.RootDirectory = link;
+        attr.Attributes = OBJ_OPENLINK;
+        attr.ObjectName = &null_str;
+        status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
+        ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status );
+        len = sizeof(buffer);
+        status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
+        ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
+        ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + target_len - sizeof(WCHAR),
+            "wrong len %u\n", len );
+        pNtClose( key );
+
+        status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
+        ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
+        len = sizeof(buffer);
+        status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
+        ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
+        ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + target_len - sizeof(WCHAR),
+            "wrong len %u\n", len );
+        pNtClose( key );
+    }
+
+    if (0)  /* crashes the Windows kernel in most versions */
+    {
+        attr.RootDirectory = link;
+        attr.Attributes = 0;
+        attr.ObjectName = &null_str;
+        status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
+        ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status );
+        len = sizeof(buffer);
+        status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
+        ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtQueryValueKey failed: 0x%08x\n", status );
+        pNtClose( key );
+
+        status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
+        ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
+        len = sizeof(buffer);
+        status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
+        ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtQueryValueKey failed: 0x%08x\n", status );
+        pNtClose( key );
+    }
+
+    /* target with terminating null doesn't work */
+    status = pNtSetValueKey( link, &symlink_str, 0, REG_LINK, target, target_len );
+    ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status );
+    attr.RootDirectory = root;
+    attr.Attributes = 0;
+    attr.ObjectName = &link_str;
+    status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
+    ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtOpenKey wrong status 0x%08x\n", status );
+
+    /* relative symlink, works only on win2k */
+    status = pNtSetValueKey( link, &symlink_str, 0, REG_LINK, targetW+1, sizeof(targetW)-2*sizeof(WCHAR) );
+    ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status );
+    attr.ObjectName = &link_str;
+    status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
+    ok( status == STATUS_SUCCESS || status == STATUS_OBJECT_NAME_NOT_FOUND,
+        "NtOpenKey wrong status 0x%08x\n", status );
+
+    status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, REG_OPTION_CREATE_LINK, 0 );
+    ok( status == STATUS_OBJECT_NAME_COLLISION, "NtCreateKey failed: 0x%08x\n", status );
+
+    status = pNtDeleteKey( link );
+    ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08x\n", status );
+    pNtClose( link );
+
+    attr.ObjectName = &target_str;
+    status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
+    ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status );
+    status = pNtDeleteKey( key );
+    ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08x\n", status );
+    pNtClose( key );
+
+    /* symlink loop */
+
+    status = pNtCreateKey( &link, KEY_ALL_ACCESS, &attr, 0, 0, REG_OPTION_CREATE_LINK, 0 );
+    ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
+    memcpy( target + target_len/sizeof(WCHAR) - 1, targetW, sizeof(targetW) );
+    status = pNtSetValueKey( link, &symlink_str, 0, REG_LINK,
+        target, target_len + sizeof(targetW) - sizeof(WCHAR) );
+    ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status );
+
+    status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
+    ok( status == STATUS_OBJECT_NAME_NOT_FOUND || status == STATUS_NAME_TOO_LONG,
+        "NtOpenKey failed: 0x%08x\n", status );
+
+    attr.Attributes = OBJ_OPENLINK;
+    status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
+    ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status );
+    pNtClose( key );
+
+    status = pNtDeleteKey( link );
+    ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08x\n", status );
+    pNtClose( link );
+
+    status = pNtDeleteKey( root );
+    ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08x\n", status );
+    pNtClose( root );
+
+    pRtlFreeHeap(GetProcessHeap(), 0, target);
+}
+
+static WCHAR valueW[] = {'v','a','l','u','e'};
+static UNICODE_STRING value_str = { sizeof(valueW), sizeof(valueW), valueW };
+static const DWORD ptr_size = 8 * sizeof(void*);
+
+static DWORD get_key_value( HANDLE root, const char *name, DWORD flags )
+{
+    char tmp[32];
+    NTSTATUS status;
+    OBJECT_ATTRIBUTES attr;
+    UNICODE_STRING str;
+    HANDLE key;
+    KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)tmp;
+    DWORD dw, len = sizeof(tmp);
+
+    attr.Length = sizeof(attr);
+    attr.RootDirectory = root;
+    attr.Attributes = OBJ_CASE_INSENSITIVE;
+    attr.ObjectName = &str;
+    attr.SecurityDescriptor = NULL;
+    attr.SecurityQualityOfService = NULL;
+    pRtlCreateUnicodeStringFromAsciiz( &str, name );
+
+    status = pNtCreateKey( &key, flags | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
+    if (status == STATUS_OBJECT_NAME_NOT_FOUND) return 0;
+    ok( status == STATUS_SUCCESS, "%08x: NtCreateKey failed: 0x%08x\n", flags, status );
+
+    status = pNtQueryValueKey( key, &value_str, KeyValuePartialInformation, info, len, &len );
+    if (status == STATUS_OBJECT_NAME_NOT_FOUND)
+        dw = 0;
+    else
+    {
+        ok( status == STATUS_SUCCESS, "%08x: NtQueryValueKey failed: 0x%08x\n", flags, status );
+        dw = *(DWORD *)info->Data;
+    }
+    pNtClose( key );
+    pRtlFreeUnicodeString( &str );
+    return dw;
+}
+
+static void _check_key_value( int line, HANDLE root, const char *name, DWORD flags, DWORD expect )
+{
+    DWORD dw = get_key_value( root, name, flags );
+    ok_(__FILE__,line)( dw == expect, "%08x: wrong value %u/%u\n", flags, dw, expect );
+}
+#define check_key_value(root,name,flags,expect) _check_key_value( __LINE__, root, name, flags, expect )
+
+static void test_redirection(void)
+{
+    static const WCHAR softwareW[] = {'\\','R','e','g','i','s','t','r','y','\\',
+                                      'M','a','c','h','i','n','e','\\',
+                                      'S','o','f','t','w','a','r','e',0};
+    static const WCHAR wownodeW[] = {'\\','R','e','g','i','s','t','r','y','\\',
+                                     'M','a','c','h','i','n','e','\\',
+                                     'S','o','f','t','w','a','r','e','\\',
+                                     'W','o','w','6','4','3','2','N','o','d','e',0};
+    static const WCHAR wine64W[] = {'\\','R','e','g','i','s','t','r','y','\\',
+                                    'M','a','c','h','i','n','e','\\',
+                                    'S','o','f','t','w','a','r','e','\\',
+                                    'W','i','n','e',0};
+    static const WCHAR wine32W[] = {'\\','R','e','g','i','s','t','r','y','\\',
+                                    'M','a','c','h','i','n','e','\\',
+                                    'S','o','f','t','w','a','r','e','\\',
+                                    'W','o','w','6','4','3','2','N','o','d','e','\\',
+                                    'W','i','n','e',0};
+    static const WCHAR key64W[] = {'\\','R','e','g','i','s','t','r','y','\\',
+                                   'M','a','c','h','i','n','e','\\',
+                                   'S','o','f','t','w','a','r','e','\\',
+                                   'W','i','n','e','\\','W','i','n','e','t','e','s','t',0};
+    static const WCHAR key32W[] = {'\\','R','e','g','i','s','t','r','y','\\',
+                                   'M','a','c','h','i','n','e','\\',
+                                   'S','o','f','t','w','a','r','e','\\',
+                                   'W','o','w','6','4','3','2','N','o','d','e','\\',
+                                   'W','i','n','e','\\', 'W','i','n','e','t','e','s','t',0};
+    static const WCHAR classes64W[] = {'\\','R','e','g','i','s','t','r','y','\\',
+                                       'M','a','c','h','i','n','e','\\',
+                                       'S','o','f','t','w','a','r','e','\\',
+                                       'C','l','a','s','s','e','s','\\',
+                                       'W','i','n','e',0};
+    static const WCHAR classes32W[] = {'\\','R','e','g','i','s','t','r','y','\\',
+                                       'M','a','c','h','i','n','e','\\',
+                                       'S','o','f','t','w','a','r','e','\\',
+                                       'C','l','a','s','s','e','s','\\',
+                                       'W','o','w','6','4','3','2','N','o','d','e','\\',
+                                       'W','i','n','e',0};
+    NTSTATUS status;
+    OBJECT_ATTRIBUTES attr;
+    UNICODE_STRING str;
+    char buffer[1024];
+    KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
+    DWORD dw, len;
+    HANDLE key, root32, root64, key32, key64;
+    BOOL is_vista = FALSE;
+
+    if (ptr_size != 64)
+    {
+        ULONG is_wow64, len;
+        if (pNtQueryInformationProcess( GetCurrentProcess(), ProcessWow64Information,
+                                        &is_wow64, sizeof(is_wow64), &len ) ||
+            !is_wow64)
+        {
+            trace( "Not on Wow64, no redirection\n" );
+            return;
+        }
+    }
+
+    attr.Length = sizeof(attr);
+    attr.RootDirectory = 0;
+    attr.Attributes = OBJ_CASE_INSENSITIVE;
+    attr.ObjectName = &str;
+    attr.SecurityDescriptor = NULL;
+    attr.SecurityQualityOfService = NULL;
+
+    pRtlInitUnicodeString( &str, wine64W );
+    status = pNtCreateKey( &root64, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
+    ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
+
+    pRtlInitUnicodeString( &str, wine32W );
+    status = pNtCreateKey( &root32, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
+    ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
+
+    pRtlInitUnicodeString( &str, key64W );
+    status = pNtCreateKey( &key64, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
+    ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
+
+    pRtlInitUnicodeString( &str, key32W );
+    status = pNtCreateKey( &key32, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
+    ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
+
+    dw = 64;
+    status = pNtSetValueKey( key64, &value_str, 0, REG_DWORD, &dw, sizeof(dw) );
+    ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status );
+
+    dw = 32;
+    status = pNtSetValueKey( key32, &value_str, 0, REG_DWORD, &dw, sizeof(dw) );
+    ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status );
+
+    len = sizeof(buffer);
+    status = pNtQueryValueKey( key32, &value_str, KeyValuePartialInformation, info, len, &len );
+    ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
+    dw = *(DWORD *)info->Data;
+    ok( dw == 32, "wrong value %u\n", dw );
+
+    len = sizeof(buffer);
+    status = pNtQueryValueKey( key64, &value_str, KeyValuePartialInformation, info, len, &len );
+    ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
+    dw = *(DWORD *)info->Data;
+    ok( dw == 64, "wrong value %u\n", dw );
+
+    pRtlInitUnicodeString( &str, softwareW );
+    status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
+    ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
+
+    if (ptr_size == 32)
+    {
+        /* the Vista mechanism allows opening Wow6432Node from a 32-bit key too */
+        /* the new (and simpler) Win7 mechanism doesn't */
+        if (get_key_value( key, "Wow6432Node\\Wine\\Winetest", 0 ) == 32)
+        {
+            trace( "using Vista-style Wow6432Node handling\n" );
+            is_vista = TRUE;
+        }
+        check_key_value( key, "Wine\\Winetest", 0, 32 );
+        check_key_value( key, "Wine\\Winetest", KEY_WOW64_64KEY, is_vista ? 64 : 32 );
+        check_key_value( key, "Wine\\Winetest", KEY_WOW64_32KEY, 32 );
+        check_key_value( key, "Wow6432Node\\Wine\\Winetest", 0, is_vista ? 32 : 0 );
+        check_key_value( key, "Wow6432Node\\Wine\\Winetest", KEY_WOW64_64KEY, is_vista ? 64 : 0 );
+        check_key_value( key, "Wow6432Node\\Wine\\Winetest", KEY_WOW64_32KEY, is_vista ? 32 : 0 );
+    }
+    else
+    {
+        check_key_value( key, "Wine\\Winetest", 0, 64 );
+        check_key_value( key, "Wow6432Node\\Wine\\Winetest", 0, 32 );
+    }
+    pNtClose( key );
+
+    if (ptr_size == 32)
+    {
+        status = pNtCreateKey( &key, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
+        ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
+        dw = get_key_value( key, "Wine\\Winetest", 0 );
+        ok( dw == 64 || broken(dw == 32) /* xp64 */, "wrong value %u\n", dw );
+        check_key_value( key, "Wine\\Winetest", KEY_WOW64_64KEY, 64 );
+        check_key_value( key, "Wine\\Winetest", KEY_WOW64_32KEY, 32 );
+        check_key_value( key, "Wow6432Node\\Wine\\Winetest", 0, 32 );
+        dw = get_key_value( key, "Wow6432Node\\Wine\\Winetest", KEY_WOW64_64KEY );
+        ok( dw == 32 || broken(dw == 64) /* xp64 */, "wrong value %u\n", dw );
+        check_key_value( key, "Wow6432Node\\Wine\\Winetest", KEY_WOW64_32KEY, 32 );
+        pNtClose( key );
+
+        status = pNtCreateKey( &key, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
+        ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
+        check_key_value( key, "Wine\\Winetest", 0, 32 );
+        check_key_value( key, "Wine\\Winetest", KEY_WOW64_64KEY, is_vista ? 64 : 32 );
+        check_key_value( key, "Wine\\Winetest", KEY_WOW64_32KEY, 32 );
+        check_key_value( key, "Wow6432Node\\Wine\\Winetest", 0, is_vista ? 32 : 0 );
+        check_key_value( key, "Wow6432Node\\Wine\\Winetest", KEY_WOW64_64KEY, is_vista ? 64 : 0 );
+        check_key_value( key, "Wow6432Node\\Wine\\Winetest", KEY_WOW64_32KEY, is_vista ? 32 : 0 );
+        pNtClose( key );
+    }
+
+    check_key_value( 0, "\\Registry\\Machine\\Software\\Wine\\Winetest", 0, ptr_size );
+    check_key_value( 0, "\\Registry\\Machine\\Software\\Wow6432Node\\Wine\\Winetest", 0, 32 );
+    if (ptr_size == 64)
+    {
+        /* KEY_WOW64 flags have no effect on 64-bit */
+        check_key_value( 0, "\\Registry\\Machine\\Software\\Wine\\Winetest", KEY_WOW64_64KEY, 64 );
+        check_key_value( 0, "\\Registry\\Machine\\Software\\Wine\\Winetest", KEY_WOW64_32KEY, 64 );
+        check_key_value( 0, "\\Registry\\Machine\\Software\\Wow6432Node\\Wine\\Winetest", KEY_WOW64_64KEY, 32 );
+        check_key_value( 0, "\\Registry\\Machine\\Software\\Wow6432Node\\Wine\\Winetest", KEY_WOW64_32KEY, 32 );
+    }
+    else
+    {
+        check_key_value( 0, "\\Registry\\Machine\\Software\\Wine\\Winetest", KEY_WOW64_64KEY, 64 );
+        check_key_value( 0, "\\Registry\\Machine\\Software\\Wine\\Winetest", KEY_WOW64_32KEY, 32 );
+        check_key_value( 0, "\\Registry\\Machine\\Software\\Wow6432Node\\Wine\\Winetest", KEY_WOW64_64KEY, is_vista ? 64 : 32 );
+        check_key_value( 0, "\\Registry\\Machine\\Software\\Wow6432Node\\Wine\\Winetest", KEY_WOW64_32KEY, 32 );
+    }
+
+    pRtlInitUnicodeString( &str, wownodeW );
+    status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
+    ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
+    check_key_value( key, "Wine\\Winetest", 0, 32 );
+    check_key_value( key, "Wine\\Winetest", KEY_WOW64_64KEY, (ptr_size == 64) ? 32 : (is_vista ? 64 : 32) );
+    check_key_value( key, "Wine\\Winetest", KEY_WOW64_32KEY, 32 );
+    pNtClose( key );
+
+    if (ptr_size == 32)
+    {
+        status = pNtCreateKey( &key, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
+        ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
+        dw = get_key_value( key, "Wine\\Winetest", 0 );
+        ok( dw == (is_vista ? 64 : 32) || broken(dw == 32) /* xp64 */, "wrong value %u\n", dw );
+        check_key_value( key, "Wine\\Winetest", KEY_WOW64_64KEY, is_vista ? 64 : 32 );
+        check_key_value( key, "Wine\\Winetest", KEY_WOW64_32KEY, 32 );
+        pNtClose( key );
+
+        status = pNtCreateKey( &key, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
+        ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
+        check_key_value( key, "Wine\\Winetest", 0, 32 );
+        check_key_value( key, "Wine\\Winetest", KEY_WOW64_64KEY, is_vista ? 64 : 32 );
+        check_key_value( key, "Wine\\Winetest", KEY_WOW64_32KEY, 32 );
+        pNtClose( key );
+    }
+
+    pRtlInitUnicodeString( &str, wine32W );
+    status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
+    ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
+    check_key_value( key, "Winetest", 0, 32 );
+    check_key_value( key, "Winetest", KEY_WOW64_64KEY, (ptr_size == 32 && is_vista) ? 64 : 32 );
+    check_key_value( key, "Winetest", KEY_WOW64_32KEY, 32 );
+    pNtClose( key );
+
+    if (ptr_size == 32)
+    {
+        status = pNtCreateKey( &key, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
+        ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
+        dw = get_key_value( key, "Winetest", 0 );
+        ok( dw == 32 || (is_vista && dw == 64), "wrong value %u\n", dw );
+        check_key_value( key, "Winetest", KEY_WOW64_64KEY, is_vista ? 64 : 32 );
+        check_key_value( key, "Winetest", KEY_WOW64_32KEY, 32 );
+        pNtClose( key );
+
+        status = pNtCreateKey( &key, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
+        ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
+        check_key_value( key, "Winetest", 0, 32 );
+        check_key_value( key, "Winetest", KEY_WOW64_64KEY, is_vista ? 64 : 32 );
+        check_key_value( key, "Winetest", KEY_WOW64_32KEY, 32 );
+        pNtClose( key );
+    }
+
+    pRtlInitUnicodeString( &str, wine64W );
+    status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
+    ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
+    check_key_value( key, "Winetest", 0, ptr_size );
+    check_key_value( key, "Winetest", KEY_WOW64_64KEY, is_vista ? 64 : ptr_size );
+    check_key_value( key, "Winetest", KEY_WOW64_32KEY, ptr_size );
+    pNtClose( key );
+
+    if (ptr_size == 32)
+    {
+        status = pNtCreateKey( &key, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
+        ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
+        dw = get_key_value( key, "Winetest", 0 );
+        ok( dw == 64 || broken(dw == 32) /* xp64 */, "wrong value %u\n", dw );
+        check_key_value( key, "Winetest", KEY_WOW64_64KEY, 64 );
+        dw = get_key_value( key, "Winetest", KEY_WOW64_32KEY );
+        todo_wine ok( dw == 32, "wrong value %u\n", dw );
+        pNtClose( key );
+
+        status = pNtCreateKey( &key, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
+        ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
+        check_key_value( key, "Winetest", 0, 32 );
+        check_key_value( key, "Winetest", KEY_WOW64_64KEY, is_vista ? 64 : 32 );
+        check_key_value( key, "Winetest", KEY_WOW64_32KEY, 32 );
+        pNtClose( key );
+    }
+
+    status = pNtDeleteKey( key32 );
+    ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08x\n", status );
+    pNtClose( key32 );
+
+    status = pNtDeleteKey( key64 );
+    ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08x\n", status );
+    pNtClose( key64 );
+
+    pNtDeleteKey( root32 );
+    pNtClose( root32 );
+    pNtDeleteKey( root64 );
+    pNtClose( root64 );
+
+    /* Software\Classes is shared/reflected so behavior is different */
+
+    pRtlInitUnicodeString( &str, classes64W );
+    status = pNtCreateKey( &key64, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
+    ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
+
+    pRtlInitUnicodeString( &str, classes32W );
+    status = pNtCreateKey( &key32, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
+    ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
+
+    dw = 64;
+    status = pNtSetValueKey( key64, &value_str, 0, REG_DWORD, &dw, sizeof(dw) );
+    ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status );
+    pNtClose( key64 );
+
+    dw = 32;
+    status = pNtSetValueKey( key32, &value_str, 0, REG_DWORD, &dw, sizeof(dw) );
+    ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status );
+    pNtClose( key32 );
+
+    pRtlInitUnicodeString( &str, classes64W );
+    status = pNtCreateKey( &key64, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
+    ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
+    len = sizeof(buffer);
+    status = pNtQueryValueKey( key64, &value_str, KeyValuePartialInformation, info, len, &len );
+    ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
+    dw = *(DWORD *)info->Data;
+    ok( dw == ptr_size, "wrong value %u\n", dw );
+
+    pRtlInitUnicodeString( &str, classes32W );
+    status = pNtCreateKey( &key32, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
+    ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
+    len = sizeof(buffer);
+    status = pNtQueryValueKey( key32, &value_str, KeyValuePartialInformation, info, len, &len );
+    ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
+    dw = *(DWORD *)info->Data;
+    ok( dw == 32, "wrong value %u\n", dw );
+
+    pNtDeleteKey( key32 );
+    pNtClose( key32 );
+    pNtDeleteKey( key64 );
+    pNtClose( key64 );
+}
+
 START_TEST(reg)
 {
     static const WCHAR winetest[] = {'\\','W','i','n','e','T','e','s','t',0};
@@ -609,8 +1249,8 @@ START_TEST(reg)
 
     pRtlAppendUnicodeToString(&winetestpath, winetest);
 
-    test_NtOpenKey();
     test_NtCreateKey();
+    test_NtOpenKey();
     test_NtSetValueKey();
     test_RtlCheckRegistryKey();
     test_RtlOpenCurrentUser();
@@ -619,6 +1259,8 @@ START_TEST(reg)
     test_NtFlushKey();
     test_NtQueryValueKey();
     test_NtDeleteKey();
+    test_symlinks();
+    test_redirection();
 
     pRtlFreeUnicodeString(&winetestpath);
 
index 7beef6d..6e76a3c 100755 (executable)
@@ -67,6 +67,11 @@ static RTL_HANDLE * (WINAPI * pRtlAllocateHandle)(RTL_HANDLE_TABLE *, ULONG *);
 static BOOLEAN   (WINAPI * pRtlFreeHandle)(RTL_HANDLE_TABLE *, RTL_HANDLE *);
 static NTSTATUS  (WINAPI *pRtlAllocateAndInitializeSid)(PSID_IDENTIFIER_AUTHORITY,BYTE,DWORD,DWORD,DWORD,DWORD,DWORD,DWORD,DWORD,DWORD,PSID*);
 static NTSTATUS  (WINAPI *pRtlFreeSid)(PSID);
+static struct _TEB * (WINAPI *pNtCurrentTeb)(void);
+static DWORD     (WINAPI *pRtlGetThreadErrorMode)(void);
+static NTSTATUS  (WINAPI *pRtlSetThreadErrorMode)(DWORD, LPDWORD);
+static HMODULE hkernel32 = 0;
+static BOOL      (WINAPI *pIsWow64Process)(HANDLE, PBOOL);
 #define LEN 16
 static const char* src_src = "This is a test!"; /* 16 bytes long, incl NUL */
 static ULONG src_aligned_block[4];
@@ -99,6 +104,14 @@ static void InitFunctionPtrs(void)
        pRtlFreeHandle = (void *)GetProcAddress(hntdll, "RtlFreeHandle");
         pRtlAllocateAndInitializeSid = (void *)GetProcAddress(hntdll, "RtlAllocateAndInitializeSid");
         pRtlFreeSid = (void *)GetProcAddress(hntdll, "RtlFreeSid");
+        pNtCurrentTeb = (void *)GetProcAddress(hntdll, "NtCurrentTeb");
+        pRtlGetThreadErrorMode = (void *)GetProcAddress(hntdll, "RtlGetThreadErrorMode");
+        pRtlSetThreadErrorMode = (void *)GetProcAddress(hntdll, "RtlSetThreadErrorMode");
+    }
+    hkernel32 = LoadLibraryA("kernel32.dll");
+    ok(hkernel32 != 0, "LoadLibrary failed\n");
+    if (hkernel32) {
+        pIsWow64Process = (void *)GetProcAddress(hkernel32, "IsWow64Process");
     }
     strcpy((char*)src_aligned_block, src_src);
     ok(strlen(src) == 15, "Source must be 16 bytes long!\n");
@@ -112,7 +125,10 @@ static void test_RtlCompareMemory(void)
   SIZE_T size;
 
   if (!pRtlCompareMemory)
+  {
+    win_skip("RtlCompareMemory is not available\n");
     return;
+  }
 
   strcpy(dest, src);
 
@@ -127,6 +143,12 @@ static void test_RtlCompareMemoryUlong(void)
     ULONG a[10];
     ULONG result;
 
+    if (!pRtlCompareMemoryUlong)
+    {
+        win_skip("RtlCompareMemoryUlong is not available\n");
+        return;
+    }
+
     a[0]= 0x0123;
     a[1]= 0x4567;
     a[2]= 0x89ab;
@@ -173,7 +195,10 @@ static void test_RtlCompareMemoryUlong(void)
 static void test_RtlMoveMemory(void)
 {
   if (!pRtlMoveMemory)
+  {
+    win_skip("RtlMoveMemory is not available\n");
     return;
+  }
 
   /* Length should be in bytes and not rounded. Use strcmp to ensure we
    * didn't write past the end (it checks for the final NUL left by memset)
@@ -201,7 +226,10 @@ static void test_RtlMoveMemory(void)
 static void test_RtlFillMemory(void)
 {
   if (!pRtlFillMemory)
+  {
+    win_skip("RtlFillMemory is not available\n");
     return;
+  }
 
   /* Length should be in bytes and not rounded. Use strcmp to ensure we
    * didn't write past the end (the remainder of the string should match)
@@ -224,7 +252,10 @@ static void test_RtlFillMemoryUlong(void)
 {
   ULONG val = ('x' << 24) | ('x' << 16) | ('x' << 8) | 'x';
   if (!pRtlFillMemoryUlong)
+  {
+    win_skip("RtlFillMemoryUlong is not available\n");
     return;
+  }
 
   /* Length should be in bytes and not rounded. Use strcmp to ensure we
    * didn't write past the end (the remainder of the string should match)
@@ -247,7 +278,10 @@ static void test_RtlFillMemoryUlong(void)
 static void test_RtlZeroMemory(void)
 {
   if (!pRtlZeroMemory)
+  {
+    win_skip("RtlZeroMemory is not available\n");
     return;
+  }
 
   /* Length should be in bytes and not rounded. */
   ZERO(0); MCMP("This is a test!");
@@ -266,6 +300,12 @@ static void test_RtlUlonglongByteSwap(void)
 {
     ULONGLONG result;
 
+    if ( !pRtlUlonglongByteSwap )
+    {
+        win_skip("RtlUlonglongByteSwap is not available\n");
+        return;
+    }
+
     if ( pRtlUlonglongByteSwap( 0 ) != 0 )
     {
         win_skip("Broken RtlUlonglongByteSwap in win2k\n");
@@ -287,6 +327,12 @@ static void test_RtlUniform(void)
     ULONG expected;
     ULONG result;
 
+    if (!pRtlUniform)
+    {
+        win_skip("RtlUniform is not available\n");
+        return;
+    }
+
 /*
  * According to the documentation RtlUniform is using D.H. Lehmer's 1948
  * algorithm. This algorithm is:
@@ -612,6 +658,12 @@ static void test_RtlRandom(void)
     ULONG result;
     ULONG result_expected;
 
+    if (!pRtlRandom)
+    {
+        win_skip("RtlRandom is not available\n");
+        return;
+    }
+
 /*
  * Unlike RtlUniform, RtlRandom is not documented. We guess that for
  * RtlRandom D.H. Lehmer's 1948 algorithm is used like stated in
@@ -820,6 +872,12 @@ static void test_RtlAreAllAccessesGranted(void)
     unsigned int test_num;
     BOOLEAN result;
 
+    if (!pRtlAreAllAccessesGranted)
+    {
+        win_skip("RtlAreAllAccessesGranted is not available\n");
+        return;
+    }
+
     for (test_num = 0; test_num < NB_ALL_ACCESSES; test_num++) {
        result = pRtlAreAllAccessesGranted(all_accesses[test_num].GrantedAccess,
                                           all_accesses[test_num].DesiredAccess);
@@ -857,6 +915,12 @@ static void test_RtlAreAnyAccessesGranted(void)
     unsigned int test_num;
     BOOLEAN result;
 
+    if (!pRtlAreAnyAccessesGranted)
+    {
+        win_skip("RtlAreAnyAccessesGranted is not available\n");
+        return;
+    }
+
     for (test_num = 0; test_num < NB_ANY_ACCESSES; test_num++) {
        result = pRtlAreAnyAccessesGranted(any_accesses[test_num].GrantedAccess,
                                           any_accesses[test_num].DesiredAccess);
@@ -873,7 +937,10 @@ static void test_RtlComputeCrc32(void)
   DWORD crc = 0;
 
   if (!pRtlComputeCrc32)
+  {
+    win_skip("RtlComputeCrc32 is not available\n");
     return;
+  }
 
   crc = pRtlComputeCrc32(crc, (const BYTE *)src, LEN);
   ok(crc == 0x40861dc2,"Expected 0x40861dc2, got %8x\n", crc);
@@ -900,6 +967,12 @@ static void test_HandleTables(void)
     MY_HANDLE * MyHandle;
     RTL_HANDLE_TABLE HandleTable;
 
+    if (!pRtlInitializeHandleTable)
+    {
+        win_skip("RtlInitializeHandleTable is not available\n");
+        return;
+    }
+
     pRtlInitializeHandleTable(0x3FFF, sizeof(MY_HANDLE), &HandleTable);
     MyHandle = (MY_HANDLE *)pRtlAllocateHandle(&HandleTable, &Index);
     ok(MyHandle != NULL, "RtlAllocateHandle failed\n");
@@ -919,14 +992,23 @@ static void test_RtlAllocateAndInitializeSid(void)
     SID_IDENTIFIER_AUTHORITY sia = {{ 1, 2, 3, 4, 5, 6 }};
     PSID psid;
 
+    if (!pRtlAllocateAndInitializeSid)
+    {
+        win_skip("RtlAllocateAndInitializeSid is not available\n");
+        return;
+    }
+
     ret = pRtlAllocateAndInitializeSid(&sia, 0, 1, 2, 3, 4, 5, 6, 7, 8, &psid);
     ok(!ret, "RtlAllocateAndInitializeSid error %08x\n", ret);
     ret = pRtlFreeSid(psid);
     ok(!ret, "RtlFreeSid error %08x\n", ret);
 
-    /* these tests crash on XP
-    ret = pRtlAllocateAndInitializeSid(NULL, 0, 1, 2, 3, 4, 5, 6, 7, 8, &psid);
-    ret = pRtlAllocateAndInitializeSid(&sia, 0, 1, 2, 3, 4, 5, 6, 7, 8, NULL);*/
+    /* these tests crash on XP */
+    if (0)
+    {
+        ret = pRtlAllocateAndInitializeSid(NULL, 0, 1, 2, 3, 4, 5, 6, 7, 8, &psid);
+        ret = pRtlAllocateAndInitializeSid(&sia, 0, 1, 2, 3, 4, 5, 6, 7, 8, NULL);
+    }
 
     ret = pRtlAllocateAndInitializeSid(&sia, 9, 1, 2, 3, 4, 5, 6, 7, 8, &psid);
     ok(ret == STATUS_INVALID_SID, "wrong error %08x\n", ret);
@@ -935,44 +1017,101 @@ static void test_RtlAllocateAndInitializeSid(void)
 static void test_RtlDeleteTimer(void)
 {
     NTSTATUS ret;
+
+    if (!pRtlDeleteTimer)
+    {
+        win_skip("RtlDeleteTimer is not available\n");
+        return;
+    }
+
     ret = pRtlDeleteTimer(NULL, NULL, NULL);
     ok(ret == STATUS_INVALID_PARAMETER_1 ||
        ret == STATUS_INVALID_PARAMETER, /* W2K */
        "expected STATUS_INVALID_PARAMETER_1 or STATUS_INVALID_PARAMETER, got %x\n", ret);
 }
 
+static void test_RtlThreadErrorMode(void)
+{
+    DWORD oldmode;
+    BOOL is_wow64;
+    DWORD mode;
+    NTSTATUS status;
+
+    if (!pRtlGetThreadErrorMode || !pRtlSetThreadErrorMode)
+    {
+        win_skip("RtlGetThreadErrorMode and/or RtlSetThreadErrorMode not available\n");
+        return;
+    }
+
+    if (!pIsWow64Process || !pIsWow64Process(GetCurrentProcess(), &is_wow64))
+        is_wow64 = FALSE;
+
+    oldmode = pRtlGetThreadErrorMode();
+
+    status = pRtlSetThreadErrorMode(0x70, &mode);
+    ok(status == STATUS_SUCCESS ||
+       status == STATUS_WAIT_1, /* Vista */
+       "RtlSetThreadErrorMode failed with error 0x%08x\n", status);
+    ok(mode == oldmode,
+       "RtlSetThreadErrorMode returned mode 0x%x, expected 0x%x\n",
+       mode, oldmode);
+    ok(pRtlGetThreadErrorMode() == 0x70,
+       "RtlGetThreadErrorMode returned 0x%x, expected 0x%x\n", mode, 0x70);
+    if (!is_wow64 && pNtCurrentTeb)
+        ok(pNtCurrentTeb()->HardErrorDisabled == 0x70,
+           "The TEB contains 0x%x, expected 0x%x\n",
+           pNtCurrentTeb()->HardErrorDisabled, 0x70);
+
+    status = pRtlSetThreadErrorMode(0, &mode);
+    ok(status == STATUS_SUCCESS ||
+       status == STATUS_WAIT_1, /* Vista */
+       "RtlSetThreadErrorMode failed with error 0x%08x\n", status);
+    ok(mode == 0x70,
+       "RtlSetThreadErrorMode returned mode 0x%x, expected 0x%x\n",
+       mode, 0x70);
+    ok(pRtlGetThreadErrorMode() == 0,
+       "RtlGetThreadErrorMode returned 0x%x, expected 0x%x\n", mode, 0);
+    if (!is_wow64 && pNtCurrentTeb)
+        ok(pNtCurrentTeb()->HardErrorDisabled == 0,
+           "The TEB contains 0x%x, expected 0x%x\n",
+           pNtCurrentTeb()->HardErrorDisabled, 0);
+
+    for (mode = 1; mode; mode <<= 1)
+    {
+        status = pRtlSetThreadErrorMode(mode, NULL);
+        if (mode & 0x70)
+            ok(status == STATUS_SUCCESS ||
+               status == STATUS_WAIT_1, /* Vista */
+               "RtlSetThreadErrorMode(%x,NULL) failed with error 0x%08x\n",
+               mode, status);
+        else
+            ok(status == STATUS_INVALID_PARAMETER_1,
+               "RtlSetThreadErrorMode(%x,NULL) returns 0x%08x, "
+               "expected STATUS_INVALID_PARAMETER_1\n",
+               mode, status);
+    }
+
+    pRtlSetThreadErrorMode(oldmode, NULL);
+}
+
 START_TEST(rtl)
 {
     InitFunctionPtrs();
 
-    if (pRtlCompareMemory)
-        test_RtlCompareMemory();
-    if (pRtlCompareMemoryUlong)
-        test_RtlCompareMemoryUlong();
-    if (pRtlMoveMemory)
-        test_RtlMoveMemory();
-    if (pRtlFillMemory)
-        test_RtlFillMemory();
-    if (pRtlFillMemoryUlong)
-        test_RtlFillMemoryUlong();
-    if (pRtlZeroMemory)
-        test_RtlZeroMemory();
-    if (pRtlUlonglongByteSwap)
-        test_RtlUlonglongByteSwap();
-    if (pRtlUniform)
-        test_RtlUniform();
-    if (pRtlRandom)
-        test_RtlRandom();
-    if (pRtlAreAllAccessesGranted)
-        test_RtlAreAllAccessesGranted();
-    if (pRtlAreAnyAccessesGranted)
-        test_RtlAreAnyAccessesGranted();
-    if (pRtlComputeCrc32)
-        test_RtlComputeCrc32();
-    if (pRtlInitializeHandleTable)
-        test_HandleTables();
-    if (pRtlAllocateAndInitializeSid)
-        test_RtlAllocateAndInitializeSid();
-    if (pRtlDeleteTimer)
-        test_RtlDeleteTimer();
+    test_RtlCompareMemory();
+    test_RtlCompareMemoryUlong();
+    test_RtlMoveMemory();
+    test_RtlFillMemory();
+    test_RtlFillMemoryUlong();
+    test_RtlZeroMemory();
+    test_RtlUlonglongByteSwap();
+    test_RtlUniform();
+    test_RtlRandom();
+    test_RtlAreAllAccessesGranted();
+    test_RtlAreAnyAccessesGranted();
+    test_RtlComputeCrc32();
+    test_HandleTables();
+    test_RtlAllocateAndInitializeSid();
+    test_RtlDeleteTimer();
+    test_RtlThreadErrorMode();
 }
index 16c1192..cd3ae37 100755 (executable)
@@ -1113,11 +1113,28 @@ static void test_wtoi64(void)
     }
 }
 
-static void test_wcsfuncs(void)
-{       
-    static const WCHAR testing[] = {'T','e','s','t','i','n','g',0};
-    ok (p_wcschr(testing,0)!=NULL, "wcschr Not finding terminating character\n");
-    ok (p_wcsrchr(testing,0)!=NULL, "wcsrchr Not finding terminating character\n");
+static void test_wcschr(void)
+{
+    static const WCHAR teststringW[] = {'a','b','r','a','c','a','d','a','b','r','a',0};
+
+    ok(p_wcschr(teststringW, 'a') == teststringW + 0,
+       "wcschr should have returned a pointer to the first 'a' character\n");
+    ok(p_wcschr(teststringW, 0) == teststringW + 11,
+       "wcschr should have returned a pointer to the null terminator\n");
+    ok(p_wcschr(teststringW, 'x') == NULL,
+       "wcschr should have returned NULL\n");
+}
+
+static void test_wcsrchr(void)
+{
+    static const WCHAR teststringW[] = {'a','b','r','a','c','a','d','a','b','r','a',0};
+
+    ok(p_wcsrchr(teststringW, 'a') == teststringW + 10,
+       "wcsrchr should have returned a pointer to the last 'a' character\n");
+    ok(p_wcsrchr(teststringW, 0) == teststringW + 11,
+       "wcsrchr should have returned a pointer to the null terminator\n");
+    ok(p_wcsrchr(teststringW, 'x') == NULL,
+       "wcsrchr should have returned NULL\n");
 }
 
 START_TEST(string)
@@ -1140,8 +1157,10 @@ START_TEST(string)
         test_wtol();
     if (p_wtoi64)
         test_wtoi64();
-    if (p_wcschr && p_wcsrchr)
-        test_wcsfuncs();
+    if (p_wcschr)
+        test_wcschr();
+    if (p_wcsrchr)
+        test_wcsrchr();
     if (patoi)
         test_atoi();
     if (patol)
index 0162d0f..b5b1674 100644 (file)
@@ -8,6 +8,7 @@
 
 extern void func_atom(void);
 extern void func_change(void);
+extern void func_directory(void);
 extern void func_env(void);
 extern void func_error(void);
 extern void func_exception(void);
@@ -29,6 +30,7 @@ const struct test winetest_testlist[] =
 {
     { "atom", func_atom },
     { "change", func_change },
+    { "directory", func_directory },
     { "env", func_env },
     { "error", func_error },
     { "exception", func_exception },
index 2e9adbc..16530c4 100755 (executable)
@@ -20,8 +20,6 @@
 
 #include "ntdll_test.h"
 
-#ifdef __WINE_WINTERNL_H
-
 #define TICKSPERSEC        10000000
 #define TICKSPERMSEC       10000
 #define SECSPERDAY         86400
@@ -95,15 +93,14 @@ static void test_pRtlTimeToTimeFields(void)
         litime.QuadPart +=  (LONGLONG) tftest.Day * TICKSPERSEC * SECSPERDAY;
     }
 }
-#endif
 
 START_TEST(time)
 {
-#ifdef __WINE_WINTERNL_H
     HMODULE mod = GetModuleHandleA("ntdll.dll");
     pRtlTimeToTimeFields = (void *)GetProcAddress(mod,"RtlTimeToTimeFields");
     pRtlTimeFieldsToTime = (void *)GetProcAddress(mod,"RtlTimeFieldsToTime");
     if (pRtlTimeToTimeFields && pRtlTimeFieldsToTime)
         test_pRtlTimeToTimeFields();
-#endif
+    else
+        win_skip("Required time conversion functions are not available\n");
 }