use std::ffi::{CStr, OsStr, c_void};
use std::os::windows::ffi::OsStrExt;
use std::ptr::NonNull;
use windows_sys::Win32::Foundation::{FreeLibrary, GetLastError};
use windows_sys::Win32::System::Diagnostics::Debug::{
FORMAT_MESSAGE_FROM_SYSTEM, FORMAT_MESSAGE_IGNORE_INSERTS, FormatMessageW,
};
use windows_sys::Win32::System::LibraryLoader::{GetProcAddress, LoadLibraryExW};
use super::Lookup;
use crate::{Error, Result};
#[derive(Debug)]
pub(crate) struct Handle(NonNull<c_void>);
unsafe impl Send for Handle {}
unsafe impl Sync for Handle {}
fn last_error() -> String {
let code = unsafe { GetLastError() };
let mut buf = [0u16; 512];
let len = unsafe {
FormatMessageW(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
std::ptr::null(),
code,
0,
buf.as_mut_ptr(),
buf.len() as u32,
std::ptr::null(),
)
};
if len == 0 {
return format!("error code {code}");
}
String::from_utf16_lossy(&buf[..len as usize])
.trim_end()
.to_owned()
}
pub(crate) unsafe fn open(path: &OsStr) -> Result<Handle> {
let wide: Vec<u16> = path.encode_wide().chain(Some(0)).collect();
if let Some(position) = wide[..wide.len() - 1].iter().position(|&c| c == 0) {
return Err(Error::InteriorNul { position });
}
let handle = unsafe { LoadLibraryExW(wide.as_ptr(), std::ptr::null_mut(), 0) };
NonNull::new(handle).map(Handle).ok_or_else(|| Error::Open {
path: path.to_owned(),
message: last_error(),
})
}
pub(crate) fn lookup(handle: &Handle, name: &CStr) -> Lookup {
match unsafe { GetProcAddress(handle.0.as_ptr(), name.as_ptr().cast()) } {
Some(f) => Lookup::Found(f as *mut c_void),
None => Lookup::NotFound(last_error()),
}
}
pub(crate) fn close(handle: Handle) -> Result<()> {
if unsafe { FreeLibrary(handle.0.as_ptr()) } != 0 {
Ok(())
} else {
Err(Error::Close {
message: last_error(),
})
}
}