diff --git a/src/os/windows/mod.rs b/src/os/windows/mod.rs index 1cbc57be3..59fa69a5e 100644 --- a/src/os/windows/mod.rs +++ b/src/os/windows/mod.rs @@ -171,6 +171,44 @@ impl Library { ret } + /// Attempts to pin the module represented by the current `Library` into memory. + /// + /// Calls `GetModuleHandleExW` with the flag `GET_MODULE_HANDLE_EX_FLAG_PIN` to pin the module. + /// See the [MSDN documentation][msdn] for more information. + /// + /// [msdn]: https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulehandleexw + /// + /// If successful, the module will remain in memory regardless of the refcount for this `Library` + pub fn pin(&self) -> Result<(), crate::Error> { + const GET_MODULE_HANDLE_EX_FLAG_PIN: u32 = 0x1; + const GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS: u32 = 0x4; + unsafe { + let mut handle: HMODULE = 0; + with_get_last_error( + |source| crate::Error::GetModuleHandleExW { source }, + || { + // Make sure no winapi calls as a result of drop happen inside this closure, because + // otherwise that might change the return value of the GetLastError. + + // We use our cached module handle of this `Library` instead of the module name. This works + // if we also pass the flag `GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS` because on Windows, module handles + // are the loaded base address of the module. + let result = GetModuleHandleExW( + GET_MODULE_HANDLE_EX_FLAG_PIN | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, + self.0 as *const u16, + &mut handle, + ); + if result == 0 { + None + } else { + Some(()) + } + }, + ) + .map_err(|e| e.unwrap_or(crate::Error::GetModuleHandleExWUnknown)) + } + } + /// Get a pointer to a function or static variable by symbol name. /// /// The `symbol` may not contain any null bytes, with the exception of the last byte. A null diff --git a/tests/functions.rs b/tests/functions.rs index c94592e7a..84d5267d9 100644 --- a/tests/functions.rs +++ b/tests/functions.rs @@ -279,6 +279,17 @@ fn works_getlasterror0() { } } +#[cfg(windows)] +#[test] +fn works_pin_module() { + use libloading::os::windows::Library; + + unsafe { + let lib = Library::new("kernel32.dll").unwrap(); + lib.pin().unwrap(); + } +} + #[cfg(windows)] #[test] fn library_open_already_loaded() {