using Microsoft.Win32.SafeHandles; using System; using System.Collections.Generic; using System.Text; using static Vanara.PInvoke.ShlwApi; namespace Vanara.Windows.Shell { /// Represents a Shell file association defined in the Windows Registry. Wraps . public class ShellAssociation { private IQueryAssociations qassoc; /// Initializes a new instance of the class. /// The file extension. This should be in the ".ext" format. private ShellAssociation(string ext) => Extension = ext; /// Gets all the file associations defined for the system. /// Returns a value. public static IReadOnlyDictionary FileAssociations { get; } = new ShellAssociationDictionary(true); /// /// The icon reference of the app associated with the file type or URI scheme. This is configured by users in their default program settings. /// public string AppIconReference => GetString(ASSOCSTR.ASSOCSTR_APPICONREFERENCE); /// /// The AppUserModelID of the app associated with the file type or URI scheme. This is configured by users in their default program settings. /// public string AppId => GetString(ASSOCSTR.ASSOCSTR_APPID); /// /// The publisher of the app associated with the file type or URI scheme. This is configured by users in their default program settings. /// public string AppPublisher => GetString(ASSOCSTR.ASSOCSTR_APPPUBLISHER); /// /// Introduced in Internet Explorer 6. Returns the path to the icon resources to use by default for this association. Positive /// numbers indicate an index into the dll's resource table, while negative numbers indicate a resource ID. An example of the syntax /// for the resource is "c:\myfolder\myfile.dll,-1". /// public IconLocation DefaultIcon => IconLocation.TryParse(GetString(ASSOCSTR.ASSOCSTR_DEFAULTICON), out var loc) ? loc : null; /// The extension string. public string Extension { get; } /// The friendly name of an executable file. public string FriendlyAppName => GetString(ASSOCSTR.ASSOCSTR_FRIENDLYAPPNAME); /// The friendly name of a document type. public string FriendlyDocName => GetString(ASSOCSTR.ASSOCSTR_FRIENDLYDOCNAME); /// /// Corresponds to the InfoTip registry value. Returns an info tip for an item, or list of properties in the form of an /// IPropertyDescriptionList from which to create an info tip, such as when hovering the cursor over a file name. The list of /// properties can be parsed with PSGetPropertyDescriptionListFromString. /// public string InfoTip => GetString(ASSOCSTR.ASSOCSTR_INFOTIP); /// /// The ProgID provided by the app associated with the file type or URI scheme. This if configured by users in their default program settings. /// public ProgId ProgId => ProgId.Open(GetString(ASSOCSTR.ASSOCSTR_PROGID), true, true, true); /// /// Introduced in Internet Explorer 6. For an object that has a Shell extension associated with it, you can use this to retrieve the /// CLSID of that Shell extension object by passing a string representation of the IID of the interface you want to retrieve as the /// parameter of IQueryAssociations::GetString. For example, if you want to retrieve a handler that implements the IExtractImage /// interface, you would specify "{BB2E617C-0920-11d1-9A0B-00C04FC2D6C1}", which is the IID of IExtractImage. /// public IndirectString ShellExtension => IndirectString.TryParse(GetString(ASSOCSTR.ASSOCSTR_SHELLEXTENSION), out var s) ? s : null; /// Introduced in Windows 8. public Guid? SupportedUriProtocols { get { try { return new Guid(GetString(ASSOCSTR.ASSOCSTR_SUPPORTED_URI_PROTOCOLS)); } catch { return null; } } } /// Gets the command verbs for this file association. /// Returns a value. public IReadOnlyDictionary Verbs => throw new NotImplementedException(); // TODO #region AllPropLists // TODO: Enhance /// /// Introduced in Internet Explorer 6. Describes a general type of MIME file association, such as image and bmp, so that applications /// can make general assumptions about a specific file type. /// public string ContentType => GetString(ASSOCSTR.ASSOCSTR_CONTENTTYPE); /// /// Introduced in Internet Explorer 6. Corresponds to the QuickTip registry value. Same as ASSOCSTR_INFOTIP, except that it always /// returns a list of property names in the form of an IPropertyDescriptionList. The difference between this value and /// ASSOCSTR_INFOTIP is that this returns properties that are safe for any scenario that causes slow property retrieval, such as /// offline or slow networks. Some of the properties returned from ASSOCSTR_INFOTIP might not be appropriate for slow property /// retrieval scenarios. The list of properties can be parsed with PSGetPropertyDescriptionListFromString. /// public string QuickTip => GetString(ASSOCSTR.ASSOCSTR_QUICKTIP); /// /// Introduced in Internet Explorer 6. Corresponds to the TileInfo registry value. Contains a list of properties to be displayed for /// a particular file type in a Windows Explorer window that is in tile view. This is the same as ASSOCSTR_INFOTIP, but, like /// ASSOCSTR_QUICKTIP, it also returns a list of property names in the form of an IPropertyDescriptionList. The list of properties /// can be parsed with PSGetPropertyDescriptionListFromString. /// public string TileInfo => GetString(ASSOCSTR.ASSOCSTR_TILEINFO); #endregion AllPropLists // TODO: Enhance /// Initializes a new instance of the class based on the supplied executable name. /// The full path of the application executable. /// A instance if exists; otherwise. public static ShellAssociation CreateFromAppExeName(string appExeName) => CreateAndInit(ASSOCF.ASSOCF_INIT_BYEXENAME, appExeName); /// Initializes a new instance of the class based on the supplied CLSID. /// The CLSID. /// A instance if exists; otherwise. public static ShellAssociation CreateFromCLSID(Guid classId) => CreateAndInit(0, classId.ToString("B")); /// Initializes a new instance of the class based on the supplied programmatic identifier (ProgId). /// The ProgId. /// A instance if exists; otherwise. public static ShellAssociation CreateFromProgId(string progId) => CreateAndInit(0, progId); /// Initializes a new instance of the class based on the supplied file extension. /// The file extension. This should be in the ".ext" format. /// A instance if exists; otherwise. public static ShellAssociation FromFileExtension(string ext) { if (ext is null) throw new ArgumentNullException(nameof(ext)); if (!ext.StartsWith(".")) throw new ArgumentException("The value must be in the format \".ext\"", nameof(ext)); return CreateAndInit(ASSOCF.ASSOCF_INIT_DEFAULTTOSTAR, ext); } private static ShellAssociation CreateAndInit(ASSOCF flags, string assoc) { // if (Environment.OSVersion.Version.Major >= 6) //var elements = new[] { new ASSOCIATIONELEMENT { ac = ASSOCCLASS.ASSOCCLASS_PROGID_STR, pszClass = progId } }; //AssocCreateForClasses(elements, (uint)elements.Length, typeof(IQueryAssociations).GUID, out var iq).ThrowIfFailed(); //ret.qassoc = (IQueryAssociations)iq; var ret = new ShellAssociation(assoc) { qassoc = AssocCreate() }; try { ret.qassoc.Init(flags, assoc); return ret; } catch { return null; } } private SafeRegistryHandle GetKey(ASSOCKEY key, string extra = null) { const ASSOCF flags = 0; qassoc.GetKey(flags, key, extra, out var hkey); return new SafeRegistryHandle((IntPtr)hkey, true); } private string GetString(ASSOCSTR astr, string extra = null) { const ASSOCF flags = ASSOCF.ASSOCF_NOTRUNCATE | ASSOCF.ASSOCF_REMAPRUNDLL; var sz = 0U; qassoc.GetString(flags, astr, extra, null, ref sz); var sb = new StringBuilder((int)sz, (int)sz); qassoc.GetString(flags, astr, extra, sb, ref sz); return sb.ToString(); } } }