11/*
2- * PROJECT: ReactOS API tests
3- * LICENSE: LGPLv2.1+ - See COPYING.LIB in the top level directory
4- * PURPOSE: Tests for delayload
5- * PROGRAMMER: Mark Jansen
2+ * PROJECT: ReactOS API tests
3+ * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
4+ * PURPOSE: Tests for delayload
5+ * COPYRIGHT: Copyright 2023-2025 Mark Jansen <mark.jansen@reactos.org>
6+ * Copyright 2025 Hermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
67 */
78
9+ #define STANDALONE
810#include < apitest.h>
911
10- #include < apitest.h>
1112#include < strsafe.h>
1213#include < delayimp.h>
1314
15+ /* See CMakeLists.txt */
16+ #define DELAYIMP_NOHOOK 0
17+ #define DELAYIMP_GLOBALHOOK 1
18+ #define DELAYIMP_RUNTIMEHOOK 2
19+
1420/* Some libraries to test against */
1521#include < mmsystem.h>
1622#include < winver.h>
@@ -47,7 +53,7 @@ const char* g_ExpectedDll = NULL;
4753const char * g_ExpectedName = NULL ;
4854char g_Target[100 ] = { 0 };
4955
50- char * target (PDelayLoadInfo pdli)
56+ static char * target (PDelayLoadInfo pdli)
5157{
5258 if (g_Target[0 ] == ' \0 ' && pdli)
5359 {
@@ -118,9 +124,14 @@ static void CheckDli_imp(unsigned dliNotify, PDelayLoadInfo pdli, BOOL ErrorHand
118124static void CheckDliDone_imp ()
119125{
120126 if (!g_DliHookExpected) return ;
121- winetest_ok (LAST_DLI == g_DliHookExpected[g_DliHookIndex],
127+ #if (DELAYIMP_TEST == DELAYIMP_NOHOOK)
128+ unsigned lastDli = 0 ;
129+ #else
130+ unsigned lastDli = LAST_DLI;
131+ #endif
132+ winetest_ok (lastDli == g_DliHookExpected[g_DliHookIndex],
122133 " Expected g_DliHookExpected[g_DliHookIndex] to be %u, was: %u for %s\n " ,
123- LAST_DLI , g_DliHookExpected[g_DliHookIndex], target (NULL ));
134+ lastDli , g_DliHookExpected[g_DliHookIndex], target (NULL ));
124135 g_DliHookExpected = NULL ;
125136 g_Target[0 ] = ' \0 ' ;
126137}
@@ -172,7 +183,6 @@ FARPROC WINAPI DliHook(unsigned dliNotify, PDelayLoadInfo pdli)
172183 }
173184 }
174185
175-
176186 if (dliNotify == dliStartProcessing)
177187 {
178188 /* Test loadlib fail */
@@ -200,7 +210,6 @@ FARPROC WINAPI DliHook(unsigned dliNotify, PDelayLoadInfo pdli)
200210 g_VersionDll = LoadLibraryA (" version.dll" );
201211 return (FARPROC)1 ;
202212 }
203-
204213 }
205214 else if (dliNotify == dliNotePreGetProcAddress)
206215 {
@@ -220,7 +229,7 @@ FARPROC WINAPI DliHook(unsigned dliNotify, PDelayLoadInfo pdli)
220229 ok (pdli->dlp .szProcName != NULL , " Expected szProcName to be valid, was NULL for %s\n " , target (pdli));
221230 else
222231 ok (pdli->dlp .dwOrdinal != 0 , " Expected dwOrdinal to be valid, was NULL for %s\n " , target (pdli));
223- switch (dliNotify)
232+ switch (dliNotify)
224233 {
225234 case dliStartProcessing:
226235 ok (pdli->hmodCur == NULL , " Expected hmodCur to be NULL, was: %p for %s\n " , pdli->hmodCur , target (pdli));
@@ -284,7 +293,7 @@ FARPROC WINAPI DliFailHook(unsigned dliNotify, PDelayLoadInfo pdli)
284293 ok (pdli->dlp .szProcName != NULL , " Expected szProcName to be valid, was NULL for %s\n " , target (pdli));
285294 else
286295 ok (pdli->dlp .dwOrdinal != 0 , " Expected dwOrdinal to be valid, was NULL for %s\n " , target (pdli));
287- switch (dliNotify)
296+ switch (dliNotify)
288297 {
289298 case dliFailLoadLib:
290299 ok (pdli->hmodCur == NULL , " Expected hmodCur to be NULL, was: %p for %s\n " , pdli->hmodCur , target (pdli));
@@ -365,14 +374,14 @@ LONG ExceptionFilter(IN PEXCEPTION_POINTERS ExceptionInfo, ULONG ExceptionCode)
365374 return EXCEPTION_EXECUTE_HANDLER;
366375}
367376
368- /* We register one hook the 'default' way and one manually,
369- so that we can check that both fallback and registration work */
370- extern " C "
377+ # if (DELAYIMP_TEST == DELAYIMP_GLOBALHOOK)
378+ /* Register static hooks */
379+ ExternC
371380{
372- extern PfnDliHook __pfnDliNotifyHook2;
373- // PfnDliHook __pfnDliFailureHook2 = DliFailHook;
381+ PfnDliHook __pfnDliNotifyHook2 = DliHook ;
382+ PfnDliHook __pfnDliFailureHook2 = DliFailHook;
374383}
375-
384+ # endif
376385
377386bool g_UsePointers = false ;
378387
@@ -399,17 +408,33 @@ unsigned g_imagehlp[] = { dliStartProcessing, dliNotePreLoadLibrary, dliFailLoad
399408
400409
401410// #define DELAYLOAD_SUPPORTS_UNLOADING
402- START_TEST (delayimp)
411+ #if (DELAYIMP_TEST == DELAYIMP_NOHOOK)
412+ START_TEST (delayimp_nohook)
413+ #elif (DELAYIMP_TEST == DELAYIMP_GLOBALHOOK)
414+ START_TEST (delayimp_globalhook)
415+ #else // (DELAYIMP_TEST == DELAYIMP_RUNTIMEHOOK)
416+ START_TEST (delayimp_runtimehook)
417+ #endif
403418{
404- __pfnDliNotifyHook2 = DliHook;
405- /* Verify that both scenario's work */
419+ /* We register hooks either the 'default' (static) way or at runtime,
420+ * so that we can check that both fallback and registration work */
421+ #if (DELAYIMP_TEST == DELAYIMP_GLOBALHOOK)
406422 ok (__pfnDliNotifyHook2 == DliHook, " Expected __pfnDliNotifyHook2 to be DliHook(%p), but was: %p\n " ,
407- DliHook, __pfnDliNotifyHook2);
423+ DliHook, __pfnDliNotifyHook2);
424+ ok (__pfnDliFailureHook2 == DliFailHook, " Expected __pfnDliFailureHook2 to be DliFailHook(%p), but was: %p\n " ,
425+ DliFailHook, __pfnDliFailureHook2);
426+ #elif (DELAYIMP_TEST == DELAYIMP_RUNTIMEHOOK)
427+ ok (__pfnDliNotifyHook2 == NULL , " Expected __pfnDliNotifyHook2 to be NULL, but was: %p\n " ,
428+ __pfnDliNotifyHook2);
408429 ok (__pfnDliFailureHook2 == NULL , " Expected __pfnDliFailureHook2 to be NULL, but was: %p\n " ,
409- __pfnDliFailureHook2);
430+ __pfnDliFailureHook2);
410431
432+ /* Register hooks at runtime */
433+ __pfnDliNotifyHook2 = DliHook;
411434 __pfnDliFailureHook2 = DliFailHook;
412-
435+ #else // (DELAYIMP_TEST == DELAYIMP_NOHOOK)
436+ /* No hook is defined */
437+ #endif
413438
414439 PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)GetModuleHandle (NULL );
415440
@@ -484,14 +509,25 @@ START_TEST(delayimp)
484509 _SEH2_END;
485510 ok (err == MMSYSERR_INVALHANDLE, " Expected err to be MMSYSERR_INVALHANDLE, was 0x%lx\n " , err);
486511 CheckDliDone ();
487- ok (g_BreakFunctionName == false , " Expected the functionname to be changed\n " );
512+ #if (DELAYIMP_TEST == DELAYIMP_NOHOOK)
513+ ok (g_BreakFunctionName == true , " Expected the function name to not be changed\n " );
514+ #else
515+ ok (g_BreakFunctionName == false , " Expected the function name to be changed\n " );
516+ #endif
488517
518+ BOOL ret;
519+ #if (DELAYIMP_TEST == DELAYIMP_NOHOOK)
520+ /* We cannot run this test with hooks disabled. The reason is that in this case,
521+ * sfc_os.dll may not export SfcIsKeyProtected() (e.g. on Windows <= 2003) and
522+ * without the delay-loading hooks, we wouldn't resolve SfcIsKeyProtected(). */
523+ #else
489524 /* Make the LoadLib fail, manually load the library in the Failure Hook,
490- Respond to the dliNotePreGetProcAddress with an alternate function address */
525+ respond to the dliNotePreGetProcAddress with an alternate function address */
491526 SetExpectedDli (g_sfc_key);
492- BOOL ret = SfcIsKeyProtected (NULL , NULL , NULL );
527+ ret = SfcIsKeyProtected (NULL , NULL , NULL );
493528 ok (ret == 12345 , " Expected ret to be 12345, was %u\n " , ret); /* The original function returns FALSE! */
494529 CheckDliDone ();
530+ #endif // DELAYIMP_NOHOOK
495531
496532 /* Show that it works with the manually returned dll */
497533 SetExpectedDli (g_sfc_file);
@@ -511,6 +547,9 @@ START_TEST(delayimp)
511547 ok (ret == FALSE , " Expected ret to be FALSE, was %u\n " , ret);
512548 CheckDliDone ();
513549
550+ #if (DELAYIMP_TEST == DELAYIMP_NOHOOK)
551+ /* This test won't run with hooks disabled */
552+ #else
514553 if (HIWORD (SymGetOptions) == NULL )
515554 {
516555 skip (" SymGetOptions until CORE-6504 is fixed\n " );
@@ -525,6 +564,7 @@ START_TEST(delayimp)
525564 ok (opt == 123 , " Expected opt to be 123, was %lu\n " , opt); /* The original function returns ERROR_INVALID_HANDLE */
526565 CheckDliDone ();
527566 }
567+ #endif // DELAYIMP_NOHOOK
528568
529569 /* Import by ordinal */
530570 g_ImportByName = false ;
0 commit comments