https://git.reactos.org/?p=reactos.git;a=commitdiff;h=ccf1e97aa1bd056b4d4b7ce6fae1a4b089c6c25b

commit ccf1e97aa1bd056b4d4b7ce6fae1a4b089c6c25b
Author:     Ratin Gao <ra...@knsoft.org>
AuthorDate: Mon Mar 3 04:13:33 2025 +0800
Commit:     GitHub <nore...@github.com>
CommitDate: Sun Mar 2 21:13:33 2025 +0100

    [NTDLL_VISTA:LDR] Implement DLL Notification (#6795)
    
    Implement DLL Load Notification, an NT6+ feature.
    
https://learn.microsoft.com/en-us/windows/win32/devnotes/dll-load-notification
    
    - [RTL] Sync `RTL_STATIC_LIST_HEAD` and `RtlFailFast` from XDK to NDK.
    - [NTDLL_VISTA] Introduce ntdll_vista_static static library and link both 
ntdll_vista and ntdll to it.
    - [NDK][LDR] Add and fix DLL Notification definitions.
    - [NTDLL_VISTA] Code improvements.
    - [NTDLL_VISTA:LDR] Implement Dll Notification.
    - [NTDLL][NTDLL_APITEST] Add Dll Notification API test.
---
 dll/ntdll/CMakeLists.txt                           |   2 +-
 dll/ntdll/def/ntdll.spec                           |   4 +-
 dll/ntdll/include/ntdllp.h                         |  14 ++
 dll/ntdll/ldr/ldrapi.c                             |   8 +-
 dll/ntdll/ldr/ldrinit.c                            |   5 +-
 dll/ntdll/ldr/ldrutils.c                           |   7 +-
 dll/ntdll/nt_0600/CMakeLists.txt                   |  11 +-
 dll/ntdll/nt_0600/DllMain.c                        |  23 +-
 dll/ntdll/nt_0600/ldr/ldrinit.c                    |   3 +
 dll/ntdll/nt_0600/ldr/ldrnotify.c                  | 154 ++++++++++++++
 dll/ntdll/nt_0600/ntdll_vista.h                    |  21 ++
 dll/ntdll/nt_0600/ntdll_vista.spec                 |   3 +
 modules/rostests/apitests/ntdll/CMakeLists.txt     |   6 +-
 .../rostests/apitests/ntdll/DllLoadNotification.c  | 235 +++++++++++++++++++++
 .../apitests/ntdll/empty_dll/CMakeLists.txt        |   6 +
 .../rostests/apitests/ntdll/empty_dll/empty_dll.c  |  11 +
 modules/rostests/apitests/ntdll/testdata.rc        |   1 +
 modules/rostests/apitests/ntdll/testlist.c         |   2 +
 sdk/include/ndk/ldrfuncs.h                         |  17 ++
 sdk/include/ndk/ldrtypes.h                         |  39 +++-
 sdk/include/ndk/rtlfuncs.h                         |  12 ++
 sdk/include/xdk/rtlfuncs.h                         |   6 +-
 22 files changed, 538 insertions(+), 52 deletions(-)

diff --git a/dll/ntdll/CMakeLists.txt b/dll/ntdll/CMakeLists.txt
index fdd7c11262a..155ee21d4b5 100644
--- a/dll/ntdll/CMakeLists.txt
+++ b/dll/ntdll/CMakeLists.txt
@@ -65,7 +65,7 @@ set_module_type(ntdll win32dll ENTRYPOINT 0)
 set_subsystem(ntdll console)
 ################# END  HACK #################
 
-target_link_libraries(ntdll etwtrace csrlib rtl rtl_um rtl_vista ntdllsys 
libcntpr uuid ${PSEH_LIB})
+target_link_libraries(ntdll ntdll_vista_static etwtrace csrlib rtl rtl_um 
rtl_vista ntdllsys libcntpr uuid ${PSEH_LIB})
 if(DLL_EXPORT_VERSION GREATER_EQUAL 0x600)
     target_link_libraries(ntdll cryptlib)
 endif()
diff --git a/dll/ntdll/def/ntdll.spec b/dll/ntdll/def/ntdll.spec
index b023b06f9c3..de0d7f19516 100644
--- a/dll/ntdll/def/ntdll.spec
+++ b/dll/ntdll/def/ntdll.spec
@@ -170,7 +170,7 @@
 @ stdcall LdrQueryImageFileKeyOption(ptr ptr long ptr long ptr)
 @ stdcall -stub -version=0x600+ LdrQueryModuleServiceTags(ptr ptr ptr)
 @ stdcall LdrQueryProcessModuleInformation(ptr long ptr)
-@ stdcall -stub -version=0x600+ LdrRegisterDllNotification(long ptr ptr ptr)
+@ stdcall -version=0x600+ LdrRegisterDllNotification(long ptr ptr ptr)
 @ stdcall -stub -version=0x600+ LdrRemoveLoadAsDataTable(ptr ptr ptr long)
 @ stub -version=0x600+ LdrResFindResource
 @ stub -version=0x600+ LdrResFindResourceDirectory
@@ -185,7 +185,7 @@
 @ stub -version=0x600+ LdrUnloadAlternateResourceModuleEx
 @ stdcall LdrUnloadDll(ptr)
 @ stdcall LdrUnlockLoaderLock(long ptr)
-@ stdcall -stub -version=0x600+ LdrUnregisterDllNotification(ptr)
+@ stdcall -version=0x600+ LdrUnregisterDllNotification(ptr)
 @ stdcall LdrVerifyImageMatchesChecksum(ptr long long long)
 @ stdcall -stub -version=0x600+ LdrVerifyImageMatchesChecksumEx(ptr ptr)
 @ stub -version=0x600+ LdrpResGetMappingSize
diff --git a/dll/ntdll/include/ntdllp.h b/dll/ntdll/include/ntdllp.h
index c5ea35a2761..bbef5444e86 100644
--- a/dll/ntdll/include/ntdllp.h
+++ b/dll/ntdll/include/ntdllp.h
@@ -229,6 +229,15 @@ VOID
 NTAPI
 LdrpFinalizeAndDeallocateDataTableEntry(IN PLDR_DATA_TABLE_ENTRY Entry);
 
+#if (_WIN32_WINNT >= _WIN32_WINNT_VISTA) || (DLL_EXPORT_VERSION >= 
_WIN32_WINNT_VISTA)
+
+VOID
+NTAPI
+LdrpSendDllNotifications(
+    _In_ PLDR_DATA_TABLE_ENTRY DllEntry,
+    _In_ ULONG NotificationReason);
+
+#endif /* (_WIN32_WINNT >= _WIN32_WINNT_VISTA) || (DLL_EXPORT_VERSION >= 
_WIN32_WINNT_VISTA) */
 
 /* path.c */
 BOOLEAN
