-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
[API Proposal]: Expose method to get system message for system error code #67872
Comments
I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label. |
Tagging subscribers to this area: @dotnet/interop-contrib Issue DetailsBackground and motivationThis optionally (and typically) retrieves the last system error (with throw new Win32Exception(); (Separate discussion of the unfortunate naming we have inherited for Win32Exception is here.) This is convenient if that's exactly what you want, but has these limitations
// some pinvoke
// some other code potentially resetting the last error by calling into the system directly or indirectly
throw new Win32Exception(); // uses the last error code, when perhaps you expected a generic message correct way: // some pinvoke
int errorCode = Marshal.GetLastWin32Error();
// some other code
throw new Win32Exception(errorCode);
string errorMessage = new Win32Exception().Message; or if you want it from the error code: string errorMessage = new Win32Exception(errorCode).Message;
This is not robust because some string formatting machinery runs and potentially resets the last error before Win32Exception internally calls throw new Win32Exception($"Could not open SCM. {new Win32Exception().Message}"); This is not robust either because Win32Exception still calls string errorMessage = new Win32Exception().Message;
throw new Win32Exception($"Could not delete service '{ServiceName}'. {errorMessage}"); One must write this to be robust (example in our code) int errorCode = Marshal.GetLastWin32Error();
string errorMessage = new Win32Exception(errorCode).Message;
throw new Win32Exception(errorCode, $"Could not delete service '{ServiceName}'. {errorMessage}"); It's unfortunate that Win32Exception calls API Proposalnamespace System.Runtime.InteropServices
{
public class Marshal
{
public static int GetLastPInvokeError(); // existing
public static string GetMessageforPinvokeError(int error); // Win32Exception parameter name is `error`, although its property is `NativeErrorCode`
}
} API Usageint errorCode = Marshal.GetLastPinvokeError(); // error is captured here
string errorMessage = Marshal.GetMessageforPinvokeError(errorCode);
throw new Win32Exception(errorCode, $"Could not delete service '{ServiceName}'. {errorMessage}"); or int errorCode = Marshal.GetLastPinvokeError(); // error is captured here
throw new Win32Exception(errorCode, $"Could not delete service '{ServiceName}'. {Marshal.GetMessageforPinvokeError(errorCode)}"); Alternative DesignsIt would be nice to add overloads to Win32Exception, that eg., take a message to prepend. However, that may not be a common enough requirement, and would be ambiguous with existing string overload. It would probably have to be a static factory method. You'd still need the Marshal method, in case you don't want the exception object. RisksNo response
|
Nit: GetMessageforPinvokeError -> GetMessageforPInvokeError |
fixed. |
|
That's an improvement. Updated. |
@AaronRobinsonMSFT if you're supportive, shall we mark ready for review? |
Setting milestone as I'd like to get this in this release. |
@danmoseley Do you want to present this to the API review board? I've put out a PR as proposed above. |
@stephentoub suggested it wasn't necessary for me to be there, although I can be if necessary if someone lets me know which one it will be at. If you attend perhaps you could speak to it? |
Sure. |
namespace System.Runtime.InteropServices;
public partial class Marshal
{
// Existing
// public static int GetLastPInvokeError();
public static string GetLastPInvokeErrorMessage();
public static string GetPInvokeErrorMessage(int error);
} |
Background and motivation
This optionally (and typically) retrieves the last system error (with
Marshal.GetLastPInvokeError()
) and the matching system error string (internally usingstrerror
orFormatMessage
) for you:(Separate discussion of the unfortunate naming we have inherited for Win32Exception is here.)
(Also note that
Marshal.GetLastPInvokeError()
==Marshal.GetLastWin32Error()
and is distinct toMarshal.GetLastSystemError()
only in that the latter is oblivious to calls toMarshal.SetLastPInvokeError()
==Marshal.SetLastWin32Error
)This is convenient if that's exactly what you want, but has these limitations
It's not discoverable, you just have to know.
It's easy to accidentally reset the error, eg.,
correct way:
or if you want it from the error code:
This is not robust because some string formatting machinery runs and potentially resets the last error before Win32Exception internally calls
Marshal.GetLastPInvokeError()
:This is not robust either because Win32Exception still calls
Marshal.GetLastPInvokeError()
to set itsNativeErrorCode
. #67827 is an example where this was wrong.One must write this to be robust (example in our code)
It's unfortunate that Win32Exception calls
Marshal.GetLastPInvokeError()
at all but that can't change now. We can at least help address 1, 3 and 4.API Proposal
Proposed API implementation - #70685
#70685 (comment) has commentary on proposed API UX.
namespace System.Runtime.InteropServices { public class Marshal { public static int GetLastPInvokeError(); // existing + public static string GetPInvokeErrorMessage(int error); // Win32Exception parameter name is `error`, although its property is `NativeErrorCode` } }
API Usage
or
Alternative Designs
It would be nice to add overloads to Win32Exception, that eg., take a message to prepend. However, that may not be a common enough requirement, and would be ambiguous with existing string overload. It would probably have to be a static factory method. You'd still need the Marshal method, in case you don't want the exception object.
Another possibility is to return message and code together as a tuple, eg
Risks
No response
The text was updated successfully, but these errors were encountered: