-
Notifications
You must be signed in to change notification settings - Fork 982
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
SetLastError=true missing on some extern declarations #3151
Comments
@danmosemsft It is typically the latter. If the caller isn't concerned about the real underlying error then there isn't a real need to capture or provide it. That being said, what wonderful bugs might we find if we do start checking it? |
We have been uplifting our interops for the past year, we verify signature correctness (e.g. I haven't personally observed many
Do we really want to know? :D |
In general the guideline should be to set it (when it is documented that the API does so) and not ignore error states. For perf reasons, however, it can be skipped if you don't intend to check it. Note that the perf there is very small, and measurable on only the simplest of APIs. I would never skip it for something that does a ton of work or isn't called in tight loops. For compat reasons (not wanting to introduce a new exception) we may not want to introduce error checking where we weren't before, but I'd recommend that we try to at least add debug asserts to flush out where we could otherwise be doing better (or worst case document the error states we're seeing). WinForms, for example, is very interop heavy and undoubtedly is throwing itself under the bus in a number of places without realizing it as it wasn't thorough about error checking. I'd be surprised if we didn't improve the overall robustness of WinForms substantively if we were more thorough. :) |
Speaking generally, this all seems very prone to error
In both cases, there is no compile or runtime error if you get it wrong. Just subtle issues like reading the error from a previous API call. Perhaps someone has written an analyzer to catch at least (1) using a database of Win32 API. I noticed that WPF segregates API into NativeMethods and NativeMethodsSetLastError possibly to help with this. |
I don't think you have to. As far as I understand it this flag exists to tell the CLR that it should not go stomping over the win32 error flag by saving it away in case you need it later. Technically you can always set it true at a small performance cost of having to save/restore the error flag. Leaving it false is just a performance optimization when you definitely know you aren't going to check the flag. So you could treat it as any performance optimization: first measure, and if you know it helps disable it. As far as analyzers are concerned, any call to In other words I'd take the reverse point of view, instead of painstakingly annotate each method
For performance sensitive code it may be useful to have both variants available, so that only the performance sensitive code calls the version with SetLastError=false (Just throwing that in as my opinion on the matter.) |
|
I'd be wary about your results. The first one I looked at is wrong. Throwing off of I don't think there is a P/Invoke resource we can use as an authoritative reference- I think we're building it. We can check some things via the headers and we can probably write analyzers to catch things that are technically correct that we don't like. |
On a related note, the last error behavior was just recently changed to clear the last error before making the call. |
Looks like Stepping back, this is just one way to annotate stuff wrong. It feels like we shouldn't all be solving this problem individually (and painstakingly in some cases)
I could imagine two analyzers
These seem separate problems to me because the second one is relatively straightforward to write and ship, but the first one needs a community around it to maintain the lists -- and may have more "policy" built in as well. If there's prior art for these it's evidently not established enough that our repos have used them? |
/cc: @AArnott |
It would be great to have something like https://www.pinvoke.net/ that has correct signatures and that align to our interop guidelines. And we are the ones who is in the best position to do so. |
I like to think that that is exactly the charter of https://github.com/aarnott/pinvoke. We have been meticulous to write signatures that are exactly correct, written to be alloc free, using blittable types so no marshaling is necessary, with test infrastructure, etc. I'll be happy to respond quickly to your group with any issues or PRs. |
And FWIW, I've applied for this repo to move into the .NET Foundation: dotnet-foundation/projects#66 |
Oh, and we write all our p/invoke signatures and structs to be safe for use in 32-bit and 64-bit processes, while still targeting AnyCPU. |
I would also like to second @AArnott's P/Invoke library. I have used it and recommend it often to people look for ready made P/Invoke signatures. As far as pinvoke.net, the owner used to be a Microsoft employee but I am unsure if they are still here. Now that we have github I much prefer that. |
A repo with tests is the best thing for confidence. https://www.pinvoke.net/ is a great idea and it has been helpful, but it is full of errors- some of which are not very obvious. While they could be fixed a wiki isn't nearly as robust as libraries in a gated public repo, particularly one that has governance like the .NET Foundation provides. I don't use the wiki and I don't contribute to it as I don't have the time to validate that my contributions don't get undone or the confidence that anyone else will keep it from degrading. As to my own repo, my goals were somewhat different. My largest priority was getting work that I did out in the open so it could be a reference. I experimented quite heavily on its composition, trying to (at one point) handle all usages, including producing libraries that contained only "blessed" Windows Store APIs. I gave up on that and have been pushing on creating wraps that allow writing Win32 apps in a C# style that still feels Win32 like. I don't really have any known consumers, and I've leveraged that to throw different approaches against the wall to see what will stick. I've done several hundred signature wraps and I'm totally fine with people using the code to help contribute to Andrew's library. I grant full usage without code attribution in this case, or for any .NET Foundation hosted repo. I haven't done it myself just because I don't have the need to consume his library. If his moves into the .NET Foundation I might allocate some of my free time to fill in some holes. I might even transition to using it for my base layer. |
There are quite a lot of extern declarations for Win32 API in the winforms repo that don't use SetLastError=true even though the docs indicate the API sets an error retrievable with GetLastError. (I attached an approximate list of some).
Of course in many, possibly all of those cases the code never calls
Marshal.GetLastWin32Error()
nor throwsWin32Exception
so it doesn't matter. I am not sure whether to interpret our guidance to mean "apply SetLastError=true whenever the API is documented to set last error" or "apply SetLastError=true whenever the API sets last error and you also intend to retrieve it". The latter saves the infrastructure calling GetLastError() for you unnecessarily, but that is presumably very fast - and avoids the possibility of reading some previous API's error because you hadn't updated the extern declaration.@AaronRobinsonMSFT @JeremyKuhne ?
list.txt
The text was updated successfully, but these errors were encountered: