[KERNEL32_WINETEST] Sync with Wine Staging 1.9.18.
authorAmine Khaldi <amine.khaldi@reactos.org>
Wed, 7 Sep 2016 22:32:56 +0000 (22:32 +0000)
committerAmine Khaldi <amine.khaldi@reactos.org>
Wed, 7 Sep 2016 22:32:56 +0000 (22:32 +0000)
svn path=/trunk/; revision=72611

rostests/winetests/kernel32/console.c
rostests/winetests/kernel32/fiber.c
rostests/winetests/kernel32/file.c
rostests/winetests/kernel32/loader.c
rostests/winetests/kernel32/locale.c
rostests/winetests/kernel32/path.c
rostests/winetests/kernel32/thread.c
rostests/winetests/kernel32/virtual.c

index 5b66d9f..c88db6a 100755 (executable)
@@ -908,6 +908,58 @@ static void testScreenBuffer(HANDLE hConOut)
     SetConsoleOutputCP(oldcp);
 }
 
+static void CALLBACK signaled_function(void *p, BOOLEAN timeout)
+{
+    HANDLE event = p;
+    SetEvent(event);
+    ok(!timeout, "wait shouldn't have timed out\n");
+}
+
+static void testWaitForConsoleInput(HANDLE input_handle)
+{
+    HANDLE wait_handle;
+    HANDLE complete_event;
+    INPUT_RECORD record;
+    DWORD events_written;
+    DWORD wait_ret;
+    BOOL ret;
+
+    complete_event = CreateEventW(NULL, FALSE, FALSE, NULL);
+
+    /* Test success case */
+    ret = RegisterWaitForSingleObject(&wait_handle, input_handle, signaled_function, complete_event, INFINITE, WT_EXECUTEONLYONCE);
+    ok(ret == TRUE, "Expected RegisterWaitForSingleObject to return TRUE, got %d\n", ret);
+    /* give worker thread a chance to start up */
+    Sleep(100);
+    record.EventType = KEY_EVENT;
+    record.Event.KeyEvent.bKeyDown = 1;
+    record.Event.KeyEvent.wRepeatCount = 1;
+    record.Event.KeyEvent.wVirtualKeyCode = VK_RETURN;
+    record.Event.KeyEvent.wVirtualScanCode = VK_RETURN;
+    record.Event.KeyEvent.uChar.UnicodeChar = '\r';
+    record.Event.KeyEvent.dwControlKeyState = 0;
+    ret = WriteConsoleInputW(input_handle, &record, 1, &events_written);
+    ok(ret == TRUE, "Expected WriteConsoleInputW to return TRUE, got %d\n", ret);
+    wait_ret = WaitForSingleObject(complete_event, INFINITE);
+    ok(wait_ret == WAIT_OBJECT_0, "Expected the handle to be signaled\n");
+    ret = UnregisterWait(wait_handle);
+    /* If the callback is still running, this fails with ERROR_IO_PENDING, but
+       that's ok and expected. */
+    ok(ret != 0 || GetLastError() == ERROR_IO_PENDING,
+        "UnregisterWait failed with error %d\n", GetLastError());
+
+    /* Test timeout case */
+    FlushConsoleInputBuffer(input_handle);
+    ret = RegisterWaitForSingleObject(&wait_handle, input_handle, signaled_function, complete_event, INFINITE, WT_EXECUTEONLYONCE);
+    wait_ret = WaitForSingleObject(complete_event, 100);
+    ok(wait_ret == WAIT_TIMEOUT, "Expected the wait to time out\n");
+    ret = UnregisterWait(wait_handle);
+    ok(ret, "UnregisterWait failed with error %d\n", GetLastError());
+
+    /* Clean up */
+    ok(CloseHandle(complete_event), "Failed to close event handle, last error %d\n", GetLastError());
+}
+
 static void test_GetSetConsoleInputExeName(void)
 {
     BOOL ret;
@@ -2938,6 +2990,55 @@ static void test_SetConsoleFont(HANDLE std_output)
     todo_wine ok(GetLastError() == ERROR_INVALID_PARAMETER, "got %u, expected 87\n", GetLastError());
 }
 
+static void test_GetConsoleScreenBufferInfoEx(HANDLE std_output)
+{
+    HANDLE hmod;
+    BOOL (WINAPI *pGetConsoleScreenBufferInfoEx)(HANDLE, CONSOLE_SCREEN_BUFFER_INFOEX *);
+    CONSOLE_SCREEN_BUFFER_INFOEX csbix;
+    BOOL ret;
+    HANDLE std_input = GetStdHandle(STD_INPUT_HANDLE);
+
+    hmod = GetModuleHandleA("kernel32.dll");
+    pGetConsoleScreenBufferInfoEx = (void *)GetProcAddress(hmod, "GetConsoleScreenBufferInfoEx");
+    if (!pGetConsoleScreenBufferInfoEx)
+    {
+        win_skip("GetConsoleScreenBufferInfoEx is not available\n");
+        return;
+    }
+
+    SetLastError(0xdeadbeef);
+    ret = pGetConsoleScreenBufferInfoEx(NULL, &csbix);
+    ok(!ret, "got %d, expected zero\n", ret);
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "got %u, expected 87\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = pGetConsoleScreenBufferInfoEx(std_input, &csbix);
+    ok(!ret, "got %d, expected zero\n", ret);
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "got %u, expected 87\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = pGetConsoleScreenBufferInfoEx(std_output, &csbix);
+    ok(!ret, "got %d, expected zero\n", ret);
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "got %u, expected 87\n", GetLastError());
+
+    csbix.cbSize = sizeof(CONSOLE_SCREEN_BUFFER_INFOEX);
+
+    SetLastError(0xdeadbeef);
+    ret = pGetConsoleScreenBufferInfoEx(NULL, &csbix);
+    ok(!ret, "got %d, expected zero\n", ret);
+    ok(GetLastError() == ERROR_INVALID_HANDLE, "got %u, expected 6\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = pGetConsoleScreenBufferInfoEx(std_input, &csbix);
+    ok(!ret, "got %d, expected zero\n", ret);
+    ok(GetLastError() == ERROR_INVALID_HANDLE, "got %u, expected 6\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = pGetConsoleScreenBufferInfoEx(std_output, &csbix);
+    ok(ret, "got %d, expected non-zero\n", ret);
+    ok(GetLastError() == 0xdeadbeef, "got %u, expected 0xdeadbeef\n", GetLastError());
+}
+
 START_TEST(console)
 {
     static const char font_name[] = "Lucida Console";
@@ -3044,6 +3145,8 @@ START_TEST(console)
     testScroll(hConOut, sbi.dwSize);
     /* will test sb creation / modification / codepage handling */
     testScreenBuffer(hConOut);
+    /* Test waiting for a console handle */
+    testWaitForConsoleInput(hConIn);
 
     /* clear duplicated console font table */
     CloseHandle(hConIn);
@@ -3086,4 +3189,5 @@ START_TEST(console)
     test_GetLargestConsoleWindowSize(hConOut);
     test_GetConsoleFontInfo(hConOut);
     test_SetConsoleFont(hConOut);
+    test_GetConsoleScreenBufferInfoEx(hConOut);
 }
index 9ff9366..7501165 100644 (file)
@@ -155,28 +155,20 @@ static void test_FiberHandling(void)
     ok(fiberCount == 1, "Wrong fiber count: %d\n", fiberCount);
     pDeleteFiber(fibers[1]);
 
-    if (!pCreateFiberEx)
+    if (pCreateFiberEx)
     {
-        win_skip( "CreateFiberEx not present\n" );
-        return;
-    }
-
-    fibers[1] = pCreateFiberEx(0,0,0,FiberMainProc,&testparam);
-    ok(fibers[1] != NULL, "CreateFiberEx failed with error %u\n", GetLastError());
-
-    pSwitchToFiber(fibers[1]);
-    ok(fiberCount == 2, "Wrong fiber count: %d\n", fiberCount);
-    pDeleteFiber(fibers[1]);
+        fibers[1] = pCreateFiberEx(0,0,0,FiberMainProc,&testparam);
+        ok(fibers[1] != NULL, "CreateFiberEx failed with error %u\n", GetLastError());
 
-    if (!pIsThreadAFiber)
-    {
-        win_skip( "IsThreadAFiber not present\n" );
-        return;
+        pSwitchToFiber(fibers[1]);
+        ok(fiberCount == 2, "Wrong fiber count: %d\n", fiberCount);
+        pDeleteFiber(fibers[1]);
     }
+    else win_skip( "CreateFiberEx not present\n" );
 
-    ok(pIsThreadAFiber(), "IsThreadAFiber reported FALSE\n");
+    if (pIsThreadAFiber) ok(pIsThreadAFiber(), "IsThreadAFiber reported FALSE\n");
     test_ConvertFiberToThread();
-    ok(!pIsThreadAFiber(), "IsThreadAFiber reported TRUE\n");
+    if (pIsThreadAFiber) ok(!pIsThreadAFiber(), "IsThreadAFiber reported TRUE\n");
 }
 
 static void test_FiberLocalStorage(void)
