#nullable enable using System; using System.ComponentModel; using System.ComponentModel.Design.Serialization; using System.Diagnostics; using System.Globalization; using System.Runtime.InteropServices; using System.Text; using Vanara.PInvoke; namespace Vanara.Windows.Shell { /// Wraps a string resource reference used by some Shell classes. [TypeConverter(typeof(IndirectStringTypeConverter))] [DebuggerDisplay("{RawValue} => {Value}")] public class IndirectString : IndirectResource { /// Initializes a new instance of the class. public IndirectString() { } /// Initializes a new instance of the class. public IndirectString(string? value) : base(value) { } /// Initializes a new instance of the class. /// The module file name. /// /// If this number is positive, this is the index of the resource in the module file. If negative, the absolute value of the number /// is the resource ID of the string in the module file. /// public IndirectString(string module, int resourceIdOrIndex) : base(module, resourceIdOrIndex) { } /// Gets the localized string referred to by this instance. /// The referenced localized string. [Browsable(false)] public string? Value { get { if (RawValue is null) return null; if (!IsValid) return RawValue; var sb = new StringBuilder(4096); SHLoadIndirectString(RawValue, sb, (uint)sb.Capacity).ThrowIfFailed(); return sb.ToString(); } } /// Performs an implicit conversion from to . /// The ind. /// The result of the conversion. public static implicit operator string?(IndirectString? ind) => ind?.RawValue; /// Performs an implicit conversion from to . /// The s. /// The result of the conversion. public static implicit operator IndirectString(string? s) => new(s); /// Tries to parse the specified string to create a instance. /// The string representation in the format of either "ModuleFileName,ResourceIndex" or "ModuleFileName,-ResourceID". /// The resulting instance on success. /// true if successfully parsed. public static bool TryParse(string? value, out IndirectString loc) { loc = new IndirectString(value); return loc.IsValid || value != null; } [DllImport("shlwapi.dll", SetLastError = false, ExactSpelling = true)] private static extern HRESULT SHLoadIndirectString([MarshalAs(UnmanagedType.LPWStr)] string pszSource, [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszOutBuf, uint cchOutBuf, IntPtr ppvReserved = default); } internal class IndirectStringTypeConverter : ExpandableObjectConverter { public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) => sourceType == typeof(string) || base.CanConvertFrom(context, sourceType); public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destType) => destType == typeof(InstanceDescriptor) || destType == typeof(string) || base.CanConvertTo(context, destType); public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) => value is string s ? IndirectString.TryParse(s, out var loc) ? loc : null : base.ConvertFrom(context, culture, value); public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? info, object? value, Type destType) { if (destType == typeof(string) && value is IndirectString s) return s.RawValue; return destType == typeof(InstanceDescriptor) ? new InstanceDescriptor(typeof(IndirectString).GetConstructor(new Type[0]), null, false) : base.ConvertTo(context, info, value, destType); } } }