-
Notifications
You must be signed in to change notification settings - Fork 122
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 an attribute for identifying methods and functions that return alternative success codes #559
Comments
If you check here, and place the value of DRAGDROP_S_DROP(262400) where self.0 is, it returns true. but so does DRAGDROP_S_CANCEL(262401). So you want identify which one you got? |
That's not really the point. The problem is that you can not get the original hresult, because if positive it never leaves pub unsafe fn DoDragDrop<'a>(
pdataobj: impl ::windows::IntoParam<'a, IDataObject>,
pdropsource: impl ::windows::IntoParam<'a, IDropSource>,
dwokeffects: u32,
pdweffect: *mut u32,
) -> ::windows::Result<()> {
#[cfg(windows)]
{
#[link(name = "OLE32")]
extern "system" {
fn DoDragDrop(
pdataobj: ::windows::RawPtr,
pdropsource: ::windows::RawPtr,
dwokeffects: u32,
pdweffect: *mut u32,
) -> ::windows::HRESULT;
}
DoDragDrop(
pdataobj.into_param().abi(),
pdropsource.into_param().abi(),
::std::mem::transmute(dwokeffects),
::std::mem::transmute(pdweffect),
)
// Here a positive HRESULT gets swallowed.
.ok()
}
#[cfg(not(windows))]
unimplemented!("Unsupported target OS");
} |
This potential issue has been acknowledged. I don't know whether this has been missed in the implementation, or OLE just being odd. Either way it's an issue that would need to be addressed. |
Yes, I'm considering a few options for dealing with this. One is to preserve the original signature as a fallback (#737) or a custom |
For me custom result object would definitely solve the issue. The object could also implement |
I'm unsure which way to achieve this is best. Two variants I see:
|
The vast majority of APIs behave nicely and only ever return S_OK on success. For those that return S_FALSE or any of the other non-error result codes, we should get those tagged in the metadata so that projections can behave differently. I don't think that this problem will be unique to rust. |
I love @damyanp's suggestion. He's absolutely right of course. The vast majority of APIs don't need this and penalizing every API to accommodate the handful that use odd success codes doesn't make sense. Transferring to the win32 metadata repo - hopefully they can add an attribute to such functions/methods and then languages like Rust can simply project their signatures accordingly. |
Didn't even know that was an option :) Sounds great, though I'm not clear on how this would work. I assume every call like this would need to be tagged manually, since there's no way to infer this from headers? |
You want an attribute that indicates that a function doesn't return S_OK on success? |
This is for functions that return success HRESULTs that aren't S_OK. So it might return S_OK, but they may also return S_FALSE, or some other >0 HRESULT to include additional data about the result. Generally in projections we'd like to map HRESULTs to exceptions or whatever the idiomatic error handling scheme is for a language. Most (if not all) of these assume that there only one possible way to indicate "success" and don't have a natural way to return "I succeeded, but....". We can build these things, but it's pretty unnatural and we think that for the vast majority of Win32/COM APIs it would be unnecessary, so we're looking for a way to only do this for the cases where we know that applications want to check for non-S_OK success codes. |
What would you like to see in the metadata specifically? I understand the scenario but I'm not clear how you want to model this. |
I was thinking of a per-function attribute that indicates that applications might want to inspect successful HRESULTs. |
So just a boolean? And not a list of all possible success HRESULTs? |
It might be possible for projections to make use of all the HRESULTs, but in practice I suspect most will opt to just pass the HRESULT back to the caller somehow, so a boolean would be fine. @kennykerr might have thoughts on this. |
Just an attribute on functions/methods that return HRESULT. The attribute doesn't need any values (not even bool). It just signals to the languages that the HRESULT has alternate status codes. |
So for example: [DllImport("OLE32", ExactSpelling = true)]
[SupportedOSPlatform("windows5.0")]
[AlternateSuccessCodes] // <-- add this
public unsafe static extern HRESULT DoDragDrop([In] IDataObject pDataObj, [In] IDropSource pDropSource, [In] uint dwOKEffects, [In][Out] uint* pdwEffect); |
Yep I'm sure folks will be happy to help report/list APIs that need the attribute. |
Ok. We can add support for an attribute but will likely need to crowd source where to apply it. |
Great, thanks! |
For this particular requirement, I agree a mere attribute is enough. However if we can document which HRESULTs may be expected, then projections can automatically emit those HRESULTs as constants as well. It bites if the user asks for one method, then has to explicitly ask individually for 8 other error codes to be generated as well where the projection could have anticipated that given info from the metadata. |
I was wondering if we should extend this concept to cover other error return mechanisms? An extreme example would be WaitForSingleObject. There are also various functions that use the "return bool, use GetLastError" pattern that would be nice to map to idomatic error handling. Another example here is that many IO functions use the bool/GLE approach, but we expect developers to know that ERROR_IO_PENDING is not actually an error. We could add a new attribute for each error reporting mechanism (in which case I think we'd need to document that some attributes are mutually exclusive) or we could have a single "error_handling" attribute that has a parameter to indicate how projections should interpret return codes. |
This is #536.
WaitForSingleObject returns an enum today which contains all the possible return codes. Projections would need to know though that WAIT_FAILED means call GetLastError to provide a friendlier interface on top. This is similar to #536. I think one requirement is, for APIs with
This is a combination of both issues. The additional requirement from this thread is listing AlternateSuccessCodes in an attribute, ideally with a list of those codes. Is this correct? I'm not sure we need one "error_handling" mega-attribute to handle all of these requirements. These could be separate attributes. |
I think your understanding matches mine. From my POV I don't have a firm requirement for a one-attribute-to-rule-them-all vs individual attributes to handle the various cases. I thinking about the potential issues with mutually exclusive attributes (eg you maybe you can't have AlternateSuccessCodes along with SetLastError). However, maybe there's extra expressiveness to be had by allowing these attributes to be combined? So maybe ReadFile would be both SetLastError and AlternateSuccessCodes(ERROR_IO_PENDING). |
ReadFile already has ERROR_IO_PENDING is returned by GetLastError, not by ReadFile itself. Is this the same for the other I/O APIs you mentioned that behave like this? That seems a little different than a function that returns S_FALSE for success, which is what the original issue here is about. How would projections want to handle this? Would we need to differentiate return codes vs. GetLastError codes in the attributes? Reading through https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-readfile#pipes, it looks like ReadFile with pipes can also return false with GetLastError() = ERROR_MORE_DATA which is similar to ERROR_IO_PENDING. Again, what would be a natural way for projections to provide a friendlier interface on top? |
IMO, I can see someone wanting to build a projection where for something like ReadFile it would call GetLastError if it returned false and then return a status code indicating whether or not it completed, or instead hit the IO_PENDING or MORE_DATA condition. There may be questions about whether or not this behavior would be right for all projections, but I think for some use cases this would be the right thing to do. It's what I personally would like windows-rs to do for example, but I understand that that may not be desirable for everyone. Maybe this can be summed up (and this might be controversial) as ideally we wouldn't have GetLastError() projected at all. Instead we would ensure that the projection did the right thing and called it as necessary, so you can eliminate the entire class of bugs where someone accidentally calls something that sets the last error before calling GetLastError to log the error. A source of the "Error: the operation completed successfully" message boxes you see from time to time. |
Ok, thanks. To me it just seemed to give the impression that the attribute could somehow be applied to some other part of the function (which I don't think makes sense). I guess I'm going to have to figure out how to read attributes on return types. 😉 |
The Windows crate for Rust now supports the |
For what its worth, I've implemented the presence of this attribute to have the exact same semantics as DllImportAttribute.PreserveSig.
|
So how do we annotate a Win32-style function for a new API so that the WinmdGenerator adds the attribute? And any thoughts about just using the |
I think using PreserveSig is a good idea. I'm reopening the issue so I can change to use PreserveSig instead of AlternateSuccessCodes. That doesn't answer your first question about how to annotate new C APIs, though. Any ideas? |
We could mint a new SAL-style annotation. #define _WIN32_METADATA_PRESERVESIG_
_WIN32_METADATA_PRESERVESIG_
STDAPI TestFunction(); That would allow us to eventually add it to the Windows SDK for in-box APIs (if desired) but also start using it today for new components and validation without waiting for a new SDK. |
|
The ECMA 335 |
@kennykerr Ah great, fixed itself! 👍 |
In CsWin32 we default to preserving non-preservesig signatures, but users can specifically choose PreserveSig for certain methods that return multiple success codes. Is the proposal here then that the metadata express that so users don't have to? |
Yes. |
Another customer blocked by this issue: microsoft/windows-rs#1371 |
My fix will be to use this instead of using the attribute I was using before. |
Just tried this in the latest update. I see the The trouble is that hundreds of other functions also have this set. I don't know if they were set before but I can't see how I can use the new bit in place of |
Yeah seems to be set for all APIs. win32metadata/sources/ClangSharpSourceToWinmd/ClangSharpSourceWinmdGenerator.cs Lines 1182 to 1187 in 27d2eb9
Doesn't appear using |
Or remove it from all methods and just apply it to those that actually need it. |
Yes, that's the right way to fix it. |
For example
DoDragDrop
returnsDRAGDROP_S_DROP
on successful drop. However there doesn't seem to be any way to retrieve this value, since windows-rs immediately callsok()
on theHRESULT
. And given thatDRAGDROP_S_DROP
doesn't have the error bit set it is not passed as error in theResult
.Is this an oversight?
The text was updated successfully, but these errors were encountered: