| File: src\runtime\src\libraries\Common\src\Interop\Windows\Kernel32\Interop.FormatMessage.cs | Web Access |
| Project: System.Net.Http.WinHttpHandler.csproj (System.Net.Http.WinHttpHandler) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Runtime.InteropServices; internal static partial class Interop { internal static partial class Kernel32 { private const int FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200; private const int FORMAT_MESSAGE_FROM_HMODULE = 0x00000800; private const int FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000; private const int FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000; private const int FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100; private const int ERROR_INSUFFICIENT_BUFFER = 0x7A; [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] [LibraryImport(Libraries.Kernel32, EntryPoint = "FormatMessageW", SetLastError = true)] private static unsafe partial int FormatMessage( int dwFlags, IntPtr lpSource, uint dwMessageId, int dwLanguageId, void* lpBuffer, int nSize, IntPtr arguments); /// <summary> /// Returns a string message for the specified Win32 error code. /// </summary> internal static string GetMessage(int errorCode) => GetMessage(errorCode, IntPtr.Zero); internal static unsafe string GetMessage(int errorCode, IntPtr moduleHandle) { int flags = FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY; if (moduleHandle != IntPtr.Zero) { flags |= FORMAT_MESSAGE_FROM_HMODULE; } // First try to format the message into the stack based buffer. Most error messages willl fit. Span<char> stackBuffer = stackalloc char[256]; // arbitrary stack limit fixed (char* bufferPtr = stackBuffer) { int length = FormatMessage(flags, moduleHandle, unchecked((uint)errorCode), 0, bufferPtr, stackBuffer.Length, IntPtr.Zero); if (length > 0) { return GetAndTrimString(stackBuffer.Slice(0, length)); } } // We got back an error. If the error indicated that there wasn't enough room to store // the error message, then call FormatMessage again, but this time rather than passing in // a buffer, have the method allocate one, which we then need to free. if (Marshal.GetLastWin32Error() == ERROR_INSUFFICIENT_BUFFER) { IntPtr nativeMsgPtr = default; try { int length = FormatMessage(flags | FORMAT_MESSAGE_ALLOCATE_BUFFER, moduleHandle, unchecked((uint)errorCode), 0, &nativeMsgPtr, 0, IntPtr.Zero); if (length > 0) { return GetAndTrimString(new ReadOnlySpan<char>((char*)nativeMsgPtr, length)); } } finally { Marshal.FreeHGlobal(nativeMsgPtr); } } // Couldn't get a message, so manufacture one. return $"Unknown error (0x{errorCode:x})"; } private static string GetAndTrimString(ReadOnlySpan<char> buffer) { int length = buffer.Length; while (length > 0 && buffer[length - 1] <= 32) { length--; // trim off spaces and non-printable ASCII chars at the end of the resource } return buffer.Slice(0, length).ToString(); } } }