[KERNEL32_WINETEST]: Sync with Wine 1.5.19.
authorAmine Khaldi <amine.khaldi@reactos.org>
Sun, 9 Dec 2012 20:39:13 +0000 (20:39 +0000)
committerAmine Khaldi <amine.khaldi@reactos.org>
Sun, 9 Dec 2012 20:39:13 +0000 (20:39 +0000)
svn path=/trunk/; revision=57853

16 files changed:
rostests/winetests/kernel32/codepage.c
rostests/winetests/kernel32/console.c
rostests/winetests/kernel32/debugger.c
rostests/winetests/kernel32/file.c
rostests/winetests/kernel32/format_msg.c
rostests/winetests/kernel32/loader.c
rostests/winetests/kernel32/locale.c
rostests/winetests/kernel32/module.c
rostests/winetests/kernel32/pipe.c
rostests/winetests/kernel32/process.c
rostests/winetests/kernel32/sync.c
rostests/winetests/kernel32/thread.c
rostests/winetests/kernel32/timer.c
rostests/winetests/kernel32/version.c
rostests/winetests/kernel32/virtual.c
rostests/winetests/kernel32/volume.c

index 61bcf54..c980348 100755 (executable)
@@ -189,6 +189,74 @@ static void test_negative_dest_length(void)
 
 }
 
+static void test_other_invalid_parameters(void)
+{
+    char c_string[] = "Hello World";
+    size_t c_string_len = sizeof(c_string);
+    WCHAR w_string[] = {'H','e','l','l','o',' ','W','o','r','l','d',0};
+    size_t w_string_len = sizeof(w_string) / sizeof(WCHAR);
+    BOOL used;
+    INT len;
+
+    /* srclen=0 => ERROR_INVALID_PARAMETER */
+    SetLastError(0xdeadbeef);
+    len = WideCharToMultiByte(CP_ACP, 0, w_string, 0, c_string, c_string_len, NULL, NULL);
+    ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%x\n", len, GetLastError());
+
+    SetLastError(0xdeadbeef);
+    len = MultiByteToWideChar(CP_ACP, 0, c_string, 0, w_string, w_string_len);
+    ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%x\n", len, GetLastError());
+
+
+    /* dst=NULL but dstlen not 0 => ERROR_INVALID_PARAMETER */
+    SetLastError(0xdeadbeef);
+    len = WideCharToMultiByte(CP_ACP, 0, w_string, w_string_len, NULL, c_string_len, NULL, NULL);
+    ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%x\n", len, GetLastError());
+
+    SetLastError(0xdeadbeef);
+    len = MultiByteToWideChar(CP_ACP, 0, c_string, c_string_len, NULL, w_string_len);
+    ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%x\n", len, GetLastError());
+
+
+    /* CP_UTF7, CP_UTF8, or CP_SYMBOL and defchar not NULL => ERROR_INVALID_PARAMETER */
+    /* CP_SYMBOL's behavior here is undocumented */
+    SetLastError(0xdeadbeef);
+    len = WideCharToMultiByte(CP_UTF7, 0, w_string, w_string_len, c_string, c_string_len, c_string, NULL);
+    ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%x\n", len, GetLastError());
+
+    SetLastError(0xdeadbeef);
+    len = WideCharToMultiByte(CP_UTF8, 0, w_string, w_string_len, c_string, c_string_len, c_string, NULL);
+    ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%x\n", len, GetLastError());
+
+    SetLastError(0xdeadbeef);
+    len = WideCharToMultiByte(CP_SYMBOL, 0, w_string, w_string_len, c_string, c_string_len, c_string, NULL);
+    ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%x\n", len, GetLastError());
+
+
+    /* CP_UTF7, CP_UTF8, or CP_SYMBOL and used not NULL => ERROR_INVALID_PARAMETER */
+    /* CP_SYMBOL's behavior here is undocumented */
+    SetLastError(0xdeadbeef);
+    len = WideCharToMultiByte(CP_UTF7, 0, w_string, w_string_len, c_string, c_string_len, NULL, &used);
+    ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%x\n", len, GetLastError());
+
+    SetLastError(0xdeadbeef);
+    len = WideCharToMultiByte(CP_UTF8, 0, w_string, w_string_len, c_string, c_string_len, NULL, &used);
+    ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%x\n", len, GetLastError());
+
+    SetLastError(0xdeadbeef);
+    len = WideCharToMultiByte(CP_SYMBOL, 0, w_string, w_string_len, c_string, c_string_len, NULL, &used);
+    ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%x\n", len, GetLastError());
+
+
+    /* CP_UTF7, flags not 0 and used not NULL => ERROR_INVALID_PARAMETER */
+    /* (tests precedence of ERROR_INVALID_PARAMETER over ERROR_INVALID_FLAGS) */
+    /* The same test with CP_SYMBOL instead of CP_UTF7 gives ERROR_INVALID_FLAGS
+       instead except on Windows NT4 */
+    SetLastError(0xdeadbeef);
+    len = WideCharToMultiByte(CP_UTF7, 1, w_string, w_string_len, c_string, c_string_len, NULL, &used);
+    ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%x\n", len, GetLastError());
+}
+
 static void test_overlapped_buffers(void)
 {
     static const WCHAR strW[] = {'j','u','s','t',' ','a',' ','t','e','s','t',0};
@@ -402,6 +470,7 @@ START_TEST(codepage)
     test_null_source();
     test_negative_source_length();
     test_negative_dest_length();
+    test_other_invalid_parameters();
     test_overlapped_buffers();
 
     /* WideCharToMultiByte has two code paths, test both here */
index d0c58f8..b1907d7 100755 (executable)
@@ -1031,7 +1031,7 @@ static void test_OpenCON(void)
         h = CreateFileW(conW, GENERIC_READ, 0, NULL, accesses[i], 0, NULL);
         /* Windows versions differ here:
          * MSDN states in CreateFile that TRUNCATE_EXISTING requires GENERIC_WRITE
-         * NT, XP, Vista comply, but Win7 doesn't and allows to open CON with TRUNCATE_EXISTING
+         * NT, XP, Vista comply, but Win7 doesn't and allows opening CON with TRUNCATE_EXISTING
          * So don't test when disposition is TRUNCATE_EXISTING
          */
         if (accesses[i] != TRUNCATE_EXISTING)
@@ -1354,12 +1354,12 @@ static void test_WriteConsoleInputA(HANDLE input_handle)
         LPDWORD written;
         DWORD expected_count;
         DWORD last_error;
-        int win7_crash;
+        int win_crash;
     } invalid_table[] =
     {
         {NULL, NULL, 0, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
         {NULL, NULL, 0, &count, 0, ERROR_INVALID_HANDLE},
-        {NULL, NULL, 1, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS},
+        {NULL, NULL, 1, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
         {NULL, NULL, 1, &count, 0xdeadbeef, ERROR_INVALID_ACCESS},
         {NULL, &event, 0, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
         {NULL, &event, 0, &count, 0, ERROR_INVALID_HANDLE},
@@ -1367,14 +1367,14 @@ static void test_WriteConsoleInputA(HANDLE input_handle)
         {NULL, &event, 1, &count, 0, ERROR_INVALID_HANDLE},
         {INVALID_HANDLE_VALUE, NULL, 0, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
         {INVALID_HANDLE_VALUE, NULL, 0, &count, 0, ERROR_INVALID_HANDLE},
-        {INVALID_HANDLE_VALUE, NULL, 1, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS},
+        {INVALID_HANDLE_VALUE, NULL, 1, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
         {INVALID_HANDLE_VALUE, NULL, 1, &count, 0xdeadbeef, ERROR_INVALID_ACCESS},
         {INVALID_HANDLE_VALUE, &event, 0, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
         {INVALID_HANDLE_VALUE, &event, 0, &count, 0, ERROR_INVALID_HANDLE},
         {INVALID_HANDLE_VALUE, &event, 1, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
         {INVALID_HANDLE_VALUE, &event, 1, &count, 0, ERROR_INVALID_HANDLE},
         {input_handle, NULL, 0, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
-        {input_handle, NULL, 1, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS},
+        {input_handle, NULL, 1, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
         {input_handle, NULL, 1, &count, 0xdeadbeef, ERROR_INVALID_ACCESS},
         {input_handle, &event, 0, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
         {input_handle, &event, 1, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
@@ -1406,7 +1406,7 @@ static void test_WriteConsoleInputA(HANDLE input_handle)
 
     for (i = 0; i < sizeof(invalid_table)/sizeof(invalid_table[0]); i++)
     {
-        if (invalid_table[i].win7_crash)
+        if (invalid_table[i].win_crash)
             continue;
 
         SetLastError(0xdeadbeef);
@@ -1597,12 +1597,12 @@ static void test_WriteConsoleInputW(HANDLE input_handle)
         LPDWORD written;
         DWORD expected_count;
         DWORD last_error;
-        int win7_crash;
+        int win_crash;
     } invalid_table[] =
     {
         {NULL, NULL, 0, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
         {NULL, NULL, 0, &count, 0, ERROR_INVALID_HANDLE},
-        {NULL, NULL, 1, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS},
+        {NULL, NULL, 1, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
         {NULL, NULL, 1, &count, 0xdeadbeef, ERROR_INVALID_ACCESS},
         {NULL, &event, 0, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
         {NULL, &event, 0, &count, 0, ERROR_INVALID_HANDLE},
@@ -1610,14 +1610,14 @@ static void test_WriteConsoleInputW(HANDLE input_handle)
         {NULL, &event, 1, &count, 0, ERROR_INVALID_HANDLE},
         {INVALID_HANDLE_VALUE, NULL, 0, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
         {INVALID_HANDLE_VALUE, NULL, 0, &count, 0, ERROR_INVALID_HANDLE},
-        {INVALID_HANDLE_VALUE, NULL, 1, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS},
+        {INVALID_HANDLE_VALUE, NULL, 1, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
         {INVALID_HANDLE_VALUE, NULL, 1, &count, 0xdeadbeef, ERROR_INVALID_ACCESS},
         {INVALID_HANDLE_VALUE, &event, 0, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
         {INVALID_HANDLE_VALUE, &event, 0, &count, 0, ERROR_INVALID_HANDLE},
         {INVALID_HANDLE_VALUE, &event, 1, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
         {INVALID_HANDLE_VALUE, &event, 1, &count, 0, ERROR_INVALID_HANDLE},
         {input_handle, NULL, 0, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
-        {input_handle, NULL, 1, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS},
+        {input_handle, NULL, 1, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
         {input_handle, NULL, 1, &count, 0xdeadbeef, ERROR_INVALID_ACCESS},
         {input_handle, &event, 0, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
         {input_handle, &event, 1, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
@@ -1649,7 +1649,7 @@ static void test_WriteConsoleInputW(HANDLE input_handle)
 
     for (i = 0; i < sizeof(invalid_table)/sizeof(invalid_table[0]); i++)
     {
-        if (invalid_table[i].win7_crash)
+        if (invalid_table[i].win_crash)
             continue;
 
         SetLastError(0xdeadbeef);
index 2094190..22f7190 100644 (file)
@@ -287,6 +287,13 @@ static void crash_and_debug(HKEY hkey, const char* argv0, const char* dbgtasks)
     }
 
     ret=RegSetValueExA(hkey, "auto", 0, REG_SZ, (BYTE*)"1", 2);
+    if (ret == ERROR_ACCESS_DENIED)
+    {
+        skip_crash_and_debug = TRUE;
+        skip("No write access to change the debugger\n");
+        return;
+    }
+
     ok(ret == ERROR_SUCCESS, "unable to set AeDebug/auto: ret=%d\n", ret);
 
     get_file_name(dbglog);
index 9cfb664..d35c38f 100755 (executable)
@@ -21,7 +21,7 @@
  */
 
 /* ReplaceFile requires Windows 2000 or newer */
-#define _WIN32_WINNT 0x0500
+#define _WIN32_WINNT 0x0600
 
 #include <stdarg.h>
 #include <stdlib.h>
@@ -32,6 +32,7 @@
 #include "windef.h"
 #include "winbase.h"
 #include "winerror.h"
+#include "winnls.h"
 
 static HANDLE (WINAPI *pFindFirstFileExA)(LPCSTR,FINDEX_INFO_LEVELS,LPVOID,FINDEX_SEARCH_OPS,LPVOID,DWORD);
 static BOOL (WINAPI *pReplaceFileA)(LPCSTR, LPCSTR, LPCSTR, DWORD, LPVOID, LPVOID);
@@ -39,6 +40,9 @@ static BOOL (WINAPI *pReplaceFileW)(LPCWSTR, LPCWSTR, LPCWSTR, DWORD, LPVOID, LP
 static UINT (WINAPI *pGetSystemWindowsDirectoryA)(LPSTR, UINT);
 static BOOL (WINAPI *pGetVolumeNameForVolumeMountPointA)(LPCSTR, LPSTR, DWORD);
 static DWORD (WINAPI *pQueueUserAPC)(PAPCFUNC pfnAPC, HANDLE hThread, ULONG_PTR dwData);
+static BOOL (WINAPI *pGetFileInformationByHandleEx)(HANDLE, FILE_INFO_BY_HANDLE_CLASS, LPVOID, DWORD);
+static HANDLE (WINAPI *pOpenFileById)(HANDLE, LPFILE_ID_DESCRIPTOR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD);
+static BOOL (WINAPI *pSetFileValidData)(HANDLE, LONGLONG);
 
 /* keep filename and filenameW the same */
 static const char filename[] = "testfile.xxx";
@@ -73,6 +77,9 @@ static void InitFunctionPointers(void)
     pGetSystemWindowsDirectoryA=(void*)GetProcAddress(hkernel32, "GetSystemWindowsDirectoryA");
     pGetVolumeNameForVolumeMountPointA = (void *) GetProcAddress(hkernel32, "GetVolumeNameForVolumeMountPointA");
     pQueueUserAPC = (void *) GetProcAddress(hkernel32, "QueueUserAPC");
+    pGetFileInformationByHandleEx = (void *) GetProcAddress(hkernel32, "GetFileInformationByHandleEx");
+    pOpenFileById = (void *) GetProcAddress(hkernel32, "OpenFileById");
+    pSetFileValidData = (void *) GetProcAddress(hkernel32, "SetFileValidData");
 }
 
 static void test__hread( void )
@@ -796,7 +803,7 @@ static void dumpmem(unsigned char *mem, int len)
         p = hex;
         c = txt;
         do {
-            p += sprintf(p, "%02hhx ", mem[x]);
+            p += sprintf(p, "%02x ", mem[x]);
             *c++ = (mem[x] >= 32 && mem[x] <= 127) ? mem[x] : '.';
         } while (++x % 16 && x < len);
         *c = '\0';
@@ -1291,6 +1298,8 @@ static void test_GetTempFileNameA(void)
 static void test_DeleteFileA( void )
 {
     BOOL ret;
+    char temp_path[MAX_PATH], temp_file[MAX_PATH];
+    HANDLE hfile;
 
     ret = DeleteFileA(NULL);
     ok(!ret && (GetLastError() == ERROR_INVALID_PARAMETER ||
@@ -1308,6 +1317,25 @@ static void test_DeleteFileA( void )
                 GetLastError() == ERROR_ACCESS_DENIED ||
                 GetLastError() == ERROR_INVALID_FUNCTION),
        "DeleteFileA(\"nul\") returned ret=%d error=%d\n",ret,GetLastError());
+
+    GetTempPathA(MAX_PATH, temp_path);
+    GetTempFileName(temp_path, "tst", 0, temp_file);
+
+    SetLastError(0xdeadbeef);
+    hfile = CreateFile(temp_file, GENERIC_READ, FILE_SHARE_DELETE | FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
+    ok(hfile != INVALID_HANDLE_VALUE, "CreateFile error %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = DeleteFile(temp_file);
+todo_wine
+    ok(ret, "DeleteFile error %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = CloseHandle(hfile);
+    ok(ret, "CloseHandle error %d\n", GetLastError());
+    ret = DeleteFile(temp_file);
+todo_wine
+    ok(!ret, "DeleteFile should fail\n");
 }
 
 static void test_DeleteFileW( void )
@@ -1758,7 +1786,7 @@ static BOOL create_fake_dll( LPCSTR filename )
 #elif defined __sparc__
     nt->FileHeader.Machine = IMAGE_FILE_MACHINE_SPARC;
 #elif defined __arm__
-    nt->FileHeader.Machine = IMAGE_FILE_MACHINE_ARMV7;
+    nt->FileHeader.Machine = IMAGE_FILE_MACHINE_ARMNT;
 #else
 # error You must specify the machine type
 #endif
@@ -3237,6 +3265,314 @@ static void test_CreatFile(void)
     DeleteFile(file_name);
 }
 
+static void test_GetFileInformationByHandleEx(void)
+{
+    int i;
+    char tempPath[MAX_PATH], tempFileName[MAX_PATH], buffer[1024];
+    BOOL ret;
+    DWORD ret2;
+    HANDLE directory;
+    FILE_ID_BOTH_DIR_INFO *bothDirInfo;
+    struct {
+        FILE_INFO_BY_HANDLE_CLASS handleClass;
+        void *ptr;
+        DWORD size;
+        DWORD errorCode;
+    } checks[] = {
+        {0xdeadbeef, NULL, 0, ERROR_INVALID_PARAMETER},
+        {FileIdBothDirectoryInfo, NULL, 0, ERROR_BAD_LENGTH},
+        {FileIdBothDirectoryInfo, NULL, sizeof(buffer), ERROR_NOACCESS},
+        {FileIdBothDirectoryInfo, buffer, 0, ERROR_BAD_LENGTH}};
+
+    if (!pGetFileInformationByHandleEx)
+    {
+        win_skip("GetFileInformationByHandleEx is missing.\n");
+        return;
+    }
+
+    ret2 = GetTempPathA(sizeof(tempPath), tempPath);
+    ok(ret2, "GetFileInformationByHandleEx: GetTempPathA failed, got error %u.\n", GetLastError());
+
+    /* ensure the existence of a file in the temp folder */
+    ret2 = GetTempFileNameA(tempPath, "abc", 0, tempFileName);
+    ok(ret2, "GetFileInformationByHandleEx: GetTempFileNameA failed, got error %u.\n", GetLastError());
+    ok(GetFileAttributesA(tempFileName) != INVALID_FILE_ATTRIBUTES, "GetFileInformationByHandleEx: "
+        "GetFileAttributesA failed to find the temp file, got error %u.\n", GetLastError());
+
+    directory = CreateFileA(tempPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+        NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+    ok(directory != INVALID_HANDLE_VALUE, "GetFileInformationByHandleEx: failed to open the temp folder, "
+        "got error %u.\n", GetLastError());
+
+    for (i = 0; i < sizeof(checks) / sizeof(checks[0]); i += 1)
+    {
+        SetLastError(0xdeadbeef);
+        ret = pGetFileInformationByHandleEx(directory, checks[i].handleClass, checks[i].ptr, checks[i].size);
+        ok(!ret && GetLastError() == checks[i].errorCode, "GetFileInformationByHandleEx: expected error %u, "
+           "got %u.\n", checks[i].errorCode, GetLastError());
+    }
+
+    while (TRUE)
+    {
+        memset(buffer, 0xff, sizeof(buffer));
+        ret = pGetFileInformationByHandleEx(directory, FileIdBothDirectoryInfo, buffer, sizeof(buffer));
+        if (!ret && GetLastError() == ERROR_NO_MORE_FILES)
+            break;
+        ok(ret, "GetFileInformationByHandleEx: failed to query for FileIdBothDirectoryInfo, got error %u.\n", GetLastError());
+        if (!ret)
+            break;
+        bothDirInfo = (FILE_ID_BOTH_DIR_INFO *)buffer;
+        while (TRUE)
+        {
+            ok(bothDirInfo->FileAttributes != 0xffffffff, "GetFileInformationByHandleEx: returned invalid file attributes.\n");
+            ok(bothDirInfo->FileId.u.LowPart != 0xffffffff, "GetFileInformationByHandleEx: returned invalid file id.\n");
+            ok(bothDirInfo->FileNameLength != 0xffffffff, "GetFileInformationByHandleEx: returned invalid file name length.\n");
+            if (!bothDirInfo->NextEntryOffset)
+                break;
+            bothDirInfo = (FILE_ID_BOTH_DIR_INFO *)(((char *)bothDirInfo) + bothDirInfo->NextEntryOffset);
+        }
+    }
+
+    CloseHandle(directory);
+    DeleteFile(tempFileName);
+}
+
+static void test_OpenFileById(void)
+{
+    char tempPath[MAX_PATH], tempFileName[MAX_PATH], buffer[256], tickCount[256];
+    WCHAR tempFileNameW[MAX_PATH];
+    BOOL ret, found;
+    DWORD ret2, count, tempFileNameLen;
+    HANDLE directory, handle, tempFile;
+    FILE_ID_BOTH_DIR_INFO *bothDirInfo;
+    FILE_ID_DESCRIPTOR fileIdDescr;
+
+    if (!pGetFileInformationByHandleEx || !pOpenFileById)
+    {
+        win_skip("GetFileInformationByHandleEx or OpenFileById is missing.\n");
+        return;
+    }
+
+    ret2 = GetTempPathA(sizeof(tempPath), tempPath);
+    ok(ret2, "OpenFileById: GetTempPath failed, got error %u.\n", GetLastError());
+
+    /* ensure the existence of a file in the temp folder */
+    ret2 = GetTempFileNameA(tempPath, "abc", 0, tempFileName);
+    ok(ret2, "OpenFileById: GetTempFileNameA failed, got error %u.\n", GetLastError());
+    ok(GetFileAttributesA(tempFileName) != INVALID_FILE_ATTRIBUTES,
+        "OpenFileById: GetFileAttributesA failed to find the temp file, got error %u\n", GetLastError());
+
+    ret2 = MultiByteToWideChar(CP_ACP, 0, tempFileName + strlen(tempPath), -1, tempFileNameW, sizeof(tempFileNameW)/sizeof(tempFileNameW[0]));
+    ok(ret2, "OpenFileById: MultiByteToWideChar failed to convert tempFileName, got error %u.\n", GetLastError());
+    tempFileNameLen = ret2 - 1;
+
+    tempFile = CreateFileA(tempFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+    ok(tempFile != INVALID_HANDLE_VALUE, "OpenFileById: failed to create a temp file, "
+           "got error %u.\n", GetLastError());
+    ret2 = sprintf(tickCount, "%u", GetTickCount());
+    ret = WriteFile(tempFile, tickCount, ret2, &count, NULL);
+    ok(ret, "OpenFileById: WriteFile failed, got error %u.\n", GetLastError());
+    CloseHandle(tempFile);
+
+    directory = CreateFileA(tempPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+        NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+    ok(directory != INVALID_HANDLE_VALUE, "OpenFileById: failed to open the temp folder, "
+        "got error %u.\n", GetLastError());
+
+    /* get info about the temp folder itself */
+    bothDirInfo = (FILE_ID_BOTH_DIR_INFO *)buffer;
+    ret = pGetFileInformationByHandleEx(directory, FileIdBothDirectoryInfo, buffer, sizeof(buffer));
+    ok(ret, "OpenFileById: failed to query for FileIdBothDirectoryInfo, got error %u.\n", GetLastError());
+    ok(bothDirInfo->FileNameLength == sizeof(WCHAR) && bothDirInfo->FileName[0] == '.',
+        "OpenFileById: failed to return the temp folder at the first entry, got error %u.\n", GetLastError());
+
+    /* open the temp folder itself */
+    fileIdDescr.dwSize    = sizeof(fileIdDescr);
+    fileIdDescr.Type      = FileIdType;
+    U(fileIdDescr).FileId = bothDirInfo->FileId;
+    handle = pOpenFileById(directory, &fileIdDescr, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, 0);
+    todo_wine
+    ok(handle != INVALID_HANDLE_VALUE, "OpenFileById: failed to open the temp folder itself, got error %u.\n", GetLastError());
+    CloseHandle(handle);
+
+    /* find the temp file in the temp folder */
+    found = FALSE;
+    while (!found)
+    {
+        ret = pGetFileInformationByHandleEx(directory, FileIdBothDirectoryInfo, buffer, sizeof(buffer));
+        ok(ret, "OpenFileById: failed to query for FileIdBothDirectoryInfo, got error %u.\n", GetLastError());
+        if (!ret)
+            break;
+        bothDirInfo = (FILE_ID_BOTH_DIR_INFO *)buffer;
+        while (TRUE)
+        {
+            if (tempFileNameLen == bothDirInfo->FileNameLength / sizeof(WCHAR) &&
+                memcmp(tempFileNameW, bothDirInfo->FileName, bothDirInfo->FileNameLength) == 0)
+            {
+                found = TRUE;
+                break;
+            }
+            if (!bothDirInfo->NextEntryOffset)
+                break;
+            bothDirInfo = (FILE_ID_BOTH_DIR_INFO *)(((char *)bothDirInfo) + bothDirInfo->NextEntryOffset);
+        }
+    }
+    ok(found, "OpenFileById: failed to find the temp file in the temp folder.\n");
+
+    SetLastError(0xdeadbeef);
+    handle = pOpenFileById(directory, NULL, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, 0);
+    ok(handle == INVALID_HANDLE_VALUE && GetLastError() == ERROR_INVALID_PARAMETER,
+        "OpenFileById: expected ERROR_INVALID_PARAMETER, got error %u.\n", GetLastError());
+
+    fileIdDescr.dwSize    = sizeof(fileIdDescr);
+    fileIdDescr.Type      = FileIdType;
+    U(fileIdDescr).FileId = bothDirInfo->FileId;
+    handle = pOpenFileById(directory, &fileIdDescr, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, 0);
+    ok(handle != INVALID_HANDLE_VALUE, "OpenFileById: failed to open the file, got error %u.\n", GetLastError());
+
+    ret = ReadFile(handle, buffer, sizeof(buffer), &count, NULL);
+    buffer[count] = 0;
+    ok(ret, "OpenFileById: ReadFile failed, got error %u.\n", GetLastError());
+    ok(strcmp(tickCount, buffer) == 0, "OpenFileById: invalid contents of the temp file.\n");
+
+    CloseHandle(handle);
+    CloseHandle(directory);
+    DeleteFile(tempFileName);
+}
+
+static void test_SetFileValidData(void)
+{
+    BOOL ret;
+    HANDLE handle;
+    DWORD error, count;
+    char path[MAX_PATH], filename[MAX_PATH];
+    TOKEN_PRIVILEGES privs;
+    HANDLE token = NULL;
+
+    if (!pSetFileValidData)
+    {
+        win_skip("SetFileValidData is missing\n");
+        return;
+    }
+    GetTempPathA(sizeof(path), path);
+    GetTempFileNameA(path, "tst", 0, filename);
+    handle = CreateFileA(filename, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
+    WriteFile(handle, "test", sizeof("test") - 1, &count, NULL);
+    CloseHandle(handle);
+
+    SetLastError(0xdeadbeef);
+    ret = pSetFileValidData(INVALID_HANDLE_VALUE, 0);
+    error = GetLastError();
+    ok(!ret, "SetFileValidData succeeded\n");
+    ok(error == ERROR_INVALID_HANDLE, "got %u\n", error);
+
+    SetLastError(0xdeadbeef);
+    ret = pSetFileValidData(INVALID_HANDLE_VALUE, -1);
+    error = GetLastError();
+    ok(!ret, "SetFileValidData succeeded\n");
+    ok(error == ERROR_INVALID_HANDLE, "got %u\n", error);
+
+    /* file opened for reading */
+    handle = CreateFileA(filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
+
+    SetLastError(0xdeadbeef);
+    ret = pSetFileValidData(handle, 0);
+    ok(!ret, "SetFileValidData succeeded\n");
+    error = GetLastError();
+    ok(error == ERROR_ACCESS_DENIED, "got %u\n", error);
+
+    SetLastError(0xdeadbeef);
+    ret = pSetFileValidData(handle, -1);
+    error = GetLastError();
+    ok(!ret, "SetFileValidData succeeded\n");
+    ok(error == ERROR_ACCESS_DENIED, "got %u\n", error);
+    CloseHandle(handle);
+
+    handle = CreateFileA(filename, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
+
+    SetLastError(0xdeadbeef);
+    ret = pSetFileValidData(handle, 0);
+    error = GetLastError();
+    ok(!ret, "SetFileValidData succeeded\n");
+    todo_wine ok(error == ERROR_PRIVILEGE_NOT_HELD, "got %u\n", error);
+    CloseHandle(handle);
+
+    privs.PrivilegeCount = 1;
+    privs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+
+    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token) ||
+        !LookupPrivilegeValue(NULL, SE_MANAGE_VOLUME_NAME, &privs.Privileges[0].Luid) ||
+        !AdjustTokenPrivileges(token, FALSE, &privs, sizeof(privs), NULL, NULL))
+    {
+        win_skip("cannot enable SE_MANAGE_VOLUME_NAME privilege\n");
+        CloseHandle(token);
+        return;
+    }
+    handle = CreateFileA(filename, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
+
+    SetLastError(0xdeadbeef);
+    ret = pSetFileValidData(handle, 0);
+    error = GetLastError();
+    ok(!ret, "SetFileValidData succeeded\n");
+    ok(error == ERROR_INVALID_PARAMETER, "got %u\n", error);
+
+    SetLastError(0xdeadbeef);
+    ret = pSetFileValidData(handle, -1);
+    error = GetLastError();
+    ok(!ret, "SetFileValidData succeeded\n");
+    ok(error == ERROR_INVALID_PARAMETER, "got %u\n", error);
+
+    SetLastError(0xdeadbeef);
+    ret = pSetFileValidData(handle, 2);
+    error = GetLastError();
+    todo_wine ok(!ret, "SetFileValidData succeeded\n");
+    todo_wine ok(error == ERROR_INVALID_PARAMETER, "got %u\n", error);
+
+    ret = pSetFileValidData(handle, 4);
+    ok(ret, "SetFileValidData failed %u\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = pSetFileValidData(handle, 8);
+    error = GetLastError();
+    ok(!ret, "SetFileValidData succeeded\n");
+    ok(error == ERROR_INVALID_PARAMETER, "got %u\n", error);
+
+    count = SetFilePointer(handle, 1024, NULL, FILE_END);
+    ok(count != INVALID_SET_FILE_POINTER, "SetFilePointer failed %u\n", GetLastError());
+    ret = SetEndOfFile(handle);
+    ok(ret, "SetEndOfFile failed %u\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = pSetFileValidData(handle, 2);
+    error = GetLastError();
+    todo_wine ok(!ret, "SetFileValidData succeeded\n");
+    todo_wine ok(error == ERROR_INVALID_PARAMETER, "got %u\n", error);
+
+    ret = pSetFileValidData(handle, 4);
+    ok(ret, "SetFileValidData failed %u\n", GetLastError());
+
+    ret = pSetFileValidData(handle, 8);
+    ok(ret, "SetFileValidData failed %u\n", GetLastError());
+
+    ret = pSetFileValidData(handle, 4);
+    error = GetLastError();
+    todo_wine ok(!ret, "SetFileValidData succeeded\n");
+    todo_wine ok(error == ERROR_INVALID_PARAMETER, "got %u\n", error);
+
+    ret = pSetFileValidData(handle, 1024);
+    ok(ret, "SetFileValidData failed %u\n", GetLastError());
+
+    ret = pSetFileValidData(handle, 2048);
+    error = GetLastError();
+    ok(!ret, "SetFileValidData succeeded\n");
+    ok(error == ERROR_INVALID_PARAMETER, "got %u\n", error);
+
+    privs.Privileges[0].Attributes = 0;
+    AdjustTokenPrivileges(token, FALSE, &privs, sizeof(privs), NULL, NULL);
+    CloseHandle(token);
+    DeleteFile(filename);
+}
+
 START_TEST(file)
 {
     InitFunctionPointers();
@@ -3276,4 +3612,7 @@ START_TEST(file)
     test_RemoveDirectory();
     test_ReplaceFileA();
     test_ReplaceFileW();
+    test_GetFileInformationByHandleEx();
+    test_OpenFileById();
+    test_SetFileValidData();
 }
index 1f56440..005e6c4 100755 (executable)
@@ -997,6 +997,195 @@ static void test_message_ignore_inserts_wide(void)
     ok(!lstrcmpW(s_2sp, out), "Expected output string \"  \", got %s\n", wine_dbgstr_w(out));
 }
 
+static void test_message_wrap(void)
+{
+    DWORD ret;
+    int i;
+    CHAR in[300], out[300], ref[300];
+
+    /* No need for wrapping */
+    ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 20,
+                         "short long line", 0, 0, out, sizeof(out), NULL);
+    ok(ret == 15, "Expected FormatMessageW to return 15, got %d\n", ret);
+    ok(!strcmp("short long line", out),"failed out=[%s]\n",out);
+
+    /* Wrap the last word */
+    ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 11,
+                         "short long line", 0, 0, out, sizeof(out), NULL);
+    ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret);
+    ok(!strcmp("short long\r\nline", out),"failed out=[%s]\n",out);
+
+    /* Wrap the very last word */
+    ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 20,
+                         "short long long line", 0, 0, out, sizeof(out), NULL);
+    ok(ret == 21, "Expected FormatMessageW to return 21, got %d\n", ret);
+    ok(!strcmp("short long long\r\nline", out),"failed out=[%s]\n",out);
+
+    /* Strictly less than 10 characters per line! */
+    ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 10,
+                         "short long line", 0, 0, out, sizeof(out), NULL);
+    ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret);
+    ok(!strcmp("short\r\nlong line", out),"failed out=[%s]\n",out);
+
+    /* Handling of duplicate spaces */
+    ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 16,
+                         "short long    line", 0, 0, out, sizeof(out), NULL);
+    ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret);
+    ok(!strcmp("short long\r\nline", out),"failed out=[%s]\n",out);
+
+    ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 16,
+                         "short long    wordlongerthanaline", 0, 0, out, sizeof(out), NULL);
+    ok(ret == 33, "Expected FormatMessageW to return 33, got %d\n", ret);
+    ok(!strcmp("short long\r\nwordlongerthanal\r\nine", out),"failed out=[%s]\n",out);
+
+    /* Breaking in the middle of spaces */
+    ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 12,
+                         "short long    line", 0, 0, out, sizeof(out), NULL);
+    ok(ret == 18, "Expected FormatMessageW to return 18, got %d\n", ret);
+    ok(!strcmp("short long\r\n  line", out),"failed out=[%s]\n",out);
+
+    ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 12,
+                         "short long    wordlongerthanaline", 0, 0, out, sizeof(out), NULL);
+    ok(ret == 35, "Expected FormatMessageW to return 35, got %d\n", ret);
+    ok(!strcmp("short long\r\n\r\nwordlongerth\r\nanaline", out),"failed out=[%s]\n",out);
+
+    /* Handling of start-of-string spaces */
+    ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 15,
+                         "   short line", 0, 0, out, sizeof(out), NULL);
+    ok(ret == 13, "Expected FormatMessageW to return 13, got %d\n", ret);
+    ok(!strcmp("   short line", out),"failed out=[%s]\n",out);
+
+    ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 11,
+                         "   shortlong line", 0, 0, out, sizeof(out), NULL);
+    ok(ret == 17, "Expected FormatMessageW to return 17, got %d\n", ret);
+    ok(!strcmp("\r\nshortlong\r\nline", out),"failed out=[%s]\n",out);
+
+    /* Handling of start-of-line spaces */
+    ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 11,
+                         "l1%n   shortlong line", 0, 0, out, sizeof(out), NULL);
+    ok(ret == 21, "Expected FormatMessageW to return 21, got %d\n", ret);
+    ok(!strcmp("l1\r\n\r\nshortlong\r\nline", out),"failed out=[%s]\n",out);
+
+    /* Pure space wrapping */
+    ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 5,
+                         "                ", 0, 0, out, sizeof(out), NULL);
+    ok(ret == 7, "Expected FormatMessageW to return 7, got %d\n", ret);
+    ok(!strcmp("\r\n\r\n\r\n ", out),"failed out=[%s]\n",out);
+
+    /* Handling of trailing spaces */
+    ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 5,
+                         "l1               ", 0, 0, out, sizeof(out), NULL);
+    ok(ret == 10, "Expected FormatMessageW to return 10, got %d\n", ret);
+    ok(!strcmp("l1\r\n\r\n\r\n  ", out),"failed out=[%s]\n",out);
+
+    /* Word that just fills the line */
+    ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 8,
+                         "shortlon", 0, 0, out, sizeof(out), NULL);
+    ok(ret == 10, "Expected FormatMessageW to return 10, got %d\n", ret);
+    ok(!strcmp("shortlon\r\n", out),"failed out=[%s]\n",out);
+
+    /* Word longer than the line */
+    ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 8,
+                         "shortlongline", 0, 0, out, sizeof(out), NULL);
+    ok(ret == 15, "Expected FormatMessageW to return 15, got %d\n", ret);
+    ok(!strcmp("shortlon\r\ngline", out),"failed out=[%s]\n",out);
+
+    /* Wrap the line multiple times */
+    ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 7,
+                         "short long line", 0, 0, out, sizeof(out), NULL);
+    ok(ret == 17, "Expected FormatMessageW to return 17, got %d\n", ret);
+    ok(!strcmp("short\r\nlong\r\nline", out),"failed out=[%s]\n",out);
+
+    /* '\n's in the source are ignored */
+    ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 11,
+                         "short\nlong line", 0, 0, out, sizeof(out), NULL);
+    ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret);
+    ok(!strcmp("short long\r\nline", out),"failed out=[%s]\n",out);
+
+    /* Wrap even before a '%n' */
+    ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 8,
+                         "shortlon%n", 0, 0, out, sizeof(out), NULL);
+    ok(ret == 12, "Expected FormatMessageW to return 12, got %d\n", ret);
+    ok(!strcmp("shortlon\r\n\r\n", out),"failed out=[%s]\n",out);
+
+    /* '%n's count as starting a new line and combine with line wrapping */
+    ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 10,
+                         "short%nlong line", 0, 0, out, sizeof(out), NULL);
+    ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret);
+    ok(!strcmp("short\r\nlong line", out),"failed out=[%s]\n",out);
+
+    ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 8,
+                         "short%nlong line", 0, 0, out, sizeof(out), NULL);
+    ok(ret == 17, "Expected FormatMessageW to return 17, got %d\n", ret);
+    ok(!strcmp("short\r\nlong\r\nline", out),"failed out=[%s]\n",out);
+
+    /* '%r's also count as starting a new line and all */
+    ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 10,
+                         "short%rlong line", 0, 0, out, sizeof(out), NULL);
+    ok(ret == 15, "Expected FormatMessageW to return 15, got %d\n", ret);
+    ok(!strcmp("short\rlong line", out),"failed out=[%s]\n",out);
+
+    ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 8,
+                         "short%rlong line", 0, 0, out, sizeof(out), NULL);
+    ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret);
+    ok(!strcmp("short\rlong\r\nline", out),"failed out=[%s]\n",out);
+
+    /* IGNORE_INSERTS does not prevent line wrapping or disable '%n' */
+    ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_IGNORE_INSERTS | 8,
+                         "short%nlong line%1", 0, 0, out, sizeof(out), NULL);
+    ok(ret == 19, "Expected FormatMessageW to return 19, got %d\n", ret);
+    ok(!strcmp("short\r\nlong\r\nline%1", out),"failed out=[%s]\n",out);
+
+    /* MAX_WIDTH_MASK is the same as specifying an infinite line width */
+    strcpy(in, "first line%n");
+    strcpy(ref, "first line\r\n");
+    for (i=0; i < 26; i++)
+    {
+        strcat(in, "123456789 ");
+        strcat(ref, "123456789 ");
+    }
+    ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_MAX_WIDTH_MASK,
+                         in, 0, 0, out, sizeof(out), NULL);
+    ok(ret == 272, "Expected FormatMessageW to return 272, got %d\n", ret);
+    ok(!strcmp(ref, out),"failed out=[%s]\n",out);
+
+    /* Wrapping and non-space characters */
+    ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 11,
+                         "short long\tline", 0, 0, out, sizeof(out), NULL);
+    ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret);
+    ok(!strcmp("short\r\nlong\tline", out),"failed out=[%s]\n",out);
+
+    ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 11,
+                         "short long-line", 0, 0, out, sizeof(out), NULL);
+    ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret);
+    ok(!strcmp("short\r\nlong-line", out),"failed out=[%s]\n",out);
+
+    ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 11,
+                         "short long_line", 0, 0, out, sizeof(out), NULL);
+    ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret);
+    ok(!strcmp("short\r\nlong_line", out),"failed out=[%s]\n",out);
+
+    ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 11,
+                         "short long.line", 0, 0, out, sizeof(out), NULL);
+    ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret);
+    ok(!strcmp("short\r\nlong.line", out),"failed out=[%s]\n",out);
+
+    ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 11,
+                         "short long,line", 0, 0, out, sizeof(out), NULL);
+    ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret);
+    ok(!strcmp("short\r\nlong,line", out),"failed out=[%s]\n",out);
+
+    ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 11,
+                         "short long!line", 0, 0, out, sizeof(out), NULL);
+    ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret);
+    ok(!strcmp("short\r\nlong!line", out),"failed out=[%s]\n",out);
+
+    ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | 11,
+                         "short long?line", 0, 0, out, sizeof(out), NULL);
+    ok(ret == 16, "Expected FormatMessageW to return 16, got %d\n", ret);
+    ok(!strcmp("short\r\nlong?line", out),"failed out=[%s]\n",out);
+}
+
 static void test_message_insufficient_buffer(void)
 {
     static const char init_buf[] = {'x', 'x', 'x', 'x', 'x'};
@@ -1593,6 +1782,7 @@ START_TEST(format_msg)
 
     test_message_from_string();
     test_message_ignore_inserts();
+    test_message_wrap();
     test_message_insufficient_buffer();
     test_message_null_buffer();
     test_message_allocate_buffer();
index e98ebc1..e503171 100644 (file)
@@ -63,7 +63,7 @@ static IMAGE_NT_HEADERS nt_header =
 #elif defined __sparc__
       IMAGE_FILE_MACHINE_SPARC, /* Machine */
 #elif defined __arm__
-      IMAGE_FILE_MACHINE_ARMV7, /* Machine */
+      IMAGE_FILE_MACHINE_ARMNT, /* Machine */
 #else
 # error You must specify the machine type
 #endif
index 1cc6610..8800d73 100755 (executable)
 #include "winerror.h"
 #include "winnls.h"
 
+static const WCHAR upper_case[] = {'\t','J','U','S','T','!',' ','A',',',' ','T','E','S','T',';',' ','S','T','R','I','N','G',' ','1','/','*','+','-','.','\r','\n',0};
+static const WCHAR lower_case[] = {'\t','j','u','s','t','!',' ','a',',',' ','t','e','s','t',';',' ','s','t','r','i','n','g',' ','1','/','*','+','-','.','\r','\n',0};
+static const WCHAR symbols_stripped[] = {'j','u','s','t','a','t','e','s','t','s','t','r','i','n','g','1',0};
+static const WCHAR fooW[] = {'f','o','o',0};
+
 static inline unsigned int strlenW( const WCHAR *str )
 {
     const WCHAR *s = str;
@@ -74,6 +79,7 @@ static BOOL (WINAPI *pEnumSystemLanguageGroupsA)(LANGUAGEGROUP_ENUMPROC, DWORD,
 static BOOL (WINAPI *pEnumLanguageGroupLocalesA)(LANGGROUPLOCALE_ENUMPROC, LGRPID, DWORD, LONG_PTR);
 static BOOL (WINAPI *pEnumUILanguagesA)(UILANGUAGE_ENUMPROC, DWORD, LONG_PTR);
 static BOOL (WINAPI *pEnumSystemLocalesEx)(LOCALE_ENUMPROCEX, DWORD, LPARAM, LPVOID);
+static INT (WINAPI *pLCMapStringEx)(LPCWSTR, DWORD, LPCWSTR, INT, LPWSTR, INT, LPNLSVERSIONINFO, LPVOID, LPARAM);
 static LCID (WINAPI *pLocaleNameToLCID)(LPCWSTR, DWORD);
 static INT  (WINAPI *pLCIDToLocaleName)(LCID, LPWSTR, INT, DWORD);
 static INT (WINAPI *pFoldStringA)(DWORD, LPCSTR, INT, LPSTR, INT);
@@ -82,6 +88,9 @@ static BOOL (WINAPI *pIsValidLanguageGroup)(LGRPID, DWORD);
 static INT (WINAPI *pIdnToNameprepUnicode)(DWORD, LPCWSTR, INT, LPWSTR, INT);
 static INT (WINAPI *pIdnToAscii)(DWORD, LPCWSTR, INT, LPWSTR, INT);
 static INT (WINAPI *pIdnToUnicode)(DWORD, LPCWSTR, INT, LPWSTR, INT);
+static INT (WINAPI *pGetLocaleInfoEx)(LPCWSTR, LCTYPE, LPWSTR, INT);
+static BOOL (WINAPI *pIsValidLocaleName)(LPCWSTR);
+static INT (WINAPI *pCompareStringOrdinal)(const WCHAR *, INT, const WCHAR *, INT, BOOL);
 
 static void InitFunctionPointers(void)
 {
@@ -90,6 +99,7 @@ static void InitFunctionPointers(void)
   pEnumLanguageGroupLocalesA = (void*)GetProcAddress(hKernel32, "EnumLanguageGroupLocalesA");
   pLocaleNameToLCID = (void*)GetProcAddress(hKernel32, "LocaleNameToLCID");
   pLCIDToLocaleName = (void*)GetProcAddress(hKernel32, "LCIDToLocaleName");
+  pLCMapStringEx = (void*)GetProcAddress(hKernel32, "LCMapStringEx");
   pFoldStringA = (void*)GetProcAddress(hKernel32, "FoldStringA");
   pFoldStringW = (void*)GetProcAddress(hKernel32, "FoldStringW");
   pIsValidLanguageGroup = (void*)GetProcAddress(hKernel32, "IsValidLanguageGroup");
@@ -98,6 +108,9 @@ static void InitFunctionPointers(void)
   pIdnToNameprepUnicode = (void*)GetProcAddress(hKernel32, "IdnToNameprepUnicode");
   pIdnToAscii = (void*)GetProcAddress(hKernel32, "IdnToAscii");
   pIdnToUnicode = (void*)GetProcAddress(hKernel32, "IdnToUnicode");
+  pGetLocaleInfoEx = (void*)GetProcAddress(hKernel32, "GetLocaleInfoEx");
+  pIsValidLocaleName = (void*)GetProcAddress(hKernel32, "IsValidLocaleName");
+  pCompareStringOrdinal = (void*)GetProcAddress(hKernel32, "CompareStringOrdinal");
 }
 
 #define eq(received, expected, label, type) \
@@ -127,9 +140,14 @@ static void test_GetLocaleInfoA(void)
   LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
   char buffer[BUFFER_SIZE];
   char expected[BUFFER_SIZE];
+  DWORD val;
 
   ok(lcid == 0x409, "wrong LCID calculated - %d\n", lcid);
 
+  ret = GetLocaleInfoA(lcid, LOCALE_ILANGUAGE|LOCALE_RETURN_NUMBER, (char*)&val, sizeof(val));
+  ok(ret, "got %d\n", ret);
+  ok(val == lcid, "got 0x%08x\n", val);
+
   /* en and ar use SUBLANG_NEUTRAL, but GetLocaleInfo assume SUBLANG_DEFAULT
      Same is true for zh on pre-Vista, but on Vista and higher GetLocaleInfo
      assumes SUBLANG_NEUTRAL for zh */
@@ -188,12 +206,57 @@ static void test_GetLocaleInfoA(void)
   ok(!strcmp(buffer, "Monday"), "Expected 'Monday', got '%s'\n", buffer);
 }
 
+struct neutralsublang_name2_t {
+    WCHAR name[3];
+    WCHAR sname[15];
+    LCID lcid;
+    LCID lcid_broken;
+    WCHAR sname_broken[15];
+    int todo;
+};
+
+static const struct neutralsublang_name2_t neutralsublang_names2[] = {
+    { {'a','r',0}, {'a','r','-','S','A',0},
+      MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_SAUDI_ARABIA), SORT_DEFAULT) },
+    { {'a','z',0}, {'a','z','-','L','a','t','n','-','A','Z',0},
+      MAKELCID(MAKELANGID(LANG_AZERI, SUBLANG_AZERI_LATIN), SORT_DEFAULT) },
+    { {'d','e',0}, {'d','e','-','D','E',0},
+      MAKELCID(MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN), SORT_DEFAULT) },
+    { {'e','n',0}, {'e','n','-','U','S',0},
+      MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT) },
+    { {'e','s',0}, {'e','s','-','E','S',0},
+      MAKELCID(MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_MODERN), SORT_DEFAULT),
+      MAKELCID(MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH), SORT_DEFAULT) /* vista */,
+      {'e','s','-','E','S','_','t','r','a','d','n','l',0}, 0x1 },
+    { {'g','a',0}, {'g','a','-','I','E',0},
+      MAKELCID(MAKELANGID(LANG_IRISH, SUBLANG_IRISH_IRELAND), SORT_DEFAULT), 0, {0}, 0x3 },
+    { {'i','t',0}, {'i','t','-','I','T',0},
+      MAKELCID(MAKELANGID(LANG_ITALIAN, SUBLANG_ITALIAN), SORT_DEFAULT) },
+    { {'m','s',0}, {'m','s','-','M','Y',0},
+      MAKELCID(MAKELANGID(LANG_MALAY, SUBLANG_MALAY_MALAYSIA), SORT_DEFAULT) },
+    { {'n','l',0}, {'n','l','-','N','L',0},
+      MAKELCID(MAKELANGID(LANG_DUTCH, SUBLANG_DUTCH), SORT_DEFAULT) },
+    { {'p','t',0}, {'p','t','-','B','R',0},
+      MAKELCID(MAKELANGID(LANG_PORTUGUESE, SUBLANG_PORTUGUESE_BRAZILIAN), SORT_DEFAULT) },
+    { {'s','r',0}, {'h','r','-','H','R',0},
+      MAKELCID(MAKELANGID(LANG_SERBIAN, SUBLANG_SERBIAN_CROATIA), SORT_DEFAULT) },
+    { {'s','v',0}, {'s','v','-','S','E',0},
+      MAKELCID(MAKELANGID(LANG_SWEDISH, SUBLANG_SWEDISH), SORT_DEFAULT) },
+    { {'u','z',0}, {'u','z','-','L','a','t','n','-','U','Z',0},
+      MAKELCID(MAKELANGID(LANG_UZBEK, SUBLANG_UZBEK_LATIN), SORT_DEFAULT) },
+    { {'z','h',0}, {'z','h','-','C','N',0},
+      MAKELCID(MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED), SORT_DEFAULT), 0, {0}, 0x3 },
+    { {0} }
+};
+
 static void test_GetLocaleInfoW(void)
 {
   LCID lcid_en = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
   LCID lcid_ru = MAKELCID(MAKELANGID(LANG_RUSSIAN, SUBLANG_NEUTRAL), SORT_DEFAULT);
+  LCID lcid_en_neut = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_NEUTRAL), SORT_DEFAULT);
   WCHAR bufferW[80], buffer2W[80];
   CHAR bufferA[80];
