Skip to content

Commit

Permalink
Add NTSTATUS message formatting support to the windows-result cra…
Browse files Browse the repository at this point in the history
…te (#2861)
  • Loading branch information
kennykerr committed Feb 20, 2024
1 parent 6682202 commit 426865a
Show file tree
Hide file tree
Showing 7 changed files with 51 additions and 7 deletions.
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

0 comments on commit 426865a

Please sign in to comment.