sbz_switch/
hresult.rs

1use std::error::Error;
2use std::ffi::OsString;
3use std::fmt;
4use std::mem::MaybeUninit;
5use std::os::windows::ffi::OsStringExt;
6use std::ptr;
7use std::slice;
8
9use winapi::shared::ntdef::HRESULT;
10use winapi::shared::winerror::FACILITY_WIN32;
11use winapi::um::winbase::{
12    FormatMessageW, LocalFree, FORMAT_MESSAGE_ALLOCATE_BUFFER, FORMAT_MESSAGE_FROM_SYSTEM,
13};
14
15/// Represents an error thrown from Win32 code.
16#[derive(Clone, Debug)]
17pub struct Win32Error {
18    /// The original `HRESULT` value.
19    pub code: HRESULT,
20}
21
22impl Win32Error {
23    /// Creates a new `Win32Error` for an HRESULT value.
24    ///
25    /// # Examples
26    ///
27    /// ```
28    /// let error = GetLastError();
29    /// Win32Error::new(error)
30    /// ```
31    pub fn new(code: HRESULT) -> Win32Error {
32        Win32Error { code }
33    }
34}
35
36impl fmt::Display for Win32Error {
37    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
38        match (self.code >> 16) & 0x1fff {
39            // if it's a win32 error, use FormatMessage to get a description
40            facility if facility == FACILITY_WIN32 => unsafe {
41                let mut buffer = MaybeUninit::<*mut u16>::uninit();
42                let len = FormatMessageW(
43                    FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
44                    ptr::null(),
45                    (self.code & 0xffff) as u32,
46                    0,
47                    buffer.as_mut_ptr() as *mut _,
48                    0,
49                    ptr::null_mut(),
50                );
51                match len {
52                    0 => write!(f, "Unexpected HRESULT: {:X}", self.code),
53                    len => {
54                        let buffer = buffer.assume_init();
55                        let str: OsString =
56                            OsStringExt::from_wide(slice::from_raw_parts(buffer, len as usize));
57                        LocalFree(buffer as *mut _);
58                        write!(
59                            f,
60                            "Unexpected HRESULT: {:X}: {}",
61                            self.code,
62                            str.to_string_lossy()
63                        )
64                    }
65                }
66            },
67            _ => write!(f, "Unexpected HRESULT: {:X}", self.code),
68        }
69    }
70}
71
72impl Error for Win32Error {
73    fn cause(&self) -> Option<&dyn Error> {
74        None
75    }
76}
77
78/// Converts an `HRESULT` to a `Result<HRESULT, Win32Error>`.
79///
80/// `Ok(HRESULT)` can usually be ignored.
81///
82/// # Examples
83///
84/// ```
85/// check(GetLastError())?
86/// ```
87#[inline]
88pub fn check(result: HRESULT) -> Result<HRESULT, Win32Error> {
89    match result {
90        err if err < 0 => Err(Win32Error::new(err)),
91        success => Ok(success),
92    }
93}