[KERNEL32_WINETEST] Sync with Wine Staging 1.7.55. CORE-10536
authorAmine Khaldi <amine.khaldi@reactos.org>
Mon, 23 Nov 2015 16:10:53 +0000 (16:10 +0000)
committerAmine Khaldi <amine.khaldi@reactos.org>
Mon, 23 Nov 2015 16:10:53 +0000 (16:10 +0000)
svn path=/trunk/; revision=70069

18 files changed:
rostests/winetests/kernel32/actctx.c
rostests/winetests/kernel32/codepage.c
rostests/winetests/kernel32/comm.c
rostests/winetests/kernel32/console.c
rostests/winetests/kernel32/debugger.c
rostests/winetests/kernel32/file.c
rostests/winetests/kernel32/heap.c
rostests/winetests/kernel32/loader.c
rostests/winetests/kernel32/locale.c
rostests/winetests/kernel32/path.c
rostests/winetests/kernel32/pipe.c
rostests/winetests/kernel32/process.c
rostests/winetests/kernel32/sync.c
rostests/winetests/kernel32/thread.c
rostests/winetests/kernel32/time.c
rostests/winetests/kernel32/toolhelp.c
rostests/winetests/kernel32/virtual.c
rostests/winetests/kernel32/volume.c

index 2976ff1..6e917fd 100644 (file)
@@ -16,6 +16,8 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#include <ntstatus.h>
+#define WIN32_NO_STATUS
 #include <wine/test.h>
 #include <winbase.h>
 #include <windef.h>
@@ -39,6 +41,10 @@ static BOOL   (WINAPI *pQueryActCtxW)(DWORD,HANDLE,PVOID,ULONG,PVOID,SIZE_T,SIZE
 static VOID   (WINAPI *pReleaseActCtx)(HANDLE);
 static BOOL   (WINAPI *pFindActCtxSectionGuid)(DWORD,const GUID*,ULONG,const GUID*,PACTCTX_SECTION_KEYED_DATA);
 
+static NTSTATUS(NTAPI *pRtlFindActivationContextSectionString)(DWORD,const GUID *,ULONG,PUNICODE_STRING,PACTCTX_SECTION_KEYED_DATA);
+static BOOLEAN (NTAPI *pRtlCreateUnicodeStringFromAsciiz)(PUNICODE_STRING, PCSZ);
+static VOID    (NTAPI *pRtlFreeUnicodeString)(PUNICODE_STRING);
+
 static const char* strw(LPCWSTR x)
 {
     static char buffer[1024];
@@ -53,6 +59,10 @@ static const char* strw(LPCWSTR x)
 #define ARCH "x86"
 #elif defined __x86_64__
 #define ARCH "amd64"
+#elif defined __arm__
+#define ARCH "arm"
+#elif defined __aarch64__
+#define ARCH "arm64"
 #else
 #define ARCH "none"
 #endif
@@ -2048,6 +2058,124 @@ static void test_app_manifest(void)
     }
 }
 
+static HANDLE create_manifest(const char *filename, const char *data, int line)
+{
+    HANDLE handle;
+    create_manifest_file(filename, data, -1, NULL, NULL);
+
+    handle = test_create(filename);
+    ok_(__FILE__, line)(handle != INVALID_HANDLE_VALUE,
+        "handle == INVALID_HANDLE_VALUE, error %u\n", GetLastError());
+
+    DeleteFileA(filename);
+    return handle;
+}
+
+static void kernel32_find(ULONG section, const char *string_to_find, BOOL should_find, int line)
+{
+    UNICODE_STRING string_to_findW;
+    ACTCTX_SECTION_KEYED_DATA data;
+    BOOL ret;
+    DWORD err;
+
+    pRtlCreateUnicodeStringFromAsciiz(&string_to_findW, string_to_find);
+
+    memset(&data, 0xfe, sizeof(data));
+    data.cbSize = sizeof(data);
+
+    SetLastError(0);
+    ret = pFindActCtxSectionStringA(0, NULL, section, string_to_find, &data);
+    err = GetLastError();
+    ok_(__FILE__, line)(ret == should_find,
+        "FindActCtxSectionStringA: expected ret = %u, got %u\n", should_find, ret);
+    ok_(__FILE__, line)(err == (should_find ? ERROR_SUCCESS : ERROR_SXS_KEY_NOT_FOUND),
+        "FindActCtxSectionStringA: unexpected error %u\n", err);
+
+    memset(&data, 0xfe, sizeof(data));
+    data.cbSize = sizeof(data);
+
+    SetLastError(0);
+    ret = pFindActCtxSectionStringW(0, NULL, section, string_to_findW.Buffer, &data);
+    err = GetLastError();
+    ok_(__FILE__, line)(ret == should_find,
+        "FindActCtxSectionStringW: expected ret = %u, got %u\n", should_find, ret);
+    ok_(__FILE__, line)(err == (should_find ? ERROR_SUCCESS : ERROR_SXS_KEY_NOT_FOUND),
+        "FindActCtxSectionStringW: unexpected error %u\n", err);
+
+    SetLastError(0);
+    ret = pFindActCtxSectionStringA(0, NULL, section, string_to_find, NULL);
+    err = GetLastError();
+    ok_(__FILE__, line)(!ret,
+        "FindActCtxSectionStringA: expected failure, got %u\n", ret);
+    ok_(__FILE__, line)(err == ERROR_INVALID_PARAMETER,
+        "FindActCtxSectionStringA: unexpected error %u\n", err);
+
+    SetLastError(0);
+    ret = pFindActCtxSectionStringW(0, NULL, section, string_to_findW.Buffer, NULL);
+    err = GetLastError();
+    ok_(__FILE__, line)(!ret,
+        "FindActCtxSectionStringW: expected failure, got %u\n", ret);
+    ok_(__FILE__, line)(err == ERROR_INVALID_PARAMETER,
+        "FindActCtxSectionStringW: unexpected error %u\n", err);
+
+    pRtlFreeUnicodeString(&string_to_findW);
+}
+
+static void ntdll_find(ULONG section, const char *string_to_find, BOOL should_find, int line)
+{
+    UNICODE_STRING string_to_findW;
+    ACTCTX_SECTION_KEYED_DATA data;
+    NTSTATUS ret;
+
+    pRtlCreateUnicodeStringFromAsciiz(&string_to_findW, string_to_find);
+
+    memset(&data, 0xfe, sizeof(data));
+    data.cbSize = sizeof(data);
+
+    ret = pRtlFindActivationContextSectionString(0, NULL, section, &string_to_findW, &data);
+    ok_(__FILE__, line)(ret == (should_find ? STATUS_SUCCESS : STATUS_SXS_KEY_NOT_FOUND),
+        "RtlFindActivationContextSectionString: unexpected status 0x%x\n", ret);
+
+    ret = pRtlFindActivationContextSectionString(0, NULL, section, &string_to_findW, NULL);
+    ok_(__FILE__, line)(ret == (should_find ? STATUS_SUCCESS : STATUS_SXS_KEY_NOT_FOUND),
+        "RtlFindActivationContextSectionString: unexpected status 0x%x\n", ret);
+
+    pRtlFreeUnicodeString(&string_to_findW);
+}
+
+static void test_findsectionstring(void)
+{
+    HANDLE handle;
+    BOOL ret;
+    ULONG_PTR cookie;
+
+    handle = create_manifest("test.manifest", testdep_manifest3, __LINE__);
+    ret = pActivateActCtx(handle, &cookie);
+    ok(ret, "ActivateActCtx failed: %u\n", GetLastError());
+
+    /* first we show the parameter validation from kernel32 */
+    kernel32_find(ACTIVATION_CONTEXT_SECTION_ASSEMBLY_INFORMATION, "testdep", FALSE, __LINE__);
+    kernel32_find(ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION, "testlib.dll", TRUE, __LINE__);
+    kernel32_find(ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION, "testlib2.dll", TRUE, __LINE__);
+    kernel32_find(ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION, "testlib3.dll", FALSE, __LINE__);
+    kernel32_find(ACTIVATION_CONTEXT_SECTION_WINDOW_CLASS_REDIRECTION, "wndClass", TRUE, __LINE__);
+    kernel32_find(ACTIVATION_CONTEXT_SECTION_WINDOW_CLASS_REDIRECTION, "wndClass2", TRUE, __LINE__);
+    kernel32_find(ACTIVATION_CONTEXT_SECTION_WINDOW_CLASS_REDIRECTION, "wndClass3", FALSE, __LINE__);
+
+    /* then we show that ntdll plays by different rules */
+    ntdll_find(ACTIVATION_CONTEXT_SECTION_ASSEMBLY_INFORMATION, "testdep", FALSE, __LINE__);
+    ntdll_find(ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION, "testlib.dll", TRUE, __LINE__);
+    ntdll_find(ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION, "testlib2.dll", TRUE, __LINE__);
+    ntdll_find(ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION, "testlib3.dll", FALSE, __LINE__);
+    ntdll_find(ACTIVATION_CONTEXT_SECTION_WINDOW_CLASS_REDIRECTION, "wndClass", TRUE, __LINE__);
+    ntdll_find(ACTIVATION_CONTEXT_SECTION_WINDOW_CLASS_REDIRECTION, "wndClass2", TRUE, __LINE__);
+    ntdll_find(ACTIVATION_CONTEXT_SECTION_WINDOW_CLASS_REDIRECTION, "wndClass3", FALSE, __LINE__);
+
+    ret = pDeactivateActCtx(0, cookie);
+    ok(ret, "DeactivateActCtx failed: %u\n", GetLastError());
+    pReleaseActCtx(handle);
+}
+
 static void run_child_process(void)
 {
     char cmdline[MAX_PATH];
@@ -2263,9 +2391,9 @@ todo_wine
 
 static BOOL init_funcs(void)
 {
-    HMODULE hKernel32 = GetModuleHandleA("kernel32.dll");
+    HMODULE hLibrary = GetModuleHandleA("kernel32.dll");
 
-#define X(f) if (!(p##f = (void*)GetProcAddress(hKernel32, #f))) return FALSE;
+#define X(f) if (!(p##f = (void*)GetProcAddress(hLibrary, #f))) return FALSE;
     X(ActivateActCtx);
     X(CreateActCtxA);
     X(CreateActCtxW);
@@ -2277,6 +2405,11 @@ static BOOL init_funcs(void)
     X(QueryActCtxW);
     X(ReleaseActCtx);
     X(FindActCtxSectionGuid);
+
+    hLibrary = GetModuleHandleA("ntdll.dll");
+    X(RtlFindActivationContextSectionString);
+    X(RtlCreateUnicodeStringFromAsciiz);
+    X(RtlFreeUnicodeString);
 #undef X
 
     return TRUE;
@@ -2303,5 +2436,6 @@ START_TEST(actctx)
 
     test_actctx();
     test_CreateActCtx();
+    test_findsectionstring();
     run_child_process();
 }
index ffac1ac..73bb7be 100755 (executable)
@@ -28,6 +28,7 @@
 #include "winbase.h"
 #include "winnls.h"
 
+static const char foobarA[] = "foobar";
 static const WCHAR foobarW[] = {'f','o','o','b','a','r',0};
 
 static void test_destination_buffer(void)
@@ -144,48 +145,57 @@ static void test_negative_source_length(void)
 static void test_negative_dest_length(void)
 {
     int len, i;
-    static char buf[LONGBUFLEN];
+    static WCHAR bufW[LONGBUFLEN];
+    static char bufA[LONGBUFLEN];
     static WCHAR originalW[LONGBUFLEN];
     static char originalA[LONGBUFLEN];
     DWORD theError;
 
     /* Test return on -1 dest length */
     SetLastError( 0xdeadbeef );
-    memset(buf,'x',sizeof(buf));
-    len = WideCharToMultiByte(CP_ACP, 0, foobarW, -1, buf, -1, NULL, NULL);
-    todo_wine {
-      ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER,
-         "WideCharToMultiByte(destlen -1): len=%d error=%x\n", len, GetLastError());
-    }
+    memset(bufA,'x',sizeof(bufA));
+    len = WideCharToMultiByte(CP_ACP, 0, foobarW, -1, bufA, -1, NULL, NULL);
+    ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER,
+       "WideCharToMultiByte(destlen -1): len=%d error=%x\n", len, GetLastError());
+
+    SetLastError( 0xdeadbeef );
+    memset(bufW,'x',sizeof(bufW));
+    len = MultiByteToWideChar(CP_ACP, 0, foobarA, -1, bufW, -1);
+    ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER,
+       "MultiByteToWideChar(destlen -1): len=%d error=%x\n", len, GetLastError());
 
     /* Test return on -1000 dest length */
     SetLastError( 0xdeadbeef );
-    memset(buf,'x',sizeof(buf));
-    len = WideCharToMultiByte(CP_ACP, 0, foobarW, -1, buf, -1000, NULL, NULL);
-    todo_wine {
-      ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER,
-         "WideCharToMultiByte(destlen -1000): len=%d error=%x\n", len, GetLastError());
-    }
+    memset(bufA,'x',sizeof(bufA));
+    len = WideCharToMultiByte(CP_ACP, 0, foobarW, -1, bufA, -1000, NULL, NULL);
+    ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER,
+       "WideCharToMultiByte(destlen -1000): len=%d error=%x\n", len, GetLastError());
+
+    SetLastError( 0xdeadbeef );
+    memset(bufW,'x',sizeof(bufW));
+    len = MultiByteToWideChar(CP_ACP, 0, foobarA, -1000, bufW, -1);
+    ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER,
+       "MultiByteToWideChar(destlen -1000): len=%d error=%x\n", len, GetLastError());
 
     /* Test return on INT_MAX dest length */
     SetLastError( 0xdeadbeef );
-    memset(buf,'x',sizeof(buf));
-    len = WideCharToMultiByte(CP_ACP, 0, foobarW, -1, buf, INT_MAX, NULL, NULL);
-    ok(len == 7 && !lstrcmpA(buf, "foobar") && GetLastError() == 0xdeadbeef,
+    memset(bufA,'x',sizeof(bufA));
+    len = WideCharToMultiByte(CP_ACP, 0, foobarW, -1, bufA, INT_MAX, NULL, NULL);
+    ok(len == 7 && !lstrcmpA(bufA, "foobar") && GetLastError() == 0xdeadbeef,
        "WideCharToMultiByte(destlen INT_MAX): len=%d error=%x\n", len, GetLastError());
 
     /* Test return on INT_MAX dest length and very long input */
     SetLastError( 0xdeadbeef );
-    memset(buf,'x',sizeof(buf));
+    memset(bufA,'x',sizeof(bufA));
     for (i=0; i < LONGBUFLEN - 1; i++) {
         originalW[i] = 'Q';
         originalA[i] = 'Q';
     }
     originalW[LONGBUFLEN-1] = 0;
     originalA[LONGBUFLEN-1] = 0;
-    len = WideCharToMultiByte(CP_ACP, 0, originalW, -1, buf, INT_MAX, NULL, NULL);
+    len = WideCharToMultiByte(CP_ACP, 0, originalW, -1, bufA, INT_MAX, NULL, NULL);
     theError = GetLastError();
-    ok(len == LONGBUFLEN && !lstrcmpA(buf, originalA) && theError == 0xdeadbeef,
+    ok(len == LONGBUFLEN && !lstrcmpA(bufA, originalA) && theError == 0xdeadbeef,
        "WideCharToMultiByte(srclen %d, destlen INT_MAX): len %d error=%x\n", LONGBUFLEN, len, theError);
 
 }
@@ -276,12 +286,14 @@ static void test_overlapped_buffers(void)
 static void test_string_conversion(LPBOOL bUsedDefaultChar)
 {
     char mbc;
-    char mbs[5];
+    char mbs[15];
     int ret;
     WCHAR wc1 = 228;                           /* Western Windows-1252 character */
     WCHAR wc2 = 1088;                          /* Russian Windows-1251 character not displayable for Windows-1252 */
     static const WCHAR wcs[] = {'T', 'h', 1088, 'i', 0}; /* String with ASCII characters and a Russian character */
     static const WCHAR dbwcs[] = {28953, 25152, 0}; /* String with Chinese (codepage 950) characters */
+    static const WCHAR dbwcs2[] = {0x7bb8, 0x3d, 0xc813, 0xac00, 0xb77d, 0};
+    static const char default_char[] = {0xa3, 0xbf, 0};
 
     SetLastError(0xdeadbeef);
     ret = WideCharToMultiByte(1252, 0, &wc1, 1, &mbc, 1, NULL, bUsedDefaultChar);
@@ -358,6 +370,11 @@ static void test_string_conversion(LPBOOL bUsedDefaultChar)
     ok(!strcmp(mbs, "??"), "mbs is %s\n", mbs);
     if(bUsedDefaultChar) ok(*bUsedDefaultChar == TRUE, "bUsedDefaultChar is %d\n", *bUsedDefaultChar);
 
+    ret = WideCharToMultiByte(936, WC_COMPOSITECHECK, dbwcs2, -1, mbs, sizeof(mbs), (const char *)default_char, bUsedDefaultChar);
+    ok(ret == 10, "ret is %d\n", ret);
+    ok(!strcmp(mbs, "\xf3\xe7\x3d\xa3\xbf\xa3\xbf\xa3\xbf"), "mbs is %s\n", mbs);
+    if(bUsedDefaultChar) ok(*bUsedDefaultChar == TRUE, "bUsedDefaultChar is %d\n", *bUsedDefaultChar);
+
     /* Length-only tests */
     SetLastError(0xdeadbeef);
     ret = WideCharToMultiByte(1252, 0, &wc2, 1, NULL, 0, NULL, bUsedDefaultChar);
index 2f708d6..367b88f 100755 (executable)
@@ -941,7 +941,8 @@ static void test_waittxempty(void)
         evtmask = 0;
         SetLastError(0xdeadbeef);
         res = WaitCommEvent(hcom, &evtmask, &ovl_wait);
-        ok((!res && GetLastError() == ERROR_IO_PENDING) || res /* busy system */, "%d: WaitCommEvent error %d\n", i, GetLastError());
+        ok(res /* busy system */ || GetLastError() == ERROR_IO_PENDING,
+           "%d: WaitCommEvent error %d\n", i, GetLastError());
 
         res = WaitForSingleObject(ovl_wait.hEvent, TIMEOUT);
         if (i == 0)
index 5928e8c..240d9d8 100755 (executable)
@@ -2588,6 +2588,99 @@ static void test_ReadConsole(void)
     ok(bytes == 0xdeadbeef, "expected 0xdeadbeef, got %#x\n", bytes);
 }
 
+static void test_GetCurrentConsoleFont(HANDLE std_output)
+{
+    BOOL ret;
+    CONSOLE_FONT_INFO cfi;
+    CONSOLE_SCREEN_BUFFER_INFO csbi;
+    short int width, height;
+    COORD c;
+
+    memset(&cfi, 0, sizeof(CONSOLE_FONT_INFO));
+    SetLastError(0xdeadbeef);
+    ret = GetCurrentConsoleFont(NULL, FALSE, &cfi);
+    ok(!ret, "got %d, expected 0\n", ret);
+    ok(GetLastError() == ERROR_INVALID_HANDLE, "got %u, expected 6\n", GetLastError());
+    ok(!cfi.dwFontSize.X, "got %d, expected 0\n", cfi.dwFontSize.X);
+    ok(!cfi.dwFontSize.Y, "got %d, expected 0\n", cfi.dwFontSize.Y);
+
+    memset(&cfi, 0, sizeof(CONSOLE_FONT_INFO));
+    SetLastError(0xdeadbeef);
+    ret = GetCurrentConsoleFont(GetStdHandle(STD_INPUT_HANDLE), FALSE, &cfi);
+    ok(!ret, "got %d, expected 0\n", ret);
+    ok(GetLastError() == ERROR_INVALID_HANDLE, "got %u, expected 6\n", GetLastError());
+    ok(!cfi.dwFontSize.X, "got %d, expected 0\n", cfi.dwFontSize.X);
+    ok(!cfi.dwFontSize.Y, "got %d, expected 0\n", cfi.dwFontSize.Y);
+
+    memset(&cfi, 0, sizeof(CONSOLE_FONT_INFO));
+    SetLastError(0xdeadbeef);
+    ret = GetCurrentConsoleFont(std_output, FALSE, &cfi);
+    ok(ret, "got %d, expected non-zero\n", ret);
+    ok(GetLastError() == 0xdeadbeef, "got %u, expected 0xdeadbeef\n", GetLastError());
+    GetConsoleScreenBufferInfo(std_output, &csbi);
+    width = csbi.srWindow.Right - csbi.srWindow.Left + 1;
+    height = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
+    c = GetConsoleFontSize(std_output, cfi.nFont);
+    ok(cfi.dwFontSize.X == width || cfi.dwFontSize.X == c.X /* Vista and higher */,
+        "got %d, expected %d\n", cfi.dwFontSize.X, width);
+    ok(cfi.dwFontSize.Y == height || cfi.dwFontSize.Y == c.Y /* Vista and higher */,
+        "got %d, expected %d\n", cfi.dwFontSize.Y, height);
+}
+
+static void test_GetConsoleFontSize(HANDLE std_output)
+{
+    COORD c;
+    DWORD index = 0;
+    CONSOLE_FONT_INFO cfi;
+    RECT r;
+    CONSOLE_SCREEN_BUFFER_INFO csbi;
+    LONG font_width, font_height;
+    HMODULE hmod;
+    DWORD (WINAPI *pGetNumberOfConsoleFonts)(void);
+
+    memset(&c, 10, sizeof(COORD));
+    SetLastError(0xdeadbeef);
+    c = GetConsoleFontSize(NULL, index);
+    ok(GetLastError() == ERROR_INVALID_HANDLE, "got %u, expected 6\n", GetLastError());
+    ok(!c.X, "got %d, expected 0\n", c.X);
+    ok(!c.Y, "got %d, expected 0\n", c.Y);
+
+    memset(&c, 10, sizeof(COORD));
+    SetLastError(0xdeadbeef);
+    c = GetConsoleFontSize(GetStdHandle(STD_INPUT_HANDLE), index);
+    ok(GetLastError() == ERROR_INVALID_HANDLE, "got %u, expected 6\n", GetLastError());
+    ok(!c.X, "got %d, expected 0\n", c.X);
+    ok(!c.Y, "got %d, expected 0\n", c.Y);
+
+    GetCurrentConsoleFont(std_output, FALSE, &cfi);
+    memset(&c, 10, sizeof(COORD));
+    SetLastError(0xdeadbeef);
+    c = GetConsoleFontSize(std_output, cfi.nFont);
+    ok(GetLastError() == 0xdeadbeef, "got %u, expected 0xdeadbeef\n", GetLastError());
+    GetClientRect(GetConsoleWindow(), &r);
+    GetConsoleScreenBufferInfo(std_output, &csbi);
+    font_width = (r.right - r.left + 1) / csbi.srWindow.Right;
+    font_height = (r.bottom - r.top + 1) / csbi.srWindow.Bottom;
+    ok(c.X == font_width, "got %d, expected %d\n", c.X, font_width);
+    ok(c.Y == font_height, "got %d, expected %d\n", c.Y, font_height);
+
+    hmod = GetModuleHandleA("kernel32.dll");
+    pGetNumberOfConsoleFonts = (void *)GetProcAddress(hmod, "GetNumberOfConsoleFonts");
+    if (!pGetNumberOfConsoleFonts)
+    {
+        win_skip("GetNumberOfConsoleFonts is not available\n");
+        return;
+    }
+    index = pGetNumberOfConsoleFonts();
+
+    memset(&c, 10, sizeof(COORD));
+    SetLastError(0xdeadbeef);
+    c = GetConsoleFontSize(std_output, index);
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "got %u, expected 87\n", GetLastError());
+    ok(!c.X, "got %d, expected 0\n", c.X);
+    ok(!c.Y, "got %d, expected 0\n", c.Y);
+}
+
 START_TEST(console)
 {
     static const char font_name[] = "Lucida Console";
@@ -2720,4 +2813,6 @@ START_TEST(console)
     test_ReadConsoleOutputCharacterA(hConOut);
     test_ReadConsoleOutputCharacterW(hConOut);
     test_ReadConsoleOutputAttribute(hConOut);
+    test_GetCurrentConsoleFont(hConOut);
+    test_GetConsoleFontSize(hConOut);
 }
