1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
use std::error::Error;
use std::fmt;
/// Represents a Windows error.
#[derive(Clone, Debug)]
pub struct WindowsError {
code: u32,
description: Option<String>,
}
impl WindowsError {
/// Creates a new [`WindowsError`] using the last error code retreived using the
/// native `GetLastError` function.
///
/// ## Example
///
/// ```rust, ignore
/// use winput::WindowsError;
///
/// let error = WindowsError::from_last_error();
/// println!("{:?}", &error.description);
/// ```
///
/// [`WindowsError`]: struct.WindowsError.html
pub fn from_last_error() -> Self {
use winapi::um::errhandlingapi::GetLastError;
let last_error_code = unsafe { GetLastError() };
Self::from_error_code(last_error_code)
}
/// Creates a new [`WindowsError`] using the given error code. The description of the
/// error is retreived using the native `FormatMessageW` function.
///
/// ## Example
///
/// ```rust, ignore
/// use winput::WindowsError;
///
/// let error = WindowsError::from_error_code(101);
/// println!("{:?}", &error.description);
/// ```
///
/// [`WindowsError`]: struct.WindowsError.html
pub fn from_error_code(error_code: u32) -> Self {
use std::{mem, ptr, slice};
use winapi::um::winbase;
let mut buffer_ptr: *mut u16 = ptr::null_mut();
// Calling C code
//
// The function returns the number of `u16` characters that were written.
let len = unsafe {
winbase::FormatMessageW(
winbase::FORMAT_MESSAGE_IGNORE_INSERTS
| winbase::FORMAT_MESSAGE_ALLOCATE_BUFFER
| winbase::FORMAT_MESSAGE_FROM_SYSTEM,
ptr::null(),
error_code,
0,
&mut buffer_ptr as *mut *mut u16 as _,
0,
ptr::null_mut(),
)
};
if len == 0 {
// The allocation failed / an invalid error code was provided
return WindowsError {
code: error_code,
description: None,
};
}
assert!(!buffer_ptr.is_null());
assert_eq!(buffer_ptr as usize % mem::align_of::<u16>(), 0);
// SAFETY:
// * `buffer_ptr` is non-null and well-aligned
// * `buffer_ptr` is not aliased anywere in the code
// * `buffer_ptr` is pointing to `len` properly initialized `u16`s.
let slice = unsafe { slice::from_raw_parts_mut(buffer_ptr, len as _) };
let description = String::from_utf16(slice).ok();
// The message is now copied into the rust world.
// We can free the allocated buffer.
unsafe { winbase::LocalFree(buffer_ptr as _) };
WindowsError {
code: error_code,
description,
}
}
}
impl fmt::Display for WindowsError {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(ref desc) = self.description {
write!(f, "\"{}\" (code: {})", desc, self.code)
} else {
write!(f, "WindowsError(code: {})", self.code)
}
}
}
impl Error for WindowsError {}