Skip to content

Commit 990a918

Browse files
authored
Allow pinning win modules (#178)
Linux modules can be pinned by passing flags like RTLD_NODELETE to dlopen. Similar functionality exists on Windows but is not currently exposed. To pin on windows you pass the flag GET_MODULE_HANDLE_EX_FLAG_PIN to GetModuleHandleEx. This PR aims to permit module pinning for Windows by exposing a function to handle this.
1 parent 2fe04f2 commit 990a918

File tree

2 files changed

+49
-0
lines changed

2 files changed

+49
-0
lines changed

src/os/windows/mod.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,44 @@ impl Library {
171171
ret
172172
}
173173

174+
/// Attempts to pin the module represented by the current `Library` into memory.
175+
///
176+
/// Calls `GetModuleHandleExW` with the flag `GET_MODULE_HANDLE_EX_FLAG_PIN` to pin the module.
177+
/// See the [MSDN documentation][msdn] for more information.
178+
///
179+
/// [msdn]: https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulehandleexw
180+
///
181+
/// If successful, the module will remain in memory regardless of the refcount for this `Library`
182+
pub fn pin(&self) -> Result<(), crate::Error> {
183+
const GET_MODULE_HANDLE_EX_FLAG_PIN: u32 = 0x1;
184+
const GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS: u32 = 0x4;
185+
unsafe {
186+
let mut handle: HMODULE = 0;
187+
with_get_last_error(
188+
|source| crate::Error::GetModuleHandleExW { source },
189+
|| {
190+
// Make sure no winapi calls as a result of drop happen inside this closure, because
191+
// otherwise that might change the return value of the GetLastError.
192+
193+
// We use our cached module handle of this `Library` instead of the module name. This works
194+
// if we also pass the flag `GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS` because on Windows, module handles
195+
// are the loaded base address of the module.
196+
let result = GetModuleHandleExW(
197+
GET_MODULE_HANDLE_EX_FLAG_PIN | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
198+
self.0 as *const u16,
199+
&mut handle,
200+
);
201+
if result == 0 {
202+
None
203+
} else {
204+
Some(())
205+
}
206+
},
207+
)
208+
.map_err(|e| e.unwrap_or(crate::Error::GetModuleHandleExWUnknown))
209+
}
210+
}
211+
174212
/// Get a pointer to a function or static variable by symbol name.
175213
///
176214
/// The `symbol` may not contain any null bytes, with the exception of the last byte. A null

tests/functions.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,17 @@ fn works_getlasterror0() {
279279
}
280280
}
281281

282+
#[cfg(windows)]
283+
#[test]
284+
fn works_pin_module() {
285+
use libloading::os::windows::Library;
286+
287+
unsafe {
288+
let lib = Library::new("kernel32.dll").unwrap();
289+
lib.pin().unwrap();
290+
}
291+
}
292+
282293
#[cfg(windows)]
283294
#[test]
284295
fn library_open_already_loaded() {

0 commit comments

Comments
 (0)