index 82a6873..0d15e64 100644 (file)
@@ -158,7 +158,7 @@ typedef struct
 
 static void doCrash(int argc,  char** argv)
 {
-    char* p;
+    volatile char* p;
 
     /* make sure the exception gets to the debugger */
     SetErrorMode( 0 );
index 051e19c..1a89245 100755 (executable)
 #include <time.h>
 #include <stdio.h>
 
-#include "wine/test.h"
-#include "windef.h"
-#include "winbase.h"
-#include "winerror.h"
-#include "winnls.h"
-#include "fileapi.h"
+#include <ntstatus.h>
+#define WIN32_NO_STATUS
+#include <wine/test.h>
+#include <windef.h>
+#include <winbase.h>
+#include <winerror.h>
+#include <wine/winternl.h>
+#include <winnls.h>
+#include <fileapi.h>
+
+#undef DeleteFile  /* needed for FILE_DISPOSITION_INFO */
 
 static HANDLE (WINAPI *pFindFirstFileExA)(LPCSTR,FINDEX_INFO_LEVELS,LPVOID,FINDEX_SEARCH_OPS,LPVOID,DWORD);
 static BOOL (WINAPI *pReplaceFileA)(LPCSTR, LPCSTR, LPCSTR, DWORD, LPVOID, LPVOID);
@@ -46,8 +51,13 @@ static HANDLE (WINAPI *pOpenFileById)(HANDLE, LPFILE_ID_DESCRIPTOR, DWORD, DWORD
 static BOOL (WINAPI *pSetFileValidData)(HANDLE, LONGLONG);
 static HRESULT (WINAPI *pCopyFile2)(PCWSTR,PCWSTR,COPYFILE2_EXTENDED_PARAMETERS*);
 static HANDLE (WINAPI *pCreateFile2)(LPCWSTR, DWORD, DWORD, DWORD, CREATEFILE2_EXTENDED_PARAMETERS*);
-static DWORD (WINAPI* pGetFinalPathNameByHandleA)(HANDLE, LPSTR, DWORD, DWORD);
-static DWORD (WINAPI* pGetFinalPathNameByHandleW)(HANDLE, LPWSTR, DWORD, DWORD);
+static DWORD (WINAPI *pGetFinalPathNameByHandleA)(HANDLE, LPSTR, DWORD, DWORD);
+static DWORD (WINAPI *pGetFinalPathNameByHandleW)(HANDLE, LPWSTR, DWORD, DWORD);
+static NTSTATUS (WINAPI *pNtCreateFile)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PIO_STATUS_BLOCK,
+                                        PLARGE_INTEGER, ULONG, ULONG, ULONG, ULONG, PVOID, ULONG);
+static BOOL (WINAPI *pRtlDosPathNameToNtPathName_U)(LPCWSTR, PUNICODE_STRING, PWSTR*, CURDIR*);
+static NTSTATUS (WINAPI *pRtlAnsiStringToUnicodeString)(PUNICODE_STRING, PCANSI_STRING, BOOLEAN);
+static BOOL (WINAPI *pSetFileInformationByHandle)(HANDLE, FILE_INFO_BY_HANDLE_CLASS, void*, DWORD);
 
 static const char filename[] = "testfile.xxx";
 static const char sillytext[] =
@@ -72,8 +82,13 @@ struct test_list {
 
 static void InitFunctionPointers(void)
 {
+    HMODULE hntdll = GetModuleHandleA("ntdll");
     HMODULE hkernel32 = GetModuleHandleA("kernel32");
 
+    pNtCreateFile = (void *)GetProcAddress(hntdll, "NtCreateFile");
+    pRtlDosPathNameToNtPathName_U = (void *)GetProcAddress(hntdll, "RtlDosPathNameToNtPathName_U");
+    pRtlAnsiStringToUnicodeString = (void *)GetProcAddress(hntdll, "RtlAnsiStringToUnicodeString");
+
     pFindFirstFileExA=(void*)GetProcAddress(hkernel32, "FindFirstFileExA");
     pReplaceFileA=(void*)GetProcAddress(hkernel32, "ReplaceFileA");
     pReplaceFileW=(void*)GetProcAddress(hkernel32, "ReplaceFileW");
@@ -87,6 +102,7 @@ static void InitFunctionPointers(void)
     pCreateFile2 = (void *) GetProcAddress(hkernel32, "CreateFile2");
     pGetFinalPathNameByHandleA = (void *) GetProcAddress(hkernel32, "GetFinalPathNameByHandleA");
     pGetFinalPathNameByHandleW = (void *) GetProcAddress(hkernel32, "GetFinalPathNameByHandleW");
+    pSetFileInformationByHandle = (void *) GetProcAddress(hkernel32, "SetFileInformationByHandle");
 }
 
 static void test__hread( void )
@@ -243,15 +259,36 @@ static void test__lclose( void )
     ok( ret != 0, "DeleteFile failed (%d)\n", GetLastError(  ) );
 }
 
+/* helper function for test__lcreat */
+static void get_nt_pathW( const char *name, UNICODE_STRING *nameW )
+{
+    UNICODE_STRING strW;
+    ANSI_STRING str;
+    NTSTATUS status;
+    BOOLEAN ret;
+    RtlInitAnsiString( &str, name );
+
+    status = pRtlAnsiStringToUnicodeString( &strW, &str, TRUE );
+    ok( !status, "RtlAnsiStringToUnicodeString failed with %08x\n", status );
+
+    ret = pRtlDosPathNameToNtPathName_U( strW.Buffer, nameW, NULL, NULL );
+    ok( ret, "RtlDosPathNameToNtPathName_U failed\n" );
+
+    RtlFreeUnicodeString( &strW );
+}
 
 static void test__lcreat( void )
 {
+    UNICODE_STRING filenameW;
+    OBJECT_ATTRIBUTES attr;
+    IO_STATUS_BLOCK io;
     HFILE filehandle;
     char buffer[10000];
     WIN32_FIND_DATAA search_results;
     char slashname[] = "testfi/";
     int err;
-    HANDLE find;
+    HANDLE find, file;
+    NTSTATUS status;
     BOOL ret;
 
     filehandle = _lcreat( filename, 0 );
@@ -287,12 +324,64 @@ static void test__lcreat( void )
     ok( INVALID_HANDLE_VALUE != find, "should be able to find file\n" );
     FindClose( find );
 
+    SetLastError( 0xdeadbeef );
     ok( 0 == DeleteFileA( filename ), "shouldn't be able to delete a readonly file\n" );
+    ok( GetLastError() == ERROR_ACCESS_DENIED, "expected ERROR_ACCESS_DENIED, got %d\n", GetLastError() );
 
     ok( SetFileAttributesA(filename, FILE_ATTRIBUTE_NORMAL ) != 0, "couldn't change attributes on file\n" );
 
     ok( DeleteFileA( filename ) != 0, "now it should be possible to delete the file!\n" );
 
+    filehandle = _lcreat( filename, 1 ); /* readonly */
+    ok( HFILE_ERROR != filehandle, "couldn't create file \"%s\" (err=%d)\n", filename, GetLastError() );
+    ok( HFILE_ERROR != _hwrite( filehandle, sillytext, strlen(sillytext) ),
+        "_hwrite shouldn't be able to write never the less\n" );
+    ok( HFILE_ERROR != _lclose(filehandle), "_lclose complains\n" );
+
+    find = FindFirstFileA( filename, &search_results );
+    ok( INVALID_HANDLE_VALUE != find, "should be able to find file\n" );
+    FindClose( find );
+
+    get_nt_pathW( filename, &filenameW );
+    attr.Length = sizeof(attr);
+    attr.RootDirectory = 0;
+    attr.Attributes = OBJ_CASE_INSENSITIVE;
+    attr.ObjectName = &filenameW;
+    attr.SecurityDescriptor = NULL;
+    attr.SecurityQualityOfService = NULL;
+
+    status = NtCreateFile( &file, GENERIC_READ | GENERIC_WRITE | DELETE, &attr, &io, NULL, 0,
+                           FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                           FILE_OPEN, FILE_DELETE_ON_CLOSE | FILE_NON_DIRECTORY_FILE, NULL, 0 );
+    ok( status == STATUS_ACCESS_DENIED, "expected STATUS_ACCESS_DENIED, got %08x\n", status );
+    ok( GetFileAttributesA( filename ) != INVALID_FILE_ATTRIBUTES, "file was deleted\n" );
+
+    status = NtCreateFile( &file, DELETE, &attr, &io, NULL, 0,
+                           FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                           FILE_OPEN, FILE_DELETE_ON_CLOSE | FILE_NON_DIRECTORY_FILE, NULL, 0 );
+    ok( status == STATUS_CANNOT_DELETE, "expected STATUS_CANNOT_DELETE, got %08x\n", status );
+
+    status = NtCreateFile( &file, DELETE, &attr, &io, NULL, 0,
+                           FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                           FILE_OPEN, FILE_DELETE_ON_CLOSE | FILE_DIRECTORY_FILE, NULL, 0 );
+    ok( status == STATUS_NOT_A_DIRECTORY, "expected STATUS_NOT_A_DIRECTORY, got %08x\n", status );
+
+    status = NtCreateFile( &file, DELETE, &attr, &io, NULL, 0,
+                           FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                           FILE_OPEN_IF, FILE_DELETE_ON_CLOSE | FILE_NON_DIRECTORY_FILE, NULL, 0 );
+    todo_wine
+    ok( status == STATUS_CANNOT_DELETE, "expected STATUS_CANNOT_DELETE, got %08x\n", status );
+    if (!status) CloseHandle( file );
+
+    RtlFreeUnicodeString( &filenameW );
+
+    todo_wine
+    ok( GetFileAttributesA( filename ) != INVALID_FILE_ATTRIBUTES, "file was deleted\n" );
+    todo_wine
+    ok( SetFileAttributesA(filename, FILE_ATTRIBUTE_NORMAL ) != 0, "couldn't change attributes on file\n" );
+    todo_wine
+    ok( DeleteFileA( filename ) != 0, "now it should be possible to delete the file\n" );
+
     filehandle = _lcreat( filename, 2 );
     ok( HFILE_ERROR != filehandle, "couldn't create file \"%s\" (err=%d)\n", filename, GetLastError(  ) );
 
@@ -1024,6 +1113,57 @@ static void test_CopyFile2(void)
     DeleteFileW(dest);
 }
 
+static DWORD WINAPI copy_progress_cb(LARGE_INTEGER total_size, LARGE_INTEGER total_transferred,
+                                     LARGE_INTEGER stream_size, LARGE_INTEGER stream_transferred,
+                                     DWORD stream, DWORD reason, HANDLE source, HANDLE dest, LPVOID userdata)
+{
+    ok(reason == CALLBACK_STREAM_SWITCH, "expected CALLBACK_STREAM_SWITCH, got %u\n", reason);
+    CloseHandle(userdata);
+    return PROGRESS_CANCEL;
+}
+
+static void test_CopyFileEx(void)
+{
+    char temp_path[MAX_PATH];
+    char source[MAX_PATH], dest[MAX_PATH];
+    static const char prefix[] = "pfx";
+    HANDLE hfile;
+    DWORD ret;
+    BOOL retok;
+
+    ret = GetTempPathA(MAX_PATH, temp_path);
+    ok(ret != 0, "GetTempPathA error %d\n", GetLastError());
+    ok(ret < MAX_PATH, "temp path should fit into MAX_PATH\n");
+
+    ret = GetTempFileNameA(temp_path, prefix, 0, source);
+    ok(ret != 0, "GetTempFileNameA error %d\n", GetLastError());
+
+    ret = GetTempFileNameA(temp_path, prefix, 0, dest);
+    ok(ret != 0, "GetTempFileNameA error %d\n", GetLastError());
+
+    hfile = CreateFileA(dest, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
+    ok(hfile != INVALID_HANDLE_VALUE, "failed to open destination file, error %d\n", GetLastError());
+    SetLastError(0xdeadbeef);
+    retok = CopyFileExA(source, dest, copy_progress_cb, hfile, NULL, 0);
+    ok(!retok, "CopyFileExA unexpectedly succeeded\n");
+    ok(GetLastError() == ERROR_REQUEST_ABORTED, "expected ERROR_REQUEST_ABORTED, got %d\n", GetLastError());
+    ok(GetFileAttributesA(dest) != INVALID_FILE_ATTRIBUTES, "file was deleted\n");
+
+    hfile = CreateFileA(dest, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                        NULL, OPEN_EXISTING, 0, 0);
+    ok(hfile != INVALID_HANDLE_VALUE, "failed to open destination file, error %d\n", GetLastError());
+    SetLastError(0xdeadbeef);
+    retok = CopyFileExA(source, dest, copy_progress_cb, hfile, NULL, 0);
+    ok(!retok, "CopyFileExA unexpectedly succeeded\n");
+    ok(GetLastError() == ERROR_REQUEST_ABORTED, "expected ERROR_REQUEST_ABORTED, got %d\n", GetLastError());
+    ok(GetFileAttributesA(dest) == INVALID_FILE_ATTRIBUTES, "file was not deleted\n");
+
+    ret = DeleteFileA(source);
+    ok(ret, "DeleteFileA failed with error %d\n", GetLastError());
+    ret = DeleteFileA(dest);
+    ok(!ret, "DeleteFileA unexpectedly succeeded\n");
+}
+
 /*
  *   Debugging routine to dump a buffer in a hexdump-like fashion.
  */
@@ -1646,14 +1786,12 @@ static void test_DeleteFileA( void )
 
     SetLastError(0xdeadbeef);
     ret = DeleteFileA(temp_file);
-todo_wine
     ok(ret, "DeleteFile error %d\n", GetLastError());
 
     SetLastError(0xdeadbeef);
     ret = CloseHandle(hfile);
     ok(ret, "CloseHandle error %d\n", GetLastError());
     ret = DeleteFileA(temp_file);
-todo_wine
     ok(!ret, "DeleteFile should fail\n");
 
     SetLastError(0xdeadbeef);
@@ -3689,11 +3827,21 @@ else
 static void test_GetFileInformationByHandleEx(void)
 {
     int i;
-    char tempPath[MAX_PATH], tempFileName[MAX_PATH], buffer[1024];
+    char tempPath[MAX_PATH], tempFileName[MAX_PATH], buffer[1024], *strPtr;
     BOOL ret;
-    DWORD ret2;
-    HANDLE directory;
+    DWORD ret2, written;
+    HANDLE directory, file;
     FILE_ID_BOTH_DIR_INFO *bothDirInfo;
+    FILE_BASIC_INFO *basicInfo;
+    FILE_STANDARD_INFO *standardInfo;
+    FILE_NAME_INFO *nameInfo;
+    LARGE_INTEGER prevWrite;
+    FILE_IO_PRIORITY_HINT_INFO priohintinfo;
+    FILE_ALLOCATION_INFO allocinfo;
+    FILE_DISPOSITION_INFO dispinfo;
+    FILE_END_OF_FILE_INFO eofinfo;
+    FILE_RENAME_INFO renameinfo;
+
     struct {
         FILE_INFO_BY_HANDLE_CLASS handleClass;
         void *ptr;
@@ -3756,6 +3904,89 @@ static void test_GetFileInformationByHandleEx(void)
     }
 
     CloseHandle(directory);
+
+    file = CreateFileA(tempFileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+        NULL, OPEN_EXISTING, 0, NULL);
+    ok(file != INVALID_HANDLE_VALUE, "GetFileInformationByHandleEx: failed to open the temp file, "
+        "got error %u.\n", GetLastError());
+
+    /* Test FileBasicInfo; make sure the write time changes when a file is updated */
+    memset(buffer, 0xff, sizeof(buffer));
+    ret = pGetFileInformationByHandleEx(file, FileBasicInfo, buffer, sizeof(buffer));
+    ok(ret, "GetFileInformationByHandleEx: failed to get FileBasicInfo, %u\n", GetLastError());
+    basicInfo = (FILE_BASIC_INFO *)buffer;
+    prevWrite = basicInfo->LastWriteTime;
+    CloseHandle(file);
+
+    Sleep(30); /* Make sure a new write time is different from the previous */
+
+    /* Write something to the file, to make sure the write time has changed */
+    file = CreateFileA(tempFileName, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+        NULL, OPEN_EXISTING, 0, NULL);
+    ok(file != INVALID_HANDLE_VALUE, "GetFileInformationByHandleEx: failed to open the temp file, "
+        "got error %u.\n", GetLastError());
+    ret = WriteFile(file, tempFileName, strlen(tempFileName), &written, NULL);
+    ok(ret, "GetFileInformationByHandleEx: Write failed\n");
+    CloseHandle(file);
+
+    file = CreateFileA(tempFileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+        NULL, OPEN_EXISTING, 0, NULL);
+    ok(file != INVALID_HANDLE_VALUE, "GetFileInformationByHandleEx: failed to open the temp file, "
+        "got error %u.\n", GetLastError());
+
+    memset(buffer, 0xff, sizeof(buffer));
+    ret = pGetFileInformationByHandleEx(file, FileBasicInfo, buffer, sizeof(buffer));
+    ok(ret, "GetFileInformationByHandleEx: failed to get FileBasicInfo, %u\n", GetLastError());
+    basicInfo = (FILE_BASIC_INFO *)buffer;
+    /* Could also check that the creation time didn't change - on windows
+     * it doesn't, but on wine, it does change even if it shouldn't. */
+    ok(basicInfo->LastWriteTime.QuadPart != prevWrite.QuadPart,
+        "GetFileInformationByHandleEx: last write time didn't change\n");
+
+    /* Test FileStandardInfo, check some basic parameters */
+    memset(buffer, 0xff, sizeof(buffer));
+    ret = pGetFileInformationByHandleEx(file, FileStandardInfo, buffer, sizeof(buffer));
+    ok(ret, "GetFileInformationByHandleEx: failed to get FileStandardInfo, %u\n", GetLastError());
+    standardInfo = (FILE_STANDARD_INFO *)buffer;
+    ok(standardInfo->NumberOfLinks == 1, "GetFileInformationByHandleEx: Unexpcted number of links\n");
+    ok(standardInfo->DeletePending == FALSE, "GetFileInformationByHandleEx: Unexpcted pending delete\n");
+    ok(standardInfo->Directory == FALSE, "GetFileInformationByHandleEx: Incorrect directory flag\n");
+
+    /* Test FileNameInfo */
+    memset(buffer, 0xff, sizeof(buffer));
+    ret = pGetFileInformationByHandleEx(file, FileNameInfo, buffer, sizeof(buffer));
+    ok(ret, "GetFileInformationByHandleEx: failed to get FileNameInfo, %u\n", GetLastError());
+    nameInfo = (FILE_NAME_INFO *)buffer;
+    strPtr = strchr(tempFileName, '\\');
+    ok(strPtr != NULL, "GetFileInformationByHandleEx: Temp filename didn't contain backslash\n");
+    ok(nameInfo->FileNameLength == strlen(strPtr) * 2,
+        "GetFileInformationByHandleEx: Incorrect file name length\n");
+    for (i = 0; i < nameInfo->FileNameLength/2; i++)
+        ok(strPtr[i] == nameInfo->FileName[i], "Incorrect filename char %d: %c vs %c\n",
+            i, strPtr[i], nameInfo->FileName[i]);
+
+    /* invalid classes */
+    SetLastError(0xdeadbeef);
+    ret = pGetFileInformationByHandleEx(file, FileEndOfFileInfo, &eofinfo, sizeof(eofinfo));
+    ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER, "got %d, error %d\n", ret, GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = pGetFileInformationByHandleEx(file, FileIoPriorityHintInfo, &priohintinfo, sizeof(priohintinfo));
+    ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER, "got %d, error %d\n", ret, GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = pGetFileInformationByHandleEx(file, FileAllocationInfo, &allocinfo, sizeof(allocinfo));
+    ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER, "got %d, error %d\n", ret, GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = pGetFileInformationByHandleEx(file, FileDispositionInfo, &dispinfo, sizeof(dispinfo));
+    ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER, "got %d, error %d\n", ret, GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = pGetFileInformationByHandleEx(file, FileRenameInfo, &renameinfo, sizeof(renameinfo));
+    ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER, "got %d, error %d\n", ret, GetLastError());
+
+    CloseHandle(file);
     DeleteFileA(tempFileName);
 }
 
@@ -4186,201 +4417,261 @@ todo_wine
     }
 }
 
-
 static void test_GetFinalPathNameByHandleA(void)
 {
     static char prefix[] = "GetFinalPathNameByHandleA";
     static char dos_prefix[] = "\\\\?\\";
     char temp_path[MAX_PATH], test_path[MAX_PATH];
     char long_path[MAX_PATH], result_path[MAX_PATH];
-    char dos_path[sizeof(dos_prefix) + MAX_PATH];
-    HANDLE hFile;
+    char dos_path[MAX_PATH + sizeof(dos_prefix)];
+    HANDLE file;
     DWORD count;
     UINT ret;
 
     if (!pGetFinalPathNameByHandleA)
     {
-        win_skip("GetFinalPathNameByHandleA is missing\n");
+        skip("GetFinalPathNameByHandleA is missing\n");
         return;
     }
 
     /* Test calling with INVALID_HANDLE_VALUE */
     SetLastError(0xdeadbeaf);
     count = pGetFinalPathNameByHandleA(INVALID_HANDLE_VALUE, result_path, MAX_PATH, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
-    ok(count == 0, "Expected length 0, got %d\n", count);
-    ok(GetLastError() == ERROR_INVALID_HANDLE, "Expected ERROR_INVALID_HANDLE, got %x\n", GetLastError());
+    ok(count == 0, "Expected length 0, got %u\n", count);
+    ok(GetLastError() == ERROR_INVALID_HANDLE, "Expected ERROR_INVALID_HANDLE, got %u\n", GetLastError());
 
     count = GetTempPathA(MAX_PATH, temp_path);
-    ok(count, "Failed to get temp path, error %x\n", GetLastError());
-    if (!count) return;
-
+    ok(count, "Failed to get temp path, error %u\n", GetLastError());
     ret = GetTempFileNameA(temp_path, prefix, 0, test_path);
-    ok(ret != 0, "GetTempFileNameA error %x\n", GetLastError());
-    if (!ret) return;
-
+    ok(ret != 0, "GetTempFileNameA error %u\n", GetLastError());
     ret = GetLongPathNameA(test_path, long_path, MAX_PATH);
-    ok(ret != 0, "GetLongPathNameA error %x\n", GetLastError());
-    if (!ret) return;
-
-    hFile = CreateFileA(test_path, GENERIC_READ | GENERIC_WRITE, 0, NULL,
-                        CREATE_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, 0);
-    ok(hFile != INVALID_HANDLE_VALUE, "CreateFileA error %x\n", GetLastError());
-    if (hFile == INVALID_HANDLE_VALUE) return;
-
-    dos_path[0] = 0;
-    strcat(dos_path, dos_prefix);
+    ok(ret != 0, "GetLongPathNameA error %u\n", GetLastError());
+    strcpy(dos_path, dos_prefix);
     strcat(dos_path, long_path);
 
+    file = CreateFileA(test_path, GENERIC_READ | GENERIC_WRITE, 0, NULL,
+                       CREATE_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, 0);
+    ok(file != INVALID_HANDLE_VALUE, "CreateFileA error %u\n", GetLastError());
+
     /* Test VOLUME_NAME_DOS with sufficient buffer size */
     memset(result_path, 0x11, sizeof(result_path));
-    count = pGetFinalPathNameByHandleA(hFile, result_path, MAX_PATH, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
+    count = pGetFinalPathNameByHandleA(file, result_path, MAX_PATH, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
     ok(count == strlen(dos_path), "Expected length %u, got %u\n", (DWORD)strlen(dos_path), count);
-    if (count && count <= MAX_PATH)
-        ok(lstrcmpiA(dos_path, result_path) == 0, "Expected %s, got %s\n", dos_path, result_path);
+    ok(lstrcmpiA(dos_path, result_path) == 0, "Expected %s, got %s\n", dos_path, result_path);
 
     /* Test VOLUME_NAME_DOS with insufficient buffer size */
     memset(result_path, 0x11, sizeof(result_path));
-    count = pGetFinalPathNameByHandleA(hFile, result_path, strlen(dos_path)-2, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
+    count = pGetFinalPathNameByHandleA(file, result_path, strlen(dos_path)-2, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
     ok(count == strlen(dos_path), "Expected length %u, got %u\n", (DWORD)strlen(dos_path), count);
     ok(result_path[0] == 0x11, "Result path was modified\n");
 
     memset(result_path, 0x11, sizeof(result_path));
-    count = pGetFinalPathNameByHandleA(hFile, result_path, strlen(dos_path)-1, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
+    count = pGetFinalPathNameByHandleA(file, result_path, strlen(dos_path)-1, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
     ok(count == strlen(dos_path), "Expected length %u, got %u\n", (DWORD)strlen(dos_path), count);
     ok(result_path[0] == 0x11, "Result path was modified\n");
 
     memset(result_path, 0x11, sizeof(result_path));
-    count = pGetFinalPathNameByHandleA(hFile, result_path, strlen(dos_path), FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
+    count = pGetFinalPathNameByHandleA(file, result_path, strlen(dos_path), FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
     ok(count == strlen(dos_path), "Expected length %u, got %u\n", (DWORD)strlen(dos_path), count);
     ok(result_path[0] == 0x11, "Result path was modified\n");
 
     memset(result_path, 0x11, sizeof(result_path));
-    count = pGetFinalPathNameByHandleA(hFile, result_path, strlen(dos_path)+1, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
+    count = pGetFinalPathNameByHandleA(file, result_path, strlen(dos_path)+1, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
     ok(count == strlen(dos_path), "Expected length %u, got %u\n", (DWORD)strlen(dos_path), count);
     ok(result_path[0] != 0x11, "Result path was not modified\n");
+    ok(!result_path[strlen(dos_path)], "Expected nullterminated string\n");
     ok(result_path[strlen(dos_path)+1] == 0x11, "Buffer overflow\n");
 
-    CloseHandle(hFile);
+    CloseHandle(file);
 }
 
 static void test_GetFinalPathNameByHandleW(void)
 {
-    static WCHAR prefix[] = {'G','e','t','F','i','n','a','l','P','a','t','h','N','a','m','e','B','y','H','a','n','d','l','e','W','\0'};
+    static WCHAR prefix[] = {'G','e','t','F','i','n','a','l','P','a','t','h',
+                             'N','a','m','e','B','y','H','a','n','d','l','e','W','\0'};
     static WCHAR dos_prefix[] = {'\\','\\','?','\\','\0'};
     WCHAR temp_path[MAX_PATH], test_path[MAX_PATH];
     WCHAR long_path[MAX_PATH], result_path[MAX_PATH];
     WCHAR dos_path[MAX_PATH + sizeof(dos_prefix)];
     WCHAR drive_part[MAX_PATH];
     WCHAR *file_part;
-    WCHAR volume_path[MAX_PATH+50];
-    WCHAR nt_path[2*MAX_PATH];
-    HANDLE hFile;
+    WCHAR volume_path[MAX_PATH + 50];
+    WCHAR nt_path[2 * MAX_PATH];
+    BOOL success;
+    HANDLE file;
     DWORD count;
     UINT ret;
 
     if (!pGetFinalPathNameByHandleW)
     {
-        win_skip("GetFinalPathNameByHandleW is missing\n");
+        skip("GetFinalPathNameByHandleW is missing\n");
         return;
     }
 
     /* Test calling with INVALID_HANDLE_VALUE */
     SetLastError(0xdeadbeaf);
     count = pGetFinalPathNameByHandleW(INVALID_HANDLE_VALUE, result_path, MAX_PATH, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
-    ok(count == 0, "Expected length 0, got %d\n", count);
-    ok(GetLastError() == ERROR_INVALID_HANDLE, "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError());
+    ok(count == 0, "Expected length 0, got %u\n", count);
+    ok(GetLastError() == ERROR_INVALID_HANDLE, "Expected ERROR_INVALID_HANDLE, got %u\n", GetLastError());
 
     count = GetTempPathW(MAX_PATH, temp_path);
-    ok(count, "Failed to get temp path, error %d\n", GetLastError());
-    if (!count) return;
-
+    ok(count, "Failed to get temp path, error %u\n", GetLastError());
     ret = GetTempFileNameW(temp_path, prefix, 0, test_path);
-    ok(ret != 0, "GetTempFileNameW error %d\n", GetLastError());
-    if (!ret) return;
-
+    ok(ret != 0, "GetTempFileNameW error %u\n", GetLastError());
     ret = GetLongPathNameW(test_path, long_path, MAX_PATH);
-    ok(ret != 0, "GetLongPathNameW error %d\n", GetLastError());
-    if (!ret) return;
-
-    hFile = CreateFileW(test_path, GENERIC_READ | GENERIC_WRITE, 0, NULL,
-                        CREATE_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, 0);
-    ok(hFile != INVALID_HANDLE_VALUE, "CreateFileW error %d\n", GetLastError());
-    if (hFile == INVALID_HANDLE_VALUE) return;
-
-    dos_path[0] = 0;
-    lstrcatW(dos_path, dos_prefix);
+    ok(ret != 0, "GetLongPathNameW error %u\n", GetLastError());
+    lstrcpyW(dos_path, dos_prefix);
     lstrcatW(dos_path, long_path);
 
+    file = CreateFileW(test_path, GENERIC_READ | GENERIC_WRITE, 0, NULL,
+                       CREATE_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, 0);
+    ok(file != INVALID_HANDLE_VALUE, "CreateFileW error %u\n", GetLastError());
+
     /* Test VOLUME_NAME_DOS with sufficient buffer size */
     memset(result_path, 0x11, sizeof(result_path));
-    count = pGetFinalPathNameByHandleW(hFile, result_path, MAX_PATH, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
-    ok(count == lstrlenW(dos_path), "Expected length %d, got %d\n", lstrlenW(dos_path), count);
-    if (count && count <= MAX_PATH)
-        ok(lstrcmpiW(dos_path, result_path) == 0, "Expected %s, got %s\n", wine_dbgstr_w(dos_path), wine_dbgstr_w(result_path));
+    count = pGetFinalPathNameByHandleW(file, result_path, MAX_PATH, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
+    ok(count == lstrlenW(dos_path), "Expected length %u, got %u\n", lstrlenW(dos_path), count);
+    ok(lstrcmpiW(dos_path, result_path) == 0, "Expected %s, got %s\n", wine_dbgstr_w(dos_path), wine_dbgstr_w(result_path));
 
     /* Test VOLUME_NAME_DOS with insufficient buffer size */
     memset(result_path, 0x11, sizeof(result_path));
-    count = pGetFinalPathNameByHandleW(hFile, result_path, lstrlenW(dos_path)-1, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
-    ok(count == lstrlenW(dos_path) + 1, "Expected length %d, got %d\n", lstrlenW(dos_path) + 1, count);
+    count = pGetFinalPathNameByHandleW(file, result_path, lstrlenW(dos_path)-1, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
+    ok(count == lstrlenW(dos_path) + 1, "Expected length %u, got %u\n", lstrlenW(dos_path) + 1, count);
     ok(result_path[0] == 0x1111, "Result path was modified\n");
 
     memset(result_path, 0x11, sizeof(result_path));
-    count = pGetFinalPathNameByHandleW(hFile, result_path, lstrlenW(dos_path), FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
-    ok(count == lstrlenW(dos_path) + 1, "Expected length %d, got %d\n", lstrlenW(dos_path) + 1, count);
+    count = pGetFinalPathNameByHandleW(file, result_path, lstrlenW(dos_path), FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
+    ok(count == lstrlenW(dos_path) + 1, "Expected length %u, got %u\n", lstrlenW(dos_path) + 1, count);
     ok(result_path[0] == 0x1111, "Result path was modified\n");
 
     memset(result_path, 0x11, sizeof(result_path));
-    count = pGetFinalPathNameByHandleW(hFile, result_path, lstrlenW(dos_path)+1, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
-    ok(count == lstrlenW(dos_path), "Expected length %d, got %d\n", lstrlenW(dos_path), count);
+    count = pGetFinalPathNameByHandleW(file, result_path, lstrlenW(dos_path)+1, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
+    ok(count == lstrlenW(dos_path), "Expected length %u, got %u\n", lstrlenW(dos_path), count);
     ok(result_path[0] != 0x1111, "Result path was not modified\n");
+    ok(!result_path[lstrlenW(dos_path)], "Expected nullterminated string\n");
     ok(result_path[lstrlenW(dos_path)+1] == 0x1111, "Buffer overflow\n");
 
-    if (!GetVolumePathNameW(long_path, drive_part, MAX_PATH))
-    {
-        ok(0, "Failed to get drive part, error: %d\n", GetLastError());
-        CloseHandle(hFile);
-        return;
-    }
+    success = GetVolumePathNameW(long_path, drive_part, MAX_PATH);
+    ok(success, "GetVolumePathNameW error %u\n", GetLastError());
+    success = GetVolumeNameForVolumeMountPointW(drive_part, volume_path, sizeof(volume_path) / sizeof(WCHAR));
+    ok(success, "GetVolumeNameForVolumeMountPointW error %u\n", GetLastError());
 
-    if (!GetVolumeNameForVolumeMountPointW(drive_part, volume_path, sizeof(volume_path) / sizeof(WCHAR)))
-        ok(0, "GetVolumeNameForVolumeMountPointW failed, error: %d\n", GetLastError());
-    else
-    {
-        /* Test for VOLUME_NAME_GUID */
-        lstrcatW(volume_path, long_path + lstrlenW(drive_part));
-        memset(result_path, 0x11, sizeof(result_path));
-        count = pGetFinalPathNameByHandleW(hFile, result_path, MAX_PATH, FILE_NAME_NORMALIZED | VOLUME_NAME_GUID);
-        ok(count == lstrlenW(volume_path), "Expected length %d, got %d\n", lstrlenW(volume_path), count);
-        if (count && count <= MAX_PATH)
-            ok(lstrcmpiW(volume_path, result_path) == 0, "Expected %s, got %s\n",
-               wine_dbgstr_w(volume_path), wine_dbgstr_w(result_path));
-    }
+    /* Test for VOLUME_NAME_GUID */
+    lstrcatW(volume_path, long_path + lstrlenW(drive_part));
+    memset(result_path, 0x11, sizeof(result_path));
+    count = pGetFinalPathNameByHandleW(file, result_path, MAX_PATH, FILE_NAME_NORMALIZED | VOLUME_NAME_GUID);
+    ok(count == lstrlenW(volume_path), "Expected length %u, got %u\n", lstrlenW(volume_path), count);
+    ok(lstrcmpiW(volume_path, result_path) == 0, "Expected %s, got %s\n",
+       wine_dbgstr_w(volume_path), wine_dbgstr_w(result_path));
 
     /* Test for VOLUME_NAME_NONE */
     file_part = long_path + lstrlenW(drive_part) - 1;
     memset(result_path, 0x11, sizeof(result_path));
-    count = pGetFinalPathNameByHandleW(hFile, result_path, MAX_PATH, FILE_NAME_NORMALIZED | VOLUME_NAME_NONE);
-    ok(count == lstrlenW(file_part), "Expected length %d, got %d\n", lstrlenW(file_part), count);
-    if (count && count <= MAX_PATH)
-        ok(lstrcmpiW(file_part, result_path) == 0, "Expected %s, got %s\n",
-           wine_dbgstr_w(file_part), wine_dbgstr_w(result_path));
+    count = pGetFinalPathNameByHandleW(file, result_path, MAX_PATH, FILE_NAME_NORMALIZED | VOLUME_NAME_NONE);
+    ok(count == lstrlenW(file_part), "Expected length %u, got %u\n", lstrlenW(file_part), count);
+    ok(lstrcmpiW(file_part, result_path) == 0, "Expected %s, got %s\n",
+       wine_dbgstr_w(file_part), wine_dbgstr_w(result_path));
 
     drive_part[lstrlenW(drive_part)-1] = 0;
-    if (!QueryDosDeviceW(drive_part, nt_path, sizeof(nt_path) / sizeof(WCHAR)))
-        ok(0, "QueryDosDeviceW failed, error: %d\n", GetLastError());
-    else
+    success = QueryDosDeviceW(drive_part, nt_path, sizeof(nt_path) / sizeof(WCHAR));
+    ok(success, "QueryDosDeviceW error %u\n", GetLastError());
+
+    /* Test for VOLUME_NAME_NT */
+    lstrcatW(nt_path, file_part);
+    memset(result_path, 0x11, sizeof(result_path));
+    count = pGetFinalPathNameByHandleW(file, result_path, MAX_PATH, FILE_NAME_NORMALIZED | VOLUME_NAME_NT);
+    ok(count == lstrlenW(nt_path), "Expected length %u, got %u\n", lstrlenW(nt_path), count);
+    ok(lstrcmpiW(nt_path, result_path) == 0, "Expected %s, got %s\n",
+       wine_dbgstr_w(nt_path), wine_dbgstr_w(result_path));
+
+    CloseHandle(file);
+}
+
+static void test_SetFileInformationByHandle(void)
+{
+    FILE_ATTRIBUTE_TAG_INFO fileattrinfo = { 0 };
+    FILE_REMOTE_PROTOCOL_INFO protinfo = { 0 };
+    FILE_STANDARD_INFO stdinfo;
+    FILE_COMPRESSION_INFO compressinfo;
+    FILE_DISPOSITION_INFO dispinfo;
+    char tempFileName[MAX_PATH];
+    char tempPath[MAX_PATH];
+    HANDLE file;
+    BOOL ret;
+
+    if (!pSetFileInformationByHandle)
     {
-        /* Test for VOLUME_NAME_NT */
-        lstrcatW(nt_path, file_part);
-        memset(result_path, 0x11, sizeof(result_path));
-        count = pGetFinalPathNameByHandleW(hFile, result_path, MAX_PATH, FILE_NAME_NORMALIZED | VOLUME_NAME_NT);
-        ok(count == lstrlenW(nt_path), "Expected length %d, got %d\n", lstrlenW(nt_path), count);
-        if (count && count <= MAX_PATH)
-            ok(lstrcmpiW(nt_path, result_path) == 0, "Expected %s, got %s\n",
-               wine_dbgstr_w(nt_path), wine_dbgstr_w(result_path));
+        win_skip("SetFileInformationByHandle is not supported\n");
+        return;
     }
 
-    CloseHandle(hFile);
+    ret = GetTempPathA(sizeof(tempPath), tempPath);
+    ok(ret, "GetTempPathA failed, got error %u.\n", GetLastError());
+
+    /* ensure the existence of a file in the temp folder */
+    ret = GetTempFileNameA(tempPath, "abc", 0, tempFileName);
+    ok(ret, "GetTempFileNameA failed, got error %u.\n", GetLastError());
+
+    file = CreateFileA(tempFileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+        NULL, OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, NULL);
+    ok(file != INVALID_HANDLE_VALUE, "failed to open the temp file, error %u.\n", GetLastError());
+
+    /* invalid classes */
+    SetLastError(0xdeadbeef);
+    ret = pSetFileInformationByHandle(file, FileStandardInfo, &stdinfo, sizeof(stdinfo));
+    ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER, "got %d, error %d\n", ret, GetLastError());
+
+    memset(&compressinfo, 0, sizeof(compressinfo));
+    SetLastError(0xdeadbeef);
+    ret = pSetFileInformationByHandle(file, FileCompressionInfo, &compressinfo, sizeof(compressinfo));
+    ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER, "got %d, error %d\n", ret, GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = pSetFileInformationByHandle(file, FileAttributeTagInfo, &fileattrinfo, sizeof(fileattrinfo));
+    ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER, "got %d, error %d\n", ret, GetLastError());
+
+    memset(&protinfo, 0, sizeof(protinfo));
+    protinfo.StructureVersion = 1;
+    protinfo.StructureSize = sizeof(protinfo);
+    SetLastError(0xdeadbeef);
+    ret = pSetFileInformationByHandle(file, FileRemoteProtocolInfo, &protinfo, sizeof(protinfo));
+    ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER, "got %d, error %d\n", ret, GetLastError());
+
+    /* test FileDispositionInfo, additional details already covered by ntdll tests */
+    SetLastError(0xdeadbeef);
+    ret = pSetFileInformationByHandle(file, FileDispositionInfo, &dispinfo, 0);
+todo_wine
+    ok(!ret && GetLastError() == ERROR_BAD_LENGTH, "got %d, error %d\n", ret, GetLastError());
+
+    dispinfo.DeleteFile = TRUE;
+    ret = pSetFileInformationByHandle(file, FileDispositionInfo, &dispinfo, sizeof(dispinfo));
+    ok(ret, "setting FileDispositionInfo failed, error %d\n", GetLastError());
+
+    CloseHandle(file);
+}
+
+static void test_GetFileAttributesExW(void)
+{
+    static const WCHAR path1[] = {'\\','\\','?','\\',0};
+    static const WCHAR path2[] = {'\\','?','?','\\',0};
+    static const WCHAR path3[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\',0};
+    WIN32_FILE_ATTRIBUTE_DATA info;
+    BOOL ret;
+
+    SetLastError(0xdeadbeef);
+    ret = GetFileAttributesExW(path1, GetFileExInfoStandard, &info);
+    ok(!ret, "GetFileAttributesExW succeeded\n");
+    ok(GetLastError() == ERROR_INVALID_NAME, "Expected error ERROR_INVALID_NAME, got %u\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = GetFileAttributesExW(path2, GetFileExInfoStandard, &info);
+    ok(!ret, "GetFileAttributesExW succeeded\n");
+    ok(GetLastError() == ERROR_INVALID_NAME, "Expected error ERROR_INVALID_NAME, got %u\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = GetFileAttributesExW(path3, GetFileExInfoStandard, &info);
+    ok(!ret, "GetFileAttributesExW succeeded\n");
+    ok(GetLastError() == ERROR_FILE_NOT_FOUND, "Expected error ERROR_FILE_NOT_FOUND, got %u\n", GetLastError());
 }
 
 START_TEST(file)
@@ -4399,6 +4690,7 @@ START_TEST(file)
     test_CopyFileA();
     test_CopyFileW();
     test_CopyFile2();
+    test_CopyFileEx();
     test_CreateFile();
     test_CreateFileA();
     test_CreateFileW();
@@ -4437,4 +4729,6 @@ START_TEST(file)
     test_file_access();
     test_GetFinalPathNameByHandleA();
     test_GetFinalPathNameByHandleW();
+    test_SetFileInformationByHandle();
+    test_GetFileAttributesExW();
 }
index b7c1285..d8ac787 100755 (executable)
@@ -39,6 +39,7 @@
 #define HEAP_VALIDATE_PARAMS  0x40000000
 
 static BOOL (WINAPI *pHeapQueryInformation)(HANDLE, HEAP_INFORMATION_CLASS, PVOID, SIZE_T, PSIZE_T);
+static BOOL (WINAPI *pGetPhysicallyInstalledSystemMemory)(ULONGLONG *);
 static ULONG (WINAPI *pRtlGetNtGlobalFlags)(void);
 
 struct heap_layout
@@ -1145,6 +1146,38 @@ static void test_child_heap( const char *arg )
     test_heap_checks( expect_heap );
 }
 
+static void test_GetPhysicallyInstalledSystemMemory(void)
+{
+    HMODULE kernel32 = GetModuleHandleA("kernel32.dll");
+    MEMORYSTATUSEX memstatus;
+    ULONGLONG total_memory;
+    BOOL ret;
+
+    pGetPhysicallyInstalledSystemMemory = (void *)GetProcAddress(kernel32, "GetPhysicallyInstalledSystemMemory");
+    if (!pGetPhysicallyInstalledSystemMemory)
+    {
+        win_skip("GetPhysicallyInstalledSystemMemory is not available\n");
+        return;
+    }
+
+    SetLastError(0xdeadbeef);
+    ret = pGetPhysicallyInstalledSystemMemory(NULL);
+    ok(!ret, "GetPhysicallyInstalledSystemMemory should fail\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER,
+       "expected ERROR_INVALID_PARAMETER, got %u\n", GetLastError());
+
+    total_memory = 0;
+    ret = pGetPhysicallyInstalledSystemMemory(&total_memory);
+    ok(ret, "GetPhysicallyInstalledSystemMemory unexpectedly failed\n");
+    ok(total_memory != 0, "expected total_memory != 0\n");
+
+    memstatus.dwLength = sizeof(memstatus);
+    ret = GlobalMemoryStatusEx(&memstatus);
+    ok(ret, "GlobalMemoryStatusEx unexpectedly failed\n");
+    ok(total_memory >= memstatus.ullTotalPhys / 1024,
+       "expected total_memory >= memstatus.ullTotalPhys / 1024\n");
+}
+
 START_TEST(heap)
 {
     int argc;
@@ -1172,7 +1205,9 @@ START_TEST(heap)
     test_sized_HeapReAlloc(1, (1 << 20));
     test_sized_HeapReAlloc((1 << 20), (2 << 20));
     test_sized_HeapReAlloc((1 << 20), 1);
+
     test_HeapQueryInformation();
+    test_GetPhysicallyInstalledSystemMemory();
 
     if (pRtlGetNtGlobalFlags)
     {
index e7dff44..be560fd 100644 (file)
@@ -68,6 +68,7 @@ static void (WINAPI *pRtlReleasePebLock)(void);
 static PVOID    (WINAPI *pResolveDelayLoadedAPI)(PVOID, PCIMAGE_DELAYLOAD_DESCRIPTOR,
                                                  PDELAYLOAD_FAILURE_DLL_CALLBACK, PVOID,
                                                  PIMAGE_THUNK_DATA ThunkAddress,ULONG);
+static PVOID (WINAPI *pRtlImageDirectoryEntryToData)(HMODULE,BOOL,WORD,ULONG *);
 
 static PVOID RVAToAddr(DWORD_PTR rva, HMODULE module)
 {
@@ -374,6 +375,16 @@ static void test_Loader(void)
           1,
           0,
           { ERROR_SUCCESS, ERROR_BAD_EXE_FORMAT } /* vista is more strict */
+        },
+        /* Minimal PE image that Windows7 is able to load: 268 bytes */
+        { 0x04,
+          0, 0xf0, /* optional header size just forces 0xf0 bytes to be written,
+                      0 or another number don't change the behaviour, what really
+                      matters is file size regardless of values in the headers */
+          0x04 /* also serves as e_lfanew in the truncated MZ header */, 0x04,
+          0x40, /* minimal image size that Windows7 accepts */
+          0,
+          { ERROR_SUCCESS }
         }
     };
     int i;
@@ -788,15 +799,6 @@ static void test_image_mapping(const char *dll_name, DWORD scn_page_access, BOOL
     size = 0;
     status = pNtMapViewOfSection(hmap, GetCurrentProcess(), &addr2, 0, 0, &offset,
                                  &size, 1 /* ViewShare */, 0, PAGE_READONLY);
-    /* FIXME: remove once Wine is fixed */
-    if (status != STATUS_IMAGE_NOT_AT_BASE)
-    {
-        todo_wine {
-        ok(status == STATUS_IMAGE_NOT_AT_BASE, "expected STATUS_IMAGE_NOT_AT_BASE, got %x\n", status);
-        ok(addr2 != 0, "mapped address should be valid\n");
-        }
-        goto wine_is_broken;
-    }
     ok(status == STATUS_IMAGE_NOT_AT_BASE, "expected STATUS_IMAGE_NOT_AT_BASE, got %x\n", status);
     ok(addr2 != 0, "mapped address should be valid\n");
     ok(addr2 != addr1, "mapped addresses should be different\n");
@@ -850,7 +852,6 @@ static void test_image_mapping(const char *dll_name, DWORD scn_page_access, BOOL
         ok(ret, "FreeLibrary error %d\n", GetLastError());
     }
 
-wine_is_broken:
     status = pNtUnmapViewOfSection(GetCurrentProcess(), addr1);
     ok(status == STATUS_SUCCESS, "NtUnmapViewOfSection error %x\n", status);
 
@@ -1411,7 +1412,8 @@ static DWORD WINAPI semaphore_thread_proc(void *param)
 
     while (1)
     {
-        trace("%04u: semaphore_thread_proc: still alive\n", GetCurrentThreadId());
+        if (winetest_debug > 1)
+            trace("%04u: semaphore_thread_proc: still alive\n", GetCurrentThreadId());
         if (WaitForSingleObject(stop_event, 50) != WAIT_TIMEOUT) break;
     }
 
@@ -1549,7 +1551,7 @@ static BOOL WINAPI dll_entry_point(HINSTANCE hinst, DWORD reason, LPVOID param)
             ok(ret == WAIT_OBJECT_0, "expected WAIT_OBJECT_0, got %#x\n", ret);
         }
 
-        /* win7 doesn't allow to create a thread during process shutdown,
+        /* win7 doesn't allow creating a thread during process shutdown but
          * earlier Windows versions allow it.
          */
         noop_thread_started = 0;
@@ -1678,8 +1680,8 @@ todo_wine
         trace("dll: %p, DLL_THREAD_DETACH, %p\n", hinst, param);
 
         ret = pRtlDllShutdownInProgress();
-        /* win7 doesn't allow to create a thread during process shutdown,
-         * earlier Windows versions allow it, and DLL_THREAD_DETACH is
+        /* win7 doesn't allow creating a thread during process shutdown but
+         * earlier Windows versions allow it. In that case DLL_THREAD_DETACH is
          * sent on thread exit, but DLL_THREAD_ATTACH is never received.
          */
         if (noop_thread_started)
@@ -2702,7 +2704,7 @@ static void test_ResolveDelayLoadedAPI(void)
         return;
     }
 
-    delaydir = RtlImageDirectoryEntryToData(hlib, TRUE, IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT, &file_size);
+    delaydir = pRtlImageDirectoryEntryToData(hlib, TRUE, IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT, &file_size);
     if (!delaydir)
     {
         skip("haven't found section for delay import directory.\n");
@@ -2782,6 +2784,7 @@ START_TEST(loader)
     pLdrUnlockLoaderLock = (void *)GetProcAddress(ntdll, "LdrUnlockLoaderLock");
     pRtlAcquirePebLock = (void *)GetProcAddress(ntdll, "RtlAcquirePebLock");
     pRtlReleasePebLock = (void *)GetProcAddress(ntdll, "RtlReleasePebLock");
+    pRtlImageDirectoryEntryToData = (void *)GetProcAddress(ntdll, "RtlImageDirectoryEntryToData");
     pResolveDelayLoadedAPI = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "ResolveDelayLoadedAPI");
 
     GetSystemInfo( &si );
index efd9e67..2a81f6d 100755 (executable)
@@ -1637,7 +1637,13 @@ static const struct comparestringa_entry comparestringa_data[] = {
   { LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "'o", -1, "/m", -1, CSTR_LESS_THAN },
   { LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "/m", -1, "'o", -1, CSTR_GREATER_THAN },
   { LOCALE_SYSTEM_DEFAULT, 0, "aLuZkUtZ", 8, "aLuZkUtZ", 9, CSTR_EQUAL },
-  { LOCALE_SYSTEM_DEFAULT, 0, "aLuZkUtZ", 7, "aLuZkUtZ\0A", 10, CSTR_LESS_THAN }
+  { LOCALE_SYSTEM_DEFAULT, 0, "aLuZkUtZ", 7, "aLuZkUtZ\0A", 10, CSTR_LESS_THAN },
+  { LOCALE_SYSTEM_DEFAULT, 0, "a-", 3, "a\0", 3, CSTR_GREATER_THAN },
+  { LOCALE_SYSTEM_DEFAULT, 0, "a'", 3, "a\0", 3, CSTR_GREATER_THAN },
+  { LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "a-", 3, "a\0", 3, CSTR_GREATER_THAN },
+  { LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "a'", 3, "a\0", 3, CSTR_GREATER_THAN },
+  { LOCALE_SYSTEM_DEFAULT, NORM_IGNORESYMBOLS, "a.", 3, "a\0", 3, CSTR_EQUAL },
+  { LOCALE_SYSTEM_DEFAULT, NORM_IGNORESYMBOLS, "a ", 3, "a\0", 3, CSTR_EQUAL },
 };
 
 static void test_CompareStringA(void)
@@ -2421,7 +2427,7 @@ static void test_LocaleNameToLCID(void)
     buffer[0] = 0;
     SetLastError(0xdeadbeef);
     lcid = pLocaleNameToLCID(LOCALE_NAME_INVARIANT, 0);
-    todo_wine ok(lcid == 0x7F, "Expected lcid = 0x7F, got %08x, error %d\n", lcid, GetLastError());
+    ok(lcid == 0x7F, "Expected lcid = 0x7F, got %08x, error %d\n", lcid, GetLastError());
     ret = pLCIDToLocaleName(lcid, buffer, LOCALE_NAME_MAX_LENGTH, 0);
     ok(ret > 0, "Expected ret > 0, got %d, error %d\n", ret, GetLastError());
     trace("%08x, %s\n", lcid, wine_dbgstr_w(buffer));
@@ -3135,13 +3141,17 @@ static void test_ConvertDefaultLocale(void)
   LCID_RES(LOCALE_SYSTEM_DEFAULT, GetSystemDefaultLCID());
   LCID_RES(LOCALE_USER_DEFAULT,   GetUserDefaultLCID());
   LCID_RES(LOCALE_NEUTRAL,        GetUserDefaultLCID());
+  lcid = ConvertDefaultLocale(LOCALE_INVARIANT);
+  ok(lcid == LOCALE_INVARIANT || broken(lcid == 0x47f) /* win2k[3]/winxp */,
+     "Expected lcid = %08x, got %08x\n", LOCALE_INVARIANT, lcid);
 }
 
 static BOOL CALLBACK langgrp_procA(LGRPID lgrpid, LPSTR lpszNum, LPSTR lpszName,
                                     DWORD dwFlags, LONG_PTR lParam)
 {
-  trace("%08x, %s, %s, %08x, %08lx\n",
-        lgrpid, lpszNum, lpszName, dwFlags, lParam);
+  if (winetest_debug > 1)
+    trace("%08x, %s, %s, %08x, %08lx\n",
+          lgrpid, lpszNum, lpszName, dwFlags, lParam);
 
   ok(pIsValidLanguageGroup(lgrpid, dwFlags) == TRUE,
      "Enumerated grp %d not valid (flags %d)\n", lgrpid, dwFlags);
@@ -3190,7 +3200,8 @@ static void test_EnumSystemLanguageGroupsA(void)
 
 static BOOL CALLBACK enum_func( LPWSTR name, DWORD flags, LPARAM lparam )
 {
-    trace( "%s %x\n", wine_dbgstr_w(name), flags );
+    if (winetest_debug > 1)
+        trace( "%s %x\n", wine_dbgstr_w(name), flags );
     return TRUE;
 }
 
@@ -3215,7 +3226,8 @@ static void test_EnumSystemLocalesEx(void)
 static BOOL CALLBACK lgrplocale_procA(LGRPID lgrpid, LCID lcid, LPSTR lpszNum,
                                       LONG_PTR lParam)
 {
-  trace("%08x, %08x, %s, %08lx\n", lgrpid, lcid, lpszNum, lParam);
+  if (winetest_debug > 1)
+    trace("%08x, %08x, %s, %08lx\n", lgrpid, lcid, lpszNum, lParam);
 
   /* invalid locale enumerated on some platforms */
   if (lcid == 0)
@@ -3296,7 +3308,8 @@ static void test_SetLocaleInfoA(void)
 
 static BOOL CALLBACK luilocale_proc1A(LPSTR value, LONG_PTR lParam)
 {
-  trace("%s %08lx\n", value, lParam);
+  if (winetest_debug > 1)
+    trace("%s %08lx\n", value, lParam);
   return(TRUE);
 }
 
@@ -3354,6 +3367,7 @@ static void test_EnumUILanguageA(void)
 }
 
 static char date_fmt_buf[1024];
+static WCHAR date_fmt_bufW[1024];
 
 static BOOL CALLBACK enum_datetime_procA(LPSTR fmt)
 {
@@ -3362,6 +3376,12 @@ static BOOL CALLBACK enum_datetime_procA(LPSTR fmt)
     return TRUE;
 }
 
+static BOOL CALLBACK enum_datetime_procW(WCHAR *fmt)
+{
+    lstrcatW(date_fmt_bufW, fmt);
+    return FALSE;
+}
+
 static void test_EnumDateFormatsA(void)
 {
     char *p, buf[256];
@@ -3469,6 +3489,57 @@ static void test_EnumTimeFormatsA(void)
     ok(!lstrcmpA(date_fmt_buf, buf), "expected \"%s\" got \"%s\"\n", date_fmt_buf, buf);
 }
 
+static void test_EnumTimeFormatsW(void)
+{
+    LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
+    WCHAR bufW[256];
+    BOOL ret;
+
+    date_fmt_bufW[0] = 0;
+    ret = EnumTimeFormatsW(enum_datetime_procW, lcid, 0);
+    ok(ret, "EnumTimeFormatsW(0) error %d\n", GetLastError());
+    ret = GetLocaleInfoW(lcid, LOCALE_STIMEFORMAT, bufW, sizeof(bufW)/sizeof(bufW[0]));
+    ok(ret, "GetLocaleInfoW(LOCALE_STIMEFORMAT) error %d\n", GetLastError());
+    ok(!lstrcmpW(date_fmt_bufW, bufW), "expected \"%s\" got \"%s\"\n", wine_dbgstr_w(date_fmt_bufW),
+        wine_dbgstr_w(bufW));
+
+    date_fmt_bufW[0] = 0;
+    ret = EnumTimeFormatsW(enum_datetime_procW, lcid, LOCALE_USE_CP_ACP);
+    ok(ret, "EnumTimeFormatsW(LOCALE_USE_CP_ACP) error %d\n", GetLastError());
+    ret = GetLocaleInfoW(lcid, LOCALE_STIMEFORMAT, bufW, sizeof(bufW)/sizeof(bufW[0]));
+    ok(ret, "GetLocaleInfoW(LOCALE_STIMEFORMAT) error %d\n", GetLastError());
+    ok(!lstrcmpW(date_fmt_bufW, bufW), "expected \"%s\" got \"%s\"\n", wine_dbgstr_w(date_fmt_bufW),
+        wine_dbgstr_w(bufW));
+
+    /* TIME_NOSECONDS is Win7+ feature */
+    date_fmt_bufW[0] = 0;
+    ret = EnumTimeFormatsW(enum_datetime_procW, lcid, TIME_NOSECONDS);
+    if (!ret && GetLastError() == ERROR_INVALID_FLAGS)
+        win_skip("EnumTimeFormatsW doesn't support TIME_NOSECONDS\n");
+    else {
+        char buf[256];
+
+        ok(ret, "EnumTimeFormatsW(TIME_NOSECONDS) error %d\n", GetLastError());
+        ret = GetLocaleInfoW(lcid, LOCALE_SSHORTTIME, bufW, sizeof(bufW)/sizeof(bufW[0]));
+        ok(ret, "GetLocaleInfoW(LOCALE_SSHORTTIME) error %d\n", GetLastError());
+        ok(!lstrcmpW(date_fmt_bufW, bufW), "expected \"%s\" got \"%s\"\n", wine_dbgstr_w(date_fmt_bufW),
+            wine_dbgstr_w(bufW));
+
+        /* EnumTimeFormatsA doesn't support this flag */
+        ret = EnumTimeFormatsA(enum_datetime_procA, lcid, TIME_NOSECONDS);
+        ok(!ret && GetLastError() == ERROR_INVALID_FLAGS, "EnumTimeFormatsA(TIME_NOSECONDS) ret %d, error %d\n", ret,
+            GetLastError());
+
+        ret = EnumTimeFormatsA(NULL, lcid, TIME_NOSECONDS);
+        ok(!ret && GetLastError() == ERROR_INVALID_FLAGS, "EnumTimeFormatsA(TIME_NOSECONDS) ret %d, error %d\n", ret,
+            GetLastError());
+
+        /* And it's not supported by GetLocaleInfoA either */
+        ret = GetLocaleInfoA(lcid, LOCALE_SSHORTTIME, buf, sizeof(buf)/sizeof(buf[0]));
+        ok(!ret && GetLastError() == ERROR_INVALID_FLAGS, "GetLocaleInfoA(LOCALE_SSHORTTIME) ret %d, error %d\n", ret,
+            GetLastError());
+    }
+}
 static void test_GetCPInfo(void)
 {
     BOOL ret;
@@ -3581,10 +3652,26 @@ static void test_GetStringTypeW(void)
     static const WCHAR space_special[] = {0x09, 0x0d, 0x85};
 
     WORD types[20];
+    BOOL ret;
     WCHAR ch;
-    BOOL res;
     int i;
 
+    /* NULL src */
+    SetLastError(0xdeadbeef);
+    ret = GetStringTypeW(CT_CTYPE1, NULL, 0, NULL);
+    ok(!ret, "got %d\n", ret);
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "got error %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = GetStringTypeW(CT_CTYPE1, NULL, 0, types);
+    ok(!ret, "got %d\n", ret);
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "got error %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = GetStringTypeW(CT_CTYPE1, NULL, 5, types);
+    ok(!ret, "got %d\n", ret);
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "got error %d\n", GetLastError());
+
     memset(types,0,sizeof(types));
     GetStringTypeW(CT_CTYPE1, blanks, 5, types);
     for (i = 0; i < 5; i++)
@@ -3640,15 +3727,6 @@ static void test_GetStringTypeW(void)
     for (i = 0; i < 3; i++)
         ok(types[i] & C1_SPACE || broken(types[i] == C1_CNTRL) || broken(types[i] == 0), "incorrect types returned for %x -> (%x does not have %x)\n",space_special[i], types[i], C1_SPACE );
 
-    for (i = -1; i < 3; i++)
-    {
-        SetLastError(0xdeadbeef);
-        memset(types, 0, sizeof(types));
-        res = GetStringTypeW(CT_CTYPE1, NULL, i, types);
-        ok(!res, "GetStringTypeW unexpectedly succeeded\n");
-        ok(GetLastError() == ERROR_INVALID_PARAMETER, "wrong error, got %u\n", GetLastError());
-    }
-
     /* surrogate pairs */
     ch = 0xd800;
     memset(types, 0, sizeof(types));
@@ -4083,6 +4161,8 @@ static void test_IsValidLocaleName(void)
     ok(!ret, "IsValidLocaleName should have failed\n");
     ret = pIsValidLocaleName(zzzzW);
     ok(!ret, "IsValidLocaleName should have failed\n");
+    ret = pIsValidLocaleName(LOCALE_NAME_INVARIANT);
+    ok(ret, "IsValidLocaleName failed\n");
 }
 
 static void test_CompareStringOrdinal(void)
@@ -4232,7 +4312,7 @@ static void test_GetGeoInfo(void)
 
     /* try invalid type value */
     SetLastError(0xdeadbeef);
-    ret = pGetGeoInfoA(203, GEO_PARENT + 1, NULL, 0, 0);
+    ret = pGetGeoInfoA(203, GEO_CURRENCYSYMBOL + 1, NULL, 0, 0);
     ok(ret == 0, "got %d\n", ret);
     ok(GetLastError() == ERROR_INVALID_FLAGS, "got %d\n", GetLastError());
 }
@@ -4304,11 +4384,196 @@ static void test_EnumSystemGeoID(void)
     }
 }
 
+struct invariant_entry {
+  const char *name;
+  int id;
+  const char *expect;
+};
+
+#define X(x)  #x, x
+static const struct invariant_entry invariant_list[] = {
+    { X(LOCALE_ILANGUAGE),                "007f" },
+    { X(LOCALE_SENGLANGUAGE),             "Invariant Language" },
+    { X(LOCALE_SABBREVLANGNAME),          "IVL" },
+    { X(LOCALE_SNATIVELANGNAME),          "Invariant Language" },
+    { X(LOCALE_ICOUNTRY),                 "1" },
+    { X(LOCALE_SENGCOUNTRY),              "Invariant Country" },
+    { X(LOCALE_SABBREVCTRYNAME),          "IVC" },
+    { X(LOCALE_SNATIVECTRYNAME),          "Invariant Country" },
+    { X(LOCALE_IDEFAULTLANGUAGE),         "0409" },
+    { X(LOCALE_IDEFAULTCOUNTRY),          "1" },
+    { X(LOCALE_IDEFAULTCODEPAGE),         "437" },
+    { X(LOCALE_IDEFAULTANSICODEPAGE),     "1252" },
+    { X(LOCALE_IDEFAULTMACCODEPAGE),      "10000" },
+    { X(LOCALE_SLIST),                    "," },
+    { X(LOCALE_IMEASURE),                 "0" },
+    { X(LOCALE_SDECIMAL),                 "." },
+    { X(LOCALE_STHOUSAND),                "," },
+    { X(LOCALE_SGROUPING),                "3;0" },
+    { X(LOCALE_IDIGITS),                  "2" },
+    { X(LOCALE_ILZERO),                   "1" },
+    { X(LOCALE_INEGNUMBER),               "1" },
+    { X(LOCALE_SNATIVEDIGITS),            "0123456789" },
+    { X(LOCALE_SCURRENCY),                "\x00a4" },
+    { X(LOCALE_SINTLSYMBOL),              "XDR" },
+    { X(LOCALE_SMONDECIMALSEP),           "." },
+    { X(LOCALE_SMONTHOUSANDSEP),          "," },
+    { X(LOCALE_SMONGROUPING),             "3;0" },
+    { X(LOCALE_ICURRDIGITS),              "2" },
+    { X(LOCALE_IINTLCURRDIGITS),          "2" },
+    { X(LOCALE_ICURRENCY),                "0" },
+    { X(LOCALE_INEGCURR),                 "0" },
+    { X(LOCALE_SDATE),                    "/" },
+    { X(LOCALE_STIME),                    ":" },
+    { X(LOCALE_SSHORTDATE),               "MM/dd/yyyy" },
+    { X(LOCALE_SLONGDATE),                "dddd, dd MMMM yyyy" },
+    { X(LOCALE_STIMEFORMAT),              "HH:mm:ss" },
+    { X(LOCALE_IDATE),                    "0" },
+    { X(LOCALE_ILDATE),                   "1" },
+    { X(LOCALE_ITIME),                    "1" },
+    { X(LOCALE_ITIMEMARKPOSN),            "0" },
+    { X(LOCALE_ICENTURY),                 "1" },
+    { X(LOCALE_ITLZERO),                  "1" },
+    { X(LOCALE_IDAYLZERO),                "1" },
+    { X(LOCALE_IMONLZERO),                "1" },
+    { X(LOCALE_S1159),                    "AM" },
+    { X(LOCALE_S2359),                    "PM" },
+    { X(LOCALE_ICALENDARTYPE),            "1" },
+    { X(LOCALE_IOPTIONALCALENDAR),        "0" },
+    { X(LOCALE_IFIRSTDAYOFWEEK),          "6" },
+    { X(LOCALE_IFIRSTWEEKOFYEAR),         "0" },
+    { X(LOCALE_SDAYNAME1),                "Monday" },
+    { X(LOCALE_SDAYNAME2),                "Tuesday" },
+    { X(LOCALE_SDAYNAME3),                "Wednesday" },
+    { X(LOCALE_SDAYNAME4),                "Thursday" },
+    { X(LOCALE_SDAYNAME5),                "Friday" },
+    { X(LOCALE_SDAYNAME6),                "Saturday" },
+    { X(LOCALE_SDAYNAME7),                "Sunday" },
+    { X(LOCALE_SABBREVDAYNAME1),          "Mon" },
+    { X(LOCALE_SABBREVDAYNAME2),          "Tue" },
+    { X(LOCALE_SABBREVDAYNAME3),          "Wed" },
+    { X(LOCALE_SABBREVDAYNAME4),          "Thu" },
+    { X(LOCALE_SABBREVDAYNAME5),          "Fri" },
+    { X(LOCALE_SABBREVDAYNAME6),          "Sat" },
+    { X(LOCALE_SABBREVDAYNAME7),          "Sun" },
+    { X(LOCALE_SMONTHNAME1),              "January" },
+    { X(LOCALE_SMONTHNAME2),              "February" },
+    { X(LOCALE_SMONTHNAME3),              "March" },
+    { X(LOCALE_SMONTHNAME4),              "April" },
+    { X(LOCALE_SMONTHNAME5),              "May" },
+    { X(LOCALE_SMONTHNAME6),              "June" },
+    { X(LOCALE_SMONTHNAME7),              "July" },
+    { X(LOCALE_SMONTHNAME8),              "August" },
+    { X(LOCALE_SMONTHNAME9),              "September" },
+    { X(LOCALE_SMONTHNAME10),             "October" },
+    { X(LOCALE_SMONTHNAME11),             "November" },
+    { X(LOCALE_SMONTHNAME12),             "December" },
+    { X(LOCALE_SMONTHNAME13),             "" },
+    { X(LOCALE_SABBREVMONTHNAME1),        "Jan" },
+    { X(LOCALE_SABBREVMONTHNAME2),        "Feb" },
+    { X(LOCALE_SABBREVMONTHNAME3),        "Mar" },
+    { X(LOCALE_SABBREVMONTHNAME4),        "Apr" },
+    { X(LOCALE_SABBREVMONTHNAME5),        "May" },
+    { X(LOCALE_SABBREVMONTHNAME6),        "Jun" },
+    { X(LOCALE_SABBREVMONTHNAME7),        "Jul" },
+    { X(LOCALE_SABBREVMONTHNAME8),        "Aug" },
+    { X(LOCALE_SABBREVMONTHNAME9),        "Sep" },
+    { X(LOCALE_SABBREVMONTHNAME10),       "Oct" },
+    { X(LOCALE_SABBREVMONTHNAME11),       "Nov" },
+    { X(LOCALE_SABBREVMONTHNAME12),       "Dec" },
+    { X(LOCALE_SABBREVMONTHNAME13),       "" },
+    { X(LOCALE_SPOSITIVESIGN),            "+" },
+    { X(LOCALE_SNEGATIVESIGN),            "-" },
+    { X(LOCALE_IPOSSIGNPOSN),             "3" },
+    { X(LOCALE_INEGSIGNPOSN),             "0" },
+    { X(LOCALE_IPOSSYMPRECEDES),          "1" },
+    { X(LOCALE_IPOSSEPBYSPACE),           "0" },
+    { X(LOCALE_INEGSYMPRECEDES),          "1" },
+    { X(LOCALE_INEGSEPBYSPACE),           "0" },
+    { X(LOCALE_SISO639LANGNAME),          "iv" },
+    { X(LOCALE_SISO3166CTRYNAME),         "IV" },
+    { X(LOCALE_IDEFAULTEBCDICCODEPAGE),   "037" },
+    { X(LOCALE_IPAPERSIZE),               "9" },
+    { X(LOCALE_SENGCURRNAME),             "International Monetary Fund" },
+    { X(LOCALE_SNATIVECURRNAME),          "International Monetary Fund" },
+    { X(LOCALE_SYEARMONTH),               "yyyy MMMM" },
+    { X(LOCALE_IDIGITSUBSTITUTION),       "1" },
+    { X(LOCALE_SNAME),                    "" },
+    { X(LOCALE_SSCRIPTS),                 "Latn;" },
+    { 0 }
+};
+#undef X
+
+static void test_invariant(void)
+{
+  int ret;
+  int len;
+  char buffer[BUFFER_SIZE];
+  const struct invariant_entry *ptr = invariant_list;
+
+  if (!GetLocaleInfoA(LOCALE_INVARIANT, NUO|LOCALE_SLANGUAGE, buffer, sizeof(buffer)))
+  {
+      win_skip("GetLocaleInfoA(LOCALE_INVARIANT) not supported\n"); /* win2k */
+      return;
+  }
+
+  while (ptr->name)
+  {
+    ret = GetLocaleInfoA(LOCALE_INVARIANT, NUO|ptr->id, buffer, sizeof(buffer));
+    if (!ret && (ptr->id == LOCALE_SNAME || ptr->id == LOCALE_SSCRIPTS))
+        win_skip("not supported\n"); /* winxp/win2k3 */
+    else
+    {
+        len = strlen(ptr->expect)+1; /* include \0 */
+        ok(ret == len, "For id %d, expected ret == %d, got %d, error %d\n",
+            ptr->id, len, ret, GetLastError());
+        ok(!strcmp(buffer, ptr->expect), "For id %d, Expected %s, got '%s'\n",
+            ptr->id, ptr->expect, buffer);
+    }
+
+    ptr++;
+  }
+
+  if ((PRIMARYLANGID(LANGIDFROMLCID(GetSystemDefaultLCID())) != LANG_ENGLISH) ||
+      (PRIMARYLANGID(LANGIDFROMLCID(GetThreadLocale())) != LANG_ENGLISH))
+  {
+      skip("Non-English locale\n");
+  }
+  else
+  {
+    /* some locales translate these */
+    static const char lang[]  = "Invariant Language (Invariant Country)";
+    static const char cntry[] = "Invariant Country";
+    static const char sortm[] = "Math Alphanumerics";
+    static const char sortd[] = "Default"; /* win2k3 */
+
+    ret = GetLocaleInfoA(LOCALE_INVARIANT, NUO|LOCALE_SLANGUAGE, buffer, sizeof(buffer));
+    len = lstrlenA(lang) + 1;
+    ok(ret == len, "Expected ret == %d, got %d, error %d\n", len, ret, GetLastError());
+    ok(!strcmp(buffer, lang), "Expected %s, got '%s'\n", lang, buffer);
+
+    ret = GetLocaleInfoA(LOCALE_INVARIANT, NUO|LOCALE_SCOUNTRY, buffer, sizeof(buffer));
+    len = lstrlenA(cntry) + 1;
+    ok(ret == len, "Expected ret == %d, got %d, error %d\n", len, ret, GetLastError());
+    ok(!strcmp(buffer, cntry), "Expected %s, got '%s'\n", cntry, buffer);
+
+    ret = GetLocaleInfoA(LOCALE_INVARIANT, NUO|LOCALE_SSORTNAME, buffer, sizeof(buffer));
+    if (ret == lstrlenA(sortm)+1)
+        ok(!strcmp(buffer, sortm), "Expected %s, got '%s'\n", sortm, buffer);
+    else if (ret == lstrlenA(sortd)+1) /* win2k3 */
+        ok(!strcmp(buffer, sortd), "Expected %s, got '%s'\n", sortd, buffer);
+    else
+        ok(0, "Expected ret == %d or %d, got %d, error %d\n",
+            lstrlenA(sortm)+1, lstrlenA(sortd)+1, ret, GetLastError());
+  }
+}
+
 START_TEST(locale)
 {
   InitFunctionPointers();
 
   test_EnumTimeFormatsA();
+  test_EnumTimeFormatsW();
   test_EnumDateFormatsA();
   test_GetLocaleInfoA();
   test_GetLocaleInfoW();
@@ -4343,6 +4608,7 @@ START_TEST(locale)
   test_CompareStringOrdinal();
   test_GetGeoInfo();
   test_EnumSystemGeoID();
+  test_invariant();
   /* this requires collation table patch to make it MS compatible */
   if (0) test_sorting();
 }
index 3600abe..15d824e 100755 (executable)
 #define ARCH "x86"
 #elif defined __x86_64__
 #define ARCH "amd64"
+#elif defined __arm__
+#define ARCH "arm"
+#elif defined __aarch64__
+#define ARCH "arm64"
 #else
 #define ARCH "none"
 #endif
@@ -75,6 +79,9 @@ static BOOL   (WINAPI *pDeactivateActCtx)(DWORD,ULONG_PTR);
 static BOOL   (WINAPI *pGetCurrentActCtx)(HANDLE *);
 static void   (WINAPI *pReleaseActCtx)(HANDLE);
 
+static BOOL (WINAPI *pCheckNameLegalDOS8Dot3W)(const WCHAR *, char *, DWORD, BOOL *, BOOL *);
+static BOOL (WINAPI *pCheckNameLegalDOS8Dot3A)(const char *, char *, DWORD, BOOL *, BOOL *);
+
 /* a structure to deal with wine todos somewhat cleanly */
 typedef struct {
   DWORD shortlen;
@@ -922,7 +929,7 @@ static void test_PathNameA(CHAR *curdir, CHAR curDrive, CHAR otherDrive)
 
 static void test_GetTempPathA(char* tmp_dir)
 {
-    DWORD len, len_with_null;
+    DWORD len, slen, len_with_null;
     char buf[MAX_PATH];
 
     len_with_null = strlen(tmp_dir) + 1;
@@ -953,12 +960,29 @@ static void test_GetTempPathA(char* tmp_dir)
     len = GetTempPathA(len, buf);
     ok(lstrcmpiA(buf, tmp_dir) == 0, "expected [%s], got [%s]\n",tmp_dir,buf);
     ok(len == strlen(buf), "returned length should be equal to the length of string\n");
+
+    memset(buf, 'a', sizeof(buf));
+    len = GetTempPathA(sizeof(buf), buf);
+    ok(lstrcmpiA(buf, tmp_dir) == 0, "expected [%s], got [%s]\n",tmp_dir,buf);
+    ok(len == strlen(buf), "returned length should be equal to the length of string\n");
+    /* The rest of the buffer remains untouched */
+    slen = len + 1;
+    for(len++; len < sizeof(buf); len++)
+        ok(buf[len] == 'a', "expected 'a' at [%d], got 0x%x\n", len, buf[len]);
+
+    /* When the buffer is not long enough it remains untouched */
+    memset(buf, 'a', sizeof(buf));
+    len = GetTempPathA(slen / 2, buf);
+    ok(len == slen || broken(len == slen + 1) /* read the big comment above */ ,
+       "expected %d, got %d\n", slen, len);
+    for(len = 0; len < sizeof(buf) / sizeof(buf[0]); len++)
+        ok(buf[len] == 'a', "expected 'a' at [%d], got 0x%x\n", len, buf[len]);
 }
 
 static void test_GetTempPathW(char* tmp_dir)
 {
-    DWORD len, len_with_null;
-    WCHAR buf[MAX_PATH];
+    DWORD len, slen, len_with_null;
+    WCHAR buf[MAX_PATH], *long_buf;
     WCHAR tmp_dirW[MAX_PATH];
     static const WCHAR fooW[] = {'f','o','o',0};
 
@@ -996,6 +1020,69 @@ static void test_GetTempPathW(char* tmp_dir)
     len = GetTempPathW(len, buf);
     ok(lstrcmpiW(buf, tmp_dirW) == 0, "GetTempPathW returned an incorrect temporary path\n");
     ok(len == lstrlenW(buf), "returned length should be equal to the length of string\n");
+
+    for(len = 0; len < sizeof(buf) / sizeof(buf[0]); len++)
+        buf[len] = 'a';
+    len = GetTempPathW(len, buf);
+    ok(lstrcmpiW(buf, tmp_dirW) == 0, "GetTempPathW returned an incorrect temporary path\n");
+    ok(len == lstrlenW(buf), "returned length should be equal to the length of string\n");
+    /* The rest of the buffer must be zeroed */
+    slen = len + 1;
+    for(len++; len < sizeof(buf) / sizeof(buf[0]); len++)
+        ok(buf[len] == '\0', "expected NULL at [%d], got 0x%x\n", len, buf[len]);
+
+    /* When the buffer is not long enough the length passed is zeroed */
+    for(len = 0; len < sizeof(buf) / sizeof(buf[0]); len++)
+        buf[len] = 'a';
+    len = GetTempPathW(slen / 2, buf);
+    ok(len == slen || broken(len == slen + 1) /* read the big comment above */ ,
+       "expected %d, got %d\n", slen, len);
+
+    {
+        /* In Windows 8 when TMP var points to a drive only (like C:) instead of a
+        * full directory the behavior changes. It will start filling the path but
+        * will later truncate the buffer before returning. So the generic test
+        * below will fail for this Windows 8 corner case.
+        */
+        char tmp_var[64];
+        DWORD version = GetVersion();
+        GetEnvironmentVariableA("TMP", tmp_var, sizeof(tmp_var));
+        if (strlen(tmp_var) == 2 && version >= 0x00060002)
+            return;
+    }
+
+    for(len = 0; len < slen / 2; len++)
+        ok(buf[len] == '\0', "expected NULL at [%d], got 0x%x\n", len, buf[len]);
+    for(; len < sizeof(buf) / sizeof(buf[0]); len++)
+        ok(buf[len] == 'a', "expected 'a' at [%d], got 0x%x\n", len, buf[len]);
+
+    /* bogus application from bug 38220 passes the count value in sizeof(buffer)
+     * instead the correct count of WCHAR, this test catches this case. */
+    slen = 65534;
+    long_buf = HeapAlloc(GetProcessHeap(), 0, slen * sizeof(WCHAR));
+    if (!long_buf)
+    {
+        skip("Could not allocate memory for the test\n");
+        return;
+    }
+    for(len = 0; len < slen; len++)
+        long_buf[len] = 0xCC;
+    len = GetTempPathW(slen, long_buf);
+    ok(lstrcmpiW(long_buf, tmp_dirW) == 0, "GetTempPathW returned an incorrect temporary path\n");
+    ok(len == lstrlenW(long_buf), "returned length should be equal to the length of string\n");
+    /* the remaining buffer must be zeroed up to different values in different OS versions.
+     * <= XP - 32766
+     *  > XP - 32767
+     * to simplify testing we will test only until XP.
+     */
+    for(; len < 32767; len++)
+        ok(long_buf[len] == '\0', "expected NULL at [%d], got 0x%x\n", len, long_buf[len]);
+    /* we will know skip the test that is in the middle of the OS difference by
+     * incrementing len and then resume the test for the untouched part. */
+    for(len++; len < slen; len++)
+        ok(long_buf[len] == 0xcc, "expected 0xcc at [%d], got 0x%x\n", len, long_buf[len]);
+
+    HeapFree(GetProcessHeap(), 0, long_buf);
 }
 
 static void test_GetTempPath(void)
@@ -1266,33 +1353,49 @@ static void test_GetLongPathNameW(void)
 
 static void test_GetShortPathNameW(void)
 {
-    WCHAR test_path[] = { 'L', 'o', 'n', 'g', 'D', 'i', 'r', 'e', 'c', 't', 'o', 'r', 'y', 'N', 'a', 'm', 'e',  0 };
-    WCHAR path[MAX_PATH];
+    static const WCHAR extended_prefix[] = {'\\','\\','?','\\',0};
+    static const WCHAR test_path[] = { 'L', 'o', 'n', 'g', 'D', 'i', 'r', 'e', 'c', 't', 'o', 'r', 'y', 'N', 'a', 'm', 'e',  0 };
+    static const WCHAR name[] = { 't', 'e', 's', 't', 0 };
+    static const WCHAR backSlash[] = { '\\', 0 };
+    static const WCHAR a_bcdeW[] = {'a','.','b','c','d','e',0};
+    WCHAR path[MAX_PATH], tmppath[MAX_PATH], *ptr;
     WCHAR short_path[MAX_PATH];
     DWORD length;
     HANDLE file;
     int ret;
-    WCHAR name[] = { 't', 'e', 's', 't', 0 };
-    WCHAR backSlash[] = { '\\', 0 };
 
     SetLastError(0xdeadbeef);
-    GetTempPathW( MAX_PATH, path );
+    GetTempPathW( MAX_PATH, tmppath );
     if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
     {
         win_skip("GetTempPathW is not implemented\n");
         return;
     }
 
+    lstrcpyW( path, tmppath );
     lstrcatW( path, test_path );
     lstrcatW( path, backSlash );
     ret = CreateDirectoryW( path, NULL );
     ok( ret, "Directory was not created. LastError = %d\n", GetLastError() );
 
     /* Starting a main part of test */
+
+    /* extended path \\?\C:\path\ */
+    lstrcpyW( path, extended_prefix );
+    lstrcatW( path, tmppath );
+    lstrcatW( path, test_path );
+    lstrcatW( path, backSlash );
+    short_path[0] = 0;
+    length = GetShortPathNameW( path, short_path, sizeof(short_path) / sizeof(*short_path) );
+    ok( length, "GetShortPathNameW returned 0.\n" );
+
+    lstrcpyW( path, tmppath );
+    lstrcatW( path, test_path );
+    lstrcatW( path, backSlash );
     length = GetShortPathNameW( path, short_path, 0 );
     ok( length, "GetShortPathNameW returned 0.\n" );
     ret = GetShortPathNameW( path, short_path, length );
-    ok( ret, "GetShortPathNameW returned 0.\n" );
+    ok( ret && ret == length-1, "GetShortPathNameW returned 0.\n" );
 
     lstrcatW( short_path, name );
 
@@ -1304,11 +1407,24 @@ static void test_GetShortPathNameW(void)
 
     file = CreateFileW( short_path, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
     ok( file != INVALID_HANDLE_VALUE, "File was not created.\n" );
-
-    /* End test */
     CloseHandle( file );
     ret = DeleteFileW( short_path );
     ok( ret, "Cannot delete file.\n" );
+
+    ptr = path + lstrlenW(path);
+    lstrcpyW( ptr, a_bcdeW);
+    file = CreateFileW( path, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
+    ok( file != INVALID_HANDLE_VALUE, "File was not created.\n" );
+    CloseHandle( file );
+
+    length = GetShortPathNameW( path, short_path, sizeof(short_path)/sizeof(*short_path) );
+    ok( length, "GetShortPathNameW failed: %u.\n", GetLastError() );
+
+    ret = DeleteFileW( path );
+    ok( ret, "Cannot delete file.\n" );
+    *ptr = 0;
+
+    /* End test */
     ret = RemoveDirectoryW( path );
     ok( ret, "Cannot delete directory.\n" );
 }
@@ -1989,6 +2105,8 @@ static void init_pointers(void)
     MAKEFUNC(DeactivateActCtx);
     MAKEFUNC(GetCurrentActCtx);
     MAKEFUNC(ReleaseActCtx);
+    MAKEFUNC(CheckNameLegalDOS8Dot3W);
+    MAKEFUNC(CheckNameLegalDOS8Dot3A);
 #undef MAKEFUNC
 }
 
@@ -2050,6 +2168,73 @@ static void test_relative_path(void)
     RemoveDirectoryA("bar");
 }
 
+static void test_CheckNameLegalDOS8Dot3(void)
+{
+    static const WCHAR has_driveW[] = {'C',':','\\','a','.','t','x','t',0};
+    static const WCHAR has_pathW[] = {'b','\\','a','.','t','x','t',0};
+    static const WCHAR too_longW[] = {'a','l','o','n','g','f','i','l','e','n','a','m','e','.','t','x','t',0};
+    static const WCHAR twodotsW[] = {'t','e','s','t','.','e','s','t','.','t','x','t',0};
+    static const WCHAR longextW[] = {'t','e','s','t','.','t','x','t','t','x','t',0};
+    static const WCHAR emptyW[] = {0};
+    static const WCHAR funnycharsW[] = {'!','#','$','%','&','\'','(',')','.','-','@','^',0};
+    static const WCHAR length8W[] = {'t','e','s','t','t','e','s','t','.','t','x','t',0};
+    static const WCHAR length1W[] = {'t',0};
+    static const WCHAR withspaceW[] = {'t','e','s','t',' ','e','s','t','.','t','x','t',0};
+
+    static const struct {
+        const WCHAR *name;
+        BOOL should_be_legal, has_space;
+    } cases[] = {
+        {has_driveW, FALSE, FALSE},
+        {has_pathW, FALSE, FALSE},
+        {too_longW, FALSE, FALSE},
+        {twodotsW, FALSE, FALSE},
+        {longextW, FALSE, FALSE},
+        {emptyW, TRUE /* ! */, FALSE},
+        {funnycharsW, TRUE, FALSE},
+        {length8W, TRUE, FALSE},
+        {length1W, TRUE, FALSE},
+        {withspaceW, TRUE, TRUE},
+    };
+
+    BOOL br, is_legal, has_space;
+    char astr[64];
+    DWORD i;
+
+    if(!pCheckNameLegalDOS8Dot3W){
+        win_skip("Missing CheckNameLegalDOS8Dot3, skipping tests\n");
+        return;
+    }
+
+    br = pCheckNameLegalDOS8Dot3W(NULL, NULL, 0, NULL, &is_legal);
+    ok(br == FALSE, "CheckNameLegalDOS8Dot3W should have failed\n");
+
+    br = pCheckNameLegalDOS8Dot3A(NULL, NULL, 0, NULL, &is_legal);
+    ok(br == FALSE, "CheckNameLegalDOS8Dot3A should have failed\n");
+
+    br = pCheckNameLegalDOS8Dot3W(length8W, NULL, 0, NULL, NULL);
+    ok(br == FALSE, "CheckNameLegalDOS8Dot3W should have failed\n");
+
+    br = pCheckNameLegalDOS8Dot3A("testtest.txt", NULL, 0, NULL, NULL);
+    ok(br == FALSE, "CheckNameLegalDOS8Dot3A should have failed\n");
+
+    for(i = 0; i < sizeof(cases)/sizeof(*cases); ++i){
+        br = pCheckNameLegalDOS8Dot3W(cases[i].name, NULL, 0, &has_space, &is_legal);
+        ok(br == TRUE, "CheckNameLegalDOS8Dot3W failed for %s\n", wine_dbgstr_w(cases[i].name));
+        ok(is_legal == cases[i].should_be_legal, "Got wrong legality for %s\n", wine_dbgstr_w(cases[i].name));
+        if(is_legal)
+            ok(has_space == cases[i].has_space, "Got wrong space for %s\n", wine_dbgstr_w(cases[i].name));
+
+        WideCharToMultiByte(CP_ACP, 0, cases[i].name, -1, astr, sizeof(astr), NULL, NULL);
+
+        br = pCheckNameLegalDOS8Dot3A(astr, NULL, 0, &has_space, &is_legal);
+        ok(br == TRUE, "CheckNameLegalDOS8Dot3W failed for %s\n", astr);
+        ok(is_legal == cases[i].should_be_legal, "Got wrong legality for %s\n", astr);
+        if(is_legal)
+            ok(has_space == cases[i].has_space, "Got wrong space for %s\n", wine_dbgstr_w(cases[i].name));
+    }
+}
+
 START_TEST(path)
 {
     CHAR origdir[MAX_PATH],curdir[MAX_PATH], curDrive, otherDrive;
@@ -2082,4 +2267,5 @@ START_TEST(path)
     test_SearchPathW();
     test_GetFullPathNameA();
     test_GetFullPathNameW();
+    test_CheckNameLegalDOS8Dot3();
 }
index 9d82f1b..d003c5c 100755 (executable)
@@ -487,7 +487,7 @@ static void test_CreateNamedPipe(int pipemode)
         ok(written == sizeof(obuf2), "write file len 3b\n");
         ok(PeekNamedPipe(hFile, ibuf, sizeof(ibuf), &readden, &avail, NULL), "Peek3\n");
         if (pipemode == PIPE_TYPE_BYTE) {
-            todo_wine ok(readden == sizeof(obuf) + sizeof(obuf2), "peek3 got %d bytes\n", readden);
+            ok(readden == sizeof(obuf) + sizeof(obuf2), "peek3 got %d bytes\n", readden);
         }
         else
         {
@@ -515,7 +515,7 @@ static void test_CreateNamedPipe(int pipemode)
         ok(written == sizeof(obuf2), "write file len 4b\n");
         ok(PeekNamedPipe(hnp, ibuf, sizeof(ibuf), &readden, &avail, NULL), "Peek4\n");
         if (pipemode == PIPE_TYPE_BYTE) {
-            todo_wine ok(readden == sizeof(obuf) + sizeof(obuf2), "peek4 got %d bytes\n", readden);
+            ok(readden == sizeof(obuf) + sizeof(obuf2), "peek4 got %d bytes\n", readden);
         }
         else
         {
@@ -1162,6 +1162,8 @@ static void test_CloseNamedPipe(void)
     char ibuf[32];
     DWORD written;
     DWORD readden;
+    DWORD state;
+    BOOL ret;
 
     hnp = CreateNamedPipeA(PIPENAME, PIPE_ACCESS_DUPLEX,
                            PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
@@ -1196,10 +1198,21 @@ static void test_CloseNamedPipe(void)
         ok(readden == sizeof(obuf), "got %d bytes\n", readden);
         /* pipe is empty now */
 
+        ret = GetNamedPipeHandleStateA(hFile, &state, NULL, NULL, NULL, NULL, 0);
+        todo_wine
+        ok(ret, "GetNamedPipeHandleState failed with %d\n", GetLastError());
+        state = PIPE_READMODE_MESSAGE | PIPE_WAIT;
+        ret = SetNamedPipeHandleState(hFile, &state, NULL, NULL);
+        todo_wine
+        ok(ret, "SetNamedPipeHandleState failed with %d\n", GetLastError());
+
         SetLastError(0xdeadbeef);
         ok(!ReadFile(hFile, ibuf, 0, &readden, NULL), "ReadFile() succeeded\n");
         ok(GetLastError() == ERROR_BROKEN_PIPE, "GetLastError() returned %08x, expected ERROR_BROKEN_PIPE\n", GetLastError());
-        SetLastError(0);
+
+        SetLastError(0xdeadbeef);
+        ok(!WriteFile(hFile, obuf, sizeof(obuf), &written, NULL), "WriteFile() succeeded\n");
+        ok(GetLastError() == ERROR_NO_DATA, "GetLastError() returned %08x, expected ERROR_NO_DATA\n", GetLastError());
 
         CloseHandle(hFile);
     }
@@ -1230,11 +1243,21 @@ static void test_CloseNamedPipe(void)
         ok(readden == 0, "got %d bytes\n", readden);
         /* pipe is empty now */
 
+        ret = GetNamedPipeHandleStateA(hFile, &state, NULL, NULL, NULL, NULL, 0);
+        todo_wine
+        ok(ret, "GetNamedPipeHandleState failed with %d\n", GetLastError());
+        state = PIPE_READMODE_MESSAGE | PIPE_WAIT;
+        ret = SetNamedPipeHandleState(hFile, &state, NULL, NULL);
+        todo_wine
+        ok(ret, "SetNamedPipeHandleState failed with %d\n", GetLastError());
+
         SetLastError(0xdeadbeef);
         ok(!ReadFile(hFile, ibuf, sizeof(ibuf), &readden, NULL), "ReadFile() succeeded\n");
-        todo_wine
         ok(GetLastError() == ERROR_BROKEN_PIPE, "GetLastError() returned %08x, expected ERROR_BROKEN_PIPE\n", GetLastError());
-        SetLastError(0);
+
+        SetLastError(0xdeadbeef);
+        ok(!WriteFile(hFile, obuf, sizeof(obuf), &written, NULL), "WriteFile() succeeded\n");
+        ok(GetLastError() == ERROR_NO_DATA, "GetLastError() returned %08x, expected ERROR_NO_DATA\n", GetLastError());
 
         CloseHandle(hFile);
     }
@@ -1272,10 +1295,21 @@ static void test_CloseNamedPipe(void)
         ok(readden == sizeof(obuf), "got %d bytes\n", readden);
         /* pipe is empty now */
 
+        ret = GetNamedPipeHandleStateA(hnp, &state, NULL, NULL, NULL, NULL, 0);
+        ok(ret, "GetNamedPipeHandleState failed with %d\n", GetLastError());
+        SetLastError(0xdeadbeef);
+        state = PIPE_READMODE_MESSAGE | PIPE_WAIT;
+        ret = SetNamedPipeHandleState(hFile, &state, NULL, NULL);
+        ok(!ret, "SetNamedPipeHandleState unexpectedly succeeded\n");
+        ok(GetLastError() == ERROR_INVALID_HANDLE, "GetLastError() returned %08x, expected ERROR_INVALID_HANDLE\n", GetLastError());
+
         SetLastError(0xdeadbeef);
         ok(!ReadFile(hnp, ibuf, 0, &readden, NULL), "ReadFile() succeeded\n");
         ok(GetLastError() == ERROR_BROKEN_PIPE, "GetLastError() returned %08x, expected ERROR_BROKEN_PIPE\n", GetLastError());
-        SetLastError(0);
+
+        SetLastError(0xdeadbeef);
+        ok(!WriteFile(hnp, obuf, sizeof(obuf), &written, NULL), "WriteFile() succeeded\n");
+        ok(GetLastError() == ERROR_NO_DATA, "GetLastError() returned %08x, expected ERROR_NO_DATA\n", GetLastError());
 
         CloseHandle(hnp);
     }
@@ -1306,10 +1340,22 @@ static void test_CloseNamedPipe(void)
         ok(readden == 0, "got %d bytes\n", readden);
         /* pipe is empty now */
 
+        ret = GetNamedPipeHandleStateA(hnp, &state, NULL, NULL, NULL, NULL, 0);
+        ok(ret, "GetNamedPipeHandleState failed with %d\n", GetLastError());
+        ret = SetNamedPipeHandleState(hFile, &state, NULL, NULL);
+        SetLastError(0xdeadbeef);
+        state = PIPE_READMODE_MESSAGE | PIPE_WAIT;
+        ret = SetNamedPipeHandleState(hFile, &state, NULL, NULL);
+        ok(!ret, "SetNamedPipeHandleState unexpectedly succeeded\n");
+        ok(GetLastError() == ERROR_INVALID_HANDLE, "GetLastError() returned %08x, expected ERROR_INVALID_HANDLE\n", GetLastError());
+
         SetLastError(0xdeadbeef);
         ok(!ReadFile(hnp, ibuf, sizeof(ibuf), &readden, NULL), "ReadFile() succeeded\n");
         ok(GetLastError() == ERROR_BROKEN_PIPE, "GetLastError() returned %08x, expected ERROR_BROKEN_PIPE\n", GetLastError());
-        SetLastError(0);
+
+        SetLastError(0xdeadbeef);
+        ok(!WriteFile(hnp, obuf, sizeof(obuf), &written, NULL), "WriteFile() succeeded\n");
+        ok(GetLastError() == ERROR_NO_DATA, "GetLastError() returned %08x, expected ERROR_NO_DATA\n", GetLastError());
 
         CloseHandle(hnp);
     }
@@ -2851,20 +2897,17 @@ static void test_readfileex_pending(void)
     ok(completion_lpoverlapped == &overlapped, "completion called with wrong overlapped pointer\n");
 
     /* free up some space in the pipe */
-    ret = ReadFile(client, read_buf, sizeof(read_buf), &num_bytes, NULL);
-    ok(ret == TRUE, "ReadFile failed\n");
-
-    ok(completion_called == 0, "completion routine called during ReadFile\n");
-
-    wait = WaitForSingleObjectEx(event, 0, TRUE);
-    ok(wait == WAIT_IO_COMPLETION || wait == WAIT_OBJECT_0, "WaitForSingleObject returned %x\n", wait);
-    if (wait == WAIT_TIMEOUT)
+    for (i=0; i<256; i++)
     {
         ret = ReadFile(client, read_buf, sizeof(read_buf), &num_bytes, NULL);
         ok(ret == TRUE, "ReadFile failed\n");
+
         ok(completion_called == 0, "completion routine called during ReadFile\n");
+
         wait = WaitForSingleObjectEx(event, 0, TRUE);
-        ok(wait == WAIT_IO_COMPLETION || wait == WAIT_OBJECT_0, "WaitForSingleObject returned %x\n", wait);
+        ok(wait == WAIT_IO_COMPLETION || wait == WAIT_OBJECT_0 || wait == WAIT_TIMEOUT,
+           "WaitForSingleObject returned %x\n", wait);
+        if (wait != WAIT_TIMEOUT) break;
     }
 
     ok(completion_called == 1, "completion routine not called\n");
@@ -2886,6 +2929,7 @@ static void test_readfileex_pending(void)
     num_bytes = 0xdeadbeef;
     SetLastError(0xdeadbeef);
     ret = ReadFile(server, read_buf, 0, &num_bytes, &overlapped);
+    ok(!ret, "ReadFile should fail\n");
     ok(GetLastError() == ERROR_IO_PENDING, "expected ERROR_IO_PENDING, got %d\n", GetLastError());
     ok(num_bytes == 0, "bytes %u\n", num_bytes);
     ok((NTSTATUS)overlapped.Internal == STATUS_PENDING, "expected STATUS_PENDING, got %#lx\n", overlapped.Internal);
@@ -2943,7 +2987,6 @@ todo_wine
     ok(completion_called == 0, "completion routine called before ReadFileEx returned\n");
     ok(GetLastError() == ERROR_NO_DATA, "expected ERROR_NO_DATA, got %d\n", GetLastError());
     wait = WaitForSingleObjectEx(event, 0, TRUE);
-    todo_wine
     ok(wait == WAIT_TIMEOUT, "WaitForSingleObjectEx returned %x\n", wait);
     ok(completion_called == 0, "completion routine called before writing to file\n");
 
@@ -3221,7 +3264,6 @@ todo_wine
     ok(completion_called == 0, "completion routine called before ReadFileEx returned\n");
     ok(GetLastError() == ERROR_NO_DATA, "expected ERROR_NO_DATA, got %d\n", GetLastError());
     wait = WaitForSingleObjectEx(event, 0, TRUE);
-    todo_wine
     ok(wait == WAIT_TIMEOUT, "WaitForSingleObjectEx returned %x\n", wait);
     ok(completion_called == 0, "completion routine called before writing to file\n");
 
index 33725ff..4eed939 100755 (executable)
@@ -3,6 +3,7 @@
  *
  * Copyright 2002 Eric Pouech
  * Copyright 2006 Dmitry Timoshkov
+ * Copyright 2014 Michael Müller
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -67,6 +68,17 @@ static BOOL   (WINAPI *pQueryFullProcessImageNameA)(HANDLE hProcess, DWORD dwFla
 static BOOL   (WINAPI *pQueryFullProcessImageNameW)(HANDLE hProcess, DWORD dwFlags, LPWSTR lpExeName, PDWORD lpdwSize);
 static DWORD  (WINAPI *pK32GetProcessImageFileNameA)(HANDLE,LPSTR,DWORD);
 static struct _TEB * (WINAPI *pNtCurrentTeb)(void);
+static HANDLE (WINAPI *pCreateJobObjectW)(LPSECURITY_ATTRIBUTES sa, LPCWSTR name);
+static BOOL   (WINAPI *pAssignProcessToJobObject)(HANDLE job, HANDLE process);
+static BOOL   (WINAPI *pIsProcessInJob)(HANDLE process, HANDLE job, PBOOL result);
+static BOOL   (WINAPI *pTerminateJobObject)(HANDLE job, UINT exit_code);
+static BOOL   (WINAPI *pQueryInformationJobObject)(HANDLE job, JOBOBJECTINFOCLASS class, LPVOID info, DWORD len, LPDWORD ret_len);
+static BOOL   (WINAPI *pSetInformationJobObject)(HANDLE job, JOBOBJECTINFOCLASS class, LPVOID info, DWORD len);
+static HANDLE (WINAPI *pCreateIoCompletionPort)(HANDLE file, HANDLE existing_port, ULONG_PTR key, DWORD threads);
+static BOOL   (WINAPI *pGetNumaProcessorNode)(UCHAR, PUCHAR);
+static NTSTATUS (WINAPI *pNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
+static BOOL   (WINAPI *pProcessIdToSessionId)(DWORD,DWORD*);
+static DWORD  (WINAPI *pWTSGetActiveConsoleSessionId)(void);
 
 /* ############################### */
 static char     base[MAX_PATH];
@@ -204,6 +216,9 @@ static BOOL init(void)
     hkernel32 = GetModuleHandleA("kernel32");
     hntdll    = GetModuleHandleA("ntdll.dll");
 
+    pNtCurrentTeb = (void *)GetProcAddress(hntdll, "NtCurrentTeb");
+    pNtQueryInformationProcess = (void *)GetProcAddress(hntdll, "NtQueryInformationProcess");
+
     pGetNativeSystemInfo = (void *) GetProcAddress(hkernel32, "GetNativeSystemInfo");
     pGetSystemRegistryQuota = (void *) GetProcAddress(hkernel32, "GetSystemRegistryQuota");
     pIsWow64Process = (void *) GetProcAddress(hkernel32, "IsWow64Process");
@@ -212,7 +227,16 @@ static BOOL init(void)
     pQueryFullProcessImageNameA = (void *) GetProcAddress(hkernel32, "QueryFullProcessImageNameA");
     pQueryFullProcessImageNameW = (void *) GetProcAddress(hkernel32, "QueryFullProcessImageNameW");
     pK32GetProcessImageFileNameA = (void *) GetProcAddress(hkernel32, "K32GetProcessImageFileNameA");
-    pNtCurrentTeb = (void *)GetProcAddress( hntdll, "NtCurrentTeb" );
+    pCreateJobObjectW = (void *)GetProcAddress(hkernel32, "CreateJobObjectW");
+    pAssignProcessToJobObject = (void *)GetProcAddress(hkernel32, "AssignProcessToJobObject");
+    pIsProcessInJob = (void *)GetProcAddress(hkernel32, "IsProcessInJob");
+    pTerminateJobObject = (void *)GetProcAddress(hkernel32, "TerminateJobObject");
+    pQueryInformationJobObject = (void *)GetProcAddress(hkernel32, "QueryInformationJobObject");
+    pSetInformationJobObject = (void *)GetProcAddress(hkernel32, "SetInformationJobObject");
+    pCreateIoCompletionPort = (void *)GetProcAddress(hkernel32, "CreateIoCompletionPort");
+    pGetNumaProcessorNode = (void *)GetProcAddress(hkernel32, "GetNumaProcessorNode");
+    pProcessIdToSessionId = (void *)GetProcAddress(hkernel32, "ProcessIdToSessionId");
+    pWTSGetActiveConsoleSessionId = (void *)GetProcAddress(hkernel32, "WTSGetActiveConsoleSessionId");
     return TRUE;
 }
 
@@ -550,7 +574,7 @@ static void test_Startup(void)
     startup.wShowWindow = SW_SHOWNORMAL;
 
     get_file_name(resfile);
-    sprintf(buffer, "\"%s\" tests/process.c \"%s\"", selfname, resfile);
+    sprintf(buffer, "\"%s\" tests/process.c dump \"%s\"", selfname, resfile);
     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
     /* wait for child to terminate */
     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
@@ -588,7 +612,7 @@ static void test_Startup(void)
     startup.dwFillAttribute = 0xA55A;
 
     get_file_name(resfile);
-    sprintf(buffer, "\"%s\" tests/process.c \"%s\"", selfname, resfile);
+    sprintf(buffer, "\"%s\" tests/process.c dump \"%s\"", selfname, resfile);
     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
     /* wait for child to terminate */
     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
@@ -626,7 +650,7 @@ static void test_Startup(void)
     startup.dwFillAttribute = 0xA55A;
 
     get_file_name(resfile);
-    sprintf(buffer, "\"%s\" tests/process.c \"%s\"", selfname, resfile);
+    sprintf(buffer, "\"%s\" tests/process.c dump \"%s\"", selfname, resfile);
     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
     /* wait for child to terminate */
     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
@@ -664,7 +688,7 @@ static void test_Startup(void)
     startup.dwFillAttribute = 0xA55A;
 
     get_file_name(resfile);
-    sprintf(buffer, "\"%s\" tests/process.c \"%s\"", selfname, resfile);
+    sprintf(buffer, "\"%s\" tests/process.c dump \"%s\"", selfname, resfile);
     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
     /* wait for child to terminate */
     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
@@ -702,7 +726,7 @@ static void test_Startup(void)
     startup.dwFillAttribute = 0xA55A;
 
     get_file_name(resfile);
-    sprintf(buffer, "\"%s\" tests/process.c \"%s\"", selfname, resfile);
+    sprintf(buffer, "\"%s\" tests/process.c dump \"%s\"", selfname, resfile);
     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
     /* wait for child to terminate */
     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
@@ -742,7 +766,7 @@ static void test_Startup(void)
     startup.dwFillAttribute = 0xA55A;
 
     get_file_name(resfile);
-    sprintf(buffer, "\"%s\" tests/process.c \"%s\"", selfname, resfile);
+    sprintf(buffer, "\"%s\" tests/process.c dump \"%s\"", selfname, resfile);
     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
     /* wait for child to terminate */
     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
@@ -780,7 +804,7 @@ static void test_Startup(void)
     startup.dwFillAttribute = 0xA55A;
 
     get_file_name(resfile);
-    sprintf(buffer, "\"%s\" tests/process.c \"%s\"", selfname, resfile);
+    sprintf(buffer, "\"%s\" tests/process.c dump \"%s\"", selfname, resfile);
     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
     /* wait for child to terminate */
     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
@@ -820,16 +844,16 @@ static void test_CommandLine(void)
 
     /* the basics */
     get_file_name(resfile);
-    sprintf(buffer, "\"%s\" tests/process.c \"%s\" \"C:\\Program Files\\my nice app.exe\"", selfname, resfile);
+    sprintf(buffer, "\"%s\" tests/process.c dump \"%s\" \"C:\\Program Files\\my nice app.exe\"", selfname, resfile);
     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
     /* wait for child to terminate */
     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
     /* child process has changed result file, so let profile functions know about it */
     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
 
-    okChildInt("Arguments", "argcA", 4);
-    okChildString("Arguments", "argvA3", "C:\\Program Files\\my nice app.exe");
-    okChildString("Arguments", "argvA4", NULL);
+    okChildInt("Arguments", "argcA", 5);
+    okChildString("Arguments", "argvA4", "C:\\Program Files\\my nice app.exe");
+    okChildString("Arguments", "argvA5", NULL);
     okChildString("Arguments", "CommandLineA", buffer);
     release_memory();
     assert(DeleteFileA(resfile) != 0);
@@ -841,18 +865,18 @@ static void test_CommandLine(void)
 
     /* from François */
     get_file_name(resfile);
-    sprintf(buffer, "\"%s\" tests/process.c \"%s\" \"a\\\"b\\\\\" c\\\" d", selfname, resfile);
+    sprintf(buffer, "\"%s\" tests/process.c dump \"%s\" \"a\\\"b\\\\\" c\\\" d", selfname, resfile);
     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
     /* wait for child to terminate */
     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
     /* child process has changed result file, so let profile functions know about it */
     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
 
-    okChildInt("Arguments", "argcA", 6);
-    okChildString("Arguments", "argvA3", "a\"b\\");
-    okChildString("Arguments", "argvA4", "c\"");
-    okChildString("Arguments", "argvA5", "d");
-    okChildString("Arguments", "argvA6", NULL);
+    okChildInt("Arguments", "argcA", 7);
+    okChildString("Arguments", "argvA4", "a\"b\\");
+    okChildString("Arguments", "argvA5", "c\"");
+    okChildString("Arguments", "argvA6", "d");
+    okChildString("Arguments", "argvA7", NULL);
     okChildString("Arguments", "CommandLineA", buffer);
     release_memory();
     assert(DeleteFileA(resfile) != 0);
@@ -860,7 +884,7 @@ static void test_CommandLine(void)
     /* Test for Bug1330 to show that XP doesn't change '/' to '\\' in argv[0]*/
     get_file_name(resfile);
     /* Use exename to avoid buffer containing things like 'C:' */
-    sprintf(buffer, "./%s tests/process.c \"%s\" \"a\\\"b\\\\\" c\\\" d", exename, resfile);
+    sprintf(buffer, "./%s tests/process.c dump \"%s\" \"a\\\"b\\\\\" c\\\" d", exename, resfile);
     SetLastError(0xdeadbeef);
     ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info);
     ok(ret, "CreateProcess (%s) failed : %d\n", buffer, GetLastError());
@@ -875,7 +899,7 @@ static void test_CommandLine(void)
 
     get_file_name(resfile);
     /* Use exename to avoid buffer containing things like 'C:' */
-    sprintf(buffer, ".\\%s tests/process.c \"%s\" \"a\\\"b\\\\\" c\\\" d", exename, resfile);
+    sprintf(buffer, ".\\%s tests/process.c dump \"%s\" \"a\\\"b\\\\\" c\\\" d", exename, resfile);
     SetLastError(0xdeadbeef);
     ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info);
     ok(ret, "CreateProcess (%s) failed : %d\n", buffer, GetLastError());
@@ -894,8 +918,8 @@ static void test_CommandLine(void)
     *(lpFilePart -1 ) = 0;
     p = strrchr(fullpath, '\\');
     /* Use exename to avoid buffer containing things like 'C:' */
-    if (p) sprintf(buffer, "..%s/%s tests/process.c \"%s\" \"a\\\"b\\\\\" c\\\" d", p, exename, resfile);
-    else sprintf(buffer, "./%s tests/process.c \"%s\" \"a\\\"b\\\\\" c\\\" d", exename, resfile);
+    if (p) sprintf(buffer, "..%s/%s tests/process.c dump \"%s\" \"a\\\"b\\\\\" c\\\" d", p, exename, resfile);
+    else sprintf(buffer, "./%s tests/process.c dump \"%s\" \"a\\\"b\\\\\" c\\\" d", exename, resfile);
     SetLastError(0xdeadbeef);
     ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info);
     ok(ret, "CreateProcess (%s) failed : %d\n", buffer, GetLastError());
@@ -918,7 +942,7 @@ static void test_CommandLine(void)
     /* Use exename to avoid buffer containing things like 'C:' */
     if (p) sprintf(buffer, "..%s/%s", p, exename);
     else sprintf(buffer, "./%s", exename);
-    sprintf(buffer2, "dummy tests/process.c \"%s\" \"a\\\"b\\\\\" c\\\" d", resfile);
+    sprintf(buffer2, "dummy tests/process.c dump \"%s\" \"a\\\"b\\\\\" c\\\" d", resfile);
     SetLastError(0xdeadbeef);
     ret = CreateProcessA(buffer, buffer2, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info);
     ok(ret, "CreateProcess (%s) failed : %d\n", buffer, GetLastError());
@@ -926,7 +950,7 @@ static void test_CommandLine(void)
     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
     /* child process has changed result file, so let profile functions know about it */
     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
-    sprintf(buffer, "tests/process.c %s", resfile);
+    sprintf(buffer, "tests/process.c dump %s", resfile);
     okChildString("Arguments", "argvA0", "dummy");
     okChildString("Arguments", "CommandLineA", buffer2);
     okChildStringWA("Arguments", "CommandLineW", buffer2);
@@ -1016,7 +1040,7 @@ static void test_Directory(void)
 
     /* the basics */
     get_file_name(resfile);
-    sprintf(buffer, "\"%s\" tests/process.c \"%s\"", selfname, resfile);
+    sprintf(buffer, "\"%s\" tests/process.c dump \"%s\"", selfname, resfile);
     GetWindowsDirectoryA( windir, sizeof(windir) );
     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, windir, &startup, &info), "CreateProcess\n");
     /* wait for child to terminate */
@@ -1123,7 +1147,7 @@ static void test_Environment(void)
 
     /* the basics */
     get_file_name(resfile);
-    sprintf(buffer, "\"%s\" tests/process.c \"%s\"", selfname, resfile);
+    sprintf(buffer, "\"%s\" tests/process.c dump \"%s\"", selfname, resfile);
     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
     /* wait for child to terminate */
     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
@@ -1142,7 +1166,7 @@ static void test_Environment(void)
 
     /* the basics */
     get_file_name(resfile);
-    sprintf(buffer, "\"%s\" tests/process.c \"%s\"", selfname, resfile);
+    sprintf(buffer, "\"%s\" tests/process.c dump \"%s\"", selfname, resfile);
 
     child_env_len = 0;
     ptr = env;
@@ -1210,7 +1234,7 @@ static  void    test_SuspendFlag(void)
     startup.wShowWindow = SW_SHOWNORMAL;
 
     get_file_name(resfile);
-    sprintf(buffer, "\"%s\" tests/process.c \"%s\"", selfname, resfile);
+    sprintf(buffer, "\"%s\" tests/process.c dump \"%s\"", selfname, resfile);
     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &startup, &info), "CreateProcess\n");
 
     ok(GetExitCodeThread(info.hThread, &exit_status) && exit_status == STILL_ACTIVE, "thread still running\n");
@@ -1260,7 +1284,7 @@ static  void    test_DebuggingFlag(void)
     startup.wShowWindow = SW_SHOWNORMAL;
 
     get_file_name(resfile);
-    sprintf(buffer, "\"%s\" tests/process.c \"%s\"", selfname, resfile);
+    sprintf(buffer, "\"%s\" tests/process.c dump \"%s\"", selfname, resfile);
     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &startup, &info), "CreateProcess\n");
 
     /* get all startup events up to the entry point break exception */
@@ -1358,7 +1382,7 @@ static void test_Console(void)
     cpOut = GetConsoleOutputCP();
 
     get_file_name(resfile);
-    sprintf(buffer, "\"%s\" tests/process.c \"%s\" console", selfname, resfile);
+    sprintf(buffer, "\"%s\" tests/process.c dump \"%s\" console", selfname, resfile);
     ok(CreateProcessA(NULL, buffer, NULL, NULL, TRUE, 0, NULL, NULL, &startup, &info), "CreateProcess\n");
 
     /* wait for child to terminate */
@@ -1472,7 +1496,7 @@ static void test_Console(void)
     startup.hStdError = hChildOutInh;
 
     get_file_name(resfile);
-    sprintf(buffer, "\"%s\" tests/process.c \"%s\" stdhandle", selfname, resfile);
+    sprintf(buffer, "\"%s\" tests/process.c dump \"%s\" stdhandle", selfname, resfile);
     ok(CreateProcessA(NULL, buffer, NULL, NULL, TRUE, DETACHED_PROCESS, NULL, NULL, &startup, &info), "CreateProcess\n");
     ok(CloseHandle(hChildInInh), "Closing handle\n");
     ok(CloseHandle(hChildOutInh), "Closing handle\n");
@@ -1509,7 +1533,7 @@ static  void    test_ExitCode(void)
     startup.wShowWindow = SW_SHOWNORMAL;
 
     get_file_name(resfile);
-    sprintf(buffer, "\"%s\" tests/process.c \"%s\" exit_code", selfname, resfile);
+    sprintf(buffer, "\"%s\" tests/process.c dump \"%s\" exit_code", selfname, resfile);
     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info), "CreateProcess\n");
 
     /* wait for child to terminate */
@@ -1607,6 +1631,34 @@ static void test_OpenProcess(void)
 
     CloseHandle(hproc);
 
+    hproc = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, GetCurrentProcessId());
+    if (hproc)
+    {
+        SetLastError(0xdeadbeef);
+        memset(&info, 0xcc, sizeof(info));
+        read_bytes = VirtualQueryEx(hproc, addr1, &info, sizeof(info));
+        if (read_bytes) /* win8 */
+        {
+            ok(read_bytes == sizeof(info), "VirtualQueryEx error %d\n", GetLastError());
+            ok(info.BaseAddress == addr1, "%p != %p\n", info.BaseAddress, addr1);
+            ok(info.AllocationBase == addr1, "%p != %p\n", info.AllocationBase, addr1);
+            ok(info.AllocationProtect == PAGE_NOACCESS, "%x != PAGE_NOACCESS\n", info.AllocationProtect);
+            ok(info.RegionSize == 0x10000, "%lx != 0x10000\n", info.RegionSize);
+            ok(info.State == MEM_RESERVE, "%x != MEM_RESERVE\n", info.State);
+            ok(info.Protect == 0, "%x != PAGE_NOACCESS\n", info.Protect);
+            ok(info.Type == MEM_PRIVATE, "%x != MEM_PRIVATE\n", info.Type);
+        }
+        else /* before win8 */
+            ok(GetLastError() == ERROR_ACCESS_DENIED, "wrong error %d\n", GetLastError());
+
+        SetLastError(0xdeadbeef);
+        ok(!pVirtualFreeEx(hproc, addr1, 0, MEM_RELEASE),
+           "VirtualFreeEx without PROCESS_VM_OPERATION rights should fail\n");
+        ok(GetLastError() == ERROR_ACCESS_DENIED, "wrong error %d\n", GetLastError());
+
+        CloseHandle(hproc);
+    }
+
     ok(VirtualFree(addr1, 0, MEM_RELEASE), "VirtualFree failed\n");
 }
 
@@ -2133,11 +2185,550 @@ static void test_DuplicateHandle(void)
     CloseHandle(out);
 }
 
-void test_StartupNoConsole(void)
+#define test_completion(a, b, c, d, e) _test_completion(__LINE__, a, b, c, d, e)
+static void _test_completion(int line, HANDLE port, DWORD ekey, ULONG_PTR evalue, ULONG_PTR eoverlapped, DWORD wait)
+{
+    LPOVERLAPPED overlapped;
+    ULONG_PTR value;
+    DWORD key;
+    BOOL ret;
+
+    ret = GetQueuedCompletionStatus(port, &key, &value, &overlapped, wait);
+
+    ok_(__FILE__, line)(ret, "GetQueuedCompletionStatus: %x\n", GetLastError());
+    if (ret)
+    {
+        ok_(__FILE__, line)(key == ekey, "unexpected key %x\n", key);
+        ok_(__FILE__, line)(value == evalue, "unexpected value %p\n", (void *)value);
+        ok_(__FILE__, line)(overlapped == (LPOVERLAPPED)eoverlapped, "unexpected overlapped %p\n", overlapped);
+    }
+}
+
+#define create_process(cmd, pi) _create_process(__LINE__, cmd, pi)
+static void _create_process(int line, const char *command, LPPROCESS_INFORMATION pi)
+{
+    BOOL ret;
+    char buffer[MAX_PATH];
+    STARTUPINFOA si = {0};
+
+    sprintf(buffer, "\"%s\" tests/process.c %s", selfname, command);
+
+    ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &si, pi);
+    ok_(__FILE__, line)(ret, "CreateProcess error %u\n", GetLastError());
+}
+
+
+static void test_IsProcessInJob(void)
+{
+    HANDLE job, job2;
+    PROCESS_INFORMATION pi;
+    BOOL ret, out;
+    DWORD dwret;
+
+    if (!pIsProcessInJob)
+    {
+        win_skip("IsProcessInJob not available.\n");
+        return;
+    }
+
+    job = pCreateJobObjectW(NULL, NULL);
+    ok(job != NULL, "CreateJobObject error %u\n", GetLastError());
+
+    job2 = pCreateJobObjectW(NULL, NULL);
+    ok(job2 != NULL, "CreateJobObject error %u\n", GetLastError());
+
+    create_process("wait", &pi);
+
+    out = TRUE;
+    ret = pIsProcessInJob(pi.hProcess, job, &out);
+    ok(ret, "IsProcessInJob error %u\n", GetLastError());
+    ok(!out, "IsProcessInJob returned out=%u\n", out);
+
+    out = TRUE;
+    ret = pIsProcessInJob(pi.hProcess, job2, &out);
+    ok(ret, "IsProcessInJob error %u\n", GetLastError());
+    ok(!out, "IsProcessInJob returned out=%u\n", out);
+
+    out = TRUE;
+    ret = pIsProcessInJob(pi.hProcess, NULL, &out);
+    ok(ret, "IsProcessInJob error %u\n", GetLastError());
+    ok(!out, "IsProcessInJob returned out=%u\n", out);
+
+    ret = pAssignProcessToJobObject(job, pi.hProcess);
+    ok(ret, "AssignProcessToJobObject error %u\n", GetLastError());
+
+    out = FALSE;
+    ret = pIsProcessInJob(pi.hProcess, job, &out);
+    ok(ret, "IsProcessInJob error %u\n", GetLastError());
+    ok(out, "IsProcessInJob returned out=%u\n", out);
+
+    out = TRUE;
+    ret = pIsProcessInJob(pi.hProcess, job2, &out);
+    ok(ret, "IsProcessInJob error %u\n", GetLastError());
+    ok(!out, "IsProcessInJob returned out=%u\n", out);
+
+    out = FALSE;
+    ret = pIsProcessInJob(pi.hProcess, NULL, &out);
+    ok(ret, "IsProcessInJob error %u\n", GetLastError());
+    ok(out, "IsProcessInJob returned out=%u\n", out);
+
+    TerminateProcess(pi.hProcess, 0);
+
+    dwret = WaitForSingleObject(pi.hProcess, 1000);
+    ok(dwret == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", dwret);
+
+    out = FALSE;
+    ret = pIsProcessInJob(pi.hProcess, job, &out);
+    ok(ret, "IsProcessInJob error %u\n", GetLastError());
+    ok(out, "IsProcessInJob returned out=%u\n", out);
+
+    CloseHandle(pi.hProcess);
+    CloseHandle(pi.hThread);
+    CloseHandle(job);
+    CloseHandle(job2);
+}
+
+static void test_TerminateJobObject(void)
+{
+    HANDLE job;
+    PROCESS_INFORMATION pi;
+    BOOL ret;
+    DWORD dwret;
+
+    job = pCreateJobObjectW(NULL, NULL);
+    ok(job != NULL, "CreateJobObject error %u\n", GetLastError());
+
+    create_process("wait", &pi);
+
+    ret = pAssignProcessToJobObject(job, pi.hProcess);
+    ok(ret, "AssignProcessToJobObject error %u\n", GetLastError());
+
+    ret = pTerminateJobObject(job, 123);
+    ok(ret, "TerminateJobObject error %u\n", GetLastError());
+
+    dwret = WaitForSingleObject(pi.hProcess, 1000);
+    ok(dwret == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", dwret);
+    if (dwret == WAIT_TIMEOUT) TerminateProcess(pi.hProcess, 0);
+
+    ret = GetExitCodeProcess(pi.hProcess, &dwret);
+    ok(ret, "GetExitCodeProcess error %u\n", GetLastError());
+    ok(dwret == 123 || broken(dwret == 0) /* randomly fails on Win 2000 / XP */,
+       "wrong exitcode %u\n", dwret);
+
+    CloseHandle(pi.hProcess);
+    CloseHandle(pi.hThread);
+
+    /* Test adding an already terminated process to a job object */
+    create_process("exit", &pi);
+
+    dwret = WaitForSingleObject(pi.hProcess, 1000);
+    ok(dwret == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", dwret);
+
+    SetLastError(0xdeadbeef);
+    ret = pAssignProcessToJobObject(job, pi.hProcess);
+    ok(!ret, "AssignProcessToJobObject unexpectedly succeeded\n");
+    expect_eq_d(ERROR_ACCESS_DENIED, GetLastError());
+
+    CloseHandle(pi.hProcess);
+    CloseHandle(pi.hThread);
+
+    CloseHandle(job);
+}
+
+static void test_QueryInformationJobObject(void)
 {
+    char buf[sizeof(JOBOBJECT_BASIC_PROCESS_ID_LIST) + sizeof(ULONG_PTR) * 4];
+    PJOBOBJECT_BASIC_PROCESS_ID_LIST pid_list = (JOBOBJECT_BASIC_PROCESS_ID_LIST *)buf;
+    JOBOBJECT_EXTENDED_LIMIT_INFORMATION ext_limit_info;
+    JOBOBJECT_BASIC_LIMIT_INFORMATION *basic_limit_info = &ext_limit_info.BasicLimitInformation;
+    DWORD dwret, ret_len;
+    PROCESS_INFORMATION pi[2];
+    HANDLE job;
+    BOOL ret;
+
+    job = pCreateJobObjectW(NULL, NULL);
+    ok(job != NULL, "CreateJobObject error %u\n", GetLastError());
+
+    /* Only active processes are returned */
+    create_process("exit", &pi[0]);
+    ret = pAssignProcessToJobObject(job, pi[0].hProcess);
+    ok(ret, "AssignProcessToJobObject error %u\n", GetLastError());
+    dwret = WaitForSingleObject(pi[0].hProcess, 1000);
+    ok(dwret == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", dwret);
+
+    CloseHandle(pi[0].hProcess);
+    CloseHandle(pi[0].hThread);
+
+    create_process("wait", &pi[0]);
+    ret = pAssignProcessToJobObject(job, pi[0].hProcess);
+    ok(ret, "AssignProcessToJobObject error %u\n", GetLastError());
+
+    create_process("wait", &pi[1]);
+    ret = pAssignProcessToJobObject(job, pi[1].hProcess);
+    ok(ret, "AssignProcessToJobObject error %u\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = QueryInformationJobObject(job, JobObjectBasicProcessIdList, pid_list,
+                                    FIELD_OFFSET(JOBOBJECT_BASIC_PROCESS_ID_LIST, ProcessIdList), &ret_len);
+    ok(!ret, "QueryInformationJobObject expected failure\n");
+    todo_wine
+    expect_eq_d(ERROR_BAD_LENGTH, GetLastError());
+
+    SetLastError(0xdeadbeef);
+    memset(buf, 0, sizeof(buf));
+    pid_list->NumberOfAssignedProcesses = 42;
+    pid_list->NumberOfProcessIdsInList  = 42;
+    ret = QueryInformationJobObject(job, JobObjectBasicProcessIdList, pid_list,
+                                    FIELD_OFFSET(JOBOBJECT_BASIC_PROCESS_ID_LIST, ProcessIdList[1]), &ret_len);
+    ok(!ret, "QueryInformationJobObject expected failure\n");
+    todo_wine
+    expect_eq_d(ERROR_MORE_DATA, GetLastError());
+    if (ret)
+    {
+        expect_eq_d(42, pid_list->NumberOfAssignedProcesses);
+        expect_eq_d(42, pid_list->NumberOfProcessIdsInList);
+    }
+
+    memset(buf, 0, sizeof(buf));
+    ret = pQueryInformationJobObject(job, JobObjectBasicProcessIdList, pid_list, sizeof(buf), &ret_len);
+    todo_wine
+    ok(ret, "QueryInformationJobObject error %u\n", GetLastError());
+    if(ret)
+    {
+        if (pid_list->NumberOfAssignedProcesses == 3) /* Win 8 */
+            win_skip("Number of assigned processes broken on Win 8\n");
+        else
+        {
+            ULONG_PTR *list = pid_list->ProcessIdList;
+
+            ok(ret_len == FIELD_OFFSET(JOBOBJECT_BASIC_PROCESS_ID_LIST, ProcessIdList[2]),
+               "QueryInformationJobObject returned ret_len=%u\n", ret_len);
+
+            expect_eq_d(2, pid_list->NumberOfAssignedProcesses);
+            expect_eq_d(2, pid_list->NumberOfProcessIdsInList);
+            expect_eq_d(pi[0].dwProcessId, list[0]);
+            expect_eq_d(pi[1].dwProcessId, list[1]);
+        }
+    }
+
+    /* test JobObjectBasicLimitInformation */
+    ret = pQueryInformationJobObject(job, JobObjectBasicLimitInformation, basic_limit_info,
+                                     sizeof(*basic_limit_info) - 1, &ret_len);
+    ok(!ret, "QueryInformationJobObject expected failure\n");
+    expect_eq_d(ERROR_BAD_LENGTH, GetLastError());
+
+    ret_len = 0xdeadbeef;
+    memset(basic_limit_info, 0x11, sizeof(*basic_limit_info));
+    ret = pQueryInformationJobObject(job, JobObjectBasicLimitInformation, basic_limit_info,
+                                     sizeof(*basic_limit_info), &ret_len);
+    ok(ret, "QueryInformationJobObject error %u\n", GetLastError());
+    ok(ret_len == sizeof(*basic_limit_info), "QueryInformationJobObject returned ret_len=%u\n", ret_len);
+    expect_eq_d(0, basic_limit_info->LimitFlags);
+
+    /* test JobObjectExtendedLimitInformation */
+    ret = pQueryInformationJobObject(job, JobObjectExtendedLimitInformation, &ext_limit_info,
+                                     sizeof(ext_limit_info) - 1, &ret_len);
+    ok(!ret, "QueryInformationJobObject expected failure\n");
+    expect_eq_d(ERROR_BAD_LENGTH, GetLastError());
+
+    ret_len = 0xdeadbeef;
+    memset(&ext_limit_info, 0x11, sizeof(ext_limit_info));
+    ret = pQueryInformationJobObject(job, JobObjectExtendedLimitInformation, &ext_limit_info,
+                                     sizeof(ext_limit_info), &ret_len);
+    ok(ret, "QueryInformationJobObject error %u\n", GetLastError());
+    ok(ret_len == sizeof(ext_limit_info), "QueryInformationJobObject returned ret_len=%u\n", ret_len);
+    expect_eq_d(0, basic_limit_info->LimitFlags);
+
+    TerminateProcess(pi[0].hProcess, 0);
+    CloseHandle(pi[0].hProcess);
+    CloseHandle(pi[0].hThread);
+
+    TerminateProcess(pi[1].hProcess, 0);
+    CloseHandle(pi[1].hProcess);
+    CloseHandle(pi[1].hThread);
+
+    CloseHandle(job);
+}
+
+static void test_CompletionPort(void)
+{
+    JOBOBJECT_ASSOCIATE_COMPLETION_PORT port_info;
+    PROCESS_INFORMATION pi;
+    HANDLE job, port;
+    DWORD dwret;
+    BOOL ret;
+
+    job = pCreateJobObjectW(NULL, NULL);
+    ok(job != NULL, "CreateJobObject error %u\n", GetLastError());
+
+    port = pCreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1);
+    ok(port != NULL, "CreateIoCompletionPort error %u\n", GetLastError());
+
+    port_info.CompletionKey = job;
+    port_info.CompletionPort = port;
+    ret = pSetInformationJobObject(job, JobObjectAssociateCompletionPortInformation, &port_info, sizeof(port_info));
+    ok(ret, "SetInformationJobObject error %u\n", GetLastError());
+
+    create_process("wait", &pi);
+
+    ret = pAssignProcessToJobObject(job, pi.hProcess);
+    ok(ret, "AssignProcessToJobObject error %u\n", GetLastError());
+
+    test_completion(port, JOB_OBJECT_MSG_NEW_PROCESS, (DWORD_PTR)job, pi.dwProcessId, 0);
+
+    TerminateProcess(pi.hProcess, 0);
+    dwret = WaitForSingleObject(pi.hProcess, 1000);
+    ok(dwret == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", dwret);
+
+    test_completion(port, JOB_OBJECT_MSG_EXIT_PROCESS, (DWORD_PTR)job, pi.dwProcessId, 0);
+    test_completion(port, JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO, (DWORD_PTR)job, 0, 100);
+
+    CloseHandle(pi.hProcess);
+    CloseHandle(pi.hThread);
+    CloseHandle(job);
+    CloseHandle(port);
+}
+
+static void test_KillOnJobClose(void)
+{
+    JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info;
+    PROCESS_INFORMATION pi;
+    DWORD dwret;
+    HANDLE job;
+    BOOL ret;
+
+    job = pCreateJobObjectW(NULL, NULL);
+    ok(job != NULL, "CreateJobObject error %u\n", GetLastError());
+
+    limit_info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
+    ret = pSetInformationJobObject(job, JobObjectExtendedLimitInformation, &limit_info, sizeof(limit_info));
+    if (!ret && GetLastError() == ERROR_INVALID_PARAMETER)
+    {
+        win_skip("Kill on job close limit not available\n");
+        return;
+    }
+    ok(ret, "SetInformationJobObject error %u\n", GetLastError());
+
+    create_process("wait", &pi);
+
+    ret = pAssignProcessToJobObject(job, pi.hProcess);
+    ok(ret, "AssignProcessToJobObject error %u\n", GetLastError());
+
+    CloseHandle(job);
+
+    dwret = WaitForSingleObject(pi.hProcess, 1000);
+    ok(dwret == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", dwret);
+    if (dwret == WAIT_TIMEOUT) TerminateProcess(pi.hProcess, 0);
+
+    CloseHandle(pi.hProcess);
+    CloseHandle(pi.hThread);
+}
+
+static void test_WaitForJobObject(void)
+{
+    HANDLE job;
+    PROCESS_INFORMATION pi;
+    BOOL ret;
+    DWORD dwret;
+
+    /* test waiting for a job object when the process is killed */
+    job = pCreateJobObjectW(NULL, NULL);
+    ok(job != NULL, "CreateJobObject error %u\n", GetLastError());
+
+    dwret = WaitForSingleObject(job, 100);
+    ok(dwret == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", dwret);
+
+    create_process("wait", &pi);
+
+    ret = pAssignProcessToJobObject(job, pi.hProcess);
+    ok(ret, "AssignProcessToJobObject error %u\n", GetLastError());
+
+    dwret = WaitForSingleObject(job, 100);
+    ok(dwret == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", dwret);
+
+    ret = pTerminateJobObject(job, 123);
+    ok(ret, "TerminateJobObject error %u\n", GetLastError());
+
+    dwret = WaitForSingleObject(job, 500);
+    ok(dwret == WAIT_OBJECT_0 || broken(dwret == WAIT_TIMEOUT),
+       "WaitForSingleObject returned %u\n", dwret);
+
+    if (dwret == WAIT_TIMEOUT) /* Win 2000/XP */
+    {
+        CloseHandle(pi.hProcess);
+        CloseHandle(pi.hThread);
+        CloseHandle(job);
+        win_skip("TerminateJobObject doesn't signal job, skipping tests\n");
+        return;
+    }
+
+    /* the object is not reset immediately */
+    dwret = WaitForSingleObject(job, 100);
+    ok(dwret == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", dwret);
+
+    CloseHandle(pi.hProcess);
+    CloseHandle(pi.hThread);
+
+    /* creating a new process doesn't reset the signalled state */
+    create_process("wait", &pi);
+
+    ret = pAssignProcessToJobObject(job, pi.hProcess);
+    ok(ret, "AssignProcessToJobObject error %u\n", GetLastError());
+
+    dwret = WaitForSingleObject(job, 100);
+    ok(dwret == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", dwret);
+
+    ret = pTerminateJobObject(job, 123);
+    ok(ret, "TerminateJobObject error %u\n", GetLastError());
+
+    CloseHandle(pi.hProcess);
+    CloseHandle(pi.hThread);
+
+    CloseHandle(job);
+
+    /* repeat the test, but this time the process terminates properly */
+    job = pCreateJobObjectW(NULL, NULL);
+    ok(job != NULL, "CreateJobObject error %u\n", GetLastError());
+
+    dwret = WaitForSingleObject(job, 100);
+    ok(dwret == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", dwret);
+
+    create_process("exit", &pi);
+
+    ret = pAssignProcessToJobObject(job, pi.hProcess);
+    ok(ret, "AssignProcessToJobObject error %u\n", GetLastError());
+
+    dwret = WaitForSingleObject(job, 100);
+    ok(dwret == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", dwret);
+
+    CloseHandle(pi.hProcess);
+    CloseHandle(pi.hThread);
+    CloseHandle(job);
+}
+
+static HANDLE test_AddSelfToJob(void)
+{
+    HANDLE job;
+    BOOL ret;
+
+    job = pCreateJobObjectW(NULL, NULL);
+    ok(job != NULL, "CreateJobObject error %u\n", GetLastError());
+
+    ret = pAssignProcessToJobObject(job, GetCurrentProcess());
+    ok(ret, "AssignProcessToJobObject error %u\n", GetLastError());
+
+    return job;
+}
+
+static void test_jobInheritance(HANDLE job)
+{
+    char buffer[MAX_PATH];
+    PROCESS_INFORMATION pi;
+    STARTUPINFOA si = {0};
+    DWORD dwret;
+    BOOL ret, out;
+
+    if (!pIsProcessInJob)
+    {
+        win_skip("IsProcessInJob not available.\n");
+        return;
+    }
+
+    sprintf(buffer, "\"%s\" tests/process.c %s", selfname, "exit");
+
+    ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
+    ok(ret, "CreateProcessA error %u\n", GetLastError());
+
+    out = FALSE;
+    ret = pIsProcessInJob(pi.hProcess, job, &out);
+    ok(ret, "IsProcessInJob error %u\n", GetLastError());
+    ok(out, "IsProcessInJob returned out=%u\n", out);
+
+    dwret = WaitForSingleObject(pi.hProcess, 1000);
+    ok(dwret == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", dwret);
+
+    CloseHandle(pi.hProcess);
+    CloseHandle(pi.hThread);
+}
+
+static void test_BreakawayOk(HANDLE job)
+{
+    JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info;
+    PROCESS_INFORMATION pi;
+    STARTUPINFOA si = {0};
+    char buffer[MAX_PATH];
+    BOOL ret, out;
+    DWORD dwret;
+
+    if (!pIsProcessInJob)
+    {
+        win_skip("IsProcessInJob not available.\n");
+        return;
+    }
+
+    sprintf(buffer, "\"%s\" tests/process.c %s", selfname, "exit");
+
+    ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, CREATE_BREAKAWAY_FROM_JOB, NULL, NULL, &si, &pi);
+    ok(!ret, "CreateProcessA expected failure\n");
+    expect_eq_d(ERROR_ACCESS_DENIED, GetLastError());
+
+    if (ret)
+    {
+        TerminateProcess(pi.hProcess, 0);
+
+        dwret = WaitForSingleObject(pi.hProcess, 1000);
+        ok(dwret == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", dwret);
+
+        CloseHandle(pi.hProcess);
+        CloseHandle(pi.hThread);
+    }
+
+    limit_info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_BREAKAWAY_OK;
+    ret = pSetInformationJobObject(job, JobObjectExtendedLimitInformation, &limit_info, sizeof(limit_info));
+    ok(ret, "SetInformationJobObject error %u\n", GetLastError());
+
+    ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, CREATE_BREAKAWAY_FROM_JOB, NULL, NULL, &si, &pi);
+    ok(ret, "CreateProcessA error %u\n", GetLastError());
+
+    ret = pIsProcessInJob(pi.hProcess, job, &out);
+    ok(ret, "IsProcessInJob error %u\n", GetLastError());
+    ok(!out, "IsProcessInJob returned out=%u\n", out);
+
+    dwret = WaitForSingleObject(pi.hProcess, 1000);
+    ok(dwret == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", dwret);
+
+    CloseHandle(pi.hProcess);
+    CloseHandle(pi.hThread);
+
+    limit_info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK;
+    ret = pSetInformationJobObject(job, JobObjectExtendedLimitInformation, &limit_info, sizeof(limit_info));
+    ok(ret, "SetInformationJobObject error %u\n", GetLastError());
+
+    ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
+    ok(ret, "CreateProcess error %u\n", GetLastError());
+
+    ret = pIsProcessInJob(pi.hProcess, job, &out);
+    ok(ret, "IsProcessInJob error %u\n", GetLastError());
+    ok(!out, "IsProcessInJob returned out=%u\n", out);
+
+    dwret = WaitForSingleObject(pi.hProcess, 1000);
+    ok(dwret == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", dwret);
+
+    CloseHandle(pi.hProcess);
+    CloseHandle(pi.hThread);
+
+    /* unset breakaway ok */
+    limit_info.BasicLimitInformation.LimitFlags = 0;
+    ret = pSetInformationJobObject(job, JobObjectExtendedLimitInformation, &limit_info, sizeof(limit_info));
+    ok(ret, "SetInformationJobObject error %u\n", GetLastError());
+}
+
+static void test_StartupNoConsole(void)
+{
+#ifndef _WIN64
     char                buffer[MAX_PATH];
-    PROCESS_INFORMATION        info;
-    STARTUPINFOA           startup;
+    STARTUPINFOA        startup;
+    PROCESS_INFORMATION info;
     DWORD               code;
 
     if (!pNtCurrentTeb)
@@ -2151,34 +2742,260 @@ void test_StartupNoConsole(void)
     startup.dwFlags = STARTF_USESHOWWINDOW;
     startup.wShowWindow = SW_SHOWNORMAL;
     get_file_name(resfile);
-    sprintf(buffer, "\"%s\" tests/process.c \"%s\"", selfname, resfile);
+    sprintf(buffer, "\"%s\" tests/process.c dump \"%s\"", selfname, resfile);
     ok(CreateProcessA(NULL, buffer, NULL, NULL, TRUE, DETACHED_PROCESS, NULL, NULL, &startup,
                       &info), "CreateProcess\n");
     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
     ok(GetExitCodeProcess(info.hProcess, &code), "Getting exit code\n");
     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
-    okChildInt("StartupInfoA", "hStdInput", (DWORD_PTR)INVALID_HANDLE_VALUE);
-    okChildInt("StartupInfoA", "hStdOutput", (DWORD_PTR)INVALID_HANDLE_VALUE);
-    okChildInt("StartupInfoA", "hStdError", (DWORD_PTR)INVALID_HANDLE_VALUE);
-    okChildInt("TEB", "hStdInput", (DWORD_PTR)0);
-    okChildInt("TEB", "hStdOutput", (DWORD_PTR)0);
-    okChildInt("TEB", "hStdError", (DWORD_PTR)0);
+    okChildInt("StartupInfoA", "hStdInput", (UINT)INVALID_HANDLE_VALUE);
+    okChildInt("StartupInfoA", "hStdOutput", (UINT)INVALID_HANDLE_VALUE);
+    okChildInt("StartupInfoA", "hStdError", (UINT)INVALID_HANDLE_VALUE);
+    okChildInt("TEB", "hStdInput", 0);
+    okChildInt("TEB", "hStdOutput", 0);
+    okChildInt("TEB", "hStdError", 0);
     release_memory();
-    assert(DeleteFileA(resfile) != 0);
+    DeleteFileA(resfile);
+#endif
+}
+
+static void test_GetNumaProcessorNode(void)
+{
+    SYSTEM_INFO si;
+    UCHAR node;
+    BOOL ret;
+    int i;
+
+    if (!pGetNumaProcessorNode)
+    {
+        win_skip("GetNumaProcessorNode is missing\n");
+        return;
+    }
+
+    GetSystemInfo(&si);
+    for (i = 0; i < 256; i++)
+    {
+        SetLastError(0xdeadbeef);
+        node = (i < si.dwNumberOfProcessors) ? 0xFF : 0xAA;
+        ret = pGetNumaProcessorNode(i, &node);
+        if (i < si.dwNumberOfProcessors)
+        {
+            ok(ret, "GetNumaProcessorNode returned FALSE for processor %d\n", i);
+            ok(node != 0xFF, "expected node != 0xFF, but got 0xFF\n");
+        }
+        else
+        {
+            ok(!ret, "GetNumaProcessorNode returned TRUE for processor %d\n", i);
+            ok(node == 0xFF || broken(node == 0xAA) /* WinXP */, "expected node 0xFF, got %x\n", node);
+            ok(GetLastError() == ERROR_INVALID_PARAMETER, "expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+        }
+    }
+}
+
+static void test_session_info(void)
+{
+    DWORD session_id, active_session;
+    BOOL r;
+
+    if (!pProcessIdToSessionId)
+    {
+        win_skip("ProcessIdToSessionId is missing\n");
+        return;
+    }
+
+    r = pProcessIdToSessionId(GetCurrentProcessId(), &session_id);
+    ok(r, "ProcessIdToSessionId failed: %u\n", GetLastError());
+    trace("session_id = %x\n", session_id);
+
+    active_session = pWTSGetActiveConsoleSessionId();
+    trace("active_session = %x\n", active_session);
+}
+
+static void test_process_info(void)
+{
+    char buf[4096];
+    static const ULONG info_size[] =
+    {
+        sizeof(PROCESS_BASIC_INFORMATION) /* ProcessBasicInformation */,
+        sizeof(QUOTA_LIMITS) /* ProcessQuotaLimits */,
+        sizeof(IO_COUNTERS) /* ProcessIoCounters */,
+        sizeof(VM_COUNTERS) /* ProcessVmCounters */,
+        sizeof(KERNEL_USER_TIMES) /* ProcessTimes */,
+        sizeof(ULONG) /* ProcessBasePriority */,
+        sizeof(ULONG) /* ProcessRaisePriority */,
+        sizeof(HANDLE) /* ProcessDebugPort */,
+        sizeof(HANDLE) /* ProcessExceptionPort */,
+        0 /* FIXME: sizeof(PROCESS_ACCESS_TOKEN) ProcessAccessToken */,
+        0 /* FIXME: sizeof(PROCESS_LDT_INFORMATION) ProcessLdtInformation */,
+        0 /* FIXME: sizeof(PROCESS_LDT_SIZE) ProcessLdtSize */,
+        sizeof(ULONG) /* ProcessDefaultHardErrorMode */,
+        0 /* ProcessIoPortHandlers: kernel-mode only */,
+        0 /* FIXME: sizeof(POOLED_USAGE_AND_LIMITS) ProcessPooledUsageAndLimits */,
+        0 /* FIXME: sizeof(PROCESS_WS_WATCH_INFORMATION) ProcessWorkingSetWatch */,
+        sizeof(ULONG) /* ProcessUserModeIOPL */,
+        sizeof(BOOLEAN) /* ProcessEnableAlignmentFaultFixup */,
+        sizeof(PROCESS_PRIORITY_CLASS) /* ProcessPriorityClass */,
+        sizeof(ULONG) /* ProcessWx86Information */,
+        sizeof(ULONG) /* ProcessHandleCount */,
+        sizeof(ULONG_PTR) /* ProcessAffinityMask */,
+        sizeof(ULONG) /* ProcessPriorityBoost */,
+        0 /* sizeof(PROCESS_DEVICEMAP_INFORMATION) ProcessDeviceMap */,
+        0 /* sizeof(PROCESS_SESSION_INFORMATION) ProcessSessionInformation */,
+        0 /* sizeof(PROCESS_FOREGROUND_BACKGROUND) ProcessForegroundInformation */,
+        sizeof(ULONG_PTR) /* ProcessWow64Information */,
+        sizeof(buf) /* ProcessImageFileName */,
+        sizeof(ULONG) /* ProcessLUIDDeviceMapsEnabled */,
+        sizeof(ULONG) /* ProcessBreakOnTermination */,
+        sizeof(HANDLE) /* ProcessDebugObjectHandle */,
+        sizeof(ULONG) /* ProcessDebugFlags */,
+        sizeof(buf) /* ProcessHandleTracing */,
+        sizeof(ULONG) /* ProcessIoPriority */,
+        sizeof(ULONG) /* ProcessExecuteFlags */,
+#if 0 /* FIXME: Add remaning classes */
+        ProcessResourceManagement,
+        sizeof(ULONG) /* ProcessCookie */,
+        sizeof(SECTION_IMAGE_INFORMATION) /* ProcessImageInformation */,
+        sizeof(PROCESS_CYCLE_TIME_INFORMATION) /* ProcessCycleTime */,
+        sizeof(ULONG) /* ProcessPagePriority */,
+        40 /* ProcessInstrumentationCallback */,
+        sizeof(PROCESS_STACK_ALLOCATION_INFORMATION) /* ProcessThreadStackAllocation */,
+        sizeof(PROCESS_WS_WATCH_INFORMATION_EX[]) /* ProcessWorkingSetWatchEx */,
+        sizeof(buf) /* ProcessImageFileNameWin32 */,
+        sizeof(HANDLE) /* ProcessImageFileMapping */,
+        sizeof(PROCESS_AFFINITY_UPDATE_MODE) /* ProcessAffinityUpdateMode */,
+        sizeof(PROCESS_MEMORY_ALLOCATION_MODE) /* ProcessMemoryAllocationMode */,
+        sizeof(USHORT[]) /* ProcessGroupInformation */,
+        sizeof(ULONG) /* ProcessTokenVirtualizationEnabled */,
+        sizeof(ULONG_PTR) /* ProcessConsoleHostProcess */,
+        sizeof(PROCESS_WINDOW_INFORMATION) /* ProcessWindowInformation */,
+        sizeof(PROCESS_HANDLE_SNAPSHOT_INFORMATION) /* ProcessHandleInformation */,
+        sizeof(PROCESS_MITIGATION_POLICY_INFORMATION) /* ProcessMitigationPolicy */,
+        sizeof(ProcessDynamicFunctionTableInformation) /* ProcessDynamicFunctionTableInformation */,
+        sizeof(?) /* ProcessHandleCheckingMode */,
+        sizeof(PROCESS_KEEPALIVE_COUNT_INFORMATION) /* ProcessKeepAliveCount */,
+        sizeof(PROCESS_REVOKE_FILE_HANDLES_INFORMATION) /* ProcessRevokeFileHandles */,
+        sizeof(PROCESS_WORKING_SET_CONTROL) /* ProcessWorkingSetControl */,
+        sizeof(?) /* ProcessHandleTable */,
+        sizeof(?) /* ProcessCheckStackExtentsMode */,
+        sizeof(buf) /* ProcessCommandLineInformation */,
+        sizeof(PS_PROTECTION) /* ProcessProtectionInformation */,
+        sizeof(PROCESS_MEMORY_EXHAUSTION_INFO) /* ProcessMemoryExhaustion */,
+        sizeof(PROCESS_FAULT_INFORMATION) /* ProcessFaultInformation */,
+        sizeof(PROCESS_TELEMETRY_ID_INFORMATION) /* ProcessTelemetryIdInformation */,
+        sizeof(PROCESS_COMMIT_RELEASE_INFORMATION) /* ProcessCommitReleaseInformation */,
+        sizeof(?) /* ProcessDefaultCpuSetsInformation */,
+        sizeof(?) /* ProcessAllowedCpuSetsInformation */,
+        0 /* ProcessReserved1Information */,
+        0 /* ProcessReserved2Information */,
+        sizeof(?) /* ProcessSubsystemProcess */,
+        sizeof(PROCESS_JOB_MEMORY_INFO) /* ProcessJobMemoryInformation */,
+#endif
+    };
+    HANDLE hproc;
+    ULONG i, status, ret_len, size;
+
+    if (!pNtQueryInformationProcess)
+    {
+        win_skip("NtQueryInformationProcess is not available on this platform\n");
+        return;
+    }
+
+    hproc = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, GetCurrentProcessId());
+    if (!hproc)
+    {
+        win_skip("PROCESS_QUERY_LIMITED_INFORMATION is not supported on this platform\n");
+        return;
+    }
+
+    for (i = 0; i < MaxProcessInfoClass; i++)
+    {
+        size = info_size[i];
+        if (!size) size = sizeof(buf);
+        ret_len = 0;
+        status = pNtQueryInformationProcess(hproc, i, buf, info_size[i], &ret_len);
+        if (status == STATUS_NOT_IMPLEMENTED) continue;
+        if (status == STATUS_INVALID_INFO_CLASS) continue;
+        if (status == STATUS_INFO_LENGTH_MISMATCH) continue;
+
+        switch (i)
+        {
+        case ProcessBasicInformation:
+        case ProcessQuotaLimits:
+        case ProcessTimes:
+        case ProcessPriorityClass:
+        case ProcessPriorityBoost:
+        case ProcessLUIDDeviceMapsEnabled:
+        case 33 /* ProcessIoPriority */:
+        case ProcessIoCounters:
+        case ProcessVmCounters:
+        case ProcessWow64Information:
+        case ProcessDefaultHardErrorMode:
+        case ProcessHandleCount:
+            ok(status == STATUS_SUCCESS, "for info %u expected STATUS_SUCCESS, got %08x (ret_len %u)\n", i, status, ret_len);
+            break;
+
+        case ProcessImageFileName:
+todo_wine
+            ok(status == STATUS_SUCCESS, "for info %u expected STATUS_SUCCESS, got %08x (ret_len %u)\n", i, status, ret_len);
+            break;
+
+        case ProcessAffinityMask:
+        case ProcessBreakOnTermination:
+            ok(status == STATUS_ACCESS_DENIED /* before win8 */ || status == STATUS_SUCCESS /* win8 is less strict */,
+               "for info %u expected STATUS_SUCCESS, got %08x (ret_len %u)\n", i, status, ret_len);
+            break;
+
+        case ProcessDebugObjectHandle:
+            ok(status == STATUS_ACCESS_DENIED || status == STATUS_PORT_NOT_SET,
+               "for info %u expected STATUS_ACCESS_DENIED, got %08x (ret_len %u)\n", i, status, ret_len);
+            break;
+
+        case ProcessExecuteFlags:
+        case ProcessDebugPort:
+        case ProcessDebugFlags:
+todo_wine
+            ok(status == STATUS_ACCESS_DENIED, "for info %u expected STATUS_ACCESS_DENIED, got %08x (ret_len %u)\n", i, status, ret_len);
+            break;
+
+        default:
+            ok(status == STATUS_ACCESS_DENIED, "for info %u expected STATUS_ACCESS_DENIED, got %08x (ret_len %u)\n", i, status, ret_len);
+            break;
+        }
+    }
 
+    CloseHandle(hproc);
 }
 
 START_TEST(process)
 {
+    HANDLE job;
     BOOL b = init();
     ok(b, "Basic init of CreateProcess test\n");
     if (!b) return;
 
     if (myARGC >= 3)
     {
-        doChild(myARGV[2], (myARGC == 3) ? NULL : myARGV[3]);
+        if (!strcmp(myARGV[2], "dump") && myARGC >= 4)
+        {
+            doChild(myARGV[3], (myARGC >= 5) ? myARGV[4] : NULL);
+            return;
+        }
+        else if (!strcmp(myARGV[2], "wait"))
+        {
+            Sleep(30000);
+            ok(0, "Child process not killed\n");
+            return;
+        }
+        else if (!strcmp(myARGV[2], "exit"))
+        {
+            Sleep(100);
+            return;
+        }
+
+        ok(0, "Unexpected command %s\n", myARGV[2]);
         return;
     }
+    test_process_info();
     test_TerminateProcess();
     test_Startup();
     test_CommandLine();
@@ -2199,9 +3016,29 @@ START_TEST(process)
     test_RegistryQuota();
     test_DuplicateHandle();
     test_StartupNoConsole();
+    test_GetNumaProcessorNode();
+    test_session_info();
+
     /* things that can be tested:
      *  lookup:         check the way program to be executed is searched
      *  handles:        check the handle inheritance stuff (+sec options)
      *  console:        check if console creation parameters work
      */
+
+    if (!pCreateJobObjectW)
+    {
+        win_skip("No job object support\n");
+        return;
+    }
+
+    test_IsProcessInJob();
+    test_TerminateJobObject();
+    test_QueryInformationJobObject();
+    test_CompletionPort();
+    test_KillOnJobClose();
+    test_WaitForJobObject();
+    job = test_AddSelfToJob();
+    test_jobInheritance(job);
+    test_BreakawayOk(job);
+    CloseHandle(job);
 }
index 3fe52c5..d76d7b4 100755 (executable)
@@ -56,6 +56,9 @@ static VOID   (WINAPI *pReleaseSRWLockExclusive)(PSRWLOCK);
 static VOID   (WINAPI *pReleaseSRWLockShared)(PSRWLOCK);
 static BOOLEAN (WINAPI *pTryAcquireSRWLockExclusive)(PSRWLOCK);
 static BOOLEAN (WINAPI *pTryAcquireSRWLockShared)(PSRWLOCK);
+
+static NTSTATUS (WINAPI *pNtAllocateVirtualMemory)(HANDLE, PVOID *, ULONG, SIZE_T *, ULONG, ULONG);
+static NTSTATUS (WINAPI *pNtFreeVirtualMemory)(HANDLE, PVOID *, SIZE_T *, ULONG);
 static NTSTATUS (WINAPI *pNtWaitForMultipleObjects)(ULONG,const HANDLE*,BOOLEAN,BOOLEAN,const LARGE_INTEGER*);
 
 static void test_signalandwait(void)
@@ -102,7 +105,7 @@ static void test_signalandwait(void)
     for (i = 0; i < 10000; i++)
     {
         r = pSignalObjectAndWait(event[0], event[0], 0, FALSE);
-        ok( r == WAIT_OBJECT_0, "should succeed\n");
+        ok(r == WAIT_OBJECT_0, "should succeed\n");
     }
 
     /* event[0] is not signalled */
@@ -2308,8 +2311,184 @@ static void test_srwlock_example(void)
     trace("number of total exclusive accesses is %d\n", srwlock_protected_value);
 }
 
+static DWORD WINAPI alertable_wait_thread(void *param)
+{
+    HANDLE *semaphores = param;
+    LARGE_INTEGER timeout;
+    NTSTATUS status;
+    DWORD result;
+
+    ReleaseSemaphore(semaphores[0], 1, NULL);
+    result = WaitForMultipleObjectsEx(1, &semaphores[1], TRUE, 1000, TRUE);
+    ok(result == WAIT_IO_COMPLETION, "expected WAIT_IO_COMPLETION, got %u\n", result);
+    result = WaitForMultipleObjectsEx(1, &semaphores[1], TRUE, 200, TRUE);
+    ok(result == WAIT_OBJECT_0, "expected WAIT_OBJECT_0, got %u\n", result);
+
+    ReleaseSemaphore(semaphores[0], 1, NULL);
+    timeout.QuadPart = -10000000;
+    status = pNtWaitForMultipleObjects(1, &semaphores[1], FALSE, TRUE, &timeout);
+    ok(status == STATUS_USER_APC, "expected STATUS_USER_APC, got %08x\n", status);
+    timeout.QuadPart = -2000000;
+    status = pNtWaitForMultipleObjects(1, &semaphores[1], FALSE, TRUE, &timeout);
+    ok(status == STATUS_WAIT_0, "expected STATUS_WAIT_0, got %08x\n", status);
+
+    ReleaseSemaphore(semaphores[0], 1, NULL);
+    timeout.QuadPart = -10000000;
+    status = pNtWaitForMultipleObjects(1, &semaphores[1], FALSE, TRUE, &timeout);
+    ok(status == STATUS_USER_APC, "expected STATUS_USER_APC, got %08x\n", status);
+    result = WaitForSingleObject(semaphores[0], 0);
+    ok(result == WAIT_TIMEOUT, "expected WAIT_TIMEOUT, got %u\n", result);
+
+    return 0;
+}
+
+static void CALLBACK alertable_wait_apc(ULONG_PTR userdata)
+{
+    HANDLE *semaphores = (void *)userdata;
+    ReleaseSemaphore(semaphores[1], 1, NULL);
+}
+
+static void CALLBACK alertable_wait_apc2(ULONG_PTR userdata)
+{
+    HANDLE *semaphores = (void *)userdata;
+    DWORD result;
+
+    result = WaitForSingleObject(semaphores[0], 1000);
+    ok(result == WAIT_OBJECT_0, "expected WAIT_OBJECT_0, got %u\n", result);
+}
+
+static void test_alertable_wait(void)
+{
+    HANDLE thread, semaphores[2];
+    DWORD result;
+
+    semaphores[0] = CreateSemaphoreW(NULL, 0, 2, NULL);
+    ok(semaphores[0] != NULL, "CreateSemaphore failed with %u\n", GetLastError());
+    semaphores[1] = CreateSemaphoreW(NULL, 0, 1, NULL);
+    ok(semaphores[1] != NULL, "CreateSemaphore failed with %u\n", GetLastError());
+    thread = CreateThread(NULL, 0, alertable_wait_thread, semaphores, 0, NULL);
+    ok(thread != NULL, "CreateThread failed with %u\n", GetLastError());
+
+    result = WaitForSingleObject(semaphores[0], 1000);
+    ok(result == WAIT_OBJECT_0, "expected WAIT_OBJECT_0, got %u\n", result);
+    Sleep(100); /* ensure the thread is blocking in WaitForMultipleObjectsEx */
+    result = QueueUserAPC(alertable_wait_apc, thread, (ULONG_PTR)semaphores);
+    ok(result != 0, "QueueUserAPC failed with %u\n", GetLastError());
+
+    result = WaitForSingleObject(semaphores[0], 1000);
+    ok(result == WAIT_OBJECT_0, "expected WAIT_OBJECT_0, got %u\n", result);
+    Sleep(100); /* ensure the thread is blocking in NtWaitForMultipleObjects */
+    result = QueueUserAPC(alertable_wait_apc, thread, (ULONG_PTR)semaphores);
+    ok(result != 0, "QueueUserAPC failed with %u\n", GetLastError());
+
+    result = WaitForSingleObject(semaphores[0], 1000);
+    ok(result == WAIT_OBJECT_0, "expected WAIT_OBJECT_0, got %u\n", result);
+    Sleep(100); /* ensure the thread is blocking in NtWaitForMultipleObjects */
+    result = QueueUserAPC(alertable_wait_apc2, thread, (ULONG_PTR)semaphores);
+    ok(result != 0, "QueueUserAPC failed with %u\n", GetLastError());
+    result = QueueUserAPC(alertable_wait_apc2, thread, (ULONG_PTR)semaphores);
+    ok(result != 0, "QueueUserAPC failed with %u\n", GetLastError());
+    ReleaseSemaphore(semaphores[0], 2, NULL);
+
+    result = WaitForSingleObject(thread, 1000);
+    ok(result == WAIT_OBJECT_0, "expected WAIT_OBJECT_0, got %u\n", result);
+    CloseHandle(thread);
+    CloseHandle(semaphores[0]);
+    CloseHandle(semaphores[1]);
+}
+
+struct apc_deadlock_info
+{
+    PROCESS_INFORMATION *pi;
+    HANDLE event;
+    BOOL running;
+};
+
+static DWORD WINAPI apc_deadlock_thread(void *param)
+{
+    struct apc_deadlock_info *info = param;
+    PROCESS_INFORMATION *pi = info->pi;
+    NTSTATUS status;
+    SIZE_T size;
+    void *base;
+
+    while (info->running)
+    {
+        base = NULL;
+        size = 0x1000;
+        status = pNtAllocateVirtualMemory(pi->hProcess, &base, 0, &size,
+                                          MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
+        ok(!status, "expected STATUS_SUCCESS, got %08x\n", status);
+        ok(base != NULL, "expected base != NULL, got %p\n", base);
+        SetEvent(info->event);
+
+        size = 0;
+        status = pNtFreeVirtualMemory(pi->hProcess, &base, &size, MEM_RELEASE);
+        ok(!status, "expected STATUS_SUCCESS, got %08x\n", status);
+        SetEvent(info->event);
+    }
+
+    return 0;
+}
+
+static void test_apc_deadlock(void)
+{
+    struct apc_deadlock_info info;
+    PROCESS_INFORMATION pi;
+    STARTUPINFOA si = { sizeof(si) };
+    char cmdline[MAX_PATH];
+    HANDLE event, thread;
+    DWORD result;
+    BOOL success;
+    char **argv;
+    int i;
+
+    winetest_get_mainargs(&argv);
+    sprintf(cmdline, "\"%s\" sync apc_deadlock", argv[0]);
+    success = CreateProcessA(argv[0], cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
+    ok(success, "CreateProcess failed with %u\n", GetLastError());
+
+    event = CreateEventA(NULL, FALSE, FALSE, NULL);
+    ok(event != NULL, "CreateEvent failed with %u\n", GetLastError());
+
+    info.pi = &pi;
+    info.event = event;
+    info.running = TRUE;
+
+    thread = CreateThread(NULL, 0, apc_deadlock_thread, &info, 0, NULL);
+    ok(thread != NULL, "CreateThread failed with %u\n", GetLastError());
+    result = WaitForSingleObject(event, 1000);
+    ok(result == WAIT_OBJECT_0, "expected WAIT_OBJECT_0, got %u\n", result);
+
+    for (i = 0; i < 1000; i++)
+    {
+        result = SuspendThread(pi.hThread);
+        ok(result == 0, "expected 0, got %u\n", result);
+
+        WaitForSingleObject(event, 0); /* reset event */
+        result = WaitForSingleObject(event, 1000);
+        ok(result == WAIT_OBJECT_0, "expected WAIT_OBJECT_0, got %u\n", result);
+
+        result = ResumeThread(pi.hThread);
+        ok(result == 1, "expected 1, got %u\n", result);
+        Sleep(1);
+    }
+
+    info.running = FALSE;
+    result = WaitForSingleObject(thread, 1000);
+    ok(result == WAIT_OBJECT_0, "expected WAIT_OBJECT_0, got %u\n", result);
+    CloseHandle(thread);
+    CloseHandle(event);
+
+    TerminateProcess(pi.hProcess, 0);
+    CloseHandle(pi.hThread);
+    CloseHandle(pi.hProcess);
+}
+
 START_TEST(sync)
 {
+    char **argv;
+    int argc;
     HMODULE hdll = GetModuleHandleA("kernel32.dll");
     HMODULE hntdll = GetModuleHandleA("ntdll.dll");
 
@@ -2338,8 +2517,21 @@ START_TEST(sync)
     pReleaseSRWLockShared = (void *)GetProcAddress(hdll, "ReleaseSRWLockShared");
     pTryAcquireSRWLockExclusive = (void *)GetProcAddress(hdll, "TryAcquireSRWLockExclusive");
     pTryAcquireSRWLockShared = (void *)GetProcAddress(hdll, "TryAcquireSRWLockShared");
+    pNtAllocateVirtualMemory = (void *)GetProcAddress(hntdll, "NtAllocateVirtualMemory");
+    pNtFreeVirtualMemory = (void *)GetProcAddress(hntdll, "NtFreeVirtualMemory");
     pNtWaitForMultipleObjects = (void *)GetProcAddress(hntdll, "NtWaitForMultipleObjects");
 
+    argc = winetest_get_mainargs( &argv );
+    if (argc >= 3)
+    {
+        if (!strcmp(argv[2], "apc_deadlock"))
+        {
+            HANDLE handle = GetCurrentThread();
+            for (;;) WaitForMultipleObjectsEx(1, &handle, FALSE, INFINITE, TRUE);
+        }
+        return;
+    }
+
     test_signalandwait();
     test_mutex();
     test_slist();
@@ -2355,4 +2547,6 @@ START_TEST(sync)
     test_condvars_consumer_producer();
     test_srwlock_base();
     test_srwlock_example();
+    test_alertable_wait();
+    test_apc_deadlock();
 }
index 2ec829d..3dc5be3 100755 (executable)
 #include <stdarg.h>
 #include <stdio.h>
 
-#include <wine/test.h>
+/* the tests intentionally pass invalid pointers and need an exception handler */
+#define WINE_NO_INLINE_STRING
 
+#include <ntstatus.h>
+#define WIN32_NO_STATUS
 #include <windef.h>
 #include <winbase.h>
 #include <winnt.h>
 #include <winerror.h>
 #include <winnls.h>
+#include <wine/winternl.h>
+#include <wine/test.h>
 
 /* THREAD_ALL_ACCESS in Vista+ PSDKs is incompatible with older Windows versions */
 #define THREAD_ALL_ACCESS_NT4 (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x3ff)
 #define ARCH "x86"
 #elif defined __x86_64__
 #define ARCH "amd64"
+#elif defined __arm__
+#define ARCH "arm"
+#elif defined __aarch64__
+#define ARCH "arm64"
 #else
 #define ARCH "none"
 #endif
@@ -84,10 +93,15 @@ static BOOL   (WINAPI *pDeactivateActCtx)(DWORD,ULONG_PTR);
 static BOOL   (WINAPI *pGetCurrentActCtx)(HANDLE *);
 static void   (WINAPI *pReleaseActCtx)(HANDLE);
 static PTP_POOL (WINAPI *pCreateThreadpool)(PVOID);
+static void (WINAPI *pCloseThreadpool)(PTP_POOL);
 static PTP_WORK (WINAPI *pCreateThreadpoolWork)(PTP_WORK_CALLBACK,PVOID,PTP_CALLBACK_ENVIRON);
 static void (WINAPI *pSubmitThreadpoolWork)(PTP_WORK);
 static void (WINAPI *pWaitForThreadpoolWorkCallbacks)(PTP_WORK,BOOL);
 static void (WINAPI *pCloseThreadpoolWork)(PTP_WORK);
+static NTSTATUS (WINAPI *pNtQueryInformationThread)(HANDLE,THREADINFOCLASS,PVOID,ULONG,PULONG);
+static BOOL (WINAPI *pGetThreadGroupAffinity)(HANDLE,GROUP_AFFINITY*);
+static BOOL (WINAPI *pSetThreadGroupAffinity)(HANDLE,const GROUP_AFFINITY*,GROUP_AFFINITY*);
+static NTSTATUS (WINAPI *pNtSetInformationThread)(HANDLE,THREADINFOCLASS,LPCVOID,ULONG);
 
 static HANDLE create_target_process(const char *arg)
 {
@@ -213,9 +227,8 @@ static DWORD WINAPI threadFunc1(LPVOID p)
 /* Double check that all threads really did run by validating that
    they have all written to the shared memory. There should be no race
    here, since all threads were synchronized after the write.*/
-   for(i=0;i<NUM_THREADS;i++) {
-     while(tstruct->threadmem[i]==0) ;
-   }
+   for (i = 0; i < NUM_THREADS; i++)
+      ok(tstruct->threadmem[i] != 0, "expected threadmem[%d] != 0\n", i);
 
    /* lstrlenA contains an exception handler so this makes sure exceptions work in threads */
    ok( lstrlenA( (char *)0xdeadbeef ) == 0, "lstrlenA: unexpected success\n" );
@@ -866,6 +879,15 @@ static VOID test_thread_processor(void)
    retMask = SetThreadAffinityMask(curthread,~0);
    ok(broken(retMask==0) || retMask==processMask,
       "SetThreadAffinityMask(thread,-1) failed to request all processors.\n");
+
+    if (retMask == processMask)
+    {
+        /* Show that the "all processors" flag is handled in ntdll */
+        DWORD_PTR mask = ~0u;
+        NTSTATUS status = pNtSetInformationThread(curthread, ThreadAffinityMask, &mask, sizeof(mask));
+        ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS in NtSetInformationThread, got %x\n", status);
+    }
+
    if (retMask == processMask && sizeof(ULONG_PTR) > sizeof(ULONG))
    {
        /* only the low 32-bits matter */
@@ -875,41 +897,99 @@ static VOID test_thread_processor(void)
        ok(retMask == processMask, "SetThreadAffinityMask failed\n");
    }
 /* NOTE: This only works on WinNT/2000/XP) */
-   if (pSetThreadIdealProcessor) {
-     SetLastError(0xdeadbeef);
-     error=pSetThreadIdealProcessor(curthread,0);
-     if (GetLastError()==ERROR_CALL_NOT_IMPLEMENTED)
-     {
-       win_skip("SetThreadIdealProcessor is not implemented\n");
-       return;
-     }
-     ok(error!=-1, "SetThreadIdealProcessor failed\n");
-
-     if (is_wow64)
-     {
-         SetLastError(0xdeadbeef);
-         error=pSetThreadIdealProcessor(curthread,MAXIMUM_PROCESSORS+1);
-         todo_wine
-         ok(error!=-1, "SetThreadIdealProcessor failed for %u on Wow64\n", MAXIMUM_PROCESSORS+1);
-
-         SetLastError(0xdeadbeef);
-         error=pSetThreadIdealProcessor(curthread,65);
-         ok(error==-1, "SetThreadIdealProcessor succeeded with an illegal processor #\n");
-         ok(GetLastError()==ERROR_INVALID_PARAMETER,
-            "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
-     }
-     else
-     {
-         SetLastError(0xdeadbeef);
-         error=pSetThreadIdealProcessor(curthread,MAXIMUM_PROCESSORS+1);
-         ok(error==-1, "SetThreadIdealProcessor succeeded with an illegal processor #\n");
-         ok(GetLastError()==ERROR_INVALID_PARAMETER,
-            "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
-     }
+    if (pSetThreadIdealProcessor)
+    {
+        SetLastError(0xdeadbeef);
+        error=pSetThreadIdealProcessor(curthread,0);
+        if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
+        {
+            ok(error!=-1, "SetThreadIdealProcessor failed\n");
+
+            if (is_wow64)
+            {
+                SetLastError(0xdeadbeef);
+                error=pSetThreadIdealProcessor(curthread,MAXIMUM_PROCESSORS+1);
+                todo_wine
+                ok(error!=-1, "SetThreadIdealProcessor failed for %u on Wow64\n", MAXIMUM_PROCESSORS+1);
+
+                SetLastError(0xdeadbeef);
+                error=pSetThreadIdealProcessor(curthread,65);
+                ok(error==-1, "SetThreadIdealProcessor succeeded with an illegal processor #\n");
+                ok(GetLastError()==ERROR_INVALID_PARAMETER,
+                   "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+            }
+            else
+            {
+                SetLastError(0xdeadbeef);
+                error=pSetThreadIdealProcessor(curthread,MAXIMUM_PROCESSORS+1);
+                ok(error==-1, "SetThreadIdealProcessor succeeded with an illegal processor #\n");
+                ok(GetLastError()==ERROR_INVALID_PARAMETER,
+                   "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+            }
+
+            error=pSetThreadIdealProcessor(curthread,MAXIMUM_PROCESSORS);
+            ok(error!=-1, "SetThreadIdealProcessor failed\n");
+        }
+        else
+            win_skip("SetThreadIdealProcessor is not implemented\n");
+    }
 
-     error=pSetThreadIdealProcessor(curthread,MAXIMUM_PROCESSORS);
-     ok(error!=-1, "SetThreadIdealProcessor failed\n");
-   }
+    if (pGetThreadGroupAffinity && pSetThreadGroupAffinity)
+    {
+        GROUP_AFFINITY affinity, affinity_new;
+        NTSTATUS status;
+
+        memset(&affinity, 0, sizeof(affinity));
+        ok(pGetThreadGroupAffinity(curthread, &affinity), "GetThreadGroupAffinity failed\n");
+
+        SetLastError(0xdeadbeef);
+        ok(!pGetThreadGroupAffinity(curthread, NULL), "GetThreadGroupAffinity succeeded\n");
+        ok(GetLastError() == ERROR_INVALID_PARAMETER || broken(GetLastError() == ERROR_NOACCESS), /* Win 7 and 8 */
+           "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+        ok(affinity.Group == 0, "Expected group 0 got %u\n", affinity.Group);
+
+        memset(&affinity_new, 0, sizeof(affinity_new));
+        affinity_new.Group = 0;
+        affinity_new.Mask  = affinity.Mask;
+        ok(pSetThreadGroupAffinity(curthread, &affinity_new, &affinity), "SetThreadGroupAffinity failed\n");
+        ok(affinity_new.Mask == affinity.Mask, "Expected old affinity mask %lx, got %lx\n",
+           affinity_new.Mask, affinity.Mask);
+
+        /* show that the "all processors" flag is not supported for SetThreadGroupAffinity */
+        affinity_new.Group = 0;
+        affinity_new.Mask  = ~0u;
+        SetLastError(0xdeadbeef);
+        ok(!pSetThreadGroupAffinity(curthread, &affinity_new, NULL), "SetThreadGroupAffinity succeeded\n");
+        ok(GetLastError() == ERROR_INVALID_PARAMETER,
+           "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+
+        affinity_new.Group = 1; /* assumes that you have less than 64 logical processors */
+        affinity_new.Mask  = 0x1;
+        SetLastError(0xdeadbeef);
+        ok(!pSetThreadGroupAffinity(curthread, &affinity_new, NULL), "SetThreadGroupAffinity succeeded\n");
+        ok(GetLastError() == ERROR_INVALID_PARAMETER,
+           "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+
+        SetLastError(0xdeadbeef);
+        ok(!pSetThreadGroupAffinity(curthread, NULL, NULL), "SetThreadGroupAffinity succeeded\n");
+        ok(GetLastError() == ERROR_NOACCESS,
+           "Expected ERROR_NOACCESS, got %d\n", GetLastError());
+
+        /* show that the access violation was detected in ntdll */
+        status = pNtSetInformationThread(curthread, ThreadGroupInformation, NULL, sizeof(affinity_new));
+        ok(status == STATUS_ACCESS_VIOLATION,
+           "Expected STATUS_ACCESS_VIOLATION, got %08x\n", status);
+
+        /* restore original mask */
+        affinity_new.Group = 0;
+        affinity_new.Mask  = affinity.Mask;
+        SetLastError(0xdeadbeef);
+        ok(pSetThreadGroupAffinity(curthread, &affinity_new, &affinity), "SetThreadGroupAffinity failed\n");
+        ok(affinity_new.Mask == affinity.Mask, "Expected old affinity mask %lx, got %lx\n",
+           affinity_new.Mask, affinity.Mask);
+    }
+    else
+        win_skip("Get/SetThreadGroupAffinity not available\n");
 }
 
 static VOID test_GetThreadExitCode(void)
@@ -1648,6 +1728,7 @@ static void test_threadpool(void)
 
     pool = pCreateThreadpool(NULL);
     ok (pool != NULL, "CreateThreadpool failed\n");
+    pCloseThreadpool(pool);
 }
 
 static void test_reserved_tls(void)
@@ -1687,9 +1768,113 @@ static void test_reserved_tls(void)
     }
 }
 
+static void test_thread_info(void)
+{
+    char buf[4096];
+    static const ULONG info_size[] =
+    {
+        sizeof(THREAD_BASIC_INFORMATION), /* ThreadBasicInformation */
+        sizeof(KERNEL_USER_TIMES), /* ThreadTimes */
+        sizeof(ULONG), /* ThreadPriority */
+        sizeof(ULONG), /* ThreadBasePriority */
+        sizeof(ULONG_PTR), /* ThreadAffinityMask */
+        sizeof(HANDLE), /* ThreadImpersonationToken */
+        sizeof(THREAD_DESCRIPTOR_INFORMATION), /* ThreadDescriptorTableEntry */
+        sizeof(BOOLEAN), /* ThreadEnableAlignmentFaultFixup */
+        0, /* ThreadEventPair_Reusable */
+        sizeof(ULONG_PTR), /* ThreadQuerySetWin32StartAddress */
+        sizeof(ULONG), /* ThreadZeroTlsCell */
+        sizeof(LARGE_INTEGER), /* ThreadPerformanceCount */
+        sizeof(ULONG), /* ThreadAmILastThread */
+        sizeof(ULONG), /* ThreadIdealProcessor */
+        sizeof(ULONG), /* ThreadPriorityBoost */
+        sizeof(ULONG_PTR), /* ThreadSetTlsArrayAddress */
+        sizeof(ULONG), /* ThreadIsIoPending */
+        sizeof(BOOLEAN), /* ThreadHideFromDebugger */
+        /* FIXME: Add remaining classes */
+    };
+    HANDLE thread;
+    ULONG i, status, ret_len;
+
+    if (!pOpenThread)
+    {
+        win_skip("OpenThread is not available on this platform\n");
+        return;
+    }
+
+    if (!pNtQueryInformationThread)
+    {
+        win_skip("NtQueryInformationThread is not available on this platform\n");
+        return;
+    }
+
+    thread = pOpenThread(THREAD_QUERY_LIMITED_INFORMATION, FALSE, GetCurrentThreadId());
+    if (!thread)
+    {
+        win_skip("THREAD_QUERY_LIMITED_INFORMATION is not supported on this platform\n");
+        return;
+    }
+
+    for (i = 0; i < sizeof(info_size)/sizeof(info_size[0]); i++)
+    {
+        memset(buf, 0, sizeof(buf));
+
+#ifdef __i386__
+        if (i == ThreadDescriptorTableEntry)
+        {
+            CONTEXT ctx;
+            THREAD_DESCRIPTOR_INFORMATION *tdi = (void *)buf;
+
+            ctx.ContextFlags = CONTEXT_SEGMENTS;
+            GetThreadContext(GetCurrentThread(), &ctx);
+            tdi->Selector = ctx.SegDs;
+        }
+#endif
+        ret_len = 0;
+        status = pNtQueryInformationThread(thread, i, buf, info_size[i], &ret_len);
+        if (status == STATUS_NOT_IMPLEMENTED) continue;
+        if (status == STATUS_INVALID_INFO_CLASS) continue;
+        if (status == STATUS_UNSUCCESSFUL) continue;
+
+        switch (i)
+        {
+        case ThreadBasicInformation:
+        case ThreadAmILastThread:
+        case ThreadPriorityBoost:
+            ok(status == STATUS_SUCCESS, "for info %u expected STATUS_SUCCESS, got %08x (ret_len %u)\n", i, status, ret_len);
+            break;
+
+#ifdef __i386__
+        case ThreadDescriptorTableEntry:
+            ok(status == STATUS_SUCCESS || broken(status == STATUS_ACCESS_DENIED) /* testbot VM is broken */,
+               "for info %u expected STATUS_SUCCESS, got %08x (ret_len %u)\n", i, status, ret_len);
+            break;
+#endif
+
+        case ThreadTimes:
+todo_wine
+            ok(status == STATUS_SUCCESS, "for info %u expected STATUS_SUCCESS, got %08x (ret_len %u)\n", i, status, ret_len);
+            break;
+
+        case ThreadAffinityMask:
+        case ThreadQuerySetWin32StartAddress:
+todo_wine
+            ok(status == STATUS_ACCESS_DENIED, "for info %u expected STATUS_ACCESS_DENIED, got %08x (ret_len %u)\n", i, status, ret_len);
+            break;
+
+        default:
+            ok(status == STATUS_ACCESS_DENIED, "for info %u expected STATUS_ACCESS_DENIED, got %08x (ret_len %u)\n", i, status, ret_len);
+            break;
+        }
+    }
+
+    CloseHandle(thread);
+}
+
 static void init_funcs(void)
 {
     HMODULE hKernel32 = GetModuleHandleA("kernel32.dll");
+    HMODULE ntdll = GetModuleHandleA("ntdll.dll");
 
 /* Neither Cygwin nor mingW export OpenThread, so do a dynamic check
    so that the compile passes */
@@ -1712,28 +1897,34 @@ static void init_funcs(void)
     X(ReleaseActCtx);
 
     X(CreateThreadpool);
+    X(CloseThreadpool);
     X(CreateThreadpoolWork);
     X(SubmitThreadpoolWork);
     X(WaitForThreadpoolWorkCallbacks);
     X(CloseThreadpoolWork);
+
+    X(GetThreadGroupAffinity);
+    X(SetThreadGroupAffinity);
+#undef X
+
+#define X(f) p##f = (void*)GetProcAddress(ntdll, #f)
+   if (ntdll)
+   {
+       X(NtQueryInformationThread);
+       X(RtlGetThreadErrorMode);
+       X(NtSetInformationThread);
+   }
 #undef X
 }
 
 START_TEST(thread)
 {
-   HINSTANCE ntdll;
    int argc;
    char **argv;
    argc = winetest_get_mainargs( &argv );
 
    init_funcs();
 
-   ntdll=GetModuleHandleA("ntdll.dll");
-   if (ntdll)
-   {
-       pRtlGetThreadErrorMode=(void *)GetProcAddress(ntdll,"RtlGetThreadErrorMode");
-   }
-
    if (argc >= 3)
    {
        if (!strcmp(argv[2], "sleep"))
@@ -1760,6 +1951,7 @@ START_TEST(thread)
        return;
    }
 
+   test_thread_info();
    test_reserved_tls();
    test_CreateRemoteThread();
    test_CreateThread_basic();
index a914ab0..95d5c74 100755 (executable)
@@ -29,6 +29,8 @@ static BOOL (WINAPI *pSystemTimeToTzSpecificLocalTime)(LPTIME_ZONE_INFORMATION,
 static BOOL (WINAPI *pGetSystemTimes)(LPFILETIME, LPFILETIME, LPFILETIME);
 static int (WINAPI *pGetCalendarInfoA)(LCID,CALID,CALTYPE,LPSTR,int,LPDWORD);
 static int (WINAPI *pGetCalendarInfoW)(LCID,CALID,CALTYPE,LPWSTR,int,LPDWORD);
+static DWORD (WINAPI *pGetDynamicTimeZoneInformation)(DYNAMIC_TIME_ZONE_INFORMATION*);
+static void (WINAPI *pGetSystemTimePreciseAsFileTime)(LPFILETIME);
 
 #define SECSPERMIN         60
 #define SECSPERDAY        86400
@@ -732,8 +734,75 @@ static void test_GetCalendarInfo(void)
     ok( ret2, "GetCalendarInfoW failed err %u\n", GetLastError() );
     ret2 = WideCharToMultiByte( CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL );
     ok( ret == ret2, "got %d, expected %d\n", ret, ret2 );
+}
+
+static void test_GetDynamicTimeZoneInformation(void)
+{
+    DYNAMIC_TIME_ZONE_INFORMATION dyninfo;
+    TIME_ZONE_INFORMATION tzinfo;
+    DWORD ret, ret2;
+
+    if (!pGetDynamicTimeZoneInformation)
+    {
+        win_skip("GetDynamicTimeZoneInformation() is not supported.\n");
+        return;
+    }
+
+    ret = pGetDynamicTimeZoneInformation(&dyninfo);
+    ret2 = GetTimeZoneInformation(&tzinfo);
+    ok(ret == ret2, "got %d, %d\n", ret, ret2);
+
+    ok(dyninfo.Bias == tzinfo.Bias, "got %d, %d\n", dyninfo.Bias, tzinfo.Bias);
+    ok(!lstrcmpW(dyninfo.StandardName, tzinfo.StandardName), "got std name %s, %s\n",
+        wine_dbgstr_w(dyninfo.StandardName), wine_dbgstr_w(tzinfo.StandardName));
+    ok(!memcmp(&dyninfo.StandardDate, &tzinfo.StandardDate, sizeof(dyninfo.StandardDate)), "got different StandardDate\n");
+    ok(dyninfo.StandardBias == tzinfo.StandardBias, "got %d, %d\n", dyninfo.StandardBias, tzinfo.StandardBias);
+    ok(!lstrcmpW(dyninfo.DaylightName, tzinfo.DaylightName), "got daylight name %s, %s\n",
+        wine_dbgstr_w(dyninfo.DaylightName), wine_dbgstr_w(tzinfo.DaylightName));
+    ok(!memcmp(&dyninfo.DaylightDate, &tzinfo.DaylightDate, sizeof(dyninfo.DaylightDate)), "got different DaylightDate\n");
+    ok(dyninfo.TimeZoneKeyName[0] != 0, "got empty tz keyname\n");
+    trace("Dyn TimeZoneKeyName %s\n", wine_dbgstr_w(dyninfo.TimeZoneKeyName));
+}
 
+static ULONGLONG get_longlong_time(FILETIME *time)
+{
+    ULARGE_INTEGER uli;
+    uli.u.LowPart = time->dwLowDateTime;
+    uli.u.HighPart = time->dwHighDateTime;
+    return uli.QuadPart;
 }
+
+static void test_GetSystemTimePreciseAsFileTime(void)
+{
+    FILETIME ft;
+    ULONGLONG time1, time2;
+    LONGLONG diff;
+
+    if (!pGetSystemTimePreciseAsFileTime)
+    {
+        win_skip("GetSystemTimePreciseAsFileTime() is not supported.\n");
+        return;
+    }
+
+    GetSystemTimeAsFileTime(&ft);
+    time1 = get_longlong_time(&ft);
+    pGetSystemTimePreciseAsFileTime(&ft);
+    time2 = get_longlong_time(&ft);
+    diff = time2 - time1;
+    if (diff < 0)
+        diff = -diff;
+    ok(diff < 1000000, "Difference between GetSystemTimeAsFileTime and GetSystemTimePreciseAsFileTime more than 100 ms\n");
+
+    pGetSystemTimePreciseAsFileTime(&ft);
+    time1 = get_longlong_time(&ft);
+    do {
+        pGetSystemTimePreciseAsFileTime(&ft);
+        time2 = get_longlong_time(&ft);
+    } while (time2 == time1);
+    diff = time2 - time1;
+    ok(diff < 10000 && diff > 0, "GetSystemTimePreciseAsFileTime incremented by more than 1 ms\n");
+}
+
 static void test_GetSystemTimes(void)
 {
 
@@ -743,7 +812,7 @@ static void test_GetSystemTimes(void)
     SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *sppi;
     SYSTEM_BASIC_INFORMATION sbi;
     ULONG ReturnLength;
-    double total_usertime = 0.0, total_kerneltime = 0.0, total_idletime = 0.0;
+    ULARGE_INTEGER total_usertime, total_kerneltime, total_idletime;
 
     if (!pGetSystemTimes)
     {
@@ -753,6 +822,9 @@ static void test_GetSystemTimes(void)
 
     ok( pGetSystemTimes(NULL, NULL, NULL), "GetSystemTimes failed unexpectedly\n" );
 
+    total_usertime.QuadPart = 0;
+    total_kerneltime.QuadPart = 0;
+    total_idletime.QuadPart = 0;
     memset( &idletime, 0x11, sizeof(idletime) );
     memset( &kerneltime, 0x11, sizeof(kerneltime) );
     memset( &usertime, 0x11, sizeof(usertime) );
@@ -761,19 +833,11 @@ static void test_GetSystemTimes(void)
 
     ul1.LowPart = idletime.dwLowDateTime;
     ul1.HighPart = idletime.dwHighDateTime;
-
-    trace( "IdleTime:   %f seconds\n", (double)ul1.QuadPart/10000000.0 );
-
     ul2.LowPart = kerneltime.dwLowDateTime;
     ul2.HighPart = kerneltime.dwHighDateTime;
-
-    trace( "KernelTime: %f seconds\n", (double)ul2.QuadPart/10000000.0 );
-
     ul3.LowPart = usertime.dwLowDateTime;
     ul3.HighPart = usertime.dwHighDateTime;
 
-    trace( "UserTime:   %f seconds\n", (double)ul3.QuadPart/10000000.0 );
-
     ok( !NtQuerySystemInformation(SystemBasicInformation, &sbi, sizeof(sbi), &ReturnLength),
                                   "NtQuerySystemInformation failed\n" );
     ok( sizeof(sbi) == ReturnLength, "Inconsistent length %d\n", ReturnLength );
@@ -787,22 +851,20 @@ static void test_GetSystemTimes(void)
                       sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION) * sbi.NumberOfProcessors);
 
     ok( !NtQuerySystemInformation( SystemProcessorPerformanceInformation, sppi,
-                                   sizeof(*sppi), &ReturnLength),
+                                   sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION) * sbi.NumberOfProcessors,
+                                   &ReturnLength),
                                    "NtQuerySystemInformation failed\n" );
 
     for (i = 0; i < sbi.NumberOfProcessors; i++)
     {
-        total_usertime += (double)(sppi[i].UserTime.QuadPart)/10000000.0;
-        total_kerneltime += (double)(sppi[i].KernelTime.QuadPart)/10000000.0;
-        total_idletime += (double)(sppi[i].IdleTime.QuadPart)/10000000.0;
+        total_usertime.QuadPart += sppi[i].UserTime.QuadPart;
+        total_kerneltime.QuadPart += sppi[i].KernelTime.QuadPart;
+        total_idletime.QuadPart += sppi[i].IdleTime.QuadPart;
     }
 
-    trace( "total_idletime %f total_kerneltime %f total_usertime %f \n", total_idletime,
-          total_kerneltime, total_usertime );
-
-    ok( (total_idletime - (double)ul1.QuadPart/10000000.0) < 1.0, "test idletime failed\n" );
-    ok( (total_kerneltime - (double)ul2.QuadPart/10000000.0) < 1.0, "test kerneltime failed\n" );
-    ok( (total_usertime - (double)ul3.QuadPart/10000000.0) < 1.0, "test usertime failed\n" );
+    ok( total_idletime.QuadPart - ul1.QuadPart < 10000000, "test idletime failed\n" );
+    ok( total_kerneltime.QuadPart - ul2.QuadPart < 10000000, "test kerneltime failed\n" );
+    ok( total_usertime.QuadPart - ul3.QuadPart < 10000000, "test usertime failed\n" );
 
     HeapFree(GetProcessHeap(), 0, sppi);
 }
@@ -815,6 +877,8 @@ START_TEST(time)
     pGetSystemTimes = (void *)GetProcAddress( hKernel, "GetSystemTimes");
     pGetCalendarInfoA = (void *)GetProcAddress(hKernel, "GetCalendarInfoA");
     pGetCalendarInfoW = (void *)GetProcAddress(hKernel, "GetCalendarInfoW");
+    pGetDynamicTimeZoneInformation = (void *)GetProcAddress(hKernel, "GetDynamicTimeZoneInformation");
+    pGetSystemTimePreciseAsFileTime = (void *)GetProcAddress(hKernel, "GetSystemTimePreciseAsFileTime");
 
     test_conversions();
     test_invalid_arg();
@@ -825,4 +889,6 @@ START_TEST(time)
     test_GetSystemTimes();
     test_FileTimeToDosDateTime();
     test_GetCalendarInfo();
+    test_GetDynamicTimeZoneInformation();
+    test_GetSystemTimePreciseAsFileTime();
 }
index ed76c9c..c40dbc4 100644 (file)
@@ -175,8 +175,8 @@ static void test_thread(DWORD curr_pid, DWORD sub_pcs_pid)
             num++;
         } while (pThread32Next( hSnapshot, &te ));
     }
-    ok(curr_found == 1, "couldn't find self in thread list\n");
-    ok(sub_found == 2, "couldn't find sub-process thread's in thread list\n");
+    ok(curr_found, "couldn't find self in thread list\n");
+    ok(sub_found >= 2, "couldn't find sub-process threads in thread list\n");
 
     /* check that first really resets enumeration */
     curr_found = 0;
@@ -192,8 +192,8 @@ static void test_thread(DWORD curr_pid, DWORD sub_pcs_pid)
             num--;
         } while (pThread32Next( hSnapshot, &te ));
     }
-    ok(curr_found == 1, "couldn't find self in thread list\n");
-    ok(sub_found == 2, "couldn't find sub-process thread's in thread list\n");
+    ok(curr_found, "couldn't find self in thread list\n");
+    ok(sub_found >= 2, "couldn't find sub-process threads in thread list\n");
 
     me.dwSize = sizeof(me);
     ok(!pModule32First( hSnapshot, &me ), "shouldn't return a module\n");
index 16fd47a..01e1554 100755 (executable)
@@ -47,7 +47,11 @@ static struct _TEB * (WINAPI *pNtCurrentTeb)(void);
 static PVOID  (WINAPI *pRtlAddVectoredExceptionHandler)(ULONG, PVECTORED_EXCEPTION_HANDLER);
 static ULONG  (WINAPI *pRtlRemoveVectoredExceptionHandler)(PVOID);
 static BOOL   (WINAPI *pGetProcessDEPPolicy)(HANDLE, LPDWORD, PBOOL);
+static BOOL   (WINAPI *pIsWow64Process)(HANDLE, PBOOL);
 static NTSTATUS (WINAPI *pNtQuerySection)(HANDLE, int, PVOID, ULONG, PULONG);
+static NTSTATUS (WINAPI *pNtProtectVirtualMemory)(HANDLE, PVOID *, SIZE_T *, ULONG, ULONG *);
+static NTSTATUS (WINAPI *pNtAllocateVirtualMemory)(HANDLE, PVOID *, ULONG, SIZE_T *, ULONG, ULONG);
+static NTSTATUS (WINAPI *pNtFreeVirtualMemory)(HANDLE, PVOID *, SIZE_T *, ULONG);
 
 /* ############################### */
 
@@ -254,6 +258,8 @@ static void test_VirtualAlloc(void)
     void *addr1, *addr2;
     DWORD old_prot;
     MEMORY_BASIC_INFORMATION info;
+    NTSTATUS status;
+    SIZE_T size;
 
     SetLastError(0xdeadbeef);
     addr1 = VirtualAlloc(0, 0, MEM_RESERVE, PAGE_NOACCESS);
@@ -389,6 +395,42 @@ static void test_VirtualAlloc(void)
         "got %d, expected ERROR_INVALID_PARAMETER\n", GetLastError());
 
     ok(VirtualFree(addr1, 0, MEM_RELEASE), "VirtualFree failed\n");
+
+    addr1 = VirtualAlloc(0, 0x1000, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
+    ok(addr1 != NULL, "VirtualAlloc failed\n");
+    ok(!((ULONG_PTR)addr1 & 0xffff), "returned memory %p is not aligned to 64k\n", addr1);
+
+    /* allocation conflicts because of 64k align */
+    size = 0x1000;
+    addr2 = (char *)addr1 + 0x1000;
+    status = pNtAllocateVirtualMemory(GetCurrentProcess(), &addr2, 0, &size,
+                                      MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
+    ok(status == STATUS_CONFLICTING_ADDRESSES, "NtAllocateVirtualMemory returned %08x\n", status);
+
+    /* it should conflict, even when zero_bits is explicitly set */
+    size = 0x1000;
+    addr2 = (char *)addr1 + 0x1000;
+    status = pNtAllocateVirtualMemory(GetCurrentProcess(), &addr2, 12, &size,
+                                      MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
+    todo_wine
+    ok(status == STATUS_CONFLICTING_ADDRESSES, "NtAllocateVirtualMemory returned %08x\n", status);
+    if (status == STATUS_SUCCESS) ok(VirtualFree(addr2, 0, MEM_RELEASE), "VirtualFree failed\n");
+
+    /* AT_ROUND_TO_PAGE flag is not supported for VirtualAlloc */
+    SetLastError(0xdeadbeef);
+    addr2 = VirtualAlloc(addr1, 0x1000, MEM_RESERVE | MEM_COMMIT | AT_ROUND_TO_PAGE, PAGE_EXECUTE_READWRITE);
+    ok(!addr2, "VirtualAlloc unexpectedly succeeded\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "got %d, expected ERROR_INVALID_PARAMETER\n", GetLastError());
+
+    /* AT_ROUND_TO_PAGE flag is not supported for NtAllocateVirtualMemory */
+    size = 0x1000;
+    addr2 = (char *)addr1 + 0x1000;
+    status = pNtAllocateVirtualMemory(GetCurrentProcess(), &addr2, 0, &size, MEM_RESERVE |
+                                      MEM_COMMIT | AT_ROUND_TO_PAGE, PAGE_EXECUTE_READWRITE);
+    todo_wine
+    ok(status == STATUS_INVALID_PARAMETER_5, "NtAllocateVirtualMemory returned %08x\n", status);
+
+    ok(VirtualFree(addr1, 0, MEM_RELEASE), "VirtualFree failed\n");
 }
 
 static void test_MapViewOfFile(void)
@@ -974,8 +1016,8 @@ static void test_NtMapViewOfSection(void)
     static const char data[] = "test data for NtMapViewOfSection";
     char buffer[sizeof(data)];
     HANDLE file, mapping;
-    void *ptr;
-    BOOL ret;
+    void *ptr, *ptr2;
+    BOOL is_wow64, ret;
     DWORD status, written;
     SIZE_T size, result;
     LARGE_INTEGER offset;
@@ -1005,12 +1047,154 @@ static void test_NtMapViewOfSection(void)
     offset.QuadPart = 0;
     status = pNtMapViewOfSection( mapping, hProcess, &ptr, 0, 0, &offset, &size, 1, 0, PAGE_READWRITE );
     ok( !status, "NtMapViewOfSection failed status %x\n", status );
+    ok( !((ULONG_PTR)ptr & 0xffff), "returned memory %p is not aligned to 64k\n", ptr );
 
     ret = ReadProcessMemory( hProcess, ptr, buffer, sizeof(buffer), &result );
     ok( ret, "ReadProcessMemory failed\n" );
     ok( result == sizeof(buffer), "ReadProcessMemory didn't read all data (%lx)\n", result );
     ok( !memcmp( buffer, data, sizeof(buffer) ), "Wrong data read\n" );
 
+    /* for some unknown reason NtMapViewOfSection fails with STATUS_NO_MEMORY when zero_bits != 0 ? */
+    ptr2 = NULL;
+    size = 0;
+    offset.QuadPart = 0;
+    status = pNtMapViewOfSection( mapping, hProcess, &ptr2, 12, 0, &offset, &size, 1, 0, PAGE_READWRITE );
+    todo_wine
+    ok( status == STATUS_NO_MEMORY, "NtMapViewOfSection returned %x\n", status );
+    if (status == STATUS_SUCCESS)
+    {
+        status = pNtUnmapViewOfSection( hProcess, ptr2 );
+        ok( !status, "NtUnmapViewOfSection failed status %x\n", status );
+    }
+
+    ptr2 = NULL;
+    size = 0;
+    status = pNtMapViewOfSection( mapping, hProcess, &ptr2, 16, 0, &offset, &size, 1, 0, PAGE_READWRITE );
+    todo_wine
+    ok( status == STATUS_NO_MEMORY, "NtMapViewOfSection returned %x\n", status );
+    if (status == STATUS_SUCCESS)
+    {
+        status = pNtUnmapViewOfSection( hProcess, ptr2 );
+        ok( !status, "NtUnmapViewOfSection failed status %x\n", status );
+    }
+
+    /* mapping at the same page conflicts */
+    ptr2 = ptr;
+    size = 0;
+    offset.QuadPart = 0;
+    status = pNtMapViewOfSection( mapping, hProcess, &ptr2, 0, 0, &offset, &size, 1, 0, PAGE_READWRITE );
+    ok( status == STATUS_CONFLICTING_ADDRESSES, "NtMapViewOfSection returned %x\n", status );
+
+    /* offset has to be aligned */
+    ptr2 = ptr;
+    size = 0;
+    offset.QuadPart = 1;
+    status = pNtMapViewOfSection( mapping, hProcess, &ptr2, 0, 0, &offset, &size, 1, 0, PAGE_READWRITE );
+    ok( status == STATUS_MAPPED_ALIGNMENT, "NtMapViewOfSection returned %x\n", status );
+
+    /* ptr has to be aligned */
+    ptr2 = (char *)ptr + 42;
+    size = 0;
+    offset.QuadPart = 0;
+    status = pNtMapViewOfSection( mapping, hProcess, &ptr2, 0, 0, &offset, &size, 1, 0, PAGE_READWRITE );
+    ok( status == STATUS_MAPPED_ALIGNMENT, "NtMapViewOfSection returned %x\n", status );
+
+    /* still not 64k aligned */
+    ptr2 = (char *)ptr + 0x1000;
+    size = 0;
+    offset.QuadPart = 0;
+    status = pNtMapViewOfSection( mapping, hProcess, &ptr2, 0, 0, &offset, &size, 1, 0, PAGE_READWRITE );
+    ok( status == STATUS_MAPPED_ALIGNMENT, "NtMapViewOfSection returned %x\n", status );
+
+    /* zero_bits != 0 is not allowed when an address is set */
+    ptr2 = (char *)ptr + 0x1000;
+    size = 0;
+    offset.QuadPart = 0;
+    status = pNtMapViewOfSection( mapping, hProcess, &ptr2, 12, 0, &offset, &size, 1, 0, PAGE_READWRITE );
+    ok( status == STATUS_INVALID_PARAMETER_4, "NtMapViewOfSection returned %x\n", status );
+
+    ptr2 = (char *)ptr + 0x1000;
+    size = 0;
+    offset.QuadPart = 0;
+    status = pNtMapViewOfSection( mapping, hProcess, &ptr2, 16, 0, &offset, &size, 1, 0, PAGE_READWRITE );
+    ok( status == STATUS_INVALID_PARAMETER_4, "NtMapViewOfSection returned %x\n", status );
+
+    ptr2 = (char *)ptr + 0x1001;
+    size = 0;
+    offset.QuadPart = 0;
+    status = pNtMapViewOfSection( mapping, hProcess, &ptr2, 16, 0, &offset, &size, 1, 0, PAGE_READWRITE );
+    ok( status == STATUS_INVALID_PARAMETER_4, "NtMapViewOfSection returned %x\n", status );
+
+    ptr2 = (char *)ptr + 0x1000;
+    size = 0;
+    offset.QuadPart = 1;
+    status = pNtMapViewOfSection( mapping, hProcess, &ptr2, 16, 0, &offset, &size, 1, 0, PAGE_READWRITE );
+    ok( status == STATUS_INVALID_PARAMETER_4, "NtMapViewOfSection returned %x\n", status );
+
+    if (sizeof(void *) == sizeof(int) && (!pIsWow64Process ||
+        !pIsWow64Process( GetCurrentProcess(), &is_wow64 ) || !is_wow64))
+    {
+        /* new memory region conflicts with previous mapping */
+        ptr2 = ptr;
+        size = 0;
+        offset.QuadPart = 0;
+        status = pNtMapViewOfSection( mapping, hProcess, &ptr2, 0, 0, &offset,
+                                      &size, 1, AT_ROUND_TO_PAGE, PAGE_READWRITE );
+        ok( status == STATUS_CONFLICTING_ADDRESSES, "NtMapViewOfSection returned %x\n", status );
+
+        ptr2 = (char *)ptr + 42;
+        size = 0;
+        offset.QuadPart = 0;
+        status = pNtMapViewOfSection( mapping, hProcess, &ptr2, 0, 0, &offset,
+                                      &size, 1, AT_ROUND_TO_PAGE, PAGE_READWRITE );
+        ok( status == STATUS_CONFLICTING_ADDRESSES, "NtMapViewOfSection returned %x\n", status );
+
+        /* in contrary to regular NtMapViewOfSection, only 4kb align is enforced */
+        ptr2 = (char *)ptr + 0x1000;
+        size = 0;
+        offset.QuadPart = 0;
+        status = pNtMapViewOfSection( mapping, hProcess, &ptr2, 0, 0, &offset,
+                                      &size, 1, AT_ROUND_TO_PAGE, PAGE_READWRITE );
+        ok( status == STATUS_SUCCESS, "NtMapViewOfSection returned %x\n", status );
+        ok( (char *)ptr2 == (char *)ptr + 0x1000,
+            "expected address %p, got %p\n", (char *)ptr + 0x1000, ptr2 );
+        status = pNtUnmapViewOfSection( hProcess, ptr2 );
+        ok( !status, "NtUnmapViewOfSection failed status %x\n", status );
+
+        /* the address is rounded down if not on a page boundary */
+        ptr2 = (char *)ptr + 0x1001;
+        size = 0;
+        offset.QuadPart = 0;
+        status = pNtMapViewOfSection( mapping, hProcess, &ptr2, 0, 0, &offset,
+                                      &size, 1, AT_ROUND_TO_PAGE, PAGE_READWRITE );
+        ok( status == STATUS_SUCCESS, "NtMapViewOfSection returned %x\n", status );
+        ok( (char *)ptr2 == (char *)ptr + 0x1000,
+            "expected address %p, got %p\n", (char *)ptr + 0x1000, ptr2 );
+        status = pNtUnmapViewOfSection( hProcess, ptr2 );
+        ok( !status, "NtUnmapViewOfSection failed status %x\n", status );
+
+        ptr2 = (char *)ptr + 0x2000;
+        size = 0;
+        offset.QuadPart = 0;
+        status = pNtMapViewOfSection( mapping, hProcess, &ptr2, 0, 0, &offset,
+                                      &size, 1, AT_ROUND_TO_PAGE, PAGE_READWRITE );
+        ok( status == STATUS_SUCCESS, "NtMapViewOfSection returned %x\n", status );
+        ok( (char *)ptr2 == (char *)ptr + 0x2000,
+            "expected address %p, got %p\n", (char *)ptr + 0x2000, ptr2 );
+        status = pNtUnmapViewOfSection( hProcess, ptr2 );
+        ok( !status, "NtUnmapViewOfSection failed status %x\n", status );
+    }
+    else
+    {
+        ptr2 = (char *)ptr + 0x1000;
+        size = 0;
+        offset.QuadPart = 0;
+        status = pNtMapViewOfSection( mapping, hProcess, &ptr2, 0, 0, &offset,
+                                      &size, 1, AT_ROUND_TO_PAGE, PAGE_READWRITE );
+        todo_wine
+        ok( status == STATUS_INVALID_PARAMETER_9, "NtMapViewOfSection returned %x\n", status );
+    }
+
     status = pNtUnmapViewOfSection( hProcess, ptr );
     ok( !status, "NtUnmapViewOfSection failed status %x\n", status );
 
@@ -1270,12 +1454,17 @@ static void test_IsBadCodePtr(void)
 
 static void test_write_watch(void)
 {
-    char *base;
-    DWORD ret, size, old_prot;
+    static const char pipename[] = "\\\\.\\pipe\\test_write_watch_pipe";
+    static const char testdata[] = "Hello World";
+    DWORD ret, size, old_prot, num_bytes;
     MEMORY_BASIC_INFORMATION info;
+    HANDLE readpipe, writepipe;
+    OVERLAPPED overlapped;
     void *results[64];
     ULONG_PTR count;
     ULONG pagesize;
+    BOOL success;
+    char *base;
 
     if (!pGetWriteWatch || !pResetWriteWatch)
     {
@@ -1423,6 +1612,64 @@ static void test_write_watch(void)
     ok( info.State == MEM_COMMIT, "wrong State 0x%x\n", info.State );
     ok( info.Protect == PAGE_READWRITE, "wrong Protect 0x%x\n", info.Protect );
 
+    /* ReadFile should trigger write watches */
+
+    memset( &overlapped, 0, sizeof(overlapped) );
+    overlapped.hEvent = CreateEventA( NULL, TRUE, FALSE, NULL );
+
+    readpipe = CreateNamedPipeA( pipename, FILE_FLAG_OVERLAPPED | PIPE_ACCESS_INBOUND,
+                                 PIPE_TYPE_BYTE | PIPE_WAIT, 1, 1024, 1024,
+                                 NMPWAIT_USE_DEFAULT_WAIT, NULL );
+    ok( readpipe != INVALID_HANDLE_VALUE, "CreateNamedPipeA failed %u\n", GetLastError() );
+
+    success = ConnectNamedPipe( readpipe, &overlapped );
+    ok( !success, "ConnectNamedPipe unexpectedly succeeded\n" );
+    ok( GetLastError() == ERROR_IO_PENDING, "expected ERROR_IO_PENDING, got %u\n", GetLastError() );
+
+    writepipe = CreateFileA( pipename, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL );
+    ok( writepipe != INVALID_HANDLE_VALUE, "CreateFileA failed %u\n", GetLastError() );
+
+    ret = WaitForSingleObject( overlapped.hEvent, 1000 );
+    ok( ret == WAIT_OBJECT_0, "expected WAIT_OBJECT_0, got %u\n", ret );
+
+    memset( base, 0, size );
+
+    count = 64;
+    ret = pGetWriteWatch( WRITE_WATCH_FLAG_RESET, base, size, results, &count, &pagesize );
+    ok( !ret, "GetWriteWatch failed %u\n", GetLastError() );
+    ok( count == 16, "wrong count %lu\n", count );
+
+    success = ReadFile( readpipe, base, size, NULL, &overlapped );
+    ok( !success, "ReadFile unexpectedly succeeded\n" );
+    ok( GetLastError() == ERROR_IO_PENDING, "expected ERROR_IO_PENDING, got %u\n", GetLastError() );
+
+    count = 64;
+    ret = pGetWriteWatch( WRITE_WATCH_FLAG_RESET, base, size, results, &count, &pagesize );
+    ok( !ret, "GetWriteWatch failed %u\n", GetLastError() );
+    ok( count == 16, "wrong count %lu\n", count );
+
+    num_bytes = 0;
+    success = WriteFile( writepipe, testdata, sizeof(testdata), &num_bytes, NULL );
+    ok( success, "WriteFile failed %u\n", GetLastError() );
+    ok( num_bytes == sizeof(testdata), "wrong number of bytes written\n" );
+
+    num_bytes = 0;
+    success = GetOverlappedResult( readpipe, &overlapped, &num_bytes, TRUE );
+    ok( success, "GetOverlappedResult failed %u\n", GetLastError() );
+    ok( num_bytes == sizeof(testdata), "wrong number of bytes read\n" );
+    ok( !memcmp( base, testdata, sizeof(testdata)), "didn't receive expected data\n" );
+
+    count = 64;
+    memset( results, 0, sizeof(results) );
+    ret = pGetWriteWatch( WRITE_WATCH_FLAG_RESET, base, size, results, &count, &pagesize );
+    ok( !ret, "GetWriteWatch failed %u\n", GetLastError() );
+    todo_wine ok( count == 1, "wrong count %lu\n", count );
+    ok( results[0] == base, "wrong result %p\n", results[0] );
+
+    CloseHandle( readpipe );
+    CloseHandle( writepipe );
+    CloseHandle( overlapped.hEvent );
+
     /* some invalid parameter tests */
 
     SetLastError( 0xdeadbeef );
@@ -2496,6 +2743,9 @@ static void test_VirtualProtect(void)
     DWORD ret, old_prot, rw_prot, exec_prot, i, j;
     MEMORY_BASIC_INFORMATION info;
     SYSTEM_INFO si;
+    void *addr;
+    SIZE_T size;
+    NTSTATUS status;
 
     GetSystemInfo(&si);
     trace("system page size %#x\n", si.dwPageSize);
@@ -2504,6 +2754,26 @@ static void test_VirtualProtect(void)
     base = VirtualAlloc(0, si.dwPageSize, MEM_RESERVE | MEM_COMMIT, PAGE_NOACCESS);
     ok(base != NULL, "VirtualAlloc failed %d\n", GetLastError());
 
+    SetLastError(0xdeadbeef);
+    ret = VirtualProtect(base, si.dwPageSize, PAGE_READONLY, NULL);
+    ok(!ret, "VirtualProtect should fail\n");
+    ok(GetLastError() == ERROR_NOACCESS, "expected ERROR_NOACCESS, got %d\n", GetLastError());
+    old_prot = 0xdeadbeef;
+    ret = VirtualProtect(base, si.dwPageSize, PAGE_NOACCESS, &old_prot);
+    ok(ret, "VirtualProtect failed %d\n", GetLastError());
+    ok(old_prot == PAGE_NOACCESS, "got %#x != expected PAGE_NOACCESS\n", old_prot);
+
+    addr = base;
+    size = si.dwPageSize;
+    status = pNtProtectVirtualMemory(GetCurrentProcess(), &addr, &size, PAGE_READONLY, NULL);
+    ok(status == STATUS_ACCESS_VIOLATION, "NtProtectVirtualMemory should fail, got %08x\n", status);
+    addr = base;
+    size = si.dwPageSize;
+    old_prot = 0xdeadbeef;
+    status = pNtProtectVirtualMemory(GetCurrentProcess(), &addr, &size, PAGE_NOACCESS, &old_prot);
+    ok(status == STATUS_SUCCESS, "NtProtectVirtualMemory should succeed, got %08x\n", status);
+    ok(old_prot == PAGE_NOACCESS, "got %#x != expected PAGE_NOACCESS\n", old_prot);
+
     for (i = 0; i < sizeof(td)/sizeof(td[0]); i++)
     {
         SetLastError(0xdeadbeef);
@@ -3726,6 +3996,7 @@ START_TEST(virtual)
     pGetWriteWatch = (void *) GetProcAddress(hkernel32, "GetWriteWatch");
     pResetWriteWatch = (void *) GetProcAddress(hkernel32, "ResetWriteWatch");
     pGetProcessDEPPolicy = (void *)GetProcAddress( hkernel32, "GetProcessDEPPolicy" );
+    pIsWow64Process = (void *)GetProcAddress( hkernel32, "IsWow64Process" );
     pNtAreMappedFilesTheSame = (void *)GetProcAddress( hntdll, "NtAreMappedFilesTheSame" );
     pNtMapViewOfSection = (void *)GetProcAddress( hntdll, "NtMapViewOfSection" );
     pNtUnmapViewOfSection = (void *)GetProcAddress( hntdll, "NtUnmapViewOfSection" );
@@ -3733,6 +4004,9 @@ START_TEST(virtual)
     pRtlAddVectoredExceptionHandler = (void *)GetProcAddress( hntdll, "RtlAddVectoredExceptionHandler" );
     pRtlRemoveVectoredExceptionHandler = (void *)GetProcAddress( hntdll, "RtlRemoveVectoredExceptionHandler" );
     pNtQuerySection = (void *)GetProcAddress( hntdll, "NtQuerySection" );
+    pNtProtectVirtualMemory = (void *)GetProcAddress( hntdll, "NtProtectVirtualMemory" );
+    pNtAllocateVirtualMemory = (void *)GetProcAddress( hntdll, "NtAllocateVirtualMemory" );
+    pNtFreeVirtualMemory = (void *)GetProcAddress( hntdll, "NtFreeVirtualMemory" );
 
     test_shared_memory(FALSE);
     test_shared_memory_ro(FALSE, FILE_MAP_READ|FILE_MAP_WRITE);
index 191987c..d7083bc 100644 (file)
@@ -54,6 +54,7 @@ static UINT (WINAPI *pGetLogicalDriveStringsA)(UINT,LPSTR);
 static UINT (WINAPI *pGetLogicalDriveStringsW)(UINT,LPWSTR);
 static BOOL (WINAPI *pGetVolumeInformationA)(LPCSTR, LPSTR, DWORD, LPDWORD, LPDWORD, LPDWORD, LPSTR, DWORD);
 static BOOL (WINAPI *pGetVolumePathNameA)(LPCSTR, LPSTR, DWORD);
+static BOOL (WINAPI *pGetVolumePathNameW)(LPWSTR, LPWSTR, DWORD);
 static BOOL (WINAPI *pGetVolumePathNamesForVolumeNameA)(LPCSTR, LPSTR, DWORD, LPDWORD);
 static BOOL (WINAPI *pGetVolumePathNamesForVolumeNameW)(LPCWSTR, LPWSTR, DWORD, LPDWORD);
 
@@ -535,7 +536,7 @@ static void test_enum_vols(void)
     /*get windows drive letter and update strings for testing  */
     ret = GetWindowsDirectoryA( windowsdir, sizeof(windowsdir) );
     ok(ret < sizeof(windowsdir), "windowsdir is abnormally long!\n");
-    ok(ret != 0, "GetWindowsDirecory: error %d\n", GetLastError());
+    ok(ret != 0, "GetWindowsDirectory: error %d\n", GetLastError());
     path[0] = windowsdir[0];
 
     /* get the unique volume name for the windows drive  */
@@ -593,82 +594,291 @@ static void test_disk_extents(void)
 
 static void test_GetVolumePathNameA(void)
 {
-    BOOL ret;
-    char volume[MAX_PATH];
-    char expected[] = "C:\\", pathC1[] = "C:\\", pathC2[] = "C::";
+    char volume_path[MAX_PATH], cwd[MAX_PATH];
+    struct {
+        const char *file_name;
+        const char *path_name;
+        DWORD       path_len;
+        DWORD       error;
+        DWORD       broken_error;
+    } test_paths[] = {
+        { /* test 0: NULL parameters, 0 output length */
+            NULL, NULL, 0,
+            ERROR_INVALID_PARAMETER, 0xdeadbeef /* winxp */
+        },
+        { /* test 1: empty input, NULL output, 0 output length */
+            "", NULL, 0,
+            ERROR_INVALID_PARAMETER, 0xdeadbeef /* winxp */
+        },
+        { /* test 2: valid input, NULL output, 0 output length */
+            "C:\\", NULL, 0,
+            ERROR_INVALID_PARAMETER, ERROR_FILENAME_EXCED_RANGE /* winxp */
+        },
+        { /* test 3: valid input, valid output, 0 output length */
+            "C:\\", "C:\\", 0,
+            ERROR_INVALID_PARAMETER, ERROR_FILENAME_EXCED_RANGE /* winxp */
+        },
+        { /* test 4: valid input, valid output, 1 output length */
+            "C:\\", "C:\\", 1,
+            ERROR_FILENAME_EXCED_RANGE, NO_ERROR
+        },
+        { /* test 5: valid input, valid output, valid output length */
+            "C:\\", "C:\\", sizeof(volume_path),
+            NO_ERROR, NO_ERROR
+        },
+        { /* test 6: lowercase input, uppercase output, valid output length */
+            "c:\\", "C:\\", sizeof(volume_path),
+            NO_ERROR, NO_ERROR
+        },
+        { /* test 7: poor quality input, valid output, valid output length */
+            "C::", "C:\\", sizeof(volume_path),
+            NO_ERROR, NO_ERROR
+        },
+        { /* test 8: really bogus input, valid output, 1 output length */
+            "\\\\$$$", "C:\\", 1,
+            ERROR_INVALID_NAME, ERROR_FILENAME_EXCED_RANGE
+        },
+        { /* test 9: a reasonable DOS path that is guaranteed to exist */
+            "C:\\windows\\system32", "C:\\", sizeof(volume_path),
+            NO_ERROR, NO_ERROR
+        },
+        { /* test 10: a reasonable DOS path that shouldn't exist */
+            "C:\\windows\\system32\\AnInvalidFolder", "C:\\", sizeof(volume_path),
+            NO_ERROR, NO_ERROR
+        },
+        { /* test 11: a reasonable NT-converted DOS path that shouldn't exist */
+            "\\\\?\\C:\\AnInvalidFolder", "\\\\?\\C:\\", sizeof(volume_path),
+            NO_ERROR, NO_ERROR
+        },
+        { /* test 12: an unreasonable NT-converted DOS path */
+            "\\\\?\\InvalidDrive:\\AnInvalidFolder", "\\\\?\\InvalidDrive:\\" /* win2k, winxp */,
+            sizeof(volume_path),
+            ERROR_INVALID_NAME, NO_ERROR
+        },
+        { /* test 13: an unreasonable NT volume path */
+            "\\\\?\\Volume{00000000-00-0000-0000-000000000000}\\AnInvalidFolder",
+            "\\\\?\\Volume{00000000-00-0000-0000-000000000000}\\" /* win2k, winxp */,
+            sizeof(volume_path),
+            ERROR_INVALID_NAME, NO_ERROR
+        },
+        { /* test 14: an unreasonable NT-ish path */
+            "\\\\ReallyBogus\\InvalidDrive:\\AnInvalidFolder",
+            "\\\\ReallyBogus\\InvalidDrive:\\" /* win2k, winxp */, sizeof(volume_path),
+            ERROR_INVALID_NAME, NO_ERROR
+        },
+        { /* test 15: poor quality input, valid output, valid output length, different drive */
+            "D::", "D:\\", sizeof(volume_path),
+            NO_ERROR, NO_ERROR
+        },
+        { /* test 16: unused drive letter */
+            "M::", "C:\\", 4,
+            ERROR_FILE_NOT_FOUND, ERROR_MORE_DATA
+        },
+        { /* test 17: an unreasonable DOS path */
+            "InvalidDrive:\\AnInvalidFolder", "%CurrentDrive%\\", sizeof(volume_path),
+            NO_ERROR, NO_ERROR
+        },
+        { /* test 18: a reasonable device path */
+            "\\??\\CdRom0", "%CurrentDrive%\\", sizeof(volume_path),
+            NO_ERROR, NO_ERROR
+        },
+        { /* test 19: an unreasonable device path */
+            "\\??\\ReallyBogus", "%CurrentDrive%\\", sizeof(volume_path),
+            NO_ERROR, NO_ERROR
+        },
+        { /* test 20 */
+            "C:", "C:", 2,
+            ERROR_FILENAME_EXCED_RANGE, NO_ERROR
+        },
+        { /* test 21 */
+            "C:", "C:", 3,
+            NO_ERROR, ERROR_FILENAME_EXCED_RANGE
+        },
+        { /* test 22 */
+            "C:\\", "C:", 2,
+            ERROR_FILENAME_EXCED_RANGE, NO_ERROR
+        },
+        { /* test 23 */
+            "C:\\", "C:", 3,
+            NO_ERROR, ERROR_FILENAME_EXCED_RANGE
+        },
+        { /* test 24 */
+            "C::", "C:", 2,
+            ERROR_FILENAME_EXCED_RANGE, NO_ERROR
+        },
+        { /* test 25 */
+            "C::", "C:", 3,
+            NO_ERROR, ERROR_FILENAME_EXCED_RANGE
+        },
+        { /* test 26 */
+            "C::", "C:\\", 4,
+            NO_ERROR, ERROR_MORE_DATA
+        },
+        { /* test 27 */
+            "C:\\windows\\system32\\AnInvalidFolder", "C:", 3,
+            NO_ERROR, ERROR_FILENAME_EXCED_RANGE
+        },
+        { /* test 28 */
+            "\\\\?\\C:\\AnInvalidFolder", "\\\\?\\C:", 3,
+            ERROR_FILENAME_EXCED_RANGE, NO_ERROR
+        },
+        { /* test 29 */
+            "\\\\?\\C:\\AnInvalidFolder", "\\\\?\\C:", 6,
+            ERROR_FILENAME_EXCED_RANGE, NO_ERROR
+        },
+        { /* test 30 */
+            "\\\\?\\C:\\AnInvalidFolder", "\\\\?\\C:", 7,
+            NO_ERROR, ERROR_FILENAME_EXCED_RANGE
+        },
+        { /* test 31 */
+            "\\\\?\\c:\\AnInvalidFolder", "\\\\?\\c:", 7,
+            NO_ERROR, ERROR_FILENAME_EXCED_RANGE
+        },
+        { /* test 32 */
+            "C:/", "C:\\", 4,
+            NO_ERROR, ERROR_MORE_DATA
+        },
+        { /* test 33 */
+            "M:/", "", 4,
+            ERROR_FILE_NOT_FOUND, ERROR_MORE_DATA
+        },
+        { /* test 34 */
+            "C:ABC:DEF:\\AnInvalidFolder", "C:\\", 4,
+            NO_ERROR, ERROR_MORE_DATA
+        },
+        { /* test 35 */
+            "?:ABC:DEF:\\AnInvalidFolder", "?:\\" /* win2k, winxp */, sizeof(volume_path),
+            ERROR_FILE_NOT_FOUND, NO_ERROR
+        },
+        { /* test 36 */
+            "relative/path", "%CurrentDrive%\\", sizeof(volume_path),
+            NO_ERROR, NO_ERROR
+        },
+        { /* test 37 */
+            "/unix-style/absolute/path", "%CurrentDrive%\\", sizeof(volume_path),
+            NO_ERROR, NO_ERROR
+        },
+        { /* test 38 */
+            "\\??\\C:\\NonExistent", "%CurrentDrive%\\", sizeof(volume_path),
+            NO_ERROR, NO_ERROR
+        },
+        { /* test 39 */
+            "\\??\\M:\\NonExistent", "%CurrentDrive%\\", sizeof(volume_path),
+            NO_ERROR, NO_ERROR
+        },
+        { /* test 40 */
+            "somefile:def", "%CurrentDrive%\\", sizeof(volume_path),
+            NO_ERROR, NO_ERROR
+        },
+        { /* test 41 */
+            "s:omefile", "S:\\" /* win2k, winxp */, sizeof(volume_path),
+            ERROR_FILE_NOT_FOUND, NO_ERROR
+        },
+    };
+    BOOL ret, success;
     DWORD error;
+    UINT i;
 
+    /* GetVolumePathNameA is not present before w2k */
     if (!pGetVolumePathNameA)
     {
         win_skip("required functions not found\n");
         return;
     }
 
-    SetLastError( 0xdeadbeef );
-    ret = pGetVolumePathNameA(NULL, NULL, 0);
-    error = GetLastError();
-    ok(!ret, "expected failure\n");
-    ok(error == ERROR_INVALID_PARAMETER
-       || broken( error == 0xdeadbeef) /* <=XP */,
-       "expected ERROR_INVALID_PARAMETER got %u\n", error);
+    /* Obtain the drive of the working directory */
+    ret = GetCurrentDirectoryA( sizeof(cwd), cwd );
+    ok( ret, "Failed to obtain the current working directory.\n" );
+    cwd[2] = 0;
+    ret = SetEnvironmentVariableA( "CurrentDrive", cwd );
+    ok( ret, "Failed to set an environment variable for the current working drive.\n" );
 
-    SetLastError( 0xdeadbeef );
-    ret = pGetVolumePathNameA("", NULL, 0);
-    error = GetLastError();
-    ok(!ret, "expected failure\n");
-    ok(error == ERROR_INVALID_PARAMETER
-       || broken( error == 0xdeadbeef) /* <=XP */,
-       "expected ERROR_INVALID_PARAMETER got %u\n", error);
+    for (i=0; i<sizeof(test_paths)/sizeof(test_paths[0]); i++)
+    {
+        BOOL broken_ret = test_paths[i].broken_error == NO_ERROR;
+        char *output = (test_paths[i].path_name != NULL ? volume_path : NULL);
+        BOOL expected_ret = test_paths[i].error == NO_ERROR;
+
+        volume_path[0] = 0;
+        if (test_paths[i].path_len < sizeof(volume_path))
+            volume_path[ test_paths[i].path_len ] = 0x11;
+
+        SetLastError( 0xdeadbeef );
+        ret = pGetVolumePathNameA( test_paths[i].file_name, output, test_paths[i].path_len );
+        error = GetLastError();
+        ok(ret == expected_ret || broken(ret == broken_ret),
+                                "GetVolumePathName test %d %s unexpectedly.\n",
+                                i, test_paths[i].error == NO_ERROR ? "failed" : "succeeded");
+
+        if (ret)
+        {
+            char path_name[MAX_PATH];
+
+            ExpandEnvironmentStringsA( test_paths[i].path_name, path_name, MAX_PATH);
+            /* If we succeeded then make sure the path is correct */
+            success = (strcmp( volume_path, path_name ) == 0)
+                      || broken(strcasecmp( volume_path, path_name ) == 0) /* XP */;
+            ok(success, "GetVolumePathName test %d unexpectedly returned path %s (expected %s).\n",
+                        i, volume_path, path_name);
+        }
+        else
+        {
+            /* On success Windows always returns ERROR_MORE_DATA, so only worry about failure */
+            success = (error == test_paths[i].error || broken(error == test_paths[i].broken_error));
+            ok(success, "GetVolumePathName test %d unexpectedly returned error 0x%x (expected 0x%x).\n",
+                        i, error, test_paths[i].error);
+        }
 
-    SetLastError( 0xdeadbeef );
-    ret = pGetVolumePathNameA(pathC1, NULL, 0);
-    error = GetLastError();
-    ok(!ret, "expected failure\n");
-    ok(error == ERROR_INVALID_PARAMETER
-       || broken(error == ERROR_FILENAME_EXCED_RANGE) /* <=XP */,
-       "expected ERROR_INVALID_PARAMETER got %u\n", error);
+        if (test_paths[i].path_len < sizeof(volume_path))
+            ok(volume_path[ test_paths[i].path_len ] == 0x11,
+               "GetVolumePathName test %d corrupted byte after end of buffer.\n", i);
+    }
+}
 
-    SetLastError( 0xdeadbeef );
-    ret = pGetVolumePathNameA(pathC1, volume, 0);
-    error = GetLastError();
-    ok(!ret, "expected failure\n");
-    ok(error == ERROR_INVALID_PARAMETER
-       || broken(error == ERROR_FILENAME_EXCED_RANGE ) /* <=XP */,
-       "expected ERROR_INVALID_PARAMETER got %u\n", error);
+static void test_GetVolumePathNameW(void)
+{
+    static WCHAR drive_c1[] = {'C',':',0};
+    static WCHAR drive_c2[] = {'C',':','\\',0};
+    WCHAR volume_path[MAX_PATH];
+    BOOL ret;
 
-    SetLastError( 0xdeadbeef );
-    ret = pGetVolumePathNameA(pathC1, volume, 1);
-    error = GetLastError();
-    ok(!ret, "expected failure\n");
-    ok(error == ERROR_FILENAME_EXCED_RANGE, "expected ERROR_FILENAME_EXCED_RANGE got %u\n", error);
-
-    volume[0] = '\0';
-    ret = pGetVolumePathNameA(pathC1, volume, sizeof(volume));
-    ok(ret, "expected success\n");
-    ok(!strcmp(expected, volume), "expected name '%s', returned '%s'\n", pathC1, volume);
-
-    pathC1[0] = tolower(pathC1[0]);
-    volume[0] = '\0';
-    ret = pGetVolumePathNameA(pathC1, volume, sizeof(volume));
-    ok(ret, "expected success\n");
-todo_wine
-    ok(!strcmp(expected, volume) || broken(!strcasecmp(expected, volume)) /* <=XP */,
-       "expected name '%s', returned '%s'\n", expected, volume);
-
-    volume[0] = '\0';
-    ret = pGetVolumePathNameA(pathC2, volume, sizeof(volume));
-todo_wine
-    ok(ret, "expected success\n");
-todo_wine
-    ok(!strcmp(expected, volume), "expected name '%s', returned '%s'\n", expected, volume);
-
-    /* test an invalid path */
-    SetLastError( 0xdeadbeef );
-    ret = pGetVolumePathNameA("\\\\$$$", volume, 1);
-    error = GetLastError();
-    ok(!ret, "expected failure\n");
-    ok(error == ERROR_INVALID_NAME || broken(ERROR_FILENAME_EXCED_RANGE) /* <=2000 */,
-       "expected ERROR_INVALID_NAME got %u\n", error);
+    if (!pGetVolumePathNameW)
+    {
+        win_skip("required functions not found\n");
+        return;
+    }
+
+    volume_path[0] = 0;
+    volume_path[1] = 0x11;
+    ret = pGetVolumePathNameW( drive_c1, volume_path, 1 );
+    ok(!ret, "GetVolumePathNameW test succeeded unexpectedly.\n");
+    ok(GetLastError() == ERROR_FILENAME_EXCED_RANGE, "GetVolumePathNameW unexpectedly returned error 0x%x (expected 0x%x).\n",
+        GetLastError(), ERROR_FILENAME_EXCED_RANGE);
+    ok(volume_path[1] == 0x11, "GetVolumePathW corrupted byte after end of buffer.\n");
+
+    volume_path[0] = 0;
+    volume_path[2] = 0x11;
+    ret = pGetVolumePathNameW( drive_c1, volume_path, 2 );
+    ok(!ret, "GetVolumePathNameW test succeeded unexpectedly.\n");
+    ok(GetLastError() == ERROR_FILENAME_EXCED_RANGE, "GetVolumePathNameW unexpectedly returned error 0x%x (expected 0x%x).\n",
+        GetLastError(), ERROR_FILENAME_EXCED_RANGE);
+    ok(volume_path[2] == 0x11, "GetVolumePathW corrupted byte after end of buffer.\n");
+
+    volume_path[0] = 0;
+    volume_path[3] = 0x11;
+    ret = pGetVolumePathNameW( drive_c1, volume_path, 3 );
+    ok(ret || broken(!ret) /* win2k */, "GetVolumePathNameW test failed unexpectedly.\n");
+    ok(memcmp(volume_path, drive_c1, sizeof(drive_c1)) == 0
+       || broken(volume_path[0] == 0) /* win2k */,
+       "GetVolumePathNameW unexpectedly returned wrong path.\n");
+    ok(volume_path[3] == 0x11, "GetVolumePathW corrupted byte after end of buffer.\n");
+
+    volume_path[0] = 0;
+    volume_path[4] = 0x11;
+    ret = pGetVolumePathNameW( drive_c1, volume_path, 4 );
+    ok(ret, "GetVolumePathNameW test failed unexpectedly.\n");
+    ok(memcmp(volume_path, drive_c2, sizeof(drive_c2)) == 0, "GetVolumePathNameW unexpectedly returned wrong path.\n");
+    ok(volume_path[4] == 0x11, "GetVolumePathW corrupted byte after end of buffer.\n");
 }
 
 static void test_GetVolumePathNamesForVolumeNameA(void)
@@ -1016,6 +1226,7 @@ START_TEST(volume)
     pGetLogicalDriveStringsW = (void *) GetProcAddress(hdll, "GetLogicalDriveStringsW");
     pGetVolumeInformationA = (void *) GetProcAddress(hdll, "GetVolumeInformationA");
     pGetVolumePathNameA = (void *) GetProcAddress(hdll, "GetVolumePathNameA");
+    pGetVolumePathNameW = (void *) GetProcAddress(hdll, "GetVolumePathNameW");
     pGetVolumePathNamesForVolumeNameA = (void *) GetProcAddress(hdll, "GetVolumePathNamesForVolumeNameA");
     pGetVolumePathNamesForVolumeNameW = (void *) GetProcAddress(hdll, "GetVolumePathNamesForVolumeNameW");
 
@@ -1023,6 +1234,7 @@ START_TEST(volume)
     test_define_dos_deviceA();
     test_FindFirstVolume();
     test_GetVolumePathNameA();
+    test_GetVolumePathNameW();
     test_GetVolumeNameForVolumeMountPointA();
     test_GetVolumeNameForVolumeMountPointW();
     test_GetLogicalDriveStringsA();