+  DWORD val;
   DWORD ret;
   INT i;
 
@@ -202,6 +265,68 @@ static void test_GetLocaleInfoW(void)
       win_skip("GetLocaleInfoW() isn't implemented\n");
       return;
   }
+
+  ret = GetLocaleInfoW(lcid_en, LOCALE_ILANGUAGE|LOCALE_RETURN_NUMBER, (WCHAR*)&val, sizeof(val)/sizeof(WCHAR));
+  ok(ret, "got %d\n", ret);
+  ok(val == lcid_en, "got 0x%08x\n", val);
+
+  ret = GetLocaleInfoW(lcid_en_neut, LOCALE_SNAME, bufferW, COUNTOF(bufferW));
+  if (ret)
+  {
+      static const WCHAR slangW[] = {'E','n','g','l','i','s','h',' ','(','U','n','i','t','e','d',' ',
+                                                                     'S','t','a','t','e','s',')',0};
+      static const WCHAR statesW[] = {'U','n','i','t','e','d',' ','S','t','a','t','e','s',0};
+      static const WCHAR enW[] = {'e','n','-','U','S',0};
+      const struct neutralsublang_name2_t *ptr = neutralsublang_names2;
+
+      ok(!lstrcmpW(bufferW, enW), "got wrong name %s\n", wine_dbgstr_w(bufferW));
+
+      ret = GetLocaleInfoW(lcid_en_neut, LOCALE_SCOUNTRY, bufferW, COUNTOF(bufferW));
+      ok(ret, "got %d\n", ret);
+      ok(!lstrcmpW(statesW, bufferW), "got wrong name %s\n", wine_dbgstr_w(bufferW));
+
+      ret = GetLocaleInfoW(lcid_en_neut, LOCALE_SLANGUAGE, bufferW, COUNTOF(bufferW));
+      ok(ret, "got %d\n", ret);
+      ok(!lstrcmpW(slangW, bufferW), "got wrong name %s\n", wine_dbgstr_w(bufferW));
+
+      while (*ptr->name)
+      {
+          LANGID langid;
+          LCID lcid;
+
+          /* make neutral lcid */
+          langid = MAKELANGID(PRIMARYLANGID(LANGIDFROMLCID(ptr->lcid)), SUBLANG_NEUTRAL);
+          lcid = MAKELCID(langid, SORT_DEFAULT);
+
+          val = 0;
+          GetLocaleInfoW(lcid, LOCALE_ILANGUAGE|LOCALE_RETURN_NUMBER, (WCHAR*)&val, sizeof(val)/sizeof(WCHAR));
+          if (ptr->todo & 0x1)
+          {
+          todo_wine
+              ok(val == ptr->lcid || (val && broken(val == ptr->lcid_broken)), "%s: got wrong lcid 0x%04x, expected 0x%04x\n",
+                  wine_dbgstr_w(ptr->name), val, ptr->lcid);
+          }
+          else
+              ok(val == ptr->lcid || (val && broken(val == ptr->lcid_broken)), "%s: got wrong lcid 0x%04x, expected 0x%04x\n",
+                  wine_dbgstr_w(ptr->name), val, ptr->lcid);
+
+          /* now check LOCALE_SNAME */
+          GetLocaleInfoW(lcid, LOCALE_SNAME, bufferW, COUNTOF(bufferW));
+          if (ptr->todo & 0x2)
+          todo_wine
+              ok(!lstrcmpW(bufferW, ptr->sname) ||
+                 (*ptr->sname_broken && broken(!lstrcmpW(bufferW, ptr->sname_broken))),
+                  "%s: got %s\n", wine_dbgstr_w(ptr->name), wine_dbgstr_w(bufferW));
+          else
+              ok(!lstrcmpW(bufferW, ptr->sname) ||
+                 (*ptr->sname_broken && broken(!lstrcmpW(bufferW, ptr->sname_broken))),
+                  "%s: got %s\n", wine_dbgstr_w(ptr->name), wine_dbgstr_w(bufferW));
+          ptr++;
+      }
+  }
+  else
+      win_skip("English neutral locale not supported\n");
+
   ret = GetLocaleInfoW(lcid_ru, LOCALE_SMONTHNAME1, bufferW, COUNTOF(bufferW));
   if (!ret) {
       win_skip("LANG_RUSSIAN locale data unavailable\n");
@@ -1206,27 +1331,27 @@ static void test_CompareStringA(void)
   }
 
   ret = CompareStringA(lcid, NORM_IGNORECASE, "Salut", -1, "Salute", -1);
