diff --git a/.github/workflows/libloading.yml b/.github/workflows/libloading.yml index aa9b3f6b5..811681d5a 100644 --- a/.github/workflows/libloading.yml +++ b/.github/workflows/libloading.yml @@ -6,7 +6,7 @@ on: - '*.mkd' - 'LICENSE' pull_request: - types: [opened, repoened, synchronize] + types: [opened, reopened, synchronize] jobs: native-test: diff --git a/src/error.rs b/src/error.rs index 6ba536709..e1c465e17 100644 --- a/src/error.rs +++ b/src/error.rs @@ -55,7 +55,7 @@ pub enum Error { /// The source error. source: WindowsError }, - /// The `LoadLibraryW` call failed and system did not report an error. + /// The `GetModuleHandleExW` call failed and system did not report an error. GetModuleHandleExWUnknown, /// The `GetProcAddress` call failed. GetProcAddress { diff --git a/src/os/windows/mod.rs b/src/os/windows/mod.rs index da1b5c97d..f1298f619 100644 --- a/src/os/windows/mod.rs +++ b/src/os/windows/mod.rs @@ -119,6 +119,45 @@ impl Library { } } + /// Load a module that is already loaded by the program. + /// + /// This function returns a `Library` corresponding to a module with the given name that is + /// already mapped into the address space of the process. If the module isn't found an error is + /// returned. + /// + /// If the `filename` does not include a full path and there are multiple different loaded + /// modules corresponding to the `filename`, it is impossible to predict which module handle + /// will be returned. For more information refer to [MSDN]. + /// + /// If the `filename` specifies a library filename without path and with extension omitted, + /// `.dll` extension is implicitly added. This behaviour may be suppressed by appending a + /// trailing `.` to the `filename`. + /// + /// This is equivalent to `GetModuleHandleExW(0, filename, _)`. + /// + /// [MSDN]: https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulehandleexw + pub fn open_already_loaded>(filename: P) -> Result { + let wide_filename: Vec = filename.as_ref().encode_wide().chain(Some(0)).collect(); + + let ret = unsafe { + let mut handle: HMODULE = std::ptr::null_mut(); + 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. + let result = libloaderapi::GetModuleHandleExW(0, wide_filename.as_ptr(), &mut handle); + if result == 0 { + None + } else { + Some(Library(handle)) + } + }).map_err(|e| e.unwrap_or(crate::Error::GetModuleHandleExWUnknown)) + }; + + drop(wide_filename); // Drop wide_filename here to ensure it doesn’t get moved and dropped + // inside the closure by mistake. See comment inside the closure. + ret + } + /// Find and load a module, additionally adjusting behaviour with flags. /// /// See [`Library::new`] for documentation on handling of the `filename` argument. See the diff --git a/tests/functions.rs b/tests/functions.rs index e8993446a..35045e808 100644 --- a/tests/functions.rs +++ b/tests/functions.rs @@ -235,3 +235,23 @@ fn works_getlasterror0() { assert_eq!(errhandlingapi::GetLastError(), gle()) } } + +#[cfg(windows)] +#[test] +fn library_open_already_loaded() { + use libloading::os::windows::Library; + + // Present on Windows systems and NOT used by any other tests to prevent races. + const LIBPATH: &str = "Msftedit.dll"; + + // Not loaded yet. + assert!(match Library::open_already_loaded(LIBPATH) { + Err(libloading::Error::GetModuleHandleExW { .. }) => true, + _ => false, + }); + + let _lib = Library::new(LIBPATH).unwrap(); + + // Loaded now. + assert!(Library::open_already_loaded(LIBPATH).is_ok()); +}