diff --git a/QuickLook/Helpers/AutoStartupHelper.cs b/QuickLook/Helpers/AutoStartupHelper.cs index 504843b..588f8cc 100644 --- a/QuickLook/Helpers/AutoStartupHelper.cs +++ b/QuickLook/Helpers/AutoStartupHelper.cs @@ -17,7 +17,9 @@ using System; using System.IO; +using System.Runtime.InteropServices.ComTypes; using QuickLook.Common.Helpers; +using QuickLook.NativeMethods; namespace QuickLook.Helpers { @@ -34,16 +36,15 @@ namespace QuickLook.Helpers try { - File.Create(StartupFullPath).Close(); + RemoveAutorunShortcut(); - var lnk = ShellLinkHelper.OpenShellLink(StartupFullPath); + var lnk = (IShellLinkW) new ShellLink(); - lnk.Path = App.AppFullPath; - lnk.Arguments = "/autorun"; // silent + lnk.SetPath(App.AppFullPath); + lnk.SetArguments("/autorun"); // silent lnk.SetIconLocation(App.AppFullPath, 0); - lnk.WorkingDirectory = App.AppPath; - - lnk.Save(StartupFullPath); + lnk.SetWorkingDirectory(App.AppPath); + ((IPersistFile) lnk).Save(StartupFullPath, false); } catch (Exception e) { @@ -57,7 +58,15 @@ namespace QuickLook.Helpers if (App.IsUWP) return; - File.Delete(StartupFullPath); + try + { + File.Delete(StartupFullPath); + } + catch (Exception e) + { + ProcessHelper.WriteLog(e.ToString()); + TrayIconManager.ShowNotification("", "Failed to delete QuickLook startup shortcut."); + } } internal static bool IsAutorun() diff --git a/QuickLook/Helpers/ShellLinkHelper.cs b/QuickLook/Helpers/ShellLinkHelper.cs deleted file mode 100644 index 44b5c82..0000000 --- a/QuickLook/Helpers/ShellLinkHelper.cs +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright © 2018 Paddy Xu -// -// This file is part of QuickLook program. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using Shell32; - -namespace QuickLook.Helpers -{ - public class ShellLinkHelper - { - public static ShellLinkObject OpenShellLink(string path) - { - if (Thread.CurrentThread.GetApartmentState() != ApartmentState.STA) - return StartSTATask(() => OpenShellLink(path)).Result; - - var shl = new Shell(); - var dir = shl.NameSpace(Path.GetDirectoryName(path)); - var itm = dir.Items().Item(Path.GetFileName(path)); - var lnk = (ShellLinkObject) itm.GetLink; - return lnk; - } - - public static string GetTarget(string path) - { - if (Path.GetExtension(path).ToLower() != ".lnk") - return path; - - try - { - return Thread.CurrentThread.GetApartmentState() != ApartmentState.STA - ? StartSTATask(() => GetTarget(path)).Result - : OpenShellLink(path).Target.Path; - } - catch (Exception) - { - // ignored - } - - return path; - } - - private static Task StartSTATask(Func func) - { - var tcs = new TaskCompletionSource(); - var thread = new Thread(() => - { - try - { - tcs.SetResult(func()); - } - catch (Exception e) - { - tcs.SetException(e); - } - }); - thread.SetApartmentState(ApartmentState.STA); - thread.Start(); - return tcs.Task; - } - } -} \ No newline at end of file diff --git a/QuickLook/NativeMethods/ShellLink.cs b/QuickLook/NativeMethods/ShellLink.cs new file mode 100644 index 0000000..0c906b0 --- /dev/null +++ b/QuickLook/NativeMethods/ShellLink.cs @@ -0,0 +1,169 @@ +// Copyright © 2018 Paddy Xu +// +// This file is part of QuickLook program. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using System; +using System.Runtime.InteropServices; +using System.Text; + +// ReSharper disable InconsistentNaming + +namespace QuickLook.NativeMethods +{ + [Flags] + internal enum SLGP_FLAGS + { + /// Retrieves the standard short (8.3 format) file name + SLGP_SHORTPATH = 0x1, + /// Retrieves the Universal Naming Convention (UNC) path name of the file + SLGP_UNCPRIORITY = 0x2, + /// + /// Retrieves the raw path name. A raw path is something that might not exist and may include environment + /// variables that need to be expanded + /// + SLGP_RAWPATH = 0x4 + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + internal struct WIN32_FIND_DATAW + { + public uint dwFileAttributes; + public long ftCreationTime; + public long ftLastAccessTime; + public long ftLastWriteTime; + public uint nFileSizeHigh; + public uint nFileSizeLow; + public uint dwReserved0; + public uint dwReserved1; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] + public string cFileName; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)] + public string cAlternateFileName; + } + + [Flags] + internal enum SLR_FLAGS + { + /// + /// Do not display a dialog box if the link cannot be resolved. When SLR_NO_UI is set, + /// the high-order word of fFlags can be set to a time-out value that specifies the + /// maximum amount of time to be spent resolving the link. The function returns if the + /// link cannot be resolved within the time-out duration. If the high-order word is set + /// to zero, the time-out duration will be set to the default value of 3,000 milliseconds + /// (3 seconds). To specify a value, set the high word of fFlags to the desired time-out + /// duration, in milliseconds. + /// + SLR_NO_UI = 0x1, + /// Obsolete and no longer used + SLR_ANY_MATCH = 0x2, + /// + /// 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, + /// 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, + /// + /// Disable distributed link tracking. By default, distributed link tracking tracks + /// removable media across multiple devices based on the volume name. It also uses the + /// Universal Naming Convention (UNC) path to track remote file systems whose drive letter + /// has changed. Setting SLR_NOLINKINFO disables both types of tracking. + /// + SLR_NOLINKINFO = 0x40, + /// Call the Microsoft Windows Installer + SLR_INVOKE_MSI = 0x80 + } + + + /// The IShellLink interface allows Shell links to be created, modified, and resolved + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("000214F9-0000-0000-C000-000000000046")] + internal interface IShellLinkW + { + /// Retrieves the path and file name of a Shell link object + void GetPath([Out] [MarshalAs(UnmanagedType.LPWStr)] + StringBuilder pszFile, int cchMaxPath, out WIN32_FIND_DATAW pfd, SLGP_FLAGS fFlags); + + /// Retrieves the list of item identifiers for a Shell link object + void GetIDList(out IntPtr ppidl); + + /// Sets the pointer to an item identifier list (PIDL) for a Shell link object. + void SetIDList(IntPtr pidl); + + /// Retrieves the description string for a Shell link object + void GetDescription([Out] [MarshalAs(UnmanagedType.LPWStr)] + StringBuilder pszName, int cchMaxName); + + /// Sets the description for a Shell link object. The description can be any application-defined string + void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName); + + /// Retrieves the name of the working directory for a Shell link object + void GetWorkingDirectory([Out] [MarshalAs(UnmanagedType.LPWStr)] + StringBuilder pszDir, int cchMaxPath); + + /// Sets the name of the working directory for a Shell link object + void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir); + + /// Retrieves the command-line arguments associated with a Shell link object + void GetArguments([Out] [MarshalAs(UnmanagedType.LPWStr)] + StringBuilder pszArgs, int cchMaxPath); + + /// Sets the command-line arguments for a Shell link object + void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs); + + /// Retrieves the hot key for a Shell link object + void GetHotkey(out short pwHotkey); + + /// Sets a hot key for a Shell link object + void SetHotkey(short wHotkey); + + /// Retrieves the show command for a Shell link object + void GetShowCmd(out int piShowCmd); + + /// Sets the show command for a Shell link object. The show command sets the initial show state of the window. + void SetShowCmd(int iShowCmd); + + /// Retrieves the location (path and index) of the icon for a Shell link object + void GetIconLocation([Out] [MarshalAs(UnmanagedType.LPWStr)] + StringBuilder pszIconPath, + int cchIconPath, out int piIcon); + + /// Sets the location (path and index) of the icon for a Shell link object + void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon); + + /// Sets the relative path to the Shell link object + void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, int dwReserved); + + /// Attempts to find the target of a Shell link, even if it has been moved or renamed + void Resolve(IntPtr hwnd, SLR_FLAGS fFlags); + + /// Sets the path and file name of a Shell link object + void SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile); + } + + // CLSID_ShellLink from ShlGuid.h + [ComImport] + [Guid("00021401-0000-0000-C000-000000000046")] + public class ShellLink + { + } +} \ No newline at end of file diff --git a/QuickLook/PipeServerManager.cs b/QuickLook/PipeServerManager.cs index caf9f68..9970b0e 100644 --- a/QuickLook/PipeServerManager.cs +++ b/QuickLook/PipeServerManager.cs @@ -19,11 +19,13 @@ using System; using System.Diagnostics; using System.IO; using System.IO.Pipes; +using System.Runtime.InteropServices.ComTypes; using System.Security.Principal; +using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Threading; -using QuickLook.Helpers; +using QuickLook.NativeMethods; namespace QuickLook { @@ -123,7 +125,8 @@ namespace QuickLook var wParam = msg.Substring(0, split); var lParam = msg.Substring(split + 1, msg.Length - split - 1); - lParam = ShellLinkHelper.GetTarget(lParam); + if (!string.IsNullOrEmpty(lParam)) + lParam = ResolveShortcut(lParam); switch (wParam) { @@ -168,5 +171,17 @@ namespace QuickLook { return _instance ?? (_instance = new PipeServerManager()); } + + public static string ResolveShortcut(string filename) + { + if (Path.GetExtension(filename).ToLower() != ".lnk") + return filename; + + var link = new ShellLink(); + ((IPersistFile) link).Load(filename, 0); + var sb = new StringBuilder(260); + ((IShellLinkW) link).GetPath(sb, sb.Capacity, out _, 0); + return sb.ToString(); + } } } \ No newline at end of file diff --git a/QuickLook/QuickLook.csproj b/QuickLook/QuickLook.csproj index 5497c67..7bbd124 100644 --- a/QuickLook/QuickLook.csproj +++ b/QuickLook/QuickLook.csproj @@ -143,7 +143,7 @@ - + @@ -245,17 +245,6 @@ Always - - - {50A7E9B0-70EF-11D1-B75A-00A0C90564FE} - 1 - 0 - 0 - tlbimp - False - True - - PreserveNewest