[KERNEL32_WINETEST]
authorAmine Khaldi <amine.khaldi@reactos.org>
Mon, 3 Oct 2011 17:58:01 +0000 (17:58 +0000)
committerAmine Khaldi <amine.khaldi@reactos.org>
Mon, 3 Oct 2011 17:58:01 +0000 (17:58 +0000)
* Sync with Wine 1.3.29.

svn path=/trunk/; revision=53966

30 files changed:
rostests/winetests/kernel32/actctx.c
rostests/winetests/kernel32/atom.c
rostests/winetests/kernel32/change.c
rostests/winetests/kernel32/codepage.c
rostests/winetests/kernel32/comm.c
rostests/winetests/kernel32/console.c
rostests/winetests/kernel32/debugger.c
rostests/winetests/kernel32/directory.c
rostests/winetests/kernel32/drive.c
rostests/winetests/kernel32/environ.c
rostests/winetests/kernel32/fiber.c
rostests/winetests/kernel32/file.c
rostests/winetests/kernel32/format_msg.c
rostests/winetests/kernel32/heap.c
rostests/winetests/kernel32/loader.c
rostests/winetests/kernel32/locale.c
rostests/winetests/kernel32/mailslot.c
rostests/winetests/kernel32/module.c
rostests/winetests/kernel32/path.c
rostests/winetests/kernel32/pipe.c
rostests/winetests/kernel32/process.c
rostests/winetests/kernel32/profile.c
rostests/winetests/kernel32/resource.c
rostests/winetests/kernel32/sync.c
rostests/winetests/kernel32/thread.c
rostests/winetests/kernel32/time.c
rostests/winetests/kernel32/toolhelp.c
rostests/winetests/kernel32/version.c
rostests/winetests/kernel32/virtual.c
rostests/winetests/kernel32/volume.c

index a158cf3..b7f3010 100644 (file)
@@ -1145,6 +1145,7 @@ static void run_child_process(void)
     STARTUPINFO si = { 0 };
     HANDLE file;
     FILETIME now;
+    BOOL ret;
 
     GetModuleFileNameA(NULL, path, MAX_PATH);
     strcat(path, ".manifest");
@@ -1165,8 +1166,8 @@ static void run_child_process(void)
         CloseHandle(file);
     }
     sprintf(cmdline, "\"%s\" %s manifest1", argv[0], argv[1]);
-    ok(CreateProcess(argv[0], cmdline, NULL, NULL, FALSE, 0, NULL, NULL,
-                     &si, &pi) != 0, "Could not create process: %u\n", GetLastError());
+    ret = CreateProcess(argv[0], cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
+    ok(ret, "Could not create process: %u\n", GetLastError());
     winetest_wait_child_process( pi.hProcess );
     CloseHandle(pi.hThread);
     CloseHandle(pi.hProcess);
index f85d272..15ef04c 100755 (executable)
@@ -192,26 +192,16 @@ static void test_get_atom_name(void)
         else
             ok( !len, "bad length %d\n", len );
 
-       SetLastError(0xdeadbeef);
+        SetLastError(0xdeadbeef);
         len = GlobalGetAtomNameA( (ATOM)i, buf, 2);
-       if (!len) /* the NT way */
-       {
-           ok(GetLastError() == (i ? ERROR_MORE_DATA : ERROR_INVALID_PARAMETER) ||
-               GetLastError() == 0xdeadbeef,  /* the Win 9x way */
-               "wrong error conditions %u for %u\n", GetLastError(), i);
-       }
-       else /* the Win 9x way */
-       {
-           ok(GetLastError() == 0xdeadbeef,
-               "wrong error conditions %u for %u\n", GetLastError(), i);
-       }
+        ok(!len, "bad length %d\n", len);
+       ok(GetLastError() == ERROR_MORE_DATA || GetLastError() == ERROR_INVALID_PARAMETER,
+            "wrong error conditions %u for %u\n", GetLastError(), i);
     }
 
     memset( buf, '.', sizeof(buf) );
     len = GlobalGetAtomNameA( atom, buf, 6 );
-    ok( len == 0 ||
-        len == 5, /* win9x */
-        "bad length %d\n", len );
+    ok( len == 0, "bad length %d\n", len );
     ok( !memcmp( buf, "fooba\0....", 10 ), "bad buffer contents\n");
     if (unicode_OS)
     {
@@ -238,14 +228,8 @@ static void test_get_atom_name(void)
     memset(out, '.', sizeof(out));
     SetLastError(0xdeadbeef);
     len = GlobalGetAtomNameA(atom, out, 10);
-    if (!len) /* the NT way */
-    {
-        ok(GetLastError() == ERROR_MORE_DATA, "wrong error code (%u instead of %u)\n", GetLastError(), ERROR_MORE_DATA);
-    }
-    else /* the Win9x way */
-    {
-        ok(GetLastError() == 0xdeadbeef, "wrong error code (%u instead of %u)\n", GetLastError(), 0xdeadbeef);
-    }
+    ok(!len, "bad length %d\n", len);
+    ok(GetLastError() == ERROR_MORE_DATA, "wrong error code (%u instead of %u)\n", GetLastError(), ERROR_MORE_DATA);
     for (i = 0; i < 9; i++)
     {
         ok(out[i] == "abcdefghij"[i % 10], "wrong string at %i (%c instead of %c)\n", i, out[i], "abcdefghij"[i % 10]);
@@ -487,13 +471,11 @@ static void test_local_get_atom_name(void)
         /* ERROR_MORE_DATA is on nt3.51 sp5 */
         if (i)
             ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER ||
-               GetLastError() == ERROR_MORE_DATA ||
-               GetLastError() == 0xdeadbeef, /* the Win 9x way */
+               GetLastError() == ERROR_MORE_DATA,
                "wrong error conditions %u for %u\n", GetLastError(), i);
         else
             ok(GetLastError() == ERROR_INVALID_PARAMETER ||
-               GetLastError() == ERROR_MORE_DATA ||
-               GetLastError() == 0xdeadbeef, /* the Win 9x way */
+               GetLastError() == ERROR_MORE_DATA,
                "wrong error conditions %u for %u\n", GetLastError(), i);
     }
     /* test string limits & overflow */
@@ -522,8 +504,7 @@ static void test_local_get_atom_name(void)
 
     /* ERROR_MORE_DATA is on nt3.51 sp5 */
     ok(GetLastError() == ERROR_INVALID_PARAMETER ||
-       GetLastError() == ERROR_MORE_DATA ||
-       GetLastError() == 0xdeadbeef, /* the Win 9x way */
+       GetLastError() == ERROR_MORE_DATA,
        "wrong error code (%u)\n", GetLastError());
 
     if (unicode_OS)
index 08d1417..4d0bc1c 100755 (executable)
@@ -47,7 +47,7 @@ static DWORD CALLBACK NotificationThread(LPVOID arg)
 
     if (status == WAIT_OBJECT_0 ) {
         notified = TRUE;
-        ret = FindNextChangeNotification(change);
+        FindNextChangeNotification(change);
     }
 
     ret = FindCloseChangeNotification(change);
@@ -369,9 +369,8 @@ static void test_ffcnMultipleThreads(void)
     ok( r == TRUE, "failed to remove dir\n");
 }
 
-typedef BOOL (WINAPI *fnReadDirectoryChangesW)(HANDLE,LPVOID,DWORD,BOOL,DWORD,
+static BOOL (WINAPI *pReadDirectoryChangesW)(HANDLE,LPVOID,DWORD,BOOL,DWORD,
                          LPDWORD,LPOVERLAPPED,LPOVERLAPPED_COMPLETION_ROUTINE);
-fnReadDirectoryChangesW pReadDirectoryChangesW;
 
 static void test_readdirectorychanges(void)
 {
@@ -785,6 +784,222 @@ static void test_readdirectorychanges_filedir(void)
     ok( r == TRUE, "failed to remove directory\n");
 }
 
+static void CALLBACK readdirectorychanges_cr(DWORD error, DWORD len, LPOVERLAPPED ov)
+{
+    ok(error == 0, "ReadDirectoryChangesW error %d\n", error);
+    ok(ov->hEvent == (void*)0xdeadbeef, "hEvent should not have changed\n");
+}
+
+static void test_readdirectorychanges_cr(void)
+{
+    static const WCHAR szBoo[] = { '\\','b','o','o','\\',0 };
+    static const WCHAR szDir[] = { 'd','i','r',0 };
+    static const WCHAR szFile[] = { 'f','i','l','e',0 };
+    static const WCHAR szBackslash[] = { '\\',0 };
+
+    WCHAR path[MAX_PATH], file[MAX_PATH], dir[MAX_PATH], sub_file[MAX_PATH];
+    FILE_NOTIFY_INFORMATION fni[1024], *fni_next;
+    OVERLAPPED ov;
+    HANDLE hdir, hfile;
+    NTSTATUS r;
+
+    if (!pReadDirectoryChangesW)
+    {
+        win_skip("ReadDirectoryChangesW is not available\n");
+        return;
+    }
+
+    SetLastError(0xdeadbeef);
+    r = GetTempPathW(MAX_PATH, path);
+    if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED))
+    {
+        win_skip("GetTempPathW is not implemented\n");
+        return;
+    }
+    ok(r != 0, "temp path failed\n");
+    if (!r)
+        return;
+
+    lstrcatW(path, szBoo);
+    lstrcpyW(dir, path);
+    lstrcatW(dir, szDir);
+    lstrcpyW(file, path);
+    lstrcatW(file, szFile);
+    lstrcpyW(sub_file, dir);
+    lstrcatW(sub_file, szBackslash);
+    lstrcatW(sub_file, szFile);
+
+    DeleteFileW(file);
+    RemoveDirectoryW(dir);
+    RemoveDirectoryW(path);
+
+    r = CreateDirectoryW(path, NULL);
+    ok(r == TRUE, "failed to create directory\n");
+
+    hdir = CreateFileW(path, GENERIC_READ,
+            FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, OPEN_EXISTING,
+            FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
+    ok(hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
+
+    memset(&ov, 0, sizeof(ov));
+    ov.hEvent = (void*)0xdeadbeef;
+    r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
+            FILE_NOTIFY_CHANGE_FILE_NAME, NULL, &ov, readdirectorychanges_cr);
+    ok(r == TRUE, "pReadDirectoryChangesW failed\n");
+
+    hfile = CreateFileW(file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
+    ok(hfile != INVALID_HANDLE_VALUE, "failed to create file\n");
+    CloseHandle(hfile);
+
+    r = SleepEx(1000, TRUE);
+    ok(r != 0, "failed to receive file creation event\n");
+    ok(fni->NextEntryOffset == 0, "there should be no more events in buffer\n");
+    ok(fni->Action == FILE_ACTION_ADDED, "Action = %d\n", fni->Action);
+    ok(fni->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR),
+            "FileNameLength = %d\n", fni->FileNameLength);
+    ok(!memcmp(fni->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)),
+            "FileName = %s\n", wine_dbgstr_w(fni->FileName));
+
+    r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
+            FILE_NOTIFY_CHANGE_FILE_NAME, NULL, &ov, readdirectorychanges_cr);
+    ok(r == TRUE, "pReadDirectoryChangesW failed\n");
+
+    /* This event will not be reported */
+    r = CreateDirectoryW(dir, NULL);
+    ok(r == TRUE, "failed to create directory\n");
+
+    r = MoveFileW(file, sub_file);
+    ok(r == TRUE, "failed to move file\n");
+
+    r = SleepEx(1000, TRUE);
+    ok(r != 0, "failed to receive file move event\n");
+    ok(fni->NextEntryOffset == 0, "there should be no more events in buffer\n");
+    ok(fni->Action == FILE_ACTION_REMOVED, "Action = %d\n", fni->Action);
+    ok(fni->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR),
+            "FileNameLength = %d\n", fni->FileNameLength);
+    ok(!memcmp(fni->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)),
+            "FileName = %s\n", wine_dbgstr_w(fni->FileName));
+
+    r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
+            FILE_NOTIFY_CHANGE_FILE_NAME, NULL, &ov, readdirectorychanges_cr);
+    ok(r == TRUE, "pReadDirectoryChangesW failed\n");
+
+    r = MoveFileW(sub_file, file);
+    ok(r == TRUE, "failed to move file\n");
+
+    r = SleepEx(1000, TRUE);
+    ok(r != 0, "failed to receive file move event\n");
+    ok(fni->NextEntryOffset == 0, "there should be no more events in buffer\n");
+    ok(fni->Action == FILE_ACTION_ADDED, "Action = %d\n", fni->Action);
+    ok(fni->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR),
+            "FileNameLength = %d\n", fni->FileNameLength);
+    ok(!memcmp(fni->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)),
+            "FileName = %s\n", wine_dbgstr_w(fni->FileName));
+
+    r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
+            FILE_NOTIFY_CHANGE_FILE_NAME, NULL, &ov, readdirectorychanges_cr);
+    ok(r == TRUE, "pReadDirectoryChangesW failed\n");
+
+    r = DeleteFileW(file);
+    ok(r == TRUE, "failed to delete file\n");
+
+    r = SleepEx(1000, TRUE);
+    ok(r != 0, "failed to receive file removal event\n");
+    ok(fni->NextEntryOffset == 0, "there should be no more events in buffer\n");
+    ok(fni->Action == FILE_ACTION_REMOVED, "Action = %d\n", fni->Action);
+    ok(fni->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR),
+            "FileNameLength = %d\n", fni->FileNameLength);
+    ok(!memcmp(fni->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)),
+            "FileName = %s\n", wine_dbgstr_w(fni->FileName));
+
+    CloseHandle(hdir);
+
+    hdir = CreateFileW(path, GENERIC_READ,
+            FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, OPEN_EXISTING,
+            FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
+    ok(hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
+
+    r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
+            FILE_NOTIFY_CHANGE_DIR_NAME, NULL, &ov, readdirectorychanges_cr);
+    ok(r == TRUE, "pReadDirectoryChangesW failed\n");
+
+    r = MoveFileW(dir, file);
+    ok(r == TRUE, "failed to move directory\n");
+
+    r = SleepEx(1000, TRUE);
+    ok(r != 0, "failed to receive directory move event\n");
+    if (fni->Action == FILE_ACTION_RENAMED_OLD_NAME)
+    {
+        ok(fni->Action == FILE_ACTION_RENAMED_OLD_NAME, "Action = %d\n", fni->Action);
+        ok(fni->FileNameLength == lstrlenW(szDir)*sizeof(WCHAR),
+                "FileNameLength = %d\n", fni->FileNameLength);
+        ok(!memcmp(fni->FileName, szDir, lstrlenW(szDir)*sizeof(WCHAR)),
+                "FileName = %s\n", wine_dbgstr_w(fni->FileName));
+        ok(fni->NextEntryOffset != 0, "no next entry in movement event\n");
+        fni_next = (FILE_NOTIFY_INFORMATION*)((char*)fni+fni->NextEntryOffset);
+        ok(fni_next->NextEntryOffset == 0, "there should be no more events in buffer\n");
+        ok(fni_next->Action == FILE_ACTION_RENAMED_NEW_NAME, "Action = %d\n", fni_next->Action);
+        ok(fni_next->FileNameLength == lstrlenW(szFile)*sizeof(WCHAR),
+                "FileNameLength = %d\n", fni_next->FileNameLength);
+        ok(!memcmp(fni_next->FileName, szFile, lstrlenW(szFile)*sizeof(WCHAR)),
+                "FileName = %s\n", wine_dbgstr_w(fni_next->FileName));
+    }
+    else
+    {
+        todo_wine ok(0, "Expected rename event\n");
+
+        if (fni->NextEntryOffset == 0)
+        {
+            r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
+                    FILE_NOTIFY_CHANGE_DIR_NAME, NULL, &ov, readdirectorychanges_cr);
+            ok(r == TRUE, "pReadDirectoryChangesW failed\n");
+
+            r = SleepEx(1000, TRUE);
+            ok(r != 0, "failed to receive directory move event\n");
+        }
+    }
+
+    r = CreateDirectoryW(dir, NULL);
+    ok(r == TRUE, "failed to create directory\n");
+
+    r = RemoveDirectoryW(dir);
+    ok(r == TRUE, "failed to remove directory\n");
+
+    r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
+            FILE_NOTIFY_CHANGE_DIR_NAME, NULL, &ov, readdirectorychanges_cr);
+    ok(r == TRUE, "pReadDirectoryChangesW failed\n");
+
+    r = SleepEx(1000, TRUE);
+    ok(r != 0, "failed to receive directory creation event\n");
+    ok(fni->Action == FILE_ACTION_ADDED, "Action = %d\n", fni->Action);
+    ok(fni->FileNameLength == lstrlenW(szDir)*sizeof(WCHAR),
+            "FileNameLength = %d\n", fni->FileNameLength);
+    ok(!memcmp(fni->FileName, szDir, lstrlenW(szDir)*sizeof(WCHAR)),
+            "FileName = %s\n", wine_dbgstr_w(fni->FileName));
+    if (fni->NextEntryOffset)
+        fni_next = (FILE_NOTIFY_INFORMATION*)((char*)fni+fni->NextEntryOffset);
+    else
+    {
+        r = pReadDirectoryChangesW(hdir, fni, sizeof(fni), FALSE,
+                FILE_NOTIFY_CHANGE_DIR_NAME, NULL, &ov, readdirectorychanges_cr);
+        ok(r == TRUE, "pReadDirectoryChangesW failed\n");
+
+        r = SleepEx(1000, TRUE);
+        ok(r != 0, "failed to receive directory removal event\n");
+        fni_next = fni;
+    }
+    ok(fni_next->NextEntryOffset == 0, "there should be no more events in buffer\n");
+    ok(fni_next->Action == FILE_ACTION_REMOVED, "Action = %d\n", fni_next->Action);
+    ok(fni_next->FileNameLength == lstrlenW(szDir)*sizeof(WCHAR),
+            "FileNameLength = %d\n", fni_next->FileNameLength);
+    ok(!memcmp(fni_next->FileName, szDir, lstrlenW(szDir)*sizeof(WCHAR)),
+            "FileName = %s\n", wine_dbgstr_w(fni_next->FileName));
+
+    CloseHandle(hdir);
+    RemoveDirectoryW(file);
+    RemoveDirectoryW(path);
+}
+
 static void test_ffcn_directory_overlap(void)
 {
     HANDLE parent_watch, child_watch, parent_thread, child_thread;
@@ -874,8 +1089,7 @@ static void test_ffcn_directory_overlap(void)
 START_TEST(change)
 {
     HMODULE hkernel32 = GetModuleHandle("kernel32");
-    pReadDirectoryChangesW = (fnReadDirectoryChangesW)
-        GetProcAddress(hkernel32, "ReadDirectoryChangesW");
+    pReadDirectoryChangesW = (void *)GetProcAddress(hkernel32, "ReadDirectoryChangesW");
 
     test_ffcnMultipleThreads();
     /* The above function runs a test that must occur before FindCloseChangeNotification is run in the
@@ -886,5 +1100,6 @@ START_TEST(change)
     test_readdirectorychanges();
     test_readdirectorychanges_null();
     test_readdirectorychanges_filedir();
+    test_readdirectorychanges_cr();
     test_ffcn_directory_overlap();
 }
index 402e4bf..8f9c4e1 100755 (executable)
@@ -111,16 +111,6 @@ static void test_null_source(void)
         len, GLE);
 }
 
-/* lstrcmpW is not supported on Win9x! */
-static int mylstrcmpW(const WCHAR* str1, const WCHAR* str2)
-{
-    while (*str1 && *str1==*str2) {
-        str1++;
-        str2++;
-    }
-    return *str1-*str2;
-}
-
 static void test_negative_source_length(void)
 {
     int len;
@@ -139,7 +129,7 @@ static void test_negative_source_length(void)
     SetLastError( 0xdeadbeef );
     memset(bufW,'x',sizeof(bufW));
     len = MultiByteToWideChar(CP_ACP, 0, "foobar", -2002, bufW, 10);
-    ok(len == 7 && !mylstrcmpW(bufW, foobarW) && GetLastError() == 0xdeadbeef,
+    ok(len == 7 && !lstrcmpW(bufW, foobarW) && GetLastError() == 0xdeadbeef,
        "MultiByteToWideChar(-2002): len=%d error=%u\n", len, GetLastError());
 
     SetLastError(0xdeadbeef);
@@ -354,6 +344,56 @@ static void test_string_conversion(LPBOOL bUsedDefaultChar)
     ok(GetLastError() == 0xdeadbeef, "GetLastError() is %u\n", GetLastError());
 }
 
+static void test_undefined_byte_char(void)
+{
+    static const struct tag_testset {
+        INT codepage;
+        LPCSTR str;
+        BOOL is_error;
+    } testset[] = {
+        {  874, "\xdd", TRUE },
+        {  932, "\xfe", TRUE },
+        {  932, "\x80", FALSE },
+        {  936, "\xff", TRUE },
+        {  949, "\xff", TRUE },
+        {  950, "\xff", TRUE },
+        { 1252, "\x90", FALSE },
+        { 1253, "\xaa", TRUE },
+        { 1255, "\xff", TRUE },
+        { 1257, "\xa5", TRUE },
+    };
+    INT i, ret;
+
+    for (i = 0; i < (sizeof(testset) / sizeof(testset[0])); i++) {
+        if (! IsValidCodePage(testset[i].codepage))
+        {
+            skip("Codepage %d not available\n", testset[i].codepage);
+            continue;
+        }
+
+        SetLastError(0xdeadbeef);
+        ret = MultiByteToWideChar(testset[i].codepage, MB_ERR_INVALID_CHARS,
+                                  testset[i].str, -1, NULL, 0);
+        if (testset[i].is_error) {
+            ok(ret == 0 && GetLastError() == ERROR_NO_UNICODE_TRANSLATION,
+               "ret is %d, GetLastError is %u (cp %d)\n",
+               ret, GetLastError(), testset[i].codepage);
+        }
+        else {
+            ok(ret == strlen(testset[i].str)+1 && GetLastError() == 0xdeadbeef,
+               "ret is %d, GetLastError is %u (cp %d)\n",
+               ret, GetLastError(), testset[i].codepage);
+        }
+
+        SetLastError(0xdeadbeef);
+        ret = MultiByteToWideChar(testset[i].codepage, 0,
+                                  testset[i].str, -1, NULL, 0);
+        ok(ret == strlen(testset[i].str)+1 && GetLastError() == 0xdeadbeef,
+           "ret is %d, GetLastError is %u (cp %d)\n",
+           ret, GetLastError(), testset[i].codepage);
+    }
+}
+
 START_TEST(codepage)
 {
     BOOL bUsedDefaultChar;
@@ -367,4 +407,6 @@ START_TEST(codepage)
     /* WideCharToMultiByte has two code paths, test both here */
     test_string_conversion(NULL);
     test_string_conversion(&bUsedDefaultChar);
+
+    test_undefined_byte_char();
 }
index 8ce6ccc..b08174f 100755 (executable)
@@ -807,7 +807,7 @@ static void test_waittxempty(HANDLE hcom)
     trace("WaitCommEvent for EV_TXEMPTY took %d ms\n", timediff);
     /* 050604: This shows a difference between XP (tested with mingw compiled crosstest):
        XP returns Writefile only after everything went out of the Serial port,
-       while wine returns immedate.
+       while wine returns immediately.
        Thus on XP, WaintCommEvent after setting the CommMask for EV_TXEMPTY
        nearly return immediate,
        while on wine the most time is spent here
@@ -927,7 +927,7 @@ static void test_LoopbackRead(HANDLE hcom)
     i=0;
     do 
     {
-       res = ReadFile(hcom, rbuf+read, sizeof(rbuf-read), &read1, NULL);
+       res = ReadFile(hcom, rbuf+read, sizeof(rbuf)-read, &read1, NULL);
        ok(res, "Readfile failed\n");
        read += read1;
        i++;
@@ -1341,7 +1341,7 @@ static void  test_AbortWaitCts(HANDLE hcom)
     trace("Success 0x%08x err %d evtmask 0x%08x diff1 %d, diff2 %d\n",
          success, err, evtmask, after-before, after1-before);
 
-    ok(evtmask == 0, "Incorect EventMask 0x%08x returned on Wait aborted bu SetCommMask, expected 0x%08x\n",
+    ok(evtmask == 0, "Incorrect EventMask 0x%08x returned on Wait aborted bu SetCommMask, expected 0x%08x\n",
                 evtmask, 0);
     ok(GetCommModemStatus(hcom, &evtmask), "GetCommModemStatus failed\n");
     diff = after1 - before;
@@ -1627,8 +1627,10 @@ static void  test_WaitBreak(HANDLE hcom)
     trace("overlapped WriteCommEvent returned.\n");
 
     if (!success && (err == ERROR_IO_PENDING))
-       ok(WaitForSingleObjectEx(hComPortEvent, TIMEOUT, TRUE) == 0,
-           "wait hComPortEvent res %d\n", GetLastError());
+    {
+        success = WaitForSingleObjectEx(hComPortEvent, TIMEOUT, TRUE);
+        ok(!success, "wait hComPortEvent res %d\n", GetLastError());
+    }
     success = GetOverlappedResult(hcom, &overlapped, &written, FALSE);
     err = GetLastError();
     after1 = GetTickCount();
index 3d8ae93..07a090b 100755 (executable)
@@ -27,6 +27,7 @@ static BOOL (WINAPI *pGetConsoleInputExeNameA)(DWORD, LPSTR);
 static DWORD (WINAPI *pGetConsoleProcessList)(LPDWORD, DWORD);
 static HANDLE (WINAPI *pOpenConsoleW)(LPCWSTR,DWORD,BOOL,DWORD);
 static BOOL (WINAPI *pSetConsoleInputExeNameA)(LPCSTR);
+static BOOL (WINAPI *pVerifyConsoleIoHandle)(HANDLE handle);
 
 /* DEFAULT_ATTRIB is used for all initial filling of the console.
  * all modifications are made with TEST_ATTRIB so that we could check
@@ -68,6 +69,7 @@ static void init_function_pointers(void)
     KERNEL32_GET_PROC(GetConsoleProcessList);
     KERNEL32_GET_PROC(OpenConsoleW);
     KERNEL32_GET_PROC(SetConsoleInputExeNameA);
+    KERNEL32_GET_PROC(VerifyConsoleIoHandle);
 
 #undef KERNEL32_GET_PROC
 }
@@ -169,9 +171,9 @@ static void testCursorInfo(HANDLE hCon)
 
 static void testEmptyWrite(HANDLE hCon)
 {
+    static const char  emptybuf[16];
     COORD              c;
     DWORD              len;
-    const char*        mytest = "";
 
     c.X = c.Y = 0;
     ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left\n");
@@ -195,13 +197,13 @@ static void testEmptyWrite(HANDLE hCon)
     }
 
     len = -1;
-    ok(WriteConsole(hCon, mytest, 0, &len, NULL) != 0 && len == 0, "WriteConsole\n");
+    ok(WriteConsole(hCon, emptybuf, 0, &len, NULL) != 0 && len == 0, "WriteConsole\n");
     okCURSOR(hCon, c);
 
     /* WriteConsole does not halt on a null terminator and is happy to write
      * memory contents beyond the actual size of the buffer. */
     len = -1;
-    ok(WriteConsole(hCon, mytest, 16, &len, NULL) != 0 && len == 16, "WriteConsole\n");
+    ok(WriteConsole(hCon, emptybuf, 16, &len, NULL) != 0 && len == 16, "WriteConsole\n");
     c.X += 16;
     okCURSOR(hCon, c);
 }
@@ -990,6 +992,37 @@ static void test_GetConsoleProcessList(void)
     HeapFree(GetProcessHeap(), 0, list);
 }
 
+static void test_OpenCON(void)
+{
+    static const WCHAR conW[] = {'C','O','N',0};
+    static const DWORD accesses[] = {CREATE_NEW, CREATE_ALWAYS, OPEN_EXISTING,
+                                     OPEN_ALWAYS, TRUNCATE_EXISTING};
+    unsigned            i;
+    HANDLE              h;
+
+    for (i = 0; i < sizeof(accesses) / sizeof(accesses[0]); i++)
+    {
+        h = CreateFileW(conW, GENERIC_WRITE, 0, NULL, accesses[i], 0, NULL);
+        ok(h != INVALID_HANDLE_VALUE, "Expected to open the CON device on write (%x)\n", accesses[i]);
+        CloseHandle(h);
+
+        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
+         * So don't test when disposition is TRUNCATE_EXISTING
+         */
+        if (accesses[i] != TRUNCATE_EXISTING)
+        {
+            ok(h != INVALID_HANDLE_VALUE, "Expected to open the CON device on read (%x)\n", accesses[i]);
+        }
+        CloseHandle(h);
+        h = CreateFileW(conW, GENERIC_READ|GENERIC_WRITE, 0, NULL, accesses[i], 0, NULL);
+        ok(h == INVALID_HANDLE_VALUE, "Expected not to open the CON device on read-write (%x)\n", accesses[i]);
+        ok(GetLastError() == ERROR_FILE_NOT_FOUND, "Unexpected error %x\n", GetLastError());
+    }
+}
+
 static void test_OpenConsoleW(void)
 {
     static const WCHAR coninW[] = {'C','O','N','I','N','$',0};
@@ -1083,11 +1116,1344 @@ static void test_OpenConsoleW(void)
         CloseHandle(ret);
 }
 
+static void test_VerifyConsoleIoHandle( HANDLE handle )
+{
+    BOOL ret;
+    DWORD error;
+
+    if (!pVerifyConsoleIoHandle)
+    {
+        win_skip("VerifyConsoleIoHandle is not available\n");
+        return;
+    }
+
+    /* invalid handle */
+    SetLastError(0xdeadbeef);
+    ret = pVerifyConsoleIoHandle((HANDLE)0xdeadbee0);
+    error = GetLastError();
+    ok(!ret, "expected VerifyConsoleIoHandle to fail\n");
+    ok(error == 0xdeadbeef, "wrong GetLastError() %d\n", error);
+
+    /* invalid handle + 1 */
+    SetLastError(0xdeadbeef);
+    ret = pVerifyConsoleIoHandle((HANDLE)0xdeadbee1);
+    error = GetLastError();
+    ok(!ret, "expected VerifyConsoleIoHandle to fail\n");
+    ok(error == 0xdeadbeef, "wrong GetLastError() %d\n", error);
+
+    /* invalid handle + 2 */
+    SetLastError(0xdeadbeef);
+    ret = pVerifyConsoleIoHandle((HANDLE)0xdeadbee2);
+    error = GetLastError();
+    ok(!ret, "expected VerifyConsoleIoHandle to fail\n");
+    ok(error == 0xdeadbeef, "wrong GetLastError() %d\n", error);
+
+    /* invalid handle + 3 */
+    SetLastError(0xdeadbeef);
+    ret = pVerifyConsoleIoHandle((HANDLE)0xdeadbee3);
+    error = GetLastError();
+    ok(!ret, "expected VerifyConsoleIoHandle to fail\n");
+    ok(error == 0xdeadbeef, "wrong GetLastError() %d\n", error);
+
+    /* valid handle */
+    SetLastError(0xdeadbeef);
+    ret = pVerifyConsoleIoHandle(handle);
+    error = GetLastError();
+    ok(ret, "expected VerifyConsoleIoHandle to succeed\n");
+    ok(error == 0xdeadbeef, "wrong GetLastError() %d\n", error);
+}
+
+static void test_GetSetStdHandle(void)
+{
+    HANDLE handle;
+    DWORD error;
+    BOOL ret;
+
+    /* get invalid std handle */
+    SetLastError(0xdeadbeef);
+    handle = GetStdHandle(42);
+    error = GetLastError();
+    ok(error == ERROR_INVALID_HANDLE || broken(error == ERROR_INVALID_FUNCTION)/* Win9x */,
+       "wrong GetLastError() %d\n", error);
+    ok(handle == INVALID_HANDLE_VALUE, "expected INVALID_HANDLE_VALUE\n");
+
+    /* get valid */
+    SetLastError(0xdeadbeef);
+    handle = GetStdHandle(STD_INPUT_HANDLE);
+    error = GetLastError();
+    ok(error == 0xdeadbeef, "wrong GetLastError() %d\n", error);
+
+    /* set invalid std handle */
+    SetLastError(0xdeadbeef);
+    ret = SetStdHandle(42, handle);
+    error = GetLastError();
+    ok(!ret, "expected SetStdHandle to fail\n");
+    ok(error == ERROR_INVALID_HANDLE || broken(error == ERROR_INVALID_FUNCTION)/* Win9x */,
+       "wrong GetLastError() %d\n", error);
+
+    /* set valid (restore old value) */
+    SetLastError(0xdeadbeef);
+    ret = SetStdHandle(STD_INPUT_HANDLE, handle);
+    error = GetLastError();
+    ok(ret, "expected SetStdHandle to succeed\n");
+    ok(error == 0xdeadbeef, "wrong GetLastError() %d\n", error);
+}
+
+static void test_GetNumberOfConsoleInputEvents(HANDLE input_handle)
+{
+    DWORD count;
+    BOOL ret;
+    int i;
+
+    const struct
+    {
+        HANDLE handle;
+        LPDWORD nrofevents;
+        DWORD last_error;
+    } invalid_table[] =
+    {
+        {NULL,                 NULL,   ERROR_INVALID_HANDLE},
+        {NULL,                 &count, ERROR_INVALID_HANDLE},
+        {INVALID_HANDLE_VALUE, NULL,   ERROR_INVALID_HANDLE},
+        {INVALID_HANDLE_VALUE, &count, ERROR_INVALID_HANDLE},
+    };
+
+    for (i = 0; i < sizeof(invalid_table)/sizeof(invalid_table[0]); i++)
+    {
+        SetLastError(0xdeadbeef);
+        if (invalid_table[i].nrofevents) count = 0xdeadbeef;
+        ret = GetNumberOfConsoleInputEvents(invalid_table[i].handle,
+                                            invalid_table[i].nrofevents);
+        ok(!ret, "[%d] Expected GetNumberOfConsoleInputEvents to return FALSE, got %d\n", i, ret);
+        if (invalid_table[i].nrofevents)
+        {
+            ok(count == 0xdeadbeef,
+               "[%d] Expected output count to be unmodified, got %u\n", i, count);
+        }
+        ok(GetLastError() == invalid_table[i].last_error,
+           "[%d] Expected last error to be %u, got %u\n",
+           i, invalid_table[i].last_error, GetLastError());
+    }
+
+    /* Test crashes on Windows 7. */
+    if (0)
+    {
+        SetLastError(0xdeadbeef);
+        ret = GetNumberOfConsoleInputEvents(input_handle, NULL);
+        ok(!ret, "Expected GetNumberOfConsoleInputEvents to return FALSE, got %d\n", ret);
+        ok(GetLastError() == ERROR_INVALID_ACCESS,
+           "Expected last error to be ERROR_INVALID_ACCESS, got %u\n",
+           GetLastError());
+    }
+
+    count = 0xdeadbeef;
+    ret = GetNumberOfConsoleInputEvents(input_handle, &count);
+    ok(ret == TRUE, "Expected GetNumberOfConsoleInputEvents to return TRUE, got %d\n", ret);
+    ok(count != 0xdeadbeef, "Expected output count to initialized\n");
+}
+
+static void test_WriteConsoleInputA(HANDLE input_handle)
+{
+    INPUT_RECORD event;
+    INPUT_RECORD event_list[5];
+    MOUSE_EVENT_RECORD mouse_event = { {0, 0}, 0, 0, MOUSE_MOVED };
+    KEY_EVENT_RECORD key_event;
+    DWORD count, console_mode;
+    BOOL ret;
+    int i;
+
+    const struct
+    {
+        HANDLE handle;
+        const INPUT_RECORD *buffer;
+        DWORD count;
+        LPDWORD written;
+        DWORD expected_count;
+        DWORD last_error;
+        int win7_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, &count, 0xdeadbeef, ERROR_INVALID_ACCESS},
+        {NULL, &event, 0, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {NULL, &event, 0, &count, 0, ERROR_INVALID_HANDLE},
+        {NULL, &event, 1, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {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, &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, &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},
+    };
+
+    /* Suppress external sources of input events for the duration of the test. */
+    ret = GetConsoleMode(input_handle, &console_mode);
+    ok(ret == TRUE, "Expected GetConsoleMode to return TRUE, got %d\n", ret);
+    if (!ret)
+    {
+        skip("GetConsoleMode failed with last error %u\n", GetLastError());
+        return;
+    }
+
+    ret = SetConsoleMode(input_handle, console_mode & ~(ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT));
+    ok(ret == TRUE, "Expected SetConsoleMode to return TRUE, got %d\n", ret);
+    if (!ret)
+    {
+        skip("SetConsoleMode failed with last error %u\n", GetLastError());
+        return;
+    }
+
+    /* Discard any events queued before the tests. */
+    ret = FlushConsoleInputBuffer(input_handle);
+    ok(ret == TRUE, "Expected FlushConsoleInputBuffer to return TRUE, got %d\n", ret);
+
+    event.EventType = MOUSE_EVENT;
+    event.Event.MouseEvent = mouse_event;
+
+    for (i = 0; i < sizeof(invalid_table)/sizeof(invalid_table[0]); i++)
+    {
+        if (invalid_table[i].win7_crash)
+            continue;
+
+        SetLastError(0xdeadbeef);
+        if (invalid_table[i].written) count = 0xdeadbeef;
+        ret = WriteConsoleInputA(invalid_table[i].handle,
+                                 invalid_table[i].buffer,
+                                 invalid_table[i].count,
+                                 invalid_table[i].written);
+        ok(!ret, "[%d] Expected WriteConsoleInputA to return FALSE, got %d\n", i, ret);
+        if (invalid_table[i].written)
+        {
+            ok(count == invalid_table[i].expected_count,
+               "[%d] Expected output count to be %u, got %u\n",
+               i, invalid_table[i].expected_count, count);
+        }
+        ok(GetLastError() == invalid_table[i].last_error,
+           "[%d] Expected last error to be %u, got %u\n",
+           i, invalid_table[i].last_error, GetLastError());
+    }
+
+    count = 0xdeadbeef;
+    ret = WriteConsoleInputA(input_handle, NULL, 0, &count);
+    ok(ret == TRUE, "Expected WriteConsoleInputA to return TRUE, got %d\n", ret);
+    ok(count == 0, "Expected count to be 0, got %u\n", count);
+
+    count = 0xdeadbeef;
+    ret = WriteConsoleInputA(input_handle, &event, 0, &count);
+    ok(ret == TRUE, "Expected WriteConsoleInputA to return TRUE, got %d\n", ret);
+    ok(count == 0, "Expected count to be 0, got %u\n", count);
+
+    count = 0xdeadbeef;
+    ret = WriteConsoleInputA(input_handle, &event, 1, &count);
+    ok(ret == TRUE, "Expected WriteConsoleInputA to return TRUE, got %d\n", ret);
+    ok(count == 1, "Expected count to be 1, got %u\n", count);
+
+    ret = FlushConsoleInputBuffer(input_handle);
+    ok(ret == TRUE, "Expected FlushConsoleInputBuffer to return TRUE, got %d\n", ret);
+
+    /* Writing a single mouse event doesn't seem to affect the count if an adjacent mouse event is already queued. */
+    event.EventType = MOUSE_EVENT;
+    event.Event.MouseEvent = mouse_event;
+
+    ret = WriteConsoleInputA(input_handle, &event, 1, &count);
+    ok(ret == TRUE, "Expected WriteConsoleInputA to return TRUE, got %d\n", ret);
+    ok(count == 1, "Expected count to be 1, got %u\n", count);
+
+    ret = GetNumberOfConsoleInputEvents(input_handle, &count);
+    ok(ret == TRUE, "Expected GetNumberOfConsoleInputEvents to return TRUE, got %d\n", ret);
+    ok(count == 1, "Expected count to be 1, got %u\n", count);
+
+    ret = WriteConsoleInputA(input_handle, &event, 1, &count);
+    ok(ret == TRUE, "Expected WriteConsoleInputA to return TRUE, got %d\n", ret);
+    ok(count == 1, "Expected count to be 1, got %u\n", count);
+
+    ret = GetNumberOfConsoleInputEvents(input_handle, &count);
+    ok(ret == TRUE, "Expected GetNumberOfConsoleInputEvents to return TRUE, got %d\n", ret);
+    todo_wine
+    ok(count == 1, "Expected count to be 1, got %u\n", count);
+
+    ret = FlushConsoleInputBuffer(input_handle);
+    ok(ret == TRUE, "Expected FlushConsoleInputBuffer to return TRUE, got %d\n", ret);
+
+    for (i = 0; i < sizeof(event_list)/sizeof(event_list[0]); i++)
+    {
+        event_list[i].EventType = MOUSE_EVENT;
+        event_list[i].Event.MouseEvent = mouse_event;
+    }
+
+    /* Writing consecutive chunks of mouse events appears to work. */
+    ret = WriteConsoleInputA(input_handle, event_list, sizeof(event_list)/sizeof(event_list[0]), &count);
+    ok(ret == TRUE, "Expected WriteConsoleInputA to return TRUE, got %d\n", ret);
+    ok(count == sizeof(event_list)/sizeof(event_list[0]),
+       "Expected count to be event list length, got %u\n", count);
+
+    ret = GetNumberOfConsoleInputEvents(input_handle, &count);
+    ok(ret == TRUE, "Expected GetNumberOfConsoleInputEvents to return TRUE, got %d\n", ret);
+    ok(count == sizeof(event_list)/sizeof(event_list[0]),
+       "Expected count to be event list length, got %u\n", count);
+
+    ret = WriteConsoleInputA(input_handle, event_list, sizeof(event_list)/sizeof(event_list[0]), &count);
+    ok(ret == TRUE, "Expected WriteConsoleInputA to return TRUE, got %d\n", ret);
+    ok(count == sizeof(event_list)/sizeof(event_list[0]),
+       "Expected count to be event list length, got %u\n", count);
+
+    ret = GetNumberOfConsoleInputEvents(input_handle, &count);
+    ok(ret == TRUE, "Expected GetNumberOfConsoleInputEvents to return TRUE, got %d\n", ret);
+    ok(count == 2*sizeof(event_list)/sizeof(event_list[0]),
+       "Expected count to be twice event list length, got %u\n", count);
+
+    /* Again, writing a single mouse event with adjacent mouse events queued doesn't appear to affect the count. */
+    ret = WriteConsoleInputA(input_handle, &event, 1, &count);
+    ok(ret == TRUE, "Expected WriteConsoleInputA to return TRUE, got %d\n", ret);
+    ok(count == 1, "Expected count to be 1, got %u\n", count);
+
+    ret = GetNumberOfConsoleInputEvents(input_handle, &count);
+    ok(ret == TRUE, "Expected GetNumberOfConsoleInputEvents to return TRUE, got %d\n", ret);
+    todo_wine
+    ok(count == 2*sizeof(event_list)/sizeof(event_list[0]),
+       "Expected count to be twice event list length, got %u\n", count);
+
+    ret = FlushConsoleInputBuffer(input_handle);
+    ok(ret == TRUE, "Expected FlushConsoleInputBuffer to return TRUE, got %d\n", ret);
+
+    key_event.bKeyDown = FALSE;
+    key_event.wRepeatCount = 0;
+    key_event.wVirtualKeyCode = VK_SPACE;
+    key_event.wVirtualScanCode = VK_SPACE;
+    key_event.uChar.AsciiChar = ' ';
+    key_event.dwControlKeyState = 0;
+
+    event.EventType = KEY_EVENT;
+    event.Event.KeyEvent = key_event;
+
+    /* Key events don't exhibit the same behavior as mouse events. */
+    ret = WriteConsoleInputA(input_handle, &event, 1, &count);
+    ok(ret == TRUE, "Expected WriteConsoleInputA to return TRUE, got %d\n", ret);
+    ok(count == 1, "Expected count to be 1, got %u\n", count);
+
+    ret = GetNumberOfConsoleInputEvents(input_handle, &count);
+    ok(ret == TRUE, "Expected GetNumberOfConsoleInputEvents to return TRUE, got %d\n", ret);
+    ok(count == 1, "Expected count to be 1, got %u\n", count);
+
+    ret = WriteConsoleInputA(input_handle, &event, 1, &count);
+    ok(ret == TRUE, "Expected WriteConsoleInputA to return TRUE, got %d\n", ret);
+    ok(count == 1, "Expected count to be 1, got %u\n", count);
+
+    ret = GetNumberOfConsoleInputEvents(input_handle, &count);
+    ok(ret == TRUE, "Expected GetNumberOfConsoleInputEvents to return TRUE, got %d\n", ret);
+    ok(count == 2, "Expected count to be 2, got %u\n", count);
+
+    ret = FlushConsoleInputBuffer(input_handle);
+    ok(ret == TRUE, "Expected FlushConsoleInputBuffer to return TRUE, got %d\n", ret);
+
+    /* Try interleaving mouse and key events. */
+    event.EventType = MOUSE_EVENT;
+    event.Event.MouseEvent = mouse_event;
+
+    ret = WriteConsoleInputA(input_handle, &event, 1, &count);
+    ok(ret == TRUE, "Expected WriteConsoleInputA to return TRUE, got %d\n", ret);
+    ok(count == 1, "Expected count to be 1, got %u\n", count);
+
+    ret = GetNumberOfConsoleInputEvents(input_handle, &count);
+    ok(ret == TRUE, "Expected GetNumberOfConsoleInputEvents to return TRUE, got %d\n", ret);
+    ok(count == 1, "Expected count to be 1, got %u\n", count);
+
+    event.EventType = KEY_EVENT;
+    event.Event.KeyEvent = key_event;
+
+    ret = WriteConsoleInputA(input_handle, &event, 1, &count);
+    ok(ret == TRUE, "Expected WriteConsoleInputA to return TRUE, got %d\n", ret);
+    ok(count == 1, "Expected count to be 1, got %u\n", count);
+
+    ret = GetNumberOfConsoleInputEvents(input_handle, &count);
+    ok(ret == TRUE, "Expected GetNumberOfConsoleInputEvents to return TRUE, got %d\n", ret);
+    ok(count == 2, "Expected count to be 2, got %u\n", count);
+
+    event.EventType = MOUSE_EVENT;
+    event.Event.MouseEvent = mouse_event;
+
+    ret = WriteConsoleInputA(input_handle, &event, 1, &count);
+    ok(ret == TRUE, "Expected WriteConsoleInputA to return TRUE, got %d\n", ret);
+    ok(count == 1, "Expected count to be 1, got %u\n", count);
+
+    ret = GetNumberOfConsoleInputEvents(input_handle, &count);
+    ok(ret == TRUE, "Expected GetNumberOfConsoleInputEvents to return TRUE, got %d\n", ret);
+    ok(count == 3, "Expected count to be 3, got %u\n", count);
+
+    /* Restore the old console mode. */
+    ret = SetConsoleMode(input_handle, console_mode);
+    ok(ret == TRUE, "Expected SetConsoleMode to return TRUE, got %d\n", ret);
+}
+
+static void test_WriteConsoleInputW(HANDLE input_handle)
+{
+    INPUT_RECORD event;
+    INPUT_RECORD event_list[5];
+    MOUSE_EVENT_RECORD mouse_event = { {0, 0}, 0, 0, MOUSE_MOVED };
+    KEY_EVENT_RECORD key_event;
+    DWORD count, console_mode;
+    BOOL ret;
+    int i;
+
+    const struct
+    {
+        HANDLE handle;
+        const INPUT_RECORD *buffer;
+        DWORD count;
+        LPDWORD written;
+        DWORD expected_count;
+        DWORD last_error;
+        int win7_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, &count, 0xdeadbeef, ERROR_INVALID_ACCESS},
+        {NULL, &event, 0, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {NULL, &event, 0, &count, 0, ERROR_INVALID_HANDLE},
+        {NULL, &event, 1, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {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, &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, &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},
+    };
+
+    /* Suppress external sources of input events for the duration of the test. */
+    ret = GetConsoleMode(input_handle, &console_mode);
+    ok(ret == TRUE, "Expected GetConsoleMode to return TRUE, got %d\n", ret);
+    if (!ret)
+    {
+        skip("GetConsoleMode failed with last error %u\n", GetLastError());
+        return;
+    }
+
+    ret = SetConsoleMode(input_handle, console_mode & ~(ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT));
+    ok(ret == TRUE, "Expected SetConsoleMode to return TRUE, got %d\n", ret);
+    if (!ret)
+    {
+        skip("SetConsoleMode failed with last error %u\n", GetLastError());
+        return;
+    }
+
+    /* Discard any events queued before the tests. */
+    ret = FlushConsoleInputBuffer(input_handle);
+    ok(ret == TRUE, "Expected FlushConsoleInputBuffer to return TRUE, got %d\n", ret);
+
+    event.EventType = MOUSE_EVENT;
+    event.Event.MouseEvent = mouse_event;
+
+    for (i = 0; i < sizeof(invalid_table)/sizeof(invalid_table[0]); i++)
+    {
+        if (invalid_table[i].win7_crash)
+            continue;
+
+        SetLastError(0xdeadbeef);
+        if (invalid_table[i].written) count = 0xdeadbeef;
+        ret = WriteConsoleInputW(invalid_table[i].handle,
+                                 invalid_table[i].buffer,
+                                 invalid_table[i].count,
+                                 invalid_table[i].written);
+        ok(!ret, "[%d] Expected WriteConsoleInputW to return FALSE, got %d\n", i, ret);
+        if (invalid_table[i].written)
+        {
+            ok(count == invalid_table[i].expected_count,
+               "[%d] Expected output count to be %u, got %u\n",
+               i, invalid_table[i].expected_count, count);
+        }
+        ok(GetLastError() == invalid_table[i].last_error,
+           "[%d] Expected last error to be %u, got %u\n",
+           i, invalid_table[i].last_error, GetLastError());
+    }
+
+    count = 0xdeadbeef;
+    ret = WriteConsoleInputW(input_handle, NULL, 0, &count);
+    ok(ret == TRUE, "Expected WriteConsoleInputW to return TRUE, got %d\n", ret);
+    ok(count == 0, "Expected count to be 0, got %u\n", count);
+
+    count = 0xdeadbeef;
+    ret = WriteConsoleInputW(input_handle, &event, 0, &count);
+    ok(ret == TRUE, "Expected WriteConsoleInputW to return TRUE, got %d\n", ret);
+    ok(count == 0, "Expected count to be 0, got %u\n", count);
+
+    count = 0xdeadbeef;
+    ret = WriteConsoleInputW(input_handle, &event, 1, &count);
+    ok(ret == TRUE, "Expected WriteConsoleInputW to return TRUE, got %d\n", ret);
+    ok(count == 1, "Expected count to be 1, got %u\n", count);
+
+    ret = FlushConsoleInputBuffer(input_handle);
+    ok(ret == TRUE, "Expected FlushConsoleInputBuffer to return TRUE, got %d\n", ret);
+
+    /* Writing a single mouse event doesn't seem to affect the count if an adjacent mouse event is already queued. */
+    event.EventType = MOUSE_EVENT;
+    event.Event.MouseEvent = mouse_event;
+
+    ret = WriteConsoleInputW(input_handle, &event, 1, &count);
+    ok(ret == TRUE, "Expected WriteConsoleInputW to return TRUE, got %d\n", ret);
+    ok(count == 1, "Expected count to be 1, got %u\n", count);
+
+    ret = GetNumberOfConsoleInputEvents(input_handle, &count);
+    ok(ret == TRUE, "Expected GetNumberOfConsoleInputEvents to return TRUE, got %d\n", ret);
+    ok(count == 1, "Expected count to be 1, got %u\n", count);
+
+    ret = WriteConsoleInputW(input_handle, &event, 1, &count);
+    ok(ret == TRUE, "Expected WriteConsoleInputW to return TRUE, got %d\n", ret);
+    ok(count == 1, "Expected count to be 1, got %u\n", count);
+
+    ret = GetNumberOfConsoleInputEvents(input_handle, &count);
+    ok(ret == TRUE, "Expected GetNumberOfConsoleInputEvents to return TRUE, got %d\n", ret);
+    todo_wine
+    ok(count == 1, "Expected count to be 1, got %u\n", count);
+
+    ret = FlushConsoleInputBuffer(input_handle);
+    ok(ret == TRUE, "Expected FlushConsoleInputBuffer to return TRUE, got %d\n", ret);
+
+    for (i = 0; i < sizeof(event_list)/sizeof(event_list[0]); i++)
+    {
+        event_list[i].EventType = MOUSE_EVENT;
+        event_list[i].Event.MouseEvent = mouse_event;
+    }
+
+    /* Writing consecutive chunks of mouse events appears to work. */
+    ret = WriteConsoleInputW(input_handle, event_list, sizeof(event_list)/sizeof(event_list[0]), &count);
+    ok(ret == TRUE, "Expected WriteConsoleInputW to return TRUE, got %d\n", ret);
+    ok(count == sizeof(event_list)/sizeof(event_list[0]),
+       "Expected count to be event list length, got %u\n", count);
+
+    ret = GetNumberOfConsoleInputEvents(input_handle, &count);
+    ok(ret == TRUE, "Expected GetNumberOfConsoleInputEvents to return TRUE, got %d\n", ret);
+    ok(count == sizeof(event_list)/sizeof(event_list[0]),
+       "Expected count to be event list length, got %u\n", count);
+
+    ret = WriteConsoleInputW(input_handle, event_list, sizeof(event_list)/sizeof(event_list[0]), &count);
+    ok(ret == TRUE, "Expected WriteConsoleInputW to return TRUE, got %d\n", ret);
+    ok(count == sizeof(event_list)/sizeof(event_list[0]),
+       "Expected count to be event list length, got %u\n", count);
+
+    ret = GetNumberOfConsoleInputEvents(input_handle, &count);
+    ok(ret == TRUE, "Expected GetNumberOfConsoleInputEvents to return TRUE, got %d\n", ret);
+    ok(count == 2*sizeof(event_list)/sizeof(event_list[0]),
+       "Expected count to be twice event list length, got %u\n", count);
+
+    /* Again, writing a single mouse event with adjacent mouse events queued doesn't appear to affect the count. */
+    ret = WriteConsoleInputW(input_handle, &event, 1, &count);
+    ok(ret == TRUE, "Expected WriteConsoleInputW to return TRUE, got %d\n", ret);
+    ok(count == 1, "Expected count to be 1, got %u\n", count);
+
+    ret = GetNumberOfConsoleInputEvents(input_handle, &count);
+    ok(ret == TRUE, "Expected GetNumberOfConsoleInputEvents to return TRUE, got %d\n", ret);
+    todo_wine
+    ok(count == 2*sizeof(event_list)/sizeof(event_list[0]),
+       "Expected count to be twice event list length, got %u\n", count);
+
+    ret = FlushConsoleInputBuffer(input_handle);
+    ok(ret == TRUE, "Expected FlushConsoleInputBuffer to return TRUE, got %d\n", ret);
+
+    key_event.bKeyDown = FALSE;
+    key_event.wRepeatCount = 0;
+    key_event.wVirtualKeyCode = VK_SPACE;
+    key_event.wVirtualScanCode = VK_SPACE;
+    key_event.uChar.UnicodeChar = ' ';
+    key_event.dwControlKeyState = 0;
+
+    event.EventType = KEY_EVENT;
+    event.Event.KeyEvent = key_event;
+
+    /* Key events don't exhibit the same behavior as mouse events. */
+    ret = WriteConsoleInputW(input_handle, &event, 1, &count);
+    ok(ret == TRUE, "Expected WriteConsoleInputW to return TRUE, got %d\n", ret);
+    ok(count == 1, "Expected count to be 1, got %u\n", count);
+
+    ret = GetNumberOfConsoleInputEvents(input_handle, &count);
+    ok(ret == TRUE, "Expected GetNumberOfConsoleInputEvents to return TRUE, got %d\n", ret);
+    ok(count == 1, "Expected count to be 1, got %u\n", count);
+
+    ret = WriteConsoleInputW(input_handle, &event, 1, &count);
+    ok(ret == TRUE, "Expected WriteConsoleInputW to return TRUE, got %d\n", ret);
+    ok(count == 1, "Expected count to be 1, got %u\n", count);
+
+    ret = GetNumberOfConsoleInputEvents(input_handle, &count);
+    ok(ret == TRUE, "Expected GetNumberOfConsoleInputEvents to return TRUE, got %d\n", ret);
+    ok(count == 2, "Expected count to be 2, got %u\n", count);
+
+    ret = FlushConsoleInputBuffer(input_handle);
+    ok(ret == TRUE, "Expected FlushConsoleInputBuffer to return TRUE, got %d\n", ret);
+
+    /* Try interleaving mouse and key events. */
+    event.EventType = MOUSE_EVENT;
+    event.Event.MouseEvent = mouse_event;
+
+    ret = WriteConsoleInputW(input_handle, &event, 1, &count);
+    ok(ret == TRUE, "Expected WriteConsoleInputW to return TRUE, got %d\n", ret);
+    ok(count == 1, "Expected count to be 1, got %u\n", count);
+
+    ret = GetNumberOfConsoleInputEvents(input_handle, &count);
+    ok(ret == TRUE, "Expected GetNumberOfConsoleInputEvents to return TRUE, got %d\n", ret);
+    ok(count == 1, "Expected count to be 1, got %u\n", count);
+
+    event.EventType = KEY_EVENT;
+    event.Event.KeyEvent = key_event;
+
+    ret = WriteConsoleInputW(input_handle, &event, 1, &count);
+    ok(ret == TRUE, "Expected WriteConsoleInputW to return TRUE, got %d\n", ret);
+    ok(count == 1, "Expected count to be 1, got %u\n", count);
+
+    ret = GetNumberOfConsoleInputEvents(input_handle, &count);
+    ok(ret == TRUE, "Expected GetNumberOfConsoleInputEvents to return TRUE, got %d\n", ret);
+    ok(count == 2, "Expected count to be 2, got %u\n", count);
+
+    event.EventType = MOUSE_EVENT;
+    event.Event.MouseEvent = mouse_event;
+
+    ret = WriteConsoleInputW(input_handle, &event, 1, &count);
+    ok(ret == TRUE, "Expected WriteConsoleInputW to return TRUE, got %d\n", ret);
+    ok(count == 1, "Expected count to be 1, got %u\n", count);
+
+    ret = GetNumberOfConsoleInputEvents(input_handle, &count);
+    ok(ret == TRUE, "Expected GetNumberOfConsoleInputEvents to return TRUE, got %d\n", ret);
+    ok(count == 3, "Expected count to be 3, got %u\n", count);
+
+    /* Restore the old console mode. */
+    ret = SetConsoleMode(input_handle, console_mode);
+    ok(ret == TRUE, "Expected SetConsoleMode to return TRUE, got %d\n", ret);
+}
+
+static void test_WriteConsoleOutputCharacterA(HANDLE output_handle)
+{
+    static const char output[] = {'a', 0};
+
+    COORD origin = {0, 0};
+    DWORD count;
+    BOOL ret;
+    int i;
+
+    const struct
+    {
+        HANDLE hConsoleOutput;
+        LPCSTR str;
+        DWORD length;
+        COORD coord;
+        LPDWORD lpNumCharsWritten;
+        DWORD expected_count;
+        DWORD last_error;
+        int win7_crash;
+    } invalid_table[] =
+    {
+        {NULL, NULL, 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {NULL, NULL, 0, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {NULL, NULL, 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {NULL, NULL, 1, origin, &count, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {NULL, output, 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {NULL, output, 0, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {NULL, output, 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {NULL, output, 1, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {INVALID_HANDLE_VALUE, NULL, 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {INVALID_HANDLE_VALUE, NULL, 0, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {INVALID_HANDLE_VALUE, NULL, 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {INVALID_HANDLE_VALUE, NULL, 1, origin, &count, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {INVALID_HANDLE_VALUE, output, 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {INVALID_HANDLE_VALUE, output, 0, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {INVALID_HANDLE_VALUE, output, 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {INVALID_HANDLE_VALUE, output, 1, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {output_handle, NULL, 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {output_handle, NULL, 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {output_handle, NULL, 1, origin, &count, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {output_handle, output, 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {output_handle, output, 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+    };
+
+    for (i = 0; i < sizeof(invalid_table)/sizeof(invalid_table[0]); i++)
+    {
+        if (invalid_table[i].win7_crash)
+            continue;
+
+        SetLastError(0xdeadbeef);
+        if (invalid_table[i].lpNumCharsWritten) count = 0xdeadbeef;
+        ret = WriteConsoleOutputCharacterA(invalid_table[i].hConsoleOutput,
+                                           invalid_table[i].str,
+                                           invalid_table[i].length,
+                                           invalid_table[i].coord,
+                                           invalid_table[i].lpNumCharsWritten);
+        ok(!ret, "[%d] Expected WriteConsoleOutputCharacterA to return FALSE, got %d\n", i, ret);
+        if (invalid_table[i].lpNumCharsWritten)
+        {
+            ok(count == invalid_table[i].expected_count,
+               "[%d] Expected count to be %u, got %u\n",
+               i, invalid_table[i].expected_count, count);
+        }
+        ok(GetLastError() == invalid_table[i].last_error,
+           "[%d] Expected last error to be %u, got %u\n",
+           i, invalid_table[i].last_error, GetLastError());
+    }
+
+    count = 0xdeadbeef;
+    ret = WriteConsoleOutputCharacterA(output_handle, NULL, 0, origin, &count);
+    ok(ret == TRUE, "Expected WriteConsoleOutputCharacterA to return TRUE, got %d\n", ret);
+    ok(count == 0, "Expected count to be 0, got %u\n", count);
+
+    count = 0xdeadbeef;
+    ret = WriteConsoleOutputCharacterA(output_handle, output, 0, origin, &count);
+    ok(ret == TRUE, "Expected WriteConsoleOutputCharacterA to return TRUE, got %d\n", ret);
+    ok(count == 0, "Expected count to be 0, got %u\n", count);
+
+    count = 0xdeadbeef;
+    ret = WriteConsoleOutputCharacterA(output_handle, output, 1, origin, &count);
+    ok(ret == TRUE, "Expected WriteConsoleOutputCharacterA to return TRUE, got %d\n", ret);
+    ok(count == 1, "Expected count to be 1, got %u\n", count);
+}
+
+static void test_WriteConsoleOutputCharacterW(HANDLE output_handle)
+{
+    static const WCHAR outputW[] = {'a',0};
+
+    COORD origin = {0, 0};
+    DWORD count;
+    BOOL ret;
+    int i;
+
+    const struct
+    {
+        HANDLE hConsoleOutput;
+        LPCWSTR str;
+        DWORD length;
+        COORD coord;
+        LPDWORD lpNumCharsWritten;
+        DWORD expected_count;
+        DWORD last_error;
+        int win7_crash;
+    } invalid_table[] =
+    {
+        {NULL, NULL, 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {NULL, NULL, 0, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {NULL, NULL, 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {NULL, NULL, 1, origin, &count, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {NULL, outputW, 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {NULL, outputW, 0, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {NULL, outputW, 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {NULL, outputW, 1, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {INVALID_HANDLE_VALUE, NULL, 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {INVALID_HANDLE_VALUE, NULL, 0, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {INVALID_HANDLE_VALUE, NULL, 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {INVALID_HANDLE_VALUE, NULL, 1, origin, &count, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {INVALID_HANDLE_VALUE, outputW, 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {INVALID_HANDLE_VALUE, outputW, 0, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {INVALID_HANDLE_VALUE, outputW, 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {INVALID_HANDLE_VALUE, outputW, 1, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {output_handle, NULL, 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {output_handle, NULL, 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {output_handle, NULL, 1, origin, &count, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {output_handle, outputW, 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {output_handle, outputW, 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+    };
+
+    for (i = 0; i < sizeof(invalid_table)/sizeof(invalid_table[0]); i++)
+    {
+        if (invalid_table[i].win7_crash)
+            continue;
+
+        SetLastError(0xdeadbeef);
+        if (invalid_table[i].lpNumCharsWritten) count = 0xdeadbeef;
+        ret = WriteConsoleOutputCharacterW(invalid_table[i].hConsoleOutput,
+                                           invalid_table[i].str,
+                                           invalid_table[i].length,
+                                           invalid_table[i].coord,
+                                           invalid_table[i].lpNumCharsWritten);
+        ok(!ret, "[%d] Expected WriteConsoleOutputCharacterW to return FALSE, got %d\n", i, ret);
+        if (invalid_table[i].lpNumCharsWritten)
+        {
+            ok(count == invalid_table[i].expected_count,
+               "[%d] Expected count to be %u, got %u\n",
+               i, invalid_table[i].expected_count, count);
+        }
+        ok(GetLastError() == invalid_table[i].last_error,
+           "[%d] Expected last error to be %u, got %u\n",
+           i, invalid_table[i].last_error, GetLastError());
+    }
+
+    count = 0xdeadbeef;
+    ret = WriteConsoleOutputCharacterW(output_handle, NULL, 0, origin, &count);
+    ok(ret == TRUE, "Expected WriteConsoleOutputCharacterW to return TRUE, got %d\n", ret);
+    ok(count == 0, "Expected count to be 0, got %u\n", count);
+
+    count = 0xdeadbeef;
+    ret = WriteConsoleOutputCharacterW(output_handle, outputW, 0, origin, &count);
+    ok(ret == TRUE, "Expected WriteConsoleOutputCharacterW to return TRUE, got %d\n", ret);
+    ok(count == 0, "Expected count to be 0, got %u\n", count);
+
+    count = 0xdeadbeef;
+    ret = WriteConsoleOutputCharacterW(output_handle, outputW, 1, origin, &count);
+    ok(ret == TRUE, "Expected WriteConsoleOutputCharacterW to return TRUE, got %d\n", ret);
+    ok(count == 1, "Expected count to be 1, got %u\n", count);
+}
+
+static void test_WriteConsoleOutputAttribute(HANDLE output_handle)
+{
+    WORD attr = FOREGROUND_BLUE;
+    COORD origin = {0, 0};
+    DWORD count;
+    BOOL ret;
+    int i;
+
+    const struct
+    {
+        HANDLE hConsoleOutput;
+        CONST WORD *attr;
+        DWORD length;
+        COORD coord;
+        LPDWORD lpNumAttrsWritten;
+        DWORD expected_count;
+        DWORD last_error;
+        int win7_crash;
+    } invalid_table[] =
+    {
+        {NULL, NULL, 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {NULL, NULL, 0, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {NULL, NULL, 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {NULL, NULL, 1, origin, &count, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {NULL, &attr, 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {NULL, &attr, 0, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {NULL, &attr, 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {NULL, &attr, 1, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {INVALID_HANDLE_VALUE, NULL, 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {INVALID_HANDLE_VALUE, NULL, 0, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {INVALID_HANDLE_VALUE, NULL, 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {INVALID_HANDLE_VALUE, NULL, 1, origin, &count, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {INVALID_HANDLE_VALUE, &attr, 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {INVALID_HANDLE_VALUE, &attr, 0, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {INVALID_HANDLE_VALUE, &attr, 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {INVALID_HANDLE_VALUE, &attr, 1, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {output_handle, NULL, 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {output_handle, NULL, 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {output_handle, NULL, 1, origin, &count, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {output_handle, &attr, 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {output_handle, &attr, 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+    };
+
+    for (i = 0; i < sizeof(invalid_table)/sizeof(invalid_table[0]); i++)
+    {
+        if (invalid_table[i].win7_crash)
+            continue;
+
+        SetLastError(0xdeadbeef);
+        if (invalid_table[i].lpNumAttrsWritten) count = 0xdeadbeef;
+        ret = WriteConsoleOutputAttribute(invalid_table[i].hConsoleOutput,
+                                          invalid_table[i].attr,
+                                          invalid_table[i].length,
+                                          invalid_table[i].coord,
+                                          invalid_table[i].lpNumAttrsWritten);
+        ok(!ret, "[%d] Expected WriteConsoleOutputAttribute to return FALSE, got %d\n", i, ret);
+        if (invalid_table[i].lpNumAttrsWritten)
+        {
+            ok(count == invalid_table[i].expected_count,
+               "[%d] Expected count to be %u, got %u\n",
+               i, invalid_table[i].expected_count, count);
+        }
+        ok(GetLastError() == invalid_table[i].last_error,
+           "[%d] Expected last error to be %u, got %u\n",
+           i, invalid_table[i].last_error, GetLastError());
+    }
+
+    count = 0xdeadbeef;
+    ret = WriteConsoleOutputAttribute(output_handle, NULL, 0, origin, &count);
+    ok(ret == TRUE, "Expected WriteConsoleOutputAttribute to return TRUE, got %d\n", ret);
+    ok(count == 0, "Expected count to be 0, got %u\n", count);
+
+    count = 0xdeadbeef;
+    ret = WriteConsoleOutputAttribute(output_handle, &attr, 0, origin, &count);
+    ok(ret == TRUE, "Expected WriteConsoleOutputAttribute to return TRUE, got %d\n", ret);
+    ok(count == 0, "Expected count to be 0, got %u\n", count);
+
+    count = 0xdeadbeef;
+    ret = WriteConsoleOutputAttribute(output_handle, &attr, 1, origin, &count);
+    ok(ret == TRUE, "Expected WriteConsoleOutputAttribute to return TRUE, got %d\n", ret);
+    ok(count == 1, "Expected count to be 1, got %u\n", count);
+}
+
+static void test_FillConsoleOutputCharacterA(HANDLE output_handle)
+{
+    COORD origin = {0, 0};
+    DWORD count;
+    BOOL ret;
+    int i;
+
+    const struct
+    {
+        HANDLE hConsoleOutput;
+        CHAR ch;
+        DWORD length;
+        COORD coord;
+        LPDWORD lpNumCharsWritten;
+        DWORD expected_count;
+        DWORD last_error;
+        int win7_crash;
+    } invalid_table[] =
+    {
+        {NULL, 'a', 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {NULL, 'a', 0, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {NULL, 'a', 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {NULL, 'a', 1, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {INVALID_HANDLE_VALUE, 'a', 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {INVALID_HANDLE_VALUE, 'a', 0, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {INVALID_HANDLE_VALUE, 'a', 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {INVALID_HANDLE_VALUE, 'a', 1, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {output_handle, 'a', 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {output_handle, 'a', 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+    };
+
+    for (i = 0; i < sizeof(invalid_table)/sizeof(invalid_table[0]); i++)
+    {
+        if (invalid_table[i].win7_crash)
+            continue;
+
+        SetLastError(0xdeadbeef);
+        if (invalid_table[i].lpNumCharsWritten) count = 0xdeadbeef;
+        ret = FillConsoleOutputCharacterA(invalid_table[i].hConsoleOutput,
+                                          invalid_table[i].ch,
+                                          invalid_table[i].length,
+                                          invalid_table[i].coord,
+                                          invalid_table[i].lpNumCharsWritten);
+        ok(!ret, "[%d] Expected FillConsoleOutputCharacterA to return FALSE, got %d\n", i, ret);
+        if (invalid_table[i].lpNumCharsWritten)
+        {
+            ok(count == invalid_table[i].expected_count,
+               "[%d] Expected count to be %u, got %u\n",
+               i, invalid_table[i].expected_count, count);
+        }
+        ok(GetLastError() == invalid_table[i].last_error,
+           "[%d] Expected last error to be %u, got %u\n",
+           i, invalid_table[i].last_error, GetLastError());
+    }
+
+    count = 0xdeadbeef;
+    ret = FillConsoleOutputCharacterA(output_handle, 'a', 0, origin, &count);
+    ok(ret == TRUE, "Expected FillConsoleOutputCharacterA to return TRUE, got %d\n", ret);
+    ok(count == 0, "Expected count to be 0, got %u\n", count);
+
+    count = 0xdeadbeef;
+    ret = FillConsoleOutputCharacterA(output_handle, 'a', 1, origin, &count);
+    ok(ret == TRUE, "Expected FillConsoleOutputCharacterA to return TRUE, got %d\n", ret);
+    ok(count == 1, "Expected count to be 1, got %u\n", count);
+}
+
+static void test_FillConsoleOutputCharacterW(HANDLE output_handle)
+{
+    COORD origin = {0, 0};
+    DWORD count;
+    BOOL ret;
+    int i;
+
+    const struct
+    {
+        HANDLE hConsoleOutput;
+        WCHAR ch;
+        DWORD length;
+        COORD coord;
+        LPDWORD lpNumCharsWritten;
+        DWORD expected_count;
+        DWORD last_error;
+        int win7_crash;
+    } invalid_table[] =
+    {
+        {NULL, 'a', 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {NULL, 'a', 0, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {NULL, 'a', 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {NULL, 'a', 1, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {INVALID_HANDLE_VALUE, 'a', 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {INVALID_HANDLE_VALUE, 'a', 0, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {INVALID_HANDLE_VALUE, 'a', 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {INVALID_HANDLE_VALUE, 'a', 1, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {output_handle, 'a', 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {output_handle, 'a', 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+    };
+
+    for (i = 0; i < sizeof(invalid_table)/sizeof(invalid_table[0]); i++)
+    {
+        if (invalid_table[i].win7_crash)
+            continue;
+
+        SetLastError(0xdeadbeef);
+        if (invalid_table[i].lpNumCharsWritten) count = 0xdeadbeef;
+        ret = FillConsoleOutputCharacterW(invalid_table[i].hConsoleOutput,
+                                          invalid_table[i].ch,
+                                          invalid_table[i].length,
+                                          invalid_table[i].coord,
+                                          invalid_table[i].lpNumCharsWritten);
+        ok(!ret, "[%d] Expected FillConsoleOutputCharacterW to return FALSE, got %d\n", i, ret);
+        if (invalid_table[i].lpNumCharsWritten)
+        {
+            ok(count == invalid_table[i].expected_count,
+               "[%d] Expected count to be %u, got %u\n",
+               i, invalid_table[i].expected_count, count);
+        }
+        ok(GetLastError() == invalid_table[i].last_error,
+           "[%d] Expected last error to be %u, got %u\n",
+           i, invalid_table[i].last_error, GetLastError());
+    }
+
+    count = 0xdeadbeef;
+    ret = FillConsoleOutputCharacterW(output_handle, 'a', 0, origin, &count);
+    ok(ret == TRUE, "Expected FillConsoleOutputCharacterW to return TRUE, got %d\n", ret);
+    ok(count == 0, "Expected count to be 0, got %u\n", count);
+
+    count = 0xdeadbeef;
+    ret = FillConsoleOutputCharacterW(output_handle, 'a', 1, origin, &count);
+    ok(ret == TRUE, "Expected FillConsoleOutputCharacterW to return TRUE, got %d\n", ret);
+    ok(count == 1, "Expected count to be 1, got %u\n", count);
+}
+
+static void test_FillConsoleOutputAttribute(HANDLE output_handle)
+{
+    COORD origin = {0, 0};
+    DWORD count;
+    BOOL ret;
+    int i;
+
+    const struct
+    {
+        HANDLE hConsoleOutput;
+        WORD attr;
+        DWORD length;
+        COORD coord;
+        LPDWORD lpNumAttrsWritten;
+        DWORD expected_count;
+        DWORD last_error;
+        int win7_crash;
+    } invalid_table[] =
+    {
+        {NULL, FOREGROUND_BLUE, 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {NULL, FOREGROUND_BLUE, 0, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {NULL, FOREGROUND_BLUE, 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {NULL, FOREGROUND_BLUE, 1, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {INVALID_HANDLE_VALUE, FOREGROUND_BLUE, 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {INVALID_HANDLE_VALUE, FOREGROUND_BLUE, 0, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {INVALID_HANDLE_VALUE, FOREGROUND_BLUE, 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {INVALID_HANDLE_VALUE, FOREGROUND_BLUE, 1, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {output_handle, FOREGROUND_BLUE, 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {output_handle, FOREGROUND_BLUE, 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+    };
+
+    for (i = 0; i < sizeof(invalid_table)/sizeof(invalid_table[0]); i++)
+    {
+        if (invalid_table[i].win7_crash)
+            continue;
+
+        SetLastError(0xdeadbeef);
+        if (invalid_table[i].lpNumAttrsWritten) count = 0xdeadbeef;
+        ret = FillConsoleOutputAttribute(invalid_table[i].hConsoleOutput,
+                                         invalid_table[i].attr,
+                                         invalid_table[i].length,
+                                         invalid_table[i].coord,
+                                         invalid_table[i].lpNumAttrsWritten);
+        ok(!ret, "[%d] Expected FillConsoleOutputAttribute to return FALSE, got %d\n", i, ret);
+        if (invalid_table[i].lpNumAttrsWritten)
+        {
+            ok(count == invalid_table[i].expected_count,
+               "[%d] Expected count to be %u, got %u\n",
+               i, invalid_table[i].expected_count, count);
+        }
+        ok(GetLastError() == invalid_table[i].last_error,
+           "[%d] Expected last error to be %u, got %u\n",
+           i, invalid_table[i].last_error, GetLastError());
+    }
+
+    count = 0xdeadbeef;
+    ret = FillConsoleOutputAttribute(output_handle, FOREGROUND_BLUE, 0, origin, &count);
+    ok(ret == TRUE, "Expected FillConsoleOutputAttribute to return TRUE, got %d\n", ret);
+    ok(count == 0, "Expected count to be 0, got %u\n", count);
+
+    count = 0xdeadbeef;
+    ret = FillConsoleOutputAttribute(output_handle, FOREGROUND_BLUE, 1, origin, &count);
+    ok(ret == TRUE, "Expected FillConsoleOutputAttribute to return TRUE, got %d\n", ret);
+    ok(count == 1, "Expected count to be 1, got %u\n", count);
+
+    count = 0xdeadbeef;
+    ret = FillConsoleOutputAttribute(output_handle, ~0, 1, origin, &count);
+    ok(ret == TRUE, "Expected FillConsoleOutputAttribute to return TRUE, got %d\n", ret);
+    ok(count == 1, "Expected count to be 1, got %u\n", count);
+}
+
+static void test_ReadConsoleOutputCharacterA(HANDLE output_handle)
+{
+    CHAR read;
+    COORD origin = {0, 0};
+    DWORD count;
+    BOOL ret;
+    int i;
+
+    const struct
+    {
+        HANDLE hConsoleOutput;
+        LPSTR lpstr;
+        DWORD length;
+        COORD coord;
+        LPDWORD read_count;
+        DWORD expected_count;
+        DWORD last_error;
+        int win7_crash;
+    } invalid_table[] =
+    {
+        {NULL, NULL, 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {NULL, NULL, 0, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {NULL, NULL, 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {NULL, NULL, 1, origin, &count, 0, ERROR_INVALID_HANDLE, 1},
+        {NULL, &read, 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {NULL, &read, 0, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {NULL, &read, 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {NULL, &read, 1, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {INVALID_HANDLE_VALUE, NULL, 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {INVALID_HANDLE_VALUE, NULL, 0, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {INVALID_HANDLE_VALUE, NULL, 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {INVALID_HANDLE_VALUE, NULL, 1, origin, &count, 0, ERROR_INVALID_HANDLE, 1},
+        {INVALID_HANDLE_VALUE, &read, 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {INVALID_HANDLE_VALUE, &read, 0, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {INVALID_HANDLE_VALUE, &read, 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {INVALID_HANDLE_VALUE, &read, 1, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {output_handle, NULL, 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {output_handle, NULL, 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {output_handle, NULL, 1, origin, &count, 1, ERROR_INVALID_ACCESS, 1},
+        {output_handle, NULL, 10, origin, &count, 10, ERROR_INVALID_ACCESS, 1},
+        {output_handle, &read, 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {output_handle, &read, 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+    };
+
+    for (i = 0; i < sizeof(invalid_table)/sizeof(invalid_table[0]); i++)
+    {
+        if (invalid_table[i].win7_crash)
+            continue;
+
+        SetLastError(0xdeadbeef);
+        if (invalid_table[i].read_count) count = 0xdeadbeef;
+        ret = ReadConsoleOutputCharacterA(invalid_table[i].hConsoleOutput,
+                                          invalid_table[i].lpstr,
+                                          invalid_table[i].length,
+                                          invalid_table[i].coord,
+                                          invalid_table[i].read_count);
+        ok(!ret, "[%d] Expected ReadConsoleOutputCharacterA to return FALSE, got %d\n", i, ret);
+        if (invalid_table[i].read_count)
+        {
+            ok(count == invalid_table[i].expected_count,
+               "[%d] Expected count to be %u, got %u\n",
+               i, invalid_table[i].expected_count, count);
+        }
+        ok(GetLastError() == invalid_table[i].last_error,
+           "[%d] Expected last error to be %u, got %u\n",
+           i, invalid_table[i].last_error, GetLastError());
+    }
+
+    count = 0xdeadbeef;
+    ret = ReadConsoleOutputCharacterA(output_handle, NULL, 0, origin, &count);
+    ok(ret == TRUE, "Expected ReadConsoleOutputCharacterA to return TRUE, got %d\n", ret);
+    ok(count == 0, "Expected count to be 0, got %u\n", count);
+
+    count = 0xdeadbeef;
+    ret = ReadConsoleOutputCharacterA(output_handle, &read, 0, origin, &count);
+    ok(ret == TRUE, "Expected ReadConsoleOutputCharacterA to return TRUE, got %d\n", ret);
+    ok(count == 0, "Expected count to be 0, got %u\n", count);
+
+    count = 0xdeadbeef;
+    ret = ReadConsoleOutputCharacterA(output_handle, &read, 1, origin, &count);
+    ok(ret == TRUE, "Expected ReadConsoleOutputCharacterA to return TRUE, got %d\n", ret);
+    ok(count == 1, "Expected count to be 1, got %u\n", count);
+}
+
+static void test_ReadConsoleOutputCharacterW(HANDLE output_handle)
+{
+    WCHAR read;
+    COORD origin = {0, 0};
+    DWORD count;
+    BOOL ret;
+    int i;
+
+    const struct
+    {
+        HANDLE hConsoleOutput;
+        LPWSTR buffer;
+        DWORD length;
+        COORD coord;
+        LPDWORD read_count;
+        DWORD expected_count;
+        DWORD last_error;
+        int win7_crash;
+    } invalid_table[] =
+    {
+        {NULL, NULL, 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {NULL, NULL, 0, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {NULL, NULL, 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {NULL, NULL, 1, origin, &count, 0, ERROR_INVALID_HANDLE, 1},
+        {NULL, &read, 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {NULL, &read, 0, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {NULL, &read, 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {NULL, &read, 1, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {INVALID_HANDLE_VALUE, NULL, 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {INVALID_HANDLE_VALUE, NULL, 0, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {INVALID_HANDLE_VALUE, NULL, 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {INVALID_HANDLE_VALUE, NULL, 1, origin, &count, 0, ERROR_INVALID_HANDLE, 1},
+        {INVALID_HANDLE_VALUE, &read, 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {INVALID_HANDLE_VALUE, &read, 0, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {INVALID_HANDLE_VALUE, &read, 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {INVALID_HANDLE_VALUE, &read, 1, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {output_handle, NULL, 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {output_handle, NULL, 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {output_handle, NULL, 1, origin, &count, 1, ERROR_INVALID_ACCESS, 1},
+        {output_handle, NULL, 10, origin, &count, 10, ERROR_INVALID_ACCESS, 1},
+        {output_handle, &read, 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {output_handle, &read, 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+    };
+
+    for (i = 0; i < sizeof(invalid_table)/sizeof(invalid_table[0]); i++)
+    {
+        if (invalid_table[i].win7_crash)
+            continue;
+
+        SetLastError(0xdeadbeef);
+        if (invalid_table[i].read_count) count = 0xdeadbeef;
+        ret = ReadConsoleOutputCharacterW(invalid_table[i].hConsoleOutput,
+                                          invalid_table[i].buffer,
+                                          invalid_table[i].length,
+                                          invalid_table[i].coord,
+                                          invalid_table[i].read_count);
+        ok(!ret, "[%d] Expected ReadConsoleOutputCharacterW to return FALSE, got %d\n", i, ret);
+        if (invalid_table[i].read_count)
+        {
+            ok(count == invalid_table[i].expected_count,
+               "[%d] Expected count to be %u, got %u\n",
+               i, invalid_table[i].expected_count, count);
+        }
+        ok(GetLastError() == invalid_table[i].last_error,
+           "[%d] Expected last error to be %u, got %u\n",
+           i, invalid_table[i].last_error, GetLastError());
+    }
+
+    count = 0xdeadbeef;
+    ret = ReadConsoleOutputCharacterW(output_handle, NULL, 0, origin, &count);
+    ok(ret == TRUE, "Expected ReadConsoleOutputCharacterW to return TRUE, got %d\n", ret);
+    ok(count == 0, "Expected count to be 0, got %u\n", count);
+
+    count = 0xdeadbeef;
+    ret = ReadConsoleOutputCharacterW(output_handle, &read, 0, origin, &count);
+    ok(ret == TRUE, "Expected ReadConsoleOutputCharacterW to return TRUE, got %d\n", ret);
+    ok(count == 0, "Expected count to be 0, got %u\n", count);
+
+    count = 0xdeadbeef;
+    ret = ReadConsoleOutputCharacterW(output_handle, &read, 1, origin, &count);
+    ok(ret == TRUE, "Expected ReadConsoleOutputCharacterW to return TRUE, got %d\n", ret);
+    ok(count == 1, "Expected count to be 1, got %u\n", count);
+}
+
+static void test_ReadConsoleOutputAttribute(HANDLE output_handle)
+{
+    WORD attr;
+    COORD origin = {0, 0};
+    DWORD count;
+    BOOL ret;
+    int i;
+
+    const struct
+    {
+        HANDLE hConsoleOutput;
+        LPWORD lpAttribute;
+        DWORD length;
+        COORD coord;
+        LPDWORD read_count;
+        DWORD expected_count;
+        DWORD last_error;
+        int win7_crash;
+    } invalid_table[] =
+    {
+        {NULL, NULL, 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {NULL, NULL, 0, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {NULL, NULL, 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {NULL, NULL, 1, origin, &count, 0, ERROR_INVALID_HANDLE, 1},
+        {NULL, &attr, 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {NULL, &attr, 0, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {NULL, &attr, 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {NULL, &attr, 1, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {INVALID_HANDLE_VALUE, NULL, 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {INVALID_HANDLE_VALUE, NULL, 0, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {INVALID_HANDLE_VALUE, NULL, 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {INVALID_HANDLE_VALUE, NULL, 1, origin, &count, 0, ERROR_INVALID_HANDLE, 1},
+        {INVALID_HANDLE_VALUE, &attr, 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {INVALID_HANDLE_VALUE, &attr, 0, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {INVALID_HANDLE_VALUE, &attr, 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {INVALID_HANDLE_VALUE, &attr, 1, origin, &count, 0, ERROR_INVALID_HANDLE},
+        {output_handle, NULL, 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {output_handle, NULL, 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {output_handle, NULL, 1, origin, &count, 1, ERROR_INVALID_ACCESS, 1},
+        {output_handle, &attr, 0, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+        {output_handle, &attr, 1, origin, NULL, 0xdeadbeef, ERROR_INVALID_ACCESS, 1},
+    };
+
+    for (i = 0; i < sizeof(invalid_table)/sizeof(invalid_table[0]); i++)
+    {
+        if (invalid_table[i].win7_crash)
+            continue;
+
+        SetLastError(0xdeadbeef);
+        if (invalid_table[i].read_count) count = 0xdeadbeef;
+        ret = ReadConsoleOutputAttribute(invalid_table[i].hConsoleOutput,
+                                         invalid_table[i].lpAttribute,
+                                         invalid_table[i].length,
+                                         invalid_table[i].coord,
+                                         invalid_table[i].read_count);
+        ok(!ret, "[%d] Expected ReadConsoleOutputAttribute to return FALSE, got %d\n", i, ret);
+        if (invalid_table[i].read_count)
+        {
+            ok(count == invalid_table[i].expected_count,
+               "[%d] Expected count to be %u, got %u\n",
+               i, invalid_table[i].expected_count, count);
+        }
+        ok(GetLastError() == invalid_table[i].last_error,
+           "[%d] Expected last error to be %u, got %u\n",
+           i, invalid_table[i].last_error, GetLastError());
+    }
+
+    count = 0xdeadbeef;
+    ret = ReadConsoleOutputAttribute(output_handle, NULL, 0, origin, &count);
+    ok(ret == TRUE, "Expected ReadConsoleOutputAttribute to return TRUE, got %d\n", ret);
+    ok(count == 0, "Expected count to be 0, got %u\n", count);
+
+    count = 0xdeadbeef;
+    ret = ReadConsoleOutputAttribute(output_handle, &attr, 0, origin, &count);
+    ok(ret == TRUE, "Expected ReadConsoleOutputAttribute to return TRUE, got %d\n", ret);
+    ok(count == 0, "Expected count to be 0, got %u\n", count);
+
+    count = 0xdeadbeef;
+    ret = ReadConsoleOutputAttribute(output_handle, &attr, 1, origin, &count);
+    ok(ret == TRUE, "Expected ReadConsoleOutputAttribute to return TRUE, got %d\n", ret);
+    ok(count == 1, "Expected count to be 1, got %u\n", count);
+}
+
 START_TEST(console)
 {
+    static const char font_name[] = "Lucida Console";
     HANDLE hConIn, hConOut;
     BOOL ret;
     CONSOLE_SCREEN_BUFFER_INFO sbi;
+    LONG err;
+    HKEY console_key;
+    char old_font[LF_FACESIZE];
+    BOOL delete = FALSE;
+    DWORD size;
 
     init_function_pointers();
 
@@ -1098,9 +2464,55 @@ START_TEST(console)
      * the curses backend
      */
 
-    /* first, we detach and open a fresh console to play with */
+    /* ReadConsoleOutputW doesn't retrieve characters from the output buffer
+     * correctly for characters that don't have a glyph in the console font. So,
+     * we first set the console font to Lucida Console (which has a wider
+     * selection of glyphs available than the default raster fonts). We want
+     * to be able to restore the original font afterwards, so don't change
+     * if we can't read the original font.
+     */
+    err = RegOpenKeyExA(HKEY_CURRENT_USER, "Console", 0,
+                        KEY_QUERY_VALUE | KEY_SET_VALUE, &console_key);
+    if (err == ERROR_SUCCESS)
+    {
+        size = sizeof(old_font);
+        err = RegQueryValueExA(console_key, "FaceName", NULL, NULL,
+                               (LPBYTE) old_font, &size);
+        if (err == ERROR_SUCCESS || err == ERROR_FILE_NOT_FOUND)
+        {
+            delete = (err == ERROR_FILE_NOT_FOUND);
+            err = RegSetValueExA(console_key, "FaceName", 0, REG_SZ,
+                                 (const BYTE *) font_name, sizeof(font_name));
+            if (err != ERROR_SUCCESS)
+                trace("Unable to change default console font, error %d\n", err);
+        }
+        else
+        {
+            trace("Unable to query default console font, error %d\n", err);
+            RegCloseKey(console_key);
+            console_key = NULL;
+        }
+    }
+    else
+    {
+        trace("Unable to open HKCU\\Console, error %d\n", err);
+        console_key = NULL;
+    }
+
+    /* Now detach and open a fresh console to play with */
     FreeConsole();
     ok(AllocConsole(), "Couldn't alloc console\n");
+
+    /* Restore default console font if needed */
+    if (console_key != NULL)
+    {
+        if (delete)
+            err = RegDeleteValueA(console_key, "FaceName");
+        else
+            err = RegSetValueExA(console_key, "FaceName", 0, REG_SZ,
+                                 (const BYTE *) old_font, strlen(old_font) + 1);
+        ok(err == ERROR_SUCCESS, "Unable to restore default console font, error %d\n", err);
+    }
     hConIn = CreateFileA("CONIN$", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
     hConOut = CreateFileA("CONOUT$", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
 
@@ -1134,4 +2546,19 @@ START_TEST(console)
 
     test_GetConsoleProcessList();
     test_OpenConsoleW();
+    test_OpenCON();
+    test_VerifyConsoleIoHandle(hConOut);
+    test_GetSetStdHandle();
+    test_GetNumberOfConsoleInputEvents(hConIn);
+    test_WriteConsoleInputA(hConIn);
+    test_WriteConsoleInputW(hConIn);
+    test_WriteConsoleOutputCharacterA(hConOut);
+    test_WriteConsoleOutputCharacterW(hConOut);
+    test_WriteConsoleOutputAttribute(hConOut);
+    test_FillConsoleOutputCharacterA(hConOut);
+    test_FillConsoleOutputCharacterW(hConOut);
+    test_FillConsoleOutputAttribute(hConOut);
+    test_ReadConsoleOutputCharacterA(hConOut);
+    test_ReadConsoleOutputCharacterW(hConOut);
+    test_ReadConsoleOutputAttribute(hConOut);
 }
index fa96767..2094190 100644 (file)
@@ -160,6 +160,10 @@ static void doCrash(int argc,  char** argv)
 {
     char* p;
 
+    /* make sure the exception gets to the debugger */
+    SetErrorMode( 0 );
+    SetUnhandledExceptionFilter( NULL );
+
     if (argc >= 4)
     {
         crash_blackbox_t blackbox;
@@ -262,6 +266,8 @@ static void doDebugger(int argc, char** argv)
 
 static void crash_and_debug(HKEY hkey, const char* argv0, const char* dbgtasks)
 {
+    static BOOL skip_crash_and_debug = FALSE;
+    BOOL bRet;
     DWORD ret;
     HANDLE start_event, done_event;
     char* cmd;
@@ -272,21 +278,28 @@ static void crash_and_debug(HKEY hkey, const char* argv0, const char* dbgtasks)
     DWORD exit_code;
     crash_blackbox_t crash_blackbox;
     debugger_blackbox_t dbg_blackbox;
+    DWORD wait_code;
+
+    if (skip_crash_and_debug)
+    {
+        win_skip("Skipping crash_and_debug\n");
+        return;
+    }
 
     ret=RegSetValueExA(hkey, "auto", 0, REG_SZ, (BYTE*)"1", 2);
     ok(ret == ERROR_SUCCESS, "unable to set AeDebug/auto: ret=%d\n", ret);
 
     get_file_name(dbglog);
     get_events(dbglog, &start_event, &done_event);
-    cmd=HeapAlloc(GetProcessHeap(), 0, strlen(argv0)+10+strlen(dbgtasks)+1+strlen(dbglog)+34+1);
-    sprintf(cmd, "%s debugger %s %s %%ld %%ld", argv0, dbgtasks, dbglog);
+    cmd=HeapAlloc(GetProcessHeap(), 0, strlen(argv0)+10+strlen(dbgtasks)+1+strlen(dbglog)+2+34+1);
+    sprintf(cmd, "%s debugger %s \"%s\" %%ld %%ld", argv0, dbgtasks, dbglog);
     ret=RegSetValueExA(hkey, "debugger", 0, REG_SZ, (BYTE*)cmd, strlen(cmd)+1);
     ok(ret == ERROR_SUCCESS, "unable to set AeDebug/debugger: ret=%d\n", ret);
     HeapFree(GetProcessHeap(), 0, cmd);
 
     get_file_name(childlog);
-    cmd=HeapAlloc(GetProcessHeap(), 0, strlen(argv0)+16+strlen(dbglog)+1);
-    sprintf(cmd, "%s debugger crash %s", argv0, childlog);
+    cmd=HeapAlloc(GetProcessHeap(), 0, strlen(argv0)+16+strlen(dbglog)+2+1);
+    sprintf(cmd, "%s debugger crash \"%s\"", argv0, childlog);
 
     memset(&startup, 0, sizeof(startup));
     startup.cb = sizeof(startup);
@@ -299,8 +312,24 @@ static void crash_and_debug(HKEY hkey, const char* argv0, const char* dbgtasks)
 
     /* The process exits... */
     trace("waiting for child exit...\n");
-    ok(WaitForSingleObject(info.hProcess, 60000) == WAIT_OBJECT_0, "Timed out waiting for the child to crash\n");
-    ok(GetExitCodeProcess(info.hProcess, &exit_code), "GetExitCodeProcess failed: err=%d\n", GetLastError());
+    wait_code = WaitForSingleObject(info.hProcess, 30000);
+#if defined(_WIN64) && defined(__MINGW32__)
+    /* Mingw x64 doesn't output proper unwind info */
+    skip_crash_and_debug = broken(wait_code == WAIT_TIMEOUT);
+    if (skip_crash_and_debug)
+    {
+        TerminateProcess(info.hProcess, WAIT_TIMEOUT);
+        WaitForSingleObject(info.hProcess, 5000);
+        CloseHandle(info.hProcess);
+        assert(DeleteFileA(dbglog) != 0);
+        assert(DeleteFileA(childlog) != 0);
+        win_skip("Giving up on child process\n");
+        return;
+    }
+#endif
+    ok(wait_code == WAIT_OBJECT_0, "Timed out waiting for the child to crash\n");
+    bRet = GetExitCodeProcess(info.hProcess, &exit_code);
+    ok(bRet, "GetExitCodeProcess failed: err=%d\n", GetLastError());
     if (strstr(dbgtasks, "code2"))
     {
         /* If, after attaching to the debuggee, the debugger exits without
@@ -308,14 +337,12 @@ static void crash_and_debug(HKEY hkey, const char* argv0, const char* dbgtasks)
          */
         ok(exit_code == STATUS_DEBUGGER_INACTIVE ||
            broken(exit_code == STATUS_ACCESS_VIOLATION) || /* Intermittent Vista+ */
-           broken(exit_code == 0xffffffff) || /* Win9x */
            broken(exit_code == WAIT_ABANDONED), /* NT4, W2K */
            "wrong exit code : %08x\n", exit_code);
     }
     else
         ok(exit_code == STATUS_ACCESS_VIOLATION ||
-           broken(exit_code == WAIT_ABANDONED) || /* NT4, W2K, W2K3 */
-           broken(exit_code == 0xffffffff), /* Win9x, WinME */
+           broken(exit_code == WAIT_ABANDONED), /* NT4, W2K, W2K3 */
            "wrong exit code : %08x\n", exit_code);
     CloseHandle(info.hProcess);
 
@@ -324,7 +351,19 @@ static void crash_and_debug(HKEY hkey, const char* argv0, const char* dbgtasks)
         ok(SetEvent(start_event), "SetEvent(start_event) failed\n");
 
     trace("waiting for the debugger...\n");
-    ok(WaitForSingleObject(done_event, 60000) == WAIT_OBJECT_0, "Timed out waiting for the debugger\n");
+    wait_code = WaitForSingleObject(done_event, 5000);
+#if defined(_WIN64) && defined(__MINGW32__)
+    /* Mingw x64 doesn't output proper unwind info */
+    skip_crash_and_debug = broken(wait_code == WAIT_TIMEOUT);
+    if (skip_crash_and_debug)
+    {
+        assert(DeleteFileA(dbglog) != 0);
+        assert(DeleteFileA(childlog) != 0);
+        win_skip("Giving up on debugger\n");
+        return;
+    }
+#endif
+    ok(wait_code == WAIT_OBJECT_0, "Timed out waiting for the debugger\n");
 
     assert(load_blackbox(childlog, &crash_blackbox, sizeof(crash_blackbox)));
     assert(load_blackbox(dbglog, &dbg_blackbox, sizeof(dbg_blackbox)));
@@ -342,6 +381,7 @@ static void crash_and_debug(HKEY hkey, const char* argv0, const char* dbgtasks)
 
 static void crash_and_winedbg(HKEY hkey, const char* argv0)
 {
+    BOOL bRet;
     DWORD ret;
     char* cmd;
     PROCESS_INFORMATION        info;
@@ -365,7 +405,8 @@ static void crash_and_winedbg(HKEY hkey, const char* argv0)
 
     trace("waiting for child exit...\n");
     ok(WaitForSingleObject(info.hProcess, 60000) == WAIT_OBJECT_0, "Timed out waiting for the child to crash\n");
-    ok(GetExitCodeProcess(info.hProcess, &exit_code), "GetExitCodeProcess failed: err=%d\n", GetLastError());
+    bRet = GetExitCodeProcess(info.hProcess, &exit_code);
+    ok(bRet, "GetExitCodeProcess failed: err=%d\n", GetLastError());
     ok(exit_code == STATUS_ACCESS_VIOLATION, "exit code = %08x\n", exit_code);
     CloseHandle(info.hProcess);
 }
@@ -407,6 +448,7 @@ static void test_ExitCode(void)
         ok(0, "could not open the AeDebug key: %d\n", ret);
         return;
     }
+    else debugger_value.data = NULL;
 
     if (debugger_value.data && debugger_value.type == REG_SZ &&
         strstr((char*)debugger_value.data, "winedbg --auto"))
@@ -434,10 +476,8 @@ static void test_ExitCode(void)
         crash_and_debug(hkey, test_exe, "dbg,none");
     else
         skip("\"none\" debugger test needs user interaction\n");
-    if (disposition == REG_CREATED_NEW_KEY)
-        win_skip("'dbg,event,order' test doesn't finish on Win9x/WinMe\n");
-    else
-        crash_and_debug(hkey, test_exe, "dbg,event,order");
+    ok(disposition == REG_OPENED_EXISTING_KEY, "expected REG_OPENED_EXISTING_KEY, got %d\n", disposition);
+    crash_and_debug(hkey, test_exe, "dbg,event,order");
     crash_and_debug(hkey, test_exe, "dbg,attach,event,code2");
     if (pDebugSetProcessKillOnExit)
         crash_and_debug(hkey, test_exe, "dbg,attach,event,nokill");
@@ -578,8 +618,8 @@ static void test_debug_loop(int argc, char **argv)
     ok(!ret, "DebugActiveProcess() succeeded on own process.\n");
 
     get_file_name(blackbox_file);
-    cmd = HeapAlloc(GetProcessHeap(), 0, strlen(argv[0]) + strlen(arguments) + strlen(blackbox_file) + 10);
-    sprintf(cmd, "%s%s%08x %s", argv[0], arguments, pid, blackbox_file);
+    cmd = HeapAlloc(GetProcessHeap(), 0, strlen(argv[0]) + strlen(arguments) + strlen(blackbox_file) + 2 + 10);
+    sprintf(cmd, "%s%s%08x \"%s\"", argv[0], arguments, pid, blackbox_file);
 
     memset(&si, 0, sizeof(si));
     si.cb = sizeof(si);
index 8d1c457..9baae47 100755 (executable)
@@ -393,6 +393,7 @@ static void test_CreateDirectoryW(void)
        "CreateDirectoryW with ? wildcard name should fail with error 183, ret=%s error=%d\n",
        ret ? " True" : "False", GetLastError());
     ret = RemoveDirectoryW(tmpdir);
+    ok(ret == FALSE, "RemoveDirectoryW should have failed\n");
 
     tmpdir[lstrlenW(tmpdir) - 1] = '*';
     ret = CreateDirectoryW(tmpdir, NULL);
@@ -400,6 +401,7 @@ static void test_CreateDirectoryW(void)
        "CreateDirectoryW with * wildcard name should fail with error 183, ret=%s error=%d\n",
        ret ? " True" : "False", GetLastError());
     ret = RemoveDirectoryW(tmpdir);
+    ok(ret == FALSE, "RemoveDirectoryW should have failed\n");
     
     GetTempPathW(MAX_PATH, tmpdir);
     lstrcatW(tmpdir, tmp_dir_name);
@@ -410,6 +412,7 @@ static void test_CreateDirectoryW(void)
       "CreateDirectoryW with multiple nonexistent directories in path should fail ret %u err %u\n",
        ret, GetLastError());
     ret = RemoveDirectoryW(tmpdir);
+    ok(ret == FALSE, "RemoveDirectoryW should have failed\n");
 }
 
 static void test_RemoveDirectoryA(void)
index e4cb26c..db9c9c4 100755 (executable)
@@ -63,11 +63,6 @@ static void test_GetDriveTypeW(void)
     for (drive[0] = 'A'; drive[0] <= 'Z'; drive[0]++)
     {
         type = GetDriveTypeW(drive);
-        if (type == DRIVE_UNKNOWN && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
-        {
-            win_skip("GetDriveTypeW is not available on Win9x\n");
-            return;
-        }
         ok(type > DRIVE_UNKNOWN && type <= DRIVE_RAMDISK,
            "not a valid drive %c: type %u\n", drive[0], type);
 
index 19d11b5..3ac86cd 100755 (executable)
@@ -34,13 +34,59 @@ static CHAR string[MAX_PATH];
 
 static BOOL (WINAPI *pGetComputerNameExA)(COMPUTER_NAME_FORMAT,LPSTR,LPDWORD);
 static BOOL (WINAPI *pGetComputerNameExW)(COMPUTER_NAME_FORMAT,LPWSTR,LPDWORD);
+static BOOL (WINAPI *pOpenProcessToken)(HANDLE,DWORD,PHANDLE);
+static BOOL (WINAPI *pGetUserProfileDirectoryA)(HANDLE,LPSTR,LPDWORD);
 
 static void init_functionpointers(void)
 {
     HMODULE hkernel32 = GetModuleHandleA("kernel32.dll");
+    HMODULE hadvapi32 = GetModuleHandleA("advapi32.dll");
+    HMODULE huserenv = LoadLibraryA("userenv.dll");
 
     pGetComputerNameExA = (void *)GetProcAddress(hkernel32, "GetComputerNameExA");
     pGetComputerNameExW = (void *)GetProcAddress(hkernel32, "GetComputerNameExW");
+    pOpenProcessToken = (void *)GetProcAddress(hadvapi32, "OpenProcessToken");
+    pGetUserProfileDirectoryA = (void *)GetProcAddress(huserenv,
+                                                       "GetUserProfileDirectoryA");
+}
+
+static void test_Predefined(void)
+{
+    char Data[1024];
+    DWORD DataSize;
+    char Env[sizeof(Data)];
+    DWORD EnvSize;
+    HANDLE Token;
+    BOOL NoErr;
+
+    /*
+     * Check value of %USERPROFILE%, should be same as GetUserProfileDirectory()
+     * If this fails, your test environment is probably not set up
+     */
+    if (pOpenProcessToken == NULL || pGetUserProfileDirectoryA == NULL)
+    {
+        skip("Skipping USERPROFILE check\n");
+        return;
+    }
+    NoErr = pOpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &Token);
+    ok(NoErr, "Failed to open token, error %u\n", GetLastError());
+    DataSize = sizeof(Data);
+    NoErr = pGetUserProfileDirectoryA(Token, Data, &DataSize);
+    ok(NoErr, "Failed to get user profile dir, error %u\n", GetLastError());
+    if (NoErr)
+    {
+        EnvSize = GetEnvironmentVariableA("USERPROFILE", Env, sizeof(Env));
+        ok(EnvSize != 0 && EnvSize <= sizeof(Env),
+           "Failed to retrieve environment variable USERPROFILE, error %u\n",
+           GetLastError());
+        ok(strcmp(Data, Env) == 0,
+           "USERPROFILE env var %s doesn't match GetUserProfileDirectory %s\n",
+           Env, Data);
+    }
+    else
+        skip("Skipping USERPROFILE check, can't get user profile dir\n");
+    NoErr = CloseHandle(Token);
+    ok(NoErr, "Failed to close token, error %u\n", GetLastError());
 }
 
 static void test_GetSetEnvironmentVariableA(void)
@@ -330,7 +376,6 @@ static void test_GetComputerName(void)
     size = 0;
     ret = GetComputerNameA((LPSTR)0xdeadbeef, &size);
     error = GetLastError();
-    todo_wine
     ok(!ret && error == ERROR_BUFFER_OVERFLOW, "GetComputerNameA should have failed with ERROR_BUFFER_OVERFLOW instead of %d\n", error);
 
     /* Only Vista returns the computer name length as documented in the MSDN */
@@ -362,7 +407,6 @@ static void test_GetComputerName(void)
         win_skip("GetComputerNameW is not implemented\n");
     else
     {
-        todo_wine
         ok(!ret && error == ERROR_BUFFER_OVERFLOW, "GetComputerNameW should have failed with ERROR_BUFFER_OVERFLOW instead of %d\n", error);
         size++; /* nul terminating character */
         nameW = HeapAlloc(GetProcessHeap(), 0, size * sizeof(nameW[0]));
@@ -514,6 +558,7 @@ START_TEST(environ)
 {
     init_functionpointers();
 
+    test_Predefined();
     test_GetSetEnvironmentVariableA();
     test_GetSetEnvironmentVariableW();
     test_ExpandEnvironmentStringsA();
index dac9d6a..0e68535 100644 (file)
@@ -101,7 +101,8 @@ static void test_ConvertFiberToThread(void)
 {
     if (pConvertFiberToThread)
     {
-        ok(pConvertFiberToThread() , "ConvertFiberToThread failed with error %d\n", GetLastError());
+        BOOL ret = pConvertFiberToThread();
+        ok(ret, "ConvertFiberToThread failed with error %d\n", GetLastError());
     }
     else
     {
@@ -137,6 +138,7 @@ static void test_FiberHandling(void)
         return;
     }
 
+    SetLastError(0xdeadbeef);
     fibers[1] = pCreateFiberEx(0,0,0,FiberMainProc,&testparam);
     ok(fibers[1] != 0, "CreateFiberEx failed with error %d\n", GetLastError());
 
index c0a36c7..df2ab37 100755 (executable)
@@ -38,6 +38,7 @@ static BOOL (WINAPI *pReplaceFileA)(LPCSTR, LPCSTR, LPCSTR, DWORD, LPVOID, LPVOI
 static BOOL (WINAPI *pReplaceFileW)(LPCWSTR, LPCWSTR, LPCWSTR, DWORD, LPVOID, LPVOID);
 static UINT (WINAPI *pGetSystemWindowsDirectoryA)(LPSTR, UINT);
 static BOOL (WINAPI *pGetVolumeNameForVolumeMountPointA)(LPCSTR, LPSTR, DWORD);
+static DWORD WINAPI (*pQueueUserAPC)(PAPCFUNC pfnAPC, HANDLE hThread, ULONG_PTR dwData);
 
 /* keep filename and filenameW the same */
 static const char filename[] = "testfile.xxx";
@@ -71,6 +72,7 @@ static void InitFunctionPointers(void)
     pReplaceFileW=(void*)GetProcAddress(hkernel32, "ReplaceFileW");
     pGetSystemWindowsDirectoryA=(void*)GetProcAddress(hkernel32, "GetSystemWindowsDirectoryA");
     pGetVolumeNameForVolumeMountPointA = (void *) GetProcAddress(hkernel32, "GetVolumeNameForVolumeMountPointA");
+    pQueueUserAPC = (void *) GetProcAddress(hkernel32, "QueueUserAPC");
 }
 
 static void test__hread( void )
@@ -178,11 +180,11 @@ static void test__hwrite( void )
     ok( 0 != memory_object, "LocalAlloc fails. (Could be out of memory.)\n" );
 
     contents = LocalLock( memory_object );
+    ok( NULL != contents, "LocalLock whines\n" );
 
     filehandle = _lopen( filename, OF_READ );
 
     contents = LocalLock( memory_object );
-
     ok( NULL != contents, "LocalLock whines\n" );
 
     ok( bytes_written == _hread( filehandle, contents, bytes_written), "read length differ from write length\n" );
@@ -541,11 +543,11 @@ static void test__lwrite( void )
     ok( 0 != memory_object, "LocalAlloc fails, could be out of memory\n" );
 
     contents = LocalLock( memory_object );
+    ok( NULL != contents, "LocalLock whines\n" );
 
     filehandle = _lopen( filename, OF_READ );
 
     contents = LocalLock( memory_object );
-
     ok( NULL != contents, "LocalLock whines\n" );
 
     ok( bytes_written == _hread( filehandle, contents, bytes_written), "read length differ from write length\n" );
@@ -874,6 +876,13 @@ static void test_CreateFileA(void)
     ret = DeleteFileA(filename);
     ok(ret, "DeleteFileA: error %d\n", GetLastError());
 
+    SetLastError(0xdeadbeef);
+    hFile = CreateFileA("c:\\*.*", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+    ok(hFile == INVALID_HANDLE_VALUE, "hFile should have been INVALID_HANDLE_VALUE\n");
+    ok(GetLastError() == ERROR_INVALID_NAME ||
+        broken(GetLastError() == ERROR_FILE_NOT_FOUND), /* Win98 */
+        "LastError should have been ERROR_INVALID_NAME or ERROR_FILE_NOT_FOUND but got %u\n", GetLastError());
+
     /* get windows drive letter */
     ret = GetWindowsDirectory(windowsdir, sizeof(windowsdir));
     ok(ret < sizeof(windowsdir), "windowsdir is abnormally long!\n");
@@ -1720,6 +1729,10 @@ static BOOL create_fake_dll( LPCSTR filename )
     nt->FileHeader.Machine = IMAGE_FILE_MACHINE_AMD64;
 #elif defined __powerpc__
     nt->FileHeader.Machine = IMAGE_FILE_MACHINE_POWERPC;
+#elif defined __sparc__
+    nt->FileHeader.Machine = IMAGE_FILE_MACHINE_SPARC;
+#elif defined __arm__
+    nt->FileHeader.Machine = IMAGE_FILE_MACHINE_ARM;
 #else
 # error You must specify the machine type
 #endif
@@ -1757,27 +1770,30 @@ static BOOL create_fake_dll( LPCSTR filename )
     return ret;
 }
 
-static int is_sharing_compatible( DWORD access1, DWORD sharing1, DWORD access2, DWORD sharing2, BOOL is_win9x )
+static unsigned int map_file_access( unsigned int access )
 {
-    if (!is_win9x)
-    {
-        if (!access1) sharing1 = FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE;
-        if (!access2) sharing2 = FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE;
-    }
-    else
-    {
-        access1 &= ~DELETE;
-        if (!access1) access1 = GENERIC_READ;
+    if (access & GENERIC_READ)    access |= FILE_GENERIC_READ;
+    if (access & GENERIC_WRITE)   access |= FILE_GENERIC_WRITE;
+    if (access & GENERIC_EXECUTE) access |= FILE_GENERIC_EXECUTE;
+    if (access & GENERIC_ALL)     access |= FILE_ALL_ACCESS;
+    return access & ~(GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL);
+}
 
-        access2 &= ~DELETE;
-        if (!access2) access2 = GENERIC_READ;
-    }
+static int is_sharing_compatible( DWORD access1, DWORD sharing1, DWORD access2, DWORD sharing2 )
+{
+    access1 = map_file_access( access1 );
+    access2 = map_file_access( access2 );
+    access1 &= FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_EXECUTE | DELETE;
+    access2 &= FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_EXECUTE | DELETE;
+
+    if (!access1) sharing1 = FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE;
+    if (!access2) sharing2 = FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE;
 
-    if ((access1 & GENERIC_READ) && !(sharing2 & FILE_SHARE_READ)) return 0;
-    if ((access1 & GENERIC_WRITE) && !(sharing2 & FILE_SHARE_WRITE)) return 0;
+    if ((access1 & (FILE_READ_DATA|FILE_EXECUTE)) && !(sharing2 & FILE_SHARE_READ)) return 0;
+    if ((access1 & (FILE_WRITE_DATA|FILE_APPEND_DATA)) && !(sharing2 & FILE_SHARE_WRITE)) return 0;
     if ((access1 & DELETE) && !(sharing2 & FILE_SHARE_DELETE)) return 0;
-    if ((access2 & GENERIC_READ) && !(sharing1 & FILE_SHARE_READ)) return 0;
-    if ((access2 & GENERIC_WRITE) && !(sharing1 & FILE_SHARE_WRITE)) return 0;
+    if ((access2 & (FILE_READ_DATA|FILE_EXECUTE)) && !(sharing1 & FILE_SHARE_READ)) return 0;
+    if ((access2 & (FILE_WRITE_DATA|FILE_APPEND_DATA)) && !(sharing1 & FILE_SHARE_WRITE)) return 0;
     if ((access2 & DELETE) && !(sharing1 & FILE_SHARE_DELETE)) return 0;
     return 1;
 }
@@ -1786,7 +1802,8 @@ static int is_sharing_map_compatible( DWORD map_access, DWORD access2, DWORD sha
 {
     if ((map_access == PAGE_READWRITE || map_access == PAGE_EXECUTE_READWRITE) &&
         !(sharing2 & FILE_SHARE_WRITE)) return 0;
-    if ((map_access & SEC_IMAGE) && (access2 & GENERIC_WRITE)) return 0;
+    access2 = map_file_access( access2 );
+    if ((map_access & SEC_IMAGE) && (access2 & FILE_WRITE_DATA)) return 0;
     return 1;
 }
 
@@ -1794,7 +1811,12 @@ static void test_file_sharing(void)
 {
     static const DWORD access_modes[] =
         { 0, GENERIC_READ, GENERIC_WRITE, GENERIC_READ|GENERIC_WRITE,
-          DELETE, GENERIC_READ|DELETE, GENERIC_WRITE|DELETE, GENERIC_READ|GENERIC_WRITE|DELETE };
+          DELETE, GENERIC_READ|DELETE, GENERIC_WRITE|DELETE, GENERIC_READ|GENERIC_WRITE|DELETE,
+          GENERIC_EXECUTE, GENERIC_EXECUTE | DELETE,
+          FILE_READ_DATA, FILE_WRITE_DATA, FILE_APPEND_DATA, FILE_READ_EA, FILE_WRITE_EA,
+          FILE_READ_DATA | FILE_EXECUTE, FILE_WRITE_DATA | FILE_EXECUTE, FILE_APPEND_DATA | FILE_EXECUTE,
+          FILE_READ_EA | FILE_EXECUTE, FILE_WRITE_EA | FILE_EXECUTE, FILE_EXECUTE,
+          FILE_DELETE_CHILD, FILE_READ_ATTRIBUTES, FILE_WRITE_ATTRIBUTES };
     static const DWORD sharing_modes[] =
         { 0, FILE_SHARE_READ,
           FILE_SHARE_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
@@ -1805,7 +1827,6 @@ static void test_file_sharing(void)
     int a1, s1, a2, s2;
     int ret;
     HANDLE h, h2;
-    BOOL is_win9x = FALSE;
 
     /* make sure the file exists */
     if (!create_fake_dll( filename ))
@@ -1813,16 +1834,11 @@ static void test_file_sharing(void)
         ok(0, "couldn't create file \"%s\" (err=%d)\n", filename, GetLastError());
         return;
     }
-    is_win9x = GetFileAttributesW(filenameW) == INVALID_FILE_ATTRIBUTES;
 
     for (a1 = 0; a1 < sizeof(access_modes)/sizeof(access_modes[0]); a1++)
     {
         for (s1 = 0; s1 < sizeof(sharing_modes)/sizeof(sharing_modes[0]); s1++)
         {
-            /* Win9x doesn't support FILE_SHARE_DELETE */
-            if (is_win9x && (sharing_modes[s1] & FILE_SHARE_DELETE))
-                continue;
-
             SetLastError(0xdeadbeef);
             h = CreateFileA( filename, access_modes[a1], sharing_modes[s1],
                              NULL, OPEN_EXISTING, 0, 0 );
@@ -1835,24 +1851,18 @@ static void test_file_sharing(void)
             {
                 for (s2 = 0; s2 < sizeof(sharing_modes)/sizeof(sharing_modes[0]); s2++)
                 {
-                    /* Win9x doesn't support FILE_SHARE_DELETE */
-                    if (is_win9x && (sharing_modes[s2] & FILE_SHARE_DELETE))
-                        continue;
-
                     SetLastError(0xdeadbeef);
                     h2 = CreateFileA( filename, access_modes[a2], sharing_modes[s2],
                                       NULL, OPEN_EXISTING, 0, 0 );
                     ret = GetLastError();
                     if (is_sharing_compatible( access_modes[a1], sharing_modes[s1],
-                                               access_modes[a2], sharing_modes[s2], is_win9x ))
+                                               access_modes[a2], sharing_modes[s2] ))
                     {
                         ok( h2 != INVALID_HANDLE_VALUE,
                             "open failed for modes %x/%x/%x/%x\n",
                             access_modes[a1], sharing_modes[s1],
                             access_modes[a2], sharing_modes[s2] );
-                        ok( ret == 0xdeadbeef /* Win9x */ ||
-                            ret == 0, /* XP */
-                             "wrong error code %d\n", ret );
+                        ok( ret == 0, "wrong error code %d\n", ret );
                     }
                     else
                     {
@@ -1891,10 +1901,6 @@ static void test_file_sharing(void)
         {
             for (s2 = 0; s2 < sizeof(sharing_modes)/sizeof(sharing_modes[0]); s2++)
             {
-                /* Win9x doesn't support FILE_SHARE_DELETE */
-                if (is_win9x && (sharing_modes[s2] & FILE_SHARE_DELETE))
-                    continue;
-
                 SetLastError(0xdeadbeef);
                 h2 = CreateFileA( filename, access_modes[a2], sharing_modes[s2],
                                   NULL, OPEN_EXISTING, 0, 0 );
@@ -1902,10 +1908,9 @@ static void test_file_sharing(void)
                 ret = GetLastError();
                 if (h2 == INVALID_HANDLE_VALUE)
                 {
-                    if (is_sharing_map_compatible(mapping_modes[a1], access_modes[a2], sharing_modes[s2]))
-                        ok( is_win9x, /* there's no sharing at all with a mapping on win9x */
-                            "open failed for modes map %x/%x/%x\n",
-                            mapping_modes[a1], access_modes[a2], sharing_modes[s2] );
+                    ok( !is_sharing_map_compatible(mapping_modes[a1], access_modes[a2], sharing_modes[s2]),
+                        "open failed for modes map %x/%x/%x\n",
+                        mapping_modes[a1], access_modes[a2], sharing_modes[s2] );
                     ok( ret == ERROR_SHARING_VIOLATION,
                         "wrong error code %d\n", ret );
                 }
@@ -1928,7 +1933,7 @@ static void test_file_sharing(void)
         h2 = CreateFileA( filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
                           NULL, CREATE_ALWAYS, 0, 0 );
         ret = GetLastError();
-        if ((mapping_modes[a1] & SEC_IMAGE) || is_win9x)
+        if (mapping_modes[a1] & SEC_IMAGE)
         {
             ok( h2 == INVALID_HANDLE_VALUE, "create succeeded for map %x\n", mapping_modes[a1] );
             ok( ret == ERROR_SHARING_VIOLATION, "wrong error code %d for %x\n", ret, mapping_modes[a1] );
@@ -1945,12 +1950,7 @@ static void test_file_sharing(void)
         h2 = CreateFileA( filename, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
                           NULL, OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, 0 );
         ret = GetLastError();
-        if (is_win9x)
-        {
-            ok( h2 == INVALID_HANDLE_VALUE, "create succeeded for map %x\n", mapping_modes[a1] );
-            ok( ret == ERROR_SHARING_VIOLATION, "wrong error code %d for %x\n", ret, mapping_modes[a1] );
-        }
-        else if (mapping_modes[a1] & SEC_IMAGE)
+        if (mapping_modes[a1] & SEC_IMAGE)
         {
             ok( h2 == INVALID_HANDLE_VALUE, "create succeeded for map %x\n", mapping_modes[a1] );
             ok( ret == ERROR_ACCESS_DENIED, "wrong error code %d for %x\n", ret, mapping_modes[a1] );
@@ -2083,7 +2083,7 @@ static void test_FindFirstFileA(void)
     strcat(buffer2, "nul");
     handle = FindFirstFileA(buffer2, &data);
     err = GetLastError();
-    ok( handle != INVALID_HANDLE_VALUE, "FindFirstFile on %s failed\n", buffer2 );
+    ok( handle != INVALID_HANDLE_VALUE, "FindFirstFile on %s failed: %d\n", buffer2, err );
     ok( 0 == lstrcmpiA(data.cFileName, "nul"), "wrong name %s\n", data.cFileName );
     ok( FILE_ATTRIBUTE_ARCHIVE == data.dwFileAttributes ||
         FILE_ATTRIBUTE_DEVICE == data.dwFileAttributes /* Win9x */,
@@ -2103,7 +2103,7 @@ static void test_FindFirstFileA(void)
     strcpy(buffer2, "lpt1");
     handle = FindFirstFileA(buffer2, &data);
     err = GetLastError();
-    ok( handle != INVALID_HANDLE_VALUE, "FindFirstFile on %s failed\n", buffer2 );
+    ok( handle != INVALID_HANDLE_VALUE, "FindFirstFile on %s failed: %d\n", buffer2, err );
     ok( 0 == lstrcmpiA(data.cFileName, "lpt1"), "wrong name %s\n", data.cFileName );
     ok( FILE_ATTRIBUTE_ARCHIVE == data.dwFileAttributes ||
         FILE_ATTRIBUTE_DEVICE == data.dwFileAttributes /* Win9x */,
@@ -2364,6 +2364,12 @@ static void test_async_file_errors(void)
     HeapFree(GetProcessHeap(), 0, lpBuffer);
 }
 
+static BOOL user_apc_ran;
+static void CALLBACK user_apc(ULONG_PTR param)
+{
+    user_apc_ran = TRUE;
+}
+
 static void test_read_write(void)
 {
     DWORD bytes, ret, old_prot;
@@ -2384,6 +2390,12 @@ static void test_read_write(void)
                         CREATE_ALWAYS, FILE_FLAG_RANDOM_ACCESS, 0);
     ok(hFile != INVALID_HANDLE_VALUE, "CreateFileA: error %d\n", GetLastError());
 
+    user_apc_ran = FALSE;
+    if (pQueueUserAPC) {
+        trace("Queueing an user APC\n"); /* verify the file is non alerable */
+        ok(pQueueUserAPC(&user_apc, GetCurrentThread(), 0), "QueueUserAPC failed: %d\n", GetLastError());
+    }
+
     SetLastError(12345678);
     bytes = 12345678;
     ret = WriteFile(hFile, NULL, 0, &bytes, NULL);
@@ -2420,6 +2432,10 @@ static void test_read_write(void)
        "ret = %d, error %d\n", ret, GetLastError());
     ok(!bytes, "bytes = %d\n", bytes);
 
+    ok(user_apc_ran == FALSE, "UserAPC ran, file using alertable io mode\n");
+    if (pQueueUserAPC)
+        SleepEx(0, TRUE); /* get rid of apc */
+
     /* test passing protected memory as buffer */
 
     mem = VirtualAlloc( NULL, 0x4000, MEM_COMMIT, PAGE_READWRITE );
@@ -3002,6 +3018,7 @@ static void test_ReplaceFileA(void)
     ok(!ret && (GetLastError() == ERROR_FILE_NOT_FOUND ||
         GetLastError() == ERROR_ACCESS_DENIED),
         "ReplaceFileA: unexpected error %d\n", GetLastError());
+    DeleteFileA( replacement );
 
     /*
      * if the first round (w/ backup) worked then as long as there is no
@@ -3096,6 +3113,7 @@ static void test_ReplaceFileW(void)
     ok(!ret && (GetLastError() == ERROR_FILE_NOT_FOUND ||
        GetLastError() == ERROR_ACCESS_DENIED),
         "ReplaceFileW: unexpected error %d\n", GetLastError());
+    DeleteFileW( replacement );
 
     if (removeBackup)
     {
index 1c4dd76..37ea3e9 100755 (executable)
@@ -143,7 +143,7 @@ static void test_message_from_string_wide(void)
     {
         SetLastError(0xdeadbeef);
         memcpy(out, init_buf, sizeof(init_buf));
-        r = FormatMessageW(FORMAT_MESSAGE_FROM_STRING, NULL, 0,
+        FormatMessageW(FORMAT_MESSAGE_FROM_STRING, NULL, 0,
             0, out, sizeof(out)/sizeof(WCHAR), NULL);
     }
 
@@ -283,6 +283,7 @@ static void test_message_from_string_wide(void)
     r = doitW(FORMAT_MESSAGE_FROM_STRING, fmt_14d, 0,
         0, out, sizeof(out)/sizeof(WCHAR), 1);
     ok(!lstrcmpW(s_14d, out), "failed out=%s\n", wine_dbgstr_w(out));
+    ok(r==4,"failed: r=%d\n", r);
 
     /* a single digit, left justified */
     r = doitW(FORMAT_MESSAGE_FROM_STRING, fmt_1_4d, 0,
@@ -466,7 +467,7 @@ static void test_message_from_string(void)
     {
         SetLastError(0xdeadbeef);
         memcpy(out, init_buf, sizeof(init_buf));
-        r = FormatMessageA(FORMAT_MESSAGE_FROM_STRING, NULL, 0,
+        FormatMessageA(FORMAT_MESSAGE_FROM_STRING, NULL, 0,
             0, out, sizeof(out)/sizeof(CHAR), NULL);
     }
 
@@ -475,9 +476,7 @@ static void test_message_from_string(void)
     memcpy(out, init_buf, sizeof(init_buf));
     r = FormatMessageA(FORMAT_MESSAGE_FROM_STRING, "", 0,
         0, out, sizeof(out)/sizeof(CHAR), NULL);
-    ok(!memcmp(out, init_buf, sizeof(init_buf)) ||
-       broken(!strcmp("", out)), /* Win9x */
-       "Expected the buffer to be untouched\n");
+    ok(!memcmp(out, init_buf, sizeof(init_buf)), "Expected the buffer to be untouched\n");
     ok(r==0, "succeeded: r=%d\n", r);
     ok(GetLastError()==0xdeadbeef,
        "last error %u\n", GetLastError());
@@ -509,29 +508,17 @@ static void test_message_from_string(void)
     memcpy(out, init_buf, sizeof(init_buf));
     r = FormatMessageA(FORMAT_MESSAGE_FROM_STRING, "%1", 0,
         0, out, sizeof(out)/sizeof(CHAR), NULL);
-    ok(!memcmp(out, init_buf, sizeof(init_buf)) ||
-       broken(!strcmp("%1", out)), /* Win9x */
-       "Expected the buffer to be untouched\n");
-    ok(r==0 ||
-       broken(r==2), /* Win9x */
-       "succeeded: r=%d\n", r);
-    ok(GetLastError()==ERROR_INVALID_PARAMETER ||
-       broken(GetLastError()==0xdeadbeef), /* Win9x */
-       "last error %u\n", GetLastError());
+    ok(!memcmp(out, init_buf, sizeof(init_buf)), "Expected the buffer to be untouched\n");
+    ok(r==0, "succeeded: r=%d\n", r);
+    ok(GetLastError()==ERROR_INVALID_PARAMETER, "last error %u\n", GetLastError());
 
     SetLastError(0xdeadbeef);
     memcpy(out, init_buf, sizeof(init_buf));
     r = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY, "%1", 0,
         0, out, sizeof(out)/sizeof(CHAR), NULL);
-    ok(!memcmp(out, init_buf, sizeof(init_buf)) ||
-       broken(!strcmp("%1", out)), /* Win9x */
-       "Expected the buffer to be untouched\n");
-    ok(r==0 ||
-       broken(r==2), /* Win9x */
-       "succeeded: r=%d\n", r);
-    ok(GetLastError()==ERROR_INVALID_PARAMETER ||
-       broken(GetLastError()==0xdeadbeef), /* Win9x */
-       "last error %u\n", GetLastError());
+    ok(!memcmp(out, init_buf, sizeof(init_buf)), "Expected the buffer to be untouched\n");
+    ok(r==0, "succeeded: r=%d\n", r);
+    ok(GetLastError()==ERROR_INVALID_PARAMETER, "last error %u\n", GetLastError());
 
     /* using the format feature */
     r = doit(FORMAT_MESSAGE_FROM_STRING, "%1!s!", 0,
@@ -765,12 +752,8 @@ static void test_message_from_string(void)
     /* line feed */
     r = doit(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_MAX_WIDTH_MASK, "hi\n", 0,
         0, out, sizeof(out)/sizeof(CHAR));
-    ok(!strcmp("hi ", out) ||
-       broken(!strcmp("hi\r\n", out)), /* Win9x */
-       "failed out=[%s]\n",out);
-    ok(r==3 ||
-       broken(r==4), /* Win9x */
-       "failed: r=%d\n",r);
+    ok(!strcmp("hi ", out), "failed out=[%s]\n",out);
+    ok(r==3, "failed: r=%d\n",r);
 
     /* carriage return line feed */
     r = doit(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_MAX_WIDTH_MASK, "hi\r\n", 0,
@@ -820,9 +803,7 @@ static void test_message_ignore_inserts(void)
     ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_IGNORE_INSERTS, "%0test", 0, 0, out,
                          sizeof(out)/sizeof(CHAR), NULL);
     ok(ret == 0, "Expected FormatMessageA to return 0, got %d\n", ret);
-    ok(!memcmp(out, init_buf, sizeof(init_buf)) ||
-       broken(!strcmp("", out)), /* Win9x */
-       "Expected the output buffer to be untouched\n");
+    ok(!memcmp(out, init_buf, sizeof(init_buf)), "Expected the output buffer to be untouched\n");
     ok(GetLastError() == 0xdeadbeef, "Expected GetLastError() to return 0xdeadbeef, got %u\n", GetLastError());
 
     /* Insert sequences are ignored. */
@@ -834,12 +815,8 @@ static void test_message_ignore_inserts(void)
     /* Only the "%n", "%r", and "%t" escape sequences are processed. */
     ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_IGNORE_INSERTS, "%%% %.%!", 0, 0, out,
                          sizeof(out)/sizeof(CHAR), NULL);
-    ok(ret == 8 ||
-       broken(ret == 7) /* Win9x */,
-       "Expected FormatMessageA to return 8, got %d\n", ret);
-    ok(!strcmp("%%% %.%!", out) ||
-       broken(!strcmp("%%% %.!", out)) /* Win9x */,
-       "Expected output string \"%%%%%% %%.%%!\", got %s\n", out);
+    ok(ret == 8, "Expected FormatMessageA to return 8, got %d\n", ret);
+    ok(!strcmp("%%% %.%!", out), "Expected output string \"%%%%%% %%.%%!\", got %s\n", out);
 
     ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_IGNORE_INSERTS, "%n%r%t", 0, 0, out,
                          sizeof(out)/sizeof(CHAR), NULL);
@@ -871,12 +848,8 @@ static void test_message_ignore_inserts(void)
     ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_IGNORE_INSERTS |
                          FORMAT_MESSAGE_MAX_WIDTH_MASK, "hi\n", 0, 0, out,
                          sizeof(out)/sizeof(CHAR), NULL);
-    ok(!strcmp("hi ", out) ||
-       broken(!strcmp("hi\r\n", out)), /* Win9x */
-       "Expected output string \"hi \", got %s\n", out);
-    ok(ret == 3 ||
-       broken(ret == 4), /* Win9x */
-       "Expected FormatMessageA to return 3, got %d\n", ret);
+    ok(!strcmp("hi ", out), "Expected output string \"hi \", got %s\n", out);
+    ok(ret == 3, "Expected FormatMessageA to return 3, got %d\n", ret);
 
     ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_IGNORE_INSERTS |
                          FORMAT_MESSAGE_MAX_WIDTH_MASK, "hi\r\n", 0, 0, out,
@@ -1104,50 +1077,37 @@ static void test_message_null_buffer(void)
     ret = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, 0, 0, NULL, 0, NULL);
     error = GetLastError();
     ok(!ret, "FormatMessageA returned %u\n", ret);
-    ok(error == ERROR_INSUFFICIENT_BUFFER ||
-       error == ERROR_INVALID_PARAMETER, /* win9x */
-       "last error %u\n", error);
+    ok(error == ERROR_INSUFFICIENT_BUFFER, "last error %u\n", error);
 
     SetLastError(0xdeadbeef);
     ret = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, 0, 0, NULL, 1, NULL);
     error = GetLastError();
     ok(!ret, "FormatMessageA returned %u\n", ret);
-    ok(error == ERROR_INSUFFICIENT_BUFFER ||
-       error == ERROR_INVALID_PARAMETER, /* win9x */
-       "last error %u\n", error);
+    ok(error == ERROR_INSUFFICIENT_BUFFER, "last error %u\n", error);
 
     if (0) /* crashes on Windows */
     {
         SetLastError(0xdeadbeef);
-        ret = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, 0, 0, NULL, 256, NULL);
-        error = GetLastError();
-        ok(!ret, "FormatMessageA returned %u\n", ret);
-        trace("last error %u\n", error);
+        FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, 0, 0, NULL, 256, NULL);
     }
 
     SetLastError(0xdeadbeef);
     ret = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, 0, 0, NULL, 0, NULL);
     error = GetLastError();
     ok(!ret, "FormatMessageA returned %u\n", ret);
-    ok(error == ERROR_NOT_ENOUGH_MEMORY ||
-       error == ERROR_INVALID_PARAMETER, /* win9x */
-       "last error %u\n", error);
+    ok(error == ERROR_NOT_ENOUGH_MEMORY, "last error %u\n", error);
 
     SetLastError(0xdeadbeef);
     ret = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, 0, 0, NULL, 1, NULL);
     error = GetLastError();
     ok(!ret, "FormatMessageA returned %u\n", ret);
-    ok(error == ERROR_NOT_ENOUGH_MEMORY ||
-       error == ERROR_INVALID_PARAMETER, /* win9x */
-       "last error %u\n", error);
+    ok(error == ERROR_NOT_ENOUGH_MEMORY, "last error %u\n", error);
 
     SetLastError(0xdeadbeef);
     ret = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, 0, 0, NULL, 256, NULL);
     error = GetLastError();
     ok(!ret, "FormatMessageA returned %u\n", ret);
-    ok(error == ERROR_NOT_ENOUGH_MEMORY ||
-       error == ERROR_INVALID_PARAMETER, /* win9x */
-       "last error %u\n", error);
+    ok(error == ERROR_NOT_ENOUGH_MEMORY, "last error %u\n", error);
 }
 
 static void test_message_null_buffer_wide(void)
@@ -1296,7 +1256,7 @@ static void test_message_allocate_buffer_wide(void)
     if (0) /* crashes on Windows */
     {
         buf = (WCHAR *)0xdeadbeef;
-        ret = FormatMessageW(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER,
+        FormatMessageW(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER,
                              NULL, 0, 0, (WCHAR *)&buf, 0, NULL);
     }
 
@@ -1393,18 +1353,14 @@ static void test_message_from_hmodule(void)
     /* Test a message string with an insertion without passing any variadic arguments. */
     ret = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_FROM_HMODULE, h, 193 /* ERROR_BAD_EXE_FORMAT */,
                          MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), out, sizeof(out)/sizeof(CHAR), NULL);
-    ok(ret == 0 ||
-       broken(ret != 0), /* Win9x */
-       "FormatMessageA returned non-zero\n");
+    ok(ret == 0, "FormatMessageA returned non-zero\n");
 
     ret = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_FROM_HMODULE |
                          FORMAT_MESSAGE_ARGUMENT_ARRAY, h, 193 /* ERROR_BAD_EXE_FORMAT */,
                          MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), out, sizeof(out)/sizeof(CHAR), NULL);
-    ok(ret == 0 ||
-       broken(ret != 0), /* Win9x */
-       "FormatMessageA returned non-zero\n");
+    ok(ret == 0, "FormatMessageA returned non-zero\n");
 
-    /*Test nonexistent messageID with varying language ID's Note: FormatMessageW behaves the same*/
+    /*Test nonexistent messageID with varying language IDs Note: FormatMessageW behaves the same*/
     SetLastError(0xdeadbeef);
     ret = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_FROM_HMODULE, h, 3044,
                          MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), out, sizeof(out)/sizeof(CHAR), NULL);
@@ -1471,9 +1427,7 @@ static void test_message_invalid_flags(void)
     ptr = (char *)0xdeadbeef;
     ret = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER, "test", 0, 0, (char *)&ptr, 0, NULL);
     ok(ret == 0, "Expected FormatMessageA to return 0, got %u\n", ret);
-    ok(ptr == NULL ||
-       broken(ptr == (char *)0xdeadbeef), /* Win9x */
-       "Expected output pointer to be initialized to NULL, got %p\n", ptr);
+    ok(ptr == NULL, "Expected output pointer to be initialized to NULL, got %p\n", ptr);
     ok(GetLastError() == ERROR_INVALID_PARAMETER,
        "Expected GetLastError() to return ERROR_INVALID_PARAMETER, got %u\n",
        GetLastError());
index 98c99d7..634e14d 100755 (executable)
@@ -85,7 +85,7 @@ static void test_heap(void)
     UINT    flags;
     HGLOBAL gbl;
     HGLOBAL hsecond;
-    SIZE_T  size;
+    SIZE_T  size, size2;
 
     /* Heap*() functions */
     mem = HeapAlloc(GetProcessHeap(), 0, 0);
@@ -403,6 +403,36 @@ static void test_heap(void)
         "MAGIC_DEAD)\n", mem, GetLastError(), GetLastError());
 
     GlobalFree(gbl);
+
+    /* trying to get size from data pointer (GMEM_MOVEABLE) */
+    gbl = GlobalAlloc(GMEM_MOVEABLE, 0x123);
+    ok(gbl != NULL, "returned NULL\n");
+    mem = GlobalLock(gbl);
+    ok(mem != NULL, "returned NULL.\n");
+    ok(gbl != mem, "unexpectedly equal.\n");
+
+    size = GlobalSize(gbl);
+    size2 = GlobalSize(mem);
+    ok(size == 0x123, "got %lu\n", size);
+    ok(size2 == 0x123, "got %lu\n", size2);
+
+    GlobalFree(gbl);
+
+    /* trying to get size from data pointer (GMEM_FIXED) */
+    gbl = GlobalAlloc(GMEM_FIXED, 0x123);
+    ok(gbl != NULL, "returned NULL\n");
+    mem = GlobalLock(gbl);
+    ok(mem != NULL, "returned NULL.\n");
+    ok(gbl == mem, "got %p, %p.\n", gbl, mem);
+
+    size = GlobalSize(gbl);
+    ok(size == 0x123, "got %lu\n", size);
+
+    GlobalFree(gbl);
+
+    size = GlobalSize((void *)0xdeadbee0);
+    ok(size == 0, "got %lu\n", size);
+
 }
 
 static void test_obsolete_flags(void)
@@ -469,11 +499,11 @@ static void test_HeapQueryInformation(void)
     if (0) /* crashes under XP */
     {
         size = 0;
-        ret = pHeapQueryInformation(0,
+        pHeapQueryInformation(0,
                                 HeapCompatibilityInformation,
                                 &info, sizeof(info), &size);
         size = 0;
-        ret = pHeapQueryInformation(GetProcessHeap(),
+        pHeapQueryInformation(GetProcessHeap(),
                                 HeapCompatibilityInformation,
                                 NULL, sizeof(info), &size);
     }
@@ -694,6 +724,11 @@ static void test_debug_heap( const char *argv0, DWORD flags )
     if (!strcmp( keyname + strlen(keyname) - 3, ".so" )) keyname[strlen(keyname) - 3] = 0;
 
     err = RegCreateKeyA( HKEY_LOCAL_MACHINE, keyname, &hkey );
+    if (err == ERROR_ACCESS_DENIED)
+    {
+        skip("Not authorized to change the image file execution options\n");
+        return;
+    }
     ok( !err, "failed to create '%s' error %u\n", keyname, err );
     if (err) return;
 
index 9361ed5..ce59843 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Unit test suite for the PE loader.
  *
- * Copyright 2006 Dmitry Timoshkov
+ * Copyright 2006,2011 Dmitry Timoshkov
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -54,6 +54,10 @@ static IMAGE_NT_HEADERS nt_header =
       IMAGE_FILE_MACHINE_AMD64, /* Machine */
 #elif defined __powerpc__
       IMAGE_FILE_MACHINE_POWERPC, /* Machine */
+#elif defined __sparc__
+      IMAGE_FILE_MACHINE_SPARC, /* Machine */
+#elif defined __arm__
+      IMAGE_FILE_MACHINE_ARM, /* Machine */
 #else
 # error You must specify the machine type
 #endif
@@ -123,55 +127,54 @@ static void test_Loader(void)
         WORD number_of_sections, size_of_optional_header;
         DWORD section_alignment, file_alignment;
         DWORD size_of_image, size_of_headers;
-        DWORD error; /* 0 means LoadLibrary should succeed */
-        DWORD alt_error; /* alternate error */
+        DWORD errors[4]; /* 0 means LoadLibrary should succeed */
     } td[] =
     {
         { &dos_header, sizeof(dos_header),
           1, 0, 0, 0, 0, 0,
-          ERROR_BAD_EXE_FORMAT
+          { ERROR_BAD_EXE_FORMAT }
         },
         { &dos_header, sizeof(dos_header),
           1, sizeof(IMAGE_OPTIONAL_HEADER), 0x1000, 0x1000,
           sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER) + 0xe00,
           sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER),
-          ERROR_BAD_EXE_FORMAT /* XP doesn't like too small image size */
+          { ERROR_BAD_EXE_FORMAT } /* XP doesn't like too small image size */
         },
         { &dos_header, sizeof(dos_header),
           1, sizeof(IMAGE_OPTIONAL_HEADER), 0x1000, 0x1000,
           sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER) + 0x1000,
           sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER),
-          ERROR_SUCCESS
+          { ERROR_SUCCESS }
         },
         { &dos_header, sizeof(dos_header),
           1, sizeof(IMAGE_OPTIONAL_HEADER), 0x1000, 0x1000,
           0x1f00,
           0x1000,
-          ERROR_SUCCESS
+          { ERROR_SUCCESS }
         },
         { &dos_header, sizeof(dos_header),
           1, sizeof(IMAGE_OPTIONAL_HEADER), 0x200, 0x200,
           sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER) + 0x200,
           sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER),
-          ERROR_SUCCESS, ERROR_INVALID_ADDRESS /* vista is more strict */
+          { ERROR_SUCCESS, ERROR_INVALID_ADDRESS } /* vista is more strict */
         },
         { &dos_header, sizeof(dos_header),
           1, sizeof(IMAGE_OPTIONAL_HEADER), 0x200, 0x1000,
           sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER) + 0x1000,
           sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER),
-          ERROR_BAD_EXE_FORMAT /* XP doesn't like alignments */
+          { ERROR_BAD_EXE_FORMAT } /* XP doesn't like alignments */
         },
         { &dos_header, sizeof(dos_header),
           1, sizeof(IMAGE_OPTIONAL_HEADER), 0x1000, 0x200,
           sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER) + 0x1000,
           sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER),
-          ERROR_SUCCESS
+          { ERROR_SUCCESS }
         },
         { &dos_header, sizeof(dos_header),
           1, sizeof(IMAGE_OPTIONAL_HEADER), 0x1000, 0x200,
           sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER) + 0x1000,
           0x200,
-          ERROR_SUCCESS
+          { ERROR_SUCCESS }
         },
         /* Mandatory are all fields up to SizeOfHeaders, everything else
          * is really optional (at least that's true for XP).
@@ -180,58 +183,59 @@ static void test_Loader(void)
           1, FIELD_OFFSET(IMAGE_OPTIONAL_HEADER, CheckSum), 0x200, 0x200,
           sizeof(dos_header) + sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER) + FIELD_OFFSET(IMAGE_OPTIONAL_HEADER, CheckSum) + sizeof(IMAGE_SECTION_HEADER) + 0x10,
           sizeof(dos_header) + sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER) + FIELD_OFFSET(IMAGE_OPTIONAL_HEADER, CheckSum) + sizeof(IMAGE_SECTION_HEADER),
-          ERROR_SUCCESS, ERROR_BAD_EXE_FORMAT /* vista is more strict */
+          { ERROR_SUCCESS, ERROR_BAD_EXE_FORMAT, ERROR_INVALID_ADDRESS,
+            ERROR_NOACCESS }
         },
         { &dos_header, sizeof(dos_header),
           0, FIELD_OFFSET(IMAGE_OPTIONAL_HEADER, CheckSum), 0x200, 0x200,
           0xd0, /* beyond of the end of file */
           0xc0, /* beyond of the end of file */
-          ERROR_SUCCESS, ERROR_BAD_EXE_FORMAT /* vista is more strict */
+          { ERROR_SUCCESS, ERROR_BAD_EXE_FORMAT } /* vista is more strict */
         },
         { &dos_header, sizeof(dos_header),
           0, FIELD_OFFSET(IMAGE_OPTIONAL_HEADER, CheckSum), 0x200, 0x200,
           0x1000,
           0,
-          ERROR_SUCCESS, ERROR_BAD_EXE_FORMAT /* vista is more strict */
+          { ERROR_SUCCESS, ERROR_BAD_EXE_FORMAT } /* vista is more strict */
         },
         { &dos_header, sizeof(dos_header),
           0, FIELD_OFFSET(IMAGE_OPTIONAL_HEADER, CheckSum), 0x200, 0x200,
           1,
           0,
-          ERROR_SUCCESS, ERROR_BAD_EXE_FORMAT /* vista is more strict */
+          { ERROR_SUCCESS, ERROR_BAD_EXE_FORMAT } /* vista is more strict */
         },
 #if 0 /* not power of 2 alignments need more test cases */
         { &dos_header, sizeof(dos_header),
           0, FIELD_OFFSET(IMAGE_OPTIONAL_HEADER, CheckSum), 0x300, 0x300,
           1,
           0,
-          ERROR_BAD_EXE_FORMAT /* alignment is not power of 2 */
+          { ERROR_BAD_EXE_FORMAT } /* alignment is not power of 2 */
         },
 #endif
         { &dos_header, sizeof(dos_header),
           0, FIELD_OFFSET(IMAGE_OPTIONAL_HEADER, CheckSum), 4, 4,
           1,
           0,
-          ERROR_SUCCESS, ERROR_BAD_EXE_FORMAT /* vista is more strict */
+          { ERROR_SUCCESS, ERROR_BAD_EXE_FORMAT } /* vista is more strict */
         },
         { &dos_header, sizeof(dos_header),
           0, FIELD_OFFSET(IMAGE_OPTIONAL_HEADER, CheckSum), 1, 1,
           1,
           0,
-          ERROR_SUCCESS, ERROR_BAD_EXE_FORMAT /* vista is more strict */
+          { ERROR_SUCCESS, ERROR_BAD_EXE_FORMAT } /* vista is more strict */
         },
         { &dos_header, sizeof(dos_header),
           0, FIELD_OFFSET(IMAGE_OPTIONAL_HEADER, CheckSum), 0x200, 0x200,
           0,
           0,
-          ERROR_BAD_EXE_FORMAT /* image size == 0 -> failure */
+          { ERROR_BAD_EXE_FORMAT } /* image size == 0 -> failure */
         },
         /* the following data mimics the PE image which upack creates */
         { &dos_header, 0x10,
           1, 0x148, 0x1000, 0x200,
           sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER) + 0x1000,
           0x200,
-          ERROR_SUCCESS
+          { ERROR_SUCCESS }
         },
         /* Minimal PE image that XP is able to load: 92 bytes */
         { &dos_header, 0x04,
@@ -239,7 +243,7 @@ static void test_Loader(void)
           0x04 /* also serves as e_lfanew in the truncated MZ header */, 0x04,
           1,
           0,
-          ERROR_SUCCESS, ERROR_BAD_EXE_FORMAT /* vista is more strict */
+          { ERROR_SUCCESS, ERROR_BAD_EXE_FORMAT } /* vista is more strict */
         }
     };
     static const char filler[0x1000];
@@ -251,6 +255,8 @@ static void test_Loader(void)
     SYSTEM_INFO si;
     char temp_path[MAX_PATH];
     char dll_name[MAX_PATH];
+    SIZE_T size;
+    BOOL ret;
 
     GetSystemInfo(&si);
     trace("system page size 0x%04x\n", si.dwPageSize);
@@ -273,8 +279,8 @@ static void test_Loader(void)
         }
 
         SetLastError(0xdeadbeef);
-        ok(WriteFile(hfile, td[i].dos_header, td[i].size_of_dos_header, &dummy, NULL),
-           "WriteFile error %d\n", GetLastError());
+        ret = WriteFile(hfile, td[i].dos_header, td[i].size_of_dos_header, &dummy, NULL);
+        ok(ret, "WriteFile error %d\n", GetLastError());
 
         nt_header.FileHeader.NumberOfSections = td[i].number_of_sections;
         nt_header.FileHeader.SizeOfOptionalHeader = td[i].size_of_optional_header;
@@ -284,23 +290,23 @@ static void test_Loader(void)
         nt_header.OptionalHeader.SizeOfImage = td[i].size_of_image;
         nt_header.OptionalHeader.SizeOfHeaders = td[i].size_of_headers;
         SetLastError(0xdeadbeef);
-        ok(WriteFile(hfile, &nt_header, sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER), &dummy, NULL),
-           "WriteFile error %d\n", GetLastError());
+        ret = WriteFile(hfile, &nt_header, sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER), &dummy, NULL);
+        ok(ret, "WriteFile error %d\n", GetLastError());
 
         if (nt_header.FileHeader.SizeOfOptionalHeader)
         {
             SetLastError(0xdeadbeef);
-            ok(WriteFile(hfile, &nt_header.OptionalHeader,
-                         min(nt_header.FileHeader.SizeOfOptionalHeader, sizeof(IMAGE_OPTIONAL_HEADER)),
-                         &dummy, NULL),
-               "WriteFile error %d\n", GetLastError());
+            ret = WriteFile(hfile, &nt_header.OptionalHeader,
+                            min(nt_header.FileHeader.SizeOfOptionalHeader, sizeof(IMAGE_OPTIONAL_HEADER)),
+                            &dummy, NULL);
+            ok(ret, "WriteFile error %d\n", GetLastError());
             if (nt_header.FileHeader.SizeOfOptionalHeader > sizeof(IMAGE_OPTIONAL_HEADER))
             {
                 file_align = nt_header.FileHeader.SizeOfOptionalHeader - sizeof(IMAGE_OPTIONAL_HEADER);
                 assert(file_align < sizeof(filler));
                 SetLastError(0xdeadbeef);
-                ok(WriteFile(hfile, filler, file_align, &dummy, NULL),
-                   "WriteFile error %d\n", GetLastError());
+                ret = WriteFile(hfile, filler, file_align, &dummy, NULL);
+                ok(ret, "WriteFile error %d\n", GetLastError());
             }
         }
 
@@ -321,13 +327,13 @@ static void test_Loader(void)
             }
 
             SetLastError(0xdeadbeef);
-            ok(WriteFile(hfile, &section, sizeof(section), &dummy, NULL),
-               "WriteFile error %d\n", GetLastError());
+            ret = WriteFile(hfile, &section, sizeof(section), &dummy, NULL);
+            ok(ret, "WriteFile error %d\n", GetLastError());
 
             /* section data */
             SetLastError(0xdeadbeef);
-            ok(WriteFile(hfile, section_data, sizeof(section_data), &dummy, NULL),
-               "WriteFile error %d\n", GetLastError());
+            ret = WriteFile(hfile, section_data, sizeof(section_data), &dummy, NULL);
+            ok(ret, "WriteFile error %d\n", GetLastError());
         }
 
         file_size = GetFileSize(hfile, NULL);
@@ -339,10 +345,11 @@ static void test_Loader(void)
         {
             MEMORY_BASIC_INFORMATION info;
 
-            ok( td[i].error == ERROR_SUCCESS, "%d: should have failed\n", i );
+            ok( td[i].errors[0] == ERROR_SUCCESS, "%d: should have failed\n", i );
 
             SetLastError(0xdeadbeef);
-            ok(VirtualQuery(hlib, &info, sizeof(info)) == sizeof(info),
+            size = VirtualQuery(hlib, &info, sizeof(info));
+            ok(size == sizeof(info),
                 "%d: VirtualQuery error %d\n", i, GetLastError());
             ok(info.BaseAddress == hlib, "%d: %p != %p\n", i, info.BaseAddress, hlib);
             ok(info.AllocationBase == hlib, "%d: %p != %p\n", i, info.AllocationBase, hlib);
@@ -357,7 +364,8 @@ static void test_Loader(void)
             ok(info.Type == SEC_IMAGE, "%d: %x != SEC_IMAGE\n", i, info.Type);
 
             SetLastError(0xdeadbeef);
-            ok(VirtualQuery((char *)hlib + info.RegionSize, &info, sizeof(info)) == sizeof(info),
+            size = VirtualQuery((char *)hlib + info.RegionSize, &info, sizeof(info));
+            ok(size == sizeof(info),
                 "%d: VirtualQuery error %d\n", i, GetLastError());
             if (nt_header.OptionalHeader.SectionAlignment == si.dwPageSize ||
                 nt_header.OptionalHeader.SectionAlignment == nt_header.OptionalHeader.FileAlignment)
@@ -398,7 +406,8 @@ static void test_Loader(void)
             if (nt_header.FileHeader.NumberOfSections)
             {
                 SetLastError(0xdeadbeef);
-                ok(VirtualQuery((char *)hlib + section.VirtualAddress, &info, sizeof(info)) == sizeof(info),
+                size = VirtualQuery((char *)hlib + section.VirtualAddress, &info, sizeof(info));
+                ok(size == sizeof(info),
                     "%d: VirtualQuery error %d\n", i, GetLastError());
                 if (nt_header.OptionalHeader.SectionAlignment < si.dwPageSize)
                 {
@@ -442,14 +451,16 @@ static void test_Loader(void)
             ok(hlib_as_data_file == hlib, "hlib_as_file and hlib are different\n");
 
             SetLastError(0xdeadbeef);
-            ok(FreeLibrary(hlib), "FreeLibrary error %d\n", GetLastError());
+            ret = FreeLibrary(hlib);
+            ok(ret, "FreeLibrary error %d\n", GetLastError());
 
             SetLastError(0xdeadbeef);
             hlib = GetModuleHandle(dll_name);
             ok(hlib != 0, "GetModuleHandle error %u\n", GetLastError());
 
             SetLastError(0xdeadbeef);
-            ok(FreeLibrary(hlib_as_data_file), "FreeLibrary error %d\n", GetLastError());
+            ret = FreeLibrary(hlib_as_data_file);
+            ok(ret, "FreeLibrary error %d\n", GetLastError());
 
             hlib = GetModuleHandle(dll_name);
             ok(!hlib, "GetModuleHandle should fail\n");
@@ -463,26 +474,27 @@ static void test_Loader(void)
             ok(!hlib, "GetModuleHandle should fail\n");
 
             SetLastError(0xdeadbeef);
-            ok(FreeLibrary(hlib_as_data_file), "FreeLibrary error %d\n", GetLastError());
+            ret = FreeLibrary(hlib_as_data_file);
+            ok(ret, "FreeLibrary error %d\n", GetLastError());
         }
         else
         {
-            ok(td[i].error || td[i].alt_error, "%d: LoadLibrary should succeed\n", i);
+            BOOL error_match;
+            int error_index;
 
-            if (GetLastError() == ERROR_GEN_FAILURE) /* Win9x, broken behaviour */
+            error_match = FALSE;
+            for (error_index = 0;
+                 ! error_match && error_index < sizeof(td[i].errors) / sizeof(DWORD);
+                 error_index++)
             {
-                trace("skipping the loader test on Win9x\n");
-                DeleteFile(dll_name);
-                return;
+                error_match = td[i].errors[error_index] == GetLastError();
             }
-
-            ok(td[i].error == GetLastError() || td[i].alt_error == GetLastError(),
-               "%d: expected error %d or %d, got %d\n",
-               i, td[i].error, td[i].alt_error, GetLastError());
+            ok(error_match, "%d: unexpected error %d\n", i, GetLastError());
         }
 
         SetLastError(0xdeadbeef);
-        ok(DeleteFile(dll_name), "DeleteFile error %d\n", GetLastError());
+        ret = DeleteFile(dll_name);
+        ok(ret, "DeleteFile error %d\n", GetLastError());
     }
 }
 
@@ -541,8 +553,148 @@ static void test_ImportDescriptors(void)
     }
 }
 
+static void test_section_access(void)
+{
+    static const struct test_data
+    {
+        DWORD scn_file_access, scn_page_access;
+    } td[] =
+    {
+        { 0, PAGE_NOACCESS },
+        { IMAGE_SCN_MEM_READ, PAGE_READONLY },
+        { IMAGE_SCN_MEM_WRITE, PAGE_WRITECOPY },
+        { IMAGE_SCN_MEM_EXECUTE, PAGE_EXECUTE },
+        { IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE, PAGE_WRITECOPY },
+        { IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_EXECUTE, PAGE_EXECUTE_READ },
+        { IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_EXECUTE, PAGE_EXECUTE_WRITECOPY },
+        { IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_EXECUTE, PAGE_EXECUTE_WRITECOPY },
+
+        { IMAGE_SCN_CNT_INITIALIZED_DATA, PAGE_NOACCESS },
+        { IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ, PAGE_READONLY },
+        { IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_WRITE, PAGE_WRITECOPY },
+        { IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_EXECUTE, PAGE_EXECUTE },
+        { IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE, PAGE_WRITECOPY },
+        { IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_EXECUTE, PAGE_EXECUTE_READ },
+        { IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_EXECUTE, PAGE_EXECUTE_WRITECOPY },
+        { IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_EXECUTE, PAGE_EXECUTE_WRITECOPY },
+
+        { IMAGE_SCN_CNT_UNINITIALIZED_DATA, PAGE_NOACCESS },
+        { IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ, PAGE_READONLY },
+        { IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_WRITE, PAGE_WRITECOPY },
+        { IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_EXECUTE, PAGE_EXECUTE },
+        { IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE, PAGE_WRITECOPY },
+        { IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_EXECUTE, PAGE_EXECUTE_READ },
+        { IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_EXECUTE, PAGE_EXECUTE_WRITECOPY },
+        { IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_EXECUTE, PAGE_EXECUTE_WRITECOPY }
+    };
+    static const char filler[0x1000];
+    static const char section_data[0x10] = "section data";
+    int i;
+    DWORD dummy, file_align;
+    HANDLE hfile;
+    HMODULE hlib;
+    SYSTEM_INFO si;
+    char temp_path[MAX_PATH];
+    char dll_name[MAX_PATH];
+    SIZE_T size;
+    MEMORY_BASIC_INFORMATION info;
+    BOOL ret;
+
+    GetSystemInfo(&si);
+    trace("system page size %#x\n", si.dwPageSize);
+
+    /* prevent displaying of the "Unable to load this DLL" message box */
+    SetErrorMode(SEM_FAILCRITICALERRORS);
+
+    GetTempPath(MAX_PATH, temp_path);
+
+    for (i = 0; i < sizeof(td)/sizeof(td[0]); i++)
+    {
+        GetTempFileName(temp_path, "ldr", 0, dll_name);
+
+        /*trace("creating %s\n", dll_name);*/
+        hfile = CreateFileA(dll_name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
+        if (hfile == INVALID_HANDLE_VALUE)
+        {
+            ok(0, "could not create %s\n", dll_name);
+            return;
+        }
+
+        SetLastError(0xdeadbeef);
+        ret = WriteFile(hfile, &dos_header, sizeof(dos_header), &dummy, NULL);
+        ok(ret, "WriteFile error %d\n", GetLastError());
+
+        nt_header.FileHeader.NumberOfSections = 1;
+        nt_header.FileHeader.SizeOfOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER);
+
+        nt_header.OptionalHeader.SectionAlignment = si.dwPageSize;
+        nt_header.OptionalHeader.FileAlignment = 0x200;
+        nt_header.OptionalHeader.SizeOfImage = sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER) + si.dwPageSize;
+        nt_header.OptionalHeader.SizeOfHeaders = sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER);
+        SetLastError(0xdeadbeef);
+        ret = WriteFile(hfile, &nt_header, sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER), &dummy, NULL);
+        ok(ret, "WriteFile error %d\n", GetLastError());
+        SetLastError(0xdeadbeef);
+        ret = WriteFile(hfile, &nt_header.OptionalHeader, sizeof(IMAGE_OPTIONAL_HEADER), &dummy, NULL);
+        ok(ret, "WriteFile error %d\n", GetLastError());
+
+        section.SizeOfRawData = sizeof(section_data);
+        section.PointerToRawData = nt_header.OptionalHeader.FileAlignment;
+        section.VirtualAddress = nt_header.OptionalHeader.SectionAlignment;
+        section.Misc.VirtualSize = section.SizeOfRawData;
+        section.Characteristics = td[i].scn_file_access;
+        SetLastError(0xdeadbeef);
+        ret = WriteFile(hfile, &section, sizeof(section), &dummy, NULL);
+        ok(ret, "WriteFile error %d\n", GetLastError());
+
+        file_align = nt_header.OptionalHeader.FileAlignment - nt_header.OptionalHeader.SizeOfHeaders;
+        assert(file_align < sizeof(filler));
+        SetLastError(0xdeadbeef);
+        ret = WriteFile(hfile, filler, file_align, &dummy, NULL);
+        ok(ret, "WriteFile error %d\n", GetLastError());
+
+        /* section data */
+        SetLastError(0xdeadbeef);
+        ret = WriteFile(hfile, section_data, sizeof(section_data), &dummy, NULL);
+        ok(ret, "WriteFile error %d\n", GetLastError());
+
+        CloseHandle(hfile);
+
+        SetLastError(0xdeadbeef);
+        hlib = LoadLibrary(dll_name);
+        ok(ret, "LoadLibrary error %d\n", GetLastError());
+
+        size = VirtualQuery((char *)hlib + section.VirtualAddress, &info, sizeof(info));
+        ok(size == sizeof(info),
+            "%d: VirtualQuery error %d\n", i, GetLastError());
+        ok(info.BaseAddress == (char *)hlib + section.VirtualAddress, "%d: got %p != expected %p\n", i, info.BaseAddress, (char *)hlib + section.VirtualAddress);
+        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].scn_file_access & IMAGE_SCN_MEM_WRITE) &&
+            (td[i].scn_file_access & IMAGE_SCN_CNT_UNINITIALIZED_DATA))
+        todo_wine ok(info.Protect == td[i].scn_page_access, "%d: got %#x != expected %#x\n", i, info.Protect, td[i].scn_page_access);
+        else
+        ok(info.Protect == td[i].scn_page_access, "%d: got %#x != expected %#x\n", i, info.Protect, td[i].scn_page_access);
+        ok(info.AllocationBase == hlib, "%d: %p != %p\n", i, info.AllocationBase, hlib);
+        ok(info.AllocationProtect == PAGE_EXECUTE_WRITECOPY, "%d: %#x != PAGE_EXECUTE_WRITECOPY\n", i, info.AllocationProtect);
+        ok(info.State == MEM_COMMIT, "%d: %#x != MEM_COMMIT\n", i, info.State);
+        ok(info.Type == SEC_IMAGE, "%d: %#x != SEC_IMAGE\n", i, info.Type);
+        if (info.Protect != PAGE_NOACCESS)
+            ok(!memcmp((const char *)info.BaseAddress, section_data, section.SizeOfRawData), "wrong section data\n");
+
+        SetLastError(0xdeadbeef);
+        ret = FreeLibrary(hlib);
+        ok(ret, "FreeLibrary error %d\n", GetLastError());
+
+        SetLastError(0xdeadbeef);
+        ret = DeleteFile(dll_name);
+        ok(ret, "DeleteFile error %d\n", GetLastError());
+    }
+}
+
 START_TEST(loader)
 {
     test_Loader();
     test_ImportDescriptors();
+    test_section_access();
 }
index f37d503..e13247e 100755 (executable)
@@ -67,23 +67,13 @@ static inline int isdigitW( WCHAR wc )
 static HMODULE hKernel32;
 static WORD enumCount;
 
-typedef BOOL (WINAPI *EnumSystemLanguageGroupsAFn)(LANGUAGEGROUP_ENUMPROC,
-                                                   DWORD, LONG_PTR);
-static EnumSystemLanguageGroupsAFn pEnumSystemLanguageGroupsA;
-typedef BOOL (WINAPI *EnumLanguageGroupLocalesAFn)(LANGGROUPLOCALE_ENUMPROC,
-                                                   LGRPID, DWORD, LONG_PTR);
-static EnumLanguageGroupLocalesAFn pEnumLanguageGroupLocalesA;
-typedef BOOL (WINAPI *EnumUILanguagesAFn)(UILANGUAGE_ENUMPROC,
-                                                   DWORD, LONG_PTR);
-static EnumUILanguagesAFn pEnumUILanguagesA;
-
-typedef INT (WINAPI *FoldStringAFn)(DWORD, LPCSTR, INT, LPSTR, INT);
-static FoldStringAFn pFoldStringA;
-typedef INT (WINAPI *FoldStringWFn)(DWORD, LPCWSTR, INT, LPWSTR, INT);
-static FoldStringWFn pFoldStringW;
-
-typedef BOOL (WINAPI *IsValidLanguageGroupFn)(LGRPID, DWORD);
-static IsValidLanguageGroupFn pIsValidLanguageGroup;
+static BOOL (WINAPI *pEnumSystemLanguageGroupsA)(LANGUAGEGROUP_ENUMPROC, DWORD, LONG_PTR);
+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 *pFoldStringA)(DWORD, LPCSTR, INT, LPSTR, INT);
+static INT (WINAPI *pFoldStringW)(DWORD, LPCWSTR, INT, LPWSTR, INT);
+static BOOL (WINAPI *pIsValidLanguageGroup)(LGRPID, DWORD);
 
 static void InitFunctionPointers(void)
 {
@@ -94,6 +84,7 @@ static void InitFunctionPointers(void)
   pFoldStringW = (void*)GetProcAddress(hKernel32, "FoldStringW");
   pIsValidLanguageGroup = (void*)GetProcAddress(hKernel32, "IsValidLanguageGroup");
   pEnumUILanguagesA = (void*)GetProcAddress(hKernel32, "EnumUILanguagesA");
+  pEnumSystemLocalesEx = (void*)GetProcAddress(hKernel32, "EnumSystemLocalesEx");
 }
 
 #define eq(received, expected, label, type) \
@@ -1352,6 +1343,14 @@ static void test_CompareStringA(void)
 
     ret = lstrcmpi("#", ".");
     todo_wine ok(ret == -1, "\"#\" vs \".\" expected -1, got %d\n", ret);
+
+    lcid = MAKELCID(MAKELANGID(LANG_POLISH, SUBLANG_DEFAULT), SORT_DEFAULT);
+
+    /* \xB9 character lies between a and b */
+    ret = CompareStringA(lcid, 0, "a", 1, "\xB9", 1);
+    todo_wine ok(ret == CSTR_LESS_THAN, "\'\\xB9\' character should be greater than \'a\'\n");
+    ret = CompareStringA(lcid, 0, "\xB9", 1, "b", 1);
+    ok(ret == CSTR_LESS_THAN, "\'\\xB9\' character should be smaller than \'b\'\n");
 }
 
 static void test_LCMapStringA(void)
@@ -2200,7 +2199,7 @@ static void test_FoldStringW(void)
 
       ok(dst[0] == ch || strchrW(outOfSequenceDigits, ch) ||
          /* Wine (correctly) maps all Unicode 4.0+ digits */
-         isdigitW(ch) || (ch >= 0x24F5 && ch <= 0x24FD) || ch == 0x24FF ||
+         isdigitW(ch) || (ch >= 0x24F5 && ch <= 0x24FD) || ch == 0x24FF || ch == 0x19da ||
          (ch >= 0x1369 && ch <= 0x1371),
          "MAP_FOLDDIGITS: ch %d 0x%04x Expected unchanged got %d\n", ch, ch, dst[0]);
     }
@@ -2346,6 +2345,29 @@ static void test_EnumSystemLanguageGroupsA(void)
   pEnumSystemLanguageGroupsA(langgrp_procA, LGRPID_SUPPORTED, 0);
 }
 
+static BOOL CALLBACK enum_func( LPWSTR name, DWORD flags, LPARAM lparam )
+{
+    trace( "%s %x\n", wine_dbgstr_w(name), flags );
+    return TRUE;
+}
+
+static void test_EnumSystemLocalesEx(void)
+{
+    BOOL ret;
+
+    if (!pEnumSystemLocalesEx)
+    {
+        win_skip( "EnumSystemLocalesEx not available\n" );
+        return;
+    }
+    SetLastError( 0xdeadbeef );
+    ret = pEnumSystemLocalesEx( enum_func, LOCALE_ALL, 0, (void *)1 );
+    ok( !ret, "should have failed\n" );
+    ok( GetLastError() == ERROR_INVALID_PARAMETER, "wrong error %u\n", GetLastError() );
+    SetLastError( 0xdeadbeef );
+    ret = pEnumSystemLocalesEx( enum_func, 0, 0, NULL );
+    ok( ret, "failed err %u\n", GetLastError() );
+}
 
 static BOOL CALLBACK lgrplocale_procA(LGRPID lgrpid, LCID lcid, LPSTR lpszNum,
                                       LONG_PTR lParam)
@@ -2418,13 +2440,13 @@ static void test_SetLocaleInfoA(void)
 
   /* IDATE */
   SetLastError(0);
-  bRet = SetLocaleInfoA(lcid, LOCALE_IDATE, (LPSTR)test_SetLocaleInfoA);
+  bRet = SetLocaleInfoA(lcid, LOCALE_IDATE, "test_SetLocaleInfoA");
   ok(!bRet && GetLastError() == ERROR_INVALID_FLAGS,
      "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
 
   /* ILDATE */
   SetLastError(0);
-  bRet = SetLocaleInfoA(lcid, LOCALE_ILDATE, (LPSTR)test_SetLocaleInfoA);
+  bRet = SetLocaleInfoA(lcid, LOCALE_ILDATE, "test_SetLocaleInfoA");
   ok(!bRet && GetLastError() == ERROR_INVALID_FLAGS,
      "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
 }
@@ -2656,6 +2678,131 @@ static void test_GetCPInfo(void)
     }
 }
 
+/*
+ * The CT_TYPE1 has varied over windows version.
+ * The current target for correct behavior is windows 7.
+ * There was a big shift between windows 2000 (first introduced) and windows Xp
+ * Most of the old values below are from windows 2000.
+ * A smaller subset of changes happened between windows Xp and Window vista/7
+ */
+static void test_GetStringTypeW(void)
+{
+    static const WCHAR blanks[] = {0x9, 0x20, 0xa0, 0x3000, 0xfeff};
+    static const WORD blanks_new[] = {C1_SPACE | C1_CNTRL | C1_BLANK | C1_DEFINED,
+                                 C1_SPACE | C1_BLANK | C1_DEFINED,
+                                 C1_SPACE | C1_BLANK | C1_DEFINED,
+                                 C1_SPACE | C1_BLANK | C1_DEFINED,
+                                 C1_CNTRL | C1_BLANK | C1_DEFINED};
+    static const WORD blanks_old[] ={C1_SPACE | C1_CNTRL | C1_BLANK,
+                                    C1_SPACE | C1_BLANK,
+                                    C1_SPACE | C1_BLANK,
+                                    C1_SPACE | C1_BLANK,
+                                    C1_SPACE | C1_BLANK};
+
+    static const WCHAR undefined[] = {0x378, 0x379, 0x604, 0xfff8, 0xfffe};
+
+                                  /* Lu, Ll, Lt */
+    static const WCHAR alpha[] = {0x47, 0x67, 0x1c5};
+    static const WORD alpha_old[] = {C1_UPPER | C1_ALPHA,
+                                     C1_LOWER | C1_ALPHA,
+                                     C1_UPPER | C1_LOWER | C1_ALPHA,
+                                     C1_ALPHA};
+
+                                  /* Sk, Sk, Mn, So, Me */
+    static const WCHAR oldpunc[] = { 0x2c2, 0x2e5, 0x322, 0x482, 0x6de,
+                                 /* Sc, Sm, No,*/
+                                     0xffe0, 0xffe9, 0x2153};
+
+                                /* Lm, Nl, Cf, 0xad(Cf), 0x1f88 (Lt), Lo, Mc */
+    static const WCHAR changed[] = {0x2b0, 0x2160, 0x600, 0xad, 0x1f88, 0x294, 0x903};
+    static const WORD changed_old[] = { C1_PUNCT, C1_PUNCT, 0, C1_PUNCT, C1_UPPER | C1_ALPHA, C1_ALPHA, C1_PUNCT };
+    static const WORD changed_xp[] = {C1_ALPHA | C1_DEFINED,
+                                      C1_ALPHA | C1_DEFINED,
+                                      C1_CNTRL | C1_DEFINED,
+                                      C1_PUNCT | C1_DEFINED,
+                                      C1_UPPER | C1_LOWER | C1_ALPHA | C1_DEFINED,
+                                      C1_ALPHA | C1_LOWER | C1_DEFINED,
+                                      C1_ALPHA | C1_DEFINED };
+    static const WORD changed_new[] = { C1_ALPHA | C1_DEFINED,
+                                      C1_ALPHA | C1_DEFINED,
+                                      C1_CNTRL | C1_DEFINED,
+                                      C1_PUNCT | C1_CNTRL | C1_DEFINED,
+                                      C1_UPPER | C1_LOWER | C1_ALPHA | C1_DEFINED,
+                                      C1_ALPHA | C1_DEFINED,
+                                      C1_DEFINED
+ };
+                                /* Pc,  Pd, Ps, Pe, Pi, Pf, Po*/
+    static const WCHAR punct[] = { 0x5f, 0x2d, 0x28, 0x29, 0xab, 0xbb, 0x21 };
+
+    static const WCHAR punct_special[] = {0x24, 0x2b, 0x3c, 0x3e, 0x5e, 0x60,
+                                          0x7c, 0x7e, 0xa2, 0xbe, 0xd7, 0xf7};
+    static const WCHAR digit_special[] = {0xb2, 0xb3, 0xb9};
+    static const WCHAR lower_special[] = {0x2071, 0x207f};
+    static const WCHAR cntrl_special[] = {0x070f, 0x200c, 0x200d,
+                  0x200e, 0x200f, 0x202a, 0x202b, 0x202c, 0x202d, 0x202e,
+                  0x206a, 0x206b, 0x206c, 0x206d, 0x206e, 0x206f, 0xfeff,
+                  0xfff9, 0xfffa, 0xfffb};
+    static const WCHAR space_special[] = {0x09, 0x0d, 0x85};
+
+    WORD types[20];
+    int i;
+
+    memset(types,0,sizeof(types));
+    GetStringTypeW(CT_CTYPE1, blanks, 5, types);
+    for (i = 0; i < 5; i++)
+        ok(types[i] == blanks_new[i] || broken(types[i] == blanks_old[i] || broken(types[i] == 0)), "incorrect type1 returned for %x -> (%x != %x)\n",blanks[i],types[i],blanks_new[i]);
+
+    memset(types,0,sizeof(types));
+    GetStringTypeW(CT_CTYPE1, alpha, 3, types);
+    for (i = 0; i < 3; i++)
+        ok(types[i] == (C1_DEFINED | alpha_old[i]) || broken(types[i] == alpha_old[i]) || broken(types[i] == 0), "incorrect types returned for %x -> (%x != %x)\n",alpha[i], types[i],(C1_DEFINED | alpha_old[i]));
+    memset(types,0,sizeof(types));
+    GetStringTypeW(CT_CTYPE1, undefined, 5, types);
+    for (i = 0; i < 5; i++)
+        ok(types[i] == 0, "incorrect types returned for %x -> (%x != 0)\n",undefined[i], types[i]);
+
+    memset(types,0,sizeof(types));
+    GetStringTypeW(CT_CTYPE1, oldpunc, 8, types);
+    for (i = 0; i < 8; i++)
+        ok(types[i] == C1_DEFINED || broken(types[i] == C1_PUNCT) || broken(types[i] == 0), "incorrect types returned for %x -> (%x != %x)\n",oldpunc[i], types[i], C1_DEFINED);
+
+    memset(types,0,sizeof(types));
+    GetStringTypeW(CT_CTYPE1, changed, 7, types);
+    for (i = 0; i < 7; i++)
+        ok(types[i] == changed_new[i] || broken(types[i] == changed_old[i]) || broken(types[i] == changed_xp[i]) || broken(types[i] == 0), "incorrect types returned for %x -> (%x != %x)\n",changed[i], types[i], changed_new[i]);
+
+    memset(types,0,sizeof(types));
+    GetStringTypeW(CT_CTYPE1, punct, 7, types);
+    for (i = 0; i < 7; i++)
+        ok(types[i] == (C1_PUNCT | C1_DEFINED) || broken(types[i] == C1_PUNCT) || broken(types[i] == 0), "incorrect types returned for %x -> (%x != %x)\n",punct[i], types[i], (C1_PUNCT | C1_DEFINED));
+
+
+    memset(types,0,sizeof(types));
+    GetStringTypeW(CT_CTYPE1, punct_special, 12, types);
+    for (i = 0; i < 12; i++)
+        ok(types[i]  & C1_PUNCT || broken(types[i] == 0), "incorrect types returned for %x -> (%x doest not have %x)\n",punct_special[i], types[i], C1_PUNCT);
+
+    memset(types,0,sizeof(types));
+    GetStringTypeW(CT_CTYPE1, digit_special, 3, types);
+    for (i = 0; i < 3; i++)
+        ok(types[i] & C1_DIGIT || broken(types[i] == 0), "incorrect types returned for %x -> (%x doest not have = %x)\n",digit_special[i], types[i], C1_DIGIT);
+
+    memset(types,0,sizeof(types));
+    GetStringTypeW(CT_CTYPE1, lower_special, 2, types);
+    for (i = 0; i < 2; i++)
+        ok(types[i] & C1_LOWER || broken(types[i] == C1_PUNCT) || broken(types[i] == 0), "incorrect types returned for %x -> (%x does not have %x)\n",lower_special[i], types[i], C1_LOWER);
+
+    memset(types,0,sizeof(types));
+    GetStringTypeW(CT_CTYPE1, cntrl_special, 20, types);
+    for (i = 0; i < 20; i++)
+        ok(types[i] & C1_CNTRL || broken(types[i] == (C1_BLANK|C1_SPACE)) || broken(types[i] == C1_PUNCT) || broken(types[i] == 0), "incorrect types returned for %x -> (%x does not have %x)\n",cntrl_special[i], types[i], C1_CNTRL);
+
+    memset(types,0,sizeof(types));
+    GetStringTypeW(CT_CTYPE1, space_special, 3, types);
+    for (i = 0; i < 3; i++)
+        ok(types[i] & C1_SPACE || broken(types[i] == C1_CNTRL) || broken(types[i] == 0), "incorrect types returned for %x -> (%x does not have %x)\n",space_special[i], types[i], C1_SPACE );
+}
+
 START_TEST(locale)
 {
   InitFunctionPointers();
@@ -2676,10 +2823,12 @@ START_TEST(locale)
   test_FoldStringW();
   test_ConvertDefaultLocale();
   test_EnumSystemLanguageGroupsA();
+  test_EnumSystemLocalesEx();
   test_EnumLanguageGroupLocalesA();
   test_SetLocaleInfoA();
   test_EnumUILanguageA();
   test_GetCPInfo();
+  test_GetStringTypeW();
   /* this requires collation table patch to make it MS compatible */
   if (0) test_sorting();
 }
index 2cea68a..fb3ec34 100755 (executable)
@@ -55,13 +55,8 @@ static int mailslot_test(void)
 
     /* open a mailslot with a null name */
     hSlot = CreateMailslot( NULL, 0, 0, NULL );
-    ok( hSlot == INVALID_HANDLE_VALUE || broken(hSlot != INVALID_HANDLE_VALUE), /* win9x */
-        "Created mailslot with invalid name\n");
-    if (hSlot == INVALID_HANDLE_VALUE)
-        ok( GetLastError() == ERROR_PATH_NOT_FOUND,
-            "error should be ERROR_PATH_NOT_FOUND\n");
-    else  /* succeeds on win9x */
-        CloseHandle( hSlot );
+    ok( hSlot == INVALID_HANDLE_VALUE, "Created mailslot with invalid name\n");
+    ok( GetLastError() == ERROR_PATH_NOT_FOUND, "error should be ERROR_PATH_NOT_FOUND\n");
 
     /* valid open, but with wacky parameters ... then check them */
     hSlot = CreateMailslot( szmspath, -1, -1, NULL );
@@ -85,7 +80,7 @@ static int mailslot_test(void)
     count = 0;
     memset(buffer, 0, sizeof buffer);
     ret = ReadFile( hSlot, buffer, sizeof buffer, &count, NULL);
-    ok( !ret || broken(ret), /* win9x */ "slot read\n");
+    ok( !ret, "slot read\n");
     if (!ret) ok( GetLastError() == ERROR_SEM_TIMEOUT, "wrong error %u\n", GetLastError() );
     else ok( count == 0, "wrong count %u\n", count );
     ok( !WriteFile( hSlot, buffer, sizeof buffer, &count, NULL),
@@ -102,8 +97,6 @@ static int mailslot_test(void)
     /* now open the client with the correct sharing mode */
     hWriter = CreateFile(szmspath, GENERIC_READ|GENERIC_WRITE,
                              FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
-    if (hWriter == INVALID_HANDLE_VALUE)  /* win9x doesn't like GENERIC_READ */
-        hWriter = CreateFile(szmspath, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
     ok( hWriter != INVALID_HANDLE_VALUE, "existing mailslot err %u\n", GetLastError());
 
     /*
@@ -111,7 +104,7 @@ static int mailslot_test(void)
      * whether we can read or write the mailslot
      */
     ret = ReadFile( hSlot, buffer, sizeof buffer/2, &count, NULL);
-    ok( !ret || broken(ret), /* win9x */ "slot read\n");
+    ok( !ret, "slot read\n");
     if (!ret) ok( GetLastError() == ERROR_SEM_TIMEOUT, "wrong error %u\n", GetLastError() );
     else ok( count == 0, "wrong count %u\n", count );
     ok( !WriteFile( hSlot, buffer, sizeof buffer/2, &count, NULL),
@@ -143,7 +136,7 @@ static int mailslot_test(void)
 
     /* but not again */
     ret = ReadFile( hSlot, buffer, sizeof buffer, &count, NULL);
-    ok( !ret || broken(ret), /* win9x */ "slot read\n");
+    ok( !ret, "slot read\n");
     if (!ret) ok( GetLastError() == ERROR_SEM_TIMEOUT, "wrong error %u\n", GetLastError() );
     else ok( count == 0, "wrong count %u\n", count );
 
@@ -205,7 +198,7 @@ static int mailslot_test(void)
 
     /* check there's still no data */
     ret = ReadFile( hSlot, buffer, sizeof buffer, &count, NULL);
-    ok( !ret || broken(ret), /* win9x */ "slot read\n");
+    ok( !ret, "slot read\n");
     if (!ret) ok( GetLastError() == ERROR_SEM_TIMEOUT, "wrong error %u\n", GetLastError() );
     else ok( count == 0, "wrong count %u\n", count );
 
@@ -242,8 +235,7 @@ static int mailslot_test(void)
         "getmailslotinfo failed\n");
     ok( dwNext == 1, "dwNext incorrect\n");
     todo_wine
-        ok( dwMsgCount == 3 || broken(dwMsgCount == 2), /* win9x */
-            "dwMsgCount incorrect %u\n", dwMsgCount);
+        ok( dwMsgCount == 3, "dwMsgCount incorrect %u\n", dwMsgCount);
 
     buffer[0]=buffer[1]=0;
 
@@ -262,8 +254,7 @@ static int mailslot_test(void)
         "getmailslotinfo failed\n");
     ok( dwNext == 2, "dwNext incorrect\n");
     todo_wine {
-        ok( dwMsgCount == 2 || broken(dwMsgCount == 1), /* win9x */
-            "dwMsgCount incorrect %u\n", dwMsgCount);
+        ok( dwMsgCount == 2, "dwMsgCount incorrect %u\n", dwMsgCount);
     }
 
     /* read the second message */
@@ -276,10 +267,9 @@ static int mailslot_test(void)
     dwNext = dwMsgCount = 0;
     ok( GetMailslotInfo( hSlot, NULL, &dwNext, &dwMsgCount, NULL ),
         "getmailslotinfo failed\n");
-    ok( dwNext == 0 || broken(dwNext == ~0u), /* win9x */ "dwNext incorrect %u\n", dwNext);
+    ok( dwNext == 0, "dwNext incorrect %u\n", dwNext);
     todo_wine {
-        ok( dwMsgCount == 1 || broken(dwMsgCount == 0), /* win9x */
-            "dwMsgCount incorrect %u\n", dwMsgCount);
+        ok( dwMsgCount == 1, "dwMsgCount incorrect %u\n", dwMsgCount);
     }
 
     /* read the 3rd (zero length) message */
@@ -301,7 +291,7 @@ static int mailslot_test(void)
 
     /* check that reads fail */
     ret = ReadFile( hSlot, buffer, sizeof buffer, &count, NULL);
-    ok( !ret || broken(ret), /* win9x */ "3rd slot read succeeded\n");
+    ok( !ret, "3rd slot read succeeded\n");
     if (!ret) ok( GetLastError() == ERROR_SEM_TIMEOUT, "wrong error %u\n", GetLastError() );
     else ok( count == 0, "wrong count %u\n", count );
 
@@ -317,8 +307,7 @@ static int mailslot_test(void)
     memset(buffer, 0, sizeof buffer);
     dwTimeout = GetTickCount();
     ok( !ReadFile( hSlot, buffer, sizeof buffer, &count, NULL), "slot read\n");
-    ok( GetLastError() == ERROR_SEM_TIMEOUT || broken(GetLastError() == ERROR_ACCESS_DENIED), /* win9x */
-        "wrong error %u\n", GetLastError() );
+    ok( GetLastError() == ERROR_SEM_TIMEOUT, "wrong error %u\n", GetLastError() );
     dwTimeout = GetTickCount() - dwTimeout;
     ok( dwTimeout >= 990, "timeout too short %u\n", dwTimeout );
     ok( CloseHandle( hSlot ), "closing the mailslot\n");
index 906646e..01fa448 100755 (executable)
@@ -221,6 +221,7 @@ static void testLoadLibraryEx(void)
     CHAR path[MAX_PATH];
     HMODULE hmodule;
     HANDLE hfile;
+    BOOL ret;
 
     hfile = CreateFileA("testfile.dll", GENERIC_READ | GENERIC_WRITE,
                         FILE_SHARE_READ | FILE_SHARE_WRITE,
@@ -367,13 +368,11 @@ static void testLoadLibraryEx(void)
     ok(hmodule != 0, "Expected valid module handle\n");
 
     SetLastError(0xdeadbeef);
-    ok(FreeLibrary(hmodule),
-       "Expected to be able to free the module, failed with %d\n",
-       GetLastError());
+    ret = FreeLibrary(hmodule);
+    ok(ret, "Expected to be able to free the module, failed with %d\n", GetLastError());
     SetLastError(0xdeadbeef);
-    ok(!FreeLibrary(hmodule),
-       "Unexpected ability to free the module, failed with %d\n",
-       GetLastError());
+    ret = FreeLibrary(hmodule);
+    ok(!ret, "Unexpected ability to free the module, failed with %d\n", GetLastError());
 
     CloseHandle(hmodule);
 
index e6d7e56..ba47910 100755 (executable)
@@ -310,11 +310,14 @@ static void test_InitPathA(CHAR *newdir, CHAR *curDrive, CHAR *otherDrive)
 {
   CHAR tmppath[MAX_PATH], /*path to TEMP */
        tmpstr[MAX_PATH],
-       tmpstr1[MAX_PATH];
+       tmpstr1[MAX_PATH],
+       invalid_dir[MAX_PATH];
+
   DWORD len,len1,drives;
   INT id;
   HANDLE hndl;
   BOOL bRes;
+  UINT unique;
 
   *curDrive = *otherDrive = NOT_A_VALID_DRIVE;
 
@@ -334,11 +337,7 @@ static void test_InitPathA(CHAR *newdir, CHAR *curDrive, CHAR *otherDrive)
   ok(len1==len+1 || broken(len1 == len), /* WinME */
      "GetTempPathA should return string length %d instead of %d\n",len+1,len1);
 
-/* Test GetTmpFileNameA
-   The only test we do here is whether GetTempFileNameA passes or not.
-   We do not thoroughly test this function yet (specifically, whether
-   it behaves correctly when 'unique' is non zero)
-*/
+/* Test GetTmpFileNameA */
   ok((id=GetTempFileNameA(tmppath,"path",0,newdir)),"GetTempFileNameA failed\n");
   sprintf(tmpstr,"pat%.4x.tmp",id & 0xffff);
   sprintf(tmpstr1,"pat%x.tmp",id & 0xffff);
@@ -361,6 +360,22 @@ static void test_InitPathA(CHAR *newdir, CHAR *curDrive, CHAR *otherDrive)
     ok(DeleteFileA(newdir),"Couldn't delete the temporary file we just created\n");
   }
 
+  for(unique=0;unique<3;unique++) {
+    /* Nonexistent path */
+    sprintf(invalid_dir, "%s\\%s",tmppath,"non_existent_dir_1jwj3y32nb3");
+    SetLastError(0xdeadbeef);
+    ok(!GetTempFileNameA(invalid_dir,"tfn",unique,newdir),"GetTempFileNameA should have failed\n");
+    ok(GetLastError()==ERROR_DIRECTORY || broken(GetLastError()==ERROR_PATH_NOT_FOUND)/*win98*/,
+    "got %d, expected ERROR_DIRECTORY\n", GetLastError());
+
+    /* Check return value for unique !=0 */
+    if(unique) {
+      ok((GetTempFileNameA(tmppath,"tfn",unique,newdir) == unique),"GetTempFileNameA unexpectedly failed\n");
+      /* if unique != 0, the actual temp files are not created: */
+      ok(!DeleteFileA(newdir) && GetLastError() == ERROR_FILE_NOT_FOUND,"Deleted a file that shouldn't exist!\n");
+    }
+  }
+
 /* Find first valid drive letter that is neither newdir[0] nor curDrive */
   drives = GetLogicalDrives() & ~(1<<(newdir[0]-'A'));
   if( *curDrive != NOT_A_VALID_DRIVE)
@@ -471,10 +486,7 @@ static void test_CurrentDirectoryA(CHAR *origdir, CHAR *newdir)
 */
   if (0)
   {
-    SetLastError( 0xdeadbeef );
-    len = GetCurrentDirectoryA( 42, (LPSTR)(MAX_PATH + 42) );
-    ok( len == 0 && GetLastError() == ERROR_INVALID_PARAMETER,
-        "GetCurrentDirectoryA failed to fail %u err %u\n", len, GetLastError() );
+      GetCurrentDirectoryA( 42, (LPSTR)(MAX_PATH + 42) );
   }
 
 /* SetCurrentDirectoryA shouldn't care whether the string has a
@@ -540,6 +552,28 @@ static void test_CleanupPathA(CHAR *origdir, CHAR *curdir)
   ok(RemoveDirectoryA(curdir),"RemoveDirectoryA failed\n");
 }
 
+/* test that short path name functions work regardless of case */
+static void test_ShortPathCase(const char *tmpdir, const char *dirname,
+                               const char *filename)
+{
+    char buf[MAX_PATH], shortbuf[MAX_PATH];
+    HANDLE hndl;
+    int i;
+
+    snprintf(buf,sizeof(buf),"%s\\%s\\%s",tmpdir,dirname,filename);
+    GetShortPathNameA(buf,shortbuf,sizeof(shortbuf));
+    hndl = CreateFileA(shortbuf,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL);
+    ok(hndl!=INVALID_HANDLE_VALUE,"CreateFileA failed (%d)\n",GetLastError());
+    CloseHandle(hndl);
+    /* Now for the real test */
+    for(i=0;i<strlen(shortbuf);i++)
+        if (i % 2)
+            shortbuf[i] = tolower(shortbuf[i]);
+    hndl = CreateFileA(shortbuf,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL);
+    ok(hndl!=INVALID_HANDLE_VALUE,"CreateFileA failed (%d)\n",GetLastError());
+    CloseHandle(hndl);
+}
+
 /* This routine will test Get(Full|Short|Long)PathNameA */
 static void test_PathNameA(CHAR *curdir, CHAR curDrive, CHAR otherDrive)
 {
@@ -863,6 +897,10 @@ static void test_PathNameA(CHAR *curdir, CHAR curDrive, CHAR otherDrive)
     sprintf(tmpstr,"Long File %c",funny_chars[i]);
     test_FunnyChars(curdir,curdir_short,tmpstr,valid,tmpstr1);
   }
+  /* Now try it on mixed case short names */
+  test_ShortPathCase(curdir,SHORTDIR,LONGFILE);
+  test_ShortPathCase(curdir,LONGDIR,SHORTFILE);
+  test_ShortPathCase(curdir,LONGDIR,LONGFILE);
 }
 
 static void test_GetTempPathA(char* tmp_dir)
@@ -1200,7 +1238,7 @@ static void test_GetLongPathNameW(void)
 
     /* NULL buffer with length crashes on Windows */
     if (0)
-    length = pGetLongPathNameW(shortpath, NULL, 20);
+        pGetLongPathNameW(shortpath, NULL, 20);
 
     ok(DeleteFileW(shortpath), "Could not delete temporary file\n");
     ok(RemoveDirectoryW(dirpath), "Could not delete temporary directory\n");
@@ -1261,7 +1299,8 @@ static void test_GetSystemDirectory(void)
     total = res;
 
     /* this crashes on XP */
-    if (0) res = GetSystemDirectory(NULL, total);
+    if (0)
+        GetSystemDirectory(NULL, total);
 
     SetLastError(0xdeadbeef);
     res = GetSystemDirectory(NULL, total-1);
@@ -1319,7 +1358,8 @@ static void test_GetWindowsDirectory(void)
 
     total = res;
     /* this crashes on XP */
-    if (0) res = GetWindowsDirectory(NULL, total);
+    if (0)
+        GetWindowsDirectory(NULL, total);
 
     SetLastError(0xdeadbeef);
     res = GetWindowsDirectory(NULL, total-1);
@@ -1374,7 +1414,7 @@ static void test_NeedCurrentDirectoryForExePathA(void)
 
     /* Crashes in Windows */
     if (0)
-        ok(pNeedCurrentDirectoryForExePathA(NULL), "returned FALSE for NULL\n");
+        pNeedCurrentDirectoryForExePathA(NULL);
 
     SetEnvironmentVariableA("NoDefaultCurrentDirectoryInExePath", NULL);
     ok(pNeedCurrentDirectoryForExePathA("."), "returned FALSE for \".\"\n");
@@ -1401,7 +1441,7 @@ static void test_NeedCurrentDirectoryForExePathW(void)
 
     /* Crashes in Windows */
     if (0)
-        ok(pNeedCurrentDirectoryForExePathW(NULL), "returned FALSE for NULL\n");
+        pNeedCurrentDirectoryForExePathW(NULL);
 
     SetEnvironmentVariableA("NoDefaultCurrentDirectoryInExePath", NULL);
     ok(pNeedCurrentDirectoryForExePathW(thispath), "returned FALSE for \".\"\n");
@@ -1524,8 +1564,7 @@ static void test_SearchPathA(void)
     SetLastError(0xdeadbeef);
     ret = pSearchPathA(pathA, fileA, NULL, sizeof(buffA)/sizeof(CHAR), buffA, &ptrA);
     ok(ret == 0, "Expected failure, got %d\n", ret);
-    ok(GetLastError() == ERROR_INVALID_PARAMETER ||
-       broken(GetLastError() == ERROR_FILE_NOT_FOUND) /* win9x */,
+    ok(GetLastError() == ERROR_INVALID_PARAMETER,
       "Expected ERROR_INVALID_PARAMETER, got %x\n", GetLastError());
 }
 
@@ -1541,24 +1580,10 @@ static void test_SearchPathW(void)
         return;
     }
 
-    /* SearchPathW is a stub on win9x and doesn't return sane error,
-       so quess if it's implemented indirectly */
-    SetLastError(0xdeadbeef);
-    GetWindowsDirectoryW(pathW, sizeof(pathW)/sizeof(WCHAR));
-    if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
-    {
-        win_skip("SearchPathW not implemented\n");
-        return;
-    }
-
 if (0)
 {
     /* NULL filename, crashes on nt4 */
-    SetLastError(0xdeadbeef);
-    ret = pSearchPathW(pathW, NULL, NULL, sizeof(buffW)/sizeof(WCHAR), buffW, &ptrW);
-    ok(ret == 0, "Expected failure, got %d\n", ret);
-    ok(GetLastError() == ERROR_INVALID_PARAMETER,
-       "Expected ERROR_INVALID_PARAMETER, got %x\n", GetLastError());
+    pSearchPathW(pathW, NULL, NULL, sizeof(buffW)/sizeof(WCHAR), buffW, &ptrW);
 }
 
     /* empty filename */
@@ -1573,7 +1598,7 @@ static void test_GetFullPathNameA(void)
 {
     char output[MAX_PATH], *filepart;
     DWORD ret;
-    int is_win9x, i;
+    int i;
 
     const struct
     {
@@ -1581,31 +1606,20 @@ static void test_GetFullPathNameA(void)
         DWORD len;
         LPSTR buffer;
         LPSTR *lastpart;
-        int win9x_crash;
     } invalid_parameters[] =
     {
-        {NULL, 0,        NULL,   NULL,      1},
-        {NULL, MAX_PATH, NULL,   NULL,      1},
-        {NULL, MAX_PATH, output, NULL,      1},
-        {NULL, MAX_PATH, output, &filepart, 1},
+        {NULL, 0,        NULL,   NULL},
+        {NULL, MAX_PATH, NULL,   NULL},
+        {NULL, MAX_PATH, output, NULL},
+        {NULL, MAX_PATH, output, &filepart},
         {"",   0,        NULL,   NULL},
         {"",   MAX_PATH, NULL,   NULL},
         {"",   MAX_PATH, output, NULL},
         {"",   MAX_PATH, output, &filepart},
     };
 
-    SetLastError(0xdeadbeef);
-    ret = GetFullPathNameW(NULL, 0, NULL, NULL);
-    is_win9x = !ret && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED;
-
-    if (is_win9x)
-        win_skip("Skipping some tests that cause GetFullPathNameA to crash on Win9x\n");
-
     for (i = 0; i < sizeof(invalid_parameters)/sizeof(invalid_parameters[0]); i++)
     {
-        if (is_win9x && invalid_parameters[i].win9x_crash)
-            continue;
-
         SetLastError(0xdeadbeef);
         strcpy(output, "deadbeef");
         filepart = (char *)0xdeadbeef;
@@ -1617,7 +1631,6 @@ static void test_GetFullPathNameA(void)
         ok(!strcmp(output, "deadbeef"), "[%d] Expected the output buffer to be unchanged, got \"%s\"\n", i, output);
         ok(filepart == (char *)0xdeadbeef, "[%d] Expected output file part pointer to be untouched, got %p\n", i, filepart);
         ok(GetLastError() == 0xdeadbeef ||
-           GetLastError() == ERROR_BAD_PATHNAME || /* Win9x */
            GetLastError() == ERROR_INVALID_NAME, /* Win7 */
            "[%d] Expected GetLastError() to return 0xdeadbeef, got %u\n",
            i, GetLastError());
index f5a56c6..4b3566a 100755 (executable)
 static HANDLE alarm_event;
 static BOOL (WINAPI *pDuplicateTokenEx)(HANDLE,DWORD,LPSECURITY_ATTRIBUTES,
                                         SECURITY_IMPERSONATION_LEVEL,TOKEN_TYPE,PHANDLE);
+static DWORD WINAPI (*pQueueUserAPC)(PAPCFUNC pfnAPC, HANDLE hThread, ULONG_PTR dwData);
 
+static BOOL user_apc_ran;
+static void CALLBACK user_apc(ULONG_PTR param)
+{
+    user_apc_ran = TRUE;
+}
 
 static void test_CreateNamedPipe(int pipemode)
 {
@@ -50,6 +56,7 @@ static void test_CreateNamedPipe(int pipemode)
     DWORD readden;
     DWORD avail;
     DWORD lpmode;
+    BOOL ret;
 
     if (pipemode == PIPE_TYPE_BYTE)
         trace("test_CreateNamedPipe starting in byte mode\n");
@@ -86,12 +93,14 @@ static void test_CreateNamedPipe(int pipemode)
         /* lpSecurityAttrib */ NULL);
     ok(hnp != INVALID_HANDLE_VALUE, "CreateNamedPipe failed\n");
 
-    ok(WaitNamedPipeA(PIPENAME, 2000), "WaitNamedPipe failed (%d)\n", GetLastError());
+    ret = WaitNamedPipeA(PIPENAME, 2000);
+    ok(ret, "WaitNamedPipe failed (%d)\n", GetLastError());
 
     hFile = CreateFileA(PIPENAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
     ok(hFile != INVALID_HANDLE_VALUE, "CreateFile failed (%d)\n", GetLastError());
 
     ok(!WaitNamedPipeA(PIPENAME, 1000), "WaitNamedPipe succeeded\n");
+
     ok(GetLastError() == ERROR_SEM_TIMEOUT, "wrong error %u\n", GetLastError());
 
     /* don't try to do i/o if one side couldn't be opened, as it hangs */
@@ -392,7 +401,7 @@ static DWORD CALLBACK alarmThreadMain(LPVOID arg)
     return 1;
 }
 
-HANDLE hnp = INVALID_HANDLE_VALUE;
+static HANDLE hnp = INVALID_HANDLE_VALUE;
 
 /** Trivial byte echo server - disconnects after each session */
 static DWORD CALLBACK serverThreadMain1(LPVOID arg)
@@ -468,6 +477,12 @@ static DWORD CALLBACK serverThreadMain2(LPVOID arg)
         DWORD readden;
         DWORD success;
 
+        user_apc_ran = FALSE;
+        if (i == 0 && pQueueUserAPC) {
+            trace("Queueing an user APC\n"); /* verify the pipe is non alerable */
+            ok(pQueueUserAPC(&user_apc, GetCurrentThread(), 0), "QueueUserAPC failed: %d\n", GetLastError());
+        }
+
         /* Wait for client to connect */
         trace("Server calling ConnectNamedPipe...\n");
         ok(ConnectNamedPipe(hnp, NULL)
@@ -491,6 +506,11 @@ static DWORD CALLBACK serverThreadMain2(LPVOID arg)
         ok(FlushFileBuffers(hnp), "FlushFileBuffers\n");
         ok(DisconnectNamedPipe(hnp), "DisconnectNamedPipe\n");
 
+        ok(user_apc_ran == FALSE, "UserAPC ran, pipe using alertable io mode\n");
+
+        if (i == 0 && pQueueUserAPC)
+            SleepEx(0, TRUE); /* get rid of apc */
+
         /* Set up next echo server */
         hnpNext =
             CreateNamedPipe(PIPENAME "serverThreadMain2", PIPE_ACCESS_DUPLEX,
@@ -642,6 +662,7 @@ static DWORD CALLBACK serverThreadMain4(LPVOID arg)
 {
     int i;
     HANDLE hcompletion;
+    BOOL ret;
 
     trace("serverThreadMain4\n");
     /* Set up a simple echo server */
@@ -738,8 +759,10 @@ static DWORD CALLBACK serverThreadMain4(LPVOID arg)
         ok(success, "DisconnectNamedPipe failed, err %u\n", GetLastError());
     }
 
-    ok(CloseHandle(hnp), "CloseHandle named pipe failed, err=%i\n", GetLastError());
-    ok(CloseHandle(hcompletion), "CloseHandle completion failed, err=%i\n", GetLastError());
+    ret = CloseHandle(hnp);
+    ok(ret, "CloseHandle named pipe failed, err=%i\n", GetLastError());
+    ret = CloseHandle(hcompletion);
+    ok(ret, "CloseHandle completion failed, err=%i\n", GetLastError());
 
     return 0;
 }
@@ -910,9 +933,13 @@ static void test_CreatePipe(void)
     BYTE *buffer;
     char readbuf[32];
 
+    user_apc_ran = FALSE;
+    if (pQueueUserAPC)
+        ok(pQueueUserAPC(user_apc, GetCurrentThread(), 0), "couldn't create user apc\n");
+
     pipe_attr.nLength = sizeof(SECURITY_ATTRIBUTES); 
     pipe_attr.bInheritHandle = TRUE; 
-    pipe_attr.lpSecurityDescriptor = NULL; 
+    pipe_attr.lpSecurityDescriptor = NULL;
     ok(CreatePipe(&piperead, &pipewrite, &pipe_attr, 0) != 0, "CreatePipe failed\n");
     ok(WriteFile(pipewrite,PIPENAME,sizeof(PIPENAME), &written, NULL), "Write to anonymous pipe failed\n");
     ok(written == sizeof(PIPENAME), "Write to anonymous pipe wrote %d bytes\n", written);
@@ -950,6 +977,9 @@ static void test_CreatePipe(void)
     ok(ReadFile(piperead,readbuf,sizeof(readbuf),&read, NULL) == 0, "Broken pipe not detected\n");
     ok(CloseHandle(piperead), "CloseHandle for the read pipe failed\n");
     HeapFree(GetProcessHeap(), 0, buffer);
+
+    ok(user_apc_ran == FALSE, "user apc ran, pipe using alertable io mode\n");
+    SleepEx(0, TRUE); /* get rid of apc */
 }
 
 struct named_pipe_client_params
@@ -1443,7 +1473,7 @@ static void test_overlapped(void)
 {
     DWORD tid, num;
     HANDLE thread, pipe;
-    int ret;
+    BOOL ret;
     struct overlapped_server_args args;
 
     args.pipe_created = CreateEventA(0, 1, 0, 0);
@@ -1457,7 +1487,7 @@ static void test_overlapped(void)
     Sleep(1);
 
     ret = WriteFile(pipe, "x", 1, &num, NULL);
-    ok(ret == 1, "ret %d\n", ret);
+    ok(ret, "WriteFile failed with error %d\n", GetLastError());
 
     WaitForSingleObject(thread, INFINITE);
     CloseHandle(pipe);
@@ -1582,6 +1612,8 @@ START_TEST(pipe)
 
     hmod = GetModuleHandle("advapi32.dll");
     pDuplicateTokenEx = (void *) GetProcAddress(hmod, "DuplicateTokenEx");
+    hmod = GetModuleHandle("kernel32.dll");
+    pQueueUserAPC = (void *) GetProcAddress(hmod, "QueueUserAPC");
 
     if (test_DisconnectNamedPipe())
         return;
index b4a048a..b3b8d18 100755 (executable)
@@ -56,6 +56,7 @@
 
 static HINSTANCE hkernel32;
 static void   (WINAPI *pGetNativeSystemInfo)(LPSYSTEM_INFO);
+static BOOL   (WINAPI *pGetSystemRegistryQuota)(PDWORD, PDWORD);
 static BOOL   (WINAPI *pIsWow64Process)(HANDLE,PBOOL);
 static LPVOID (WINAPI *pVirtualAllocEx)(HANDLE, LPVOID, SIZE_T, DWORD, DWORD);
 static BOOL   (WINAPI *pVirtualFreeEx)(HANDLE, LPVOID, SIZE_T, DWORD);
@@ -197,6 +198,7 @@ static int     init(void)
 
     hkernel32 = GetModuleHandleA("kernel32");
     pGetNativeSystemInfo = (void *) GetProcAddress(hkernel32, "GetNativeSystemInfo");
+    pGetSystemRegistryQuota = (void *) GetProcAddress(hkernel32, "GetSystemRegistryQuota");
     pIsWow64Process = (void *) GetProcAddress(hkernel32, "IsWow64Process");
     pVirtualAllocEx = (void *) GetProcAddress(hkernel32, "VirtualAllocEx");
     pVirtualFreeEx = (void *) GetProcAddress(hkernel32, "VirtualFreeEx");
@@ -247,8 +249,8 @@ static void     doChild(const char* file, const char* option)
     STARTUPINFOA        siA;
     STARTUPINFOW        siW;
     int                 i;
-    char*               ptrA;
-    WCHAR*              ptrW;
+    char                *ptrA, *ptrA_save;
+    WCHAR               *ptrW, *ptrW_save;
     char                bufA[MAX_PATH];
     WCHAR               bufW[MAX_PATH];
     HANDLE              hFile = CreateFileA(file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
@@ -309,7 +311,7 @@ static void     doChild(const char* file, const char* option)
     childPrintf(hFile, "CommandLineW=%s\n\n", encodeW(GetCommandLineW()));
 
     /* output of environment (Ansi) */
-    ptrA = GetEnvironmentStringsA();
+    ptrA_save = ptrA = GetEnvironmentStringsA();
     if (ptrA)
     {
         char    env_var[MAX_LISTED_ENV_VAR];
@@ -324,10 +326,11 @@ static void     doChild(const char* file, const char* option)
             ptrA += strlen(ptrA) + 1;
         }
         childPrintf(hFile, "len=%d\n\n", i);
+        FreeEnvironmentStringsA(ptrA_save);
     }
 
     /* output of environment (Unicode) */
-    ptrW = GetEnvironmentStringsW();
+    ptrW_save = ptrW = GetEnvironmentStringsW();
     if (ptrW)
     {
         WCHAR   env_var[MAX_LISTED_ENV_VAR];
@@ -343,6 +346,7 @@ static void     doChild(const char* file, const char* option)
             ptrW += lstrlenW(ptrW) + 1;
         }
         childPrintf(hFile, "len=%d\n\n", i);
+        FreeEnvironmentStringsW(ptrW_save);
     }
 
     childPrintf(hFile, "[Misc]\n");
@@ -539,7 +543,7 @@ static void test_Startup(void)
     startup.wShowWindow = SW_SHOWNORMAL;
 
     get_file_name(resfile);
-    sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
+    sprintf(buffer, "\"%s\" tests/process.c \"%s\"", selfname, resfile);
     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
     /* wait for child to terminate */
     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
@@ -577,7 +581,7 @@ static void test_Startup(void)
     startup.dwFillAttribute = 0xA55A;
 
     get_file_name(resfile);
-    sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
+    sprintf(buffer, "\"%s\" tests/process.c \"%s\"", selfname, resfile);
     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
     /* wait for child to terminate */
     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
@@ -615,7 +619,7 @@ static void test_Startup(void)
     startup.dwFillAttribute = 0xA55A;
 
     get_file_name(resfile);
-    sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
+    sprintf(buffer, "\"%s\" tests/process.c \"%s\"", selfname, resfile);
     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
     /* wait for child to terminate */
     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
@@ -653,7 +657,7 @@ static void test_Startup(void)
     startup.dwFillAttribute = 0xA55A;
 
     get_file_name(resfile);
-    sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
+    sprintf(buffer, "\"%s\" tests/process.c \"%s\"", selfname, resfile);
     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
     /* wait for child to terminate */
     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
@@ -691,7 +695,7 @@ static void test_Startup(void)
     startup.dwFillAttribute = 0xA55A;
 
     get_file_name(resfile);
-    sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
+    sprintf(buffer, "\"%s\" tests/process.c \"%s\"", selfname, resfile);
     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
     /* wait for child to terminate */
     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
@@ -731,7 +735,7 @@ static void test_Startup(void)
     startup.dwFillAttribute = 0xA55A;
 
     get_file_name(resfile);
-    sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
+    sprintf(buffer, "\"%s\" tests/process.c \"%s\"", selfname, resfile);
     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
     /* wait for child to terminate */
     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
@@ -769,7 +773,7 @@ static void test_Startup(void)
     startup.dwFillAttribute = 0xA55A;
 
     get_file_name(resfile);
-    sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
+    sprintf(buffer, "\"%s\" tests/process.c \"%s\"", selfname, resfile);
     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
     /* wait for child to terminate */
     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
@@ -809,7 +813,7 @@ static void test_CommandLine(void)
 
     /* the basics */
     get_file_name(resfile);
-    sprintf(buffer, "%s tests/process.c %s \"C:\\Program Files\\my nice app.exe\"", selfname, resfile);
+    sprintf(buffer, "\"%s\" tests/process.c \"%s\" \"C:\\Program Files\\my nice app.exe\"", selfname, resfile);
     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
     /* wait for child to terminate */
     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
@@ -828,9 +832,9 @@ static void test_CommandLine(void)
     startup.dwFlags = STARTF_USESHOWWINDOW;
     startup.wShowWindow = SW_SHOWNORMAL;
 
-    /* from Frangois */
+    /* from François */
     get_file_name(resfile);
-    sprintf(buffer, "%s tests/process.c %s \"a\\\"b\\\\\" c\\\" d", selfname, resfile);
+    sprintf(buffer, "\"%s\" tests/process.c \"%s\" \"a\\\"b\\\\\" c\\\" d", selfname, resfile);
     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
     /* wait for child to terminate */
     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
@@ -849,7 +853,7 @@ static void test_CommandLine(void)
     /* Test for Bug1330 to show that XP doesn't change '/' to '\\' in argv[0]*/
     get_file_name(resfile);
     /* Use exename to avoid buffer containing things like 'C:' */
-    sprintf(buffer, "./%s tests/process.c %s \"a\\\"b\\\\\" c\\\" d", exename, resfile);
+    sprintf(buffer, "./%s tests/process.c \"%s\" \"a\\\"b\\\\\" c\\\" d", exename, resfile);
     SetLastError(0xdeadbeef);
     ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info);
     ok(ret, "CreateProcess (%s) failed : %d\n", buffer, GetLastError());
@@ -864,7 +868,7 @@ static void test_CommandLine(void)
 
     get_file_name(resfile);
     /* Use exename to avoid buffer containing things like 'C:' */
-    sprintf(buffer, ".\\%s tests/process.c %s \"a\\\"b\\\\\" c\\\" d", exename, resfile);
+    sprintf(buffer, ".\\%s tests/process.c \"%s\" \"a\\\"b\\\\\" c\\\" d", exename, resfile);
     SetLastError(0xdeadbeef);
     ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info);
     ok(ret, "CreateProcess (%s) failed : %d\n", buffer, GetLastError());
@@ -883,8 +887,8 @@ static void test_CommandLine(void)
     *(lpFilePart -1 ) = 0;
     p = strrchr(fullpath, '\\');
     /* Use exename to avoid buffer containing things like 'C:' */
-    if (p) sprintf(buffer, "..%s/%s tests/process.c %s \"a\\\"b\\\\\" c\\\" d", p, exename, resfile);
-    else sprintf(buffer, "./%s tests/process.c %s \"a\\\"b\\\\\" c\\\" d", exename, resfile);
+    if (p) sprintf(buffer, "..%s/%s tests/process.c \"%s\" \"a\\\"b\\\\\" c\\\" d", p, exename, resfile);
+    else sprintf(buffer, "./%s tests/process.c \"%s\" \"a\\\"b\\\\\" c\\\" d", exename, resfile);
     SetLastError(0xdeadbeef);
     ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info);
     ok(ret, "CreateProcess (%s) failed : %d\n", buffer, GetLastError());
@@ -907,7 +911,7 @@ static void test_CommandLine(void)
     /* Use exename to avoid buffer containing things like 'C:' */
     if (p) sprintf(buffer, "..%s/%s", p, exename);
     else sprintf(buffer, "./%s", exename);
-    sprintf(buffer2, "dummy tests/process.c %s \"a\\\"b\\\\\" c\\\" d", resfile);
+    sprintf(buffer2, "dummy tests/process.c \"%s\" \"a\\\"b\\\\\" c\\\" d", resfile);
     SetLastError(0xdeadbeef);
     ret = CreateProcessA(buffer, buffer2, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info);
     ok(ret, "CreateProcess (%s) failed : %d\n", buffer, GetLastError());
@@ -1005,7 +1009,7 @@ static void test_Directory(void)
 
     /* the basics */
     get_file_name(resfile);
-    sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
+    sprintf(buffer, "\"%s\" tests/process.c \"%s\"", selfname, resfile);
     GetWindowsDirectoryA( windir, sizeof(windir) );
     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, windir, &startup, &info), "CreateProcess\n");
     /* wait for child to terminate */
@@ -1098,10 +1102,11 @@ static void test_Environment(void)
     char                buffer[MAX_PATH];
     PROCESS_INFORMATION        info;
     STARTUPINFOA       startup;
-    char*               child_env;
+    char                *child_env;
     int                 child_env_len;
-    char*               ptr;
-    char*               env;
+    char                *ptr;
+    char                *ptr2;
+    char                *env;
     int                 slen;
 
     memset(&startup, 0, sizeof(startup));
@@ -1111,14 +1116,15 @@ static void test_Environment(void)
 
     /* the basics */
     get_file_name(resfile);
-    sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
+    sprintf(buffer, "\"%s\" tests/process.c \"%s\"", selfname, resfile);
     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
     /* wait for child to terminate */
     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
     /* child process has changed result file, so let profile functions know about it */
     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
-    
-    cmpEnvironment(GetEnvironmentStringsA());
+
+    env = GetEnvironmentStringsA();
+    cmpEnvironment(env);
     release_memory();
     assert(DeleteFileA(resfile) != 0);
 
@@ -1129,10 +1135,10 @@ static void test_Environment(void)
 
     /* the basics */
     get_file_name(resfile);
-    sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
-    
+    sprintf(buffer, "\"%s\" tests/process.c \"%s\"", selfname, resfile);
+
     child_env_len = 0;
-    ptr = GetEnvironmentStringsA();
+    ptr = env;
     while(*ptr)
     {
         slen = strlen(ptr)+1;
@@ -1142,7 +1148,7 @@ static void test_Environment(void)
     /* Add space for additional environment variables */
     child_env_len += 256;
     child_env = HeapAlloc(GetProcessHeap(), 0, child_env_len);
-    
+
     ptr = child_env;
     sprintf(ptr, "=%c:=%s", 'C', "C:\\FOO\\BAR");
     ptr += strlen(ptr) + 1;
@@ -1157,13 +1163,13 @@ static void test_Environment(void)
      * - PATH (already set above)
      * - the directory definitions (=[A-Z]:=)
      */
-    for (env = GetEnvironmentStringsA(); *env; env += strlen(env) + 1)
+    for (ptr2 = env; *ptr2; ptr2 += strlen(ptr2) + 1)
     {
-        if (strncmp(env, "PATH=", 5) != 0 &&
-            strncmp(env, "WINELOADER=", 11) != 0 &&
-            !is_str_env_drive_dir(env))
+        if (strncmp(ptr2, "PATH=", 5) != 0 &&
+            strncmp(ptr2, "WINELOADER=", 11) != 0 &&
+            !is_str_env_drive_dir(ptr2))
         {
-            strcpy(ptr, env);
+            strcpy(ptr, ptr2);
             ptr += strlen(ptr) + 1;
         }
     }
@@ -1173,10 +1179,11 @@ static void test_Environment(void)
     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
     /* child process has changed result file, so let profile functions know about it */
     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
-    
+
     cmpEnvironment(child_env);
 
     HeapFree(GetProcessHeap(), 0, child_env);
+    FreeEnvironmentStringsA(env);
     release_memory();
     assert(DeleteFileA(resfile) != 0);
 }
@@ -1196,7 +1203,7 @@ static  void    test_SuspendFlag(void)
     startup.wShowWindow = SW_SHOWNORMAL;
 
     get_file_name(resfile);
-    sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
+    sprintf(buffer, "\"%s\" tests/process.c \"%s\"", selfname, resfile);
     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &startup, &info), "CreateProcess\n");
 
     ok(GetExitCodeThread(info.hThread, &exit_status) && exit_status == STILL_ACTIVE, "thread still running\n");
@@ -1246,7 +1253,7 @@ static  void    test_DebuggingFlag(void)
     startup.wShowWindow = SW_SHOWNORMAL;
 
     get_file_name(resfile);
-    sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
+    sprintf(buffer, "\"%s\" tests/process.c \"%s\"", selfname, resfile);
     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &startup, &info), "CreateProcess\n");
 
     /* get all startup events up to the entry point break exception */
@@ -1344,7 +1351,7 @@ static void test_Console(void)
     cpOut = GetConsoleOutputCP();
 
     get_file_name(resfile);
-    sprintf(buffer, "%s tests/process.c %s console", selfname, resfile);
+    sprintf(buffer, "\"%s\" tests/process.c \"%s\" console", selfname, resfile);
     ok(CreateProcessA(NULL, buffer, NULL, NULL, TRUE, 0, NULL, NULL, &startup, &info), "CreateProcess\n");
 
     /* wait for child to terminate */
@@ -1458,7 +1465,7 @@ static void test_Console(void)
     startup.hStdError = hChildOutInh;
 
     get_file_name(resfile);
-    sprintf(buffer, "%s tests/process.c %s stdhandle", selfname, resfile);
+    sprintf(buffer, "\"%s\" tests/process.c \"%s\" stdhandle", selfname, resfile);
     ok(CreateProcessA(NULL, buffer, NULL, NULL, TRUE, DETACHED_PROCESS, NULL, NULL, &startup, &info), "CreateProcess\n");
     ok(CloseHandle(hChildInInh), "Closing handle\n");
     ok(CloseHandle(hChildOutInh), "Closing handle\n");
@@ -1495,7 +1502,7 @@ static  void    test_ExitCode(void)
     startup.wShowWindow = SW_SHOWNORMAL;
 
     get_file_name(resfile);
-    sprintf(buffer, "%s tests/process.c %s exit_code", selfname, resfile);
+    sprintf(buffer, "\"%s\" tests/process.c \"%s\" exit_code", selfname, resfile);
     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info), "CreateProcess\n");
 
     /* wait for child to terminate */
@@ -1516,6 +1523,7 @@ static void test_OpenProcess(void)
     void *addr1;
     MEMORY_BASIC_INFORMATION info;
     SIZE_T dummy, read_bytes;
+    BOOL ret;
 
     /* not exported in all windows versions */
     if ((!pVirtualAllocEx) || (!pVirtualFreeEx)) {
@@ -1540,8 +1548,8 @@ static void test_OpenProcess(void)
 
     read_bytes = 0xdeadbeef;
     SetLastError(0xdeadbeef);
-    ok(ReadProcessMemory(hproc, test_OpenProcess, &dummy, sizeof(dummy), &read_bytes),
-       "ReadProcessMemory error %d\n", GetLastError());
+    ret = ReadProcessMemory(hproc, test_OpenProcess, &dummy, sizeof(dummy), &read_bytes);
+    ok(ret, "ReadProcessMemory error %d\n", GetLastError());
     ok(read_bytes == sizeof(dummy), "wrong read bytes %ld\n", read_bytes);
 
     CloseHandle(hproc);
@@ -1571,8 +1579,8 @@ static void test_OpenProcess(void)
     hproc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, GetCurrentProcessId());
 
     memset(&info, 0xcc, sizeof(info));
-    ok(VirtualQueryEx(hproc, addr1, &info, sizeof(info)) == sizeof(info),
-       "VirtualQueryEx error %d\n", GetLastError());
+    read_bytes = VirtualQueryEx(hproc, addr1, &info, sizeof(info));
+    ok(read_bytes == sizeof(info), "VirtualQueryEx error %d\n", GetLastError());
 
     ok(info.BaseAddress == addr1, "%p != %p\n", info.BaseAddress, addr1);
     ok(info.AllocationBase == addr1, "%p != %p\n", info.AllocationBase, addr1);
@@ -1669,7 +1677,7 @@ static void test_ProcessNameA(void)
 
     /* this is a difference between the ascii and the unicode version
      * the unicode version crashes when the size is big enough to hold the result
-     * ascii version throughs an error
+     * ascii version through an error
      */
     size = 1024;
     expect_eq_d(FALSE, pQueryFullProcessImageNameA(GetCurrentProcess(), 0, NULL, &size));
@@ -1810,11 +1818,11 @@ static void test_SystemInfo(void)
     pGetNativeSystemInfo(&nsi);
     if (is_wow64)
     {
-        if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL)
+        if (S(U(si)).wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL)
         {
-            ok(nsi.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64,
+            ok(S(U(nsi)).wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64,
                "Expected PROCESSOR_ARCHITECTURE_AMD64, got %d\n",
-               nsi.wProcessorArchitecture);
+               S(U(nsi)).wProcessorArchitecture);
             ok(nsi.dwProcessorType == PROCESSOR_AMD_X8664,
                "Expected PROCESSOR_AMD_X8664, got %d\n",
                nsi.dwProcessorType);
@@ -1822,15 +1830,43 @@ static void test_SystemInfo(void)
     }
     else
     {
-        ok(si.wProcessorArchitecture == nsi.wProcessorArchitecture,
+        ok(S(U(si)).wProcessorArchitecture == S(U(nsi)).wProcessorArchitecture,
            "Expected no difference for wProcessorArchitecture, got %d and %d\n",
-           si.wProcessorArchitecture, nsi.wProcessorArchitecture);
+           S(U(si)).wProcessorArchitecture, S(U(nsi)).wProcessorArchitecture);
         ok(si.dwProcessorType == nsi.dwProcessorType,
            "Expected no difference for dwProcessorType, got %d and %d\n",
            si.dwProcessorType, nsi.dwProcessorType);
     }
 }
 
+static void test_RegistryQuota(void)
+{
+    BOOL ret;
+    DWORD max_quota, used_quota;
+
+    if (!pGetSystemRegistryQuota)
+    {
+        win_skip("GetSystemRegistryQuota is not available\n");
+        return;
+    }
+
+    ret = pGetSystemRegistryQuota(NULL, NULL);
+    ok(ret == TRUE,
+       "Expected GetSystemRegistryQuota to return TRUE, got %d\n", ret);
+
+    ret = pGetSystemRegistryQuota(&max_quota, NULL);
+    ok(ret == TRUE,
+       "Expected GetSystemRegistryQuota to return TRUE, got %d\n", ret);
+
+    ret = pGetSystemRegistryQuota(NULL, &used_quota);
+    ok(ret == TRUE,
+       "Expected GetSystemRegistryQuota to return TRUE, got %d\n", ret);
+
+    ret = pGetSystemRegistryQuota(&max_quota, &used_quota);
+    ok(ret == TRUE,
+       "Expected GetSystemRegistryQuota to return TRUE, got %d\n", ret);
+}
+
 START_TEST(process)
 {
     int b = init();
@@ -1856,6 +1892,7 @@ START_TEST(process)
     test_ProcessName();
     test_Handles();
     test_SystemInfo();
+    test_RegistryQuota();
     /* things that can be tested:
      *  lookup:         check the way program to be executed is searched
      *  handles:        check the handle inheritance stuff (+sec options)
index e3f7156..07b351c 100755 (executable)
@@ -146,6 +146,8 @@ static void test_profile_string(void)
     ret=GetPrivateProfileStringW(emptyW, keyW, emptyW, bufW,
                                  sizeof(bufW)/sizeof(bufW[0]), TESTFILE2W);
     todo_wine
+    ok(ret == 13, "expected 13, got %u\n", ret);
+    todo_wine
     ok(!lstrcmpW(valsectionW,bufW), "expected %s, got %s\n",
         wine_dbgstr_w(valsectionW), wine_dbgstr_w(bufW) );
 
@@ -153,6 +155,8 @@ static void test_profile_string(void)
     ret=GetPrivateProfileStringW(sW, emptyW, emptyW, bufW,
                                  sizeof(bufW)/sizeof(bufW[0]), TESTFILE2W);
     todo_wine
+    ok(ret == 10, "expected 10, got %u\n", ret);
+    todo_wine
     ok(!lstrcmpW(valnokeyW,bufW), "expected %s, got %s\n",
         wine_dbgstr_w(valnokeyW), wine_dbgstr_w(bufW) );
 
index a2c02c8..a85e6d2 100644 (file)
 static const char filename[] = "test_.exe";
 static DWORD GLE;
 
-static int build_exe( void )
+enum constants {
+    page_size = 0x1000,
+    rva_rsrc_start = page_size * 3,
+    max_sections = 3
+};
+
+/* rodata @ [0x1000-0x3000) */
+static const IMAGE_SECTION_HEADER sh_rodata_1 =
+{
+    ".rodata", {2*page_size}, page_size, 2*page_size, page_size, 0, 0, 0, 0,
+    IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
+};
+
+/* rodata @ [0x1000-0x4000) */
+static const IMAGE_SECTION_HEADER sh_rodata_2 =
+{
+    ".rodata", {3*page_size}, page_size, 3*page_size, page_size, 0, 0, 0, 0,
+    IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
+};
+
+/* rodata @ [0x1000-0x2000) */
+static const IMAGE_SECTION_HEADER sh_rodata_3 =
+{
+    ".rodata", {page_size}, page_size, page_size, page_size, 0, 0, 0, 0,
+    IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
+};
+
+/* rsrc @ [0x3000-0x4000) */
+static const IMAGE_SECTION_HEADER sh_rsrc_1 =
+{
+    ".rsrc\0\0", {page_size}, rva_rsrc_start, page_size, rva_rsrc_start, 0, 0, 0, 0,
+    IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
+};
+
+/* rsrc @ [0x4000-0x5000) */
+static const IMAGE_SECTION_HEADER sh_rsrc_2 =
+{
+    ".rsrc\0\0", {page_size}, 4*page_size, page_size, 4*page_size, 0, 0, 0, 0,
+    IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
+};
+
+/* rsrc @ [0x2000-0x4000) */
+static const IMAGE_SECTION_HEADER sh_rsrc_3 =
+{
+    ".rsrc\0\0", {2*page_size}, rva_rsrc_start-page_size, 2*page_size, rva_rsrc_start-page_size, 0, 0, 0, 0,
+    IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
+};
+
+/* rsrc @ [0x2000-0x3000) */
+static const IMAGE_SECTION_HEADER sh_rsrc_4 =
+{
+    ".rsrc\0\0", {page_size}, rva_rsrc_start-page_size, page_size, rva_rsrc_start-page_size, 0, 0, 0, 0,
+    IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
+};
+
+/* rsrc @ [0x3000-0x6000) */
+static const IMAGE_SECTION_HEADER sh_rsrc_5 =
+{
+    ".rsrc\0\0", {3*page_size}, rva_rsrc_start, 3*page_size, rva_rsrc_start, 0, 0, 0, 0,
+    IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
+};
+
+/* rsrc @ [0x4000-0x7000) */
+static const IMAGE_SECTION_HEADER sh_rsrc_6 =
+{
+    ".rsrc\0\0", {3*page_size}, 4*page_size, 3*page_size, 4*page_size, 0, 0, 0, 0,
+    IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
+};
+
+/* rsrc @ [0x2000-0x5000) */
+static const IMAGE_SECTION_HEADER sh_rsrc_7 =
+{
+    ".rsrc\0\0", {3*page_size}, 2*page_size, 3*page_size, 2*page_size, 0, 0, 0, 0,
+    IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
+};
+
+/* rsrc @ [0x3000-0x4000), small SizeOfRawData */
+static const IMAGE_SECTION_HEADER sh_rsrc_8 =
+{
+    ".rsrc\0\0", {page_size}, rva_rsrc_start, 8, rva_rsrc_start, 0, 0, 0, 0,
+    IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
+};
+
+/* reloc @ [0x4000-0x5000) */
+static const IMAGE_SECTION_HEADER sh_junk =
+{
+    ".reloc\0", {page_size}, 4*page_size, page_size, 4*page_size, 0, 0, 0, 0,
+    IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
+};
+
+/* reloc @ [0x6000-0x7000) */
+static const IMAGE_SECTION_HEADER sh_junk_2 =
+{
+    ".reloc\0", {page_size}, 6*page_size, page_size, 6*page_size, 0, 0, 0, 0,
+    IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
+};
+
+typedef struct _sec_build
+{
+    const IMAGE_SECTION_HEADER *sect_in[max_sections];
+} sec_build;
+
+typedef struct _sec_verify
+{
+    const IMAGE_SECTION_HEADER *sect_out[max_sections];
+    DWORD length;
+    int rsrc_section;
+    DWORD NumberOfNamedEntries, NumberOfIdEntries;
+} sec_verify;
+
+static const struct _sec_variants
+{
+    sec_build build;
+    sec_verify chk_none, chk_delete, chk_version, chk_bigdata;
+} sec_variants[] =
+{
+    /* .rsrc is the last section, data directory entry points to whole section */
+    {
+        {{&sh_rodata_1, &sh_rsrc_1, NULL}},
+        {{&sh_rodata_1, &sh_rsrc_1, NULL}, 4*page_size, 1, 0, 0},
+        {{&sh_rodata_1, &sh_rsrc_1, NULL}, 4*page_size, 1, 0, 0},
+        {{&sh_rodata_1, &sh_rsrc_1, NULL}, 4*page_size, 1, 0, 1},
+        {{&sh_rodata_1, &sh_rsrc_5, NULL}, 6*page_size, 1, 0, 1}
+    },
+    /* single .rodata section with compatible characteristics, data directory entry points to section end */
+    /* Vista+ - existing section isn't used, new section is created at the end of file */
+    /* NT4/2000/2003 - image is broken */
+#if 0
+    {
+        {{&sh_rodata_2, NULL, NULL}},
+        {{&sh_rodata_2, &sh_rsrc_2, NULL}, 5*page_size, 1, 0, 0},
+        {{&sh_rodata_2, &sh_rsrc_2, NULL}, 5*page_size, 1, 0, 0},
+        {{&sh_rodata_2, &sh_rsrc_2, NULL}, 5*page_size, 1, 0, 1},
+        {{&sh_rodata_2, &sh_rsrc_6, NULL}, 7*page_size, 1, 0, 1}
+    },
+#endif
+    /* .rsrc is the last section, data directory entry points to section end */
+    /* Vista+ - resources are moved to section start (trashing data that could be there), and section is trimmed */
+    /* NT4/2000/2003 - resources are moved to section start (trashing data that could be there); section isn't trimmed */
+    {
+        {{&sh_rodata_3, &sh_rsrc_3, NULL}},
+        {{&sh_rodata_3, &sh_rsrc_4, NULL}, 3*page_size, 1, 0, 0},
+        {{&sh_rodata_3, &sh_rsrc_4, NULL}, 3*page_size, 1, 0, 0},
+        {{&sh_rodata_3, &sh_rsrc_4, NULL}, 3*page_size, 1, 0, 1},
+        {{&sh_rodata_3, &sh_rsrc_7, NULL}, 5*page_size, 1, 0, 1}
+    },
+    /* .rsrc is not the last section */
+    /* section is reused; sections after .rsrc are shifted to give space to rsrc (in-image offset and RVA!) */
+    {
+        {{&sh_rodata_1, &sh_rsrc_1, &sh_junk}},
+        {{&sh_rodata_1, &sh_rsrc_1, &sh_junk}, 5*page_size, 1, 0, 0},
+        {{&sh_rodata_1, &sh_rsrc_1, &sh_junk}, 5*page_size, 1, 0, 0},
+        {{&sh_rodata_1, &sh_rsrc_1, &sh_junk}, 5*page_size, 1, 0, 1},
+        {{&sh_rodata_1, &sh_rsrc_5, &sh_junk_2}, 7*page_size, 1, 0, 1}
+    },
+    /* .rsrc is the last section, data directory entry points to whole section, file size is not aligned on FileAlign */
+    {
+        {{&sh_rodata_1, &sh_rsrc_8, NULL}},
+        {{&sh_rodata_1, &sh_rsrc_1, NULL}, 4*page_size, 1, 0, 0},
+        {{&sh_rodata_1, &sh_rsrc_1, NULL}, 4*page_size, 1, 0, 0},
+        {{&sh_rodata_1, &sh_rsrc_1, NULL}, 4*page_size, 1, 0, 1},
+        {{&sh_rodata_1, &sh_rsrc_5, NULL}, 6*page_size, 1, 0, 1}
+    }
+};
+
+static int build_exe( const sec_build* sec_descr )
 {
     IMAGE_DOS_HEADER *dos;
     IMAGE_NT_HEADERS *nt;
     IMAGE_SECTION_HEADER *sec;
     IMAGE_OPTIONAL_HEADER *opt;
     HANDLE file;
-    DWORD written;
-    BYTE page[0x1000];
-    const int page_size = 0x1000;
+    DWORD written, i, file_size;
+    BYTE page[page_size];
 
     memset( page, 0, sizeof page );
 
@@ -47,7 +211,7 @@ static int build_exe( void )
 
     nt->Signature = IMAGE_NT_SIGNATURE;
     nt->FileHeader.Machine = IMAGE_FILE_MACHINE_I386;
-    nt->FileHeader.NumberOfSections = 2;
+    nt->FileHeader.NumberOfSections = 0;
     nt->FileHeader.SizeOfOptionalHeader = sizeof nt->OptionalHeader;
     nt->FileHeader.Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DLL;
 
@@ -61,7 +225,7 @@ static int build_exe( void )
     opt->MajorImageVersion = 1;
     opt->MajorSubsystemVersion = 4;
     opt->SizeOfHeaders = sizeof *dos + sizeof *nt + sizeof *sec * 2;
-    opt->SizeOfImage = page_size*3;
+    opt->SizeOfImage = page_size;
     opt->Subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI;
 
     /* if SectionAlignment and File alignment are not specified */
@@ -69,21 +233,27 @@ static int build_exe( void )
     opt->SectionAlignment = page_size;
     opt->FileAlignment = page_size;
 
-    sec = (void*) &nt[1];
+    opt->DataDirectory[IMAGE_FILE_RESOURCE_DIRECTORY].VirtualAddress = rva_rsrc_start;
+    opt->DataDirectory[IMAGE_FILE_RESOURCE_DIRECTORY].Size = page_size;
 
-    memcpy( sec[0].Name, ".rodata", 8 );
-    sec[0].Misc.VirtualSize = page_size;
-    sec[0].PointerToRawData = page_size;
-    sec[0].SizeOfRawData = page_size;
-    sec[0].VirtualAddress = page_size;
-    sec[0].Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ;
+    sec = (void*) &nt[1];
 
-    memcpy( sec[1].Name, ".rsrc", 6 );
-    sec[1].Misc.VirtualSize = page_size;
-    sec[1].SizeOfRawData = page_size;
-    sec[1].PointerToRawData = page_size*2;
-    sec[1].VirtualAddress = page_size*2;
-    sec[1].Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ;
+    file_size = 0;
+    for ( i = 0; i < max_sections; i++ )
+        if ( sec_descr->sect_in[i] )
+        {
+            DWORD virt_end_of_section = sec_descr->sect_in[i]->Misc.VirtualSize +
+                sec_descr->sect_in[i]->VirtualAddress;
+            DWORD phys_end_of_section = sec_descr->sect_in[i]->SizeOfRawData +
+                sec_descr->sect_in[i]->PointerToRawData;
+            memcpy( sec + nt->FileHeader.NumberOfSections, sec_descr->sect_in[i],
+                    sizeof(sec[0]) );
+            nt->FileHeader.NumberOfSections++;
+            if ( opt->SizeOfImage < virt_end_of_section )
+                opt->SizeOfImage = virt_end_of_section;
+            if ( file_size < phys_end_of_section )
+                file_size = phys_end_of_section;
+        }
 
     file = CreateFile(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
     ok (file != INVALID_HANDLE_VALUE, "failed to create file\n");
@@ -91,13 +261,13 @@ static int build_exe( void )
     /* write out the header */
     WriteFile( file, page, sizeof page, &written, NULL );
 
-    /* write out an empty page for rodata */
+    /* write out zeroed pages for sections */
     memset( page, 0, sizeof page );
-    WriteFile( file, page, sizeof page, &written, NULL );
-
-    /* write out an empty page for the resources */
-    memset( page, 0, sizeof page );
-    WriteFile( file, page, sizeof page, &written, NULL );
+    for ( i = page_size; i < file_size; i += page_size )
+    {
+       DWORD size = min(page_size, file_size - i);
+        WriteFile( file, page, size, &written, NULL );
+    }
 
     CloseHandle( file );
 
@@ -169,7 +339,7 @@ static void update_resources_delete( void )
     ok( r, "EndUpdateResource failed\n");
 }
 
-static void update_resources_version(void)
+static void update_resources_version( void )
 {
     HANDLE res = NULL;
     BOOL r;
@@ -199,41 +369,41 @@ static void update_resources_version(void)
     ok( r, "EndUpdateResource failed: %d\n", GetLastError());
 }
 
-
-typedef void (*res_check_func)( IMAGE_RESOURCE_DIRECTORY* );
-
-static void check_empty( IMAGE_RESOURCE_DIRECTORY *dir )
+static void update_resources_bigdata( void )
 {
-    char *pad;
-
-    ok( dir->NumberOfNamedEntries == 0, "NumberOfNamedEntries should be 0 instead of %d\n", dir->NumberOfNamedEntries);
-    ok( dir->NumberOfIdEntries == 0, "NumberOfIdEntries should be 0 instead of %d\n", dir->NumberOfIdEntries);
+    HANDLE res = NULL;
+    BOOL r;
+    char foo[2*page_size] = "foobar";
 
-    pad = (char*) &dir[1];
+    res = BeginUpdateResource( filename, TRUE );
+    ok( res != NULL, "BeginUpdateResource succeeded\n");
 
-    ok( !memcmp( pad, "PADDINGXXPADDING", 16), "padding wrong\n");
-}
+    r = UpdateResource( res,
+                        MAKEINTRESOURCE(0x3012),
+                        MAKEINTRESOURCE(0x5647),
+                        0xcdba,
+                        foo, sizeof foo );
+    ok( r == TRUE, "UpdateResource failed: %d\n", GetLastError());
 
-static void check_not_empty( IMAGE_RESOURCE_DIRECTORY *dir )
-{
-    ok( dir->NumberOfNamedEntries == 0, "NumberOfNamedEntries should be 0 instead of %d\n", dir->NumberOfNamedEntries);
-    ok( dir->NumberOfIdEntries == 1, "NumberOfIdEntries should be 1 instead of %d\n", dir->NumberOfIdEntries);
+    r = EndUpdateResource( res, FALSE );
+    ok( r, "EndUpdateResource failed\n");
 }
 
-static void check_exe( res_check_func fn )
+static void check_exe( const sec_verify *verify )
 {
+    int i;
     IMAGE_DOS_HEADER *dos;
     IMAGE_NT_HEADERS *nt;
     IMAGE_SECTION_HEADER *sec;
     IMAGE_RESOURCE_DIRECTORY *dir;
     HANDLE file, mapping;
-    DWORD length;
+    DWORD length, sec_count = 0;
 
     file = CreateFile(filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, 0);
     ok (file != INVALID_HANDLE_VALUE, "failed to create file (%d)\n", GetLastError());
 
     length = GetFileSize( file, NULL );
-    ok( length == 0x3000, "file size wrong\n");
+    ok( length >= verify->length, "file size wrong\n");
 
     mapping = CreateFileMapping( file, NULL, PAGE_READONLY, 0, 0, NULL );
     ok (mapping != NULL, "failed to create file\n");
@@ -245,24 +415,36 @@ static void check_exe( res_check_func fn )
         goto end;
 
     nt = (void*) ((BYTE*) dos + dos->e_lfanew);
-    ok( nt->FileHeader.NumberOfSections == 2, "number of sections wrong\n" );
-
-    if (nt->FileHeader.NumberOfSections < 2)
-        goto end;
-
     sec = (void*) &nt[1];
 
-    ok( !memcmp(sec[1].Name, ".rsrc", 6), "resource section name wrong\n");
-
-    dir = (void*) ((BYTE*) dos + sec[1].VirtualAddress);
+    for(i = 0; i < max_sections; i++)
+        if (verify->sect_out[i])
+        {
+            ok( !memcmp(&verify->sect_out[i]->Name, &sec[sec_count].Name, 8), "section %d name wrong\n", sec_count);
+            ok( verify->sect_out[i]->VirtualAddress == sec[sec_count].VirtualAddress, "section %d vaddr wrong\n", sec_count);
+            ok( verify->sect_out[i]->SizeOfRawData <= sec[sec_count].SizeOfRawData, "section %d SizeOfRawData wrong (%d vs %d)\n", sec_count, verify->sect_out[i]->SizeOfRawData ,sec[sec_count].SizeOfRawData);
+            ok( verify->sect_out[i]->PointerToRawData == sec[sec_count].PointerToRawData, "section %d PointerToRawData wrong\n", sec_count);
+            ok( verify->sect_out[i]->Characteristics == sec[sec_count].Characteristics , "section %d characteristics wrong\n", sec_count);
+            sec_count++;
+        }
 
-    ok( dir->Characteristics == 0, "Characteristics wrong\n");
-    ok( dir->TimeDateStamp == 0 || abs( dir->TimeDateStamp - GetTickCount() ) < 1000 /* nt4 */,
-        "TimeDateStamp wrong %u\n", dir->TimeDateStamp);
-    ok( dir->MajorVersion == 4, "MajorVersion wrong\n");
-    ok( dir->MinorVersion == 0, "MinorVersion wrong\n");
+    ok( nt->FileHeader.NumberOfSections == sec_count, "number of sections wrong\n" );
 
-    fn( dir );
+    if (verify->rsrc_section >= 0 && verify->rsrc_section < nt->FileHeader.NumberOfSections)
+    {
+        dir = (void*) ((BYTE*) dos + sec[verify->rsrc_section].VirtualAddress);
+
+        ok( dir->Characteristics == 0, "Characteristics wrong\n");
+        ok( dir->TimeDateStamp == 0 || abs( dir->TimeDateStamp - GetTickCount() ) < 1000 /* nt4 */,
+            "TimeDateStamp wrong %u\n", dir->TimeDateStamp);
+        ok( dir->MajorVersion == 4, "MajorVersion wrong\n");
+        ok( dir->MinorVersion == 0, "MinorVersion wrong\n");
+
+        ok( dir->NumberOfNamedEntries == verify->NumberOfNamedEntries, "NumberOfNamedEntries should be %d instead of %d\n",
+                verify->NumberOfNamedEntries, dir->NumberOfNamedEntries);
+        ok( dir->NumberOfIdEntries == verify->NumberOfIdEntries, "NumberOfIdEntries should be %d instead of %d\n",
+                verify->NumberOfIdEntries, dir->NumberOfIdEntries);
+    }
 
 end:
     UnmapViewOfFile( dos );
@@ -310,6 +492,8 @@ static void test_find_resource(void)
 
 START_TEST(resource)
 {
+    DWORD i;
+
     DeleteFile( filename );
     update_missing_exe();
 
@@ -320,13 +504,20 @@ START_TEST(resource)
     }
 
     update_empty_exe();
-    build_exe();
-    update_resources_none();
-    check_exe( check_empty );
-    update_resources_delete();
-    check_exe( check_empty );
-    update_resources_version();
-    check_exe( check_not_empty );
-    DeleteFile( filename );
+
+    for(i=0; i < sizeof( sec_variants ) / sizeof( sec_variants[0] ); i++)
+    {
+        const struct _sec_variants *sec = &sec_variants[i];
+        build_exe( &sec->build );
+        update_resources_none();
+        check_exe( &sec->chk_none );
+        update_resources_delete();
+        check_exe( &sec->chk_delete );
+        update_resources_version();
+        check_exe( &sec->chk_version );
+        update_resources_bigdata();
+        check_exe( &sec->chk_bigdata );
+        DeleteFile( filename );
+    }
     test_find_resource();
 }
index ea36fc6..98d996f 100755 (executable)
@@ -41,8 +41,7 @@ static void test_signalandwait(void)
     DWORD (WINAPI *pSignalObjectAndWait)(HANDLE, HANDLE, DWORD, BOOL);
     HMODULE kernel32;
     DWORD r;
-    int i;
-    HANDLE event[2], maxevents[MAXIMUM_WAIT_OBJECTS], semaphore[2], file;
+    HANDLE event[2], semaphore[2], file;
 
     kernel32 = GetModuleHandle("kernel32");
     pSignalObjectAndWait = (void*) GetProcAddress(kernel32, "SignalObjectAndWait");
@@ -94,19 +93,6 @@ static void test_signalandwait(void)
     CloseHandle(event[0]);
     CloseHandle(event[1]);
 
-    /* create the maximum number of events and make sure 
-     * we can wait on that many */
-    for (i=0; i<MAXIMUM_WAIT_OBJECTS; i++)
-    {
-        maxevents[i] = CreateEvent(NULL, 1, 1, NULL);
-        ok( maxevents[i] != 0, "should create enough events\n");
-    }
-    r = WaitForMultipleObjects(MAXIMUM_WAIT_OBJECTS, maxevents, 0, 0);
-    ok( r != WAIT_FAILED && r != WAIT_TIMEOUT, "should succeed\n");
-
-    for (i=0; i<MAXIMUM_WAIT_OBJECTS; i++)
-        if (maxevents[i]) CloseHandle(maxevents[i]);
-
     /* semaphores */
     semaphore[0] = CreateSemaphore( NULL, 0, 1, NULL );
     semaphore[1] = CreateSemaphore( NULL, 1, 1, NULL );
@@ -142,20 +128,53 @@ static void test_mutex(void)
     BOOL ret;
     HANDLE hCreated;
     HANDLE hOpened;
+    int i;
+    DWORD failed = 0;
 
     hCreated = CreateMutex(NULL, FALSE, "WineTestMutex");
     ok(hCreated != NULL, "CreateMutex failed with error %d\n", GetLastError());
-    wait_ret = WaitForSingleObject(hCreated, INFINITE);
-    ok(wait_ret == WAIT_OBJECT_0, "WaitForSingleObject failed with error 0x%08x\n", wait_ret);
 
-    /* yes, opening with just READ_CONTROL access allows us to successfully
-     * call ReleaseMutex */
-    hOpened = OpenMutex(READ_CONTROL, FALSE, "WineTestMutex");
+    hOpened = OpenMutex(0, FALSE, "WineTestMutex");
+    ok(hOpened == NULL, "OpenMutex succeded\n");
+
+    hOpened = OpenMutex(GENERIC_EXECUTE, FALSE, "WineTestMutex");
+    ok(hOpened != NULL, "OpenMutex failed with error %d\n", GetLastError());
+    wait_ret = WaitForSingleObject(hOpened, INFINITE);
+    ok(wait_ret == WAIT_OBJECT_0, "WaitForSingleObject failed with error %d\n", GetLastError());
+    CloseHandle(hOpened);
+
+    for(i=0; i < 31; i++)
+    {
+        wait_ret = WaitForSingleObject(hCreated, INFINITE);
+        ok(wait_ret == WAIT_OBJECT_0, "WaitForSingleObject failed with error 0x%08x\n", wait_ret);
+    }
+
+    hOpened = OpenMutex(GENERIC_READ | GENERIC_WRITE, FALSE, "WineTestMutex");
     ok(hOpened != NULL, "OpenMutex failed with error %d\n", GetLastError());
-    ret = ReleaseMutex(hOpened);
-    todo_wine ok(ret, "ReleaseMutex failed with error %d\n", GetLastError());
+    wait_ret = WaitForSingleObject(hOpened, INFINITE);
+    ok(wait_ret == WAIT_FAILED, "WaitForSingleObject succeeded\n");
+    CloseHandle(hOpened);
+
+    for (i = 0; i < 32; i++)
+    {
+        hOpened = OpenMutex(0x1 << i, FALSE, "WineTestMutex");
+        if(hOpened != NULL)
+        {
+            ret = ReleaseMutex(hOpened);
+            ok(ret, "ReleaseMutex failed with error %d, access %x\n", GetLastError(), 1 << i);
+            CloseHandle(hOpened);
+        }
+        else
+        {
+            ReleaseMutex(hCreated);
+            failed |=0x1 << i;
+        }
+    }
+
+    ok( failed == 0x0de0fffe, "open succeded when it shouldn't: %x\n", failed);
+
     ret = ReleaseMutex(hCreated);
-    todo_wine ok(!ret && (GetLastError() == ERROR_NOT_OWNER),
+    ok(!ret && (GetLastError() == ERROR_NOT_OWNER),
         "ReleaseMutex should have failed with ERROR_NOT_OWNER instead of %d\n", GetLastError());
 
     /* test case sensitivity */
@@ -163,16 +182,12 @@ static void test_mutex(void)
     SetLastError(0xdeadbeef);
     hOpened = OpenMutex(READ_CONTROL, FALSE, "WINETESTMUTEX");
     ok(!hOpened, "OpenMutex succeeded\n");
-    ok(GetLastError() == ERROR_FILE_NOT_FOUND ||
-       GetLastError() == ERROR_INVALID_NAME, /* win9x */
-       "wrong error %u\n", GetLastError());
+    ok(GetLastError() == ERROR_FILE_NOT_FOUND, "wrong error %u\n", GetLastError());
 
     SetLastError(0xdeadbeef);
     hOpened = OpenMutex(READ_CONTROL, FALSE, "winetestmutex");
     ok(!hOpened, "OpenMutex succeeded\n");
-    ok(GetLastError() == ERROR_FILE_NOT_FOUND ||
-       GetLastError() == ERROR_INVALID_NAME, /* win9x */
-       "wrong error %u\n", GetLastError());
+    ok(GetLastError() == ERROR_FILE_NOT_FOUND, "wrong error %u\n", GetLastError());
 
     SetLastError(0xdeadbeef);
     hOpened = CreateMutex(NULL, FALSE, "WineTestMutex");
@@ -342,9 +357,7 @@ static void test_event(void)
     SetLastError(0xdeadbeef);
     handle2 = OpenEventA( EVENT_ALL_ACCESS, FALSE, __FILE__ ": TEST EVENT");
     ok( !handle2, "OpenEvent succeeded\n");
-    ok( GetLastError() == ERROR_FILE_NOT_FOUND ||
-        GetLastError() == ERROR_INVALID_NAME, /* win9x */
-        "wrong error %u\n", GetLastError());
+    ok( GetLastError() == ERROR_FILE_NOT_FOUND, "wrong error %u\n", GetLastError());
 
     CloseHandle( handle );
 }
@@ -380,9 +393,7 @@ static void test_semaphore(void)
     SetLastError(0xdeadbeef);
     handle2 = OpenSemaphoreA( SEMAPHORE_ALL_ACCESS, FALSE, __FILE__ ": TEST SEMAPHORE");
     ok( !handle2, "OpenSemaphore succeeded\n");
-    ok( GetLastError() == ERROR_FILE_NOT_FOUND ||
-        GetLastError() == ERROR_INVALID_NAME, /* win9x */
-        "wrong error %u\n", GetLastError());
+    ok( GetLastError() == ERROR_FILE_NOT_FOUND, "wrong error %u\n", GetLastError());
 
     CloseHandle( handle );
 }
@@ -939,6 +950,120 @@ static void test_timer_queue(void)
        GetLastError());
 }
 
+static HANDLE modify_handle(HANDLE handle, DWORD modify)
+{
+    DWORD tmp = HandleToULong(handle);
+    tmp |= modify;
+    return ULongToHandle(tmp);
+}
+
+static void test_WaitForSingleObject(void)
+{
+    HANDLE signaled, nonsignaled, invalid;
+    DWORD ret;
+
+    signaled = CreateEventW(NULL, TRUE, TRUE, NULL);
+    nonsignaled = CreateEventW(NULL, TRUE, FALSE, NULL);
+    invalid = (HANDLE) 0xdeadbee0;
+
+    /* invalid handle with different values for lower 2 bits */
+    SetLastError(0xdeadbeef);
+    ret = WaitForSingleObject(invalid, 0);
+    ok(ret == WAIT_FAILED, "expected WAIT_FAILED, got %d\n", ret);
+    ok(GetLastError() == ERROR_INVALID_HANDLE, "expected ERROR_INVALID_HANDLE, got %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = WaitForSingleObject(modify_handle(invalid, 1), 0);
+    ok(ret == WAIT_FAILED, "expected WAIT_FAILED, got %d\n", ret);
+    ok(GetLastError() == ERROR_INVALID_HANDLE, "expected ERROR_INVALID_HANDLE, got %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = WaitForSingleObject(modify_handle(invalid, 2), 0);
+    ok(ret == WAIT_FAILED, "expected WAIT_FAILED, got %d\n", ret);
+    ok(GetLastError() == ERROR_INVALID_HANDLE, "expected ERROR_INVALID_HANDLE, got %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = WaitForSingleObject(modify_handle(invalid, 3), 0);
+    ok(ret == WAIT_FAILED, "expected WAIT_FAILED, got %d\n", ret);
+    ok(GetLastError() == ERROR_INVALID_HANDLE, "expected ERROR_INVALID_HANDLE, got %d\n", GetLastError());
+
+    /* valid handle with different values for lower 2 bits */
+    SetLastError(0xdeadbeef);
+    ret = WaitForSingleObject(nonsignaled, 0);
+    ok(ret == WAIT_TIMEOUT, "expected WAIT_TIMEOUT, got %d\n", ret);
+    ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = WaitForSingleObject(modify_handle(nonsignaled, 1), 0);
+    ok(ret == WAIT_TIMEOUT, "expected WAIT_TIMEOUT, got %d\n", ret);
+    ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = WaitForSingleObject(modify_handle(nonsignaled, 2), 0);
+    ok(ret == WAIT_TIMEOUT, "expected WAIT_TIMEOUT, got %d\n", ret);
+    ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = WaitForSingleObject(modify_handle(nonsignaled, 3), 0);
+    ok(ret == WAIT_TIMEOUT, "expected WAIT_TIMEOUT, got %d\n", ret);
+    ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %d\n", GetLastError());
+
+    /* valid handle with different values for lower 2 bits */
+    SetLastError(0xdeadbeef);
+    ret = WaitForSingleObject(signaled, 0);
+    ok(ret == WAIT_OBJECT_0, "expected WAIT_OBJECT_0, got %d\n", ret);
+    ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = WaitForSingleObject(modify_handle(signaled, 1), 0);
+    ok(ret == WAIT_OBJECT_0, "expected WAIT_OBJECT_0, got %d\n", ret);
+    ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = WaitForSingleObject(modify_handle(signaled, 2), 0);
+    ok(ret == WAIT_OBJECT_0, "expected WAIT_OBJECT_0, got %d\n", ret);
+    ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = WaitForSingleObject(modify_handle(signaled, 3), 0);
+    ok(ret == WAIT_OBJECT_0, "expected WAIT_OBJECT_0, got %d\n", ret);
+    ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %d\n", GetLastError());
+
+    CloseHandle(signaled);
+    CloseHandle(nonsignaled);
+}
+
+static void test_WaitForMultipleObjects(void)
+{
+    DWORD r;
+    int i;
+    HANDLE maxevents[MAXIMUM_WAIT_OBJECTS];
+
+    /* create the maximum number of events and make sure
+     * we can wait on that many */
+    for (i=0; i<MAXIMUM_WAIT_OBJECTS; i++)
+    {
+        maxevents[i] = CreateEvent(NULL, i==0, TRUE, NULL);
+        ok( maxevents[i] != 0, "should create enough events\n");
+    }
+
+    /* a manual-reset event remains signaled, an auto-reset event is cleared */
+    r = WaitForMultipleObjects(MAXIMUM_WAIT_OBJECTS, maxevents, 0, 0);
+    ok( r == WAIT_OBJECT_0, "should signal lowest handle first, got %d\n", r);
+    r = WaitForMultipleObjects(MAXIMUM_WAIT_OBJECTS, maxevents, 0, 0);
+    ok( r == WAIT_OBJECT_0, "should signal handle #0 first, got %d\n", r);
+    ok(ResetEvent(maxevents[0]), "ResetEvent\n");
+    for (i=1; i<MAXIMUM_WAIT_OBJECTS; i++)
+    {
+        /* the lowest index is checked first and remaining events are untouched */
+        r = WaitForMultipleObjects(MAXIMUM_WAIT_OBJECTS, maxevents, 0, 0);
+        ok( r == WAIT_OBJECT_0+i, "should signal handle #%d first, got %d\n", i, r);
+    }
+
+    for (i=0; i<MAXIMUM_WAIT_OBJECTS; i++)
+        if (maxevents[i]) CloseHandle(maxevents[i]);
+}
+
 START_TEST(sync)
 {
     HMODULE hdll = GetModuleHandle("kernel32");
@@ -958,4 +1083,6 @@ START_TEST(sync)
     test_waitable_timer();
     test_iocp_callback();
     test_timer_queue();
+    test_WaitForSingleObject();
+    test_WaitForMultipleObjects();
 }
index 3da29a2..19726af 100755 (executable)
@@ -71,14 +71,16 @@ static HANDLE create_target_process(const char *arg)
     char **argv;
     char cmdline[MAX_PATH];
     PROCESS_INFORMATION pi;
+    BOOL ret;
     STARTUPINFO si = { 0 };
     si.cb = sizeof(si);
 
     winetest_get_mainargs( &argv );
     sprintf(cmdline, "%s %s %s", argv[0], argv[1], arg);
-    ok(CreateProcess(NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL,
-                     &si, &pi) != 0, "error: %u\n", GetLastError());
-    ok(CloseHandle(pi.hThread) != 0, "error %u\n", GetLastError());
+    ret = CreateProcess(NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
+    ok(ret, "error: %u\n", GetLastError());
+    ret = CloseHandle(pi.hThread);
+    ok(ret, "error %u\n", GetLastError());
     return pi.hProcess;
 }
 
@@ -146,7 +148,7 @@ static void cleanup_thread_sync_helpers(void)
   CloseHandle(stop_event);
 }
 
-DWORD tlsIndex;
+static DWORD tlsIndex;
 
 typedef struct {
   int threadnum;
@@ -157,7 +159,7 @@ typedef struct {
 /* WinME supports OpenThread but doesn't know about access restrictions so
    we require them to be either completely ignored or always obeyed.
 */
-INT obeying_ars = 0; /* -1 == no, 0 == dunno yet, 1 == yes */
+static INT obeying_ars = 0; /* -1 == no, 0 == dunno yet, 1 == yes */
 #define obey_ar(x) \
   (obeying_ars == 0 \
     ? ((x) \
@@ -286,6 +288,7 @@ static VOID test_CreateRemoteThread(void)
         skip("child process wasn't mapped at same address, so can't do CreateRemoteThread tests.\n");
         return;
     }
+    ok(ret == WAIT_OBJECT_0 || broken(ret == WAIT_OBJECT_0+1 /* nt4,w2k */), "WaitForAllObjects 2 events %d\n", ret);
 
     hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
     ok(hEvent != NULL, "Can't create event, err=%u\n", GetLastError());
@@ -310,18 +313,18 @@ static VOID test_CreateRemoteThread(void)
     ok(ret == 2, "ret=%u, err=%u\n", ret, GetLastError());
 
     /* thread still suspended, so wait times out */
-    ret = WaitForSingleObject(hEvent, 100);
+    ret = WaitForSingleObject(hEvent, 1000);
     ok(ret == WAIT_TIMEOUT, "wait did not time out, ret=%u\n", ret);
 
     ret = ResumeThread(hThread);
     ok(ret == 1, "ret=%u, err=%u\n", ret, GetLastError());
 
     /* wait that doesn't time out */
-    ret = WaitForSingleObject(hEvent, 100);
+    ret = WaitForSingleObject(hEvent, 1000);
     ok(ret == WAIT_OBJECT_0, "object not signaled, ret=%u\n", ret);
 
     /* wait for thread end */
-    ret = WaitForSingleObject(hThread, 100);
+    ret = WaitForSingleObject(hThread, 1000);
     ok(ret == WAIT_OBJECT_0, "waiting for thread failed, ret=%u\n", ret);
     CloseHandle(hThread);
 
@@ -330,7 +333,7 @@ static VOID test_CreateRemoteThread(void)
                                  threadFunc_CloseHandle,
                                  hRemoteEvent, 0, &tid);
     ok(hThread != NULL, "CreateRemoteThread failed, err=%u\n", GetLastError());
-    ret = WaitForSingleObject(hThread, 100);
+    ret = WaitForSingleObject(hThread, 1000);
     ok(ret == WAIT_OBJECT_0, "waiting for thread failed, ret=%u\n", ret);
     CloseHandle(hThread);
 
@@ -341,7 +344,7 @@ static VOID test_CreateRemoteThread(void)
     ok(hThread != NULL, "CreateRemoteThread failed, err=%u\n", GetLastError());
 
     /* closed handle, so wait times out */
-    ret = WaitForSingleObject(hEvent, 100);
+    ret = WaitForSingleObject(hEvent, 1000);
     ok(ret == WAIT_TIMEOUT, "wait did not time out, ret=%u\n", ret);
 
     /* check that remote SetEvent() failed */
@@ -368,6 +371,7 @@ static VOID test_CreateThread_basic(void)
    DWORD i,j;
    DWORD GLE, ret;
    DWORD tid;
+   BOOL bRet;
 
    /* lstrlenA contains an exception handler so this makes sure exceptions work in the main thread */
    ok( lstrlenA( (char *)0xdeadbeef ) == 0, "lstrlenA: unexpected success\n" );
@@ -423,7 +427,8 @@ static VOID test_CreateThread_basic(void)
   }
 
   SetLastError(0xCAFEF00D);
-  ok(TlsFree(tlsIndex)!=0,"TlsFree failed: %08x\n", GetLastError());
+  bRet = TlsFree(tlsIndex);
+  ok(bRet, "TlsFree failed: %08x\n", GetLastError());
   ok(GetLastError()==0xCAFEF00D,
      "GetLastError: expected 0xCAFEF00D, got %08x\n", GetLastError());
 
@@ -861,7 +866,7 @@ static VOID test_thread_processor(void)
      }
 
      error=pSetThreadIdealProcessor(curthread,MAXIMUM_PROCESSORS);
-     ok(error==0, "SetThreadIdealProcessor returned an incorrect value\n");
+     ok(error!=-1, "SetThreadIdealProcessor failed\n");
    }
 }
 
@@ -894,6 +899,7 @@ static HANDLE event;
 static void WINAPI set_test_val( int val )
 {
     test_value += val;
+    ExitThread(0);
 }
 
 static DWORD WINAPI threadFunc6(LPVOID p)
@@ -940,7 +946,8 @@ static void test_SetThreadContext(void)
         ctx.Esp -= 2 * sizeof(int *);
         ctx.Eip = (DWORD)set_test_val;
         SetLastError(0xdeadbeef);
-        ok( SetThreadContext( thread, &ctx ), "SetThreadContext failed : (%d)\n", GetLastError() );
+        ret = SetThreadContext( thread, &ctx );
+        ok( ret, "SetThreadContext failed : (%d)\n", GetLastError() );
     }
 
     SetLastError(0xdeadbeef);
@@ -949,7 +956,23 @@ static void test_SetThreadContext(void)
                          prevcount, GetLastError() );
 
     WaitForSingleObject( thread, INFINITE );
-    ok( test_value == 20, "test_value %d instead of 20\n", test_value );
+    ok( test_value == 10, "test_value %d\n", test_value );
+
+    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() );
+
+    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() );
+
+    CloseHandle( thread );
 }
 
 #endif  /* __i386__ */
@@ -1226,7 +1249,7 @@ static void test_TLS(void)
   }
 
   ret = WaitForMultipleObjects(2, threads, TRUE, 60000);
-  ok(ret == WAIT_OBJECT_0 || ret == WAIT_OBJECT_0+1 /* nt4 */, "WaitForMultipleObjects failed %u\n",ret);
+  ok(ret == WAIT_OBJECT_0 || broken(ret == WAIT_OBJECT_0+1 /* nt4,w2k */), "WaitForAllObjects 2 threads %d\n",ret);
 
   for (i = 0; i < 2; ++i)
     CloseHandle(threads[i]);
@@ -1329,21 +1352,16 @@ static void test_ThreadErrorMode(void)
     pSetThreadErrorMode(oldmode, NULL);
 }
 
-void _fpreset(void) {} /* override the mingw fpu init code */
-
+#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
 static inline void set_fpu_cw(WORD cw)
 {
-#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
     __asm__ volatile ("fnclex; fldcw %0" : : "m" (cw));
-#endif
 }
 
 static inline WORD get_fpu_cw(void)
 {
     WORD cw = 0;
-#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
     __asm__ volatile ("fnstcw %0" : "=m" (cw));
-#endif
     return cw;
 }
 
@@ -1411,6 +1429,7 @@ static void test_thread_fpu_cw(void)
     cw = get_fpu_cw();
     ok(cw == initial_cw, "Expected FPU control word %#x, got %#x.\n", initial_cw, cw);
 }
+#endif
 
 START_TEST(thread)
 {
index 0b3959a..e8df15b 100755 (executable)
@@ -25,6 +25,8 @@
 
 static BOOL (WINAPI *pTzSpecificLocalTimeToSystemTime)(LPTIME_ZONE_INFORMATION, LPSYSTEMTIME, LPSYSTEMTIME);
 static BOOL (WINAPI *pSystemTimeToTzSpecificLocalTime)(LPTIME_ZONE_INFORMATION, LPSYSTEMTIME, LPSYSTEMTIME);
+static int (WINAPI *pGetCalendarInfoA)(LCID,CALID,CALTYPE,LPSTR,int,LPDWORD);
+static int (WINAPI *pGetCalendarInfoW)(LCID,CALID,CALTYPE,LPWSTR,int,LPDWORD);
 
 #define SECSPERMIN         60
 #define SECSPERDAY        86400
@@ -437,8 +439,8 @@ static void test_TzSpecificLocalTimeToSystemTime(void)
     tzE.StandardBias=0;
     tzE.DaylightBias=-60;
     tzE.StandardDate.wMonth=10;
-    tzE.StandardDate.wDayOfWeek=0; /*sunday */
-    tzE.StandardDate.wDay=5;       /* last (sunday) of the month */
+    tzE.StandardDate.wDayOfWeek=0; /* Sunday */
+    tzE.StandardDate.wDay=5;       /* last (Sunday) of the month */
     tzE.StandardDate.wHour=3;
     tzE.DaylightDate.wMonth=3;
     tzE.DaylightDate.wDay=5;
@@ -448,19 +450,19 @@ static void test_TzSpecificLocalTimeToSystemTime(void)
     tzW.StandardBias=0;
     tzW.DaylightBias=-60;
     tzW.StandardDate.wMonth=10;
-    tzW.StandardDate.wDayOfWeek=0; /*sunday */
-    tzW.StandardDate.wDay=4;       /* 4th (sunday) of the month */
+    tzW.StandardDate.wDayOfWeek=0; /* Sunday */
+    tzW.StandardDate.wDay=4;       /* 4th (Sunday) of the month */
     tzW.StandardDate.wHour=2;
     tzW.DaylightDate.wMonth=4;
     tzW.DaylightDate.wDay=1;
     tzW.DaylightDate.wHour=2;
-    /* timezone Eastern hemisphere */
+    /* timezone Southern hemisphere */
     tzS.Bias=240;
     tzS.StandardBias=0;
     tzS.DaylightBias=-60;
     tzS.StandardDate.wMonth=4;
-    tzS.StandardDate.wDayOfWeek=0; /*sunday */
-    tzS.StandardDate.wDay=1;       /* 1st  (sunday) of the month */
+    tzS.StandardDate.wDayOfWeek=0; /*Sunday */
+    tzS.StandardDate.wDay=1;       /* 1st (Sunday) of the month */
     tzS.StandardDate.wHour=2;
     tzS.DaylightDate.wMonth=10;
     tzS.DaylightDate.wDay=4;
@@ -483,7 +485,7 @@ static void test_TzSpecificLocalTimeToSystemTime(void)
             { 10, &tzW, {2004,10,-1,24,1,0,0,0}, 4},
             { 11, &tzW, {2004,10,-1,24,1,59,59,999}, 4},
             { 12, &tzW, {2004,10,-1,24,2,0,0,0 }, 6},
-            /* and now south */
+            /* and now South */
             { 13, &tzS, {2004,4,-1,4,1,0,0,0}, 4},
             { 14, &tzS, {2004,4,-1,4,1,59,59,999}, 4},
             { 15, &tzS, {2004,4,-1,4,2,0,0,0}, 6},
@@ -548,7 +550,7 @@ static void test_TzSpecificLocalTimeToSystemTime(void)
             { 10, &tzW, {2004,10,-1,24,4,0,0,0}, 1},
             { 11, &tzW, {2004,10,-1,24,4,59,59,999}, 1},
             { 12, &tzW, {2004,10,-1,24,5,0,0,0 }, 1},
-            /* and now south */
+            /* and now South */
             { 13, &tzS, {2004,4,-1,4,4,0,0,0}, 1},
             { 14, &tzS, {2004,4,-1,4,4,59,59,999}, 1},
             { 15, &tzS, {2004,4,-1,4,5,0,0,0}, 1},
@@ -582,6 +584,7 @@ static void test_TzSpecificLocalTimeToSystemTime(void)
             , {25,28,2,22,2,22}  /* 2017 */
             , {24,27,1,28,1,28}  /* 2018 */
             , {30,26,7,27,7,27}  /* 2019 */
+            , {0}
         };
         for( j=0 , year = 1999; yeardays[j][0] ; j++, year++) {
             for (i=0; cases[i].nr; i++) {
@@ -599,11 +602,106 @@ static void test_TzSpecificLocalTimeToSystemTime(void)
     }        
 }
 
+static void test_FileTimeToDosDateTime(void)
+{
+    FILETIME ft = { 0 };
+    WORD fatdate, fattime;
+    BOOL ret;
+
+    if (0)
+    {
+        /* Crashes */
+        FileTimeToDosDateTime(NULL, NULL, NULL);
+    }
+    /* Parameter checking */
+    SetLastError(0xdeadbeef);
+    ret = FileTimeToDosDateTime(&ft, NULL, NULL);
+    ok(!ret, "expected failure\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER,
+       "expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = FileTimeToDosDateTime(&ft, &fatdate, NULL);
+    ok(!ret, "expected failure\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER,
+       "expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = FileTimeToDosDateTime(&ft, NULL, &fattime);
+    ok(!ret, "expected failure\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER,
+       "expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = FileTimeToDosDateTime(&ft, &fatdate, &fattime);
+    ok(!ret, "expected failure\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER,
+       "expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+}
+
+static void test_GetCalendarInfo(void)
+{
+    char bufferA[20];
+    WCHAR bufferW[20];
+    DWORD val1, val2;
+    int ret;
+
+    if (!pGetCalendarInfoA || !pGetCalendarInfoW)
+    {
+        trace( "GetCalendarInfo missing\n" );
+        return;
+    }
+
+    ret = pGetCalendarInfoA( 0x0409, CAL_GREGORIAN, CAL_ITWODIGITYEARMAX | CAL_RETURN_NUMBER,
+                             NULL, 0, &val1 );
+    ok( ret, "GetCalendarInfoA failed err %u\n", GetLastError() );
+    ok( ret == sizeof(val1), "wrong size %u\n", ret );
+    ok( val1 >= 2000 && val1 < 2100, "wrong value %u\n", val1 );
+
+    ret = pGetCalendarInfoW( 0x0409, CAL_GREGORIAN, CAL_ITWODIGITYEARMAX | CAL_RETURN_NUMBER,
+                             NULL, 0, &val2 );
+    ok( ret, "GetCalendarInfoW failed err %u\n", GetLastError() );
+    ok( ret == sizeof(val2)/sizeof(WCHAR), "wrong size %u\n", ret );
+    ok( val1 == val2, "A/W mismatch %u/%u\n", val1, val2 );
+
+    ret = pGetCalendarInfoA( 0x0409, CAL_GREGORIAN, CAL_ITWODIGITYEARMAX, bufferA, sizeof(bufferA), NULL );
+    ok( ret, "GetCalendarInfoA failed err %u\n", GetLastError() );
+    ok( ret == 5, "wrong size %u\n", ret );
+    ok( atoi( bufferA ) == val1, "wrong value %s/%u\n", bufferA, val1 );
+
+    ret = pGetCalendarInfoW( 0x0409, CAL_GREGORIAN, CAL_ITWODIGITYEARMAX, bufferW, sizeof(bufferW), NULL );
+    ok( ret, "GetCalendarInfoW failed err %u\n", GetLastError() );
+    ok( ret == 5, "wrong size %u\n", ret );
+    memset( bufferA, 0x55, sizeof(bufferA) );
+    WideCharToMultiByte( CP_ACP, 0, bufferW, -1, bufferA, sizeof(bufferA), NULL, NULL );
+    ok( atoi( bufferA ) == val1, "wrong value %s/%u\n", bufferA, val1 );
+
+    ret = pGetCalendarInfoA( 0x0409, CAL_GREGORIAN, CAL_ITWODIGITYEARMAX | CAL_RETURN_NUMBER,
+                             NULL, 0, NULL );
+    ok( !ret, "GetCalendarInfoA succeeded\n" );
+    ok( GetLastError() == ERROR_INVALID_PARAMETER, "wrong error %u\n", GetLastError() );
+
+    ret = pGetCalendarInfoA( 0x0409, CAL_GREGORIAN, CAL_ITWODIGITYEARMAX, NULL, 0, NULL );
+    ok( ret, "GetCalendarInfoA failed err %u\n", GetLastError() );
+    ok( ret == 5, "wrong size %u\n", ret );
+
+    ret = pGetCalendarInfoW( 0x0409, CAL_GREGORIAN, CAL_ITWODIGITYEARMAX | CAL_RETURN_NUMBER,
+                             NULL, 0, NULL );
+    ok( !ret, "GetCalendarInfoW succeeded\n" );
+    ok( GetLastError() == ERROR_INVALID_PARAMETER, "wrong error %u\n", GetLastError() );
+
+    ret = pGetCalendarInfoW( 0x0409, CAL_GREGORIAN, CAL_ITWODIGITYEARMAX, NULL, 0, NULL );
+    ok( ret, "GetCalendarInfoW failed err %u\n", GetLastError() );
+    ok( ret == 5, "wrong size %u\n", ret );
+}
+
 START_TEST(time)
 {
     HMODULE hKernel = GetModuleHandle("kernel32");
     pTzSpecificLocalTimeToSystemTime = (void *)GetProcAddress(hKernel, "TzSpecificLocalTimeToSystemTime");
     pSystemTimeToTzSpecificLocalTime = (void *)GetProcAddress( hKernel, "SystemTimeToTzSpecificLocalTime");
+    pGetCalendarInfoA = (void *)GetProcAddress(hKernel, "GetCalendarInfoA");
+    pGetCalendarInfoW = (void *)GetProcAddress(hKernel, "GetCalendarInfoW");
 
     test_conversions();
     test_invalid_arg();
@@ -611,4 +709,6 @@ START_TEST(time)
     test_FileTimeToSystemTime();
     test_FileTimeToLocalFileTime();
     test_TzSpecificLocalTimeToSystemTime();
+    test_FileTimeToDosDateTime();
+    test_GetCalendarInfo();
 }
index dfe4ad8..bfa3127 100644 (file)
@@ -206,14 +206,14 @@ static const char* curr_expected_modules[] =
 {
     "kernel32_test.exe"
     "kernel32.dll",
-    /* FIXME: could test for ntdll on NT and Wine */
+    "ntdll.dll",
 };
 static const char* sub_expected_modules[] =
 {
     "kernel32_test.exe",
     "kernel32.dll",
-    "shell32.dll"
-    /* FIXME: could test for ntdll on NT and Wine */
+    "shell32.dll",
+    "ntdll.dll",
 };
 #define NUM_OF(x) (sizeof(x) / sizeof(x[0]))
 
index 5151170..9f9b72e 100644 (file)
@@ -52,7 +52,7 @@ static void test_GetVersionEx(void)
     if (0)
     {
         /* Silently crashes on XP */
-        ret = GetVersionExA(NULL);
+        GetVersionExA(NULL);
     }
 
     SetLastError(0xdeadbeef);
@@ -166,6 +166,7 @@ static void test_VerifyVersionInfo(void)
     ret = pVerifyVersionInfoA(&info, VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
         pVerSetConditionMask(pVerSetConditionMask(0, VER_MINORVERSION, VER_GREATER_EQUAL),
             VER_MAJORVERSION, VER_GREATER_EQUAL));
+    ok(ret, "VerifyVersionInfoA failed with error %d\n", GetLastError());
 
     info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
     GetVersionEx((OSVERSIONINFO *)&info);
index f95fdd9..cd5417c 100755 (executable)
 #include <stdarg.h>
 #include <stdio.h>
 
+#include "ntstatus.h"
+#define WIN32_NO_STATUS
 #include "windef.h"
 #include "winbase.h"
+#include "winternl.h"
 #include "winerror.h"
 #include "wine/test.h"
 
@@ -34,6 +37,7 @@ static LPVOID (WINAPI *pVirtualAllocEx)(HANDLE, LPVOID, SIZE_T, DWORD, DWORD);
 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);
 
 /* ############################### */
 
@@ -42,14 +46,16 @@ static HANDLE create_target_process(const char *arg)
     char **argv;
     char cmdline[MAX_PATH];
     PROCESS_INFORMATION pi;
+    BOOL ret;
     STARTUPINFO si = { 0 };
     si.cb = sizeof(si);
 
     winetest_get_mainargs( &argv );
     sprintf(cmdline, "%s %s %s", argv[0], argv[1], arg);
-    ok(CreateProcess(NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL,
-                     &si, &pi) != 0, "error: %u\n", GetLastError());
-    ok(CloseHandle(pi.hThread) != 0, "error %u\n", GetLastError());
+    ret = CreateProcess(NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
+    ok(ret, "error: %u\n", GetLastError());
+    ret = CloseHandle(pi.hThread);
+    ok(ret, "error %u\n", GetLastError());
     return pi.hProcess;
 }
 
@@ -832,6 +838,144 @@ static void test_NtMapViewOfSection(void)
     CloseHandle(hProcess);
 }
 
+static void test_NtAreMappedFilesTheSame(void)
+{
+    static const char testfile[] = "testfile.xxx";
+    HANDLE file, file2, mapping, map2;
+    void *ptr, *ptr2;
+    NTSTATUS status;
+    char path[MAX_PATH];
+
+    if (!pNtAreMappedFilesTheSame)
+    {
+        win_skip( "NtAreMappedFilesTheSame not available\n" );
+        return;
+    }
+
+    file = CreateFileA( testfile, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
+                        NULL, CREATE_ALWAYS, 0, 0 );
+    ok( file != INVALID_HANDLE_VALUE, "CreateFile error %u\n", GetLastError() );
+    SetFilePointer( file, 4096, NULL, FILE_BEGIN );
+    SetEndOfFile( file );
+
+    mapping = CreateFileMappingA( file, NULL, PAGE_READWRITE, 0, 4096, NULL );
+    ok( mapping != 0, "CreateFileMapping error %u\n", GetLastError() );
+
+    ptr = MapViewOfFile( mapping, FILE_MAP_READ, 0, 0, 4096 );
+    ok( ptr != NULL, "MapViewOfFile FILE_MAP_READ error %u\n", GetLastError() );
+
+    file2 = CreateFileA( testfile, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE,
+                         NULL, OPEN_EXISTING, 0, 0 );
+    ok( file2 != INVALID_HANDLE_VALUE, "CreateFile error %u\n", GetLastError() );
+
+    map2 = CreateFileMappingA( file2, NULL, PAGE_READONLY, 0, 4096, NULL );
+    ok( map2 != 0, "CreateFileMapping error %u\n", GetLastError() );
+    ptr2 = MapViewOfFile( map2, FILE_MAP_READ, 0, 0, 4096 );
+    ok( ptr2 != NULL, "MapViewOfFile FILE_MAP_READ error %u\n", GetLastError() );
+    status = pNtAreMappedFilesTheSame( ptr, ptr2 );
+    ok( status == STATUS_NOT_SAME_DEVICE, "NtAreMappedFilesTheSame returned %x\n", status );
+    UnmapViewOfFile( ptr2 );
+
+    ptr2 = MapViewOfFile( mapping, FILE_MAP_READ, 0, 0, 4096 );
+    ok( ptr2 != NULL, "MapViewOfFile FILE_MAP_READ error %u\n", GetLastError() );
+    status = pNtAreMappedFilesTheSame( ptr, ptr2 );
+    ok( status == STATUS_NOT_SAME_DEVICE, "NtAreMappedFilesTheSame returned %x\n", status );
+    UnmapViewOfFile( ptr2 );
+    CloseHandle( map2 );
+
+    map2 = CreateFileMappingA( file, NULL, PAGE_READONLY, 0, 4096, NULL );
+    ok( map2 != 0, "CreateFileMapping error %u\n", GetLastError() );
+    ptr2 = MapViewOfFile( map2, FILE_MAP_READ, 0, 0, 4096 );
+    ok( ptr2 != NULL, "MapViewOfFile FILE_MAP_READ error %u\n", GetLastError() );
+    status = pNtAreMappedFilesTheSame( ptr, ptr2 );
+    ok( status == STATUS_NOT_SAME_DEVICE, "NtAreMappedFilesTheSame returned %x\n", status );
+    UnmapViewOfFile( ptr2 );
+    CloseHandle( map2 );
+    CloseHandle( file2 );
+
+    status = pNtAreMappedFilesTheSame( ptr, ptr );
+    ok( status == STATUS_NOT_SAME_DEVICE, "NtAreMappedFilesTheSame returned %x\n", status );
+
+    status = pNtAreMappedFilesTheSame( ptr, (char *)ptr + 30 );
+    ok( status == STATUS_NOT_SAME_DEVICE, "NtAreMappedFilesTheSame returned %x\n", status );
+
+    status = pNtAreMappedFilesTheSame( ptr, GetModuleHandleA("kernel32.dll") );
+    ok( status == STATUS_NOT_SAME_DEVICE, "NtAreMappedFilesTheSame returned %x\n", status );
+
+    status = pNtAreMappedFilesTheSame( ptr, (void *)0xdeadbeef );
+    ok( status == STATUS_CONFLICTING_ADDRESSES || status == STATUS_INVALID_ADDRESS,
+        "NtAreMappedFilesTheSame returned %x\n", status );
+
+    status = pNtAreMappedFilesTheSame( ptr, NULL );
+    ok( status == STATUS_INVALID_ADDRESS, "NtAreMappedFilesTheSame returned %x\n", status );
+
+    status = pNtAreMappedFilesTheSame( ptr, (void *)GetProcessHeap() );
+    ok( status == STATUS_CONFLICTING_ADDRESSES, "NtAreMappedFilesTheSame returned %x\n", status );
+
+    status = pNtAreMappedFilesTheSame( NULL, NULL );
+    ok( status == STATUS_INVALID_ADDRESS, "NtAreMappedFilesTheSame returned %x\n", status );
+
+    ptr2 = VirtualAlloc( NULL, 0x10000, MEM_COMMIT, PAGE_READWRITE );
+    ok( ptr2 != NULL, "VirtualAlloc error %u\n", GetLastError() );
+    status = pNtAreMappedFilesTheSame( ptr, ptr2 );
+    ok( status == STATUS_CONFLICTING_ADDRESSES, "NtAreMappedFilesTheSame returned %x\n", status );
+    VirtualFree( ptr2, 0, MEM_RELEASE );
+
+    UnmapViewOfFile( ptr );
+    CloseHandle( mapping );
+    CloseHandle( file );
+
+    status = pNtAreMappedFilesTheSame( GetModuleHandleA("ntdll.dll"),
+                                       GetModuleHandleA("kernel32.dll") );
+    ok( status == STATUS_NOT_SAME_DEVICE, "NtAreMappedFilesTheSame returned %x\n", status );
+    status = pNtAreMappedFilesTheSame( GetModuleHandleA("kernel32.dll"),
+                                       GetModuleHandleA("kernel32.dll") );
+    ok( status == STATUS_SUCCESS, "NtAreMappedFilesTheSame returned %x\n", status );
+    status = pNtAreMappedFilesTheSame( GetModuleHandleA("kernel32.dll"),
+                                       (char *)GetModuleHandleA("kernel32.dll") + 4096 );
+    ok( status == STATUS_SUCCESS, "NtAreMappedFilesTheSame returned %x\n", status );
+
+    GetSystemDirectoryA( path, MAX_PATH );
+    strcat( path, "\\kernel32.dll" );
+    file = CreateFileA( path, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0 );
+    ok( file != INVALID_HANDLE_VALUE, "CreateFile error %u\n", GetLastError() );
+
+    mapping = CreateFileMappingA( file, NULL, PAGE_READONLY, 0, 4096, NULL );
+    ok( mapping != 0, "CreateFileMapping error %u\n", GetLastError() );
+    ptr = MapViewOfFile( mapping, FILE_MAP_READ, 0, 0, 4096 );
+    ok( ptr != NULL, "MapViewOfFile FILE_MAP_READ error %u\n", GetLastError() );
+    status = pNtAreMappedFilesTheSame( ptr, GetModuleHandleA("kernel32.dll") );
+    ok( status == STATUS_NOT_SAME_DEVICE, "NtAreMappedFilesTheSame returned %x\n", status );
+    UnmapViewOfFile( ptr );
+    CloseHandle( mapping );
+
+    mapping = CreateFileMappingA( file, NULL, PAGE_READONLY | SEC_IMAGE, 0, 0, NULL );
+    ok( mapping != 0, "CreateFileMapping error %u\n", GetLastError() );
+    ptr = MapViewOfFile( mapping, FILE_MAP_READ, 0, 0, 0 );
+    ok( ptr != NULL, "MapViewOfFile FILE_MAP_READ error %u\n", GetLastError() );
+    status = pNtAreMappedFilesTheSame( ptr, GetModuleHandleA("kernel32.dll") );
+    todo_wine
+    ok( status == STATUS_SUCCESS, "NtAreMappedFilesTheSame returned %x\n", status );
+
+    file2 = CreateFileA( path, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0 );
+    ok( file2 != INVALID_HANDLE_VALUE, "CreateFile error %u\n", GetLastError() );
+    map2 = CreateFileMappingA( file2, NULL, PAGE_READONLY | SEC_IMAGE, 0, 0, NULL );
+    ok( map2 != 0, "CreateFileMapping error %u\n", GetLastError() );
+    ptr2 = MapViewOfFile( map2, FILE_MAP_READ, 0, 0, 0 );
+    ok( ptr2 != NULL, "MapViewOfFile FILE_MAP_READ error %u\n", GetLastError() );
+    status = pNtAreMappedFilesTheSame( ptr, ptr2 );
+    ok( status == STATUS_SUCCESS, "NtAreMappedFilesTheSame returned %x\n", status );
+    UnmapViewOfFile( ptr2 );
+    CloseHandle( map2 );
+    CloseHandle( file2 );
+
+    UnmapViewOfFile( ptr );
+    CloseHandle( mapping );
+
+    CloseHandle( file );
+    DeleteFileA( testfile );
+}
+
 static void test_CreateFileMapping(void)
 {
     HANDLE handle, handle2;
@@ -1271,11 +1415,14 @@ START_TEST(virtual)
     pVirtualFreeEx = (void *) GetProcAddress(hkernel32, "VirtualFreeEx");
     pGetWriteWatch = (void *) GetProcAddress(hkernel32, "GetWriteWatch");
     pResetWriteWatch = (void *) GetProcAddress(hkernel32, "ResetWriteWatch");
+    pNtAreMappedFilesTheSame = (void *)GetProcAddress( GetModuleHandle("ntdll.dll"),
+                                                       "NtAreMappedFilesTheSame" );
 
     test_VirtualAllocEx();
     test_VirtualAlloc();
     test_MapViewOfFile();
     test_NtMapViewOfSection();
+    test_NtAreMappedFilesTheSame();
     test_CreateFileMapping();
     test_IsBadReadPtr();
     test_IsBadWritePtr();
index 14c3330..6f0fd67 100644 (file)
@@ -20,6 +20,7 @@
 
 #include "wine/test.h"
 #include "winbase.h"
+#include "winioctl.h"
 #include <stdio.h>
 
 static HINSTANCE hdll;
@@ -31,6 +32,8 @@ static BOOL (WINAPI *pFindVolumeClose)(HANDLE);
 static UINT (WINAPI *pGetLogicalDriveStringsA)(UINT,LPSTR);
 static UINT (WINAPI *pGetLogicalDriveStringsW)(UINT,LPWSTR);
 static BOOL (WINAPI *pGetVolumeInformationA)(LPCSTR, LPSTR, DWORD, LPDWORD, LPDWORD, LPDWORD, LPSTR, DWORD);
+static BOOL (WINAPI *pGetVolumePathNamesForVolumeNameA)(LPCSTR, LPSTR, DWORD, LPDWORD);
+static BOOL (WINAPI *pGetVolumePathNamesForVolumeNameW)(LPCWSTR, LPWSTR, DWORD, LPDWORD);
 
 /* ############################### */
 
@@ -41,21 +44,13 @@ static void test_query_dos_deviceA(void)
     DWORD ret, ret2, buflen=32768;
     BOOL found = FALSE;
 
-    if (!pFindFirstVolumeA) {
-        win_skip("On win9x, HARDDISK and RAMDISK not present\n");
-        return;
-    }
-
     buffer = HeapAlloc( GetProcessHeap(), 0, buflen );
+    SetLastError(0xdeadbeef);
     ret = QueryDosDeviceA( NULL, buffer, buflen );
-    ok(ret && GetLastError() != ERROR_INSUFFICIENT_BUFFER,
-        "QueryDosDevice buffer too small\n");
-    if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
-        HeapFree( GetProcessHeap(), 0, buffer );
-        return;
-    }
-    ok(ret, "QueryDosDeviceA failed to return list, last error %u\n", GetLastError());
-    if (ret) {
+    ok((ret && GetLastError() != ERROR_INSUFFICIENT_BUFFER),
+        "QueryDosDeviceA failed to return list, last error %u\n", GetLastError());
+
+    if (ret && GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
         p = buffer;
         for (;;) {
             if (!strlen(p)) break;
@@ -69,6 +64,8 @@ static void test_query_dos_deviceA(void)
     for (;drivestr[0] <= 'z'; drivestr[0]++) {
         /* Older W2K fails with ERROR_INSUFFICIENT_BUFFER when buflen is > 32767 */
         ret = QueryDosDeviceA( drivestr, buffer, buflen - 1);
+        ok(ret || GetLastError() == ERROR_FILE_NOT_FOUND,
+            "QueryDosDeviceA failed to return current mapping for %s, last error %u\n", drivestr, GetLastError());
         if(ret) {
             for (p = buffer; *p; p++) *p = toupper(*p);
             if (strstr(buffer, "HARDDISK") || strstr(buffer, "RAMDISK")) found = TRUE;
@@ -78,13 +75,48 @@ static void test_query_dos_deviceA(void)
     HeapFree( GetProcessHeap(), 0, buffer );
 }
 
+static void test_define_dos_deviceA(void)
+{
+    char drivestr[3];
+    char buf[MAX_PATH];
+    DWORD ret;
+
+    /* Find an unused drive letter */
+    drivestr[1] = ':';
+    drivestr[2] = 0;
+    for (drivestr[0] = 'a'; drivestr[0] <= 'z'; drivestr[0]++) {
+        ret = QueryDosDeviceA( drivestr, buf, sizeof(buf));
+        if (!ret) break;
+    }
+    if (drivestr[0] > 'z') {
+        skip("can't test creating a dos drive, none available\n");
+        return;
+    }
+
+    /* Map it to point to the current directory */
+    ret = GetCurrentDirectory(sizeof(buf), buf);
+    ok(ret, "GetCurrentDir\n");
+
+    ret = DefineDosDeviceA(0, drivestr, buf);
+    todo_wine
+    ok(ret, "Could not make drive %s point to %s!\n", drivestr, buf);
+
+    if (!ret) {
+        skip("can't test removing fake drive\n");
+    } else {
+       ret = DefineDosDeviceA(DDD_REMOVE_DEFINITION, drivestr, NULL);
+       ok(ret, "Could not remove fake drive %s!\n", drivestr);
+    }
+}
+
 static void test_FindFirstVolume(void)
 {
     char volume[51];
     HANDLE handle;
 
+    /* not present before w2k */
     if (!pFindFirstVolumeA) {
-        skip("FindFirstVolumeA not found\n");
+        win_skip("FindFirstVolumeA not found\n");
         return;
     }
 
@@ -120,7 +152,7 @@ static void test_GetVolumeNameForVolumeMountPointA(void)
 
     /* not present before w2k */
     if (!pGetVolumeNameForVolumeMountPointA) {
-        skip("GetVolumeNameForVolumeMountPointA not found\n");
+        win_skip("GetVolumeNameForVolumeMountPointA not found\n");
         return;
     }
 
@@ -194,7 +226,7 @@ static void test_GetVolumeNameForVolumeMountPointW(void)
 
     /* not present before w2k */
     if (!pGetVolumeNameForVolumeMountPointW) {
-        skip("GetVolumeNameForVolumeMountPointW not found\n");
+        win_skip("GetVolumeNameForVolumeMountPointW not found\n");
         return;
     }
 
@@ -221,8 +253,8 @@ static void test_GetLogicalDriveStringsA(void)
     UINT size, size2;
     char *buf, *ptr;
 
+    ok( pGetLogicalDriveStringsA != NULL, "GetLogicalDriveStringsA not available\n");
     if(!pGetLogicalDriveStringsA) {
-        win_skip("GetLogicalDriveStringsA not available\n");
         return;
     }
 
@@ -240,9 +272,7 @@ static void test_GetLogicalDriveStringsA(void)
     ok(size2 == size-1, "size2 = %d\n", size2);
 
     for(ptr = buf; ptr < buf+size2; ptr += 4) {
-        ok(('A' <= *ptr && *ptr <= 'Z') ||
-           (broken('a' <= *ptr && *ptr <= 'z')), /* Win9x and WinMe */
-           "device name '%c' is not uppercase\n", *ptr);
+        ok(('A' <= *ptr && *ptr <= 'Z'), "device name '%c' is not uppercase\n", *ptr);
         ok(ptr[1] == ':', "ptr[1] = %c, expected ':'\n", ptr[1]);
         ok(ptr[2] == '\\', "ptr[2] = %c expected '\\'\n", ptr[2]);
         ok(!ptr[3], "ptr[3] = %c expected nullbyte\n", ptr[3]);
@@ -257,8 +287,8 @@ static void test_GetLogicalDriveStringsW(void)
     UINT size, size2;
     WCHAR *buf, *ptr;
 
+    ok( pGetLogicalDriveStringsW != NULL, "GetLogicalDriveStringsW not available\n");
     if(!pGetLogicalDriveStringsW) {
-        win_skip("GetLogicalDriveStringsW not available\n");
         return;
     }
 
@@ -295,21 +325,17 @@ static void test_GetVolumeInformationA(void)
 {
     BOOL ret;
     UINT result;
-    char Root_Dir0[]="C:";
-    char Root_Dir1[]="C:\\";
-    char Root_Dir2[]="\\\\?\\C:\\";
+    char Root_Colon[]="C:";
+    char Root_Slash[]="C:\\";
+    char Root_UNC[]="\\\\?\\C:\\";
     char volume[MAX_PATH+1];
     DWORD vol_name_size=MAX_PATH+1, vol_serial_num=-1, max_comp_len=0, fs_flags=0, fs_name_len=MAX_PATH+1;
     char vol_name_buf[MAX_PATH+1], fs_name_buf[MAX_PATH+1];
     char windowsdir[MAX_PATH+10];
     char currentdir[MAX_PATH+1];
 
-    if (!pGetVolumeInformationA) {
-        win_skip("GetVolumeInformationA not found\n");
-        return;
-    }
-    if (!pGetVolumeNameForVolumeMountPointA) {
-        win_skip("GetVolumeNameForVolumeMountPointA not found\n");
+    ok( pGetVolumeInformationA != NULL, "GetVolumeInformationA not found\n");
+    if(!pGetVolumeInformationA) {
         return;
     }
 
@@ -317,81 +343,147 @@ static void test_GetVolumeInformationA(void)
     result = GetWindowsDirectory(windowsdir, sizeof(windowsdir));
     ok(result < sizeof(windowsdir), "windowsdir is abnormally long!\n");
     ok(result != 0, "GetWindowsDirectory: error %d\n", GetLastError());
-    Root_Dir0[0] = windowsdir[0];
-    Root_Dir1[0] = windowsdir[0];
-    Root_Dir2[4] = windowsdir[0];
-
-    /* get the unique volume name for the windows drive  */
-    ret = pGetVolumeNameForVolumeMountPointA(Root_Dir1, volume, MAX_PATH);
-    ok(ret == TRUE, "GetVolumeNameForVolumeMountPointA failed\n");
+    Root_Colon[0] = windowsdir[0];
+    Root_Slash[0] = windowsdir[0];
+    Root_UNC[4] = windowsdir[0];
 
     result = GetCurrentDirectory(MAX_PATH, currentdir);
     ok(result, "GetCurrentDirectory: error %d\n", GetLastError());
+    /* Note that GetCurrentDir yields no trailing slash for subdirs */
 
-    /*  ****  now start the tests       ****  */
-    /* check for error on no trailing \   */
-    if (result > 3)
-    {
-        ret = pGetVolumeInformationA(Root_Dir0, vol_name_buf, vol_name_size, NULL,
-                NULL, NULL, fs_name_buf, fs_name_len);
-        ok(!ret && GetLastError() == ERROR_INVALID_NAME,
-            "GetVolumeInformationA w/o '\\' did not fail, last error %u\n", GetLastError());
-    }
-    else
-        skip("Running on a root directory\n");
-
-    /* check for error on no trailing \ when current dir is root dir */
-    ret = SetCurrentDirectory(Root_Dir1);
+    /* check for NO error on no trailing \ when current dir is root dir */
+    ret = SetCurrentDirectory(Root_Slash);
     ok(ret, "SetCurrentDirectory: error %d\n", GetLastError());
-    ret = pGetVolumeInformationA(Root_Dir0, vol_name_buf, vol_name_size, NULL,
+    ret = pGetVolumeInformationA(Root_Colon, vol_name_buf, vol_name_size, NULL,
             NULL, NULL, fs_name_buf, fs_name_len);
-    todo_wine
-    ok(ret, "GetVolumeInformationA failed, last error %u\n", GetLastError());
+    ok(ret, "GetVolumeInformationA root failed, last error %u\n", GetLastError());
 
-    /* check for error on no trailing \ when current dir is windows dir */
+    /* check for error on no trailing \ when current dir is subdir (windows) of queried drive */
     ret = SetCurrentDirectory(windowsdir);
     ok(ret, "SetCurrentDirectory: error %d\n", GetLastError());
-    ret = pGetVolumeInformationA(Root_Dir0, vol_name_buf, vol_name_size, NULL,
+    SetLastError(0xdeadbeef);
+    ret = pGetVolumeInformationA(Root_Colon, vol_name_buf, vol_name_size, NULL,
             NULL, NULL, fs_name_buf, fs_name_len);
-    ok(!ret && GetLastError() == ERROR_INVALID_NAME,
-        "GetVolumeInformationA did not fail, last error %u\n", GetLastError());
+    ok(!ret && (GetLastError() == ERROR_INVALID_NAME),
+        "GetVolumeInformationA did%s fail, last error %u\n", ret ? " not":"", GetLastError());
 
     /* reset current directory */
     ret = SetCurrentDirectory(currentdir);
     ok(ret, "SetCurrentDirectory: error %d\n", GetLastError());
 
+    if (toupper(currentdir[0]) == toupper(windowsdir[0])) {
+        skip("Please re-run from another device than %c:\n", windowsdir[0]);
+        /* FIXME: Use GetLogicalDrives to find another device to avoid this skip. */
+    } else {
+        char Root_Env[]="=C:"; /* where MS maintains the per volume directory */
+        Root_Env[1] = windowsdir[0];
+
+        /* C:\windows becomes the current directory on drive C: */
+        /* Note that paths to subdirs are stored without trailing slash, like what GetCurrentDir yields. */
+        ret = SetEnvironmentVariable(Root_Env, windowsdir);
+        ok(ret, "SetEnvironmentVariable %s failed\n", Root_Env);
+
+        ret = SetCurrentDirectory(windowsdir);
+        ok(ret, "SetCurrentDirectory: error %d\n", GetLastError());
+        ret = SetCurrentDirectory(currentdir);
+        ok(ret, "SetCurrentDirectory: error %d\n", GetLastError());
+
+        /* windows dir is current on the root drive, call fails */
+        SetLastError(0xdeadbeef);
+        ret = pGetVolumeInformationA(Root_Colon, vol_name_buf, vol_name_size, NULL,
+                NULL, NULL, fs_name_buf, fs_name_len);
+        ok(!ret && (GetLastError() == ERROR_INVALID_NAME),
+           "GetVolumeInformationA did%s fail, last error %u\n", ret ? " not":"", GetLastError());
+
+        /* Try normal drive letter with trailing \ */
+        ret = pGetVolumeInformationA(Root_Slash, vol_name_buf, vol_name_size, NULL,
+                NULL, NULL, fs_name_buf, fs_name_len);
+        ok(ret, "GetVolumeInformationA with \\ failed, last error %u\n", GetLastError());
+
+        ret = SetCurrentDirectory(Root_Slash);
+        ok(ret, "SetCurrentDirectory: error %d\n", GetLastError());
+        ret = SetCurrentDirectory(currentdir);
+        ok(ret, "SetCurrentDirectory: error %d\n", GetLastError());
+
+        /* windows dir is STILL CURRENT on root drive; the call fails as before,   */
+        /* proving that SetCurrentDir did not remember the other drive's directory */
+        SetLastError(0xdeadbeef);
+        ret = pGetVolumeInformationA(Root_Colon, vol_name_buf, vol_name_size, NULL,
+                NULL, NULL, fs_name_buf, fs_name_len);
+        ok(!ret && (GetLastError() == ERROR_INVALID_NAME),
+           "GetVolumeInformationA did%s fail, last error %u\n", ret ? " not":"", GetLastError());
+
+        /* Now C:\ becomes the current directory on drive C: */
+        ret = SetEnvironmentVariable(Root_Env, Root_Slash); /* set =C:=C:\ */
+        ok(ret, "SetEnvironmentVariable %s failed\n", Root_Env);
+
+        /* \ is current on root drive, call succeeds */
+        ret = pGetVolumeInformationA(Root_Colon, vol_name_buf, vol_name_size, NULL,
+                NULL, NULL, fs_name_buf, fs_name_len);
+        ok(ret, "GetVolumeInformationA failed, last error %u\n", GetLastError());
+
+        /* again, SetCurrentDirectory on another drive does not matter */
+        ret = SetCurrentDirectory(Root_Slash);
+        ok(ret, "SetCurrentDirectory: error %d\n", GetLastError());
+        ret = SetCurrentDirectory(currentdir);
+        ok(ret, "SetCurrentDirectory: error %d\n", GetLastError());
+
+        /* \ is current on root drive, call succeeds */
+        ret = pGetVolumeInformationA(Root_Colon, vol_name_buf, vol_name_size, NULL,
+                NULL, NULL, fs_name_buf, fs_name_len);
+        ok(ret, "GetVolumeInformationA failed, last error %u\n", GetLastError());
+    }
+
     /* try null root directory to return "root of the current directory"  */
     ret = pGetVolumeInformationA(NULL, vol_name_buf, vol_name_size, NULL,
             NULL, NULL, fs_name_buf, fs_name_len);
     ok(ret, "GetVolumeInformationA failed on null root dir, last error %u\n", GetLastError());
 
     /* Try normal drive letter with trailing \  */
-    ret = pGetVolumeInformationA(Root_Dir1, vol_name_buf, vol_name_size,
-            &vol_serial_num, &max_comp_len, &fs_flags, fs_name_buf, fs_name_len);
-    ok(ret, "GetVolumeInformationA failed, root=%s, last error=%u\n", Root_Dir1, GetLastError());
-
-    /* try again with dirve letter and the "disable parsing" prefix */
-    ret = pGetVolumeInformationA(Root_Dir2, vol_name_buf, vol_name_size,
+    ret = pGetVolumeInformationA(Root_Slash, vol_name_buf, vol_name_size,
             &vol_serial_num, &max_comp_len, &fs_flags, fs_name_buf, fs_name_len);
-    todo_wine ok(ret, "GetVolumeInformationA failed, root=%s, last error=%u\n", Root_Dir2, GetLastError());
+    ok(ret, "GetVolumeInformationA failed, root=%s, last error=%u\n", Root_Slash, GetLastError());
 
-    /* try again with unique voluem name */
-    ret = pGetVolumeInformationA(volume, vol_name_buf, vol_name_size,
+    /* try again with drive letter and the "disable parsing" prefix */
+    SetLastError(0xdeadbeef);
+    ret = pGetVolumeInformationA(Root_UNC, vol_name_buf, vol_name_size,
             &vol_serial_num, &max_comp_len, &fs_flags, fs_name_buf, fs_name_len);
-    todo_wine ok(ret, "GetVolumeInformationA failed, root=%s, last error=%u\n", volume, GetLastError());
+    ok(ret, "GetVolumeInformationA did%s fail, root=%s, last error=%u\n", ret ? " not":"", Root_UNC, GetLastError());
 
     /* try again with device name space  */
-    Root_Dir2[2] = '.';
-    ret = pGetVolumeInformationA(Root_Dir2, vol_name_buf, vol_name_size,
+    Root_UNC[2] = '.';
+    SetLastError(0xdeadbeef);
+    ret = pGetVolumeInformationA(Root_UNC, vol_name_buf, vol_name_size,
             &vol_serial_num, &max_comp_len, &fs_flags, fs_name_buf, fs_name_len);
-    todo_wine ok(ret, "GetVolumeInformationA failed, root=%s, last error=%u\n", Root_Dir2, GetLastError());
+    ok(ret, "GetVolumeInformationA did%s fail, root=%s, last error=%u\n", ret ? " not":"", Root_UNC, GetLastError());
 
     /* try again with a directory off the root - should generate error  */
     if (windowsdir[strlen(windowsdir)-1] != '\\') strcat(windowsdir, "\\");
+    SetLastError(0xdeadbeef);
+    ret = pGetVolumeInformationA(windowsdir, vol_name_buf, vol_name_size,
+            &vol_serial_num, &max_comp_len, &fs_flags, fs_name_buf, fs_name_len);
+    ok(!ret && (GetLastError()==ERROR_DIR_NOT_ROOT),
+          "GetVolumeInformationA did%s fail, root=%s, last error=%u\n", ret ? " not":"", windowsdir, GetLastError());
+    /* A subdir with trailing \ yields DIR_NOT_ROOT instead of INVALID_NAME */
+    if (windowsdir[strlen(windowsdir)-1] == '\\') windowsdir[strlen(windowsdir)-1] = 0;
+    SetLastError(0xdeadbeef);
     ret = pGetVolumeInformationA(windowsdir, vol_name_buf, vol_name_size,
             &vol_serial_num, &max_comp_len, &fs_flags, fs_name_buf, fs_name_len);
-    todo_wine ok(!ret && GetLastError()==ERROR_DIR_NOT_ROOT,
-          "GetVolumeInformationA failed, root=%s, last error=%u\n", windowsdir, GetLastError());
+    ok(!ret && (GetLastError()==ERROR_INVALID_NAME),
+          "GetVolumeInformationA did%s fail, root=%s, last error=%u\n", ret ? " not":"", windowsdir, GetLastError());
+
+    if (!pGetVolumeNameForVolumeMountPointA) {
+        win_skip("GetVolumeNameForVolumeMountPointA not found\n");
+        return;
+    }
+    /* get the unique volume name for the windows drive  */
+    ret = pGetVolumeNameForVolumeMountPointA(Root_Slash, volume, MAX_PATH);
+    ok(ret == TRUE, "GetVolumeNameForVolumeMountPointA failed\n");
+
+    /* try again with unique volume name */
+    ret = pGetVolumeInformationA(volume, vol_name_buf, vol_name_size,
+            &vol_serial_num, &max_comp_len, &fs_flags, fs_name_buf, fs_name_len);
+    ok(ret, "GetVolumeInformationA failed, root=%s, last error=%u\n", volume, GetLastError());
 }
 
 /* Test to check that unique volume name from windows dir mount point  */
@@ -427,7 +519,7 @@ static void test_enum_vols(void)
     hFind = pFindFirstVolumeA( Volume_2, MAX_PATH );
     ok(hFind != INVALID_HANDLE_VALUE, "FindFirstVolume failed, err=%u\n",
                 GetLastError());
-    if (hFind != INVALID_HANDLE_VALUE) {
+
     do
     {
         /* validate correct length of unique volume name  */
@@ -440,7 +532,196 @@ static void test_enum_vols(void)
     } while (pFindNextVolumeA( hFind, Volume_2, MAX_PATH ));
     ok(found, "volume name %s not found by Find[First/Next]Volume\n", Volume_1);
     pFindVolumeClose( hFind );
+}
+
+static void test_disk_extents(void)
+{
+    BOOL ret;
+    DWORD size;
+    HANDLE handle;
+    static DWORD data[16];
+
+    handle = CreateFileA( "\\\\.\\c:", GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0 );
+    if (handle == INVALID_HANDLE_VALUE)
+    {
+        win_skip("can't open c: drive %u\n", GetLastError());
+        return;
+    }
+    size = 0;
+    ret = DeviceIoControl( handle, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, &data,
+                           sizeof(data), &data, sizeof(data), &size, NULL );
+    if (!ret && GetLastError() == ERROR_INVALID_FUNCTION)
+    {
+        win_skip("IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS not supported\n");
+        CloseHandle( handle );
+        return;
+    }
+    ok(ret, "DeviceIoControl failed %u\n", GetLastError());
+    ok(size == 32, "expected 32, got %u\n", size);
+    CloseHandle( handle );
+}
+
+static void test_GetVolumePathNamesForVolumeNameA(void)
+{
+    BOOL ret;
+    char volume[MAX_PATH], buffer[MAX_PATH];
+    DWORD len, error;
+
+    if (!pGetVolumePathNamesForVolumeNameA || !pGetVolumeNameForVolumeMountPointA)
+    {
+        win_skip("required functions not found\n");
+        return;
     }
+
+    ret = pGetVolumeNameForVolumeMountPointA( "c:\\", volume, sizeof(volume) );
+    ok(ret, "failed to get volume name %u\n", GetLastError());
+    trace("c:\\ -> %s\n", volume);
+
+    SetLastError( 0xdeadbeef );
+    ret = pGetVolumePathNamesForVolumeNameA( NULL, NULL, 0, NULL );
+    error = GetLastError();
+    ok(!ret, "expected failure\n");
+    ok(error == ERROR_INVALID_NAME, "expected ERROR_INVALID_NAME got %u\n", error);
+
+    SetLastError( 0xdeadbeef );
+    ret = pGetVolumePathNamesForVolumeNameA( "", NULL, 0, NULL );
+    error = GetLastError();
+    ok(!ret, "expected failure\n");
+    ok(error == ERROR_INVALID_NAME, "expected ERROR_INVALID_NAME got %u\n", error);
+
+    SetLastError( 0xdeadbeef );
+    ret = pGetVolumePathNamesForVolumeNameA( volume, NULL, 0, NULL );
+    error = GetLastError();
+    ok(!ret, "expected failure\n");
+    ok(error == ERROR_MORE_DATA, "expected ERROR_MORE_DATA got %u\n", error);
+
+    SetLastError( 0xdeadbeef );
+    ret = pGetVolumePathNamesForVolumeNameA( volume, buffer, 0, NULL );
+    error = GetLastError();
+    ok(!ret, "expected failure\n");
+    ok(error == ERROR_MORE_DATA, "expected ERROR_MORE_DATA got %u\n", error);
+
+    memset( buffer, 0xff, sizeof(buffer) );
+    ret = pGetVolumePathNamesForVolumeNameA( volume, buffer, sizeof(buffer), NULL );
+    ok(ret, "failed to get path names %u\n", GetLastError());
+    ok(!strcmp( "C:\\", buffer ), "expected \"\\C:\" got \"%s\"\n", buffer);
+    ok(!buffer[4], "expected double null-terminated buffer\n");
+
+    len = 0;
+    SetLastError( 0xdeadbeef );
+    ret = pGetVolumePathNamesForVolumeNameA( NULL, NULL, 0, &len );
+    error = GetLastError();
+    ok(!ret, "expected failure\n");
+    ok(error == ERROR_INVALID_NAME, "expected ERROR_INVALID_NAME got %u\n", error);
+
+    len = 0;
+    SetLastError( 0xdeadbeef );
+    ret = pGetVolumePathNamesForVolumeNameA( NULL, NULL, sizeof(buffer), &len );
+    error = GetLastError();
+    ok(!ret, "expected failure\n");
+    ok(error == ERROR_INVALID_NAME, "expected ERROR_INVALID_NAME got %u\n", error);
+
+    len = 0;
+    SetLastError( 0xdeadbeef );
+    ret = pGetVolumePathNamesForVolumeNameA( NULL, buffer, sizeof(buffer), &len );
+    error = GetLastError();
+    ok(!ret, "expected failure\n");
+    ok(error == ERROR_INVALID_NAME, "expected ERROR_INVALID_NAME got %u\n", error);
+
+    len = 0;
+    SetLastError( 0xdeadbeef );
+    ret = pGetVolumePathNamesForVolumeNameA( NULL, buffer, sizeof(buffer), &len );
+    error = GetLastError();
+    ok(!ret, "expected failure\n");
+    ok(error == ERROR_INVALID_NAME, "expected ERROR_INVALID_NAME got %u\n", error);
+
+    len = 0;
+    memset( buffer, 0xff, sizeof(buffer) );
+    ret = pGetVolumePathNamesForVolumeNameA( volume, buffer, sizeof(buffer), &len );
+    ok(ret, "failed to get path names %u\n", GetLastError());
+    ok(len == 5 || broken(len == 2), "expected 5 got %u\n", len);
+    ok(!strcmp( "C:\\", buffer ), "expected \"\\C:\" got \"%s\"\n", buffer);
+    ok(!buffer[4], "expected double null-terminated buffer\n");
+}
+
+static void test_GetVolumePathNamesForVolumeNameW(void)
+{
+    static const WCHAR empty[] = {0};
+    static const WCHAR drive_c[] = {'c',':','\\',0};
+    static const WCHAR volume_null[] = {'\\','\\','?','\\','V','o','l','u','m','e',
+        '{','0','0','0','0','0','0','0','0','-','0','0','0','0','-','0','0','0','0',
+        '-','0','0','0','0','-','0','0','0','0','0','0','0','0','0','0','0','0','}','\\',0};
+    BOOL ret;
+    WCHAR volume[MAX_PATH], buffer[MAX_PATH];
+    DWORD len, error;
+
+    if (!pGetVolumePathNamesForVolumeNameW || !pGetVolumeNameForVolumeMountPointW)
+    {
+        win_skip("required functions not found\n");
+        return;
+    }
+
+    ret = pGetVolumeNameForVolumeMountPointW( drive_c, volume, sizeof(volume)/sizeof(volume[0]) );
+    ok(ret, "failed to get volume name %u\n", GetLastError());
+
+    SetLastError( 0xdeadbeef );
+    ret = pGetVolumePathNamesForVolumeNameW( empty, NULL, 0, NULL );
+    error = GetLastError();
+    ok(!ret, "expected failure\n");
+    ok(error == ERROR_INVALID_NAME, "expected ERROR_INVALID_NAME got %u\n", error);
+
+    SetLastError( 0xdeadbeef );
+    ret = pGetVolumePathNamesForVolumeNameW( volume, NULL, 0, NULL );
+    error = GetLastError();
+    ok(!ret, "expected failure\n");
+    ok(error == ERROR_MORE_DATA, "expected ERROR_MORE_DATA got %u\n", error);
+
+    SetLastError( 0xdeadbeef );
+    ret = pGetVolumePathNamesForVolumeNameW( volume, buffer, 0, NULL );
+    error = GetLastError();
+    ok(!ret, "expected failure\n");
+    ok(error == ERROR_MORE_DATA, "expected ERROR_MORE_DATA got %u\n", error);
+
+    if (0) { /* crash */
+    ret = pGetVolumePathNamesForVolumeNameW( volume, NULL, sizeof(buffer), NULL );
+    ok(ret, "failed to get path names %u\n", GetLastError());
+    }
+
+    ret = pGetVolumePathNamesForVolumeNameW( volume, buffer, sizeof(buffer), NULL );
+    ok(ret, "failed to get path names %u\n", GetLastError());
+
+    len = 0;
+    memset( buffer, 0xff, sizeof(buffer) );
+    ret = pGetVolumePathNamesForVolumeNameW( volume, buffer, sizeof(buffer), &len );
+    ok(ret, "failed to get path names %u\n", GetLastError());
+    ok(len == 5, "expected 5 got %u\n", len);
+    ok(!buffer[4], "expected double null-terminated buffer\n");
+
+    len = 0;
+    volume[1] = '?';
+    volume[lstrlenW( volume ) - 1] = 0;
+    SetLastError( 0xdeadbeef );
+    ret = pGetVolumePathNamesForVolumeNameW( volume, buffer, sizeof(buffer), &len );
+    error = GetLastError();
+    ok(!ret, "expected failure\n");
+    ok(error == ERROR_INVALID_NAME, "expected ERROR_INVALID_NAME got %u\n", error);
+
+    len = 0;
+    volume[0] = '\\';
+    volume[1] = 0;
+    SetLastError( 0xdeadbeef );
+    ret = pGetVolumePathNamesForVolumeNameW( volume, buffer, sizeof(buffer), &len );
+    error = GetLastError();
+    ok(!ret, "expected failure\n");
+    todo_wine ok(error == ERROR_INVALID_PARAMETER, "expected ERROR_INVALID_PARAMETER got %u\n", error);
+
+    len = 0;
+    lstrcpyW( volume, volume_null );
+    SetLastError( 0xdeadbeef );
+    ret = pGetVolumePathNamesForVolumeNameW( volume, buffer, sizeof(buffer), &len );
+    error = GetLastError();
+    ok(!ret, "expected failure\n");
+    ok(error == ERROR_FILE_NOT_FOUND, "expected ERROR_FILE_NOT_FOUND got %u\n", error);
 }
 
 START_TEST(volume)
@@ -454,8 +735,11 @@ START_TEST(volume)
     pGetLogicalDriveStringsA = (void *) GetProcAddress(hdll, "GetLogicalDriveStringsA");
     pGetLogicalDriveStringsW = (void *) GetProcAddress(hdll, "GetLogicalDriveStringsW");
     pGetVolumeInformationA = (void *) GetProcAddress(hdll, "GetVolumeInformationA");
+    pGetVolumePathNamesForVolumeNameA = (void *) GetProcAddress(hdll, "GetVolumePathNamesForVolumeNameA");
+    pGetVolumePathNamesForVolumeNameW = (void *) GetProcAddress(hdll, "GetVolumePathNamesForVolumeNameW");
 
     test_query_dos_deviceA();
+    test_define_dos_deviceA();
     test_FindFirstVolume();
     test_GetVolumeNameForVolumeMountPointA();
     test_GetVolumeNameForVolumeMountPointW();
@@ -463,4 +747,7 @@ START_TEST(volume)
     test_GetLogicalDriveStringsW();
     test_GetVolumeInformationA();
     test_enum_vols();
+    test_disk_extents();
+    test_GetVolumePathNamesForVolumeNameA();
+    test_GetVolumePathNamesForVolumeNameW();
 }