Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add NTSTATUS message formatting support to the windows-result crate #2861

Merged
merged 2 commits into from
Feb 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ impl NTSTATUS {
}
#[inline]
pub const fn to_hresult(self) -> ::windows_core::HRESULT {
::windows_core::HRESULT(self.0 | 0x1000_0000)
::windows_core::HRESULT::from_nt(self.0)
}
#[inline]
pub fn ok(self) -> ::windows_core::Result<()> {
Expand Down
6 changes: 6 additions & 0 deletions crates/libs/result/src/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
::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!("kernel32.dll" "system" fn LoadLibraryExA(lplibfilename : PCSTR, hfile : HANDLE, dwflags : LOAD_LIBRARY_FLAGS) -> HMODULE);
::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));
Expand All @@ -23,6 +24,7 @@ pub const ERROR_NO_UNICODE_TRANSLATION: WIN32_ERROR = 1113u32;
pub const E_INVALIDARG: HRESULT = 0x80070057_u32 as _;
pub const E_UNEXPECTED: HRESULT = 0x8000FFFF_u32 as _;
pub const FORMAT_MESSAGE_ALLOCATE_BUFFER: FORMAT_MESSAGE_OPTIONS = 256u32;
pub const FORMAT_MESSAGE_FROM_HMODULE: FORMAT_MESSAGE_OPTIONS = 2048u32;
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;
Expand Down Expand Up @@ -51,6 +53,7 @@ impl GUID {
}
pub type HANDLE = isize;
pub type HEAP_FLAGS = u32;
pub type HMODULE = isize;
pub type HRESULT = i32;
pub const IID_IErrorInfo: GUID = GUID::from_u128(0x1cf2b120_547d_101b_8e65_08002b2bd119);
#[repr(C)]
Expand Down Expand Up @@ -86,6 +89,9 @@ pub struct IUnknown_Vtbl {
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 LOAD_LIBRARY_FLAGS = u32;
pub const LOAD_LIBRARY_SEARCH_DEFAULT_DIRS: LOAD_LIBRARY_FLAGS = 4096u32;
pub type PCSTR = *const u8;
pub type PCWSTR = *const u16;
pub type PWSTR = *mut u16;
pub type WIN32_ERROR = u32;
31 changes: 26 additions & 5 deletions crates/libs/result/src/hresult.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,26 @@ impl HRESULT {
/// The error message describing the error.
pub fn message(&self) -> String {
let mut message = HeapString::default();
let mut code = self.0;
let mut module = 0;

let mut flags = FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_IGNORE_INSERTS;

unsafe {
if self.0 & 0x1000_0000 == 0x1000_0000 {
code ^= 0x1000_0000;
flags |= FORMAT_MESSAGE_FROM_HMODULE;

module =
LoadLibraryExA(b"ntdll.dll\0".as_ptr(), 0, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
}

let size = FormatMessageW(
FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_IGNORE_INSERTS,
std::ptr::null(),
self.0 as u32,
flags,
module as _,
code as _,
0,
&mut message.0 as *mut _ as *mut _,
0,
Expand All @@ -98,6 +110,15 @@ impl HRESULT {
(error & 0x0000_FFFF) | (7 << 16) | 0x8000_0000
} as i32)
}

/// Maps an NT error code to an HRESULT value.
pub const fn from_nt(error: i32) -> Self {
Self(if error >= 0 {
error
} else {
error | 0x1000_0000
})
}
}

impl<T> From<Result<T>> for HRESULT {
Expand Down
3 changes: 3 additions & 0 deletions crates/libs/result/tests/bindings.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@
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_HMODULE
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.LOAD_LIBRARY_SEARCH_DEFAULT_DIRS
Windows.Win32.System.LibraryLoader.LoadLibraryExA
Windows.Win32.System.Memory.GetProcessHeap
Windows.Win32.System.Memory.HeapFree
Windows.Win32.System.WinRT.IRestrictedErrorInfo
Expand Down
2 changes: 1 addition & 1 deletion crates/libs/windows/src/Windows/Win32/Foundation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11689,7 +11689,7 @@ impl NTSTATUS {
}
#[inline]
pub const fn to_hresult(self) -> ::windows_core::HRESULT {
::windows_core::HRESULT(self.0 | 0x1000_0000)
::windows_core::HRESULT::from_nt(self.0)
}
#[inline]
pub fn ok(self) -> ::windows_core::Result<()> {
Expand Down
1 change: 1 addition & 0 deletions crates/tests/error/tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ fn ntstatus() -> Result<()> {
let hresult: HRESULT = STATUS_NOT_FOUND.into();

assert_eq!(error.code(), hresult);
assert_eq!(error.message(), "The object was not found.");
assert_eq!(STATUS_NOT_FOUND.is_ok(), false);
assert_eq!(STATUS_NOT_FOUND.is_err(), true);
assert_eq!(STATUS_SUCCESS.is_ok(), true);
Expand Down
13 changes: 13 additions & 0 deletions crates/tests/result/tests/hresult.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@ 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);

const STATUS_NOT_FOUND: i32 = -1073741275;
const E_STATUS_NOT_FOUND: HRESULT = HRESULT::from_nt(STATUS_NOT_FOUND);

#[test]
fn is_ok() {
assert!(S_OK.is_ok());
Expand Down Expand Up @@ -53,13 +57,22 @@ fn message() {
"The operation was canceled by the user."
);

assert_eq!(E_STATUS_NOT_FOUND.message(), "The object was not found.");
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));
assert_eq!(HRESULT(0), HRESULT::from_win32(0));
}

#[test]
fn from_nt() {
assert_eq!(E_STATUS_NOT_FOUND, HRESULT::from_nt(STATUS_NOT_FOUND));
assert_eq!(S_OK, HRESULT::from_nt(0));
assert_eq!(HRESULT(1), HRESULT::from_nt(1));
}

#[test]
Expand Down