Skip to content

Commit 299d79d

Browse files
committed
Add os::windows::Library::open_already_loaded()
1 parent 7e4e69d commit 299d79d

File tree

2 files changed

+59
-0
lines changed

2 files changed

+59
-0
lines changed

src/os/windows/mod.rs

+39
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,45 @@ impl Library {
119119
}
120120
}
121121

122+
/// Load a module that is already loaded by the program.
123+
///
124+
/// This function returns a `Library` corresponding to a module with the given name that is
125+
/// already mapped into the address space of the process. If the module isn't found an error is
126+
/// returned.
127+
///
128+
/// If the `filename` does not include a full path and there are multiple different loaded
129+
/// modules corresponding to the `filename`, it is impossible to predict which module handle
130+
/// will be returned. For more information refer to [MSDN].
131+
///
132+
/// If the `filename` specifies a library filename without path and with extension omitted,
133+
/// `.dll` extension is implicitly added. This behaviour may be suppressed by appending a
134+
/// trailing `.` to the `filename`.
135+
///
136+
/// This is equivalent to `GetModuleHandleExW(0, filename, _)`.
137+
///
138+
/// [MSDN]: https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulehandleexw
139+
pub fn open_already_loaded<P: AsRef<OsStr>>(filename: P) -> Result<Library, crate::Error> {
140+
let wide_filename: Vec<u16> = filename.as_ref().encode_wide().chain(Some(0)).collect();
141+
142+
let ret = unsafe {
143+
let mut handle: HMODULE = std::ptr::null_mut();
144+
with_get_last_error(|source| crate::Error::GetModuleHandleExW { source }, || {
145+
// Make sure no winapi calls as a result of drop happen inside this closure, because
146+
// otherwise that might change the return value of the GetLastError.
147+
let result = libloaderapi::GetModuleHandleExW(0, wide_filename.as_ptr(), &mut handle);
148+
if result == 0 {
149+
None
150+
} else {
151+
Some(Library(handle))
152+
}
153+
}).map_err(|e| e.unwrap_or(crate::Error::GetModuleHandleExWUnknown))
154+
};
155+
156+
drop(wide_filename); // Drop wide_filename here to ensure it doesn’t get moved and dropped
157+
// inside the closure by mistake. See comment inside the closure.
158+
ret
159+
}
160+
122161
/// Find and load a module, additionally adjusting behaviour with flags.
123162
///
124163
/// See [`Library::new`] for documentation on handling of the `filename` argument. See the

tests/functions.rs

+20
Original file line numberDiff line numberDiff line change
@@ -235,3 +235,23 @@ fn works_getlasterror0() {
235235
assert_eq!(errhandlingapi::GetLastError(), gle())
236236
}
237237
}
238+
239+
#[cfg(windows)]
240+
#[test]
241+
fn library_open_already_loaded() {
242+
use libloading::os::windows::Library;
243+
244+
// Present on Windows systems and NOT used by any other tests to prevent races.
245+
const LIBPATH: &str = "Msftedit.dll";
246+
247+
// Not loaded yet.
248+
assert!(match Library::open_already_loaded(LIBPATH) {
249+
Err(libloading::Error::GetModuleHandleExW { .. }) => true,
250+
_ => false,
251+
});
252+
253+
let _lib = Library::new(LIBPATH).unwrap();
254+
255+
// Loaded now.
256+
assert!(Library::open_already_loaded(LIBPATH).is_ok());
257+
}

0 commit comments

Comments
 (0)