-
Notifications
You must be signed in to change notification settings - Fork 531
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
Transform NTSTATUS
functions into Result<()>
#994
Conversation
@kennykerr With this adding |
@MarijnS95 good catch yes this fixes #739 as well. There is also #656 which is the larger issue of traits for structs. Enums already derive all the usual traits. |
@kennykerr Thanks, good to know. Are there many non- |
This looks better than what I eventually came up with, thanks a lot! Though I feel that support is a bit too minimal now. It's dropping two pieces of information: A non-zero success code, and the actual I'm not aware of a way to implement a custom comparison operator in Rust between different types. Implementing TryFrom on I'm leaving a few comments in the code as well. |
impl NTSTATUS { | ||
#[inline] | ||
pub const fn is_ok(self) -> bool { | ||
self.0 == 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This deviates from the NT_SUCCESS
macro, which essentially does self.0 >= 0
. I'm worried that keeping it like this will damage error propagation ergonomics through the ?
operator.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, good point.
#[inline] | ||
pub fn ok(self) -> ::windows::Result<()> { | ||
if self.is_ok() { | ||
Ok(()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about changing this to return ::windows::Result<NTSTATUS>
and Ok(self)
? The benefits are that clients can continue to use the ?
operator for error propagation in case of true errors, but still get access to a non-zero success code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder how common this is? How often would this end up being used? Is there a representative example?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not aware of one. There are about 80 non-zero status codes with severity 0 (success) and around 100 with severity 1 (information) defined in ntstatus.h. While I did recognize some (e.g. STATUS_PENDING
) I wouldn't know of a Windows API call where those actually show up as the return value.
Things are different with driver development, though, where functions like IoRegisterDeviceInterface can indeed return values like STATUS_OBJECT_NAME_EXISTS
(0x40000000). I don't know whether driver development is in scope for the Windows crate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, perhaps it would be better to keep the .ok()
method but remove the automatic Result
transformation so that developers can easily call method().ok()
for most cases but deal directly with the error code in other cases.
NTSTATUS
is much the same as HRESULT
in its structure and usage but it certainly does seem to have a lot more use for success codes. I tried writing a simple wrapper for BCryptVerifySignature
but its certainly not convenient.
We'd like to eventually support driver development
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps just a to_hresult
method on NTSTATUS
to make this a littler simpler:
fn verify() -> Result<bool> {
match BCryptVerifySignature() {
Err(e) => {
if e.code() == STATUS_INVALID_SIGNATURE.to_hresult() {
Ok(false)
} else {
Err(e)
}
}
_ => Ok(true),
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And ironically the docs state that it could return NTE_NO_MEMORY
, which is actually an HRESULT
. 😉
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is what impl NTSTATUS {
#[inline]
pub const fn hresult(self) -> ::windows::HRESULT {
::windows::HRESULT(self.0 | 0x10000000)
}
}
impl PartialEq<::windows::HRESULT> for NTSTATUS {
fn eq(&self, hresult: &::windows::HRESULT) -> bool {
self.hresult() == *hresult
}
} And the other way around ( |
Thanks, Marijn, that's actually what I asked for, but not what I wanted/meant to ask. I was going for something like if let Err(e) = BCryptVerifySignature(...) {
match e {
STATUS_INVALID_SIGNATURE => /* ... */,
STATUS_NOT_SUPPORTED => /* ... */,
_ => return Err(e),
}
} As far as I understand, the |
@tim-weis It uses Alternatively you could write: if let Err(e) = BCryptVerifySignature(...) {
match e.to_ntresult() {
Some(STATUS_INVALID_SIGNATURE) => /* ... */,
Some(STATUS_NOT_SUPPORTED) => /* ... */,
Some(ntresult) => /* ... */,
None => return Err(e),
}
} if the function is expected to return non- |
This provides minimal support for transforming functions returning
NTSTATUS
values intoResult<()>
so that they can more easily be used with other Windows functions, providing uniform error propagation.@tim-weis what do you think of this as a solution for #983 - it avoids complicating the core windows crate with knowledge of
NTSTATUS
. Since most APIs will never need it there seems to be a case for keeping it specific to such APIs and out of the base crate.Here's a BCrypt example:
And the output:
I also filed a bug for the BCrypt handle types: microsoft/win32metadata#585
Fixes #739
Fixes #983