mirror of https://github.com/dahall/Vanara.git
Fixed bug in HRESULT type converter and added ability to add values and libs to error message cache
parent
18a005c8a2
commit
f968d1aafa
|
@ -3,37 +3,80 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Vanara.PInvoke
|
||||
namespace Vanara.PInvoke;
|
||||
|
||||
/// <summary>Gets a static field's name from its value and caches the list for faster lookups.</summary>
|
||||
public class StaticFieldValueHash
|
||||
{
|
||||
/// <summary>Gets a static field's name from its value and caches the list for faster lookups.</summary>
|
||||
public static class StaticFieldValueHash
|
||||
private static readonly Dictionary<(Type, Type), IDictionary<int, (string, string)>> cache = new();
|
||||
|
||||
/// <summary>Adds the seqence of field values to the associated cache.</summary>
|
||||
/// <typeparam name="TType">The type of the type.</typeparam>
|
||||
/// <typeparam name="TFieldType">The type of the field type.</typeparam>
|
||||
/// <param name="fields">The list of field values and names to add.</param>
|
||||
/// <param name="lib">The optional library name.</param>
|
||||
public static void AddFields<TType, TFieldType>(IEnumerable<(TFieldType value, string name)> fields, string lib = null)
|
||||
where TFieldType : struct, IComparable
|
||||
{
|
||||
private static readonly Dictionary<(Type, Type), IDictionary<int, string>> cache = new();
|
||||
|
||||
/// <summary>Tries to get the name of a static field from it's value.</summary>
|
||||
/// <typeparam name="TType">The type of the type.</typeparam>
|
||||
/// <typeparam name="TFieldType">The type of the field type.</typeparam>
|
||||
/// <param name="value">The value for which to search.</param>
|
||||
/// <param name="fieldName">On success, the name of the field.</param>
|
||||
/// <returns><see langword="true"/> if the value was found, otherwise <see langword="false"/>.</returns>
|
||||
public static bool TryGetFieldName<TType, TFieldType>(TFieldType value, out string fieldName)
|
||||
TryGetFieldName<TType, TFieldType>(default, out _); // Load default values
|
||||
var tt = (typeof(TType), typeof(TFieldType));
|
||||
if (cache.TryGetValue(tt, out var hash))
|
||||
{
|
||||
var tt = (typeof(TType), typeof(TFieldType));
|
||||
lock (cache)
|
||||
{
|
||||
if (!cache.TryGetValue(tt, out var hash))
|
||||
cache.Add(tt, hash = typeof(TType).GetFields(BindingFlags.Public | BindingFlags.Static).Where(fi => fi.FieldType == typeof(TFieldType)).Distinct(FIValueComp<TFieldType>.Default).ToDictionary(fi => fi.GetValue(null).GetHashCode(), fi => fi.Name));
|
||||
return hash.TryGetValue(value.GetHashCode(), out fieldName);
|
||||
}
|
||||
}
|
||||
|
||||
private class FIValueComp<TFieldType> : IEqualityComparer<FieldInfo>
|
||||
{
|
||||
bool IEqualityComparer<FieldInfo>.Equals(FieldInfo x, FieldInfo y) => Comparer<TFieldType>.Default.Compare((TFieldType)x.GetValue(null), (TFieldType)y.GetValue(null)) == 0;
|
||||
|
||||
int IEqualityComparer<FieldInfo>.GetHashCode(FieldInfo obj) => ((TFieldType)obj.GetValue(null)).GetHashCode();
|
||||
|
||||
public static readonly FIValueComp<TFieldType> Default = new();
|
||||
foreach (var (value, name) in fields)
|
||||
if (!hash.ContainsKey(value.GetHashCode()))
|
||||
hash.Add(value.GetHashCode(), (name, lib));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Adds the seqence of field values to the associated cache.</summary>
|
||||
/// <typeparam name="TType">The type of the type.</typeparam>
|
||||
/// <typeparam name="TFieldType">The type of the field type.</typeparam>
|
||||
/// <typeparam name="TEnum">The type of the enum to added.</typeparam>
|
||||
/// <param name="lib">The optional library name.</param>
|
||||
public static void AddFields<TType, TFieldType, TEnum>(string lib = null) where TFieldType : struct, IComparable =>
|
||||
AddFields<TType, TFieldType>(Enum.GetValues(typeof(TEnum)).Cast<TFieldType>().Select(v => (v, Enum.GetName(typeof(TEnum), v))), lib);
|
||||
|
||||
/// <summary>Tries to get the name of a value's library.</summary>
|
||||
/// <typeparam name="TType">The type of the type.</typeparam>
|
||||
/// <typeparam name="TFieldType">The type of the field type.</typeparam>
|
||||
/// <param name="value">The value for which to search.</param>
|
||||
/// <returns>The name of the library or <see langword="null"/>.</returns>
|
||||
public static string GetFieldLib<TType, TFieldType>(TFieldType value)
|
||||
where TFieldType : struct, IComparable
|
||||
{
|
||||
var tt = (typeof(TType), typeof(TFieldType));
|
||||
return cache.TryGetValue(tt, out var hash) && hash.TryGetValue(value.GetHashCode(), out var t) ? t.Item2 : null;
|
||||
}
|
||||
|
||||
/// <summary>Tries to get the name of a static field from it's value.</summary>
|
||||
/// <typeparam name="TType">The type of the type.</typeparam>
|
||||
/// <typeparam name="TFieldType">The type of the field type.</typeparam>
|
||||
/// <param name="value">The value for which to search.</param>
|
||||
/// <param name="fieldName">On success, the name of the field.</param>
|
||||
/// <returns><see langword="true"/> if the value was found, otherwise <see langword="false"/>.</returns>
|
||||
public static bool TryGetFieldName<TType, TFieldType>(TFieldType value, out string fieldName)
|
||||
where TFieldType : struct, IComparable
|
||||
{
|
||||
var tt = (typeof(TType), typeof(TFieldType));
|
||||
if (!cache.TryGetValue(tt, out var hash))
|
||||
{
|
||||
hash = typeof(TType).GetFields(BindingFlags.Public | BindingFlags.Static).
|
||||
Where(fi => fi.FieldType == typeof(TFieldType)).Distinct(FIValueComp<TFieldType>.Default).
|
||||
ToDictionary<FieldInfo, int, (string, string)>(fi => fi.GetValue(null)!.GetHashCode(), fi => (fi.Name, null));
|
||||
cache.Add(tt, hash);
|
||||
}
|
||||
var ret = hash.TryGetValue(value.GetHashCode(), out var t);
|
||||
fieldName = t.Item1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
private class FIValueComp<TFieldType> : IEqualityComparer<FieldInfo> where TFieldType : struct, IComparable
|
||||
{
|
||||
bool IEqualityComparer<FieldInfo>.Equals(FieldInfo x, FieldInfo y) =>
|
||||
Comparer<TFieldType?>.Default.Compare((TFieldType?)x?.GetValue(null), (TFieldType?)y?.GetValue(null)) == 0;
|
||||
|
||||
int IEqualityComparer<FieldInfo>.GetHashCode(FieldInfo obj) => ((TFieldType?)obj.GetValue(null))?.GetHashCode() ?? 0;
|
||||
|
||||
public static readonly FIValueComp<TFieldType> Default = new();
|
||||
}
|
||||
}
|
|
@ -745,16 +745,16 @@ namespace Vanara.PInvoke
|
|||
// Check for defined HRESULT value
|
||||
if (!StaticFieldValueHash.TryGetFieldName<HRESULT, int>(_value, out var err) && Facility == FacilityCode.FACILITY_WIN32)
|
||||
{
|
||||
foreach (var info2 in typeof(Win32Error).GetFields(BindingFlags.Public | BindingFlags.Static).Where(fi => fi.FieldType == typeof(uint)))
|
||||
foreach (FieldInfo info2 in typeof(Win32Error).GetFields(BindingFlags.Public | BindingFlags.Static).Where(fi => fi.FieldType == typeof(uint)))
|
||||
{
|
||||
if ((HRESULT)(Win32Error)(uint)info2.GetValue(null) == this)
|
||||
if ((HRESULT)(Win32Error)(uint)info2.GetValue(null)! == this)
|
||||
{
|
||||
err = $"HRESULT_FROM_WIN32({info2.Name})";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
var msg = FormatMessage(unchecked((uint)_value));
|
||||
var msg = FormatMessage(unchecked((uint)_value), StaticFieldValueHash.GetFieldLib<HRESULT, int>(_value));
|
||||
return (err ?? string.Format(CultureInfo.InvariantCulture, "0x{0:X8}", _value)) + (msg == null ? "" : ": " + msg);
|
||||
}
|
||||
|
||||
|
@ -800,26 +800,42 @@ namespace Vanara.PInvoke
|
|||
/// <summary>Formats the message.</summary>
|
||||
/// <param name="id">The error.</param>
|
||||
/// <returns>The string.</returns>
|
||||
internal static string FormatMessage(uint id)
|
||||
internal static string FormatMessage(uint id, string? lib = null)
|
||||
{
|
||||
var flags = 0x1200U; // FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM
|
||||
var buf = new System.Text.StringBuilder(1024);
|
||||
do
|
||||
var flags = lib is null ? 0x1200U /*FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM*/ : 0xA00U /*FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE*/;
|
||||
HINSTANCE hInst = lib is null ? default : LoadLibraryEx(lib, default, 0x1002 /*LOAD_LIBRARY_SEARCH_DEFAULT_DIRS | LOAD_LIBRARY_AS_DATAFILE*/);
|
||||
var buf = new StringBuilder(1024);
|
||||
try
|
||||
{
|
||||
if (0 != FormatMessage(flags, default, id, 0, buf, (uint)buf.Capacity, default))
|
||||
return buf.ToString();
|
||||
var lastError = Win32Error.GetLastError();
|
||||
if (lastError == Win32Error.ERROR_MR_MID_NOT_FOUND || lastError == Win32Error.ERROR_MUI_FILE_NOT_FOUND)
|
||||
break;
|
||||
if (lastError != Win32Error.ERROR_INSUFFICIENT_BUFFER)
|
||||
lastError.ThrowIfFailed();
|
||||
buf.Capacity *= 2;
|
||||
} while (true && buf.Capacity < 1024 * 16); // Don't go crazy
|
||||
do
|
||||
{
|
||||
if (0 != FormatMessage(flags, hInst, id, 0, buf, (uint)buf.Capacity, default))
|
||||
return buf.ToString();
|
||||
var lastError = Win32Error.GetLastError();
|
||||
if (lastError == Win32Error.ERROR_MR_MID_NOT_FOUND || lastError == Win32Error.ERROR_MUI_FILE_NOT_FOUND || lastError == Win32Error.ERROR_RESOURCE_TYPE_NOT_FOUND)
|
||||
break;
|
||||
if (lastError != Win32Error.ERROR_INSUFFICIENT_BUFFER)
|
||||
lastError.ThrowIfFailed();
|
||||
buf.Capacity *= 2;
|
||||
} while (true && buf.Capacity < 1024 * 16); // Don't go crazy
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (hInst != default)
|
||||
FreeLibrary(hInst);
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
[DllImport(Lib.Kernel32, SetLastError = true, CharSet = CharSet.Auto)]
|
||||
private static extern int FormatMessage(uint dwFlags, HINSTANCE lpSource, uint dwMessageId, uint dwLanguageId, System.Text.StringBuilder lpBuffer, uint nSize, IntPtr Arguments);
|
||||
[DllImport(Lib.Kernel32, SetLastError = true, CharSet = CharSet.Auto)]
|
||||
static extern int FormatMessage(uint dwFlags, HINSTANCE lpSource, uint dwMessageId, uint dwLanguageId, StringBuilder lpBuffer, uint nSize, IntPtr Arguments);
|
||||
|
||||
[DllImport(Lib.Kernel32, SetLastError = true, ExactSpelling = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
static extern bool FreeLibrary([In] HINSTANCE hLibModule);
|
||||
|
||||
[DllImport(Lib.Kernel32, SetLastError = true, CharSet = CharSet.Auto)]
|
||||
static extern HINSTANCE LoadLibraryEx([MarshalAs(UnmanagedType.LPTStr)] string lpLibFileName, HANDLE hFile, uint dwFlags);
|
||||
}
|
||||
|
||||
private static int? ValueFromObj(object obj)
|
||||
{
|
||||
|
@ -845,7 +861,7 @@ namespace Vanara.PInvoke
|
|||
{
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
if (sourceType is IErrorProvider || sourceType.IsPrimitive && sourceType != typeof(char))
|
||||
if (typeof(IErrorProvider).IsAssignableFrom(sourceType) || sourceType.IsPrimitive && sourceType != typeof(char))
|
||||
return true;
|
||||
return base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue