1 | use super::*; |
2 | |
3 | /// An error object consists of both an error code as well as detailed error information for debugging. |
4 | #[derive (Clone, PartialEq, Eq)] |
5 | pub struct Error { |
6 | pub(crate) code: HRESULT, |
7 | pub(crate) info: Option<crate::imp::IRestrictedErrorInfo>, |
8 | } |
9 | |
10 | impl Error { |
11 | /// An error object without any failure information. |
12 | pub const OK: Self = Self { code: HRESULT(0), info: None }; |
13 | |
14 | /// This creates a new WinRT error object, capturing the stack and other information about the |
15 | /// point of failure. |
16 | pub fn new(code: HRESULT, message: HSTRING) -> Self { |
17 | unsafe { |
18 | if let Some(function) = crate::imp::delay_load::<RoOriginateError>(s!("combase.dll" ), s!("RoOriginateError" )) { |
19 | function(code, std::mem::transmute_copy(&message)); |
20 | } |
21 | let info = GetErrorInfo().and_then(|e| e.cast()).ok(); |
22 | Self { code, info } |
23 | } |
24 | } |
25 | |
26 | pub fn from_win32() -> Self { |
27 | unsafe { Self { code: HRESULT::from_win32(crate::imp::GetLastError()), info: None } } |
28 | } |
29 | |
30 | /// The error code describing the error. |
31 | pub const fn code(&self) -> HRESULT { |
32 | self.code |
33 | } |
34 | |
35 | /// The error information describing the error. |
36 | pub const fn info(&self) -> &Option<crate::imp::IRestrictedErrorInfo> { |
37 | &self.info |
38 | } |
39 | |
40 | /// The error message describing the error. |
41 | pub fn message(&self) -> HSTRING { |
42 | // First attempt to retrieve the restricted error information. |
43 | if let Some(info) = &self.info { |
44 | let mut fallback = BSTR::default(); |
45 | let mut message = BSTR::default(); |
46 | let mut code = HRESULT(0); |
47 | |
48 | unsafe { |
49 | let _ = info.GetErrorDetails(&mut fallback, &mut code, &mut message, &mut BSTR::default()); |
50 | } |
51 | |
52 | if self.code == code { |
53 | let message = if !message.is_empty() { message } else { fallback }; |
54 | return HSTRING::from_wide(crate::imp::wide_trim_end(message.as_wide())).unwrap_or_default(); |
55 | } |
56 | } |
57 | |
58 | self.code.message() |
59 | } |
60 | } |
61 | |
62 | impl std::convert::From<Error> for HRESULT { |
63 | fn from(error: Error) -> Self { |
64 | let code: HRESULT = error.code; |
65 | let info: Option<crate::imp::IErrorInfo> = error.info.and_then(|info: IRestrictedErrorInfo| info.cast().ok()); |
66 | |
67 | unsafe { |
68 | let _ = crate::imp::SetErrorInfo(dwreserved:0, perrinfo:info.as_ref()); |
69 | } |
70 | |
71 | code |
72 | } |
73 | } |
74 | |
75 | impl std::convert::From<Error> for std::io::Error { |
76 | fn from(from: Error) -> Self { |
77 | Self::from_raw_os_error(code:from.code.0) |
78 | } |
79 | } |
80 | |
81 | impl std::convert::From<std::string::FromUtf16Error> for Error { |
82 | fn from(_: std::string::FromUtf16Error) -> Self { |
83 | Self { code: HRESULT::from_win32(crate::imp::ERROR_NO_UNICODE_TRANSLATION), info: None } |
84 | } |
85 | } |
86 | |
87 | impl std::convert::From<std::string::FromUtf8Error> for Error { |
88 | fn from(_: std::string::FromUtf8Error) -> Self { |
89 | Self { code: HRESULT::from_win32(crate::imp::ERROR_NO_UNICODE_TRANSLATION), info: None } |
90 | } |
91 | } |
92 | |
93 | impl std::convert::From<std::num::TryFromIntError> for Error { |
94 | fn from(_: std::num::TryFromIntError) -> Self { |
95 | Self { code: HRESULT(crate::imp::E_INVALIDARG), info: None } |
96 | } |
97 | } |
98 | |
99 | // Unfortunately this is needed to make types line up. The Rust type system does |
100 | // not know the `Infallible` can never be constructed. This code needs to be here |
101 | // to satesify the type checker but it will never be run. Once `!` is stabilizied |
102 | // this can be removed. |
103 | impl std::convert::From<std::convert::Infallible> for Error { |
104 | fn from(_: std::convert::Infallible) -> Self { |
105 | unreachable!() |
106 | } |
107 | } |
108 | |
109 | impl std::convert::From<HRESULT> for Error { |
110 | fn from(code: HRESULT) -> Self { |
111 | let info: Option<crate::imp::IRestrictedErrorInfo> = GetErrorInfo().and_then(|e| e.cast()).ok(); |
112 | |
113 | if let Some(info) = info { |
114 | // If it does (and therefore running on a recent version of Windows) |
115 | // then capture_propagation_context adds a breadcrumb to the error |
116 | // info to make debugging easier. |
117 | if let Ok(capture) = info.cast::<crate::imp::ILanguageExceptionErrorInfo2>() { |
118 | unsafe { |
119 | let _ = capture.CapturePropagationContext(None); |
120 | } |
121 | } |
122 | |
123 | return Self { code, info: Some(info) }; |
124 | } |
125 | |
126 | if let Ok(info) = GetErrorInfo() { |
127 | let message = unsafe { info.GetDescription().unwrap_or_default() }; |
128 | Self::new(code, HSTRING::from_wide(message.as_wide()).unwrap_or_default()) |
129 | } else { |
130 | Self { code, info: None } |
131 | } |
132 | } |
133 | } |
134 | |
135 | impl std::fmt::Debug for Error { |
136 | fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
137 | let mut debug: DebugStruct<'_, '_> = fmt.debug_struct(name:"Error" ); |
138 | debug.field("code" , &self.code).field(name:"message" , &self.message()).finish() |
139 | } |
140 | } |
141 | |
142 | impl std::fmt::Display for Error { |
143 | fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
144 | let message: HSTRING = self.message(); |
145 | if message.is_empty() { |
146 | std::write!(fmt, " {}" , self.code()) |
147 | } else { |
148 | std::write!(fmt, " {} ( {})" , self.message(), self.code()) |
149 | } |
150 | } |
151 | } |
152 | |
153 | impl std::error::Error for Error {} |
154 | |
155 | type RoOriginateError = extern "system" fn(code: HRESULT, message: *mut std::ffi::c_void) -> i32; |
156 | |
157 | fn GetErrorInfo() -> Result<crate::imp::IErrorInfo> { |
158 | unsafe { crate::imp::GetErrorInfo(dwreserved:0) } |
159 | } |
160 | |