[SDK][SDK_APITEST] Improve delayload support CORE-10935
authorMark Jansen <mark.jansen@reactos.org>
Sat, 23 Apr 2016 20:38:06 +0000 (20:38 +0000)
committerMark Jansen <mark.jansen@reactos.org>
Sat, 23 Apr 2016 20:38:06 +0000 (20:38 +0000)
- Add an apitest for our delayload functions.
- Improve the delayload (mainly the failure cases, throw exceptions where expected etc).
- Add some tests for advanced delayload features (currently blocked on CORE-6504 and CORE-10957)

svn path=/trunk/; revision=71190

reactos/sdk/include/psdk/delayimp.h
reactos/sdk/lib/delayimp/CMakeLists.txt
reactos/sdk/lib/delayimp/delayimp.c
rostests/apitests/CMakeLists.txt
rostests/apitests/sdk/CMakeLists.txt [new file with mode: 0644]
rostests/apitests/sdk/delayimp.cpp [new file with mode: 0644]
rostests/apitests/sdk/testlist.c [new file with mode: 0644]

index b0ccdb4..da87b53 100644 (file)
@@ -6,6 +6,17 @@
 #define __ImageBase _image_base__
 #endif
 
+#if defined(__cplusplus)
+#define ExternC extern "C"
+#else
+#define ExternC extern
+#endif
+
+#ifndef FACILITY_VISUALCPP
+#define FACILITY_VISUALCPP  ((LONG)0x6d)
+#endif
+#define VcppException(sev,err)  ((sev) | (FACILITY_VISUALCPP<<16) | err)
+
 #define DELAYLOAD_VERSION 0x200
 
 typedef DWORD RVA;
@@ -65,24 +76,8 @@ typedef struct DelayLoadInfo
 
 typedef FARPROC (WINAPI *PfnDliHook)(unsigned, PDelayLoadInfo);
 
-FORCEINLINE
-unsigned
-IndexFromPImgThunkData(PCImgThunkData pData, PCImgThunkData pBase)
-{
-    return pData - pBase;
-}
-
-extern const IMAGE_DOS_HEADER __ImageBase;
-
-FORCEINLINE
-PVOID
-PFromRva(RVA rva)
-{
-    return (PVOID)(((ULONG_PTR)(rva)) + ((ULONG_PTR)&__ImageBase));
-}
-
 
-extern PfnDliHook __pfnDliNotifyHook2;
-extern PfnDliHook __pfnDliFailureHook2;
+ExternC PfnDliHook __pfnDliNotifyHook2;
+ExternC PfnDliHook __pfnDliFailureHook2;
 
 #endif /* not _delayimp_h */
index dadd06e..c2c1224 100644 (file)
@@ -2,3 +2,4 @@
 add_definitions(-DUNICODE -D_UNICODE)
 add_library(delayimp delayimp.c)
 add_dependencies(delayimp psdk)
+add_importlibs(delayimp kernel32)
index f53353a..f439a78 100644 (file)
@@ -4,6 +4,7 @@
  * FILE:            lib/sdk/delayimp/delayimp.c
  * PURPOSE:         Library for delay importing from dlls
  * PROGRAMMERS:     Timo Kreuzer <timo.kreuzer@reactos.org>
+ *                  Mark Jansen
  *
  */
 
 #include <winbase.h>
 #include <delayimp.h>
 
+/**** Linker magic: provide a default (NULL) pointer, but allow the user to override it ****/
+
+#if defined(__GNUC__)
+PfnDliHook __pfnDliNotifyHook2;
+PfnDliHook __pfnDliFailureHook2;
+#else
+/* The actual items we use */
+extern PfnDliHook __pfnDliNotifyHook2;
+extern PfnDliHook __pfnDliFailureHook2;
+
+/* The fallback symbols */
+extern PfnDliHook __pfnDliNotifyHook2Default = NULL;
+extern PfnDliHook __pfnDliFailureHook2Default = NULL;
+
+/* Tell the linker to use the fallback symbols */
+#pragma comment(linker, "/alternatename:___pfnDliNotifyHook2=___pfnDliNotifyHook2Default")
+#pragma comment(linker, "/alternatename:___pfnDliFailureHook2=___pfnDliFailureHook2Default")
+#endif
+
+
+/**** Helper functions to convert from RVA to address ****/
+
+FORCEINLINE
+unsigned
+IndexFromPImgThunkData(PCImgThunkData pData, PCImgThunkData pBase)
+{
+    return pData - pBase;
+}
+
+extern const IMAGE_DOS_HEADER __ImageBase;
+
+FORCEINLINE
+PVOID
+PFromRva(RVA rva)
+{
+    return (PVOID)(((ULONG_PTR)(rva)) + ((ULONG_PTR)&__ImageBase));
+}
+
+
 /**** load helper ****/
 
 FARPROC WINAPI
 __delayLoadHelper2(PCImgDelayDescr pidd, PImgThunkData pIATEntry)
 {
-    DelayLoadInfo dli;
+    DelayLoadInfo dli = {0};
     int index;
     PImgThunkData pIAT;
     PImgThunkData pINT;
     HMODULE *phMod;
-    FARPROC pProc;
 
     pIAT = PFromRva(pidd->rvaIAT);
     pINT = PFromRva(pidd->rvaINT);
@@ -31,9 +70,9 @@ __delayLoadHelper2(PCImgDelayDescr pidd, PImgThunkData pIATEntry)
 
     dli.cb = sizeof(dli);
     dli.pidd = pidd;
-    dli.ppfn = (FARPROC*)pIATEntry->u1.Function;
+    dli.ppfn = (FARPROC*)&pIAT[index].u1.Function;
     dli.szDll = PFromRva(pidd->rvaDLLName);
-    dli.dlp.fImportByName = !(pINT[index].u1.Ordinal & IMAGE_ORDINAL_FLAG);
+    dli.dlp.fImportByName = !IMAGE_SNAP_BY_ORDINAL(pINT[index].u1.Ordinal);
     if (dli.dlp.fImportByName)
     {
         /* u1.AdressOfData points to a IMAGE_IMPORT_BY_NAME struct */
@@ -42,61 +81,80 @@ __delayLoadHelper2(PCImgDelayDescr pidd, PImgThunkData pIATEntry)
     }
     else
     {
-        dli.dlp.dwOrdinal = pINT[index].u1.Ordinal & ~IMAGE_ORDINAL_FLAG;
+        dli.dlp.dwOrdinal = IMAGE_ORDINAL(pINT[index].u1.Ordinal);
     }
-    dli.hmodCur = *phMod;
-    dli.pfnCur = (FARPROC)pIAT[index].u1.Function;
-    dli.dwLastError = GetLastError();
-    pProc = __pfnDliNotifyHook2(dliStartProcessing, &dli);
-    if (pProc)
+
+    if (__pfnDliNotifyHook2)
     {
-        pIAT[index].u1.Function = (DWORD_PTR)pProc;
-        return pProc;
+        dli.pfnCur = __pfnDliNotifyHook2(dliStartProcessing, &dli);
+        if (dli.pfnCur)
+        {
+            pIAT[index].u1.Function = (DWORD_PTR)dli.pfnCur;
+            if (__pfnDliNotifyHook2)
+                __pfnDliNotifyHook2(dliNoteEndProcessing, &dli);
+
+            return dli.pfnCur;
+        }
     }
 
+    dli.hmodCur = *phMod;
+
     if (dli.hmodCur == NULL)
     {
-        dli.hmodCur = LoadLibraryA(dli.szDll);
-        if (!dli.hmodCur)
+        if (__pfnDliNotifyHook2)
+            dli.hmodCur = (HMODULE)__pfnDliNotifyHook2(dliNotePreLoadLibrary, &dli);
+        if (dli.hmodCur == NULL)
         {
-            dli.dwLastError = GetLastError();
-            __pfnDliFailureHook2(dliFailLoadLib, &dli);
-//            if (ret)
-//            {
-//            }
-            // FIXME: raise exception;
-            return NULL;
+            dli.hmodCur = LoadLibraryA(dli.szDll);
+            if (dli.hmodCur == NULL)
+            {
+                dli.dwLastError = GetLastError();
+                if (__pfnDliFailureHook2)
+                    dli.hmodCur = (HMODULE)__pfnDliFailureHook2(dliFailLoadLib, &dli);
+
+                if (dli.hmodCur == NULL)
+                {
+                    ULONG_PTR args[] = { (ULONG_PTR)&dli };
+                    RaiseException(VcppException(ERROR_SEVERITY_ERROR, ERROR_MOD_NOT_FOUND), 0, 1, args);
+
+                    /* If we survive the exception, we are expected to use pfnCur directly.. */
+                    return dli.pfnCur;
+                }
+            }
         }
         *phMod = dli.hmodCur;
     }
 
-    /* dli.dlp.szProcName might also contain the ordinal */
-    pProc = GetProcAddress(dli.hmodCur, dli.dlp.szProcName);
-    if (!pProc)
+    dli.dwLastError = ERROR_SUCCESS;
+
+    if (__pfnDliNotifyHook2)
+        dli.pfnCur = (FARPROC)__pfnDliNotifyHook2(dliNotePreGetProcAddress, &dli);
+    if (dli.pfnCur == NULL)
     {
-        dli.dwLastError = GetLastError();
-        __pfnDliFailureHook2(dliFailGetProc, &dli);
-        // FIXME: handle return value & raise exception
-        return NULL;
-    }
-    pIAT[index].u1.Function = (DWORD_PTR)pProc;
+        /* dli.dlp.szProcName might also contain the ordinal */
+        dli.pfnCur = GetProcAddress(dli.hmodCur, dli.dlp.szProcName);
+        if (dli.pfnCur == NULL)
+        {
+            dli.dwLastError = GetLastError();
+            if (__pfnDliFailureHook2)
+               dli.pfnCur = __pfnDliFailureHook2(dliFailGetProc, &dli);
 
-    return pProc;
-}
+            if (dli.pfnCur == NULL)
+            {
+                ULONG_PTR args[] = { (ULONG_PTR)&dli };
+                RaiseException(VcppException(ERROR_SEVERITY_ERROR, ERROR_PROC_NOT_FOUND), 0, 1, args);
+            }
 
-/*** The default hooks ***/
+            //return NULL;
+        }
+    }
 
