using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ComponentModel; using System.Drawing; using System.Runtime.InteropServices; using System.Windows.Forms; namespace Yaulw.Installer { #region ShellLink Object /// /// Summary description for ShellLink. /// /// public class ShellLink : IDisposable { #region ComInterop for IShellLink #region IPersist Interface [ComImportAttribute()] [GuidAttribute("0000010C-0000-0000-C000-000000000046")] [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] private interface IPersist { [PreserveSig] //[helpstring("Returns the class identifier for the component object")] void GetClassID(out Guid pClassID); } #endregion #region IPersistFile Interface [ComImportAttribute()] [GuidAttribute("0000010B-0000-0000-C000-000000000046")] [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] private interface IPersistFile { // can't get this to go if I extend IPersist, so put it here: [PreserveSig] void GetClassID(out Guid pClassID); //[helpstring("Checks for changes since last file write")] void IsDirty(); //[helpstring("Opens the specified file and initializes the object from its contents")] void Load( [MarshalAs(UnmanagedType.LPWStr)] string pszFileName, uint dwMode); //[helpstring("Saves the object into the specified file")] void Save( [MarshalAs(UnmanagedType.LPWStr)] string pszFileName, [MarshalAs(UnmanagedType.Bool)] bool fRemember); //[helpstring("Notifies the object that save is completed")] void SaveCompleted( [MarshalAs(UnmanagedType.LPWStr)] string pszFileName); //[helpstring("Gets the current name of the file associated with the object")] void GetCurFile( [MarshalAs(UnmanagedType.LPWStr)] out string ppszFileName); } #endregion #region IShellLink Interface [ComImportAttribute()] [GuidAttribute("000214EE-0000-0000-C000-000000000046")] [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] private interface IShellLinkA { //[helpstring("Retrieves the path and filename of a shell link object")] void GetPath( [Out(), MarshalAs(UnmanagedType.LPStr)] StringBuilder pszFile, int cchMaxPath, ref _WIN32_FIND_DATAA pfd, uint fFlags); //[helpstring("Retrieves the list of shell link item identifiers")] void GetIDList(out IntPtr ppidl); //[helpstring("Sets the list of shell link item identifiers")] void SetIDList(IntPtr pidl); //[helpstring("Retrieves the shell link description string")] void GetDescription( [Out(), MarshalAs(UnmanagedType.LPStr)] StringBuilder pszFile, int cchMaxName); //[helpstring("Sets the shell link description string")] void SetDescription( [MarshalAs(UnmanagedType.LPStr)] string pszName); //[helpstring("Retrieves the name of the shell link working directory")] void GetWorkingDirectory( [Out(), MarshalAs(UnmanagedType.LPStr)] StringBuilder pszDir, int cchMaxPath); //[helpstring("Sets the name of the shell link working directory")] void SetWorkingDirectory( [MarshalAs(UnmanagedType.LPStr)] string pszDir); //[helpstring("Retrieves the shell link command-line arguments")] void GetArguments( [Out(), MarshalAs(UnmanagedType.LPStr)] StringBuilder pszArgs, int cchMaxPath); //[helpstring("Sets the shell link command-line arguments")] void SetArguments( [MarshalAs(UnmanagedType.LPStr)] string pszArgs); //[propget, helpstring("Retrieves or sets the shell link hot key")] void GetHotkey(out short pwHotkey); //[propput, helpstring("Retrieves or sets the shell link hot key")] void SetHotkey(short pwHotkey); //[propget, helpstring("Retrieves or sets the shell link show command")] void GetShowCmd(out uint piShowCmd); //[propput, helpstring("Retrieves or sets the shell link show command")] void SetShowCmd(uint piShowCmd); //[helpstring("Retrieves the location (path and index) of the shell link icon")] void GetIconLocation( [Out(), MarshalAs(UnmanagedType.LPStr)] StringBuilder pszIconPath, int cchIconPath, out int piIcon); //[helpstring("Sets the location (path and index) of the shell link icon")] void SetIconLocation( [MarshalAs(UnmanagedType.LPStr)] string pszIconPath, int iIcon); //[helpstring("Sets the shell link relative path")] void SetRelativePath( [MarshalAs(UnmanagedType.LPStr)] string pszPathRel, uint dwReserved); //[helpstring("Resolves a shell link. The system searches for the shell link object and updates the shell link path and its list of identifiers (if necessary)")] void Resolve( IntPtr hWnd, uint fFlags); //[helpstring("Sets the shell link path and filename")] void SetPath( [MarshalAs(UnmanagedType.LPStr)] string pszFile); } [ComImportAttribute()] [GuidAttribute("000214F9-0000-0000-C000-000000000046")] [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] private interface IShellLinkW { //[helpstring("Retrieves the path and filename of a shell link object")] void GetPath( [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxPath, ref _WIN32_FIND_DATAW pfd, uint fFlags); //[helpstring("Retrieves the list of shell link item identifiers")] void GetIDList(out IntPtr ppidl); //[helpstring("Sets the list of shell link item identifiers")] void SetIDList(IntPtr pidl); //[helpstring("Retrieves the shell link description string")] void GetDescription( [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxName); //[helpstring("Sets the shell link description string")] void SetDescription( [MarshalAs(UnmanagedType.LPWStr)] string pszName); //[helpstring("Retrieves the name of the shell link working directory")] void GetWorkingDirectory( [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath); //[helpstring("Sets the name of the shell link working directory")] void SetWorkingDirectory( [MarshalAs(UnmanagedType.LPWStr)] string pszDir); //[helpstring("Retrieves the shell link command-line arguments")] void GetArguments( [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cchMaxPath); //[helpstring("Sets the shell link command-line arguments")] void SetArguments( [MarshalAs(UnmanagedType.LPWStr)] string pszArgs); //[propget, helpstring("Retrieves or sets the shell link hot key")] void GetHotkey(out short pwHotkey); //[propput, helpstring("Retrieves or sets the shell link hot key")] void SetHotkey(short pwHotkey); //[propget, helpstring("Retrieves or sets the shell link show command")] void GetShowCmd(out uint piShowCmd); //[propput, helpstring("Retrieves or sets the shell link show command")] void SetShowCmd(uint piShowCmd); //[helpstring("Retrieves the location (path and index) of the shell link icon")] void GetIconLocation( [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, int cchIconPath, out int piIcon); //[helpstring("Sets the location (path and index) of the shell link icon")] void SetIconLocation( [MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon); //[helpstring("Sets the shell link relative path")] void SetRelativePath( [MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, uint dwReserved); //[helpstring("Resolves a shell link. The system searches for the shell link object and updates the shell link path and its list of identifiers (if necessary)")] void Resolve( IntPtr hWnd, uint fFlags); //[helpstring("Sets the shell link path and filename")] void SetPath( [MarshalAs(UnmanagedType.LPWStr)] string pszFile); } #endregion #region ShellLinkCoClass [GuidAttribute("00021401-0000-0000-C000-000000000046")] [ClassInterfaceAttribute(ClassInterfaceType.None)] [ComImportAttribute()] private class CShellLink{} #endregion #region Private IShellLink enumerations private enum EShellLinkGP : uint { SLGP_SHORTPATH = 1, SLGP_UNCPRIORITY = 2 } [Flags] private enum EShowWindowFlags : uint { SW_HIDE = 0, SW_SHOWNORMAL = 1, SW_NORMAL = 1, SW_SHOWMINIMIZED = 2, SW_SHOWMAXIMIZED = 3, SW_MAXIMIZE = 3, SW_SHOWNOACTIVATE = 4, SW_SHOW = 5, SW_MINIMIZE = 6, SW_SHOWMINNOACTIVE = 7, SW_SHOWNA = 8, SW_RESTORE = 9, SW_SHOWDEFAULT = 10, SW_MAX = 10 } #endregion #region IShellLink Private structs [StructLayoutAttribute(LayoutKind.Sequential, Pack=4, Size=0, CharSet=CharSet.Unicode)] private struct _WIN32_FIND_DATAW { public uint dwFileAttributes; public _FILETIME ftCreationTime; public _FILETIME ftLastAccessTime; public _FILETIME ftLastWriteTime; public uint nFileSizeHigh; public uint nFileSizeLow; public uint dwReserved0; public uint dwReserved1; [MarshalAs(UnmanagedType.ByValTStr , SizeConst = 260)] // MAX_PATH public string cFileName; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)] public string cAlternateFileName; } [StructLayoutAttribute(LayoutKind.Sequential, Pack=4, Size=0, CharSet=CharSet.Ansi)] private struct _WIN32_FIND_DATAA { public uint dwFileAttributes; public _FILETIME ftCreationTime; public _FILETIME ftLastAccessTime; public _FILETIME ftLastWriteTime; public uint nFileSizeHigh; public uint nFileSizeLow; public uint dwReserved0; public uint dwReserved1; [MarshalAs(UnmanagedType.ByValTStr , SizeConst = 260)] // MAX_PATH public string cFileName; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)] public string cAlternateFileName; } [StructLayoutAttribute(LayoutKind.Sequential, Pack=4, Size=0)] private struct _FILETIME { public uint dwLowDateTime; public uint dwHighDateTime; } #endregion #region UnManaged Methods private class UnManagedMethods { [DllImport("Shell32", CharSet=CharSet.Auto)] internal extern static int ExtractIconEx ( [MarshalAs(UnmanagedType.LPTStr)] string lpszFile, int nIconIndex, IntPtr[] phIconLarge, IntPtr[] phIconSmall, int nIcons); [DllImport("user32")] internal static extern int DestroyIcon(IntPtr hIcon); } #endregion #endregion #region Enumerations /// /// Flags determining how the links with missing /// targets are resolved. /// [Flags] public enum EShellLinkResolveFlags : uint { /// /// Allow any match during resolution. Has no effect /// on ME/2000 or above, use the other flags instead. /// SLR_ANY_MATCH = 0x2, /// /// Call the Microsoft Windows Installer. /// SLR_INVOKE_MSI = 0x80, /// /// Disable distributed link tracking. By default, /// distributed link tracking tracks removable media /// across multiple devices based on the volume name. /// It also uses the UNC path to track remote file /// systems whose drive letter has changed. Setting /// SLR_NOLINKINFO disables both types of tracking. /// SLR_NOLINKINFO = 0x40, /// /// Do not display a dialog box if the link cannot be resolved. /// When SLR_NO_UI is set, a time-out value that specifies the /// maximum amount of time to be spent resolving the link can /// be specified in milliseconds. The function returns if the /// link cannot be resolved within the time-out duration. /// If the timeout is not set, the time-out duration will be /// set to the default value of 3,000 milliseconds (3 seconds). /// SLR_NO_UI = 0x1, /// /// Not documented in SDK. Assume same as SLR_NO_UI but /// intended for applications without a hWnd. /// SLR_NO_UI_WITH_MSG_PUMP = 0x101, /// /// Do not update the link information. /// SLR_NOUPDATE = 0x8, /// /// Do not execute the search heuristics. /// SLR_NOSEARCH = 0x10, /// /// Do not use distributed link tracking. /// SLR_NOTRACK = 0x20, /// /// If the link object has changed, update its path and list /// of identifiers. If SLR_UPDATE is set, you do not need to /// call IPersistFile::IsDirty to determine whether or not /// the link object has changed. /// SLR_UPDATE = 0x4 } public enum LinkDisplayMode : uint { edmNormal = EShowWindowFlags.SW_NORMAL, edmMinimized = EShowWindowFlags.SW_SHOWMINNOACTIVE, edmMaximized = EShowWindowFlags.SW_MAXIMIZE } #endregion #region Member Variables // Use Unicode (W) under NT, otherwise use ANSI private IShellLinkW linkW; private IShellLinkA linkA; private string shortcutFile = ""; #endregion #region Constructor /// /// Creates an instance of the Shell Link object. /// public ShellLink() { if (System.Environment.OSVersion.Platform == PlatformID.Win32NT) { linkW = (IShellLinkW)new CShellLink(); } else { linkA = (IShellLinkA)new CShellLink(); } } /// /// Creates an instance of a Shell Link object /// from the specified link file /// /// The Shortcut file to open public ShellLink(string linkFile) : this() { Open(linkFile); } #endregion #region Destructor and Dispose /// /// Call dispose just in case it hasn't happened yet /// ~ShellLink() { Dispose(); } /// /// Dispose the object, releasing the COM ShellLink object /// public void Dispose() { if (linkW != null ) { Marshal.ReleaseComObject(linkW); linkW = null; } if (linkA != null) { Marshal.ReleaseComObject(linkA); linkA = null; } } #endregion #region Implementation public string ShortCutFile { get { return this.shortcutFile; } set { this.shortcutFile = value; } } /// /// Gets a System.Drawing.Icon containing the icon for this /// ShellLink object. /// public Icon LargeIcon { get { return getIcon(true); } } public Icon SmallIcon { get { return getIcon(false); } } private Icon getIcon(bool large) { // Get icon index and path: int iconIndex = 0; StringBuilder iconPath = new StringBuilder(260, 260); if (linkA == null) { linkW.GetIconLocation(iconPath, iconPath.Capacity, out iconIndex); } else { linkA.GetIconLocation(iconPath, iconPath.Capacity, out iconIndex); } string iconFile = iconPath.ToString(); // If there are no details set for the icon, then we must use // the shell to get the icon for the target: if (iconFile.Length == 0) { // Use the FileIcon object to get the icon: FileIcon.SHGetFileInfoConstants flags = FileIcon.SHGetFileInfoConstants.SHGFI_ICON | FileIcon.SHGetFileInfoConstants.SHGFI_ATTRIBUTES; if (large) { flags = flags | FileIcon.SHGetFileInfoConstants.SHGFI_LARGEICON; } else { flags = flags | FileIcon.SHGetFileInfoConstants.SHGFI_SMALLICON; } FileIcon fileIcon = new FileIcon(Target, flags); return fileIcon.ShellIcon; } else { // Use ExtractIconEx to get the icon: IntPtr[] hIconEx = new IntPtr[1] {IntPtr.Zero}; int iconCount = 0; if (large) { iconCount = UnManagedMethods.ExtractIconEx( iconFile, iconIndex, hIconEx, null, 1); } else { iconCount = UnManagedMethods.ExtractIconEx( iconFile, iconIndex, null, hIconEx, 1); } // If success then return as a GDI+ object Icon icon = null; if (hIconEx[0] != IntPtr.Zero) { icon = Icon.FromHandle(hIconEx[0]); //UnManagedMethods.DestroyIcon(hIconEx[0]); } return icon; } } /// /// Gets the path to the file containing the icon for this shortcut. /// public string IconPath { get { StringBuilder iconPath = new StringBuilder(260, 260); int iconIndex = 0; if (linkA == null) { linkW.GetIconLocation(iconPath, iconPath.Capacity, out iconIndex); } else { linkA.GetIconLocation(iconPath, iconPath.Capacity, out iconIndex); } return iconPath.ToString(); } set { StringBuilder iconPath = new StringBuilder(260, 260); int iconIndex = 0; if (linkA == null) { linkW.GetIconLocation(iconPath, iconPath.Capacity, out iconIndex); } else { linkA.GetIconLocation(iconPath, iconPath.Capacity, out iconIndex); } if (linkA == null) { linkW.SetIconLocation(value, iconIndex); } else { linkA.SetIconLocation(value, iconIndex); } } } /// /// Gets the index of this icon within the icon path's resources /// public int IconIndex { get { StringBuilder iconPath = new StringBuilder(260, 260); int iconIndex = 0; if (linkA == null) { linkW.GetIconLocation(iconPath, iconPath.Capacity, out iconIndex); } else { linkA.GetIconLocation(iconPath, iconPath.Capacity, out iconIndex); } return iconIndex; } set { StringBuilder iconPath = new StringBuilder(260, 260); int iconIndex = 0; if (linkA == null) { linkW.GetIconLocation(iconPath, iconPath.Capacity, out iconIndex); } else { linkA.GetIconLocation(iconPath, iconPath.Capacity, out iconIndex); } if (linkA == null) { linkW.SetIconLocation(iconPath.ToString(), value); } else { linkA.SetIconLocation(iconPath.ToString(), value); } } } /// /// Gets/sets the fully qualified path to the link's target /// public string Target { get { StringBuilder target = new StringBuilder(260, 260); if (linkA == null) { _WIN32_FIND_DATAW fd = new _WIN32_FIND_DATAW(); linkW.GetPath(target, target.Capacity, ref fd, (uint)EShellLinkGP.SLGP_UNCPRIORITY); } else { _WIN32_FIND_DATAA fd = new _WIN32_FIND_DATAA(); linkA.GetPath(target, target.Capacity, ref fd, (uint)EShellLinkGP.SLGP_UNCPRIORITY); } return target.ToString(); } set { if (linkA == null) { linkW.SetPath(value); } else { linkA.SetPath(value); } } } /// /// Gets/sets the Working Directory for the Link /// public string WorkingDirectory { get { StringBuilder path = new StringBuilder(260, 260); if (linkA == null) { linkW.GetWorkingDirectory(path, path.Capacity); } else { linkA.GetWorkingDirectory(path, path.Capacity); } return path.ToString(); } set { if (linkA == null) { linkW.SetWorkingDirectory(value); } else { linkA.SetWorkingDirectory(value); } } } /// /// Gets/sets the description of the link /// public string Description { get { StringBuilder description = new StringBuilder(1024, 1024); if (linkA == null) { linkW.GetDescription(description, description.Capacity); } else { linkA.GetDescription(description, description.Capacity); } return description.ToString(); } set { if (linkA == null) { linkW.SetDescription(value); } else { linkA.SetDescription(value); } } } /// /// Gets/sets any command line arguments associated with the link /// public string Arguments { get { StringBuilder arguments = new StringBuilder(260, 260); if (linkA == null) { linkW.GetArguments(arguments, arguments.Capacity); } else { linkA.GetArguments(arguments, arguments.Capacity); } return arguments.ToString(); } set { if (linkA == null) { linkW.SetArguments(value); } else { linkA.SetArguments(value); } } } /// /// Gets/sets the initial display mode when the shortcut is /// run /// public LinkDisplayMode DisplayMode { get { uint cmd = 0; if (linkA == null) { linkW.GetShowCmd(out cmd); } else { linkA.GetShowCmd(out cmd); } return (LinkDisplayMode)cmd; } set { if (linkA == null) { linkW.SetShowCmd((uint)value); } else { linkA.SetShowCmd((uint)value); } } } /// /// Gets/sets the HotKey to start the shortcut (if any) /// public Keys HotKey { get { short key = 0; if (linkA == null) { linkW.GetHotkey(out key); } else { linkA.GetHotkey(out key); } return (Keys)key; } set { if (linkA == null) { linkW.SetHotkey((short)value); } else { linkA.SetHotkey((short)value); } } } /// /// Saves the shortcut to ShortCutFile. /// public void Save() { Save(shortcutFile); } /// /// Saves the shortcut to the specified file /// /// The shortcut file (.lnk) public void Save( string linkFile ) { // Save the object to disk if (linkA == null) { ((IPersistFile)linkW).Save(linkFile, true); shortcutFile = linkFile; } else { ((IPersistFile)linkA).Save(linkFile, true); shortcutFile = linkFile; } } /// /// Loads a shortcut from the specified file /// /// The shortcut file (.lnk) to load public void Open( string linkFile ) { Open(linkFile, IntPtr.Zero, (EShellLinkResolveFlags.SLR_ANY_MATCH | EShellLinkResolveFlags.SLR_NO_UI), 1); } /// /// Loads a shortcut from the specified file, and allows flags controlling /// the UI behaviour if the shortcut's target isn't found to be set. /// /// The shortcut file (.lnk) to load /// The window handle of the application's UI, if any /// Flags controlling resolution behaviour public void Open( string linkFile, IntPtr hWnd, EShellLinkResolveFlags resolveFlags ) { Open(linkFile, hWnd, resolveFlags, 1); } /// /// Loads a shortcut from the specified file, and allows flags controlling /// the UI behaviour if the shortcut's target isn't found to be set. If /// no SLR_NO_UI is specified, you can also specify a timeout. /// /// The shortcut file (.lnk) to load /// The window handle of the application's UI, if any /// Flags controlling resolution behaviour /// Timeout if SLR_NO_UI is specified, in ms. public void Open( string linkFile, IntPtr hWnd, EShellLinkResolveFlags resolveFlags, ushort timeOut ) { uint flags; if ((resolveFlags & EShellLinkResolveFlags.SLR_NO_UI) == EShellLinkResolveFlags.SLR_NO_UI) { flags = (uint)((int)resolveFlags | (timeOut << 16)); } else { flags = (uint)resolveFlags; } if (linkA == null) { ((IPersistFile)linkW).Load(linkFile, 0); //STGM_DIRECT) linkW.Resolve(hWnd, flags); this.shortcutFile = linkFile; } else { ((IPersistFile)linkA).Load(linkFile, 0); //STGM_DIRECT) linkA.Resolve(hWnd, flags); this.shortcutFile = linkFile; } } #endregion } #endregion }