-  ok (ret== 1, "(Salut/Salute) Expected 1, got %d\n", ret);
+  ok (ret == CSTR_LESS_THAN, "(Salut/Salute) Expected CSTR_LESS_THAN, got %d\n", ret);
 
   ret = CompareStringA(lcid, NORM_IGNORECASE, "Salut", -1, "SaLuT", -1);
-  ok (ret== 2, "(Salut/SaLuT) Expected 2, got %d\n", ret);
+  ok (ret == CSTR_EQUAL, "(Salut/SaLuT) Expected CSTR_EQUAL, got %d\n", ret);
 
   ret = CompareStringA(lcid, NORM_IGNORECASE, "Salut", -1, "hola", -1);
-  ok (ret== 3, "(Salut/hola) Expected 3, got %d\n", ret);
+  ok (ret == CSTR_GREATER_THAN, "(Salut/hola) Expected CSTR_GREATER_THAN, got %d\n", ret);
 
   ret = CompareStringA(lcid, NORM_IGNORECASE, "haha", -1, "hoho", -1);
-  ok (ret== 1, "(haha/hoho) Expected 1, got %d\n", ret);
+  ok (ret == CSTR_LESS_THAN, "(haha/hoho) Expected CSTR_LESS_THAN, got %d\n", ret);
 
   lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
 
   ret = CompareStringA(lcid, NORM_IGNORECASE, "haha", -1, "hoho", -1);
-  ok (ret== 1, "(haha/hoho) Expected 1, got %d\n", ret);
+  ok (ret == CSTR_LESS_THAN, "(haha/hoho) Expected CSTR_LESS_THAN, got %d\n", ret);
 
   ret = CompareStringA(lcid, NORM_IGNORECASE, "haha", -1, "hoho", 0);
-  ok (ret== 3, "(haha/hoho) Expected 3, got %d\n", ret);
+  ok (ret == CSTR_GREATER_THAN, "(haha/hoho) Expected CSTR_GREATER_THAN, got %d\n", ret);
 
     ret = CompareStringA(lcid, NORM_IGNORECASE, "Salut", 5, "saLuT", -1);
-    ok (ret == 2, "(Salut/saLuT) Expected 2, got %d\n", ret);
+    ok (ret == CSTR_EQUAL, "(Salut/saLuT) Expected CSTR_EQUAL, got %d\n", ret);
 
     /* test for CompareStringA flags */
     SetLastError(0xdeadbeef);
@@ -1256,52 +1381,52 @@ static void test_CompareStringA(void)
 
     if (0) { /* this requires collation table patch to make it MS compatible */
     ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "'o", -1, "-o", -1 );
-    ok(ret == 1, "'o vs -o expected 1, got %d\n", ret);
+    ok(ret == CSTR_LESS_THAN, "'o vs -o expected CSTR_LESS_THAN, got %d\n", ret);
 
     ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "'o", -1, "-o", -1 );
-    ok(ret == 1, "'o vs -o expected 1, got %d\n", ret);
+    ok(ret == CSTR_LESS_THAN, "'o vs -o expected CSTR_LESS_THAN, got %d\n", ret);
 
     ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "'", -1, "-", -1 );
-    ok(ret == 1, "' vs - expected 1, got %d\n", ret);
+    ok(ret == CSTR_LESS_THAN, "' vs - expected CSTR_LESS_THAN, got %d\n", ret);
 
     ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "'", -1, "-", -1 );
-    ok(ret == 1, "' vs - expected 1, got %d\n", ret);
+    ok(ret == CSTR_LESS_THAN, "' vs - expected CSTR_LESS_THAN, got %d\n", ret);
 
     ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "`o", -1, "/m", -1 );
-    ok(ret == 3, "`o vs /m expected 3, got %d\n", ret);
+    ok(ret == CSTR_GREATER_THAN, "`o vs /m CSTR_GREATER_THAN got %d\n", ret);
 
     ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "/m", -1, "`o", -1 );
-    ok(ret == 1, "/m vs `o expected 1, got %d\n", ret);
+    ok(ret == CSTR_LESS_THAN, "/m vs `o expected CSTR_LESS_THAN, got %d\n", ret);
 
     ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "`o", -1, "/m", -1 );
-    ok(ret == 3, "`o vs /m expected 3, got %d\n", ret);
+    ok(ret == CSTR_GREATER_THAN, "`o vs /m CSTR_GREATER_THAN got %d\n", ret);
 
     ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "/m", -1, "`o", -1 );
-    ok(ret == 1, "/m vs `o expected 1, got %d\n", ret);
+    ok(ret == CSTR_LESS_THAN, "/m vs `o expected CSTR_LESS_THAN, got %d\n", ret);
 
     ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "`o", -1, "-m", -1 );
-    ok(ret == 1, "`o vs -m expected 1, got %d\n", ret);
+    ok(ret == CSTR_LESS_THAN, "`o vs -m expected CSTR_LESS_THAN, got %d\n", ret);
 
     ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "-m", -1, "`o", -1 );
-    ok(ret == 3, "-m vs `o expected 3, got %d\n", ret);
+    ok(ret == CSTR_GREATER_THAN, "-m vs `o CSTR_GREATER_THAN got %d\n", ret);
 
     ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "`o", -1, "-m", -1 );
-    ok(ret == 3, "`o vs -m expected 3, got %d\n", ret);
+    ok(ret == CSTR_GREATER_THAN, "`o vs -m CSTR_GREATER_THAN got %d\n", ret);
 
     ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "-m", -1, "`o", -1 );
-    ok(ret == 1, "-m vs `o expected 1, got %d\n", ret);
+    ok(ret == CSTR_LESS_THAN, "-m vs `o expected CSTR_LESS_THAN, got %d\n", ret);
     }
 
 
     /* WinXP handles embedded NULLs differently than earlier versions */
     ret = CompareStringA(LOCALE_USER_DEFAULT, 0, "aLuZkUtZ", 8, "aLuZkUtZ\0A", 10);
-    ok(ret == 1 || ret == 2, "aLuZkUtZ vs aLuZkUtZ\\0A expected 1 or 2, got %d\n", ret);
+    ok(ret == CSTR_LESS_THAN || ret == CSTR_EQUAL, "aLuZkUtZ vs aLuZkUtZ\\0A expected CSTR_LESS_THAN or CSTR_EQUAL, got %d\n", ret);
 
     ret = CompareStringA(LOCALE_USER_DEFAULT, 0, "aLu\0ZkUtZ", 8, "aLu\0ZkUtZ\0A", 10);
-    ok(ret == 1 || ret == 2, "aLu\\0ZkUtZ vs aLu\\0ZkUtZ\\0A expected 1 or 2, got %d\n", ret);
+    ok(ret == CSTR_LESS_THAN || ret == CSTR_EQUAL, "aLu\\0ZkUtZ vs aLu\\0ZkUtZ\\0A expected CSTR_LESS_THAN or CSTR_EQUAL, got %d\n", ret);
 
     ret = CompareStringA(lcid, 0, "a\0b", -1, "a", -1);
-    ok(ret == 2, "a vs a expected 2, got %d\n", ret);
+    ok(ret == CSTR_EQUAL, "a vs a expected CSTR_EQUAL, got %d\n", ret);
 
     ret = CompareStringA(lcid, 0, "a\0b", 4, "a", 2);
     ok(ret == CSTR_EQUAL || /* win2k */
@@ -1309,7 +1434,7 @@ static void test_CompareStringA(void)
        "a\\0b vs a expected CSTR_EQUAL or CSTR_GREATER_THAN, got %d\n", ret);
 
     ret = CompareStringA(lcid, 0, "\2", 2, "\1", 2);
-    todo_wine ok(ret != 2, "\\2 vs \\1 expected unequal\n");
+    todo_wine ok(ret != CSTR_EQUAL, "\\2 vs \\1 expected unequal\n");
 
     ret = CompareStringA(lcid, NORM_IGNORECASE | LOCALE_USE_CP_ACP, "#", -1, ".", -1);
     todo_wine ok(ret == CSTR_LESS_THAN, "\"#\" vs \".\" expected CSTR_LESS_THAN, got %d\n", ret);
@@ -1493,168 +1618,253 @@ static void test_LCMapStringA(void)
        "unexpected error code %d\n", GetLastError());
 }
 
-static void test_LCMapStringW(void)
+typedef INT (*lcmapstring_wrapper)(DWORD, LPCWSTR, INT, LPWSTR, INT);
+
+static void test_lcmapstring_unicode(lcmapstring_wrapper func_ptr, const char *func_name)
 {
     int ret, ret2;
     WCHAR buf[256], buf2[256];
     char *p_buf = (char *)buf, *p_buf2 = (char *)buf2;
-    static const WCHAR upper_case[] = {'\t','J','U','S','T','!',' ','A',',',' ','T','E','S','T',';',' ','S','T','R','I','N','G',' ','1','/','*','+','-','.','\r','\n',0};
-    static const WCHAR lower_case[] = {'\t','j','u','s','t','!',' ','a',',',' ','t','e','s','t',';',' ','s','t','r','i','n','g',' ','1','/','*','+','-','.','\r','\n',0};
-    static const WCHAR symbols_stripped[] = {'j','u','s','t','a','t','e','s','t','s','t','r','i','n','g','1',0};
-    static const WCHAR fooW[] = {'f','o','o',0};
 
-    ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE | LCMAP_UPPERCASE,
+    ret = func_ptr(LCMAP_LOWERCASE | LCMAP_UPPERCASE,
                        upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR));
-    if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
-    {
-        win_skip("LCMapStringW is not implemented\n");
-        return;
-    }
     if (broken(ret))
         ok(lstrcmpW(buf, upper_case) == 0, "Expected upper case string\n");
     else
     {
-        ok(!ret, "LCMAP_LOWERCASE and LCMAP_UPPERCASE are mutually exclusive\n");
-        ok(GetLastError() == ERROR_INVALID_FLAGS,
-           "unexpected error code %d\n", GetLastError());
+        ok(!ret, "%s LCMAP_LOWERCASE and LCMAP_UPPERCASE are mutually exclusive\n", func_name);
+        ok(GetLastError() == ERROR_INVALID_FLAGS, "%s unexpected error code %d\n",
+           func_name, GetLastError());
     }
 
-    ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_HIRAGANA | LCMAP_KATAKANA,
+    ret = func_ptr(LCMAP_HIRAGANA | LCMAP_KATAKANA,
                        upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR));
-    ok(!ret, "LCMAP_HIRAGANA and LCMAP_KATAKANA are mutually exclusive\n");
-    ok(GetLastError() == ERROR_INVALID_FLAGS,
-       "unexpected error code %d\n", GetLastError());
+    ok(!ret, "%s LCMAP_HIRAGANA and LCMAP_KATAKANA are mutually exclusive\n", func_name);
+    ok(GetLastError() == ERROR_INVALID_FLAGS, "%s unexpected error code %d\n",
+       func_name, GetLastError());
 
-    ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_HALFWIDTH | LCMAP_FULLWIDTH,
+    ret = func_ptr(LCMAP_HALFWIDTH | LCMAP_FULLWIDTH,
                        upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR));
-    ok(!ret, "LCMAP_HALFWIDTH | LCMAP_FULLWIDTH are mutually exclusive\n");
-    ok(GetLastError() == ERROR_INVALID_FLAGS,
-       "unexpected error code %d\n", GetLastError());
+    ok(!ret, "%s LCMAP_HALFWIDTH | LCMAP_FULLWIDTH are mutually exclusive\n", func_name);
+    ok(GetLastError() == ERROR_INVALID_FLAGS, "%s unexpected error code %d\n",
+       func_name, GetLastError());
 
-    ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE,
+    ret = func_ptr(LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE,
                        upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR));
-    ok(!ret, "LCMAP_TRADITIONAL_CHINESE and LCMAP_SIMPLIFIED_CHINESE are mutually exclusive\n");
-    ok(GetLastError() == ERROR_INVALID_FLAGS,
-       "unexpected error code %d\n", GetLastError());
+    ok(!ret, "%s LCMAP_TRADITIONAL_CHINESE and LCMAP_SIMPLIFIED_CHINESE are mutually exclusive\n",
+       func_name);
+    ok(GetLastError() == ERROR_INVALID_FLAGS, "%s unexpected error code %d\n",
+       func_name, GetLastError());
 
     /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
     SetLastError(0xdeadbeef);
-    ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE | SORT_STRINGSORT,
+    ret = func_ptr(LCMAP_LOWERCASE | SORT_STRINGSORT,
                        upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR));
-    ok(GetLastError() == ERROR_INVALID_FLAGS, "expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
-    ok(!ret, "SORT_STRINGSORT without LCMAP_SORTKEY must fail\n");
+    ok(GetLastError() == ERROR_INVALID_FLAGS, "%s expected ERROR_INVALID_FLAGS, got %d\n",
+       func_name, GetLastError());
+    ok(!ret, "%s SORT_STRINGSORT without LCMAP_SORTKEY must fail\n", func_name);
 
     /* test LCMAP_LOWERCASE */
-    ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE,
+    ret = func_ptr(LCMAP_LOWERCASE,
                        upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR));
-    ok(ret == lstrlenW(upper_case) + 1,
-       "ret %d, error %d, expected value %d\n",
+    ok(ret == lstrlenW(upper_case) + 1, "%s ret %d, error %d, expected value %d\n", func_name,
        ret, GetLastError(), lstrlenW(upper_case) + 1);
-    ok(!lstrcmpW(buf, lower_case), "string compare mismatch\n");
+    ok(!lstrcmpW(buf, lower_case), "%s string compare mismatch\n", func_name);
 
     /* test LCMAP_UPPERCASE */
-    ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_UPPERCASE,
+    ret = func_ptr(LCMAP_UPPERCASE,
                        lower_case, -1, buf, sizeof(buf)/sizeof(WCHAR));
-    ok(ret == lstrlenW(lower_case) + 1,
-       "ret %d, error %d, expected value %d\n",
+    ok(ret == lstrlenW(lower_case) + 1, "%s ret %d, error %d, expected value %d\n", func_name,
        ret, GetLastError(), lstrlenW(lower_case) + 1);
-    ok(!lstrcmpW(buf, upper_case), "string compare mismatch\n");
+    ok(!lstrcmpW(buf, upper_case), "%s string compare mismatch\n", func_name);
 
     /* test buffer overflow */
     SetLastError(0xdeadbeef);
-    ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_UPPERCASE,
+    ret = func_ptr(LCMAP_UPPERCASE,
                        lower_case, -1, buf, 4);
     ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
-       "should return 0 and ERROR_INSUFFICIENT_BUFFER, got %d\n", ret);
+       "%s should return 0 and ERROR_INSUFFICIENT_BUFFER, got %d\n", func_name, ret);
 
     /* LCMAP_UPPERCASE or LCMAP_LOWERCASE should accept src == dst */
     lstrcpyW(buf, lower_case);
-    ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_UPPERCASE,
+    ret = func_ptr(LCMAP_UPPERCASE,
                        buf, -1, buf, sizeof(buf)/sizeof(WCHAR));
-    ok(ret == lstrlenW(lower_case) + 1,
-       "ret %d, error %d, expected value %d\n",
+    ok(ret == lstrlenW(lower_case) + 1, "%s ret %d, error %d, expected value %d\n", func_name,
        ret, GetLastError(), lstrlenW(lower_case) + 1);
-    ok(!lstrcmpW(buf, upper_case), "string compare mismatch\n");
+    ok(!lstrcmpW(buf, upper_case), "%s string compare mismatch\n", func_name);
 
     lstrcpyW(buf, upper_case);
-    ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE,
+    ret = func_ptr(LCMAP_LOWERCASE,
                        buf, -1, buf, sizeof(buf)/sizeof(WCHAR));
-    ok(ret == lstrlenW(upper_case) + 1,
-       "ret %d, error %d, expected value %d\n",
+    ok(ret == lstrlenW(upper_case) + 1, "%s ret %d, error %d, expected value %d\n", func_name,
        ret, GetLastError(), lstrlenW(lower_case) + 1);
-    ok(!lstrcmpW(buf, lower_case), "string compare mismatch\n");
+    ok(!lstrcmpW(buf, lower_case), "%s string compare mismatch\n", func_name);
 
     /* otherwise src == dst should fail */
     SetLastError(0xdeadbeef);
-    ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_SORTKEY | LCMAP_UPPERCASE,
+    ret = func_ptr(LCMAP_SORTKEY | LCMAP_UPPERCASE,
                        buf, 10, buf, sizeof(buf));
     ok(GetLastError() == ERROR_INVALID_FLAGS /* NT */ ||
-       GetLastError() == ERROR_INVALID_PARAMETER /* Win9x */,
-       "unexpected error code %d\n", GetLastError());
-    ok(!ret, "src == dst without LCMAP_UPPERCASE or LCMAP_LOWERCASE must fail\n");
+       GetLastError() == ERROR_INVALID_PARAMETER /* Win7+ */,
+       "%s unexpected error code %d\n", func_name, GetLastError());
+    ok(!ret, "%s src == dst without LCMAP_UPPERCASE or LCMAP_LOWERCASE must fail\n", func_name);
 
     /* test whether '\0' is always appended */
-    ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
+    ret = func_ptr(LCMAP_SORTKEY,
                        upper_case, -1, buf, sizeof(buf));
-    ok(ret, "LCMapStringW must succeed\n");
-    ret2 = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
+    ok(ret, "%s func_ptr must succeed\n", func_name);
+    ret2 = func_ptr(LCMAP_SORTKEY,
                        upper_case, lstrlenW(upper_case), buf2, sizeof(buf2));
-    ok(ret, "LCMapStringW must succeed\n");
-    ok(ret == ret2, "lengths of sort keys must be equal\n");
-    ok(!lstrcmpA(p_buf, p_buf2), "sort keys must be equal\n");
+    ok(ret, "%s func_ptr must succeed\n", func_name);
+    ok(ret == ret2, "%s lengths of sort keys must be equal\n", func_name);
+    ok(!lstrcmpA(p_buf, p_buf2), "%s sort keys must be equal\n", func_name);
 
     /* test LCMAP_SORTKEY | NORM_IGNORECASE */
-    ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_SORTKEY | NORM_IGNORECASE,
+    ret = func_ptr(LCMAP_SORTKEY | NORM_IGNORECASE,
                        upper_case, -1, buf, sizeof(buf));
-    ok(ret, "LCMapStringW must succeed\n");
-    ret2 = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
+    ok(ret, "%s func_ptr must succeed\n", func_name);
+    ret2 = func_ptr(LCMAP_SORTKEY,
                        lower_case, -1, buf2, sizeof(buf2));
-    ok(ret2, "LCMapStringW must succeed\n");
-    ok(ret == ret2, "lengths of sort keys must be equal\n");
-    ok(!lstrcmpA(p_buf, p_buf2), "sort keys must be equal\n");
+    ok(ret2, "%s func_ptr must succeed\n", func_name);
+    ok(ret == ret2, "%s lengths of sort keys must be equal\n", func_name);
+    ok(!lstrcmpA(p_buf, p_buf2), "%s sort keys must be equal\n", func_name);
 
     /* Don't test LCMAP_SORTKEY | NORM_IGNORENONSPACE, produces different
        results from plain LCMAP_SORTKEY on Vista */
 
     /* test LCMAP_SORTKEY | NORM_IGNORESYMBOLS */
-    ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_SORTKEY | NORM_IGNORESYMBOLS,
+    ret = func_ptr(LCMAP_SORTKEY | NORM_IGNORESYMBOLS,
                        lower_case, -1, buf, sizeof(buf));
-    ok(ret, "LCMapStringW must succeed\n");
-    ret2 = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
+    ok(ret, "%s func_ptr must succeed\n", func_name);
+    ret2 = func_ptr(LCMAP_SORTKEY,
                        symbols_stripped, -1, buf2, sizeof(buf2));
-    ok(ret2, "LCMapStringW must succeed\n");
-    ok(ret == ret2, "lengths of sort keys must be equal\n");
-    ok(!lstrcmpA(p_buf, p_buf2), "sort keys must be equal\n");
+    ok(ret2, "%s func_ptr must succeed\n", func_name);
+    ok(ret == ret2, "%s lengths of sort keys must be equal\n", func_name);
+    ok(!lstrcmpA(p_buf, p_buf2), "%s sort keys must be equal\n", func_name);
 
     /* test NORM_IGNORENONSPACE */
     lstrcpyW(buf, fooW);
-    ret = LCMapStringW(LOCALE_USER_DEFAULT, NORM_IGNORENONSPACE,
+    ret = func_ptr(NORM_IGNORENONSPACE,
                        lower_case, -1, buf, sizeof(buf)/sizeof(WCHAR));
-    ok(ret == lstrlenW(lower_case) + 1, "LCMapStringW should return %d, ret = %d\n",
-       lstrlenW(lower_case) + 1, ret);
-    ok(!lstrcmpW(buf, lower_case), "string comparison mismatch\n");
+    ok(ret == lstrlenW(lower_case) + 1, "%s func_ptr should return %d, ret = %d\n", func_name,
+    lstrlenW(lower_case) + 1, ret);
+    ok(!lstrcmpW(buf, lower_case), "%s string comparison mismatch\n", func_name);
 
     /* test NORM_IGNORESYMBOLS */
     lstrcpyW(buf, fooW);
-    ret = LCMapStringW(LOCALE_USER_DEFAULT, NORM_IGNORESYMBOLS,
+    ret = func_ptr(NORM_IGNORESYMBOLS,
                        lower_case, -1, buf, sizeof(buf)/sizeof(WCHAR));
-    ok(ret == lstrlenW(symbols_stripped) + 1, "LCMapStringW should return %d, ret = %d\n",
-       lstrlenW(symbols_stripped) + 1, ret);
-    ok(!lstrcmpW(buf, symbols_stripped), "string comparison mismatch\n");
+    ok(ret == lstrlenW(symbols_stripped) + 1, "%s func_ptr should return %d, ret = %d\n", func_name,
+    lstrlenW(symbols_stripped) + 1, ret);
+    ok(!lstrcmpW(buf, symbols_stripped), "%s string comparison mismatch\n", func_name);
 
     /* test srclen = 0 */
     SetLastError(0xdeadbeef);
-    ret = LCMapStringW(LOCALE_USER_DEFAULT, 0, upper_case, 0, buf, sizeof(buf)/sizeof(WCHAR));
-    ok(!ret, "LCMapStringW should fail with srclen = 0\n");
+    ret = func_ptr(0, upper_case, 0, buf, sizeof(buf)/sizeof(WCHAR));
+    ok(!ret, "%s func_ptr should fail with srclen = 0\n", func_name);
     ok(GetLastError() == ERROR_INVALID_PARAMETER,
-       "unexpected error code %d\n", GetLastError());
+       "%s unexpected error code %d\n", func_name, GetLastError());
+}
+
+static INT LCMapStringW_wrapper(DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen)
+{
+    return LCMapStringW(LOCALE_USER_DEFAULT, flags, src, srclen, dst, dstlen);
+}
+
+static void test_LCMapStringW(void)
+{
+    int ret;
+    WCHAR buf[256];
+
+    trace("testing LCMapStringW\n");
+
+    SetLastError(0xdeadbeef);
+    ret = LCMapStringW((LCID)-1, LCMAP_LOWERCASE, upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR));
+    todo_wine {
+    ok(!ret, "LCMapStringW should fail with bad lcid\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "unexpected error code %d\n", GetLastError());
+    }
+
+    test_lcmapstring_unicode(LCMapStringW_wrapper, "LCMapStringW:");
+}
+
+static INT LCMapStringEx_wrapper(DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen)
+{
+    return pLCMapStringEx(LOCALE_NAME_USER_DEFAULT, flags, src, srclen, dst, dstlen, NULL, NULL, 0);
 }
 
-static void test_LocaleNames(void)
+static void test_LCMapStringEx(void)
+{
+    int ret;
+    WCHAR buf[256];
+
+    if (!pLCMapStringEx)
+    {
+        win_skip( "LCMapStringEx not available\n" );
+        return;
+    }
+
+    trace("testing LCMapStringEx\n");
+
+    SetLastError(0xdeadbeef);
+    ret = pLCMapStringEx(fooW, LCMAP_LOWERCASE,
+                         upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR), NULL, NULL, 0);
+    todo_wine {
+    ok(!ret, "LCMapStringEx should fail with bad locale name\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "unexpected error code %d\n", GetLastError());
+    }
+
+    /* test reserved parameters */
+    ret = pLCMapStringEx(LOCALE_NAME_USER_DEFAULT, LCMAP_LOWERCASE,
+                         upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR), NULL, NULL, 1);
+    ok(ret == lstrlenW(upper_case) + 1, "ret %d, error %d, expected value %d\n",
+       ret, GetLastError(), lstrlenW(upper_case) + 1);
+    ok(!lstrcmpW(buf, lower_case), "string compare mismatch\n");
+
+    ret = pLCMapStringEx(LOCALE_NAME_USER_DEFAULT, LCMAP_LOWERCASE,
+                         upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR), NULL, (void*)1, 0);
+    ok(ret == lstrlenW(upper_case) + 1, "ret %d, error %d, expected value %d\n",
+       ret, GetLastError(), lstrlenW(upper_case) + 1);
+    ok(!lstrcmpW(buf, lower_case), "string compare mismatch\n");
+
+    /* crashes on native */
+    if(0)
+        ret = pLCMapStringEx(LOCALE_NAME_USER_DEFAULT, LCMAP_LOWERCASE,
+                             upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR), (void*)1, NULL, 0);
+
+    test_lcmapstring_unicode(LCMapStringEx_wrapper, "LCMapStringEx:");
+}
+
+struct neutralsublang_name_t {
+    WCHAR name[3];
+    LCID lcid;
+    int todo;
+};
+
+static const struct neutralsublang_name_t neutralsublang_names[] = {
+    { {'a','r',0}, MAKELCID(MAKELANGID(LANG_ARABIC,     SUBLANG_ARABIC_SAUDI_ARABIA), SORT_DEFAULT) },
+    { {'a','z',0}, MAKELCID(MAKELANGID(LANG_AZERI,      SUBLANG_AZERI_LATIN), SORT_DEFAULT) },
+    { {'d','e',0}, MAKELCID(MAKELANGID(LANG_GERMAN,     SUBLANG_GERMAN), SORT_DEFAULT) },
+    { {'e','n',0}, MAKELCID(MAKELANGID(LANG_ENGLISH,    SUBLANG_ENGLISH_US), SORT_DEFAULT) },
+    { {'e','s',0}, MAKELCID(MAKELANGID(LANG_SPANISH,    SUBLANG_SPANISH_MODERN), SORT_DEFAULT), 1 },
+    { {'g','a',0}, MAKELCID(MAKELANGID(LANG_IRISH,      SUBLANG_IRISH_IRELAND), SORT_DEFAULT), 1 },
+    { {'i','t',0}, MAKELCID(MAKELANGID(LANG_ITALIAN,    SUBLANG_ITALIAN), SORT_DEFAULT) },
+    { {'m','s',0}, MAKELCID(MAKELANGID(LANG_MALAY,      SUBLANG_MALAY_MALAYSIA), SORT_DEFAULT) },
+    { {'n','l',0}, MAKELCID(MAKELANGID(LANG_DUTCH,      SUBLANG_DUTCH), SORT_DEFAULT) },
+    { {'p','t',0}, MAKELCID(MAKELANGID(LANG_PORTUGUESE, SUBLANG_PORTUGUESE_BRAZILIAN), SORT_DEFAULT) },
+    { {'s','r',0}, MAKELCID(MAKELANGID(LANG_SERBIAN,    SUBLANG_SERBIAN_SERBIA_LATIN), SORT_DEFAULT), 1 },
+    { {'s','v',0}, MAKELCID(MAKELANGID(LANG_SWEDISH,    SUBLANG_SWEDISH), SORT_DEFAULT) },
+    { {'u','z',0}, MAKELCID(MAKELANGID(LANG_UZBEK,      SUBLANG_UZBEK_LATIN), SORT_DEFAULT) },
+    { {'z','h',0}, MAKELCID(MAKELANGID(LANG_CHINESE,    SUBLANG_CHINESE_SIMPLIFIED), SORT_DEFAULT), 1 },
+    { {0} }
+};
+
+static void test_LocaleNameToLCID(void)
 {
     LCID lcid;
     INT ret;
     WCHAR buffer[LOCALE_NAME_MAX_LENGTH];
+    static const WCHAR enW[] = {'e','n',0};
 
     if (!pLocaleNameToLCID)
     {
@@ -1664,27 +1874,65 @@ static void test_LocaleNames(void)
 
     /* special cases */
     buffer[0] = 0;
+    SetLastError(0xdeadbeef);
     lcid = pLocaleNameToLCID(LOCALE_NAME_USER_DEFAULT, 0);
     ok(lcid == GetUserDefaultLCID() || broken(GetLastError() == ERROR_INVALID_PARAMETER /* Vista */),
-       "Expected lcid == %08x, got %08x, error %d\n", lcid, GetUserDefaultLCID(), GetLastError());
+       "Expected lcid == %08x, got %08x, error %d\n", GetUserDefaultLCID(), 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));
 
     buffer[0] = 0;
