[NTDLL_WINETEST] Sync with Wine Staging 2.16. CORE-13762
authorAmine Khaldi <amine.khaldi@reactos.org>
Sat, 21 Oct 2017 13:36:29 +0000 (14:36 +0100)
committerAmine Khaldi <amine.khaldi@reactos.org>
Mon, 23 Oct 2017 12:48:05 +0000 (13:48 +0100)
18 files changed:
modules/rostests/winetests/ntdll/CMakeLists.txt
modules/rostests/winetests/ntdll/directory.c
modules/rostests/winetests/ntdll/exception.c
modules/rostests/winetests/ntdll/file.c
modules/rostests/winetests/ntdll/info.c
modules/rostests/winetests/ntdll/large_int.c
modules/rostests/winetests/ntdll/ntdll_test.h
modules/rostests/winetests/ntdll/om.c
modules/rostests/winetests/ntdll/path.c
modules/rostests/winetests/ntdll/pipe.c
modules/rostests/winetests/ntdll/process.c [new file with mode: 0644]
modules/rostests/winetests/ntdll/reg.c
modules/rostests/winetests/ntdll/rtl.c
modules/rostests/winetests/ntdll/rtlbitmap.c
modules/rostests/winetests/ntdll/string.c
modules/rostests/winetests/ntdll/testlist.c
modules/rostests/winetests/ntdll/threadpool.c [new file with mode: 0644]
modules/rostests/winetests/ntdll/time.c

index 4fe5a26..f1055f8 100644 (file)
@@ -1,7 +1,7 @@
 
 include_directories(BEFORE ${REACTOS_SOURCE_DIR}/sdk/include/reactos/wine)
 remove_definitions(-DWINVER=0x502 -D_WIN32_IE=0x600 -D_WIN32_WINNT=0x502)
-add_definitions(-D__WINESRC__)
+add_definitions(-D__WINESRC__ -DWINETEST_USE_DBGSTR_LONGLONG)
 
 list(APPEND SOURCE
     atom.c
@@ -17,6 +17,7 @@ list(APPEND SOURCE
     path.c
     pipe.c
     port.c
+    process.c
     reg.c
     rtl.c
     rtlbitmap.c
index 89b786d..21f22ad 100644 (file)
 #define WIN32_NO_STATUS
 
 #include "wine/test.h"
+#include "winnls.h"
 #include "wine/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 NTSTATUS (WINAPI *pNtQueryInformationFile)(HANDLE,PIO_STATUS_BLOCK,PVOID,LONG,FILE_INFORMATION_CLASS);
+static NTSTATUS (WINAPI *pNtSetInformationFile)(HANDLE,PIO_STATUS_BLOCK,PVOID,ULONG,FILE_INFORMATION_CLASS);
 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 LONG     (WINAPI *pRtlCompareUnicodeString)( const UNICODE_STRING*, const UNICODE_STRING*,BOOLEAN );
 static NTSTATUS (WINAPI *pRtlMultiByteToUnicodeN)( LPWSTR dst, DWORD dstlen, LPDWORD reslen,
                                                    LPCSTR src, DWORD srclen );
 static NTSTATUS (WINAPI *pRtlWow64EnableFsRedirection)( BOOLEAN enable );
@@ -53,47 +57,56 @@ static NTSTATUS (WINAPI *pRtlWow64EnableFsRedirectionEx)( ULONG disable, ULONG *
 static struct testfile_s {
     BOOL attr_done;           /* set if attributes were tested for this file already */
     const DWORD attr;         /* desired attribute */
-    const char *name;         /* filename to use */
+    WCHAR name[20];           /* 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" },
-    { 0, FILE_ATTRIBUTE_HIDDEN,    "h.tmp", NULL, "hidden" },
-    { 0, FILE_ATTRIBUTE_SYSTEM,    "s.tmp", NULL, "system" },
-    { 0, FILE_ATTRIBUTE_DIRECTORY, "d.tmp", NULL, "directory" },
-    { 0, FILE_ATTRIBUTE_DIRECTORY, ".",     NULL, ". directory" },
-    { 0, FILE_ATTRIBUTE_DIRECTORY, "..",    NULL, ".. directory" },
-    { 0, 0, NULL }
+    { 0, FILE_ATTRIBUTE_NORMAL,    {'l','o','n','g','f','i','l','e','n','a','m','e','.','t','m','p'}, "normal" },
+    { 0, FILE_ATTRIBUTE_NORMAL,    {'n','.','t','m','p',}, "normal" },
+    { 0, FILE_ATTRIBUTE_HIDDEN,    {'h','.','t','m','p',}, "hidden" },
+    { 0, FILE_ATTRIBUTE_SYSTEM,    {'s','.','t','m','p',}, "system" },
+    { 0, FILE_ATTRIBUTE_DIRECTORY, {'d','.','t','m','p',}, "directory" },
+    { 0, FILE_ATTRIBUTE_NORMAL,    {0xe9,'a','.','t','m','p'}, "normal" },
+    { 0, FILE_ATTRIBUTE_NORMAL,    {0xc9,'b','.','t','m','p'}, "normal" },
+    { 0, FILE_ATTRIBUTE_NORMAL,    {'e','a','.','t','m','p'},  "normal" },
+    { 0, FILE_ATTRIBUTE_DIRECTORY, {'.'},                  ". directory" },
+    { 0, FILE_ATTRIBUTE_DIRECTORY, {'.','.'},              ".. directory" }
 };
-static const int max_test_dir_size = 20;  /* size of above plus some for .. etc */
+static const int test_dir_count = sizeof(testfiles) / sizeof(testfiles[0]);
+static const int max_test_dir_size = sizeof(testfiles) / sizeof(testfiles[0]) + 5;  /* size of above plus some for .. etc */
+
+static const WCHAR dummyW[] = {'d','u','m','m','y',0};
+static const WCHAR dotW[] = {'.',0};
+static const WCHAR dotdotW[] = {'.','.',0};
+static const WCHAR backslashW[] = {'\\',0};
 
 /* Create a test directory full of attribute test files, clear counts */
-static void set_up_attribute_test(const char *testdirA)
+static void set_up_attribute_test(const WCHAR *testdir)
 {
     int i;
     BOOL ret;
 
-    ret = CreateDirectoryA(testdirA, NULL);
-    ok(ret, "couldn't create dir '%s', error %d\n", testdirA, GetLastError());
+    ret = CreateDirectoryW(testdir, NULL);
+    ok(ret, "couldn't create dir %s, error %d\n", wine_dbgstr_w(testdir), 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);
+    for (i=0; i < test_dir_count; i++) {
+        WCHAR buf[MAX_PATH];
 
-        if (strcmp(testfiles[i].name, ".") == 0 || strcmp(testfiles[i].name, "..") == 0)
+        if (lstrcmpW(testfiles[i].name, dotW) == 0 || lstrcmpW(testfiles[i].name, dotdotW) == 0)
             continue;
-        sprintf(buf, "%s\\%s", testdirA, testfiles[i].name);
+        lstrcpyW( buf, testdir );
+        lstrcatW( buf, backslashW );
+        lstrcatW( buf, testfiles[i].name );
         if (testfiles[i].attr & FILE_ATTRIBUTE_DIRECTORY) {
-            ret = CreateDirectoryA(buf, NULL);
-            ok(ret, "couldn't create dir '%s', error %d\n", buf, GetLastError());
+            ret = CreateDirectoryW(buf, NULL);
+            ok(ret, "couldn't create dir %s, error %d\n", wine_dbgstr_w(buf), GetLastError());
         } else {
-            HANDLE h = CreateFileA(buf,
+            HANDLE h = CreateFileW(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 );
+            ok( h != INVALID_HANDLE_VALUE, "failed to create temp file %s\n", wine_dbgstr_w(buf) );
             CloseHandle(h);
         }
     }
@@ -103,32 +116,34 @@ static void reset_found_files(void)
 {
     int i;
 
-    for (i = 0; testfiles[i].name; i++)
+    for (i = 0; i < test_dir_count; i++)
         testfiles[i].nfound = 0;
 }
 
 /* Remove the given test directory and the attribute test files, if any */
-static void tear_down_attribute_test(const char *testdirA)
+static void tear_down_attribute_test(const WCHAR *testdir)
 {
     int i;
 
-    for (i=0; testfiles[i].name; i++) {
+    for (i = 0; i < test_dir_count; i++) {
         int ret;
-        char buf[MAX_PATH];
-        if (strcmp(testfiles[i].name, ".") == 0 || strcmp(testfiles[i].name, "..") == 0)
+        WCHAR buf[MAX_PATH];
+        if (lstrcmpW(testfiles[i].name, dotW) == 0 || lstrcmpW(testfiles[i].name, dotdotW) == 0)
             continue;
-        sprintf(buf, "%s\\%s", testdirA, testfiles[i].name);
+        lstrcpyW( buf, testdir );
+        lstrcatW( buf, backslashW );
+        lstrcatW( buf, testfiles[i].name );
         if (testfiles[i].attr & FILE_ATTRIBUTE_DIRECTORY) {
-            ret = RemoveDirectoryA(buf);
+            ret = RemoveDirectoryW(buf);
             ok(ret || (GetLastError() == ERROR_PATH_NOT_FOUND),
-               "Failed to rmdir %s, error %d\n", buf, GetLastError());
+               "Failed to rmdir %s, error %d\n", wine_dbgstr_w(buf), GetLastError());
         } else {
-            ret = DeleteFileA(buf);
+            ret = DeleteFileW(buf);
             ok(ret || (GetLastError() == ERROR_PATH_NOT_FOUND),
-               "Failed to rm %s, error %d\n", buf, GetLastError());
+               "Failed to rm %s, error %d\n", wine_dbgstr_w(buf), GetLastError());
         }
     }
-    RemoveDirectoryA(testdirA);
+    RemoveDirectoryW(testdir);
 }
 
 /* Match one found file against testfiles[], increment count if found */
@@ -141,35 +156,37 @@ static void tally_test_file(FILE_BOTH_DIRECTORY_INFORMATION *dir_info)
     WCHAR *nameW = dir_info->FileName;
     int namelen = dir_info->FileNameLength / sizeof(WCHAR);
 
-    for (i=0; testfiles[i].name; i++) {
-        int len = strlen(testfiles[i].name);
-        if (namelen != len || memcmp(nameW, testfiles[i].nameW, len*sizeof(WCHAR)))
+    for (i = 0; i < test_dir_count; i++) {
+        int len = lstrlenW(testfiles[i].name);
+        if (namelen != len || memcmp(nameW, testfiles[i].name, len*sizeof(WCHAR)))
             continue;
         if (!testfiles[i].attr_done) {
-            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);
+            ok (attrib == (testfiles[i].attr & attribmask), "file %s: expected %s (%x), got %x (is your linux new enough?)\n", wine_dbgstr_w(testfiles[i].name), testfiles[i].description, testfiles[i].attr, attrib);
             testfiles[i].attr_done = TRUE;
         }
         testfiles[i].nfound++;
         break;
     }
-    ok(testfiles[i].name != NULL, "unexpected file found\n");
+    ok(i < test_dir_count, "unexpected file found %s\n", wine_dbgstr_wn(dir_info->FileName, namelen));
 }
 
 static void test_flags_NtQueryDirectoryFile(OBJECT_ATTRIBUTES *attr, const char *testdirA,
                                             UNICODE_STRING *mask,
                                             BOOLEAN single_entry, BOOLEAN restart_flag)
 {
-    HANDLE dirh;
+    UNICODE_STRING dummy_mask;
+    HANDLE dirh, new_dirh;
     IO_STATUS_BLOCK io;
     UINT data_pos, data_size;
     UINT data_len;    /* length of dir data */
     BYTE data[8192];  /* directory data */
     FILE_BOTH_DIRECTORY_INFORMATION *dir_info;
-    DWORD status;
+    NTSTATUS status;
     int numfiles;
     int i;
 
     reset_found_files();
+    pRtlInitUnicodeString( &dummy_mask, dummyW );
 
     data_size = mask ? offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[256] ) : sizeof(data);
 
@@ -182,12 +199,18 @@ static void test_flags_NtQueryDirectoryFile(OBJECT_ATTRIBUTES *attr, const char
        return;
     }
 
-    pNtQueryDirectoryFile( dirh, NULL, NULL, NULL, &io, data, data_size,
-                       FileBothDirectoryInformation, single_entry, mask, restart_flag );
+    U(io).Status = 0xdeadbeef;
+    status = pNtQueryDirectoryFile( dirh, NULL, NULL, NULL, &io, data, data_size,
+                                    FileBothDirectoryInformation, single_entry, mask, restart_flag );
+    ok (status == STATUS_SUCCESS, "failed to query directory; status %x\n", status);
     ok (U(io).Status == STATUS_SUCCESS, "failed 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");
 
+    DuplicateHandle( GetCurrentProcess(), dirh, GetCurrentProcess(), &new_dirh,
+                     0, FALSE, DUPLICATE_SAME_ACCESS );
+    pNtClose(dirh);
+
     data_pos = 0;
     numfiles = 0;
     while ((data_pos < data_len) && (numfiles < max_test_dir_size)) {
@@ -196,11 +219,12 @@ static void test_flags_NtQueryDirectoryFile(OBJECT_ATTRIBUTES *attr, const char
         tally_test_file(dir_info);
 
         if (dir_info->NextEntryOffset == 0) {
-            pNtQueryDirectoryFile( dirh, 0, NULL, NULL, &io, data, data_size,
-                               FileBothDirectoryInformation, single_entry, mask, FALSE );
-            if (U(io).Status == STATUS_NO_MORE_FILES)
-                break;
-            ok (U(io).Status == STATUS_SUCCESS, "failed to query directory; status %x\n", U(io).Status);
+            U(io).Status = 0xdeadbeef;
+            status = pNtQueryDirectoryFile( new_dirh, 0, NULL, NULL, &io, data, data_size,
+                                            FileBothDirectoryInformation, single_entry, &dummy_mask, FALSE );
+            ok (U(io).Status == status, "wrong status %x / %x\n", status, U(io).Status);
+            if (status == STATUS_NO_MORE_FILES) break;
+            ok (status == STATUS_SUCCESS, "failed to query directory; status %x\n", status);
             data_len = io.Information;
             if (data_len < sizeof(FILE_BOTH_DIRECTORY_INFORMATION))
                 break;
@@ -213,33 +237,238 @@ static void test_flags_NtQueryDirectoryFile(OBJECT_ATTRIBUTES *attr, const char
     ok(numfiles < max_test_dir_size, "too many loops\n");
 
     if (mask)
-        for (i=0; testfiles[i].name; i++)
-            ok(testfiles[i].nfound == (testfiles[i].nameW == mask->Buffer),
+        for (i = 0; i < test_dir_count; i++)
+            ok(testfiles[i].nfound == (testfiles[i].name == mask->Buffer),
                "Wrong number %d of %s files found (single_entry=%d,mask=%s)\n",
                testfiles[i].nfound, testfiles[i].description, single_entry,
                wine_dbgstr_wn(mask->Buffer, mask->Length/sizeof(WCHAR) ));
     else
-        for (i=0; testfiles[i].name; i++)
+        for (i = 0; i < test_dir_count; i++)
             ok(testfiles[i].nfound == 1, "Wrong number %d of %s files found (single_entry=%d,restart=%d)\n",
                testfiles[i].nfound, testfiles[i].description, single_entry, restart_flag);
-    pNtClose(dirh);
+    pNtClose(new_dirh);
 }
 
-static void test_NtQueryDirectoryFile(void)
+static void test_directory_sort( const WCHAR *testdir )
 {
     OBJECT_ATTRIBUTES attr;
     UNICODE_STRING ntdirname;
+    IO_STATUS_BLOCK io;
+    UINT data_pos, data_len, count;
+    BYTE data[8192];
+    WCHAR prev[MAX_PATH], name[MAX_PATH];
+    UNICODE_STRING prev_str, name_str;
+    FILE_BOTH_DIRECTORY_INFORMATION *info;
+    NTSTATUS status;
+    HANDLE handle;
+    int res;
+
+    if (!pRtlDosPathNameToNtPathName_U( testdir, &ntdirname, NULL, NULL ))
+    {
+        ok(0, "RtlDosPathNametoNtPathName_U failed\n");
+        return;
+    }
+    InitializeObjectAttributes( &attr, &ntdirname, OBJ_CASE_INSENSITIVE, 0, NULL );
+    status = pNtOpenFile( &handle, SYNCHRONIZE | FILE_LIST_DIRECTORY, &attr, &io, FILE_SHARE_READ,
+                        FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT | FILE_DIRECTORY_FILE );
+    ok(status == STATUS_SUCCESS, "failed to open dir %s\n", wine_dbgstr_w(testdir) );
+
+    U(io).Status = 0xdeadbeef;
+    status = pNtQueryDirectoryFile( handle, NULL, NULL, NULL, &io, data, sizeof(data),
+                                    FileBothDirectoryInformation, FALSE, NULL, TRUE );
+    ok( status == STATUS_SUCCESS, "failed to query directory; status %x\n", status );
+    ok( U(io).Status == STATUS_SUCCESS, "failed 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;
+    count = 0;
+
+    while (data_pos < data_len)
+    {
+        info = (FILE_BOTH_DIRECTORY_INFORMATION *)(data + data_pos);
+
+        memcpy( name, info->FileName, info->FileNameLength );
+        name[info->FileNameLength / sizeof(WCHAR)] = 0;
+        switch (count)
+        {
+        case 0:  /* first entry must be '.' */
+            ok( !lstrcmpW( name, dotW ), "wrong name %s\n", wine_dbgstr_w( name ));
+            break;
+        case 1:  /* second entry must be '..' */
+            ok( !lstrcmpW( name, dotdotW ), "wrong name %s\n", wine_dbgstr_w( name ));
+            break;
+        case 2:  /* nothing to compare against */
+            break;
+        default:
+            pRtlInitUnicodeString( &prev_str, prev );
+            pRtlInitUnicodeString( &name_str, name );
+            res = pRtlCompareUnicodeString( &prev_str, &name_str, TRUE );
+            ok( res < 0, "wrong result %d %s %s\n", res, wine_dbgstr_w( prev ), wine_dbgstr_w( name ));
+            break;
+        }
+        count++;
+        lstrcpyW( prev, name );
+
+        if (info->NextEntryOffset == 0)
+        {
+            U(io).Status = 0xdeadbeef;
+            status = pNtQueryDirectoryFile( handle, 0, NULL, NULL, &io, data, sizeof(data),
+                                            FileBothDirectoryInformation, FALSE, NULL, FALSE );
+            ok (U(io).Status == status, "wrong status %x / %x\n", status, U(io).Status);
+            if (status == STATUS_NO_MORE_FILES) break;
+            ok( status == STATUS_SUCCESS, "failed to query directory; status %x\n", status );
+            data_len = io.Information;
+            data_pos = 0;
+        }
+        else data_pos += info->NextEntryOffset;
+    }
+
+    pNtClose( handle );
+    pRtlFreeUnicodeString( &ntdirname );
+}
+
+static void test_NtQueryDirectoryFile_classes( HANDLE handle, UNICODE_STRING *mask )
+{
+    IO_STATUS_BLOCK io;
+    UINT data_size;
+    ULONG data[256];
+    NTSTATUS status;
+    int class;
+
+    for (class = 0; class < FileMaximumInformation; class++)
+    {
+        U(io).Status = 0xdeadbeef;
+        U(io).Information = 0xdeadbeef;
+        data_size = 0;
+        memset( data, 0x55, sizeof(data) );
+
+        status = pNtQueryDirectoryFile( handle, 0, NULL, NULL, &io, data, data_size,
+                                        class, FALSE, mask, TRUE );
+        ok( U(io).Status == 0xdeadbeef, "%u: wrong status %x\n", class, U(io).Status );
+        ok( U(io).Information == 0xdeadbeef, "%u: wrong info %lx\n", class, U(io).Information );
+        ok(data[0] == 0x55555555, "%u: wrong offset %x\n",  class, data[0] );
+
+        switch (class)
+        {
+        case FileIdGlobalTxDirectoryInformation:
+        case FileIdExtdDirectoryInformation:
+        case FileIdExtdBothDirectoryInformation:
+            if (status == STATUS_INVALID_INFO_CLASS || status == STATUS_NOT_IMPLEMENTED) continue;
+            /* fall through */
+        case FileDirectoryInformation:
+        case FileFullDirectoryInformation:
+        case FileBothDirectoryInformation:
+        case FileNamesInformation:
+        case FileIdBothDirectoryInformation:
+        case FileIdFullDirectoryInformation:
+        case FileObjectIdInformation:
+        case FileQuotaInformation:
+        case FileReparsePointInformation:
+            ok( status == STATUS_INFO_LENGTH_MISMATCH, "%u: wrong status %x\n", class, status );
+            break;
+        default:
+            ok( status == STATUS_INVALID_INFO_CLASS || status == STATUS_NOT_IMPLEMENTED,
+                "%u: wrong status %x\n", class, status );
+            continue;
+        }
+
+        for (data_size = 1; data_size < sizeof(data); data_size++)
+        {
+            status = pNtQueryDirectoryFile( handle, 0, NULL, NULL, &io, data, data_size,
+                                            class, FALSE, mask, TRUE );
+            if (status == STATUS_BUFFER_OVERFLOW)
+            {
+                ok( U(io).Status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %x\n", class, U(io).Status );
+                ok( U(io).Information == data_size, "%u: wrong info %lx\n", class, U(io).Information );
+                ok(data[0] == 0, "%u: wrong offset %x\n",  class, data[0] );
+            }
+            else
+            {
+                ok( U(io).Status == 0xdeadbeef, "%u: wrong status %x\n", class, U(io).Status );
+                ok( U(io).Information == 0xdeadbeef, "%u: wrong info %lx\n", class, U(io).Information );
+                ok(data[0] == 0x55555555, "%u: wrong offset %x\n",  class, data[0] );
+            }
+            if (status != STATUS_INFO_LENGTH_MISMATCH) break;
+        }
+
+        switch (class)
+        {
+        case FileDirectoryInformation:
+            ok( status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %x\n", class, status );
+            ok( data_size == ((offsetof( FILE_DIRECTORY_INFORMATION, FileName[1] ) + 7) & ~7),
+                "%u: wrong size %u\n", class, data_size );
+            break;
+        case FileFullDirectoryInformation:
+            ok( status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %x\n", class, status );
+            ok( data_size == ((offsetof( FILE_FULL_DIRECTORY_INFORMATION, FileName[1] ) + 7) & ~7),
+                "%u: wrong size %u\n", class, data_size );
+            break;
+        case FileBothDirectoryInformation:
+            ok( status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %x\n", class, status );
+            ok( data_size == ((offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] ) + 7) & ~7),
+                "%u: wrong size %u\n", class, data_size );
+            break;
+        case FileNamesInformation:
+            ok( status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %x\n", class, status );
+            ok( data_size == ((offsetof( FILE_NAMES_INFORMATION, FileName[1] ) + 7) & ~7),
+                "%u: wrong size %u\n", class, data_size );
+            break;
+        case FileIdBothDirectoryInformation:
+            ok( status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %x\n", class, status );
+            ok( data_size == ((offsetof( FILE_ID_BOTH_DIRECTORY_INFORMATION, FileName[1] ) + 7) & ~7),
+                "%u: wrong size %u\n", class, data_size );
+            break;
+        case FileIdFullDirectoryInformation:
+            ok( status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %x\n", class, status );
+            ok( data_size == ((offsetof( FILE_ID_FULL_DIRECTORY_INFORMATION, FileName[1] ) + 7) & ~7),
+                "%u: wrong size %u\n", class, data_size );
+            break;
+        case FileIdGlobalTxDirectoryInformation:
+            ok( status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %x\n", class, status );
+            ok( data_size == ((offsetof( FILE_ID_GLOBAL_TX_DIR_INFORMATION, FileName[1] ) + 7) & ~7),
+                "%u: wrong size %u\n", class, data_size );
+            break;
+        case FileObjectIdInformation:
+            ok( status == STATUS_INVALID_INFO_CLASS, "%u: wrong status %x\n", class, status );
+            ok( data_size == sizeof(FILE_OBJECTID_INFORMATION), "%u: wrong size %u\n", class, data_size );
+            break;
+        case FileQuotaInformation:
+            ok( status == STATUS_INVALID_INFO_CLASS, "%u: wrong status %x\n", class, status );
+            ok( data_size == sizeof(FILE_QUOTA_INFORMATION), "%u: wrong size %u\n", class, data_size );
+            break;
+        case FileReparsePointInformation:
+            ok( status == STATUS_INVALID_INFO_CLASS, "%u: wrong status %x\n", class, status );
+            ok( data_size == sizeof(FILE_REPARSE_POINT_INFORMATION), "%u: wrong size %u\n", class, data_size );
+            break;
+        }
+    }
+}
+
+static void test_NtQueryDirectoryFile(void)
+{
+    OBJECT_ATTRIBUTES attr;
+    UNICODE_STRING ntdirname, mask;
     char testdirA[MAX_PATH];
     WCHAR testdirW[MAX_PATH];
     int i;
+    IO_STATUS_BLOCK io;
+    WCHAR short_name[12];
+    UINT data_size;
+    BYTE data[8192];
+    FILE_BOTH_DIRECTORY_INFORMATION *next, *fbdi = (FILE_BOTH_DIRECTORY_INFORMATION*)data;
+    FILE_POSITION_INFORMATION pos_info;
+    FILE_NAMES_INFORMATION *names;
+    const WCHAR *filename = fbdi->FileName;
+    NTSTATUS status;
+    HANDLE dirh;
 
     /* 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);
-
     pRtlMultiByteToUnicodeN(testdirW, sizeof(testdirW), NULL, testdirA, strlen(testdirA)+1);
+    tear_down_attribute_test(testdirW);
+    set_up_attribute_test(testdirW);
+
     if (!pRtlDosPathNameToNtPathName_U(testdirW, &ntdirname, NULL, NULL))
     {
         ok(0, "RtlDosPathNametoNtPathName_U failed\n");
@@ -252,21 +481,250 @@ static void test_NtQueryDirectoryFile(void)
     test_flags_NtQueryDirectoryFile(&attr, testdirA, NULL, TRUE, TRUE);
     test_flags_NtQueryDirectoryFile(&attr, testdirA, NULL, TRUE, FALSE);
 
-    for (i = 0; testfiles[i].name; i++)
+    for (i = 0; i < test_dir_count; i++)
     {
-        UNICODE_STRING mask;
-
-        if (testfiles[i].nameW[0] == '.') continue;  /* . and .. as masks are broken on Windows */
-        mask.Buffer = testfiles[i].nameW;
-        mask.Length = mask.MaximumLength = lstrlenW(testfiles[i].nameW) * sizeof(WCHAR);
+        if (testfiles[i].name[0] == '.') continue;  /* . and .. as masks are broken on Windows */
+        mask.Buffer = testfiles[i].name;
+        mask.Length = mask.MaximumLength = lstrlenW(testfiles[i].name) * sizeof(WCHAR);
         test_flags_NtQueryDirectoryFile(&attr, testdirA, &mask, FALSE, TRUE);
         test_flags_NtQueryDirectoryFile(&attr, testdirA, &mask, FALSE, FALSE);
         test_flags_NtQueryDirectoryFile(&attr, testdirA, &mask, TRUE, TRUE);
         test_flags_NtQueryDirectoryFile(&attr, testdirA, &mask, TRUE, FALSE);
     }
 
+    /* short path passed as mask */
+    status = pNtOpenFile(&dirh, SYNCHRONIZE | FILE_LIST_DIRECTORY, &attr, &io, FILE_SHARE_READ,
+            FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT | FILE_DIRECTORY_FILE);
+    ok(status == STATUS_SUCCESS, "failed to open dir '%s'\n", testdirA);
+    if (status != STATUS_SUCCESS) {
+        skip("can't test if we can't open the directory\n");
+        return;
+    }
+    status = pNtQueryInformationFile( dirh, &io, &pos_info, sizeof(pos_info), FilePositionInformation );
+    ok( status == STATUS_SUCCESS, "NtQueryInformationFile failed %x\n", status );
+    ok( pos_info.CurrentByteOffset.QuadPart == 0, "wrong pos %s\n",
+        wine_dbgstr_longlong(pos_info.CurrentByteOffset.QuadPart));
+
+    pos_info.CurrentByteOffset.QuadPart = 0xbeef;
+    status = pNtSetInformationFile( dirh, &io, &pos_info, sizeof(pos_info), FilePositionInformation );
+    ok( status == STATUS_SUCCESS, "NtQueryInformationFile failed %x\n", status );
+
+    status = pNtQueryInformationFile( dirh, &io, &pos_info, sizeof(pos_info), FilePositionInformation );
+    ok( status == STATUS_SUCCESS, "NtQueryInformationFile failed %x\n", status );
+    ok( pos_info.CurrentByteOffset.QuadPart == 0xbeef, "wrong pos %s\n",
+        wine_dbgstr_longlong(pos_info.CurrentByteOffset.QuadPart));
+
+    mask.Buffer = testfiles[0].name;
+    mask.Length = mask.MaximumLength = lstrlenW(testfiles[0].name) * sizeof(WCHAR);
+    data_size = offsetof(FILE_BOTH_DIRECTORY_INFORMATION, FileName[256]);
+    U(io).Status = 0xdeadbeef;
+    status = pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size,
+                                   FileBothDirectoryInformation, TRUE, &mask, FALSE);
+    ok(status == STATUS_SUCCESS, "failed to query directory; status %x\n", status);
+    ok(U(io).Status == STATUS_SUCCESS, "failed to query directory; status %x\n", U(io).Status);
+    ok(fbdi->ShortName[0], "ShortName is empty\n");
+
+    status = pNtQueryInformationFile( dirh, &io, &pos_info, sizeof(pos_info), FilePositionInformation );
+    ok( status == STATUS_SUCCESS, "NtQueryInformationFile failed %x\n", status );
+    ok( pos_info.CurrentByteOffset.QuadPart == 0xbeef, "wrong pos %s\n",
+        wine_dbgstr_longlong(pos_info.CurrentByteOffset.QuadPart) );
+
+    mask.Length = mask.MaximumLength = fbdi->ShortNameLength;
+    memcpy(short_name, fbdi->ShortName, mask.Length);
+    mask.Buffer = short_name;
+    U(io).Status = 0xdeadbeef;
+    U(io).Information = 0xdeadbeef;
+    status = pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size,
+                                   FileBothDirectoryInformation, TRUE, &mask, TRUE);
+    ok(status == STATUS_SUCCESS, "failed to query directory status %x\n", status);
+    ok(U(io).Status == STATUS_SUCCESS, "failed to query directory status %x\n", U(io).Status);
+    ok(U(io).Information == offsetof(FILE_BOTH_DIRECTORY_INFORMATION, FileName[lstrlenW(testfiles[0].name)]),
+       "wrong info %lx\n", U(io).Information);
+    ok(fbdi->FileNameLength == lstrlenW(testfiles[0].name)*sizeof(WCHAR) &&
+            !memcmp(fbdi->FileName, testfiles[0].name, fbdi->FileNameLength),
+            "incorrect long file name: %s\n", wine_dbgstr_wn(fbdi->FileName,
+                fbdi->FileNameLength/sizeof(WCHAR)));
+
+    status = pNtQueryInformationFile( dirh, &io, &pos_info, sizeof(pos_info), FilePositionInformation );
+    ok( status == STATUS_SUCCESS, "NtQueryInformationFile failed %x\n", status );
+    ok( pos_info.CurrentByteOffset.QuadPart == 0xbeef, "wrong pos %s\n",
+        wine_dbgstr_longlong(pos_info.CurrentByteOffset.QuadPart) );
+
+    /* tests with short buffer */
+    memset( data, 0x55, data_size );
+    U(io).Status = 0xdeadbeef;
+    U(io).Information = 0xdeadbeef;
+    data_size = offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] );
+    status = pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size,
+                                   FileBothDirectoryInformation, TRUE, &mask, TRUE);
+    ok( status == STATUS_BUFFER_OVERFLOW, "wrong status %x\n", status );
+    ok( U(io).Status == STATUS_BUFFER_OVERFLOW, "wrong status %x\n", U(io).Status );
+    ok( U(io).Information == data_size || broken( U(io).Information == 0),
+        "wrong info %lx\n", U(io).Information );
+    ok( fbdi->NextEntryOffset == 0, "wrong offset %x\n",  fbdi->NextEntryOffset );
+    ok( fbdi->FileNameLength == lstrlenW(testfiles[0].name) * sizeof(WCHAR),
+        "wrong length %x\n", fbdi->FileNameLength );
+    ok( filename[0] == testfiles[0].name[0], "incorrect long file name: %s\n",
+        wine_dbgstr_wn(fbdi->FileName, fbdi->FileNameLength/sizeof(WCHAR)));
+    ok( filename[1] == 0x5555, "incorrect long file name: %s\n",
+        wine_dbgstr_wn(fbdi->FileName, fbdi->FileNameLength/sizeof(WCHAR)));
+
+    test_NtQueryDirectoryFile_classes( dirh, &mask );
+
+    /* mask may or may not be ignored when restarting the search */
+    pRtlInitUnicodeString( &mask, dummyW );
+    U(io).Status = 0xdeadbeef;
+    data_size = offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[256] );
+    status = pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size,
+                                   FileBothDirectoryInformation, TRUE, &mask, TRUE);
+    ok( status == STATUS_SUCCESS || status == STATUS_NO_MORE_FILES, "wrong status %x\n", status );
+    ok( U(io).Status == status, "wrong status %x / %x\n", U(io).Status, status );
+    if (!status)
+        ok( fbdi->FileNameLength == lstrlenW(testfiles[0].name)*sizeof(WCHAR) &&
+            !memcmp(fbdi->FileName, testfiles[0].name, fbdi->FileNameLength),
+            "incorrect long file name: %s\n",
+            wine_dbgstr_wn(fbdi->FileName, fbdi->FileNameLength/sizeof(WCHAR)));
+
+    pNtClose(dirh);
+
+    status = pNtOpenFile(&dirh, SYNCHRONIZE | FILE_LIST_DIRECTORY, &attr, &io, FILE_SHARE_READ,
+            FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT | FILE_DIRECTORY_FILE);
+    ok(status == STATUS_SUCCESS, "failed to open dir '%s'\n", testdirA);
+
+    memset( data, 0x55, data_size );
+    data_size = sizeof(data);
+    U(io).Status = 0xdeadbeef;
+    U(io).Information = 0xdeadbeef;
+    status = pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size,
+                                   FileBothDirectoryInformation, FALSE, NULL, TRUE);
+    ok(status == STATUS_SUCCESS, "wrong status %x\n", status);
+    ok(U(io).Status == STATUS_SUCCESS, "wrong status %x\n", U(io).Status);
+    ok(U(io).Information > 0 && U(io).Information < data_size, "wrong info %lx\n", U(io).Information);
+    ok( fbdi->NextEntryOffset == ((offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] ) + 7) & ~7),
+        "wrong offset %x\n",  fbdi->NextEntryOffset );
+    ok( fbdi->FileNameLength == sizeof(WCHAR), "wrong length %x\n", fbdi->FileNameLength );
+    ok( fbdi->FileName[0] == '.', "incorrect long file name: %s\n",
+        wine_dbgstr_wn(fbdi->FileName, fbdi->FileNameLength/sizeof(WCHAR)));
+    next = (FILE_BOTH_DIRECTORY_INFORMATION *)(data + fbdi->NextEntryOffset);
+    ok( next->NextEntryOffset == ((offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[2] ) + 7) & ~7),
+        "wrong offset %x\n",  next->NextEntryOffset );
+    ok( next->FileNameLength == 2 * sizeof(WCHAR), "wrong length %x\n", next->FileNameLength );
+    filename = next->FileName;
+    ok( filename[0] == '.' && filename[1] == '.', "incorrect long file name: %s\n",
+        wine_dbgstr_wn(next->FileName, next->FileNameLength/sizeof(WCHAR)));
+
+    data_size = fbdi->NextEntryOffset + offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] ),
+    memset( data, 0x55, data_size );
+    U(io).Status = 0xdeadbeef;
+    U(io).Information = 0xdeadbeef;
+    status = pNtQueryDirectoryFile( dirh, 0, NULL, NULL, &io, data, data_size,
+                                    FileBothDirectoryInformation, FALSE, NULL, TRUE );
+    ok( status == STATUS_SUCCESS, "wrong status %x\n", status );
+    ok( U(io).Status == STATUS_SUCCESS, "wrong status %x\n", U(io).Status );
+    ok( U(io).Information == offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] ),
+        "wrong info %lx\n", U(io).Information );
+    ok( fbdi->NextEntryOffset == 0, "wrong offset %x\n",  fbdi->NextEntryOffset );
+    ok( fbdi->FileNameLength == sizeof(WCHAR), "wrong length %x\n", fbdi->FileNameLength );
+    ok( fbdi->FileName[0] == '.', "incorrect long file name: %s\n",
+        wine_dbgstr_wn(fbdi->FileName, fbdi->FileNameLength/sizeof(WCHAR)));
+    next = (FILE_BOTH_DIRECTORY_INFORMATION *)&fbdi->FileName[1];
+    ok( next->NextEntryOffset == 0x55555555, "wrong offset %x\n",  next->NextEntryOffset );
+
+    data_size = fbdi->NextEntryOffset + offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[2] ),
+    memset( data, 0x55, data_size );
+    U(io).Status = 0xdeadbeef;
+    U(io).Information = 0xdeadbeef;
+    status = pNtQueryDirectoryFile( dirh, 0, NULL, NULL, &io, data, data_size,
+                                    FileBothDirectoryInformation, FALSE, NULL, TRUE );
+    ok( status == STATUS_SUCCESS, "wrong status %x\n", status );
+    ok( U(io).Status == STATUS_SUCCESS, "wrong status %x\n", U(io).Status );
+    ok( U(io).Information == offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] ),
+        "wrong info %lx\n", U(io).Information );
+    ok( fbdi->NextEntryOffset == 0, "wrong offset %x\n",  fbdi->NextEntryOffset );
+
+    data_size = ((offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] ) + 7) & ~7) +
+                  offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[2] );
+    memset( data, 0x55, data_size );
+    U(io).Status = 0xdeadbeef;
+    U(io).Information = 0xdeadbeef;
+    status = pNtQueryDirectoryFile( dirh, 0, NULL, NULL, &io, data, data_size,
+                                    FileBothDirectoryInformation, FALSE, NULL, TRUE );
+    ok( status == STATUS_SUCCESS, "wrong status %x\n", status );
+    ok( U(io).Status == STATUS_SUCCESS, "wrong status %x\n", U(io).Status );
+    ok( U(io).Information == data_size, "wrong info %lx / %x\n", U(io).Information, data_size );
+    ok( fbdi->NextEntryOffset == ((offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] ) + 7) & ~7),
+        "wrong offset %x\n",  fbdi->NextEntryOffset );
+    ok( fbdi->FileNameLength == sizeof(WCHAR), "wrong length %x\n", fbdi->FileNameLength );
+    ok( fbdi->FileName[0] == '.', "incorrect long file name: %s\n",
+        wine_dbgstr_wn(fbdi->FileName, fbdi->FileNameLength/sizeof(WCHAR)));
+    next = (FILE_BOTH_DIRECTORY_INFORMATION *)(data + fbdi->NextEntryOffset);
+    ok( next->NextEntryOffset == 0, "wrong offset %x\n",  next->NextEntryOffset );
+    ok( next->FileNameLength == 2 * sizeof(WCHAR), "wrong length %x\n", next->FileNameLength );
+    filename = next->FileName;
+    ok( filename[0] == '.' && filename[1] == '.', "incorrect long file name: %s\n",
+        wine_dbgstr_wn(next->FileName, next->FileNameLength/sizeof(WCHAR)));
+
+    data_size = ((offsetof( FILE_NAMES_INFORMATION, FileName[1] ) + 7) & ~7) +
+                  offsetof( FILE_NAMES_INFORMATION, FileName[2] );
+    memset( data, 0x55, data_size );
+    U(io).Status = 0xdeadbeef;
+    U(io).Information = 0xdeadbeef;
+    status = pNtQueryDirectoryFile( dirh, 0, NULL, NULL, &io, data, data_size,
+                                    FileNamesInformation, FALSE, NULL, TRUE );
+    ok( status == STATUS_SUCCESS, "wrong status %x\n", status );
+    ok( U(io).Status == STATUS_SUCCESS, "wrong status %x\n", U(io).Status );
+    ok( U(io).Information == data_size, "wrong info %lx / %x\n", U(io).Information, data_size );
+    names = (FILE_NAMES_INFORMATION *)data;
+    ok( names->NextEntryOffset == ((offsetof( FILE_NAMES_INFORMATION, FileName[1] ) + 7) & ~7),
+        "wrong offset %x\n",  names->NextEntryOffset );
+    ok( names->FileNameLength == sizeof(WCHAR), "wrong length %x\n", names->FileNameLength );
+    ok( names->FileName[0] == '.', "incorrect long file name: %s\n",
+        wine_dbgstr_wn(names->FileName, names->FileNameLength/sizeof(WCHAR)));
+    names = (FILE_NAMES_INFORMATION *)(data + names->NextEntryOffset);
+    ok( names->NextEntryOffset == 0, "wrong offset %x\n",  names->NextEntryOffset );
+    ok( names->FileNameLength == 2 * sizeof(WCHAR), "wrong length %x\n", names->FileNameLength );
+    filename = names->FileName;
+    ok( filename[0] == '.' && filename[1] == '.', "incorrect long file name: %s\n",
+        wine_dbgstr_wn(names->FileName, names->FileNameLength/sizeof(WCHAR)));
+
+    pNtClose(dirh);
+
+    /* create new handle to change mask */
+    status = pNtOpenFile(&dirh, SYNCHRONIZE | FILE_LIST_DIRECTORY, &attr, &io, FILE_SHARE_READ,
+            FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT | FILE_DIRECTORY_FILE);
+    ok(status == STATUS_SUCCESS, "failed to open dir '%s'\n", testdirA);
+
+    pRtlInitUnicodeString( &mask, dummyW );
+    U(io).Status = 0xdeadbeef;
+    data_size = sizeof(data);
+    status = pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size,
+                                   FileBothDirectoryInformation, TRUE, &mask, TRUE);
+    ok(status == STATUS_NO_SUCH_FILE, "wrong status %x\n", status);
+    ok(U(io).Status == 0xdeadbeef, "wrong status %x\n", U(io).Status);
+
+    U(io).Status = 0xdeadbeef;
+    status = pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size,
+                                   FileBothDirectoryInformation, TRUE, NULL, FALSE);
+    ok(status == STATUS_NO_MORE_FILES, "wrong status %x\n", status);
+    ok(U(io).Status == STATUS_NO_MORE_FILES, "wrong status %x\n", U(io).Status);
+
+    U(io).Status = 0xdeadbeef;
+    status = pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size,
+                                   FileBothDirectoryInformation, TRUE, NULL, TRUE);
+    ok(status == STATUS_NO_MORE_FILES, "wrong status %x\n", status);
+    ok(U(io).Status == STATUS_NO_MORE_FILES, "wrong status %x\n", U(io).Status);
+
+    pNtClose(dirh);
+
+    U(io).Status = 0xdeadbeef;
+    status = pNtQueryDirectoryFile( (HANDLE)0xbeef, 0, NULL, NULL, &io, data, data_size,
+                                    FileBothDirectoryInformation, TRUE, NULL, TRUE );
+    ok(status == STATUS_INVALID_HANDLE, "wrong status %x\n", status);
+    ok(U(io).Status == 0xdeadbeef, "wrong status %x\n", U(io).Status);
+
 done:
-    tear_down_attribute_test(testdirA);
+    test_directory_sort( testdirW );
+    tear_down_attribute_test( testdirW );
     pRtlFreeUnicodeString(&ntdirname);
 }
 
@@ -403,6 +861,8 @@ static void test_redirection(void)
     ok( status == STATUS_ACCESS_VIOLATION, "RtlWow64EnableFsRedirectionEx failed with status %x\n", status );
     status = pRtlWow64EnableFsRedirectionEx( TRUE, (void*)1 );
     ok( status == STATUS_ACCESS_VIOLATION, "RtlWow64EnableFsRedirectionEx failed with status %x\n", status );
+    status = pRtlWow64EnableFsRedirectionEx( TRUE, (void*)0xDEADBEEF );
+    ok( status == STATUS_ACCESS_VIOLATION, "RtlWow64EnableFsRedirectionEx failed with status %x\n", status );
 
     status = pRtlWow64EnableFsRedirection( FALSE );
     ok( !status, "RtlWow64EnableFsRedirectionEx failed status %x\n", status );
@@ -415,6 +875,7 @@ static void test_redirection(void)
 
 START_TEST(directory)
 {
+    WCHAR sysdir[MAX_PATH];
     HMODULE hntdll = GetModuleHandleA("ntdll.dll");
     if (!hntdll)
     {
@@ -425,14 +886,19 @@ START_TEST(directory)
     pNtClose                = (void *)GetProcAddress(hntdll, "NtClose");
     pNtOpenFile             = (void *)GetProcAddress(hntdll, "NtOpenFile");
     pNtQueryDirectoryFile   = (void *)GetProcAddress(hntdll, "NtQueryDirectoryFile");
+    pNtQueryInformationFile = (void *)GetProcAddress(hntdll, "NtQueryInformationFile");
+    pNtSetInformationFile   = (void *)GetProcAddress(hntdll, "NtSetInformationFile");
     pRtlCreateUnicodeStringFromAsciiz = (void *)GetProcAddress(hntdll, "RtlCreateUnicodeStringFromAsciiz");
     pRtlDosPathNameToNtPathName_U = (void *)GetProcAddress(hntdll, "RtlDosPathNameToNtPathName_U");
     pRtlInitUnicodeString   = (void *)GetProcAddress(hntdll, "RtlInitUnicodeString");
     pRtlFreeUnicodeString   = (void *)GetProcAddress(hntdll, "RtlFreeUnicodeString");
+    pRtlCompareUnicodeString = (void *)GetProcAddress(hntdll, "RtlCompareUnicodeString");
     pRtlMultiByteToUnicodeN = (void *)GetProcAddress(hntdll,"RtlMultiByteToUnicodeN");
     pRtlWow64EnableFsRedirection = (void *)GetProcAddress(hntdll,"RtlWow64EnableFsRedirection");
     pRtlWow64EnableFsRedirectionEx = (void *)GetProcAddress(hntdll,"RtlWow64EnableFsRedirectionEx");
 
+    GetSystemDirectoryW( sysdir, MAX_PATH );
+    test_directory_sort( sysdir );
     test_NtQueryDirectoryFile();
     test_NtQueryDirectoryFile_case();
     test_redirection();
index 4bd400b..8dc2d90 100644 (file)
 
 static void *code_mem;
 
-static struct _TEB * (WINAPI *pNtCurrentTeb)(void);
 static NTSTATUS  (WINAPI *pNtGetContextThread)(HANDLE,CONTEXT*);
 static NTSTATUS  (WINAPI *pNtSetContextThread)(HANDLE,CONTEXT*);
 static NTSTATUS  (WINAPI *pRtlRaiseException)(EXCEPTION_RECORD *rec);
 static PVOID     (WINAPI *pRtlUnwind)(PVOID, PVOID, PEXCEPTION_RECORD, PVOID);
+static VOID      (WINAPI *pRtlCaptureContext)(CONTEXT*);
 static PVOID     (WINAPI *pRtlAddVectoredExceptionHandler)(ULONG first, PVECTORED_EXCEPTION_HANDLER func);
 static ULONG     (WINAPI *pRtlRemoveVectoredExceptionHandler)(PVOID handler);
 static PVOID     (WINAPI *pRtlAddVectoredContinueHandler)(ULONG first, PVECTORED_EXCEPTION_HANDLER func);
@@ -56,10 +56,72 @@ static BOOL      (WINAPI *pIsWow64Process)(HANDLE, PBOOL);
 static NTSTATUS  (WINAPI *pNtClose)(HANDLE);
 
 #if defined(__x86_64__)
+typedef struct
+{
+    ULONG Count;
+    struct
+    {
+        ULONG BeginAddress;
+        ULONG EndAddress;
+        ULONG HandlerAddress;
+        ULONG JumpTarget;
+    } ScopeRecord[1];
+} SCOPE_TABLE;
+
+typedef struct
+{
+    ULONG64               ControlPc;
+    ULONG64               ImageBase;
+    PRUNTIME_FUNCTION     FunctionEntry;
+    ULONG64               EstablisherFrame;
+    ULONG64               TargetIp;
+    PCONTEXT              ContextRecord;
+    void* /*PEXCEPTION_ROUTINE*/ LanguageHandler;
+    PVOID                 HandlerData;
+    PUNWIND_HISTORY_TABLE HistoryTable;
+    ULONG                 ScopeIndex;
+} DISPATCHER_CONTEXT;
+
+typedef struct _SETJMP_FLOAT128
+{
+    unsigned __int64 DECLSPEC_ALIGN(16) Part[2];
+} SETJMP_FLOAT128;
+
+typedef struct _JUMP_BUFFER
+{
+    unsigned __int64 Frame;
+    unsigned __int64 Rbx;
+    unsigned __int64 Rsp;
+    unsigned __int64 Rbp;
+    unsigned __int64 Rsi;
+    unsigned __int64 Rdi;
+    unsigned __int64 R12;
+    unsigned __int64 R13;
+    unsigned __int64 R14;
+    unsigned __int64 R15;
+    unsigned __int64 Rip;
+    unsigned __int64 Spare;
+    SETJMP_FLOAT128  Xmm6;
+    SETJMP_FLOAT128  Xmm7;
+    SETJMP_FLOAT128  Xmm8;
+    SETJMP_FLOAT128  Xmm9;
+    SETJMP_FLOAT128  Xmm10;
+    SETJMP_FLOAT128  Xmm11;
+    SETJMP_FLOAT128  Xmm12;
+    SETJMP_FLOAT128  Xmm13;
+    SETJMP_FLOAT128  Xmm14;
+    SETJMP_FLOAT128  Xmm15;
+} _JUMP_BUFFER;
+
 static BOOLEAN   (CDECL *pRtlAddFunctionTable)(RUNTIME_FUNCTION*, DWORD, DWORD64);
 static BOOLEAN   (CDECL *pRtlDeleteFunctionTable)(RUNTIME_FUNCTION*);
 static BOOLEAN   (CDECL *pRtlInstallFunctionTableCallback)(DWORD64, DWORD64, DWORD, PGET_RUNTIME_FUNCTION_CALLBACK, PVOID, PCWSTR);
 static PRUNTIME_FUNCTION (WINAPI *pRtlLookupFunctionEntry)(ULONG64, ULONG64*, UNWIND_HISTORY_TABLE*);
+static EXCEPTION_DISPOSITION (WINAPI *p__C_specific_handler)(EXCEPTION_RECORD*, ULONG64, CONTEXT*, DISPATCHER_CONTEXT*);
+static VOID      (WINAPI *pRtlCaptureContext)(CONTEXT*);
+static VOID      (CDECL *pRtlRestoreContext)(CONTEXT*, EXCEPTION_RECORD*);
+static VOID      (CDECL *pRtlUnwindEx)(VOID*, VOID*, EXCEPTION_RECORD*, VOID*, CONTEXT*, UNWIND_HISTORY_TABLE*);
+static int       (CDECL *p_setjmp)(_JUMP_BUFFER*);
 #endif
 
 #ifdef __i386__
@@ -221,6 +283,11 @@ static const struct exception
 
     { { 0xf1, 0x90, 0xc3 },  /* icebp; nop; ret */
       1, 1, FALSE, STATUS_SINGLE_STEP, 0 },
+    { { 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,          /* mov $0xb8b8b8b8, %eax */
+        0xb9, 0xb9, 0xb9, 0xb9, 0xb9,          /* mov $0xb9b9b9b9, %ecx */
+        0xba, 0xba, 0xba, 0xba, 0xba,          /* mov $0xbabababa, %edx */
+        0xcd, 0x2d, 0xc3 },                    /* int $0x2d; ret */
+      17, 0, FALSE, STATUS_BREAKPOINT, 3, { 0xb8b8b8b8, 0xb9b9b9b9, 0xbabababa } },
 };
 
 static int got_exception;
@@ -238,16 +305,16 @@ static void run_exception_test(void *handler, const void* context,
     DWORD oldaccess, oldaccess2;
 
     exc_frame.frame.Handler = handler;
-    exc_frame.frame.Prev = pNtCurrentTeb()->Tib.ExceptionList;
+    exc_frame.frame.Prev = NtCurrentTeb()->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;
+    NtCurrentTeb()->Tib.ExceptionList = &exc_frame.frame;
     func();
-    pNtCurrentTeb()->Tib.ExceptionList = exc_frame.frame.Prev;
+    NtCurrentTeb()->Tib.ExceptionList = exc_frame.frame.Prev;
 
     if(access)
         VirtualProtect(code_mem, code_size, oldaccess, &oldaccess2);
@@ -263,7 +330,7 @@ static LONG CALLBACK rtlraiseexception_vectored_handler(EXCEPTION_POINTERS *Exce
     ok(rec->ExceptionAddress == (char *)code_mem + 0xb, "ExceptionAddress at %p instead of %p\n",
        rec->ExceptionAddress, (char *)code_mem + 0xb);
 
-    if (pNtCurrentTeb()->Peb->BeingDebugged)
+    if (NtCurrentTeb()->Peb->BeingDebugged)
         ok((void *)context->Eax == pRtlRaiseException ||
            broken( is_wow64 && context->Eax == 0xf00f00f1 ), /* broken on vista */
            "debugger managed to modify Eax to %x should be %p\n",
@@ -360,11 +427,11 @@ static void run_rtlraiseexception_test(DWORD exceptioncode)
     record.NumberParameters = 0;
 
     frame.Handler = rtlraiseexception_handler;
-    frame.Prev = pNtCurrentTeb()->Tib.ExceptionList;
+    frame.Prev = NtCurrentTeb()->Tib.ExceptionList;
 
     memcpy(code_mem, call_one_arg_code, sizeof(call_one_arg_code));
 
-    pNtCurrentTeb()->Tib.ExceptionList = &frame;
+    NtCurrentTeb()->Tib.ExceptionList = &frame;
     if (have_vectored_api)
     {
         vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &rtlraiseexception_vectored_handler);
@@ -377,7 +444,7 @@ static void run_rtlraiseexception_test(DWORD exceptioncode)
 
     if (have_vectored_api)
         pRtlRemoveVectoredExceptionHandler(vectored_handler);
-    pNtCurrentTeb()->Tib.ExceptionList = frame.Prev;
+    NtCurrentTeb()->Tib.ExceptionList = frame.Prev;
 }
 
 static void test_rtlraiseexception(void)
@@ -445,37 +512,37 @@ static void test_unwind(void)
 
     /* add first unwind handler */
     frame1->Handler = unwind_handler;
-    frame1->Prev = pNtCurrentTeb()->Tib.ExceptionList;
-    pNtCurrentTeb()->Tib.ExceptionList = frame1;
+    frame1->Prev = NtCurrentTeb()->Tib.ExceptionList;
+    NtCurrentTeb()->Tib.ExceptionList = frame1;
 
     /* add second unwind handler */
     frame2->Handler = unwind_handler;
-    frame2->Prev = pNtCurrentTeb()->Tib.ExceptionList;
-    pNtCurrentTeb()->Tib.ExceptionList = frame2;
+    frame2->Prev = NtCurrentTeb()->Tib.ExceptionList;
+    NtCurrentTeb()->Tib.ExceptionList = frame2;
 
     /* test unwind to current frame */
     unwind_expected_eax = 0xDEAD0000;
     retval = func(pRtlUnwind, frame2, NULL, 0xDEAD0000);
     ok(retval == 0xDEAD0000, "RtlUnwind returned eax %08x instead of %08x\n", retval, 0xDEAD0000);
-    ok(pNtCurrentTeb()->Tib.ExceptionList == frame2, "Exception record points to %p instead of %p\n",
-       pNtCurrentTeb()->Tib.ExceptionList, frame2);
+    ok(NtCurrentTeb()->Tib.ExceptionList == frame2, "Exception record points to %p instead of %p\n",
+       NtCurrentTeb()->Tib.ExceptionList, frame2);
 
     /* unwind to frame1 */
     unwind_expected_eax = 0xDEAD0000;
     retval = func(pRtlUnwind, frame1, NULL, 0xDEAD0000);
     ok(retval == 0xDEAD0001, "RtlUnwind returned eax %08x instead of %08x\n", retval, 0xDEAD0001);
-    ok(pNtCurrentTeb()->Tib.ExceptionList == frame1, "Exception record points to %p instead of %p\n",
-       pNtCurrentTeb()->Tib.ExceptionList, frame1);
+    ok(NtCurrentTeb()->Tib.ExceptionList == frame1, "Exception record points to %p instead of %p\n",
+       NtCurrentTeb()->Tib.ExceptionList, frame1);
 
     /* restore original handler */
-    pNtCurrentTeb()->Tib.ExceptionList = frame1->Prev;
+    NtCurrentTeb()->Tib.ExceptionList = frame1->Prev;
 }
 
 static DWORD handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
                       CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
 {
     const struct exception *except = *(const struct exception **)(frame + 1);
-    unsigned int i, entry = except - exceptions;
+    unsigned int i, parameter_count, entry = except - exceptions;
 
     got_exception++;
     trace( "exception %u: %x flags:%x addr:%p\n",
@@ -484,20 +551,23 @@ static DWORD handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *fram
     ok( rec->ExceptionCode == except->status ||
         (except->alt_status != 0 && rec->ExceptionCode == except->alt_status),
         "%u: Wrong exception code %x/%x\n", entry, rec->ExceptionCode, except->status );
-    ok( rec->ExceptionAddress == (char*)code_mem + except->offset,
-        "%u: Wrong exception address %p/%p\n", entry,
-        rec->ExceptionAddress, (char*)code_mem + except->offset );
-
-    if (except->alt_status == 0 || rec->ExceptionCode != except->alt_status)
-    {
-        ok( rec->NumberParameters == except->nb_params,
-            "%u: Wrong number of parameters %u/%u\n", entry, rec->NumberParameters, except->nb_params );
-    }
+    ok( context->Eip == (DWORD_PTR)code_mem + except->offset,
+        "%u: Unexpected eip %#x/%#lx\n", entry,
+        context->Eip, (DWORD_PTR)code_mem + except->offset );
+    ok( rec->ExceptionAddress == (char*)context->Eip ||
+        (rec->ExceptionCode == STATUS_BREAKPOINT && rec->ExceptionAddress == (char*)context->Eip + 1),
+        "%u: Unexpected exception address %p/%p\n", entry,
+        rec->ExceptionAddress, (char*)context->Eip );
+
+    if (except->status == STATUS_BREAKPOINT && is_wow64)
+        parameter_count = 1;
+    else if (except->alt_status == 0 || rec->ExceptionCode != except->alt_status)
+        parameter_count = except->nb_params;
     else
-    {
-        ok( rec->NumberParameters == except->alt_nb_params,
-            "%u: Wrong number of parameters %u/%u\n", entry, rec->NumberParameters, except->nb_params );
-    }
+        parameter_count = except->alt_nb_params;
+
+    ok( rec->NumberParameters == parameter_count,
+        "%u: Unexpected parameter count %u/%u\n", entry, rec->NumberParameters, parameter_count );
 
     /* Most CPUs (except Intel Core apparently) report a segment limit violation */
     /* instead of page faults for accesses beyond 0xffffffff */
@@ -532,7 +602,7 @@ static DWORD handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *fram
 
 skip_params:
     /* don't handle exception if it's not the address we expected */
-    if (rec->ExceptionAddress != (char*)code_mem + except->offset) return ExceptionContinueSearch;
+    if (context->Eip != (DWORD_PTR)code_mem + except->offset) return ExceptionContinueSearch;
 
     context->Eip += except->length;
     return ExceptionContinueExecution;
@@ -754,12 +824,21 @@ static DWORD int3_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD
 
 static const BYTE int3_code[] = { 0xCC, 0xc3 };  /* int 3, ret */
 
+static DWORD WINAPI hw_reg_exception_thread( void *arg )
+{
+    int expect = (ULONG_PTR)arg;
+    got_exception = 0;
+    run_exception_test( bpx_handler, NULL, dummy_code, sizeof(dummy_code), 0 );
+    ok( got_exception == expect, "expected %u exceptions, got %d\n", expect, got_exception );
+    return 0;
+}
 
 static void test_exceptions(void)
 {
     CONTEXT ctx;
     NTSTATUS res;
     struct dbgreg_test dreg_test;
+    HANDLE h;
 
     if (!pNtGetContextThread || !pNtSetContextThread)
     {
@@ -813,6 +892,33 @@ static void test_exceptions(void)
 
     /* test int3 handling */
     run_exception_test(int3_handler, NULL, int3_code, sizeof(int3_code), 0);
+
+    /* test that hardware breakpoints are not inherited by created threads */
+    res = pNtSetContextThread( GetCurrentThread(), &ctx );
+    ok( res == STATUS_SUCCESS, "NtSetContextThread failed with %x\n", res );
+
+    h = CreateThread( NULL, 0, hw_reg_exception_thread, 0, 0, NULL );
+    WaitForSingleObject( h, 10000 );
+    CloseHandle( h );
+
+    h = CreateThread( NULL, 0, hw_reg_exception_thread, (void *)4, CREATE_SUSPENDED, NULL );
+    ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
+    res = pNtGetContextThread( h, &ctx );
+    ok( res == STATUS_SUCCESS, "NtGetContextThread failed with %x\n", res );
+    ok( ctx.Dr0 == 0, "dr0 %x\n", ctx.Dr0 );
+    ok( ctx.Dr7 == 0, "dr7 %x\n", ctx.Dr7 );
+    ctx.Dr0 = (DWORD)code_mem;
+    ctx.Dr7 = 3;
+    res = pNtSetContextThread( h, &ctx );
+    ok( res == STATUS_SUCCESS, "NtSetContextThread failed with %x\n", res );
+    ResumeThread( h );
+    WaitForSingleObject( h, 10000 );
+    CloseHandle( h );
+
+    ctx.Dr0 = 0;
+    ctx.Dr7 = 0;
+    res = pNtSetContextThread( GetCurrentThread(), &ctx );
+    ok( res == STATUS_SUCCESS, "NtSetContextThread failed with %x\n", res );
 }
 
 static void test_debugger(void)
@@ -855,7 +961,7 @@ static void test_debugger(void)
 
         if (de.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT)
         {
-            if(de.u.CreateProcessInfo.lpBaseOfImage != pNtCurrentTeb()->Peb->ImageBaseAddress)
+            if(de.u.CreateProcessInfo.lpBaseOfImage != NtCurrentTeb()->Peb->ImageBaseAddress)
             {
                 skip("child process loaded at different address, terminating it\n");
                 pNtTerminateProcess(pi.hProcess, 0);
@@ -940,6 +1046,24 @@ static void test_debugger(void)
                     }
                 }
                 else if (stage == 7 || stage == 8)
+                {
+                    ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT,
+                       "expected EXCEPTION_BREAKPOINT, got %08x\n", de.u.Exception.ExceptionRecord.ExceptionCode);
+                    ok((char *)ctx.Eip == (char *)code_mem_address + 0x1d,
+                       "expected Eip = %p, got 0x%x\n", (char *)code_mem_address + 0x1d, ctx.Eip);
+
+                    if (stage == 8) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
+                }
+                else if (stage == 9 || stage == 10)
+                {
+                    ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT,
+                       "expected EXCEPTION_BREAKPOINT, got %08x\n", de.u.Exception.ExceptionRecord.ExceptionCode);
+                    ok((char *)ctx.Eip == (char *)code_mem_address + 2,
+                       "expected Eip = %p, got 0x%x\n", (char *)code_mem_address + 2, ctx.Eip);
+
+                    if (stage == 10) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
+                }
+                else if (stage == 11 || stage == 12)
                 {
                     ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_INVALID_HANDLE,
                        "unexpected exception code %08x, expected %08x\n", de.u.Exception.ExceptionRecord.ExceptionCode,
@@ -947,7 +1071,7 @@ static void test_debugger(void)
                     ok(de.u.Exception.ExceptionRecord.NumberParameters == 0,
                        "unexpected number of parameters %d, expected 0\n", de.u.Exception.ExceptionRecord.NumberParameters);
 
-                    if (stage == 8) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
+                    if (stage == 12) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
                 }
                 else
                     ok(FALSE, "unexpected stage %x\n", stage);
@@ -1035,22 +1159,25 @@ static DWORD simd_fault_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_R
         context->Eip += 3; /* skip addps */
         return ExceptionContinueExecution;
     }
-
-    /* stage 2 - divide by zero fault */
-    if( rec->ExceptionCode == EXCEPTION_ILLEGAL_INSTRUCTION)
-        skip("system doesn't support SIMD exceptions\n");
-    else {
-        ok( rec->ExceptionCode ==  STATUS_FLOAT_MULTIPLE_TRAPS,
-            "exception code: %#x, should be %#x\n",
-            rec->ExceptionCode,  STATUS_FLOAT_MULTIPLE_TRAPS);
-        ok( rec->NumberParameters == 1 || broken(is_wow64 && rec->NumberParameters == 2),
-            "# of params: %i, should be 1\n",
-            rec->NumberParameters);
-        if( rec->NumberParameters == 1 )
-            ok( rec->ExceptionInformation[0] == 0, "param #1: %lx, should be 0\n", rec->ExceptionInformation[0]);
+    else if ( *stage == 2 || *stage == 3 ) {
+        /* stage 2 - divide by zero fault */
+        /* stage 3 - invalid operation fault */
+        if( rec->ExceptionCode == EXCEPTION_ILLEGAL_INSTRUCTION)
+            skip("system doesn't support SIMD exceptions\n");
+        else {
+            ok( rec->ExceptionCode ==  STATUS_FLOAT_MULTIPLE_TRAPS,
+                "exception code: %#x, should be %#x\n",
+                rec->ExceptionCode,  STATUS_FLOAT_MULTIPLE_TRAPS);
+            ok( rec->NumberParameters == 1 || broken(is_wow64 && rec->NumberParameters == 2),
+                "# of params: %i, should be 1\n",
+                rec->NumberParameters);
+            if( rec->NumberParameters == 1 )
+                ok( rec->ExceptionInformation[0] == 0, "param #1: %lx, should be 0\n", rec->ExceptionInformation[0]);
+        }
+        context->Eip += 3; /* skip divps */
     }
-
-    context->Eip += 3; /* skip divps */
+    else
+        ok(FALSE, "unexpected stage %x\n", *stage);
 
     return ExceptionContinueExecution;
 }
@@ -1058,6 +1185,7 @@ static DWORD simd_fault_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_R
 static const BYTE simd_exception_test[] = {
     0x83, 0xec, 0x4,                     /* sub $0x4, %esp       */
     0x0f, 0xae, 0x1c, 0x24,              /* stmxcsr (%esp)       */
+    0x8b, 0x04, 0x24,                    /* mov    (%esp),%eax   * store mxcsr */
     0x66, 0x81, 0x24, 0x24, 0xff, 0xfd,  /* andw $0xfdff,(%esp)  * enable divide by */
     0x0f, 0xae, 0x14, 0x24,              /* ldmxcsr (%esp)       * zero exceptions  */
     0x6a, 0x01,                          /* push   $0x1          */
@@ -1068,7 +1196,22 @@ static const BYTE simd_exception_test[] = {
     0x0f, 0x57, 0xc0,                    /* xorps  %xmm0,%xmm0   * clear divisor  */
     0x0f, 0x5e, 0xc8,                    /* divps  %xmm0,%xmm1   * generate fault */
     0x83, 0xc4, 0x10,                    /* add    $0x10,%esp    */
-    0x66, 0x81, 0x0c, 0x24, 0x00, 0x02,  /* orw    $0x200,(%esp) * disable exceptions */
+    0x89, 0x04, 0x24,                    /* mov    %eax,(%esp)   * restore to old mxcsr */
+    0x0f, 0xae, 0x14, 0x24,              /* ldmxcsr (%esp)       */
+    0x83, 0xc4, 0x04,                    /* add    $0x4,%esp     */
+    0xc3,                                /* ret */
+};
+
+static const BYTE simd_exception_test2[] = {
+    0x83, 0xec, 0x4,                     /* sub $0x4, %esp       */
+    0x0f, 0xae, 0x1c, 0x24,              /* stmxcsr (%esp)       */
+    0x8b, 0x04, 0x24,                    /* mov    (%esp),%eax   * store mxcsr */
+    0x66, 0x81, 0x24, 0x24, 0x7f, 0xff,  /* andw $0xff7f,(%esp)  * enable invalid       */
+    0x0f, 0xae, 0x14, 0x24,              /* ldmxcsr (%esp)       * operation exceptions */
+    0x0f, 0x57, 0xc9,                    /* xorps  %xmm1,%xmm1   * clear dividend */
+    0x0f, 0x57, 0xc0,                    /* xorps  %xmm0,%xmm0   * clear divisor  */
+    0x0f, 0x5e, 0xc8,                    /* divps  %xmm0,%xmm1   * generate fault */
+    0x89, 0x04, 0x24,                    /* mov    %eax,(%esp)   * restore to old mxcsr */
     0x0f, 0xae, 0x14, 0x24,              /* ldmxcsr (%esp)       */
     0x83, 0xc4, 0x04,                    /* add    $0x4,%esp     */
     0xc3,                                /* ret */
@@ -1097,7 +1240,14 @@ static void test_simd_exceptions(void)
     got_exception = 0;
     run_exception_test(simd_fault_handler, &stage, simd_exception_test,
                        sizeof(simd_exception_test), 0);
-    ok( got_exception == 1, "got exception: %i, should be 1\n", got_exception);
+    ok(got_exception == 1, "got exception: %i, should be 1\n", got_exception);
+
+    /* generate a SIMD exception, test FPE_FLTINV */
+    stage = 3;
+    got_exception = 0;
+    run_exception_test(simd_fault_handler, &stage, simd_exception_test2,
+                       sizeof(simd_exception_test2), 0);
+    ok(got_exception == 1, "got exception: %i, should be 1\n", got_exception);
 }
 
 struct fpu_exception_info
@@ -1166,7 +1316,7 @@ static void test_fpu_exceptions(void)
     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 ||
-       broken( is_wow64 && info.exception_offset == info.eip_offset ),
+       broken( info.exception_offset == info.eip_offset ),
        "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);
 
@@ -1175,7 +1325,7 @@ static void test_fpu_exceptions(void)
     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 ||
-       broken( is_wow64 && info.exception_offset == info.eip_offset ),
+       broken( info.exception_offset == info.eip_offset ),
        "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);
 }
@@ -1323,8 +1473,138 @@ static void test_dpe_exceptions(void)
     ok(stat == STATUS_ACCESS_DENIED, "enabling DEP while permanent: status %08x\n", stat);
 }
 
+static void test_thread_context(void)
+{
+    CONTEXT context;
+    NTSTATUS status;
+    struct expected
+    {
+        DWORD Eax, Ebx, Ecx, Edx, Esi, Edi, Ebp, Esp, Eip,
+            SegCs, SegDs, SegEs, SegFs, SegGs, SegSs, EFlags, prev_frame;
+    } expect;
+    NTSTATUS (*func_ptr)( struct expected *res, void *func, void *arg1, void *arg2 ) = (void *)code_mem;
+
+    static const BYTE call_func[] =
+    {
+        0x55,             /* pushl  %ebp */
+        0x89, 0xe5,       /* mov    %esp,%ebp */
+        0x50,             /* pushl  %eax ; add a bit of offset to the stack */
+        0x50,             /* pushl  %eax */
+        0x50,             /* pushl  %eax */
+        0x50,             /* pushl  %eax */
+        0x8b, 0x45, 0x08, /* mov    0x8(%ebp),%eax */
+        0x8f, 0x00,       /* popl   (%eax) */
+        0x89, 0x58, 0x04, /* mov    %ebx,0x4(%eax) */
+        0x89, 0x48, 0x08, /* mov    %ecx,0x8(%eax) */
+        0x89, 0x50, 0x0c, /* mov    %edx,0xc(%eax) */
+        0x89, 0x70, 0x10, /* mov    %esi,0x10(%eax) */
+        0x89, 0x78, 0x14, /* mov    %edi,0x14(%eax) */
+        0x89, 0x68, 0x18, /* mov    %ebp,0x18(%eax) */
+        0x89, 0x60, 0x1c, /* mov    %esp,0x1c(%eax) */
+        0xff, 0x75, 0x04, /* pushl  0x4(%ebp) */
+        0x8f, 0x40, 0x20, /* popl   0x20(%eax) */
+        0x8c, 0x48, 0x24, /* mov    %cs,0x24(%eax) */
+        0x8c, 0x58, 0x28, /* mov    %ds,0x28(%eax) */
+        0x8c, 0x40, 0x2c, /* mov    %es,0x2c(%eax) */
+        0x8c, 0x60, 0x30, /* mov    %fs,0x30(%eax) */
+        0x8c, 0x68, 0x34, /* mov    %gs,0x34(%eax) */
+        0x8c, 0x50, 0x38, /* mov    %ss,0x38(%eax) */
+        0x9c,             /* pushf */
+        0x8f, 0x40, 0x3c, /* popl   0x3c(%eax) */
+        0xff, 0x75, 0x00, /* pushl  0x0(%ebp) ; previous stack frame */
+        0x8f, 0x40, 0x40, /* popl   0x40(%eax) */
+        0x8b, 0x00,       /* mov    (%eax),%eax */
+        0xff, 0x75, 0x14, /* pushl  0x14(%ebp) */
+        0xff, 0x75, 0x10, /* pushl  0x10(%ebp) */
+        0xff, 0x55, 0x0c, /* call   *0xc(%ebp) */
+        0xc9,             /* leave */
+        0xc3,             /* ret */
+    };
+
+    memcpy( func_ptr, call_func, sizeof(call_func) );
+
+#define COMPARE(reg) \
+    ok( context.reg == expect.reg, "wrong " #reg " %08x/%08x\n", context.reg, expect.reg )
+
+    memset( &context, 0xcc, sizeof(context) );
+    memset( &expect, 0xcc, sizeof(expect) );
+    func_ptr( &expect, pRtlCaptureContext, &context, 0 );
+    trace( "expect: eax=%08x ebx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x ebp=%08x esp=%08x "
+           "eip=%08x cs=%04x ds=%04x es=%04x fs=%04x gs=%04x ss=%04x flags=%08x prev=%08x\n",
+           expect.Eax, expect.Ebx, expect.Ecx, expect.Edx, expect.Esi, expect.Edi,
+           expect.Ebp, expect.Esp, expect.Eip, expect.SegCs, expect.SegDs, expect.SegEs,
+           expect.SegFs, expect.SegGs, expect.SegSs, expect.EFlags, expect.prev_frame );
+    trace( "actual: eax=%08x ebx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x ebp=%08x esp=%08x "
+           "eip=%08x cs=%04x ds=%04x es=%04x fs=%04x gs=%04x ss=%04x flags=%08x\n",
+           context.Eax, context.Ebx, context.Ecx, context.Edx, context.Esi, context.Edi,
+           context.Ebp, context.Esp, context.Eip, context.SegCs, context.SegDs, context.SegEs,
+           context.SegFs, context.SegGs, context.SegSs, context.EFlags );
+
+    ok( context.ContextFlags == (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS) ||
+        broken( context.ContextFlags == 0xcccccccc ),  /* <= vista */
+        "wrong flags %08x\n", context.ContextFlags );
+    COMPARE( Eax );
+    COMPARE( Ebx );
+    COMPARE( Ecx );
+    COMPARE( Edx );
+    COMPARE( Esi );
+    COMPARE( Edi );
+    COMPARE( Eip );
+    COMPARE( SegCs );
+    COMPARE( SegDs );
+    COMPARE( SegEs );
+    COMPARE( SegFs );
+    COMPARE( SegGs );
+    COMPARE( SegSs );
+    COMPARE( EFlags );
+    /* Ebp is from the previous stackframe */
+    ok( context.Ebp == expect.prev_frame, "wrong Ebp %08x/%08x\n", context.Ebp, expect.prev_frame );
+    /* Esp is the value on entry to the previous stackframe */
+    ok( context.Esp == expect.Ebp + 8, "wrong Esp %08x/%08x\n", context.Esp, expect.Ebp + 8 );
+
+    memset( &context, 0xcc, sizeof(context) );
+    memset( &expect, 0xcc, sizeof(expect) );
+    context.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS;
+    status = func_ptr( &expect, pNtGetContextThread, (void *)GetCurrentThread(), &context );
+    ok( status == STATUS_SUCCESS, "NtGetContextThread failed %08x\n", status );
+    trace( "expect: eax=%08x ebx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x ebp=%08x esp=%08x "
+           "eip=%08x cs=%04x ds=%04x es=%04x fs=%04x gs=%04x ss=%04x flags=%08x prev=%08x\n",
+           expect.Eax, expect.Ebx, expect.Ecx, expect.Edx, expect.Esi, expect.Edi,
+           expect.Ebp, expect.Esp, expect.Eip, expect.SegCs, expect.SegDs, expect.SegEs,
+           expect.SegFs, expect.SegGs, expect.SegSs, expect.EFlags, expect.prev_frame );
+    trace( "actual: eax=%08x ebx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x ebp=%08x esp=%08x "
+           "eip=%08x cs=%04x ds=%04x es=%04x fs=%04x gs=%04x ss=%04x flags=%08x\n",
+           context.Eax, context.Ebx, context.Ecx, context.Edx, context.Esi, context.Edi,
+           context.Ebp, context.Esp, context.Eip, context.SegCs, context.SegDs, context.SegEs,
+           context.SegFs, context.SegGs, context.SegSs, context.EFlags );
+    /* Eax, Ecx, Edx, EFlags are not preserved */
+    COMPARE( Ebx );
+    COMPARE( Esi );
+    COMPARE( Edi );
+    COMPARE( Ebp );
+    /* Esp is the stack upon entry to NtGetContextThread */
+    ok( context.Esp == expect.Esp - 12 || context.Esp == expect.Esp - 16,
+        "wrong Esp %08x/%08x\n", context.Esp, expect.Esp );
+    /* Eip is somewhere close to the NtGetContextThread implementation */
+    ok( (char *)context.Eip >= (char *)pNtGetContextThread - 0x10000 &&
+        (char *)context.Eip <= (char *)pNtGetContextThread + 0x10000,
+        "wrong Eip %08x/%08x\n", context.Eip, (DWORD)pNtGetContextThread );
+    ok( *(WORD *)context.Eip == 0xc483 || *(WORD *)context.Eip == 0x08c2 || *(WORD *)context.Eip == 0x8dc3,
+        "expected 0xc483 or 0x08c2 or 0x8dc3, got %04x\n", *(WORD *)context.Eip );
+    /* segment registers clear the high word */
+    ok( context.SegCs == LOWORD(expect.SegCs), "wrong SegCs %08x/%08x\n", context.SegCs, expect.SegCs );
+    ok( context.SegDs == LOWORD(expect.SegDs), "wrong SegDs %08x/%08x\n", context.SegDs, expect.SegDs );
+    ok( context.SegEs == LOWORD(expect.SegEs), "wrong SegEs %08x/%08x\n", context.SegEs, expect.SegEs );
+    ok( context.SegFs == LOWORD(expect.SegFs), "wrong SegFs %08x/%08x\n", context.SegFs, expect.SegFs );
+    ok( context.SegGs == LOWORD(expect.SegGs), "wrong SegGs %08x/%08x\n", context.SegGs, expect.SegGs );
+    ok( context.SegSs == LOWORD(expect.SegSs), "wrong SegSs %08x/%08x\n", context.SegSs, expect.SegGs );
+#undef COMPARE
+}
+
 #elif defined(__x86_64__)
 
+#define is_wow64 0
+
 #define UNW_FLAG_NHANDLER  0
 #define UNW_FLAG_EHANDLER  1
 #define UNW_FLAG_UHANDLER  2
@@ -1598,6 +1878,131 @@ static void test_virtual_unwind(void)
         call_virtual_unwind( i, &tests[i] );
 }
 
+static int consolidate_dummy_called;
+static PVOID CALLBACK test_consolidate_dummy(EXCEPTION_RECORD *rec)
+{
+    CONTEXT *ctx = (CONTEXT *)rec->ExceptionInformation[1];
+    consolidate_dummy_called = 1;
+    ok(ctx->Rip == 0xdeadbeef, "test_consolidate_dummy failed for Rip, expected: 0xdeadbeef, got: %lx\n", ctx->Rip);
+    return (PVOID)rec->ExceptionInformation[2];
+}
+
+static void test_restore_context(void)
+{
+    SETJMP_FLOAT128 *fltsave;
+    EXCEPTION_RECORD rec;
+    _JUMP_BUFFER buf;
+    CONTEXT ctx;
+    int i, pass;
+
+    if (!pRtlUnwindEx || !pRtlRestoreContext || !pRtlCaptureContext || !p_setjmp)
+    {
+        skip("RtlUnwindEx/RtlCaptureContext/RtlRestoreContext/_setjmp not found\n");
+        return;
+    }
+
+    /* RtlRestoreContext(NULL, NULL); crashes on Windows */
+
+    /* test simple case of capture and restore context */
+    pass = 0;
+    InterlockedIncrement(&pass); /* interlocked to prevent compiler from moving after capture */
+    pRtlCaptureContext(&ctx);
+    if (InterlockedIncrement(&pass) == 2) /* interlocked to prevent compiler from moving before capture */
+    {
+        pRtlRestoreContext(&ctx, NULL);
+        ok(0, "shouldn't be reached\n");
+    }
+    else
+        ok(pass < 4, "unexpected pass %d\n", pass);
+
+    /* test with jmp using RltRestoreContext */
+    pass = 0;
+    InterlockedIncrement(&pass);
+    RtlCaptureContext(&ctx);
+    InterlockedIncrement(&pass); /* only called once */
+    p_setjmp(&buf);
+    InterlockedIncrement(&pass);
+    if (pass == 3)
+    {
+        rec.ExceptionCode = STATUS_LONGJUMP;
+        rec.NumberParameters = 1;
+        rec.ExceptionInformation[0] = (DWORD64)&buf;
+
+        /* uses buf.Rip instead of ctx.Rip */
+        pRtlRestoreContext(&ctx, &rec);
+        ok(0, "shouldn't be reached\n");
+    }
+    else if (pass == 4)
+    {
+        ok(buf.Rbx == ctx.Rbx, "longjmp failed for Rbx, expected: %lx, got: %lx\n", buf.Rbx, ctx.Rbx);
+        ok(buf.Rsp == ctx.Rsp, "longjmp failed for Rsp, expected: %lx, got: %lx\n", buf.Rsp, ctx.Rsp);
+        ok(buf.Rbp == ctx.Rbp, "longjmp failed for Rbp, expected: %lx, got: %lx\n", buf.Rbp, ctx.Rbp);
+        ok(buf.Rsi == ctx.Rsi, "longjmp failed for Rsi, expected: %lx, got: %lx\n", buf.Rsi, ctx.Rsi);
+        ok(buf.Rdi == ctx.Rdi, "longjmp failed for Rdi, expected: %lx, got: %lx\n", buf.Rdi, ctx.Rdi);
+        ok(buf.R12 == ctx.R12, "longjmp failed for R12, expected: %lx, got: %lx\n", buf.R12, ctx.R12);
+        ok(buf.R13 == ctx.R13, "longjmp failed for R13, expected: %lx, got: %lx\n", buf.R13, ctx.R13);
+        ok(buf.R14 == ctx.R14, "longjmp failed for R14, expected: %lx, got: %lx\n", buf.R14, ctx.R14);
+        ok(buf.R15 == ctx.R15, "longjmp failed for R15, expected: %lx, got: %lx\n", buf.R15, ctx.R15);
+
+        fltsave = &buf.Xmm6;
+        for (i = 0; i < 10; i++)
+        {
+            ok(fltsave[i].Part[0] == ctx.u.FltSave.XmmRegisters[i + 6].Low,
+                "longjmp failed for Xmm%d, expected %lx, got %lx\n", i + 6,
+                fltsave[i].Part[0], ctx.u.FltSave.XmmRegisters[i + 6].Low);
+
+            ok(fltsave[i].Part[1] == ctx.u.FltSave.XmmRegisters[i + 6].High,
+                "longjmp failed for Xmm%d, expected %lx, got %lx\n", i + 6,
+                fltsave[i].Part[1], ctx.u.FltSave.XmmRegisters[i + 6].High);
+        }
+    }
+    else
+        ok(0, "unexpected pass %d\n", pass);
+
+    /* test with jmp through RtlUnwindEx */
+    pass = 0;
+    InterlockedIncrement(&pass);
+    pRtlCaptureContext(&ctx);
+    InterlockedIncrement(&pass); /* only called once */
+    p_setjmp(&buf);
+    InterlockedIncrement(&pass);
+    if (pass == 3)
+    {
+        rec.ExceptionCode = STATUS_LONGJUMP;
+        rec.NumberParameters = 1;
+        rec.ExceptionInformation[0] = (DWORD64)&buf;
+
+        /* uses buf.Rip instead of bogus 0xdeadbeef */
+        pRtlUnwindEx((void*)buf.Rsp, (void*)0xdeadbeef, &rec, NULL, &ctx, NULL);
+        ok(0, "shouldn't be reached\n");
+    }
+    else
+        ok(pass == 4, "unexpected pass %d\n", pass);
+
+
+    /* test with consolidate */
+    pass = 0;
+    InterlockedIncrement(&pass);
+    RtlCaptureContext(&ctx);
+    InterlockedIncrement(&pass);
+    if (pass == 2)
+    {
+        rec.ExceptionCode = STATUS_UNWIND_CONSOLIDATE;
+        rec.NumberParameters = 3;
+        rec.ExceptionInformation[0] = (DWORD64)test_consolidate_dummy;
+        rec.ExceptionInformation[1] = (DWORD64)&ctx;
+        rec.ExceptionInformation[2] = ctx.Rip;
+        ctx.Rip = 0xdeadbeef;
+
+        pRtlRestoreContext(&ctx, &rec);
+        ok(0, "shouldn't be reached\n");
+    }
+    else if (pass == 3)
+        ok(consolidate_dummy_called, "test_consolidate_dummy not called\n");
+    else
+        ok(0, "unexpected pass %d\n", pass);
+}
+
 static RUNTIME_FUNCTION* CALLBACK dynamic_unwind_callback( DWORD64 pc, PVOID context )
 {
     static const int code_offset = 1024;
@@ -1714,9 +2119,155 @@ static void test_dynamic_unwind(void)
 
 }
 
+static int termination_handler_called;
+static void WINAPI termination_handler(ULONG flags, ULONG64 frame)
+{
+    termination_handler_called++;
+
+    ok(flags == 1 || broken(flags == 0x401), "flags = %x\n", flags);
+    ok(frame == 0x1234, "frame = %p\n", (void*)frame);
+}
+
+static void test___C_specific_handler(void)
+{
+    DISPATCHER_CONTEXT dispatch;
+    EXCEPTION_RECORD rec;
+    CONTEXT context;
+    ULONG64 frame;
+    EXCEPTION_DISPOSITION ret;
+    SCOPE_TABLE scope_table;
+
+    if (!p__C_specific_handler)
+    {
+        win_skip("__C_specific_handler not available\n");
+        return;
+    }
+
+    memset(&rec, 0, sizeof(rec));
+    rec.ExceptionFlags = 2; /* EH_UNWINDING */
+    frame = 0x1234;
+    memset(&dispatch, 0, sizeof(dispatch));
+    dispatch.ImageBase = (ULONG_PTR)GetModuleHandleA(NULL);
+    dispatch.ControlPc = dispatch.ImageBase + 0x200;
+    dispatch.HandlerData = &scope_table;
+    dispatch.ContextRecord = &context;
+    scope_table.Count = 1;
+    scope_table.ScopeRecord[0].BeginAddress = 0x200;
+    scope_table.ScopeRecord[0].EndAddress = 0x400;
+    scope_table.ScopeRecord[0].HandlerAddress = (ULONG_PTR)termination_handler-dispatch.ImageBase;
+    scope_table.ScopeRecord[0].JumpTarget = 0;
+    memset(&context, 0, sizeof(context));
+
+    termination_handler_called = 0;
+    ret = p__C_specific_handler(&rec, frame, &context, &dispatch);
+    ok(ret == ExceptionContinueSearch, "__C_specific_handler returned %x\n", ret);
+    ok(termination_handler_called == 1, "termination_handler_called = %d\n",
+            termination_handler_called);
+    ok(dispatch.ScopeIndex == 1, "dispatch.ScopeIndex = %d\n", dispatch.ScopeIndex);
+
+    ret = p__C_specific_handler(&rec, frame, &context, &dispatch);
+    ok(ret == ExceptionContinueSearch, "__C_specific_handler returned %x\n", ret);
+    ok(termination_handler_called == 1, "termination_handler_called = %d\n",
+            termination_handler_called);
+    ok(dispatch.ScopeIndex == 1, "dispatch.ScopeIndex = %d\n", dispatch.ScopeIndex);
+}
+
 #endif  /* __x86_64__ */
 
 #if defined(__i386__) || defined(__x86_64__)
+
+static DWORD WINAPI register_check_thread(void *arg)
+{
+    NTSTATUS status;
+    CONTEXT ctx;
+
+    memset(&ctx, 0, sizeof(ctx));
+    ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
+
+    status = pNtGetContextThread(GetCurrentThread(), &ctx);
+    ok(status == STATUS_SUCCESS, "NtGetContextThread failed with %x\n", status);
+    ok(!ctx.Dr0, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr0);
+    ok(!ctx.Dr1, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr1);
+    ok(!ctx.Dr2, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr2);
+    ok(!ctx.Dr3, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr3);
+    ok(!ctx.Dr6, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr6);
+    ok(!ctx.Dr7, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr7);
+
+    return 0;
+}
+
+static void test_debug_registers(void)
+{
+    static const struct
+    {
+        ULONG_PTR dr0, dr1, dr2, dr3, dr6, dr7;
+    }
+    tests[] =
+    {
+        { 0x42424240, 0, 0x126bb070, 0x0badbad0, 0, 0xffff0115 },
+        { 0x42424242, 0, 0x100f0fe7, 0x0abebabe, 0, 0x115 },
+    };
+    NTSTATUS status;
+    CONTEXT ctx;
+    HANDLE thread;
+    int i;
+
+    for (i = 0; i < sizeof(tests)/sizeof(tests[0]); i++)
+    {
+        memset(&ctx, 0, sizeof(ctx));
+        ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
+        ctx.Dr0 = tests[i].dr0;
+        ctx.Dr1 = tests[i].dr1;
+        ctx.Dr2 = tests[i].dr2;
+        ctx.Dr3 = tests[i].dr3;
+        ctx.Dr6 = tests[i].dr6;
+        ctx.Dr7 = tests[i].dr7;
+
+        status = pNtSetContextThread(GetCurrentThread(), &ctx);
+        ok(status == STATUS_SUCCESS, "NtGetContextThread failed with %08x\n", status);
+
+        memset(&ctx, 0, sizeof(ctx));
+        ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
+
+        status = pNtGetContextThread(GetCurrentThread(), &ctx);
+        ok(status == STATUS_SUCCESS, "NtGetContextThread failed with %08x\n", status);
+        ok(ctx.Dr0 == tests[i].dr0, "test %d: expected %lx, got %lx\n", i, tests[i].dr0, (DWORD_PTR)ctx.Dr0);
+        ok(ctx.Dr1 == tests[i].dr1, "test %d: expected %lx, got %lx\n", i, tests[i].dr1, (DWORD_PTR)ctx.Dr1);
+        ok(ctx.Dr2 == tests[i].dr2, "test %d: expected %lx, got %lx\n", i, tests[i].dr2, (DWORD_PTR)ctx.Dr2);
+        ok(ctx.Dr3 == tests[i].dr3, "test %d: expected %lx, got %lx\n", i, tests[i].dr3, (DWORD_PTR)ctx.Dr3);
+        ok((ctx.Dr6 &  0xf00f) == tests[i].dr6, "test %d: expected %lx, got %lx\n", i, tests[i].dr6, (DWORD_PTR)ctx.Dr6);
+        ok((ctx.Dr7 & ~0xdc00) == tests[i].dr7, "test %d: expected %lx, got %lx\n", i, tests[i].dr7, (DWORD_PTR)ctx.Dr7);
+    }
+
+    memset(&ctx, 0, sizeof(ctx));
+    ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
+    ctx.Dr0 = 0xffffffff;
+    ctx.Dr1 = 0xffffffff;
+    ctx.Dr2 = 0xffffffff;
+    ctx.Dr3 = 0xffffffff;
+    ctx.Dr6 = 0xffffffff;
+    ctx.Dr7 = 0x00000400;
+    status = pNtSetContextThread(GetCurrentThread(), &ctx);
+    ok(status == STATUS_SUCCESS, "NtSetContextThread failed with %x\n", status);
+
+    thread = CreateThread(NULL, 0, register_check_thread, NULL, CREATE_SUSPENDED, NULL);
+    ok(thread != INVALID_HANDLE_VALUE, "CreateThread failed with %d\n", GetLastError());
+
+    ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
+    status = pNtGetContextThread(thread, &ctx);
+    ok(status == STATUS_SUCCESS, "NtGetContextThread failed with %x\n", status);
+    ok(!ctx.Dr0, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr0);
+    ok(!ctx.Dr1, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr1);
+    ok(!ctx.Dr2, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr2);
+    ok(!ctx.Dr3, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr3);
+    ok(!ctx.Dr6, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr6);
+    ok(!ctx.Dr7, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr7);
+
+    ResumeThread(thread);
+    WaitForSingleObject(thread, 10000);
+    CloseHandle(thread);
+}
+
 static DWORD outputdebugstring_exceptions;
 
 static LONG CALLBACK outputdebugstring_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo)
@@ -1750,6 +2301,7 @@ static void test_outputdebugstring(DWORD numexc)
 
     outputdebugstring_exceptions = 0;
     OutputDebugStringA("Hello World");
+
     ok(outputdebugstring_exceptions == numexc, "OutputDebugStringA generated %d exceptions, expected %d\n",
        outputdebugstring_exceptions, numexc);
 
@@ -1805,6 +2357,236 @@ static void test_ripevent(DWORD numexc)
     pRtlRemoveVectoredExceptionHandler(vectored_handler);
 }
 
+static DWORD debug_service_exceptions;
+
+static LONG CALLBACK debug_service_handler(EXCEPTION_POINTERS *ExceptionInfo)
+{
+    EXCEPTION_RECORD *rec = ExceptionInfo->ExceptionRecord;
+    trace("vect. handler %08x addr:%p\n", rec->ExceptionCode, rec->ExceptionAddress);
+
+    ok(rec->ExceptionCode == EXCEPTION_BREAKPOINT, "ExceptionCode is %08x instead of %08x\n",
+       rec->ExceptionCode, EXCEPTION_BREAKPOINT);
+
+#ifdef __i386__
+    ok(ExceptionInfo->ContextRecord->Eip == (DWORD)code_mem + 0x1c,
+       "expected Eip = %x, got %x\n", (DWORD)code_mem + 0x1c, ExceptionInfo->ContextRecord->Eip);
+    ok(rec->NumberParameters == (is_wow64 ? 1 : 3),
+       "ExceptionParameters is %d instead of %d\n", rec->NumberParameters, is_wow64 ? 1 : 3);
+    ok(rec->ExceptionInformation[0] == ExceptionInfo->ContextRecord->Eax,
+       "expected ExceptionInformation[0] = %x, got %lx\n",
+       ExceptionInfo->ContextRecord->Eax, rec->ExceptionInformation[0]);
+    if (!is_wow64)
+    {
+        ok(rec->ExceptionInformation[1] == 0x11111111,
+           "got ExceptionInformation[1] = %lx\n", rec->ExceptionInformation[1]);
+        ok(rec->ExceptionInformation[2] == 0x22222222,
+           "got ExceptionInformation[2] = %lx\n", rec->ExceptionInformation[2]);
+    }
+#else
+    ok(ExceptionInfo->ContextRecord->Rip == (DWORD_PTR)code_mem + 0x2f,
+       "expected Rip = %lx, got %lx\n", (DWORD_PTR)code_mem + 0x2f, ExceptionInfo->ContextRecord->Rip);
+    ok(rec->NumberParameters == 1,
+       "ExceptionParameters is %d instead of 1\n", rec->NumberParameters);
+    ok(rec->ExceptionInformation[0] == ExceptionInfo->ContextRecord->Rax,
+       "expected ExceptionInformation[0] = %lx, got %lx\n",
+       ExceptionInfo->ContextRecord->Rax, rec->ExceptionInformation[0]);
+#endif
+
+    debug_service_exceptions++;
+    return (rec->ExceptionCode == EXCEPTION_BREAKPOINT) ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_CONTINUE_SEARCH;
+}
+
+#ifdef __i386__
+
+static const BYTE call_debug_service_code[] = {
+    0x53,                         /* pushl %ebx */
+    0x57,                         /* pushl %edi */
+    0x8b, 0x44, 0x24, 0x0c,       /* movl 12(%esp),%eax */
+    0xb9, 0x11, 0x11, 0x11, 0x11, /* movl $0x11111111,%ecx */
+    0xba, 0x22, 0x22, 0x22, 0x22, /* movl $0x22222222,%edx */
+    0xbb, 0x33, 0x33, 0x33, 0x33, /* movl $0x33333333,%ebx */
+    0xbf, 0x44, 0x44, 0x44, 0x44, /* movl $0x44444444,%edi */
+    0xcd, 0x2d,                   /* int $0x2d */
+    0xeb,                         /* jmp $+17 */
+    0x0f, 0x1f, 0x00,             /* nop */
+    0x31, 0xc0,                   /* xorl %eax,%eax */
+    0xeb, 0x0c,                   /* jmp $+14 */
+    0x90, 0x90, 0x90, 0x90,       /* nop */
+    0x90, 0x90, 0x90, 0x90,
+    0x90,
+    0x31, 0xc0,                   /* xorl %eax,%eax */
+    0x40,                         /* incl %eax */
+    0x5f,                         /* popl %edi */
+    0x5b,                         /* popl %ebx */
+    0xc3,                         /* ret */
+};
+
+#else
+
+static const BYTE call_debug_service_code[] = {
+    0x53,                         /* push %rbx */
+    0x57,                         /* push %rdi */
+    0x48, 0x89, 0xc8,             /* movl %rcx,%rax */
+    0x48, 0xb9, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, /* movabs $0x1111111111111111,%rcx */
+    0x48, 0xba, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, /* movabs $0x2222222222222222,%rdx */
+    0x48, 0xbb, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, /* movabs $0x3333333333333333,%rbx */
+    0x48, 0xbf, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, /* movabs $0x4444444444444444,%rdi */
+    0xcd, 0x2d,                   /* int $0x2d */
+    0xeb,                         /* jmp $+17 */
+    0x0f, 0x1f, 0x00,             /* nop */
+    0x48, 0x31, 0xc0,             /* xor %rax,%rax */
+    0xeb, 0x0e,                   /* jmp $+16 */
+    0x90, 0x90, 0x90, 0x90,       /* nop */
+    0x90, 0x90, 0x90, 0x90,
+    0x48, 0x31, 0xc0,             /* xor %rax,%rax */
+    0x48, 0xff, 0xc0,             /* inc %rax */
+    0x5f,                         /* pop %rdi */
+    0x5b,                         /* pop %rbx */
+    0xc3,                         /* ret */
+};
+
+#endif
+
+static void test_debug_service(DWORD numexc)
+{
+    DWORD (CDECL *func)(DWORD_PTR) = code_mem;
+    DWORD expected_exc, expected_ret;
+    void *vectored_handler;
+    DWORD ret;
+
+    /* code will return 0 if execution resumes immediately after "int $0x2d", otherwise 1 */
+    memcpy(code_mem, call_debug_service_code, sizeof(call_debug_service_code));
+
+    vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &debug_service_handler);
+    ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n");
+
+    expected_exc = numexc;
+    expected_ret = (numexc != 0);
+
+    /* BREAKPOINT_BREAK */
+    debug_service_exceptions = 0;
+    ret = func(0);
+    ok(debug_service_exceptions == expected_exc,
+       "BREAKPOINT_BREAK generated %u exceptions, expected %u\n",
+       debug_service_exceptions, expected_exc);
+    ok(ret == expected_ret,
+       "BREAKPOINT_BREAK returned %u, expected %u\n", ret, expected_ret);
+
+    /* BREAKPOINT_PROMPT */
+    debug_service_exceptions = 0;
+    ret = func(2);
+    ok(debug_service_exceptions == expected_exc,
+       "BREAKPOINT_PROMPT generated %u exceptions, expected %u\n",
+       debug_service_exceptions, expected_exc);
+    ok(ret == expected_ret,
+       "BREAKPOINT_PROMPT returned %u, expected %u\n", ret, expected_ret);
+
+    /* invalid debug service */
+    debug_service_exceptions = 0;
+    ret = func(6);
+    ok(debug_service_exceptions == expected_exc,
+       "invalid debug service generated %u exceptions, expected %u\n",
+       debug_service_exceptions, expected_exc);
+    ok(ret == expected_ret,
+      "invalid debug service returned %u, expected %u\n", ret, expected_ret);
+
+    expected_exc = (is_wow64 ? numexc : 0);
+    expected_ret = (is_wow64 && numexc);
+
+    /* BREAKPOINT_PRINT */
+    debug_service_exceptions = 0;
+    ret = func(1);
+    ok(debug_service_exceptions == expected_exc,
+       "BREAKPOINT_PRINT generated %u exceptions, expected %u\n",
+       debug_service_exceptions, expected_exc);
+    ok(ret == expected_ret,
+       "BREAKPOINT_PRINT returned %u, expected %u\n", ret, expected_ret);
+
+    /* BREAKPOINT_LOAD_SYMBOLS */
+    debug_service_exceptions = 0;
+    ret = func(3);
+    ok(debug_service_exceptions == expected_exc,
+       "BREAKPOINT_LOAD_SYMBOLS generated %u exceptions, expected %u\n",
+       debug_service_exceptions, expected_exc);
+    ok(ret == expected_ret,
+       "BREAKPOINT_LOAD_SYMBOLS returned %u, expected %u\n", ret, expected_ret);
+
+    /* BREAKPOINT_UNLOAD_SYMBOLS */
+    debug_service_exceptions = 0;
+    ret = func(4);
+    ok(debug_service_exceptions == expected_exc,
+       "BREAKPOINT_UNLOAD_SYMBOLS generated %u exceptions, expected %u\n",
+       debug_service_exceptions, expected_exc);
+    ok(ret == expected_ret,
+       "BREAKPOINT_UNLOAD_SYMBOLS returned %u, expected %u\n", ret, expected_ret);
+
+    /* BREAKPOINT_COMMAND_STRING */
+    debug_service_exceptions = 0;
+    ret = func(5);
+    ok(debug_service_exceptions == expected_exc || broken(debug_service_exceptions == numexc),
+       "BREAKPOINT_COMMAND_STRING generated %u exceptions, expected %u\n",
+       debug_service_exceptions, expected_exc);
+    ok(ret == expected_ret || broken(ret == (numexc != 0)),
+       "BREAKPOINT_COMMAND_STRING returned %u, expected %u\n", ret, expected_ret);
+
+    pRtlRemoveVectoredExceptionHandler(vectored_handler);
+}
+
+static DWORD breakpoint_exceptions;
+
+static LONG CALLBACK breakpoint_handler(EXCEPTION_POINTERS *ExceptionInfo)
+{
+    EXCEPTION_RECORD *rec = ExceptionInfo->ExceptionRecord;
+    trace("vect. handler %08x addr:%p\n", rec->ExceptionCode, rec->ExceptionAddress);
+
+    ok(rec->ExceptionCode == EXCEPTION_BREAKPOINT, "ExceptionCode is %08x instead of %08x\n",
+       rec->ExceptionCode, EXCEPTION_BREAKPOINT);
+
+#ifdef __i386__
+    ok(ExceptionInfo->ContextRecord->Eip == (DWORD)code_mem + 1,
+       "expected Eip = %x, got %x\n", (DWORD)code_mem + 1, ExceptionInfo->ContextRecord->Eip);
+    ok(rec->NumberParameters == (is_wow64 ? 1 : 3),
+       "ExceptionParameters is %d instead of %d\n", rec->NumberParameters, is_wow64 ? 1 : 3);
+    ok(rec->ExceptionInformation[0] == 0,
+       "got ExceptionInformation[0] = %lx\n", rec->ExceptionInformation[0]);
+    ExceptionInfo->ContextRecord->Eip = (DWORD)code_mem + 2;
+#else
+    ok(ExceptionInfo->ContextRecord->Rip == (DWORD_PTR)code_mem + 1,
+       "expected Rip = %lx, got %lx\n", (DWORD_PTR)code_mem + 1, ExceptionInfo->ContextRecord->Rip);
+    ok(rec->NumberParameters == 1,
+       "ExceptionParameters is %d instead of 1\n", rec->NumberParameters);
+    ok(rec->ExceptionInformation[0] == 0,
+       "got ExceptionInformation[0] = %lx\n", rec->ExceptionInformation[0]);
+    ExceptionInfo->ContextRecord->Rip = (DWORD_PTR)code_mem + 2;
+#endif
+
+    breakpoint_exceptions++;
+    return (rec->ExceptionCode == EXCEPTION_BREAKPOINT) ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_CONTINUE_SEARCH;
+}
+
+static const BYTE breakpoint_code[] = {
+    0xcd, 0x03,                   /* int $0x3 */
+    0xc3,                         /* ret */
+};
+
+static void test_breakpoint(DWORD numexc)
+{
+    DWORD (CDECL *func)(void) = code_mem;
+    void *vectored_handler;
+
+    memcpy(code_mem, breakpoint_code, sizeof(breakpoint_code));
+
+    vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &breakpoint_handler);
+    ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n");
+
+    breakpoint_exceptions = 0;
+    func();
+    ok(breakpoint_exceptions == numexc, "int $0x3 generated %u exceptions, expected %u\n",
+       breakpoint_exceptions, numexc);
+
+    pRtlRemoveVectoredExceptionHandler(vectored_handler);
+}
+
 static DWORD invalid_handle_exceptions;
 
 static LONG CALLBACK invalid_handle_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo)
@@ -1893,6 +2675,10 @@ static void test_vectored_continue_handler(void)
 START_TEST(exception)
 {
     HMODULE hntdll = GetModuleHandleA("ntdll.dll");
+#if defined(__x86_64__)
+    HMODULE hmsvcrt = LoadLibraryA("msvcrt.dll");
+#endif
+
 #ifdef __REACTOS__
     if (!winetest_interactive &&
         !strcmp(winetest_platform, "windows"))
@@ -1907,13 +2693,13 @@ START_TEST(exception)
         return;
     }
 
-    pNtCurrentTeb        = (void *)GetProcAddress( hntdll, "NtCurrentTeb" );
     pNtGetContextThread  = (void *)GetProcAddress( hntdll, "NtGetContextThread" );
     pNtSetContextThread  = (void *)GetProcAddress( hntdll, "NtSetContextThread" );
     pNtReadVirtualMemory = (void *)GetProcAddress( hntdll, "NtReadVirtualMemory" );
     pNtClose             = (void *)GetProcAddress( hntdll, "NtClose" );
     pRtlUnwind           = (void *)GetProcAddress( hntdll, "RtlUnwind" );
     pRtlRaiseException   = (void *)GetProcAddress( hntdll, "RtlRaiseException" );
+    pRtlCaptureContext   = (void *)GetProcAddress( hntdll, "RtlCaptureContext" );
     pNtTerminateProcess  = (void *)GetProcAddress( hntdll, "NtTerminateProcess" );
     pRtlAddVectoredExceptionHandler    = (void *)GetProcAddress( hntdll,
                                                                  "RtlAddVectoredExceptionHandler" );
@@ -1930,11 +2716,6 @@ START_TEST(exception)
     pIsWow64Process = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "IsWow64Process");
 
 #ifdef __i386__
-    if (!pNtCurrentTeb)
-    {
-        skip( "NtCurrentTeb not found\n" );
-        return;
-    }
     if (!pIsWow64Process || !pIsWow64Process( GetCurrentProcess(), &is_wow64 )) is_wow64 = FALSE;
 
     if (pRtlAddVectoredExceptionHandler && pRtlRemoveVectoredExceptionHandler)
@@ -1955,7 +2736,7 @@ START_TEST(exception)
         }
 
         /* child must be run under a debugger */
-        if (!pNtCurrentTeb()->Peb->BeingDebugged)
+        if (!NtCurrentTeb()->Peb->BeingDebugged)
         {
             ok(FALSE, "child process not being debugged?\n");
             return;
@@ -1980,8 +2761,16 @@ START_TEST(exception)
             test_stage = 6;
             test_ripevent(1);
             test_stage = 7;
-            test_closehandle(0);
+            test_debug_service(0);
             test_stage = 8;
+            test_debug_service(1);
+            test_stage = 9;
+            test_breakpoint(0);
+            test_stage = 10;
+            test_breakpoint(1);
+            test_stage = 11;
+            test_closehandle(0);
+            test_stage = 12;
             test_closehandle(1);
         }
         else
@@ -1994,8 +2783,11 @@ START_TEST(exception)
     test_unwind();
     test_exceptions();
     test_rtlraiseexception();
+    test_debug_registers();
     test_outputdebugstring(1);
     test_ripevent(1);
+    test_debug_service(1);
+    test_breakpoint(1);
     test_closehandle(0);
     test_vectored_continue_handler();
     test_debugger();
@@ -2003,6 +2795,7 @@ START_TEST(exception)
     test_fpu_exceptions();
     test_dpe_exceptions();
     test_prot_fault();
+    test_thread_context();
 
 #elif defined(__x86_64__)
     pRtlAddFunctionTable               = (void *)GetProcAddress( hntdll,
@@ -2013,12 +2806,27 @@ START_TEST(exception)
                                                                  "RtlInstallFunctionTableCallback" );
     pRtlLookupFunctionEntry            = (void *)GetProcAddress( hntdll,
                                                                  "RtlLookupFunctionEntry" );
-
+    p__C_specific_handler              = (void *)GetProcAddress( hntdll,
+                                                                 "__C_specific_handler" );
+    pRtlCaptureContext                 = (void *)GetProcAddress( hntdll,
+                                                                 "RtlCaptureContext" );
+    pRtlRestoreContext                 = (void *)GetProcAddress( hntdll,
+                                                                 "RtlRestoreContext" );
+    pRtlUnwindEx                       = (void *)GetProcAddress( hntdll,
+                                                                 "RtlUnwindEx" );
+    p_setjmp                           = (void *)GetProcAddress( hmsvcrt,
+                                                                 "_setjmp" );
+
+    test_debug_registers();
     test_outputdebugstring(1);
     test_ripevent(1);
+    test_debug_service(1);
+    test_breakpoint(1);
     test_closehandle(0);
     test_vectored_continue_handler();
     test_virtual_unwind();
+    test___C_specific_handler();
+    test_restore_context();
 
     if (pRtlAddFunctionTable && pRtlDeleteFunctionTable && pRtlInstallFunctionTableCallback && pRtlLookupFunctionEntry)
       test_dynamic_unwind();
@@ -2027,5 +2835,5 @@ START_TEST(exception)
 
 #endif
 
-    VirtualFree(code_mem, 0, MEM_FREE);
+    VirtualFree(code_mem, 0, MEM_RELEASE);
 }
index 0387398..3316399 100644 (file)
@@ -94,6 +94,7 @@ static NTSTATUS (WINAPI *pNtWriteFile)(HANDLE hFile, HANDLE hEvent,
 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 *pNtFsControlFile) (HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, PVOID apc_context, PIO_STATUS_BLOCK io, ULONG code, PVOID in_buffer, ULONG in_size, PVOID out_buffer, ULONG out_size);
 
 static NTSTATUS (WINAPI *pNtCreateIoCompletion)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, ULONG);
 static NTSTATUS (WINAPI *pNtOpenIoCompletion)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES);
@@ -106,6 +107,7 @@ static NTSTATUS (WINAPI *pNtQueryDirectoryFile)(HANDLE,HANDLE,PIO_APC_ROUTINE,PV
                                                 PVOID,ULONG,FILE_INFORMATION_CLASS,BOOLEAN,PUNICODE_STRING,BOOLEAN);
 static NTSTATUS (WINAPI *pNtQueryVolumeInformationFile)(HANDLE,PIO_STATUS_BLOCK,PVOID,ULONG,FS_INFORMATION_CLASS);
 static NTSTATUS (WINAPI *pNtQueryFullAttributesFile)(const OBJECT_ATTRIBUTES*, FILE_NETWORK_OPEN_INFORMATION*);
+static NTSTATUS (WINAPI *pNtFlushBuffersFile)(HANDLE, IO_STATUS_BLOCK*);
 static NTSTATUS (WINAPI *pNtQueryEaFile)(HANDLE,PIO_STATUS_BLOCK,PVOID,ULONG,BOOLEAN,PVOID,ULONG,PULONG,BOOLEAN);
 
 static inline BOOL is_signaled( HANDLE obj )
@@ -113,31 +115,8 @@ static inline BOOL is_signaled( HANDLE obj )
     return WaitForSingleObject( obj, 0 ) == WAIT_OBJECT_0;
 }
 
-static const char* debugstr_longlong(ULONGLONG ll)
-{
-    static char str[17];
-    if (sizeof(ll) > sizeof(unsigned long) && ll >> 32)
-        sprintf(str, "%lx%08lx", (unsigned long)(ll >> 32), (unsigned long)ll);
-    else
-        sprintf(str, "%lx", (unsigned long)ll);
-    return str;
-}
-
-#define PIPENAME "\\\\.\\pipe\\ntdll_tests_file.c"
 #define TEST_BUF_LEN 3
 
-static BOOL create_pipe( HANDLE *read, HANDLE *write, ULONG flags, ULONG size )
-{
-    *read = CreateNamedPipeA(PIPENAME, PIPE_ACCESS_INBOUND | flags, PIPE_TYPE_BYTE | PIPE_WAIT,
-                            1, size, size, NMPWAIT_USE_DEFAULT_WAIT, NULL);
-    ok(*read != INVALID_HANDLE_VALUE, "CreateNamedPipe failed\n");
-
-    *write = CreateFileA(PIPENAME, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
-    ok(*write != INVALID_HANDLE_VALUE, "CreateFile failed (%d)\n", GetLastError());
-
-    return TRUE;
-}
-
 static HANDLE create_temp_file( ULONG flags )
 {
     char path[MAX_PATH], buffer[MAX_PATH];
@@ -198,18 +177,22 @@ static void WINAPI apc( void *arg, IO_STATUS_BLOCK *iosb, ULONG reserved )
 
 static void create_file_test(void)
 {
+    static const WCHAR notepadW[] = {'n','o','t','e','p','a','d','.','e','x','e',0};
     static const WCHAR systemrootW[] = {'\\','S','y','s','t','e','m','R','o','o','t',
                                         '\\','f','a','i','l','i','n','g',0};
+    static const WCHAR systemrootExplorerW[] = {'\\','S','y','s','t','e','m','R','o','o','t',
+                                               '\\','e','x','p','l','o','r','e','r','.','e','x','e',0};
     static const WCHAR questionmarkInvalidNameW[] = {'a','f','i','l','e','?',0};
     static const WCHAR pipeInvalidNameW[]  = {'a','|','b',0};
     static const WCHAR pathInvalidNtW[] = {'\\','\\','?','\\',0};
     static const WCHAR pathInvalidNt2W[] = {'\\','?','?','\\',0};
     static const WCHAR pathInvalidDosW[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\',0};
     static const char testdata[] = "Hello World";
+    static const WCHAR sepW[] = {'\\',0};
     FILE_NETWORK_OPEN_INFORMATION info;
     NTSTATUS status;
     HANDLE dir, file;
-    WCHAR path[MAX_PATH];
+    WCHAR path[MAX_PATH], temp[MAX_PATH];
     OBJECT_ATTRIBUTES attr;
     IO_STATUS_BLOCK io;
     UNICODE_STRING nameW;
@@ -389,6 +372,25 @@ static void create_file_test(void)
     status = pNtQueryFullAttributesFile( &attr, &info );
     ok( status == STATUS_OBJECT_NAME_INVALID,
         "query %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status );
+
+    GetWindowsDirectoryW( path, MAX_PATH );
+    path[2] = 0;
+    ok( QueryDosDeviceW( path, temp, MAX_PATH ),
+        "QueryDosDeviceW failed with error %u\n", GetLastError() );
+    lstrcatW( temp, sepW );
+    lstrcatW( temp, path+3 );
+    lstrcatW( temp, sepW );
+    lstrcatW( temp, notepadW );
+
+    pRtlInitUnicodeString( &nameW, temp );
+    status = pNtQueryFullAttributesFile( &attr, &info );
+    ok( status == STATUS_SUCCESS,
+        "query %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status );
+
+    pRtlInitUnicodeString( &nameW, systemrootExplorerW );
+    status = pNtQueryFullAttributesFile( &attr, &info );
+    ok( status == STATUS_SUCCESS,
+        "query %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status );
 }
 
 static void open_file_test(void)
@@ -660,268 +662,14 @@ static void delete_file_test(void)
 static void read_file_test(void)
 {
     const char text[] = "foobar";
-    HANDLE handle, read, write;
+    HANDLE handle;
+    IO_STATUS_BLOCK iosb;
     NTSTATUS status;
-    IO_STATUS_BLOCK iosb, iosb2;
-    DWORD written;
     int apc_count = 0;
     char buffer[128];
     LARGE_INTEGER offset;
     HANDLE event = CreateEventA( NULL, TRUE, FALSE, NULL );
-    BOOL ret;
-
-    buffer[0] = 1;
-
-    if (!create_pipe( &read, &write, FILE_FLAG_OVERLAPPED, 4096 )) return;
-
-    /* try read with no data */
-    U(iosb).Status = 0xdeadbabe;
-    iosb.Information = 0xdeadbeef;
-    ok( is_signaled( read ), "read handle is not signaled\n" );
-    status = pNtReadFile( read, event, apc, &apc_count, &iosb, buffer, 1, NULL, NULL );
-    ok( status == STATUS_PENDING, "wrong status %x\n", status );
-    ok( !is_signaled( read ), "read handle is signaled\n" );
-    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" );
-    ret = WriteFile( write, buffer, 1, &written, NULL );
-    ok(ret && written == 1, "WriteFile error %d\n", GetLastError());
-    /* iosb updated here by async i/o */
-    Sleep(1);  /* FIXME: needed for wine to run the i/o apc  */
-    ok( U(iosb).Status == 0, "wrong status %x\n", U(iosb).Status );
-    ok( iosb.Information == 1, "wrong info %lu\n", iosb.Information );
-    ok( !is_signaled( read ), "read handle is signaled\n" );
-    ok( is_signaled( event ), "event is not signaled\n" );
-    ok( !apc_count, "apc was called\n" );
-    apc_count = 0;
-    SleepEx( 1, FALSE ); /* non-alertable sleep */
-    ok( !apc_count, "apc was called\n" );
-    SleepEx( 1, TRUE ); /* alertable sleep */
-    ok( apc_count == 1, "apc not called\n" );
-
-    /* with no event, the pipe handle itself gets signaled */
-    apc_count = 0;
-    U(iosb).Status = 0xdeadbabe;
-    iosb.Information = 0xdeadbeef;
-    ok( !is_signaled( read ), "read handle is signaled\n" );
-    status = pNtReadFile( read, 0, apc, &apc_count, &iosb, buffer, 1, NULL, NULL );
-    ok( status == STATUS_PENDING, "wrong status %x\n", status );
-    ok( !is_signaled( read ), "read handle 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" );
-    ret = WriteFile( write, buffer, 1, &written, NULL );
-    ok(ret && written == 1, "WriteFile error %d\n", GetLastError());
-    /* iosb updated here by async i/o */
-    Sleep(1);  /* FIXME: needed for wine to run the i/o apc  */
-    ok( U(iosb).Status == 0, "wrong status %x\n", U(iosb).Status );
-    ok( iosb.Information == 1, "wrong info %lu\n", iosb.Information );
-    ok( is_signaled( read ), "read handle is not signaled\n" );
-    ok( !apc_count, "apc was called\n" );
-    apc_count = 0;
-    SleepEx( 1, FALSE ); /* non-alertable sleep */
-    ok( !apc_count, "apc was called\n" );
-    SleepEx( 1, TRUE ); /* alertable sleep */
-    ok( apc_count == 1, "apc not called\n" );
 
-    /* now read with data ready */
-    apc_count = 0;
-    U(iosb).Status = 0xdeadbabe;
-    iosb.Information = 0xdeadbeef;
-    ResetEvent( event );
-    ret = WriteFile( write, buffer, 1, &written, NULL );
-    ok(ret && written == 1, "WriteFile error %d\n", GetLastError());
-    status = pNtReadFile( read, event, apc, &apc_count, &iosb, buffer, 1, NULL, NULL );
-    ok( status == STATUS_SUCCESS, "wrong status %x\n", status );
-    ok( U(iosb).Status == 0, "wrong status %x\n", U(iosb).Status );
-    ok( iosb.Information == 1, "wrong info %lu\n", iosb.Information );
-    ok( is_signaled( event ), "event is not signaled\n" );
-    ok( !apc_count, "apc was called\n" );
-    SleepEx( 1, FALSE ); /* non-alertable sleep */
-    ok( !apc_count, "apc was called\n" );
-    SleepEx( 1, TRUE ); /* alertable sleep */
-    ok( apc_count == 1, "apc not called\n" );
-
-    /* try read with no data */
-    apc_count = 0;
-    U(iosb).Status = 0xdeadbabe;
-    iosb.Information = 0xdeadbeef;
-    ok( is_signaled( event ), "event is not signaled\n" ); /* check that read resets the event */
-    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" );
-    ret = WriteFile( write, buffer, 1, &written, NULL );
-    ok(ret && written == 1, "WriteFile error %d\n", GetLastError());
-    /* partial read is good enough */
-    Sleep(1);  /* FIXME: needed for wine to run the i/o apc  */
-    ok( is_signaled( event ), "event is not signaled\n" );
-    ok( U(iosb).Status == 0, "wrong status %x\n", U(iosb).Status );
-    ok( iosb.Information == 1, "wrong info %lu\n", iosb.Information );
-    ok( !apc_count, "apc was called\n" );
-    SleepEx( 1, TRUE ); /* alertable sleep */
-    ok( apc_count == 1, "apc was not called\n" );
-
-    /* read from disconnected pipe */
-    apc_count = 0;
-    U(iosb).Status = 0xdeadbabe;
-    iosb.Information = 0xdeadbeef;
-    CloseHandle( write );
-    status = pNtReadFile( read, event, apc, &apc_count, &iosb, buffer, 1, NULL, NULL );
-    ok( status == STATUS_PIPE_BROKEN, "wrong status %x\n", status );
-    ok( U(iosb).Status == 0xdeadbabe, "wrong status %x\n", U(iosb).Status );
-    ok( iosb.Information == 0xdeadbeef, "wrong info %lu\n", iosb.Information );
-    ok( !is_signaled( event ), "event is signaled\n" );
-    ok( !apc_count, "apc was called\n" );
-    SleepEx( 1, TRUE ); /* alertable sleep */
-    ok( !apc_count, "apc was called\n" );
-    CloseHandle( read );
-
-    /* read from closed handle */
-    apc_count = 0;
-    U(iosb).Status = 0xdeadbabe;
-    iosb.Information = 0xdeadbeef;
-    SetEvent( event );
-    status = pNtReadFile( read, event, apc, &apc_count, &iosb, buffer, 1, NULL, NULL );
-    ok( status == STATUS_INVALID_HANDLE, "wrong status %x\n", status );
-    ok( U(iosb).Status == 0xdeadbabe, "wrong status %x\n", U(iosb).Status );
-    ok( iosb.Information == 0xdeadbeef, "wrong info %lu\n", iosb.Information );
-    ok( is_signaled( event ), "event is not signaled\n" );  /* not reset on invalid handle */
-    ok( !apc_count, "apc was called\n" );
-    SleepEx( 1, TRUE ); /* alertable sleep */
-    ok( !apc_count, "apc was called\n" );
-
-    /* disconnect while async read is in progress */
-    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" );
-    CloseHandle( write );
-    Sleep(1);  /* FIXME: needed for wine to run the i/o apc  */
-    ok( U(iosb).Status == STATUS_PIPE_BROKEN, "wrong status %x\n", U(iosb).Status );
-    ok( iosb.Information == 0, "wrong info %lu\n", iosb.Information );
-    ok( is_signaled( event ), "event is not signaled\n" );
-    ok( !apc_count, "apc was called\n" );
-    SleepEx( 1, TRUE ); /* alertable sleep */
-    ok( apc_count == 1, "apc was not called\n" );
-    CloseHandle( read );
-
-    if (!create_pipe( &read, &write, FILE_FLAG_OVERLAPPED, 4096 )) return;
-    ret = DuplicateHandle(GetCurrentProcess(), read, GetCurrentProcess(), &handle, 0, TRUE, DUPLICATE_SAME_ACCESS);
-    ok(ret, "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 not signaled\n" );
-    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 not signaled\n" );
-    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 not signaled\n" );
-        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 not signaled\n" );
-        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;
     U(iosb).Status = 0xdeadbabe;
@@ -1297,7 +1045,7 @@ static void test_iocp_fileio(HANDLE h)
             ok( completionKey == CKEY_SECOND, "Invalid completion key: %lx\n", completionKey );
             ok( ioSb.Information == 0, "Invalid ioSb.Information: %ld\n", ioSb.Information );
             /* wine sends wrong status here */
-            todo_wine ok( U(ioSb).Status == STATUS_PIPE_BROKEN, "Invalid ioSb.Status: %x\n", U(ioSb).Status);
+            ok( U(ioSb).Status == STATUS_PIPE_BROKEN, "Invalid ioSb.Status: %x\n", U(ioSb).Status);
             ok( completionValue == (ULONG_PTR)&o, "Invalid completion value: %lx\n", completionValue );
         }
     }
@@ -1315,6 +1063,7 @@ static void test_iocp_fileio(HANDLE h)
     {
         OVERLAPPED o = {0,};
         BYTE send_buf[TEST_BUF_LEN], recv_buf[TEST_BUF_LEN];
+        int apc_count = 0;
         DWORD read;
         long count;
 
@@ -1343,6 +1092,58 @@ static void test_iocp_fileio(HANDLE h)
         }
         count = get_pending_msgs(h);
         ok( !count, "Unexpected msg count: %ld\n", count );
+
+        /* using APCs on handle with associated completion port is not allowed */
+        res = NtReadFile( hPipeSrv, NULL, apc, &apc_count, &iosb, recv_buf, sizeof(recv_buf), NULL, NULL );
+        ok(res == STATUS_INVALID_PARAMETER, "NtReadFile returned %x\n", res);
+    }
+
+    CloseHandle( hPipeSrv );
+    CloseHandle( hPipeClt );
+
+    /* test associating a completion port with a handle after an async using APC is queued */
+    hPipeSrv = CreateNamedPipeA( pipe_name, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, 4, 1024, 1024, 1000, NULL );
+    ok( hPipeSrv != INVALID_HANDLE_VALUE, "Cannot create named pipe\n" );
+    if (hPipeSrv == INVALID_HANDLE_VALUE )
+        return;
+    hPipeClt = CreateFileA( pipe_name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL );
+    ok( hPipeClt != INVALID_HANDLE_VALUE, "Cannot connect to pipe\n" );
+    if (hPipeClt != INVALID_HANDLE_VALUE)
+    {
+        BYTE send_buf[TEST_BUF_LEN], recv_buf[TEST_BUF_LEN];
+        int apc_count = 0;
+        DWORD read;
+        long count;
+
+        memset( send_buf, 0, TEST_BUF_LEN );
+        memset( recv_buf, 0xde, TEST_BUF_LEN );
+        count = get_pending_msgs(h);
+        ok( !count, "Unexpected msg count: %ld\n", count );
+
+        res = NtReadFile( hPipeSrv, NULL, apc, &apc_count, &iosb, recv_buf, sizeof(recv_buf), NULL, NULL );
+        ok(res == STATUS_PENDING, "NtReadFile returned %x\n", res);
+
+        U(iosb).Status = 0xdeadbeef;
+        res = pNtSetInformationFile( hPipeSrv, &iosb, &fci, sizeof(fci), FileCompletionInformation );
+        ok( res == STATUS_SUCCESS, "NtSetInformationFile failed: %x\n", res );
+        ok( U(iosb).Status == STATUS_SUCCESS, "iosb.Status invalid: %x\n", U(iosb).Status );
+        count = get_pending_msgs(h);
+        ok( !count, "Unexpected msg count: %ld\n", count );
+
+        WriteFile( hPipeClt, send_buf, TEST_BUF_LEN, &read, NULL );
+
+        ok(!apc_count, "apc_count = %u\n", apc_count);
+        count = get_pending_msgs(h);
+        ok( !count, "Unexpected msg count: %ld\n", count );
+
+        SleepEx(1, TRUE); /* alertable sleep */
+        ok(apc_count == 1, "apc was not called\n");
+        count = get_pending_msgs(h);
+        ok( !count, "Unexpected msg count: %ld\n", count );
+
+        /* using APCs on handle with associated completion port is not allowed */
+        res = NtReadFile( hPipeSrv, NULL, apc, &apc_count, &iosb, recv_buf, sizeof(recv_buf), NULL, NULL );
+        ok(res == STATUS_INVALID_PARAMETER, "NtReadFile returned %x\n", res);
     }
 
     CloseHandle( hPipeSrv );
@@ -1371,10 +1172,10 @@ static void test_file_full_size_information(void)
     /* Test for FileFsSizeInformation */
     ok(fsi.TotalAllocationUnits.QuadPart > 0,
         "[fsi] TotalAllocationUnits expected positive, got 0x%s\n",
-        debugstr_longlong(fsi.TotalAllocationUnits.QuadPart));
+        wine_dbgstr_longlong(fsi.TotalAllocationUnits.QuadPart));
     ok(fsi.AvailableAllocationUnits.QuadPart > 0,
         "[fsi] AvailableAllocationUnits expected positive, got 0x%s\n",
-        debugstr_longlong(fsi.AvailableAllocationUnits.QuadPart));
+        wine_dbgstr_longlong(fsi.AvailableAllocationUnits.QuadPart));
 
     /* Assume file system is NTFS */
     ok(fsi.BytesPerSector == 512, "[fsi] BytesPerSector expected 512, got %d\n",fsi.BytesPerSector);
@@ -1382,21 +1183,21 @@ static void test_file_full_size_information(void)
 
     ok(ffsi.TotalAllocationUnits.QuadPart > 0,
         "[ffsi] TotalAllocationUnits expected positive, got negative value 0x%s\n",
-        debugstr_longlong(ffsi.TotalAllocationUnits.QuadPart));
+        wine_dbgstr_longlong(ffsi.TotalAllocationUnits.QuadPart));
     ok(ffsi.CallerAvailableAllocationUnits.QuadPart > 0,
         "[ffsi] CallerAvailableAllocationUnits expected positive, got negative value 0x%s\n",
-        debugstr_longlong(ffsi.CallerAvailableAllocationUnits.QuadPart));
+        wine_dbgstr_longlong(ffsi.CallerAvailableAllocationUnits.QuadPart));
     ok(ffsi.ActualAvailableAllocationUnits.QuadPart > 0,
         "[ffsi] ActualAvailableAllocationUnits expected positive, got negative value 0x%s\n",
-        debugstr_longlong(ffsi.ActualAvailableAllocationUnits.QuadPart));
+        wine_dbgstr_longlong(ffsi.ActualAvailableAllocationUnits.QuadPart));
     ok(ffsi.TotalAllocationUnits.QuadPart == fsi.TotalAllocationUnits.QuadPart,
         "[ffsi] TotalAllocationUnits error fsi:0x%s, ffsi:0x%s\n",
-        debugstr_longlong(fsi.TotalAllocationUnits.QuadPart),
-        debugstr_longlong(ffsi.TotalAllocationUnits.QuadPart));
+        wine_dbgstr_longlong(fsi.TotalAllocationUnits.QuadPart),
+        wine_dbgstr_longlong(ffsi.TotalAllocationUnits.QuadPart));
     ok(ffsi.CallerAvailableAllocationUnits.QuadPart == fsi.AvailableAllocationUnits.QuadPart,
         "[ffsi] CallerAvailableAllocationUnits error fsi:0x%s, ffsi: 0x%s\n",
-        debugstr_longlong(fsi.AvailableAllocationUnits.QuadPart),
-        debugstr_longlong(ffsi.CallerAvailableAllocationUnits.QuadPart));
+        wine_dbgstr_longlong(fsi.AvailableAllocationUnits.QuadPart),
+        wine_dbgstr_longlong(ffsi.CallerAvailableAllocationUnits.QuadPart));
 
     /* Assume file system is NTFS */
     ok(ffsi.BytesPerSector == 512, "[ffsi] BytesPerSector expected 512, got %d\n",ffsi.BytesPerSector);
@@ -1592,7 +1393,7 @@ static void test_file_rename_information(void)
     res = pNtQueryInformationFile( handle, &io, fni, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR), FileNameInformation );
     ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %x\n", res );
     fni->FileName[ fni->FileNameLength / sizeof(WCHAR) ] = 0;
-    ok( !lstrcmpW(fni->FileName, newpath + 2), "FileName expected %s, got %s\n",
+    ok( !lstrcmpiW(fni->FileName, newpath + 2), "FileName expected %s, got %s\n",
         wine_dbgstr_w(newpath + 2), wine_dbgstr_w(fni->FileName) );
     HeapFree( GetProcessHeap(), 0, fni );
 
@@ -1762,7 +1563,7 @@ static void test_file_rename_information(void)
     res = pNtQueryInformationFile( handle, &io, fni, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR), FileNameInformation );
     ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %x\n", res );
     fni->FileName[ fni->FileNameLength / sizeof(WCHAR) ] = 0;
-    ok( !lstrcmpW(fni->FileName, newpath + 2), "FileName expected %s, got %s\n",
+    ok( !lstrcmpiW(fni->FileName, newpath + 2), "FileName expected %s, got %s\n",
         wine_dbgstr_w(newpath + 2), wine_dbgstr_w(fni->FileName) );
     HeapFree( GetProcessHeap(), 0, fni );
 
@@ -2168,7 +1969,7 @@ static void test_file_rename_information(void)
     res = pNtQueryInformationFile( handle, &io, fni, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR), FileNameInformation );
     ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %x\n", res );
     fni->FileName[ fni->FileNameLength / sizeof(WCHAR) ] = 0;
-    ok( !lstrcmpW(fni->FileName, newpath + 2), "FileName expected %s, got %s\n",
+    ok( !lstrcmpiW(fni->FileName, newpath + 2), "FileName expected %s, got %s\n",
         wine_dbgstr_w(newpath + 2), wine_dbgstr_w(fni->FileName) );
     HeapFree( GetProcessHeap(), 0, fni );
 
@@ -2181,6 +1982,7 @@ static void test_file_rename_information(void)
 
 static void test_file_link_information(void)
 {
+    static const WCHAR pipeW[] = {'\\','\\','.','\\','p','i','p','e','\\','w','i','n','e','_','t','e','s','t',0};
     static const WCHAR foo_txtW[] = {'\\','f','o','o','.','t','x','t',0};
     static const WCHAR fooW[] = {'f','o','o',0};
     WCHAR tmp_path[MAX_PATH], oldpath[MAX_PATH + 16], newpath[MAX_PATH + 16], *filename, *p;
@@ -2224,7 +2026,7 @@ static void test_file_link_information(void)
     res = pNtQueryInformationFile( handle, &io, fni, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR), FileNameInformation );
     ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %x\n", res );
     fni->FileName[ fni->FileNameLength / sizeof(WCHAR) ] = 0;
-    ok( !lstrcmpW(fni->FileName, oldpath + 2), "FileName expected %s, got %s\n",
+    ok( !lstrcmpiW(fni->FileName, oldpath + 2), "FileName expected %s, got %s\n",
         wine_dbgstr_w(oldpath + 2), wine_dbgstr_w(fni->FileName) );
     HeapFree( GetProcessHeap(), 0, fni );
 
@@ -2394,7 +2196,7 @@ static void test_file_link_information(void)
     res = pNtQueryInformationFile( handle, &io, fni, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR), FileNameInformation );
     ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %x\n", res );
     fni->FileName[ fni->FileNameLength / sizeof(WCHAR) ] = 0;
-    ok( !lstrcmpW(fni->FileName, oldpath + 2), "FileName expected %s, got %s\n",
+    ok( !lstrcmpiW(fni->FileName, oldpath + 2), "FileName expected %s, got %s\n",
         wine_dbgstr_w(oldpath + 2), wine_dbgstr_w(fni->FileName) );
     HeapFree( GetProcessHeap(), 0, fni );
 
@@ -2797,12 +2599,36 @@ static void test_file_link_information(void)
     res = pNtQueryInformationFile( handle, &io, fni, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR), FileNameInformation );
     ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %x\n", res );
     fni->FileName[ fni->FileNameLength / sizeof(WCHAR) ] = 0;
-    ok( !lstrcmpW(fni->FileName, oldpath + 2), "FileName expected %s, got %s\n",
+    ok( !lstrcmpiW(fni->FileName, oldpath + 2), "FileName expected %s, got %s\n",
         wine_dbgstr_w(oldpath + 2), wine_dbgstr_w(fni->FileName) );
     HeapFree( GetProcessHeap(), 0, fni );
 
     CloseHandle( handle );
     CloseHandle( handle2 );
+
+    handle = CreateEventA( NULL, FALSE, FALSE, "wine_test_event" );
+    ok( !!handle, "Failed to create event: %u\n", GetLastError());
+
+    fni = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR) );
+    res = pNtQueryInformationFile( handle, &io, fni, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR), FileNameInformation );
+    ok( res == STATUS_OBJECT_TYPE_MISMATCH, "res expected STATUS_OBJECT_TYPE_MISMATCH, got %x\n", res );
+    HeapFree( GetProcessHeap(), 0, fni );
+
+    CloseHandle( handle );
+
+    handle = CreateNamedPipeW( pipeW, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE|PIPE_READMODE_BYTE, 10, 512, 512, 0, NULL);
+    ok( handle != INVALID_HANDLE_VALUE, "Failed to create named pipe: %u\n", GetLastError());
+
+    fni = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR) );
+    res = pNtQueryInformationFile( handle, &io, fni, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR), FileNameInformation );
+    ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %x\n", res );
+    fni->FileName[ fni->FileNameLength / sizeof(WCHAR) ] = 0;
+    ok( !lstrcmpiW(fni->FileName, pipeW + 8), "FileName expected %s, got %s\n",
+        wine_dbgstr_w(pipeW + 8), wine_dbgstr_w(fni->FileName) );
+    HeapFree( GetProcessHeap(), 0, fni );
+
+    CloseHandle( handle );
+
     HeapFree( GetProcessHeap(), 0, fli );
     delete_object( oldpath );
     delete_object( newpath );
@@ -3349,6 +3175,213 @@ static void test_file_all_name_information(void)
     HeapFree( GetProcessHeap(), 0, file_name );
 }
 
+static void test_file_completion_information(void)
+{
+    static const char buf[] = "testdata";
+    FILE_IO_COMPLETION_NOTIFICATION_INFORMATION info;
+    OVERLAPPED ov, *pov;
+    IO_STATUS_BLOCK io;
+    NTSTATUS status;
+    DWORD num_bytes;
+    HANDLE port, h;
+    ULONG_PTR key;
+    BOOL ret;
+    int i;
+
+    if (!(h = create_temp_file(0))) return;
+
+    status = pNtSetInformationFile(h, &io, &info, sizeof(info) - 1, FileIoCompletionNotificationInformation);
+    ok(status == STATUS_INFO_LENGTH_MISMATCH || status == STATUS_INVALID_INFO_CLASS /* XP */,
+       "expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status);
+    if (status == STATUS_INVALID_INFO_CLASS || status == STATUS_NOT_IMPLEMENTED)
+    {
+        win_skip("FileIoCompletionNotificationInformation class not supported\n");
+        CloseHandle(h);
+        return;
+    }
+
+    info.Flags = FILE_SKIP_COMPLETION_PORT_ON_SUCCESS;
+    status = pNtSetInformationFile(h, &io, &info, sizeof(info), FileIoCompletionNotificationInformation);
+    ok(status == STATUS_INVALID_PARAMETER, "expected STATUS_INVALID_PARAMETER, got %08x\n", status);
+
+    CloseHandle(h);
+    if (!(h = create_temp_file(FILE_FLAG_OVERLAPPED))) return;
+
+    info.Flags = FILE_SKIP_SET_EVENT_ON_HANDLE;
+    status = pNtSetInformationFile(h, &io, &info, sizeof(info), FileIoCompletionNotificationInformation);
+    ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status);
+
+    info.Flags = FILE_SKIP_SET_USER_EVENT_ON_FAST_IO;
+    status = pNtSetInformationFile(h, &io, &info, sizeof(info), FileIoCompletionNotificationInformation);
+    ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status);
+
+    CloseHandle(h);
+    if (!(h = create_temp_file(FILE_FLAG_OVERLAPPED))) return;
+
+    info.Flags = ~0U;
+    status = pNtQueryInformationFile(h, &io, &info, sizeof(info), FileIoCompletionNotificationInformation);
+    ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status);
+    ok(!(info.Flags & FILE_SKIP_COMPLETION_PORT_ON_SUCCESS), "got %08x\n", info.Flags);
+
+    memset(&ov, 0, sizeof(ov));
+    ov.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
+    port = CreateIoCompletionPort(h, NULL, 0xdeadbeef, 0);
+    ok(port != NULL, "CreateIoCompletionPort failed, error %u\n", GetLastError());
+
+    for (i = 0; i < 10; i++)
+    {
+        SetLastError(0xdeadbeef);
+        ret = WriteFile(h, buf, sizeof(buf), &num_bytes, &ov);
+        if (ret || GetLastError() != ERROR_IO_PENDING) break;
+        ret = GetOverlappedResult(h, &ov, &num_bytes, TRUE);
+        ok(ret, "GetOverlappedResult failed, error %u\n", GetLastError());
+        ret = GetQueuedCompletionStatus(port, &num_bytes, &key, &pov, 1000);
+        ok(ret, "GetQueuedCompletionStatus failed, error %u\n", GetLastError());
+        ret = FALSE;
+    }
+    if (ret)
+    {
+        ok(num_bytes == sizeof(buf), "expected sizeof(buf), got %u\n", num_bytes);
+
+        key = 0;
+        pov = NULL;
+        ret = GetQueuedCompletionStatus(port, &num_bytes, &key, &pov, 1000);
+        ok(ret, "GetQueuedCompletionStatus failed, error %u\n", GetLastError());
+        ok(key == 0xdeadbeef, "expected 0xdeadbeef, got %lx\n", key);
+        ok(pov == &ov, "expected %p, got %p\n", &ov, pov);
+    }
+    else
+        win_skip("WriteFile never returned TRUE\n");
+
+    info.Flags = FILE_SKIP_COMPLETION_PORT_ON_SUCCESS;
+    status = pNtSetInformationFile(h, &io, &info, sizeof(info), FileIoCompletionNotificationInformation);
+    ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status);
+
+    info.Flags = 0;
+    status = pNtQueryInformationFile(h, &io, &info, sizeof(info), FileIoCompletionNotificationInformation);
+    ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status);
+    ok((info.Flags & FILE_SKIP_COMPLETION_PORT_ON_SUCCESS) != 0, "got %08x\n", info.Flags);
+
+    for (i = 0; i < 10; i++)
+    {
+        SetLastError(0xdeadbeef);
+        ret = WriteFile(h, buf, sizeof(buf), &num_bytes, &ov);
+        if (ret || GetLastError() != ERROR_IO_PENDING) break;
+        ret = GetOverlappedResult(h, &ov, &num_bytes, TRUE);
+        ok(ret, "GetOverlappedResult failed, error %u\n", GetLastError());
+        ret = FALSE;
+    }
+    if (ret)
+    {
+        ok(num_bytes == sizeof(buf), "expected sizeof(buf), got %u\n", num_bytes);
+
+        pov = (void *)0xdeadbeef;
+        ret = GetQueuedCompletionStatus(port, &num_bytes, &key, &pov, 500);
+        ok(!ret, "GetQueuedCompletionStatus succeeded\n");
+        ok(pov == NULL, "expected NULL, got %p\n", pov);
+    }
+    else
+        win_skip("WriteFile never returned TRUE\n");
+
+    info.Flags = 0;
+    status = pNtSetInformationFile(h, &io, &info, sizeof(info), FileIoCompletionNotificationInformation);
+    ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status);
+
+    info.Flags = 0;
+    status = pNtQueryInformationFile(h, &io, &info, sizeof(info), FileIoCompletionNotificationInformation);
+    ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status);
+    ok((info.Flags & FILE_SKIP_COMPLETION_PORT_ON_SUCCESS) != 0, "got %08x\n", info.Flags);
+
+    for (i = 0; i < 10; i++)
+    {
+        SetLastError(0xdeadbeef);
+        ret = WriteFile(h, buf, sizeof(buf), &num_bytes, &ov);
+        if (ret || GetLastError() != ERROR_IO_PENDING) break;
+        ret = GetOverlappedResult(h, &ov, &num_bytes, TRUE);
+        ok(ret, "GetOverlappedResult failed, error %u\n", GetLastError());
+        ret = GetQueuedCompletionStatus(port, &num_bytes, &key, &pov, 1000);
+        ok(ret, "GetQueuedCompletionStatus failed, error %u\n", GetLastError());
+        ret = FALSE;
+    }
+    if (ret)
+    {
+        ok(num_bytes == sizeof(buf), "expected sizeof(buf), got %u\n", num_bytes);
+
+        pov = (void *)0xdeadbeef;
+        ret = GetQueuedCompletionStatus(port, &num_bytes, &key, &pov, 1000);
+        ok(!ret, "GetQueuedCompletionStatus succeeded\n");
+        ok(pov == NULL, "expected NULL, got %p\n", pov);
+    }
+    else
+        win_skip("WriteFile never returned TRUE\n");
+
+    CloseHandle(ov.hEvent);
+    CloseHandle(port);
+    CloseHandle(h);
+}
+
+static void test_file_id_information(void)
+{
+    BY_HANDLE_FILE_INFORMATION info;
+    FILE_ID_INFORMATION fid;
+    IO_STATUS_BLOCK io;
+    NTSTATUS status;
+    DWORD *dwords;
+    HANDLE h;
+    BOOL ret;
+
+    if (!(h = create_temp_file(0))) return;
+
+    memset( &fid, 0x11, sizeof(fid) );
+    status = pNtQueryInformationFile( h, &io, &fid, sizeof(fid), FileIdInformation );
+    if (status == STATUS_NOT_IMPLEMENTED || status == STATUS_INVALID_INFO_CLASS)
+    {
+        win_skip( "FileIdInformation not supported\n" );
+        CloseHandle( h );
+        return;
+    }
+
+    memset( &info, 0x22, sizeof(info) );
+    ret = GetFileInformationByHandle( h, &info );
+    ok( ret, "GetFileInformationByHandle failed\n" );
+
+    dwords = (DWORD *)&fid.VolumeSerialNumber;
+    ok( dwords[0] == info.dwVolumeSerialNumber, "expected %08x, got %08x\n",
+        info.dwVolumeSerialNumber, dwords[0] );
+    ok( dwords[1] != 0x11111111, "expected != 0x11111111\n" );
+
+    dwords = (DWORD *)&fid.FileId;
+    ok( dwords[0] == info.nFileIndexLow, "expected %08x, got %08x\n", info.nFileIndexLow, dwords[0] );
+    ok( dwords[1] == info.nFileIndexHigh, "expected %08x, got %08x\n", info.nFileIndexHigh, dwords[1] );
+    ok( dwords[2] == 0, "expected 0, got %08x\n", dwords[2] );
+    ok( dwords[3] == 0, "expected 0, got %08x\n", dwords[3] );
+
+    CloseHandle( h );
+}
+
+static void test_file_access_information(void)
+{
+    FILE_ACCESS_INFORMATION info;
+    IO_STATUS_BLOCK io;
+    NTSTATUS status;
+    HANDLE h;
+
+    if (!(h = create_temp_file(0))) return;
+
+    status = pNtQueryInformationFile( h, &io, &info, sizeof(info) - 1, FileAccessInformation );
+    ok( status == STATUS_INFO_LENGTH_MISMATCH, "expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status );
+
+    status = pNtQueryInformationFile( (HANDLE)0xdeadbeef, &io, &info, sizeof(info), FileAccessInformation );
+    ok( status == STATUS_INVALID_HANDLE, "expected STATUS_INVALID_HANDLE, got %08x\n", status );
+
+    memset(&info, 0x11, sizeof(info));
+    status = pNtQueryInformationFile( h, &io, &info, sizeof(info), FileAccessInformation );
+    ok( status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status );
+    ok( info.AccessFlags == 0x13019f, "got %08x\n", info.AccessFlags );
+
+    CloseHandle( h );
+}
+
 static void test_query_volume_information_file(void)
 {
     NTSTATUS status;
@@ -3616,13 +3649,15 @@ static void test_read_write(void)
 {
     static const char contents[14] = "1234567890abcd";
     char buf[256];
-    HANDLE hfile;
+    HANDLE hfile, event;
     OVERLAPPED ovl;
     IO_STATUS_BLOCK iob;
     DWORD ret, bytes, status, off;
     LARGE_INTEGER offset;
     LONG i;
 
+    event = CreateEventA( NULL, TRUE, FALSE, NULL );
+
     U(iob).Status = -1;
     iob.Information = -1;
     offset.QuadPart = 0;
@@ -3631,6 +3666,22 @@ static void test_read_write(void)
     ok(U(iob).Status == -1, "expected -1, got %#x\n", U(iob).Status);
     ok(iob.Information == -1, "expected -1, got %lu\n", iob.Information);
 
+    U(iob).Status = -1;
+    iob.Information = -1;
+    offset.QuadPart = 0;
+    status = pNtReadFile(INVALID_HANDLE_VALUE, 0, NULL, NULL, &iob, NULL, sizeof(buf), &offset, NULL);
+    ok(status == STATUS_OBJECT_TYPE_MISMATCH || status == STATUS_INVALID_HANDLE, "expected STATUS_OBJECT_TYPE_MISMATCH, got %#x\n", status);
+    ok(U(iob).Status == -1, "expected -1, got %#x\n", U(iob).Status);
+    ok(iob.Information == -1, "expected -1, got %lu\n", iob.Information);
+
+    U(iob).Status = -1;
+    iob.Information = -1;
+    offset.QuadPart = 0;
+    status = pNtWriteFile(INVALID_HANDLE_VALUE, 0, NULL, NULL, &iob, buf, sizeof(buf), &offset, NULL);
+    ok(status == STATUS_OBJECT_TYPE_MISMATCH || status == STATUS_INVALID_HANDLE, "expected STATUS_OBJECT_TYPE_MISMATCH, got %#x\n", status);
+    ok(U(iob).Status == -1, "expected -1, got %#x\n", U(iob).Status);
+    ok(iob.Information == -1, "expected -1, got %lu\n", iob.Information);
+
     U(iob).Status = -1;
     iob.Information = -1;
     offset.QuadPart = 0;
@@ -3649,6 +3700,15 @@ static void test_read_write(void)
     ok(U(iob).Status == -1, "expected -1, got %#x\n", U(iob).Status);
     ok(iob.Information == -1, "expected -1, got %lu\n", iob.Information);
 
+    U(iob).Status = -1;
+    iob.Information = -1;
+    SetEvent(event);
+    status = pNtWriteFile(hfile, event, NULL, NULL, &iob, NULL, sizeof(contents), NULL, NULL);
+    ok(status == STATUS_INVALID_USER_BUFFER, "expected STATUS_INVALID_USER_BUFFER, got %#x\n", status);
+    ok(U(iob).Status == -1, "expected -1, got %#x\n", U(iob).Status);
+    ok(iob.Information == -1, "expected -1, got %lu\n", iob.Information);
+    ok(!is_signaled(event), "event is not signaled\n");
+
     U(iob).Status = -1;
     iob.Information = -1;
     status = pNtReadFile(hfile, 0, NULL, NULL, &iob, NULL, sizeof(contents), NULL, NULL);
@@ -3656,6 +3716,24 @@ static void test_read_write(void)
     ok(U(iob).Status == -1, "expected -1, got %#x\n", U(iob).Status);
     ok(iob.Information == -1, "expected -1, got %lu\n", iob.Information);
 
+    U(iob).Status = -1;
+    iob.Information = -1;
+    SetEvent(event);
+    status = pNtReadFile(hfile, event, NULL, NULL, &iob, NULL, sizeof(contents), NULL, NULL);
+    ok(status == STATUS_ACCESS_VIOLATION, "expected STATUS_ACCESS_VIOLATION, got %#x\n", status);
+    ok(U(iob).Status == -1, "expected -1, got %#x\n", U(iob).Status);
+    ok(iob.Information == -1, "expected -1, got %lu\n", iob.Information);
+    ok(is_signaled(event), "event is not signaled\n");
+
+    U(iob).Status = -1;
+    iob.Information = -1;
+    SetEvent(event);
+    status = pNtReadFile(hfile, event, NULL, NULL, &iob, (void*)0xdeadbeef, sizeof(contents), NULL, NULL);
+    ok(status == STATUS_ACCESS_VIOLATION, "expected STATUS_ACCESS_VIOLATION, got %#x\n", status);
+    ok(U(iob).Status == -1, "expected -1, got %#x\n", U(iob).Status);
+    ok(iob.Information == -1, "expected -1, got %lu\n", iob.Information);
+    ok(is_signaled(event), "event is not signaled\n");
+
     U(iob).Status = -1;
     iob.Information = -1;
     status = pNtWriteFile(hfile, 0, NULL, NULL, &iob, contents, 7, NULL, NULL);
@@ -4321,7 +4399,78 @@ static void test_read_write(void)
     off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT);
     ok(off == 0, "expected 0, got %u\n", off);
 
+    CloseHandle(event);
+    CloseHandle(hfile);
+}
+
+static void test_ioctl(void)
+{
+    HANDLE event = CreateEventA(NULL, TRUE, FALSE, NULL);
+    IO_STATUS_BLOCK iosb;
+    HANDLE file;
+    NTSTATUS status;
+
+    file = create_temp_file(FILE_FLAG_OVERLAPPED);
+    ok(file != INVALID_HANDLE_VALUE, "could not create temp file\n");
+
+    SetEvent(event);
+    status = pNtFsControlFile(file, event, NULL, NULL, &iosb, 0xdeadbeef, 0, 0, 0, 0);
+    todo_wine
+    ok(status == STATUS_INVALID_DEVICE_REQUEST, "NtFsControlFile returned %x\n", status);
+    ok(!is_signaled(event), "event is signaled\n");
+
+    status = pNtFsControlFile(file, (HANDLE)0xdeadbeef, NULL, NULL, &iosb, 0xdeadbeef, 0, 0, 0, 0);
+    ok(status == STATUS_INVALID_HANDLE, "NtFsControlFile returned %x\n", status);
+
+    CloseHandle(event);
+    CloseHandle(file);
+}
+
+static void test_flush_buffers_file(void)
+{
+    char path[MAX_PATH], buffer[MAX_PATH];
+    HANDLE hfile, hfileread;
+    NTSTATUS status;
+    IO_STATUS_BLOCK io_status_block;
+
+    GetTempPathA(MAX_PATH, path);
+    GetTempFileNameA(path, "foo", 0, buffer);
+    hfile = CreateFileA(buffer, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS,
+            FILE_ATTRIBUTE_NORMAL, 0);
+    ok(hfile != INVALID_HANDLE_VALUE, "failed to create temp file.\n" );
+
+    hfileread = CreateFileA(buffer, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+            OPEN_EXISTING, 0, NULL);
+    ok(hfileread != INVALID_HANDLE_VALUE, "could not open temp file, error %d.\n", GetLastError());
+
+    status = pNtFlushBuffersFile(hfile, NULL);
+    todo_wine
+    ok(status == STATUS_ACCESS_VIOLATION, "expected STATUS_ACCESS_VIOLATION, got %#x.\n", status);
+
+    status = pNtFlushBuffersFile(hfile, (IO_STATUS_BLOCK *)0xdeadbeaf);
+    todo_wine
+    ok(status == STATUS_ACCESS_VIOLATION, "expected STATUS_ACCESS_VIOLATION, got %#x.\n", status);
+
+    status = pNtFlushBuffersFile(hfile, &io_status_block);
+    ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x.\n", status);
+
+    status = pNtFlushBuffersFile(hfileread, &io_status_block);
+    ok(status == STATUS_ACCESS_DENIED, "expected STATUS_ACCESS_DENIED, got %#x.\n", status);
+
+    status = pNtFlushBuffersFile(NULL, &io_status_block);
+    ok(status == STATUS_INVALID_HANDLE, "expected STATUS_INVALID_HANDLE, got %#x.\n", status);
+
+    CloseHandle(hfileread);
     CloseHandle(hfile);
+    hfile = CreateFileA(buffer, FILE_APPEND_DATA, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+            OPEN_EXISTING, 0, NULL);
+    ok(hfile != INVALID_HANDLE_VALUE, "could not open temp file, error %d.\n", GetLastError());
+
+    status = pNtFlushBuffersFile(hfile, &io_status_block);
+    ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x.\n", status);
+
+    CloseHandle(hfile);
+    DeleteFileA(buffer);
 }
 
 static void test_query_ea(void)
@@ -4601,6 +4750,7 @@ START_TEST(file)
     pNtCancelIoFile         = (void *)GetProcAddress(hntdll, "NtCancelIoFile");
     pNtCancelIoFileEx       = (void *)GetProcAddress(hntdll, "NtCancelIoFileEx");
     pNtClose                = (void *)GetProcAddress(hntdll, "NtClose");
+    pNtFsControlFile        = (void *)GetProcAddress(hntdll, "NtFsControlFile");
     pNtCreateIoCompletion   = (void *)GetProcAddress(hntdll, "NtCreateIoCompletion");
     pNtOpenIoCompletion     = (void *)GetProcAddress(hntdll, "NtOpenIoCompletion");
     pNtQueryIoCompletion    = (void *)GetProcAddress(hntdll, "NtQueryIoCompletion");
@@ -4611,6 +4761,7 @@ START_TEST(file)
     pNtQueryDirectoryFile   = (void *)GetProcAddress(hntdll, "NtQueryDirectoryFile");
     pNtQueryVolumeInformationFile = (void *)GetProcAddress(hntdll, "NtQueryVolumeInformationFile");
     pNtQueryFullAttributesFile = (void *)GetProcAddress(hntdll, "NtQueryFullAttributesFile");
+    pNtFlushBuffersFile = (void *)GetProcAddress(hntdll, "NtFlushBuffersFile");
     pNtQueryEaFile          = (void *)GetProcAddress(hntdll, "NtQueryEaFile");
 
     test_read_write();
@@ -4632,8 +4783,13 @@ START_TEST(file)
     test_file_rename_information();
     test_file_link_information();
     test_file_disposition_information();
+    test_file_completion_information();
+    test_file_id_information();
+    test_file_access_information();
     test_query_volume_information_file();
     test_query_attribute_information_file();
+    test_ioctl();
+    test_flush_buffers_file();
     test_query_ea();
     test_junction_points();
 }
index c0e2b1a..d03d26c 100755 (executable)
@@ -22,7 +22,9 @@
 #include <winnls.h>
 #include <stdio.h>
 
+static NTSTATUS (WINAPI * pRtlDowncaseUnicodeString)(UNICODE_STRING *, const UNICODE_STRING *, BOOLEAN);
 static NTSTATUS (WINAPI * pNtQuerySystemInformation)(SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG);
+static NTSTATUS (WINAPI * pNtQuerySystemInformationEx)(SYSTEM_INFORMATION_CLASS, void*, ULONG, void*, ULONG, ULONG*);
 static NTSTATUS (WINAPI * pNtPowerInformation)(POWER_INFORMATION_LEVEL, PVOID, ULONG, PVOID, ULONG);
 static NTSTATUS (WINAPI * pNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
 static NTSTATUS (WINAPI * pNtQueryInformationThread)(HANDLE, THREADINFOCLASS, PVOID, ULONG, PULONG);
@@ -36,6 +38,7 @@ static NTSTATUS (WINAPI * pNtUnmapViewOfSection)(HANDLE,PVOID);
 static NTSTATUS (WINAPI * pNtClose)(HANDLE);
 static ULONG    (WINAPI * pNtGetCurrentProcessorNumber)(void);
 static BOOL     (WINAPI * pIsWow64Process)(HANDLE, PBOOL);
+static BOOL     (WINAPI * pGetLogicalProcessorInformationEx)(LOGICAL_PROCESSOR_RELATIONSHIP,SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*,DWORD*);
 
 static BOOL is_wow64;
 
@@ -56,12 +59,15 @@ static BOOL InitFunctionPtrs(void)
 {
     /* All needed functions are NT based, so using GetModuleHandle is a good check */
     HMODULE hntdll = GetModuleHandleA("ntdll");
+    HMODULE hkernel32 = GetModuleHandleA("kernel32");
+
     if (!hntdll)
     {
         win_skip("Not running on NT\n");
         return FALSE;
     }
 
+    NTDLL_GET_PROC(RtlDowncaseUnicodeString);
     NTDLL_GET_PROC(NtQuerySystemInformation);
     NTDLL_GET_PROC(NtPowerInformation);
     NTDLL_GET_PROC(NtQueryInformationProcess);
@@ -78,8 +84,16 @@ static BOOL InitFunctionPtrs(void)
     /* not present before XP */
     pNtGetCurrentProcessorNumber = (void *) GetProcAddress(hntdll, "NtGetCurrentProcessorNumber");
 
-    pIsWow64Process = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "IsWow64Process");
+    pIsWow64Process = (void *)GetProcAddress(hkernel32, "IsWow64Process");
     if (!pIsWow64Process || !pIsWow64Process( GetCurrentProcess(), &is_wow64 )) is_wow64 = FALSE;
+
+    /* starting with Win7 */
+    pNtQuerySystemInformationEx = (void *) GetProcAddress(hntdll, "NtQuerySystemInformationEx");
+    if (!pNtQuerySystemInformationEx)
+        win_skip("NtQuerySystemInformationEx() is not supported, some tests will be skipped.\n");
+
+    pGetLogicalProcessorInformationEx = (void *) GetProcAddress(hkernel32, "GetLogicalProcessorInformationEx");
+
     return TRUE;
 }
 
@@ -288,7 +302,7 @@ static void test_query_process(void)
     /* test ReturnLength */
     ReturnLength = 0;
     status = pNtQuerySystemInformation(SystemProcessInformation, NULL, 0, &ReturnLength);
-    ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_LENGTH_MISMATCH got %08x\n", status);
+    ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH got %08x\n", status);
     ok( ReturnLength > 0 || broken(ReturnLength == 0) /* NT4, Win2K */,
         "Expected a ReturnLength to show the needed length\n");
 
@@ -474,13 +488,15 @@ static void test_query_module(void)
 static void test_query_handle(void)
 {
     NTSTATUS status;
-    ULONG ReturnLength;
+    ULONG ExpectedLength, ReturnLength;
     ULONG SystemInformationLength = sizeof(SYSTEM_HANDLE_INFORMATION);
     SYSTEM_HANDLE_INFORMATION* shi = HeapAlloc(GetProcessHeap(), 0, SystemInformationLength);
-    HANDLE event_handle;
+    HANDLE EventHandle;
+    BOOL found;
+    INT i;
 
-    event_handle = CreateEventA(NULL, FALSE, FALSE, NULL);
-    ok( event_handle != NULL, "CreateEventA failed %u\n", GetLastError() );
+    EventHandle = CreateEventA(NULL, FALSE, FALSE, NULL);
+    ok( EventHandle != NULL, "CreateEventA failed %u\n", GetLastError() );
 
     /* Request the needed length : a SystemInformationLength greater than one struct sets ReturnLength */
     ReturnLength = 0xdeadbeef;
@@ -490,34 +506,116 @@ static void test_query_handle(void)
 
     SystemInformationLength = ReturnLength;
     shi = HeapReAlloc(GetProcessHeap(), 0, shi , SystemInformationLength);
+    memset(shi, 0x55, SystemInformationLength);
 
     ReturnLength = 0xdeadbeef;
     status = pNtQuerySystemInformation(SystemHandleInformation, shi, SystemInformationLength, &ReturnLength);
-    if (status != STATUS_INFO_LENGTH_MISMATCH) /* vista */
+    while (status == STATUS_INFO_LENGTH_MISMATCH) /* Vista / 2008 */
     {
-        ULONG ExpectedLength = FIELD_OFFSET(SYSTEM_HANDLE_INFORMATION, Handle[shi->Count]);
-        unsigned int i;
-        BOOL found = FALSE;
+        SystemInformationLength *= 2;
+        shi = HeapReAlloc(GetProcessHeap(), 0, shi, SystemInformationLength);
+        memset(shi, 0x55, SystemInformationLength);
+        status = pNtQuerySystemInformation(SystemHandleInformation, shi, SystemInformationLength, &ReturnLength);
+    }
+    ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status );
+    ExpectedLength = FIELD_OFFSET(SYSTEM_HANDLE_INFORMATION, Handle[shi->Count]);
+    ok( ReturnLength == ExpectedLength || broken(ReturnLength == ExpectedLength - sizeof(DWORD)), /* Vista / 2008 */
+        "Expected length %u, got %u\n", ExpectedLength, ReturnLength );
+    ok( shi->Count > 1, "Expected more than 1 handle, got %u\n", shi->Count );
+    ok( shi->Handle[1].HandleValue != 0x5555 || broken( shi->Handle[1].HandleValue == 0x5555 ), /* Vista / 2008 */
+        "Uninitialized second handle\n" );
+    if (shi->Handle[1].HandleValue == 0x5555)
+    {
+        win_skip("Skipping broken SYSTEM_HANDLE_INFORMATION\n");
+        CloseHandle(EventHandle);
+        goto done;
+    }
+
+    for (i = 0, found = FALSE; i < shi->Count && !found; i++)
+        found = (shi->Handle[i].OwnerPid == GetCurrentProcessId()) &&
+                ((HANDLE)(ULONG_PTR)shi->Handle[i].HandleValue == EventHandle);
+    ok( found, "Expected to find event handle %p (pid %x) in handle list\n", EventHandle, GetCurrentProcessId() );
 
-        ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status );
-        ok( ReturnLength == ExpectedLength, "Expected length %u, got %u\n", ExpectedLength, ReturnLength );
-        ok( shi->Count > 1, "Expected more than 1 handles, got %u\n", shi->Count );
+    if (!found)
         for (i = 0; i < shi->Count; i++)
-        {
-            if (shi->Handle[i].OwnerPid == GetCurrentProcessId() &&
-                (HANDLE)(ULONG_PTR)shi->Handle[i].HandleValue == event_handle)
-            {
-                found = TRUE;
-                break;
-            }
-        }
-        ok( found, "Expected to find event handle in handle list\n" );
+            trace( "%d: handle %x pid %x\n", i, shi->Handle[i].HandleValue, shi->Handle[i].OwnerPid );
+
+    CloseHandle(EventHandle);
+
+    ReturnLength = 0xdeadbeef;
+    status = pNtQuerySystemInformation(SystemHandleInformation, shi, SystemInformationLength, &ReturnLength);
+    while (status == STATUS_INFO_LENGTH_MISMATCH) /* Vista / 2008 */
+    {
+        SystemInformationLength *= 2;
+        shi = HeapReAlloc(GetProcessHeap(), 0, shi, SystemInformationLength);
+        status = pNtQuerySystemInformation(SystemHandleInformation, shi, SystemInformationLength, &ReturnLength);
     }
+    ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status );
+    for (i = 0, found = FALSE; i < shi->Count && !found; i++)
+        found = (shi->Handle[i].OwnerPid == GetCurrentProcessId()) &&
+                ((HANDLE)(ULONG_PTR)shi->Handle[i].HandleValue == EventHandle);
+    ok( !found, "Unexpectedly found event handle in handle list\n" );
 
     status = pNtQuerySystemInformation(SystemHandleInformation, NULL, SystemInformationLength, &ReturnLength);
     ok( status == STATUS_ACCESS_VIOLATION, "Expected STATUS_ACCESS_VIOLATION, got %08x\n", status );
 
-    CloseHandle(event_handle);
+done:
+    HeapFree( GetProcessHeap(), 0, shi);
+}
+
+static void test_query_handle_ex(void)
+{
+    NTSTATUS status;
+    ULONG ExpectedLength, ReturnLength;
+    ULONG SystemInformationLength = sizeof(SYSTEM_HANDLE_INFORMATION_EX);
+    SYSTEM_HANDLE_INFORMATION_EX* shi = HeapAlloc(GetProcessHeap(), 0, SystemInformationLength);
+    HANDLE EventHandle;
+    BOOL found;
+    INT i;
+
+    EventHandle = CreateEventA(NULL, FALSE, FALSE, NULL);
+    ok( EventHandle != NULL, "CreateEventA failed %u\n", GetLastError() );
+
+    ReturnLength = 0xdeadbeef;
+    status = pNtQuerySystemInformation(SystemExtendedHandleInformation, shi, SystemInformationLength, &ReturnLength);
+    ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status);
+    ok( ReturnLength != 0xdeadbeef, "Expected valid ReturnLength\n" );
+
+    SystemInformationLength = ReturnLength;
+    shi = HeapReAlloc(GetProcessHeap(), 0, shi , SystemInformationLength);
+    memset(shi, 0x55, SystemInformationLength);
+
+    ReturnLength = 0xdeadbeef;
+    status = pNtQuerySystemInformation(SystemExtendedHandleInformation, shi, SystemInformationLength, &ReturnLength);
+    ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status );
+    ExpectedLength = FIELD_OFFSET(SYSTEM_HANDLE_INFORMATION_EX, Handle[shi->Count]);
+    ok( ReturnLength == ExpectedLength, "Expected length %u, got %u\n", ExpectedLength, ReturnLength );
+    ok( shi->Count > 1, "Expected more than 1 handle, got %u\n", (DWORD)shi->Count );
+
+    for (i = 0, found = FALSE; i < shi->Count && !found; i++)
+        found = (shi->Handle[i].UniqueProcessId == GetCurrentProcessId()) &&
+                ((HANDLE)(ULONG_PTR)shi->Handle[i].HandleValue == EventHandle);
+    ok( found, "Expected to find event handle %p (pid %x) in handle list\n", EventHandle, GetCurrentProcessId() );
+
+    if (!found)
+    {
+        for (i = 0; i < shi->Count; i++)
+            trace( "%d: handle %x pid %x\n", i, (DWORD)shi->Handle[i].HandleValue, (DWORD)shi->Handle[i].UniqueProcessId );
+    }
+
+    CloseHandle(EventHandle);
+
+    ReturnLength = 0xdeadbeef;
+    status = pNtQuerySystemInformation(SystemExtendedHandleInformation, shi, SystemInformationLength, &ReturnLength);
+    ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status );
+    for (i = 0, found = FALSE; i < shi->Count && !found; i++)
+        found = (shi->Handle[i].UniqueProcessId == GetCurrentProcessId()) &&
+                ((HANDLE)(ULONG_PTR)shi->Handle[i].HandleValue == EventHandle);
+    ok( !found, "Unexpectedly found event handle in handle list\n" );
+
+    status = pNtQuerySystemInformation(SystemExtendedHandleInformation, NULL, SystemInformationLength, &ReturnLength);
+    ok( status == STATUS_ACCESS_VIOLATION, "Expected STATUS_ACCESS_VIOLATION, got %08x\n", status );
+
     HeapFree( GetProcessHeap(), 0, shi);
 }
 
@@ -684,6 +782,110 @@ static void test_query_logicalproc(void)
     HeapFree(GetProcessHeap(), 0, slpi);
 }
 
+static void test_query_logicalprocex(void)
+{
+    SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *infoex, *infoex2;
+    DWORD relationship, len2, len;
+    NTSTATUS status;
+    BOOL ret;
+
+    if (!pNtQuerySystemInformationEx)
+        return;
+
+    len = 0;
+    relationship = RelationProcessorCore;
+    status = pNtQuerySystemInformationEx(SystemLogicalProcessorInformationEx, &relationship, sizeof(relationship), NULL, 0, &len);
+    ok(status == STATUS_INFO_LENGTH_MISMATCH, "got 0x%08x\n", status);
+    ok(len > 0, "got %u\n", len);
+
+    len = 0;
+    relationship = RelationAll;
+    status = pNtQuerySystemInformationEx(SystemLogicalProcessorInformationEx, &relationship, sizeof(relationship), NULL, 0, &len);
+    ok(status == STATUS_INFO_LENGTH_MISMATCH, "got 0x%08x\n", status);
+    ok(len > 0, "got %u\n", len);
+
+    len2 = 0;
+    ret = pGetLogicalProcessorInformationEx(RelationAll, NULL, &len2);
+    ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "got %d, error %d\n", ret, GetLastError());
+    ok(len == len2, "got %u, expected %u\n", len2, len);
+
+    if (len && len == len2) {
+        int j, i;
+
+        infoex = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len);
+        infoex2 = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len);
+
+        status = pNtQuerySystemInformationEx(SystemLogicalProcessorInformationEx, &relationship, sizeof(relationship), infoex, len, &len);
+        ok(status == STATUS_SUCCESS, "got 0x%08x\n", status);
+
+        ret = pGetLogicalProcessorInformationEx(RelationAll, infoex2, &len2);
+        ok(ret, "got %d, error %d\n", ret, GetLastError());
+        ok(!memcmp(infoex, infoex2, len), "returned info data mismatch\n");
+
+        for(i = 0; status == STATUS_SUCCESS && i < len; ){
+            SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *ex = (void*)(((char *)infoex) + i);
+
+            ok(ex->Relationship >= RelationProcessorCore && ex->Relationship <= RelationGroup,
+                    "Got invalid relationship value: 0x%x\n", ex->Relationship);
+            if (!ex->Size)
+            {
+                ok(0, "got infoex[%u].Size=0\n", i);
+                break;
+            }
+
+            trace("infoex[%u].Size: %u\n", i, ex->Size);
+            switch(ex->Relationship){
+            case RelationProcessorCore:
+            case RelationProcessorPackage:
+                trace("infoex[%u].Relationship: 0x%x (Core == 0x0 or Package == 0x3)\n", i, ex->Relationship);
+                trace("infoex[%u].Processor.Flags: 0x%x\n", i, ex->Processor.Flags);
+#ifndef __REACTOS__
+                trace("infoex[%u].Processor.EfficiencyClass: 0x%x\n", i, ex->Processor.EfficiencyClass);
+#endif
+                trace("infoex[%u].Processor.GroupCount: 0x%x\n", i, ex->Processor.GroupCount);
+                for(j = 0; j < ex->Processor.GroupCount; ++j){
+                    trace("infoex[%u].Processor.GroupMask[%u].Mask: 0x%lx\n", i, j, ex->Processor.GroupMask[j].Mask);
+                    trace("infoex[%u].Processor.GroupMask[%u].Group: 0x%x\n", i, j, ex->Processor.GroupMask[j].Group);
+                }
+                break;
+            case RelationNumaNode:
+                trace("infoex[%u].Relationship: 0x%x (NumaNode)\n", i, ex->Relationship);
+                trace("infoex[%u].NumaNode.NodeNumber: 0x%x\n", i, ex->NumaNode.NodeNumber);
+                trace("infoex[%u].NumaNode.GroupMask.Mask: 0x%lx\n", i, ex->NumaNode.GroupMask.Mask);
+                trace("infoex[%u].NumaNode.GroupMask.Group: 0x%x\n", i, ex->NumaNode.GroupMask.Group);
+                break;
+            case RelationCache:
+                trace("infoex[%u].Relationship: 0x%x (Cache)\n", i, ex->Relationship);
+                trace("infoex[%u].Cache.Level: 0x%x\n", i, ex->Cache.Level);
+                trace("infoex[%u].Cache.Associativity: 0x%x\n", i, ex->Cache.Associativity);
+                trace("infoex[%u].Cache.LineSize: 0x%x\n", i, ex->Cache.LineSize);
+                trace("infoex[%u].Cache.CacheSize: 0x%x\n", i, ex->Cache.CacheSize);
+                trace("infoex[%u].Cache.Type: 0x%x\n", i, ex->Cache.Type);
+                trace("infoex[%u].Cache.GroupMask.Mask: 0x%lx\n", i, ex->Cache.GroupMask.Mask);
+                trace("infoex[%u].Cache.GroupMask.Group: 0x%x\n", i, ex->Cache.GroupMask.Group);
+                break;
+            case RelationGroup:
+                trace("infoex[%u].Relationship: 0x%x (Group)\n", i, ex->Relationship);
+                trace("infoex[%u].Group.MaximumGroupCount: 0x%x\n", i, ex->Group.MaximumGroupCount);
+                trace("infoex[%u].Group.ActiveGroupCount: 0x%x\n", i, ex->Group.ActiveGroupCount);
+                for(j = 0; j < ex->Group.ActiveGroupCount; ++j){
+                    trace("infoex[%u].Group.GroupInfo[%u].MaximumProcessorCount: 0x%x\n", i, j, ex->Group.GroupInfo[j].MaximumProcessorCount);
+                    trace("infoex[%u].Group.GroupInfo[%u].ActiveProcessorCount: 0x%x\n", i, j, ex->Group.GroupInfo[j].ActiveProcessorCount);
+                    trace("infoex[%u].Group.GroupInfo[%u].ActiveProcessorMask: 0x%lx\n", i, j, ex->Group.GroupInfo[j].ActiveProcessorMask);
+                }
+                break;
+            default:
+                break;
+            }
+
+            i += ex->Size;
+        }
+
+        HeapFree(GetProcessHeap(), 0, infoex);
+        HeapFree(GetProcessHeap(), 0, infoex2);
+    }
+}
+
 static void test_query_processor_power_info(void)
 {
     NTSTATUS status;
@@ -904,12 +1106,32 @@ static void test_query_process_basic(void)
     ok( pbi.UniqueProcessId > 0, "Expected a ProcessID > 0, got 0\n");
 }
 
+static void dump_vm_counters(const char *header, const VM_COUNTERS *pvi)
+{
+    trace("%s:\n", header);
+    trace("PeakVirtualSize           : %lu\n", pvi->PeakVirtualSize);
+    trace("VirtualSize               : %lu\n", pvi->VirtualSize);
+    trace("PageFaultCount            : %u\n",  pvi->PageFaultCount);
+    trace("PeakWorkingSetSize        : %lu\n", pvi->PeakWorkingSetSize);
+    trace("WorkingSetSize            : %lu\n", pvi->WorkingSetSize);
+    trace("QuotaPeakPagedPoolUsage   : %lu\n", pvi->QuotaPeakPagedPoolUsage);
+    trace("QuotaPagedPoolUsage       : %lu\n", pvi->QuotaPagedPoolUsage);
+    trace("QuotaPeakNonPagePoolUsage : %lu\n", pvi->QuotaPeakNonPagedPoolUsage);
+    trace("QuotaNonPagePoolUsage     : %lu\n", pvi->QuotaNonPagedPoolUsage);
+    trace("PagefileUsage             : %lu\n", pvi->PagefileUsage);
+    trace("PeakPagefileUsage         : %lu\n", pvi->PeakPagefileUsage);
+}
+
 static void test_query_process_vm(void)
 {
     NTSTATUS status;
     ULONG ReturnLength;
     VM_COUNTERS pvi;
     ULONG old_size = FIELD_OFFSET(VM_COUNTERS,PrivatePageCount);
+    HANDLE process;
+    SIZE_T prev_size;
+    const SIZE_T alloc_size = 16 * 1024 * 1024;
+    void *ptr;
 
     status = pNtQueryInformationProcess(NULL, ProcessVmCounters, NULL, sizeof(pvi), NULL);
     ok( status == STATUS_ACCESS_VIOLATION || status == STATUS_INVALID_HANDLE,
@@ -935,11 +1157,72 @@ static void test_query_process_vm(void)
     ok( ReturnLength == old_size || ReturnLength == sizeof(pvi), "Inconsistent length %d\n", ReturnLength);
 
     /* Check if we have some return values */
-    trace("WorkingSetSize : %ld\n", pvi.WorkingSetSize);
-    todo_wine
-    {
-        ok( pvi.WorkingSetSize > 0, "Expected a WorkingSetSize > 0\n");
-    }
+    dump_vm_counters("VM counters for GetCurrentProcess", &pvi);
+    ok( pvi.WorkingSetSize > 0, "Expected a WorkingSetSize > 0\n");
+    ok( pvi.PagefileUsage > 0, "Expected a PagefileUsage > 0\n");
+
+    process = OpenProcess(PROCESS_VM_READ, FALSE, GetCurrentProcessId());
+    status = pNtQueryInformationProcess(process, ProcessVmCounters, &pvi, sizeof(pvi), NULL);
+    ok( status == STATUS_ACCESS_DENIED, "Expected STATUS_ACCESS_DENIED, got %08x\n", status);
+    CloseHandle(process);
+
+    process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, GetCurrentProcessId());
+    status = pNtQueryInformationProcess(process, ProcessVmCounters, &pvi, sizeof(pvi), NULL);
+    ok( status == STATUS_SUCCESS || broken(!process) /* XP */, "Expected STATUS_SUCCESS, got %08x\n", status);
+    CloseHandle(process);
+
+    memset(&pvi, 0, sizeof(pvi));
+    process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, GetCurrentProcessId());
+    status = pNtQueryInformationProcess(process, ProcessVmCounters, &pvi, sizeof(pvi), NULL);
+    ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status);
+
+    /* Check if we have some return values */
+    dump_vm_counters("VM counters for GetCurrentProcessId", &pvi);
+    ok( pvi.WorkingSetSize > 0, "Expected a WorkingSetSize > 0\n");
+    ok( pvi.PagefileUsage > 0, "Expected a PagefileUsage > 0\n");
+
+    CloseHandle(process);
+
+    /* Check if we have real counters */
+    status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessVmCounters, &pvi, sizeof(pvi), NULL);
+    ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status);
+    prev_size = pvi.VirtualSize;
+    if (winetest_debug > 1)
+        dump_vm_counters("VM counters before VirtualAlloc", &pvi);
+    ptr = VirtualAlloc(NULL, alloc_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
+    ok( ptr != NULL, "VirtualAlloc failed, err %u\n", GetLastError());
+    status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessVmCounters, &pvi, sizeof(pvi), NULL);
+    ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status);
+    if (winetest_debug > 1)
+        dump_vm_counters("VM counters after VirtualAlloc", &pvi);
+    todo_wine ok( pvi.VirtualSize >= prev_size + alloc_size,
+        "Expected to be greater than %lu, got %lu\n", prev_size + alloc_size, pvi.VirtualSize);
+    VirtualFree( ptr, 0, MEM_RELEASE);
+
+    status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessVmCounters, &pvi, sizeof(pvi), NULL);
+    ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status);
+    prev_size = pvi.VirtualSize;
+    if (winetest_debug > 1)
+        dump_vm_counters("VM counters before VirtualAlloc", &pvi);
+    ptr = VirtualAlloc(NULL, alloc_size, MEM_RESERVE, PAGE_READWRITE);
+    ok( ptr != NULL, "VirtualAlloc failed, err %u\n", GetLastError());
+    status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessVmCounters, &pvi, sizeof(pvi), NULL);
+    ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status);
+    if (winetest_debug > 1)
+        dump_vm_counters("VM counters after VirtualAlloc(MEM_RESERVE)", &pvi);
+    todo_wine ok( pvi.VirtualSize >= prev_size + alloc_size,
+        "Expected to be greater than %lu, got %lu\n", prev_size + alloc_size, pvi.VirtualSize);
+    prev_size = pvi.VirtualSize;
+
+    ptr = VirtualAlloc(ptr, alloc_size, MEM_COMMIT, PAGE_READWRITE);
+    ok( ptr != NULL, "VirtualAlloc failed, err %u\n", GetLastError());
+    status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessVmCounters, &pvi, sizeof(pvi), NULL);
+    ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status);
+    if (winetest_debug > 1)
+        dump_vm_counters("VM counters after VirtualAlloc(MEM_COMMIT)", &pvi);
+    ok( pvi.VirtualSize == prev_size,
+        "Expected to equal to %lu, got %lu\n", prev_size, pvi.VirtualSize);
+    VirtualFree( ptr, 0, MEM_RELEASE);
 }
 
 static void test_query_process_io(void)
@@ -975,7 +1258,7 @@ static void test_query_process_io(void)
     ok( sizeof(pii) == ReturnLength, "Inconsistent length %d\n", ReturnLength);
 
     /* Check if we have some return values */
-    trace("OtherOperationCount : 0x%x%08x\n", (DWORD)(pii.OtherOperationCount >> 32), (DWORD)pii.OtherOperationCount);
+    trace("OtherOperationCount : 0x%s\n", wine_dbgstr_longlong(pii.OtherOperationCount));
     todo_wine
     {
         ok( pii.OtherOperationCount > 0, "Expected an OtherOperationCount > 0\n");
@@ -1070,7 +1353,7 @@ static void test_query_process_debug_port(int argc, char **argv)
 
     status = pNtQueryInformationProcess(NULL, ProcessDebugPort,
             &debug_port, sizeof(debug_port), NULL);
-    ok(status == STATUS_INVALID_HANDLE, "Expected STATUS_ACCESS_VIOLATION, got %#x.\n", status);
+    ok(status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got %#x.\n", status);
 
     status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessDebugPort,
             &debug_port, sizeof(debug_port) - 1, NULL);
@@ -1111,6 +1394,40 @@ static void test_query_process_debug_port(int argc, char **argv)
     ok(ret, "CloseHandle failed, last error %#x.\n", GetLastError());
 }
 
+static void test_query_process_priority(void)
+{
+    PROCESS_PRIORITY_CLASS priority[2];
+    ULONG ReturnLength;
+    DWORD orig_priority;
+    NTSTATUS status;
+    BOOL ret;
+
+    status = pNtQueryInformationProcess(NULL, ProcessPriorityClass, NULL, sizeof(priority[0]), NULL);
+    ok(status == STATUS_ACCESS_VIOLATION || broken(status == STATUS_INVALID_HANDLE) /* w2k3 */,
+       "Expected STATUS_ACCESS_VIOLATION, got %08x\n", status);
+
+    status = pNtQueryInformationProcess(NULL, ProcessPriorityClass, &priority, sizeof(priority[0]), NULL);
+    ok(status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got %08x\n", status);
+
+    status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessPriorityClass, &priority, 1, &ReturnLength);
+    ok(status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status);
+
+    status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessPriorityClass, &priority, sizeof(priority), &ReturnLength);
+    ok(status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status);
+
+    orig_priority = GetPriorityClass(GetCurrentProcess());
+    ret = SetPriorityClass(GetCurrentProcess(), BELOW_NORMAL_PRIORITY_CLASS);
+    ok(ret, "Failed to set priority class: %u\n", GetLastError());
+
+    status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessPriorityClass, &priority, sizeof(priority[0]), &ReturnLength);
+    ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status);
+    ok(priority[0].PriorityClass == PROCESS_PRIOCLASS_BELOW_NORMAL,
+       "Expected PROCESS_PRIOCLASS_BELOW_NORMAL, got %u\n", priority[0].PriorityClass);
+
+    ret = SetPriorityClass(GetCurrentProcess(), orig_priority);
+    ok(ret, "Failed to reset priority class: %u\n", GetLastError());
+}
+
 static void test_query_process_handlecount(void)
 {
     NTSTATUS status;
@@ -1293,27 +1610,27 @@ static void test_query_process_debug_object_handle(int argc, char **argv)
 
 static void test_query_process_debug_flags(int argc, char **argv)
 {
+    static const DWORD test_flags[] = { DEBUG_PROCESS,
+                                        DEBUG_ONLY_THIS_PROCESS,
+                                        DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS,
+                                        CREATE_SUSPENDED };
     DWORD debug_flags = 0xdeadbeef;
     char cmdline[MAX_PATH];
     PROCESS_INFORMATION pi;
     STARTUPINFOA si = { 0 };
     NTSTATUS status;
+    DEBUG_EVENT ev;
+    DWORD result;
     BOOL ret;
+    int i, j;
 
-    sprintf(cmdline, "%s %s %s", argv[0], argv[1], "debuggee");
-
-    si.cb = sizeof(si);
-    ret = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS, NULL, NULL, &si, &pi);
-    ok(ret, "CreateProcess failed, last error %#x.\n", GetLastError());
-    if (!ret) return;
+    /* test invalid arguments */
+    status = pNtQueryInformationProcess(NULL, ProcessDebugFlags, NULL, 0, NULL);
+    ok(status == STATUS_INFO_LENGTH_MISMATCH || broken(status == STATUS_INVALID_INFO_CLASS) /* WOW64 */,
+            "Expected STATUS_INFO_LENGTH_MISMATCH, got %#x.\n", status);
 
-    status = pNtQueryInformationProcess(NULL, ProcessDebugFlags,
-            NULL, 0, NULL);
-    ok(status == STATUS_INFO_LENGTH_MISMATCH || broken(status == STATUS_INVALID_INFO_CLASS) /* NT4 */, "Expected STATUS_INFO_LENGTH_MISMATCH, got %#x.\n", status);
-
-    status = pNtQueryInformationProcess(NULL, ProcessDebugFlags,
-            NULL, sizeof(debug_flags), NULL);
-    ok(status == STATUS_INVALID_HANDLE || status == STATUS_ACCESS_VIOLATION || broken(status == STATUS_INVALID_INFO_CLASS) /* W7PROX64 (32-bit) */,
+    status = pNtQueryInformationProcess(NULL, ProcessDebugFlags, NULL, sizeof(debug_flags), NULL);
+    ok(status == STATUS_INVALID_HANDLE || status == STATUS_ACCESS_VIOLATION || broken(status == STATUS_INVALID_INFO_CLASS) /* WOW64 */,
             "Expected STATUS_INVALID_HANDLE, got %#x.\n", status);
 
     status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessDebugFlags,
@@ -1322,45 +1639,123 @@ static void test_query_process_debug_flags(int argc, char **argv)
 
     status = pNtQueryInformationProcess(NULL, ProcessDebugFlags,
             &debug_flags, sizeof(debug_flags), NULL);
-    ok(status == STATUS_INVALID_HANDLE || broken(status == STATUS_INVALID_INFO_CLASS) /* NT4 */, "Expected STATUS_ACCESS_VIOLATION, got %#x.\n", status);
+    ok(status == STATUS_INVALID_HANDLE || broken(status == STATUS_INVALID_INFO_CLASS) /* WOW64 */,
+            "Expected STATUS_INVALID_HANDLE, got %#x.\n", status);
 
     status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessDebugFlags,
             &debug_flags, sizeof(debug_flags) - 1, NULL);
-    ok(status == STATUS_INFO_LENGTH_MISMATCH || broken(status == STATUS_INVALID_INFO_CLASS) /* NT4 */, "Expected STATUS_INFO_LENGTH_MISMATCH, got %#x.\n", status);
+    ok(status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %#x.\n", status);
 
     status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessDebugFlags,
             &debug_flags, sizeof(debug_flags) + 1, NULL);
-    ok(status == STATUS_INFO_LENGTH_MISMATCH || broken(status == STATUS_INVALID_INFO_CLASS) /* NT4 */, "Expected STATUS_INFO_LENGTH_MISMATCH, got %#x.\n", status);
+    ok(status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %#x.\n", status);
 
+    /* test ProcessDebugFlags of current process */
     status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessDebugFlags,
             &debug_flags, sizeof(debug_flags), NULL);
-    ok(!status || broken(status == STATUS_INVALID_INFO_CLASS) /* NT4 */, "NtQueryInformationProcess failed, status %#x.\n", status);
-    ok(debug_flags == TRUE|| broken(status == STATUS_INVALID_INFO_CLASS) /* NT4 */, "Expected flag TRUE, got %x.\n", debug_flags);
-
-    status = pNtQueryInformationProcess(pi.hProcess, ProcessDebugFlags,
-            &debug_flags, sizeof(debug_flags), NULL);
-    ok(!status || broken(status == STATUS_INVALID_INFO_CLASS) /* NT4 */, "NtQueryInformationProcess failed, status %#x.\n", status);
-    ok(debug_flags == FALSE || broken(status == STATUS_INVALID_INFO_CLASS) /* NT4 */, "Expected flag FALSE, got %x.\n", debug_flags);
+    ok(!status, "NtQueryInformationProcess failed, status %#x.\n", status);
+    ok(debug_flags == TRUE, "Expected flag TRUE, got %x.\n", debug_flags);
 
-    for (;;)
+    for (i = 0; i < sizeof(test_flags)/sizeof(test_flags[0]); i++)
     {
-        DEBUG_EVENT ev;
+        DWORD expected_flags = !(test_flags[i] & DEBUG_ONLY_THIS_PROCESS);
+        sprintf(cmdline, "%s %s %s", argv[0], argv[1], "debuggee");
 
-        ret = WaitForDebugEvent(&ev, INFINITE);
-        ok(ret, "WaitForDebugEvent failed, last error %#x.\n", GetLastError());
-        if (!ret) break;
+        si.cb = sizeof(si);
+        ret = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, test_flags[i], NULL, NULL, &si, &pi);
+        ok(ret, "CreateProcess failed, last error %#x.\n", GetLastError());
 
-        if (ev.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT) break;
+        if (!(test_flags[i] & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS)))
+        {
+            /* test ProcessDebugFlags before attaching with debugger */
+            status = pNtQueryInformationProcess(pi.hProcess, ProcessDebugFlags,
+                    &debug_flags, sizeof(debug_flags), NULL);
+            ok(!status, "NtQueryInformationProcess failed, status %#x.\n", status);
+            ok(debug_flags == TRUE, "Expected flag TRUE, got %x.\n", debug_flags);
+
+            ret = DebugActiveProcess(pi.dwProcessId);
+            ok(ret, "DebugActiveProcess failed, last error %#x.\n", GetLastError());
+            expected_flags = FALSE;
+        }
 
-        ret = ContinueDebugEvent(ev.dwProcessId, ev.dwThreadId, DBG_CONTINUE);
-        ok(ret, "ContinueDebugEvent failed, last error %#x.\n", GetLastError());
-        if (!ret) break;
-    }
+        /* test ProcessDebugFlags after attaching with debugger */
+        status = pNtQueryInformationProcess(pi.hProcess, ProcessDebugFlags,
+                &debug_flags, sizeof(debug_flags), NULL);
+        ok(!status, "NtQueryInformationProcess failed, status %#x.\n", status);
+        ok(debug_flags == expected_flags, "Expected flag %x, got %x.\n", expected_flags, debug_flags);
 
-    ret = CloseHandle(pi.hThread);
-    ok(ret, "CloseHandle failed, last error %#x.\n", GetLastError());
-    ret = CloseHandle(pi.hProcess);
-    ok(ret, "CloseHandle failed, last error %#x.\n", GetLastError());
+        if (!(test_flags[i] & CREATE_SUSPENDED))
+        {
+            /* Continue a couple of times to make sure the process is fully initialized,
+             * otherwise Windows XP deadlocks in the following DebugActiveProcess(). */
+            for (;;)
+            {
+                ret = WaitForDebugEvent(&ev, 1000);
+                ok(ret, "WaitForDebugEvent failed, last error %#x.\n", GetLastError());
+                if (!ret) break;
+
+                if (ev.dwDebugEventCode == LOAD_DLL_DEBUG_EVENT) break;
+
+                ret = ContinueDebugEvent(ev.dwProcessId, ev.dwThreadId, DBG_CONTINUE);
+                ok(ret, "ContinueDebugEvent failed, last error %#x.\n", GetLastError());
+                if (!ret) break;
+            }
+
+            result = SuspendThread(pi.hThread);
+            ok(result == 0, "Expected 0, got %u.\n", result);
+        }
+
+        ret = DebugActiveProcessStop(pi.dwProcessId);
+        ok(ret, "DebugActiveProcessStop failed, last error %#x.\n", GetLastError());
+
+        /* test ProcessDebugFlags after detaching debugger */
+        status = pNtQueryInformationProcess(pi.hProcess, ProcessDebugFlags,
+                &debug_flags, sizeof(debug_flags), NULL);
+        ok(!status, "NtQueryInformationProcess failed, status %#x.\n", status);
+        ok(debug_flags == expected_flags, "Expected flag %x, got %x.\n", expected_flags, debug_flags);
+
+        ret = DebugActiveProcess(pi.dwProcessId);
+        ok(ret, "DebugActiveProcess failed, last error %#x.\n", GetLastError());
+
+        /* test ProcessDebugFlags after re-attaching debugger */
+        status = pNtQueryInformationProcess(pi.hProcess, ProcessDebugFlags,
+                &debug_flags, sizeof(debug_flags), NULL);
+        ok(!status, "NtQueryInformationProcess failed, status %#x.\n", status);
+        ok(debug_flags == FALSE, "Expected flag FALSE, got %x.\n", debug_flags);
+
+        result = ResumeThread(pi.hThread);
+        todo_wine ok(result == 2, "Expected 2, got %u.\n", result);
+
+        /* Wait until the process is terminated. On Windows XP the process randomly
+         * gets stuck in a non-continuable exception, so stop after 100 iterations.
+         * On Windows 2003, the debugged process disappears (or stops?) without
+         * any EXIT_PROCESS_DEBUG_EVENT after a couple of events. */
+        for (j = 0; j < 100; j++)
+        {
+            ret = WaitForDebugEvent(&ev, 1000);
+            ok(ret || broken(GetLastError() == ERROR_SEM_TIMEOUT),
+                "WaitForDebugEvent failed, last error %#x.\n", GetLastError());
+            if (!ret) break;
+
+            if (ev.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT) break;
+
+            ret = ContinueDebugEvent(ev.dwProcessId, ev.dwThreadId, DBG_CONTINUE);
+            ok(ret, "ContinueDebugEvent failed, last error %#x.\n", GetLastError());
+            if (!ret) break;
+        }
+        ok(j < 100 || broken(j >= 100) /* Win XP */, "Expected less than 100 debug events.\n");
+
+        /* test ProcessDebugFlags after process has terminated */
+        status = pNtQueryInformationProcess(pi.hProcess, ProcessDebugFlags,
+                &debug_flags, sizeof(debug_flags), NULL);
+        ok(!status, "NtQueryInformationProcess failed, status %#x.\n", status);
+        ok(debug_flags == FALSE, "Expected flag FALSE, got %x.\n", debug_flags);
+
+        ret = CloseHandle(pi.hThread);
+        ok(ret, "CloseHandle failed, last error %#x.\n", GetLastError());
+        ret = CloseHandle(pi.hProcess);
+        ok(ret, "CloseHandle failed, last error %#x.\n", GetLastError());
+    }
 }
 
 static void test_readvirtualmemory(void)
@@ -1439,7 +1834,7 @@ static void test_mapprotection(void)
     status = pNtSetInformationProcess( GetCurrentProcess(), ProcessExecuteFlags, &flags, sizeof(flags) );
     ok( (status == STATUS_SUCCESS) || (status == STATUS_INVALID_INFO_CLASS), "Expected STATUS_SUCCESS, got %08x\n", status);
 
-    size.u.LowPart  = 0x1000;
+    size.u.LowPart  = 0x2000;
     size.u.HighPart = 0;
     status = pNtCreateSection ( &h,
         STANDARD_RIGHTS_REQUIRED | SECTION_QUERY | SECTION_MAP_READ | SECTION_MAP_WRITE | SECTION_MAP_EXECUTE,
@@ -1453,7 +1848,7 @@ static void test_mapprotection(void)
 
     offset.u.LowPart  = 0;
     offset.u.HighPart = 0;
-    count = 0x1000;
+    count = 0x2000;
     addr = NULL;
     status = pNtMapViewOfSection ( h, GetCurrentProcess(), &addr, 0, 0, &offset, &count, ViewShare, 0, PAGE_READWRITE);
     ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status);
@@ -1476,7 +1871,7 @@ static void test_mapprotection(void)
     ok( retlen == sizeof(info), "Expected STATUS_SUCCESS, got %08x\n", status);
     ok((info.Protect & ~PAGE_NOCACHE) == PAGE_READWRITE, "addr.Protect is not PAGE_READWRITE, but 0x%x\n", info.Protect);
 
-    status = pNtUnmapViewOfSection (GetCurrentProcess(), addr);
+    status = pNtUnmapViewOfSection( GetCurrentProcess(), (char *)addr + 0x1050 );
     ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status);
     pNtClose (h);
 
@@ -1488,12 +1883,17 @@ static void test_queryvirtualmemory(void)
 {
     NTSTATUS status;
     SIZE_T readcount;
+    static const WCHAR windowsW[] = {'w','i','n','d','o','w','s'};
     static const char teststring[] = "test string";
     static char datatestbuf[42] = "abc";
     static char rwtestbuf[42];
     MEMORY_BASIC_INFORMATION mbi;
     char stackbuf[42];
     HMODULE module;
+    char buffer_name[sizeof(MEMORY_SECTION_NAME) + MAX_PATH * sizeof(WCHAR)];
+    MEMORY_SECTION_NAME *msn = (MEMORY_SECTION_NAME *)buffer_name;
+    BOOL found;
+    int i;
 
     module = GetModuleHandleA( "ntdll.dll" );
     trace("Check flags of the PE header of NTDLL.DLL at %p\n", module);
@@ -1567,6 +1967,43 @@ static void test_queryvirtualmemory(void)
             "mbi.Protect is 0x%x\n", mbi.Protect);
     }
     else skip( "bss is outside of module\n" );  /* this can happen on Mac OS */
+
+    trace("Check section name of NTDLL.DLL with invalid size\n");
+    module = GetModuleHandleA( "ntdll.dll" );
+    memset(msn, 0, sizeof(*msn));
+    readcount = 0;
+    status = pNtQueryVirtualMemory(NtCurrentProcess(), module, MemorySectionName, msn, sizeof(*msn), &readcount);
+    ok( status == STATUS_BUFFER_OVERFLOW, "Expected STATUS_BUFFER_OVERFLOW, got %08x\n", status);
+    ok( readcount > 0, "Expected readcount to be > 0\n");
+
+    trace("Check section name of NTDLL.DLL with invalid size\n");
+    module = GetModuleHandleA( "ntdll.dll" );
+    memset(msn, 0, sizeof(*msn));
+    readcount = 0;
+    status = pNtQueryVirtualMemory(NtCurrentProcess(), module, MemorySectionName, msn, sizeof(*msn) - 1, &readcount);
+    ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status);
+    ok( readcount > 0, "Expected readcount to be > 0\n");
+
+    trace("Check section name of NTDLL.DLL\n");
+    module = GetModuleHandleA( "ntdll.dll" );
+    memset(msn, 0x55, sizeof(*msn));
+    memset(buffer_name, 0x77, sizeof(buffer_name));
+    readcount = 0;
+    status = pNtQueryVirtualMemory(NtCurrentProcess(), module, MemorySectionName, msn, sizeof(buffer_name), &readcount);
+    ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status);
+    ok( readcount > 0, "Expected readcount to be > 0\n");
+    trace ("Section Name: %s\n", wine_dbgstr_w(msn->SectionFileName.Buffer));
+    pRtlDowncaseUnicodeString( &msn->SectionFileName, &msn->SectionFileName, FALSE );
+    for (found = FALSE, i = (msn->SectionFileName.Length - sizeof(windowsW)) / sizeof(WCHAR); i >= 0; i--)
+        found |= !memcmp( &msn->SectionFileName.Buffer[i], windowsW, sizeof(windowsW) );
+    ok( found, "Section name does not contain \"Windows\"\n");
+
+    trace("Check section name of non mapped memory\n");
+    memset(msn, 0, sizeof(*msn));
+    readcount = 0;
+    status = pNtQueryVirtualMemory(NtCurrentProcess(), &buffer_name, MemorySectionName, msn, sizeof(buffer_name), &readcount);
+    ok( status == STATUS_INVALID_ADDRESS, "Expected STATUS_INVALID_ADDRESS, got %08x\n", status);
+    ok( readcount == 0 || broken(readcount != 0) /* wow64 */, "Expected readcount to be 0\n");
 }
 
 static void test_affinity(void)
@@ -1780,6 +2217,39 @@ static void test_thread_start_address(void)
     CloseHandle(thread);
 }
 
+static void test_query_data_alignment(void)
+{
+    ULONG ReturnLength;
+    NTSTATUS status;
+    DWORD value;
+
+    value = 0xdeadbeef;
+    status = pNtQuerySystemInformation(SystemRecommendedSharedDataAlignment, &value, sizeof(value), &ReturnLength);
+    ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status);
+    ok(sizeof(value) == ReturnLength, "Inconsistent length %u\n", ReturnLength);
+    ok(value == 64, "Expected 64, got %u\n", value);
+}
+
+static void test_working_set_limit(void)
+{
+    DWORD_PTR lower = 0, upper = ~(DWORD_PTR)0;
+    MEMORY_BASIC_INFORMATION mbi;
+    SIZE_T readcount;
+    NTSTATUS status;
+
+    while (lower != upper)
+    {
+        DWORD_PTR check = (lower >> 1) + (upper >> 1) + (lower & upper & 1);
+        status = pNtQueryVirtualMemory(NtCurrentProcess(), (void *)check, MemoryBasicInformation,
+                                       &mbi, sizeof(MEMORY_BASIC_INFORMATION), &readcount);
+        if (status == STATUS_INVALID_PARAMETER) upper = check;
+        else lower = check + 1;
+    }
+
+    trace("working set limit is %p\n", (void *)upper);
+    ok(upper != ~(DWORD_PTR)0, "expected != ~(DWORD_PTR)0\n");
+}
+
 START_TEST(info)
 {
     char **argv;
@@ -1825,6 +2295,10 @@ START_TEST(info)
     trace("Starting test_query_handle()\n");
     test_query_handle();
 
+    /* 0x40 SystemHandleInformation */
+    trace("Starting test_query_handle_ex()\n");
+    test_query_handle_ex();
+
     /* 0x15 SystemCacheInformation */
     trace("Starting test_query_cache()\n");
     test_query_cache();
@@ -1844,6 +2318,7 @@ START_TEST(info)
     /* 0x49 SystemLogicalProcessorInformation */
     trace("Starting test_query_logicalproc()\n");
     test_query_logicalproc();
+    test_query_logicalprocex();
 
     /* NtPowerInformation */
 
@@ -1873,6 +2348,10 @@ START_TEST(info)
     trace("Starting test_process_debug_port()\n");
     test_query_process_debug_port(argc, argv);
 
+    /* 0x12 ProcessPriorityClass */
+    trace("Starting test_query_process_priority()\n");
+    test_query_process_priority();
+
     /* 0x14 ProcessHandleCount */
     trace("Starting test_query_process_handlecount()\n");
     test_query_process_handlecount();
@@ -1911,4 +2390,10 @@ START_TEST(info)
 
     trace("Starting test_thread_start_address()\n");
     test_thread_start_address();
+
+    trace("Starting test_query_data_alignment()\n");
+    test_query_data_alignment();
+
+    trace("Starting test_working_set_limit()\n");
+    test_working_set_limit();
 }
index f13ae88..4562e67 100755 (executable)
@@ -33,6 +33,8 @@ static VOID     (WINAPI *pRtlFreeAnsiString)(PSTRING);
 static NTSTATUS (WINAPI *pRtlInt64ToUnicodeString)(ULONGLONG, ULONG, UNICODE_STRING *);
 static NTSTATUS (WINAPI *pRtlLargeIntegerToChar)(ULONGLONG *, ULONG, ULONG, PCHAR);
 static NTSTATUS (WINAPI *pRtlUnicodeStringToAnsiString)(STRING *, const UNICODE_STRING *, BOOLEAN);
+static void     (WINAPI *p_alldvrm)(LONGLONG, LONGLONG);
+static void     (WINAPI *p_aulldvrm)(ULONGLONG, ULONGLONG);
 
 
 static void InitFunctionPtrs(void)
@@ -45,6 +47,8 @@ static void InitFunctionPtrs(void)
        pRtlInt64ToUnicodeString = (void *)GetProcAddress(hntdll, "RtlInt64ToUnicodeString");
        pRtlLargeIntegerToChar = (void *)GetProcAddress(hntdll, "RtlLargeIntegerToChar");
        pRtlUnicodeStringToAnsiString = (void *)GetProcAddress(hntdll, "RtlUnicodeStringToAnsiString");
+        p_alldvrm = (void *)GetProcAddress(hntdll, "_alldvrm");
+        p_aulldvrm = (void *)GetProcAddress(hntdll, "_aulldvrm");
     } /* if */
 }
 
@@ -113,10 +117,9 @@ static void test_RtlExtendedMagicDivide(void)
     for (i = 0; i < NB_MAGIC_DIVIDE; i++) {
        result = pRtlExtendedMagicDivide(magic_divide[i].a, magic_divide[i].b, magic_divide[i].shift);
        ok(result == magic_divide[i].result,
-           "call failed: RtlExtendedMagicDivide(0x%x%08x, 0x%x%08x, %d) has result 0x%x%08x, expected 0x%x%08x\n",
-          (DWORD)(magic_divide[i].a >> 32), (DWORD)magic_divide[i].a, (DWORD)(magic_divide[i].b >> 32),
-          (DWORD)magic_divide[i].b, magic_divide[i].shift, (DWORD)(result >> 32), (DWORD)result,
-          (DWORD)(magic_divide[i].result >> 32), (DWORD)magic_divide[i].result);
+           "call failed: RtlExtendedMagicDivide(0x%s, 0x%s, %d) has result 0x%s, expected 0x%s\n",
+          wine_dbgstr_longlong(magic_divide[i].a), wine_dbgstr_longlong(magic_divide[i].b), magic_divide[i].shift,
+       wine_dbgstr_longlong(result), wine_dbgstr_longlong(magic_divide[i].result));
     }
 }
 
@@ -332,14 +335,12 @@ static void one_RtlInt64ToUnicodeString_test(int test_num, const largeint2str_t
        } /* if */
     } else {
        ok(result == largeint2str->result,
-           "(test %d): RtlInt64ToUnicodeString(0x%x%08x, %d, [out]) has result %x, expected: %x\n",
-          test_num, (DWORD)(largeint2str->value >> 32), (DWORD)largeint2str->value,
-          largeint2str->base, result, largeint2str->result);
+           "(test %d): RtlInt64ToUnicodeString(0x%s, %d, [out]) has result %x, expected: %x\n",
+          test_num, wine_dbgstr_longlong(largeint2str->value), largeint2str->base, result, largeint2str->result);
        if (result == STATUS_SUCCESS) {
            ok(unicode_string.Buffer[unicode_string.Length/sizeof(WCHAR)] == '\0',
-               "(test %d): RtlInt64ToUnicodeString(0x%x%08x, %d, [out]) string \"%s\" is not NULL terminated\n",
-              test_num, (DWORD)(largeint2str->value >> 32), (DWORD)largeint2str->value,
-              largeint2str->base, ansi_str.Buffer);
+               "(test %d): RtlInt64ToUnicodeString(0x%s, %d, [out]) string \"%s\" is not NULL terminated\n",
+              test_num, wine_dbgstr_longlong(largeint2str->value), largeint2str->base, ansi_str.Buffer);
        } /* if */
     } /* if */
     ok(memcmp(unicode_string.Buffer, expected_unicode_string.Buffer, LARGE_STRI_BUFFER_LENGTH * sizeof(WCHAR)) == 0,
@@ -347,12 +348,12 @@ static void one_RtlInt64ToUnicodeString_test(int test_num, const largeint2str_t
        test_num, (DWORD)(largeint2str->value >>32), (DWORD)largeint2str->value, largeint2str->base, 
        ansi_str.Buffer, expected_ansi_str.Buffer);
     ok(unicode_string.Length == expected_unicode_string.Length,
-       "(test %d): RtlInt64ToUnicodeString(0x%x%08x, %d, [out]) string has Length %d, expected: %d\n",
-       test_num, (DWORD)(largeint2str->value >> 32), (DWORD)largeint2str->value, largeint2str->base,
+       "(test %d): RtlInt64ToUnicodeString(0x%s, %d, [out]) string has Length %d, expected: %d\n",
+       test_num, wine_dbgstr_longlong(largeint2str->value), largeint2str->base,
        unicode_string.Length, expected_unicode_string.Length);
     ok(unicode_string.MaximumLength == expected_unicode_string.MaximumLength,
-       "(test %d): RtlInt64ToUnicodeString(0x%x%08x, %d, [out]) string has MaximumLength %d, expected: %d\n",
-       test_num, (DWORD)(largeint2str->value >> 32), (DWORD)largeint2str->value, largeint2str->base,
+       "(test %d): RtlInt64ToUnicodeString(0x%s, %d, [out]) string has MaximumLength %d, expected: %d\n",
+       test_num, wine_dbgstr_longlong(largeint2str->value), largeint2str->base,
        unicode_string.MaximumLength, expected_unicode_string.MaximumLength);
     pRtlFreeAnsiString(&expected_ansi_str);
     pRtlFreeAnsiString(&ansi_str);
@@ -392,12 +393,12 @@ static void one_RtlLargeIntegerToChar_test(int test_num, const largeint2str_t *l
        result = pRtlLargeIntegerToChar(&value, largeint2str->base, largeint2str->MaximumLength, dest_str);
     } /* if */
     ok(result == largeint2str->result,
-       "(test %d): RtlLargeIntegerToChar(0x%x%08x, %d, %d, [out]) has result %x, expected: %x\n",
-       test_num, (DWORD)(largeint2str->value >> 32), (DWORD)largeint2str->value, largeint2str->base,
+       "(test %d): RtlLargeIntegerToChar(0x%s, %d, %d, [out]) has result %x, expected: %x\n",
+       test_num, wine_dbgstr_longlong(largeint2str->value), largeint2str->base,
        largeint2str->MaximumLength, result, largeint2str->result);
     ok(memcmp(dest_str, largeint2str->Buffer, LARGE_STRI_BUFFER_LENGTH) == 0,
-       "(test %d): RtlLargeIntegerToChar(0x%x%08x, %d, %d, [out]) assigns string \"%s\", expected: \"%s\"\n",
-       test_num, (DWORD)(largeint2str->value >> 32), (DWORD)largeint2str->value, largeint2str->base,
+       "(test %d): RtlLargeIntegerToChar(0x%s, %d, %d, [out]) assigns string \"%s\", expected: \"%s\"\n",
+       test_num, wine_dbgstr_longlong(largeint2str->value), largeint2str->base,
        largeint2str->MaximumLength, dest_str, largeint2str->Buffer);
 }
 
@@ -415,30 +416,89 @@ static void test_RtlLargeIntegerToChar(void)
     value = largeint2str[0].value;
     result = pRtlLargeIntegerToChar(&value, 20, largeint2str[0].MaximumLength, NULL);
     ok(result == STATUS_INVALID_PARAMETER,
-       "(test a): RtlLargeIntegerToChar(0x%x%08x, %d, %d, NULL) has result %x, expected: %x\n",
-       (DWORD)(largeint2str[0].value >> 32), (DWORD)largeint2str[0].value, 20,
+       "(test a): RtlLargeIntegerToChar(0x%s, %d, %d, NULL) has result %x, expected: %x\n",
+       wine_dbgstr_longlong(largeint2str[0].value), 20,
        largeint2str[0].MaximumLength, result, STATUS_INVALID_PARAMETER);
 
     result = pRtlLargeIntegerToChar(&value, 20, 0, NULL);
     ok(result == STATUS_INVALID_PARAMETER,
-       "(test b): RtlLargeIntegerToChar(0x%x%08x, %d, %d, NULL) has result %x, expected: %x\n",
-       (DWORD)(largeint2str[0].value >> 32), (DWORD)largeint2str[0].value, 20,
+       "(test b): RtlLargeIntegerToChar(0x%s, %d, %d, NULL) has result %x, expected: %x\n",
+       wine_dbgstr_longlong(largeint2str[0].value), 20,
        largeint2str[0].MaximumLength, result, STATUS_INVALID_PARAMETER);
 
     result = pRtlLargeIntegerToChar(&value, largeint2str[0].base, 0, NULL);
     ok(result == STATUS_BUFFER_OVERFLOW,
-       "(test c): RtlLargeIntegerToChar(0x%x%08x, %d, %d, NULL) has result %x, expected: %x\n",
-       (DWORD)(largeint2str[0].value >> 32), (DWORD)largeint2str[0].value,
-       largeint2str[0].base, 0, result, STATUS_BUFFER_OVERFLOW);
+       "(test c): RtlLargeIntegerToChar(0x%s, %d, %d, NULL) has result %x, expected: %x\n",
+       wine_dbgstr_longlong(largeint2str[0].value), largeint2str[0].base, 0, result, STATUS_BUFFER_OVERFLOW);
 
     result = pRtlLargeIntegerToChar(&value, largeint2str[0].base, largeint2str[0].MaximumLength, NULL);
     ok(result == STATUS_ACCESS_VIOLATION,
-       "(test d): RtlLargeIntegerToChar(0x%x%08x, %d, %d, NULL) has result %x, expected: %x\n",
-       (DWORD)(largeint2str[0].value >> 32), (DWORD)largeint2str[0].value,
+       "(test d): RtlLargeIntegerToChar(0x%s, %d, %d, NULL) has result %x, expected: %x\n",
+       wine_dbgstr_longlong(largeint2str[0].value),
        largeint2str[0].base, largeint2str[0].MaximumLength, result, STATUS_ACCESS_VIOLATION);
 }
 
 
+#ifdef __i386__
+
+#include "pshpack1.h"
+struct lldvrm_thunk
+{
+    BYTE push_ebx;      /* pushl %ebx */
+    DWORD push_esp1;    /* pushl 24(%esp) */
+    DWORD push_esp2;    /* pushl 24(%esp) */
+    DWORD push_esp3;    /* pushl 24(%esp) */
+    DWORD push_esp4;    /* pushl 24(%esp) */
+    DWORD call;         /* call 24(%esp) */
+    WORD mov_ecx_eax;   /* movl %ecx,%eax */
+    WORD mov_ebx_edx;   /* movl %ebx,%edx */
+    BYTE pop_ebx;       /* popl %ebx */
+    BYTE ret;           /* ret */
+};
+#include "poppack.h"
+
+static void test__alldvrm(void)
+{
+    struct lldvrm_thunk *thunk = VirtualAlloc(NULL, sizeof(*thunk), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
+    ULONGLONG (CDECL *call_lldvrm_func)(void *func, ULONGLONG, ULONGLONG) = (void *)thunk;
+    ULONGLONG ret;
+
+    memset(thunk, 0x90, sizeof(*thunk));
+    thunk->push_ebx  = 0x53;        /* pushl %ebx */
+    thunk->push_esp1 = 0x182474ff;  /* pushl 24(%esp) */
+    thunk->push_esp2 = 0x182474ff;  /* pushl 24(%esp) */
+    thunk->push_esp3 = 0x182474ff;  /* pushl 24(%esp) */
+    thunk->push_esp4 = 0x182474ff;  /* pushl 24(%esp) */
+    thunk->call      = 0x182454ff;  /* call 24(%esp) */
+    thunk->pop_ebx   = 0x5b;        /* popl %ebx */
+    thunk->ret       = 0xc3;        /* ret */
+
+    ret = call_lldvrm_func(p_alldvrm, 0x0123456701234567ULL, 3);
+    ok(ret == 0x61172255b66c77ULL, "got %x%08x\n", (DWORD)(ret >> 32), (DWORD)ret);
+    ret = call_lldvrm_func(p_alldvrm, 0x0123456701234567ULL, -3);
+    ok(ret == 0xff9ee8ddaa499389ULL, "got %x%08x\n", (DWORD)(ret >> 32), (DWORD)ret);
+
+    ret = call_lldvrm_func(p_aulldvrm, 0x0123456701234567ULL, 3);
+    ok(ret == 0x61172255b66c77ULL, "got %x%08x\n", (DWORD)(ret >> 32), (DWORD)ret);
+    ret = call_lldvrm_func(p_aulldvrm, 0x0123456701234567ULL, -3);
+    ok(ret == 0, "got %x%08x\n", (DWORD)(ret >> 32), (DWORD)ret);
+
+    thunk->mov_ecx_eax = 0xc889;
+    thunk->mov_ebx_edx = 0xda89;
+
+    ret = call_lldvrm_func(p_alldvrm, 0x0123456701234567ULL, 3);
+    ok(ret == 2, "got %x%08x\n", (DWORD)(ret >> 32), (DWORD)ret);
+    ret = call_lldvrm_func(p_alldvrm, 0x0123456701234567ULL, -3);
+    ok(ret == 2, "got %x%08x\n", (DWORD)(ret >> 32), (DWORD)ret);
+
+    ret = call_lldvrm_func(p_aulldvrm, 0x0123456701234567ULL, 3);
+    ok(ret == 2, "got %x%08x\n", (DWORD)(ret >> 32), (DWORD)ret);
+    ret = call_lldvrm_func(p_aulldvrm, 0x0123456701234567ULL, -3);
+    ok(ret == 0x123456701234567ULL, "got %x%08x\n", (DWORD)(ret >> 32), (DWORD)ret);
+}
+#endif  /* __i386__ */
+
+
 START_TEST(large_int)
 {
     InitFunctionPtrs();
@@ -449,4 +509,8 @@ START_TEST(large_int)
            test_RtlInt64ToUnicodeString();
     if (pRtlLargeIntegerToChar)
         test_RtlLargeIntegerToChar();
+
+#ifdef __i386__
+    test__alldvrm();
+#endif  /* __i386__ */
 }
index 8d88308..84ed6a4 100755 (executable)
 
 #include <stdarg.h>
 
-#ifndef _WIN32_WINNT
-#define _WIN32_WINNT 0x500 /* For NTSTATUS */
-#endif
-
 #include "ntstatus.h"
 #define WIN32_NO_STATUS
 #include "windef.h"
index 65e9260..651db0a 100644 (file)
@@ -33,12 +33,25 @@ static NTSTATUS (WINAPI *pNtCreateEvent) ( PHANDLE, ACCESS_MASK, const POBJECT_A
 static NTSTATUS (WINAPI *pNtOpenEvent)   ( PHANDLE, ACCESS_MASK, const POBJECT_ATTRIBUTES);
 static NTSTATUS (WINAPI *pNtPulseEvent)  ( HANDLE, PULONG );
 static NTSTATUS (WINAPI *pNtQueryEvent)  ( HANDLE, EVENT_INFORMATION_CLASS, PVOID, ULONG, PULONG );
+static NTSTATUS (WINAPI *pNtCreateJobObject)( PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES );
+static NTSTATUS (WINAPI *pNtOpenJobObject)( PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES );
+static NTSTATUS (WINAPI *pNtCreateKey)( PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, ULONG,
+                                        const UNICODE_STRING *, ULONG, PULONG );
+static NTSTATUS (WINAPI *pNtOpenKey)( PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES );
+static NTSTATUS (WINAPI *pNtDeleteKey)( HANDLE );
+static NTSTATUS (WINAPI *pNtCreateMailslotFile)( PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PIO_STATUS_BLOCK,
+                                                 ULONG, ULONG, ULONG, PLARGE_INTEGER );
 static NTSTATUS (WINAPI *pNtCreateMutant)( PHANDLE, ACCESS_MASK, const POBJECT_ATTRIBUTES, BOOLEAN );
 static NTSTATUS (WINAPI *pNtOpenMutant)  ( PHANDLE, ACCESS_MASK, const POBJECT_ATTRIBUTES );
+static NTSTATUS (WINAPI *pNtQueryMutant) ( HANDLE, MUTANT_INFORMATION_CLASS, PVOID, ULONG, PULONG );
+static NTSTATUS (WINAPI *pNtReleaseMutant)( HANDLE, PLONG );
 static NTSTATUS (WINAPI *pNtCreateSemaphore)( PHANDLE, ACCESS_MASK,const POBJECT_ATTRIBUTES,LONG,LONG );
+static NTSTATUS (WINAPI *pNtOpenSemaphore)( PHANDLE, ACCESS_MASK, const POBJECT_ATTRIBUTES );
 static NTSTATUS (WINAPI *pNtCreateTimer) ( PHANDLE, ACCESS_MASK, const POBJECT_ATTRIBUTES, TIMER_TYPE );
+static NTSTATUS (WINAPI *pNtOpenTimer)( PHANDLE, ACCESS_MASK, const POBJECT_ATTRIBUTES );
 static NTSTATUS (WINAPI *pNtCreateSection)( PHANDLE, ACCESS_MASK, const POBJECT_ATTRIBUTES, const PLARGE_INTEGER,
                                             ULONG, ULONG, HANDLE );
+static NTSTATUS (WINAPI *pNtOpenSection)( PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES );
 static NTSTATUS (WINAPI *pNtOpenFile)    ( PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PIO_STATUS_BLOCK, ULONG, ULONG );
 static NTSTATUS (WINAPI *pNtClose)       ( HANDLE );
 static NTSTATUS (WINAPI *pNtCreateNamedPipeFile)( PHANDLE, ULONG, POBJECT_ATTRIBUTES, PIO_STATUS_BLOCK,
@@ -55,11 +68,28 @@ static NTSTATUS (WINAPI *pNtOpenKeyedEvent)( HANDLE *, ACCESS_MASK, const OBJECT
 static NTSTATUS (WINAPI *pNtWaitForKeyedEvent)( HANDLE, const void *, BOOLEAN, const LARGE_INTEGER * );
 static NTSTATUS (WINAPI *pNtReleaseKeyedEvent)( HANDLE, const void *, BOOLEAN, const LARGE_INTEGER * );
 static NTSTATUS (WINAPI *pNtCreateIoCompletion)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, ULONG);
+static NTSTATUS (WINAPI *pNtOpenIoCompletion)( PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES );
+static NTSTATUS (WINAPI *pNtQuerySystemInformation)(SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG);
 
 #define KEYEDEVENT_WAIT       0x0001
 #define KEYEDEVENT_WAKE       0x0002
 #define KEYEDEVENT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | 0x0003)
 
+#define ROUND_UP(value, alignment) (((value) + ((alignment) - 1)) & ~((alignment)-1))
+
+static LPCSTR wine_dbgstr_us( const UNICODE_STRING *us )
+{
+    if (!us) return "(null)";
+    return wine_dbgstr_wn(us->Buffer, us->Length / sizeof(WCHAR));
+}
+
+static inline int strncmpW( const WCHAR *str1, const WCHAR *str2, int n )
+{
+    if (n <= 0) return 0;
+    while ((--n > 0) && *str1 && (*str1 == *str2)) { str1++; str2++; }
+    return *str1 - *str2;
+}
+
 static void test_case_sensitive (void)
 {
     static const WCHAR buffer1[] = {'\\','B','a','s','e','N','a','m','e','d','O','b','j','e','c','t','s','\\','t','e','s','t',0};
@@ -165,33 +195,34 @@ static void test_namespace_pipe(void)
        status == STATUS_OBJECT_NAME_INVALID, /* vista */
         "NtOpenFile should have failed with STATUS_OBJECT_NAME_NOT_FOUND got(%08x)\n", status);
 
+    str.Length -= 4 * sizeof(WCHAR);
+    status = pNtOpenFile(&h, GENERIC_READ, &attr, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE, 0);
+    ok(status == STATUS_SUCCESS, "NtOpenFile should have succeeded got %08x\n", status);
+    pNtClose( h );
+
+    str.Length -= sizeof(WCHAR);
+    status = pNtOpenFile(&h, GENERIC_READ, &attr, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE, 0);
+    ok(status == STATUS_SUCCESS, "NtOpenFile should have succeeded got %08x\n", status);
+    pNtClose( h );
+
     pNtClose(pipe);
 }
 
 #define DIRECTORY_QUERY (0x0001)
 #define SYMBOLIC_LINK_QUERY 0x0001
 
-#define DIR_TEST_CREATE_FAILURE(h,e) \
-    status = pNtCreateDirectoryObject(h, DIRECTORY_QUERY, &attr);\
-    ok(status == e,"NtCreateDirectoryObject should have failed with %s got(%08x)\n", #e, status);
-#define DIR_TEST_OPEN_FAILURE(h,e) \
-    status = pNtOpenDirectoryObject(h, DIRECTORY_QUERY, &attr);\
-    ok(status == e,"NtOpenDirectoryObject should have failed with %s got(%08x)\n", #e, status);
-#define DIR_TEST_CREATE_OPEN_FAILURE(h,n,e) \
-    pRtlCreateUnicodeStringFromAsciiz(&str, n);\
-    DIR_TEST_CREATE_FAILURE(h,e) DIR_TEST_OPEN_FAILURE(h,e)\
-    pRtlFreeUnicodeString(&str);
-
-#define DIR_TEST_CREATE_SUCCESS(h) \
-    status = pNtCreateDirectoryObject(h, DIRECTORY_QUERY, &attr); \
-    ok(status == STATUS_SUCCESS, "Failed to create Directory(%08x)\n", status);
-#define DIR_TEST_OPEN_SUCCESS(h) \
-    status = pNtOpenDirectoryObject(h, DIRECTORY_QUERY, &attr); \
-    ok(status == STATUS_SUCCESS, "Failed to open Directory(%08x)\n", status);
-#define DIR_TEST_CREATE_OPEN_SUCCESS(h,n) \
-    pRtlCreateUnicodeStringFromAsciiz(&str, n);\
-    DIR_TEST_CREATE_SUCCESS(&h) pNtClose(h); DIR_TEST_OPEN_SUCCESS(&h) pNtClose(h); \
-    pRtlFreeUnicodeString(&str);
+#define DIR_TEST_CREATE_OPEN(n,e) \
+    do { \
+        HANDLE h; \
+        pRtlCreateUnicodeStringFromAsciiz(&str, n); \
+        status = pNtCreateDirectoryObject( &h, DIRECTORY_QUERY, &attr ); \
+        ok( status == e, "NtCreateDirectoryObject(%s) got %08x\n", n, status ); \
+        if (!status) pNtClose( h ); \
+        status = pNtOpenDirectoryObject( &h, DIRECTORY_QUERY, &attr ); \
+        ok( status == e, "NtOpenDirectoryObject(%s) got %08x\n", n, status ); \
+        if (!status) pNtClose( h ); \
+        pRtlFreeUnicodeString(&str); \
+    } while(0)
 
 static BOOL is_correct_dir( HANDLE dir, const char *name )
 {
@@ -216,13 +247,14 @@ static HANDLE get_base_dir(void)
     UNICODE_STRING str;
     OBJECT_ATTRIBUTES attr;
     HANDLE dir, h;
-    unsigned int i;
+    char name[40];
 
     h = CreateMutexA(NULL, FALSE, objname);
     ok(h != 0, "CreateMutexA failed got ret=%p (%d)\n", h, GetLastError());
     InitializeObjectAttributes(&attr, &str, OBJ_OPENIF, 0, NULL);
 
-    pRtlCreateUnicodeStringFromAsciiz(&str, "\\BaseNamedObjects\\Local");
+    sprintf( name, "\\BaseNamedObjects\\Session\\%u", NtCurrentTeb()->Peb->SessionId );
+    pRtlCreateUnicodeStringFromAsciiz(&str, name );
     status = pNtOpenDirectoryObject(&dir, DIRECTORY_QUERY, &attr);
     pRtlFreeUnicodeString(&str);
     if (!status && is_correct_dir( dir, objname )) goto done;
@@ -234,16 +266,6 @@ static HANDLE get_base_dir(void)
     if (!status && is_correct_dir( dir, objname )) goto done;
     if (!status) pNtClose( dir );
 
-    for (i = 0; i < 20; i++)
-    {
-        char name[40];
-        sprintf( name, "\\BaseNamedObjects\\Session\\%u", i );
-        pRtlCreateUnicodeStringFromAsciiz(&str, name );
-        status = pNtOpenDirectoryObject(&dir, DIRECTORY_QUERY, &attr);
-        pRtlFreeUnicodeString(&str);
-        if (!status && is_correct_dir( dir, objname )) goto done;
-        if (!status) pNtClose( dir );
-    }
     dir = 0;
 
 done:
@@ -262,10 +284,12 @@ static void test_name_collisions(void)
 
     InitializeObjectAttributes(&attr, &str, 0, 0, NULL);
     pRtlCreateUnicodeStringFromAsciiz(&str, "\\");
-    DIR_TEST_CREATE_FAILURE(&h, STATUS_OBJECT_NAME_COLLISION)
+    status = pNtCreateDirectoryObject( &h, DIRECTORY_QUERY, &attr );
+    ok( status == STATUS_OBJECT_NAME_COLLISION, "NtCreateDirectoryObject got %08x\n", status );
     InitializeObjectAttributes(&attr, &str, OBJ_OPENIF, 0, NULL);
 
-    DIR_TEST_CREATE_FAILURE(&h, STATUS_OBJECT_NAME_EXISTS)
+    status = pNtCreateDirectoryObject( &h, DIRECTORY_QUERY, &attr );
+    ok( status == STATUS_OBJECT_NAME_EXISTS, "NtCreateDirectoryObject got %08x\n", status );
     pNtClose(h);
     status = pNtCreateMutant(&h, GENERIC_ALL, &attr, FALSE);
     ok(status == STATUS_OBJECT_TYPE_MISMATCH,
@@ -356,12 +380,529 @@ static void test_name_collisions(void)
     pNtClose(dir);
 }
 
+static void test_all_kernel_objects( UINT line, OBJECT_ATTRIBUTES *attr,
+                                     NTSTATUS create_expect, NTSTATUS open_expect )
+{
+    UNICODE_STRING target;
+    LARGE_INTEGER size;
+    NTSTATUS status, status2;
+    HANDLE ret, ret2;
+
+    pRtlCreateUnicodeStringFromAsciiz( &target, "\\DosDevices" );
+    size.QuadPart = 4096;
+
+    status = pNtCreateMutant( &ret, GENERIC_ALL, attr, FALSE );
+    ok( status == create_expect, "%u: NtCreateMutant failed %x\n", line, status );
+    status2 = pNtOpenMutant( &ret2, GENERIC_ALL, attr );
+    ok( status2 == open_expect, "%u: NtOpenMutant failed %x\n", line, status2 );
+    if (!status) pNtClose( ret );
+    if (!status2) pNtClose( ret2 );
+    status = pNtCreateSemaphore( &ret, GENERIC_ALL, attr, 1, 2 );
+    ok( status == create_expect, "%u: NtCreateSemaphore failed %x\n", line, status );
+    status2 = pNtOpenSemaphore( &ret2, GENERIC_ALL, attr );
+    ok( status2 == open_expect, "%u: NtOpenSemaphore failed %x\n", line, status2 );
+    if (!status) pNtClose( ret );
+    if (!status2) pNtClose( ret2 );
+    status = pNtCreateEvent( &ret, GENERIC_ALL, attr, 1, 0 );
+    ok( status == create_expect, "%u: NtCreateEvent failed %x\n", line, status );
+    status2 = pNtOpenEvent( &ret2, GENERIC_ALL, attr );
+    ok( status2 == open_expect, "%u: NtOpenEvent failed %x\n", line, status2 );
+    if (!status) pNtClose( ret );
+    if (!status2) pNtClose( ret2 );
+    status = pNtCreateKeyedEvent( &ret, GENERIC_ALL, attr, 0 );
+    ok( status == create_expect, "%u: NtCreateKeyedEvent failed %x\n", line, status );
+    status2 = pNtOpenKeyedEvent( &ret2, GENERIC_ALL, attr );
+    ok( status2 == open_expect, "%u: NtOpenKeyedEvent failed %x\n", line, status2 );
+    if (!status) pNtClose( ret );
+    if (!status2) pNtClose( ret2 );
+    status = pNtCreateTimer( &ret, GENERIC_ALL, attr, NotificationTimer );
+    ok( status == create_expect, "%u: NtCreateTimer failed %x\n", line, status );
+    status2 = pNtOpenTimer( &ret2, GENERIC_ALL, attr );
+    ok( status2 == open_expect, "%u: NtOpenTimer failed %x\n", line, status2 );
+    if (!status) pNtClose( ret );
+    if (!status2) pNtClose( ret2 );
+    status = pNtCreateIoCompletion( &ret, GENERIC_ALL, attr, 0 );
+    ok( status == create_expect, "%u: NtCreateCompletion failed %x\n", line, status );
+    status2 = pNtOpenIoCompletion( &ret2, GENERIC_ALL, attr );
+    ok( status2 == open_expect, "%u: NtOpenCompletion failed %x\n", line, status2 );
+    if (!status) pNtClose( ret );
+    if (!status2) pNtClose( ret2 );
+    status = pNtCreateJobObject( &ret, GENERIC_ALL, attr );
+    ok( status == create_expect, "%u: NtCreateJobObject failed %x\n", line, status );
+    status2 = pNtOpenJobObject( &ret2, GENERIC_ALL, attr );
+    ok( status2 == open_expect, "%u: NtOpenJobObject failed %x\n", line, status2 );
+    if (!status) pNtClose( ret );
+    if (!status2) pNtClose( ret2 );
+    status = pNtCreateDirectoryObject( &ret, GENERIC_ALL, attr );
+    ok( status == create_expect, "%u: NtCreateDirectoryObject failed %x\n", line, status );
+    status2 = pNtOpenDirectoryObject( &ret2, GENERIC_ALL, attr );
+    ok( status2 == open_expect, "%u: NtOpenDirectoryObject failed %x\n", line, status2 );
+    if (!status) pNtClose( ret );
+    if (!status2) pNtClose( ret2 );
+    status = pNtCreateSymbolicLinkObject( &ret, GENERIC_ALL, attr, &target );
+    ok( status == create_expect, "%u: NtCreateSymbolicLinkObject failed %x\n", line, status );
+    status2 = pNtOpenSymbolicLinkObject( &ret2, GENERIC_ALL, attr );
+    ok( status2 == open_expect, "%u: NtOpenSymbolicLinkObject failed %x\n", line, status2 );
+    if (!status) pNtClose( ret );
+    if (!status2) pNtClose( ret2 );
+    status = pNtCreateSection( &ret, SECTION_MAP_WRITE, attr, &size, PAGE_READWRITE, SEC_COMMIT, 0 );
+    ok( status == create_expect, "%u: NtCreateSection failed %x\n", line, status );
+    status2 = pNtOpenSection( &ret2, SECTION_MAP_WRITE, attr );
+    ok( status2 == open_expect, "%u: NtOpenSection failed %x\n", line, status2 );
+    if (!status) pNtClose( ret );
+    if (!status2) pNtClose( ret2 );
+    pRtlFreeUnicodeString( &target );
+}
+
+static void test_name_limits(void)
+{
+    static const WCHAR localW[]    = {'\\','B','a','s','e','N','a','m','e','d','O','b','j','e','c','t','s','\\','L','o','c','a','l',0};
+    static const WCHAR pipeW[]     = {'\\','D','e','v','i','c','e','\\','N','a','m','e','d','P','i','p','e','\\'};
+    static const WCHAR mailslotW[] = {'\\','D','e','v','i','c','e','\\','M','a','i','l','S','l','o','t','\\'};
+    static const WCHAR registryW[] = {'\\','R','E','G','I','S','T','R','Y','\\','M','a','c','h','i','n','e','\\','S','O','F','T','W','A','R','E','\\','M','i','c','r','o','s','o','f','t','\\'};
+    OBJECT_ATTRIBUTES attr, attr2, attr3;
+    IO_STATUS_BLOCK iosb;
+    LARGE_INTEGER size, timeout;
+    UNICODE_STRING str, str2, target;
+    NTSTATUS status;
+    HANDLE ret, ret2;
+    DWORD i;
+
+    InitializeObjectAttributes( &attr, &str, 0, 0, NULL );
+    InitializeObjectAttributes( &attr2, &str, 0, (HANDLE)0xdeadbeef, NULL );
+    InitializeObjectAttributes( &attr3, &str, 0, 0, NULL );
+    str.Buffer = HeapAlloc( GetProcessHeap(), 0, 65536 + sizeof(registryW));
+    str.MaximumLength = 65534;
+    for (i = 0; i < 65536 / sizeof(WCHAR); i++) str.Buffer[i] = 'a';
+    size.QuadPart = 4096;
+    pRtlCreateUnicodeStringFromAsciiz( &target, "\\DosDevices" );
+
+    if (!(attr.RootDirectory = get_base_dir()))
+    {
+        win_skip( "couldn't find the BaseNamedObjects dir\n" );
+        return;
+    }
+
+    str.Length = 0;
+    status = pNtCreateMutant( &ret, GENERIC_ALL, &attr2, FALSE );
+    ok( status == STATUS_SUCCESS, "%u: NtCreateMutant failed %x\n", str.Length, status );
+    attr3.RootDirectory = ret;
+    status = pNtOpenMutant( &ret2, GENERIC_ALL, &attr );
+    ok( status == STATUS_OBJECT_TYPE_MISMATCH, "%u: NtOpenMutant failed %x\n", str.Length, status );
+    status = pNtOpenMutant( &ret2, GENERIC_ALL, &attr3 );
+    ok( status == STATUS_OBJECT_TYPE_MISMATCH || status == STATUS_INVALID_HANDLE,
+        "%u: NtOpenMutant failed %x\n", str.Length, status );
+    pNtClose( ret );
+    status = pNtCreateSemaphore( &ret, GENERIC_ALL, &attr2, 1, 2 );
+    ok( status == STATUS_SUCCESS, "%u: NtCreateSemaphore failed %x\n", str.Length, status );
+    attr3.RootDirectory = ret;
+    status = pNtOpenSemaphore( &ret2, GENERIC_ALL, &attr );
+    ok( status == STATUS_OBJECT_TYPE_MISMATCH, "%u: NtOpenSemaphore failed %x\n", str.Length, status );
+    status = pNtOpenSemaphore( &ret2, GENERIC_ALL, &attr3 );
+    ok( status == STATUS_OBJECT_TYPE_MISMATCH || status == STATUS_INVALID_HANDLE,
+        "%u: NtOpenSemaphore failed %x\n", str.Length, status );
+    pNtClose( ret );
+    status = pNtCreateEvent( &ret, GENERIC_ALL, &attr2, 1, 0 );
+    ok( status == STATUS_SUCCESS, "%u: NtCreateEvent failed %x\n", str.Length, status );
+    attr3.RootDirectory = ret;
+    status = pNtOpenEvent( &ret2, GENERIC_ALL, &attr );
+    ok( status == STATUS_OBJECT_TYPE_MISMATCH, "%u: NtOpenEvent failed %x\n", str.Length, status );
+    status = pNtOpenEvent( &ret2, GENERIC_ALL, &attr3 );
+    ok( status == STATUS_OBJECT_TYPE_MISMATCH || status == STATUS_INVALID_HANDLE,
+        "%u: NtOpenEvent failed %x\n", str.Length, status );
+    pNtClose( ret );
+    status = pNtCreateKeyedEvent( &ret, GENERIC_ALL, &attr2, 0 );
+    ok( status == STATUS_SUCCESS, "%u: NtCreateKeyedEvent failed %x\n", str.Length, status );
+    attr3.RootDirectory = ret;
+    status = pNtOpenKeyedEvent( &ret2, GENERIC_ALL, &attr );
+    ok( status == STATUS_OBJECT_TYPE_MISMATCH, "%u: NtOpenKeyedEvent failed %x\n", str.Length, status );
+    status = pNtOpenKeyedEvent( &ret2, GENERIC_ALL, &attr3 );
+    ok( status == STATUS_OBJECT_TYPE_MISMATCH || status == STATUS_INVALID_HANDLE,
+        "%u: NtOpenKeyedEvent failed %x\n", str.Length, status );
+    pNtClose( ret );
+    status = pNtCreateTimer( &ret, GENERIC_ALL, &attr2, NotificationTimer );
+    ok( status == STATUS_SUCCESS, "%u: NtCreateTimer failed %x\n", str.Length, status );
+    attr3.RootDirectory = ret;
+    status = pNtOpenTimer( &ret2, GENERIC_ALL, &attr );
+    ok( status == STATUS_OBJECT_TYPE_MISMATCH, "%u: NtOpenTimer failed %x\n", str.Length, status );
+    status = pNtOpenTimer( &ret2, GENERIC_ALL, &attr3 );
+    ok( status == STATUS_OBJECT_TYPE_MISMATCH || status == STATUS_INVALID_HANDLE,
+        "%u: NtOpenTimer failed %x\n", str.Length, status );
+    pNtClose( ret );
+    status = pNtCreateIoCompletion( &ret, GENERIC_ALL, &attr2, 0 );
+    ok( status == STATUS_SUCCESS, "%u: NtCreateCompletion failed %x\n", str.Length, status );
+    attr3.RootDirectory = ret;
+    status = pNtOpenIoCompletion( &ret2, GENERIC_ALL, &attr );
+    ok( status == STATUS_OBJECT_TYPE_MISMATCH, "%u: NtOpenCompletion failed %x\n", str.Length, status );
+    status = pNtOpenIoCompletion( &ret2, GENERIC_ALL, &attr3 );
+    ok( status == STATUS_OBJECT_TYPE_MISMATCH || status == STATUS_INVALID_HANDLE,
+        "%u: NtOpenCompletion failed %x\n", str.Length, status );
+    pNtClose( ret );
+    status = pNtCreateJobObject( &ret, GENERIC_ALL, &attr2 );
+    ok( status == STATUS_SUCCESS, "%u: NtCreateJobObject failed %x\n", str.Length, status );
+    attr3.RootDirectory = ret;
+    status = pNtOpenJobObject( &ret2, GENERIC_ALL, &attr );
+    ok( status == STATUS_OBJECT_TYPE_MISMATCH, "%u: NtOpenJobObject failed %x\n", str.Length, status );
+    status = pNtOpenJobObject( &ret2, GENERIC_ALL, &attr3 );
+    ok( status == STATUS_OBJECT_TYPE_MISMATCH || status == STATUS_INVALID_HANDLE,
+        "%u: NtOpenJobObject failed %x\n", str.Length, status );
+    pNtClose( ret );
+    status = pNtCreateDirectoryObject( &ret, GENERIC_ALL, &attr2 );
+    ok( status == STATUS_SUCCESS, "%u: NtCreateDirectoryObject failed %x\n", str.Length, status );
+    attr3.RootDirectory = ret;
+    status = pNtOpenDirectoryObject( &ret2, GENERIC_ALL, &attr );
+    ok( status == STATUS_SUCCESS || broken(status == STATUS_ACCESS_DENIED), /* winxp */
+        "%u: NtOpenDirectoryObject failed %x\n", str.Length, status );
+    if (!status) pNtClose( ret2 );
+    status = pNtOpenDirectoryObject( &ret2, GENERIC_ALL, &attr3 );
+    ok( status == STATUS_SUCCESS, "%u: NtOpenDirectoryObject failed %x\n", str.Length, status );
+    pNtClose( ret2 );
+    pNtClose( ret );
+    status = pNtCreateSymbolicLinkObject( &ret, GENERIC_ALL, &attr2, &target );
+    ok( status == STATUS_SUCCESS, "%u: NtCreateSymbolicLinkObject failed %x\n", str.Length, status );
+    attr3.RootDirectory = ret;
+    status = pNtOpenSymbolicLinkObject( &ret2, GENERIC_ALL, &attr );
+    ok( status == STATUS_OBJECT_TYPE_MISMATCH, "%u: NtOpenSymbolicLinkObject failed %x\n", str.Length, status );
+    status = pNtOpenSymbolicLinkObject( &ret2, GENERIC_ALL, &attr3 );
+    ok( status == STATUS_SUCCESS, "%u: NtOpenSymbolicLinkObject failed %x\n", str.Length, status );
+    pNtClose( ret2 );
+    pNtClose( ret );
+    status = pNtCreateSection( &ret, SECTION_MAP_WRITE, &attr2, &size, PAGE_READWRITE, SEC_COMMIT, 0 );
+    ok( status == STATUS_SUCCESS, "%u: NtCreateSection failed %x\n", str.Length, status );
+    attr3.RootDirectory = ret;
+    status = pNtOpenSection( &ret2, SECTION_MAP_WRITE, &attr );
+    ok( status == STATUS_OBJECT_TYPE_MISMATCH, "%u: NtOpenSection failed %x\n", str.Length, status );
+    status = pNtOpenSection( &ret2, SECTION_MAP_WRITE, &attr3 );
+    ok( status == STATUS_OBJECT_TYPE_MISMATCH || status == STATUS_INVALID_HANDLE,
+        "%u: NtOpenSection failed %x\n", str.Length, status );
+    pNtClose( ret );
+
+    str.Length = 67;
+    test_all_kernel_objects( __LINE__, &attr2, STATUS_OBJECT_NAME_INVALID, STATUS_OBJECT_NAME_INVALID );
+
+    str.Length = 65532;
+    test_all_kernel_objects( __LINE__, &attr, STATUS_SUCCESS, STATUS_SUCCESS );
+
+    str.Length = 65534;
+    test_all_kernel_objects( __LINE__, &attr, STATUS_OBJECT_NAME_INVALID, STATUS_OBJECT_NAME_INVALID );
+
+    str.Length = 128;
+    for (attr.Length = 0; attr.Length <= 2 * sizeof(attr); attr.Length++)
+    {
+        if (attr.Length == sizeof(attr))
+            test_all_kernel_objects( __LINE__, &attr, STATUS_SUCCESS, STATUS_SUCCESS );
+        else
+            test_all_kernel_objects( __LINE__, &attr, STATUS_INVALID_PARAMETER, STATUS_INVALID_PARAMETER );
+    }
+    attr.Length = sizeof(attr);
+
+    /* null attributes or ObjectName, with or without RootDirectory */
+    attr3.RootDirectory = 0;
+    attr2.ObjectName = attr3.ObjectName = NULL;
+    test_all_kernel_objects( __LINE__, &attr2, STATUS_OBJECT_NAME_INVALID, STATUS_OBJECT_NAME_INVALID );
+    test_all_kernel_objects( __LINE__, &attr3, STATUS_SUCCESS, STATUS_OBJECT_PATH_SYNTAX_BAD );
+
+    attr3.ObjectName = &str2;
+    pRtlInitUnicodeString( &str2, localW );
+    status = pNtOpenSymbolicLinkObject( &ret, SYMBOLIC_LINK_QUERY, &attr3 );
+    ok( status == STATUS_SUCCESS, "can't open BaseNamedObjects\\Local %x\n", status );
+    attr3.ObjectName = &str;
+    attr3.RootDirectory = ret;
+    test_all_kernel_objects( __LINE__, &attr3, STATUS_OBJECT_TYPE_MISMATCH, STATUS_OBJECT_TYPE_MISMATCH );
+    pNtClose( attr3.RootDirectory );
+
+    status = pNtCreateMutant( &ret, GENERIC_ALL, NULL, FALSE );
+    ok( status == STATUS_SUCCESS, "NULL: NtCreateMutant failed %x\n", status );
+    pNtClose( ret );
+    status = pNtOpenMutant( &ret, GENERIC_ALL, NULL );
+    ok( status == STATUS_INVALID_PARAMETER, "NULL: NtOpenMutant failed %x\n", status );
+    status = pNtCreateSemaphore( &ret, GENERIC_ALL, NULL, 1, 2 );
+    ok( status == STATUS_SUCCESS, "NULL: NtCreateSemaphore failed %x\n", status );
+    pNtClose( ret );
+    status = pNtOpenSemaphore( &ret, GENERIC_ALL, NULL );
+    ok( status == STATUS_INVALID_PARAMETER, "NULL: NtOpenSemaphore failed %x\n", status );
+    status = pNtCreateEvent( &ret, GENERIC_ALL, NULL, 1, 0 );
+    ok( status == STATUS_SUCCESS, "NULL: NtCreateEvent failed %x\n", status );
+    pNtClose( ret );
+    status = pNtOpenEvent( &ret, GENERIC_ALL, NULL );
+    ok( status == STATUS_INVALID_PARAMETER, "NULL: NtOpenEvent failed %x\n", status );
+    status = pNtCreateKeyedEvent( &ret, GENERIC_ALL, NULL, 0 );
+    ok( status == STATUS_SUCCESS, "NULL: NtCreateKeyedEvent failed %x\n", status );
+    pNtClose( ret );
+    status = pNtOpenKeyedEvent( &ret, GENERIC_ALL, NULL );
+    ok( status == STATUS_INVALID_PARAMETER, "NULL: NtOpenKeyedEvent failed %x\n", status );
+    status = pNtCreateTimer( &ret, GENERIC_ALL, NULL, NotificationTimer );
+    ok( status == STATUS_SUCCESS, "NULL: NtCreateTimer failed %x\n", status );
+    pNtClose( ret );
+    status = pNtOpenTimer( &ret, GENERIC_ALL, NULL );
+    ok( status == STATUS_INVALID_PARAMETER, "NULL: NtOpenTimer failed %x\n", status );
+    status = pNtCreateIoCompletion( &ret, GENERIC_ALL, NULL, 0 );
+    ok( status == STATUS_SUCCESS, "NULL: NtCreateCompletion failed %x\n", status );
+    pNtClose( ret );
+    status = pNtOpenIoCompletion( &ret, GENERIC_ALL, NULL );
+    ok( status == STATUS_INVALID_PARAMETER, "NULL: NtOpenCompletion failed %x\n", status );
+    status = pNtCreateJobObject( &ret, GENERIC_ALL, NULL );
+    ok( status == STATUS_SUCCESS, "NULL: NtCreateJobObject failed %x\n", status );
+    pNtClose( ret );
+    status = pNtOpenJobObject( &ret, GENERIC_ALL, NULL );
+    ok( status == STATUS_INVALID_PARAMETER, "NULL: NtOpenJobObject failed %x\n", status );
+    status = pNtCreateDirectoryObject( &ret, GENERIC_ALL, NULL );
+    ok( status == STATUS_SUCCESS, "NULL: NtCreateDirectoryObject failed %x\n", status );
+    pNtClose( ret );
+    status = pNtOpenDirectoryObject( &ret, GENERIC_ALL, NULL );
+    ok( status == STATUS_INVALID_PARAMETER, "NULL: NtOpenDirectoryObject failed %x\n", status );
+    status = pNtCreateSymbolicLinkObject( &ret, GENERIC_ALL, NULL, &target );
+    ok( status == STATUS_ACCESS_VIOLATION || broken( status == STATUS_SUCCESS), /* winxp */
+        "NULL: NtCreateSymbolicLinkObject failed %x\n", status );
+    if (!status) pNtClose( ret );
+    status = pNtOpenSymbolicLinkObject( &ret, GENERIC_ALL, NULL );
+    ok( status == STATUS_INVALID_PARAMETER, "NULL: NtOpenSymbolicLinkObject failed %x\n", status );
+    status = pNtCreateSection( &ret, SECTION_MAP_WRITE, NULL, &size, PAGE_READWRITE, SEC_COMMIT, 0 );
+    ok( status == STATUS_SUCCESS, "NULL: NtCreateSection failed %x\n", status );
+    pNtClose( ret );
+    status = pNtOpenSection( &ret, SECTION_MAP_WRITE, NULL );
+    ok( status == STATUS_INVALID_PARAMETER, "NULL: NtOpenSection failed %x\n", status );
+    attr2.ObjectName = attr3.ObjectName = &str;
+
+    /* named pipes */
+    memcpy( str.Buffer, pipeW, sizeof(pipeW) );
+    for (i = 0; i < 65536 / sizeof(WCHAR); i++) str.Buffer[i + sizeof(pipeW)/sizeof(WCHAR)] = 'a';
+    str.Length = 0;
+    attr.RootDirectory = 0;
+    attr.Attributes = OBJ_CASE_INSENSITIVE;
+    timeout.QuadPart = -10000;
+    status = pNtCreateNamedPipeFile( &ret, GENERIC_ALL, &attr, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE,
+                                     FILE_CREATE, FILE_PIPE_FULL_DUPLEX, 0, 0, 0, 1, 256, 256, &timeout );
+    ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "%u: NtCreateNamedPipeFile failed %x\n", str.Length, status );
+    status = pNtCreateNamedPipeFile( &ret, GENERIC_ALL, &attr2, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE,
+                                     FILE_CREATE, FILE_PIPE_FULL_DUPLEX, 0, 0, 0, 1, 256, 256, &timeout );
+    ok( status == STATUS_INVALID_HANDLE, "%u: NtCreateNamedPipeFile failed %x\n", str.Length, status );
+    str.Length = 67;
+    status = pNtCreateNamedPipeFile( &ret, GENERIC_ALL, &attr2, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE,
+                                     FILE_CREATE, FILE_PIPE_FULL_DUPLEX, 0, 0, 0, 1, 256, 256, &timeout );
+    ok( status == STATUS_OBJECT_NAME_INVALID, "%u: NtCreateNamedPipeFile failed %x\n", str.Length, status );
+    str.Length = 128;
+    for (attr.Length = 0; attr.Length <= 2 * sizeof(attr); attr.Length++)
+    {
+        status = pNtCreateNamedPipeFile( &ret, GENERIC_ALL, &attr, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE,
+                                      FILE_CREATE, FILE_PIPE_FULL_DUPLEX, 0, 0, 0, 1, 256, 256, &timeout );
+        if (attr.Length == sizeof(attr))
+        {
+            ok( status == STATUS_SUCCESS, "%u: NtCreateNamedPipeFile failed %x\n", str.Length, status );
+            pNtClose( ret );
+        }
+        else ok( status == STATUS_INVALID_PARAMETER,
+                 "%u: NtCreateNamedPipeFile failed %x\n", str.Length, status );
+    }
+    attr.Length = sizeof(attr);
+    str.Length = 65532;
+    status = pNtCreateNamedPipeFile( &ret, GENERIC_ALL, &attr, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE,
+                                     FILE_CREATE, FILE_PIPE_FULL_DUPLEX, 0, 0, 0, 1, 256, 256, &timeout );
+    ok( status == STATUS_SUCCESS, "%u: NtCreateNamedPipeFile failed %x\n", str.Length, status );
+    pNtClose( ret );
+    str.Length = 65534;
+    status = pNtCreateNamedPipeFile( &ret, GENERIC_ALL, &attr, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE,
+                                     FILE_CREATE, FILE_PIPE_FULL_DUPLEX, 0, 0, 0, 1, 256, 256, &timeout );
+    ok( status == STATUS_OBJECT_NAME_INVALID, "%u: NtCreateNamedPipeFile failed %x\n", str.Length, status );
+    attr3.RootDirectory = 0;
+    attr2.ObjectName = attr3.ObjectName = NULL;
+    status = pNtCreateNamedPipeFile( &ret, GENERIC_ALL, &attr2, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE,
+                                     FILE_CREATE, FILE_PIPE_FULL_DUPLEX, 0, 0, 0, 1, 256, 256, &timeout );
+    ok( status == STATUS_OBJECT_NAME_INVALID, "NULL: NtCreateNamedPipeFile failed %x\n", status );
+    status = pNtCreateNamedPipeFile( &ret, GENERIC_ALL, &attr3, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE,
+                                     FILE_CREATE, FILE_PIPE_FULL_DUPLEX, 0, 0, 0, 1, 256, 256, &timeout );
+    ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NULL: NtCreateNamedPipeFile failed %x\n", status );
+    status = pNtCreateNamedPipeFile( &ret, GENERIC_ALL, NULL, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE,
+                                     FILE_CREATE, FILE_PIPE_FULL_DUPLEX, 0, 0, 0, 1, 256, 256, &timeout );
+    ok( status == STATUS_INVALID_PARAMETER, "NULL: NtCreateNamedPipeFile failed %x\n", status );
+    attr2.ObjectName = attr3.ObjectName = &str;
+
+    /* mailslots */
+    memcpy( str.Buffer, mailslotW, sizeof(mailslotW) );
+    for (i = 0; i < 65536 / sizeof(WCHAR); i++) str.Buffer[i + sizeof(mailslotW)/sizeof(WCHAR)] = 'a';
+    str.Length = 0;
+    status = pNtCreateMailslotFile( &ret, GENERIC_ALL, &attr, &iosb, 0, 0, 0, NULL );
+    ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "%u: NtCreateMailslotFile failed %x\n", str.Length, status );
+    status = pNtCreateMailslotFile( &ret, GENERIC_ALL, &attr2, &iosb, 0, 0, 0, NULL );
+    ok( status == STATUS_INVALID_HANDLE, "%u: NtCreateMailslotFile failed %x\n", str.Length, status );
+    str.Length = 67;
+    status = pNtCreateMailslotFile( &ret, GENERIC_ALL, &attr2, &iosb, 0, 0, 0, NULL );
+    ok( status == STATUS_OBJECT_NAME_INVALID, "%u: NtCreateMailslotFile failed %x\n", str.Length, status );
+    str.Length = 128;
+    for (attr.Length = 0; attr.Length <= 2 * sizeof(attr); attr.Length++)
+    {
+        status = pNtCreateMailslotFile( &ret, GENERIC_ALL, &attr, &iosb, 0, 0, 0, NULL );
+        if (attr.Length == sizeof(attr))
+        {
+            ok( status == STATUS_SUCCESS, "%u: NtCreateMailslotFile failed %x\n", str.Length, status );
+            pNtClose( ret );
+        }
+        else ok( status == STATUS_INVALID_PARAMETER,
+                 "%u: NtCreateMailslotFile failed %x\n", str.Length, status );
+    }
+    attr.Length = sizeof(attr);
+    str.Length = 65532;
+    status = pNtCreateMailslotFile( &ret, GENERIC_ALL, &attr, &iosb, 0, 0, 0, NULL );
+    ok( status == STATUS_SUCCESS, "%u: NtCreateMailslotFile failed %x\n", str.Length, status );
+    pNtClose( ret );
+    str.Length = 65534;
+    status = pNtCreateMailslotFile( &ret, GENERIC_ALL, &attr, &iosb, 0, 0, 0, NULL );
+    ok( status == STATUS_OBJECT_NAME_INVALID, "%u: NtCreateMailslotFile failed %x\n", str.Length, status );
+    attr3.RootDirectory = 0;
+    attr2.ObjectName = attr3.ObjectName = NULL;
+    status = pNtCreateMailslotFile( &ret, GENERIC_ALL, &attr2, &iosb, 0, 0, 0, NULL );
+    ok( status == STATUS_OBJECT_NAME_INVALID, "NULL: NtCreateMailslotFile failed %x\n", status );
+    status = pNtCreateMailslotFile( &ret, GENERIC_ALL, &attr3, &iosb, 0, 0, 0, NULL );
+    ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NULL: NtCreateMailslotFile failed %x\n", status );
+    status = pNtCreateMailslotFile( &ret, GENERIC_ALL, NULL, &iosb, 0, 0, 0, NULL );
+    ok( status == STATUS_INVALID_PARAMETER, "NULL: NtCreateMailslotFile failed %x\n", status );
+    attr2.ObjectName = attr3.ObjectName = &str;
+
+    /* registry keys */
+    memcpy( str.Buffer, registryW, sizeof(registryW) );
+    for (i = 0; i < 65536 / sizeof(WCHAR); i++) str.Buffer[i + sizeof(registryW)/sizeof(WCHAR)] = 'a';
+    str.Length = 0;
+    status = pNtCreateKey( &ret, GENERIC_ALL, &attr, 0, NULL, 0, NULL );
+    todo_wine
+    ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "%u: NtCreateKey failed %x\n", str.Length, status );
+    status = pNtCreateKey( &ret, GENERIC_ALL, &attr2, 0, NULL, 0, NULL );
+    ok( status == STATUS_INVALID_HANDLE, "%u: NtCreateKey failed %x\n", str.Length, status );
+    status = pNtOpenKey( &ret, GENERIC_ALL, &attr2 );
+    ok( status == STATUS_INVALID_HANDLE, "%u: NtOpenKey failed %x\n", str.Length, status );
+    str.Length = sizeof(registryW) + 250 * sizeof(WCHAR) + 1;
+    status = pNtCreateKey( &ret, GENERIC_ALL, &attr, 0, NULL, 0, NULL );
+    ok( status == STATUS_OBJECT_NAME_INVALID ||
+        status == STATUS_INVALID_PARAMETER ||
+        broken( status == STATUS_SUCCESS ), /* wow64 */
+        "%u: NtCreateKey failed %x\n", str.Length, status );
+    if (!status)
+    {
+        pNtDeleteKey( ret );
+        pNtClose( ret );
+    }
+    str.Length = sizeof(registryW) + 256 * sizeof(WCHAR);
+    status = pNtCreateKey( &ret, GENERIC_ALL, &attr, 0, NULL, 0, NULL );
+    ok( status == STATUS_SUCCESS || status == STATUS_ACCESS_DENIED,
+        "%u: NtCreateKey failed %x\n", str.Length, status );
+    if (!status)
+    {
+        status = pNtOpenKey( &ret2, KEY_READ, &attr );
+        ok( status == STATUS_SUCCESS, "%u: NtOpenKey failed %x\n", str.Length, status );
+        pNtClose( ret2 );
+        attr3.RootDirectory = ret;
+        str.Length = 0;
+        status = pNtOpenKey( &ret2, KEY_READ, &attr3 );
+        ok( status == STATUS_SUCCESS, "%u: NtOpenKey failed %x\n", str.Length, status );
+        pNtClose( ret2 );
+        pNtDeleteKey( ret );
+        pNtClose( ret );
+
+        str.Length = sizeof(registryW) + 256 * sizeof(WCHAR);
+        for (attr.Length = 0; attr.Length <= 2 * sizeof(attr); attr.Length++)
+        {
+            if (attr.Length == sizeof(attr))
+            {
+                status = pNtCreateKey( &ret, GENERIC_ALL, &attr, 0, NULL, 0, NULL );
+                ok( status == STATUS_SUCCESS, "%u: NtCreateKey failed %x\n", str.Length, status );
+                status = pNtOpenKey( &ret2, KEY_READ, &attr );
+                ok( status == STATUS_SUCCESS, "%u: NtOpenKey failed %x\n", str.Length, status );
+                pNtClose( ret2 );
+                pNtDeleteKey( ret );
+                pNtClose( ret );
+            }
+            else
+            {
+                status = pNtCreateKey( &ret, GENERIC_ALL, &attr, 0, NULL, 0, NULL );
+                ok( status == STATUS_INVALID_PARAMETER, "%u: NtCreateKey failed %x\n", str.Length, status );
+                status = pNtOpenKey( &ret2, KEY_READ, &attr );
+                ok( status == STATUS_INVALID_PARAMETER, "%u: NtOpenKey failed %x\n", str.Length, status );
+            }
+        }
+        attr.Length = sizeof(attr);
+    }
+    str.Length = sizeof(registryW) + 256 * sizeof(WCHAR) + 1;
+    status = pNtCreateKey( &ret, GENERIC_ALL, &attr, 0, NULL, 0, NULL );
+    ok( status == STATUS_OBJECT_NAME_INVALID ||
+        status == STATUS_INVALID_PARAMETER ||
+        broken( status == STATUS_SUCCESS ), /* win7 */
+        "%u: NtCreateKey failed %x\n", str.Length, status );
+    if (!status)
+    {
+        pNtDeleteKey( ret );
+        pNtClose( ret );
+    }
+    status = pNtOpenKey( &ret, GENERIC_ALL, &attr );
+    ok( status == STATUS_OBJECT_NAME_INVALID ||
+        status == STATUS_INVALID_PARAMETER ||
+        broken( status == STATUS_OBJECT_NAME_NOT_FOUND ), /* wow64 */
+        "%u: NtOpenKey failed %x\n", str.Length, status );
+    str.Length++;
+    status = pNtCreateKey( &ret, GENERIC_ALL, &attr, 0, NULL, 0, NULL );
+    ok( status == STATUS_INVALID_PARAMETER, "%u: NtCreateKey failed %x\n", str.Length, status );
+    status = pNtOpenKey( &ret, GENERIC_ALL, &attr );
+    todo_wine
+    ok( status == STATUS_INVALID_PARAMETER, "%u: NtOpenKey failed %x\n", str.Length, status );
+    str.Length = 2000;
+    status = pNtCreateKey( &ret, GENERIC_ALL, &attr, 0, NULL, 0, NULL );
+    ok( status == STATUS_INVALID_PARAMETER, "%u: NtCreateKey failed %x\n", str.Length, status );
+    status = pNtOpenKey( &ret, GENERIC_ALL, &attr );
+    todo_wine
+    ok( status == STATUS_INVALID_PARAMETER, "%u: NtOpenKey failed %x\n", str.Length, status );
+    /* some Windows versions change the error past 2050 chars, others past 4066 chars, some don't */
+    str.Length = 5000;
+    status = pNtCreateKey( &ret, GENERIC_ALL, &attr, 0, NULL, 0, NULL );
+    ok( status == STATUS_BUFFER_OVERFLOW ||
+        status == STATUS_BUFFER_TOO_SMALL ||
+        status == STATUS_INVALID_PARAMETER,
+        "%u: NtCreateKey failed %x\n", str.Length, status );
+    status = pNtOpenKey( &ret, GENERIC_ALL, &attr );
+    todo_wine
+    ok( status == STATUS_BUFFER_OVERFLOW ||
+        status == STATUS_BUFFER_TOO_SMALL ||
+        status == STATUS_INVALID_PARAMETER,
+         "%u: NtOpenKey failed %x\n", str.Length, status );
+    str.Length = 65534;
+    status = pNtCreateKey( &ret, GENERIC_ALL, &attr, 0, NULL, 0, NULL );
+    ok( status == STATUS_OBJECT_NAME_INVALID ||
+        status == STATUS_BUFFER_OVERFLOW ||
+        status == STATUS_BUFFER_TOO_SMALL,
+        "%u: NtCreateKey failed %x\n", str.Length, status );
+    status = pNtOpenKey( &ret, GENERIC_ALL, &attr );
+    todo_wine
+    ok( status == STATUS_OBJECT_NAME_INVALID ||
+        status == STATUS_BUFFER_OVERFLOW ||
+        status == STATUS_BUFFER_TOO_SMALL,
+         "%u: NtOpenKey failed %x\n", str.Length, status );
+    attr3.RootDirectory = 0;
+    attr2.ObjectName = attr3.ObjectName = NULL;
+    status = pNtCreateKey( &ret, GENERIC_ALL, &attr2, 0, NULL, 0, NULL );
+    todo_wine
+    ok( status == STATUS_ACCESS_VIOLATION || status == STATUS_INVALID_HANDLE,
+        "NULL: NtCreateKey failed %x\n", status );
+    status = pNtCreateKey( &ret, GENERIC_ALL, &attr3, 0, NULL, 0, NULL );
+    todo_wine
+    ok( status == STATUS_ACCESS_VIOLATION, "NULL: NtCreateKey failed %x\n", status );
+    status = pNtCreateKey( &ret, GENERIC_ALL, NULL, 0, NULL, 0, NULL );
+    ok( status == STATUS_ACCESS_VIOLATION, "NULL: NtCreateKey failed %x\n", status );
+    status = pNtOpenKey( &ret, GENERIC_ALL, &attr2 );
+    ok( status == STATUS_ACCESS_VIOLATION || status == STATUS_INVALID_HANDLE,
+        "NULL: NtOpenKey failed %x\n", status );
+    status = pNtOpenKey( &ret, GENERIC_ALL, &attr3 );
+    ok( status == STATUS_ACCESS_VIOLATION, "NULL: NtOpenKey failed %x\n", status );
+    status = pNtOpenKey( &ret, GENERIC_ALL, NULL );
+    ok( status == STATUS_ACCESS_VIOLATION, "NULL: NtOpenKey failed %x\n", status );
+    attr2.ObjectName = attr3.ObjectName = &str;
+
+    pRtlFreeUnicodeString( &str );
+    pRtlFreeUnicodeString( &target );
+}
+
 static void test_directory(void)
 {
     NTSTATUS status;
     UNICODE_STRING str;
     OBJECT_ATTRIBUTES attr;
-    HANDLE dir, dir1, h;
+    HANDLE dir, dir1, h, h2;
     BOOL is_nt4;
 
     /* No name and/or no attributes */
@@ -380,28 +921,34 @@ static void test_directory(void)
         "NtOpenDirectoryObject should have failed with STATUS_INVALID_PARAMETER got(%08x)\n", status);
 
     InitializeObjectAttributes(&attr, NULL, 0, 0, NULL);
-    DIR_TEST_CREATE_SUCCESS(&dir)
-    DIR_TEST_OPEN_FAILURE(&h, STATUS_OBJECT_PATH_SYNTAX_BAD)
+    status = pNtCreateDirectoryObject( &dir, DIRECTORY_QUERY, &attr );
+    ok( status == STATUS_SUCCESS, "Failed to create directory %08x\n", status );
+    status = pNtOpenDirectoryObject( &h, DIRECTORY_QUERY, &attr );
+    ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NtOpenDirectoryObject got %08x\n", status );
 
     /* Bad name */
     InitializeObjectAttributes(&attr, &str, 0, 0, NULL);
 
     pRtlCreateUnicodeStringFromAsciiz(&str, "");
-    DIR_TEST_CREATE_SUCCESS(&h)
+    status = pNtCreateDirectoryObject( &h, DIRECTORY_QUERY, &attr );
+    ok( status == STATUS_SUCCESS, "Failed to create directory %08x\n", status );
     pNtClose(h);
-    DIR_TEST_OPEN_FAILURE(&h, STATUS_OBJECT_PATH_SYNTAX_BAD)
+    status = pNtOpenDirectoryObject( &h, DIRECTORY_QUERY, &attr );
+    ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NtOpenDirectoryObject got %08x\n", status );
     pRtlFreeUnicodeString(&str);
     pNtClose(dir);
 
-    DIR_TEST_CREATE_OPEN_FAILURE(&h, "BaseNamedObjects", STATUS_OBJECT_PATH_SYNTAX_BAD)
-    DIR_TEST_CREATE_OPEN_FAILURE(&h, "\\BaseNamedObjects\\", STATUS_OBJECT_NAME_INVALID)
-    DIR_TEST_CREATE_OPEN_FAILURE(&h, "\\\\BaseNamedObjects", STATUS_OBJECT_NAME_INVALID)
-    DIR_TEST_CREATE_OPEN_FAILURE(&h, "\\BaseNamedObjects\\\\om.c-test", STATUS_OBJECT_NAME_INVALID)
-    DIR_TEST_CREATE_OPEN_FAILURE(&h, "\\BaseNamedObjects\\om.c-test\\", STATUS_OBJECT_PATH_NOT_FOUND)
+    DIR_TEST_CREATE_OPEN( "BaseNamedObjects", STATUS_OBJECT_PATH_SYNTAX_BAD );
+    DIR_TEST_CREATE_OPEN( "\\BaseNamedObjects\\", STATUS_OBJECT_NAME_INVALID );
+    DIR_TEST_CREATE_OPEN( "\\\\BaseNamedObjects", STATUS_OBJECT_NAME_INVALID );
+    DIR_TEST_CREATE_OPEN( "\\BaseNamedObjects\\\\om.c-test", STATUS_OBJECT_NAME_INVALID );
+    DIR_TEST_CREATE_OPEN( "\\BaseNamedObjects\\om.c-test\\", STATUS_OBJECT_PATH_NOT_FOUND );
 
     pRtlCreateUnicodeStringFromAsciiz(&str, "\\BaseNamedObjects\\om.c-test");
-    DIR_TEST_CREATE_SUCCESS(&h)
-    DIR_TEST_OPEN_SUCCESS(&dir1)
+    status = pNtCreateDirectoryObject( &h, DIRECTORY_QUERY, &attr );
+    ok( status == STATUS_SUCCESS, "Failed to create directory %08x\n", status );
+    status = pNtOpenDirectoryObject( &dir1, DIRECTORY_QUERY, &attr );
+    ok( status == STATUS_SUCCESS, "Failed to open directory %08x\n", status );
     pRtlFreeUnicodeString(&str);
     pNtClose(h);
     pNtClose(dir1);
@@ -423,9 +970,43 @@ static void test_directory(void)
         pRtlFreeUnicodeString(&str);
         InitializeObjectAttributes(&attr, &str, 0, dir, NULL);
         pRtlCreateUnicodeStringFromAsciiz(&str, "one more level");
-        DIR_TEST_CREATE_FAILURE(&h, STATUS_OBJECT_TYPE_MISMATCH)
+        status = pNtCreateDirectoryObject( &h, DIRECTORY_QUERY, &attr );
+        ok( status == STATUS_OBJECT_TYPE_MISMATCH, "NtCreateDirectoryObject got %08x\n", status );
         pRtlFreeUnicodeString(&str);
 
+        pRtlCreateUnicodeStringFromAsciiz( &str, "\\BaseNamedObjects\\Local\\om.c-test" );
+        InitializeObjectAttributes( &attr, &str, 0, 0, NULL );
+        status = pNtCreateDirectoryObject( &dir1, DIRECTORY_QUERY, &attr );
+        ok( status == STATUS_SUCCESS, "Failed to create directory %08x\n", status );
+        pRtlFreeUnicodeString( &str );
+        pRtlCreateUnicodeStringFromAsciiz( &str, "om.c-test" );
+        InitializeObjectAttributes( &attr, &str, 0, dir, NULL );
+        status = pNtOpenDirectoryObject( &h, DIRECTORY_QUERY, &attr );
+        ok( status == STATUS_OBJECT_TYPE_MISMATCH, "Failed to open directory %08x\n", status );
+        if (!status) pNtClose(h);
+        pRtlFreeUnicodeString( &str );
+
+        pRtlCreateUnicodeStringFromAsciiz( &str, "om.c-event" );
+        InitializeObjectAttributes( &attr, &str, 0, dir1, NULL );
+        status = pNtCreateEvent( &h, GENERIC_ALL, &attr, 1, 0 );
+        ok( status == STATUS_SUCCESS, "NtCreateEvent failed %x\n", status );
+        status = pNtOpenEvent( &h2, GENERIC_ALL, &attr );
+        ok( status == STATUS_SUCCESS, "NtOpenEvent failed %x\n", status );
+        pNtClose( h2 );
+        pRtlFreeUnicodeString( &str );
+        pRtlCreateUnicodeStringFromAsciiz( &str, "om.c-test\\om.c-event" );
+        InitializeObjectAttributes( &attr, &str, 0, dir, NULL );
+        status = pNtOpenEvent( &h2, GENERIC_ALL, &attr );
+        ok( status == STATUS_OBJECT_TYPE_MISMATCH, "NtOpenEvent failed %x\n", status );
+        pRtlFreeUnicodeString( &str );
+        pRtlCreateUnicodeStringFromAsciiz( &str, "\\BasedNamedObjects\\Local\\om.c-test\\om.c-event" );
+        InitializeObjectAttributes( &attr, &str, 0, 0, NULL );
+        status = pNtOpenEvent( &h2, GENERIC_ALL, &attr );
+        ok( status == STATUS_OBJECT_PATH_NOT_FOUND, "NtOpenEvent failed %x\n", status );
+        pRtlFreeUnicodeString( &str );
+        pNtClose( h );
+        pNtClose( dir1 );
+
         str.Buffer = buffer;
         str.MaximumLength = sizeof(buffer);
         len = 0xdeadbeef;
@@ -463,22 +1044,26 @@ error:
 
     pRtlCreateUnicodeStringFromAsciiz(&str, "\\BaseNamedObjects");
     InitializeObjectAttributes(&attr, &str, 0, 0, NULL);
-    DIR_TEST_OPEN_SUCCESS(&dir)
+    status = pNtOpenDirectoryObject( &dir, DIRECTORY_QUERY, &attr );
+    ok( status == STATUS_SUCCESS, "Failed to open directory %08x\n", status );
     pRtlFreeUnicodeString(&str);
 
     InitializeObjectAttributes(&attr, NULL, 0, dir, NULL);
-    DIR_TEST_OPEN_FAILURE(&h, STATUS_OBJECT_NAME_INVALID)
+    status = pNtOpenDirectoryObject( &h, DIRECTORY_QUERY, &attr );
+    ok( status == STATUS_OBJECT_NAME_INVALID, "NtOpenDirectoryObject got %08x\n", status );
 
     InitializeObjectAttributes(&attr, &str, 0, dir, NULL);
-    DIR_TEST_CREATE_OPEN_SUCCESS(h, "")
-    DIR_TEST_CREATE_OPEN_FAILURE(&h, "\\", STATUS_OBJECT_PATH_SYNTAX_BAD)
-    DIR_TEST_CREATE_OPEN_FAILURE(&h, "\\om.c-test", STATUS_OBJECT_PATH_SYNTAX_BAD)
-    DIR_TEST_CREATE_OPEN_FAILURE(&h, "\\om.c-test\\", STATUS_OBJECT_PATH_SYNTAX_BAD)
-    DIR_TEST_CREATE_OPEN_FAILURE(&h, "om.c-test\\", STATUS_OBJECT_PATH_NOT_FOUND)
+    DIR_TEST_CREATE_OPEN( "", STATUS_SUCCESS );
+    DIR_TEST_CREATE_OPEN( "\\", STATUS_OBJECT_PATH_SYNTAX_BAD );
+    DIR_TEST_CREATE_OPEN( "\\om.c-test", STATUS_OBJECT_PATH_SYNTAX_BAD );
+    DIR_TEST_CREATE_OPEN( "\\om.c-test\\", STATUS_OBJECT_PATH_SYNTAX_BAD );
+    DIR_TEST_CREATE_OPEN( "om.c-test\\", STATUS_OBJECT_PATH_NOT_FOUND );
 
     pRtlCreateUnicodeStringFromAsciiz(&str, "om.c-test");
-    DIR_TEST_CREATE_SUCCESS(&dir1)
-    DIR_TEST_OPEN_SUCCESS(&h)
+    status = pNtCreateDirectoryObject( &dir1, DIRECTORY_QUERY, &attr );
+    ok( status == STATUS_SUCCESS, "Failed to create directory %08x\n", status );
+    status = pNtOpenDirectoryObject( &h, DIRECTORY_QUERY, &attr );
+    ok( status == STATUS_SUCCESS, "Failed to open directory %08x\n", status );
     pRtlFreeUnicodeString(&str);
 
     pNtClose(h);
@@ -488,23 +1073,28 @@ error:
     /* Nested directories */
     pRtlCreateUnicodeStringFromAsciiz(&str, "\\");
     InitializeObjectAttributes(&attr, &str, 0, 0, NULL);
-    DIR_TEST_OPEN_SUCCESS(&dir)
+    status = pNtOpenDirectoryObject( &dir, DIRECTORY_QUERY, &attr );
+    ok( status == STATUS_SUCCESS, "Failed to open directory %08x\n", status );
     InitializeObjectAttributes(&attr, &str, 0, dir, NULL);
-    DIR_TEST_OPEN_FAILURE(&h, STATUS_OBJECT_PATH_SYNTAX_BAD)
+    status = pNtOpenDirectoryObject( &h, DIRECTORY_QUERY, &attr );
+    ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NtOpenDirectoryObject got %08x\n", status );
     pRtlFreeUnicodeString(&str);
     pNtClose(dir);
 
     InitializeObjectAttributes(&attr, &str, 0, 0, NULL);
     pRtlCreateUnicodeStringFromAsciiz(&str, "\\BaseNamedObjects\\om.c-test");
-    DIR_TEST_CREATE_SUCCESS(&dir)
+    status = pNtCreateDirectoryObject( &dir, DIRECTORY_QUERY, &attr );
+    ok( status == STATUS_SUCCESS, "Failed to create directory %08x\n", status );
     pRtlFreeUnicodeString(&str);
     pRtlCreateUnicodeStringFromAsciiz(&str, "\\BaseNamedObjects\\om.c-test\\one more level");
-    DIR_TEST_CREATE_SUCCESS(&h)
+    status = pNtCreateDirectoryObject( &h, DIRECTORY_QUERY, &attr );
+    ok( status == STATUS_SUCCESS, "Failed to create directory %08x\n", status );
     pRtlFreeUnicodeString(&str);
     pNtClose(h);
     InitializeObjectAttributes(&attr, &str, 0, dir, NULL);
     pRtlCreateUnicodeStringFromAsciiz(&str, "one more level");
-    DIR_TEST_CREATE_SUCCESS(&h)
+    status = pNtCreateDirectoryObject( &h, DIRECTORY_QUERY, &attr );
+    ok( status == STATUS_SUCCESS, "Failed to create directory %08x\n", status );
     pRtlFreeUnicodeString(&str);
     pNtClose(h);
 
@@ -514,15 +1104,18 @@ error:
     {
         InitializeObjectAttributes(&attr, &str, 0, 0, NULL);
         pRtlCreateUnicodeStringFromAsciiz(&str, "\\BaseNamedObjects\\Global\\om.c-test");
-        DIR_TEST_CREATE_SUCCESS(&dir)
+        status = pNtCreateDirectoryObject( &dir, DIRECTORY_QUERY, &attr );
+        ok( status == STATUS_SUCCESS, "Failed to create directory %08x\n", status );
         pRtlFreeUnicodeString(&str);
         pRtlCreateUnicodeStringFromAsciiz(&str, "\\BaseNamedObjects\\Local\\om.c-test\\one more level");
-        DIR_TEST_CREATE_SUCCESS(&h)
+        status = pNtCreateDirectoryObject( &h, DIRECTORY_QUERY, &attr );
+        ok( status == STATUS_SUCCESS, "Failed to create directory %08x\n", status );
         pRtlFreeUnicodeString(&str);
         pNtClose(h);
         InitializeObjectAttributes(&attr, &str, 0, dir, NULL);
         pRtlCreateUnicodeStringFromAsciiz(&str, "one more level");
-        DIR_TEST_CREATE_SUCCESS(&dir)
+        status = pNtCreateDirectoryObject( &dir, DIRECTORY_QUERY, &attr );
+        ok( status == STATUS_SUCCESS, "Failed to create directory %08x\n", status );
         pRtlFreeUnicodeString(&str);
         pNtClose(h);
         pNtClose(dir);
@@ -532,7 +1125,8 @@ error:
 
     InitializeObjectAttributes(&attr, &str, 0, 0, NULL);
     pRtlCreateUnicodeStringFromAsciiz(&str, "\\BaseNamedObjects");
-    DIR_TEST_OPEN_SUCCESS(&dir)
+    status = pNtOpenDirectoryObject( &dir, DIRECTORY_QUERY, &attr );
+    ok( status == STATUS_SUCCESS, "Failed to open directory %08x\n", status );
     pRtlFreeUnicodeString(&str);
     InitializeObjectAttributes(&attr, &str, 0, dir, NULL);
 
@@ -563,20 +1157,6 @@ error:
     pNtClose(dir);
 }
 
-#define SYMLNK_TEST_CREATE_OPEN_FAILURE2(h,n,t,e,e2) \
-    pRtlCreateUnicodeStringFromAsciiz(&str, n);\
-    pRtlCreateUnicodeStringFromAsciiz(&target, t);\
-    status = pNtCreateSymbolicLinkObject(h, SYMBOLIC_LINK_QUERY, &attr, &target);\
-    ok(status == e || status == e2, \
-       "NtCreateSymbolicLinkObject should have failed with %s or %s got(%08x)\n", #e, #e2, status);\
-    status = pNtOpenSymbolicLinkObject(h, SYMBOLIC_LINK_QUERY, &attr);\
-    ok(status == e || status == e2, \
-       "NtOpenSymbolicLinkObject should have failed with %s or %s got(%08x)\n", #e, #e2, status);\
-    pRtlFreeUnicodeString(&target);\
-    pRtlFreeUnicodeString(&str);
-
-#define SYMLNK_TEST_CREATE_OPEN_FAILURE(h,n,t,e) SYMLNK_TEST_CREATE_OPEN_FAILURE2(h,n,t,e,e)
-
 static void test_symboliclink(void)
 {
     NTSTATUS status;
@@ -587,7 +1167,13 @@ static void test_symboliclink(void)
 
     /* No name and/or no attributes */
     InitializeObjectAttributes(&attr, NULL, 0, 0, NULL);
-    SYMLNK_TEST_CREATE_OPEN_FAILURE2(NULL, "", "", STATUS_ACCESS_VIOLATION, STATUS_INVALID_PARAMETER)
+    pRtlCreateUnicodeStringFromAsciiz(&target, "\\DosDevices");
+    status = pNtCreateSymbolicLinkObject( NULL, SYMBOLIC_LINK_QUERY, &attr, &target );
+    ok( status == STATUS_ACCESS_VIOLATION || status == STATUS_INVALID_PARAMETER,
+       "NtCreateSymbolicLinkObject got %08x\n", status );
+    status = pNtOpenSymbolicLinkObject( NULL, SYMBOLIC_LINK_QUERY, &attr );
+    ok( status == STATUS_ACCESS_VIOLATION || status == STATUS_INVALID_PARAMETER,
+       "NtOpenSymbolicLinkObject got %08x\n", status );
 
     status = pNtCreateSymbolicLinkObject(&h, SYMBOLIC_LINK_QUERY, NULL, NULL);
     ok(status == STATUS_ACCESS_VIOLATION,
@@ -597,7 +1183,6 @@ static void test_symboliclink(void)
         "NtOpenSymbolicLinkObject should have failed with STATUS_INVALID_PARAMETER got(%08x)\n", status);
 
     /* No attributes */
-    pRtlCreateUnicodeStringFromAsciiz(&target, "\\DosDevices");
     status = pNtCreateSymbolicLinkObject(&h, SYMBOLIC_LINK_QUERY, NULL, &target);
     ok(status == STATUS_SUCCESS || status == STATUS_ACCESS_VIOLATION, /* nt4 */
        "NtCreateSymbolicLinkObject failed(%08x)\n", status);
@@ -634,13 +1219,45 @@ static void test_symboliclink(void)
     pRtlFreeUnicodeString(&str);
     pRtlFreeUnicodeString(&target);
 
-    SYMLNK_TEST_CREATE_OPEN_FAILURE(&h, "BaseNamedObjects", "->Somewhere", STATUS_OBJECT_PATH_SYNTAX_BAD)
-    SYMLNK_TEST_CREATE_OPEN_FAILURE(&h, "\\BaseNamedObjects\\", "->Somewhere", STATUS_OBJECT_NAME_INVALID)
-    SYMLNK_TEST_CREATE_OPEN_FAILURE(&h, "\\\\BaseNamedObjects", "->Somewhere", STATUS_OBJECT_NAME_INVALID)
-    SYMLNK_TEST_CREATE_OPEN_FAILURE(&h, "\\BaseNamedObjects\\\\om.c-test", "->Somewhere", STATUS_OBJECT_NAME_INVALID)
-    SYMLNK_TEST_CREATE_OPEN_FAILURE2(&h, "\\BaseNamedObjects\\om.c-test\\", "->Somewhere",
-                                     STATUS_OBJECT_NAME_INVALID, STATUS_OBJECT_PATH_NOT_FOUND)
-
+    pRtlCreateUnicodeStringFromAsciiz( &target, "->Somewhere");
+
+    pRtlCreateUnicodeStringFromAsciiz( &str, "BaseNamedObjects" );
+    status = pNtCreateSymbolicLinkObject( &h, SYMBOLIC_LINK_QUERY, &attr, &target );
+    ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NtCreateSymbolicLinkObject got %08x\n", status );
+    status = pNtOpenSymbolicLinkObject( &h, SYMBOLIC_LINK_QUERY, &attr );
+    ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NtOpenSymbolicLinkObject got %08x\n", status );
+    pRtlFreeUnicodeString( &str );
+
+    pRtlCreateUnicodeStringFromAsciiz( &str, "\\BaseNamedObjects\\" );
+    status = pNtCreateSymbolicLinkObject( &h, SYMBOLIC_LINK_QUERY, &attr, &target );
+    ok( status == STATUS_OBJECT_NAME_INVALID, "NtCreateSymbolicLinkObject got %08x\n", status );
+    status = pNtOpenSymbolicLinkObject( &h, SYMBOLIC_LINK_QUERY, &attr );
+    ok( status == STATUS_OBJECT_NAME_INVALID, "NtOpenSymbolicLinkObject got %08x\n", status );
+    pRtlFreeUnicodeString( &str );
+
+    pRtlCreateUnicodeStringFromAsciiz( &str, "\\\\BaseNamedObjects" );
+    status = pNtCreateSymbolicLinkObject( &h, SYMBOLIC_LINK_QUERY, &attr, &target );
+    ok( status == STATUS_OBJECT_NAME_INVALID, "NtCreateSymbolicLinkObject got %08x\n", status );
+    status = pNtOpenSymbolicLinkObject( &h, SYMBOLIC_LINK_QUERY, &attr );
+    ok( status == STATUS_OBJECT_NAME_INVALID, "NtOpenSymbolicLinkObject got %08x\n", status );
+    pRtlFreeUnicodeString( &str );
+
+    pRtlCreateUnicodeStringFromAsciiz( &str, "\\BaseNamedObjects\\\\om.c-test" );
+    status = pNtCreateSymbolicLinkObject( &h, SYMBOLIC_LINK_QUERY, &attr, &target );
+    ok( status == STATUS_OBJECT_NAME_INVALID, "NtCreateSymbolicLinkObject got %08x\n", status );
+    status = pNtOpenSymbolicLinkObject( &h, SYMBOLIC_LINK_QUERY, &attr );
+    ok( status == STATUS_OBJECT_NAME_INVALID, "NtOpenSymbolicLinkObject got %08x\n", status );
+    pRtlFreeUnicodeString( &str );
+
+    pRtlCreateUnicodeStringFromAsciiz( &str, "\\BaseNamedObjects\\om.c-test\\" );
+    status = pNtCreateSymbolicLinkObject( &h, SYMBOLIC_LINK_QUERY, &attr, &target );
+    ok( status == STATUS_OBJECT_NAME_INVALID || status == STATUS_OBJECT_PATH_NOT_FOUND,
+        "NtCreateSymbolicLinkObject got %08x\n", status );
+    status = pNtOpenSymbolicLinkObject( &h, SYMBOLIC_LINK_QUERY, &attr );
+    ok( status == STATUS_OBJECT_NAME_INVALID || status == STATUS_OBJECT_PATH_NOT_FOUND,
+        "NtOpenSymbolicLinkObject got %08x\n", status );
+    pRtlFreeUnicodeString( &str );
+    pRtlFreeUnicodeString(&target);
 
     /* Compound test */
     if (!(dir = get_base_dir()))
@@ -682,10 +1299,15 @@ static void test_query_object(void)
     char buffer[1024];
     NTSTATUS status;
     ULONG len, expected_len;
-    UNICODE_STRING *str;
+    OBJECT_ATTRIBUTES attr;
+    UNICODE_STRING path, session, *str;
     char dir[MAX_PATH], tmp_path[MAX_PATH], file1[MAX_PATH + 16];
     LARGE_INTEGER size;
 
+    sprintf( tmp_path, "\\Sessions\\%u", NtCurrentTeb()->Peb->SessionId );
+    pRtlCreateUnicodeStringFromAsciiz( &session, tmp_path );
+    InitializeObjectAttributes( &attr, &path, 0, 0, 0 );
+
     handle = CreateEventA( NULL, FALSE, FALSE, "test_event" );
 
     len = 0;
@@ -715,9 +1337,15 @@ static void test_query_object(void)
     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) ),
+    ok( len > sizeof(UNICODE_STRING) + sizeof("\\test_event") * sizeof(WCHAR),
+        "name too short %s\n", wine_dbgstr_w(str->Buffer) );
+    /* check for \\Sessions prefix in the name */
+    ok( (str->Length > session.Length &&
+         !memcmp( str->Buffer, session.Buffer, session.Length ) &&
+         !memcmp( str->Buffer + session.Length / sizeof(WCHAR), name, sizeof(name) )) ||
+        broken( !memcmp( str->Buffer, name, sizeof(name) )), /* winxp */
         "wrong name %s\n", wine_dbgstr_w(str->Buffer) );
+    trace( "got %s len %u\n", wine_dbgstr_w(str->Buffer), len );
 
     len -= sizeof(WCHAR);
     status = pNtQueryObject( handle, ObjectNameInformation, buffer, len, &len );
@@ -851,6 +1479,167 @@ static void test_query_object(void)
     ok( str->Buffer && !memcmp( str->Buffer, type_section, sizeof(type_section) ),
                   "wrong/bad type name %s (%p)\n", wine_dbgstr_w(str->Buffer), str->Buffer );
     pNtClose( handle );
+
+    handle = CreateMailslotA( "\\\\.\\mailslot\\test_mailslot", 100, 1000, NULL );
+    ok( handle != INVALID_HANDLE_VALUE, "CreateMailslot failed err %u\n", GetLastError() );
+    len = 0;
+    status = pNtQueryObject( handle, ObjectNameInformation, buffer, sizeof(buffer), &len );
+    ok( status == STATUS_SUCCESS , "NtQueryObject returned %x\n", status );
+    str = (UNICODE_STRING *)buffer;
+    ok( len > sizeof(UNICODE_STRING), "unexpected len %u\n", len );
+    str = (UNICODE_STRING *)buffer;
+    expected_len = sizeof(UNICODE_STRING) + str->Length + sizeof(WCHAR);
+    ok( len == expected_len || broken(len == expected_len - sizeof(WCHAR)), /* NT4 */
+        "unexpected len %u\n", len );
+    ok( len > sizeof(UNICODE_STRING) + sizeof("\\test_mailslot") * sizeof(WCHAR),
+        "name too short %s\n", wine_dbgstr_w(str->Buffer) );
+    trace( "got %s len %u\n", wine_dbgstr_w(str->Buffer), len );
+    pNtClose( handle );
+
+    handle = CreateNamedPipeA( "\\\\.\\pipe\\test_pipe", PIPE_ACCESS_DUPLEX, PIPE_READMODE_BYTE,
+                               1, 1000, 1000, 1000, NULL );
+    ok( handle != INVALID_HANDLE_VALUE, "CreateNamedPipe failed err %u\n", GetLastError() );
+    len = 0;
+    status = pNtQueryObject( handle, ObjectNameInformation, buffer, sizeof(buffer), &len );
+    ok( status == STATUS_SUCCESS , "NtQueryObject returned %x\n", status );
+    str = (UNICODE_STRING *)buffer;
+    ok( len > sizeof(UNICODE_STRING), "unexpected len %u\n", len );
+    str = (UNICODE_STRING *)buffer;
+    expected_len = sizeof(UNICODE_STRING) + str->Length + sizeof(WCHAR);
+    ok( len == expected_len || broken(len == expected_len - sizeof(WCHAR)), /* NT4 */
+        "unexpected len %u\n", len );
+    ok( len > sizeof(UNICODE_STRING) + sizeof("\\test_pipe") * sizeof(WCHAR),
+        "name too short %s\n", wine_dbgstr_w(str->Buffer) );
+    trace( "got %s len %u\n", wine_dbgstr_w(str->Buffer), len );
+    pNtClose( handle );
+
+    pRtlCreateUnicodeStringFromAsciiz( &path, "\\REGISTRY\\Machine\\Software\\Classes" );
+    status = pNtCreateKey( &handle, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
+    ok( status == STATUS_SUCCESS || status == STATUS_ACCESS_DENIED,
+        "NtCreateKey failed status %x\n", status );
+    pRtlFreeUnicodeString( &path );
+    if (status == STATUS_SUCCESS)
+    {
+        len = 0;
+        status = pNtQueryObject( handle, ObjectNameInformation, buffer, sizeof(buffer), &len );
+        ok( status == STATUS_SUCCESS , "NtQueryObject returned %x\n", status );
+        str = (UNICODE_STRING *)buffer;
+        todo_wine
+        ok( len > sizeof(UNICODE_STRING), "unexpected len %u\n", len );
+        str = (UNICODE_STRING *)buffer;
+        expected_len = sizeof(UNICODE_STRING) + str->Length + sizeof(WCHAR);
+        todo_wine
+        ok( len == expected_len || broken(len == expected_len - sizeof(WCHAR)), /* NT4 */
+            "unexpected len %u\n", len );
+        todo_wine
+        ok( len > sizeof(UNICODE_STRING) + sizeof("\\Classes") * sizeof(WCHAR),
+            "name too short %s\n", wine_dbgstr_w(str->Buffer) );
+        trace( "got %s len %u\n", wine_dbgstr_w(str->Buffer), len );
+        pNtClose( handle );
+    }
+    pRtlFreeUnicodeString( &session );
+}
+
+static BOOL winver_equal_or_newer(WORD major, WORD minor)
+{
+    OSVERSIONINFOEXW info = {sizeof(info)};
+    ULONGLONG mask = 0;
+
+    info.dwMajorVersion = major;
+    info.dwMinorVersion = minor;
+
+    VER_SET_CONDITION(mask, VER_MAJORVERSION, VER_GREATER_EQUAL);
+    VER_SET_CONDITION(mask, VER_MINORVERSION, VER_GREATER_EQUAL);
+
+    return VerifyVersionInfoW(&info, VER_MAJORVERSION | VER_MINORVERSION, mask);
+}
+
+static void test_query_object_types(void)
+{
+    static const WCHAR typeW[] = {'T','y','p','e'};
+    static const WCHAR eventW[] = {'E','v','e','n','t'};
+    SYSTEM_HANDLE_INFORMATION_EX *shi;
+    OBJECT_TYPES_INFORMATION *buffer;
+    OBJECT_TYPE_INFORMATION *type;
+    NTSTATUS status;
+    HANDLE handle;
+    BOOL found;
+    ULONG len, i, event_type_index = 0;
+
+    buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(OBJECT_TYPES_INFORMATION) );
+    ok( buffer != NULL, "Failed to allocate memory\n" );
+
+    status = pNtQueryObject( NULL, ObjectTypesInformation, buffer, sizeof(OBJECT_TYPES_INFORMATION), &len );
+    ok( status == STATUS_INFO_LENGTH_MISMATCH, "NtQueryObject failed %x\n", status );
+    ok( len, "len is zero\n");
+
+    buffer = HeapReAlloc( GetProcessHeap(), 0, buffer, len );
+    ok( buffer != NULL, "Failed to allocate memory\n" );
+
+    memset( buffer, 0, len );
+    status = pNtQueryObject( NULL, ObjectTypesInformation, buffer, len, &len );
+    ok( status == STATUS_SUCCESS, "NtQueryObject failed %x\n", status );
+    ok( buffer->NumberOfTypes, "NumberOfTypes is zero\n" );
+
+    type = (OBJECT_TYPE_INFORMATION *)(buffer + 1);
+    for (i = 0; i < buffer->NumberOfTypes; i++)
+    {
+        USHORT length = type->TypeName.MaximumLength;
+        trace( "Type %u: %s\n", i, wine_dbgstr_us(&type->TypeName) );
+
+        if (i == 0)
+        {
+            ok( type->TypeName.Length == sizeof(typeW) && !strncmpW(typeW, type->TypeName.Buffer, 4),
+                "Expected 'Type' as first type, got %s\n", wine_dbgstr_us(&type->TypeName) );
+        }
+        if (type->TypeName.Length == sizeof(eventW) && !strncmpW(eventW, type->TypeName.Buffer, 5))
+        {
+            if (winver_equal_or_newer( 6, 2 ))
+                event_type_index = type->TypeIndex;
+            else
+                event_type_index = winver_equal_or_newer( 6, 1 ) ? i + 2 : i + 1;
+        }
+
+        type = (OBJECT_TYPE_INFORMATION *)ROUND_UP( (DWORD_PTR)(type + 1) + length, sizeof(DWORD_PTR) );
+    }
+
+    HeapFree( GetProcessHeap(), 0, buffer );
+
+    ok( event_type_index, "Could not find object type for events\n" );
+
+    handle = CreateEventA( NULL, FALSE, FALSE, NULL );
+    ok( handle != NULL, "Failed to create event\n" );
+
+    shi = HeapAlloc( GetProcessHeap(), 0, sizeof(*shi) );
+    ok( shi != NULL, "Failed to allocate memory\n" );
+
+    status = pNtQuerySystemInformation( SystemExtendedHandleInformation, shi, sizeof(*shi), &len );
+    ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status );
+
+    shi = HeapReAlloc( GetProcessHeap(), 0, shi, len );
+    ok( shi != NULL, "Failed to allocate memory\n" );
+
+    status = pNtQuerySystemInformation( SystemExtendedHandleInformation, shi, len, &len );
+    ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status );
+
+    found = FALSE;
+    for (i = 0; i < shi->Count; i++)
+    {
+        if (shi->Handle[i].UniqueProcessId != GetCurrentProcessId())
+            continue;
+        if ((HANDLE)(ULONG_PTR)shi->Handle[i].HandleValue != handle)
+            continue;
+
+        ok( shi->Handle[i].ObjectTypeIndex == event_type_index, "Event type does not match: %u vs %u\n",
+            shi->Handle[i].ObjectTypeIndex, event_type_index );
+
+        found = TRUE;
+        break;
+    }
+    ok( found, "Expected to find event handle %p (pid %x) in handle list\n", handle, GetCurrentProcessId() );
+
+    HeapFree( GetProcessHeap(), 0, shi );
+    CloseHandle( handle );
 }
 
 static void test_type_mismatch(void)
@@ -1190,6 +1979,121 @@ static void test_null_device(void)
     CloseHandle(ov.hEvent);
 }
 
+static DWORD WINAPI mutant_thread( void *arg )
+{
+    MUTANT_BASIC_INFORMATION info;
+    NTSTATUS status;
+    HANDLE mutant;
+    DWORD ret;
+
+    mutant = arg;
+    ret = WaitForSingleObject( mutant, 1000 );
+    ok( ret == WAIT_OBJECT_0, "WaitForSingleObject failed %08x\n", ret );
+
+    memset(&info, 0xcc, sizeof(info));
+    status = pNtQueryMutant(mutant, MutantBasicInformation, &info, sizeof(info), NULL);
+    ok( status == STATUS_SUCCESS, "NtQueryMutant failed %08x\n", status );
+    ok( info.CurrentCount == 0, "expected 0, got %d\n", info.CurrentCount );
+    ok( info.OwnedByCaller == TRUE, "expected TRUE, got %d\n", info.OwnedByCaller );
+    ok( info.AbandonedState == FALSE, "expected FALSE, got %d\n", info.AbandonedState );
+    /* abandon mutant */
+
+    return 0;
+}
+
+static void test_mutant(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','_','m','u','t','a','n','t',0};
+    MUTANT_BASIC_INFORMATION info;
+    OBJECT_ATTRIBUTES attr;
+    UNICODE_STRING str;
+    NTSTATUS status;
+    HANDLE mutant;
+    HANDLE thread;
+    DWORD ret;
+    ULONG len;
+    LONG prev;
+
+    pRtlInitUnicodeString(&str, name);
+    InitializeObjectAttributes(&attr, &str, 0, 0, NULL);
+    status = pNtCreateMutant(&mutant, GENERIC_ALL, &attr, TRUE);
+    ok( status == STATUS_SUCCESS, "Failed to create Mutant(%08x)\n", status );
+
+    /* bogus */
+    status = pNtQueryMutant(mutant, MutantBasicInformation, &info, 0, NULL);
+    ok( status == STATUS_INFO_LENGTH_MISMATCH,
+        "Failed to NtQueryMutant, expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status );
+    status = pNtQueryMutant(mutant, 0x42, &info, sizeof(info), NULL);
+    ok( status == STATUS_INVALID_INFO_CLASS || broken(status == STATUS_NOT_IMPLEMENTED), /* 32-bit on Vista/2k8 */
+        "Failed to NtQueryMutant, expected STATUS_INVALID_INFO_CLASS, got %08x\n", status );
+    status = pNtQueryMutant((HANDLE)0xdeadbeef, MutantBasicInformation, &info, sizeof(info), NULL);
+    ok( status == STATUS_INVALID_HANDLE,
+        "Failed to NtQueryMutant, expected STATUS_INVALID_HANDLE, got %08x\n", status );
+
+    /* new */
+    len = -1;
+    memset(&info, 0xcc, sizeof(info));
+    status = pNtQueryMutant(mutant, MutantBasicInformation, &info, sizeof(info), &len);
+    ok( status == STATUS_SUCCESS, "NtQueryMutant failed %08x\n", status );
+    ok( info.CurrentCount == 0, "expected 0, got %d\n", info.CurrentCount );
+    ok( info.OwnedByCaller == TRUE, "expected TRUE, got %d\n", info.OwnedByCaller );
+    ok( info.AbandonedState == FALSE, "expected FALSE, got %d\n", info.AbandonedState );
+    ok( len == sizeof(info), "got %u\n", len );
+
+    ret = WaitForSingleObject( mutant, 1000 );
+    ok( ret == WAIT_OBJECT_0, "WaitForSingleObject failed %08x\n", ret );
+
+    memset(&info, 0xcc, sizeof(info));
+    status = pNtQueryMutant(mutant, MutantBasicInformation, &info, sizeof(info), NULL);
+    ok( status == STATUS_SUCCESS, "NtQueryMutant failed %08x\n", status );
+    ok( info.CurrentCount == -1, "expected -1, got %d\n", info.CurrentCount );
+    ok( info.OwnedByCaller == TRUE, "expected TRUE, got %d\n", info.OwnedByCaller );
+    ok( info.AbandonedState == FALSE, "expected FALSE, got %d\n", info.AbandonedState );
+
+    prev = 0xdeadbeef;
+    status = pNtReleaseMutant(mutant, &prev);
+    ok( status == STATUS_SUCCESS, "NtQueryRelease failed %08x\n", status );
+    ok( prev == -1, "NtQueryRelease failed, expected -1, got %d\n", prev );
+
+    prev = 0xdeadbeef;
+    status = pNtReleaseMutant(mutant, &prev);
+    ok( status == STATUS_SUCCESS, "NtQueryRelease failed %08x\n", status );
+    ok( prev == 0, "NtQueryRelease failed, expected 0, got %d\n", prev );
+
+    memset(&info, 0xcc, sizeof(info));
+    status = pNtQueryMutant(mutant, MutantBasicInformation, &info, sizeof(info), NULL);
+    ok( status == STATUS_SUCCESS, "NtQueryMutant failed %08x\n", status );
+    ok( info.CurrentCount == 1, "expected 1, got %d\n", info.CurrentCount );
+    ok( info.OwnedByCaller == FALSE, "expected FALSE, got %d\n", info.OwnedByCaller );
+    ok( info.AbandonedState == FALSE, "expected FALSE, got %d\n", info.AbandonedState );
+
+    /* abandoned */
+    thread = CreateThread( NULL, 0, mutant_thread, mutant, 0, NULL );
+    ret = WaitForSingleObject( thread, 1000 );
+    ok( ret == WAIT_OBJECT_0, "WaitForSingleObject failed %08x\n", ret );
+    CloseHandle( thread );
+
+    memset(&info, 0xcc, sizeof(info));
+    status = pNtQueryMutant(mutant, MutantBasicInformation, &info, sizeof(info), NULL);
+    ok( status == STATUS_SUCCESS, "NtQueryMutant failed %08x\n", status );
+    ok( info.CurrentCount == 1, "expected 0, got %d\n", info.CurrentCount );
+    ok( info.OwnedByCaller == FALSE, "expected FALSE, got %d\n", info.OwnedByCaller );
+    ok( info.AbandonedState == TRUE, "expected TRUE, got %d\n", info.AbandonedState );
+
+    ret = WaitForSingleObject( mutant, 1000 );
+    ok( ret == WAIT_ABANDONED_0, "WaitForSingleObject failed %08x\n", ret );
+
+    memset(&info, 0xcc, sizeof(info));
+    status = pNtQueryMutant(mutant, MutantBasicInformation, &info, sizeof(info), NULL);
+    ok( status == STATUS_SUCCESS, "NtQueryMutant failed %08x\n", status );
+    ok( info.CurrentCount == 0, "expected 0, got %d\n", info.CurrentCount );
+    ok( info.OwnedByCaller == TRUE, "expected TRUE, got %d\n", info.OwnedByCaller );
+    ok( info.AbandonedState == FALSE, "expected FALSE, got %d\n", info.AbandonedState );
+
+    NtClose( mutant );
+}
+
 START_TEST(om)
 {
     HMODULE hntdll = GetModuleHandleA("ntdll.dll");
@@ -1206,11 +2110,19 @@ START_TEST(om)
     pRtlCreateUnicodeStringFromAsciiz = (void *)GetProcAddress(hntdll, "RtlCreateUnicodeStringFromAsciiz");
     pRtlFreeUnicodeString   = (void *)GetProcAddress(hntdll, "RtlFreeUnicodeString");
     pNtCreateEvent          = (void *)GetProcAddress(hntdll, "NtCreateEvent");
+    pNtCreateJobObject      = (void *)GetProcAddress(hntdll, "NtCreateJobObject");
+    pNtOpenJobObject        = (void *)GetProcAddress(hntdll, "NtOpenJobObject");
+    pNtCreateKey            = (void *)GetProcAddress(hntdll, "NtCreateKey");
+    pNtOpenKey              = (void *)GetProcAddress(hntdll, "NtOpenKey");
+    pNtDeleteKey            = (void *)GetProcAddress(hntdll, "NtDeleteKey");
+    pNtCreateMailslotFile   = (void *)GetProcAddress(hntdll, "NtCreateMailslotFile");
     pNtCreateMutant         = (void *)GetProcAddress(hntdll, "NtCreateMutant");
     pNtOpenEvent            = (void *)GetProcAddress(hntdll, "NtOpenEvent");
     pNtQueryEvent           = (void *)GetProcAddress(hntdll, "NtQueryEvent");
     pNtPulseEvent           = (void *)GetProcAddress(hntdll, "NtPulseEvent");
     pNtOpenMutant           = (void *)GetProcAddress(hntdll, "NtOpenMutant");
+    pNtQueryMutant          = (void *)GetProcAddress(hntdll, "NtQueryMutant");
+    pNtReleaseMutant        = (void *)GetProcAddress(hntdll, "NtReleaseMutant");
     pNtOpenFile             = (void *)GetProcAddress(hntdll, "NtOpenFile");
     pNtClose                = (void *)GetProcAddress(hntdll, "NtClose");
     pRtlInitUnicodeString   = (void *)GetProcAddress(hntdll, "RtlInitUnicodeString");
@@ -1221,8 +2133,11 @@ START_TEST(om)
     pNtCreateSymbolicLinkObject = (void *)GetProcAddress(hntdll, "NtCreateSymbolicLinkObject");
     pNtQuerySymbolicLinkObject  = (void *)GetProcAddress(hntdll, "NtQuerySymbolicLinkObject");
     pNtCreateSemaphore      =  (void *)GetProcAddress(hntdll, "NtCreateSemaphore");
+    pNtOpenSemaphore        =  (void *)GetProcAddress(hntdll, "NtOpenSemaphore");
     pNtCreateTimer          =  (void *)GetProcAddress(hntdll, "NtCreateTimer");
+    pNtOpenTimer            =  (void *)GetProcAddress(hntdll, "NtOpenTimer");
     pNtCreateSection        =  (void *)GetProcAddress(hntdll, "NtCreateSection");
+    pNtOpenSection          =  (void *)GetProcAddress(hntdll, "NtOpenSection");
     pNtQueryObject          =  (void *)GetProcAddress(hntdll, "NtQueryObject");
     pNtReleaseSemaphore     =  (void *)GetProcAddress(hntdll, "NtReleaseSemaphore");
     pNtCreateKeyedEvent     =  (void *)GetProcAddress(hntdll, "NtCreateKeyedEvent");
@@ -1230,15 +2145,20 @@ START_TEST(om)
     pNtWaitForKeyedEvent    =  (void *)GetProcAddress(hntdll, "NtWaitForKeyedEvent");
     pNtReleaseKeyedEvent    =  (void *)GetProcAddress(hntdll, "NtReleaseKeyedEvent");
     pNtCreateIoCompletion   =  (void *)GetProcAddress(hntdll, "NtCreateIoCompletion");
+    pNtOpenIoCompletion     =  (void *)GetProcAddress(hntdll, "NtOpenIoCompletion");
+    pNtQuerySystemInformation = (void *)GetProcAddress(hntdll, "NtQuerySystemInformation");
 
     test_case_sensitive();
     test_namespace_pipe();
     test_name_collisions();
+    test_name_limits();
     test_directory();
     test_symboliclink();
     test_query_object();
+    test_query_object_types();
     test_type_mismatch();
     test_event();
+    test_mutant();
     test_keyed_events();
     test_null_device();
 }
index 673bc93..81cc17e 100755 (executable)
@@ -267,7 +267,7 @@ static void test_RtlGetFullPathName_U(void)
             { "c:/test/  ....   ..   ",      "c:\\test\\",       NULL},
             { "c:/test/..",                  "c:\\",             NULL},
             { "c:/test/.. ",                 "c:\\test\\",       NULL},
-            { "c:/TEST",                     "c:\\test",         "test"},
+            { "c:/TEST",                     "c:\\TEST",         "TEST"},
             { "c:/test/file",                "c:\\test\\file",   "file"},
             { "c:/test./file",               "c:\\test\\file",   "file"},
             { "c:/test.. /file",             "c:\\test.. \\file","file"},
@@ -281,6 +281,7 @@ static void test_RtlGetFullPathName_U(void)
             { "c:///test\\..\\file\\..\\//", "c:\\",             NULL},
             { "c:/test../file",              "c:\\test.\\file",  "file",
                                              "c:\\test..\\file", "file"},  /* vista */
+            { "c:\\test",                    "c:\\test",         "test"},
             { NULL, NULL, NULL}
         };
 
@@ -327,14 +328,14 @@ static void test_RtlGetFullPathName_U(void)
             "Wrong result %d/%d for \"%s\"\n", ret, len, test->path );
         ok(pRtlUnicodeToMultiByteN(rbufferA,MAX_PATH,&reslen,rbufferW,(lstrlenW(rbufferW) + 1) * sizeof(WCHAR)) == STATUS_SUCCESS,
            "RtlUnicodeToMultiByteN failed\n");
-        ok(!lstrcmpiA(rbufferA,test->rname) || (test->alt_rname && !lstrcmpiA(rbufferA,test->alt_rname)),
+        ok(!lstrcmpA(rbufferA,test->rname) || (test->alt_rname && !lstrcmpA(rbufferA,test->alt_rname)),
            "Got \"%s\" expected \"%s\"\n",rbufferA,test->rname);
         if (file_part)
         {
             ok(pRtlUnicodeToMultiByteN(rfileA,MAX_PATH,&reslen,file_part,(lstrlenW(file_part) + 1) * sizeof(WCHAR)) == STATUS_SUCCESS,
                "RtlUnicodeToMultiByteN failed\n");
-            ok((test->rfile && !lstrcmpiA(rfileA,test->rfile)) ||
-               (test->alt_rfile && !lstrcmpiA(rfileA,test->alt_rfile)),
+            ok((test->rfile && !lstrcmpA(rfileA,test->rfile)) ||
+               (test->alt_rfile && !lstrcmpA(rfileA,test->alt_rfile)),
                "Got \"%s\" expected \"%s\"\n",rfileA,test->rfile);
         }
         else
index 9e4fd2f..a3984e1 100644 (file)
@@ -75,6 +75,7 @@ static NTSTATUS (WINAPI *pNtCreateNamedPipeFile) (PHANDLE handle, ULONG access,
 static NTSTATUS (WINAPI *pNtQueryInformationFile) (IN HANDLE FileHandle, OUT PIO_STATUS_BLOCK IoStatusBlock, OUT PVOID FileInformation, IN ULONG Length, IN FILE_INFORMATION_CLASS FileInformationClass);
 static NTSTATUS (WINAPI *pNtSetInformationFile) (HANDLE handle, PIO_STATUS_BLOCK io, PVOID ptr, ULONG len, FILE_INFORMATION_CLASS class);
 static NTSTATUS (WINAPI *pNtCancelIoFile) (HANDLE hFile, PIO_STATUS_BLOCK io_status);
+static NTSTATUS (WINAPI *pNtCancelIoFileEx) (HANDLE hFile, IO_STATUS_BLOCK *iosb, IO_STATUS_BLOCK *io_status);
 static void (WINAPI *pRtlInitUnicodeString) (PUNICODE_STRING target, PCWSTR source);
 
 static HANDLE (WINAPI *pOpenThread)(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwThreadId);
@@ -95,6 +96,7 @@ static BOOL init_func_ptrs(void)
     loadfunc(NtQueryInformationFile)
     loadfunc(NtSetInformationFile)
     loadfunc(NtCancelIoFile)
+    loadfunc(NtCancelIoFileEx)
     loadfunc(RtlInitUnicodeString)
 
     /* not fatal */
@@ -104,6 +106,11 @@ static BOOL init_func_ptrs(void)
     return TRUE;
 }
 
+static inline BOOL is_signaled( HANDLE obj )
+{
+    return WaitForSingleObject( obj, 0 ) == WAIT_OBJECT_0;
+}
+
 static const WCHAR testpipe[] = { '\\', '\\', '.', '\\', 'p', 'i', 'p', 'e', '\\',
                                   't', 'e', 's', 't', 'p', 'i', 'p', 'e', 0 };
 static const WCHAR testpipe_nt[] = { '\\', '?', '?', '\\', 'p', 'i', 'p', 'e', '\\',
@@ -133,6 +140,21 @@ static NTSTATUS create_pipe(PHANDLE handle, ULONG sharing, ULONG options)
     return res;
 }
 
+static BOOL ioapc_called;
+static void CALLBACK ioapc(void *arg, PIO_STATUS_BLOCK io, ULONG reserved)
+{
+    ioapc_called = TRUE;
+}
+
+static NTSTATUS listen_pipe(HANDLE hPipe, HANDLE hEvent, PIO_STATUS_BLOCK iosb, BOOL use_apc)
+{
+    int dummy;
+
+    ioapc_called = FALSE;
+
+    return pNtFsControlFile(hPipe, hEvent, use_apc ? &ioapc: NULL, use_apc ? &dummy: NULL, iosb, FSCTL_PIPE_LISTEN, 0, 0, 0, 0);
+}
+
 static void test_create_invalid(void)
 {
     IO_STATUS_BLOCK iosb;
@@ -200,6 +222,7 @@ static void test_create(void)
     int j, k;
     FILE_PIPE_LOCAL_INFORMATION info;
     IO_STATUS_BLOCK iosb;
+    HANDLE hEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
 
     static const DWORD access[] = { 0, GENERIC_READ, GENERIC_WRITE, GENERIC_READ | GENERIC_WRITE};
     static const DWORD sharing[] =    { FILE_SHARE_READ, FILE_SHARE_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE };
@@ -210,12 +233,15 @@ static void test_create(void)
             HANDLE hclient;
             BOOL should_succeed = TRUE;
 
-            res  = create_pipe(&hserver, sharing[j], FILE_SYNCHRONOUS_IO_NONALERT);
+            res  = create_pipe(&hserver, sharing[j], 0);
             if (res) {
                 ok(0, "NtCreateNamedPipeFile returned %x, sharing: %x\n", res, sharing[j]);
                 continue;
             }
 
+            res = listen_pipe(hserver, hEvent, &iosb, FALSE);
+            ok(res == STATUS_PENDING, "NtFsControlFile returned %x\n", res);
+
             res = pNtQueryInformationFile(hserver, &iosb, &info, sizeof(info), (FILE_INFORMATION_CLASS)24);
             ok(!res, "NtQueryInformationFile for server returned %x, sharing: %x\n", res, sharing[j]);
             ok(info.NamedPipeConfiguration == pipe_config[j], "wrong duplex status for pipe: %d, expected %d\n",
@@ -245,21 +271,7 @@ static void test_create(void)
             CloseHandle(hserver);
         }
     }
-}
-
-static BOOL ioapc_called;
-static void CALLBACK ioapc(void *arg, PIO_STATUS_BLOCK io, ULONG reserved)
-{
-    ioapc_called = TRUE;
-}
-
-static NTSTATUS listen_pipe(HANDLE hPipe, HANDLE hEvent, PIO_STATUS_BLOCK iosb, BOOL use_apc)
-{
-    int dummy;
-
-    ioapc_called = FALSE;
-
-    return pNtFsControlFile(hPipe, hEvent, use_apc ? &ioapc: NULL, use_apc ? &dummy: NULL, iosb, FSCTL_PIPE_LISTEN, 0, 0, 0, 0);
+    CloseHandle(hEvent);
 }
 
 static void test_overlapped(void)
@@ -305,10 +317,12 @@ static void test_overlapped(void)
 
     if (hClient != INVALID_HANDLE_VALUE)
     {
+        SetEvent(hEvent);
         memset(&iosb, 0x55, sizeof(iosb));
         res = listen_pipe(hPipe, hEvent, &iosb, TRUE);
         ok(res == STATUS_PIPE_CONNECTED, "NtFsControlFile returned %x\n", res);
         ok(U(iosb).Status == 0x55555555, "iosb.Status got changed to %x\n", U(iosb).Status);
+        ok(!is_signaled(hEvent), "hEvent not signaled\n");
 
         CloseHandle(hClient);
     }
@@ -317,6 +331,73 @@ static void test_overlapped(void)
     CloseHandle(hEvent);
 }
 
+static void test_completion(void)
+{
+    static const char buf[] = "testdata";
+    FILE_IO_COMPLETION_NOTIFICATION_INFORMATION info;
+    HANDLE port, pipe, client;
+    IO_STATUS_BLOCK iosb;
+    OVERLAPPED ov, *pov;
+    IO_STATUS_BLOCK io;
+    NTSTATUS status;
+    DWORD num_bytes;
+    ULONG_PTR key;
+    DWORD dwret;
+    BOOL ret;
+
+    memset(&ov, 0, sizeof(ov));
+    ov.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
+    ok(ov.hEvent != INVALID_HANDLE_VALUE, "CreateEvent failed, error %u\n", GetLastError());
+
+    status = create_pipe(&pipe, FILE_SHARE_READ | FILE_SHARE_WRITE, 0 /* OVERLAPPED */);
+    ok(!status, "NtCreateNamedPipeFile returned %x\n", status);
+    status = listen_pipe(pipe, ov.hEvent, &iosb, FALSE);
+    ok(status == STATUS_PENDING, "NtFsControlFile returned %x\n", status);
+
+    client = CreateFileW(testpipe, GENERIC_READ | GENERIC_WRITE, 0, 0,
+                         OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
+    ok(client != INVALID_HANDLE_VALUE, "CreateFile failed, error %u\n", GetLastError());
+    dwret = WaitForSingleObject(ov.hEvent, 0);
+    ok(dwret == WAIT_OBJECT_0, "expected WAIT_OBJECT_0, got %u\n", dwret);
+
+    port = CreateIoCompletionPort(client, NULL, 0xdeadbeef, 0);
+    ok(port != NULL, "CreateIoCompletionPort failed, error %u\n", GetLastError());
+
+    ret = WriteFile(client, buf, sizeof(buf), &num_bytes, &ov);
+    ok(ret, "WriteFile failed, error %u\n", GetLastError());
+    ok(num_bytes == sizeof(buf), "expected sizeof(buf), got %u\n", num_bytes);
+
+    key = 0;
+    pov = NULL;
+    ret = GetQueuedCompletionStatus(port, &num_bytes, &key, &pov, 1000);
+    ok(ret, "GetQueuedCompletionStatus failed, error %u\n", GetLastError());
+    ok(key == 0xdeadbeef, "expected 0xdeadbeef, got %lx\n", key);
+    ok(pov == &ov, "expected %p, got %p\n", &ov, pov);
+
+    info.Flags = FILE_SKIP_COMPLETION_PORT_ON_SUCCESS;
+    status = pNtSetInformationFile(client, &io, &info, sizeof(info), FileIoCompletionNotificationInformation);
+    ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status);
+
+    info.Flags = 0;
+    status = pNtQueryInformationFile(client, &io, &info, sizeof(info), FileIoCompletionNotificationInformation);
+    ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status);
+    ok((info.Flags & FILE_SKIP_COMPLETION_PORT_ON_SUCCESS) != 0, "got %08x\n", info.Flags);
+
+    ret = WriteFile(client, buf, sizeof(buf), &num_bytes, &ov);
+    ok(ret, "WriteFile failed, error %u\n", GetLastError());
+    ok(num_bytes == sizeof(buf), "expected sizeof(buf), got %u\n", num_bytes);
+
+    pov = (void *)0xdeadbeef;
+    ret = GetQueuedCompletionStatus(port, &num_bytes, &key, &pov, 1000);
+    ok(!ret, "GetQueuedCompletionStatus succeeded\n");
+    ok(pov == NULL, "expected NULL, got %p\n", pov);
+
+    CloseHandle(ov.hEvent);
+    CloseHandle(client);
+    CloseHandle(pipe);
+    CloseHandle(port);
+}
+
 static BOOL userapc_called;
 static void CALLBACK userapc(ULONG_PTR dwParam)
 {
@@ -498,6 +579,21 @@ static void test_cancelio(void)
 
     ok(ioapc_called, "IOAPC didn't run\n");
 
+    CloseHandle(hPipe);
+
+    res = create_pipe(&hPipe, FILE_SHARE_READ | FILE_SHARE_WRITE, 0 /* OVERLAPPED */);
+    ok(!res, "NtCreateNamedPipeFile returned %x\n", res);
+
+    memset(&iosb, 0x55, sizeof(iosb));
+    res = listen_pipe(hPipe, hEvent, &iosb, FALSE);
+    ok(res == STATUS_PENDING, "NtFsControlFile returned %x\n", res);
+
+    res = pNtCancelIoFileEx(hPipe, &iosb, &cancel_sb);
+    ok(!res, "NtCancelIoFileEx returned %x\n", res);
+
+    ok(U(iosb).Status == STATUS_CANCELLED, "Wrong iostatus %x\n", U(iosb).Status);
+    ok(WaitForSingleObject(hEvent, 0) == 0, "hEvent not signaled\n");
+
     CloseHandle(hEvent);
     CloseHandle(hPipe);
 }
@@ -688,6 +784,389 @@ static void test_filepipeinfo(void)
     CloseHandle(hServer);
 }
 
+static void WINAPI apc( void *arg, IO_STATUS_BLOCK *iosb, ULONG reserved )
+{
+    int *count = arg;
+    (*count)++;
+    ok( !reserved, "reserved is not 0: %x\n", reserved );
+}
+
+static void test_peek(HANDLE pipe, BOOL is_msgmode)
+{
+    FILE_PIPE_PEEK_BUFFER buf;
+    IO_STATUS_BLOCK iosb;
+    HANDLE event = CreateEventA( NULL, TRUE, FALSE, NULL );
+    NTSTATUS status;
+
+    memset(&iosb, 0x55, sizeof(iosb));
+    status = NtFsControlFile(pipe, NULL, NULL, NULL, &iosb, FSCTL_PIPE_PEEK, NULL, 0, &buf, sizeof(buf));
+    ok(!status || status == STATUS_PENDING, "NtFsControlFile failed: %x\n", status);
+    ok(!iosb.Status, "iosb.Status = %x\n", iosb.Status);
+    ok(buf.ReadDataAvailable == 1, "ReadDataAvailable = %u\n", buf.ReadDataAvailable);
+
+    ResetEvent(event);
+    memset(&iosb, 0x55, sizeof(iosb));
+    status = NtFsControlFile(pipe, event, NULL, NULL, &iosb, FSCTL_PIPE_PEEK, NULL, 0, &buf, sizeof(buf));
+    ok(!status || status == STATUS_PENDING, "NtFsControlFile failed: %x\n", status);
+    ok(buf.ReadDataAvailable == 1, "ReadDataAvailable = %u\n", buf.ReadDataAvailable);
+    ok(!iosb.Status, "iosb.Status = %x\n", iosb.Status);
+    todo_wine_if(!is_msgmode)
+    ok(is_signaled(event), "event is not signaled\n");
+
+    CloseHandle(event);
+}
+
+#define PIPENAME "\\\\.\\pipe\\ntdll_tests_pipe.c"
+
+static BOOL create_pipe_pair( HANDLE *read, HANDLE *write, ULONG flags, ULONG type, ULONG size )
+{
+    const BOOL server_reader = flags & PIPE_ACCESS_INBOUND;
+    HANDLE client, server;
+
+    server = CreateNamedPipeA(PIPENAME, flags, PIPE_WAIT | type,
+                              1, size, size, NMPWAIT_USE_DEFAULT_WAIT, NULL);
+    ok(server != INVALID_HANDLE_VALUE, "CreateNamedPipe failed\n");
+
+    client = CreateFileA(PIPENAME, server_reader ? GENERIC_WRITE : GENERIC_READ | FILE_WRITE_ATTRIBUTES, 0,
+                         NULL, OPEN_EXISTING, flags & FILE_FLAG_OVERLAPPED, 0);
+    ok(client != INVALID_HANDLE_VALUE, "CreateFile failed (%d)\n", GetLastError());
+
+    if(server_reader)
+    {
+        *read = server;
+        *write = client;
+    }
+    else
+    {
+        if(type & PIPE_READMODE_MESSAGE)
+        {
+            DWORD read_mode = PIPE_READMODE_MESSAGE;
+            ok(SetNamedPipeHandleState(client, &read_mode, NULL, NULL), "Change mode\n");
+        }
+
+        *read = client;
+        *write = server;
+    }
+    return TRUE;
+}
+
+static void read_pipe_test(ULONG pipe_flags, ULONG pipe_type)
+{
+    IO_STATUS_BLOCK iosb, iosb2;
+    HANDLE handle, read, write;
+    HANDLE event = CreateEventA( NULL, TRUE, FALSE, NULL );
+    int apc_count = 0;
+    char buffer[128];
+    DWORD written;
+    BOOL ret;
+    NTSTATUS status;
+
+    if (!create_pipe_pair( &read, &write, FILE_FLAG_OVERLAPPED | pipe_flags, pipe_type, 4096 )) return;
+
+    /* try read with no data */
+    U(iosb).Status = 0xdeadbabe;
+    iosb.Information = 0xdeadbeef;
+    ok( is_signaled( read ), "read handle is not signaled\n" );
+    status = NtReadFile( read, event, apc, &apc_count, &iosb, buffer, 1, NULL, NULL );
+    ok( status == STATUS_PENDING, "wrong status %x\n", status );
+    ok( !is_signaled( read ), "read handle is signaled\n" );
+    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" );
+    ret = WriteFile( write, buffer, 1, &written, NULL );
+    ok(ret && written == 1, "WriteFile error %d\n", GetLastError());
+    /* iosb updated here by async i/o */
+    Sleep(1);  /* FIXME: needed for wine to run the i/o apc  */
+    ok( U(iosb).Status == 0, "wrong status %x\n", U(iosb).Status );
+    ok( iosb.Information == 1, "wrong info %lu\n", iosb.Information );
+    ok( !is_signaled( read ), "read handle is signaled\n" );
+    ok( is_signaled( event ), "event is not signaled\n" );
+    ok( !apc_count, "apc was called\n" );
+    apc_count = 0;
+    SleepEx( 1, FALSE ); /* non-alertable sleep */
+    ok( !apc_count, "apc was called\n" );
+    SleepEx( 1, TRUE ); /* alertable sleep */
+    ok( apc_count == 1, "apc not called\n" );
+
+    /* with no event, the pipe handle itself gets signaled */
+    apc_count = 0;
+    U(iosb).Status = 0xdeadbabe;
+    iosb.Information = 0xdeadbeef;
+    ok( !is_signaled( read ), "read handle is signaled\n" );
+    status = NtReadFile( read, 0, apc, &apc_count, &iosb, buffer, 1, NULL, NULL );
+    ok( status == STATUS_PENDING, "wrong status %x\n", status );
+    ok( !is_signaled( read ), "read handle 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" );
+    ret = WriteFile( write, buffer, 1, &written, NULL );
+    ok(ret && written == 1, "WriteFile error %d\n", GetLastError());
+    /* iosb updated here by async i/o */
+    Sleep(1);  /* FIXME: needed for wine to run the i/o apc  */
+    ok( U(iosb).Status == 0, "wrong status %x\n", U(iosb).Status );
+    ok( iosb.Information == 1, "wrong info %lu\n", iosb.Information );
+    ok( is_signaled( read ), "read handle is not signaled\n" );
+    ok( !apc_count, "apc was called\n" );
+    apc_count = 0;
+    SleepEx( 1, FALSE ); /* non-alertable sleep */
+    ok( !apc_count, "apc was called\n" );
+    SleepEx( 1, TRUE ); /* alertable sleep */
+    ok( apc_count == 1, "apc not called\n" );
+
+    /* now read with data ready */
+    apc_count = 0;
+    U(iosb).Status = 0xdeadbabe;
+    iosb.Information = 0xdeadbeef;
+    ResetEvent( event );
+    ret = WriteFile( write, buffer, 1, &written, NULL );
+    ok(ret && written == 1, "WriteFile error %d\n", GetLastError());
+
+    test_peek(read, pipe_type & PIPE_TYPE_MESSAGE);
+
+    status = NtReadFile( read, event, apc, &apc_count, &iosb, buffer, 1, NULL, NULL );
+    ok( status == STATUS_SUCCESS, "wrong status %x\n", status );
+    ok( U(iosb).Status == 0, "wrong status %x\n", U(iosb).Status );
+    ok( iosb.Information == 1, "wrong info %lu\n", iosb.Information );
+    ok( is_signaled( event ), "event is not signaled\n" );
+    ok( !apc_count, "apc was called\n" );
+    SleepEx( 1, FALSE ); /* non-alertable sleep */
+    ok( !apc_count, "apc was called\n" );
+    SleepEx( 1, TRUE ); /* alertable sleep */
+    ok( apc_count == 1, "apc not called\n" );
+
+    /* now partial read with data ready */
+    apc_count = 0;
+    U(iosb).Status = 0xdeadbabe;
+    iosb.Information = 0xdeadbeef;
+    ResetEvent( event );
+    ret = WriteFile( write, buffer, 2, &written, NULL );
+    ok(ret && written == 2, "WriteFile error %d\n", GetLastError());
+    status = NtReadFile( read, event, apc, &apc_count, &iosb, buffer, 1, NULL, NULL );
+    if (pipe_type & PIPE_READMODE_MESSAGE)
+    {
+        ok( status == STATUS_BUFFER_OVERFLOW, "wrong status %x\n", status );
+        ok( U(iosb).Status == STATUS_BUFFER_OVERFLOW, "wrong status %x\n", U(iosb).Status );
+    }
+    else
+    {
+        ok( status == STATUS_SUCCESS, "wrong status %x\n", status );
+        ok( U(iosb).Status == 0, "wrong status %x\n", U(iosb).Status );
+    }
+    ok( iosb.Information == 1, "wrong info %lu\n", iosb.Information );
+    ok( is_signaled( event ), "event is not signaled\n" );
+    ok( !apc_count, "apc was called\n" );
+    SleepEx( 1, FALSE ); /* non-alertable sleep */
+    ok( !apc_count, "apc was called\n" );
+    SleepEx( 1, TRUE ); /* alertable sleep */
+    ok( apc_count == 1, "apc not called\n" );
+    apc_count = 0;
+    status = NtReadFile( read, event, apc, &apc_count, &iosb, buffer, 1, NULL, NULL );
+    ok( status == STATUS_SUCCESS, "wrong status %x\n", status );
+    ok( U(iosb).Status == 0, "wrong status %x\n", U(iosb).Status );
+    ok( iosb.Information == 1, "wrong info %lu\n", iosb.Information );
+    ok( is_signaled( event ), "event is not signaled\n" );
+    ok( !apc_count, "apc was called\n" );
+    SleepEx( 1, FALSE ); /* non-alertable sleep */
+    ok( !apc_count, "apc was called\n" );
+    SleepEx( 1, TRUE ); /* alertable sleep */
+    ok( apc_count == 1, "apc not called\n" );
+
+    /* try read with no data */
+    apc_count = 0;
+    U(iosb).Status = 0xdeadbabe;
+    iosb.Information = 0xdeadbeef;
+    ok( is_signaled( event ), "event is not signaled\n" ); /* check that read resets the event */
+    status = NtReadFile( 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" );
+    ret = WriteFile( write, buffer, 1, &written, NULL );
+    ok(ret && written == 1, "WriteFile error %d\n", GetLastError());
+    /* partial read is good enough */
+    Sleep(1);  /* FIXME: needed for wine to run the i/o apc  */
+    ok( is_signaled( event ), "event is not signaled\n" );
+    ok( U(iosb).Status == 0, "wrong status %x\n", U(iosb).Status );
+    ok( iosb.Information == 1, "wrong info %lu\n", iosb.Information );
+    ok( !apc_count, "apc was called\n" );
+    SleepEx( 1, TRUE ); /* alertable sleep */
+    ok( apc_count == 1, "apc was not called\n" );
+
+    /* read from disconnected pipe */
+    apc_count = 0;
+    U(iosb).Status = 0xdeadbabe;
+    iosb.Information = 0xdeadbeef;
+    CloseHandle( write );
+    status = NtReadFile( read, event, apc, &apc_count, &iosb, buffer, 1, NULL, NULL );
+    ok( status == STATUS_PIPE_BROKEN, "wrong status %x\n", status );
+    ok( U(iosb).Status == 0xdeadbabe, "wrong status %x\n", U(iosb).Status );
+    ok( iosb.Information == 0xdeadbeef, "wrong info %lu\n", iosb.Information );
+    ok( !is_signaled( event ), "event is signaled\n" );
+    ok( !apc_count, "apc was called\n" );
+    SleepEx( 1, TRUE ); /* alertable sleep */
+    ok( !apc_count, "apc was called\n" );
+    CloseHandle( read );
+
+    /* read from disconnected pipe, with invalid event handle */
+    apc_count = 0;
+    U(iosb).Status = 0xdeadbabe;
+    iosb.Information = 0xdeadbeef;
+    status = NtReadFile( read, (HANDLE)0xdeadbeef, apc, &apc_count, &iosb, buffer, 1, NULL, NULL );
+    ok( status == STATUS_INVALID_HANDLE, "wrong status %x\n", status );
+    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" );
+    SleepEx( 1, TRUE ); /* alertable sleep */
+    ok( !apc_count, "apc was called\n" );
+    CloseHandle( read );
+
+    /* read from closed handle */
+    apc_count = 0;
+    U(iosb).Status = 0xdeadbabe;
+    iosb.Information = 0xdeadbeef;
+    SetEvent( event );
+    status = NtReadFile( read, event, apc, &apc_count, &iosb, buffer, 1, NULL, NULL );
+    ok( status == STATUS_INVALID_HANDLE, "wrong status %x\n", status );
+    ok( U(iosb).Status == 0xdeadbabe, "wrong status %x\n", U(iosb).Status );
+    ok( iosb.Information == 0xdeadbeef, "wrong info %lu\n", iosb.Information );
+    ok( is_signaled( event ), "event is not signaled\n" );  /* not reset on invalid handle */
+    ok( !apc_count, "apc was called\n" );
+    SleepEx( 1, TRUE ); /* alertable sleep */
+    ok( !apc_count, "apc was called\n" );
+
+    /* disconnect while async read is in progress */
+    if (!create_pipe_pair( &read, &write, FILE_FLAG_OVERLAPPED | pipe_flags, pipe_type, 4096 )) return;
+    apc_count = 0;
+    U(iosb).Status = 0xdeadbabe;
+    iosb.Information = 0xdeadbeef;
+    status = NtReadFile( 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" );
+    CloseHandle( write );
+    Sleep(1);  /* FIXME: needed for wine to run the i/o apc  */
+    todo_wine_if(!(pipe_type & PIPE_TYPE_MESSAGE) && (pipe_flags & PIPE_ACCESS_OUTBOUND))
+    ok( U(iosb).Status == STATUS_PIPE_BROKEN, "wrong status %x\n", U(iosb).Status );
+    ok( iosb.Information == 0, "wrong info %lu\n", iosb.Information );
+    ok( is_signaled( event ), "event is not signaled\n" );
+    ok( !apc_count, "apc was called\n" );
+    SleepEx( 1, TRUE ); /* alertable sleep */
+    ok( apc_count == 1, "apc was not called\n" );
+    CloseHandle( read );
+
+    if (!create_pipe_pair( &read, &write, FILE_FLAG_OVERLAPPED | pipe_flags, pipe_type, 4096 )) return;
+    ret = DuplicateHandle(GetCurrentProcess(), read, GetCurrentProcess(), &handle, 0, TRUE, DUPLICATE_SAME_ACCESS);
+    ok(ret, "Failed to duplicate handle: %d\n", GetLastError());
+
+    apc_count = 0;
+    U(iosb).Status = 0xdeadbabe;
+    iosb.Information = 0xdeadbeef;
+    status = NtReadFile( 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 not signaled\n" );
+    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 = NtReadFile( 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 not signaled\n" );
+    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_pair( &read, &write, FILE_FLAG_OVERLAPPED | pipe_flags, pipe_type, 4096 )) return;
+
+        apc_count = 0;
+        U(iosb).Status = 0xdeadbabe;
+        iosb.Information = 0xdeadbeef;
+        status = NtReadFile( 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 not signaled\n" );
+        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 = NtReadFile( 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 = NtReadFile( 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 not signaled\n" );
+        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 );
+    }
+
+    CloseHandle(event);
+}
+
 START_TEST(pipe)
 {
     if (!init_func_ptrs())
@@ -702,6 +1181,9 @@ START_TEST(pipe)
     trace("starting overlapped tests\n");
     test_overlapped();
 
+    trace("starting completion tests\n");
+    test_completion();
+
     trace("starting FILE_PIPE_INFORMATION tests\n");
     test_filepipeinfo();
 
@@ -716,4 +1198,17 @@ START_TEST(pipe)
 
     trace("starting cancelio tests\n");
     test_cancelio();
+
+    trace("starting byte read in byte mode client -> server\n");
+    read_pipe_test(PIPE_ACCESS_INBOUND, PIPE_TYPE_BYTE);
+    trace("starting byte read in message mode client -> server\n");
+    read_pipe_test(PIPE_ACCESS_INBOUND, PIPE_TYPE_MESSAGE);
+    trace("starting message read in message mode client -> server\n");
+    read_pipe_test(PIPE_ACCESS_INBOUND, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE);
+    trace("starting byte read in byte mode server -> client\n");
+    read_pipe_test(PIPE_ACCESS_OUTBOUND, PIPE_TYPE_BYTE);
+    trace("starting byte read in message mode server -> client\n");
+    read_pipe_test(PIPE_ACCESS_OUTBOUND, PIPE_TYPE_MESSAGE);
+    trace("starting message read in message mode server -> client\n");
+    read_pipe_test(PIPE_ACCESS_OUTBOUND, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE);
 }
diff --git a/modules/rostests/winetests/ntdll/process.c b/modules/rostests/winetests/ntdll/process.c
new file mode 100644 (file)
index 0000000..41303b7
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * Unit test suite for process functions
+ *
+ * Copyright 2017 Michael Müller
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <stdio.h>
+
+#include "ntdll_test.h"
+
+#include "windef.h"
+#include "winbase.h"
+
+static NTSTATUS (WINAPI *pNtResumeProcess)(HANDLE);
+static NTSTATUS (WINAPI *pNtSuspendProcess)(HANDLE);
+static NTSTATUS (WINAPI *pNtSuspendThread)(HANDLE,PULONG);
+static NTSTATUS (WINAPI *pNtResumeThread)(HANDLE);
+
+static void test_NtSuspendProcess(char *process_name)
+{
+    PROCESS_INFORMATION info;
+    DEBUG_EVENT ev;
+    STARTUPINFOA startup;
+    NTSTATUS status;
+    HANDLE event;
+    char buffer[MAX_PATH];
+    ULONG count;
+    DWORD ret;
+
+    status = pNtResumeProcess(GetCurrentProcess());
+    ok(status == STATUS_SUCCESS, "NtResumeProcess failed: %x\n", status);
+
+    event = CreateEventA(NULL, TRUE, FALSE, "wine_suspend_event");
+    ok(!!event, "Failed to create event: %u\n", GetLastError());
+
+    memset(&startup, 0, sizeof(startup));
+    startup.cb = sizeof(startup);
+
+    sprintf(buffer, "%s tests/process.c dummy_process wine_suspend_event", process_name);
+    ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info);
+    ok(ret, "CreateProcess failed with error %u\n", GetLastError());
+
+    ret = WaitForSingleObject(event, 500);
+    ok(ret == WAIT_OBJECT_0, "Event was not signaled: %d\n", ret);
+
+    status = pNtSuspendProcess(info.hProcess);
+    ok(status == STATUS_SUCCESS, "NtResumeProcess failed: %x\n", status);
+
+    ResetEvent(event);
+
+    ret = WaitForSingleObject(event, 200);
+    ok(ret == WAIT_TIMEOUT, "Expected timeout, got: %d\n", ret);
+
+    status = NtResumeThread(info.hThread, &count);
+    ok(status == STATUS_SUCCESS, "NtResumeProcess failed: %x\n", status);
+    ok(count == 1, "Expected count 1, got %d\n", count);
+
+    ret = WaitForSingleObject(event, 200);
+    ok(ret == WAIT_OBJECT_0, "Event was not signaled: %d\n", ret);
+
+    status = pNtResumeProcess(info.hProcess);
+    ok(status == STATUS_SUCCESS, "NtResumeProcess failed: %x\n", status);
+
+    status = pNtSuspendThread(info.hThread, &count);
+    ok(status == STATUS_SUCCESS, "NtSuspendThread failed: %x\n", status);
+    ok(count == 0, "Expected count 0, got %d\n", count);
+
+    ResetEvent(event);
+
+    ret = WaitForSingleObject(event, 200);
+    ok(ret == WAIT_TIMEOUT, "Expected timeout, got: %d\n", ret);
+
+    status = pNtResumeProcess(info.hProcess);
+    ok(status == STATUS_SUCCESS, "NtResumeProcess failed: %x\n", status);
+
+    ret = WaitForSingleObject(event, 200);
+    ok(ret == WAIT_OBJECT_0, "Event was not signaled: %d\n", ret);
+
+    status = pNtSuspendThread(info.hThread, &count);
+    ok(status == STATUS_SUCCESS, "NtSuspendThread failed: %x\n", status);
+    ok(count == 0, "Expected count 0, got %d\n", count);
+
+    status = pNtSuspendThread(info.hThread, &count);
+    ok(status == STATUS_SUCCESS, "NtSuspendThread failed: %x\n", status);
+    ok(count == 1, "Expected count 1, got %d\n", count);
+
+    ResetEvent(event);
+
+    ret = WaitForSingleObject(event, 200);
+    ok(ret == WAIT_TIMEOUT, "Expected timeout, got: %d\n", ret);
+
+    status = pNtResumeProcess(info.hProcess);
+    ok(status == STATUS_SUCCESS, "NtResumeProcess failed: %x\n", status);
+
+    ret = WaitForSingleObject(event, 200);
+    ok(ret == WAIT_TIMEOUT, "Expected timeout, got: %d\n", ret);
+
+    status = pNtResumeProcess(info.hProcess);
+    ok(status == STATUS_SUCCESS, "NtResumeProcess failed: %x\n", status);
+
+    ret = WaitForSingleObject(event, 200);
+    ok(ret == WAIT_OBJECT_0, "Event was not signaled: %d\n", ret);
+
+    ret = DebugActiveProcess(info.dwProcessId);
+    ok(ret, "Failed to debug process: %d\n", GetLastError());
+
+    ResetEvent(event);
+
+    ret = WaitForSingleObject(event, 200);
+    ok(ret == WAIT_TIMEOUT, "Expected timeout, got: %d\n", ret);
+
+    for (;;)
+    {
+        ret = WaitForDebugEvent(&ev, INFINITE);
+        ok(ret, "WaitForDebugEvent failed, last error %#x.\n", GetLastError());
+        if (!ret) break;
+
+        if (ev.dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENT) break;
+
+        ret = ContinueDebugEvent(ev.dwProcessId, ev.dwThreadId, DBG_CONTINUE);
+        ok(ret, "ContinueDebugEvent failed, last error %#x.\n", GetLastError());
+        if (!ret) break;
+    }
+
+    ResetEvent(event);
+
+    ret = WaitForSingleObject(event, 200);
+    ok(ret == WAIT_TIMEOUT, "Expected timeout, got: %d\n", ret);
+
+    status = pNtResumeProcess(info.hProcess);
+    ok(status == STATUS_SUCCESS, "NtResumeProcess failed: %x\n", status);
+
+    ret = WaitForSingleObject(event, 200);
+    ok(ret == WAIT_TIMEOUT, "Expected timeout, got: %d\n", ret);
+
+    status = NtResumeThread(info.hThread, &count);
+    ok(status == STATUS_SUCCESS, "NtResumeProcess failed: %x\n", status);
+    ok(count == 0, "Expected count 0, got %d\n", count);
+
+    ret = WaitForSingleObject(event, 200);
+    ok(ret == WAIT_TIMEOUT, "Expected timeout, got: %d\n", ret);
+
+    ret = ContinueDebugEvent(ev.dwProcessId, ev.dwThreadId, DBG_CONTINUE);
+    ok(ret, "ContinueDebugEvent failed, last error %#x.\n", GetLastError());
+
+    ret = WaitForSingleObject(event, 200);
+    ok(ret == WAIT_OBJECT_0, "Event was not signaled: %d\n", ret);
+
+    TerminateProcess(info.hProcess, 0);
+
+    CloseHandle(info.hProcess);
+    CloseHandle(info.hThread);
+}
+
+static void dummy_process(char *event_name)
+{
+    HANDLE event = OpenEventA(EVENT_ALL_ACCESS, FALSE, event_name);
+
+    while (TRUE)
+    {
+        SetEvent(event);
+        OutputDebugStringA("test");
+        Sleep(5);
+    }
+}
+
+START_TEST(process)
+{
+    HMODULE mod;
+    char **argv;
+    int argc;
+
+    argc = winetest_get_mainargs(&argv);
+    if (argc >= 4 && strcmp(argv[2], "dummy_process") == 0)
+    {
+        dummy_process(argv[3]);
+        return;
+    }
+
+    mod = GetModuleHandleA("ntdll.dll");
+    if (!mod)
+    {
+        win_skip("Not running on NT, skipping tests\n");
+        return;
+    }
+
+    pNtResumeProcess  = (void*)GetProcAddress(mod, "NtResumeProcess");
+    pNtSuspendProcess = (void*)GetProcAddress(mod, "NtSuspendProcess");
+    pNtResumeThread   = (void*)GetProcAddress(mod, "NtResumeThread");
+    pNtSuspendThread  = (void*)GetProcAddress(mod, "NtSuspendThread");
+
+    test_NtSuspendProcess(argv[0]);
+}
index 0a8359a..67833b1 100755 (executable)
@@ -364,16 +364,22 @@ static void test_NtOpenKey(void)
 
     /* Zero accessmask */
     attr.Length = sizeof(attr);
+    key = (HANDLE)0xdeadbeef;
     status = pNtOpenKey(&key, 0, &attr);
 todo_wine
     ok(status == STATUS_ACCESS_DENIED, "Expected STATUS_ACCESS_DENIED, got: 0x%08x\n", status);
+todo_wine
+    ok(!key, "key = %p\n", key);
     if (status == STATUS_SUCCESS) NtClose(key);
 
     /* Calling without parent key requres full registry path. */
     pRtlCreateUnicodeStringFromAsciiz( &str, "Machine" );
     InitializeObjectAttributes(&attr, &str, 0, 0, 0);
+    key = (HANDLE)0xdeadbeef;
     status = pNtOpenKey(&key, KEY_READ, &attr);
     todo_wine ok(status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NtOpenKey Failed: 0x%08x\n", status);
+todo_wine
+    ok(!key, "key = %p\n", key);
     pRtlFreeUnicodeString( &str );
 
     /* Open is case sensitive unless OBJ_CASE_INSENSITIVE is specified. */
@@ -1070,8 +1076,10 @@ static void test_symlinks(void)
     /* try opening the target through the link */
 
     attr.ObjectName = &link_str;
+    key = (HANDLE)0xdeadbeef;
     status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
     ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtOpenKey wrong status 0x%08x\n", status );
+    ok( !key, "key = %p\n", key );
 
     attr.ObjectName = &target_str;
     status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
@@ -1135,6 +1143,40 @@ static void test_symlinks(void)
         "wrong len %u\n", len );
     pNtClose( key );
 
+    if (pNtOpenKeyEx)
+    {
+        /* REG_OPTION_OPEN_LINK flag doesn't matter */
+        status = pNtOpenKeyEx( &key, KEY_ALL_ACCESS, &attr, REG_OPTION_OPEN_LINK );
+        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 = pNtOpenKeyEx( &key, KEY_ALL_ACCESS, &attr, 0 );
+        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 );
+
+        attr.Attributes = 0;
+        status = pNtOpenKeyEx( &key, KEY_ALL_ACCESS, &attr, REG_OPTION_OPEN_LINK );
+        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 );
+    }
+
+    attr.Attributes = OBJ_OPENLINK;
     status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
     ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
     len = sizeof(buffer);
@@ -1144,6 +1186,36 @@ static void test_symlinks(void)
         "wrong len %u\n", len );
     pNtClose( key );
 
+    /* delete target and create by NtCreateKey on 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 );
+
+    attr.ObjectName = &link_str;
+    attr.Attributes = 0;
+    status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
+    ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtOpenKey wrong status 0x%08x\n", status );
+
+    status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
+    todo_wine ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
+    pNtClose( key );
+    if (status) /* can be removed once todo_wine above is fixed */
+    {
+        attr.ObjectName = &target_str;
+        attr.Attributes = OBJ_OPENLINK;
+        status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
+        ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
+        pNtClose( key );
+    }
+
+    attr.ObjectName = &target_str;
+    attr.Attributes = OBJ_OPENLINK;
+    status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
+    ok( status == STATUS_SUCCESS, "NtOpenKey wrong status 0x%08x\n", status );
+
     if (0)  /* crashes the Windows kernel on some Vista systems */
     {
         /* reopen the link from itself */
@@ -1207,8 +1279,10 @@ static void test_symlinks(void)
     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 );
+    key = (HKEY)0xdeadbeef;
+    status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, REG_OPTION_CREATE_LINK, NULL );
     ok( status == STATUS_OBJECT_NAME_COLLISION, "NtCreateKey failed: 0x%08x\n", status );
+    ok( !key, "key = %p\n", key );
 
     status = pNtDeleteKey( link );
     ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08x\n", status );
index 2679aff..5ba6b11 100755 (executable)
@@ -86,7 +86,6 @@ 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 IMAGE_BASE_RELOCATION *(WINAPI *pLdrProcessRelocationBlock)(void*,UINT,USHORT*,INT_PTR);
@@ -111,7 +110,10 @@ static NTSTATUS  (WINAPI *pRtlCompressBuffer)(USHORT, const UCHAR*, ULONG, PUCHA
 static BOOL      (WINAPI *pRtlIsCriticalSectionLocked)(CRITICAL_SECTION *);
 static BOOL      (WINAPI *pRtlIsCriticalSectionLockedByThread)(CRITICAL_SECTION *);
 static NTSTATUS  (WINAPI *pRtlInitializeCriticalSectionEx)(CRITICAL_SECTION *, ULONG, ULONG);
+static NTSTATUS  (WINAPI *pLdrEnumerateLoadedModules)(void *, void *, void *);
 static NTSTATUS  (WINAPI *pRtlQueryPackageIdentity)(HANDLE, WCHAR*, SIZE_T*, WCHAR*, SIZE_T*, BOOLEAN*);
+static NTSTATUS  (WINAPI *pLdrRegisterDllNotification)(ULONG, PLDR_DLL_NOTIFICATION_FUNCTION, void *, void **);
+static NTSTATUS  (WINAPI *pLdrUnregisterDllNotification)(void *);
 
 static HMODULE hkernel32 = 0;
 static BOOL      (WINAPI *pIsWow64Process)(HANDLE, PBOOL);
@@ -119,6 +121,9 @@ static BOOL      (WINAPI *pIsWow64Process)(HANDLE, PBOOL);
 
 #define LEN 16
 static const char* src_src = "This is a test!"; /* 16 bytes long, incl NUL */
+static WCHAR ws2_32dllW[] = {'w','s','2','_','3','2','.','d','l','l',0};
+static WCHAR wintrustdllW[] = {'w','i','n','t','r','u','s','t','.','d','l','l',0};
+static WCHAR crypt32dllW[] = {'c','r','y','p','t','3','2','.','d','l','l',0};
 static ULONG src_aligned_block[4];
 static ULONG dest_aligned_block[32];
 static const char *src = (const char*)src_aligned_block;
@@ -149,7 +154,6 @@ 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");
         pLdrProcessRelocationBlock  = (void *)GetProcAddress(hntdll, "LdrProcessRelocationBlock");
@@ -174,7 +178,10 @@ static void InitFunctionPtrs(void)
         pRtlIsCriticalSectionLocked = (void *)GetProcAddress(hntdll, "RtlIsCriticalSectionLocked");
         pRtlIsCriticalSectionLockedByThread = (void *)GetProcAddress(hntdll, "RtlIsCriticalSectionLockedByThread");
         pRtlInitializeCriticalSectionEx = (void *)GetProcAddress(hntdll, "RtlInitializeCriticalSectionEx");
+        pLdrEnumerateLoadedModules = (void *)GetProcAddress(hntdll, "LdrEnumerateLoadedModules");
         pRtlQueryPackageIdentity = (void *)GetProcAddress(hntdll, "RtlQueryPackageIdentity");
+        pLdrRegisterDllNotification = (void *)GetProcAddress(hntdll, "LdrRegisterDllNotification");
+        pLdrUnregisterDllNotification = (void *)GetProcAddress(hntdll, "LdrUnregisterDllNotification");
     }
     hkernel32 = LoadLibraryA("kernel32.dll");
     ok(hkernel32 != 0, "LoadLibrary failed\n");
@@ -382,8 +389,8 @@ static void test_RtlUlonglongByteSwap(void)
 
     result = pRtlUlonglongByteSwap( ((ULONGLONG)0x76543210 << 32) | 0x87654321 );
     ok( (((ULONGLONG)0x21436587 << 32) | 0x10325476) == result,
-       "RtlUlonglongByteSwap(0x7654321087654321) returns 0x%x%08x, expected 0x2143658710325476\n",
-       (DWORD)(result >> 32), (DWORD)result);
+       "RtlUlonglongByteSwap(0x7654321087654321) returns 0x%s, expected 0x2143658710325476\n",
+       wine_dbgstr_longlong(result));
 }
 
 
@@ -624,11 +631,11 @@ static void test_RtlUniform(void)
         seed_bak = seed;
         result = pRtlUniform(&seed);
         ok(result == expected,
-                "test: 0x%x%08x RtlUniform(&seed (seed == %x)) returns %x, expected %x\n",
-                (DWORD)(num >> 32), (DWORD)num, seed_bak, result, expected);
+                "test: 0x%s RtlUniform(&seed (seed == %x)) returns %x, expected %x\n",
+                wine_dbgstr_longlong(num), seed_bak, result, expected);
         ok(seed == expected,
-                "test: 0x%x%08x RtlUniform(&seed (seed == %x)) sets seed to %x, expected %x\n",
-                (DWORD)(num >> 32), (DWORD)num, seed_bak, result, expected);
+                "test: 0x%s RtlUniform(&seed (seed == %x)) sets seed to %x, expected %x\n",
+                wine_dbgstr_longlong(num), seed_bak, result, expected);
     } /* for */
 /*
  * Further investigation shows: In the different regions the highest bit
@@ -671,11 +678,11 @@ static void test_RtlUniform(void)
         seed_bak = seed;
         result = pRtlUniform(&seed);
         ok(result == expected,
-                "test: 0x%x%08x RtlUniform(&seed (seed == %x)) returns %x, expected %x\n",
-                (DWORD)(num >> 32), (DWORD)num, seed_bak, result, expected);
+                "test: 0x%s RtlUniform(&seed (seed == %x)) returns %x, expected %x\n",
+                wine_dbgstr_longlong(num), seed_bak, result, expected);
         ok(seed == expected,
-                "test: 0x%x%08x RtlUniform(&seed (seed == %x)) sets seed to %x, expected %x\n",
-                (DWORD)(num >> 32), (DWORD)num, seed_bak, result, expected);
+                "test: 0x%s RtlUniform(&seed (seed == %x)) sets seed to %x, expected %x\n",
+                wine_dbgstr_longlong(num), seed_bak, result, expected);
     } /* for */
 /*
  * More tests show that RtlUniform does not return 0x7ffffffd for seed values
@@ -919,10 +926,12 @@ static void test_RtlThreadErrorMode(void)
        mode, oldmode);
     ok(pRtlGetThreadErrorMode() == 0x70,
        "RtlGetThreadErrorMode returned 0x%x, expected 0x%x\n", mode, 0x70);
-    if (!is_wow64 && pNtCurrentTeb)
-        ok(pNtCurrentTeb()->HardErrorDisabled == 0x70,
+    if (!is_wow64)
+    {
+        ok(NtCurrentTeb()->HardErrorDisabled == 0x70,
            "The TEB contains 0x%x, expected 0x%x\n",
-           pNtCurrentTeb()->HardErrorDisabled, 0x70);
+           NtCurrentTeb()->HardErrorDisabled, 0x70);
+    }
 
     status = pRtlSetThreadErrorMode(0, &mode);
     ok(status == STATUS_SUCCESS ||
@@ -933,10 +942,12 @@ static void test_RtlThreadErrorMode(void)
        mode, 0x70);
     ok(pRtlGetThreadErrorMode() == 0,
        "RtlGetThreadErrorMode returned 0x%x, expected 0x%x\n", mode, 0);
-    if (!is_wow64 && pNtCurrentTeb)
-        ok(pNtCurrentTeb()->HardErrorDisabled == 0,
+    if (!is_wow64)
+    {
+        ok(NtCurrentTeb()->HardErrorDisabled == 0,
            "The TEB contains 0x%x, expected 0x%x\n",
-           pNtCurrentTeb()->HardErrorDisabled, 0);
+           NtCurrentTeb()->HardErrorDisabled, 0);
+    }
 
     for (mode = 1; mode; mode <<= 1)
     {
@@ -3158,6 +3169,119 @@ static void test_RtlInitializeCriticalSectionEx(void)
     RtlDeleteCriticalSection((PRTL_CRITICAL_SECTION)&cs);
 }
 
+static void test_RtlLeaveCriticalSection(void)
+{
+    RTL_CRITICAL_SECTION cs;
+    NTSTATUS status;
+
+    if (!pRtlInitializeCriticalSectionEx)
+        return; /* Skip winxp */
+
+    status = RtlInitializeCriticalSection(&cs);
+    ok(!status, "RtlInitializeCriticalSection failed: %x\n", status);
+
+    status = RtlEnterCriticalSection(&cs);
+    ok(!status, "RtlEnterCriticalSection failed: %x\n", status);
+    todo_wine
+    ok(cs.LockCount == -2, "expected LockCount == -2, got %d\n", cs.LockCount);
+    ok(cs.RecursionCount == 1, "expected RecursionCount == 1, got %d\n", cs.RecursionCount);
+    ok(cs.OwningThread == ULongToHandle(GetCurrentThreadId()), "unexpected OwningThread\n");
+
+    status = RtlLeaveCriticalSection(&cs);
+    ok(!status, "RtlLeaveCriticalSection failed: %x\n", status);
+    ok(cs.LockCount == -1, "expected LockCount == -1, got %d\n", cs.LockCount);
+    ok(cs.RecursionCount == 0, "expected RecursionCount == 0, got %d\n", cs.RecursionCount);
+    ok(!cs.OwningThread, "unexpected OwningThread %p\n", cs.OwningThread);
+
+    /*
+     * Trying to leave a section that wasn't acquired modifies RecursionCount to an invalid value,
+     * but doesn't modify LockCount so that an attempt to enter the section later will work.
+     */
+    status = RtlLeaveCriticalSection(&cs);
+    ok(!status, "RtlLeaveCriticalSection failed: %x\n", status);
+    ok(cs.LockCount == -1, "expected LockCount == -1, got %d\n", cs.LockCount);
+    ok(cs.RecursionCount == -1, "expected RecursionCount == -1, got %d\n", cs.RecursionCount);
+    ok(!cs.OwningThread, "unexpected OwningThread %p\n", cs.OwningThread);
+
+    /* and again */
+    status = RtlLeaveCriticalSection(&cs);
+    ok(!status, "RtlLeaveCriticalSection failed: %x\n", status);
+    ok(cs.LockCount == -1, "expected LockCount == -1, got %d\n", cs.LockCount);
+    ok(cs.RecursionCount == -2, "expected RecursionCount == -2, got %d\n", cs.RecursionCount);
+    ok(!cs.OwningThread, "unexpected OwningThread %p\n", cs.OwningThread);
+
+    /* entering section fixes RecursionCount */
+    status = RtlEnterCriticalSection(&cs);
+    ok(!status, "RtlEnterCriticalSection failed: %x\n", status);
+    todo_wine
+    ok(cs.LockCount == -2, "expected LockCount == -2, got %d\n", cs.LockCount);
+    ok(cs.RecursionCount == 1, "expected RecursionCount == 1, got %d\n", cs.RecursionCount);
+    ok(cs.OwningThread == ULongToHandle(GetCurrentThreadId()), "unexpected OwningThread\n");
+
+    status = RtlLeaveCriticalSection(&cs);
+    ok(!status, "RtlLeaveCriticalSection failed: %x\n", status);
+    ok(cs.LockCount == -1, "expected LockCount == -1, got %d\n", cs.LockCount);
+    ok(cs.RecursionCount == 0, "expected RecursionCount == 0, got %d\n", cs.RecursionCount);
+    ok(!cs.OwningThread, "unexpected OwningThread %p\n", cs.OwningThread);
+
+    status = RtlDeleteCriticalSection(&cs);
+    ok(!status, "RtlDeleteCriticalSection failed: %x\n", status);
+}
+
+struct ldr_enum_context
+{
+    BOOL abort;
+    BOOL found;
+    int  count;
+};
+
+static void WINAPI ldr_enum_callback(LDR_MODULE *module, void *context, BOOLEAN *stop)
+{
+    static const WCHAR ntdllW[] = {'n','t','d','l','l','.','d','l','l',0};
+    struct ldr_enum_context *ctx = context;
+
+    if (!lstrcmpiW(module->BaseDllName.Buffer, ntdllW))
+        ctx->found = TRUE;
+
+    ctx->count++;
+    *stop = ctx->abort;
+}
+
+static void test_LdrEnumerateLoadedModules(void)
+{
+    struct ldr_enum_context ctx;
+    NTSTATUS status;
+
+    if (!pLdrEnumerateLoadedModules)
+    {
+        win_skip("LdrEnumerateLoadedModules not available\n");
+        return;
+    }
+
+    ctx.abort = FALSE;
+    ctx.found = FALSE;
+    ctx.count = 0;
+    status = pLdrEnumerateLoadedModules(NULL, ldr_enum_callback, &ctx);
+    ok(status == STATUS_SUCCESS, "LdrEnumerateLoadedModules failed with %08x\n", status);
+    ok(ctx.count > 1, "Expected more than one module, got %d\n", ctx.count);
+    ok(ctx.found, "Could not find ntdll in list of modules\n");
+
+    ctx.abort = TRUE;
+    ctx.count = 0;
+    status = pLdrEnumerateLoadedModules(NULL, ldr_enum_callback, &ctx);
+    ok(status == STATUS_SUCCESS, "LdrEnumerateLoadedModules failed with %08x\n", status);
+    ok(ctx.count == 1, "Expected exactly one module, got %d\n", ctx.count);
+
+    status = pLdrEnumerateLoadedModules((void *)0x1, ldr_enum_callback, (void *)0xdeadbeef);
+    ok(status == STATUS_INVALID_PARAMETER, "expected STATUS_INVALID_PARAMETER, got 0x%08x\n", status);
+
+    status = pLdrEnumerateLoadedModules((void *)0xdeadbeef, ldr_enum_callback, (void *)0xdeadbeef);
+    ok(status == STATUS_INVALID_PARAMETER, "expected STATUS_INVALID_PARAMETER, got 0x%08x\n", status);
+
+    status = pLdrEnumerateLoadedModules(NULL, NULL, (void *)0xdeadbeef);
+    ok(status == STATUS_INVALID_PARAMETER, "expected STATUS_INVALID_PARAMETER, got 0x%08x\n", status);
+}
+
 static void test_RtlQueryPackageIdentity(void)
 {
     const WCHAR programW[] = {'M','i','c','r','o','s','o','f','t','.','W','i','n','d','o','w','s','.',
@@ -3228,6 +3352,248 @@ done:
     CoUninitialize();
 }
 
+static DWORD (CALLBACK *orig_entry)(HMODULE,DWORD,LPVOID);
+static DWORD *dll_main_data;
+
+static inline void *get_rva( HMODULE module, DWORD va )
+{
+    return (void *)((char *)module + va);
+}
+
+static void CALLBACK ldr_notify_callback1(ULONG reason, LDR_DLL_NOTIFICATION_DATA *data, void *context)
+{
+    const IMAGE_IMPORT_DESCRIPTOR *imports;
+    const IMAGE_THUNK_DATA *import_list;
+    IMAGE_THUNK_DATA *thunk_list;
+    DWORD *calls = context;
+    LIST_ENTRY *mark;
+    LDR_MODULE *mod;
+    ULONG size;
+    int i, j;
+
+    *calls <<= 4;
+    *calls |= reason;
+
+    ok(data->Loaded.Flags == 0, "Expected flags 0, got %x\n", data->Loaded.Flags);
+    ok(!lstrcmpiW(data->Loaded.BaseDllName->Buffer, ws2_32dllW), "Expected ws2_32.dll, got %s\n",
+       wine_dbgstr_w(data->Loaded.BaseDllName->Buffer));
+    ok(!!data->Loaded.DllBase, "Expected non zero base address\n");
+    ok(data->Loaded.SizeOfImage, "Expected non zero image size\n");
+
+    /* expect module to be last module listed in LdrData load order list */
+    mark = &NtCurrentTeb()->Peb->LdrData->InMemoryOrderModuleList;
+    mod = CONTAINING_RECORD(mark->Blink, LDR_MODULE, InMemoryOrderModuleList);
+    ok(mod->BaseAddress == data->Loaded.DllBase, "Expected base address %p, got %p\n",
+       data->Loaded.DllBase, mod->BaseAddress);
+    ok(!lstrcmpiW(mod->BaseDllName.Buffer, ws2_32dllW), "Expected ws2_32.dll, got %s\n",
+       wine_dbgstr_w(mod->BaseDllName.Buffer));
+
+    /* show that imports have already been resolved */
+    imports = RtlImageDirectoryEntryToData(data->Loaded.DllBase, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &size);
+    ok(!!imports, "Expected dll to have imports\n");
+
+    for (i = 0; imports[i].Name; i++)
+    {
+        thunk_list = get_rva(data->Loaded.DllBase, (DWORD)imports[i].FirstThunk);
+        if (imports[i].OriginalFirstThunk)
+            import_list = get_rva(data->Loaded.DllBase, (DWORD)imports[i].OriginalFirstThunk);
+        else
+            import_list = thunk_list;
+
+        for (j = 0; import_list[j].u1.Ordinal; j++)
+        {
+            ok(thunk_list[j].u1.AddressOfData > data->Loaded.SizeOfImage,
+               "Import has not been resolved: %p\n", (void*)thunk_list[j].u1.Function);
+        }
+    }
+}
+
+static void CALLBACK ldr_notify_callback2(ULONG reason, LDR_DLL_NOTIFICATION_DATA *data, void *context)
+{
+    DWORD *calls = context;
+    *calls <<= 4;
+    *calls |= reason + 2;
+}
+
+static BOOL WINAPI fake_dll_main(HINSTANCE instance, DWORD reason, void* reserved)
+{
+    if (reason == DLL_PROCESS_ATTACH)
+    {
+        *dll_main_data <<= 4;
+        *dll_main_data |= 3;
+    }
+    else if (reason == DLL_PROCESS_DETACH)
+    {
+        *dll_main_data <<= 4;
+        *dll_main_data |= 4;
+    }
+    return orig_entry(instance, reason, reserved);
+}
+
+static void CALLBACK ldr_notify_callback_dll_main(ULONG reason, LDR_DLL_NOTIFICATION_DATA *data, void *context)
+{
+    DWORD *calls = context;
+    LIST_ENTRY *mark;
+    LDR_MODULE *mod;
+
+    *calls <<= 4;
+    *calls |= reason;
+
+    if (reason != LDR_DLL_NOTIFICATION_REASON_LOADED)
+        return;
+
+    mark = &NtCurrentTeb()->Peb->LdrData->InMemoryOrderModuleList;
+    mod = CONTAINING_RECORD(mark->Blink, LDR_MODULE, InMemoryOrderModuleList);
+    ok(mod->BaseAddress == data->Loaded.DllBase, "Expected base address %p, got %p\n",
+       data->Loaded.DllBase, mod->BaseAddress);
+    if (mod->BaseAddress != data->Loaded.DllBase)
+       return;
+
+    orig_entry = mod->EntryPoint;
+    mod->EntryPoint = fake_dll_main;
+    dll_main_data = calls;
+}
+
+static BOOL WINAPI fake_dll_main_fail(HINSTANCE instance, DWORD reason, void* reserved)
+{
+    if (reason == DLL_PROCESS_ATTACH)
+    {
+        *dll_main_data <<= 4;
+        *dll_main_data |= 3;
+    }
+    else if (reason == DLL_PROCESS_DETACH)
+    {
+        *dll_main_data <<= 4;
+        *dll_main_data |= 4;
+    }
+    return FALSE;
+}
+
+static void CALLBACK ldr_notify_callback_fail(ULONG reason, LDR_DLL_NOTIFICATION_DATA *data, void *context)
+{
+    DWORD *calls = context;
+    LIST_ENTRY *mark;
+    LDR_MODULE *mod;
+
+    *calls <<= 4;
+    *calls |= reason;
+
+    if (reason != LDR_DLL_NOTIFICATION_REASON_LOADED)
+        return;
+
+    mark = &NtCurrentTeb()->Peb->LdrData->InMemoryOrderModuleList;
+    mod = CONTAINING_RECORD(mark->Blink, LDR_MODULE, InMemoryOrderModuleList);
+    ok(mod->BaseAddress == data->Loaded.DllBase, "Expected base address %p, got %p\n",
+       data->Loaded.DllBase, mod->BaseAddress);
+    if (mod->BaseAddress != data->Loaded.DllBase)
+       return;
+
+    orig_entry = mod->EntryPoint;
+    mod->EntryPoint = fake_dll_main_fail;
+    dll_main_data = calls;
+}
+
+static void CALLBACK ldr_notify_callback_imports(ULONG reason, LDR_DLL_NOTIFICATION_DATA *data, void *context)
+{
+    DWORD *calls = context;
+
+    if (reason != LDR_DLL_NOTIFICATION_REASON_LOADED)
+        return;
+
+    if (!lstrcmpiW(data->Loaded.BaseDllName->Buffer, crypt32dllW))
+    {
+        *calls <<= 4;
+        *calls |= 1;
+    }
+
+    if (!lstrcmpiW(data->Loaded.BaseDllName->Buffer, wintrustdllW))
+    {
+        *calls <<= 4;
+        *calls |= 2;
+    }
+}
+
+static void test_LdrRegisterDllNotification(void)
+{
+    void *cookie, *cookie2;
+    NTSTATUS status;
+    HMODULE mod;
+    DWORD calls;
+
+    if (!pLdrRegisterDllNotification || !pLdrUnregisterDllNotification)
+    {
+        win_skip("Ldr(Un)RegisterDllNotification not available\n");
+        return;
+    }
+
+    /* generic test */
+    status = pLdrRegisterDllNotification(0, ldr_notify_callback1, &calls, &cookie);
+    ok(!status, "Expected STATUS_SUCCESS, got %08x\n", status);
+
+    calls = 0;
+    mod = LoadLibraryW(ws2_32dllW);
+    ok(!!mod, "Failed to load library: %d\n", GetLastError());
+    ok(calls == LDR_DLL_NOTIFICATION_REASON_LOADED, "Expected LDR_DLL_NOTIFICATION_REASON_LOADED, got %x\n", calls);
+
+    calls = 0;
+    FreeLibrary(mod);
+    ok(calls == LDR_DLL_NOTIFICATION_REASON_UNLOADED, "Expected LDR_DLL_NOTIFICATION_REASON_UNLOADED, got %x\n", calls);
+
+    /* test order of callbacks */
+    status = pLdrRegisterDllNotification(0, ldr_notify_callback2, &calls, &cookie2);
+    ok(!status, "Expected STATUS_SUCCESS, got %08x\n", status);
+
+    calls = 0;
+    mod = LoadLibraryW(ws2_32dllW);
+    ok(!!mod, "Failed to load library: %d\n", GetLastError());
+    ok(calls == 0x13, "Expected order 0x13, got %x\n", calls);
+
+    calls = 0;
+    FreeLibrary(mod);
+    ok(calls == 0x24, "Expected order 0x24, got %x\n", calls);
+
+    pLdrUnregisterDllNotification(cookie2);
+    pLdrUnregisterDllNotification(cookie);
+
+    /* test dll main order */
+    status = pLdrRegisterDllNotification(0, ldr_notify_callback_dll_main, &calls, &cookie);
+    ok(!status, "Expected STATUS_SUCCESS, got %08x\n", status);
+
+    calls = 0;
+    mod = LoadLibraryW(ws2_32dllW);
+    ok(!!mod, "Failed to load library: %d\n", GetLastError());
+    ok(calls == 0x13, "Expected order 0x13, got %x\n", calls);
+
+    calls = 0;
+    FreeLibrary(mod);
+    ok(calls == 0x42, "Expected order 0x42, got %x\n", calls);
+
+    pLdrUnregisterDllNotification(cookie);
+
+    /* test dll main order */
+    status = pLdrRegisterDllNotification(0, ldr_notify_callback_fail, &calls, &cookie);
+    ok(!status, "Expected STATUS_SUCCESS, got %08x\n", status);
+
+    calls = 0;
+    mod = LoadLibraryW(ws2_32dllW);
+    ok(!mod, "Expected library to fail loading\n");
+    ok(calls == 0x1342, "Expected order 0x1342, got %x\n", calls);
+
+    pLdrUnregisterDllNotification(cookie);
+
+    /* test dll with dependencies */
+    status = pLdrRegisterDllNotification(0, ldr_notify_callback_imports, &calls, &cookie);
+    ok(!status, "Expected STATUS_SUCCESS, got %08x\n", status);
+
+    calls = 0;
+    mod = LoadLibraryW(wintrustdllW);
+    ok(!!mod, "Failed to load library: %d\n", GetLastError());
+    ok(calls == 0x12, "Expected order 0x12, got %x\n", calls);
+
+    FreeLibrary(mod);
+    pLdrUnregisterDllNotification(cookie);
+}
+
 START_TEST(rtl)
 {
     InitFunctionPtrs();
@@ -3264,5 +3630,8 @@ START_TEST(rtl)
     test_RtlDecompressBuffer();
     test_RtlIsCriticalSectionLocked();
     test_RtlInitializeCriticalSectionEx();
+    test_RtlLeaveCriticalSection();
+    test_LdrEnumerateLoadedModules();
     test_RtlQueryPackageIdentity();
+    test_LdrRegisterDllNotification();
 }
index 4c1559b..9552318 100755 (executable)
@@ -425,15 +425,15 @@ static void test_RtlFindMostSignificantBit(void)
     ulLong <<= i;
 
     cPos = pRtlFindMostSignificantBit(ulLong);
-    ok (cPos == i, "didn't find MSB 0x%x%08x %d %d\n",
-        (DWORD)(ulLong >> 32), (DWORD)ulLong, i, cPos);
+    ok (cPos == i, "didn't find MSB 0x%s %d %d\n",
+        wine_dbgstr_longlong(ulLong ), i, cPos);
 
     /* Set all bits lower than bit i */
     ulLong = ((ulLong - 1) << 1) | 1;
 
     cPos = pRtlFindMostSignificantBit(ulLong);
-    ok (cPos == i, "didn't find MSB 0x%x%08x %d %d\n",
-        (DWORD)(ulLong >> 32), (DWORD)ulLong, i, cPos);
+    ok (cPos == i, "didn't find MSB 0x%s %d %d\n",
+        wine_dbgstr_longlong(ulLong ), i, cPos);
   }
   cPos = pRtlFindMostSignificantBit(0);
   ok (cPos == -1, "found bit when not set\n");
@@ -453,14 +453,14 @@ static void test_RtlFindLeastSignificantBit(void)
     ulLong = (ULONGLONG)1 << i;
 
     cPos = pRtlFindLeastSignificantBit(ulLong);
-    ok (cPos == i, "didn't find LSB 0x%x%08x %d %d\n",
-        (DWORD)(ulLong >> 32), (DWORD)ulLong, i, cPos);
+    ok (cPos == i, "didn't find LSB 0x%s %d %d\n",
+        wine_dbgstr_longlong(ulLong ), i, cPos);
 
     ulLong = ~((ULONGLONG)0) << i;
 
     cPos = pRtlFindLeastSignificantBit(ulLong);
-    ok (cPos == i, "didn't find LSB 0x%x%08x %d %d\n",
-        (DWORD)(ulLong >> 32), (DWORD)ulLong, i, cPos);
+    ok (cPos == i, "didn't find LSB 0x%s %d %d\n",
+        wine_dbgstr_longlong(ulLong ), i, cPos);
   }
   cPos = pRtlFindLeastSignificantBit(0);
   ok (cPos == -1, "found bit when not set\n");
index adaa7c3..9bbaddd 100755 (executable)
@@ -600,22 +600,21 @@ static void one_i64toa_test(int test_num, const ulonglong2str_t *ulonglong2str)
     dest_str[LARGE_STRI_BUFFER_LENGTH] = '\0';
     result = p_i64toa(ulonglong2str->value, dest_str, ulonglong2str->base);
     ok(result == dest_str,
-       "(test %d): _i64toa(%08x%08x, [out], %d) has result %p, expected: %p\n",
-       test_num, (DWORD)(ulonglong2str->value >> 32), (DWORD)ulonglong2str->value,
-       ulonglong2str->base, result, dest_str);
+       "(test %d): _i64toa(%s, [out], %d) has result %p, expected: %p\n",
+       test_num, wine_dbgstr_longlong(ulonglong2str->value), ulonglong2str->base, result, dest_str);
     if (ulonglong2str->mask & 0x04) {
        if (memcmp(dest_str, ulonglong2str->Buffer, LARGE_STRI_BUFFER_LENGTH) != 0) {
            if (memcmp(dest_str, ulonglong2str[1].Buffer, LARGE_STRI_BUFFER_LENGTH) != 0) {
                ok(memcmp(dest_str, ulonglong2str->Buffer, LARGE_STRI_BUFFER_LENGTH) == 0,
-                  "(test %d): _i64toa(%08x%08x, [out], %d) assigns string \"%s\", expected: \"%s\"\n",
-                  test_num, (DWORD)(ulonglong2str->value >> 32), (DWORD)ulonglong2str->value,
+                  "(test %d): _i64toa(%s, [out], %d) assigns string \"%s\", expected: \"%s\"\n",
+                  test_num, wine_dbgstr_longlong(ulonglong2str->value),
                    ulonglong2str->base, dest_str, ulonglong2str->Buffer);
            } /* if */
        } /* if */
     } else {
        ok(memcmp(dest_str, ulonglong2str->Buffer, LARGE_STRI_BUFFER_LENGTH) == 0,
-          "(test %d): _i64toa(%08x%08x, [out], %d) assigns string \"%s\", expected: \"%s\"\n",
-          test_num, (DWORD)(ulonglong2str->value >> 32), (DWORD)ulonglong2str->value,
+          "(test %d): _i64toa(%s, [out], %d) assigns string \"%s\", expected: \"%s\"\n",
+          test_num, wine_dbgstr_longlong(ulonglong2str->value),
            ulonglong2str->base, dest_str, ulonglong2str->Buffer);
     } /* if */
 }
@@ -630,12 +629,11 @@ static void one_ui64toa_test(int test_num, const ulonglong2str_t *ulonglong2str)
     dest_str[LARGE_STRI_BUFFER_LENGTH] = '\0';
     result = p_ui64toa(ulonglong2str->value, dest_str, ulonglong2str->base);
     ok(result == dest_str,
-       "(test %d): _ui64toa(%08x%08x, [out], %d) has result %p, expected: %p\n",
-       test_num, (DWORD)(ulonglong2str->value >> 32), (DWORD)ulonglong2str->value,
-       ulonglong2str->base, result, dest_str);
+       "(test %d): _ui64toa(%s, [out], %d) has result %p, expected: %p\n",
+       test_num, wine_dbgstr_longlong(ulonglong2str->value), ulonglong2str->base, result, dest_str);
     ok(memcmp(dest_str, ulonglong2str->Buffer, LARGE_STRI_BUFFER_LENGTH) == 0,
-       "(test %d): _ui64toa(%08x%08x, [out], %d) assigns string \"%s\", expected: \"%s\"\n",
-       test_num, (DWORD)(ulonglong2str->value >> 32), (DWORD)ulonglong2str->value,
+       "(test %d): _ui64toa(%s, [out], %d) assigns string \"%s\", expected: \"%s\"\n",
+       test_num, wine_dbgstr_longlong(ulonglong2str->value),
        ulonglong2str->base, dest_str, ulonglong2str->Buffer);
 }
 
@@ -682,9 +680,8 @@ static void one_i64tow_test(int test_num, const ulonglong2str_t *ulonglong2str)
     result = p_i64tow(ulonglong2str->value, dest_wstr, ulonglong2str->base);
     pRtlUnicodeStringToAnsiString(&ansi_str, &unicode_string, 1);
     ok(result == dest_wstr,
-       "(test %d): _i64tow(0x%x%08x, [out], %d) has result %p, expected: %p\n",
-       test_num, (DWORD)(ulonglong2str->value >> 32), (DWORD)ulonglong2str->value,
-       ulonglong2str->base, result, dest_wstr);
+       "(test %d): _i64tow(0x%s, [out], %d) has result %p, expected: %p\n",
+       test_num, wine_dbgstr_longlong(ulonglong2str->value), ulonglong2str->base, result, dest_wstr);
     if (ulonglong2str->mask & 0x04) {
        if (memcmp(dest_wstr, expected_wstr, LARGE_STRI_BUFFER_LENGTH * sizeof(WCHAR)) != 0) {
            for (pos = 0; pos < LARGE_STRI_BUFFER_LENGTH; pos++) {
@@ -693,15 +690,15 @@ static void one_i64tow_test(int test_num, const ulonglong2str_t *ulonglong2str)
            expected_wstr[LARGE_STRI_BUFFER_LENGTH] = '\0';
            if (memcmp(dest_wstr, expected_wstr, LARGE_STRI_BUFFER_LENGTH * sizeof(WCHAR)) != 0) {
                ok(memcmp(dest_wstr, expected_wstr, LARGE_STRI_BUFFER_LENGTH * sizeof(WCHAR)) == 0,
-                   "(test %d): _i64tow(0x%x%08x, [out], %d) assigns string \"%s\", expected: \"%s\"\n",
-                  test_num, (DWORD)(ulonglong2str->value >> 32), (DWORD)ulonglong2str->value,
+                   "(test %d): _i64tow(0x%s, [out], %d) assigns string \"%s\", expected: \"%s\"\n",
+                  test_num, wine_dbgstr_longlong(ulonglong2str->value),
                   ulonglong2str->base, ansi_str.Buffer, ulonglong2str->Buffer);
            } /* if */
        } /* if */
     } else {
        ok(memcmp(dest_wstr, expected_wstr, LARGE_STRI_BUFFER_LENGTH * sizeof(WCHAR)) == 0,
-           "(test %d): _i64tow(0x%x%08x, [out], %d) assigns string \"%s\", expected: \"%s\"\n",
-          test_num, (DWORD)(ulonglong2str->value >> 32), (DWORD)ulonglong2str->value,
+           "(test %d): _i64tow(0x%s, [out], %d) assigns string \"%s\", expected: \"%s\"\n",
+          test_num, wine_dbgstr_longlong(ulonglong2str->value),
           ulonglong2str->base, ansi_str.Buffer, ulonglong2str->Buffer);
     } /* if */
     pRtlFreeAnsiString(&ansi_str);
@@ -733,12 +730,12 @@ static void one_ui64tow_test(int test_num, const ulonglong2str_t *ulonglong2str)
     result = p_ui64tow(ulonglong2str->value, dest_wstr, ulonglong2str->base);
     pRtlUnicodeStringToAnsiString(&ansi_str, &unicode_string, 1);
     ok(result == dest_wstr,
-       "(test %d): _ui64tow(0x%x%08x, [out], %d) has result %p, expected: %p\n",
-       test_num, (DWORD)(ulonglong2str->value >> 32), (DWORD)ulonglong2str->value,
+       "(test %d): _ui64tow(0x%s, [out], %d) has result %p, expected: %p\n",
+       test_num, wine_dbgstr_longlong(ulonglong2str->value),
        ulonglong2str->base, result, dest_wstr);
     ok(memcmp(dest_wstr, expected_wstr, LARGE_STRI_BUFFER_LENGTH * sizeof(WCHAR)) == 0,
-       "(test %d): _ui64tow(0x%x%08x, [out], %d) assigns string \"%s\", expected: \"%s\"\n",
-       test_num, (DWORD)(ulonglong2str->value >> 32), (DWORD)ulonglong2str->value,
+       "(test %d): _ui64tow(0x%s, [out], %d) assigns string \"%s\", expected: \"%s\"\n",
+       test_num, wine_dbgstr_longlong(ulonglong2str->value),
        ulonglong2str->base, ansi_str.Buffer, ulonglong2str->Buffer);
     pRtlFreeAnsiString(&ansi_str);
 }
@@ -764,8 +761,8 @@ static void test_ulonglongtow(void)
         /* Crashes on XP and W2K3 */
         result = p_i64tow(ulonglong2str[0].value, NULL, 10);
         ok(result == NULL,
-           "(test d): _i64tow(0x%x%08x, NULL, 10) has result %p, expected: NULL\n",
-           (DWORD)(ulonglong2str[0].value >> 32), (DWORD)ulonglong2str[0].value, result);
+           "(test d): _i64tow(0x%s, NULL, 10) has result %p, expected: NULL\n",
+           wine_dbgstr_longlong(ulonglong2str[0].value), result);
     }
 
     if (p_ui64tow) {
@@ -773,8 +770,8 @@ static void test_ulonglongtow(void)
             /* Crashes on XP and W2K3 */
            result = p_ui64tow(ulonglong2str[0].value, NULL, 10);
            ok(result == NULL,
-               "(test e): _ui64tow(0x%x%08x, NULL, 10) has result %p, expected: NULL\n",
-              (DWORD)(ulonglong2str[0].value >> 32), (DWORD)ulonglong2str[0].value, result);
+               "(test e): _ui64tow(0x%s, NULL, 10) has result %p, expected: NULL\n",
+              wine_dbgstr_longlong(ulonglong2str[0].value), result);
         }
     } /* if */
 }
@@ -1085,14 +1082,14 @@ static void test_atoi64(void)
             ok(result == str2longlong[test_num].value ||
                (result == ((str2longlong[test_num].overflow == -1) ?
                 ULL(0x80000000,0x00000000) : ULL(0x7fffffff,0xffffffff))),
-               "(test %d): call failed: _atoi64(\"%s\") has result 0x%x%08x, expected: 0x%x%08x\n",
-               test_num, str2longlong[test_num].str, (DWORD)(result >> 32), (DWORD)result,
-               (DWORD)(str2longlong[test_num].value >> 32), (DWORD)str2longlong[test_num].value);
+               "(test %d): call failed: _atoi64(\"%s\") has result 0x%s, expected: 0x%s\n",
+               test_num, str2longlong[test_num].str, wine_dbgstr_longlong(result),
+               wine_dbgstr_longlong(str2longlong[test_num].value));
         else
             ok(result == str2longlong[test_num].value,
-               "(test %d): call failed: _atoi64(\"%s\") has result 0x%x%08x, expected: 0x%x%08x\n",
-               test_num, str2longlong[test_num].str, (DWORD)(result >> 32), (DWORD)result,
-               (DWORD)(str2longlong[test_num].value >> 32), (DWORD)str2longlong[test_num].value);
+               "(test %d): call failed: _atoi64(\"%s\") has result 0x%s, expected: 0x%s\n",
+               test_num, str2longlong[test_num].str, wine_dbgstr_longlong(result),
+               wine_dbgstr_longlong(str2longlong[test_num].value));
     }
 }
 
@@ -1110,14 +1107,14 @@ static void test_wtoi64(void)
             ok(result == str2longlong[test_num].value ||
                (result == ((str2longlong[test_num].overflow == -1) ?
                 ULL(0x80000000,0x00000000) : ULL(0x7fffffff,0xffffffff))),
-               "(test %d): call failed: _atoi64(\"%s\") has result 0x%x%08x, expected: 0x%x%08x\n",
-               test_num, str2longlong[test_num].str, (DWORD)(result >> 32), (DWORD)result,
-               (DWORD)(str2longlong[test_num].value >> 32), (DWORD)str2longlong[test_num].value);
+               "(test %d): call failed: _atoi64(\"%s\") has result 0x%s, expected: 0x%s\n",
+               test_num, str2longlong[test_num].str, wine_dbgstr_longlong(result),
+               wine_dbgstr_longlong(str2longlong[test_num].value));
         else
             ok(result == str2longlong[test_num].value,
-               "(test %d): call failed: _atoi64(\"%s\") has result 0x%x%08x, expected: 0x%x%08x\n",
-               test_num, str2longlong[test_num].str, (DWORD)(result >> 32), (DWORD)result,
-               (DWORD)(str2longlong[test_num].value >> 32), (DWORD)str2longlong[test_num].value);
+               "(test %d): call failed: _atoi64(\"%s\") has result 0x%s, expected: 0x%s\n",
+               test_num, str2longlong[test_num].str, wine_dbgstr_longlong(result),
+               wine_dbgstr_longlong(str2longlong[test_num].value));
        pRtlFreeUnicodeString(&uni);
     }
 }
index ae9be8d..989040c 100644 (file)
@@ -17,6 +17,7 @@ extern void func_om(void);
 extern void func_path(void);
 extern void func_pipe(void);
 extern void func_port(void);
+extern void func_process(void);
 extern void func_reg(void);
 extern void func_rtl(void);
 extern void func_rtlbitmap(void);
@@ -42,6 +43,7 @@ const struct test winetest_testlist[] =
     { "path", func_path },
     { "pipe", func_pipe },
     { "port", func_port },
+    { "process", func_process },
     { "reg", func_reg },
     { "rtl", func_rtl },
     { "rtlbitmap", func_rtlbitmap },
diff --git a/modules/rostests/winetests/ntdll/threadpool.c b/modules/rostests/winetests/ntdll/threadpool.c
new file mode 100644 (file)
index 0000000..af0b667
--- /dev/null
@@ -0,0 +1,1919 @@
+/*
+ * Unit test suite for thread pool functions
+ *
+ * Copyright 2015-2016 Sebastian Lackner
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "ntdll_test.h"
+
+static HMODULE hntdll = 0;
+static NTSTATUS (WINAPI *pTpAllocCleanupGroup)(TP_CLEANUP_GROUP **);
+static NTSTATUS (WINAPI *pTpAllocPool)(TP_POOL **,PVOID);
+static NTSTATUS (WINAPI *pTpAllocTimer)(TP_TIMER **,PTP_TIMER_CALLBACK,PVOID,TP_CALLBACK_ENVIRON *);
+static NTSTATUS (WINAPI *pTpAllocWait)(TP_WAIT **,PTP_WAIT_CALLBACK,PVOID,TP_CALLBACK_ENVIRON *);
+static NTSTATUS (WINAPI *pTpAllocWork)(TP_WORK **,PTP_WORK_CALLBACK,PVOID,TP_CALLBACK_ENVIRON *);
+static NTSTATUS (WINAPI *pTpCallbackMayRunLong)(TP_CALLBACK_INSTANCE *);
+static VOID     (WINAPI *pTpCallbackReleaseSemaphoreOnCompletion)(TP_CALLBACK_INSTANCE *,HANDLE,DWORD);
+static VOID     (WINAPI *pTpDisassociateCallback)(TP_CALLBACK_INSTANCE *);
+static BOOL     (WINAPI *pTpIsTimerSet)(TP_TIMER *);
+static VOID     (WINAPI *pTpReleaseWait)(TP_WAIT *);
+static VOID     (WINAPI *pTpPostWork)(TP_WORK *);
+static VOID     (WINAPI *pTpReleaseCleanupGroup)(TP_CLEANUP_GROUP *);
+static VOID     (WINAPI *pTpReleaseCleanupGroupMembers)(TP_CLEANUP_GROUP *,BOOL,PVOID);
+static VOID     (WINAPI *pTpReleasePool)(TP_POOL *);
+static VOID     (WINAPI *pTpReleaseTimer)(TP_TIMER *);
+static VOID     (WINAPI *pTpReleaseWork)(TP_WORK *);
+static VOID     (WINAPI *pTpSetPoolMaxThreads)(TP_POOL *,DWORD);
+static VOID     (WINAPI *pTpSetTimer)(TP_TIMER *,LARGE_INTEGER *,LONG,LONG);
+static VOID     (WINAPI *pTpSetWait)(TP_WAIT *,HANDLE,LARGE_INTEGER *);
+static NTSTATUS (WINAPI *pTpSimpleTryPost)(PTP_SIMPLE_CALLBACK,PVOID,TP_CALLBACK_ENVIRON *);
+static VOID     (WINAPI *pTpWaitForTimer)(TP_TIMER *,BOOL);
+static VOID     (WINAPI *pTpWaitForWait)(TP_WAIT *,BOOL);
+static VOID     (WINAPI *pTpWaitForWork)(TP_WORK *,BOOL);
+
+#define NTDLL_GET_PROC(func) \
+    do \
+    { \
+        p ## func = (void *)GetProcAddress(hntdll, #func); \
+        if (!p ## func) trace("Failed to get address for %s\n", #func); \
+    } \
+    while (0)
+
+static BOOL init_threadpool(void)
+{
+    hntdll = GetModuleHandleA("ntdll");
+    if (!hntdll)
+    {
+        win_skip("Could not load ntdll\n");
+        return FALSE;
+    }
+
+    NTDLL_GET_PROC(TpAllocCleanupGroup);
+    NTDLL_GET_PROC(TpAllocPool);
+    NTDLL_GET_PROC(TpAllocTimer);
+    NTDLL_GET_PROC(TpAllocWait);
+    NTDLL_GET_PROC(TpAllocWork);
+    NTDLL_GET_PROC(TpCallbackMayRunLong);
+    NTDLL_GET_PROC(TpCallbackReleaseSemaphoreOnCompletion);
+    NTDLL_GET_PROC(TpDisassociateCallback);
+    NTDLL_GET_PROC(TpIsTimerSet);
+    NTDLL_GET_PROC(TpPostWork);
+    NTDLL_GET_PROC(TpReleaseCleanupGroup);
+    NTDLL_GET_PROC(TpReleaseCleanupGroupMembers);
+    NTDLL_GET_PROC(TpReleasePool);
+    NTDLL_GET_PROC(TpReleaseTimer);
+    NTDLL_GET_PROC(TpReleaseWait);
+    NTDLL_GET_PROC(TpReleaseWork);
+    NTDLL_GET_PROC(TpSetPoolMaxThreads);
+    NTDLL_GET_PROC(TpSetTimer);
+    NTDLL_GET_PROC(TpSetWait);
+    NTDLL_GET_PROC(TpSimpleTryPost);
+    NTDLL_GET_PROC(TpWaitForTimer);
+    NTDLL_GET_PROC(TpWaitForWait);
+    NTDLL_GET_PROC(TpWaitForWork);
+
+    if (!pTpAllocPool)
+    {
+        win_skip("Threadpool functions not supported, skipping tests\n");
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+#undef NTDLL_GET_PROC
+
+
+static DWORD CALLBACK rtl_work_cb(void *userdata)
+{
+    HANDLE semaphore = userdata;
+    trace("Running rtl_work callback\n");
+    ReleaseSemaphore(semaphore, 1, NULL);
+    return 0;
+}
+
+static void test_RtlQueueWorkItem(void)
+{
+    HANDLE semaphore;
+    NTSTATUS status;
+    DWORD result;
+
+    semaphore = CreateSemaphoreA(NULL, 0, 1, NULL);
+    ok(semaphore != NULL, "CreateSemaphoreA failed %u\n", GetLastError());
+
+    status = RtlQueueWorkItem(rtl_work_cb, semaphore, WT_EXECUTEDEFAULT);
+    ok(!status, "RtlQueueWorkItem failed with status %x\n", status);
+    result = WaitForSingleObject(semaphore, 1000);
+    ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
+
+    status = RtlQueueWorkItem(rtl_work_cb, semaphore, WT_EXECUTEINIOTHREAD);
+    ok(!status, "RtlQueueWorkItem failed with status %x\n", status);
+    result = WaitForSingleObject(semaphore, 1000);
+    ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
+
+    status = RtlQueueWorkItem(rtl_work_cb, semaphore, WT_EXECUTEINPERSISTENTTHREAD);
+    ok(!status, "RtlQueueWorkItem failed with status %x\n", status);
+    result = WaitForSingleObject(semaphore, 1000);
+    ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
+
+    status = RtlQueueWorkItem(rtl_work_cb, semaphore, WT_EXECUTELONGFUNCTION);
+    ok(!status, "RtlQueueWorkItem failed with status %x\n", status);
+    result = WaitForSingleObject(semaphore, 1000);
+    ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
+
+    status = RtlQueueWorkItem(rtl_work_cb, semaphore, WT_TRANSFER_IMPERSONATION);
+    ok(!status, "RtlQueueWorkItem failed with status %x\n", status);
+    result = WaitForSingleObject(semaphore, 1000);
+    ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
+
+    CloseHandle(semaphore);
+}
+
+struct rtl_wait_info
+{
+    HANDLE semaphore1;
+    HANDLE semaphore2;
+    DWORD wait_result;
+    DWORD threadid;
+    LONG userdata;
+};
+
+static void CALLBACK rtl_wait_cb(void *userdata, BOOLEAN timeout)
+{
+    struct rtl_wait_info *info = userdata;
+    DWORD result;
+
+    trace("Running rtl_wait callback\n");
+
+    if (!timeout)
+        InterlockedIncrement(&info->userdata);
+    else
+        InterlockedExchangeAdd(&info->userdata, 0x10000);
+    info->threadid = GetCurrentThreadId();
+    ReleaseSemaphore(info->semaphore1, 1, NULL);
+
+    if (info->semaphore2)
+    {
+        result = WaitForSingleObject(info->semaphore2, 200);
+        ok(result == info->wait_result, "expected %u, got %u\n", info->wait_result, result);
+        ReleaseSemaphore(info->semaphore1, 1, NULL);
+    }
+}
+
+static HANDLE rtl_wait_apc_semaphore;
+
+static void CALLBACK rtl_wait_apc_cb(ULONG_PTR userdata)
+{
+    trace("Running rtl_wait_apc callback\n");
+    if (rtl_wait_apc_semaphore)
+        ReleaseSemaphore(rtl_wait_apc_semaphore, 1, NULL);
+}
+
+static void test_RtlRegisterWait(void)
+{
+    HANDLE wait1, event, thread;
+    struct rtl_wait_info info;
+    HANDLE semaphores[2];
+    NTSTATUS status;
+    DWORD result;
+
+    semaphores[0] = CreateSemaphoreW(NULL, 0, 2, NULL);
+    ok(semaphores[0] != NULL, "failed to create semaphore\n");
+    semaphores[1] = CreateSemaphoreW(NULL, 0, 1, NULL);
+    ok(semaphores[1] != NULL, "failed to create semaphore\n");
+    info.semaphore1 = semaphores[0];
+    info.semaphore2 = NULL;
+
+    event = CreateEventW(NULL, FALSE, FALSE, NULL);
+    ok(event != NULL, "failed to create event\n");
+
+    /* basic test for RtlRegisterWait and RtlDeregisterWait */
+    wait1 = NULL;
+    info.userdata = 0;
+    status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, INFINITE, WT_EXECUTEDEFAULT);
+    ok(!status, "RtlRegisterWait failed with status %x\n", status);
+    ok(wait1 != NULL, "expected wait1 != NULL\n");
+    status = RtlDeregisterWait(wait1);
+    ok(!status, "RtlDeregisterWait failed with status %x\n", status);
+    ok(info.userdata == 0, "expected info.userdata = 0, got %u\n", info.userdata);
+
+    /* infinite timeout, signal the semaphore two times */
+    info.userdata = 0;
+    status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, INFINITE, WT_EXECUTEDEFAULT);
+    ok(!status, "RtlRegisterWait failed with status %x\n", status);
+    ReleaseSemaphore(semaphores[1], 1, NULL);
+    result = WaitForSingleObject(semaphores[0], 100);
+    ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
+    ok(info.userdata == 1, "expected info.userdata = 1, got %u\n", info.userdata);
+    ReleaseSemaphore(semaphores[1], 1, NULL);
+    result = WaitForSingleObject(semaphores[0], 100);
+    ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
+    ok(info.userdata == 2, "expected info.userdata = 2, got %u\n", info.userdata);
+    result = WaitForSingleObject(semaphores[1], 0);
+    ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
+    Sleep(50);
+    status = RtlDeregisterWait(wait1);
+    ok(!status, "RtlDeregisterWait failed with status %x\n", status);
+
+    /* repeat test with WT_EXECUTEONLYONCE */
+    info.userdata = 0;
+    status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, INFINITE, WT_EXECUTEONLYONCE);
+    ok(!status, "RtlRegisterWait failed with status %x\n", status);
+    ReleaseSemaphore(semaphores[1], 1, NULL);
+    result = WaitForSingleObject(semaphores[0], 100);
+    ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
+    ok(info.userdata == 1, "expected info.userdata = 1, got %u\n", info.userdata);
+    ReleaseSemaphore(semaphores[1], 1, NULL);
+    result = WaitForSingleObject(semaphores[0], 100);
+    ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
+    ok(info.userdata == 1, "expected info.userdata = 1, got %u\n", info.userdata);
+    result = WaitForSingleObject(semaphores[1], 0);
+    ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
+    Sleep(50);