@@ -242,6 +251,11 @@ NTAPI
 RtlpInitializeKeyedEvent(
     VOID);
 
+VOID
+NTAPI
+RtlpCloseKeyedEvent(
+    VOID);
+
 VOID
 NTAPI
 RtlpInitializeThreadPooling(
diff --git a/dll/ntdll/ldr/ldrapi.c b/dll/ntdll/ldr/ldrapi.c
index a688385cc3a..8bb1f25ae13 100644
--- a/dll/ntdll/ldr/ldrapi.c
+++ b/dll/ntdll/ldr/ldrapi.c
@@ -1497,6 +1497,11 @@ LdrUnloadDll(
             DPRINT1("LDR: Unmapping [%ws]\n", LdrEntry->BaseDllName.Buffer);
         }
 
+#if (_WIN32_WINNT >= _WIN32_WINNT_VISTA) || (DLL_EXPORT_VERSION >= 
_WIN32_WINNT_VISTA)
+        /* Send shutdown notification */
+        LdrpSendDllNotifications(CurrentEntry, 
LDR_DLL_NOTIFICATION_REASON_UNLOADED);
+#endif
+
         /* Check if this is a .NET executable */
         CorImageData = RtlImageDirectoryEntryToData(LdrEntry->DllBase,
                                                     TRUE,
@@ -1520,9 +1525,6 @@ LdrUnloadDll(
         /* Unload the alternate resource module, if any */
         LdrUnloadAlternateResourceModule(CurrentEntry->DllBase);
 
-        /* FIXME: Send shutdown notification */
-        //LdrpSendDllNotifications(CurrentEntry, 2, LdrpShutdownInProgress);
-
         /* Check if a Hotpatch is active */
         if (LdrEntry->PatchInformation)
         {
diff --git a/dll/ntdll/ldr/ldrinit.c b/dll/ntdll/ldr/ldrinit.c
index cde86383759..64831f660dd 100644
--- a/dll/ntdll/ldr/ldrinit.c
+++ b/dll/ntdll/ldr/ldrinit.c
@@ -56,9 +56,7 @@ ULONG LdrpNumberOfProcessors;
 PVOID NtDllBase;
 extern LARGE_INTEGER RtlpTimeout;
 extern BOOLEAN RtlpTimeoutDisable;
-PVOID LdrpHeap;
 LIST_ENTRY LdrpHashTable[LDR_HASH_TABLE_ENTRIES];
-LIST_ENTRY LdrpDllNotificationList;
 HANDLE LdrpKnownDllObjectDirectory;
 UNICODE_STRING LdrpKnownDllPath;
 WCHAR LdrpKnownDllPathBuffer[128];
@@ -2008,9 +2006,8 @@ LdrpInitializeProcess(IN PCONTEXT Context,
     //Peb->FastPebLockRoutine = (PPEBLOCKROUTINE)RtlEnterCriticalSection;
     //Peb->FastPebUnlockRoutine = (PPEBLOCKROUTINE)RtlLeaveCriticalSection;
 
-    /* Setup Callout Lock and Notification list */
+    /* Setup Callout Lock */
     //RtlInitializeCriticalSection(&RtlpCalloutEntryLock);
-    InitializeListHead(&LdrpDllNotificationList);
 
     /* For old executables, use 16-byte aligned heap */
     if ((NtHeader->OptionalHeader.MajorSubsystemVersion <= 3) &&
diff --git a/dll/ntdll/ldr/ldrutils.c b/dll/ntdll/ldr/ldrutils.c
index e46bb3446dd..19c90865531 100644
--- a/dll/ntdll/ldr/ldrutils.c
+++ b/dll/ntdll/ldr/ldrutils.c
@@ -1226,7 +1226,12 @@ SkipCheck:
     /* Insert this entry */
     LdrpInsertMemoryTableEntry(LdrEntry);
 
-    // LdrpSendDllNotifications(LdrEntry, TRUE, Status == 
STATUS_IMAGE_NOT_AT_BASE)
+#if (_WIN32_WINNT >= _WIN32_WINNT_VISTA) || (DLL_EXPORT_VERSION >= 
_WIN32_WINNT_VISTA)
+    LdrpSendDllNotifications(LdrEntry, LDR_DLL_NOTIFICATION_REASON_LOADED);
+#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
+    LdrEntry->Flags |= LDRP_LOAD_NOTIFICATIONS_SENT; /* 
LdrEntry->LoadNotificationsSent = TRUE; */
+#endif
+#endif
 
     /* Check for invalid CPU Image */
     if (Status == STATUS_IMAGE_MACHINE_TYPE_MISMATCH)
diff --git a/dll/ntdll/nt_0600/CMakeLists.txt b/dll/ntdll/nt_0600/CMakeLists.txt
index 2eec0df9940..69b9b5f0ff8 100644
--- a/dll/ntdll/nt_0600/CMakeLists.txt
+++ b/dll/ntdll/nt_0600/CMakeLists.txt
@@ -11,12 +11,15 @@ include_directories(
     ${REACTOS_SOURCE_DIR}/sdk/include/reactos/subsys)
 
 list(APPEND SOURCE
-    DllMain.c
-    ${CMAKE_CURRENT_BINARY_DIR}/ntdll_vista.def)
+    ldr/ldrinit.c
+    ldr/ldrnotify.c)
 
-add_library(ntdll_vista MODULE ${SOURCE} ntdll_vista.rc)
+add_library(ntdll_vista_static ${SOURCE})
+target_link_libraries(ntdll_vista_static)
+add_dependencies(ntdll_vista_static psdk)
+add_library(ntdll_vista MODULE DllMain.c 
${CMAKE_CURRENT_BINARY_DIR}/ntdll_vista.def ntdll_vista.rc)
 set_module_type(ntdll_vista win32dll ENTRYPOINT DllMain 12)
-target_link_libraries(ntdll_vista smlib rtl_vista)
+target_link_libraries(ntdll_vista ntdll_vista_static smlib rtl_vista 
${PSEH_LIB})
 if(ARCH STREQUAL "arm")
     target_link_libraries(ntdll_vista chkstk)
 endif()
diff --git a/dll/ntdll/nt_0600/DllMain.c b/dll/ntdll/nt_0600/DllMain.c
index 90a01419b97..38a0d448916 100644
--- a/dll/ntdll/nt_0600/DllMain.c
+++ b/dll/ntdll/nt_0600/DllMain.c
@@ -1,25 +1,4 @@
-#include <stdarg.h>
-
-#define WIN32_NO_STATUS
-
-#include <windef.h>
-#include <winbase.h>
-#include <winreg.h>
-#include <winuser.h>
-#include <winwlx.h>
-#include <ndk/rtltypes.h>
-#include <ndk/umfuncs.h>
-
-#define NDEBUG
-#include <debug.h>
-
-VOID
-NTAPI
-RtlpInitializeKeyedEvent(VOID);
-
-VOID
-NTAPI
-RtlpCloseKeyedEvent(VOID);
+#include "ntdll_vista.h"
 
 BOOL
 WINAPI
diff --git a/dll/ntdll/nt_0600/ldr/ldrinit.c b/dll/ntdll/nt_0600/ldr/ldrinit.c
new file mode 100644
index 00000000000..e9251d9ebf5
--- /dev/null
+++ b/dll/ntdll/nt_0600/ldr/ldrinit.c
@@ -0,0 +1,3 @@
+#include "ntdll_vista.h"
+
+PVOID LdrpHeap;
diff --git a/dll/ntdll/nt_0600/ldr/ldrnotify.c 
b/dll/ntdll/nt_0600/ldr/ldrnotify.c
new file mode 100644
index 00000000000..70ae996c131
--- /dev/null
+++ b/dll/ntdll/nt_0600/ldr/ldrnotify.c
@@ -0,0 +1,154 @@
+/*
+ * PROJECT:     ReactOS NT Layer/System API
+ * LICENSE:     MIT (https://spdx.org/licenses/MIT)
+ * PURPOSE:     DLL Load Notification Implementation
+ * COPYRIGHT:   Copyright 2024 Ratin Gao <ra...@knsoft.org>
+ */
+
+#include "ntdll_vista.h"
+
+/* GLOBALS *******************************************************************/
+
+typedef struct _LDR_DLL_NOTIFICATION_ENTRY
+{
+    LIST_ENTRY List;
+    PLDR_DLL_NOTIFICATION_FUNCTION Callback;
+    PVOID Context;
+} LDR_DLL_NOTIFICATION_ENTRY, *PLDR_DLL_NOTIFICATION_ENTRY;
+
+static RTL_STATIC_LIST_HEAD(LdrpDllNotificationList);
+
+/* Initialize critical section statically */
+static RTL_CRITICAL_SECTION LdrpDllNotificationLock;
+static RTL_CRITICAL_SECTION_DEBUG LdrpDllNotificationLockDebug = {
+    .CriticalSection = &LdrpDllNotificationLock
+};
+static RTL_CRITICAL_SECTION LdrpDllNotificationLock = {
+    &LdrpDllNotificationLockDebug,
+    -1,
+    0,
+    0,
+    0,
+    0
+};
+
+/* FUNCTIONS *****************************************************************/
+
+NTSTATUS
+NTAPI
+LdrRegisterDllNotification(
+    _In_ ULONG Flags,
+    _In_ PLDR_DLL_NOTIFICATION_FUNCTION NotificationFunction,
+    _In_opt_ PVOID Context,
+    _Out_ PVOID *Cookie)
+{
+    PLDR_DLL_NOTIFICATION_ENTRY NewEntry;
+
+    /* Check input parameters */
+    if (Flags != 0 || NotificationFunction == NULL || Cookie == NULL)
+    {
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    /* Allocate new entry and assign input values */
+    NewEntry = RtlAllocateHeap(LdrpHeap, 0, sizeof(*NewEntry));
+    if (NewEntry == NULL)
+    {
+        return STATUS_NO_MEMORY;
+    }
+    NewEntry->Callback = NotificationFunction;
+    NewEntry->Context = Context;
+
+    /* Add node to the end of global list */
+    RtlEnterCriticalSection(&LdrpDllNotificationLock);
+    InsertTailList(&LdrpDllNotificationList, &NewEntry->List);
+    RtlLeaveCriticalSection(&LdrpDllNotificationLock);
+
+    /* Cookie is address of the new entry */
+    *Cookie = NewEntry;
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+NTAPI
+LdrUnregisterDllNotification(
+    _In_ PVOID Cookie)
+{
+    NTSTATUS Status = STATUS_DLL_NOT_FOUND;
+    PLIST_ENTRY Entry;
+
+    /* Find entry to remove */
+    RtlEnterCriticalSection(&LdrpDllNotificationLock);
+    for (Entry = LdrpDllNotificationList.Flink;
+         Entry != &LdrpDllNotificationList;
+         Entry = Entry->Flink)
+    {
+        if (Entry == Cookie)
+        {
+            RemoveEntryList(Entry);
+            Status = STATUS_SUCCESS;
+            break;
+        }
+    }
+    RtlLeaveCriticalSection(&LdrpDllNotificationLock);
+
+    if (NT_SUCCESS(Status))
+    {
+        RtlFreeHeap(LdrpHeap, 0, Entry);
+    }
+    return Status;
+}
+
+VOID
+NTAPI
+LdrpSendDllNotifications(
+    _In_ PLDR_DATA_TABLE_ENTRY DllEntry,
+    _In_ ULONG NotificationReason)
+{
+    PLIST_ENTRY Entry;
+    PLDR_DLL_NOTIFICATION_ENTRY NotificationEntry;
+    LDR_DLL_NOTIFICATION_DATA NotificationData;
+
+    /*
+     * LDR_DLL_LOADED_NOTIFICATION_DATA and LDR_DLL_UNLOADED_NOTIFICATION_DATA
+     * currently are the same. Use C_ASSERT to ensure it, then fill either of 
them.
+     */
+#define LdrpAssertDllNotificationDataMember(x)\
+    C_ASSERT(FIELD_OFFSET(LDR_DLL_NOTIFICATION_DATA, Loaded.x) ==\
+             FIELD_OFFSET(LDR_DLL_NOTIFICATION_DATA, Unloaded.x))
+
+    C_ASSERT(sizeof(NotificationData.Loaded) == 
sizeof(NotificationData.Unloaded));
+    LdrpAssertDllNotificationDataMember(Flags);
+    LdrpAssertDllNotificationDataMember(FullDllName);
+    LdrpAssertDllNotificationDataMember(BaseDllName);
+    LdrpAssertDllNotificationDataMember(DllBase);
+    LdrpAssertDllNotificationDataMember(SizeOfImage);
+
+#undef LdrpAssertDllNotificationDataMember
+
+    NotificationData.Loaded.Flags = 0; /* Reserved and always 0, not 
DllEntry->Flags */
+    NotificationData.Loaded.FullDllName = &DllEntry->FullDllName;
+    NotificationData.Loaded.BaseDllName = &DllEntry->BaseDllName;
+    NotificationData.Loaded.DllBase = DllEntry->DllBase;
+    NotificationData.Loaded.SizeOfImage = DllEntry->SizeOfImage;
+
+    /* Send notification to all registered callbacks */
+    RtlEnterCriticalSection(&LdrpDllNotificationLock);
+    _SEH2_TRY
+    {
+        for (Entry = LdrpDllNotificationList.Flink;
+             Entry != &LdrpDllNotificationList;
+             Entry = Entry->Flink)
+        {
+            NotificationEntry = CONTAINING_RECORD(Entry, 
LDR_DLL_NOTIFICATION_ENTRY, List);
+            NotificationEntry->Callback(NotificationReason,
+                                        &NotificationData,
+                                        NotificationEntry->Context);
+        }
+    }
+    _SEH2_FINALLY
+    {
+        RtlLeaveCriticalSection(&LdrpDllNotificationLock);
+    }
+    _SEH2_END;
+}
diff --git a/dll/ntdll/nt_0600/ntdll_vista.h b/dll/ntdll/nt_0600/ntdll_vista.h
new file mode 100644
index 00000000000..47051576c5c
--- /dev/null
+++ b/dll/ntdll/nt_0600/ntdll_vista.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#define _NTSYSTEM_
+#define _NTDLLBUILD_
+
+#include <stdarg.h>
+
+#define WIN32_NO_STATUS
+#include <windef.h>
+#include <winbase.h>
+#include <winnt.h>
+
+#define NTOS_MODE_USER
+#include <ndk/rtlfuncs.h>
+#include <ndk/umfuncs.h>
+#include <ndk/ldrfuncs.h>
+
+#define NDEBUG
+#include <debug.h>
+
+#include "../include/ntdllp.h"
diff --git a/dll/ntdll/nt_0600/ntdll_vista.spec 
b/dll/ntdll/nt_0600/ntdll_vista.spec
index d36c6449d8e..2bf9f747a00 100644
--- a/dll/ntdll/nt_0600/ntdll_vista.spec
+++ b/dll/ntdll/nt_0600/ntdll_vista.spec
@@ -1,3 +1,6 @@
+@ stdcall LdrRegisterDllNotification(long ptr ptr ptr)
+@ stdcall LdrUnregisterDllNotification(ptr)
+
 @ stdcall RtlInitializeConditionVariable(ptr)
 @ stdcall RtlWakeConditionVariable(ptr)
 @ stdcall RtlWakeAllConditionVariable(ptr)
diff --git a/modules/rostests/apitests/ntdll/CMakeLists.txt 
b/modules/rostests/apitests/ntdll/CMakeLists.txt
index 29c1cf96ed9..e371e95305f 100644
--- a/modules/rostests/apitests/ntdll/CMakeLists.txt
+++ b/modules/rostests/apitests/ntdll/CMakeLists.txt
@@ -1,11 +1,14 @@
 
 add_subdirectory(load_notifications)
+add_subdirectory(empty_dll)
 
 include_directories($<TARGET_FILE_DIR:load_notifications>)
+include_directories($<TARGET_FILE_DIR:empty_dll>)
 include_directories(${REACTOS_SOURCE_DIR}/ntoskrnl/include)
 spec2def(ntdll_apitest.exe ntdll_apitest.spec)
 
 list(APPEND SOURCE
+    DllLoadNotification.c
     LdrEnumResources.c
     LdrLoadDll.c
     load_notifications.c
@@ -118,6 +121,7 @@ list(APPEND PCH_SKIP_SOURCE
     testlist.c)
 
 add_rc_deps(testdata.rc 
${CMAKE_CURRENT_BINARY_DIR}/load_notifications/load_notifications.dll)
+add_rc_deps(testdata.rc ${CMAKE_CURRENT_BINARY_DIR}/empty_dll/empty_dll.dll)
 
 add_executable(ntdll_apitest
     ${SOURCE}
@@ -135,7 +139,7 @@ target_link_libraries(ntdll_apitest rtl_test_lib wine uuid 
${PSEH_LIB})
 set_module_type(ntdll_apitest win32cui)
 add_importlibs(ntdll_apitest msvcrt advapi32 kernel32 ntdll)
 add_pch(ntdll_apitest precomp.h "${PCH_SKIP_SOURCE}")
-add_dependencies(ntdll_apitest load_notifications)
+add_dependencies(ntdll_apitest load_notifications empty_dll)
 
 if(NOT MSVC)
     set_source_files_properties(RtlGetFullPathName_UstrEx.c PROPERTIES 
COMPILE_OPTIONS "-Wno-format")
diff --git a/modules/rostests/apitests/ntdll/DllLoadNotification.c 
b/modules/rostests/apitests/ntdll/DllLoadNotification.c
new file mode 100644
index 00000000000..30ed50e38a3
--- /dev/null
+++ b/modules/rostests/apitests/ntdll/DllLoadNotification.c
@@ -0,0 +1,235 @@
+/*
+ * PROJECT:     ReactOS API Tests
+ * LICENSE:     MIT (https://spdx.org/licenses/MIT)
+ * PURPOSE:     Test for DLL Load Notification API
+ * COPYRIGHT:   Copyright 2024 Ratin Gao <ra...@knsoft.org>
+ */
+
+#define UNICODE
+
+#include "precomp.h"
+
+#include <winuser.h>
+
+static WCHAR g_szDllPath[MAX_PATH];
+static UNICODE_STRING g_usDllPath;
+static UNICODE_STRING g_usDllName;
+static volatile LONG g_lDllLoadCount = 0;
+
+typedef
+NTSTATUS
+NTAPI
+FN_LdrRegisterDllNotification(
+    _In_ ULONG Flags,
+    _In_ PLDR_DLL_NOTIFICATION_FUNCTION NotificationFunction,
+    _In_opt_ PVOID Context,
+    _Out_ PVOID* Cookie);
+
+typedef
+NTSTATUS
+NTAPI
+FN_LdrUnregisterDllNotification(
+    _In_ PVOID Cookie);
+
+static BOOL ExtractResource(
+    _In_z_ PCWSTR SavePath,
+    _In_ PCWSTR ResourceType,
+    _In_ PCWSTR ResourceName)
+{
+    BOOL bSuccess;
+    DWORD dwWritten, dwSize;
+    HGLOBAL hGlobal;
+    LPVOID pData;
+    HANDLE hFile;
+    HRSRC hRsrc;
+
+    /* Load resource */
+    if ((hRsrc = FindResourceW(NULL, ResourceName, ResourceType)) == NULL ||
+        (dwSize = SizeofResource(NULL, hRsrc)) == 0 ||
+        (hGlobal = LoadResource(NULL, hRsrc)) == NULL ||
+        (pData = LockResource(hGlobal)) == NULL)
+    {
+        return FALSE;
+    }
+
+    /* Save to file */
+    hFile = CreateFileW(SavePath,
+                        GENERIC_WRITE,
+                        FILE_SHARE_READ,
+                        NULL,
+                        CREATE_ALWAYS,
+                        FILE_ATTRIBUTE_NORMAL,
+                        NULL);
+    if (hFile == INVALID_HANDLE_VALUE)
+    {
+        return FALSE;
+    }
+    bSuccess = WriteFile(hFile, pData, dwSize, &dwWritten, NULL);
+    CloseHandle(hFile);
+    if (!bSuccess)
+    {
+        return FALSE;
+    }
+    else if (dwWritten != dwSize)
+    {
+        trace("Extract resource failed, written size (%lu) is not actual size 
(%lu)\n", dwWritten, dwSize);
+        DeleteFileW(SavePath);
+        SetLastError(ERROR_INCORRECT_SIZE);
+        return FALSE;
+    }
+    return TRUE;
+}
+
+static VOID NTAPI DllLoadCallback(
+    _In_ ULONG NotificationReason,
+    _In_ PCLDR_DLL_NOTIFICATION_DATA NotificationData,
+    _In_opt_ PVOID Context)
+{
+    LONG lRet;
+    HMODULE* phNotifiedDllBase = Context;
+
+    /*
+     * Verify the data,
+     * NotificationData->Loaded and NotificationData->Unloaded currently are 
the same.
+     */
+
+    /* Verify the FullDllName and BaseDllName */
+    ok_eq_ulong(NotificationData->Loaded.Flags, 0UL);
+    lRet = RtlCompareUnicodeString(NotificationData->Loaded.FullDllName,
+                                   (PCUNICODE_STRING)&g_usDllPath,
+                                   TRUE);
+    ok_eq_long(lRet, 0L);
+    lRet = RtlCompareUnicodeString(NotificationData->Loaded.BaseDllName,
+                                   (PCUNICODE_STRING)&g_usDllName,
+                                   TRUE);
+    ok_eq_long(lRet, 0L);
+
+    /*
+     * Verify SizeOfImage and read SizeOfImage from PE header,
+     * make sure the DLL is not unmapped, the memory is still accessible.
+     */
+    ok_eq_ulong(NotificationData->Loaded.SizeOfImage,
+                
RtlImageNtHeader(NotificationData->Loaded.DllBase)->OptionalHeader.SizeOfImage);
+
+    /* Reason can be load or unload */
+    ok(NotificationReason == LDR_DLL_NOTIFICATION_REASON_LOADED ||
+       NotificationReason == LDR_DLL_NOTIFICATION_REASON_UNLOADED, "Incorrect 
NotificationReason\n");
+    if (NotificationReason == LDR_DLL_NOTIFICATION_REASON_LOADED)
+    {
+        *phNotifiedDllBase = NotificationData->Loaded.DllBase;
+        InterlockedIncrement(&g_lDllLoadCount);
+    }
+    else if (NotificationReason == LDR_DLL_NOTIFICATION_REASON_UNLOADED)
+    {
+        InterlockedDecrement(&g_lDllLoadCount);
+    }
+}
+
+START_TEST(DllLoadNotification)
+{
+    WCHAR szTempPath[MAX_PATH];
+    PCWSTR pszDllName;
+    HMODULE hNtDll, hTestDll, hNotifiedDllBase;
+    FN_LdrRegisterDllNotification* pfnLdrRegisterDllNotification;
+    FN_LdrUnregisterDllNotification* pfnLdrUnregisterDllNotification;
+    NTSTATUS Status;
+    PVOID Cookie1, Cookie2;
+
+    /* Load functions */
+    hNtDll = GetModuleHandleW(L"ntdll.dll");
+    if (hNtDll == NULL)
+    {
+        skip("GetModuleHandleW for ntdll failed with 0x%08lX\n", 
GetLastError());
+        return;
+    }
+    pfnLdrRegisterDllNotification = 
(FN_LdrRegisterDllNotification*)GetProcAddress(hNtDll, 
"LdrRegisterDllNotification");
+    pfnLdrUnregisterDllNotification = 
(FN_LdrUnregisterDllNotification*)GetProcAddress(hNtDll, 
"LdrUnregisterDllNotification");
+    if (!pfnLdrRegisterDllNotification || !pfnLdrUnregisterDllNotification)
+    {
+        skip("ntdll.dll!Ldr[Un]RegisterDllNotification not found\n");
+        return;
+    }
+
+    /* Extract DLL to temp directory */
+    if (!GetTempPathW(ARRAYSIZE(szTempPath), szTempPath))
+    {
+        skip("GetTempPathW failed with 0x%08lX\n", GetLastError());
+        return;
+    }
+    if (GetTempFileNameW(szTempPath, L"DLN", 0, g_szDllPath) == 0)
+    {
+        skip("GetTempFileNameW failed with 0x%08lX\n", GetLastError());
+        return;
+    }
+    RtlInitUnicodeString(&g_usDllPath, g_szDllPath);
+    pszDllName = wcsrchr(g_szDllPath, L'\\') + 1;
+    if (pszDllName == NULL)
+    {
+        skip("Find file name of %ls failed\n", g_szDllPath);
+        return;
+    }
+    RtlInitUnicodeString(&g_usDllName, pszDllName);
+    if (!ExtractResource(g_szDllPath, RT_RCDATA, MAKEINTRESOURCEW(102)))
+    {
+        skip("ExtractResource failed with 0x%08lX\n", GetLastError());
+        return;
+    }
+
+    /* Register DLL load notification callback */
+    hNotifiedDllBase = NULL;
+    Cookie1 = NULL;
+    Cookie2 = NULL;
+    Status = pfnLdrRegisterDllNotification(0, DllLoadCallback, 
&hNotifiedDllBase, &Cookie1);
+    ok_eq_bool(NT_SUCCESS(Status), TRUE);
+    ok(Cookie1 != NULL, "Cookie1 is NULL\n");
+
+    /* Register the callback again is valid */
+    Status = pfnLdrRegisterDllNotification(0, DllLoadCallback, 
&hNotifiedDllBase, &Cookie2);
+    ok_eq_bool(NT_SUCCESS(Status), TRUE);
+    ok(Cookie2 != NULL, "Cookie2 is NULL\n");
+
+    /* Load the test DLL */
+    hTestDll = LoadLibraryW(g_szDllPath);
+    if (!hTestDll)
+    {
+        skip("LoadLibraryW failed with 0x%08lX\n", GetLastError());
+        goto _exit;
+    }
+
+    /* Verify the Dll base received in callback and returned via context */
+    ok_eq_pointer(hNotifiedDllBase, hTestDll);
+
+    /* The count should be 2 because the callback was registered twice */
+    ok_eq_long(g_lDllLoadCount, 2L);
+
+    /*
+     * Callback will not be triggered because following
+     * load and unload actions change the DLL reference count only
+     */
+    LoadLibraryW(g_szDllPath);
+    ok_eq_long(g_lDllLoadCount, 2L);
+    FreeLibrary(hTestDll);
+    ok_eq_long(g_lDllLoadCount, 2L);
+
+    /* Unregister the callback once */
+    Status = pfnLdrUnregisterDllNotification(Cookie1);
+    ok_eq_bool(NT_SUCCESS(Status), TRUE);
+
+    /* Unload the test DLL */
+    if (FreeLibrary(hTestDll))
+    {
+        /* The count will decrease 1 because the last callback still there */
+        ok_eq_long(g_lDllLoadCount, 1L);
+    }
+    else
+    {
+        skip("FreeLibrary failed with 0x%08lX\n", GetLastError());
+    }
+
+    /* Unregister the last callback */
+    Status = pfnLdrUnregisterDllNotification(Cookie2);
+    ok_eq_bool(NT_SUCCESS(Status), TRUE);
+
+_exit:
+    DeleteFileW(g_szDllPath);
+}
diff --git a/modules/rostests/apitests/ntdll/empty_dll/CMakeLists.txt 
b/modules/rostests/apitests/ntdll/empty_dll/CMakeLists.txt
new file mode 100644
index 00000000000..36513ed6703
--- /dev/null
+++ b/modules/rostests/apitests/ntdll/empty_dll/CMakeLists.txt
@@ -0,0 +1,6 @@
+
+add_library(empty_dll MODULE empty_dll.c)
+set_module_type(empty_dll win32dll ENTRYPOINT DllMain 12)
+add_importlibs(empty_dll kernel32 ntdll)
+add_dependencies(empty_dll psdk)
+add_rostests_file(TARGET empty_dll)
diff --git a/modules/rostests/apitests/ntdll/empty_dll/empty_dll.c 
b/modules/rostests/apitests/ntdll/empty_dll/empty_dll.c
new file mode 100644
index 00000000000..ddd4be786a8
--- /dev/null
+++ b/modules/rostests/apitests/ntdll/empty_dll/empty_dll.c
@@ -0,0 +1,11 @@
+#include <windows.h>
+
+BOOL
+WINAPI
+DllMain(
+    _In_ HINSTANCE hinstDLL,
+    _In_ DWORD fdwReason,
+    _In_ LPVOID lpvReserved)
+{
+    return TRUE;
+}
diff --git a/modules/rostests/apitests/ntdll/testdata.rc 
b/modules/rostests/apitests/ntdll/testdata.rc
index 3c16769902c..3937c7302ef 100644
--- a/modules/rostests/apitests/ntdll/testdata.rc
+++ b/modules/rostests/apitests/ntdll/testdata.rc
@@ -1,2 +1,3 @@
 
 101 10 "load_notifications.dll"
+102 10 "empty_dll.dll"
diff --git a/modules/rostests/apitests/ntdll/testlist.c 
b/modules/rostests/apitests/ntdll/testlist.c
index 86f653d5adf..c4da00ff0c0 100644
--- a/modules/rostests/apitests/ntdll/testlist.c
+++ b/modules/rostests/apitests/ntdll/testlist.c
@@ -3,6 +3,7 @@
 #define STANDALONE
 #include <apitest.h>
 
+extern void func_DllLoadNotification(void);
 extern void func_LdrEnumResources(void);
 extern void func_LdrLoadDll(void);
 extern void func_load_notifications(void);
@@ -107,6 +108,7 @@ extern void func_UserModeException(void);
 
 const struct test winetest_testlist[] =
 {
+    { "DllLoadNotification",            func_DllLoadNotification },
     { "LdrEnumResources",               func_LdrEnumResources },
     { "LdrLoadDll",                     func_LdrLoadDll },
     { "load_notifications",             func_load_notifications },
diff --git a/sdk/include/ndk/ldrfuncs.h b/sdk/include/ndk/ldrfuncs.h
index 62e334aacf7..bdade6abcf9 100644
--- a/sdk/include/ndk/ldrfuncs.h
+++ b/sdk/include/ndk/ldrfuncs.h
@@ -147,6 +147,23 @@ LdrEnumerateLoadedModules(
     _In_opt_ PVOID Context
 );
 
+#if (_WIN32_WINNT >= _WIN32_WINNT_VISTA) || (DLL_EXPORT_VERSION >= 
_WIN32_WINNT_VISTA)
+
+NTSTATUS
+NTAPI
+LdrRegisterDllNotification(
+    _In_ ULONG Flags,
+    _In_ PLDR_DLL_NOTIFICATION_FUNCTION NotificationFunction,
+    _In_opt_ PVOID Context,
+    _Out_ PVOID* Cookie);
+
+NTSTATUS
+NTAPI
+LdrUnregisterDllNotification(
+    _In_ PVOID Cookie);
+
+#endif /* (_WIN32_WINNT >= _WIN32_WINNT_VISTA) || (DLL_EXPORT_VERSION >= 
_WIN32_WINNT_VISTA) */
+
 #ifdef NTOS_MODE_USER
 NTSYSAPI
 BOOLEAN
diff --git a/sdk/include/ndk/ldrtypes.h b/sdk/include/ndk/ldrtypes.h
index 3b71d022887..4c16bd524c1 100644
--- a/sdk/include/ndk/ldrtypes.h
+++ b/sdk/include/ndk/ldrtypes.h
@@ -37,7 +37,11 @@ Author:
 //
 #define LDRP_STATIC_LINK                        0x00000002
 #define LDRP_IMAGE_DLL                          0x00000004
+#if (NTDDI_VERSION < NTDDI_WIN8)
 #define LDRP_SHIMENG_SUPPRESSED_ENTRY           0x00000008
+#else
+#define LDRP_LOAD_NOTIFICATIONS_SENT            0x00000008
+#endif
 #define LDRP_IMAGE_INTEGRITY_FORCED             0x00000020
 #define LDRP_LOAD_IN_PROGRESS                   0x00001000
 #define LDRP_UNLOAD_IN_PROGRESS                 0x00002000
@@ -196,26 +200,39 @@ typedef struct _LDR_ENUM_RESOURCE_INFO
 //
 // DLL Notifications
 //
+#define LDR_DLL_NOTIFICATION_REASON_LOADED   1
+#define LDR_DLL_NOTIFICATION_REASON_UNLOADED 2
+
 typedef struct _LDR_DLL_LOADED_NOTIFICATION_DATA
 {
     ULONG Flags;
-    PUNICODE_STRING FullDllName;
-    PUNICODE_STRING BaseDllName;
+    PCUNICODE_STRING FullDllName;
+    PCUNICODE_STRING BaseDllName;
     PVOID DllBase;
     ULONG SizeOfImage;
 } LDR_DLL_LOADED_NOTIFICATION_DATA, *PLDR_DLL_LOADED_NOTIFICATION_DATA;
 
-typedef VOID
-(NTAPI *PLDR_DLL_LOADED_NOTIFICATION_CALLBACK)(
-    _In_ BOOLEAN Type,
-    _In_ struct _LDR_DLL_LOADED_NOTIFICATION_DATA *Data
-);
+typedef struct _LDR_DLL_UNLOADED_NOTIFICATION_DATA
+{
+    ULONG Flags;
+    PCUNICODE_STRING FullDllName;
+    PCUNICODE_STRING BaseDllName;
+    PVOID DllBase;
+    ULONG SizeOfImage;
+} LDR_DLL_UNLOADED_NOTIFICATION_DATA, *PLDR_DLL_UNLOADED_NOTIFICATION_DATA;
 
-typedef struct _LDR_DLL_LOADED_NOTIFICATION_ENTRY
+typedef union _LDR_DLL_NOTIFICATION_DATA
 {
-    LIST_ENTRY NotificationListEntry;
-    PLDR_DLL_LOADED_NOTIFICATION_CALLBACK Callback;
-} LDR_DLL_LOADED_NOTIFICATION_ENTRY, *PLDR_DLL_LOADED_NOTIFICATION_ENTRY;
+    LDR_DLL_LOADED_NOTIFICATION_DATA Loaded;
+    LDR_DLL_UNLOADED_NOTIFICATION_DATA Unloaded;
+} LDR_DLL_NOTIFICATION_DATA, *PLDR_DLL_NOTIFICATION_DATA;
+typedef const LDR_DLL_NOTIFICATION_DATA *PCLDR_DLL_NOTIFICATION_DATA;
+
+typedef VOID
+(NTAPI *PLDR_DLL_NOTIFICATION_FUNCTION)(
+    _In_ ULONG NotificationReason,
+    _In_ PCLDR_DLL_NOTIFICATION_DATA NotificationData,
+    _In_opt_ PVOID Context);
 
 //
 // Alternate Resources Support
diff --git a/sdk/include/ndk/rtlfuncs.h b/sdk/include/ndk/rtlfuncs.h
index c9135cfc909..f506e595c18 100644
--- a/sdk/include/ndk/rtlfuncs.h
+++ b/sdk/include/ndk/rtlfuncs.h
@@ -39,6 +39,18 @@ extern "C" {
 //
 // List Functions
 //
+
+DECLSPEC_NORETURN
+FORCEINLINE
+VOID
+RtlFailFast(
+    _In_ ULONG Code)
+{
+    __fastfail(Code);
+}
+
+#define RTL_STATIC_LIST_HEAD(x) LIST_ENTRY x = { &x, &x }
+
 FORCEINLINE
 VOID
 InitializeListHead(
diff --git a/sdk/include/xdk/rtlfuncs.h b/sdk/include/xdk/rtlfuncs.h
index 6aeca552d6e..f35b8e28452 100644
--- a/sdk/include/xdk/rtlfuncs.h
+++ b/sdk/include/xdk/rtlfuncs.h
@@ -22,6 +22,9 @@ $if (_WDMDDK_ || _WINNT_)
 #define FAST_FAIL_MRDATA_MODIFIED               19
 #define FAST_FAIL_INVALID_FAST_FAIL_CODE        0xFFFFFFFF
 
+$endif(_WDMDDK_ || _WINNT_)
+$if (_WDMDDK_)
+
 DECLSPEC_NORETURN
 FORCEINLINE
 VOID
@@ -31,9 +34,6 @@ RtlFailFast(
   __fastfail(Code);
 }
 
-$endif(_WDMDDK_ || _WINNT_)
-$if (_WDMDDK_)
-
 #if !defined(NO_KERNEL_LIST_ENTRY_CHECKS) && (defined(_M_CEE_PURE) || 
defined(_M_CEE_SAFE))
 #define NO_KERNEL_LIST_ENTRY_CHECKS
 #endif

Reply via email to