// A hack for docs.rs to build documentation that has both windows and linux documentation in the
// same rustdoc build visible.
#[cfg(all(docsrs, not(windows)))]
mod windows_imports {
pub(super) enum WORD {}
pub(super) struct DWORD;
pub(super) enum HMODULE {}
pub(super) enum FARPROC {}
pub(super) mod consts {
use super::DWORD;
pub(crate) const LOAD_IGNORE_CODE_AUTHZ_LEVEL: DWORD = DWORD;
pub(crate) const LOAD_LIBRARY_AS_DATAFILE: DWORD = DWORD;
pub(crate) const LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE: DWORD = DWORD;
pub(crate) const LOAD_LIBRARY_AS_IMAGE_RESOURCE: DWORD = DWORD;
pub(crate) const LOAD_LIBRARY_SEARCH_APPLICATION_DIR: DWORD = DWORD;
pub(crate) const LOAD_LIBRARY_SEARCH_DEFAULT_DIRS: DWORD = DWORD;
pub(crate) const LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR: DWORD = DWORD;
pub(crate) const LOAD_LIBRARY_SEARCH_SYSTEM32: DWORD = DWORD;
pub(crate) const LOAD_LIBRARY_SEARCH_USER_DIRS: DWORD = DWORD;
pub(crate) const LOAD_WITH_ALTERED_SEARCH_PATH: DWORD = DWORD;
pub(crate) const LOAD_LIBRARY_REQUIRE_SIGNED_TARGET: DWORD = DWORD;
pub(crate) const LOAD_LIBRARY_SAFE_CURRENT_DIRS: DWORD = DWORD;
}
}
#[cfg(any(not(docsrs), windows))]
mod windows_imports {
extern crate winapi;
pub(super) use self::winapi::shared::minwindef::{WORD, DWORD, HMODULE, FARPROC};
pub(super) use self::winapi::shared::ntdef::WCHAR;
pub(super) use self::winapi::um::{errhandlingapi, libloaderapi};
pub(super) use std::os::windows::ffi::{OsStrExt, OsStringExt};
pub(super) const SEM_FAILCE: DWORD = 1;
pub(super) mod consts {
pub(crate) use super::winapi::um::libloaderapi::{
LOAD_IGNORE_CODE_AUTHZ_LEVEL,
LOAD_LIBRARY_AS_DATAFILE,
LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE,
LOAD_LIBRARY_AS_IMAGE_RESOURCE,
LOAD_LIBRARY_SEARCH_APPLICATION_DIR,
LOAD_LIBRARY_SEARCH_DEFAULT_DIRS,
LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR,
LOAD_LIBRARY_SEARCH_SYSTEM32,
LOAD_LIBRARY_SEARCH_USER_DIRS,
LOAD_WITH_ALTERED_SEARCH_PATH,
LOAD_LIBRARY_REQUIRE_SIGNED_TARGET,
LOAD_LIBRARY_SAFE_CURRENT_DIRS,
};
}
}
use self::windows_imports::*;
use util::{ensure_compatible_types, cstr_cow_from_bytes};
use std::ffi::{OsStr, OsString};
use std::{fmt, io, marker, mem, ptr};
/// A platform-specific counterpart of the cross-platform [`Library`](crate::Library).
pub struct Library(HMODULE);
unsafe impl Send for Library {}
// Now, this is sort-of-tricky. MSDN documentation does not really make any claims as to safety of
// the Win32 APIs. Sadly, whomever I asked, even current and former Microsoft employees, couldn’t
// say for sure, whether the Win32 APIs used to implement `Library` are thread-safe or not.
//
// My investigation ended up with a question about thread-safety properties of the API involved
// being sent to an internal (to MS) general question mailing-list. The conclusion of the mail is
// as such:
//
// * Nobody inside MS (at least out of all the people who have seen the question) knows for
// sure either;
// * However, the general consensus between MS developers is that one can rely on the API being
// thread-safe. In case it is not thread-safe it should be considered a bug on the Windows
// part. (NB: bugs filled at https://connect.microsoft.com/ against Windows Server)
unsafe impl Sync for Library {}
impl Library {
/// Find and load a module.
///
/// If the `filename` specifies a full path, the function only searches that path for the
/// module. Otherwise, if the `filename` specifies a relative path or a module name without a
/// path, the function uses a windows-specific search strategy to find the module; for more
/// information, see the [Remarks on MSDN][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 [Library::load_with_flags](filename, 0).
///
/// [msdn]: https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryw#remarks
///
/// # Safety
///
/// When a library is loaded initialization routines contained within the library are executed.
/// For the purposes of safety, execution of these routines is conceptually the same calling an
/// unknown foreign function and may impose arbitrary requirements on the caller for the call
/// to be sound.
///
/// Additionally, the callers of this function must also ensure that execution of the
/// termination routines contained within the library is safe as well. These routines may be
/// executed when the library is unloaded.
#[inline]
pub unsafe fn new>(filename: P) -> Result {
Library::load_with_flags(filename, 0)
}
/// Get the `Library` representing the original program executable.
///
/// Note that behaviour of `Library` loaded with this method is different from
/// Libraries loaded with [`os::unix::Library::this`]. For more information refer to [MSDN].
///
/// Corresponds to `GetModuleHandleExW(0, NULL, _)`.
///
/// [`os::unix::Library::this`]: crate::os::unix::Library::this
/// [MSDN]: https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulehandleexw
pub fn this() -> Result {
unsafe {
let mut handle: HMODULE = std::ptr::null_mut();
with_get_last_error(|source| crate::Error::GetModuleHandleExW { source }, || {
let result = libloaderapi::GetModuleHandleExW(0, std::ptr::null_mut(), &mut handle);
if result == 0 {
None
} else {
Some(Library(handle))
}
}).map_err(|e| e.unwrap_or(crate::Error::GetModuleHandleExWUnknown))
}
}
/// Get 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
/// [flag table on MSDN][flags] for information on applicable values for the `flags` argument.
///
/// Corresponds to `LoadLibraryExW(filename, reserved: NULL, flags)`.
///
/// [flags]: https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw#parameters
///
/// # Safety
///
/// When a library is loaded initialization routines contained within the library are executed.
/// For the purposes of safety, execution of these routines is conceptually the same calling an
/// unknown foreign function and may impose arbitrary requirements on the caller for the call
/// to be sound.
///
/// Additionally, the callers of this function must also ensure that execution of the
/// termination routines contained within the library is safe as well. These routines may be
/// executed when the library is unloaded.
pub unsafe fn load_with_flags>(filename: P, flags: DWORD) -> Result {
let wide_filename: Vec = filename.as_ref().encode_wide().chain(Some(0)).collect();
let _guard = ErrorModeGuard::new();
let ret = with_get_last_error(|source| crate::Error::LoadLibraryExW { 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 handle =
libloaderapi::LoadLibraryExW(wide_filename.as_ptr(), std::ptr::null_mut(), flags);
if handle.is_null() {
None
} else {
Some(Library(handle))
}
}).map_err(|e| e.unwrap_or(crate::Error::LoadLibraryExWUnknown));
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
}
/// Get a pointer to function or static variable by symbol name.
///
/// The `symbol` may not contain any null bytes, with an exception of last byte. A null
/// terminated `symbol` may avoid a string allocation in some cases.
///
/// Symbol is interpreted as-is; no mangling is done. This means that symbols like `x::y` are
/// most likely invalid.
///
/// # Safety
///
/// Users of this API must specify the correct type of the function or variable loaded. Using a
/// `Symbol` with a wrong type is undefined.
pub unsafe fn get(&self, symbol: &[u8]) -> Result, crate::Error> {
ensure_compatible_types::()?;
let symbol = cstr_cow_from_bytes(symbol)?;
with_get_last_error(|source| crate::Error::GetProcAddress { source }, || {
let symbol = libloaderapi::GetProcAddress(self.0, symbol.as_ptr());
if symbol.is_null() {
None
} else {
Some(Symbol {
pointer: symbol,
pd: marker::PhantomData
})
}
}).map_err(|e| e.unwrap_or(crate::Error::GetProcAddressUnknown))
}
/// Get a pointer to function or static variable by ordinal number.
///
/// # Safety
///
/// Users of this API must specify the correct type of the function or variable loaded. Using a
/// `Symbol` with a wrong type is undefined.
pub unsafe fn get_ordinal(&self, ordinal: WORD) -> Result, crate::Error> {
ensure_compatible_types::()?;
with_get_last_error(|source| crate::Error::GetProcAddress { source }, || {
let ordinal = ordinal as usize as *mut _;
let symbol = libloaderapi::GetProcAddress(self.0, ordinal);
if symbol.is_null() {
None
} else {
Some(Symbol {
pointer: symbol,
pd: marker::PhantomData
})
}
}).map_err(|e| e.unwrap_or(crate::Error::GetProcAddressUnknown))
}
/// Convert the `Library` to a raw handle.
pub fn into_raw(self) -> HMODULE {
let handle = self.0;
mem::forget(self);
handle
}
/// Convert a raw handle to a `Library`.
///
/// # Safety
///
/// The handle shall be a result of a successful call of `LoadLibraryA`, `LoadLibraryW`,
/// `LoadLibraryExW`, `LoadLibraryExA` or a handle previously returned by the
/// `Library::into_raw` call.
pub unsafe fn from_raw(handle: HMODULE) -> Library {
Library(handle)
}
/// Unload the library.
///
/// You only need to call this if you are interested in handling any errors that may arise when
/// library is unloaded. Otherwise this will be done when `Library` is dropped.
///
/// The underlying data structures may still get leaked if an error does occur.
pub fn close(self) -> Result<(), crate::Error> {
let result = with_get_last_error(|source| crate::Error::FreeLibrary { source }, || {
if unsafe { libloaderapi::FreeLibrary(self.0) == 0 } {
None
} else {
Some(())
}
}).map_err(|e| e.unwrap_or(crate::Error::FreeLibraryUnknown));
// While the library is not free'd yet in case of an error, there is no reason to try
// dropping it again, because all that will do is try calling `FreeLibrary` again. only
// this time it would ignore the return result, which we already seen failing…
std::mem::forget(self);
result
}
}
impl Drop for Library {
fn drop(&mut self) {
unsafe { libloaderapi::FreeLibrary(self.0); }
}
}
impl fmt::Debug for Library {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
unsafe {
// FIXME: use Maybeuninit::uninit_array when stable
let mut buf =
mem::MaybeUninit::<[mem::MaybeUninit::; 1024]>::uninit().assume_init();
let len = libloaderapi::GetModuleFileNameW(self.0,
(&mut buf[..]).as_mut_ptr().cast(), 1024) as usize;
if len == 0 {
f.write_str(&format!("Library@{:p}", self.0))
} else {
let string: OsString = OsString::from_wide(
// FIXME: use Maybeuninit::slice_get_ref when stable
&*(&buf[..len] as *const [_] as *const [WCHAR])
);
f.write_str(&format!("Library@{:p} from {:?}", self.0, string))
}
}
}
}
/// Symbol from a library.
///
/// A major difference compared to the cross-platform `Symbol` is that this does not ensure the
/// `Symbol` does not outlive `Library` it comes from.
pub struct Symbol {
pointer: FARPROC,
pd: marker::PhantomData
}
impl Symbol {
/// Convert the loaded Symbol into a handle.
pub fn into_raw(self) -> FARPROC {
let pointer = self.pointer;
mem::forget(self);
pointer
}
}
impl Symbol