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.Globalization; 6 using System.Runtime.InteropServices; 7 using System.Runtime.Serialization.Formatters.Tests; 8 using System.Text; 9 using Xunit; 10 11 namespace System.ComponentModel.Tests 12 { 13 public static partial class Win32ExceptionTestType 14 { 15 private const int FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200; 16 private const int FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000; 17 private const int FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000; 18 private const int ERROR_INSUFFICIENT_BUFFER = 0x7A; 19 private const int FirstPassBufferSize = 256; 20 private const int E_FAIL = unchecked((int)0x80004005); 21 22 [DllImport("kernel32.dll", CharSet = CharSet.Unicode, EntryPoint = "FormatMessageW", SetLastError = true, BestFitMapping = true)] FormatMessage( int dwFlags, IntPtr lpSource_mustBeNull, uint dwMessageId, int dwLanguageId, StringBuilder lpBuffer, int nSize, IntPtr[] arguments)23 private static extern int FormatMessage( 24 int dwFlags, 25 IntPtr lpSource_mustBeNull, 26 uint dwMessageId, 27 int dwLanguageId, 28 StringBuilder lpBuffer, 29 int nSize, 30 IntPtr[] arguments); 31 IsExceptionMessageLong(int errorCode)32 private static bool IsExceptionMessageLong(int errorCode) 33 { 34 StringBuilder sb = new StringBuilder(FirstPassBufferSize); // Buffer length in the first pass in the implementation. 35 36 int result = FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS | 37 FORMAT_MESSAGE_FROM_SYSTEM | 38 FORMAT_MESSAGE_ARGUMENT_ARRAY, 39 IntPtr.Zero, (uint)errorCode, 0, sb, sb.Capacity, 40 null); 41 if (result == 0) 42 { 43 return (Marshal.GetLastWin32Error() == ERROR_INSUFFICIENT_BUFFER); 44 } 45 46 return false; 47 } 48 49 [Fact] InstantiateException()50 public static void InstantiateException() 51 { 52 int error = 5; 53 string message = "This is an error message."; 54 string toStringStart = string.Format( 55 CultureInfo.InvariantCulture, 56 "{0} ({1})", 57 typeof(Win32Exception).ToString(), 58 PlatformDetection.IsFullFramework ? $"0x{E_FAIL:X8}" : error.ToString(CultureInfo.InvariantCulture)); 59 60 Exception innerException = new FormatException(); 61 62 // Test each of the constructors and validate the properties of the resulting instance 63 64 Win32Exception ex = new Win32Exception(); 65 Assert.Equal(expected: E_FAIL, actual: ex.HResult); 66 67 ex = new Win32Exception(error); 68 Assert.Equal(expected: E_FAIL, actual: ex.HResult); 69 Assert.Equal(expected: error, actual: ex.NativeErrorCode); 70 Assert.StartsWith(expectedStartString: toStringStart, actualString: ex.ToString(), comparisonType: StringComparison.Ordinal); 71 72 ex = new Win32Exception(message); 73 Assert.Equal(expected: E_FAIL, actual: ex.HResult); 74 Assert.Equal(expected: message, actual: ex.Message); 75 76 ex = new Win32Exception(error, message); 77 Assert.Equal(expected: E_FAIL, actual: ex.HResult); 78 Assert.Equal(expected: error, actual: ex.NativeErrorCode); 79 Assert.Equal(expected: message, actual: ex.Message); 80 Assert.StartsWith(expectedStartString: toStringStart, actualString: ex.ToString(), comparisonType: StringComparison.Ordinal); 81 82 ex = new Win32Exception(message, innerException); 83 Assert.Equal(expected: E_FAIL, actual: ex.HResult); 84 Assert.Equal(expected: message, actual: ex.Message); 85 Assert.Same(expected: innerException, actual: ex.InnerException); 86 } 87 88 [Fact] 89 [PlatformSpecific(TestPlatforms.Windows)] // Uses P/Invokes to check whether the exception resource length >256 chars InstantiateExceptionWithLongErrorString()90 public static void InstantiateExceptionWithLongErrorString() 91 { 92 // This test checks that Win32Exception supports error strings greater than 256 characters. 93 // Since we will have to rely on a message associated with an error code, 94 // we try to reduce the flakiness by doing the following. 95 // 1. Call FormatMessage to check whether the exception resource length >256 chars. 96 // 2. If true, we validate that Win32Exception class can retrieve the complete resource string. 97 // 3. If not we skip testing. 98 int errorCode = 0x268; 99 if (IsExceptionMessageLong(errorCode)) // Localized error string for 0x268 is not guaranteed to be >256 chars. 100 { 101 Win32Exception ex = new Win32Exception(errorCode); 102 Assert.NotEqual("Unknown error (0x268)", ex.Message); 103 Assert.True(ex.Message.Length > FirstPassBufferSize); 104 105 ex = new Win32Exception(0x23); 106 Assert.Equal(expected: "Unknown error (0x23)", actual: ex.Message); 107 } 108 } 109 } 110 } 111