Signed-off-by: Paul Gofman <pgofman(a)codeweavers.com>
---
dlls/kernel32/tests/module.c | 107 +++++++++++++++++++++++++++++++++++
dlls/ntdll/loader.c | 44 ++++++++++++--
dlls/ntdll/ntdll.spec | 1 +
include/winternl.h | 5 ++
4 files changed, 152 insertions(+), 5 deletions(-)
diff --git a/dlls/kernel32/tests/module.c b/dlls/kernel32/tests/module.c
index 0066f7de5a8..5ca137ad6ba 100644
--- a/dlls/kernel32/tests/module.c
+++ b/dlls/kernel32/tests/module.c
@@ -39,6 +39,9 @@ static BOOL (WINAPI *pK32GetModuleInformation)(HANDLE process, HMODULE module,
static NTSTATUS (WINAPI *pLdrGetDllDirectory)(UNICODE_STRING*);
static NTSTATUS (WINAPI *pLdrSetDllDirectory)(UNICODE_STRING*);
+static NTSTATUS (WINAPI *pLdrGetDllHandle)( LPCWSTR load_path, ULONG flags, const UNICODE_STRING *name, HMODULE *base );
+static NTSTATUS (WINAPI *pLdrGetDllHandleEx)( ULONG flags, LPCWSTR load_path, ULONG *dll_characteristics,
+ const UNICODE_STRING *name, HMODULE *base );
static BOOL is_unicode_enabled = TRUE;
@@ -826,6 +829,8 @@ static void init_pointers(void)
mod = GetModuleHandleA( "ntdll.dll" );
MAKEFUNC(LdrGetDllDirectory);
MAKEFUNC(LdrSetDllDirectory);
+ MAKEFUNC(LdrGetDllHandle);
+ MAKEFUNC(LdrGetDllHandleEx);
#undef MAKEFUNC
/* before Windows 7 this was not exported in kernel32 */
@@ -1143,6 +1148,107 @@ static void test_SetDefaultDllDirectories(void)
pSetDefaultDllDirectories( LOAD_LIBRARY_SEARCH_DEFAULT_DIRS );
}
+static void check_refcount( HMODULE mod, unsigned int refcount )
+{
+ unsigned int i;
+ BOOL ret;
+
+ for (i = 0; i < min( refcount, 10 ); ++i)
+ {
+ ret = FreeLibrary( mod );
+ ok( ret || broken( refcount == ~0u && GetLastError() == ERROR_MOD_NOT_FOUND && i == 2 ) /* Win8 */,
+ "Refcount test failed, i %u, error %u.\n", i, GetLastError() );
+ if (!ret) return;
+ }
+ if (refcount != ~0u)
+ {
+ ret = FreeLibrary( mod );
+ ok( !ret && GetLastError() == ERROR_MOD_NOT_FOUND, "Refcount test failed, ret %d, error %u.\n",
+ ret, GetLastError() );
+ }
+}
+
+static void test_LdrGetDllHandleEx(void)
+{
+ HMODULE mod, loaded_mod;
+ UNICODE_STRING name;
+ NTSTATUS status;
+ unsigned int i;
+
+ if (!pLdrGetDllHandleEx)
+ {
+ win_skip( "LdrGetDllHandleEx is not available.\n" );
+ return;
+ }
+
+ RtlInitUnicodeString( &name, L"unknown.dll" );
+ status = pLdrGetDllHandleEx( 0, NULL, NULL, &name, &mod );
+ ok( status == STATUS_DLL_NOT_FOUND, "Got unexpected status %#x.\n", status );
+
+ RtlInitUnicodeString( &name, L"authz.dll" );
+ loaded_mod = LoadLibraryW( name.Buffer );
+ ok( !!loaded_mod, "Failed to load module.\n" );
+ status = pLdrGetDllHandleEx( 0, NULL, NULL, &name, &mod );
+ ok( !status, "Got unexpected status %#x.\n", status );
+ ok( mod == loaded_mod, "got %p\n", mod );
+ winetest_push_context( "Flags 0" );
+ check_refcount( loaded_mod, 2 );
+ winetest_pop_context();
+
+ loaded_mod = LoadLibraryW( name.Buffer );
+ ok( !!loaded_mod, "Failed to load module.\n" );
+ status = pLdrGetDllHandleEx( LDR_GET_DLL_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, NULL,
+ NULL, &name, &mod );
+ ok( !status, "Got unexpected status %#x.\n", status );
+ ok( mod == loaded_mod, "got %p\n", mod );
+ winetest_push_context( "LDR_GET_DLL_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT" );
+ check_refcount( loaded_mod, 1 );
+ winetest_pop_context();
+
+ loaded_mod = LoadLibraryW( name.Buffer );
+ ok( !!loaded_mod, "Failed to load module.\n" );
+ status = pLdrGetDllHandle( NULL, ~0u, &name, &mod );
+ ok( !status, "Got unexpected status %#x.\n", status );
+ ok( mod == loaded_mod, "got %p\n", mod );
+ winetest_push_context( "LdrGetDllHandle" );
+ check_refcount( loaded_mod, 1 );
+ winetest_pop_context();
+
+ loaded_mod = LoadLibraryW( name.Buffer );
+ ok( !!loaded_mod, "Failed to load module.\n" );
+ status = pLdrGetDllHandleEx( 4, NULL, NULL, (void *)&name, &mod );
+ ok( !status, "Got unexpected status %#x.\n", status );
+ ok( mod == loaded_mod, "got %p\n", mod );
+ winetest_push_context( "Flag 4" );
+ check_refcount( loaded_mod, 2 );
+ winetest_pop_context();
+
+ for (i = 3; i < 32; ++i)
+ {
+ loaded_mod = LoadLibraryW( name.Buffer );
+ ok( !!loaded_mod, "Failed to load module.\n" );
+ status = pLdrGetDllHandleEx( 1 << i, NULL, NULL, &name, &mod );
+ ok( status == STATUS_INVALID_PARAMETER, "Got unexpected status %#x.\n", status );
+ winetest_push_context( "Invalid flags, i %u", i );
+ check_refcount( loaded_mod, 1 );
+ winetest_pop_context();
+ }
+
+ status = pLdrGetDllHandleEx( LDR_GET_DLL_HANDLE_EX_FLAG_PIN | LDR_GET_DLL_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
+ NULL, NULL, &name, &mod );
+ ok( status == STATUS_INVALID_PARAMETER, "Got unexpected status %#x.\n", status );
+
+ loaded_mod = LoadLibraryW( name.Buffer );
+ ok( !!loaded_mod, "Failed to load module.\n" );
+ status = pLdrGetDllHandleEx( LDR_GET_DLL_HANDLE_EX_FLAG_PIN, NULL,
+ NULL, &name, &mod );
+ ok( !status, "Got unexpected status %#x.\n", status );
+ ok( mod == loaded_mod, "got %p\n", mod );
+ winetest_push_context( "LDR_GET_DLL_HANDLE_EX_FLAG_PIN" );
+ check_refcount( loaded_mod, ~0u );
+ winetest_pop_context();
+}
+
START_TEST(module)
{
WCHAR filenameW[MAX_PATH];
@@ -1175,4 +1281,5 @@ START_TEST(module)
testK32GetModuleInformation();
test_AddDllDirectory();
test_SetDefaultDllDirectories();
+ test_LdrGetDllHandleEx();
}
diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c
index 819cf51b9fc..3e3f991eba2 100644
--- a/dlls/ntdll/loader.c
+++ b/dlls/ntdll/loader.c
@@ -2996,16 +2996,33 @@ NTSTATUS WINAPI DECLSPEC_HOTPATCH LdrLoadDll(LPCWSTR path_name, DWORD flags,
/******************************************************************
- * LdrGetDllHandle (NTDLL.@)
+ * LdrGetDllHandleEx (NTDLL.@)
*/
-NTSTATUS WINAPI LdrGetDllHandle( LPCWSTR load_path, ULONG flags, const UNICODE_STRING *name, HMODULE *base )
+NTSTATUS WINAPI LdrGetDllHandleEx( ULONG flags, LPCWSTR load_path, ULONG *dll_characteristics,
+ const UNICODE_STRING *name, HMODULE *base )
{
- NTSTATUS status;
+ static const ULONG supported_flags = LDR_GET_DLL_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT
+ | LDR_GET_DLL_HANDLE_EX_FLAG_PIN;
+ static const ULONG valid_flags = LDR_GET_DLL_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT
+ | LDR_GET_DLL_HANDLE_EX_FLAG_PIN | 4;
+ SECTION_IMAGE_INFORMATION image_info;
UNICODE_STRING nt_name;
+ struct file_id id;
+ NTSTATUS status;
WINE_MODREF *wm;
HANDLE mapping;
- SECTION_IMAGE_INFORMATION image_info;
- struct file_id id;
+
+ TRACE( "flag %#x, load_path %p, dll_characteristics %p, name %p, base %p.\n",
+ flags, load_path, dll_characteristics, name, base );
+
+ if (flags & ~valid_flags) return STATUS_INVALID_PARAMETER;
+
+ if ((flags & (LDR_GET_DLL_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | LDR_GET_DLL_HANDLE_EX_FLAG_PIN))
+ == (LDR_GET_DLL_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | LDR_GET_DLL_HANDLE_EX_FLAG_PIN))
+ return STATUS_INVALID_PARAMETER;
+
+ if (flags & ~supported_flags) FIXME( "Unsupported flags %#x.\n", flags );
+ if (dll_characteristics) FIXME( "dll_characteristics unsupported.\n" );
RtlEnterCriticalSection( &loader_section );
@@ -3019,12 +3036,29 @@ NTSTATUS WINAPI LdrGetDllHandle( LPCWSTR load_path, ULONG flags, const UNICODE_S
}
RtlFreeUnicodeString( &nt_name );
+ if (!status)
+ {
+ if (flags & LDR_GET_DLL_HANDLE_EX_FLAG_PIN)
+ LdrAddRefDll( LDR_ADDREF_DLL_PIN, *base );
+ else if (!(flags & LDR_GET_DLL_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT))
+ LdrAddRefDll( 0, *base );
+ }
+
RtlLeaveCriticalSection( &loader_section );
TRACE( "%s -> %p (load path %s)\n", debugstr_us(name), status ? NULL : *base, debugstr_w(load_path) );
return status;
}
+/******************************************************************
+ * LdrGetDllHandle (NTDLL.@)
+ */
+NTSTATUS WINAPI LdrGetDllHandle( LPCWSTR load_path, ULONG flags, const UNICODE_STRING *name, HMODULE *base )
+{
+ return LdrGetDllHandleEx( LDR_GET_DLL_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, load_path, NULL, name, base );
+}
+
+
/******************************************************************
* LdrAddRefDll (NTDLL.@)
*/
diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec
index 9837b09fd51..0f8ddbea669 100644
--- a/dlls/ntdll/ntdll.spec
+++ b/dlls/ntdll/ntdll.spec
@@ -88,6 +88,7 @@
@ stub LdrFlushAlternateResourceModules
@ stdcall LdrGetDllDirectory(ptr)
@ stdcall LdrGetDllHandle(wstr long ptr ptr)
+@ stdcall LdrGetDllHandleEx(long ptr ptr ptr ptr)
# @ stub LdrGetDllHandleEx
@ stdcall LdrGetDllPath(wstr long ptr ptr)
@ stdcall LdrGetProcedureAddress(ptr ptr long ptr)
diff --git a/include/winternl.h b/include/winternl.h
index 9e96591ad8e..7a3a5e94702 100644
--- a/include/winternl.h
+++ b/include/winternl.h
@@ -3380,6 +3380,10 @@ typedef void (CALLBACK *PLDR_DLL_NOTIFICATION_FUNCTION)(ULONG, LDR_DLL_NOTIFICAT
/* flag for LdrAddRefDll */
#define LDR_ADDREF_DLL_PIN 0x00000001
+/* flags for LdrGetDllHandleEx */
+#define LDR_GET_DLL_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT 0x00000001
+#define LDR_GET_DLL_HANDLE_EX_FLAG_PIN 0x00000002
+
#define LDR_DLL_NOTIFICATION_REASON_LOADED 1
#define LDR_DLL_NOTIFICATION_REASON_UNLOADED 2
@@ -3778,6 +3782,7 @@ NTSYSAPI NTSTATUS WINAPI LdrFindResourceDirectory_U(HMODULE,const LDR_RESOURCE_
NTSYSAPI NTSTATUS WINAPI LdrFindResource_U(HMODULE,const LDR_RESOURCE_INFO*,ULONG,const IMAGE_RESOURCE_DATA_ENTRY**);
NTSYSAPI NTSTATUS WINAPI LdrGetDllDirectory(UNICODE_STRING*);
NTSYSAPI NTSTATUS WINAPI LdrGetDllHandle(LPCWSTR, ULONG, const UNICODE_STRING*, HMODULE*);
+NTSYSAPI NTSTATUS WINAPI LdrGetDllHandleEx(ULONG, LPCWSTR, ULONG *, const UNICODE_STRING*, HMODULE*);
NTSYSAPI NTSTATUS WINAPI LdrGetDllPath(PCWSTR,ULONG,PWSTR*,PWSTR*);
NTSYSAPI NTSTATUS WINAPI LdrGetProcedureAddress(HMODULE, const ANSI_STRING*, ULONG, void**);
NTSYSAPI NTSTATUS WINAPI LdrLoadDll(LPCWSTR, DWORD, const UNICODE_STRING*, HMODULE*);
--
2.31.1