use std::fmt;
use std::io;
use windows_sys::Win32::System::Diagnostics::Debug::{
GetThreadErrorMode, SEM_FAILCRITICALERRORS, SEM_NOGPFAULTERRORBOX, SEM_NOOPENFILEERRORBOX,
SetThreadErrorMode, THREAD_ERROR_MODE,
};
const SUPPORTED: THREAD_ERROR_MODE =
SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct ThreadErrorMode(THREAD_ERROR_MODE);
impl ThreadErrorMode {
pub const NONE: Self = Self(0);
pub const FAIL_CRITICAL_ERRORS: Self = Self(SEM_FAILCRITICALERRORS);
pub const NO_GP_FAULT_ERROR_BOX: Self = Self(SEM_NOGPFAULTERRORBOX);
pub const NO_OPEN_FILE_ERROR_BOX: Self = Self(SEM_NOOPENFILEERRORBOX);
#[must_use]
pub const fn bits(self) -> THREAD_ERROR_MODE {
self.0
}
pub const fn from_bits(bits: THREAD_ERROR_MODE) -> Result<Self, UnsupportedBits> {
let unsupported = bits & !SUPPORTED;
if unsupported == 0 {
Ok(Self(bits))
} else {
Err(UnsupportedBits { bits: unsupported })
}
}
#[must_use]
pub const fn union(self, other: Self) -> Self {
Self(self.0 | other.0)
}
#[must_use]
pub const fn contains(self, other: Self) -> bool {
self.0 & other.0 == other.0
}
#[must_use]
pub const fn is_empty(self) -> bool {
self.0 == 0
}
pub fn capture() -> Result<Self, UnsupportedBits> {
Self::from_bits(unsafe { GetThreadErrorMode() })
}
pub fn apply(self) -> Result<ErrorModeGuard, ApplyError> {
let mut previous: THREAD_ERROR_MODE = 0;
let ok = unsafe { SetThreadErrorMode(self.0, &mut previous) };
if ok == 0 {
return Err(ApplyError {
requested: self,
source: io::Error::last_os_error(),
});
}
Ok(ErrorModeGuard {
previous,
released: false,
})
}
}
impl fmt::Display for ThreadErrorMode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "0x{:04X}", self.0)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct UnsupportedBits {
bits: THREAD_ERROR_MODE,
}
impl UnsupportedBits {
#[must_use]
pub const fn bits(self) -> THREAD_ERROR_MODE {
self.bits
}
}
impl fmt::Display for UnsupportedBits {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"0x{:04X} cannot be set on a thread; SetThreadErrorMode rejects it \
and would install none of the accompanying bits either",
self.bits
)
}
}
impl std::error::Error for UnsupportedBits {}
#[derive(Debug)]
pub struct ApplyError {
requested: ThreadErrorMode,
source: io::Error,
}
impl ApplyError {
#[must_use]
pub const fn requested(&self) -> ThreadErrorMode {
self.requested
}
#[must_use]
pub fn raw_os_error(&self) -> Option<i32> {
self.source.raw_os_error()
}
}
impl fmt::Display for ApplyError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"could not install thread error mode {}: {}",
self.requested, self.source
)
}
}
impl std::error::Error for ApplyError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&self.source)
}
}
#[derive(Debug)]
pub struct RestoreError {
unrestored: THREAD_ERROR_MODE,
source: io::Error,
}
impl RestoreError {
#[must_use]
pub const fn unrestored_bits(&self) -> THREAD_ERROR_MODE {
self.unrestored
}
#[must_use]
pub fn raw_os_error(&self) -> Option<i32> {
self.source.raw_os_error()
}
}
impl fmt::Display for RestoreError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"could not restore thread error mode 0x{:04X}; the thread is left \
contaminated: {}",
self.unrestored, self.source
)
}
}
impl std::error::Error for RestoreError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&self.source)
}
}
#[must_use = "dropping the guard restores the error mode but discards any failure to do so"]
#[derive(Debug)]
pub struct ErrorModeGuard {
previous: THREAD_ERROR_MODE,
released: bool,
}
impl ErrorModeGuard {
pub const fn previous(&self) -> Result<ThreadErrorMode, UnsupportedBits> {
ThreadErrorMode::from_bits(self.previous)
}
pub fn release(mut self) -> Result<(), RestoreError> {
self.released = true;
Self::restore(self.previous)
}
fn restore(previous: THREAD_ERROR_MODE) -> Result<(), RestoreError> {
let mut ignored: THREAD_ERROR_MODE = 0;
let ok = unsafe { SetThreadErrorMode(previous, &mut ignored) };
if ok == 0 {
return Err(RestoreError {
unrestored: previous,
source: io::Error::last_os_error(),
});
}
Ok(())
}
}
impl Drop for ErrorModeGuard {
fn drop(&mut self) {
if !self.released {
let _ = Self::restore(self.previous);
}
}
}
#[cfg(test)]
mod tests;