internal static extern int LoadString(SafeLibraryHandle handle, int id, [Out] StringBuilder buffer, int bufferLength);
// FormatMessageW will AV if you don't pass in enough format strings. If you call TryFormatMessage we ensure insertionStrings // is long enough. You don't want to call this directly unless you're sure insertionStrings is long enough! internal static string UnsafeTryFormatMessage(SafeLibraryHandle hModule, uint messageNum, string[] insertionStrings) { string msg = null; int msgLen = 0; StringBuilder buf = new StringBuilder(1024); int flags = NativeMethods.FORMAT_MESSAGE_FROM_HMODULE | NativeMethods.FORMAT_MESSAGE_ARGUMENT_ARRAY; IntPtr[] addresses = new IntPtr[insertionStrings.Length]; GCHandle[] handles = new GCHandle[insertionStrings.Length]; GCHandle stringsRoot = GCHandle.Alloc(addresses, GCHandleType.Pinned); // Make sure that we don't try to pass in a zero length array of addresses. If there are no insertion strings, // we'll use the FORMAT_MESSAGE_IGNORE_INSERTS flag . // If you change this behavior, make sure you look at TryFormatMessage which depends on this behavior! if (insertionStrings.Length == 0) { flags |= NativeMethods.FORMAT_MESSAGE_IGNORE_INSERTS; } try { for (int i=0; i<handles.Length; i++) { handles[i] = GCHandle.Alloc(insertionStrings[i], GCHandleType.Pinned); addresses[i] = handles[i].AddrOfPinnedObject(); } int lastError = NativeMethods.ERROR_INSUFFICIENT_BUFFER; while (msgLen == 0 && lastError == NativeMethods.ERROR_INSUFFICIENT_BUFFER) { msgLen = SafeNativeMethods.FormatMessage( flags, hModule, messageNum, 0, buf, buf.Capacity, addresses); if (msgLen == 0) { lastError = Marshal.GetLastWin32Error(); if (lastError == NativeMethods.ERROR_INSUFFICIENT_BUFFER) buf.Capacity = buf.Capacity * 2; } } } catch { msgLen = 0; // return empty on failure } finally { for (int i=0; i<handles.Length; i++) { if (handles[i].IsAllocated) handles[i].Free(); } stringsRoot.Free(); } if (msgLen > 0) { msg = buf.ToString(); // chop off a single CR/LF pair from the end if there is one. FormatMessage always appends one extra. if (msg.Length > 1 && msg[msg.Length-1] == '\n') msg = msg.Substring(0, msg.Length-2); } return msg; }
public static unsafe extern int FormatMessage(int dwFlags, SafeLibraryHandle lpSource, uint dwMessageId, int dwLanguageId, StringBuilder lpBuffer, int nSize, IntPtr[] arguments);
// Format message in specific DLL. Return <null> on failure. internal static string TryFormatMessage(SafeLibraryHandle hModule, uint messageNum, string[] insertionStrings) { if (insertionStrings.Length == 0) { // UnsafeTryFromatMessage will set FORMAT_MESSAGE_IGNORE_INSERTS when calling into the OS // when there are no insertion strings, in this case we don't have to guard against insertionStrings // not having enough data since it is unused when FORMAT_MESSAGE_IGNORE_INSERTS is specified return UnsafeTryFormatMessage(hModule, messageNum, insertionStrings); } // If you pass in an empty array UnsafeTryFormatMessage will just pull out the message. string formatString = UnsafeTryFormatMessage(hModule, messageNum, new string[0]); if (formatString == null) { return null; } int largestNumber = 0; for (int i = 0; i < formatString.Length; i++) { if (formatString[i] == '%') { // See if a number follows this, if so, grab the number. if(formatString.Length > i + 1) { StringBuilder sb = new StringBuilder(); while (i + 1 < formatString.Length && Char.IsDigit(formatString[i + 1])) { sb.Append(formatString[i + 1]); i++; } // move over the non number character that broke us out of the loop i++; if (sb.Length > 0) { int num = -1; if (Int32.TryParse(sb.ToString(), NumberStyles.None, CultureInfo.InvariantCulture, out num)) { largestNumber = Math.Max(largestNumber, num); } } } } } // Replacement strings are 1 indexed. if (largestNumber > insertionStrings.Length) { string[] newStrings = new string[largestNumber]; Array.Copy(insertionStrings, newStrings, insertionStrings.Length); for (int i = insertionStrings.Length; i < newStrings.Length; i++) { newStrings[i] = "%" + (i + 1); } insertionStrings = newStrings; } return UnsafeTryFormatMessage(hModule, messageNum, insertionStrings); }
internal static extern int LoadString(SafeLibraryHandle handle, int id, StringBuilder buffer, int bufferLength);