Libs/ShellLink/ShellLink.cs
using System;
using System.IO; using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes; using System.Text; using ComTypes = System.Runtime.InteropServices.ComTypes; // Original code is https://emoacht.wordpress.com/2012/11/14/csharp-appusermodelid/ public class ShellLink : IDisposable { #region Win32 and COM // IShellLink Interface [ComImport] [Guid ("000214F9-0000-0000-C000-000000000046")] [InterfaceType (ComInterfaceType.InterfaceIsIUnknown)] private interface IShellLinkW { uint GetPath ([Out, MarshalAs (UnmanagedType.LPWStr)] StringBuilder pszFile, int cch, ref WIN32_FIND_DATAW pfd, uint fFlags); uint GetIDList (out IntPtr ppidl); uint SetIDList (IntPtr pidl); uint GetDescription ([Out, MarshalAs (UnmanagedType.LPWStr)] StringBuilder pszName, int cch); uint SetDescription ([MarshalAs (UnmanagedType.LPWStr)] string pszName); uint GetWorkingDirectory ([Out, MarshalAs (UnmanagedType.LPWStr)] StringBuilder pszDir, int cch); uint SetWorkingDirectory ([MarshalAs (UnmanagedType.LPWStr)] string pszDir); uint GetArguments ([Out, MarshalAs (UnmanagedType.LPWStr)] StringBuilder pszArgs, int cch); uint SetArguments ([MarshalAs (UnmanagedType.LPWStr)] string pszArgs); uint GetHotKey (out ushort pwHotkey); uint SetHotKey (ushort wHotKey); uint GetShowCmd (out int piShowCmd); uint SetShowCmd (int iShowCmd); uint GetIconLocation ([Out, MarshalAs (UnmanagedType.LPWStr)] StringBuilder pszIconPath, int cch, out int piIcon); uint SetIconLocation ([MarshalAs (UnmanagedType.LPWStr)] string pszIconPath, int iIcon); uint SetRelativePath ([MarshalAs (UnmanagedType.LPWStr)] string pszPathRel, uint dwReserved); uint Resolve (IntPtr hwnd, uint fFlags); uint SetPath ([MarshalAs (UnmanagedType.LPWStr)] string pszFile); } // ShellLink CoClass (ShellLink object) [ComImport] [ClassInterface (ClassInterfaceType.None)] [Guid ("00021401-0000-0000-C000-000000000046")] private class CShellLink { } // WIN32_FIND_DATAW Structure [StructLayout (LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Unicode)] private struct WIN32_FIND_DATAW { public uint dwFileAttributes; public ComTypes.FILETIME ftCreationTime; public ComTypes.FILETIME ftLastAccessTime; public ComTypes.FILETIME ftLastWriteTime; public uint nFileSizeHigh; public uint nFileSizeLow; public uint dwReserved0; public uint dwReserved1; [MarshalAs (UnmanagedType.ByValTStr, SizeConst = MAX_PATH)] public string cFileName; [MarshalAs (UnmanagedType.ByValTStr, SizeConst = 14)] public string cAlternateFileName; } // IPropertyStore Interface [ComImport] [InterfaceType (ComInterfaceType.InterfaceIsIUnknown)] [Guid ("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99")] private interface IPropertyStore { uint GetCount ([Out] out uint cProps); uint GetAt ([In] uint iProp, out PropertyKey pkey); uint GetValue ([In] ref PropertyKey key, [Out] PropVariant pv); uint SetValue ([In] ref PropertyKey key, [In] PropVariant pv); uint Commit (); } // PropertyKey Structure // Narrowed down from PropertyKey.cs of Windows API Code Pack 1.1 [StructLayout (LayoutKind.Sequential, Pack = 4)] private struct PropertyKey { #region Fields private Guid formatId; // Unique GUID for property private Int32 propertyId; // Property identifier (PID) #endregion #region Public Properties public Guid FormatId { get { return formatId; } } public Int32 PropertyId { get { return propertyId; } } #endregion #region Constructor public PropertyKey (Guid formatId, Int32 propertyId) { this.formatId = formatId; this.propertyId = propertyId; } public PropertyKey (string formatId, Int32 propertyId) { this.formatId = new Guid (formatId); this.propertyId = propertyId; } #endregion } // PropVariant Class (only for string value) // Narrowed down from PropVariant.cs of Windows API Code Pack 1.1 // Originally from http://blogs.msdn.com/b/adamroot/archive/2008/04/11 // /interop-with-propvariants-in-net.aspx [StructLayout (LayoutKind.Explicit)] private sealed class PropVariant : IDisposable { #region Fields [FieldOffset (0)] ushort valueType; // Value type // [FieldOffset(2)] // ushort wReserved1; // Reserved field // [FieldOffset(4)] // ushort wReserved2; // Reserved field // [FieldOffset(6)] // ushort wReserved3; // Reserved field [FieldOffset (8)] IntPtr ptr; // Value #endregion #region Public Properties // Value type (System.Runtime.InteropServices.VarEnum) public VarEnum VarType { get { return (VarEnum) valueType; } set { valueType = (ushort) value; } } // Whether value is empty or null public bool IsNullOrEmpty { get { return (valueType == (ushort) VarEnum.VT_EMPTY || valueType == (ushort) VarEnum.VT_NULL); } } // Value (only for string value) public string Value { get { return Marshal.PtrToStringUni (ptr); } } #endregion #region Constructor public PropVariant () { } // Construct with string value public PropVariant (string value) { if (value == null) throw new ArgumentException ("Failed to set value."); valueType = (ushort) VarEnum.VT_LPWSTR; ptr = Marshal.StringToCoTaskMemUni (value); } #endregion #region Destructor ~PropVariant () { Dispose (); } public void Dispose () { PropVariantClear (this); GC.SuppressFinalize (this); } #endregion } [DllImport ("Ole32.dll", PreserveSig = false)] private extern static void PropVariantClear ([In, Out] PropVariant pvar); #endregion #region Private private IShellLinkW shellLinkW = null; private readonly PropertyKey AppUserModelIDKey = new PropertyKey ("{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}", 5); private const int MAX_PATH = 260; private const int INFOTIPSIZE = 1024; private const int SW_SHOWNORMAL = 1; private const int SW_SHOWMINIMIZED = 2; private const int SW_SHOWMAXIMIZED = 3; private const int SW_SHOWMINNOACTIVE = 7; private const int STGM_READ = 0x00000000; private const int STGM_READWRITE = 0x00000002; private const uint SLGP_UNCPRIORITY = 0x0002; private IPersistFile PersistFile { get { IPersistFile PersistFile = (IPersistFile) shellLinkW; if (PersistFile == null) throw new COMException ("Failed to create IPersistFile."); else return PersistFile; } } private IPropertyStore PropertyStore { get { IPropertyStore PropertyStore = (IPropertyStore) shellLinkW; if (PropertyStore == null) throw new COMException ("Failed to create IPropertyStore."); else return PropertyStore; } } #endregion #region Public // Path of loaded shortcut file public string FilePath { get { string shortcutFile; PersistFile.GetCurFile (out shortcutFile); return shortcutFile; } } // Path of target file public string TargetPath { get { StringBuilder targetPath = new StringBuilder (MAX_PATH); WIN32_FIND_DATAW data = new WIN32_FIND_DATAW (); VerifySucceeded (shellLinkW.GetPath (targetPath, targetPath.Capacity, ref data, SLGP_UNCPRIORITY)); return targetPath.ToString (); } set { VerifySucceeded (shellLinkW.SetPath (value)); } } // Description public string Description { get { StringBuilder description = new StringBuilder (INFOTIPSIZE); VerifySucceeded (shellLinkW.GetDescription (description, description.Capacity)); return description.ToString (); } set { VerifySucceeded (shellLinkW.SetDescription (value)); } } // Arguments public string Arguments { get { StringBuilder arguments = new StringBuilder (INFOTIPSIZE); VerifySucceeded (shellLinkW.GetArguments (arguments, arguments.Capacity)); return arguments.ToString (); } set { VerifySucceeded (shellLinkW.SetArguments (value)); } } // WorkingDirectory public string WorkingDirectory { get { StringBuilder workingDirectory = new StringBuilder (MAX_PATH); VerifySucceeded (shellLinkW.GetWorkingDirectory (workingDirectory, workingDirectory.Capacity)); return workingDirectory.ToString (); } set { VerifySucceeded (shellLinkW.SetWorkingDirectory (value)); } } // IconLocation public string IconLocation { get { StringBuilder iconLocation = new StringBuilder (MAX_PATH); int iconIdx; VerifySucceeded (shellLinkW.GetIconLocation (iconLocation, iconLocation.Capacity, out iconIdx)); iconLocation.Append (","); iconLocation.Append (iconIdx.ToString ()); return iconLocation.ToString (); } set { int idx = value.LastIndexOf (","); string iconLocation; string strIdx; int iconIdx; if (idx >= 0) { strIdx = value.Substring (idx + 1); if (Int32.TryParse (strIdx, out iconIdx)) { iconLocation = value.Substring (0, idx); } else { iconLocation = value; iconIdx = 0; } } else { iconLocation = value; iconIdx = 0; } VerifySucceeded (shellLinkW.SetIconLocation (iconLocation, iconIdx)); } } // WindowStyle public int WindowStyle { get { int windowStyle; VerifySucceeded (shellLinkW.GetShowCmd (out windowStyle)); switch (windowStyle) { case SW_SHOWMINIMIZED: case SW_SHOWMINNOACTIVE: return SW_SHOWMINNOACTIVE; case SW_SHOWMAXIMIZED: return SW_SHOWMAXIMIZED; case SW_SHOWNORMAL: return SW_SHOWNORMAL; default: return 0; } } set { int windowStyle; switch (value) { case 0: case 1: windowStyle = SW_SHOWNORMAL; break; case 3: windowStyle = SW_SHOWMAXIMIZED; break; case 7: windowStyle = SW_SHOWMINNOACTIVE; break; default: throw new ArgumentException ("Unsupported value."); } VerifySucceeded (shellLinkW.SetShowCmd (windowStyle)); } } // Hotkey public ushort Hotkey { get { ushort hotKey; VerifySucceeded (shellLinkW.GetHotKey (out hotKey)); return hotKey; } set { VerifySucceeded (shellLinkW.SetHotKey (value)); } } // AppUserModelID // https://docs.microsoft.com/en-us/windows/win32/shell/appids public string AppUserModelID { get { using (PropVariant pv = new PropVariant ()) { VerifySucceeded (PropertyStore.GetValue (AppUserModelIDKey, pv)); if (pv.Value == null) return string.Empty; else return pv.Value; } } set { using (PropVariant pv = new PropVariant (value)) { VerifySucceeded (PropertyStore.SetValue (AppUserModelIDKey, pv)); VerifySucceeded (PropertyStore.Commit ()); } } } #endregion #region Constructor public ShellLink () : this (null) { } // Construct with loading shortcut file. public ShellLink (string file) { try { shellLinkW = (IShellLinkW) new CShellLink (); } catch { throw new COMException ("Failed to create ShellLink object."); } if (file != null) Load (file); } #endregion #region Destructor ~ShellLink () { Dispose (false); } public void Dispose () { Dispose (true); GC.SuppressFinalize (this); } protected virtual void Dispose (bool disposing) { if (shellLinkW != null) { // Release all references. Marshal.FinalReleaseComObject (shellLinkW); shellLinkW = null; } } #endregion #region Methods // Save shortcut file. public void Save () { string file = FilePath; if (file == null) throw new InvalidOperationException ("File name is not given."); else Save (file); } public void Save (string file) { if (file == null) throw new ArgumentNullException ("File name is required."); else PersistFile.Save (file, true); } // Load shortcut file. public void Load (string file) { Load (file, STGM_READWRITE); } public void Load (string file, int flags) { if (!File.Exists (file)) throw new FileNotFoundException ("File is not found.", file); else PersistFile.Load (file, flags); } // Verify if operation succeeded. public static void VerifySucceeded (uint hresult) { if (hresult > 1) throw new InvalidOperationException ("Failed with HRESULT: " + hresult.ToString ("X")); } #endregion } |