diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 8231028c9b..a9f2ba2834 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -120,6 +120,7 @@ jobs: cargo clippy -p test_readme && cargo clippy -p test_reserved && cargo clippy -p test_resources && + cargo clippy -p test_result && cargo clippy -p test_return_handle && cargo clippy -p test_return_struct && cargo clippy -p test_riddle && @@ -153,6 +154,7 @@ jobs: cargo clippy -p windows-implement && cargo clippy -p windows-interface && cargo clippy -p windows-metadata && + cargo clippy -p windows-result && cargo clippy -p windows-sys && cargo clippy -p windows-targets && cargo clippy -p windows-version && diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0d22a6f9c7..67fc454d48 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -105,8 +105,8 @@ jobs: cargo test -p test_debugger_visualizer && cargo test -p test_deprecated && cargo test -p test_dispatch && - cargo clean && cargo test -p test_does_not_return && + cargo clean && cargo test -p test_enums && cargo test -p test_error && cargo test -p test_event && @@ -128,6 +128,7 @@ jobs: cargo test -p test_readme && cargo test -p test_reserved && cargo test -p test_resources && + cargo test -p test_result && cargo test -p test_return_handle && cargo test -p test_return_struct && cargo test -p test_riddle && @@ -161,6 +162,7 @@ jobs: cargo test -p windows-implement && cargo test -p windows-interface && cargo test -p windows-metadata && + cargo test -p windows-result && cargo test -p windows-sys && cargo test -p windows-targets && cargo test -p windows-version && diff --git a/crates/libs/core/Cargo.toml b/crates/libs/core/Cargo.toml index 5bf48cc52f..905f3f714f 100644 --- a/crates/libs/core/Cargo.toml +++ b/crates/libs/core/Cargo.toml @@ -21,6 +21,10 @@ targets = [] version = "0.52.0" path = "../targets" +[dependencies.windows-result] +version = "0.1.0" +path = "../result" + [features] default = [] implement = [] diff --git a/crates/libs/core/src/error.rs b/crates/libs/core/src/error.rs deleted file mode 100644 index 7e618d106c..0000000000 --- a/crates/libs/core/src/error.rs +++ /dev/null @@ -1,168 +0,0 @@ -#![allow(missing_docs)] - -use super::*; - -/// An error object consists of both an error code as well as detailed error information for debugging. -#[derive(Clone, PartialEq, Eq)] -pub struct Error { - pub(crate) code: HRESULT, - pub(crate) info: Option, -} - -unsafe impl Send for Error {} -unsafe impl Sync for Error {} - -impl Error { - /// An error object without any failure information. - pub const OK: Self = Self { code: HRESULT(0), info: None }; - - /// This creates a new error object, capturing the stack and other information about the - /// point of failure. - pub fn new(code: HRESULT, message: HSTRING) -> Self { - unsafe { - crate::imp::RoOriginateError(code.0, std::mem::transmute_copy(&message)); - Self { code, info: GetErrorInfo() } - } - } - - /// Creates a new `Error` from the Win32 error code returned by `GetLastError()`. - pub fn from_win32() -> Self { - unsafe { Self { code: HRESULT::from_win32(crate::imp::GetLastError()), info: None } } - } - - /// The error code describing the error. - pub const fn code(&self) -> HRESULT { - self.code - } - - /// The error object describing the error. - pub fn info(&self) -> Option { - self.info.as_ref().and_then(|info| info.cast::().ok()) - } - - /// The error message describing the error. - pub fn message(&self) -> HSTRING { - if let Some(info) = &self.info { - let mut message = BSTR::default(); - - // First attempt to retrieve the restricted error information. - if let Ok(info) = info.cast::() { - let mut fallback = BSTR::default(); - let mut code = HRESULT(0); - - unsafe { - // The vfptr is called directly to avoid the default error propagation logic. - _ = (info.vtable().GetErrorDetails)(info.as_raw(), &mut fallback as *mut _ as _, &mut code, &mut message as *mut _ as _, &mut BSTR::default() as *mut _ as _); - } - - if message.is_empty() { - message = fallback - }; - } - - // Next attempt to retrieve the regular error information. - if message.is_empty() { - unsafe { - // The vfptr is called directly to avoid the default error propagation logic. - _ = (info.vtable().GetDescription)(info.as_raw(), &mut message as *mut _ as _); - } - } - - return HSTRING::from_wide(crate::imp::wide_trim_end(message.as_wide())).unwrap_or_default(); - } - - // Otherwise fallback to a generic error code description. - self.code.message() - } -} - -impl From for HRESULT { - fn from(error: Error) -> Self { - if error.info.is_some() { - unsafe { - crate::imp::SetErrorInfo(0, std::mem::transmute_copy(&error.info)); - } - } - - error.code - } -} - -impl From for Error { - fn from(code: HRESULT) -> Self { - let info = GetErrorInfo(); - - // Call CapturePropagationContext here if a use case presents itself. Otherwise, we can avoid the overhead for error propagation. - - Self { code, info } - } -} - -impl From for std::io::Error { - fn from(from: Error) -> Self { - Self::from_raw_os_error(from.code.0) - } -} - -impl From for Error { - fn from(from: std::io::Error) -> Self { - match from.raw_os_error() { - Some(status) => HRESULT::from_win32(status as u32).into(), - None => crate::imp::E_UNEXPECTED.into(), - } - } -} - -impl From for Error { - fn from(_: std::string::FromUtf16Error) -> Self { - Self { code: HRESULT::from_win32(crate::imp::ERROR_NO_UNICODE_TRANSLATION), info: None } - } -} - -impl From for Error { - fn from(_: std::string::FromUtf8Error) -> Self { - Self { code: HRESULT::from_win32(crate::imp::ERROR_NO_UNICODE_TRANSLATION), info: None } - } -} - -impl From for Error { - fn from(_: std::num::TryFromIntError) -> Self { - Self { code: crate::imp::E_INVALIDARG, info: None } - } -} - -// Unfortunately this is needed to make types line up. The Rust type system does -// not know the `Infallible` can never be constructed. This code needs to be here -// to satesify the type checker but it will never be run. Once `!` is stabilizied -// this can be removed. -impl From for Error { - fn from(_: std::convert::Infallible) -> Self { - unreachable!() - } -} - -impl std::fmt::Debug for Error { - fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let mut debug = fmt.debug_struct("Error"); - debug.field("code", &self.code).field("message", &self.message()).finish() - } -} - -impl std::fmt::Display for Error { - fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let message = self.message(); - if message.is_empty() { - std::write!(fmt, "{}", self.code()) - } else { - std::write!(fmt, "{} ({})", self.message(), self.code()) - } - } -} - -impl std::error::Error for Error {} - -fn GetErrorInfo() -> Option { - let mut info = None; - unsafe { crate::imp::GetErrorInfo(0, &mut info as *mut _ as _) }; - info -} diff --git a/crates/libs/core/src/imp/bindings.rs b/crates/libs/core/src/imp/bindings.rs index 08aeeadb8d..07ac83861a 100644 --- a/crates/libs/core/src/imp/bindings.rs +++ b/crates/libs/core/src/imp/bindings.rs @@ -1,14 +1,11 @@ // Bindings generated by `windows-bindgen` 0.52.0 #![allow(non_snake_case, non_upper_case_globals, non_camel_case_types, dead_code, clippy::all)] -::windows_targets::link!("api-ms-win-core-winrt-error-l1-1-0.dll" "system" fn RoOriginateError(error : HRESULT, message : * mut::core::ffi::c_void) -> BOOL); ::windows_targets::link!("api-ms-win-core-winrt-l1-1-0.dll" "system" fn RoGetActivationFactory(activatableclassid : * mut::core::ffi::c_void, iid : *const GUID, factory : *mut *mut ::core::ffi::c_void) -> HRESULT); ::windows_targets::link!("kernel32.dll" "system" fn CloseHandle(hobject : HANDLE) -> BOOL); ::windows_targets::link!("kernel32.dll" "system" fn CreateEventW(lpeventattributes : *const SECURITY_ATTRIBUTES, bmanualreset : BOOL, binitialstate : BOOL, lpname : PCWSTR) -> HANDLE); ::windows_targets::link!("kernel32.dll" "system" fn EncodePointer(ptr : *const ::core::ffi::c_void) -> *mut ::core::ffi::c_void); -::windows_targets::link!("kernel32.dll" "system" fn FormatMessageW(dwflags : FORMAT_MESSAGE_OPTIONS, lpsource : *const ::core::ffi::c_void, dwmessageid : u32, dwlanguageid : u32, lpbuffer : PWSTR, nsize : u32, arguments : *const *const i8) -> u32); ::windows_targets::link!("kernel32.dll" "system" fn FreeLibrary(hlibmodule : HMODULE) -> BOOL); -::windows_targets::link!("kernel32.dll" "system" fn GetLastError() -> WIN32_ERROR); ::windows_targets::link!("kernel32.dll" "system" fn GetProcAddress(hmodule : HMODULE, lpprocname : PCSTR) -> FARPROC); ::windows_targets::link!("kernel32.dll" "system" fn GetProcessHeap() -> HANDLE); ::windows_targets::link!("kernel32.dll" "system" fn HeapAlloc(hheap : HANDLE, dwflags : HEAP_FLAGS, dwbytes : usize) -> *mut ::core::ffi::c_void); @@ -21,8 +18,6 @@ ::windows_targets::link!("ole32.dll" "system" fn CoTaskMemFree(pv : *const ::core::ffi::c_void)); ::windows_targets::link!("ole32.dll" "system" fn PropVariantClear(pvar : *mut PROPVARIANT) -> HRESULT); ::windows_targets::link!("ole32.dll" "system" fn PropVariantCopy(pvardest : *mut PROPVARIANT, pvarsrc : *const PROPVARIANT) -> HRESULT); -::windows_targets::link!("oleaut32.dll" "system" fn GetErrorInfo(dwreserved : u32, pperrinfo : *mut * mut::core::ffi::c_void) -> HRESULT); -::windows_targets::link!("oleaut32.dll" "system" fn SetErrorInfo(dwreserved : u32, perrinfo : * mut::core::ffi::c_void) -> HRESULT); ::windows_targets::link!("oleaut32.dll" "system" fn SysAllocStringLen(strin : PCWSTR, ui : u32) -> BSTR); ::windows_targets::link!("oleaut32.dll" "system" fn SysFreeString(bstrstring : BSTR)); ::windows_targets::link!("oleaut32.dll" "system" fn SysStringLen(pbstr : BSTR) -> u32); @@ -469,7 +464,6 @@ impl ::core::clone::Clone for ELEMDESC_0 { *self } } -pub const ERROR_NO_UNICODE_TRANSLATION: WIN32_ERROR = 1113u32; #[repr(C)] pub struct EXCEPINFO { pub wCode: u16, @@ -500,10 +494,6 @@ impl ::core::clone::Clone for FILETIME { *self } } -pub const FORMAT_MESSAGE_ALLOCATE_BUFFER: FORMAT_MESSAGE_OPTIONS = 256u32; -pub const FORMAT_MESSAGE_FROM_SYSTEM: FORMAT_MESSAGE_OPTIONS = 4096u32; -pub const FORMAT_MESSAGE_IGNORE_INSERTS: FORMAT_MESSAGE_OPTIONS = 512u32; -pub type FORMAT_MESSAGE_OPTIONS = u32; #[repr(C)] pub struct FUNCDESC { pub memid: i32, @@ -996,4 +986,3 @@ pub const VT_UI4: VARENUM = 19u16; pub const VT_UI8: VARENUM = 21u16; pub const VT_UNKNOWN: VARENUM = 13u16; pub type WAIT_EVENT = u32; -pub type WIN32_ERROR = u32; diff --git a/crates/libs/core/src/imp/com_bindings.rs b/crates/libs/core/src/imp/com_bindings.rs index c4b3a4a8b5..1cfb6d5374 100644 --- a/crates/libs/core/src/imp/com_bindings.rs +++ b/crates/libs/core/src/imp/com_bindings.rs @@ -28,7 +28,6 @@ impl ::core::fmt::Debug for AgileReferenceOptions { f.debug_tuple("AgileReferenceOptions").field(&self.0).finish() } } -pub const CLASS_E_CLASSNOTAVAILABLE: ::windows_core::HRESULT = ::windows_core::HRESULT(-2147221231i32); pub const CO_E_NOTINITIALIZED: ::windows_core::HRESULT = ::windows_core::HRESULT(-2147221008i32); #[repr(C)] pub struct DateTime { @@ -63,10 +62,8 @@ impl ::core::default::Default for DateTime { } } pub const E_BOUNDS: ::windows_core::HRESULT = ::windows_core::HRESULT(-2147483637i32); -pub const E_INVALIDARG: ::windows_core::HRESULT = ::windows_core::HRESULT(-2147024809i32); pub const E_NOINTERFACE: ::windows_core::HRESULT = ::windows_core::HRESULT(-2147467262i32); pub const E_OUTOFMEMORY: ::windows_core::HRESULT = ::windows_core::HRESULT(-2147024882i32); -pub const E_UNEXPECTED: ::windows_core::HRESULT = ::windows_core::HRESULT(-2147418113i32); ::windows_core::imp::com_interface!(IAgileObject, IAgileObject_Vtbl, 0x94ea2b94_e9cc_49e0_c0ff_ee64ca8f5b90); ::windows_core::imp::interface_hierarchy!(IAgileObject, ::windows_core::IUnknown); impl IAgileObject {} @@ -90,81 +87,6 @@ pub struct IAgileReference_Vtbl { pub base__: ::windows_core::IUnknown_Vtbl, pub Resolve: unsafe extern "system" fn(*mut ::core::ffi::c_void, *const ::windows_core::GUID, *mut *mut ::core::ffi::c_void) -> ::windows_core::HRESULT, } -::windows_core::imp::com_interface!(IErrorInfo, IErrorInfo_Vtbl, 0x1cf2b120_547d_101b_8e65_08002b2bd119); -::windows_core::imp::interface_hierarchy!(IErrorInfo, ::windows_core::IUnknown); -impl IErrorInfo { - pub unsafe fn GetGUID(&self) -> ::windows_core::Result<::windows_core::GUID> { - let mut result__ = ::std::mem::zeroed(); - (::windows_core::Interface::vtable(self).GetGUID)(::windows_core::Interface::as_raw(self), &mut result__).map(|| result__) - } - pub unsafe fn GetSource(&self) -> ::windows_core::Result<::windows_core::BSTR> { - let mut result__ = ::std::mem::zeroed(); - (::windows_core::Interface::vtable(self).GetSource)(::windows_core::Interface::as_raw(self), &mut result__).and_then(|| ::windows_core::Type::from_abi(result__)) - } - pub unsafe fn GetDescription(&self) -> ::windows_core::Result<::windows_core::BSTR> { - let mut result__ = ::std::mem::zeroed(); - (::windows_core::Interface::vtable(self).GetDescription)(::windows_core::Interface::as_raw(self), &mut result__).and_then(|| ::windows_core::Type::from_abi(result__)) - } - pub unsafe fn GetHelpFile(&self) -> ::windows_core::Result<::windows_core::BSTR> { - let mut result__ = ::std::mem::zeroed(); - (::windows_core::Interface::vtable(self).GetHelpFile)(::windows_core::Interface::as_raw(self), &mut result__).and_then(|| ::windows_core::Type::from_abi(result__)) - } - pub unsafe fn GetHelpContext(&self) -> ::windows_core::Result { - let mut result__ = ::std::mem::zeroed(); - (::windows_core::Interface::vtable(self).GetHelpContext)(::windows_core::Interface::as_raw(self), &mut result__).map(|| result__) - } -} -#[repr(C)] -pub struct IErrorInfo_Vtbl { - pub base__: ::windows_core::IUnknown_Vtbl, - pub GetGUID: unsafe extern "system" fn(*mut ::core::ffi::c_void, *mut ::windows_core::GUID) -> ::windows_core::HRESULT, - pub GetSource: unsafe extern "system" fn(*mut ::core::ffi::c_void, *mut ::std::mem::MaybeUninit<::windows_core::BSTR>) -> ::windows_core::HRESULT, - pub GetDescription: unsafe extern "system" fn(*mut ::core::ffi::c_void, *mut ::std::mem::MaybeUninit<::windows_core::BSTR>) -> ::windows_core::HRESULT, - pub GetHelpFile: unsafe extern "system" fn(*mut ::core::ffi::c_void, *mut ::std::mem::MaybeUninit<::windows_core::BSTR>) -> ::windows_core::HRESULT, - pub GetHelpContext: unsafe extern "system" fn(*mut ::core::ffi::c_void, *mut u32) -> ::windows_core::HRESULT, -} -::windows_core::imp::com_interface!(ILanguageExceptionErrorInfo, ILanguageExceptionErrorInfo_Vtbl, 0x04a2dbf3_df83_116c_0946_0812abf6e07d); -::windows_core::imp::interface_hierarchy!(ILanguageExceptionErrorInfo, ::windows_core::IUnknown); -impl ILanguageExceptionErrorInfo { - pub unsafe fn GetLanguageException(&self) -> ::windows_core::Result<::windows_core::IUnknown> { - let mut result__ = ::std::mem::zeroed(); - (::windows_core::Interface::vtable(self).GetLanguageException)(::windows_core::Interface::as_raw(self), &mut result__).and_then(|| ::windows_core::Type::from_abi(result__)) - } -} -#[repr(C)] -pub struct ILanguageExceptionErrorInfo_Vtbl { - pub base__: ::windows_core::IUnknown_Vtbl, - pub GetLanguageException: unsafe extern "system" fn(*mut ::core::ffi::c_void, *mut *mut ::core::ffi::c_void) -> ::windows_core::HRESULT, -} -::windows_core::imp::com_interface!(ILanguageExceptionErrorInfo2, ILanguageExceptionErrorInfo2_Vtbl, 0x5746e5c4_5b97_424c_b620_2822915734dd); -::windows_core::imp::interface_hierarchy!(ILanguageExceptionErrorInfo2, ::windows_core::IUnknown, ILanguageExceptionErrorInfo); -impl ILanguageExceptionErrorInfo2 { - pub unsafe fn GetLanguageException(&self) -> ::windows_core::Result<::windows_core::IUnknown> { - let mut result__ = ::std::mem::zeroed(); - (::windows_core::Interface::vtable(self).base__.GetLanguageException)(::windows_core::Interface::as_raw(self), &mut result__).and_then(|| ::windows_core::Type::from_abi(result__)) - } - pub unsafe fn GetPreviousLanguageExceptionErrorInfo(&self) -> ::windows_core::Result { - let mut result__ = ::std::mem::zeroed(); - (::windows_core::Interface::vtable(self).GetPreviousLanguageExceptionErrorInfo)(::windows_core::Interface::as_raw(self), &mut result__).and_then(|| ::windows_core::Type::from_abi(result__)) - } - pub unsafe fn CapturePropagationContext(&self, languageexception: P0) -> ::windows_core::Result<()> - where - P0: ::windows_core::IntoParam<::windows_core::IUnknown>, - { - (::windows_core::Interface::vtable(self).CapturePropagationContext)(::windows_core::Interface::as_raw(self), languageexception.into_param().abi()).ok() - } - pub unsafe fn GetPropagationContextHead(&self) -> ::windows_core::Result { - let mut result__ = ::std::mem::zeroed(); - (::windows_core::Interface::vtable(self).GetPropagationContextHead)(::windows_core::Interface::as_raw(self), &mut result__).and_then(|| ::windows_core::Type::from_abi(result__)) - } -} -#[repr(C)] -pub struct ILanguageExceptionErrorInfo2_Vtbl { - pub base__: ILanguageExceptionErrorInfo_Vtbl, - pub GetPreviousLanguageExceptionErrorInfo: unsafe extern "system" fn(*mut ::core::ffi::c_void, *mut *mut ::core::ffi::c_void) -> ::windows_core::HRESULT, - pub CapturePropagationContext: unsafe extern "system" fn(*mut ::core::ffi::c_void, *mut ::core::ffi::c_void) -> ::windows_core::HRESULT, - pub GetPropagationContextHead: unsafe extern "system" fn(*mut ::core::ffi::c_void, *mut *mut ::core::ffi::c_void) -> ::windows_core::HRESULT, -} ::windows_core::imp::com_interface!(IPropertyValue, IPropertyValue_Vtbl, 0x4bd682dd_7554_40e9_9a9b_82654ede7e62); ::windows_core::imp::interface_hierarchy!(IPropertyValue, ::windows_core::IUnknown, ::windows_core::IInspectable); impl IPropertyValue { @@ -726,25 +648,6 @@ where pub Value: unsafe extern "system" fn(*mut ::core::ffi::c_void, *mut ::windows_core::AbiType) -> ::windows_core::HRESULT, pub T: ::core::marker::PhantomData, } -::windows_core::imp::com_interface!(IRestrictedErrorInfo, IRestrictedErrorInfo_Vtbl, 0x82ba7092_4c88_427d_a7bc_16dd93feb67e); -::windows_core::imp::interface_hierarchy!(IRestrictedErrorInfo, ::windows_core::IUnknown); -impl IRestrictedErrorInfo { - pub unsafe fn GetErrorDetails(&self, description: *mut ::windows_core::BSTR, error: *mut ::windows_core::HRESULT, restricteddescription: *mut ::windows_core::BSTR, capabilitysid: *mut ::windows_core::BSTR) -> ::windows_core::Result<()> { - (::windows_core::Interface::vtable(self).GetErrorDetails)(::windows_core::Interface::as_raw(self), ::core::mem::transmute(description), error, ::core::mem::transmute(restricteddescription), ::core::mem::transmute(capabilitysid)).ok() - } - pub unsafe fn GetReference(&self) -> ::windows_core::Result<::windows_core::BSTR> { - let mut result__ = ::std::mem::zeroed(); - (::windows_core::Interface::vtable(self).GetReference)(::windows_core::Interface::as_raw(self), &mut result__).and_then(|| ::windows_core::Type::from_abi(result__)) - } -} -unsafe impl ::core::marker::Send for IRestrictedErrorInfo {} -unsafe impl ::core::marker::Sync for IRestrictedErrorInfo {} -#[repr(C)] -pub struct IRestrictedErrorInfo_Vtbl { - pub base__: ::windows_core::IUnknown_Vtbl, - pub GetErrorDetails: unsafe extern "system" fn(*mut ::core::ffi::c_void, *mut ::std::mem::MaybeUninit<::windows_core::BSTR>, *mut ::windows_core::HRESULT, *mut ::std::mem::MaybeUninit<::windows_core::BSTR>, *mut ::std::mem::MaybeUninit<::windows_core::BSTR>) -> ::windows_core::HRESULT, - pub GetReference: unsafe extern "system" fn(*mut ::core::ffi::c_void, *mut ::std::mem::MaybeUninit<::windows_core::BSTR>) -> ::windows_core::HRESULT, -} ::windows_core::imp::com_interface!(IStringable, IStringable_Vtbl, 0x96369f54_8eb6_48f0_abce_c1b211e627c3); ::windows_core::imp::interface_hierarchy!(IStringable, ::windows_core::IUnknown, ::windows_core::IInspectable); impl IStringable { diff --git a/crates/libs/core/src/imp/factory_cache.rs b/crates/libs/core/src/imp/factory_cache.rs index f7bb536e8d..eb4b2dac0e 100644 --- a/crates/libs/core/src/imp/factory_cache.rs +++ b/crates/libs/core/src/imp/factory_cache.rs @@ -136,7 +136,7 @@ mod tests { if unsafe { library.as_bytes() } == &b"A.dll"[..] { Ok(42) } else { - Err(crate::Error::OK) + Err(crate::Error::empty()) } }); assert!(matches!(end_result, Some(42))); @@ -146,7 +146,7 @@ mod tests { let mut results = Vec::new(); let end_result = search_path(path, |library| { results.push(unsafe { library.to_string().unwrap() }); - crate::Result::<()>::Err(crate::Error::OK) + crate::Result::<()>::Err(crate::Error::empty()) }); assert!(end_result.is_none()); assert_eq!(results, vec!["A.B.dll", "A.dll"]); diff --git a/crates/libs/core/src/interface.rs b/crates/libs/core/src/interface.rs index 39f3706d27..9f3535b0f0 100644 --- a/crates/libs/core/src/interface.rs +++ b/crates/libs/core/src/interface.rs @@ -85,7 +85,7 @@ pub unsafe trait Interface: Sized + Clone { // to `T` which the implementor of `Interface` has guaranteed is correct unsafe { _ = self.query(&T::IID, &mut result as *mut _ as _) }; - result.ok_or_else(|| Error { code: crate::imp::E_NOINTERFACE, info: None }) + result.ok_or_else(|| Error::from_hresult(crate::imp::E_NOINTERFACE)) } /// Attempts to create a [`Weak`] reference to this object. diff --git a/crates/libs/core/src/lib.rs b/crates/libs/core/src/lib.rs index c0cd6dedc1..8d9d1f655a 100644 --- a/crates/libs/core/src/lib.rs +++ b/crates/libs/core/src/lib.rs @@ -14,10 +14,8 @@ pub mod imp; mod agile_reference; mod array; mod as_impl; -mod error; mod event; mod guid; -mod hresult; mod inspectable; mod interface; mod param; @@ -33,10 +31,8 @@ mod weak; pub use agile_reference::*; pub use array::*; pub use as_impl::*; -pub use error::*; pub use event::*; pub use guid::*; -pub use hresult::*; pub use inspectable::*; pub use interface::*; pub use param::*; @@ -48,9 +44,7 @@ pub use strings::*; pub use unknown::*; pub use variant::*; pub use weak::*; - -/// A specialized [`Result`] type that provides Windows error information. -pub type Result = std::result::Result; +pub use windows_result::*; /// Attempts to load the factory object for the given WinRT class. /// This can be used to access COM interfaces implemented on a Windows Runtime class factory. diff --git a/crates/libs/core/src/type.rs b/crates/libs/core/src/type.rs index edf42e283e..3b4884962c 100644 --- a/crates/libs/core/src/type.rs +++ b/crates/libs/core/src/type.rs @@ -35,12 +35,12 @@ where if !abi.is_null() { Ok(std::mem::transmute_copy(&abi)) } else { - Err(Error::OK) + Err(Error::empty()) } } fn from_default(default: &Self::Default) -> Result { - default.as_ref().cloned().ok_or(Error::OK) + default.as_ref().cloned().ok_or(Error::empty()) } } diff --git a/crates/libs/core/src/variant.rs b/crates/libs/core/src/variant.rs index 084c1766e8..f3b3c353a5 100644 --- a/crates/libs/core/src/variant.rs +++ b/crates/libs/core/src/variant.rs @@ -209,7 +209,7 @@ impl TryFrom<&VARIANT> for IUnknown { let unknown: &IUnknown = std::mem::transmute(&from.0.Anonymous.Anonymous.Anonymous.punkVal); Ok(unknown.clone()) } else { - Err(Error { code: imp::TYPE_E_TYPEMISMATCH, info: None }) + Err(Error::from_hresult(imp::TYPE_E_TYPEMISMATCH)) } } } @@ -223,7 +223,7 @@ impl TryFrom<&PROPVARIANT> for IUnknown { let unknown: &IUnknown = std::mem::transmute(&from.0.Anonymous.Anonymous.Anonymous.punkVal); Ok(unknown.clone()) } else { - Err(Error { code: imp::TYPE_E_TYPEMISMATCH, info: None }) + Err(Error::from_hresult(imp::TYPE_E_TYPEMISMATCH)) } } } diff --git a/crates/libs/core/windows.natvis b/crates/libs/core/windows.natvis index 32859b0a6c..5c3903a7d3 100644 --- a/crates/libs/core/windows.natvis +++ b/crates/libs/core/windows.natvis @@ -11,14 +11,14 @@ - + code info - + {(HRESULT)__0} diff --git a/crates/libs/result/Cargo.toml b/crates/libs/result/Cargo.toml new file mode 100644 index 0000000000..b2ddfad10a --- /dev/null +++ b/crates/libs/result/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "windows-result" +version = "0.1.0" +authors = ["Microsoft"] +edition = "2021" +rust-version = "1.60" +license = "MIT OR Apache-2.0" +description = "Windows error handling" +repository = "https://github.com/microsoft/windows-rs" +readme = "readme.md" +categories = ["os::windows-apis"] +exclude = ["tests"] + +[lints] +workspace = true + +[package.metadata.docs.rs] +default-target = "x86_64-pc-windows-msvc" +targets = [] + +[dependencies.windows-targets] +version = "0.52.0" +path = "../targets" + +[dev-dependencies.windows-bindgen] +version = "0.52.0" +path = "../bindgen" diff --git a/crates/libs/result/license-apache-2.0 b/crates/libs/result/license-apache-2.0 new file mode 100644 index 0000000000..b5ed4ecec2 --- /dev/null +++ b/crates/libs/result/license-apache-2.0 @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) Microsoft Corporation. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/crates/libs/result/license-mit b/crates/libs/result/license-mit new file mode 100644 index 0000000000..9e841e7a26 --- /dev/null +++ b/crates/libs/result/license-mit @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE diff --git a/crates/libs/result/readme.md b/crates/libs/result/readme.md new file mode 100644 index 0000000000..238b8a6995 --- /dev/null +++ b/crates/libs/result/readme.md @@ -0,0 +1,7 @@ +## Windows error handling + +The [windows-result](https://crates.io/crates/windows-result) crate provides efficient Windows error handling and propagation with support for Win32, COM, and WinRT APIs. + +* [Getting started](https://kennykerr.ca/rust-getting-started/) +* [Samples](https://github.com/microsoft/windows-rs/tree/0.52.0/crates/samples) +* [Releases](https://github.com/microsoft/windows-rs/releases) diff --git a/crates/libs/result/rustfmt.toml b/crates/libs/result/rustfmt.toml new file mode 100644 index 0000000000..43d4840c79 --- /dev/null +++ b/crates/libs/result/rustfmt.toml @@ -0,0 +1 @@ +newline_style = "Unix" diff --git a/crates/libs/result/src/bindings.rs b/crates/libs/result/src/bindings.rs new file mode 100644 index 0000000000..99f2c5a4dc --- /dev/null +++ b/crates/libs/result/src/bindings.rs @@ -0,0 +1,90 @@ +// Bindings generated by `windows-bindgen` 0.52.0 + +#![allow( + non_snake_case, + non_upper_case_globals, + non_camel_case_types, + dead_code, + clippy::all +)] +::windows_targets::link!("api-ms-win-core-winrt-error-l1-1-0.dll" "system" fn RoOriginateErrorW(error : HRESULT, cchmax : u32, message : PCWSTR) -> BOOL); +::windows_targets::link!("kernel32.dll" "system" fn FormatMessageW(dwflags : FORMAT_MESSAGE_OPTIONS, lpsource : *const ::core::ffi::c_void, dwmessageid : u32, dwlanguageid : u32, lpbuffer : PWSTR, nsize : u32, arguments : *const *const i8) -> u32); +::windows_targets::link!("kernel32.dll" "system" fn GetLastError() -> WIN32_ERROR); +::windows_targets::link!("kernel32.dll" "system" fn GetProcessHeap() -> HANDLE); +::windows_targets::link!("kernel32.dll" "system" fn HeapFree(hheap : HANDLE, dwflags : HEAP_FLAGS, lpmem : *const ::core::ffi::c_void) -> BOOL); +::windows_targets::link!("oleaut32.dll" "system" fn GetErrorInfo(dwreserved : u32, pperrinfo : *mut * mut::core::ffi::c_void) -> HRESULT); +::windows_targets::link!("oleaut32.dll" "system" fn SetErrorInfo(dwreserved : u32, perrinfo : * mut::core::ffi::c_void) -> HRESULT); +::windows_targets::link!("oleaut32.dll" "system" fn SysFreeString(bstrstring : BSTR)); +::windows_targets::link!("oleaut32.dll" "system" fn SysStringLen(pbstr : BSTR) -> u32); +pub type BOOL = i32; +pub type BSTR = *const u16; +pub const ERROR_NO_UNICODE_TRANSLATION: WIN32_ERROR = 1113u32; +pub const E_INVALIDARG: HRESULT = -2147024809i32; +pub const E_UNEXPECTED: HRESULT = -2147418113i32; +pub const FORMAT_MESSAGE_ALLOCATE_BUFFER: FORMAT_MESSAGE_OPTIONS = 256u32; +pub const FORMAT_MESSAGE_FROM_SYSTEM: FORMAT_MESSAGE_OPTIONS = 4096u32; +pub const FORMAT_MESSAGE_IGNORE_INSERTS: FORMAT_MESSAGE_OPTIONS = 512u32; +pub type FORMAT_MESSAGE_OPTIONS = u32; +#[repr(C)] +pub struct GUID { + pub data1: u32, + pub data2: u16, + pub data3: u16, + pub data4: [u8; 8], +} +impl ::core::marker::Copy for GUID {} +impl ::core::clone::Clone for GUID { + fn clone(&self) -> Self { + *self + } +} +impl GUID { + pub const fn from_u128(uuid: u128) -> Self { + Self { + data1: (uuid >> 96) as u32, + data2: (uuid >> 80 & 0xffff) as u16, + data3: (uuid >> 64 & 0xffff) as u16, + data4: (uuid as u64).to_be_bytes(), + } + } +} +pub type HANDLE = isize; +pub type HEAP_FLAGS = u32; +pub type HRESULT = i32; +pub const IID_IErrorInfo: GUID = GUID::from_u128(0x1cf2b120_547d_101b_8e65_08002b2bd119); +#[repr(C)] +pub struct IErrorInfo_Vtbl { + pub base__: IUnknown_Vtbl, + pub GetGUID: unsafe extern "system" fn(*mut ::core::ffi::c_void, *mut GUID) -> HRESULT, + pub GetSource: unsafe extern "system" fn(*mut ::core::ffi::c_void, *mut BSTR) -> HRESULT, + pub GetDescription: unsafe extern "system" fn(*mut ::core::ffi::c_void, *mut BSTR) -> HRESULT, + pub GetHelpFile: unsafe extern "system" fn(*mut ::core::ffi::c_void, *mut BSTR) -> HRESULT, + pub GetHelpContext: unsafe extern "system" fn(*mut ::core::ffi::c_void, *mut u32) -> HRESULT, +} +pub const IID_IRestrictedErrorInfo: GUID = GUID::from_u128(0x82ba7092_4c88_427d_a7bc_16dd93feb67e); +#[repr(C)] +pub struct IRestrictedErrorInfo_Vtbl { + pub base__: IUnknown_Vtbl, + pub GetErrorDetails: unsafe extern "system" fn( + *mut ::core::ffi::c_void, + *mut BSTR, + *mut HRESULT, + *mut BSTR, + *mut BSTR, + ) -> HRESULT, + pub GetReference: unsafe extern "system" fn(*mut ::core::ffi::c_void, *mut BSTR) -> HRESULT, +} +pub const IID_IUnknown: GUID = GUID::from_u128(0x00000000_0000_0000_c000_000000000046); +#[repr(C)] +pub struct IUnknown_Vtbl { + pub QueryInterface: unsafe extern "system" fn( + this: *mut ::core::ffi::c_void, + iid: *const GUID, + interface: *mut *mut ::core::ffi::c_void, + ) -> HRESULT, + pub AddRef: unsafe extern "system" fn(this: *mut ::core::ffi::c_void) -> u32, + pub Release: unsafe extern "system" fn(this: *mut ::core::ffi::c_void) -> u32, +} +pub type PCWSTR = *const u16; +pub type PWSTR = *mut u16; +pub type WIN32_ERROR = u32; diff --git a/crates/libs/result/src/com.rs b/crates/libs/result/src/com.rs new file mode 100644 index 0000000000..6c150081fd --- /dev/null +++ b/crates/libs/result/src/com.rs @@ -0,0 +1,56 @@ +use super::*; + +#[doc(hidden)] +#[macro_export] +macro_rules! com_call { + ($vtbl:ty, $this:ident.$method:ident($($args:tt)*)) => { + ((&**($this.as_raw() as *mut *mut $vtbl)).$method)($this.as_raw(), $($args)*) + } +} + +pub use com_call; + +#[repr(transparent)] +pub struct ComPtr(std::ptr::NonNull); + +impl ComPtr { + pub fn as_raw(&self) -> *mut std::ffi::c_void { + unsafe { std::mem::transmute_copy(self) } + } + + pub fn cast(&self, iid: &GUID) -> Option { + let mut result = None; + unsafe { + com_call!( + IUnknown_Vtbl, + self.QueryInterface(iid, &mut result as *mut _ as _) + ); + } + result + } +} + +impl PartialEq for ComPtr { + fn eq(&self, other: &Self) -> bool { + self.cast(&IID_IUnknown).unwrap().0 == other.cast(&IID_IUnknown).unwrap().0 + } +} + +impl Eq for ComPtr {} + +impl Clone for ComPtr { + fn clone(&self) -> Self { + unsafe { + com_call!(IUnknown_Vtbl, self.AddRef()); + } + Self(self.0) + } +} + +impl Drop for ComPtr { + fn drop(&mut self) { + unsafe { + com_call!(IUnknown_Vtbl, self.Release()); + } + } +} diff --git a/crates/libs/result/src/error.rs b/crates/libs/result/src/error.rs new file mode 100644 index 0000000000..60dad5efe7 --- /dev/null +++ b/crates/libs/result/src/error.rs @@ -0,0 +1,188 @@ +use super::*; + +/// An error object consists of both an error code as well as detailed error information for debugging. +#[derive(Clone, PartialEq, Eq)] +pub struct Error { + code: HRESULT, + info: Option, +} + +impl Error { + /// Creates an error object without any failure information. + pub const fn empty() -> Self { + Self { + code: HRESULT(0), + info: None, + } + } + + /// Creates a new error object, capturing the stack and other information about the + /// point of failure. + pub fn new>(code: HRESULT, message: T) -> Self { + let message: Vec<_> = message.as_ref().encode_utf16().collect(); + + if message.is_empty() { + Self::from_hresult(code) + } else { + unsafe { + RoOriginateErrorW(code.0, message.len() as u32, message.as_ptr()); + } + code.into() + } + } + + /// Creates a new error object with an error code, but without additional error information. + pub fn from_hresult(code: HRESULT) -> Self { + Self { code, info: None } + } + + /// Creates a new `Error` from the Win32 error code returned by `GetLastError()`. + pub fn from_win32() -> Self { + Self { + code: HRESULT::from_win32(unsafe { GetLastError() }), + info: None, + } + } + + /// The error code describing the error. + pub const fn code(&self) -> HRESULT { + self.code + } + + /// The error message describing the error. + pub fn message(&self) -> String { + if let Some(info) = &self.info { + let mut message = BasicString::default(); + + // First attempt to retrieve the restricted error information. + if let Some(info) = info.cast(&IID_IRestrictedErrorInfo) { + let mut fallback = BasicString::default(); + let mut code = 0; + + unsafe { + com_call!( + IRestrictedErrorInfo_Vtbl, + info.GetErrorDetails( + &mut fallback as *mut _ as _, + &mut code, + &mut message as *mut _ as _, + &mut BasicString::default() as *mut _ as _ + ) + ); + } + + if message.is_empty() { + message = fallback + }; + } + + // Next attempt to retrieve the regular error information. + if message.is_empty() { + unsafe { + com_call!( + IErrorInfo_Vtbl, + info.GetDescription(&mut message as *mut _ as _) + ); + } + } + + return String::from_utf16_lossy(wide_trim_end(message.as_wide())); + } + + // Otherwise fallback to a generic error code description. + self.code.message() + } + + /// The error object describing the error. + pub fn as_ptr(&self) -> *mut std::ffi::c_void { + self.info + .as_ref() + .map_or(std::ptr::null_mut(), |info| info.as_raw()) + } +} + +impl std::error::Error for Error {} +unsafe impl Send for Error {} +unsafe impl Sync for Error {} + +impl From for HRESULT { + fn from(error: Error) -> Self { + if let Some(info) = error.info { + unsafe { + SetErrorInfo(0, info.as_raw()); + } + } + error.code + } +} + +impl From for Error { + fn from(code: HRESULT) -> Self { + let mut info = None; + unsafe { GetErrorInfo(0, &mut info as *mut _ as _) }; + Self { code, info } + } +} + +impl From for std::io::Error { + fn from(from: Error) -> Self { + Self::from_raw_os_error(from.code.0) + } +} + +impl From for Error { + fn from(from: std::io::Error) -> Self { + match from.raw_os_error() { + Some(status) => HRESULT::from_win32(status as u32).into(), + None => HRESULT(E_UNEXPECTED).into(), + } + } +} + +impl From for Error { + fn from(_: std::string::FromUtf16Error) -> Self { + Self { + code: HRESULT::from_win32(ERROR_NO_UNICODE_TRANSLATION), + info: None, + } + } +} + +impl From for Error { + fn from(_: std::string::FromUtf8Error) -> Self { + Self { + code: HRESULT::from_win32(ERROR_NO_UNICODE_TRANSLATION), + info: None, + } + } +} + +impl From for Error { + fn from(_: std::num::TryFromIntError) -> Self { + Self { + code: HRESULT(E_INVALIDARG), + info: None, + } + } +} + +impl std::fmt::Debug for Error { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut debug = fmt.debug_struct("Error"); + debug + .field("code", &self.code) + .field("message", &self.message()) + .finish() + } +} + +impl std::fmt::Display for Error { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let message = self.message(); + if message.is_empty() { + std::write!(fmt, "{}", self.code()) + } else { + std::write!(fmt, "{} ({})", self.message(), self.code()) + } + } +} diff --git a/crates/libs/core/src/hresult.rs b/crates/libs/result/src/hresult.rs similarity index 67% rename from crates/libs/core/src/hresult.rs rename to crates/libs/result/src/hresult.rs index 5b83b45428..d51bafe031 100644 --- a/crates/libs/core/src/hresult.rs +++ b/crates/libs/result/src/hresult.rs @@ -36,7 +36,7 @@ impl HRESULT { if self.is_ok() { Ok(()) } else { - Err(Error::from(self)) + Err(self.into()) } } @@ -63,39 +63,48 @@ impl HRESULT { } /// The error message describing the error. - pub fn message(&self) -> HSTRING { - let mut message = HeapString(std::ptr::null_mut()); + pub fn message(&self) -> String { + let mut message = HeapString::default(); unsafe { - let size = crate::imp::FormatMessageW(crate::imp::FORMAT_MESSAGE_ALLOCATE_BUFFER | crate::imp::FORMAT_MESSAGE_FROM_SYSTEM | crate::imp::FORMAT_MESSAGE_IGNORE_INSERTS, std::ptr::null(), self.0 as u32, 0, &mut message.0 as *mut _ as *mut _, 0, std::ptr::null()); + let size = FormatMessageW( + FORMAT_MESSAGE_ALLOCATE_BUFFER + | FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_IGNORE_INSERTS, + std::ptr::null(), + self.0 as u32, + 0, + &mut message.0 as *mut _ as *mut _, + 0, + std::ptr::null(), + ); + if !message.0.is_null() && size > 0 { - HSTRING::from_wide(crate::imp::wide_trim_end(std::slice::from_raw_parts(message.0 as *const u16, size as usize))).unwrap_or_default() + String::from_utf16_lossy(wide_trim_end(std::slice::from_raw_parts( + message.0, + size as usize, + ))) } else { - HSTRING::default() + String::default() } } } /// Maps a Win32 error code to an HRESULT value. pub const fn from_win32(error: u32) -> Self { - Self(if error as i32 <= 0 { error } else { (error & 0x0000_FFFF) | (7 << 16) | 0x8000_0000 } as i32) + Self(if error as i32 <= 0 { + error + } else { + (error & 0x0000_FFFF) | (7 << 16) | 0x8000_0000 + } as i32) } } -impl RuntimeType for HRESULT { - const SIGNATURE: crate::imp::ConstBuffer = crate::imp::ConstBuffer::from_slice(b"struct(Windows.Foundation.HResult;i32)"); -} - -impl TypeKind for HRESULT { - type TypeKind = CopyType; -} - impl From> for HRESULT { fn from(result: Result) -> Self { if let Err(error) = result { return error.into(); } - HRESULT(0) } } @@ -111,15 +120,3 @@ impl std::fmt::Debug for HRESULT { f.write_fmt(format_args!("HRESULT({})", self)) } } - -struct HeapString(*mut u16); - -impl Drop for HeapString { - fn drop(&mut self) { - if !self.0.is_null() { - unsafe { - crate::imp::heap_free(self.0 as _); - } - } - } -} diff --git a/crates/libs/result/src/lib.rs b/crates/libs/result/src/lib.rs new file mode 100644 index 0000000000..f39d634736 --- /dev/null +++ b/crates/libs/result/src/lib.rs @@ -0,0 +1,21 @@ +/*! +Learn more about Rust for Windows here: +*/ + +mod bindings; +use bindings::*; + +mod com; +use com::*; + +mod strings; +use strings::*; + +mod error; +pub use error::Error; + +mod hresult; +pub use hresult::HRESULT; + +/// A specialized [`Result`] type that provides Windows error information. +pub type Result = std::result::Result; diff --git a/crates/libs/result/src/strings.rs b/crates/libs/result/src/strings.rs new file mode 100644 index 0000000000..44b8f7e68d --- /dev/null +++ b/crates/libs/result/src/strings.rs @@ -0,0 +1,73 @@ +use super::*; + +pub struct HeapString(pub *mut u16); + +impl Default for HeapString { + fn default() -> Self { + Self(std::ptr::null_mut()) + } +} + +impl Drop for HeapString { + fn drop(&mut self) { + if !self.0.is_null() { + unsafe { + HeapFree(GetProcessHeap(), 0, self.0 as _); + } + } + } +} + +#[repr(transparent)] +pub struct BasicString(*const u16); + +impl BasicString { + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + pub fn len(&self) -> usize { + if self.0.is_null() { + 0 + } else { + unsafe { SysStringLen(self.0) as usize } + } + } + + pub fn as_wide(&self) -> &[u16] { + unsafe { std::slice::from_raw_parts(self.as_ptr(), self.len()) } + } + + pub fn as_ptr(&self) -> *const u16 { + if !self.is_empty() { + self.0 + } else { + const EMPTY: [u16; 1] = [0]; + EMPTY.as_ptr() + } + } +} + +impl Default for BasicString { + fn default() -> Self { + Self(std::ptr::null_mut()) + } +} + +impl Drop for BasicString { + fn drop(&mut self) { + if !self.0.is_null() { + unsafe { SysFreeString(self.0) } + } + } +} + +pub fn wide_trim_end(mut wide: &[u16]) -> &[u16] { + while let Some(last) = wide.last() { + match last { + 32 | 9..=13 => wide = &wide[..wide.len() - 1], + _ => break, + } + } + wide +} diff --git a/crates/libs/result/tests/bindings.rs b/crates/libs/result/tests/bindings.rs new file mode 100644 index 0000000000..803ee2f2c8 --- /dev/null +++ b/crates/libs/result/tests/bindings.rs @@ -0,0 +1,4 @@ +#[test] +fn bindings() { + windows_bindgen::bindgen(["--etc", "tests/bindings.txt"]).unwrap(); +} diff --git a/crates/libs/result/tests/bindings.txt b/crates/libs/result/tests/bindings.txt new file mode 100644 index 0000000000..ddef671a55 --- /dev/null +++ b/crates/libs/result/tests/bindings.txt @@ -0,0 +1,21 @@ +--out src/bindings.rs +--config flatten sys minimal vtbl + +--filter + Windows.Win32.Foundation.E_INVALIDARG + Windows.Win32.Foundation.E_UNEXPECTED + Windows.Win32.Foundation.ERROR_NO_UNICODE_TRANSLATION + Windows.Win32.Foundation.GetLastError + Windows.Win32.Foundation.SysFreeString + Windows.Win32.Foundation.SysStringLen + Windows.Win32.System.Com.GetErrorInfo + Windows.Win32.System.Com.IErrorInfo + Windows.Win32.System.Com.SetErrorInfo + Windows.Win32.System.Diagnostics.Debug.FORMAT_MESSAGE_ALLOCATE_BUFFER + Windows.Win32.System.Diagnostics.Debug.FORMAT_MESSAGE_FROM_SYSTEM + Windows.Win32.System.Diagnostics.Debug.FORMAT_MESSAGE_IGNORE_INSERTS + Windows.Win32.System.Diagnostics.Debug.FormatMessageW + Windows.Win32.System.Memory.GetProcessHeap + Windows.Win32.System.Memory.HeapFree + Windows.Win32.System.WinRT.IRestrictedErrorInfo + Windows.Win32.System.WinRT.RoOriginateErrorW diff --git a/crates/samples/components/json_validator/src/lib.rs b/crates/samples/components/json_validator/src/lib.rs index cd5d35fa63..f62f43a3bf 100644 --- a/crates/samples/components/json_validator/src/lib.rs +++ b/crates/samples/components/json_validator/src/lib.rs @@ -44,7 +44,7 @@ unsafe fn create_validator(schema: *const u8, schema_len: usize, handle: *mut us let schema = json_from_raw_parts(schema, schema_len)?; let compiled = JSONSchema::compile(&schema) - .map_err(|error| Error::new(E_INVALIDARG, error.to_string().into()))?; + .map_err(|error| Error::new(E_INVALIDARG, error.to_string()))?; if handle.is_null() { return Err(E_POINTER.into()); @@ -98,7 +98,7 @@ unsafe fn validate( message = error.to_string(); } - Err(Error::new(E_INVALIDARG, message.into())) + Err(Error::new(E_INVALIDARG, message)) } } @@ -113,7 +113,7 @@ unsafe fn json_from_raw_parts(value: *const u8, value_len: usize) -> Result Result { let value = String::try_from(value)?; - serde_json::from_str(&value) - .map_err(|error| Error::new(E_INVALIDARG, format!("{error}").into())) + serde_json::from_str(&value).map_err(|error| Error::new(E_INVALIDARG, format!("{error}"))) } #[no_mangle] diff --git a/crates/tests/core/tests/error.rs b/crates/tests/core/tests/error.rs index aed4ccff00..37bf9e8910 100644 --- a/crates/tests/core/tests/error.rs +++ b/crates/tests/core/tests/error.rs @@ -5,7 +5,7 @@ use windows::{ #[test] fn display_debug() { - assert!(helpers::set_thread_ui_language()); + helpers::set_thread_ui_language(); let e = Error::from(ERROR_NO_UNICODE_TRANSLATION); let display = format!("{e}"); @@ -54,14 +54,11 @@ fn set_error_info() -> Result<()> { fn suppressed_error_info() -> Result<()> { unsafe { RoSetErrorReportingFlags(RO_ERROR_REPORTING_SUPPRESSSETERRORINFO.0 as u32)? }; - assert_eq!( - Error::new(E_FAIL, "message".into()).message(), - "Unspecified error" - ); + assert_eq!(Error::new(E_FAIL, "message").message(), "Unspecified error"); unsafe { RoSetErrorReportingFlags(RO_ERROR_REPORTING_USESETERRORINFO.0 as u32)? }; - assert_eq!(Error::new(E_FAIL, "message".into()).message(), "message"); + assert_eq!(Error::new(E_FAIL, "message").message(), "message"); Ok(()) } @@ -71,5 +68,5 @@ fn suppressed_error_info() -> Result<()> { fn just_hresult() { let e: Error = E_NOTIMPL.into(); assert!(e.code() == E_NOTIMPL); - assert!(e.info::().is_none()); + assert!(e.as_ptr().is_null()); } diff --git a/crates/tests/core/tests/hresult.rs b/crates/tests/core/tests/hresult.rs index d8d0fd8555..5fbc3af6a0 100644 --- a/crates/tests/core/tests/hresult.rs +++ b/crates/tests/core/tests/hresult.rs @@ -2,7 +2,7 @@ use windows::{core::*, Win32::Foundation::*}; #[test] fn ok() { - let error = Error::new(E_FAIL, "test info".into()); + let error = Error::new(E_FAIL, "test info"); let code: HRESULT = error.into(); // SetErrorInfo is called before dropping the Error object. let result: Result<()> = code.ok(); // GetErrorInfo is called to retrieve the error info. diff --git a/crates/tests/core/tests/pcstr.rs b/crates/tests/core/tests/pcstr.rs index 32982cb27f..0f4e109fc0 100644 --- a/crates/tests/core/tests/pcstr.rs +++ b/crates/tests/core/tests/pcstr.rs @@ -2,7 +2,7 @@ use windows::{core::*, Win32::Foundation::*}; #[test] fn test() -> Result<()> { - assert!(helpers::set_thread_ui_language()); + helpers::set_thread_ui_language(); let p: PCSTR = s!("hello"); let s: String = unsafe { p.to_string()? }; diff --git a/crates/tests/core/tests/pcwstr.rs b/crates/tests/core/tests/pcwstr.rs index 89a9b86730..1ddaf5231f 100644 --- a/crates/tests/core/tests/pcwstr.rs +++ b/crates/tests/core/tests/pcwstr.rs @@ -2,7 +2,7 @@ use windows::{core::*, Win32::Foundation::*}; #[test] fn test() -> Result<()> { - assert!(helpers::set_thread_ui_language()); + helpers::set_thread_ui_language(); let p: PCWSTR = w!("hello"); let s: String = unsafe { p.to_string()? }; diff --git a/crates/tests/core/tests/pstr.rs b/crates/tests/core/tests/pstr.rs index 1462968e6b..eb0638ccc4 100644 --- a/crates/tests/core/tests/pstr.rs +++ b/crates/tests/core/tests/pstr.rs @@ -2,7 +2,7 @@ use windows::{core::*, Win32::Foundation::*}; #[test] fn test() -> Result<()> { - assert!(helpers::set_thread_ui_language()); + helpers::set_thread_ui_language(); let p = PSTR::from_raw(s!("hello").as_ptr() as *mut _); let s: String = unsafe { p.to_string()? }; diff --git a/crates/tests/core/tests/pwstr.rs b/crates/tests/core/tests/pwstr.rs index c1c651b08c..a442f0d6a2 100644 --- a/crates/tests/core/tests/pwstr.rs +++ b/crates/tests/core/tests/pwstr.rs @@ -2,7 +2,7 @@ use windows::{core::*, Win32::Foundation::*}; #[test] fn test() -> Result<()> { - assert!(helpers::set_thread_ui_language()); + helpers::set_thread_ui_language(); let p = PWSTR::from_raw(w!("hello").as_ptr() as *mut _); let s: String = unsafe { p.to_string()? }; diff --git a/crates/tests/debugger_visualizer/tests/test.rs b/crates/tests/debugger_visualizer/tests/test.rs index 4f90ba2851..4f264112ad 100644 --- a/crates/tests/debugger_visualizer/tests/test.rs +++ b/crates/tests/debugger_visualizer/tests/test.rs @@ -13,10 +13,10 @@ struct Test; impl IErrorInfo_Impl for Test { fn GetGUID(&self) -> Result { - Err(Error::new(E_OUTOFMEMORY, "Out of memory message".into())) + Err(Error::new(E_OUTOFMEMORY, "Out of memory message")) } fn GetSource(&self) -> Result { - Err(Error::new(E_INVALIDARG, "Invalid argument message".into())) + Err(Error::new(E_INVALIDARG, "Invalid argument message")) } fn GetDescription(&self) -> Result { Ok(BSTR::new()) @@ -173,13 +173,13 @@ hstring : "This is an HSTRING" [Type: windows_core::strings::hstring::H [16] : 0x4e 'N' [Type: char16_t] [17] : 0x47 'G' [Type: char16_t] -out_of_memory_error : 0x8007000e (Not enough memory resources are available to complete this operation.) [Type: windows_core::error::Error] - [] [Type: windows_core::error::Error] - [info] : Some [Type: enum2$ >] +out_of_memory_error : 0x8007000e (Not enough memory resources are available to complete this operation.) [Type: windows_result::error::Error] + [] [Type: windows_result::error::Error] + [info] : Some [Type: enum2$ >] -invalid_argument_error : 0x80070057 (The parameter is incorrect.) [Type: windows_core::error::Error] - [] [Type: windows_core::error::Error] - [info] : Some [Type: enum2$ >] +invalid_argument_error : 0x80070057 (The parameter is incorrect.) [Type: windows_result::error::Error] + [] [Type: windows_result::error::Error] + [info] : Some [Type: enum2$ >] "# )] fn test_debugger_visualizer() { diff --git a/crates/tests/enums/tests/win.rs b/crates/tests/enums/tests/win.rs index cd43cd1fc6..69a2536535 100644 --- a/crates/tests/enums/tests/win.rs +++ b/crates/tests/enums/tests/win.rs @@ -36,7 +36,7 @@ fn const_pattern() { #[test] fn win32_error() { - assert!(helpers::set_thread_ui_language()); + helpers::set_thread_ui_language(); let e: WIN32_ERROR = ERROR_ACCESS_DENIED; assert!(e.0 == 5); diff --git a/crates/tests/error/tests/std.rs b/crates/tests/error/tests/std.rs index b2d28f9f20..e4b3861fbb 100644 --- a/crates/tests/error/tests/std.rs +++ b/crates/tests/error/tests/std.rs @@ -2,7 +2,7 @@ use windows::Win32::Foundation::*; #[test] fn conversions() { - assert!(helpers::set_thread_ui_language()); + helpers::set_thread_ui_language(); // Baseline HRESULT assert_eq!(E_INVALIDARG.message(), "The parameter is incorrect."); diff --git a/crates/tests/error/tests/trim.rs b/crates/tests/error/tests/trim.rs index c6c37d53c9..e45916e143 100644 --- a/crates/tests/error/tests/trim.rs +++ b/crates/tests/error/tests/trim.rs @@ -2,11 +2,11 @@ use windows::{core::*, Win32::Foundation::*}; #[test] fn test() { - assert!(helpers::set_thread_ui_language()); + helpers::set_thread_ui_language(); assert_eq!(E_FAIL.message(), "Unspecified error"); - assert_eq!(Error::new(E_FAIL, "Test \t\n\r".into()).message(), "Test"); + assert_eq!(Error::new(E_FAIL, "Test \t\n\r").message(), "Test"); - assert_eq!(Error::new(E_FAIL, " \t\n\r ".into()).message(), ""); + assert_eq!(Error::new(E_FAIL, " \t\n\r ").message(), ""); } diff --git a/crates/tests/helpers/Cargo.toml b/crates/tests/helpers/Cargo.toml index fd29670759..226a575a2c 100644 --- a/crates/tests/helpers/Cargo.toml +++ b/crates/tests/helpers/Cargo.toml @@ -4,9 +4,5 @@ version = "0.0.0" edition = "2021" publish = false -[dependencies.windows] -path = "../../libs/windows" -features = [ - "Win32_Foundation", - "Win32_Globalization", -] +[dependencies.windows-targets] +path = "../../libs/targets" diff --git a/crates/tests/helpers/src/lib.rs b/crates/tests/helpers/src/lib.rs index 5827334f06..1755e3cdd7 100644 --- a/crates/tests/helpers/src/lib.rs +++ b/crates/tests/helpers/src/lib.rs @@ -1,6 +1,17 @@ -use windows::core::*; -use windows::Win32::Globalization::*; +// Enables testing without pulling in a dependency on the `windows` crate. +windows_targets::link!("kernel32.dll" "system" fn SetThreadPreferredUILanguages(flags : u32, language : *const u16, _ : *mut u32) -> i32); +pub const MUI_LANGUAGE_NAME: u32 = 8u32; -pub fn set_thread_ui_language() -> bool { - unsafe { SetThreadPreferredUILanguages(MUI_LANGUAGE_NAME, w!("en-US"), None).as_bool() } +pub fn set_thread_ui_language() { + let language: Vec<_> = "en-US".encode_utf16().chain(std::iter::once(0)).collect(); + unsafe { + assert_eq!( + 1, + SetThreadPreferredUILanguages( + MUI_LANGUAGE_NAME, + language.as_ptr(), + std::ptr::null_mut() + ) + ); + } } diff --git a/crates/tests/implement/tests/data_object.rs b/crates/tests/implement/tests/data_object.rs index 8d28433f9d..39ae5b9b60 100644 --- a/crates/tests/implement/tests/data_object.rs +++ b/crates/tests/implement/tests/data_object.rs @@ -58,7 +58,7 @@ impl IDataObject_Impl for Test { fn EnumFormatEtc(&self, _: u32) -> Result { unsafe { (*self.0.get()).EnumFormatEtc = true; - Err(Error::OK) + Err(Error::empty()) } } @@ -79,7 +79,7 @@ impl IDataObject_Impl for Test { fn EnumDAdvise(&self) -> Result { unsafe { (*self.0.get()).EnumDAdvise = true; - Err(Error::OK) + Err(Error::empty()) } } } @@ -100,7 +100,7 @@ fn test() -> Result<()> { assert!(r.is_err()); let e = r.unwrap_err(); assert!(e.code() == S_OK); - assert!(e.info::().is_none()); + assert!(e.as_ptr().is_null()); d.DAdvise(&Default::default(), 0, None)?; diff --git a/crates/tests/implement/tests/error.rs b/crates/tests/implement/tests/error.rs index 57d78efb8d..eeca0990b3 100644 --- a/crates/tests/implement/tests/error.rs +++ b/crates/tests/implement/tests/error.rs @@ -5,7 +5,7 @@ struct Test; impl IStringable_Impl for Test { fn ToString(&self) -> Result { - Err(Error::new(E_INVALIDARG, "Test message".into())) + Err(Error::new(E_INVALIDARG, "Test message")) } } diff --git a/crates/tests/implement/tests/generic_default.rs b/crates/tests/implement/tests/generic_default.rs index 5b30ebd185..cca8bd83c8 100644 --- a/crates/tests/implement/tests/generic_default.rs +++ b/crates/tests/implement/tests/generic_default.rs @@ -21,7 +21,7 @@ where fn GetAt(&self, index: u32) -> Result { match self.0.get(index as usize) { Some(value) => T::from_default(value), - None => Err(Error::new(E_BOUNDS, "".into())), + None => Err(Error::new(E_BOUNDS, "")), } } diff --git a/crates/tests/implement/tests/into_impl.rs b/crates/tests/implement/tests/into_impl.rs index 8131efe804..0fef5c54cf 100644 --- a/crates/tests/implement/tests/into_impl.rs +++ b/crates/tests/implement/tests/into_impl.rs @@ -24,7 +24,7 @@ where if owner.0.len() > (*this).1 { Ok(owner.0[(*this).1].clone()) } else { - Err(Error::new(E_BOUNDS, "".into())) + Err(Error::new(E_BOUNDS, "")) } } } diff --git a/crates/tests/result/Cargo.toml b/crates/tests/result/Cargo.toml new file mode 100644 index 0000000000..6a3c69acf4 --- /dev/null +++ b/crates/tests/result/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "test_result" +version = "0.0.0" +edition = "2021" +publish = false + +[dependencies.windows-result] +path = "../../libs/result" + +[dependencies.windows-targets] +path = "../../libs/targets" + +[dependencies] +helpers = { package = "test_helpers", path = "../helpers" } diff --git a/crates/tests/result/src/lib.rs b/crates/tests/result/src/lib.rs new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/crates/tests/result/src/lib.rs @@ -0,0 +1 @@ + diff --git a/crates/tests/result/tests/error.rs b/crates/tests/result/tests/error.rs new file mode 100644 index 0000000000..4ca95d3e28 --- /dev/null +++ b/crates/tests/result/tests/error.rs @@ -0,0 +1,51 @@ +use windows_result::*; + +const S_OK: HRESULT = HRESULT(0); +const E_INVALIDARG: HRESULT = HRESULT(-2147024809i32); +const ERROR_CANCELLED: u32 = 1223; +const E_CANCELLED: HRESULT = HRESULT::from_win32(ERROR_CANCELLED); + +windows_targets::link!("kernel32.dll" "system" fn SetLastError(code: u32)); + +#[test] +fn empty() { + const EMPTY: Error = Error::empty(); + assert_eq!(EMPTY.code(), S_OK); + assert!(EMPTY.as_ptr().is_null()); + assert_eq!(EMPTY.message(), "The operation completed successfully."); +} + +#[test] +fn new() { + let e = Error::new(E_INVALIDARG, ""); + assert_eq!(e.code(), E_INVALIDARG); + assert!(e.as_ptr().is_null()); + + let e = Error::new(E_INVALIDARG, "test message"); + assert_eq!(e.code(), E_INVALIDARG); + assert!(!e.as_ptr().is_null()); + assert_eq!(e.message(), "test message"); +} + +#[test] +fn from_hresult() { + let e = Error::from_hresult(E_INVALIDARG); + assert_eq!(e.code(), E_INVALIDARG); + assert!(e.as_ptr().is_null()); + assert_eq!(e.message(), "The parameter is incorrect."); +} + +#[test] +fn from_win32() { + unsafe { SetLastError(0) }; + + let e = Error::from_win32(); + assert_eq!(e.code(), S_OK); + assert!(e.as_ptr().is_null()); + + unsafe { SetLastError(ERROR_CANCELLED) }; + + let e = Error::from_win32(); + assert!(e.as_ptr().is_null()); + assert_eq!(e.code(), E_CANCELLED); +} diff --git a/crates/tests/result/tests/hresult.rs b/crates/tests/result/tests/hresult.rs new file mode 100644 index 0000000000..74aee0a485 --- /dev/null +++ b/crates/tests/result/tests/hresult.rs @@ -0,0 +1,77 @@ +use windows_result::*; + +const S_OK: HRESULT = HRESULT(0); +const S_FALSE: HRESULT = HRESULT(1); +const E_INVALIDARG: HRESULT = HRESULT(-2147024809i32); +const ERROR_CANCELLED: u32 = 1223; +const E_CANCELLED: HRESULT = HRESULT::from_win32(ERROR_CANCELLED); + +#[test] +fn is_ok() { + assert!(S_OK.is_ok()); + assert!(S_FALSE.is_ok()); + assert!(!E_INVALIDARG.is_ok()); +} + +#[test] +fn is_err() { + assert!(!S_OK.is_err()); + assert!(!S_FALSE.is_err()); + assert!(E_INVALIDARG.is_err()); +} + +#[test] +fn ok() { + assert!(S_OK.ok().is_ok()); + assert!(S_FALSE.ok().is_ok()); + assert!(E_INVALIDARG.ok().is_err()); +} + +#[test] +fn map() { + assert_eq!(123, S_OK.map(|| 123).unwrap()); + assert_eq!(E_INVALIDARG, E_INVALIDARG.map(|| 123).unwrap_err().code()); +} + +#[test] +fn and_then() { + assert_eq!(123, S_OK.and_then(|| Ok(123)).unwrap()); + + assert_eq!( + E_INVALIDARG, + E_INVALIDARG.and_then(|| Ok(123)).unwrap_err().code() + ); +} + +#[test] +fn message() { + helpers::set_thread_ui_language(); + assert_eq!(S_OK.message(), "The operation completed successfully."); + + assert_eq!( + E_CANCELLED.message(), + "The operation was canceled by the user." + ); + + assert_eq!(HRESULT(-1).message(), ""); +} + +#[test] +fn from_win32() { + assert_eq!(E_INVALIDARG, HRESULT::from_win32(E_INVALIDARG.0 as u32)); + assert_eq!(E_CANCELLED, HRESULT::from_win32(ERROR_CANCELLED)); +} + +#[test] +fn from_result() { + let result: Result<()> = Err(Error::new(E_INVALIDARG, "test message")); + let err = HRESULT::from(result).ok().unwrap_err(); + assert_eq!(err.code(), E_INVALIDARG); + assert_eq!(err.message(), "test message"); +} + +#[test] +fn display() { + assert_eq!(E_INVALIDARG.to_string(), "0x80070057"); + assert_eq!(format!("{:?}", E_INVALIDARG), "HRESULT(0x80070057)"); +} diff --git a/crates/tests/win32/tests/hresult.rs b/crates/tests/win32/tests/hresult.rs index a97288450a..045d423374 100644 --- a/crates/tests/win32/tests/hresult.rs +++ b/crates/tests/win32/tests/hresult.rs @@ -4,7 +4,7 @@ use windows::Win32::Foundation::*; #[test] fn test_message() { - assert!(helpers::set_thread_ui_language()); + helpers::set_thread_ui_language(); let code: HRESULT = ERROR_SUCCESS.into(); let message: String = code.message().try_into().unwrap(); diff --git a/crates/tests/win32/tests/win32.rs b/crates/tests/win32/tests/win32.rs index 82a75a8d6a..7516079bf1 100644 --- a/crates/tests/win32/tests/win32.rs +++ b/crates/tests/win32/tests/win32.rs @@ -132,7 +132,7 @@ fn function() -> windows::core::Result<()> { #[test] fn bool_as_error() { unsafe { - assert!(helpers::set_thread_ui_language()); + helpers::set_thread_ui_language(); let error = SetEvent(HANDLE(0)).unwrap_err(); assert_eq!(error.code(), windows::core::HRESULT(-2147024890)); diff --git a/crates/tests/winrt/tests/collisions.rs b/crates/tests/winrt/tests/collisions.rs index 493d43fd93..9c7c64e5d2 100644 --- a/crates/tests/winrt/tests/collisions.rs +++ b/crates/tests/winrt/tests/collisions.rs @@ -16,7 +16,7 @@ fn wifi() -> windows::core::Result<()> { assert!(!a.is_empty()); // from_id_async from IWiFiDirectDeviceStatics - assert!(WiFiDirectDevice::FromIdAsync(&a)?.get() == Err(windows::core::Error::OK)); + assert!(WiFiDirectDevice::FromIdAsync(&a)?.get() == Err(windows::core::Error::empty())); // get_device_selector overload from IWiFiDirectDeviceStatics2 is renamed to get_device_selector2 let c = WiFiDirectDevice::GetDeviceSelector2(WiFiDirectDeviceSelectorType::DeviceInterface)?; diff --git a/crates/tests/winrt/tests/error.rs b/crates/tests/winrt/tests/error.rs index cc2328f9fb..0cd9650624 100644 --- a/crates/tests/winrt/tests/error.rs +++ b/crates/tests/winrt/tests/error.rs @@ -3,7 +3,7 @@ use windows::{Foundation::Uri, Win32::Foundation::E_NOINTERFACE}; #[test] fn from_hresult() { - assert!(helpers::set_thread_ui_language()); + helpers::set_thread_ui_language(); let error: windows::core::Error = windows::core::HRESULT(-2147467260).into(); @@ -14,8 +14,7 @@ fn from_hresult() { #[test] fn originate() { - let error = - windows::core::Error::new(windows::core::HRESULT(-2147467260), "test originate".into()); + let error = windows::core::Error::new(windows::core::HRESULT(-2147467260), "test originate"); assert_eq!(error.code(), windows::core::HRESULT(-2147467260)); assert_eq!(error.message(), "test originate"); @@ -30,7 +29,7 @@ fn originate() { #[test] fn bad_uri() { - assert!(helpers::set_thread_ui_language()); + helpers::set_thread_ui_language(); let result = Uri::CreateUri(&windows::core::HSTRING::from("INVALID")); let error: windows::core::Error = result.unwrap_err(); @@ -42,10 +41,7 @@ fn bad_uri() { #[test] fn convertible() { fn windows_error() -> windows::core::Result<()> { - Err(windows::core::Error::new( - E_NOINTERFACE, - "test message".into(), - )) + Err(windows::core::Error::new(E_NOINTERFACE, "test message")) } fn convertible_error() -> core::result::Result<(), Box> { diff --git a/crates/tests/winrt/tests/send_sync.rs b/crates/tests/winrt/tests/send_sync.rs index 9bc4c2dc30..d8ed780889 100644 --- a/crates/tests/winrt/tests/send_sync.rs +++ b/crates/tests/winrt/tests/send_sync.rs @@ -86,7 +86,7 @@ fn send_async_no_class() { #[test] fn send_sync_err() { - assert!(helpers::set_thread_ui_language()); + helpers::set_thread_ui_language(); let err = Uri::CreateUri(&HSTRING::from("BADURI")).unwrap_err(); let code = err.code(); diff --git a/crates/tools/core/bindings.txt b/crates/tools/core/bindings.txt index 5107f2ff2f..cb5d520850 100644 --- a/crates/tools/core/bindings.txt +++ b/crates/tools/core/bindings.txt @@ -5,9 +5,7 @@ --filter Windows.Win32.Foundation.CloseHandle - Windows.Win32.Foundation.ERROR_NO_UNICODE_TRANSLATION Windows.Win32.Foundation.FreeLibrary - Windows.Win32.Foundation.GetLastError Windows.Win32.Foundation.SysAllocStringLen Windows.Win32.Foundation.SysFreeString Windows.Win32.Foundation.SysStringLen @@ -30,10 +28,6 @@ Windows.Win32.System.Com.StructuredStorage.PropVariantToVariant Windows.Win32.System.Com.StructuredStorage.VariantToPropVariant Windows.Win32.System.Diagnostics.Debug.EncodePointer - Windows.Win32.System.Diagnostics.Debug.FORMAT_MESSAGE_ALLOCATE_BUFFER - Windows.Win32.System.Diagnostics.Debug.FORMAT_MESSAGE_FROM_SYSTEM - Windows.Win32.System.Diagnostics.Debug.FORMAT_MESSAGE_IGNORE_INSERTS - Windows.Win32.System.Diagnostics.Debug.FormatMessageW Windows.Win32.System.LibraryLoader.GetProcAddress Windows.Win32.System.LibraryLoader.LOAD_LIBRARY_SEARCH_DEFAULT_DIRS Windows.Win32.System.LibraryLoader.LoadLibraryExA @@ -69,6 +63,3 @@ Windows.Win32.System.Variant.VT_UI8 Windows.Win32.System.Variant.VT_UNKNOWN Windows.Win32.System.WinRT.RoGetActivationFactory - Windows.Win32.System.WinRT.RoOriginateError - Windows.Win32.System.Com.GetErrorInfo - Windows.Win32.System.Com.SetErrorInfo diff --git a/crates/tools/core/com_bindings.txt b/crates/tools/core/com_bindings.txt index 50b43c1e5f..8f9d5a9c57 100644 --- a/crates/tools/core/com_bindings.txt +++ b/crates/tools/core/com_bindings.txt @@ -7,22 +7,16 @@ Windows.Foundation.IReference Windows.Foundation.IStringable Windows.Foundation.PropertyValue - Windows.Win32.Foundation.CLASS_E_CLASSNOTAVAILABLE Windows.Win32.Foundation.CO_E_NOTINITIALIZED Windows.Win32.Foundation.E_BOUNDS - Windows.Win32.Foundation.E_INVALIDARG Windows.Win32.Foundation.E_NOINTERFACE Windows.Win32.Foundation.E_OUTOFMEMORY - Windows.Win32.Foundation.E_UNEXPECTED Windows.Win32.Foundation.JSCRIPT_E_CANTEXECUTE Windows.Win32.Foundation.RPC_E_DISCONNECTED Windows.Win32.Foundation.TYPE_E_TYPEMISMATCH Windows.Win32.System.Com.CoCreateGuid Windows.Win32.System.Com.IAgileObject - Windows.Win32.System.Com.IErrorInfo Windows.Win32.System.WinRT.AGILEREFERENCE_DEFAULT Windows.Win32.System.WinRT.IAgileReference - Windows.Win32.System.WinRT.ILanguageExceptionErrorInfo2 - Windows.Win32.System.WinRT.IRestrictedErrorInfo Windows.Win32.System.WinRT.IWeakReferenceSource Windows.Win32.System.WinRT.RoGetAgileReference