using System; using System.Runtime.InteropServices; using System.Text; using JetBrains.Annotations; namespace Microsoft.Win32.TaskScheduler { /// /// Some string values of properties can be set to retrieve their value from existing DLLs as a resource. This class facilitates creating those reference strings. /// [PublicAPI] public class ResourceReferenceValue { /// /// Initializes a new instance of the class. /// /// The DLL path. /// The resource identifier. public ResourceReferenceValue([NotNull] string dllPath, int resourceId) { ResourceFilePath = dllPath; ResourceIdentifier = resourceId; } /// /// Gets or sets the resource file path. This can be a relative path, full path or lookup path (e.g. %SystemRoot%\System32\ResourceName.dll). /// /// /// The resource file path. /// public string ResourceFilePath { get; set; } /// /// Gets or sets the resource identifier. /// /// The resource identifier. public int ResourceIdentifier { get; set; } /// /// Performs an implicit conversion from to . /// /// The value. /// The result of the conversion. public static implicit operator string(ResourceReferenceValue value) => value.ToString(); /// /// Parses the input string. String must be in the format "$(@ [Dll], [ResourceID])". /// /// The input string value. /// A new instance on success or null on failure. /// is null /// is not in the format "$(@ [Dll], [ResourceID])" [NotNull] public static ResourceReferenceValue Parse([NotNull] string value) { if (value == null) throw new ArgumentNullException(nameof(value)); ResourceReferenceValue r; if (!TryParse(value, out r)) throw new FormatException(); return r; } /// /// Tries to parse to input string. String must be in the format "$(@ [Dll], [ResourceID])". /// /// The input string value. /// The resource reference to be returned. On failure, this value equals null. /// A new instance on success or null on failure. public static bool TryParse(string value, out ResourceReferenceValue resourceRef) { if (!string.IsNullOrEmpty(value)) { var m = System.Text.RegularExpressions.Regex.Match(value, @"^\$\(\@ (?[^,]+), (?-?\d+)\)$"); if (m.Success) { resourceRef = new ResourceReferenceValue(m.Groups["x"].Value, int.Parse(m.Groups["i"].Value)); return true; } } resourceRef = null; return false; } /// /// Gets the result of pulling the string from the resource file using the identifier. /// /// from resource file. /// cannot be found. /// Unable to load or string identified by . [NotNull] public string GetResolvedString() { if (!System.IO.File.Exists(ResourceFilePath)) throw new System.IO.FileNotFoundException("Invalid resource file path.", ResourceFilePath); IntPtr hLib = IntPtr.Zero; try { hLib = NativeMethods.LoadLibrary(ResourceFilePath); if (hLib == IntPtr.Zero) throw new System.ComponentModel.Win32Exception(); var sb = new StringBuilder(8192); int l = LoadString(hLib, ResourceIdentifier, sb, sb.Capacity); if (l == 0) throw new System.ComponentModel.Win32Exception(); return sb.ToString(0, l); } finally { if (hLib != IntPtr.Zero) NativeMethods.FreeLibrary(hLib); } } /// /// Returns a in the format required by the Task Scheduler to reference a string in a DLL. /// /// A formatted in the format $(@ [Dll], [ResourceID]). public override string ToString() => $"$(@ {ResourceFilePath}, {ResourceIdentifier})"; [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false, ThrowOnUnmappableChar = true)] private static extern int LoadString(IntPtr hInstance, int wID, StringBuilder lpBuffer, int nBufferMax); } }