-FARPROC WINAPI
-DefaultDliNotifyHook2(unsigned dliNotify, PDelayLoadInfo pdli)
-{
-    return NULL;
-}
+    pIAT[index].u1.Function = (DWORD_PTR)dli.pfnCur;
+    dli.dwLastError = ERROR_SUCCESS;
 
-FARPROC WINAPI
-DefaultDliFailureHook2(unsigned dliNotify, PDelayLoadInfo pdli)
-{
-    return NULL;
+    if (__pfnDliNotifyHook2)
+        __pfnDliNotifyHook2(dliNoteEndProcessing, &dli);
+
+    return dli.pfnCur;
 }
 
-PfnDliHook __pfnDliNotifyHook2 = DefaultDliNotifyHook2;
-PfnDliHook __pfnDliFailureHook2 = DefaultDliFailureHook2;
index 0340943..7afcb69 100644 (file)
@@ -18,6 +18,7 @@ add_subdirectory(ntdll)
 add_subdirectory(ole32)
 add_subdirectory(pefile)
 add_subdirectory(powrprof)
+add_subdirectory(sdk)
 add_subdirectory(setupapi)
 add_subdirectory(shell32)
 add_subdirectory(psapi)
diff --git a/rostests/apitests/sdk/CMakeLists.txt b/rostests/apitests/sdk/CMakeLists.txt
new file mode 100644 (file)
index 0000000..a983e8b
--- /dev/null
@@ -0,0 +1,7 @@
+
+add_executable(sdk_apitest delayimp.cpp testlist.c)
+set_module_type(sdk_apitest win32cui)
+target_link_libraries(sdk_apitest ${PSEH_LIB})
+add_importlibs(sdk_apitest msvcrt kernel32 ntdll)
+add_delay_importlibs(sdk_apitest winmm version dbghelp shlwapi sfc_os imagehlp)
+add_cd_file(TARGET sdk_apitest DESTINATION reactos/bin FOR all)
diff --git a/rostests/apitests/sdk/delayimp.cpp b/rostests/apitests/sdk/delayimp.cpp
new file mode 100644 (file)
index 0000000..0993fe0
--- /dev/null
@@ -0,0 +1,562 @@
+/*
+ * PROJECT:         ReactOS API tests
+ * LICENSE:         LGPLv2.1+ - See COPYING.LIB in the top level directory
+ * PURPOSE:         Tests for delayload
+ * PROGRAMMER:      Mark Jansen
+ */
+
+#include <apitest.h>
+
+#include <apitest.h>
+#include <strsafe.h>
+#include <delayimp.h>
+
+/* Some libraries to test against */
+#include <mmsystem.h>
+#include <winver.h>
+#include <shlwapi.h>
+#include <intshcut.h>
+#include <sfc.h>
+#include <imagehlp.h>
+
+/* Compatibility with the MS defines */
+
+#ifndef FACILITY_VISUALCPP
+#define FACILITY_VISUALCPP  ((LONG)0x6d)
+#endif
+
+#ifndef VcppException
+#define VcppException(sev,err)  ((sev) | (FACILITY_VISUALCPP<<16) | err)
+#endif
+
+#ifdef __REACTOS__
+#define WINMM_DLLNAME   "winmm.dll"
+#else
+#define WINMM_DLLNAME   "WINMM.dll"
+#endif
+
+bool g_BreakFunctionName = false;
+bool g_BrokenFunctionName = false;
+bool g_BypassMost = false;
+bool g_ExceptionIsModule = false;
+bool g_ImportByName = true;
+const char* g_ExpectedDll = NULL;
+const char* g_ExpectedName = NULL;
+char g_Target[100] = { 0 };
+
+char* target(PDelayLoadInfo pdli)
+{
+    if (g_Target[0] == '\0' && pdli)
+    {
+        if (pdli->dlp.fImportByName)
+            sprintf(g_Target, "%s!%s", pdli->szDll, pdli->dlp.szProcName);
+        else
+            sprintf(g_Target, "%s!#%lu", pdli->szDll, pdli->dlp.dwOrdinal);
+    }
+    return g_Target;
+}
+
+
+struct UnProtect
+{
+    UnProtect(PVOID addr)
+        :mAddr(NULL), mProt(0)
+    {
+        if (IsBadWritePtr(addr, 1))
+        {
+            mAddr = addr;
+            VirtualProtect(addr, 1, PAGE_EXECUTE_READWRITE, &mProt);
+        }
+    }
+    ~UnProtect()
+    {
+        DWORD dwOld;
+        if (mAddr)
+            VirtualProtect(mAddr, 1, mProt, &dwOld);
+    }
+
+    PVOID mAddr;
+    DWORD mProt;
+};
+
+
+unsigned* g_DliHookExpected = NULL;
+size_t g_DliHookIndex = 0;
+#define LAST_DLI    333
+
+static void SetExpectedDli(unsigned* order)
+{
+    g_DliHookExpected = order;
+    g_DliHookIndex = 0;
+    g_Target[0] = '\0';
+}
+
+static void CheckDli_imp(unsigned dliNotify, PDelayLoadInfo pdli, BOOL ErrorHandler)
+{
+    if (!g_DliHookExpected) return;
+
+    winetest_ok(dliNotify == g_DliHookExpected[g_DliHookIndex], "Expected dliNotify to be %u, was: %u for %s\n",
+        g_DliHookExpected[g_DliHookIndex], dliNotify, target(pdli));
+    if (ErrorHandler)
+    {
+        winetest_ok(dliNotify == dliFailGetProc || dliNotify == dliFailLoadLib,
+            "Expected code %u to be processed by the Hook, not the ErrorHandler for %s\n", dliNotify, target(pdli));
+    }
+    else
+    {
+        winetest_ok(dliNotify == dliStartProcessing || dliNotify == dliNotePreLoadLibrary ||
+            dliNotify == dliNotePreGetProcAddress || dliNotify == dliNoteEndProcessing,
+            "Expected code %u to be processed by the ErrorHandler, not the Hook for %s\n", dliNotify, target(pdli));
+    }
+    if (g_DliHookExpected[g_DliHookIndex] != LAST_DLI)
+        g_DliHookIndex++;
+}
+
+static void CheckDliDone_imp()
+{
+    if (!g_DliHookExpected) return;
+    winetest_ok(LAST_DLI == g_DliHookExpected[g_DliHookIndex],
+        "Expected g_DliHookExpected[g_DliHookIndex] to be %u, was: %u for %s\n",
+        LAST_DLI, g_DliHookExpected[g_DliHookIndex], target(NULL));
+    g_DliHookExpected = NULL;
+    g_Target[0] = '\0';
+}
+
+#define CheckDli        (winetest_set_location(__FILE__, __LINE__), 0) ? (void)0 : CheckDli_imp
+#define CheckDliDone    (winetest_set_location(__FILE__, __LINE__), 0) ? (void)0 : CheckDliDone_imp
+
+
+/* Replacement functions */
+int __stdcall MyFunction()
+{
+    return 123;
+}
+
+BOOL WINAPI MySfcIsKeyProtected(HKEY KeyHandle, LPCWSTR SubKeyName, REGSAM KeySam)
+{
+    return 12345;
+}
+
+
+static HMODULE g_VersionDll;
+FARPROC WINAPI DliHook(unsigned dliNotify, PDelayLoadInfo pdli)
+{
+    ok(pdli && pdli->cb >= 36, "Expected a valid pointer with a struct that is big enough: %p, %lu\n",
+        pdli, pdli ? pdli->cb : 0u);
+    if (!pdli || pdli->cb < 36) return NULL;
+
+    CheckDli(dliNotify, pdli, FALSE);
+
+    if (g_BreakFunctionName && pdli->dlp.fImportByName)
+    {
+        g_BreakFunctionName = false;
+        g_BrokenFunctionName = true;
+        char* procname = (char*)pdli->dlp.szProcName;
+        UnProtect prot(procname);
+        char c = procname[0];
+        procname[0] = isupper(c) ? tolower(c) : toupper(c);
+    }
+
+    /* Validate dll name when required */
+    if (g_ExpectedDll && g_ExpectedName && !g_BrokenFunctionName)
+    {
+        ok(!strcmp(g_ExpectedDll, pdli->szDll), "Expected szDll to be '%s', but was: '%s'\n", g_ExpectedDll, pdli->szDll);
+        ok(pdli->dlp.fImportByName, "Expected import by name (%s!%s)\n", g_ExpectedDll, g_ExpectedName);
+        if (pdli->dlp.fImportByName)
+        {
+            ok(!strcmp(g_ExpectedName, pdli->dlp.szProcName), "Expected szProcName to be '%s', but was: '%s'\n",
+                g_ExpectedName, pdli->dlp.szProcName);
+        }
+    }
+
+
+    if (dliNotify == dliStartProcessing)
+    {
+        /* Test loadlib fail */
+        if (!_stricmp(pdli->szDll, "sfc_os.dll"))
+        {
+            char* dll = (char*)pdli->szDll;
+            UnProtect u(dll);
+            dll[0] = 'l'; dll[1] = '_'; dll[2] = 'm';
+        }
+        if (!_stricmp(pdli->szDll, "imagehlp.dll"))
+        {
+            char* dll = (char*)pdli->szDll;
+            UnProtect u(dll);
+            dll[0] = 'x'; dll[1] = 'x'; dll[2] = 'x'; dll[3] = 'x'; dll[4] = 'x';
+        }
+        /* Test bypass */
+        if (!_stricmp(pdli->szDll, "dbghelp.dll"))
+            return MyFunction;
+    }
+    else if (dliNotify == dliNotePreLoadLibrary)
+    {
+        /* Show that this value is actually used! */
+        if (!_stricmp(pdli->szDll, "version.dll"))
+        {
+            g_VersionDll = LoadLibraryA("version.dll");
+            return (FARPROC)1;
+        }
+            
+    }
+    else if (dliNotify == dliNotePreGetProcAddress)
+    {
+        if (pdli->dlp.fImportByName && !strcmp(pdli->dlp.szProcName, "SfcIsKeyProtected"))
+        {
+            return (FARPROC)MySfcIsKeyProtected;
+        }
+    }
+
+    /* Parameter validation */
+    ok(pdli->ppfn != NULL, "Expected ppfn to be valid, was NULL for %s\n", target(pdli));
+    ok(pdli->szDll != NULL, "Expected szDll to be valid, was NULL for %s\n", target(pdli));
+    ok(pdli->dwLastError == ERROR_SUCCESS,
+        "Expected dwLastError to be ERROR_SUCCESS, was: %lu for %s\n", pdli->dwLastError, target(pdli));
+    ok(g_ImportByName == !!pdli->dlp.fImportByName, "Expected pdli->dlp.fImportByName to equal g_ImportByname\n");
+    if (pdli->dlp.fImportByName)
+        ok(pdli->dlp.szProcName != NULL, "Expected szProcName to be valid, was NULL for %s\n", target(pdli));
+    else
+        ok(pdli->dlp.dwOrdinal != 0, "Expected dwOrdinal to be valid, was NULL for %s\n", target(pdli));
+    switch(dliNotify)
+    {
+        case dliStartProcessing:
+            ok(pdli->hmodCur == NULL, "Expected hmodCur to be NULL, was: %p for %s\n", pdli->hmodCur, target(pdli));
+            ok(pdli->pfnCur == NULL, "Expected pfnCur to be NULL, was: %p for %s\n", pdli->pfnCur, target(pdli));
+            break;
+        case dliNotePreLoadLibrary:
+            ok(pdli->hmodCur == NULL, "Expected hmodCur to be NULL, was: %p for %s\n", pdli->hmodCur, target(pdli));
+            ok(pdli->pfnCur == NULL, "Expected pfnCur to be NULL, was: %p for %s\n", pdli->pfnCur, target(pdli));
+            break;
+        case dliNotePreGetProcAddress:
+            ok(pdli->hmodCur != NULL, "Expected hmodCur to be valid, was NULL for %s\n", target(pdli));
+            ok(pdli->pfnCur == NULL, "Expected pfnCur to be NULL, was: %p for %s\n", pdli->pfnCur, target(pdli));
+            break;
+        case dliNoteEndProcessing:
+            if (!g_BypassMost)
+                ok(pdli->hmodCur != NULL, "Expected hmodCur to be valid, was NULL for %s\n", target(pdli));
+            ok(pdli->pfnCur != NULL, "Expected pfnCur to be a valid pointer, was NULL for %s\n", target(pdli));
+            if (g_ExpectedDll && g_ExpectedName && !g_BrokenFunctionName)
+            {
+                FARPROC targetProc = GetProcAddress(GetModuleHandleA(g_ExpectedDll), g_ExpectedName);
+                ok(targetProc != NULL, "This should not happen, the function i need is unavail! (%s!%s)\n",
+                    g_ExpectedDll, g_ExpectedName);
+                ok(targetProc == pdli->pfnCur, "Expected pfnCur to be %p, was %p for %s\n", targetProc, pdli->pfnCur, target(pdli));
+                ok(pdli->ppfn && targetProc == *pdli->ppfn,
+                    "Expected ppfn to be valid and to result in %p, was: %p(%p) for %s\n",
+                    target, pdli->ppfn, pdli->ppfn ? *pdli->ppfn : NULL, target(pdli));
+            }
+            break;
+        default:
+            break;
+    }
+    return NULL;
+}
+
+FARPROC WINAPI DliFailHook(unsigned dliNotify, PDelayLoadInfo pdli)
+{
+    ok(pdli && pdli->cb >= 36,
+        "Expected a valid pointer with a struct that is big enough: %p, %lu\n", pdli, pdli ? pdli->cb : 0u);
+    if (!pdli || pdli->cb < 36) return NULL;
+
+    CheckDli(dliNotify, pdli, TRUE);
+
+    /* Redirections / fixes */
+    if (dliNotify == dliFailLoadLib)
+    {
+        if (!_stricmp(pdli->szDll, "l_m_os.dll"))
+            return (FARPROC)LoadLibraryA("sfc_os.dll");
+    }
+    else if (dliNotify == dliFailGetProc)
+    {
+        if (pdli->dlp.fImportByName && pdli->hmodCur == (HMODULE)1)
+        {
+            return GetProcAddress(g_VersionDll, pdli->dlp.szProcName);
+        }
+    }
+
+    /* Parameter validation */
+    ok(pdli->ppfn != NULL, "Expected ppfn to be valid, was NULL for %s\n", target(pdli));
+    ok(pdli->szDll != NULL, "Expected szDll to be valid, was NULL for %s\n", target(pdli));
+    if (pdli->dlp.fImportByName)
+        ok(pdli->dlp.szProcName != NULL, "Expected szProcName to be valid, was NULL for %s\n", target(pdli));
+    else
+        ok(pdli->dlp.dwOrdinal != 0, "Expected dwOrdinal to be valid, was NULL for %s\n", target(pdli));
+    switch(dliNotify)
+    {
+        case dliFailLoadLib:
+            ok(pdli->hmodCur == NULL, "Expected hmodCur to be NULL, was: %p for %s\n", pdli->hmodCur, target(pdli));
+            ok(pdli->pfnCur == NULL, "Expected pfnCur to be NULL, was: %p for %s\n", pdli->pfnCur, target(pdli));
+            ok(pdli->dwLastError == ERROR_MOD_NOT_FOUND,
+                "Expected dwLastError to be ERROR_MOD_NOT_FOUND, was: %lu for %s\n", pdli->dwLastError, target(pdli));
+            break;
+        case dliFailGetProc:
+            ok(pdli->hmodCur != NULL, "Expected hmodCur to be valid, was NULL for %s\n", target(pdli));
+            ok(pdli->pfnCur == NULL, "Expected pfnCur to be NULL, was: %p for %s\n", pdli->pfnCur, target(pdli));
+            ok(pdli->dwLastError == ERROR_PROC_NOT_FOUND,
+                "Expected dwLastError to be ERROR_PROC_NOT_FOUND, was: %lu for %s\n", pdli->dwLastError, target(pdli));
+            break;
+    }
+
+    return NULL;
+}
+
+
+LONG ExceptionFilter(IN PEXCEPTION_POINTERS ExceptionInfo, ULONG ExceptionCode)
+{
+    DWORD expected = VcppException(ERROR_SEVERITY_ERROR, (g_ExceptionIsModule ? ERROR_MOD_NOT_FOUND : ERROR_PROC_NOT_FOUND));
+    ok(ExceptionCode == expected, "Expected code to be 0x%lx, was: 0x%lx\n", expected, ExceptionCode);
+    ok(ExceptionInfo != NULL, "Expected to get exception info\n");
+    ok(ExceptionInfo->ExceptionRecord != NULL, "Expected to get a valid record info\n");
+
+    if (ExceptionCode != expected)
+    {
+        skip("Skipping other checks, this was not the exception we expected!\n");
+        return EXCEPTION_EXECUTE_HANDLER;
+    }
+
+    if (ExceptionInfo && ExceptionInfo->ExceptionRecord)
+    {
+        PEXCEPTION_RECORD ExceptionRecord = ExceptionInfo->ExceptionRecord;
+        ok(ExceptionRecord->ExceptionCode == expected, "Expected ExceptionCode to be 0x%lx, was 0x%lx\n",
+            expected, ExceptionRecord->ExceptionCode);
+        /* We can still continue. */
+        ok(ExceptionRecord->ExceptionFlags == 0, "Expected ExceptionFlags to be 0, was: 0x%lx\n",
+            ExceptionRecord->ExceptionFlags);
+        ok(ExceptionRecord->NumberParameters == 1, "Expected 1 parameter, got %lu\n",
+            ExceptionRecord->NumberParameters);
+        if (ExceptionRecord->NumberParameters == 1)
+        {
+            PDelayLoadInfo LoadInfo = (PDelayLoadInfo)ExceptionRecord->ExceptionInformation[0];
+            ok(LoadInfo && LoadInfo->cb >= 36, "Expected a valid pointer with a struct that is big enough: %p, %lu\n",
+                LoadInfo, LoadInfo ? LoadInfo->cb : 0);
+
+            if (g_ExpectedDll)
+                ok(!strcmp(g_ExpectedDll, LoadInfo->szDll), "Expected szDll to be '%s', but was: '%s'\n",
+                g_ExpectedDll, LoadInfo->szDll);
+            if (g_ExpectedName)
+            {
+                ok(LoadInfo->dlp.fImportByName, "Expected import by name\n");
+                if (LoadInfo->dlp.fImportByName)
+                {
+                    ok(!strcmp(g_ExpectedName, LoadInfo->dlp.szProcName),
+                    "Expected szProcName to be '%s', but was: '%s'\n", g_ExpectedName, LoadInfo->dlp.szProcName);
+
+                    if (g_ExceptionIsModule)
+                    {
+                        HMODULE mod = LoadLibraryA("imagehlp.dll");
+                        LoadInfo->pfnCur = GetProcAddress(mod, g_ExpectedName);
+                    }
+                    else
+                    {
+                        char buf[100];
+                        char first = isupper(g_ExpectedName[0]) ? tolower(g_ExpectedName[0]) : toupper(g_ExpectedName[0]);
+                        sprintf(buf, "%c%s", first, g_ExpectedName + 1);
+                        LoadInfo->pfnCur = GetProcAddress(GetModuleHandleA(g_ExpectedDll), buf);
+                    }
+                    return LoadInfo->pfnCur ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_EXECUTE_HANDLER;
+                }
+            }
+        }
+    }
+
+    return EXCEPTION_EXECUTE_HANDLER;
+}
+
+/* We register one hook the 'default' way and one manually,
+so that we can check that both fallback and registration work*/
+extern "C"
+{
+    PfnDliHook __pfnDliNotifyHook2 = DliHook;
+    //PfnDliHook __pfnDliFailureHook2 = DliFailHook;
+}
+
+
+bool g_UsePointers = false;
+
+template<typename PTR>
+PTR Rva2Addr(PIMAGE_DOS_HEADER dos, RVA rva)
+{
+    /* Old delayload type */
+    if (g_UsePointers)
+        return reinterpret_cast<PTR>(rva);
+    return reinterpret_cast<PTR>((reinterpret_cast<PBYTE>(dos) + rva));
+}
+
+
+unsigned g_winmm_snd_play_a[] = { dliStartProcessing, dliNotePreLoadLibrary, dliNotePreGetProcAddress, dliNoteEndProcessing, LAST_DLI };
+unsigned g_winmm_snd_play_w[] = { dliStartProcessing, dliNotePreGetProcAddress, dliNoteEndProcessing, LAST_DLI };
+unsigned g_winmm_play_w[] = { dliStartProcessing, dliNotePreGetProcAddress, dliFailGetProc, dliNoteEndProcessing, LAST_DLI };
+unsigned g_sfc_key[] = { dliStartProcessing, dliNotePreLoadLibrary, dliFailLoadLib, dliNotePreGetProcAddress, dliNoteEndProcessing, LAST_DLI };
+unsigned g_sfc_file[] = { dliStartProcessing, dliNotePreGetProcAddress, dliNoteEndProcessing, LAST_DLI };
+unsigned g_version_a[] = { dliStartProcessing, dliNotePreLoadLibrary, dliNotePreGetProcAddress, dliFailGetProc, dliNoteEndProcessing, LAST_DLI };
+unsigned g_version_w[] = { dliStartProcessing, dliNotePreGetProcAddress, dliFailGetProc, dliNoteEndProcessing, LAST_DLI };
+unsigned g_scard[] = { dliStartProcessing, dliNoteEndProcessing, LAST_DLI };
+unsigned g_shlwapi[] = { dliStartProcessing, dliNotePreLoadLibrary, dliNotePreGetProcAddress, dliNoteEndProcessing, LAST_DLI };
+unsigned g_imagehlp[] = { dliStartProcessing, dliNotePreLoadLibrary, dliFailLoadLib, LAST_DLI };    /* This exception does not fire EndProcessing! */
+
+
+//#define DELAYLOAD_SUPPORTS_UNLOADING
+START_TEST(delayimp)
+{
+    /* Verify that both scenario's work */
+    ok(__pfnDliNotifyHook2 == DliHook, "Expected __pfnDliNotifyHook2 to be DliHook(%p), but was: %p\n",
+        DliHook, __pfnDliNotifyHook2);
+    ok(__pfnDliFailureHook2 == NULL, "Expected __pfnDliFailureHook2 to be NULL, but was: %p\n",
+        __pfnDliFailureHook2);
+
+    __pfnDliFailureHook2 = DliFailHook;
+
+
+    PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)GetModuleHandle(NULL);
+
+    ok(dos->e_magic == IMAGE_DOS_SIGNATURE, "Expected a DOS header\n");
+    if (dos->e_magic != IMAGE_DOS_SIGNATURE)
+        return;
+
+    PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)((PBYTE)dos + dos->e_lfanew);
+    PIMAGE_DATA_DIRECTORY delaydir = nt->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT;
+
+    /* Test some advanced features (loading / unloading) */
+    if (delaydir->Size != 0)
+    {
+#if defined(_DELAY_IMP_VER) && _DELAY_IMP_VER == 2 && defined(DELAYLOAD_SUPPORTS_UNLOADING)
+        /* First, before mangling the delayload stuff, let's try some v2 functions */
+        HMODULE mod = GetModuleHandleA(WINMM_DLLNAME);
+        ok(mod == NULL, "Expected mod to be NULL, was %p\n", mod);
+        /* Now, a mistyped module (case sensitive!) */
+        HRESULT hr = __HrLoadAllImportsForDll("WiNmM.DlL");
+        ok(hr == HRESULT_FROM_WIN32(ERROR_MOD_NOT_FOUND), "Expected hr to be HRESULT_FROM_WIN32(ERROR_MOD_NOT_FOUND), was %lu\n", hr);
+        mod = GetModuleHandleA(WINMM_DLLNAME);
+        ok(mod == NULL, "Expected mod to be NULL, was %p\n", mod);
+
+        /* Let's load it */
+        hr = __HrLoadAllImportsForDll(WINMM_DLLNAME);
+        ok(hr == S_OK, "Expected hr to be S_OK, was %lu\n", hr);
+        mod = GetModuleHandleA(WINMM_DLLNAME);
+        ok(mod != NULL, "Expected mod to be valid, was NULL\n");
+
+        BOOL status = __FUnloadDelayLoadedDLL2(WINMM_DLLNAME);
+        ok(status == TRUE, "Expected __FUnloadDelayLoadedDLL2 to succeed\n");
+        mod = GetModuleHandleA(WINMM_DLLNAME);
+        ok(mod == NULL, "Expected mod to be NULL, was %p\n", mod);
+#else
+        trace("Binary compiled without support for unloading\n");
+#endif
+    }
+    else
+    {
+        skip("No IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT found, some advanced features might not work!\n");
+    }
+
+    /* Test the normal flow without a dll loaded */
+    BOOL ret = 123;
+    SetExpectedDli(g_winmm_snd_play_a);
+    g_ExpectedDll = WINMM_DLLNAME;
+    g_ExpectedName = "sndPlaySoundA";
+    ret = sndPlaySoundA(NULL, SND_NOWAIT | SND_NOSTOP);
+    ok(ret == TRUE, "Expected ret to be TRUE, was %u\n", ret);
+    CheckDliDone();
+
+    /* Test the normal flow with a dll loaded */
+    SetExpectedDli(g_winmm_snd_play_w);
+    g_ExpectedDll = WINMM_DLLNAME;
+    g_ExpectedName = "sndPlaySoundW";
+    ret = sndPlaySoundW(NULL, SND_NOWAIT | SND_NOSTOP);
+    ok(ret == TRUE, "Expected ret to be TRUE, was %u\n", ret);
+    CheckDliDone();
+
+    /* Make sure GetProcAddress fails, also ignore the Failure hook, use the exception to set the address */
+    SetExpectedDli(g_winmm_play_w);
+    g_ExpectedDll = WINMM_DLLNAME;
+    g_ExpectedName = "playSoundW";
+    g_BreakFunctionName = true;
+    ret = 123;
+    _SEH2_TRY
+    {
+        ret = PlaySoundW(NULL, NULL, SND_NOWAIT | SND_NOSTOP);
+    }
+    _SEH2_EXCEPT(ExceptionFilter(_SEH2_GetExceptionInformation(), _SEH2_GetExceptionCode()))
+    {
+        ;
+    }
+    _SEH2_END;
+    ok(ret == TRUE, "Expected ret to be TRUE, was %u\n", ret);
+    CheckDliDone();
+    ok(g_BreakFunctionName == false, "Expected the functionname to be changed\n");
+
+    /* Make the LoadLib fail, manually load the library in the Failure Hook,
+    Respond to the dliNotePreGetProcAddress with an alternate function address */
+    SetExpectedDli(g_sfc_key);
+    ret = SfcIsKeyProtected(NULL, NULL, NULL);
+    ok(ret == 12345, "Expected ret to be 12345, was %u\n", ret);    /* The original function returns FALSE! */
+    CheckDliDone();
+
+    /* Show that it works with the manually returned dll */
+    SetExpectedDli(g_sfc_file);
+    ret = SfcGetNextProtectedFile(NULL, NULL);
+    ok(ret == FALSE, "Expected ret to be FALSE, was %u\n", ret);
+    CheckDliDone();
+
+    /* Return a fake dll handle, so that we can see when it is being used, and manually return a function in the Failure Hook */
+    SetExpectedDli(g_version_a);
+    ret = GetFileVersionInfoA(NULL, NULL, NULL, NULL);
+    ok(ret == FALSE, "Expected ret to be FALSE, was %u\n", ret);
+    CheckDliDone();
+
+    /* Manually return a function in the failure hook, when the module is the previously set bad one */
+    SetExpectedDli(g_version_w);
+    ret = GetFileVersionInfoW(NULL, NULL, NULL, NULL);
+    ok(ret == FALSE, "Expected ret to be FALSE, was %u\n", ret);
+    CheckDliDone();
+
+    if (HIWORD(SymGetOptions) == NULL)
+    {
+        skip("SymGetOptions until CORE-6504 is fixed\n");
+    }
+    else
+    {
+        /* Completely bypass most hooks, by directly replying with a function address */
+        SetExpectedDli(g_scard);
+        g_BypassMost = true;
+        DWORD opt = SymGetOptions();
+        g_BypassMost = false;
+        ok(opt == 123, "Expected opt to be 123, was %lu\n", opt);    /* The original function returns ERROR_INVALID_HANDLE */
+        CheckDliDone();
+    }
+
+    /* Import by ordinal */
+    g_ImportByName = false;
+    SetExpectedDli(g_shlwapi);
+    PARSEDURLA pua = { sizeof(pua), 0 };
+    HRESULT hr = ParseURLA("", &pua);
+    ok(hr == URL_E_INVALID_SYNTAX, "Expected tmp to be URL_E_INVALID_SYNTAX, was %lx\n", hr);
+    CheckDliDone();
+    g_ImportByName = true;
+
+    /* Handle LoadLib failure with an exception handler */
+    if (HIWORD(MapAndLoad) == NULL)
+    {
+        skip("MapAndLoad until CORE-6504 is fixed\n");
+    }
+    else
+    {
+        SetExpectedDli(g_imagehlp);
+        LOADED_IMAGE img = {0};
+        ret = 123;
+        g_ExceptionIsModule = true;
+        g_ExpectedDll = "xxxxxhlp.dll";
+        g_ExpectedName = "MapAndLoad";
+        _SEH2_TRY
+        {
+            ret = MapAndLoad("some_not_existing_file.aabbcc", NULL, &img, FALSE, TRUE);
+        }
+        _SEH2_EXCEPT(ExceptionFilter(_SEH2_GetExceptionInformation(), _SEH2_GetExceptionCode()))
+        {
+            ;
+        }
+        _SEH2_END;
+        g_ExceptionIsModule = false;
+        ok(ret == FALSE, "Expected ret to be FALSE, was %u\n", ret);
+        CheckDliDone();
+    }
+}
diff --git a/rostests/apitests/sdk/testlist.c b/rostests/apitests/sdk/testlist.c
new file mode 100644 (file)
index 0000000..eea7413
--- /dev/null
@@ -0,0 +1,12 @@
+#define __ROS_LONG64__
+
+#define STANDALONE
+#include <apitest.h>
+
+extern void func_delayimp(void);
+
+const struct test winetest_testlist[] =
+{
+    { "delayimp", func_delayimp },
+    { 0, 0 }
+};