use std::ffi::{CStr, OsStr};
use std::io::{Error as IoError, ErrorKind};
use std::os::windows::ffi::OsStrExt;
use std::ptr::null_mut;
use std::sync::atomic::{AtomicBool, Ordering, ATOMIC_BOOL_INIT};
use std::sync::Mutex;
use winapi;
use Error;
static USE_ERRORMODE: AtomicBool = ATOMIC_BOOL_INIT;
struct SetErrorModeData {
pub count: u32,
pub previous: winapi::shared::minwindef::DWORD,
}
lazy_static! {
static ref SET_ERR_MODE_DATA: Mutex<SetErrorModeData> = Mutex::new(SetErrorModeData {
count: 0,
previous: 0
});
}
pub type Handle = winapi::shared::minwindef::HMODULE;
const ERROR_MODE: winapi::shared::minwindef::DWORD = 1;
enum ErrorModeGuard {
ThreadPreviousValue(winapi::shared::minwindef::DWORD),
DoNothing,
Process,
}
impl ErrorModeGuard {
fn new() -> Result<ErrorModeGuard, IoError> {
if !USE_ERRORMODE.load(Ordering::Acquire) {
let mut previous: winapi::shared::minwindef::DWORD = 0;
if unsafe { winapi::um::errhandlingapi::SetThreadErrorMode(ERROR_MODE, &mut previous) }
== 0
{
let error = unsafe { winapi::um::errhandlingapi::GetLastError() };
if error == winapi::shared::winerror::ERROR_CALL_NOT_IMPLEMENTED {
USE_ERRORMODE.store(true, Ordering::Release);
} else {
return Err(IoError::from_raw_os_error(error as i32));
}
} else {
return Ok(if previous == ERROR_MODE {
ErrorModeGuard::DoNothing
} else {
ErrorModeGuard::ThreadPreviousValue(previous)
});
}
}
let mut lock = SET_ERR_MODE_DATA.lock().expect("Mutex got poisoned");
if lock.count == 0 {
lock.previous = unsafe { winapi::um::errhandlingapi::SetErrorMode(ERROR_MODE) };
if lock.previous == ERROR_MODE {
return Ok(ErrorModeGuard::DoNothing);
}
}
lock.count += 1;
Ok(ErrorModeGuard::Process)
}
}
impl Drop for ErrorModeGuard {
fn drop(&mut self) {
match self {
&mut ErrorModeGuard::DoNothing => (),
&mut ErrorModeGuard::Process => {
let mut lock = SET_ERR_MODE_DATA.lock().expect("Mutex got poisoned");
lock.count -= 1;
if lock.count == 0 {
unsafe { winapi::um::errhandlingapi::SetErrorMode(lock.previous) };
}
}
&mut ErrorModeGuard::ThreadPreviousValue(previous) => unsafe {
winapi::um::errhandlingapi::SetThreadErrorMode(previous, null_mut());
},
}
}
}
unsafe fn get_win_error() -> IoError {
let error = winapi::um::errhandlingapi::GetLastError();
if error == 0 {
IoError::new(
ErrorKind::Other,
"Could not obtain information about the error",
)
} else {
IoError::from_raw_os_error(error as i32)
}
}
#[inline]
pub unsafe fn get_sym(handle: Handle, name: &CStr) -> Result<*mut (), Error> {
let symbol = winapi::um::libloaderapi::GetProcAddress(handle, name.as_ptr());
if symbol.is_null() {
Err(Error::SymbolGettingError(get_win_error()))
} else {
Ok(symbol as *mut ())
}
}
#[inline]
pub unsafe fn open_lib(name: &OsStr) -> Result<Handle, Error> {
let wide_name: Vec<u16> = name.encode_wide().chain(Some(0)).collect();
let _guard = match ErrorModeGuard::new() {
Ok(val) => val,
Err(err) => return Err(Error::OpeningLibraryError(err)),
};
let handle = winapi::um::libloaderapi::LoadLibraryW(wide_name.as_ptr());
if handle.is_null() {
Err(Error::OpeningLibraryError(get_win_error()))
} else {
Ok(handle)
}
}
#[inline]
pub fn close_lib(handle: Handle) -> Handle {
if unsafe { winapi::um::libloaderapi::FreeLibrary(handle) } == 0 {
panic!("FreeLibrary() failed, the error is {}", unsafe {
get_win_error()
});
}
null_mut()
}