Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/corert.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.FormatMessage.cs')
-rw-r--r--src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.FormatMessage.cs108
1 files changed, 42 insertions, 66 deletions
diff --git a/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.FormatMessage.cs b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.FormatMessage.cs
index 94722b6c8..3ea12d7cf 100644
--- a/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.FormatMessage.cs
+++ b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.FormatMessage.cs
@@ -3,7 +3,6 @@
// See the LICENSE file in the project root for more information.
using System;
-using System.Text;
using System.Runtime.InteropServices;
internal partial class Interop
@@ -14,99 +13,76 @@ internal partial class Interop
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;
[DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, EntryPoint = "FormatMessageW", SetLastError = true, BestFitMapping = true)]
- private static extern int FormatMessage(
+ private static extern unsafe int FormatMessage(
int dwFlags,
IntPtr lpSource,
uint dwMessageId,
int dwLanguageId,
- [Out] StringBuilder lpBuffer,
+ void* lpBuffer,
int nSize,
- IntPtr[] arguments);
+ IntPtr arguments);
/// <summary>
/// Returns a string message for the specified Win32 error code.
/// </summary>
- internal static string GetMessage(int errorCode)
- {
- return GetMessage(IntPtr.Zero, errorCode);
- }
-
- internal static string GetMessage(IntPtr moduleHandle, int errorCode)
- {
- var sb = new StringBuilder(InitialBufferSize);
- do
- {
- string errorMsg;
- if (TryGetErrorMessage(moduleHandle, errorCode, sb, out errorMsg))
- {
- return errorMsg;
- }
- else
- {
- // increase the capacity of the StringBuilder.
- sb.Capacity *= BufferSizeIncreaseFactor;
- }
- }
- while (sb.Capacity < MaxAllowedBufferSize);
-
- // If you come here then a size as large as 65K is also not sufficient and so we give the generic errorMsg.
- return string.Format("Unknown error (0x{0:x})", errorCode);
- }
+ internal static string GetMessage(int errorCode) =>
+ GetMessage(errorCode, IntPtr.Zero);
- private static bool TryGetErrorMessage(IntPtr moduleHandle, int errorCode, StringBuilder sb, out string errorMsg)
+ internal static unsafe string GetMessage(int errorCode, IntPtr moduleHandle)
{
- errorMsg = "";
-
int flags = FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY;
if (moduleHandle != IntPtr.Zero)
{
flags |= FORMAT_MESSAGE_FROM_HMODULE;
}
- int result = FormatMessage(flags, moduleHandle, (uint)errorCode, 0, sb, sb.Capacity, null);
- if (result != 0)
+ // 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 = &MemoryMarshal.GetReference(stackBuffer))
{
- int i = sb.Length;
- while (i > 0)
+ int length = FormatMessage(flags, moduleHandle, unchecked((uint)errorCode), 0, bufferPtr, stackBuffer.Length, IntPtr.Zero);
+ if (length > 0)
{
- char ch = sb[i - 1];
- if (ch > 32 && ch != '.') break;
- i--;
+ return GetAndTrimString(stackBuffer.Slice(0, length));
}
- errorMsg = sb.ToString(0, i);
- }
- else if (Marshal.GetLastWin32Error() == ERROR_INSUFFICIENT_BUFFER)
- {
- return false;
}
- else
+
+ // 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)
{
- errorMsg = string.Format("Unknown error (0x{0:x})", errorCode);
+ 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 Span<char>((char*)nativeMsgPtr, length));
+ }
+ }
+ finally
+ {
+ Marshal.FreeHGlobal(nativeMsgPtr);
+ }
}
- return true;
+ // Couldn't get a message, so manufacture one.
+ return string.Format("Unknown error (0x{0:x})", errorCode);
}
- // Windows API FormatMessage lets you format a message string given an errorcode.
- // Unlike other APIs this API does not support a way to query it for the total message size.
- //
- // So the API can only be used in one of these two ways.
- // a. You pass a buffer of appropriate size and get the resource.
- // b. Windows creates a buffer and passes the address back and the onus of releasing the buffer lies on the caller.
- //
- // Since the error code is coming from the user, it is not possible to know the size in advance.
- // Unfortunately we can't use option b. since the buffer can only be freed using LocalFree and it is a private API on onecore.
- // Also, using option b is ugly for the managed code and could cause memory leak in situations where freeing is unsuccessful.
- //
- // As a result we use the following approach.
- // We initially call the API with a buffer size of 256 and then gradually increase the size in case of failure until we reach the maximum allowed limit of 65K.
- private const int InitialBufferSize = 256;
- private const int BufferSizeIncreaseFactor = 4;
- private const int MaxAllowedBufferSize = 65 * 1024;
+ private static string GetAndTrimString(Span<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();
+ }
}
}