+    SetLastError(0xdeadbeef);
     lcid = pLocaleNameToLCID(LOCALE_NAME_SYSTEM_DEFAULT, 0);
-    todo_wine ok(!lcid && GetLastError() == ERROR_INVALID_PARAMETER,
-                 "Expected lcid != 0, got %08x, error %d\n", lcid, GetLastError());
+    ok(!lcid && GetLastError() == ERROR_INVALID_PARAMETER,
+       "Expected lcid == 0, 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));
 
     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());
     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));
+
+    /* bad name */
+    SetLastError(0xdeadbeef);
+    lcid = pLocaleNameToLCID(fooW, 0);
+    ok(!lcid && GetLastError() == ERROR_INVALID_PARAMETER,
+       "Expected lcid == 0, got got %08x, error %d\n", lcid, GetLastError());
+
+    /* english neutral name */
+    lcid = pLocaleNameToLCID(enW, 0);
+    ok(lcid == MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT) ||
+       broken(lcid == 0) /* Vista */, "got 0x%04x\n", lcid);
+    if (lcid)
+    {
+        const struct neutralsublang_name_t *ptr = neutralsublang_names;
+
+        while (*ptr->name)
+        {
+            lcid = pLocaleNameToLCID(ptr->name, 0);
+            if (ptr->todo)
+            todo_wine
+                ok(lcid == ptr->lcid, "%s: got wrong lcid 0x%04x, expected 0x%04x\n",
+                    wine_dbgstr_w(ptr->name), lcid, ptr->lcid);
+            else
+                ok(lcid == ptr->lcid, "%s: got wrong lcid 0x%04x, expected 0x%04x\n",
+                    wine_dbgstr_w(ptr->name), lcid, ptr->lcid);
+
+            *buffer = 0;
+            ret = pLCIDToLocaleName(lcid, buffer, sizeof(buffer)/sizeof(WCHAR), 0);
+            ok(ret > 0, "%s: got %d\n", wine_dbgstr_w(ptr->name), ret);
+            ok(lstrcmpW(ptr->name, buffer), "%s: got wrong locale name %s\n",
+                wine_dbgstr_w(ptr->name), wine_dbgstr_w(buffer));
+
+            ptr++;
+        }
+    }
 }
 
 /* this requires collation table patch to make it MS compatible */
@@ -3140,6 +3388,173 @@ static void test_IdnToUnicode(void)
     }
 }
 
+static void test_GetLocaleInfoEx(void)
+{
+    static const WCHAR enW[] = {'e','n',0};
+    WCHAR bufferW[80];
+    INT ret;
+
+    if (!pGetLocaleInfoEx)
+    {
+        win_skip("GetLocaleInfoEx not supported\n");
+        return;
+    }
+
+    ret = pGetLocaleInfoEx(enW, LOCALE_SNAME, bufferW, sizeof(bufferW)/sizeof(WCHAR));
+    ok(ret || broken(ret == 0) /* Vista */, "got %d\n", ret);
+    if (ret)
+    {
+        static const WCHAR statesW[] = {'U','n','i','t','e','d',' ','S','t','a','t','e','s',0};
+        static const WCHAR dummyW[] = {'d','u','m','m','y',0};
+        static const WCHAR enusW[] = {'e','n','-','U','S',0};
+        static const WCHAR usaW[] = {'U','S','A',0};
+        static const WCHAR enuW[] = {'E','N','U',0};
+        const struct neutralsublang_name_t *ptr = neutralsublang_names;
+        DWORD val;
+
+        ok(ret == lstrlenW(bufferW)+1, "got %d\n", ret);
+        ok(!lstrcmpW(bufferW, enW), "got %s\n", wine_dbgstr_w(bufferW));
+
+        SetLastError(0xdeadbeef);
+        ret = pGetLocaleInfoEx(enW, LOCALE_SNAME, bufferW, 2);
+        ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "got %d, %d\n", ret, GetLastError());
+
+        SetLastError(0xdeadbeef);
+        ret = pGetLocaleInfoEx(enW, LOCALE_SNAME, NULL, 0);
+        ok(ret == 3 && GetLastError() == 0xdeadbeef, "got %d, %d\n", ret, GetLastError());
+
+        ret = pGetLocaleInfoEx(enusW, LOCALE_SNAME, bufferW, sizeof(bufferW)/sizeof(WCHAR));
+        ok(ret == lstrlenW(bufferW)+1, "got %d\n", ret);
+        ok(!lstrcmpW(bufferW, enusW), "got %s\n", wine_dbgstr_w(bufferW));
+
+        ret = pGetLocaleInfoEx(enW, LOCALE_SABBREVCTRYNAME, bufferW, sizeof(bufferW)/sizeof(WCHAR));
+        ok(ret == lstrlenW(bufferW)+1, "got %d\n", ret);
+        ok(!lstrcmpW(bufferW, usaW), "got %s\n", wine_dbgstr_w(bufferW));
+
+        ret = pGetLocaleInfoEx(enW, LOCALE_SABBREVLANGNAME, bufferW, sizeof(bufferW)/sizeof(WCHAR));
+        ok(ret == lstrlenW(bufferW)+1, "got %d\n", ret);
+        ok(!lstrcmpW(bufferW, enuW), "got %s\n", wine_dbgstr_w(bufferW));
+
+        ret = pGetLocaleInfoEx(enW, LOCALE_SCOUNTRY, bufferW, sizeof(bufferW)/sizeof(WCHAR));
+        ok(ret == lstrlenW(bufferW)+1, "got %d\n", ret);
+        ok(!lstrcmpW(bufferW, statesW), "got %s\n", wine_dbgstr_w(bufferW));
+
+        bufferW[0] = 0;
+        SetLastError(0xdeadbeef);
+        ret = pGetLocaleInfoEx(dummyW, LOCALE_SNAME, bufferW, sizeof(bufferW)/sizeof(WCHAR));
+        ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER, "got %d, error %d\n", ret, GetLastError());
+
+        while (*ptr->name)
+        {
+            val = 0;
+            pGetLocaleInfoEx(ptr->name, LOCALE_ILANGUAGE|LOCALE_RETURN_NUMBER, (WCHAR*)&val, sizeof(val)/sizeof(WCHAR));
+            if (ptr->todo)
+            todo_wine
+                ok(val == ptr->lcid, "%s: got wrong lcid 0x%04x, expected 0x%04x\n", wine_dbgstr_w(ptr->name), val, ptr->lcid);
+            else
+                ok(val == ptr->lcid, "%s: got wrong lcid 0x%04x, expected 0x%04x\n", wine_dbgstr_w(ptr->name), val, ptr->lcid);
+            bufferW[0] = 0;
+            ret = pGetLocaleInfoEx(ptr->name, LOCALE_SNAME, bufferW, sizeof(bufferW)/sizeof(WCHAR));
+            ok(ret == lstrlenW(bufferW)+1, "%s: got ret value %d\n", wine_dbgstr_w(ptr->name), ret);
+            ok(!lstrcmpW(bufferW, ptr->name), "%s: got wrong LOCALE_SNAME %s\n", wine_dbgstr_w(ptr->name), wine_dbgstr_w(bufferW));
+            ptr++;
+        }
+    }
+}
+
+static void test_IsValidLocaleName(void)
+{
+    static const WCHAR enusW[] = {'e','n','-','U','S',0};
+    static const WCHAR zzW[] = {'z','z',0};
+    static const WCHAR zzzzW[] = {'z','z','-','Z','Z',0};
+    BOOL ret;
+
+    if (!pIsValidLocaleName)
+    {
+        win_skip("IsValidLocaleName not supported\n");
+        return;
+    }
+
+    ret = pIsValidLocaleName(enusW);
+    ok(ret, "IsValidLocaleName failed\n");
+    ret = pIsValidLocaleName(zzW);
+    ok(!ret, "IsValidLocaleName should have failed\n");
+    ret = pIsValidLocaleName(zzzzW);
+    ok(!ret, "IsValidLocaleName should have failed\n");
+}
+
+static void test_CompareStringOrdinal(void)
+{
+    INT ret;
+    WCHAR test1[] = { 't','e','s','t',0 };
+    WCHAR test2[] = { 'T','e','S','t',0 };
+    WCHAR test3[] = { 't','e','s','t','3',0 };
+    WCHAR null1[] = { 'a',0,'a',0 };
+    WCHAR null2[] = { 'a',0,'b',0 };
+    WCHAR bills1[] = { 'b','i','l','l','\'','s',0 };
+    WCHAR bills2[] = { 'b','i','l','l','s',0 };
+    WCHAR coop1[] = { 'c','o','-','o','p',0 };
+    WCHAR coop2[] = { 'c','o','o','p',0 };
+    WCHAR nonascii1[] = { 0x0102,0 };
+    WCHAR nonascii2[] = { 0x0201,0 };
+
+    if (!pCompareStringOrdinal)
+    {
+        win_skip("CompareStringOrdinal not supported\n");
+        return;
+    }
+
+    /* Check errors */
+    SetLastError(0xdeadbeef);
+    ret = pCompareStringOrdinal(NULL, 0, NULL, 0, FALSE);
+    ok(!ret, "Got %u, expected 0\n", ret);
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got %x, expected %x\n", GetLastError(), ERROR_INVALID_PARAMETER);
+    SetLastError(0xdeadbeef);
+    ret = pCompareStringOrdinal(test1, -1, NULL, 0, FALSE);
+    ok(!ret, "Got %u, expected 0\n", ret);
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got %x, expected %x\n", GetLastError(), ERROR_INVALID_PARAMETER);
+    SetLastError(0xdeadbeef);
+    ret = pCompareStringOrdinal(NULL, 0, test1, -1, FALSE);
+    ok(!ret, "Got %u, expected 0\n", ret);
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got %x, expected %x\n", GetLastError(), ERROR_INVALID_PARAMETER);
+
+    /* Check case */
+    ret = pCompareStringOrdinal(test1, -1, test1, -1, FALSE);
+    ok(ret == CSTR_EQUAL, "Got %u, expected %u\n", ret, CSTR_EQUAL);
+    ret = pCompareStringOrdinal(test1, -1, test2, -1, FALSE);
+    ok(ret == CSTR_GREATER_THAN, "Got %u, expected %u\n", ret, CSTR_GREATER_THAN);
+    ret = pCompareStringOrdinal(test2, -1, test1, -1, FALSE);
+    ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
+    ret = pCompareStringOrdinal(test1, -1, test2, -1, TRUE);
+    ok(ret == CSTR_EQUAL, "Got %u, expected %u\n", ret, CSTR_EQUAL);
+
+    /* Check different sizes */
+    ret = pCompareStringOrdinal(test1, 3, test2, -1, TRUE);
+    ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
+    ret = pCompareStringOrdinal(test1, -1, test2, 3, TRUE);
+    ok(ret == CSTR_GREATER_THAN, "Got %u, expected %u\n", ret, CSTR_GREATER_THAN);
+
+    /* Check null character */
+    ret = pCompareStringOrdinal(null1, 3, null2, 3, FALSE);
+    ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
+    ret = pCompareStringOrdinal(null1, 3, null2, 3, TRUE);
+    ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
+    ret = pCompareStringOrdinal(test1, 5, test3, 5, FALSE);
+    ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
+    ret = pCompareStringOrdinal(test1, 4, test1, 5, FALSE);
+    ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
+
+    /* Check ordinal behaviour */
+    ret = pCompareStringOrdinal(bills1, -1, bills2, -1, FALSE);
+    ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
+    ret = pCompareStringOrdinal(coop2, -1, coop1, -1, FALSE);
+    ok(ret == CSTR_GREATER_THAN, "Got %u, expected %u\n", ret, CSTR_GREATER_THAN);
+    ret = pCompareStringOrdinal(nonascii1, -1, nonascii2, -1, FALSE);
+    ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
+    ret = pCompareStringOrdinal(nonascii1, -1, nonascii2, -1, TRUE);
+    ok(ret == CSTR_LESS_THAN, "Got %u, expected %u\n", ret, CSTR_LESS_THAN);
+}
+
 START_TEST(locale)
 {
   InitFunctionPointers();
@@ -3148,6 +3563,7 @@ START_TEST(locale)
   test_EnumDateFormatsA();
   test_GetLocaleInfoA();
   test_GetLocaleInfoW();
+  test_GetLocaleInfoEx();
   test_GetTimeFormatA();
   test_GetDateFormatA();
   test_GetDateFormatW();
@@ -3156,7 +3572,8 @@ START_TEST(locale)
   test_CompareStringA();
   test_LCMapStringA();
   test_LCMapStringW();
-  test_LocaleNames();
+  test_LCMapStringEx();
+  test_LocaleNameToLCID();
   test_FoldStringA();
   test_FoldStringW();
   test_ConvertDefaultLocale();
@@ -3170,6 +3587,8 @@ START_TEST(locale)
   test_IdnToNameprepUnicode();
   test_IdnToAscii();
   test_IdnToUnicode();
+  test_IsValidLocaleName();
+  test_CompareStringOrdinal();
   /* this requires collation table patch to make it MS compatible */
   if (0) test_sorting();
 }
index f88c8cb..e8d51e1 100755 (executable)
@@ -476,12 +476,16 @@ static void testGetDllDirectory(void)
         ok(bufferW[0] == 0 || /* XP, 2003 */
            broken(bufferW[0] == 'A'), "i=%d, Buffer overflow\n", i);
 
-        /* no buffer, but too short length */
-        ret = pGetDllDirectoryA(length, NULL);
-        ok(ret == length + 1, "i=%d, Expected %u, got %u\n", i, length + 1, ret);
+        if (0)
+        {
+            /* crashes on win8 */
+            /* no buffer, but too short length */
+            ret = pGetDllDirectoryA(length, NULL);
+            ok(ret == length + 1, "i=%d, Expected %u, got %u\n", i, length + 1, ret);
 
-        ret = pGetDllDirectoryW(length, NULL);
-        ok(ret == length + 1, "i=%d, Expected %u, got %u\n", i, length + 1, ret);
+            ret = pGetDllDirectoryW(length, NULL);
+            ok(ret == length + 1, "i=%d, Expected %u, got %u\n", i, length + 1, ret);
+        }
     }
 
     /* unset whatever we did so following tests won't be affected */
index 3484c62..923c0e7 100755 (executable)
@@ -798,6 +798,113 @@ static DWORD CALLBACK serverThreadMain4(LPVOID arg)
     return 0;
 }
 
+static int completion_called;
+static DWORD completion_errorcode;
+static DWORD completion_num_bytes;
+static LPOVERLAPPED completion_lpoverlapped;
+
+static VOID WINAPI completion_routine(DWORD errorcode, DWORD num_bytes, LPOVERLAPPED lpoverlapped)
+{
+    completion_called++;
+    completion_errorcode = errorcode;
+    completion_num_bytes = num_bytes;
+    completion_lpoverlapped = lpoverlapped;
+    SetEvent(lpoverlapped->hEvent);
+}
+
+/** Trivial byte echo server - uses ReadFileEx/WriteFileEx */
+static DWORD CALLBACK serverThreadMain5(LPVOID arg)
+{
+    int i;
+    HANDLE hEvent;
+
+    trace("serverThreadMain5\n");
+    /* Set up a simple echo server */
+    hnp = CreateNamedPipe(PIPENAME "serverThreadMain5", PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
+        PIPE_TYPE_BYTE | PIPE_WAIT,
+        /* nMaxInstances */ 1,
+        /* nOutBufSize */ 1024,
+        /* nInBufSize */ 1024,
+        /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT,
+        /* lpSecurityAttrib */ NULL);
+    ok(hnp != INVALID_HANDLE_VALUE, "CreateNamedPipe failed\n");
+
+    hEvent = CreateEvent(NULL,  /* security attribute */
+        TRUE,                   /* manual reset event */
+        FALSE,                  /* initial state */
+        NULL);                  /* name */
+    ok(hEvent != NULL, "CreateEvent\n");
+
+    for (i = 0; i < NB_SERVER_LOOPS; i++) {
+        char buf[512];
+        DWORD readden;
+        DWORD success;
+        OVERLAPPED oOverlap;
+        DWORD err;
+
+        memset(&oOverlap, 0, sizeof(oOverlap));
+        oOverlap.hEvent = hEvent;
+
+        /* Wait for client to connect */
+        trace("Server calling ConnectNamedPipe...\n");
+        success = ConnectNamedPipe(hnp, NULL);
+        err = GetLastError();
+        ok(success || (err == ERROR_PIPE_CONNECTED), "ConnectNamedPipe failed: %d\n", err);
+        trace("ConnectNamedPipe operation complete.\n");
+
+        /* Echo bytes once */
+        memset(buf, 0, sizeof(buf));
+
+        trace("Server reading...\n");
+        completion_called = 0;
+        ResetEvent(hEvent);
+        success = ReadFileEx(hnp, buf, sizeof(buf), &oOverlap, completion_routine);
+        trace("Server ReadFileEx returned...\n");
+        ok(success, "ReadFileEx failed, err=%i\n", GetLastError());
+        ok(completion_called == 0, "completion routine called before ReadFileEx return\n");
+        trace("ReadFileEx returned.\n");
+        if (success) {
+            DWORD ret;
+            do {
+                ret = WaitForSingleObjectEx(hEvent, INFINITE, TRUE);
+            } while (ret == WAIT_IO_COMPLETION);
+            ok(ret == 0, "wait ReadFileEx returned %x\n", ret);
+        }
+        ok(completion_called == 1, "completion routine called %i times\n", completion_called);
+        ok(completion_errorcode == ERROR_SUCCESS, "completion routine got error %d\n", completion_errorcode);
+        ok(completion_num_bytes != 0, "read 0 bytes\n");
+        ok(completion_lpoverlapped == &oOverlap, "got wrong overlapped pointer %p\n", completion_lpoverlapped);
+        readden = completion_num_bytes;
+        trace("Server done reading.\n");
+
+        trace("Server writing...\n");
+        completion_called = 0;
+        ResetEvent(hEvent);
+        success = WriteFileEx(hnp, buf, readden, &oOverlap, completion_routine);
+        trace("Server WriteFileEx returned...\n");
+        ok(success, "WriteFileEx failed, err=%i\n", GetLastError());
+        ok(completion_called == 0, "completion routine called before ReadFileEx return\n");
+        trace("overlapped WriteFile returned.\n");
+        if (success) {
+            DWORD ret;
+            do {
+                ret = WaitForSingleObjectEx(hEvent, INFINITE, TRUE);
+            } while (ret == WAIT_IO_COMPLETION);
+            ok(ret == 0, "wait WriteFileEx returned %x\n", ret);
+        }
+        trace("Server done writing.\n");
+        ok(completion_called == 1, "completion routine called %i times\n", completion_called);
+        ok(completion_errorcode == ERROR_SUCCESS, "completion routine got error %d\n", completion_errorcode);
+        ok(completion_num_bytes == readden, "read %i bytes wrote %i\n", readden, completion_num_bytes);
+        ok(completion_lpoverlapped == &oOverlap, "got wrong overlapped pointer %p\n", completion_lpoverlapped);
+
+        /* finish this connection, wait for next one */
+        ok(FlushFileBuffers(hnp), "FlushFileBuffers\n");
+        ok(DisconnectNamedPipe(hnp), "DisconnectNamedPipe\n");
+    }
+    return 0;
+}
+
 static void exercizeServer(const char *pipename, HANDLE serverThread)
 {
     int i;
@@ -891,6 +998,12 @@ static void test_NamedPipe_2(void)
     ok(serverThread != NULL, "CreateThread failed: %d\n", GetLastError());
     exercizeServer(PIPENAME "serverThreadMain4", serverThread);
 
+    /* Try server #5 */
+    SetLastError(0xdeadbeef);
+    serverThread = CreateThread(NULL, 0, serverThreadMain5, 0, 0, &serverThreadId);
+    ok(serverThread != NULL, "CreateThread failed: %d\n", GetLastError());
+    exercizeServer(PIPENAME "serverThreadMain5", serverThread);
+
     ok(SetEvent( alarm_event ), "SetEvent\n");
     CloseHandle( alarm_event );
     trace("test_NamedPipe_2 returning\n");
@@ -1637,6 +1750,116 @@ static void test_NamedPipeHandleState(void)
     CloseHandle(server);
 }
 
+static void test_readfileex_pending(void)
+{
+    HANDLE server, client, event;
+    BOOL ret;
+    DWORD err, wait, num_bytes;
+    OVERLAPPED overlapped;
+    char read_buf[1024];
+    char write_buf[1024];
+    const char test_string[] = "test";
+    int i;
+
+    server = CreateNamedPipe(PIPENAME, FILE_FLAG_OVERLAPPED | PIPE_ACCESS_DUPLEX,
+        /* dwOpenMode */ PIPE_TYPE_BYTE | PIPE_WAIT,
+        /* nMaxInstances */ 1,
+        /* nOutBufSize */ 1024,
+        /* nInBufSize */ 1024,
+        /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT,
+        /* lpSecurityAttrib */ NULL);
+    ok(server != INVALID_HANDLE_VALUE, "cf failed\n");
+
+    event = CreateEventA(NULL, TRUE, FALSE, NULL);
+    ok(event != NULL, "CreateEventA failed\n");
+
+    memset(&overlapped, 0, sizeof(overlapped));
+    overlapped.hEvent = event;
+
+    ret = ConnectNamedPipe(server, &overlapped);
+    err = GetLastError();
+    ok(ret == FALSE, "ConnectNamedPipe succeeded\n");
+    ok(err == ERROR_IO_PENDING, "ConnectNamedPipe set error %i\n", err);
+
+    wait = WaitForSingleObject(event, 0);
+    ok(wait == WAIT_TIMEOUT, "WaitForSingleObject returned %x\n", wait);
+
+    client = CreateFileA(PIPENAME, GENERIC_READ|GENERIC_WRITE, 0, NULL,
+        OPEN_EXISTING, 0, NULL);
+    ok(client != INVALID_HANDLE_VALUE, "cf failed\n");
+
+    wait = WaitForSingleObject(event, 0);
+    ok(wait == WAIT_OBJECT_0, "WaitForSingleObject returned %x\n", wait);
+
+    /* Start a read that can't complete immediately. */
+    completion_called = 0;
+    ResetEvent(event);
+    ret = ReadFileEx(server, read_buf, sizeof(read_buf), &overlapped, completion_routine);
+    ok(ret == TRUE, "ReadFileEx failed, err=%i\n", GetLastError());
+    ok(completion_called == 0, "completion routine called before ReadFileEx returned\n");
+
+    ret = WriteFile(client, test_string, strlen(test_string), &num_bytes, NULL);
+    ok(ret == TRUE, "WriteFile failed\n");
+    ok(num_bytes == strlen(test_string), "only %i bytes written\n", num_bytes);
+
+    ok(completion_called == 0, "completion routine called during WriteFile\n");
+
+    wait = WaitForSingleObjectEx(event, 0, TRUE);
+    ok(wait == WAIT_IO_COMPLETION || wait == WAIT_OBJECT_0, "WaitForSingleObjectEx returned %x\n", wait);
+
+    ok(completion_called == 1, "completion not called after writing pipe\n");
+    ok(completion_errorcode == 0, "completion called with error %x\n", completion_errorcode);
+    ok(completion_num_bytes == strlen(test_string), "ReadFileEx returned only %d bytes\n", completion_num_bytes);
+    ok(completion_lpoverlapped == &overlapped, "completion called with wrong overlapped pointer\n");
+    ok(!memcmp(test_string, read_buf, strlen(test_string)), "ReadFileEx read wrong bytes\n");
+
+    /* Make writes until the pipe is full and the write fails */
+    memset(write_buf, 0xaa, sizeof(write_buf));
+    for (i=0; i<256; i++)
+    {
+        completion_called = 0;
+        ResetEvent(event);
+        ret = WriteFileEx(server, write_buf, sizeof(write_buf), &overlapped, completion_routine);
+        err = GetLastError();
+
+        ok(completion_called == 0, "completion routine called during WriteFileEx\n");
+
+        wait = WaitForSingleObjectEx(event, 0, TRUE);
+
+        if (wait == WAIT_TIMEOUT)
+            /* write couldn't complete immediately, presumably the pipe is full */
+            break;
+
+        ok(wait == WAIT_IO_COMPLETION || wait == WAIT_OBJECT_0, "WaitForSingleObject returned %x\n", wait);
+
+        ok(ret == TRUE, "WriteFileEx failed, err=%i\n", err);
+        ok(completion_errorcode == 0, "completion called with error %x\n", completion_errorcode);
+        ok(completion_lpoverlapped == &overlapped, "completion called with wrong overlapped pointer\n");
+    }
+
+    ok(ret == TRUE, "WriteFileEx failed, err=%i\n", err);
+    ok(completion_called == 0, "completion routine called but wait timed out\n");
+    ok(completion_errorcode == 0, "completion called with error %x\n", completion_errorcode);
+    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);
+
+    ok(completion_called == 1, "completion routine not called\n");
+    ok(completion_errorcode == 0, "completion called with error %x\n", completion_errorcode);
+    ok(completion_lpoverlapped == &overlapped, "completion called with wrong overlapped pointer\n");
+
+    CloseHandle(client);
+    CloseHandle(server);
+    CloseHandle(event);
+}
+
 START_TEST(pipe)
 {
     HMODULE hmod;
@@ -1656,4 +1879,5 @@ START_TEST(pipe)
     test_impersonation();
     test_overlapped();
     test_NamedPipeHandleState();
+    test_readfileex_pending();
 }
index 8aadf2a..be0e948 100755 (executable)
@@ -298,18 +298,6 @@ static void     doChild(const char* file, const char* option)
         childPrintf(hFile, "argvA%d=%s\n", i, encodeA(myARGV[i]));
     }
     childPrintf(hFile, "CommandLineA=%s\n", encodeA(GetCommandLineA()));
-
-#if 0
-    int                 argcW;
-    WCHAR**             argvW;
-
-    /* this is part of shell32... and should be tested there */
-    argvW = CommandLineToArgvW(GetCommandLineW(), &argcW);
-    for (i = 0; i < argcW; i++)
-    {
-        childPrintf(hFile, "argvW%d=%s\n", i, encodeW(argvW[i]));
-    }
-#endif
     childPrintf(hFile, "CommandLineW=%s\n\n", encodeW(GetCommandLineW()));
 
     /* output of environment (Ansi) */
index e875c79..5b68c8c 100755 (executable)
@@ -18,7 +18,7 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-#define _WIN32_WINNT 0x502
+#define _WIN32_WINNT 0x600
 #include <stdarg.h>
 #include <stdlib.h>
 #include <stdio.h>
@@ -37,6 +37,15 @@ static BOOL   (WINAPI *pDeleteTimerQueueTimer)(HANDLE, HANDLE, HANDLE);
 static HANDLE (WINAPI *pOpenWaitableTimerA)(DWORD,BOOL,LPCSTR);
 static HANDLE (WINAPI *pCreateMemoryResourceNotification)(MEMORY_RESOURCE_NOTIFICATION_TYPE);
 static BOOL   (WINAPI *pQueryMemoryResourceNotification)(HANDLE, PBOOL);
+static VOID   (WINAPI *pInitOnceInitialize)(PINIT_ONCE);
+static BOOL   (WINAPI *pInitOnceExecuteOnce)(PINIT_ONCE,PINIT_ONCE_FN,PVOID,LPVOID*);
+static BOOL   (WINAPI *pInitOnceBeginInitialize)(PINIT_ONCE,DWORD,BOOL*,LPVOID*);
+static BOOL   (WINAPI *pInitOnceComplete)(PINIT_ONCE,DWORD,LPVOID);
+
+static VOID   (WINAPI *pInitializeConditionVariable)(PCONDITION_VARIABLE);
+static BOOL   (WINAPI *pSleepConditionVariableCS)(PCONDITION_VARIABLE,PCRITICAL_SECTION,DWORD);
+static VOID   (WINAPI *pWakeAllConditionVariable)(PCONDITION_VARIABLE);
+static VOID   (WINAPI *pWakeConditionVariable)(PCONDITION_VARIABLE);
 
 static void test_signalandwait(void)
 {
@@ -704,11 +713,11 @@ static void CALLBACK timer_queue_cb6(PVOID p, BOOLEAN timedOut)
 
 static void test_timer_queue(void)
 {
-    HANDLE q, t1, t2, t3, t4, t5;
-    int n1, n2, n3, n4, n5;
+    HANDLE q, t0, t1, t2, t3, t4, t5;
+    int n0, n1, n2, n3, n4, n5;
     struct timer_queue_data1 d1, d2, d3, d4;
     HANDLE e, et1, et2;
-    BOOL ret;
+    BOOL ret, ret0;
 
     if (!pChangeTimerQueueTimer || !pCreateTimerQueue || !pCreateTimerQueueTimer
         || !pDeleteTimerQueueEx || !pDeleteTimerQueueTimer)
@@ -731,6 +740,18 @@ static void test_timer_queue(void)
     q = pCreateTimerQueue();
     ok(q != NULL, "CreateTimerQueue\n");
 
+    /* Not called. */
+    t0 = NULL;
+    n0 = 0;
+    ret = pCreateTimerQueueTimer(&t0, q, timer_queue_cb1, &n0, 0,
+                                 300, 0);
+    ok(ret, "CreateTimerQueueTimer\n");
+    ok(t0 != NULL, "CreateTimerQueueTimer\n");
+    ret0 = pDeleteTimerQueueTimer(q, t0, NULL);
+    ok((!ret0 && GetLastError() == ERROR_IO_PENDING) ||
+       broken(ret0), /* Win 2000 & XP & 2003 */
+       "DeleteTimerQueueTimer ret=%d le=%u\n", ret0, GetLastError());
+
     /* Called once.  */
     t1 = NULL;
     n1 = 0;
@@ -784,10 +805,12 @@ static void test_timer_queue(void)
 
     ret = pDeleteTimerQueueEx(q, INVALID_HANDLE_VALUE);
     ok(ret, "DeleteTimerQueueEx\n");
-    ok(n1 == 1, "Timer callback 1\n");
-    ok(n2 < n3, "Timer callback 2 should be much slower than 3\n");
-    ok(n4 == 0, "Timer callback 4\n");
-    ok(n5 == 1, "Timer callback 5\n");
+    todo_wine
+    ok(n0 == 1 || broken(ret0 && n0 == 0), "Timer callback 0 expected 1 got %d\n", n0);
+    ok(n1 == 1, "Timer callback 1 expected 1 got %d\n", n1);
+    ok(n2 < n3, "Timer callback 2 & 3 expected %d < %d\n", n2, n3);
+    ok(n4 == 0, "Timer callback 4 expected 0 got %d\n", n4);
+    ok(n5 == 1, "Timer callback 5 expected 1 got %d\n", n5);
 
     /* Test synchronous deletion of the timer/queue with event trigger. */
     e = CreateEvent(NULL, TRUE, FALSE, NULL);
@@ -1121,6 +1144,211 @@ static void test_WaitForMultipleObjects(void)
         if (maxevents[i]) CloseHandle(maxevents[i]);
 }
 
+static BOOL g_initcallback_ret, g_initcallback_called;
+static void *g_initctxt;
+
+static BOOL CALLBACK initonce_callback(INIT_ONCE *initonce, void *parameter, void **ctxt)
+{
+    g_initcallback_called = TRUE;
+    /* zero bit set means here that initialization is taking place - initialization locked */
+    ok(g_initctxt == *ctxt, "got wrong context value %p, expected %p\n", *ctxt, g_initctxt);
+    ok(initonce->Ptr == (void*)0x1, "got %p\n", initonce->Ptr);
+    ok(parameter == (void*)0xdeadbeef, "got wrong parameter\n");
+    return g_initcallback_ret;
+}
+
+static void test_initonce(void)
+{
+    INIT_ONCE initonce;
+    BOOL ret, pending;
+
+    if (!pInitOnceInitialize || !pInitOnceExecuteOnce)
+    {
+        skip("one-time initialization API not supported\n");
+        return;
+    }
+
+    /* blocking initialization with callback */
+    initonce.Ptr = (void*)0xdeadbeef;
+    pInitOnceInitialize(&initonce);
+    ok(initonce.Ptr == NULL, "got %p\n", initonce.Ptr);
+
+    /* initialisation completed successfully */
+    g_initcallback_ret = TRUE;
+    g_initctxt = NULL;
+    ret = pInitOnceExecuteOnce(&initonce, initonce_callback, (void*)0xdeadbeef, &g_initctxt);
+    ok(ret, "got wrong ret value %d\n", ret);
+    ok(initonce.Ptr == (void*)0x2, "got %p\n", initonce.Ptr);
+    ok(g_initctxt == NULL, "got %p\n", g_initctxt);
+    ok(g_initcallback_called, "got %d\n", g_initcallback_called);
+
+    /* so it's been called already so won't be called again */
+    g_initctxt = NULL;
+    g_initcallback_called = FALSE;
+    ret = pInitOnceExecuteOnce(&initonce, initonce_callback, (void*)0xdeadbeef, &g_initctxt);
+    ok(ret, "got wrong ret value %d\n", ret);
+    ok(initonce.Ptr == (void*)0x2, "got %p\n", initonce.Ptr);
+    ok(g_initctxt == NULL, "got %p\n", g_initctxt);
+    ok(!g_initcallback_called, "got %d\n", g_initcallback_called);
+
+    pInitOnceInitialize(&initonce);
+    g_initcallback_called = FALSE;
+    /* 2 lower order bits should never be used, you'll get a crash in result */
+    g_initctxt = (void*)0xFFFFFFF0;
+    ret = pInitOnceExecuteOnce(&initonce, initonce_callback, (void*)0xdeadbeef, &g_initctxt);
+    ok(ret, "got wrong ret value %d\n", ret);
+    ok(initonce.Ptr == (void*)0xFFFFFFF2, "got %p\n", initonce.Ptr);
+    ok(g_initctxt == (void*)0xFFFFFFF0, "got %p\n", g_initctxt);
+    ok(g_initcallback_called, "got %d\n", g_initcallback_called);
+
+    /* callback failed */
+    g_initcallback_ret = FALSE;
+    g_initcallback_called = FALSE;
+    g_initctxt = NULL;
+    pInitOnceInitialize(&initonce);
+    ret = pInitOnceExecuteOnce(&initonce, initonce_callback, (void*)0xdeadbeef, &g_initctxt);
+    ok(!ret, "got wrong ret value %d\n", ret);
+    ok(initonce.Ptr == NULL, "got %p\n", initonce.Ptr);
+    ok(g_initctxt == NULL, "got %p\n", g_initctxt);
+    ok(g_initcallback_called, "got %d\n", g_initcallback_called);
+
+    /* blocking initialzation without a callback */
+    pInitOnceInitialize(&initonce);
+    g_initctxt = NULL;
+    pending = FALSE;
+    ret = pInitOnceBeginInitialize(&initonce, 0, &pending, &g_initctxt);
+    ok(ret, "got wrong ret value %d\n", ret);
+    ok(pending, "got %d\n", pending);
+    ok(initonce.Ptr == (void*)1, "got %p\n", initonce.Ptr);
+    ok(g_initctxt == NULL, "got %p\n", g_initctxt);
+    /* another attempt to begin initialization with block a single thread */
+
+    g_initctxt = NULL;
+    pending = 0xf;
+    ret = pInitOnceBeginInitialize(&initonce, INIT_ONCE_CHECK_ONLY, &pending, &g_initctxt);
+    ok(!ret, "got wrong ret value %d\n", ret);
+    ok(pending == 0xf, "got %d\n", pending);
+    ok(initonce.Ptr == (void*)1, "got %p\n", initonce.Ptr);
+    ok(g_initctxt == NULL, "got %p\n", g_initctxt);
+
+    g_initctxt = (void*)0xdeadbee0;
+    ret = pInitOnceComplete(&initonce, INIT_ONCE_INIT_FAILED, g_initctxt);
+    ok(!ret, "got wrong ret value %d\n", ret);
+    ok(initonce.Ptr == (void*)1, "got %p\n", initonce.Ptr);
+
+    /* once failed already */
+    g_initctxt = (void*)0xdeadbee0;
+    ret = pInitOnceComplete(&initonce, 0, g_initctxt);
+    ok(ret, "got wrong ret value %d\n", ret);
+    ok(initonce.Ptr == (void*)0xdeadbee2, "got %p\n", initonce.Ptr);
+}
+
+static CONDITION_VARIABLE buffernotempty,buffernotfull;
+static CRITICAL_SECTION   buffercrit;
+static BOOL condvar_stop = FALSE, condvar_sleeperr = FALSE;
+static LONG bufferlen,totalproduced,totalconsumed;
+static LONG condvar_producer_sleepcnt,condvar_consumer_sleepcnt;
+
+#define BUFFER_SIZE 10
+
+static DWORD WINAPI condvar_producer(LPVOID x) {
+    while (1) {
+        Sleep(rand() % 10);
+
+        EnterCriticalSection(&buffercrit);
+        while ((bufferlen == BUFFER_SIZE) && !condvar_stop) {
+            condvar_producer_sleepcnt++;
+            if (!pSleepConditionVariableCS(&buffernotfull, &buffercrit, 2000))
+                condvar_sleeperr = TRUE;
+        }
+        if (condvar_stop) {
+            LeaveCriticalSection(&buffercrit);
+            break;
+        }
+        bufferlen++;
+        totalproduced++;
+        LeaveCriticalSection(&buffercrit);
+        pWakeConditionVariable(&buffernotempty);
+    }
+    return 0;
+}
+
+static DWORD WINAPI condvar_consumer(LPVOID x) {
+    DWORD *cnt = (DWORD*)x;
+
+    while (1) {
+        EnterCriticalSection(&buffercrit);
+        while ((bufferlen == 0) && !condvar_stop) {
+            condvar_consumer_sleepcnt++;
+            if (!pSleepConditionVariableCS (&buffernotempty, &buffercrit, 2000))
+                condvar_sleeperr = TRUE;
+        }
+        if (condvar_stop && (bufferlen == 0)) {
+            LeaveCriticalSection(&buffercrit);
+            break;
+        }
+        bufferlen--;
+        totalconsumed++;
+        (*cnt)++;
+        LeaveCriticalSection(&buffercrit);
+        pWakeConditionVariable(&buffernotfull);
+        Sleep(rand() % 10);
+    }
+    return 0;
+}
+
+static void test_condvars(void)
+{
+    HANDLE hp1,hp2,hc1,hc2;
+    DWORD dummy;
+    DWORD cnt1,cnt2;
+
+    if (!pInitializeConditionVariable) {
+        /* function is not yet in XP, only in newer Windows */
+        /* and not yet implemented in Wine for some days/weeks */
+        todo_wine win_skip("no condition variable support.\n");
+        return;
+    }
+
+    /* Implement a producer / consumer scheme with non-full / non-empty triggers */
+    pInitializeConditionVariable(&buffernotfull);
+    pInitializeConditionVariable(&buffernotempty);
+    InitializeCriticalSection(&buffercrit);
+    bufferlen = totalproduced = totalconsumed = cnt1 = cnt2 = 0;
+
+    hp1 = CreateThread(NULL, 0, condvar_producer, NULL, 0, &dummy);
+    hp2 = CreateThread(NULL, 0, condvar_producer, NULL, 0, &dummy);
+    hc1 = CreateThread(NULL, 0, condvar_consumer, (PVOID)&cnt1, 0, &dummy);
+    hc2 = CreateThread(NULL, 0, condvar_consumer, (PVOID)&cnt2, 0, &dummy);
+
+    /* Limit run to 0.5 seconds. */
+    Sleep(500);
+
+    /* tear down start */
+    condvar_stop = TRUE;
+
+    /* final wake up call */
+    pWakeAllConditionVariable (&buffernotfull);
+    pWakeAllConditionVariable (&buffernotempty);
+
+    WaitForSingleObject(hp1, 1000);
+    WaitForSingleObject(hp2, 1000);
+    WaitForSingleObject(hc1, 1000);
+    WaitForSingleObject(hc2, 1000);
+
+    ok(totalconsumed == totalproduced,
+       "consumed %d != produced %d\n", totalconsumed, totalproduced);
+    ok (!condvar_sleeperr, "error occurred during SleepConditionVariableCS\n");
+
+    /* Checking cnt1 - cnt2 for non-0 would be not good, the case where
+     * one consumer does not get anything to do is possible. */
+    trace("produced %d, c1 %d, c2 %d\n", totalproduced, cnt1, cnt2);
+    /* The sleeps of the producer or consumer should not go above 100* produced count,
+     * otherwise the implementation does not sleep correctly. But yet again, this is
+     * not hard defined. */
+    trace("producer sleep %d, consumer sleep %d\n", condvar_producer_sleepcnt, condvar_consumer_sleepcnt);
+}
+
 START_TEST(sync)
 {
     HMODULE hdll = GetModuleHandle("kernel32");
@@ -1133,6 +1361,14 @@ START_TEST(sync)
     pOpenWaitableTimerA = (void*)GetProcAddress(hdll, "OpenWaitableTimerA");
     pCreateMemoryResourceNotification = (void *)GetProcAddress(hdll, "CreateMemoryResourceNotification");
     pQueryMemoryResourceNotification = (void *)GetProcAddress(hdll, "QueryMemoryResourceNotification");
+    pInitOnceInitialize = (void *)GetProcAddress(hdll, "InitOnceInitialize");
+    pInitOnceExecuteOnce = (void *)GetProcAddress(hdll, "InitOnceExecuteOnce");
+    pInitOnceBeginInitialize = (void *)GetProcAddress(hdll, "InitOnceBeginInitialize");
+    pInitOnceComplete = (void *)GetProcAddress(hdll, "InitOnceComplete");
+    pInitializeConditionVariable = (void *)GetProcAddress(hdll, "InitializeConditionVariable");
+    pSleepConditionVariableCS = (void *)GetProcAddress(hdll, "SleepConditionVariableCS");
+    pWakeAllConditionVariable = (void *)GetProcAddress(hdll, "WakeAllConditionVariable");
+    pWakeConditionVariable = (void *)GetProcAddress(hdll, "WakeConditionVariable");
 
     test_signalandwait();
     test_mutex();
@@ -1144,4 +1380,6 @@ START_TEST(sync)
     test_timer_queue();
     test_WaitForSingleObject();
     test_WaitForMultipleObjects();
+    test_initonce();
+    test_condvars();
 }
index 19726af..76a7a33 100755 (executable)
@@ -961,16 +961,19 @@ static void test_SetThreadContext(void)
     ctx.ContextFlags = CONTEXT_FULL;
     SetLastError(0xdeadbeef);
     ret = GetThreadContext( thread, &ctx );
-    ok( !ret, "GetThreadContext succeeded\n" );
-    ok( GetLastError() == ERROR_GEN_FAILURE || broken(GetLastError() == ERROR_INVALID_HANDLE), /* win2k */
-        "wrong error %u\n", GetLastError() );
+    ok( (!ret && (GetLastError() == ERROR_GEN_FAILURE)) ||
+        (!ret && broken(GetLastError() == ERROR_INVALID_HANDLE)) || /* win2k */
+        broken(ret),   /* 32bit application on NT 5.x 64bit */
+        "got %d with %u (expected FALSE with ERROR_GEN_FAILURE)\n",
+        ret, GetLastError() );
 
     SetLastError(0xdeadbeef);
     ret = SetThreadContext( thread, &ctx );
-    ok( !ret, "SetThreadContext succeeded\n" );
-    ok( GetLastError() == ERROR_GEN_FAILURE || GetLastError() == ERROR_ACCESS_DENIED ||
-        broken(GetLastError() == ERROR_INVALID_HANDLE), /* win2k */
-        "wrong error %u\n", GetLastError() );
+    ok( (!ret && ((GetLastError() == ERROR_GEN_FAILURE) || (GetLastError() == ERROR_ACCESS_DENIED))) ||
+        (!ret && broken(GetLastError() == ERROR_INVALID_HANDLE)) || /* win2k */
+        broken(ret),   /* 32bit application on NT 5.x 64bit */
+        "got %d with %u (expected FALSE with ERROR_GEN_FAILURE or ERROR_ACCESS_DENIED)\n",
+        ret, GetLastError() );
 
     CloseHandle( thread );
 }
index 8c233f0..a06217a 100755 (executable)
 #include "wine/test.h"
 #include "winbase.h"
 
-typedef HANDLE (WINAPI *fnCreateWaitableTimerA)( SECURITY_ATTRIBUTES*, BOOL, LPSTR );
-typedef BOOL (WINAPI *fnSetWaitableTimer)(HANDLE, LARGE_INTEGER*, LONG, PTIMERAPCROUTINE, LPVOID, BOOL);
-
 
 static void test_timer(void)
 {
+    HANDLE (WINAPI *pCreateWaitableTimerA)( SECURITY_ATTRIBUTES*, BOOL, LPSTR );
+    BOOL (WINAPI *pSetWaitableTimer)(HANDLE, LARGE_INTEGER*, LONG, PTIMERAPCROUTINE, LPVOID, BOOL);
     HMODULE hker = GetModuleHandle("kernel32");
-    fnCreateWaitableTimerA pCreateWaitableTimerA;
-    fnSetWaitableTimer pSetWaitableTimer;
     HANDLE handle;
     BOOL r;
     LARGE_INTEGER due;
 
-    pCreateWaitableTimerA = (fnCreateWaitableTimerA) GetProcAddress( hker, "CreateWaitableTimerA");
+    pCreateWaitableTimerA = (void*)GetProcAddress( hker, "CreateWaitableTimerA");
     if( !pCreateWaitableTimerA )
     {
         win_skip("CreateWaitableTimerA is not available\n");
         return;
     }
 
-    pSetWaitableTimer = (fnSetWaitableTimer) GetProcAddress( hker, "SetWaitableTimer");
+    pSetWaitableTimer = (void*)GetProcAddress( hker, "SetWaitableTimer");
     if( !pSetWaitableTimer )
     {
         win_skip("SetWaitableTimer is not available\n");
index 9f9b72e..4b9a4f3 100644 (file)
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+/* Needed for PRODUCT_* defines and GetProductInfo() */
+#define _WIN32_WINNT 0x0600
+
 #include <assert.h>
 
 #include "wine/test.h"
 #include "winbase.h"
 
+static BOOL (WINAPI * pGetProductInfo)(DWORD, DWORD, DWORD, DWORD, DWORD *);
 static BOOL (WINAPI * pVerifyVersionInfoA)(LPOSVERSIONINFOEXA, DWORD, DWORDLONG);
 static ULONGLONG (WINAPI * pVerSetConditionMask)(ULONGLONG, DWORD, BYTE);
 
 #define KERNEL32_GET_PROC(func)                                     \
-    p##func = (void *)GetProcAddress(hKernel32, #func);             \
-    if(!p##func) trace("GetProcAddress(hKernel32, '%s') failed\n", #func);
+    p##func = (void *)GetProcAddress(hKernel32, #func);
 
 static void init_function_pointers(void)
 {
     HMODULE hKernel32;
 
-    pVerifyVersionInfoA = NULL;
-    pVerSetConditionMask = NULL;
-
     hKernel32 = GetModuleHandleA("kernel32.dll");
-    assert(hKernel32);
+
+    KERNEL32_GET_PROC(GetProductInfo);
     KERNEL32_GET_PROC(VerifyVersionInfoA);
     KERNEL32_GET_PROC(VerSetConditionMask);
 }
 
+static void test_GetProductInfo(void)
+{
+    DWORD product;
+    DWORD res;
+    DWORD table[] = {9,8,7,6,
+                     7,0,0,0,
+                     6,2,0,0,
+                     6,1,2,0,
+                     6,1,1,0,
+                     6,1,0,2,
+                     6,1,0,0,
+                     6,0,3,0,
+                     6,0,2,0,
+                     6,0,1,5,
+                     6,0,1,0,
+                     6,0,0,0,
+                     5,3,0,0,
+                     5,2,0,0,
+                     5,1,0,0,
+                     5,0,0,0,
+                     0};
+
+    DWORD *entry = table;
+
+    if (!pGetProductInfo)
+    {
+        /* Not present before Vista */
+        win_skip("GetProductInfo() not available\n");
+        return;
+    }
+
+    while (*entry)
+    {
+        /* SetLastError() / GetLastError(): value is untouched */
+        product = 0xdeadbeef;
+        SetLastError(0xdeadbeef);
+        res = pGetProductInfo(entry[0], entry[1], entry[2], entry[3], &product);
+
+        if (entry[0] >= 6)
+            ok(res && (product > PRODUCT_UNDEFINED) && (product <= PRODUCT_PROFESSIONAL_WMC),
+               "got %d and 0x%x (expected TRUE and a valid PRODUCT_* value)\n", res, product);
+        else
+            ok(!res && !product && (GetLastError() == 0xdeadbeef),
+               "got %d and 0x%x with 0x%x (expected FALSE and PRODUCT_UNDEFINED with LastError untouched)\n",
+               res, product, GetLastError());
+
+        entry+= 4;
+    }
+
+    /* NULL pointer is not a problem */
+    SetLastError(0xdeadbeef);
+    res = pGetProductInfo(6, 1, 0, 0, NULL);
+    ok( (!res) && (GetLastError() == 0xdeadbeef),
+        "got %d with 0x%x (expected FALSE with LastError untouched\n", res, GetLastError());
+}
+
 static void test_GetVersionEx(void)
 {
     OSVERSIONINFOA infoA;
@@ -95,8 +152,6 @@ static void test_GetVersionEx(void)
     ok(ret ||
        broken(ret == 0), /* win95 */
        "Expected GetVersionExA to succeed\n");
-    ok(GetLastError() == 0xdeadbeef,
-        "Expected 0xdeadbeef, got %d\n", GetLastError());
 }
 
 static void test_VerifyVersionInfo(void)
@@ -292,6 +347,7 @@ START_TEST(version)
 {
     init_function_pointers();
 
+    test_GetProductInfo();
     test_GetVersionEx();
     test_VerifyVersionInfo();
 }
index e958271..63badc6 100755 (executable)
@@ -38,6 +38,8 @@ static BOOL   (WINAPI *pVirtualFreeEx)(HANDLE, LPVOID, SIZE_T, DWORD);
 static UINT   (WINAPI *pGetWriteWatch)(DWORD,LPVOID,SIZE_T,LPVOID*,ULONG_PTR*,ULONG*);
 static UINT   (WINAPI *pResetWriteWatch)(LPVOID,SIZE_T);
 static NTSTATUS (WINAPI *pNtAreMappedFilesTheSame)(PVOID,PVOID);
+static NTSTATUS (WINAPI *pNtMapViewOfSection)(HANDLE, HANDLE, PVOID *, ULONG, SIZE_T, const LARGE_INTEGER *, SIZE_T *, ULONG, ULONG, ULONG);
+static DWORD (WINAPI *pNtUnmapViewOfSection)(HANDLE, PVOID);
 
 /* ############################### */
 
@@ -422,6 +424,7 @@ static void test_MapViewOfFile(void)
     ret = DuplicateHandle( GetCurrentProcess(), mapping, GetCurrentProcess(), &map2,
                            FILE_MAP_READ, FALSE, 0 );
     ok( ret, "DuplicateHandle failed error %u\n", GetLastError());
+    SetLastError(0xdeadbeef);
     ptr = MapViewOfFile( map2, FILE_MAP_WRITE, 0, 0, 4096 );
     if (!ptr)
     {
@@ -429,6 +432,7 @@ static void test_MapViewOfFile(void)
         CloseHandle( map2 );
         ret = DuplicateHandle( GetCurrentProcess(), mapping, GetCurrentProcess(), &map2, 0, FALSE, 0 );
         ok( ret, "DuplicateHandle failed error %u\n", GetLastError());
+        SetLastError(0xdeadbeef);
         ptr = MapViewOfFile( map2, 0, 0, 0, 4096 );
         ok( !ptr, "MapViewOfFile succeeded\n" );
         ok( GetLastError() == ERROR_ACCESS_DENIED, "Wrong error %d\n", GetLastError() );
@@ -603,12 +607,14 @@ static void test_MapViewOfFile(void)
     ptr = MapViewOfFile( mapping, FILE_MAP_WRITE, 0, 0, 0 );
     if (!ptr)
     {
+        SIZE_T size;
         ok( GetLastError() == ERROR_ACCESS_DENIED, "Wrong error %d\n", GetLastError() );
         SetLastError(0xdeadbeef);
         ptr = MapViewOfFile( mapping, FILE_MAP_READ, 0, 0, 0 );
         ok( ptr != NULL, "MapViewOfFile FILE_MAP_READ error %u\n", GetLastError() );
         SetLastError(0xdeadbeef);
-        ok( VirtualQuery( ptr, &info, sizeof(info) ) == sizeof(info),
+        size = VirtualQuery( ptr, &info, sizeof(info) );
+        ok( size == sizeof(info),
             "VirtualQuery error %u\n", GetLastError() );
         ok( info.BaseAddress == ptr, "%p != %p\n", info.BaseAddress, ptr );
         ok( info.AllocationBase == ptr, "%p != %p\n", info.AllocationBase, ptr );
@@ -628,12 +634,14 @@ static void test_MapViewOfFile(void)
     ptr = MapViewOfFile( mapping, FILE_MAP_READ, 0, 0, 0 );
     if (!ptr)
     {
+        SIZE_T size;
         ok( GetLastError() == ERROR_ACCESS_DENIED, "Wrong error %d\n", GetLastError() );
         SetLastError(0xdeadbeef);
         ptr = MapViewOfFile( mapping, FILE_MAP_WRITE, 0, 0, 0 );
         ok( ptr != NULL, "MapViewOfFile FILE_MAP_WRITE error %u\n", GetLastError() );
         SetLastError(0xdeadbeef);
-        ok( VirtualQuery( ptr, &info, sizeof(info) ) == sizeof(info),
+        size = VirtualQuery( ptr, &info, sizeof(info) );
+        ok( size == sizeof(info),
             "VirtualQuery error %u\n", GetLastError() );
         ok( info.BaseAddress == ptr, "%p != %p\n", info.BaseAddress, ptr );
         ok( info.AllocationBase == ptr, "%p != %p\n", info.AllocationBase, ptr );
@@ -664,7 +672,7 @@ static void test_MapViewOfFile(void)
     ok(ret, "VirtualQuery failed with error %d\n", GetLastError());
     ok(info.BaseAddress == ptr, "BaseAddress should have been %p but was %p instead\n", ptr, info.BaseAddress);
     ok(info.AllocationBase == ptr, "AllocationBase should have been %p but was %p instead\n", ptr, info.AllocationBase);
-    ok(info.RegionSize == MAPPING_SIZE, "RegionSize should have been 0x%x but was 0x%x\n", MAPPING_SIZE, (unsigned int)info.RegionSize);
+    ok(info.RegionSize == MAPPING_SIZE, "RegionSize should have been 0x%x but was 0x%lx\n", MAPPING_SIZE, info.RegionSize);
     ok(info.State == MEM_RESERVE, "State should have been MEM_RESERVE instead of 0x%x\n", info.State);
     if (info.Type == MEM_PRIVATE)  /* win9x is different for uncommitted mappings */
     {
@@ -692,7 +700,7 @@ static void test_MapViewOfFile(void)
         ok(info.AllocationProtect == PAGE_READWRITE,
            "AllocationProtect should have been PAGE_READWRITE but was 0x%x\n", info.AllocationProtect);
         ok(info.RegionSize == MAPPING_SIZE,
-           "RegionSize should have been 0x%x but was 0x%x\n", MAPPING_SIZE, (unsigned int)info.RegionSize);
+           "RegionSize should have been 0x%x but was 0x%lx\n", MAPPING_SIZE, info.RegionSize);
         ok(info.State == MEM_RESERVE,
            "State should have been MEM_RESERVE instead of 0x%x\n", info.State);
         ok(info.Protect == 0,
@@ -708,7 +716,7 @@ static void test_MapViewOfFile(void)
     ok(ret, "VirtualQuery failed with error %d\n", GetLastError());
     ok(info.BaseAddress == ptr, "BaseAddress should have been %p but was %p instead\n", ptr, info.BaseAddress);
     ok(info.AllocationBase == ptr, "AllocationBase should have been %p but was %p instead\n", ptr, info.AllocationBase);
-    ok(info.RegionSize == 0x10000, "RegionSize should have been 0x10000 but was 0x%x\n", (unsigned int)info.RegionSize);
+    ok(info.RegionSize == 0x10000, "RegionSize should have been 0x10000 but was 0x%lx\n", info.RegionSize);
     ok(info.State == MEM_COMMIT, "State should have been MEM_RESERVE instead of 0x%x\n", info.State);
     ok(info.Protect == PAGE_READONLY, "Protect should have been 0 instead of 0x%x\n", info.Protect);
     if (info.Type == MEM_PRIVATE)  /* win9x is different for uncommitted mappings */
@@ -737,11 +745,11 @@ static void test_MapViewOfFile(void)
         ok(info.AllocationProtect == PAGE_READWRITE,
            "AllocationProtect should have been PAGE_READWRITE but was 0x%x\n", info.AllocationProtect);
         ok(info.RegionSize == 0x10000,
-           "RegionSize should have been 0x10000 but was 0x%x\n", (unsigned int)info.RegionSize);
+           "RegionSize should have been 0x10000 but was 0x%lx\n", info.RegionSize);
         ok(info.State == MEM_COMMIT,
-           "State should have been MEM_RESERVE instead of 0x%x\n", info.State);
+           "State should have been MEM_COMMIT instead of 0x%x\n", info.State);
         ok(info.Protect == PAGE_READWRITE,
-           "Protect should have been 0 instead of 0x%x\n", info.Protect);
+           "Protect should have been PAGE_READWRITE instead of 0x%x\n", info.Protect);
         ok(info.Type == MEM_MAPPED, "Type should have been MEM_MAPPED instead of 0x%x\n", info.Type);
     }
 
@@ -782,13 +790,166 @@ else
        "got %u, expected ERROR_INVALID_ADDRESS\n", GetLastError());
 
     ok( VirtualFree(addr, 0, MEM_RELEASE), "VirtualFree failed\n" );
-}
 
-static DWORD (WINAPI *pNtMapViewOfSection)( HANDLE handle, HANDLE process, PVOID *addr_ptr,
-                                            ULONG zero_bits, SIZE_T commit_size,
-                                            const LARGE_INTEGER *offset_ptr, SIZE_T *size_ptr,
-                                            ULONG inherit, ULONG alloc_type, ULONG protect );
-static DWORD (WINAPI *pNtUnmapViewOfSection)( HANDLE process, PVOID addr );
+    /* close named mapping handle without unmapping */
+    name = "Foo";
+    SetLastError(0xdeadbeef);
+    mapping = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, MAPPING_SIZE, name);
+    ok( mapping != 0, "CreateFileMappingA failed with error %d\n", GetLastError() );
+    SetLastError(0xdeadbeef);
+    ptr = MapViewOfFile(mapping, FILE_MAP_WRITE, 0, 0, 0);
+    ok( ptr != NULL, "MapViewOfFile failed with error %d\n", GetLastError() );
+    SetLastError(0xdeadbeef);
+    map2 = OpenFileMappingA(FILE_MAP_READ, FALSE, name);
+    ok( map2 != 0, "OpenFileMappingA failed with error %d\n", GetLastError() );
+    SetLastError(0xdeadbeef);
+    ret = CloseHandle(map2);
+    ok(ret, "CloseHandle error %d\n", GetLastError());
+    SetLastError(0xdeadbeef);
+    ret = CloseHandle(mapping);
+    ok(ret, "CloseHandle error %d\n", GetLastError());
+
+    ret = IsBadReadPtr(ptr, MAPPING_SIZE);
+    ok( !ret, "memory is not accessible\n" );
+
+    ret = VirtualQuery(ptr, &info, sizeof(info));
+    ok(ret, "VirtualQuery error %d\n", GetLastError());
+    ok(info.BaseAddress == ptr, "got %p != expected %p\n", info.BaseAddress, ptr);
+    ok(info.RegionSize == MAPPING_SIZE, "got %#lx != expected %#x\n", info.RegionSize, MAPPING_SIZE);
+    ok(info.Protect == PAGE_READWRITE, "got %#x != expected PAGE_READWRITE\n", info.Protect);
+    ok(info.AllocationBase == ptr, "%p != %p\n", info.AllocationBase, ptr);
+    ok(info.AllocationProtect == PAGE_READWRITE, "%#x != PAGE_READWRITE\n", info.AllocationProtect);
+    ok(info.State == MEM_COMMIT, "%#x != MEM_COMMIT\n", info.State);
+    ok(info.Type == MEM_MAPPED, "%#x != MEM_MAPPED\n", info.Type);
+
+    SetLastError(0xdeadbeef);
+    map2 = OpenFileMappingA(FILE_MAP_READ, FALSE, name);
+    todo_wine
+    ok( map2 == 0, "OpenFileMappingA succeeded\n" );
+    todo_wine
+    ok( GetLastError() == ERROR_FILE_NOT_FOUND, "OpenFileMappingA set error %d\n", GetLastError() );
+    if (map2) CloseHandle(map2); /* FIXME: remove once Wine is fixed */
+    SetLastError(0xdeadbeef);
+    mapping = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, MAPPING_SIZE, name);
+    ok( mapping != 0, "CreateFileMappingA failed\n" );
+    todo_wine
+    ok( GetLastError() == ERROR_SUCCESS, "CreateFileMappingA set error %d\n", GetLastError() );
+    SetLastError(0xdeadbeef);
+    ret = CloseHandle(mapping);
+    ok(ret, "CloseHandle error %d\n", GetLastError());
+
+    ret = IsBadReadPtr(ptr, MAPPING_SIZE);
+    ok( !ret, "memory is not accessible\n" );
+
+    ret = VirtualQuery(ptr, &info, sizeof(info));
+    ok(ret, "VirtualQuery error %d\n", GetLastError());
+    ok(info.BaseAddress == ptr, "got %p != expected %p\n", info.BaseAddress, ptr);
+    ok(info.RegionSize == MAPPING_SIZE, "got %#lx != expected %#x\n", info.RegionSize, MAPPING_SIZE);
+    ok(info.Protect == PAGE_READWRITE, "got %#x != expected PAGE_READWRITE\n", info.Protect);
+    ok(info.AllocationBase == ptr, "%p != %p\n", info.AllocationBase, ptr);
+    ok(info.AllocationProtect == PAGE_READWRITE, "%#x != PAGE_READWRITE\n", info.AllocationProtect);
+    ok(info.State == MEM_COMMIT, "%#x != MEM_COMMIT\n", info.State);
+    ok(info.Type == MEM_MAPPED, "%#x != MEM_MAPPED\n", info.Type);
+
+    SetLastError(0xdeadbeef);
+    ret = UnmapViewOfFile(ptr);
+    ok( ret, "UnmapViewOfFile failed with error %d\n", GetLastError() );
+
+    ret = IsBadReadPtr(ptr, MAPPING_SIZE);
+    ok( ret, "memory is accessible\n" );
+
+    ret = VirtualQuery(ptr, &info, sizeof(info));
+    ok(ret, "VirtualQuery error %d\n", GetLastError());
+    ok(info.BaseAddress == ptr, "got %p != expected %p\n", info.BaseAddress, ptr);
+    ok(info.Protect == PAGE_NOACCESS, "got %#x != expected PAGE_NOACCESS\n", info.Protect);
+    ok(info.AllocationBase == NULL, "%p != NULL\n", info.AllocationBase);
+    ok(info.AllocationProtect == 0, "%#x != 0\n", info.AllocationProtect);
+    ok(info.State == MEM_FREE, "%#x != MEM_FREE\n", info.State);
+    ok(info.Type == 0, "%#x != 0\n", info.Type);
+
+    SetLastError(0xdeadbeef);
+    file = CreateFileA(testfile, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
+    ok( file != INVALID_HANDLE_VALUE, "CreateFile error %u\n", GetLastError() );
+    SetFilePointer(file, 4096, NULL, FILE_BEGIN);
+    SetEndOfFile(file);
+
+    SetLastError(0xdeadbeef);
+    mapping = CreateFileMappingA(file, NULL, PAGE_READWRITE, 0, MAPPING_SIZE, name);
+    ok( mapping != 0, "CreateFileMappingA failed with error %d\n", GetLastError() );
+    SetLastError(0xdeadbeef);
+    ptr = MapViewOfFile(mapping, FILE_MAP_WRITE, 0, 0, 0);
+    ok( ptr != NULL, "MapViewOfFile failed with error %d\n", GetLastError() );
+    SetLastError(0xdeadbeef);
+    map2 = OpenFileMappingA(FILE_MAP_READ, FALSE, name);
+    ok( map2 != 0, "OpenFileMappingA failed with error %d\n", GetLastError() );
+    SetLastError(0xdeadbeef);
+    ret = CloseHandle(map2);
+    ok(ret, "CloseHandle error %d\n", GetLastError());
+    SetLastError(0xdeadbeef);
+    ret = CloseHandle(mapping);
+    ok(ret, "CloseHandle error %d\n", GetLastError());
+
+    ret = IsBadReadPtr(ptr, MAPPING_SIZE);
+    ok( !ret, "memory is not accessible\n" );
+
+    ret = VirtualQuery(ptr, &info, sizeof(info));
+    ok(ret, "VirtualQuery error %d\n", GetLastError());
+    ok(info.BaseAddress == ptr, "got %p != expected %p\n", info.BaseAddress, ptr);
+    ok(info.RegionSize == MAPPING_SIZE, "got %#lx != expected %#x\n", info.RegionSize, MAPPING_SIZE);
+    ok(info.Protect == PAGE_READWRITE, "got %#x != expected PAGE_READWRITE\n", info.Protect);
+    ok(info.AllocationBase == ptr, "%p != %p\n", info.AllocationBase, ptr);
+    ok(info.AllocationProtect == PAGE_READWRITE, "%#x != PAGE_READWRITE\n", info.AllocationProtect);
+    ok(info.State == MEM_COMMIT, "%#x != MEM_COMMIT\n", info.State);
+    ok(info.Type == MEM_MAPPED, "%#x != MEM_MAPPED\n", info.Type);
+
+    SetLastError(0xdeadbeef);
+    map2 = OpenFileMappingA(FILE_MAP_READ, FALSE, name);
+    todo_wine
+    ok( map2 == 0, "OpenFileMappingA succeeded\n" );
+    todo_wine
+    ok( GetLastError() == ERROR_FILE_NOT_FOUND, "OpenFileMappingA set error %d\n", GetLastError() );
+    CloseHandle(map2);
+    SetLastError(0xdeadbeef);
+    mapping = CreateFileMappingA(file, NULL, PAGE_READWRITE, 0, MAPPING_SIZE, name);
+    ok( mapping != 0, "CreateFileMappingA failed\n" );
+    todo_wine
+    ok( GetLastError() == ERROR_SUCCESS, "CreateFileMappingA set error %d\n", GetLastError() );
+    SetLastError(0xdeadbeef);
+    ret = CloseHandle(mapping);
+    ok(ret, "CloseHandle error %d\n", GetLastError());
+
+    ret = IsBadReadPtr(ptr, MAPPING_SIZE);
+    ok( !ret, "memory is not accessible\n" );
+
+    ret = VirtualQuery(ptr, &info, sizeof(info));
+    ok(ret, "VirtualQuery error %d\n", GetLastError());
+    ok(info.BaseAddress == ptr, "got %p != expected %p\n", info.BaseAddress, ptr);
+    ok(info.RegionSize == MAPPING_SIZE, "got %#lx != expected %#x\n", info.RegionSize, MAPPING_SIZE);
+    ok(info.Protect == PAGE_READWRITE, "got %#x != expected PAGE_READWRITE\n", info.Protect);
+    ok(info.AllocationBase == ptr, "%p != %p\n", info.AllocationBase, ptr);
+    ok(info.AllocationProtect == PAGE_READWRITE, "%#x != PAGE_READWRITE\n", info.AllocationProtect);
+    ok(info.State == MEM_COMMIT, "%#x != MEM_COMMIT\n", info.State);
+    ok(info.Type == MEM_MAPPED, "%#x != MEM_MAPPED\n", info.Type);
+
+    SetLastError(0xdeadbeef);
+    ret = UnmapViewOfFile(ptr);
+    ok( ret, "UnmapViewOfFile failed with error %d\n", GetLastError() );
+
+    ret = IsBadReadPtr(ptr, MAPPING_SIZE);
+    ok( ret, "memory is accessible\n" );
+
+    ret = VirtualQuery(ptr, &info, sizeof(info));
+    ok(ret, "VirtualQuery error %d\n", GetLastError());
+    ok(info.BaseAddress == ptr, "got %p != expected %p\n", info.BaseAddress, ptr);
+    ok(info.Protect == PAGE_NOACCESS, "got %#x != expected PAGE_NOACCESS\n", info.Protect);
+    ok(info.AllocationBase == NULL, "%p != NULL\n", info.AllocationBase);
+    ok(info.AllocationProtect == 0, "%#x != 0\n", info.AllocationProtect);
+    ok(info.State == MEM_FREE, "%#x != MEM_FREE\n", info.State);
+    ok(info.Type == 0, "%#x != 0\n", info.Type);
+
+    CloseHandle(file);
+    DeleteFileA(testfile);
+}
 
 static void test_NtMapViewOfSection(void)
 {
@@ -804,8 +965,6 @@ static void test_NtMapViewOfSection(void)
     SIZE_T size, result;
     LARGE_INTEGER offset;
 
-    pNtMapViewOfSection = (void *)GetProcAddress( GetModuleHandle("ntdll.dll"), "NtMapViewOfSection" );
-    pNtUnmapViewOfSection = (void *)GetProcAddress( GetModuleHandle("ntdll.dll"), "NtUnmapViewOfSection" );
     if (!pNtMapViewOfSection || !pNtUnmapViewOfSection)
     {
         win_skip( "NtMapViewOfSection not available\n" );
@@ -1392,6 +1551,851 @@ static void test_write_watch(void)
     VirtualFree( base, 0, MEM_FREE );
 }
 
+static void test_VirtualProtect(void)
+{
+    static const struct test_data
+    {
+        DWORD prot_set, prot_get;
+    } td[] =
+    {
+        { 0, 0 }, /* 0x00 */
+        { PAGE_NOACCESS, PAGE_NOACCESS }, /* 0x01 */
+        { PAGE_READONLY, PAGE_READONLY }, /* 0x02 */
+        { PAGE_READONLY | PAGE_NOACCESS, 0 }, /* 0x03 */
+        { PAGE_READWRITE, PAGE_READWRITE }, /* 0x04 */
+        { PAGE_READWRITE | PAGE_NOACCESS, 0 }, /* 0x05 */
+        { PAGE_READWRITE | PAGE_READONLY, 0 }, /* 0x06 */
+        { PAGE_READWRITE | PAGE_READONLY | PAGE_NOACCESS, 0 }, /* 0x07 */
+        { PAGE_WRITECOPY, 0 }, /* 0x08 */
+        { PAGE_WRITECOPY | PAGE_NOACCESS, 0 }, /* 0x09 */
+        { PAGE_WRITECOPY | PAGE_READONLY, 0 }, /* 0x0a */
+        { PAGE_WRITECOPY | PAGE_NOACCESS | PAGE_READONLY, 0 }, /* 0x0b */
+        { PAGE_WRITECOPY | PAGE_READWRITE, 0 }, /* 0x0c */
+        { PAGE_WRITECOPY | PAGE_READWRITE | PAGE_NOACCESS, 0 }, /* 0x0d */
+        { PAGE_WRITECOPY | PAGE_READWRITE | PAGE_READONLY, 0 }, /* 0x0e */
+        { PAGE_WRITECOPY | PAGE_READWRITE | PAGE_READONLY | PAGE_NOACCESS, 0 }, /* 0x0f */
+
+        { PAGE_EXECUTE, PAGE_EXECUTE }, /* 0x10 */
+        { PAGE_EXECUTE_READ, PAGE_EXECUTE_READ }, /* 0x20 */
+        { PAGE_EXECUTE_READ | PAGE_EXECUTE, 0 }, /* 0x30 */
+        { PAGE_EXECUTE_READWRITE, PAGE_EXECUTE_READWRITE }, /* 0x40 */
+        { PAGE_EXECUTE_READWRITE | PAGE_EXECUTE, 0 }, /* 0x50 */
+        { PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_READ, 0 }, /* 0x60 */
+        { PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_READ | PAGE_EXECUTE, 0 }, /* 0x70 */
+        { PAGE_EXECUTE_WRITECOPY, 0 }, /* 0x80 */
+        { PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE, 0 }, /* 0x90 */
+        { PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE_READ, 0 }, /* 0xa0 */
+        { PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE_READ | PAGE_EXECUTE, 0 }, /* 0xb0 */
+        { PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE_READWRITE, 0 }, /* 0xc0 */
+        { PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE, 0 }, /* 0xd0 */
+        { PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_READ, 0 }, /* 0xe0 */
+        { PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_READ | PAGE_EXECUTE, 0 } /* 0xf0 */
+    };
+    char *base;
+    DWORD ret, old_prot, rw_prot, exec_prot, i, j;
+    MEMORY_BASIC_INFORMATION info;
+    SYSTEM_INFO si;
+
+    GetSystemInfo(&si);
+    trace("system page size %#x\n", si.dwPageSize);
+
+    SetLastError(0xdeadbeef);
+    base = VirtualAlloc(0, si.dwPageSize, MEM_RESERVE | MEM_COMMIT, PAGE_NOACCESS);
+    ok(base != NULL, "VirtualAlloc failed %d\n", GetLastError());
+
+    for (i = 0; i < sizeof(td)/sizeof(td[0]); i++)
+    {
+        SetLastError(0xdeadbeef);
+        ret = VirtualQuery(base, &info, sizeof(info));
+        ok(ret, "VirtualQuery failed %d\n", GetLastError());
+        ok(info.BaseAddress == base, "%d: got %p != expected %p\n", i, info.BaseAddress, base);
+        ok(info.RegionSize == si.dwPageSize, "%d: got %#lx != expected %#x\n", i, info.RegionSize, si.dwPageSize);
+        ok(info.Protect == PAGE_NOACCESS, "%d: got %#x != expected PAGE_NOACCESS\n", i, info.Protect);
+        ok(info.AllocationBase == base, "%d: %p != %p\n", i, info.AllocationBase, base);
+        ok(info.AllocationProtect == PAGE_NOACCESS, "%d: %#x != PAGE_NOACCESS\n", i, info.AllocationProtect);
+        ok(info.State == MEM_COMMIT, "%d: %#x != MEM_COMMIT\n", i, info.State);
+        ok(info.Type == MEM_PRIVATE, "%d: %#x != MEM_PRIVATE\n", i, info.Type);
+
+        old_prot = 0xdeadbeef;
+        SetLastError(0xdeadbeef);
+        ret = VirtualProtect(base, si.dwPageSize, td[i].prot_set, &old_prot);
+        if (td[i].prot_get)
+        {
+            ok(ret, "%d: VirtualProtect error %d\n", i, GetLastError());
+            ok(old_prot == PAGE_NOACCESS, "%d: got %#x != expected PAGE_NOACCESS\n", i, old_prot);
+
+            SetLastError(0xdeadbeef);
+            ret = VirtualQuery(base, &info, sizeof(info));
+            ok(ret, "VirtualQuery failed %d\n", GetLastError());
+            ok(info.BaseAddress == base, "%d: got %p != expected %p\n", i, info.BaseAddress, base);
+            ok(info.RegionSize == si.dwPageSize, "%d: got %#lx != expected %#x\n", i, info.RegionSize, si.dwPageSize);
+            ok(info.Protect == td[i].prot_get, "%d: got %#x != expected %#x\n", i, info.Protect, td[i].prot_get);
+            ok(info.AllocationBase == base, "%d: %p != %p\n", i, info.AllocationBase, base);
+            ok(info.AllocationProtect == PAGE_NOACCESS, "%d: %#x != PAGE_NOACCESS\n", i, info.AllocationProtect);
+            ok(info.State == MEM_COMMIT, "%d: %#x != MEM_COMMIT\n", i, info.State);
+            ok(info.Type == MEM_PRIVATE, "%d: %#x != MEM_PRIVATE\n", i, info.Type);
+        }
+        else
+        {
+            ok(!ret, "%d: VirtualProtect should fail\n", i);
+            ok(GetLastError() == ERROR_INVALID_PARAMETER, "%d: expected ERROR_INVALID_PARAMETER, got %d\n", i, GetLastError());
+        }
+
+        old_prot = 0xdeadbeef;
+        SetLastError(0xdeadbeef);
+        ret = VirtualProtect(base, si.dwPageSize, PAGE_NOACCESS, &old_prot);
+        ok(ret, "%d: VirtualProtect error %d\n", i, GetLastError());
+        if (td[i].prot_get)
+            ok(old_prot == td[i].prot_get, "%d: got %#x != expected %#x\n", i, old_prot, td[i].prot_get);
+        else
+            ok(old_prot == PAGE_NOACCESS, "%d: got %#x != expected PAGE_NOACCESS\n", i, old_prot);
+    }
+
+    exec_prot = 0;
+
+    for (i = 0; i <= 4; i++)
+    {
+        rw_prot = 0;
+
+        for (j = 0; j <= 4; j++)
+        {
+            DWORD prot = exec_prot | rw_prot;
+
+            SetLastError(0xdeadbeef);
+            ret = VirtualProtect(base, si.dwPageSize, prot, &old_prot);
+            if ((rw_prot && exec_prot) || (!rw_prot && !exec_prot))
+            {
+                ok(!ret, "VirtualProtect(%02x) should fail\n", prot);
+                ok(GetLastError() == ERROR_INVALID_PARAMETER, "expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+            }
+            else
+            {
+                if (prot & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY))
+                {
+                    ok(!ret, "VirtualProtect(%02x) should fail\n", prot);
+                    ok(GetLastError() == ERROR_INVALID_PARAMETER, "expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+                }
+                else
+                    ok(ret, "VirtualProtect(%02x) error %d\n", prot, GetLastError());
+            }
+
+            rw_prot = 1 << j;
+        }
+
+        exec_prot = 1 << (i + 4);
+    }
+
+    VirtualFree(base, 0, MEM_FREE);
+}
+
+static BOOL is_mem_writable(DWORD prot)
+{
+    switch (prot & 0xff)
+    {
+        case PAGE_READWRITE:
+        case PAGE_WRITECOPY:
+        case PAGE_EXECUTE_READWRITE:
+        case PAGE_EXECUTE_WRITECOPY:
+            return TRUE;
+
+        default:
+            return FALSE;
+    }
+}
+
+static void test_VirtualAlloc_protection(void)
+{
+    static const struct test_data
+    {
+        DWORD prot;
+        BOOL success;
+    } td[] =
+    {
+        { 0, FALSE }, /* 0x00 */
+        { PAGE_NOACCESS, TRUE }, /* 0x01 */
+        { PAGE_READONLY, TRUE }, /* 0x02 */
+        { PAGE_READONLY | PAGE_NOACCESS, FALSE }, /* 0x03 */
+        { PAGE_READWRITE, TRUE }, /* 0x04 */
+        { PAGE_READWRITE | PAGE_NOACCESS, FALSE }, /* 0x05 */
+        { PAGE_READWRITE | PAGE_READONLY, FALSE }, /* 0x06 */
+        { PAGE_READWRITE | PAGE_READONLY | PAGE_NOACCESS, FALSE }, /* 0x07 */
+        { PAGE_WRITECOPY, FALSE }, /* 0x08 */
+        { PAGE_WRITECOPY | PAGE_NOACCESS, FALSE }, /* 0x09 */
+        { PAGE_WRITECOPY | PAGE_READONLY, FALSE }, /* 0x0a */
+        { PAGE_WRITECOPY | PAGE_NOACCESS | PAGE_READONLY, FALSE }, /* 0x0b */
+        { PAGE_WRITECOPY | PAGE_READWRITE, FALSE }, /* 0x0c */
+        { PAGE_WRITECOPY | PAGE_READWRITE | PAGE_NOACCESS, FALSE }, /* 0x0d */
+        { PAGE_WRITECOPY | PAGE_READWRITE | PAGE_READONLY, FALSE }, /* 0x0e */
+        { PAGE_WRITECOPY | PAGE_READWRITE | PAGE_READONLY | PAGE_NOACCESS, FALSE }, /* 0x0f */
+
+        { PAGE_EXECUTE, TRUE }, /* 0x10 */
+        { PAGE_EXECUTE_READ, TRUE }, /* 0x20 */
+        { PAGE_EXECUTE_READ | PAGE_EXECUTE, FALSE }, /* 0x30 */
+        { PAGE_EXECUTE_READWRITE, TRUE }, /* 0x40 */
+        { PAGE_EXECUTE_READWRITE | PAGE_EXECUTE, FALSE }, /* 0x50 */
+        { PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_READ, FALSE }, /* 0x60 */
+        { PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_READ | PAGE_EXECUTE, FALSE }, /* 0x70 */
+        { PAGE_EXECUTE_WRITECOPY, FALSE }, /* 0x80 */
+        { PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE, FALSE }, /* 0x90 */
+        { PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE_READ, FALSE }, /* 0xa0 */
+        { PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE_READ | PAGE_EXECUTE, FALSE }, /* 0xb0 */
+        { PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE_READWRITE, FALSE }, /* 0xc0 */
+        { PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE, FALSE }, /* 0xd0 */
+        { PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_READ, FALSE }, /* 0xe0 */
+        { PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_READ | PAGE_EXECUTE, FALSE } /* 0xf0 */
+    };
+    char *base;
+    DWORD ret, i;
+    MEMORY_BASIC_INFORMATION info;
+    SYSTEM_INFO si;
+
+    GetSystemInfo(&si);
+    trace("system page size %#x\n", si.dwPageSize);
+
+    for (i = 0; i < sizeof(td)/sizeof(td[0]); i++)
+    {
+        SetLastError(0xdeadbeef);
+        base = VirtualAlloc(0, si.dwPageSize, MEM_COMMIT, td[i].prot);
+
+        if (td[i].success)
+        {
+            ok(base != NULL, "%d: VirtualAlloc failed %d\n", i, GetLastError());
+
+            SetLastError(0xdeadbeef);
+            ret = VirtualQuery(base, &info, sizeof(info));
+            ok(ret, "VirtualQuery failed %d\n", GetLastError());
+            ok(info.BaseAddress == base, "%d: got %p != expected %p\n", i, info.BaseAddress, base);
+            ok(info.RegionSize == si.dwPageSize, "%d: got %#lx != expected %#x\n", i, info.RegionSize, si.dwPageSize);
+            ok(info.Protect == td[i].prot, "%d: got %#x != expected %#x\n", i, info.Protect, td[i].prot);
+            ok(info.AllocationBase == base, "%d: %p != %p\n", i, info.AllocationBase, base);
+            ok(info.AllocationProtect == td[i].prot, "%d: %#x != %#x\n", i, info.AllocationProtect, td[i].prot);
+            ok(info.State == MEM_COMMIT, "%d: %#x != MEM_COMMIT\n", i, info.State);
+            ok(info.Type == MEM_PRIVATE, "%d: %#x != MEM_PRIVATE\n", i, info.Type);
+
+            if (is_mem_writable(info.Protect))
+            {
+                base[0] = 0xfe;
+
+                SetLastError(0xdeadbeef);
+                ret = VirtualQuery(base, &info, sizeof(info));
+                ok(ret, "VirtualQuery failed %d\n", GetLastError());
+                ok(info.Protect == td[i].prot, "%d: got %#x != expected %#x\n", i, info.Protect, td[i].prot);
+            }
+
+            VirtualFree(base, 0, MEM_FREE);
+        }
+        else
+        {
+            ok(!base, "%d: VirtualAlloc should fail\n", i);
+            ok(GetLastError() == ERROR_INVALID_PARAMETER, "%d: expected ERROR_INVALID_PARAMETER, got %d\n", i, GetLastError());
+        }
+    }
+}
+
+static void test_CreateFileMapping_protection(void)
+{
+    static const struct test_data
+    {
+        DWORD prot;
+        BOOL success;
+        DWORD prot_after_write;
+    } td[] =
+    {
+        { 0, FALSE, 0 }, /* 0x00 */
+        { PAGE_NOACCESS, FALSE, PAGE_NOACCESS }, /* 0x01 */
+        { PAGE_READONLY, TRUE, PAGE_READONLY }, /* 0x02 */
+        { PAGE_READONLY | PAGE_NOACCESS, FALSE, PAGE_NOACCESS }, /* 0x03 */
+        { PAGE_READWRITE, TRUE, PAGE_READWRITE }, /* 0x04 */
+        { PAGE_READWRITE | PAGE_NOACCESS, FALSE, PAGE_NOACCESS }, /* 0x05 */
+        { PAGE_READWRITE | PAGE_READONLY, FALSE, PAGE_NOACCESS }, /* 0x06 */
+        { PAGE_READWRITE | PAGE_READONLY | PAGE_NOACCESS, FALSE, PAGE_NOACCESS }, /* 0x07 */
+        { PAGE_WRITECOPY, TRUE, PAGE_READWRITE }, /* 0x08 */
+        { PAGE_WRITECOPY | PAGE_NOACCESS, FALSE, PAGE_NOACCESS }, /* 0x09 */
+        { PAGE_WRITECOPY | PAGE_READONLY, FALSE, PAGE_NOACCESS }, /* 0x0a */
+        { PAGE_WRITECOPY | PAGE_NOACCESS | PAGE_READONLY, FALSE, PAGE_NOACCESS }, /* 0x0b */
+        { PAGE_WRITECOPY | PAGE_READWRITE, FALSE, PAGE_NOACCESS }, /* 0x0c */
+        { PAGE_WRITECOPY | PAGE_READWRITE | PAGE_NOACCESS, FALSE, PAGE_NOACCESS }, /* 0x0d */
+        { PAGE_WRITECOPY | PAGE_READWRITE | PAGE_READONLY, FALSE, PAGE_NOACCESS }, /* 0x0e */
+        { PAGE_WRITECOPY | PAGE_READWRITE | PAGE_READONLY | PAGE_NOACCESS, FALSE, PAGE_NOACCESS }, /* 0x0f */
+
+        { PAGE_EXECUTE, FALSE, PAGE_EXECUTE }, /* 0x10 */
+        { PAGE_EXECUTE_READ, TRUE, PAGE_EXECUTE_READ }, /* 0x20 */
+        { PAGE_EXECUTE_READ | PAGE_EXECUTE, FALSE, PAGE_EXECUTE_READ }, /* 0x30 */
+        { PAGE_EXECUTE_READWRITE, TRUE, PAGE_EXECUTE_READWRITE }, /* 0x40 */
+        { PAGE_EXECUTE_READWRITE | PAGE_EXECUTE, FALSE, PAGE_NOACCESS }, /* 0x50 */
+        { PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_READ, FALSE, PAGE_NOACCESS }, /* 0x60 */
+        { PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_READ | PAGE_EXECUTE, FALSE, PAGE_NOACCESS }, /* 0x70 */
+        { PAGE_EXECUTE_WRITECOPY, TRUE, PAGE_EXECUTE_READWRITE }, /* 0x80 */
+        { PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE, FALSE, PAGE_NOACCESS }, /* 0x90 */
+        { PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE_READ, FALSE, PAGE_NOACCESS }, /* 0xa0 */
+        { PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE_READ | PAGE_EXECUTE, FALSE, PAGE_NOACCESS }, /* 0xb0 */
+        { PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE_READWRITE, FALSE, PAGE_NOACCESS }, /* 0xc0 */
+        { PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE, FALSE, PAGE_NOACCESS }, /* 0xd0 */
+        { PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_READ, FALSE, PAGE_NOACCESS }, /* 0xe0 */
+        { PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_READ | PAGE_EXECUTE, FALSE, PAGE_NOACCESS } /* 0xf0 */
+    };
+    char *base;
+    DWORD ret, i, alloc_prot, prot, old_prot;
+    MEMORY_BASIC_INFORMATION info;
+    SYSTEM_INFO si;
+    char temp_path[MAX_PATH];
+    char file_name[MAX_PATH];
+    HANDLE hfile, hmap;
+    BOOL page_exec_supported = TRUE;
+
+    GetSystemInfo(&si);
+    trace("system page size %#x\n", si.dwPageSize);
+
+    GetTempPath(MAX_PATH, temp_path);
+    GetTempFileName(temp_path, "map", 0, file_name);
+
+    SetLastError(0xdeadbeef);
+    hfile = CreateFile(file_name, GENERIC_READ|GENERIC_WRITE|GENERIC_EXECUTE, 0, NULL, CREATE_ALWAYS, 0, 0);
+    ok(hfile != INVALID_HANDLE_VALUE, "CreateFile(%s) error %d\n", file_name, GetLastError());
+    SetFilePointer(hfile, si.dwPageSize, NULL, FILE_BEGIN);
+    SetEndOfFile(hfile);
+
+    for (i = 0; i < sizeof(td)/sizeof(td[0]); i++)
+    {
+        SetLastError(0xdeadbeef);
+        hmap = CreateFileMapping(hfile, NULL, td[i].prot | SEC_COMMIT, 0, si.dwPageSize, NULL);
+
+        if (td[i].success)
+        {
+            if (!hmap)
+            {
+                trace("%d: CreateFileMapping(%04x) failed: %d\n", i, td[i].prot, GetLastError());
+                /* NT4 and win2k don't support EXEC on file mappings */
+                if (td[i].prot == PAGE_EXECUTE_READ || td[i].prot == PAGE_EXECUTE_READWRITE)
+                {
+                    page_exec_supported = FALSE;
+                    ok(broken(!hmap), "%d: CreateFileMapping doesn't support PAGE_EXECUTE\n", i);
+                    continue;
+                }
+                /* Vista+ supports PAGE_EXECUTE_WRITECOPY, earlier versions don't */
+                if (td[i].prot == PAGE_EXECUTE_WRITECOPY)
+                {
+                    page_exec_supported = FALSE;
+                    ok(broken(!hmap), "%d: CreateFileMapping doesn't support PAGE_EXECUTE_WRITECOPY\n", i);
+                    continue;
+                }
+            }
+            ok(hmap != 0, "%d: CreateFileMapping(%04x) error %d\n", i, td[i].prot, GetLastError());
+
+            base = MapViewOfFile(hmap, FILE_MAP_READ, 0, 0, 0);
+            ok(base != NULL, "%d: MapViewOfFile failed %d\n", i, GetLastError());
+
+            SetLastError(0xdeadbeef);
+            ret = VirtualQuery(base, &info, sizeof(info));
+            ok(ret, "VirtualQuery failed %d\n", GetLastError());
+            ok(info.BaseAddress == base, "%d: got %p != expected %p\n", i, info.BaseAddress, base);
+            ok(info.RegionSize == si.dwPageSize, "%d: got %#lx != expected %#x\n", i, info.RegionSize, si.dwPageSize);
+            ok(info.Protect == PAGE_READONLY, "%d: got %#x != expected PAGE_READONLY\n", i, info.Protect);
+            ok(info.AllocationBase == base, "%d: %p != %p\n", i, info.AllocationBase, base);
+            ok(info.AllocationProtect == PAGE_READONLY, "%d: %#x != PAGE_READONLY\n", i, info.AllocationProtect);
+            ok(info.State == MEM_COMMIT, "%d: %#x != MEM_COMMIT\n", i, info.State);
+            ok(info.Type == MEM_MAPPED, "%d: %#x != MEM_MAPPED\n", i, info.Type);
+
+            if (is_mem_writable(info.Protect))
+            {
+                base[0] = 0xfe;
+
+                SetLastError(0xdeadbeef);
+                ret = VirtualQuery(base, &info, sizeof(info));
+                ok(ret, "VirtualQuery failed %d\n", GetLastError());
+                ok(info.Protect == td[i].prot, "%d: got %#x != expected %#x\n", i, info.Protect, td[i].prot);
+            }
+
+            UnmapViewOfFile(base);
+            CloseHandle(hmap);
+        }
+        else
+        {
+            ok(!hmap, "%d: CreateFileMapping should fail\n", i);
+            ok(GetLastError() == ERROR_INVALID_PARAMETER, "%d: expected ERROR_INVALID_PARAMETER, got %d\n", i, GetLastError());
+        }
+    }
+
+    if (page_exec_supported) alloc_prot = PAGE_EXECUTE_READWRITE;
+    else alloc_prot = PAGE_READWRITE;
+    SetLastError(0xdeadbeef);
+    hmap = CreateFileMapping(hfile, NULL, alloc_prot, 0, si.dwPageSize, NULL);
+    ok(hmap != 0, "%d: CreateFileMapping error %d\n", i, GetLastError());
+
+    SetLastError(0xdeadbeef);
+    base = MapViewOfFile(hmap, FILE_MAP_READ | FILE_MAP_WRITE | (page_exec_supported ? FILE_MAP_EXECUTE : 0), 0, 0, 0);
+    ok(base != NULL, "MapViewOfFile failed %d\n", GetLastError());
+
+    old_prot = 0xdeadbeef;
+    SetLastError(0xdeadbeef);
+    ret = VirtualProtect(base, si.dwPageSize, PAGE_NOACCESS, &old_prot);
+    ok(ret, "VirtualProtect error %d\n", GetLastError());
+    ok(old_prot == alloc_prot, "got %#x != expected %#x\n", old_prot, alloc_prot);
+
+    for (i = 0; i < sizeof(td)/sizeof(td[0]); i++)
+    {
+        SetLastError(0xdeadbeef);
+        ret = VirtualQuery(base, &info, sizeof(info));
+        ok(ret, "VirtualQuery failed %d\n", GetLastError());
+        ok(info.BaseAddress == base, "%d: got %p != expected %p\n", i, info.BaseAddress, base);
+        ok(info.RegionSize == si.dwPageSize, "%d: got %#lx != expected %#x\n", i, info.RegionSize, si.dwPageSize);
+        ok(info.Protect == PAGE_NOACCESS, "%d: got %#x != expected PAGE_NOACCESS\n", i, info.Protect);
+        ok(info.AllocationBase == base, "%d: %p != %p\n", i, info.AllocationBase, base);
+        ok(info.AllocationProtect == alloc_prot, "%d: %#x != %#x\n", i, info.AllocationProtect, alloc_prot);
+        ok(info.State == MEM_COMMIT, "%d: %#x != MEM_COMMIT\n", i, info.State);
+        ok(info.Type == MEM_MAPPED, "%d: %#x != MEM_MAPPED\n", i, info.Type);
+
+        old_prot = 0xdeadbeef;
+        SetLastError(0xdeadbeef);
+        ret = VirtualProtect(base, si.dwPageSize, td[i].prot, &old_prot);
+        if (td[i].success || td[i].prot == PAGE_NOACCESS || td[i].prot == PAGE_EXECUTE)
+        {
+            if (!ret)
+            {
+                /* win2k and XP don't support EXEC on file mappings */
+                if (td[i].prot == PAGE_EXECUTE)
+                {
+                    ok(broken(!ret), "%d: VirtualProtect doesn't support PAGE_EXECUTE\n", i);
+                    continue;
+                }
+                /* NT4 and win2k don't support EXEC on file mappings */
+                if (td[i].prot == PAGE_EXECUTE_READ || td[i].prot == PAGE_EXECUTE_READWRITE)
+                {
+                    ok(broken(!ret), "%d: VirtualProtect doesn't support PAGE_EXECUTE\n", i);
+                    continue;
+                }
+                /* Vista+ supports PAGE_EXECUTE_WRITECOPY, earlier versions don't */
+                if (td[i].prot == PAGE_EXECUTE_WRITECOPY)
+                {
+                    ok(broken(!ret), "%d: VirtualProtect doesn't support PAGE_EXECUTE_WRITECOPY\n", i);
+                    continue;
+                }
+            }
+
+            ok(ret, "%d: VirtualProtect error %d\n", i, GetLastError());
+            ok(old_prot == PAGE_NOACCESS, "%d: got %#x != expected PAGE_NOACCESS\n", i, old_prot);
+
+            prot = td[i].prot;
+            /* looks strange but Windows doesn't do this for PAGE_WRITECOPY */
+            if (prot == PAGE_EXECUTE_WRITECOPY) prot = PAGE_EXECUTE_READWRITE;
+
+            SetLastError(0xdeadbeef);
+            ret = VirtualQuery(base, &info, sizeof(info));
+            ok(ret, "VirtualQuery failed %d\n", GetLastError());
+            ok(info.BaseAddress == base, "%d: got %p != expected %p\n", i, info.BaseAddress, base);
+            ok(info.RegionSize == si.dwPageSize, "%d: got %#lx != expected %#x\n", i, info.RegionSize, si.dwPageSize);
+            /* FIXME: remove the condition below once Wine is fixed */
+            if (td[i].prot == PAGE_EXECUTE_WRITECOPY)
+                todo_wine ok(info.Protect == prot, "%d: got %#x != expected %#x\n", i, info.Protect, prot);
+            else
+                ok(info.Protect == prot, "%d: got %#x != expected %#x\n", i, info.Protect, prot);
+            ok(info.AllocationBase == base, "%d: %p != %p\n", i, info.AllocationBase, base);
+            ok(info.AllocationProtect == alloc_prot, "%d: %#x != %#x\n", i, info.AllocationProtect, alloc_prot);
+            ok(info.State == MEM_COMMIT, "%d: %#x != MEM_COMMIT\n", i, info.State);
+            ok(info.Type == MEM_MAPPED, "%d: %#x != MEM_MAPPED\n", i, info.Type);
+
+            if (is_mem_writable(info.Protect))
+            {
+                base[0] = 0xfe;
+
+                SetLastError(0xdeadbeef);
+                ret = VirtualQuery(base, &info, sizeof(info));
+                ok(ret, "VirtualQuery failed %d\n", GetLastError());
+                /* FIXME: remove the condition below once Wine is fixed */
+                if (td[i].prot == PAGE_WRITECOPY || td[i].prot == PAGE_EXECUTE_WRITECOPY)
+                    todo_wine ok(info.Protect == td[i].prot_after_write, "%d: got %#x != expected %#x\n", i, info.Protect, td[i].prot_after_write);
+                else
+                    ok(info.Protect == td[i].prot_after_write, "%d: got %#x != expected %#x\n", i, info.Protect, td[i].prot_after_write);
+            }
+        }
+        else
+        {
+            ok(!ret, "%d: VirtualProtect should fail\n", i);
+            ok(GetLastError() == ERROR_INVALID_PARAMETER, "%d: expected ERROR_INVALID_PARAMETER, got %d\n", i, GetLastError());
+            continue;
+        }
+
+        old_prot = 0xdeadbeef;
+        SetLastError(0xdeadbeef);
+        ret = VirtualProtect(base, si.dwPageSize, PAGE_NOACCESS, &old_prot);
+        ok(ret, "%d: VirtualProtect error %d\n", i, GetLastError());
+        /* FIXME: remove the condition below once Wine is fixed */
+        if (td[i].prot == PAGE_WRITECOPY || td[i].prot == PAGE_EXECUTE_WRITECOPY)
+            todo_wine ok(old_prot == td[i].prot_after_write, "%d: got %#x != expected %#x\n", i, old_prot, td[i].prot_after_write);
+        else
+            ok(old_prot == td[i].prot_after_write, "%d: got %#x != expected %#x\n", i, old_prot, td[i].prot_after_write);
+    }
+
+    UnmapViewOfFile(base);
+    CloseHandle(hmap);
+
+    CloseHandle(hfile);
+    DeleteFile(file_name);
+}
+
+#define ACCESS_READ      0x01
+#define ACCESS_WRITE     0x02
+#define ACCESS_EXECUTE   0x04
+#define ACCESS_WRITECOPY 0x08
+
+static DWORD page_prot_to_access(DWORD prot)
+{
+    switch (prot)
+    {
+    case PAGE_READWRITE:
+        return ACCESS_READ | ACCESS_WRITE;
+
+    case PAGE_EXECUTE:
+    case PAGE_EXECUTE_READ:
+        return ACCESS_READ | ACCESS_EXECUTE;
+
+    case PAGE_EXECUTE_READWRITE:
+        return ACCESS_READ | ACCESS_WRITE | ACCESS_WRITECOPY | ACCESS_EXECUTE;
+
+    case PAGE_EXECUTE_WRITECOPY:
+        return ACCESS_READ | ACCESS_WRITECOPY | ACCESS_EXECUTE;
+
+    case PAGE_READONLY:
+        return ACCESS_READ;
+
+    case PAGE_WRITECOPY:
+        return ACCESS_READ;
+
+    default:
+        return 0;
+    }
+}
+
+static BOOL is_compatible_protection(DWORD map_prot, DWORD view_prot, DWORD prot)
+{
+    DWORD map_access, view_access, prot_access;
+
+    map_access = page_prot_to_access(map_prot);
+    view_access = page_prot_to_access(view_prot);
+    prot_access = page_prot_to_access(prot);
+
+    if (view_access == prot_access) return TRUE;
+    if (!view_access) return FALSE;
+
+    if ((view_access & prot_access) != prot_access) return FALSE;
+    if ((map_access & prot_access) == prot_access) return TRUE;
+
+    return FALSE;
+}
+
+static DWORD map_prot_to_access(DWORD prot)
+{
+    switch (prot)
+    {
+    case PAGE_READWRITE:
+    case PAGE_EXECUTE_READWRITE:
+        return SECTION_MAP_READ | SECTION_MAP_WRITE | SECTION_MAP_EXECUTE | SECTION_MAP_EXECUTE_EXPLICIT | SECTION_QUERY;
+    case PAGE_READONLY:
+    case PAGE_WRITECOPY:
+    case PAGE_EXECUTE:
+    case PAGE_EXECUTE_READ:
+    case PAGE_EXECUTE_WRITECOPY:
+        return SECTION_MAP_READ | SECTION_MAP_EXECUTE | SECTION_MAP_EXECUTE_EXPLICIT | SECTION_QUERY;
+    default:
+        return 0;
+    }
+}
+
+static BOOL is_compatible_access(DWORD map_prot, DWORD view_prot)
+{
+    DWORD access = map_prot_to_access(map_prot);
+    if (!view_prot) view_prot = SECTION_MAP_READ;
+    return (view_prot & access) == view_prot;
+}
+
+static void *map_view_of_file(HANDLE handle, DWORD access)
+{
+    NTSTATUS status;
+    LARGE_INTEGER offset;
+    SIZE_T count;
+    ULONG protect;
+    BOOL exec;
+    void *addr;
+
+    if (!pNtMapViewOfSection) return NULL;
+
+    count = 0;
+    offset.u.LowPart  = 0;
+    offset.u.HighPart = 0;
+
+    exec = access & FILE_MAP_EXECUTE;
+    access &= ~FILE_MAP_EXECUTE;
+
+    if (access == FILE_MAP_COPY)
+    {
+        if (exec)
+            protect = PAGE_EXECUTE_WRITECOPY;
+        else
+            protect = PAGE_WRITECOPY;
+    }
+    else if (access & FILE_MAP_WRITE)
+    {
+        if (exec)
+            protect = PAGE_EXECUTE_READWRITE;
+        else
+            protect = PAGE_READWRITE;
+    }
+    else if (access & FILE_MAP_READ)
+    {
+        if (exec)
+            protect = PAGE_EXECUTE_READ;
+        else
+            protect = PAGE_READONLY;
+    }
+    else protect = PAGE_NOACCESS;
+
+    addr = NULL;
+    status = pNtMapViewOfSection(handle, GetCurrentProcess(), &addr, 0, 0, &offset,
+                                 &count, 1 /* ViewShare */, 0, protect);
+    if (status)
+    {
+        /* for simplicity */
+        SetLastError(ERROR_ACCESS_DENIED);
+        addr = NULL;
+    }
+    return addr;
+}
+
+static void test_mapping(void)
+{
+    static const DWORD page_prot[] =
+    {
+        PAGE_NOACCESS, PAGE_READONLY, PAGE_READWRITE, PAGE_WRITECOPY,
+        PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_EXECUTE_WRITECOPY
+    };
+    static const struct
+    {
+        DWORD access, prot;
+    } view[] =
+    {
+        { 0, PAGE_NOACCESS }, /* 0x00 */
+        { FILE_MAP_COPY, PAGE_WRITECOPY }, /* 0x01 */
+        { FILE_MAP_WRITE, PAGE_READWRITE }, /* 0x02 */
+        { FILE_MAP_WRITE | FILE_MAP_COPY, PAGE_READWRITE }, /* 0x03 */
+        { FILE_MAP_READ, PAGE_READONLY }, /* 0x04 */
+        { FILE_MAP_READ | FILE_MAP_COPY, PAGE_READONLY }, /* 0x05 */
+        { FILE_MAP_READ | FILE_MAP_WRITE, PAGE_READWRITE }, /* 0x06 */
+        { FILE_MAP_READ | FILE_MAP_WRITE | FILE_MAP_COPY, PAGE_READWRITE }, /* 0x07 */
+        { SECTION_MAP_EXECUTE, PAGE_NOACCESS }, /* 0x08 */
+        { SECTION_MAP_EXECUTE | FILE_MAP_COPY, PAGE_NOACCESS }, /* 0x09 */
+        { SECTION_MAP_EXECUTE | FILE_MAP_WRITE, PAGE_READWRITE }, /* 0x0a */
+        { SECTION_MAP_EXECUTE | FILE_MAP_WRITE | FILE_MAP_COPY, PAGE_READWRITE }, /* 0x0b */
+        { SECTION_MAP_EXECUTE | FILE_MAP_READ, PAGE_READONLY }, /* 0x0c */
+        { SECTION_MAP_EXECUTE | FILE_MAP_READ | FILE_MAP_COPY, PAGE_READONLY }, /* 0x0d */
+        { SECTION_MAP_EXECUTE | FILE_MAP_READ | FILE_MAP_WRITE, PAGE_READWRITE }, /* 0x0e */
+        { SECTION_MAP_EXECUTE | FILE_MAP_READ | FILE_MAP_WRITE | FILE_MAP_COPY, PAGE_READWRITE }, /* 0x0f */
+        { FILE_MAP_EXECUTE, PAGE_NOACCESS }, /* 0x20 */
+        { FILE_MAP_EXECUTE | FILE_MAP_COPY, PAGE_EXECUTE_WRITECOPY }, /* 0x21 */
+        { FILE_MAP_EXECUTE | FILE_MAP_WRITE, PAGE_EXECUTE_READWRITE }, /* 0x22 */
+        { FILE_MAP_EXECUTE | FILE_MAP_WRITE | FILE_MAP_COPY, PAGE_EXECUTE_READWRITE }, /* 0x23 */
+        { FILE_MAP_EXECUTE | FILE_MAP_READ, PAGE_EXECUTE_READ }, /* 0x24 */
+        { FILE_MAP_EXECUTE | FILE_MAP_READ | FILE_MAP_COPY, PAGE_EXECUTE_READ }, /* 0x25 */
+        { FILE_MAP_EXECUTE | FILE_MAP_READ | FILE_MAP_WRITE, PAGE_EXECUTE_READWRITE }, /* 0x26 */
+        { FILE_MAP_EXECUTE | FILE_MAP_READ | FILE_MAP_WRITE | FILE_MAP_COPY, PAGE_EXECUTE_READWRITE }, /* 0x27 */
+        { FILE_MAP_EXECUTE | SECTION_MAP_EXECUTE, PAGE_NOACCESS }, /* 0x28 */
+        { FILE_MAP_EXECUTE | SECTION_MAP_EXECUTE | FILE_MAP_COPY, PAGE_NOACCESS }, /* 0x29 */
+        { FILE_MAP_EXECUTE | SECTION_MAP_EXECUTE | FILE_MAP_WRITE, PAGE_EXECUTE_READWRITE }, /* 0x2a */
+        { FILE_MAP_EXECUTE | SECTION_MAP_EXECUTE | FILE_MAP_WRITE | FILE_MAP_COPY, PAGE_EXECUTE_READWRITE }, /* 0x2b */
+        { FILE_MAP_EXECUTE | SECTION_MAP_EXECUTE | FILE_MAP_READ, PAGE_EXECUTE_READ }, /* 0x2c */
+        { FILE_MAP_EXECUTE | SECTION_MAP_EXECUTE | FILE_MAP_READ | FILE_MAP_COPY, PAGE_EXECUTE_READ }, /* 0x2d */
+        { FILE_MAP_EXECUTE | SECTION_MAP_EXECUTE | FILE_MAP_READ | FILE_MAP_WRITE, PAGE_EXECUTE_READWRITE }, /* 0x2e */
+        { FILE_MAP_EXECUTE | SECTION_MAP_EXECUTE | FILE_MAP_READ | FILE_MAP_WRITE | FILE_MAP_COPY, PAGE_EXECUTE_READWRITE } /* 0x2f */
+    };
+    void *base, *nt_base;
+    DWORD i, j, k, ret, old_prot, prev_prot;
+    SYSTEM_INFO si;
+    char temp_path[MAX_PATH];
+    char file_name[MAX_PATH];
+    HANDLE hfile, hmap;
+    MEMORY_BASIC_INFORMATION info, nt_info;
+
+    GetSystemInfo(&si);
+    trace("system page size %#x\n", si.dwPageSize);
+
+    GetTempPath(MAX_PATH, temp_path);
+    GetTempFileName(temp_path, "map", 0, file_name);
+
+    SetLastError(0xdeadbeef);
+    hfile = CreateFile(file_name, GENERIC_READ|GENERIC_WRITE|GENERIC_EXECUTE, 0, NULL, CREATE_ALWAYS, 0, 0);
+    ok(hfile != INVALID_HANDLE_VALUE, "CreateFile(%s) error %d\n", file_name, GetLastError());
+    SetFilePointer(hfile, si.dwPageSize, NULL, FILE_BEGIN);
+    SetEndOfFile(hfile);
+
+    for (i = 0; i < sizeof(page_prot)/sizeof(page_prot[0]); i++)
+    {
+        SetLastError(0xdeadbeef);
+        hmap = CreateFileMapping(hfile, NULL, page_prot[i] | SEC_COMMIT, 0, si.dwPageSize, NULL);
+
+        if (page_prot[i] == PAGE_NOACCESS)
+        {
+            HANDLE hmap2;
+
+            ok(!hmap, "CreateFileMapping(PAGE_NOACCESS) should fail\n");
+            ok(GetLastError() == ERROR_INVALID_PARAMETER, "expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+
+            /* A trick to create a not accessible mapping */
+            SetLastError(0xdeadbeef);
+            hmap = CreateFileMapping(hfile, NULL, PAGE_READWRITE | SEC_COMMIT, 0, si.dwPageSize, NULL);
+            ok(hmap != 0, "CreateFileMapping(PAGE_READWRITE) error %d\n", GetLastError());
+            SetLastError(0xdeadbeef);
+            ret = DuplicateHandle(GetCurrentProcess(), hmap, GetCurrentProcess(), &hmap2, 0, FALSE, 0);
+            ok(ret, "DuplicateHandle error %d\n", GetLastError());
+            CloseHandle(hmap);
+            hmap = hmap2;
+        }
+
+        if (!hmap)
+        {
+            trace("%d: CreateFileMapping(%04x) failed: %d\n", i, page_prot[i], GetLastError());
+
+            /* NT4 and win2k don't support EXEC on file mappings */
+            if (page_prot[i] == PAGE_EXECUTE_READ || page_prot[i] == PAGE_EXECUTE_READWRITE)
+            {
+                ok(broken(!hmap), "%d: CreateFileMapping doesn't support PAGE_EXECUTE\n", i);
+                continue;
+            }
+            /* Vista+ supports PAGE_EXECUTE_WRITECOPY, earlier versions don't */
+            if (page_prot[i] == PAGE_EXECUTE_WRITECOPY)
+            {
+                ok(broken(!hmap), "%d: CreateFileMapping doesn't support PAGE_EXECUTE_WRITECOPY\n", i);
+                continue;
+            }
+        }
+
+        ok(hmap != 0, "%d: CreateFileMapping(%04x) error %d\n", i, page_prot[i], GetLastError());
+
+        for (j = 0; j < sizeof(view)/sizeof(view[0]); j++)
+        {
+            nt_base = map_view_of_file(hmap, view[j].access);
+            if (nt_base)
+            {
+                SetLastError(0xdeadbeef);
+                ret = VirtualQuery(nt_base, &nt_info, sizeof(nt_info));
+                ok(ret, "%d: VirtualQuery failed %d\n", j, GetLastError());
+                UnmapViewOfFile(nt_base);
+            }
+
+            SetLastError(0xdeadbeef);
+            base = MapViewOfFile(hmap, view[j].access, 0, 0, 0);
+
+            /* Vista+ supports FILE_MAP_EXECUTE properly, earlier versions don't */
+            ok(!nt_base == !base ||
+               broken((view[j].access & FILE_MAP_EXECUTE) && !nt_base != !base),
+               "%d: (%04x/%04x) NT %p kernel %p\n", j, page_prot[i], view[j].access, nt_base, base);
+
+            if (!is_compatible_access(page_prot[i], view[j].access))
+            {
+                ok(!base, "%d: MapViewOfFile(%04x/%04x) should fail\n", j, page_prot[i], view[j].access);
+                ok(GetLastError() == ERROR_ACCESS_DENIED, "wrong error %d\n", GetLastError());
+                continue;
+            }
+
+            /* Vista+ properly supports FILE_MAP_EXECUTE, earlier versions don't */
+            if (!base && (view[j].access & FILE_MAP_EXECUTE))
+            {
+                ok(broken(!base), "%d: MapViewOfFile(%04x/%04x) failed %d\n", j, page_prot[i], view[j].access, GetLastError());
+                continue;
+            }
+
+            ok(base != NULL, "%d: MapViewOfFile(%04x/%04x) failed %d\n", j, page_prot[i], view[j].access, GetLastError());
+
+            SetLastError(0xdeadbeef);
+            ret = VirtualQuery(base, &info, sizeof(info));
+            ok(ret, "%d: VirtualQuery failed %d\n", j, GetLastError());
+            ok(info.BaseAddress == base, "%d: (%04x) got %p, expected %p\n", j, view[j].access, info.BaseAddress, base);
+            ok(info.RegionSize == si.dwPageSize, "%d: (%04x) got %#lx != expected %#x\n", j, view[j].access, info.RegionSize, si.dwPageSize);
+            ok(info.Protect == view[j].prot ||
+               broken(view[j].prot == PAGE_EXECUTE_READ && info.Protect == PAGE_READONLY) || /* win2k */
+               broken(view[j].prot == PAGE_EXECUTE_READWRITE && info.Protect == PAGE_READWRITE) || /* win2k */
+               broken(view[j].prot == PAGE_EXECUTE_WRITECOPY && info.Protect == PAGE_NOACCESS), /* XP */
+               "%d: (%04x) got %#x, expected %#x\n", j, view[j].access, info.Protect, view[j].prot);
+            ok(info.AllocationBase == base, "%d: (%04x) got %p, expected %p\n", j, view[j].access, info.AllocationBase, base);
+            ok(info.AllocationProtect == info.Protect, "%d: (%04x) got %#x, expected %#x\n", j, view[j].access, info.AllocationProtect, info.Protect);
+            ok(info.State == MEM_COMMIT, "%d: (%04x) got %#x, expected MEM_COMMIT\n", j, view[j].access, info.State);
+            ok(info.Type == MEM_MAPPED, "%d: (%04x) got %#x, expected MEM_MAPPED\n", j, view[j].access, info.Type);
+
+            if (nt_base && base)
+            {
+                ok(nt_info.RegionSize == info.RegionSize, "%d: (%04x) got %#lx != expected %#lx\n", j, view[j].access, nt_info.RegionSize, info.RegionSize);
+                ok(nt_info.Protect == info.Protect /* Vista+ */ ||
+                   broken(nt_info.AllocationProtect == PAGE_EXECUTE_WRITECOPY && info.Protect == PAGE_NOACCESS), /* XP */
+                   "%d: (%04x) got %#x, expected %#x\n", j, view[j].access, nt_info.Protect, info.Protect);
+                ok(nt_info.AllocationProtect == info.AllocationProtect /* Vista+ */ ||
+                   broken(nt_info.AllocationProtect == PAGE_EXECUTE_WRITECOPY && info.Protect == PAGE_NOACCESS), /* XP */
+                   "%d: (%04x) got %#x, expected %#x\n", j, view[j].access, nt_info.AllocationProtect, info.AllocationProtect);
+                ok(nt_info.State == info.State, "%d: (%04x) got %#x, expected %#x\n", j, view[j].access, nt_info.State, info.State);
+                ok(nt_info.Type == info.Type, "%d: (%04x) got %#x, expected %#x\n", j, view[j].access, nt_info.Type, info.Type);
+            }
+
+            prev_prot = info.Protect;
+
+            for (k = 0; k < sizeof(page_prot)/sizeof(page_prot[0]); k++)
+            {
+                /*trace("map %#x, view %#x, requested prot %#x\n", page_prot[i], view[j].prot, page_prot[k]);*/
+                SetLastError(0xdeadbeef);
+                old_prot = 0xdeadbeef;
+                ret = VirtualProtect(base, si.dwPageSize, page_prot[k], &old_prot);
+                if (is_compatible_protection(page_prot[i], view[j].prot, page_prot[k]))
+                {
+                    /* win2k and XP don't support EXEC on file mappings */
+                    if (!ret && page_prot[k] == PAGE_EXECUTE)
+                    {
+                        ok(broken(!ret), "VirtualProtect doesn't support PAGE_EXECUTE\n");
+                        continue;
+                    }
+                    /* NT4 and win2k don't support EXEC on file mappings */
+                    if (!ret && (page_prot[k] == PAGE_EXECUTE_READ || page_prot[k] == PAGE_EXECUTE_READWRITE))
+                    {
+                        ok(broken(!ret), "VirtualProtect doesn't support PAGE_EXECUTE\n");
+                        continue;
+                    }
+                    /* Vista+ supports PAGE_EXECUTE_WRITECOPY, earlier versions don't */
+                    if (!ret && page_prot[k] == PAGE_EXECUTE_WRITECOPY)
+                    {
+                        ok(broken(!ret), "VirtualProtect doesn't support PAGE_EXECUTE_WRITECOPY\n");
+                        continue;
+                    }
+                    /* win2k and XP don't support PAGE_EXECUTE_WRITECOPY views properly  */
+                    if (!ret && view[j].prot == PAGE_EXECUTE_WRITECOPY)
+                    {
+                        ok(broken(!ret), "VirtualProtect doesn't support PAGE_EXECUTE_WRITECOPY view properly\n");
+                        continue;
+                    }
+
+                    ok(ret, "VirtualProtect error %d, map %#x, view %#x, requested prot %#x\n", GetLastError(), page_prot[i], view[j].prot, page_prot[k]);
+                    ok(old_prot == prev_prot, "got %#x, expected %#x\n", old_prot, prev_prot);
+                    prev_prot = page_prot[k];
+                }
+                else
+                {
+                    /* NT4 doesn't fail on incompatible map and view */
+                    if (ret)
+                    {
+                        ok(broken(ret), "VirtualProtect should fail, map %#x, view %#x, requested prot %#x\n", page_prot[i], view[j].prot, page_prot[k]);
+                        skip("Incompatible map and view are not properly handled on this platform\n");
+                        break; /* NT4 won't pass remaining tests */
+                    }
+
+                    ok(!ret, "VirtualProtect should fail, map %#x, view %#x, requested prot %#x\n", page_prot[i], view[j].prot, page_prot[k]);
+                    ok(GetLastError() == ERROR_INVALID_PARAMETER, "expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+                }
+            }
+
+            UnmapViewOfFile(base);
+        }
+
+        CloseHandle(hmap);
+    }
+
+    CloseHandle(hfile);
+    DeleteFile(file_name);
+}
+
 START_TEST(virtual)
 {
     int argc;
@@ -1427,7 +2431,13 @@ START_TEST(virtual)
     pResetWriteWatch = (void *) GetProcAddress(hkernel32, "ResetWriteWatch");
     pNtAreMappedFilesTheSame = (void *)GetProcAddress( GetModuleHandle("ntdll.dll"),
                                                        "NtAreMappedFilesTheSame" );
+    pNtMapViewOfSection = (void *)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtMapViewOfSection");
+    pNtUnmapViewOfSection = (void *)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtUnmapViewOfSection");
 
+    test_mapping();
+    test_CreateFileMapping_protection();
+    test_VirtualAlloc_protection();
+    test_VirtualProtect();
     test_VirtualAllocEx();
     test_VirtualAlloc();
     test_MapViewOfFile();
index 1d02782..7dd7388 100644 (file)
 #include "winbase.h"
 #include "winioctl.h"
 #include <stdio.h>
+#include "wine/ddk/ntddcdvd.h"
+
+#include <pshpack1.h>
+struct COMPLETE_DVD_LAYER_DESCRIPTOR
+{
+    DVD_DESCRIPTOR_HEADER Header;
+    DVD_LAYER_DESCRIPTOR Descriptor;
+    UCHAR Padding;
+};
+#include <poppack.h>
+C_ASSERT(sizeof(struct COMPLETE_DVD_LAYER_DESCRIPTOR) == 22);
+
+#include <pshpack1.h>
+struct COMPLETE_DVD_MANUFACTURER_DESCRIPTOR
+{
+    DVD_DESCRIPTOR_HEADER Header;
+    DVD_MANUFACTURER_DESCRIPTOR Descriptor;
+    UCHAR Padding;
+};
+#include <poppack.h>
+C_ASSERT(sizeof(struct COMPLETE_DVD_MANUFACTURER_DESCRIPTOR) == 2053);
 
 static HINSTANCE hdll;
 static BOOL (WINAPI * pGetVolumeNameForVolumeMountPointA)(LPCSTR, LPSTR, DWORD);
@@ -732,6 +753,175 @@ static void test_GetVolumePathNamesForVolumeNameW(void)
     ok(error == ERROR_FILE_NOT_FOUND, "expected ERROR_FILE_NOT_FOUND got %u\n", error);
 }
 
+static void test_dvd_read_structure(HANDLE handle)
+{
+    int i;
+    DWORD nbBytes;
+    BOOL ret;
+    DVD_READ_STRUCTURE dvdReadStructure;
+    DVD_LAYER_DESCRIPTOR dvdLayerDescriptor;
+    struct COMPLETE_DVD_LAYER_DESCRIPTOR completeDvdLayerDescriptor;
+    DVD_COPYRIGHT_DESCRIPTOR dvdCopyrightDescriptor;
+    struct COMPLETE_DVD_MANUFACTURER_DESCRIPTOR completeDvdManufacturerDescriptor;
+
+    dvdReadStructure.BlockByteOffset.QuadPart = 0;
+    dvdReadStructure.SessionId = 0;
+    dvdReadStructure.LayerNumber = 0;
+
+
+    /* DvdPhysicalDescriptor */
+    dvdReadStructure.Format = 0;
+
+    SetLastError(0xdeadbeef);
+
+    /* Test whether this ioctl is supported */
+    ret = DeviceIoControl(handle, IOCTL_DVD_READ_STRUCTURE, &dvdReadStructure, sizeof(DVD_READ_STRUCTURE),
+        &completeDvdLayerDescriptor, sizeof(struct COMPLETE_DVD_LAYER_DESCRIPTOR), &nbBytes, NULL);
+    if ((!ret && GetLastError() == ERROR_INVALID_FUNCTION)
+     || (!ret && GetLastError() == ERROR_NOT_SUPPORTED))
+    {
+        skip("IOCTL_DVD_READ_STRUCTURE not supported\n");
+        return;
+    }
+
+    ok(ret || broken(GetLastError() == ERROR_NOT_READY) || broken(GetLastError() == ERROR_INVALID_PARAMETER),
+        "IOCTL_DVD_READ_STRUCTURE (DvdPhysicalDescriptor) failed, last error = %u\n", GetLastError());
+    if(!ret)
+        return;
+
+    /* Confirm there is always a header before the actual data */
+    ok( completeDvdLayerDescriptor.Header.Length == 0x0802, "Length is 0x%04x instead of 0x0802\n", completeDvdLayerDescriptor.Header.Length);
+    ok( completeDvdLayerDescriptor.Header.Reserved[0] == 0, "Reserved[0] is %x instead of 0\n", completeDvdLayerDescriptor.Header.Reserved[0]);
+    ok( completeDvdLayerDescriptor.Header.Reserved[1] == 0, "Reserved[1] is %x instead of 0\n", completeDvdLayerDescriptor.Header.Reserved[1]);
+
+    /* TODO: Also check completeDvdLayerDescriptor.Descriptor content (via IOCTL_SCSI_PASS_THROUGH_DIRECT ?) */
+
+    /* Insufficient output buffer */
+    for(i=0; i<sizeof(DVD_DESCRIPTOR_HEADER); i++)
+    {
+        SetLastError(0xdeadbeef);
+
+        ret = DeviceIoControl(handle, IOCTL_DVD_READ_STRUCTURE, &dvdReadStructure, sizeof(DVD_READ_STRUCTURE),
+            &completeDvdLayerDescriptor, i, &nbBytes, NULL);
+        ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,"IOCTL_DVD_READ_STRUCTURE should fail with small buffer\n");
+    }
+
+    SetLastError(0xdeadbeef);
+
+    /* On newer version, an output buffer of sizeof(DVD_READ_STRUCTURE) size fails.
+        I think this is to force developers to realize that there is a header before the actual content */
+    ret = DeviceIoControl(handle, IOCTL_DVD_READ_STRUCTURE, &dvdReadStructure, sizeof(DVD_READ_STRUCTURE),
+        &dvdLayerDescriptor, sizeof(DVD_LAYER_DESCRIPTOR), &nbBytes, NULL);
+    ok( (!ret && GetLastError() == ERROR_INVALID_PARAMETER) || broken(ret) /* < Win7 */,
+        "IOCTL_DVD_READ_STRUCTURE should have failed\n");
+
+    SetLastError(0xdeadbeef);
+
+    ret = DeviceIoControl(handle, IOCTL_DVD_READ_STRUCTURE, NULL, sizeof(DVD_READ_STRUCTURE),
+        &completeDvdLayerDescriptor, sizeof(struct COMPLETE_DVD_LAYER_DESCRIPTOR), &nbBytes, NULL);
+    ok( (!ret && GetLastError() == ERROR_INVALID_PARAMETER),
+        "IOCTL_DVD_READ_STRUCTURE should have failed\n");
+
+    /* Test wrong input parameters */
+    for(i=0; i<sizeof(DVD_READ_STRUCTURE); i++)
+    {
+        SetLastError(0xdeadbeef);
+
+        ret = DeviceIoControl(handle, IOCTL_DVD_READ_STRUCTURE, &dvdReadStructure, i,
+        &completeDvdLayerDescriptor, sizeof(struct COMPLETE_DVD_LAYER_DESCRIPTOR), &nbBytes, NULL);
+        ok( (!ret && GetLastError() == ERROR_INVALID_PARAMETER),
+            "IOCTL_DVD_READ_STRUCTURE should have failed\n");
+    }
+
+
+    /* DvdCopyrightDescriptor */
+    dvdReadStructure.Format = 1;
+
+    SetLastError(0xdeadbeef);
+
+    /* Strangely, with NULL lpOutBuffer, last error is insufficient buffer, not invalid parameter as we could expect */
+    ret = DeviceIoControl(handle, IOCTL_DVD_READ_STRUCTURE, &dvdReadStructure, sizeof(DVD_READ_STRUCTURE),
+        NULL, sizeof(DVD_COPYRIGHT_DESCRIPTOR), &nbBytes, NULL);
+    ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "IOCTL_DVD_READ_STRUCTURE should have failed %d %u\n", ret, GetLastError());
+
+    for(i=0; i<sizeof(DVD_COPYRIGHT_DESCRIPTOR); i++)
+    {
+        SetLastError(0xdeadbeef);
+
+        ret = DeviceIoControl(handle, IOCTL_DVD_READ_STRUCTURE, &dvdReadStructure, sizeof(DVD_READ_STRUCTURE),
+            &dvdCopyrightDescriptor, i, &nbBytes, NULL);
+        ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "IOCTL_DVD_READ_STRUCTURE should have failed %d %u\n", ret, GetLastError());
+    }
+
+
+    /* DvdManufacturerDescriptor */
+    dvdReadStructure.Format = 4;
+
+    SetLastError(0xdeadbeef);
+
+    ret = DeviceIoControl(handle, IOCTL_DVD_READ_STRUCTURE, &dvdReadStructure, sizeof(DVD_READ_STRUCTURE),
+        &completeDvdManufacturerDescriptor, sizeof(DVD_MANUFACTURER_DESCRIPTOR), &nbBytes, NULL);
+    ok(ret || broken(GetLastError() == ERROR_NOT_READY),
+        "IOCTL_DVD_READ_STRUCTURE (DvdManufacturerDescriptor) failed, last error = %u\n", GetLastError());
+    if(!ret)
+        return;
+
+    /* Confirm there is always a header before the actual data */
+    ok( completeDvdManufacturerDescriptor.Header.Length == 0x0802, "Length is 0x%04x instead of 0x0802\n", completeDvdManufacturerDescriptor.Header.Length);
+    ok( completeDvdManufacturerDescriptor.Header.Reserved[0] == 0, "Reserved[0] is %x instead of 0\n", completeDvdManufacturerDescriptor.Header.Reserved[0]);
+    ok( completeDvdManufacturerDescriptor.Header.Reserved[1] == 0, "Reserved[1] is %x instead of 0\n", completeDvdManufacturerDescriptor.Header.Reserved[1]);
+
+    SetLastError(0xdeadbeef);
+
+    /* Basic parameter check */
+    ret = DeviceIoControl(handle, IOCTL_DVD_READ_STRUCTURE, &dvdReadStructure, sizeof(DVD_READ_STRUCTURE),
+        NULL, sizeof(DVD_MANUFACTURER_DESCRIPTOR), &nbBytes, NULL);
+    ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "IOCTL_DVD_READ_STRUCTURE should have failed %d %u\n", ret, GetLastError());
+}
+
+static void test_cdrom_ioctl(void)
+{
+    char drive_letter, drive_path[] = "A:\\", drive_full_path[] = "\\\\.\\A:";
+    DWORD bitmask;
+    HANDLE handle;
+
+    bitmask = GetLogicalDrives();
+    if(!bitmask)
+    {
+        trace("GetLogicalDrives failed : %u\n", GetLastError());
+        return;
+    }
+
+    for(drive_letter='A'; drive_letter<='Z'; drive_letter++)
+    {
+        if(!(bitmask & (1 << (drive_letter-'A') )))
+            continue;
+
+        drive_path[0] = drive_letter;
+        if(GetDriveTypeA(drive_path) != DRIVE_CDROM)
+        {
+            trace("Skipping %c:, not a CDROM drive.\n", drive_letter);
+            continue;
+        }
+
+        trace("Testing with %c:\n", drive_letter);
+
+        drive_full_path[4] = drive_letter;
+        handle = CreateFileA(drive_full_path, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
+        if(handle == INVALID_HANDLE_VALUE)
+        {
+            trace("Failed to open the device : %u\n", GetLastError());
+            continue;
+        }
+
+        /* Add your tests here */
+        test_dvd_read_structure(handle);
+
+        CloseHandle(handle);
+    }
+
+}
+
 START_TEST(volume)
 {
     hdll = GetModuleHandleA("kernel32.dll");
@@ -758,4 +948,5 @@ START_TEST(volume)
     test_disk_extents();
     test_GetVolumePathNamesForVolumeNameA();
     test_GetVolumePathNamesForVolumeNameW();
+    test_cdrom_ioctl();
 }