https://git.reactos.org/?p=reactos.git;a=commitdiff;h=ccf1e97aa1bd056b4d4b7…
commit ccf1e97aa1bd056b4d4b7ce6fae1a4b089c6c25b
Author: Ratin Gao <ratin(a)knsoft.org>
AuthorDate: Mon Mar 3 04:13:33 2025 +0800
Commit: GitHub <noreply(a)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-notificat…
- [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 <ratin(a)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 <ratin(a)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