1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System; 6 using System.Text; 7 using System.Runtime.InteropServices; 8 9 internal partial class Interop 10 { 11 internal partial class Kernel32 12 { 13 private const int FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200; 14 private const int FORMAT_MESSAGE_FROM_HMODULE = 0x00000800; 15 private const int FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000; 16 private const int FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000; 17 18 19 private const int ERROR_INSUFFICIENT_BUFFER = 0x7A; 20 21 [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, EntryPoint = "FormatMessageW", SetLastError = true, BestFitMapping = true)] FormatMessage( int dwFlags, IntPtr lpSource, uint dwMessageId, int dwLanguageId, [Out] StringBuilder lpBuffer, int nSize, IntPtr[] arguments)22 private static extern int FormatMessage( 23 int dwFlags, 24 IntPtr lpSource, 25 uint dwMessageId, 26 int dwLanguageId, 27 [Out] StringBuilder lpBuffer, 28 int nSize, 29 IntPtr[] arguments); 30 31 /// <summary> 32 /// Returns a string message for the specified Win32 error code. 33 /// </summary> GetMessage(int errorCode)34 internal static string GetMessage(int errorCode) 35 { 36 return GetMessage(IntPtr.Zero, errorCode); 37 } 38 GetMessage(IntPtr moduleHandle, int errorCode)39 internal static string GetMessage(IntPtr moduleHandle, int errorCode) 40 { 41 var sb = new StringBuilder(InitialBufferSize); 42 do 43 { 44 string errorMsg; 45 if (TryGetErrorMessage(moduleHandle, errorCode, sb, out errorMsg)) 46 { 47 return errorMsg; 48 } 49 else 50 { 51 // increase the capacity of the StringBuilder. 52 sb.Capacity *= BufferSizeIncreaseFactor; 53 } 54 } 55 while (sb.Capacity < MaxAllowedBufferSize); 56 57 // If you come here then a size as large as 65K is also not sufficient and so we give the generic errorMsg. 58 return string.Format("Unknown error (0x{0:x})", errorCode); 59 } 60 TryGetErrorMessage(IntPtr moduleHandle, int errorCode, StringBuilder sb, out string errorMsg)61 private static bool TryGetErrorMessage(IntPtr moduleHandle, int errorCode, StringBuilder sb, out string errorMsg) 62 { 63 errorMsg = ""; 64 65 int flags = FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY; 66 if (moduleHandle != IntPtr.Zero) 67 { 68 flags |= FORMAT_MESSAGE_FROM_HMODULE; 69 } 70 71 int result = FormatMessage(flags, moduleHandle, (uint)errorCode, 0, sb, sb.Capacity, null); 72 if (result != 0) 73 { 74 int i = sb.Length; 75 while (i > 0) 76 { 77 char ch = sb[i - 1]; 78 if (ch > 32 && ch != '.') break; 79 i--; 80 } 81 errorMsg = sb.ToString(0, i); 82 } 83 else if (Marshal.GetLastWin32Error() == ERROR_INSUFFICIENT_BUFFER) 84 { 85 return false; 86 } 87 else 88 { 89 errorMsg = string.Format("Unknown error (0x{0:x})", errorCode); 90 } 91 92 return true; 93 } 94 95 // Windows API FormatMessage lets you format a message string given an errorcode. 96 // Unlike other APIs this API does not support a way to query it for the total message size. 97 // 98 // So the API can only be used in one of these two ways. 99 // a. You pass a buffer of appropriate size and get the resource. 100 // b. Windows creates a buffer and passes the address back and the onus of releasing the buffer lies on the caller. 101 // 102 // Since the error code is coming from the user, it is not possible to know the size in advance. 103 // Unfortunately we can't use option b. since the buffer can only be freed using LocalFree and it is a private API on onecore. 104 // Also, using option b is ugly for the managed code and could cause memory leak in situations where freeing is unsuccessful. 105 // 106 // As a result we use the following approach. 107 // 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. 108 private const int InitialBufferSize = 256; 109 private const int BufferSizeIncreaseFactor = 4; 110 private const int MaxAllowedBufferSize = 65 * 1024; 111 } 112 } 113