index cb9a986..9b6377d 100755 (executable)
@@ -4449,17 +4449,10 @@ static void test_file_access(void)
             else
             {
                 /* FIXME: Remove once Wine is fixed */
-                if ((td[j].access & (GENERIC_READ | GENERIC_WRITE)) ||
-                    (!(td[i].access & (GENERIC_WRITE | FILE_WRITE_DATA)) && (td[j].access & FILE_WRITE_DATA)) ||
-                    (!(td[i].access & (GENERIC_READ | FILE_READ_DATA)) && (td[j].access & FILE_READ_DATA)) ||
-                    (!(td[i].access & (GENERIC_WRITE)) && (td[j].access & FILE_APPEND_DATA)))
-                {
-todo_wine
-                ok(!ret, "DuplicateHandle(%#x => %#x) should fail\n", td[i].access, td[j].access);
-todo_wine
-                ok(GetLastError() == ERROR_ACCESS_DENIED, "expected ERROR_ACCESS_DENIED, got %d\n", GetLastError());
-                }
-                else
+                todo_wine_if((td[j].access & (GENERIC_READ | GENERIC_WRITE) ||
+                             (!(td[i].access & (GENERIC_WRITE | FILE_WRITE_DATA)) && (td[j].access & FILE_WRITE_DATA)) ||
+                             (!(td[i].access & (GENERIC_READ | FILE_READ_DATA)) && (td[j].access & FILE_READ_DATA)) ||
+                             (!(td[i].access & (GENERIC_WRITE)) && (td[j].access & FILE_APPEND_DATA))))
                 {
                 ok(!ret, "DuplicateHandle(%#x => %#x) should fail\n", td[i].access, td[j].access);
                 ok(GetLastError() == ERROR_ACCESS_DENIED, "expected ERROR_ACCESS_DENIED, got %d\n", GetLastError());
index 87bbe93..0fd57cc 100644 (file)
@@ -70,6 +70,10 @@ static PVOID    (WINAPI *pResolveDelayLoadedAPI)(PVOID, PCIMAGE_DELAYLOAD_DESCRI
                                                  PDELAYLOAD_FAILURE_DLL_CALLBACK, PVOID,
                                                  PIMAGE_THUNK_DATA ThunkAddress,ULONG);
 static PVOID (WINAPI *pRtlImageDirectoryEntryToData)(HMODULE,BOOL,WORD,ULONG *);
+static DWORD (WINAPI *pFlsAlloc)(PFLS_CALLBACK_FUNCTION);
+static BOOL (WINAPI *pFlsSetValue)(DWORD, PVOID);
+static PVOID (WINAPI *pFlsGetValue)(DWORD);
+static BOOL (WINAPI *pFlsFree)(DWORD);
 
 static PVOID RVAToAddr(DWORD_PTR rva, HMODULE module)
 {
@@ -143,9 +147,9 @@ static const IMAGE_NT_HEADERS nt_header_template =
 static IMAGE_SECTION_HEADER section =
 {
     ".rodata", /* Name */
-    { 0x10 }, /* Misc */
+    { 0 }, /* Misc */
     0, /* VirtualAddress */
-    0x0a, /* SizeOfRawData */
+    0, /* SizeOfRawData */
     0, /* PointerToRawData */
     0, /* PointerToRelocations */
     0, /* PointerToLinenumbers */
@@ -196,6 +200,8 @@ static DWORD create_test_dll( const IMAGE_DOS_HEADER *dos_header, UINT dos_size,
     assert(nt_header->FileHeader.NumberOfSections <= 1);
     if (nt_header->FileHeader.NumberOfSections)
     {
+        section.SizeOfRawData = 10;
+
         if (nt_header->OptionalHeader.SectionAlignment >= page_size)
         {
             section.PointerToRawData = dos_size;
@@ -262,9 +268,13 @@ static void query_image_section( int id, const char *dll_name, const IMAGE_NT_HE
     ok( image.CommittedStackSize == nt_header->OptionalHeader.SizeOfStackCommit || broken(truncated),
         "%u: CommittedStackSize wrong %lx / %lx\n", id,
         image.CommittedStackSize, (SIZE_T)nt_header->OptionalHeader.SizeOfStackCommit );
-    ok( image.SubSystemType == nt_header->OptionalHeader.Subsystem || broken(truncated),
-        "%u: SubSystemType wrong %08x / %08x\n", id,
-        image.SubSystemType, nt_header->OptionalHeader.Subsystem );
+    if (truncated)
+        ok( !image.SubSystemType || broken(truncated),
+            "%u: SubSystemType wrong %08x / 00000000\n", id, image.SubSystemType );
+    else
+        ok( image.SubSystemType == nt_header->OptionalHeader.Subsystem,
+            "%u: SubSystemType wrong %08x / %08x\n", id,
+            image.SubSystemType, nt_header->OptionalHeader.Subsystem );
     ok( image.SubsystemVersionLow == nt_header->OptionalHeader.MinorSubsystemVersion,
         "%u: SubsystemVersionLow wrong %04x / %04x\n", id,
         image.SubsystemVersionLow, nt_header->OptionalHeader.MinorSubsystemVersion );
@@ -1470,6 +1480,7 @@ static HANDLE attached_thread[MAX_COUNT];
 static DWORD attached_thread_count;
 HANDLE stop_event, event, mutex, semaphore, loader_lock_event, peb_lock_event, heap_lock_event, ack_event;
 static int test_dll_phase, inside_loader_lock, inside_peb_lock, inside_heap_lock;
+static LONG fls_callback_count;
 
 static DWORD WINAPI mutex_thread_proc(void *param)
 {
@@ -1552,9 +1563,18 @@ static DWORD WINAPI noop_thread_proc(void *param)
     return 195;
 }
 
+static VOID WINAPI fls_callback(PVOID lpFlsData)
+{
+    ok(lpFlsData == (void*) 0x31415, "lpFlsData is %p, expected %p\n", lpFlsData, (void*) 0x31415);
+    InterlockedIncrement(&fls_callback_count);
+}
+
 static BOOL WINAPI dll_entry_point(HINSTANCE hinst, DWORD reason, LPVOID param)
 {
     static LONG noop_thread_started;
+    static DWORD fls_index = FLS_OUT_OF_INDEXES;
+    static int fls_count = 0;
+    static int thread_detach_count = 0;
     DWORD ret;
 
     ok(!inside_loader_lock, "inside_loader_lock should not be set\n");
@@ -1568,6 +1588,23 @@ static BOOL WINAPI dll_entry_point(HINSTANCE hinst, DWORD reason, LPVOID param)
         ret = pRtlDllShutdownInProgress();
         ok(!ret, "RtlDllShutdownInProgress returned %d\n", ret);
 
+        /* Set up the FLS slot, if FLS is available */
+        if (pFlsGetValue)
+        {
+            void* value;
+            BOOL bret;
+            ret = pFlsAlloc(&fls_callback);
+            ok(ret != FLS_OUT_OF_INDEXES, "FlsAlloc returned %d\n", ret);
+            fls_index = ret;
+            SetLastError(0xdeadbeef);
+            value = pFlsGetValue(fls_index);
+            ok(!value, "FlsGetValue returned %p, expected NULL\n", value);
+            ok(GetLastError() == ERROR_SUCCESS, "FlsGetValue failed with error %u\n", GetLastError());
+            bret = pFlsSetValue(fls_index, (void*) 0x31415);
+            ok(bret, "FlsSetValue failed\n");
+            fls_count++;
+        }
+
         break;
     case DLL_PROCESS_DETACH:
     {
@@ -1621,6 +1658,43 @@ static BOOL WINAPI dll_entry_point(HINSTANCE hinst, DWORD reason, LPVOID param)
                 ok(!ret || broken(ret) /* before Vista */, "RtlDllShutdownInProgress returned %d\n", ret);
         }
 
+        /* In the case that the process is terminating, FLS slots should still be accessible, but
+         * the callback should be already run for this thread and the contents already NULL.
+         * Note that this is broken for Win2k3, which runs the callbacks *after* the DLL entry
+         * point has already run.
+         */
+        if (param && pFlsGetValue)
+        {
+            void* value;
+            SetLastError(0xdeadbeef);
+            value = pFlsGetValue(fls_index);
+            todo_wine
+            {
+                ok(broken(value == (void*) 0x31415) || /* Win2k3 */
+                   value == NULL, "FlsGetValue returned %p, expected NULL\n", value);
+            }
+            ok(GetLastError() == ERROR_SUCCESS, "FlsGetValue failed with error %u\n", GetLastError());
+            todo_wine
+            {
+                ok(broken(fls_callback_count == thread_detach_count) || /* Win2k3 */
+                   fls_callback_count == thread_detach_count + 1,
+                   "wrong FLS callback count %d, expected %d\n", fls_callback_count, thread_detach_count + 1);
+            }
+        }
+        if (pFlsFree)
+        {
+            BOOL ret;
+            /* Call FlsFree now and run the remaining callbacks from uncleanly terminated threads */
+            ret = pFlsFree(fls_index);
+            ok(ret, "FlsFree failed with error %u\n", GetLastError());
+            fls_index = FLS_OUT_OF_INDEXES;
+            todo_wine
+            {
+                ok(fls_callback_count == fls_count,
+                   "wrong FLS callback count %d, expected %d\n", fls_callback_count, fls_count);
+            }
+        }
+
         ok(attached_thread_count >= 2, "attached thread count should be >= 2\n");
 
         for (i = 0; i < attached_thread_count; i++)
@@ -1791,9 +1865,26 @@ todo_wine
                             0, TRUE, DUPLICATE_SAME_ACCESS);
             attached_thread_count++;
         }
+
+        /* Make sure the FLS slot is empty, if FLS is available */
+        if (pFlsGetValue)
+        {
+            void* value;
+            BOOL ret;
+            SetLastError(0xdeadbeef);
+            value = pFlsGetValue(fls_index);
+            ok(!value, "FlsGetValue returned %p, expected NULL\n", value);
+            todo_wine
+                ok(GetLastError() == ERROR_SUCCESS, "FlsGetValue failed with error %u\n", GetLastError());
+            ret = pFlsSetValue(fls_index, (void*) 0x31415);
+            ok(ret, "FlsSetValue failed\n");
+            fls_count++;
+        }
+
         break;
     case DLL_THREAD_DETACH:
         trace("dll: %p, DLL_THREAD_DETACH, %p\n", hinst, param);
+        thread_detach_count++;
 
         ret = pRtlDllShutdownInProgress();
         /* win7 doesn't allow creating a thread during process shutdown but
@@ -1805,6 +1896,23 @@ todo_wine
         else
             ok(!ret, "RtlDllShutdownInProgress returned %d\n", ret);
 
+        /* FLS data should already be destroyed, if FLS is available.
+         * Note that this is broken for Win2k3, which runs the callbacks *after* the DLL entry
+         * point has already run.
+         */
+        if (pFlsGetValue && fls_index != FLS_OUT_OF_INDEXES)
+        {
+            void* value;
+            SetLastError(0xdeadbeef);
+            value = pFlsGetValue(fls_index);
+            todo_wine
+            {
+                ok(broken(value == (void*) 0x31415) || /* Win2k3 */
+                   !value, "FlsGetValue returned %p, expected NULL\n", value);
+            }
+            ok(GetLastError() == ERROR_SUCCESS, "FlsGetValue failed with error %u\n", GetLastError());
+        }
+
         break;
     default:
         trace("dll: %p, %d, %p\n", hinst, reason, param);
@@ -2126,7 +2234,6 @@ static void test_ExitProcess(void)
     } section_data = { 0xb8, dll_entry_point, { 0xff,0xe0 } };
 #endif
 #include "poppack.h"
-    static const char filler[0x1000];
     DWORD dummy, file_align;
     HANDLE file, thread, process, hmap, hmap_dup;
     char temp_path[MAX_PATH], dll_name[MAX_PATH], cmdline[MAX_PATH * 2];
@@ -2603,7 +2710,7 @@ static PVOID WINAPI failuredllhook(ULONG ul, DELAYLOAD_INFO* pd)
 
         ok(!!pd->ThunkAddress, "no ThunkAddress supplied\n");
         if (pd->ThunkAddress)
-            ok(pd->ThunkAddress->u1.Ordinal == 0, "expected 0, got %x\n", (UINT)pd->ThunkAddress->u1.Ordinal);
+            ok(pd->ThunkAddress->u1.Ordinal, "no ThunkAddress value supplied\n");
 
         ok(!!pd->TargetDllName, "no TargetDllName supplied\n");
         if (pd->TargetDllName)
@@ -2628,7 +2735,6 @@ static void test_ResolveDelayLoadedAPI(void)
 {
     static const char test_dll[] = "secur32.dll";
     static const char test_func[] = "SealMessage";
-    static const char filler[0x1000];
     char temp_path[MAX_PATH];
     char dll_name[MAX_PATH];
     IMAGE_DELAYLOAD_DESCRIPTOR idd, *delaydir;
@@ -2722,7 +2828,8 @@ static void test_ResolveDelayLoadedAPI(void)
     /* sections */
     section.PointerToRawData = nt_header.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT].VirtualAddress;
     section.VirtualAddress = nt_header.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT].VirtualAddress;
-    section.Misc.VirtualSize = nt_header.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT].Size;
+    section.Misc.VirtualSize = 2 * sizeof(idd);
+    section.SizeOfRawData = section.Misc.VirtualSize;
     section.Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ;
     SetLastError(0xdeadbeef);
     ret = WriteFile(hfile, &section, sizeof(section), &dummy, NULL);
@@ -2734,18 +2841,14 @@ static void test_ResolveDelayLoadedAPI(void)
     section.Misc.VirtualSize = sizeof(test_dll) + sizeof(hint) + sizeof(test_func) + sizeof(HMODULE) +
                                2 * (i + 1) * sizeof(IMAGE_THUNK_DATA);
     ok(section.Misc.VirtualSize <= 0x1000, "Too much tests, add a new section!\n");
+    section.SizeOfRawData = section.Misc.VirtualSize;
     section.Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
     SetLastError(0xdeadbeef);
     ret = WriteFile(hfile, &section, sizeof(section), &dummy, NULL);
     ok(ret, "WriteFile error %d\n", GetLastError());
 
     /* fill up to delay data */
-    file_size = GetFileSize(hfile, NULL);
-    SetLastError(0xdeadbeef);
-    ret = WriteFile(hfile, filler,
-                    nt_header.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT].VirtualAddress - file_size,
-                    &dummy, NULL);
-    ok(ret, "WriteFile error %d\n", GetLastError());
+    SetFilePointer( hfile, nt_header.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT].VirtualAddress, NULL, SEEK_SET );
 
     /* delay data */
     idd.Attributes.AllAttributes = 1;
@@ -2766,10 +2869,7 @@ static void test_ResolveDelayLoadedAPI(void)
     ok(ret, "WriteFile error %d\n", GetLastError());
 
     /* fill up to extended delay data */
-    file_size = GetFileSize(hfile, NULL);
-    SetLastError(0xdeadbeef);
-    ret = WriteFile(hfile, filler, idd.DllNameRVA - file_size, &dummy, NULL);
-    ok(ret, "WriteFile error %d\n", GetLastError());
+    SetFilePointer( hfile, idd.DllNameRVA, NULL, SEEK_SET );
 
     /* extended delay data */
     SetLastError(0xdeadbeef);
@@ -2784,9 +2884,20 @@ static void test_ResolveDelayLoadedAPI(void)
     ret = WriteFile(hfile, test_func, sizeof(test_func), &dummy, NULL);
     ok(ret, "WriteFile error %d\n", GetLastError());
 
-    file_size = GetFileSize(hfile, NULL);
+    SetFilePointer( hfile, idd.ImportAddressTableRVA, NULL, SEEK_SET );
+
+    for (i = 0; i < sizeof(td)/sizeof(td[0]); i++)
+    {
+        /* 0x1a00 is an empty space between delay data and extended delay data, real thunks are not necessary */
+        itd32.u1.Function = nt_header.OptionalHeader.ImageBase + 0x1a00 + i * 0x20;
+        SetLastError(0xdeadbeef);
+        ret = WriteFile(hfile, &itd32, sizeof(itd32), &dummy, NULL);
+        ok(ret, "WriteFile error %d\n", GetLastError());
+    }
+
+    itd32.u1.Function = 0;
     SetLastError(0xdeadbeef);
-    ret = WriteFile(hfile, filler, idd.ImportNameTableRVA - file_size, &dummy, NULL);
+    ret = WriteFile(hfile, &itd32, sizeof(itd32), &dummy, NULL);
     ok(ret, "WriteFile error %d\n", GetLastError());
 
     for (i = 0; i < sizeof(td)/sizeof(td[0]); i++)
@@ -2806,10 +2917,8 @@ static void test_ResolveDelayLoadedAPI(void)
     ok(ret, "WriteFile error %d\n", GetLastError());
 
     /* fill up to eof */
-    file_size = GetFileSize(hfile, NULL);
-    SetLastError(0xdeadbeef);
-    ret = WriteFile(hfile, filler, section.VirtualAddress + section.Misc.VirtualSize - file_size, &dummy, NULL);
-    ok(ret, "WriteFile error %d\n", GetLastError());
+    SetFilePointer( hfile, section.VirtualAddress + section.Misc.VirtualSize, NULL, SEEK_SET );
+    SetEndOfFile( hfile );
     CloseHandle(hfile);
 
     SetLastError(0xdeadbeef);
@@ -2902,10 +3011,11 @@ START_TEST(loader)
 {
     int argc;
     char **argv;
-    HANDLE ntdll, mapping;
+    HANDLE ntdll, mapping, kernel32;
     SYSTEM_INFO si;
 
     ntdll = GetModuleHandleA("ntdll.dll");
+    kernel32 = GetModuleHandleA("kernel32.dll");
     pNtCreateSection = (void *)GetProcAddress(ntdll, "NtCreateSection");
     pNtQuerySection = (void *)GetProcAddress(ntdll, "NtQuerySection");
     pNtMapViewOfSection = (void *)GetProcAddress(ntdll, "NtMapViewOfSection");
@@ -2922,7 +3032,11 @@ START_TEST(loader)
     pRtlAcquirePebLock = (void *)GetProcAddress(ntdll, "RtlAcquirePebLock");
     pRtlReleasePebLock = (void *)GetProcAddress(ntdll, "RtlReleasePebLock");
     pRtlImageDirectoryEntryToData = (void *)GetProcAddress(ntdll, "RtlImageDirectoryEntryToData");
-    pResolveDelayLoadedAPI = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "ResolveDelayLoadedAPI");
+    pFlsAlloc = (void *)GetProcAddress(kernel32, "FlsAlloc");
+    pFlsSetValue = (void *)GetProcAddress(kernel32, "FlsSetValue");
+    pFlsGetValue = (void *)GetProcAddress(kernel32, "FlsGetValue");
+    pFlsFree = (void *)GetProcAddress(kernel32, "FlsFree");
+    pResolveDelayLoadedAPI = (void *)GetProcAddress(kernel32, "ResolveDelayLoadedAPI");
 
     GetSystemInfo( &si );
     page_size = si.dwPageSize;
index 3760126..70a3d24 100755 (executable)
@@ -102,6 +102,7 @@ static BOOL (WINAPI *pEnumSystemGeoID)(GEOCLASS, GEOID, GEO_ENUMPROC);
 static BOOL (WINAPI *pGetSystemPreferredUILanguages)(DWORD, ULONG*, WCHAR*, ULONG*);
 static BOOL (WINAPI *pGetThreadPreferredUILanguages)(DWORD, ULONG*, WCHAR*, ULONG*);
 static WCHAR (WINAPI *pRtlUpcaseUnicodeChar)(WCHAR);
+static INT (WINAPI *pGetNumberFormatEx)(LPCWSTR, DWORD, LPCWSTR, const NUMBERFMTW *, LPWSTR, int);
 
 static void InitFunctionPointers(void)
 {
@@ -132,6 +133,7 @@ static void InitFunctionPointers(void)
   X(EnumSystemGeoID);
   X(GetSystemPreferredUILanguages);
   X(GetThreadPreferredUILanguages);
+  X(GetNumberFormatEx);
 
   mod = GetModuleHandleA("ntdll");
   X(RtlUpcaseUnicodeChar);
@@ -1593,6 +1595,188 @@ static void test_GetNumberFormatA(void)
   }
 }
 
+static void test_GetNumberFormatEx(void)
+{
+  int ret;
+  NUMBERFMTW format;
+  static WCHAR dotW[] = {'.',0};
+  static WCHAR commaW[] = {',',0};
+  static const WCHAR enW[] = {'e','n','-','U','S',0};
+  static const WCHAR frW[] = {'f','r','-','F','R',0};
+  static const WCHAR bogusW[] = {'b','o','g','u','s',0};
+  WCHAR buffer[BUFFER_SIZE], input[BUFFER_SIZE], Expected[BUFFER_SIZE];
+
+  if (!pGetNumberFormatEx)
+  {
+    win_skip("GetNumberFormatEx is not available.\n");
+    return;
+  }
+
+  STRINGSW("23",""); /* NULL output, length > 0 --> Error */
+  ret = pGetNumberFormatEx(enW, 0, input, NULL, NULL, COUNTOF(buffer));
+  ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
+      "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+
+  STRINGSW("23,53",""); /* Invalid character --> Error */
+  ret = pGetNumberFormatEx(enW, 0, input, NULL, buffer, COUNTOF(buffer));
+  ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
+      "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+
+  STRINGSW("--",""); /* Double '-' --> Error */
+  ret = pGetNumberFormatEx(enW, 0, input, NULL, buffer, COUNTOF(buffer));
+  ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
+      "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+
+  STRINGSW("0-",""); /* Trailing '-' --> Error */
+  ret = pGetNumberFormatEx(enW, 0, input, NULL, buffer, COUNTOF(buffer));
+  ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
+      "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+
+  STRINGSW("0..",""); /* Double '.' --> Error */
+  ret = pGetNumberFormatEx(enW, 0, input, NULL, buffer, COUNTOF(buffer));
+  ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
+      "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+
+  STRINGSW(" 0.1",""); /* Leading space --> Error */
+  ret = pGetNumberFormatEx(enW, 0, input, NULL, buffer, COUNTOF(buffer));
+  ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
+      "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+
+  STRINGSW("1234","1"); /* Length too small --> Write up to length chars */
+  ret = pGetNumberFormatEx(enW, NUO, input, NULL, buffer, 2);
+  ok( !ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
+      "Expected ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
+
+  STRINGSW("23",""); /* Bogus locale --> Error */
+  ret = pGetNumberFormatEx(bogusW, NUO, input, NULL, buffer, COUNTOF(buffer));
+  ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
+      "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+
+  memset(&format, 0, sizeof(format));
+
+  STRINGSW("2353",""); /* Format and flags given --> Error */
+  ret = pGetNumberFormatEx(enW, NUO, input, &format, buffer, COUNTOF(buffer));
+  ok( !ret, "Expected ret == 0, got %d\n", ret);
+  ok( GetLastError() == ERROR_INVALID_FLAGS || GetLastError() == ERROR_INVALID_PARAMETER,
+      "Expected ERROR_INVALID_FLAGS, got %d\n", GetLastError());
+
+  STRINGSW("2353",""); /* Invalid format --> Error */
+  ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, COUNTOF(buffer));
+  ok( !ret && GetLastError() == ERROR_INVALID_PARAMETER,
+      "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+
+  STRINGSW("2353","2,353.00"); /* Valid number */
+  ret = pGetNumberFormatEx(enW, NUO, input, NULL, buffer, COUNTOF(buffer));
+  ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
+  EXPECT_LENW; EXPECT_EQW;
+
+  STRINGSW("-2353","-2,353.00"); /* Valid negative number */
+  ret = pGetNumberFormatEx(enW, NUO, input, NULL, buffer, COUNTOF(buffer));
+  ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
+  EXPECT_LENW; EXPECT_EQW;
+
+  STRINGSW("-353","-353.00"); /* test for off by one error in grouping */
+  ret = pGetNumberFormatEx(enW, NUO, input, NULL, buffer, COUNTOF(buffer));
+  ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
+  EXPECT_LENW; EXPECT_EQW;
+
+  STRINGSW("2353.1","2,353.10"); /* Valid real number */
+  ret = pGetNumberFormatEx(enW, NUO, input, NULL, buffer, COUNTOF(buffer));
+  ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
+  EXPECT_LENW; EXPECT_EQW;
+
+  STRINGSW("2353.111","2,353.11"); /* Too many DP --> Truncated */
+  ret = pGetNumberFormatEx(enW, NUO, input, NULL, buffer, COUNTOF(buffer));
+  ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
+  EXPECT_LENW; EXPECT_EQW;
+
+  STRINGSW("2353.119","2,353.12");  /* Too many DP --> Rounded */
+  ret = pGetNumberFormatEx(enW, NUO, input, NULL, buffer, COUNTOF(buffer));
+  ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
+  EXPECT_LENW; EXPECT_EQW;
+
+  format.NumDigits = 0; /* No decimal separator */
+  format.LeadingZero = 0;
+  format.Grouping = 0;  /* No grouping char */
+  format.NegativeOrder = 0;
+  format.lpDecimalSep = dotW;
+  format.lpThousandSep = commaW;
+
+  STRINGSW("2353","2353"); /* No decimal or grouping chars expected */
+  ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, COUNTOF(buffer));
+  ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
+  EXPECT_LENW; EXPECT_EQW;
+
+  format.NumDigits = 1; /* 1 DP --> Expect decimal separator */
+  STRINGSW("2353","2353.0");
+  ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, COUNTOF(buffer));
+  ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
+  EXPECT_LENW; EXPECT_EQW;
+
+  format.Grouping = 2; /* Group by 100's */
+  STRINGSW("2353","23,53.0");
+  ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, COUNTOF(buffer));
+  ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
+  EXPECT_LENW; EXPECT_EQW;
+
+  STRINGSW("235","235.0"); /* Grouping of a positive number */
+  format.Grouping = 3;
+  ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, COUNTOF(buffer));
+  ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
+  EXPECT_LENW; EXPECT_EQW;
+
+  STRINGSW("-235","-235.0"); /* Grouping of a negative number */
+  format.NegativeOrder = NEG_LEFT;
+  ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, COUNTOF(buffer));
+  ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
+  EXPECT_LENW; EXPECT_EQW;
+
+  format.LeadingZero = 1; /* Always provide leading zero */
+  STRINGSW(".5","0.5");
+  ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, COUNTOF(buffer));
+  ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
+  EXPECT_LENW; EXPECT_EQW;
+
+  format.NegativeOrder = NEG_PARENS;
+  STRINGSW("-1","(1.0)");
+  ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, COUNTOF(buffer));
+  ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
+  EXPECT_LENW; EXPECT_EQW;
+
+  format.NegativeOrder = NEG_LEFT;
+  STRINGSW("-1","-1.0");
+  ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, COUNTOF(buffer));
+  ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
+  EXPECT_LENW; EXPECT_EQW;
+
+  format.NegativeOrder = NEG_LEFT_SPACE;
+  STRINGSW("-1","- 1.0");
+  ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, COUNTOF(buffer));
+  ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
+  EXPECT_LENW; EXPECT_EQW;
+
+  format.NegativeOrder = NEG_RIGHT;
+  STRINGSW("-1","1.0-");
+  ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, COUNTOF(buffer));
+  ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
+  EXPECT_LENW; EXPECT_EQW;
+
+  format.NegativeOrder = NEG_RIGHT_SPACE;
+  STRINGSW("-1","1.0 -");
+  ret = pGetNumberFormatEx(enW, 0, input, &format, buffer, COUNTOF(buffer));
+  ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
+  EXPECT_LENW; EXPECT_EQW;
+
+  if (pIsValidLocaleName(frW))
+  {
+    STRINGSW("-12345","-12 345,00"); /* Try French formatting */
+    Expected[3] = 160; /* Non breaking space */
+    ret = pGetNumberFormatEx(frW, NUO, input, NULL, buffer, COUNTOF(buffer));
+    ok(ret, "Expected ret != 0, got %d, error %d\n", ret, GetLastError());
+    EXPECT_LENW; EXPECT_EQW;
+  }
+}
+
 struct comparestringa_entry {
   LCID lcid;
   DWORD flags;
@@ -4102,7 +4286,7 @@ static void test_IdnToUnicode(void)
 static void test_GetLocaleInfoEx(void)
 {
     static const WCHAR enW[] = {'e','n',0};
-    WCHAR bufferW[80];
+    WCHAR bufferW[80], buffer2[80];
     INT ret;
 
     if (!pGetLocaleInfoEx)
@@ -4173,6 +4357,12 @@ static void test_GetLocaleInfoEx(void)
             ok(!lstrcmpW(bufferW, ptr->name), "%s: got wrong LOCALE_SNAME %s\n", wine_dbgstr_w(ptr->name), wine_dbgstr_w(bufferW));
             ptr++;
         }
+
+        ret = pGetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, LOCALE_SNAME, bufferW, sizeof(bufferW)/sizeof(WCHAR));
+        ok(ret && ret == lstrlenW(bufferW)+1, "got ret value %d\n", ret);
+        ret = GetLocaleInfoW(GetUserDefaultLCID(), LOCALE_SNAME, buffer2, sizeof(buffer2)/sizeof(WCHAR));
+        ok(ret && ret == lstrlenW(buffer2)+1, "got ret value %d\n", ret);
+        ok(!lstrcmpW(bufferW, buffer2), "LOCALE_SNAMEs don't match %s %s\n", wine_dbgstr_w(bufferW), wine_dbgstr_w(buffer2));
     }
 }
 
@@ -4853,6 +5043,7 @@ START_TEST(locale)
   test_GetDateFormatW();
   test_GetCurrencyFormatA(); /* Also tests the W version */
   test_GetNumberFormatA();   /* Also tests the W version */
+  test_GetNumberFormatEx();
   test_CompareStringA();
   test_CompareStringW();
   test_CompareStringEx();
index 4d23fbc..ff6480c 100755 (executable)
@@ -187,15 +187,8 @@ static void test_ValidPathA(const CHAR *curdir, const CHAR *subdir, const CHAR *
     len=pGetLongPathNameA(fullpath,tmpstr,MAX_PATH);
     if(passfail==NULL) {
       ok(len, "%s: GetLongPathNameA failed\n",errstr);
-      if(HAS_TRAIL_SLASH_A(fullpath)) {
-        ok(lstrcmpiA(fullpathlong,tmpstr)==0,
-           "%s: GetLongPathNameA returned '%s' instead of '%s'\n",
-           errstr,tmpstr,fullpathlong);
-      } else {
-        ok(lstrcmpiA(fullpathlong,tmpstr)==0,
-          "%s: GetLongPathNameA returned '%s' instead of '%s'\n",
-          errstr,tmpstr,fullpathlong);
-      }
+      ok(!lstrcmpiA(fullpathlong, tmpstr), "%s: GetLongPathNameA returned '%s' instead of '%s'\n",
+         errstr, tmpstr, fullpathlong);
     } else {
       passfail->longlen=len;
       passfail->longerror=GetLastError();
@@ -952,6 +945,28 @@ static void test_PathNameA(CHAR *curdir, CHAR curDrive, CHAR otherDrive)
   test_ShortPathCase(curdir,SHORTDIR,LONGFILE);
   test_ShortPathCase(curdir,LONGDIR,SHORTFILE);
   test_ShortPathCase(curdir,LONGDIR,LONGFILE);
+
+  /* test double delimiters */
+  sprintf(tmpstr,"%s\\\\%s", SHORTDIR,SHORTFILE);
+  ok(GetShortPathNameA(tmpstr,tmpstr1,MAX_PATH),"GetShortPathNameA failed\n");
+  ok(lstrcmpiA(tmpstr,tmpstr1)==0,
+       "GetShortPathNameA returned '%s' instead of '%s'\n",tmpstr1,tmpstr);
+  sprintf(tmpstr,".\\\\%s\\\\%s", SHORTDIR,SHORTFILE);
+  ok(GetShortPathNameA(tmpstr,tmpstr1,MAX_PATH),"GetShortPathNameA failed\n");
+  ok(lstrcmpiA(tmpstr,tmpstr1)==0,
+       "GetShortPathNameA returned '%s' instead of '%s'\n",tmpstr1,tmpstr);
+
+  if (pGetLongPathNameA) {
+    sprintf(tmpstr,"%s\\\\%s",LONGDIR,LONGFILE);
+    ok(pGetLongPathNameA(tmpstr,tmpstr1,MAX_PATH),"GetLongPathNameA failed\n");
+    ok(lstrcmpiA(tmpstr,tmpstr1)==0,
+        "GetLongPathNameA returned '%s' instead of '%s'\n",tmpstr1,tmpstr);
+
+    sprintf(tmpstr,".\\\\%s\\\\%s",LONGDIR,LONGFILE);
+    ok(pGetLongPathNameA(tmpstr,tmpstr1,MAX_PATH),"GetLongPathNameA failed\n");
+    ok(lstrcmpiA(tmpstr,tmpstr1)==0,
+       "GetLongPathNameA returned '%s' instead of '%s'\n",tmpstr1,tmpstr);
+  }
 }
 
 static void test_GetTempPathA(char* tmp_dir)
@@ -2216,6 +2231,16 @@ static void test_relative_path(void)
     ok(ret, "GetShortPathName error %d\n", GetLastError());
     ok(!strcmp(buf, ".\\..\\foo\\file"), "expected .\\..\\foo\\file, got %s\n", buf);
 
+    /* test double delimiters */
+    strcpy(buf, "deadbeef");
+    ret = pGetLongPathNameA("..\\\\foo\\file", buf, MAX_PATH);
+    ok(ret, "GetLongPathName error %d\n", GetLastError());
+    ok(!strcmp(buf, "..\\\\foo\\file"), "expected ..\\\\foo\\file, got %s\n", buf);
+    strcpy(buf, "deadbeef");
+    ret = GetShortPathNameA("..\\\\foo\\file", buf, MAX_PATH);
+    ok(ret, "GetShortPathName error %d\n", GetLastError());
+    ok(!strcmp(buf, "..\\\\foo\\file"), "expected ..\\\\foo\\file, got %s\n", buf);
+
     SetCurrentDirectoryA("..");
     DeleteFileA("foo\\file");
     RemoveDirectoryA("foo");
index 7e82364..cb3a67c 100755 (executable)
@@ -1318,33 +1318,64 @@ static void test_RegisterWaitForSingleObject(void)
 
     ret = pUnregisterWait(wait_handle);
     ok(ret, "UnregisterWait failed with error %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = pUnregisterWait(NULL);
+    ok(!ret, "Expected UnregisterWait to fail\n");
+    ok(GetLastError() == ERROR_INVALID_HANDLE,
+       "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError());
 }
 
-static DWORD TLS_main;
-static DWORD TLS_index0, TLS_index1;
+static DWORD LS_main;
+static DWORD LS_index0, LS_index1;
+static DWORD LS_OutOfIndexesValue;
+
+/* Function pointers to the FLS/TLS functions to test in LS_ThreadProc() */
+static DWORD (WINAPI *LS_AllocFunc)(void);
+static PVOID (WINAPI *LS_GetValueFunc)(DWORD);
+static BOOL (WINAPI *LS_SetValueFunc)(DWORD, PVOID);
+static BOOL (WINAPI *LS_FreeFunc)(DWORD);
+
+/* Names of the functions tested in LS_ThreadProc(), for error messages */
+static const char* LS_AllocFuncName = "";
+static const char* LS_GetValueFuncName = "";
+static const char* LS_SetValueFuncName = "";
+static const char* LS_FreeFuncName = "";
+
+/* FLS entry points, dynamically loaded in platforms that support them */
+static DWORD (WINAPI *pFlsAlloc)(PFLS_CALLBACK_FUNCTION);
+static BOOL (WINAPI *pFlsFree)(DWORD);
+static PVOID (WINAPI *pFlsGetValue)(DWORD);
+static BOOL (WINAPI *pFlsSetValue)(DWORD,PVOID);
+
+/* A thunk function to make FlsAlloc compatible with the signature of TlsAlloc */
+static DWORD WINAPI FLS_AllocFuncThunk(void)
+{
+  return pFlsAlloc(NULL);
+}
 
-static DWORD WINAPI TLS_InheritanceProc(LPVOID p)
+static DWORD WINAPI LS_InheritanceProc(LPVOID p)
 {
-  /* We should NOT inherit the TLS values from our parent or from the
+  /* We should NOT inherit the FLS/TLS values from our parent or from the
      main thread.  */
   LPVOID val;
 
-  val = TlsGetValue(TLS_main);
-  ok(val == NULL, "TLS inheritance failed\n");
+  val = LS_GetValueFunc(LS_main);
+  ok(val == NULL, "%s inheritance failed\n", LS_GetValueFuncName);
 
-  val = TlsGetValue(TLS_index0);
-  ok(val == NULL, "TLS inheritance failed\n");
+  val = LS_GetValueFunc(LS_index0);
+  ok(val == NULL, "%s inheritance failed\n", LS_GetValueFuncName);
 
-  val = TlsGetValue(TLS_index1);
-  ok(val == NULL, "TLS inheritance failed\n");
+  val = LS_GetValueFunc(LS_index1);
+  ok(val == NULL, "%s inheritance failed\n", LS_GetValueFuncName);
 
   return 0;
 }
 
-/* Basic TLS usage test.  Make sure we can create slots and the values we
-   store in them are separate among threads.  Also test TLS value
-   inheritance with TLS_InheritanceProc.  */
-static DWORD WINAPI TLS_ThreadProc(LPVOID p)
+/* Basic FLS/TLS usage test.  Make sure we can create slots and the values we
+   store in them are separate among threads.  Also test FLS/TLS value
+   inheritance with LS_InheritanceProc.  */
+static DWORD WINAPI LS_ThreadProc(LPVOID p)
 {
   LONG_PTR id = (LONG_PTR) p;
   LPVOID val;
@@ -1352,80 +1383,80 @@ static DWORD WINAPI TLS_ThreadProc(LPVOID p)
 
   if (sync_threads_and_run_one(0, id))
   {
-    TLS_index0 = TlsAlloc();
-    ok(TLS_index0 != TLS_OUT_OF_INDEXES, "TlsAlloc failed\n");
+    LS_index0 = LS_AllocFunc();
+    ok(LS_index0 != LS_OutOfIndexesValue, "%s failed\n", LS_AllocFuncName);
   }
   resync_after_run();
 
   if (sync_threads_and_run_one(1, id))
   {
-    TLS_index1 = TlsAlloc();
-    ok(TLS_index1 != TLS_OUT_OF_INDEXES, "TlsAlloc failed\n");
+    LS_index1 = LS_AllocFunc();
+    ok(LS_index1 != LS_OutOfIndexesValue, "%s failed\n", LS_AllocFuncName);
 
     /* Slot indices should be different even if created in different
        threads.  */
-    ok(TLS_index0 != TLS_index1, "TlsAlloc failed\n");
+    ok(LS_index0 != LS_index1, "%s failed\n", LS_AllocFuncName);
 
     /* Both slots should be initialized to NULL */
-    val = TlsGetValue(TLS_index0);
-    ok(GetLastError() == ERROR_SUCCESS, "TlsGetValue failed\n");
-    ok(val == NULL, "TLS slot not initialized correctly\n");
+    val = LS_GetValueFunc(LS_index0);
+    ok(GetLastError() == ERROR_SUCCESS, "%s failed\n", LS_GetValueFuncName);
+    ok(val == NULL, "Slot not initialized correctly\n");
 
-    val = TlsGetValue(TLS_index1);
-    ok(GetLastError() == ERROR_SUCCESS, "TlsGetValue failed\n");
-    ok(val == NULL, "TLS slot not initialized correctly\n");
+    val = LS_GetValueFunc(LS_index1);
+    ok(GetLastError() == ERROR_SUCCESS, "%s failed\n", LS_GetValueFuncName);
+    ok(val == NULL, "Slot not initialized correctly\n");
   }
   resync_after_run();
 
   if (sync_threads_and_run_one(0, id))
   {
-    val = TlsGetValue(TLS_index0);
-    ok(GetLastError() == ERROR_SUCCESS, "TlsGetValue failed\n");
-    ok(val == NULL, "TLS slot not initialized correctly\n");
+    val = LS_GetValueFunc(LS_index0);
+    ok(GetLastError() == ERROR_SUCCESS, "%s failed\n", LS_GetValueFuncName);
+    ok(val == NULL, "Slot not initialized correctly\n");
 
-    val = TlsGetValue(TLS_index1);
-    ok(GetLastError() == ERROR_SUCCESS, "TlsGetValue failed\n");
-    ok(val == NULL, "TLS slot not initialized correctly\n");
+    val = LS_GetValueFunc(LS_index1);
+    ok(GetLastError() == ERROR_SUCCESS, "%s failed\n", LS_GetValueFuncName);
+    ok(val == NULL, "Slot not initialized correctly\n");
 
-    ret = TlsSetValue(TLS_index0, (LPVOID) 1);
-    ok(ret, "TlsSetValue failed\n");
+    ret = LS_SetValueFunc(LS_index0, (LPVOID) 1);
+    ok(ret, "%s failed\n", LS_SetValueFuncName);
 
-    ret = TlsSetValue(TLS_index1, (LPVOID) 2);
-    ok(ret, "TlsSetValue failed\n");
+    ret = LS_SetValueFunc(LS_index1, (LPVOID) 2);
+    ok(ret, "%s failed\n", LS_SetValueFuncName);
 
-    val = TlsGetValue(TLS_index0);
-    ok(GetLastError() == ERROR_SUCCESS, "TlsGetValue failed\n");
-    ok(val == (LPVOID) 1, "TLS slot not initialized correctly\n");
+    val = LS_GetValueFunc(LS_index0);
+    ok(GetLastError() == ERROR_SUCCESS, "%s failed\n", LS_GetValueFuncName);
+    ok(val == (LPVOID) 1, "Slot not initialized correctly\n");
 
-    val = TlsGetValue(TLS_index1);
-    ok(GetLastError() == ERROR_SUCCESS, "TlsGetValue failed\n");
-    ok(val == (LPVOID) 2, "TLS slot not initialized correctly\n");
+    val = LS_GetValueFunc(LS_index1);
+    ok(GetLastError() == ERROR_SUCCESS, "%s failed\n", LS_GetValueFuncName);
+    ok(val == (LPVOID) 2, "Slot not initialized correctly\n");
   }
   resync_after_run();
 
   if (sync_threads_and_run_one(1, id))
   {
-    val = TlsGetValue(TLS_index0);
-    ok(GetLastError() == ERROR_SUCCESS, "TlsGetValue failed\n");
-    ok(val == NULL, "TLS slot not initialized correctly\n");
+    val = LS_GetValueFunc(LS_index0);
+    ok(GetLastError() == ERROR_SUCCESS, "%s failed\n", LS_GetValueFuncName);
+    ok(val == NULL, "Slot not initialized correctly\n");
 
-    val = TlsGetValue(TLS_index1);
-    ok(GetLastError() == ERROR_SUCCESS, "TlsGetValue failed\n");
-    ok(val == NULL, "TLS slot not initialized correctly\n");
+    val = LS_GetValueFunc(LS_index1);
+    ok(GetLastError() == ERROR_SUCCESS, "%s failed\n", LS_GetValueFuncName);
+    ok(val == NULL, "Slot not initialized correctly\n");
 
-    ret = TlsSetValue(TLS_index0, (LPVOID) 3);
-    ok(ret, "TlsSetValue failed\n");
+    ret = LS_SetValueFunc(LS_index0, (LPVOID) 3);
+    ok(ret, "%s failed\n", LS_SetValueFuncName);
 
-    ret = TlsSetValue(TLS_index1, (LPVOID) 4);
-    ok(ret, "TlsSetValue failed\n");
+    ret = LS_SetValueFunc(LS_index1, (LPVOID) 4);
+    ok(ret, "%s failed\n", LS_SetValueFuncName);
 
-    val = TlsGetValue(TLS_index0);
-    ok(GetLastError() == ERROR_SUCCESS, "TlsGetValue failed\n");
-    ok(val == (LPVOID) 3, "TLS slot not initialized correctly\n");
+    val = LS_GetValueFunc(LS_index0);
+    ok(GetLastError() == ERROR_SUCCESS, "%s failed\n", LS_GetValueFuncName);
+    ok(val == (LPVOID) 3, "Slot not initialized correctly\n");
 
-    val = TlsGetValue(TLS_index1);
-    ok(GetLastError() == ERROR_SUCCESS, "TlsGetValue failed\n");
-    ok(val == (LPVOID) 4, "TLS slot not initialized correctly\n");
+    val = LS_GetValueFunc(LS_index1);
+    ok(GetLastError() == ERROR_SUCCESS, "%s failed\n", LS_GetValueFuncName);
+    ok(val == (LPVOID) 4, "Slot not initialized correctly\n");
   }
   resync_after_run();
 
@@ -1434,36 +1465,36 @@ static DWORD WINAPI TLS_ThreadProc(LPVOID p)
     HANDLE thread;
     DWORD waitret, tid;
 
-    val = TlsGetValue(TLS_index0);
-    ok(GetLastError() == ERROR_SUCCESS, "TlsGetValue failed\n");
-    ok(val == (LPVOID) 1, "TLS slot not initialized correctly\n");
+    val = LS_GetValueFunc(LS_index0);
+    ok(GetLastError() == ERROR_SUCCESS, "%s failed\n", LS_GetValueFuncName);
+    ok(val == (LPVOID) 1, "Slot not initialized correctly\n");
 
-    val = TlsGetValue(TLS_index1);
-    ok(GetLastError() == ERROR_SUCCESS, "TlsGetValue failed\n");
-    ok(val == (LPVOID) 2, "TLS slot not initialized correctly\n");
+    val = LS_GetValueFunc(LS_index1);
+    ok(GetLastError() == ERROR_SUCCESS, "%s failed\n", LS_GetValueFuncName);
+    ok(val == (LPVOID) 2, "Slot not initialized correctly\n");
 
-    thread = CreateThread(NULL, 0, TLS_InheritanceProc, 0, 0, &tid);
+    thread = CreateThread(NULL, 0, LS_InheritanceProc, 0, 0, &tid);
     ok(thread != NULL, "CreateThread failed\n");
     waitret = WaitForSingleObject(thread, 60000);
     ok(waitret == WAIT_OBJECT_0, "WaitForSingleObject failed\n");
     CloseHandle(thread);
 
-    ret = TlsFree(TLS_index0);
-    ok(ret, "TlsFree failed\n");
+    ret = LS_FreeFunc(LS_index0);
+    ok(ret, "%s failed\n", LS_FreeFuncName);
   }
   resync_after_run();
 
   if (sync_threads_and_run_one(1, id))
   {
-    ret = TlsFree(TLS_index1);
-    ok(ret, "TlsFree failed\n");
+    ret = LS_FreeFunc(LS_index1);
+    ok(ret, "%s failed\n", LS_FreeFuncName);
   }
   resync_after_run();
 
   return 0;
 }
 
-static void test_TLS(void)
+static void run_LS_tests(void)
 {
   HANDLE threads[2];
   LONG_PTR i;
@@ -1472,17 +1503,17 @@ static void test_TLS(void)
 
   init_thread_sync_helpers();
 
-  /* Allocate a TLS slot in the main thread to test for inheritance.  */
-  TLS_main = TlsAlloc();
-  ok(TLS_main != TLS_OUT_OF_INDEXES, "TlsAlloc failed\n");
-  suc = TlsSetValue(TLS_main, (LPVOID) 4114);
-  ok(suc, "TlsSetValue failed\n");
+  /* Allocate a slot in the main thread to test for inheritance.  */
+  LS_main = LS_AllocFunc();
+  ok(LS_main != LS_OutOfIndexesValue, "%s failed\n", LS_AllocFuncName);
+  suc = LS_SetValueFunc(LS_main, (LPVOID) 4114);
+  ok(suc, "%s failed\n", LS_SetValueFuncName);
 
   for (i = 0; i < 2; ++i)
   {
     DWORD tid;
 
-    threads[i] = CreateThread(NULL, 0, TLS_ThreadProc, (LPVOID) i, 0, &tid);
+    threads[i] = CreateThread(NULL, 0, LS_ThreadProc, (LPVOID) i, 0, &tid);
     ok(threads[i] != NULL, "CreateThread failed\n");
   }
 
@@ -1492,11 +1523,51 @@ static void test_TLS(void)
   for (i = 0; i < 2; ++i)
     CloseHandle(threads[i]);
 
-  suc = TlsFree(TLS_main);
-  ok(suc, "TlsFree failed\n");
+  suc = LS_FreeFunc(LS_main);
+  ok(suc, "%s failed\n", LS_FreeFuncName);
   cleanup_thread_sync_helpers();
 }
 
+static void test_TLS(void)
+{
+  LS_OutOfIndexesValue = TLS_OUT_OF_INDEXES;
+
+  LS_AllocFunc = &TlsAlloc;
+  LS_GetValueFunc = &TlsGetValue;
+  LS_SetValueFunc = &TlsSetValue;
+  LS_FreeFunc = &TlsFree;
+
+  LS_AllocFuncName = "TlsAlloc";
+  LS_GetValueFuncName = "TlsGetValue";
+  LS_SetValueFuncName = "TlsSetValue";
+  LS_FreeFuncName = "TlsFree";
+
+  run_LS_tests();
+}
+
+static void test_FLS(void)
+{
+  if (!pFlsAlloc || !pFlsFree || !pFlsGetValue || !pFlsSetValue)
+  {
+     win_skip("Fiber Local Storage not supported\n");
+     return;
+  }
+
+  LS_OutOfIndexesValue = FLS_OUT_OF_INDEXES;
+
+  LS_AllocFunc = &FLS_AllocFuncThunk;
+  LS_GetValueFunc = pFlsGetValue;
+  LS_SetValueFunc = pFlsSetValue;
+  LS_FreeFunc = pFlsFree;
+
+  LS_AllocFuncName = "FlsAlloc";
+  LS_GetValueFuncName = "FlsGetValue";
+  LS_SetValueFuncName = "FlsSetValue";
+  LS_FreeFuncName = "FlsFree";
+
+  run_LS_tests();
+}
+
 static void test_ThreadErrorMode(void)
 {
     DWORD oldmode;
@@ -2018,6 +2089,11 @@ static void init_funcs(void)
 
     X(GetThreadGroupAffinity);
     X(SetThreadGroupAffinity);
+
+    X(FlsAlloc);
+    X(FlsFree);
+    X(FlsSetValue);
+    X(FlsGetValue);
 #undef X
 
 #define X(f) p##f = (void*)GetProcAddress(ntdll, #f)
@@ -2085,6 +2161,7 @@ START_TEST(thread)
    test_QueueUserWorkItem();
    test_RegisterWaitForSingleObject();
    test_TLS();
+   test_FLS();
    test_ThreadErrorMode();
 #if (defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))) || (defined(_MSC_VER) && defined(__i386__))
    test_thread_fpu_cw();
index 504823e..37dfb66 100755 (executable)
@@ -2799,10 +2799,7 @@ static void test_atl_thunk_emulation( ULONG dep_flags )
     ret = send_message_excpt( hWnd, WM_USER, 0, 0 );
     ok( ret == 43, "call returned wrong result, expected 43, got %d\n", ret );
     ok( num_guard_page_calls == 0, "expected no STATUS_GUARD_PAGE_VIOLATION exception, got %d exceptions\n", num_guard_page_calls );
-    if ((dep_flags & MEM_EXECUTE_OPTION_DISABLE) && (dep_flags & MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION))
-        ok( num_execute_fault_calls == 0, "expected no STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls );
-    else
-        ok( num_execute_fault_calls == 0, "expected no STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls );
+    ok( num_execute_fault_calls == 0, "expected no STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls );
 
     /* Now a bit more complicated, the page containing the code is protected with
      * PAGE_GUARD memory protection. */
@@ -4109,14 +4106,7 @@ START_TEST(virtual)
         }
         if (!strcmp(argv[2], "sharedmemro"))
         {
-            if(!winetest_interactive)
-            {
-                skip("CORE-8541: Skipping test_shared_memory_ro(TRUE, strtol(argv[3], NULL, 16))\n");
-            }
-            else
-            {
-                test_shared_memory_ro(TRUE, strtol(argv[3], NULL, 16));
-            }
+            test_shared_memory_ro(TRUE, strtol(argv[3], NULL, 16));
             return;
         }
         while (1)