From 16f051bf823f0db37c642c992522b7ae5692bdbf Mon Sep 17 00:00:00 2001 From: "Lin Yu-Chieh (Victor)" Date: Sat, 25 Jan 2025 00:53:35 +0800 Subject: [PATCH] right click ContextMenu + cancellationToken --- .github/ISSUE_TEMPLATE/bug_report.md | 11 +- .github/workflows/packageManagers.yml | 18 +- ...ity.PowerToys.Run.Plugin.Everything.csproj | 19 +- .../ContextMenuLoader.cs | 612 +++---- ContextMenu/ShellContextMenu.cs | 1455 +++++++++++++++++ Debugger.cs | 16 - Everything.cs | 86 +- Everything64.dll | Bin 97800 -> 0 bytes EverythingARM64.dll | Bin 91696 -> 0 bytes Interop/NativeMethods.cs | 124 +- Main.cs | 88 +- NSIS/exeCreator.nsi | 4 +- Properties/Resources.Designer.cs | 40 +- Properties/Resources.it-it.resx | 2 +- Properties/Resources.resx | 14 +- Properties/Resources.zh-tw.resx | 18 +- SearchHelper/SearchResult.cs | 2 +- Settings.cs | 67 +- Update.cs => Update/UpdateChecker.cs | 52 +- Update/UpdateSettings.cs | 9 + plugin.json | 2 +- 21 files changed, 2098 insertions(+), 541 deletions(-) rename ContextMenuLoader.cs => ContextMenu/ContextMenuLoader.cs (79%) create mode 100644 ContextMenu/ShellContextMenu.cs delete mode 100644 Debugger.cs delete mode 100644 Everything64.dll delete mode 100644 EverythingARM64.dll rename Update.cs => Update/UpdateChecker.cs (72%) create mode 100644 Update/UpdateSettings.cs diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index fa59565..7920573 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -8,7 +8,8 @@ labels: bug **Flight Check** - [ ] I'm sure existing (open and closed) don't have duplicates -- [ ] I've tried reinstalling PT, Everything, EverythingPT +- [ ] I've updated PowerToys (PT), Everything, EverythingPT (EPT) to the lastest version +- [ ] I've tried reinstalling PT, Everything, EPT **Describe the bug** @@ -28,9 +29,11 @@ A clear and concise description of what you expected to happen. If applicable, add screenshots to help explain your problem. **Logs (please upload or provide link to you log):** - Logs can be generated by right clicking the taskbar icon > Report Bug -or be found at -%localappdata%\Microsoft\PowerToys\PowerToys Run\Logs + Logs can be found at `%localappdata%\Microsoft\PowerToys\PowerToys Run\Logs` + + > Note: + > If log says `Couldn't get assembly types`, you need to update PT and EPT + > If log says `Couldn't load assembly`, you're on the wrong version of EPT (x64 vs ARM64), most people should use x64 **Version:** - PowerToy: diff --git a/.github/workflows/packageManagers.yml b/.github/workflows/packageManagers.yml index 79076e3..f556e5b 100644 --- a/.github/workflows/packageManagers.yml +++ b/.github/workflows/packageManagers.yml @@ -45,15 +45,15 @@ jobs: choco push everythingpowertoys.$ver.nupkg --source https://push.chocolatey.org/ # winget - # $wingetPackage = "lin-ycv.EverythingPowerToys" - # echo $wingetPackage $ver $exe $exehash $exeARM $exeARMhash - # rm -Path .\* -Recurse -Force - # git clone -b winget https://github.com/lin-ycv/EverythingPowerToys.git --depth 1 - # Invoke-WebRequest https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe - # cd .\EverythingPowerToys - # rm .git -Recurse -Force -Confirm:$false - # Get-ChildItem *.* -Recurse | ForEach { (Get-Content $_) | ForEach {$_ -Replace '_VERSION_', $ver} | ForEach {$_ -Replace '_URL_', $exe} | ForEach {$_ -Replace '_CRC_', $exehash} | ForEach {$_ -Replace '_armURL_', $exeARM} | ForEach {$_ -Replace '_armCRC_', $exeARMhash} | Set-Content $_ } - # ..\wingetcreate submit -p "New version: lin-ycv.EverythingPowerToys version $ver" -t ${{ secrets.EVERYTHINGPT }} . + $wingetPackage = "lin-ycv.EverythingPowerToys" + echo $wingetPackage $ver $exe $exehash $exeARM $exeARMhash + rm -Path .\* -Recurse -Force + git clone -b winget https://github.com/lin-ycv/EverythingPowerToys.git --depth 1 + Invoke-WebRequest https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe + cd .\EverythingPowerToys + rm .git -Recurse -Force -Confirm:$false + Get-ChildItem *.* -Recurse | ForEach { (Get-Content $_) | ForEach {$_ -Replace '_VERSION_', $ver} | ForEach {$_ -Replace '_URL_', $exe} | ForEach {$_ -Replace '_CRC_', $exehash} | ForEach {$_ -Replace '_armURL_', $exeARM} | ForEach {$_ -Replace '_armCRC_', $exeARMhash} | Set-Content $_ } + ..\wingetcreate submit -p "New version: lin-ycv.EverythingPowerToys version $ver" -t ${{ secrets.EVERYTHINGPT }} . # scoop # cd .. diff --git a/Community.PowerToys.Run.Plugin.Everything.csproj b/Community.PowerToys.Run.Plugin.Everything.csproj index 76b20ac..f095614 100644 --- a/Community.PowerToys.Run.Plugin.Everything.csproj +++ b/Community.PowerToys.Run.Plugin.Everything.csproj @@ -28,7 +28,7 @@ - TRACE + bin\$(Platform)\$(Configuration)\Everything\ true none @@ -49,12 +49,19 @@ false + False + + + False + False false + False false + False @@ -88,11 +95,11 @@ - - PreserveNewest + + Never - - PreserveNewest + + Never PreserveNewest @@ -117,7 +124,7 @@ - + diff --git a/ContextMenuLoader.cs b/ContextMenu/ContextMenuLoader.cs similarity index 79% rename from ContextMenuLoader.cs rename to ContextMenu/ContextMenuLoader.cs index d820cd7..0656266 100644 --- a/ContextMenuLoader.cs +++ b/ContextMenu/ContextMenuLoader.cs @@ -1,278 +1,334 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.ComponentModel; -using System.Diagnostics; -using System.IO; -using System.Reflection; -using System.Security.AccessControl; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Input; -using Community.PowerToys.Run.Plugin.Everything.Properties; -using Wox.Infrastructure; -using Wox.Plugin; -using Wox.Plugin.Logger; - -namespace Community.PowerToys.Run.Plugin.Everything -{ - internal sealed class ContextMenuLoader(PluginInitContext context, string options) : IContextMenu - { - private readonly PluginInitContext _context = context; - - // Extensions for adding run as admin context menu item for applications - private readonly string[] _appExtensions = [".exe", ".bat", ".appref-ms", ".lnk"]; - - private bool _swapCopy; - private string _options = options; - private string _customProgram; - private string _customArg; - internal void Update(Settings s) - { - _swapCopy = s.Copy; - _options = s.Context; - _customProgram = s.CustomProgram; - _customArg = s.CustomArg; - } - - public List LoadContextMenus(Result selectedResult) - { - var contextMenus = new List(); - if (selectedResult.ContextData is SearchResult record) - { - bool isFile = record.File, runAs = CanFileBeRunAsAdmin(record.Path); - foreach (char o in _options) - { - switch (o) - { - case '0': - // Open folder - if (isFile) - { - contextMenus.Add(new ContextMenuResult - { - PluginName = Assembly.GetExecutingAssembly().GetName().Name, - Title = Resources.open_containing_folder, - Glyph = "\xE838", - FontFamily = "Segoe MDL2 Assets", - AcceleratorKey = Key.E, - AcceleratorModifiers = ModifierKeys.Control | ModifierKeys.Shift, - Action = _ => - { - if (!Helper.OpenInShell("explorer.exe", $"/select,\"{record.Path}\"")) - { - var message = $"{Resources.folder_open_failed} {Path.GetDirectoryName(record.Path)}"; - _context.API.ShowMsg(message); - return false; - } - - return true; - }, - }); - } - - break; - case '1': - // Run as Adsmin - if (runAs) - { - contextMenus.Add(new ContextMenuResult - { - PluginName = Assembly.GetExecutingAssembly().GetName().Name, - Title = Resources.run_as_admin, - Glyph = "\xE7EF", - FontFamily = "Segoe MDL2 Assets", - AcceleratorKey = Key.Enter, - AcceleratorModifiers = ModifierKeys.Control | ModifierKeys.Shift, - Action = _ => - { - try - { - Task.Run(() => Helper.RunAsAdmin(record.Path)); - return true; - } - catch (Exception e) - { - Log.Exception($"Failed to run {record.Path} as admin, {e.Message}", e, MethodBase.GetCurrentMethod().DeclaringType); - return false; - } - }, - }); - } - - break; - case '2': - // Run as User - if (runAs) - { - contextMenus.Add(new ContextMenuResult - { - PluginName = Assembly.GetExecutingAssembly().GetName().Name, - Title = Resources.run_as_user, - Glyph = "\xE7EE", - FontFamily = "Segoe MDL2 Assets", - AcceleratorKey = Key.U, - AcceleratorModifiers = ModifierKeys.Control | ModifierKeys.Shift, - Action = _ => - { - try - { - Task.Run(() => Helper.RunAsUser(record.Path)); - return true; - } - catch (Exception e) - { - Log.Exception($"Failed to run {record.Path} as different user, {e.Message}", e, MethodBase.GetCurrentMethod().DeclaringType); - return false; - } - }, - }); - } - - break; - case '3': - // Copy File/Folder - contextMenus.Add(new ContextMenuResult - { - PluginName = Assembly.GetExecutingAssembly().GetName().Name, - Title = Resources.copy_file + (_swapCopy ? Resources.copy_shortcut : Resources.copy_shortcutAlt), - Glyph = "\xE8C8", - FontFamily = "Segoe MDL2 Assets", - AcceleratorKey = Key.C, - AcceleratorModifiers = _swapCopy ? ModifierKeys.Control : ModifierKeys.Control | ModifierKeys.Alt, - - Action = (context) => - { - try - { - Clipboard.SetData(DataFormats.FileDrop, new string[] { record.Path }); - return true; - } - catch (Exception e) - { - var message = Resources.clipboard_failed; - Log.Exception(message, e, GetType()); - - _context.API.ShowMsg(message); - return false; - } - }, - }); - break; - case '4': - // Copy Path - contextMenus.Add(new ContextMenuResult - { - PluginName = Assembly.GetExecutingAssembly().GetName().Name, - Title = Resources.copy_path + (_swapCopy ? Resources.copy_shortcutAlt : Resources.copy_shortcut), - Glyph = "\xE71B", - FontFamily = "Segoe MDL2 Assets", - AcceleratorKey = Key.C, - AcceleratorModifiers = _swapCopy ? ModifierKeys.Control | ModifierKeys.Alt : ModifierKeys.Control, - - Action = (context) => - { - try - { - Clipboard.SetDataObject(record.Path); - return true; - } - catch (Exception e) - { - var message = Resources.clipboard_failed; - Log.Exception(message, e, GetType()); - - _context.API.ShowMsg(message); - return false; - } - }, - }); - break; - case '5': - // Open in Shell - contextMenus.Add(new ContextMenuResult - { - PluginName = Assembly.GetExecutingAssembly().GetName().Name, - Title = Resources.open_in_console, - Glyph = "\xE756", - FontFamily = "Segoe MDL2 Assets", - AcceleratorKey = Key.C, - AcceleratorModifiers = ModifierKeys.Control | ModifierKeys.Shift, - - Action = (context) => - { - try - { - if (isFile) - { - Helper.OpenInConsole(Path.GetDirectoryName(record.Path)); - } - else - { - Helper.OpenInConsole(record.Path); - } - - return true; - } - catch (Exception e) - { - Log.Exception($"Failed to open {record.Path} in console, {e.Message}", e, GetType()); - return false; - } - }, - }); - break; - case '6': - // Pass to custom program as parameter - contextMenus.Add(new ContextMenuResult - { - PluginName = Assembly.GetExecutingAssembly().GetName().Name, - Title = Resources.open_in_custom, - Glyph = "\xE8A7", - FontFamily = "Segoe MDL2 Assets", - AcceleratorKey = Key.N, - AcceleratorModifiers = ModifierKeys.Control, - - Action = (context) => - { - using var process = new Process(); - process.StartInfo.FileName = _customProgram; - process.StartInfo.Arguments = $"\"{_customArg.Replace("$P", record.Path)}\""; - try - { - process.Start(); - return true; - } - catch (Exception e) - { - Log.Exception($"Failed to execute {_customProgram} with arguments {_customArg}", e, GetType()); - return false; - } - }, - }); - break; - default: - break; - } - } - } - - return contextMenus; - } - - private bool CanFileBeRunAsAdmin(string path) - { - string fileExtension = Path.GetExtension(path); - foreach (string extension in _appExtensions) - { - // Using OrdinalIgnoreCase since this is internal - if (extension.Equals(fileExtension, StringComparison.OrdinalIgnoreCase)) - { - return true; - } - } - - return false; - } - } -} +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Input; +using Community.PowerToys.Run.Plugin.Everything.Properties; +using Community.PowerToys.Run.Plugin.Everything.SearchHelper; +using Wox.Infrastructure; +using Wox.Plugin; +using Wox.Plugin.Logger; +using wf = System.Windows.Forms; + +namespace Community.PowerToys.Run.Plugin.Everything.ContextMenu +{ + internal sealed class ContextMenuLoader(PluginInitContext context, string options) : IContextMenu + { + private readonly PluginInitContext _context = context; + + // Extensions for adding run as admin context menu item for applications + private readonly string[] _appExtensions = [".exe", ".bat", ".appref-ms", ".lnk"]; + + private bool _swapCopy; + private string _options = options; + private string _customProgram; + private string _customArg; + internal void Update(Settings s) + { + _swapCopy = s.Copy; + _options = s.Context; + _customProgram = s.CustomProgram; + _customArg = s.CustomArg; + } + + public List LoadContextMenus(Result selectedResult) + { + var contextMenus = new List(); + if (selectedResult.ContextData is SearchResult record) + { + bool isFile = record.File, runAs = CanFileBeRunAsAdmin(record.Path); + foreach (char o in _options) + { + switch (o) + { + case '0': + // Open folder + if (isFile) + { + contextMenus.Add(new ContextMenuResult + { + PluginName = Assembly.GetExecutingAssembly().GetName().Name, + Title = Resources.open_containing_folder, + Glyph = "\xE838", + FontFamily = "Segoe MDL2 Assets", + AcceleratorKey = Key.E, + AcceleratorModifiers = ModifierKeys.Control | ModifierKeys.Shift, + Action = _ => + { + if (!Helper.OpenInShell("explorer.exe", $"/select,\"{record.Path}\"")) + { + var message = $"{Resources.folder_open_failed} {Path.GetDirectoryName(record.Path)}"; + _context.API.ShowMsg(message); + return false; + } + + return true; + }, + }); + } + + break; + case '1': + // Run as Admin + if (runAs) + { + contextMenus.Add(new ContextMenuResult + { + PluginName = Assembly.GetExecutingAssembly().GetName().Name, + Title = Resources.run_as_admin, + Glyph = "\xE7EF", + FontFamily = "Segoe MDL2 Assets", + AcceleratorKey = Key.Enter, + AcceleratorModifiers = ModifierKeys.Control | ModifierKeys.Shift, + Action = _ => + { + try + { + Task.Run(() => Helper.RunAsAdmin(record.Path)); + return true; + } + catch (Exception e) + { + Log.Exception($"Failed to run {record.Path} as admin, {e.Message}", e, MethodBase.GetCurrentMethod().DeclaringType); + return false; + } + }, + }); + } + + break; + case '2': + // Run as User + if (runAs) + { + contextMenus.Add(new ContextMenuResult + { + PluginName = Assembly.GetExecutingAssembly().GetName().Name, + Title = Resources.run_as_user, + Glyph = "\xE7EE", + FontFamily = "Segoe MDL2 Assets", + AcceleratorKey = Key.U, + AcceleratorModifiers = ModifierKeys.Control | ModifierKeys.Shift, + Action = _ => + { + try + { + Task.Run(() => Helper.RunAsUser(record.Path)); + return true; + } + catch (Exception e) + { + Log.Exception($"Failed to run {record.Path} as different user, {e.Message}", e, MethodBase.GetCurrentMethod().DeclaringType); + return false; + } + }, + }); + } + + break; + case '3': + // Copy File/Folder + contextMenus.Add(new ContextMenuResult + { + PluginName = Assembly.GetExecutingAssembly().GetName().Name, + Title = Resources.copy_file + (_swapCopy ? Resources.copy_shortcut : Resources.copy_shortcutAlt), + Glyph = "\xE8C8", + FontFamily = "Segoe MDL2 Assets", + AcceleratorKey = Key.C, + AcceleratorModifiers = _swapCopy ? ModifierKeys.Control : ModifierKeys.Control | ModifierKeys.Alt, + + Action = (context) => + { + try + { + Clipboard.SetData(DataFormats.FileDrop, new string[] { record.Path }); + return true; + } + catch (Exception e) + { + var message = Resources.clipboard_failed; + Log.Exception(message, e, GetType()); + + _context.API.ShowMsg(message); + return false; + } + }, + }); + break; + case '4': + // Copy Path + contextMenus.Add(new ContextMenuResult + { + PluginName = Assembly.GetExecutingAssembly().GetName().Name, + Title = Resources.copy_path + (_swapCopy ? Resources.copy_shortcutAlt : Resources.copy_shortcut), + Glyph = "\xE71B", + FontFamily = "Segoe MDL2 Assets", + AcceleratorKey = Key.C, + AcceleratorModifiers = _swapCopy ? ModifierKeys.Control | ModifierKeys.Alt : ModifierKeys.Control, + + Action = (context) => + { + try + { + Clipboard.SetDataObject(record.Path); + return true; + } + catch (Exception e) + { + var message = Resources.clipboard_failed; + Log.Exception(message, e, GetType()); + + _context.API.ShowMsg(message); + return false; + } + }, + }); + break; + case '5': + // Open in Shell + contextMenus.Add(new ContextMenuResult + { + PluginName = Assembly.GetExecutingAssembly().GetName().Name, + Title = Resources.open_in_console, + Glyph = "\xE756", + FontFamily = "Segoe MDL2 Assets", + AcceleratorKey = Key.C, + AcceleratorModifiers = ModifierKeys.Control | ModifierKeys.Shift, + + Action = (context) => + { + try + { + if (isFile) + { + Helper.OpenInConsole(Path.GetDirectoryName(record.Path)); + } + else + { + Helper.OpenInConsole(record.Path); + } + + return true; + } + catch (Exception e) + { + Log.Exception($"Failed to open {record.Path} in console, {e.Message}", e, GetType()); + return false; + } + }, + }); + break; + case '6': + // Pass to custom program as parameter + contextMenus.Add(new ContextMenuResult + { + PluginName = Assembly.GetExecutingAssembly().GetName().Name, + Title = Resources.open_in_custom, + Glyph = "\xE8A7", + FontFamily = "Segoe MDL2 Assets", + AcceleratorKey = Key.N, + AcceleratorModifiers = ModifierKeys.Control, + + Action = (context) => + { + using var process = new Process(); + process.StartInfo.FileName = _customProgram; + process.StartInfo.Arguments = $"\"{_customArg.Replace("$P", record.Path)}\""; + try + { + process.Start(); + return true; + } + catch (Exception e) + { + Log.Exception($"Failed to execute {_customProgram} with arguments {_customArg}", e, GetType()); + return false; + } + }, + }); + break; + case '7': + // Delete + contextMenus.Add(new ContextMenuResult + { + PluginName = Assembly.GetExecutingAssembly().GetName().Name, + Title = Resources.delete_result, + Glyph = "\xE74D", + FontFamily = "Segoe MDL2 Assets", + AcceleratorKey = Key.Delete, + AcceleratorModifiers = ModifierKeys.Control, + Action = (context) => + { + try + { + if (isFile) + File.Delete(record.Path); + else + Directory.Delete(record.Path, true); + return true; + } + catch (Exception e) + { + Log.Exception($"Failed to delete {record.Path}", e, GetType()); + return false; + } + }, + }); + break; + case '8': + // Right Click Context Menu + contextMenus.Add(new ContextMenuResult + { + PluginName = Assembly.GetExecutingAssembly().GetName().Name, + Title = Resources.right_click, + Glyph = "\xE712", + FontFamily = "Segoe MDL2 Assets", + AcceleratorKey = Key.M, + AcceleratorModifiers = ModifierKeys.Control, + Action = (context) => + { + try + { + ShellContextMenu scm = new(); + if (isFile) + scm.ShowContextMenu(new FileInfo(record.Path), wf.Cursor.Position); + else + scm.ShowContextMenu(new DirectoryInfo(record.Path), wf.Cursor.Position); + return true; + } + catch (Exception e) + { + Log.Exception($"Failed to open right click context menu for {record.Path}", e, GetType()); + return false; + } + }, + }); + break; + default: + break; + } + } + } + + return contextMenus; + } + + private bool CanFileBeRunAsAdmin(string path) + { + string fileExtension = Path.GetExtension(path); + foreach (string extension in _appExtensions) + { + // Using OrdinalIgnoreCase since this is internal + if (extension.Equals(fileExtension, StringComparison.OrdinalIgnoreCase)) + { + return true; + } + } + + return false; + } + } +} diff --git a/ContextMenu/ShellContextMenu.cs b/ContextMenu/ShellContextMenu.cs new file mode 100644 index 0000000..c4853f2 --- /dev/null +++ b/ContextMenu/ShellContextMenu.cs @@ -0,0 +1,1455 @@ +using System; +using System.Drawing; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; +using System.Windows.Forms; + +#pragma warning disable CA1051 +#pragma warning disable CA1401 +#pragma warning disable SA1123 +#pragma warning disable SA1310 +#pragma warning disable SA1401 +#pragma warning disable SA1402 +#pragma warning disable SA1513 +#pragma warning disable SA1514 +#pragma warning disable SA1515 +#pragma warning disable SA1617 +#pragma warning disable SA1642 +#pragma warning disable SA1643 +namespace Community.PowerToys.Run.Plugin.Everything.ContextMenu +{ + /// + /// "Stand-alone" shell context menu + /// + /// It isn't really debugged but is mostly working. + /// Create an instance and call ShowContextMenu with a list of FileInfo for the files. + /// Limitation is that it only handles files in the same directory but it can be fixed + /// by changing the way files are translated into PIDLs. + /// + /// Based on FileBrowser in C# from CodeProject + /// http://www.codeproject.com/useritems/FileBrowser.asp + /// + /// Hooking class taken from MSDN Magazine Cutting Edge column + /// http://msdn.microsoft.com/msdnmag/issues/02/10/CuttingEdge/ + /// + /// Andreas Johansson + /// afjohansson@hotmail.com + /// http://afjohansson.spaces.live.com + /// + /// Revised by Victor Yu Chieh Lin for EverythingPowerToys + /// + /// + /// ShellContextMenu scm = new(); + /// FileInfo file = new FileInfo(@"c:\windows\notepad.exe"); + /// scm.ShowContextMenu(file, Cursor.Position); + /// + public class ShellContextMenu : NativeWindow + { + #region Constructor + /// Default constructor + public ShellContextMenu() + { + CreateHandle(new CreateParams()); + } + #endregion + + #region Destructor + /// Ensure all resources get released + ~ShellContextMenu() + { + ReleaseAll(); + } + #endregion + + #region GetContextMenuInterfaces() + /// Gets the interfaces to the context menu + /// Parent folder + /// PIDLs + /// true if it got the interfaces, otherwise false + private bool GetContextMenuInterfaces(IShellFolder oParentFolder, nint pild, out nint ctxMenuPtr) + { + var nResult = oParentFolder.GetUIObjectOf( + nint.Zero, + 1U, + [pild], + ref iID_IContextMenu, + nint.Zero, + out ctxMenuPtr); + + if (nResult == S_OK) + { + _oContextMenu = (IContextMenu)Marshal.GetTypedObjectForIUnknown(ctxMenuPtr, typeof(IContextMenu)); + + return true; + } + + ctxMenuPtr = nint.Zero; + _oContextMenu = null; + return false; + } + #endregion + + #region Override + + /// + /// This method receives WindowMessages. It will make the "Open With" and "Send To" work + /// by calling HandleMenuMsg and HandleMenuMsg2. It will also call the OnContextMenuMouseHover + /// method of Browser when hovering over a ContextMenu item. + /// + /// the Message of the Browser's WndProc + /// true if the message has been handled, false otherwise + protected override void WndProc(ref Message m) + { + #region IContextMenu2 + + if (_oContextMenu2 != null && + (m.Msg == (int)WM.INITMENUPOPUP || + m.Msg == (int)WM.MEASUREITEM || + m.Msg == (int)WM.DRAWITEM)) + { + if (_oContextMenu2.HandleMenuMsg( + (uint)m.Msg, m.WParam, m.LParam) == S_OK) + return; + } + + #endregion + + #region IContextMenu3 + + if (_oContextMenu3 != null && + m.Msg == (int)WM.MENUCHAR) + { + if (_oContextMenu3.HandleMenuMsg2( + (uint)m.Msg, m.WParam, m.LParam, nint.Zero) == S_OK) + return; + } + + #endregion + + base.WndProc(ref m); + } + + #endregion + + #region InvokeCommand + private void InvokeCommand(IContextMenu oContextMenu, uint nCmd, string strFolder, Point pointInvoke) + { + var invoke = new CMINVOKECOMMANDINFOEX + { + CbSize = CbInvokeCommand, + LpVerb = (nint)(nCmd - CMD_FIRST), + LpDirectory = strFolder, + LpVerbW = (nint)(nCmd - CMD_FIRST), + LpDirectoryW = strFolder, + FMask = CMIC.UNICODE | CMIC.PTINVOKE | + ((Control.ModifierKeys & Keys.Control) != 0 ? CMIC.CONTROL_DOWN : 0) | + ((Control.ModifierKeys & Keys.Shift) != 0 ? CMIC.SHIFT_DOWN : 0), + PtInvoke = new POINT(pointInvoke.X, pointInvoke.Y), + NShow = SW.SHOWNORMAL, + }; + + oContextMenu.InvokeCommand(ref invoke); + } + #endregion + + #region ReleaseAll() + /// + /// Release all allocated interfaces, PIDLs + /// + private void ReleaseAll() + { + if (_oContextMenu != null) + { + Marshal.ReleaseComObject(_oContextMenu); + _oContextMenu = null; + } + if (_oContextMenu2 != null) + { + Marshal.ReleaseComObject(_oContextMenu2); + _oContextMenu2 = null; + } + if (_oContextMenu3 != null) + { + Marshal.ReleaseComObject(_oContextMenu3); + _oContextMenu3 = null; + } + if (_oDesktopFolder != null) + { + Marshal.ReleaseComObject(_oDesktopFolder); + _oDesktopFolder = null; + } + if (_oParentFolder != null) + { + Marshal.ReleaseComObject(_oParentFolder); + _oParentFolder = null; + } + if (_pidl != nint.Zero) + { + FreePIDLs(_pidl); + _pidl = nint.Zero; + } + } + #endregion + + #region GetDesktopFolder() + /// + /// Gets the desktop folder + /// + /// IShellFolder for desktop folder + private IShellFolder GetDesktopFolder() + { + if (_oDesktopFolder == null) + { + // Get desktop IShellFolder + var nResult = SHGetDesktopFolder(out var pUnkownDesktopFolder); + if (nResult != S_OK) + { + throw new ShellContextMenuException("Failed to get the desktop shell folder"); + } + _oDesktopFolder = (IShellFolder)Marshal.GetTypedObjectForIUnknown(pUnkownDesktopFolder, typeof(IShellFolder)); + } + + return _oDesktopFolder; + } + #endregion + + #region GetParentFolder() + /// + /// Gets the parent folder + /// + /// Folder path + /// IShellFolder for the folder (relative from the desktop) + private IShellFolder GetParentFolder(string folderName) + { + if (_oParentFolder == null) + { + var oDesktopFolder = GetDesktopFolder(); + if (oDesktopFolder == null) + { + return null; + } + + // Get the PIDL for the folder file is in + uint pchEaten = 0; + SFGAO pdwAttributes = 0; + var nResult = oDesktopFolder.ParseDisplayName(nint.Zero, nint.Zero, folderName, ref pchEaten, out var pPIDL, ref pdwAttributes); + if (nResult != S_OK) + { + return null; + } + + var pStrRet = Marshal.AllocCoTaskMem((MAX_PATH * 2) + 4); + Marshal.WriteInt32(pStrRet, 0, 0); + _ = _oDesktopFolder.GetDisplayNameOf(pPIDL, SHGNO.FORPARSING, pStrRet); + var strFolder = new StringBuilder(MAX_PATH); + _ = StrRetToBuf(pStrRet, pPIDL, strFolder, MAX_PATH); + Marshal.FreeCoTaskMem(pStrRet); + _strParentFolder = strFolder.ToString(); + + // Get the IShellFolder for folder + nResult = oDesktopFolder.BindToObject(pPIDL, nint.Zero, ref iID_IShellFolder, out var pUnknownParentFolder); + // Free the PIDL first + Marshal.FreeCoTaskMem(pPIDL); + if (nResult != S_OK) + { + return null; + } + _oParentFolder = (IShellFolder)Marshal.GetTypedObjectForIUnknown(pUnknownParentFolder, typeof(IShellFolder)); + } + + return _oParentFolder; + } + #endregion + + #region GetPIDL() + /// + /// Get the PIDLs + /// + /// Array of FileInfo + /// Array of PIDLs + protected nint GetPIDL(FileInfo fi) + { + if (fi == null || !fi.Exists) + { + return nint.Zero; + } + + var oParentFolder = GetParentFolder(fi.DirectoryName); + if (oParentFolder == null) + { + return nint.Zero; + } + + nint pidl = nint.Zero; + // Get the file relative to folder + uint pchEaten = 0; + SFGAO pdwAttributes = 0; + var nResult = oParentFolder.ParseDisplayName(nint.Zero, nint.Zero, fi.Name, ref pchEaten, out nint ppidl, ref pdwAttributes); + if (nResult != S_OK) + { + FreePIDLs(pidl); + return nint.Zero; + } + pidl = ppidl; + + return pidl; + } + + /// + /// Get the PIDLs + /// + /// DirectoryInfo + /// PIDL + protected nint GetPIDL(DirectoryInfo di) + { + if (di == null || !di.Exists) + { + return nint.Zero; + } + + var oParentFolder = GetParentFolder(di.Parent.FullName); + if (oParentFolder == null) + { + return nint.Zero; + } + + nint pidl = nint.Zero; + + uint pchEaten = 0; + SFGAO pdwAttributes = 0; + var nResult = oParentFolder.ParseDisplayName(nint.Zero, nint.Zero, di.Name, ref pchEaten, out nint pPIDL, ref pdwAttributes); + if (nResult != S_OK) + { + FreePIDLs(pidl); + return nint.Zero; + } + pidl = pPIDL; + + return pidl; + } + #endregion + + #region FreePIDLs() + /// + /// Free the PIDLs + /// + /// PIDL (IntPtr) + protected void FreePIDLs(nint pidl) + { + if (pidl != nint.Zero) + { + Marshal.FreeCoTaskMem(pidl); + pidl = nint.Zero; + } + } + #endregion + + #region ShowContextMenu() + + /// + /// Shows the context menu + /// + /// FileInfos + /// Where to show the menu + public void ShowContextMenu(FileInfo file, Point pointScreen) + { + // Release all resources first. + ReleaseAll(); + _pidl = GetPIDL(file); + ShowContextMenu(pointScreen); + } + + /// + /// Shows the context menu + /// + /// DirectoryInfos (should all be in same directory) + /// Where to show the menu + public void ShowContextMenu(DirectoryInfo dir, Point pointScreen) + { + // Release all resources first. + ReleaseAll(); + _pidl = GetPIDL(dir); + ShowContextMenu(pointScreen); + } + + /// + /// Shows the context menu + /// + /// Where to show the menu + private void ShowContextMenu(Point pointScreen) + { + nint pMenu = nint.Zero, + iContextMenuPtr = nint.Zero, + iContextMenuPtr2 = nint.Zero, + iContextMenuPtr3 = nint.Zero; + + try + { + if (_pidl == nint.Zero) + { + ReleaseAll(); + return; + } + + if (!GetContextMenuInterfaces(_oParentFolder, _pidl, out iContextMenuPtr)) + { + ReleaseAll(); + return; + } + + pMenu = CreatePopupMenu(); + + _oContextMenu.QueryContextMenu( + pMenu, + 0, + CMD_FIRST, + CMD_LAST, + CMF.EXPLORE | CMF.NORMAL | CMF.EXTENDEDVERBS); + + Marshal.QueryInterface(iContextMenuPtr, in iID_IContextMenu2, out iContextMenuPtr2); + Marshal.QueryInterface(iContextMenuPtr, in iID_IContextMenu3, out iContextMenuPtr3); + + _oContextMenu2 = (IContextMenu2)Marshal.GetTypedObjectForIUnknown(iContextMenuPtr2, typeof(IContextMenu2)); + _oContextMenu3 = (IContextMenu3)Marshal.GetTypedObjectForIUnknown(iContextMenuPtr3, typeof(IContextMenu3)); + + var nSelected = TrackPopupMenuEx( + pMenu, + TPM.RETURNCMD, + pointScreen.X, + pointScreen.Y, + Handle, + nint.Zero); + + DestroyMenu(pMenu); + pMenu = nint.Zero; + + if (nSelected != 0) + { + InvokeCommand(_oContextMenu, nSelected, _strParentFolder, pointScreen); + } + } + finally + { + //hook.Uninstall(); + if (pMenu != nint.Zero) + { + DestroyMenu(pMenu); + } + + if (iContextMenuPtr != nint.Zero) + Marshal.Release(iContextMenuPtr); + + if (iContextMenuPtr2 != nint.Zero) + Marshal.Release(iContextMenuPtr2); + + if (iContextMenuPtr3 != nint.Zero) + Marshal.Release(iContextMenuPtr3); + + ReleaseAll(); + } + } + #endregion + + #region Local variabled + private IContextMenu _oContextMenu; + private IContextMenu2 _oContextMenu2; + private IContextMenu3 _oContextMenu3; + private IShellFolder _oDesktopFolder; + private IShellFolder _oParentFolder; + private nint _pidl; + private string _strParentFolder; + #endregion + + #region Variables and Constants + + private const int MAX_PATH = 260; + private const uint CMD_FIRST = 1; + private const uint CMD_LAST = 30000; + + private const int S_OK = 0; + private static readonly int CbMenuItemInfo = Marshal.SizeOf(); + private static readonly int CbInvokeCommand = Marshal.SizeOf(); + + #endregion + + #region DLL Import + + // Retrieves the IShellFolder interface for the desktop folder, which is the root of the Shell's namespace. + [DllImport("shell32.dll")] + private static extern int SHGetDesktopFolder(out nint ppshf); + + // Takes a STRRET structure returned by IShellFolder::GetDisplayNameOf, converts it to a string, and places the result in a buffer. + [DllImport("shlwapi.dll", EntryPoint = "StrRetToBuf", ExactSpelling = false, CharSet = CharSet.Unicode, SetLastError = true)] + private static extern int StrRetToBuf(nint pstr, nint pidl, StringBuilder pszBuf, int cchBuf); + + // The TrackPopupMenuEx function displays a shortcut menu at the specified location and tracks the selection of items on the shortcut menu. The shortcut menu can appear anywhere on the screen. + [DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto)] + private static extern uint TrackPopupMenuEx(nint hmenu, TPM flags, int x, int y, nint hwnd, nint lptpm); + + // The CreatePopupMenu function creates a drop-down menu, submenu, or shortcut menu. The menu is initially empty. You can insert or append menu items by using the InsertMenuItem function. You can also use the InsertMenu function to insert menu items and the AppendMenu function to append menu items. + [DllImport("user32", SetLastError = true, CharSet = CharSet.Auto)] + private static extern nint CreatePopupMenu(); + + // The DestroyMenu function destroys the specified menu and frees any memory that the menu occupies. + [DllImport("user32", SetLastError = true, CharSet = CharSet.Auto)] + private static extern bool DestroyMenu(nint hMenu); + + #endregion + + #region Shell GUIDs + + private static Guid iID_IShellFolder = new("{000214E6-0000-0000-C000-000000000046}"); + private static Guid iID_IContextMenu = new("{000214e4-0000-0000-c000-000000000046}"); + private static Guid iID_IContextMenu2 = new("{000214f4-0000-0000-c000-000000000046}"); + private static Guid iID_IContextMenu3 = new("{bcfce0a0-ec17-11d0-8d10-00a0c90f2719}"); + + #endregion + + #region Structs + + [StructLayout(LayoutKind.Sequential)] + private struct CWPSTRUCT + { + public nint Lparam; + public nint Wparam; + public int Message; + public nint Hwnd; + } + + // Contains extended information about a shortcut menu command + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + private struct CMINVOKECOMMANDINFOEX + { + public int CbSize; + public CMIC FMask; + public nint Hwnd; + public nint LpVerb; + [MarshalAs(UnmanagedType.LPStr)] + public string LpParameters; + [MarshalAs(UnmanagedType.LPStr)] + public string LpDirectory; + public SW NShow; + public int DwHotKey; + public nint HIcon; + [MarshalAs(UnmanagedType.LPStr)] + public string LpTitle; + public nint LpVerbW; + [MarshalAs(UnmanagedType.LPWStr)] + public string LpParametersW; + [MarshalAs(UnmanagedType.LPWStr)] + public string LpDirectoryW; + [MarshalAs(UnmanagedType.LPWStr)] + public string LpTitleW; + public POINT PtInvoke; + } + + // Contains information about a menu item + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + private struct MENUITEMINFO + { + public MENUITEMINFO(string text) + { + CbSize = CbMenuItemInfo; + DwTypeData = text; + Cch = text.Length; + FMask = 0; + FType = 0; + FState = 0; + WID = 0; + HSubMenu = nint.Zero; + HbmpChecked = nint.Zero; + HbmpUnchecked = nint.Zero; + DwItemData = nint.Zero; + HbmpItem = nint.Zero; + } + + public int CbSize; + public MIIM FMask; + public MFT FType; + public MFS FState; + public uint WID; + public nint HSubMenu; + public nint HbmpChecked; + public nint HbmpUnchecked; + public nint DwItemData; + [MarshalAs(UnmanagedType.LPTStr)] + public string DwTypeData; + public int Cch; + public nint HbmpItem; + } + + // A generalized global memory handle used for data transfer operations by the + // IAdviseSink, IDataObject, and IOleCache interfaces + [StructLayout(LayoutKind.Sequential)] + private struct STGMEDIUM + { + public TYMED Tymed; + public nint HBitmap; + public nint HMetaFilePict; + public nint HEnhMetaFile; + public nint HGlobal; + public nint LpszFileName; + public nint Pstm; + public nint Pstg; + public nint PUnkForRelease; + } + + // Defines the x- and y-coordinates of a point + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + private struct POINT + { + public POINT(int x, int y) + { + X = x; + Y = y; + } + + public int X; + public int Y; + } + + #endregion + + #region Enums + + // Defines the values used with the IShellFolder::GetDisplayNameOf and IShellFolder::SetNameOf + // methods to specify the type of file or folder names used by those methods + [Flags] + private enum SHGNO + { + NORMAL = 0x0000, + INFOLDER = 0x0001, + FOREDITING = 0x1000, + FORADDRESSBAR = 0x4000, + FORPARSING = 0x8000, + } + + // The attributes that the caller is requesting, when calling IShellFolder::GetAttributesOf + [Flags] + private enum SFGAO : uint + { + BROWSABLE = 0x8000000, + CANCOPY = 1, + CANDELETE = 0x20, + CANLINK = 4, + CANMONIKER = 0x400000, + CANMOVE = 2, + CANRENAME = 0x10, + CAPABILITYMASK = 0x177, + COMPRESSED = 0x4000000, + CONTENTSMASK = 0x80000000, + DISPLAYATTRMASK = 0xfc000, + DROPTARGET = 0x100, + ENCRYPTED = 0x2000, + FILESYSANCESTOR = 0x10000000, + FILESYSTEM = 0x40000000, + FOLDER = 0x20000000, + GHOSTED = 0x8000, + HASPROPSHEET = 0x40, + HASSTORAGE = CANMONIKER, + HASSUBFOLDER = CONTENTSMASK, + HIDDEN = 0x80000, + ISSLOW = 0x4000, + LINK = 0x10000, + NEWCONTENT = 0x200000, + NONENUMERATED = 0x100000, + READONLY = 0x40000, + REMOVABLE = 0x2000000, + SHARE = 0x20000, + STORAGE = 8, + STORAGEANCESTOR = 0x800000, + STORAGECAPMASK = 0x70c50008, + STREAM = CANMONIKER, + VALIDATE = 0x1000000, + } + + // Determines the type of items included in an enumeration. + // These values are used with the IShellFolder::EnumObjects method + [Flags] + private enum SHCONTF + { + FOLDERS = 0x0020, + NONFOLDERS = 0x0040, + INCLUDEHIDDEN = 0x0080, + INIT_ON_FIRST_NEXT = 0x0100, + NETPRINTERSRCH = 0x0200, + SHAREABLE = 0x0400, + STORAGE = 0x0800, + } + + // Specifies how the shortcut menu can be changed when calling IContextMenu::QueryContextMenu + [Flags] + private enum CMF : uint + { + NORMAL = 0x00000000, + DEFAULTONLY = 0x00000001, + VERBSONLY = 0x00000002, + EXPLORE = 0x00000004, + NOVERBS = 0x00000008, + CANRENAME = 0x00000010, + NODEFAULT = 0x00000020, + INCLUDESTATIC = 0x00000040, + EXTENDEDVERBS = 0x00000100, + RESERVED = 0xffff0000, + } + + // Flags specifying the information to return when calling IContextMenu::GetCommandString + [Flags] + private enum GCS : uint + { + VERBA = 0, + HELPTEXTA = 1, + VALIDATEA = 2, + VERBW = 4, + HELPTEXTW = 5, + VALIDATEW = 6, + } + + // Specifies how TrackPopupMenuEx positions the shortcut menu horizontally + [Flags] + private enum TPM : uint + { + LEFTBUTTON = 0x0000, + RIGHTBUTTON = 0x0002, + LEFTALIGN = LEFTBUTTON, + CENTERALIGN = 0x0004, + RIGHTALIGN = 0x0008, + TOPALIGN = LEFTBUTTON, + VCENTERALIGN = 0x0010, + BOTTOMALIGN = 0x0020, + HORIZONTAL = LEFTBUTTON, + VERTICAL = 0x0040, + NONOTIFY = 0x0080, + RETURNCMD = 0x0100, + RECURSE = 0x0001, + HORPOSANIMATION = 0x0400, + HORNEGANIMATION = 0x0800, + VERPOSANIMATION = 0x1000, + VERNEGANIMATION = 0x2000, + NOANIMATION = 0x4000, + LAYOUTRTL = 0x8000, + } + + // The cmd for a custom added menu item + private enum CMD_CUSTOM + { + ExpandCollapse = (int)CMD_LAST + 1, + } + + // Flags used with the CMINVOKECOMMANDINFOEX structure + [Flags] + private enum CMIC : uint + { + HOTKEY = 0x00000020, + ICON = 0x00000010, + FLAG_NO_UI = 0x00000400, + UNICODE = 0x00004000, + NO_CONSOLE = 0x00008000, + ASYNCOK = 0x00100000, + NOZONECHECKS = 0x00800000, + SHIFT_DOWN = 0x10000000, + CONTROL_DOWN = 0x40000000, + FLAG_LOG_USAGE = 0x04000000, + PTINVOKE = 0x20000000, + } + + // Specifies how the window is to be shown + [Flags] + private enum SW + { + HIDE = 0, + SHOWNORMAL = 1, + NORMAL = SHOWNORMAL, + SHOWMINIMIZED = 2, + SHOWMAXIMIZED = 3, + MAXIMIZE = SHOWMAXIMIZED, + SHOWNOACTIVATE = 4, + SHOW = 5, + MINIMIZE = 6, + SHOWMINNOACTIVE = 7, + SHOWNA = 8, + RESTORE = 9, + SHOWDEFAULT = 10, + } + + // Window message flags + [Flags] + private enum WM : uint + { + ACTIVATE = 0x6, + ACTIVATEAPP = 0x1C, + AFXFIRST = 0x360, + AFXLAST = 0x37F, + APP = 0x8000, + ASKCBFORMATNAME = 0x30C, + CANCELJOURNAL = 0x4B, + CANCELMODE = 0x1F, + CAPTURECHANGED = 0x215, + CHANGECBCHAIN = 0x30D, + CHAR = 0x102, + CHARTOITEM = 0x2F, + CHILDACTIVATE = 0x22, + CLEAR = 0x303, + CLOSE = 0x10, + COMMAND = 0x111, + COMPACTING = 0x41, + COMPAREITEM = 0x39, + CONTEXTMENU = 0x7B, + COPY = 0x301, + COPYDATA = 0x4A, + CREATE = 0x1, + CTLCOLORBTN = 0x135, + CTLCOLORDLG = 0x136, + CTLCOLOREDIT = 0x133, + CTLCOLORLISTBOX = 0x134, + CTLCOLORMSGBOX = 0x132, + CTLCOLORSCROLLBAR = 0x137, + CTLCOLORSTATIC = 0x138, + CUT = 0x300, + DEADCHAR = 0x103, + DELETEITEM = 0x2D, + DESTROY = 0x2, + DESTROYCLIPBOARD = 0x307, + DEVICECHANGE = 0x219, + DEVMODECHANGE = 0x1B, + DISPLAYCHANGE = 0x7E, + DRAWCLIPBOARD = 0x308, + DRAWITEM = 0x2B, + DROPFILES = 0x233, + ENABLE = 0xA, + ENDSESSION = 0x16, + ENTERIDLE = 0x121, + ENTERMENULOOP = 0x211, + ENTERSIZEMOVE = 0x231, + ERASEBKGND = 0x14, + EXITMENULOOP = 0x212, + EXITSIZEMOVE = 0x232, + FONTCHANGE = 0x1D, + GETDLGCODE = 0x87, + GETFONT = 0x31, + GETHOTKEY = 0x33, + GETICON = 0x7F, + GETMINMAXINFO = 0x24, + GETOBJECT = 0x3D, + GETSYSMENU = 0x313, + GETTEXT = 0xD, + GETTEXTLENGTH = 0xE, + HANDHELDFIRST = 0x358, + HANDHELDLAST = 0x35F, + HELP = 0x53, + HOTKEY = 0x312, + HSCROLL = 0x114, + HSCROLLCLIPBOARD = 0x30E, + ICONERASEBKGND = 0x27, + IME_CHAR = 0x286, + IME_COMPOSITION = 0x10F, + IME_COMPOSITIONFULL = 0x284, + IME_CONTROL = 0x283, + IME_ENDCOMPOSITION = 0x10E, + IME_KEYDOWN = 0x290, + IME_KEYLAST = IME_COMPOSITION, + IME_KEYUP = 0x291, + IME_NOTIFY = 0x282, + IME_REQUEST = 0x288, + IME_SELECT = 0x285, + IME_SETCONTEXT = 0x281, + IME_STARTCOMPOSITION = 0x10D, + INITDIALOG = 0x110, + INITMENU = 0x116, + INITMENUPOPUP = 0x117, + INPUTLANGCHANGE = 0x51, + INPUTLANGCHANGEREQUEST = 0x50, + KEYDOWN = 0x100, + KEYFIRST = KEYDOWN, + KEYLAST = 0x108, + KEYUP = 0x101, + KILLFOCUS = 0x8, + LBUTTONDBLCLK = 0x203, + LBUTTONDOWN = 0x201, + LBUTTONUP = 0x202, + LVM_GETEDITCONTROL = 0x1018, + LVM_SETIMAGELIST = 0x1003, + MBUTTONDBLCLK = 0x209, + MBUTTONDOWN = 0x207, + MBUTTONUP = 0x208, + MDIACTIVATE = 0x222, + MDICASCADE = 0x227, + MDICREATE = 0x220, + MDIDESTROY = 0x221, + MDIGETACTIVE = 0x229, + MDIICONARRANGE = 0x228, + MDIMAXIMIZE = 0x225, + MDINEXT = 0x224, + MDIREFRESHMENU = 0x234, + MDIRESTORE = 0x223, + MDISETMENU = 0x230, + MDITILE = 0x226, + MEASUREITEM = 0x2C, + MENUCHAR = 0x120, + MENUCOMMAND = 0x126, + MENUDRAG = 0x123, + MENUGETOBJECT = 0x124, + MENURBUTTONUP = 0x122, + MENUSELECT = 0x11F, + MOUSEACTIVATE = 0x21, + MOUSEFIRST = 0x200, + MOUSEHOVER = 0x2A1, + MOUSELAST = 0x20A, + MOUSELEAVE = 0x2A3, + MOUSEMOVE = MOUSEFIRST, + MOUSEWHEEL = MOUSELAST, + MOVE = 0x3, + MOVING = 0x216, + NCACTIVATE = 0x86, + NCCALCSIZE = 0x83, + NCCREATE = 0x81, + NCDESTROY = 0x82, + NCHITTEST = 0x84, + NCLBUTTONDBLCLK = 0xA3, + NCLBUTTONDOWN = 0xA1, + NCLBUTTONUP = 0xA2, + NCMBUTTONDBLCLK = 0xA9, + NCMBUTTONDOWN = 0xA7, + NCMBUTTONUP = 0xA8, + NCMOUSEHOVER = 0x2A0, + NCMOUSELEAVE = 0x2A2, + NCMOUSEMOVE = 0xA0, + NCPAINT = 0x85, + NCRBUTTONDBLCLK = 0xA6, + NCRBUTTONDOWN = 0xA4, + NCRBUTTONUP = 0xA5, + NEXTDLGCTL = 0x28, + NEXTMENU = 0x213, + NOTIFY = 0x4E, + NOTIFYFORMAT = 0x55, + NULL = 0x0, + PAINT = 0xF, + PAINTCLIPBOARD = 0x309, + PAINTICON = 0x26, + PALETTECHANGED = 0x311, + PALETTEISCHANGING = 0x310, + PARENTNOTIFY = 0x210, + PASTE = 0x302, + PENWINFIRST = 0x380, + PENWINLAST = 0x38F, + POWER = 0x48, + PRINT = 0x317, + PRINTCLIENT = 0x318, + QUERYDRAGICON = 0x37, + QUERYENDSESSION = 0x11, + QUERYNEWPALETTE = 0x30F, + QUERYOPEN = 0x13, + QUEUESYNC = 0x23, + QUIT = 0x12, + RBUTTONDBLCLK = 0x206, + RBUTTONDOWN = 0x204, + RBUTTONUP = 0x205, + RENDERALLFORMATS = 0x306, + RENDERFORMAT = 0x305, + SETCURSOR = 0x20, + SETFOCUS = 0x7, + SETFONT = 0x30, + SETHOTKEY = 0x32, + SETICON = 0x80, + SETMARGINS = 0xD3, + SETREDRAW = 0xB, + SETTEXT = 0xC, + SETTINGCHANGE = 0x1A, + SHOWWINDOW = 0x18, + SIZE = 0x5, + SIZECLIPBOARD = 0x30B, + SIZING = 0x214, + SPOOLERSTATUS = 0x2A, + STYLECHANGED = 0x7D, + STYLECHANGING = 0x7C, + SYNCPAINT = 0x88, + SYSCHAR = 0x106, + SYSCOLORCHANGE = 0x15, + SYSCOMMAND = 0x112, + SYSDEADCHAR = 0x107, + SYSKEYDOWN = 0x104, + SYSKEYUP = 0x105, + TCARD = 0x52, + TIMECHANGE = 0x1E, + TIMER = 0x113, + TVM_GETEDITCONTROL = 0x110F, + TVM_SETIMAGELIST = 0x1109, + UNDO = 0x304, + UNINITMENUPOPUP = 0x125, + USER = 0x400, + USERCHANGED = 0x54, + VKEYTOITEM = 0x2E, + VSCROLL = 0x115, + VSCROLLCLIPBOARD = 0x30A, + WINDOWPOSCHANGED = 0x47, + WINDOWPOSCHANGING = 0x46, + WININICHANGE = SETTINGCHANGE, + SH_NOTIFY = 0x0401, + } + + // Specifies the content of the new menu item + [Flags] + private enum MFT : uint + { + GRAYED = 0x00000003, + DISABLED = GRAYED, + CHECKED = 0x00000008, + SEPARATOR = 0x00000800, + RADIOCHECK = 0x00000200, + BITMAP = 0x00000004, + OWNERDRAW = 0x00000100, + MENUBARBREAK = 0x00000020, + MENUBREAK = 0x00000040, + RIGHTORDER = 0x00002000, + BYCOMMAND = 0x00000000, + BYPOSITION = 0x00000400, + POPUP = 0x00000010, + } + + // Specifies the state of the new menu item + [Flags] + private enum MFS : uint + { + GRAYED = 0x00000003, + DISABLED = GRAYED, + CHECKED = 0x00000008, + HILITE = 0x00000080, + ENABLED = 0x00000000, + UNCHECKED = ENABLED, + UNHILITE = ENABLED, + DEFAULT = 0x00001000, + } + + // Specifies the content of the new menu item + [Flags] + private enum MIIM : uint + { + BITMAP = 0x80, + CHECKMARKS = 0x08, + DATA = 0x20, + FTYPE = 0x100, + ID = 0x02, + STATE = 0x01, + STRING = 0x40, + SUBMENU = 0x04, + TYPE = 0x10, + } + + // Indicates the type of storage medium being used in a data transfer + [Flags] + private enum TYMED + { + ENHMF = 0x40, + FILE = 2, + GDI = 0x10, + HGLOBAL = 1, + ISTORAGE = 8, + ISTREAM = 4, + MFPICT = 0x20, + NULL = 0, + } + + #endregion + + #region IShellFolder + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("000214E6-0000-0000-C000-000000000046")] + private interface IShellFolder + { + // Translates a file object's or folder's display name into an item identifier list. + // Return value: error code, if any + [PreserveSig] + int ParseDisplayName( + nint hwnd, + nint pbc, + [MarshalAs(UnmanagedType.LPWStr)] + string pszDisplayName, + ref uint pchEaten, + out nint ppidl, + ref SFGAO pdwAttributes); + + // Allows a client to determine the contents of a folder by creating an item + // identifier enumeration object and returning its IEnumIDList interface. + // Return value: error code, if any + [PreserveSig] + int EnumObjects( + nint hwnd, + SHCONTF grfFlags, + out nint enumIDList); + + // Retrieves an IShellFolder object for a subfolder. + // Return value: error code, if any + [PreserveSig] + int BindToObject( + nint pidl, + nint pbc, + ref Guid riid, + out nint ppv); + + // Requests a pointer to an object's storage interface. + // Return value: error code, if any + [PreserveSig] + int BindToStorage( + nint pidl, + nint pbc, + ref Guid riid, + out nint ppv); + + // Determines the relative order of two file objects or folders, given their + // item identifier lists. Return value: If this method is successful, the + // CODE field of the HRESULT contains one of the following values (the code + // can be retrived using the helper function GetHResultCode): Negative A + // negative return value indicates that the first item should precede + // the second (pidl1 < pidl2). + + // Positive A positive return value indicates that the first item should + // follow the second (pidl1 > pidl2). Zero A return value of zero + // indicates that the two items are the same (pidl1 = pidl2). + [PreserveSig] + int CompareIDs( + nint lParam, + nint pidl1, + nint pidl2); + + // Requests an object that can be used to obtain information from or interact + // with a folder object. + // Return value: error code, if any + [PreserveSig] + int CreateViewObject( + nint hwndOwner, + Guid riid, + out nint ppv); + + // Retrieves the attributes of one or more file objects or subfolders. + // Return value: error code, if any + [PreserveSig] + int GetAttributesOf( + uint cidl, + [MarshalAs(UnmanagedType.LPArray)] + nint[] apidl, + ref SFGAO rgfInOut); + + // Retrieves an OLE interface that can be used to carry out actions on the + // specified file objects or folders. + // Return value: error code, if any + [PreserveSig] + int GetUIObjectOf( + nint hwndOwner, + uint cidl, + [MarshalAs(UnmanagedType.LPArray)] + nint[] apidl, + ref Guid riid, + nint rgfReserved, + out nint ppv); + + // Retrieves the display name for the specified file object or subfolder. + // Return value: error code, if any + [PreserveSig] + int GetDisplayNameOf( + nint pidl, + SHGNO uFlags, + nint lpName); + + // Sets the display name of a file object or subfolder, changing the item + // identifier in the process. + // Return value: error code, if any + [PreserveSig] + int SetNameOf( + nint hwnd, + nint pidl, + [MarshalAs(UnmanagedType.LPWStr)] + string pszName, + SHGNO uFlags, + out nint ppidlOut); + } + #endregion + + #region IContextMenu + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("000214e4-0000-0000-c000-000000000046")] + private interface IContextMenu + { + // Adds commands to a shortcut menu + [PreserveSig] + int QueryContextMenu( + nint hmenu, + uint iMenu, + uint idCmdFirst, + uint idCmdLast, + CMF uFlags); + + // Carries out the command associated with a shortcut menu item + [PreserveSig] + int InvokeCommand( + ref CMINVOKECOMMANDINFOEX info); + + // Retrieves information about a shortcut menu command, + // including the help string and the language-independent, + // or canonical, name for the command + [PreserveSig] + int GetCommandString( + uint idcmd, + GCS uflags, + uint reserved, + [MarshalAs(UnmanagedType.LPArray)] + byte[] commandstring, + int cch); + } + + [ComImport] + [Guid("000214f4-0000-0000-c000-000000000046")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + private interface IContextMenu2 + { + // Adds commands to a shortcut menu + [PreserveSig] + int QueryContextMenu( + nint hmenu, + uint iMenu, + uint idCmdFirst, + uint idCmdLast, + CMF uFlags); + + // Carries out the command associated with a shortcut menu item + [PreserveSig] + int InvokeCommand( + ref CMINVOKECOMMANDINFOEX info); + + // Retrieves information about a shortcut menu command, + // including the help string and the language-independent, + // or canonical, name for the command + [PreserveSig] + int GetCommandString( + uint idcmd, + GCS uflags, + uint reserved, + [MarshalAs(UnmanagedType.LPWStr)] + StringBuilder commandstring, + int cch); + + // Allows client objects of the IContextMenu interface to + // handle messages associated with owner-drawn menu items + [PreserveSig] + int HandleMenuMsg( + uint uMsg, + nint wParam, + nint lParam); + } + + [ComImport] + [Guid("bcfce0a0-ec17-11d0-8d10-00a0c90f2719")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + private interface IContextMenu3 + { + // Adds commands to a shortcut menu + [PreserveSig] + int QueryContextMenu( + nint hmenu, + uint iMenu, + uint idCmdFirst, + uint idCmdLast, + CMF uFlags); + + // Carries out the command associated with a shortcut menu item + [PreserveSig] + int InvokeCommand( + ref CMINVOKECOMMANDINFOEX info); + + // Retrieves information about a shortcut menu command, + // including the help string and the language-independent, + // or canonical, name for the command + [PreserveSig] + int GetCommandString( + uint idcmd, + GCS uflags, + uint reserved, + [MarshalAs(UnmanagedType.LPWStr)] + StringBuilder commandstring, + int cch); + + // Allows client objects of the IContextMenu interface to + // handle messages associated with owner-drawn menu items + [PreserveSig] + int HandleMenuMsg( + uint uMsg, + nint wParam, + nint lParam); + + // Allows client objects of the IContextMenu3 interface to + // handle messages associated with owner-drawn menu items + [PreserveSig] + int HandleMenuMsg2( + uint uMsg, + nint wParam, + nint lParam, + nint plResult); + } + #endregion + } + + #region ShellContextMenuException + public class ShellContextMenuException : Exception + { + /// Default contructor + public ShellContextMenuException() + { + } + + /// Constructor with message + /// Message + public ShellContextMenuException(string message) + : base(message) + { + } + } + #endregion + + #region Class HookEventArgs + public class HookEventArgs : EventArgs + { + public int HookCode; // Hook code + public nint WParam; // WPARAM argument + public nint LParam; // LPARAM argument + } + #endregion + + #region Enum HookType + // Hook Types + public enum HookType + { + WH_JOURNALRECORD = 0, + WH_JOURNALPLAYBACK = 1, + WH_KEYBOARD = 2, + WH_GETMESSAGE = 3, + WH_CALLWNDPROC = 4, + WH_CBT = 5, + WH_SYSMSGFILTER = 6, + WH_MOUSE = 7, + WH_HARDWARE = 8, + WH_DEBUG = 9, + WH_SHELL = 10, + WH_FOREGROUNDIDLE = 11, + WH_CALLWNDPROCRET = 12, + WH_KEYBOARD_LL = 13, + WH_MOUSE_LL = 14, + } + #endregion + + #region Class LocalWindowsHook + public class LocalWindowsHook + { + // ************************************************************************ + // Filter function delegate + public delegate int HookProc(int code, nint wParam, nint lParam); + // ************************************************************************ + + // ************************************************************************ + // Internal properties + protected nint hhook = nint.Zero; + protected HookProc filterFunc; + protected HookType hookType; + // ************************************************************************ + + // ************************************************************************ + // Event delegate + public delegate void HookEventHandler(object sender, HookEventArgs e); + // ************************************************************************ + + // ************************************************************************ + // Event: HookInvoked + public event HookEventHandler HookInvoked; + protected void OnHookInvoked(HookEventArgs e) + { + HookInvoked?.Invoke(this, e); + } + // ************************************************************************ + + // ************************************************************************ + // Class constructor(s) + public +LocalWindowsHook(HookType hook) + { + hookType = hook; + filterFunc = CoreHookProc; + } + public LocalWindowsHook(HookType hook, HookProc func) + { + hookType = hook; + filterFunc = func; + } + // ************************************************************************ + + // ************************************************************************ + // Default filter function + protected int CoreHookProc(int code, nint wParam, nint lParam) + { + if (code < 0) + return CallNextHookEx(hhook, code, wParam, lParam); + + // Let clients determine what to do + var e = new HookEventArgs + { + HookCode = code, + WParam = wParam, + LParam = lParam, + }; + OnHookInvoked(e); + + // Yield to the next hook in the chain + return CallNextHookEx(hhook, code, wParam, lParam); + } + // ************************************************************************ + + // ************************************************************************ + // Install the hook + public void Install() + { + hhook = SetWindowsHookEx( + hookType, + filterFunc, + nint.Zero, + Environment.CurrentManagedThreadId); + } + // ************************************************************************ + + // ************************************************************************ + // Uninstall the hook + public void Uninstall() + { + _ = UnhookWindowsHookEx(hhook); + } + // ************************************************************************ + + #region Win32 Imports + // ************************************************************************ + // Win32: SetWindowsHookEx() + [DllImport("user32.dll")] + protected static extern nint SetWindowsHookEx( + HookType code, + HookProc func, + nint hInstance, + int threadID); + // ************************************************************************ + + // ************************************************************************ + // Win32: UnhookWindowsHookEx() + [DllImport("user32.dll")] + protected static extern int UnhookWindowsHookEx(nint hhook); + // ************************************************************************ + + // ************************************************************************ + // Win32: CallNextHookEx() + [DllImport("user32.dll")] + protected static extern int CallNextHookEx( + nint hhook, + int code, + nint wParam, + nint lParam); + // ************************************************************************ + #endregion + } + #endregion +} diff --git a/Debugger.cs b/Debugger.cs deleted file mode 100644 index 3f53c54..0000000 --- a/Debugger.cs +++ /dev/null @@ -1,16 +0,0 @@ -#if DEBUG -using System; -using System.IO; -namespace Community.PowerToys.Run.Plugin.Everything -{ - internal static class Debugger - { - private static readonly string FilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "EverythingPT.log"); - public static void Write(string message) - { - using StreamWriter writer = new(FilePath, true); - writer.WriteLine(message); - } - } -} -#endif diff --git a/Everything.cs b/Everything.cs index 3929afa..f2426f4 100644 --- a/Everything.cs +++ b/Everything.cs @@ -4,7 +4,10 @@ using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; +using System.Threading; using Community.PowerToys.Run.Plugin.Everything.Properties; +using Community.PowerToys.Run.Plugin.Everything.SearchHelper; +using NLog; using Wox.Plugin; using Wox.Plugin.Logger; using static Community.PowerToys.Run.Plugin.Everything.Interop.NativeMethods; @@ -40,19 +43,19 @@ namespace Community.PowerToys.Run.Plugin.Everything } } - internal IEnumerable Query(string query, Settings setting) + internal IEnumerable Query(string query, Settings setting, CancellationToken token) { -#if DEBUG - if (setting.Log > LogLevel.None) + if (setting.LoggingLevel <= LogLevel.Debug) { - Debugger.Write($"\r\n\r\nNew Query: {query}\r\n" + - $"Prefix {setting.Prefix} | " + - $"Sort {(int)setting.Sort}_{Everything_GetSort()} | " + - $"Max {setting.Max}_{Everything_GetMax()} | " + - $"Match Path {setting.MatchPath}_{Everything_GetMatchPath()} | " + - $"Regex {setting.RegEx}_{Everything_GetRegex()}"); + Log.Info( + $"EPT:\nNew Query: {query}\n" + + $"Prefix {setting.Prefix}\n" + + $"Sort {(int)setting.Sort}_{Everything_GetSort()}\n" + + $"Max {setting.Max}_{Everything_GetMax()}\n" + + $"Match Path {setting.MatchPath}_{Everything_GetMatchPath()}\n" + + $"Regex {setting.RegEx}_{Everything_GetRegex()}", + GetType()); } -#endif string orgqry = query; @@ -62,43 +65,37 @@ namespace Community.PowerToys.Run.Plugin.Everything if (setting.EnvVar && orgqry.Contains('%')) { query = Environment.ExpandEnvironmentVariables(query).Replace(';', '|'); -#if DEBUG - if (setting.Log > LogLevel.None) - Debugger.Write($"EnvVariable\r\n{query}"); -#endif + if (setting.LoggingLevel <= LogLevel.Debug) + Log.Info($"EPT:EnvVariable\n{query}", GetType()); } - if (Everything_GetMinorVersion() < 5 && orgqry.Contains(':')) + if (setting.Is1_4 && orgqry.Contains(':')) { foreach (var kv in setting.Filters) { if (query.Contains(kv.Key, StringComparison.OrdinalIgnoreCase)) { - query = query.Replace(kv.Key, kv.Value); -#if DEBUG - if (setting.Log > LogLevel.None) - Debugger.Write($"Contains Filter: {kv.Key}\r\n{query}"); -#endif + query = query.Replace(kv.Key, string.Empty).Trim() + $" {kv.Value}"; + if (setting.LoggingLevel <= LogLevel.Debug) + Log.Info($"EPT: Contains Filter: {kv.Key}\n{query}", GetType()); } } } + token.ThrowIfCancellationRequested(); Everything_SetSearchW(query); if (!Everything_QueryW(true)) { -#if DEBUG - if (setting.Log > LogLevel.None) - Debugger.Write("\r\nUnable to Query\r\n"); -#endif + if (setting.LoggingLevel < LogLevel.Error) + Log.Warn($"EPT: Unable to Query ({Everything_GetLastError()})", GetType()); throw new Win32Exception("Unable to Query"); } uint resultCount = Everything_GetNumResults(); -#if DEBUG - if (setting.Log > LogLevel.None) - Debugger.Write($"Results: {resultCount}"); -#endif + if (setting.LoggingLevel <= LogLevel.Debug) + Log.Info($"EPT: Results = {resultCount}", GetType()); + token.ThrowIfCancellationRequested(); bool showMore = setting.ShowMore && !string.IsNullOrEmpty(exe) && resultCount == setting.Max; if (showMore) { @@ -131,31 +128,32 @@ namespace Community.PowerToys.Run.Plugin.Everything for (uint i = 0; i < resultCount; i++) { -#if DEBUG - if (setting.Log > LogLevel.None) - Debugger.Write($"\r\n===== RESULT #{i} ====="); -#endif + token.ThrowIfCancellationRequested(); string name = Marshal.PtrToStringUni(Everything_GetResultFileNameW(i)); string path = Marshal.PtrToStringUni(Everything_GetResultPathW(i)); - if (name == null || path == null) + if (setting.LoggingLevel < LogLevel.Error && (name == null || path == null)) { - Log.Warn($"Result {i} is null for {name} and/or {path}, query: {query}", GetType()); + Log.Warn($"EPT: Result {i} is null for {name} and/or {path}, query: {query}", GetType()); continue; } string fullPath = Path.Combine(path, name); -#if DEBUG - if (setting.Log > LogLevel.None) - Debugger.Write($"{fullPath.Length} {(setting.Log == LogLevel.Verbose ? fullPath : string.Empty)}"); -#endif + bool isFolder = Everything_IsFolderResult(i); if (isFolder) path = fullPath; - string ext = Path.GetExtension(fullPath.Replace(".lnk", string.Empty)); -#if DEBUG - if (setting.Log > LogLevel.None) - Debugger.Write($"Folder: {isFolder}\r\nFile Path {(setting.Log == LogLevel.Verbose ? path : path.Length)}\r\nFile Name {(setting.Log == LogLevel.Verbose ? name : name.Length)}\r\nExt: {ext}"); -#endif + + if (setting.LoggingLevel <= LogLevel.Debug) + { + Log.Info( + $"=====EPT: RESULT #{i} =====\n" + + $"Folder : {isFolder}\n" + + $"File Path : ({path.Length}) {(setting.LoggingLevel == LogLevel.Trace ? path : string.Empty)}\n" + + $"File Name : ({name.Length}) {(setting.LoggingLevel == LogLevel.Trace ? name : string.Empty)}\n" + + $"Ext : {Path.GetExtension(fullPath)}", + GetType()); + } + var r = new Result() { Title = name, @@ -180,7 +178,7 @@ namespace Community.PowerToys.Run.Plugin.Everything try { process.Start(); - _ = Everything_IncRunCountFromFileName(fullPath); + _ = Everything_IncRunCountFromFileNameW(fullPath); return true; } catch (Win32Exception) diff --git a/Everything64.dll b/Everything64.dll deleted file mode 100644 index f34745ac8d7fc96b6c3a485b52a4852286788453..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 97800 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

j1l&yOU_ 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 Ps>^)MulqC#Z|eOB0X5J! diff --git a/Interop/NativeMethods.cs b/Interop/NativeMethods.cs index 82239d9..aac2a48 100644 --- a/Interop/NativeMethods.cs +++ b/Interop/NativeMethods.cs @@ -3,7 +3,7 @@ using System.Runtime.InteropServices; namespace Community.PowerToys.Run.Plugin.Everything.Interop { - public sealed class NativeMethods + public sealed partial class NativeMethods { #region FlagsEnums [Flags] @@ -56,90 +56,48 @@ namespace Community.PowerToys.Run.Plugin.Everything.Interop DATE_RUN_ASCENDING, DATE_RUN_DESCENDING, } - - [Flags] - internal enum AssocF - { - NONE = 0x00000000, - INIT_NOREMAPCLSID = 0x00000001, - INIT_BYEXENAME = 0x00000002, - INIT_DEFAULTTOSTAR = 0x00000004, - INIT_DEFAULTTOFOLDER = 0x00000008, - NOUSERSETTINGS = 0x00000010, - NOTRUNCATE = 0x00000020, - VERIFY = 0x00000040, - REMAPRUNDLL = 0x00000080, - NOFIXUPS = 0x00000100, - IGNOREBASECLASS = 0x00000200, - INIT_IGNOREUNKNOWN = 0x00000400, - INIT_FIXED_PROGID = 0x00000800, - IS_PROTOCOL = 0x00001000, - INIT_FOR_FILE = 0x00002000, - } - - internal enum AssocStr - { - COMMAND = 1, - EXECUTABLE, - FRIENDLYDOCNAME, - FRIENDLYAPPNAME, - NOOPEN, - SHELLNEWVALUE, - DDECOMMAND, - DDEIFEXEC, - DDEAPPLICATION, - DDETOPIC, - INFOTIP, - QUICKTIP, - TILEINFO, - CONTENTTYPE, - DEFAULTICON, - SHELLEXTENSION, - DROPTARGET, - DELEGATEEXECUTE, - SUPPORTED_URI_PROTOCOLS, - PROGID, - APPID, - APPPUBLISHER, - APPICONREFERENCE, - MAX, - } #endregion internal const string dllName = "Everything64.dll"; - [DllImport(dllName)] - internal static extern uint Everything_GetNumResults(); - [DllImport(dllName)] + [LibraryImport(dllName)] + internal static partial uint Everything_GetLastError(); + [LibraryImport(dllName)] + internal static partial uint Everything_GetNumResults(); //Everything3_GetResultListCount + [LibraryImport(dllName)] [return: MarshalAs(UnmanagedType.Bool)] - internal static extern bool Everything_GetMatchPath(); - [DllImport(dllName)] - internal static extern uint Everything_GetMax(); - [DllImport(dllName)] - internal static extern uint Everything_GetMinorVersion(); - [DllImport(dllName)] - internal static extern bool Everything_GetRegex(); - [DllImport(dllName, CharSet = CharSet.Unicode)] - internal static extern IntPtr Everything_GetResultFileNameW(uint nIndex); - [DllImport(dllName, CharSet = CharSet.Unicode)] - internal static extern IntPtr Everything_GetResultPathW(uint nIndex); - [DllImport(dllName)] - internal static extern uint Everything_GetSort(); - [DllImport(dllName, CharSet = CharSet.Unicode)] - internal static extern uint Everything_IncRunCountFromFileName(string lpFileName); - [DllImport(dllName)] - internal static extern bool Everything_IsFolderResult(uint index); - [DllImport(dllName)] - internal static extern bool Everything_QueryW([MarshalAs(UnmanagedType.Bool)] bool bWait); - [DllImport(dllName)] - internal static extern void Everything_SetMax(uint dwMax); - [DllImport(dllName)] - internal static extern void Everything_SetRegex([MarshalAs(UnmanagedType.Bool)] bool bEnable); - [DllImport(dllName)] - internal static extern void Everything_SetRequestFlags(Request RequestFlags); - [DllImport(dllName, CharSet = CharSet.Unicode)] - internal static extern void Everything_SetSearchW(string lpSearchString); - [DllImport(dllName)] - internal static extern bool Everything_SetMatchPath([MarshalAs(UnmanagedType.Bool)] bool bEnable); - [DllImport(dllName)] - internal static extern void Everything_SetSort(Sort SortType); + internal static partial bool Everything_GetMatchPath(); + [LibraryImport(dllName)] + internal static partial uint Everything_GetMax(); + [LibraryImport(dllName)] + internal static partial uint Everything_GetMinorVersion(); + [LibraryImport(dllName)] + [return: MarshalAs(UnmanagedType.Bool)] + internal static partial bool Everything_GetRegex(); + [LibraryImport(dllName)] + internal static partial IntPtr Everything_GetResultFileNameW(uint nIndex); + [LibraryImport(dllName)] + internal static partial IntPtr Everything_GetResultPathW(uint nIndex); + [LibraryImport(dllName)] + internal static partial uint Everything_GetSort(); + [LibraryImport(dllName, StringMarshalling = StringMarshalling.Utf16)] + internal static partial uint Everything_IncRunCountFromFileNameW(string lpFileName); + [LibraryImport(dllName)] + [return: MarshalAs(UnmanagedType.Bool)] + internal static partial bool Everything_IsFolderResult(uint index); + [LibraryImport(dllName)] + [return: MarshalAs(UnmanagedType.Bool)] + internal static partial bool Everything_QueryW([MarshalAs(UnmanagedType.Bool)] bool bWait); + [LibraryImport(dllName)] + internal static partial void Everything_SetMax(uint dwMax); + [LibraryImport(dllName)] + internal static partial void Everything_SetRegex([MarshalAs(UnmanagedType.Bool)] bool bEnable); + [LibraryImport(dllName)] + internal static partial void Everything_SetRequestFlags(Request RequestFlags); + [LibraryImport(dllName, StringMarshalling = StringMarshalling.Utf16)] + internal static partial void Everything_SetSearchW(string lpSearchString); + [LibraryImport(dllName)] + [return: MarshalAs(UnmanagedType.Bool)] + internal static partial bool Everything_SetMatchPath([MarshalAs(UnmanagedType.Bool)] bool bEnable); + [LibraryImport(dllName)] + internal static partial void Everything_SetSort(Sort SortType); } } diff --git a/Main.cs b/Main.cs index e0cbc1f..1be9ea3 100644 --- a/Main.cs +++ b/Main.cs @@ -1,25 +1,37 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; +using System.Net.Http; using System.Reflection; +using System.Runtime.InteropServices; +using System.Threading; using System.Threading.Tasks; +using System.Windows; using System.Windows.Controls; +using Community.PowerToys.Run.Plugin.Everything.ContextMenu; using Community.PowerToys.Run.Plugin.Everything.Properties; using Microsoft.PowerToys.Settings.UI.Library; +using NLog; +using PowerLauncher.Plugin; +using Wox.Infrastructure.Storage; using Wox.Plugin; using Wox.Plugin.Logger; using static Community.PowerToys.Run.Plugin.Everything.Interop.NativeMethods; namespace Community.PowerToys.Run.Plugin.Everything { - public class Main : IPlugin, IDisposable, IDelayedExecutionPlugin, IContextMenu, ISettingProvider, IPluginI18n + public class Main : IPlugin, IDisposable, IDelayedExecutionPlugin, IContextMenu, ISettingProvider, IPluginI18n, ISavable { public static string PluginID => "A86867E2D932459CBD77D176373DD657"; public string Name => Resources.plugin_name; public string Description => Resources.plugin_description; private readonly Settings _setting = new(); + private readonly PluginJsonStorage _storage = new(); + private readonly bool _isArm = RuntimeInformation.ProcessArchitecture == Architecture.Arm64; private Everything _everything; private ContextMenuLoader _contextMenuLoader; + private CancellationTokenSource cts = new(); private bool _disposed; public IEnumerable AdditionalOptions => @@ -137,30 +149,56 @@ namespace Community.PowerToys.Run.Plugin.Everything DisplayDescription = $"v{Assembly.GetExecutingAssembly().GetName().Version}", Value = _setting.Updates, }, -#if DEBUG new() { - Key = nameof(Settings.Log), - DisplayLabel = "Debug Mode", + Key = nameof(Settings.LoggingLevel), + DisplayLabel = "Log Level", PluginOptionType = PluginAdditionalOption.AdditionalOptionType.Combobox, - ComboBoxItems = Enum.GetValues(typeof(LogLevel)).Cast().Select(d => new KeyValuePair(((LogLevel)d).ToString(), d + string.Empty)).ToList(), - ComboBoxValue = (int)_setting.Log, + ComboBoxItems = LogLevel.AllLoggingLevels.Select(d => new KeyValuePair(d.ToString(), d.Ordinal + string.Empty)).ToList(), + ComboBoxValue = _setting.LoggingLevel.Ordinal, }, -#endif ]; public void Init(PluginInitContext context) { + string dll = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Everything64.dll"); + + if (!File.Exists(dll)) + { + MessageBoxResult mbox = MessageBox.Show(Resources.MissingLib, "EPT: Downloader", MessageBoxButton.YesNo); + if (mbox == MessageBoxResult.Yes) + { + using HttpClient httpClient = new(); + httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0"); + string url = $"https://github.com/lin-ycv/EverythingPowerToys/raw/refs/heads/lib/Everything{(_isArm ? "ARM" : string.Empty)}64.dll"; + byte[] fileContent = httpClient.GetByteArrayAsync(url).Result; + string fileName = dll; + File.WriteAllBytes(fileName, fileContent); + } + else + { + throw new DllNotFoundException("EPT: Everything64.dll not found, either press Yes on the download prompt, or manually load in the dll @ %LOCALAPPDATA%\\Microsoft\\PowerToys\\PowerToys Run\\Plugins\\Everything"); + } + } + + if (_setting.LoggingLevel <= LogLevel.Debug) + Log.Info("EPT: Init", GetType()); if (_setting.Updates) - Task.Run(() => new Update().UpdateAsync(Assembly.GetExecutingAssembly().GetName().Version, _setting)); + { + Update.UpdateSettings upSettings; + upSettings = _storage.Load(); + Task.Run(() => new Update.UpdateChecker().Async(Assembly.GetExecutingAssembly().GetName().Version, _setting, upSettings, _isArm)); + } + if (Everything_GetMinorVersion() < 5) _setting.Getfilters(); _everything = new Everything(_setting); _contextMenuLoader = new ContextMenuLoader(context, _setting.Context); _contextMenuLoader.Update(_setting); -#if DEBUG - if (_setting.Log > LogLevel.None) - Debugger.Write("Init Complete\r\n"); -#endif + var history = PluginManager.GlobalPlugins.FirstOrDefault(p => p.Metadata.ID == "C88512156BB74580AADF7252E130BA8D" && !p.Metadata.Disabled); + if (history != null) + Task.Run(() => MessageBox.Show(Resources.History, "EPT: History Conflict", MessageBoxButton.OK, MessageBoxImage.Warning)); + if (_setting.LoggingLevel <= LogLevel.Debug) + Log.Info("EPT: Init Complete", GetType()); } public void UpdateSettings(PowerLauncherPluginSettings settings) @@ -182,9 +220,7 @@ namespace Community.PowerToys.Run.Plugin.Everything _setting.CustomProgram = settings.AdditionalOptions.FirstOrDefault(x => x.Key == nameof(_setting.CustomProgram)).TextValue; _setting.CustomArg = settings.AdditionalOptions.FirstOrDefault(x => x.Key == nameof(_setting.CustomArg)).TextValue; _setting.ShowMore = settings.AdditionalOptions.FirstOrDefault(x => x.Key == nameof(_setting.ShowMore)).Value; -#if DEBUG - _setting.Log = (LogLevel)settings.AdditionalOptions.FirstOrDefault(x => x.Key == nameof(_setting.Log)).ComboBoxValue; -#endif + _setting.LoggingLevel = LogLevel.FromOrdinal(settings.AdditionalOptions.FirstOrDefault(x => x.Key == nameof(_setting.LoggingLevel)).ComboBoxValue); _everything?.UpdateSettings(_setting); _contextMenuLoader?.Update(_setting); @@ -193,8 +229,7 @@ namespace Community.PowerToys.Run.Plugin.Everything public List Query(Query query) { - List results = []; - return results; + return null; } public List Query(Query query, bool delayedExecution) @@ -206,7 +241,14 @@ namespace Community.PowerToys.Run.Plugin.Everything try { - results.AddRange(_everything.Query(searchQuery, _setting)); + cts.Cancel(); + cts = new(); + results.AddRange(_everything.Query(searchQuery, _setting, cts.Token)); + } + catch (OperationCanceledException) + { + if (_setting.LoggingLevel <= LogLevel.Debug) + Log.Info("EPT: Query Cancelled", GetType()); } catch (System.ComponentModel.Win32Exception) { @@ -220,12 +262,7 @@ namespace Community.PowerToys.Run.Plugin.Everything } catch (Exception e) { -#if DEBUG - if (_setting.Log > LogLevel.None) - Debugger.Write($"Everything Exception: {e.Message}\r\n{e.StackTrace}\r\n"); -#endif - - Log.Exception($"Everything Exception: {e.Message}\r\n{e.StackTrace}\r\n", e, GetType()); + Log.Exception($"EPT: Exception! {e.Message}\n", e, GetType()); } } @@ -238,6 +275,8 @@ namespace Community.PowerToys.Run.Plugin.Everything { if (disposing) { + cts.Cancel(); + cts.Dispose(); } _disposed = true; @@ -255,5 +294,6 @@ namespace Community.PowerToys.Run.Plugin.Everything public Control CreateSettingPanel() => throw new NotImplementedException(); public string GetTranslatedPluginTitle() => Resources.plugin_name; public string GetTranslatedPluginDescription() => Resources.plugin_description; + public void Save() => _storage.Save(); } } diff --git a/NSIS/exeCreator.nsi b/NSIS/exeCreator.nsi index 025cff8..1db2803 100644 --- a/NSIS/exeCreator.nsi +++ b/NSIS/exeCreator.nsi @@ -26,7 +26,7 @@ InstallDir "$LOCALAPPDATA\Microsoft\PowerToys\PowerToys Run\Plugins\Everything" Name "${EPT}" OutFile ".\..\bin\${EPT}-${ver}-${platform}.exe" RequestExecutionLevel user -SetCompressor /SOLID /FINAL lzma +;SetCompressor /SOLID /FINAL lzma ; this increases chances of AV FP? LicenseData "..\LICENSE" ;-------------------------------- @@ -47,7 +47,7 @@ Section "" IfErrors 0 +5 SetErrorlevel 1 IfSilent +2 - MessageBox MB_ICONEXCLAMATION "Unable to install, PowerToys is probably still running, please close it manually before install." + MessageBox MB_ICONEXCLAMATION "Unable to (un)install, PowerToys is probably still running, please close it manually before (un)install." Abort SectionEnd \ No newline at end of file diff --git a/Properties/Resources.Designer.cs b/Properties/Resources.Designer.cs index 30f0cd5..a94f9e8 100644 --- a/Properties/Resources.Designer.cs +++ b/Properties/Resources.Designer.cs @@ -80,7 +80,7 @@ namespace Community.PowerToys.Run.Plugin.Everything.Properties { ///

/// Looks up a localized string similar to Configure context menu options and order. - ///0_Open folder, 1_Run as Admin, 2_Run as User, 3_Copy, 4_Copy Path, 5_Open in Console, 6_Open in custom program. + ///0_Open folder, 1_Run as Admin, 2_Run as User, 3_Copy, 4_Copy Path, 5_Open in Console, 6_Open in custom program, 7_Delete file/folder, 8_Right click context menu. /// public static string Context_Description { get { @@ -160,6 +160,15 @@ namespace Community.PowerToys.Run.Plugin.Everything.Properties { } } + /// + /// Looks up a localized string similar to Delete file/folder (Ctrl+Del). + /// + public static string delete_result { + get { + return ResourceManager.GetString("delete_result", resourceCulture); + } + } + /// /// Looks up a localized string similar to Enviroment Variables. /// @@ -223,6 +232,15 @@ namespace Community.PowerToys.Run.Plugin.Everything.Properties { } } + /// + /// Looks up a localized string similar to The "Include inglobal result" option is turned on for the History plugin, which could lead to conflicts with EPT.. + /// + public static string History { + get { + return ResourceManager.GetString("History", resourceCulture); + } + } + /// /// Looks up a localized string similar to Match Path. /// @@ -259,6 +277,15 @@ namespace Community.PowerToys.Run.Plugin.Everything.Properties { } } + /// + /// Looks up a localized string similar to Everything64.dll is missing, automatically download this file?. + /// + public static string MissingLib { + get { + return ResourceManager.GetString("MissingLib", resourceCulture); + } + } + /// /// Looks up a localized string similar to See more.... /// @@ -333,7 +360,7 @@ namespace Community.PowerToys.Run.Plugin.Everything.Properties { /// /// Looks up a localized string similar to Adds prefix to all queries, so that you can always include certain filters/modifiers without typing it every time. - ///Space is not added automatically between prfix and query.. + ///Space is not added automatically between prefix and query.. /// public static string Prefix_Description { get { @@ -396,6 +423,15 @@ namespace Community.PowerToys.Run.Plugin.Everything.Properties { } } + /// + /// Looks up a localized string similar to Right click context menu (Ctrl+M). + /// + public static string right_click { + get { + return ResourceManager.GetString("right_click", resourceCulture); + } + } + /// /// Looks up a localized string similar to Run as administrator (Ctrl+Shift+Enter). /// diff --git a/Properties/Resources.it-it.resx b/Properties/Resources.it-it.resx index adf27bb..1b0ad14 100644 --- a/Properties/Resources.it-it.resx +++ b/Properties/Resources.it-it.resx @@ -162,7 +162,7 @@ Il numero massimo di elementi da mostrare nell'elenco dei risultati. - + Vedi altro... diff --git a/Properties/Resources.resx b/Properties/Resources.resx index f10d334..be6cba5 100644 --- a/Properties/Resources.resx +++ b/Properties/Resources.resx @@ -125,7 +125,7 @@ Configure context menu options and order. -0_Open folder, 1_Run as Admin, 2_Run as User, 3_Copy, 4_Copy Path, 5_Open in Console, 6_Open in custom program +0_Open folder, 1_Run as Admin, 2_Run as User, 3_Copy, 4_Copy Path, 5_Open in Console, 6_Open in custom program, 7_Delete file/folder, 8_Right click context menu Copy @@ -265,4 +265,16 @@ Latest: {1} Open path in custom program (Ctrl+N) + + Everything64.dll is missing, automatically download this file? + + + The "Include inglobal result" option is turned on for the History plugin, which could lead to conflicts with EPT. + + + Right click context menu (Ctrl+M) + + + Delete file/folder (Ctrl+Del) + \ No newline at end of file diff --git a/Properties/Resources.zh-tw.resx b/Properties/Resources.zh-tw.resx index 221a17f..af855cc 100644 --- a/Properties/Resources.zh-tw.resx +++ b/Properties/Resources.zh-tw.resx @@ -122,7 +122,7 @@ 配置選單選項與順序 -0_開啟資料夾、1_以管理員身分執行、2_以其他使用者身分執行、3_複製、4_複製路徑、5_在命令提示字元中開啟、6_在指定軟體中開啟 +0_開啟資料夾、1_以管理員身分執行、2_以其他使用者身分執行、3_複製、4_複製路徑、5_在命令提示字元中開啟、6_在指定軟體中開啟、 7_刪除檔案/資料夾、 8_右鍵選單 複製 @@ -130,6 +130,10 @@ 複製路徑 + + + + 搜尋時可用環境變數,但會增加搜尋時間 @@ -214,4 +218,16 @@ 在指定軟體裡開起 (Ctrl+N) + + 缺少 Everything.64.dll,是否自動下載? + + + 已啟用「包含全域結果中」選項於History插件,可能會與 EPT 發生衝突。 + + + 右鍵選單 (Ctrl+M) + + + 刪除檔案/資料夾 (Ctrl+Del) + \ No newline at end of file diff --git a/SearchHelper/SearchResult.cs b/SearchHelper/SearchResult.cs index 66cc240..c0996ff 100644 --- a/SearchHelper/SearchResult.cs +++ b/SearchHelper/SearchResult.cs @@ -1,6 +1,6 @@ using Wox.Plugin.Interfaces; -namespace Community.PowerToys.Run.Plugin.Everything +namespace Community.PowerToys.Run.Plugin.Everything.SearchHelper { public class SearchResult : IFileDropResult { diff --git a/Settings.cs b/Settings.cs index bc29c9e..da5ee35 100644 --- a/Settings.cs +++ b/Settings.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Reflection; +using NLog; using Wox.Plugin.Logger; using static Community.PowerToys.Run.Plugin.Everything.Interop.NativeMethods; @@ -9,44 +10,39 @@ namespace Community.PowerToys.Run.Plugin.Everything { public class Settings { + internal bool Is1_4 { get; set; } + // Settings from PTR settings - public Sort Sort { get; set; } = Sort.NAME_ASCENDING; - public uint Max { get; set; } = 10; - public string Context { get; set; } = "0123456"; - public bool Copy { get; set; } - public bool MatchPath { get; set; } - public bool Preview { get; set; } = true; - public bool QueryText { get; set; } - public bool RegEx { get; set; } - public bool EnvVar { get; set; } - public bool Updates { get; set; } = true; - public string Skip { get; set; } - public string Prefix { get; set; } - public string EverythingPath { get; set; } - public bool ShowMore { get; set; } = true; - public string CustomProgram { get; set; } = "notepad.exe"; - public string CustomArg { get; set; } = "$P"; -#if DEBUG - public LogLevel Log { get; set; } = LogLevel.None; -#endif + internal Sort Sort { get; set; } = Sort.NAME_ASCENDING; + internal uint Max { get; set; } = 10; + internal string Context { get; set; } = "01234568"; + internal bool Copy { get; set; } + internal bool MatchPath { get; set; } + internal bool Preview { get; set; } = true; + internal bool QueryText { get; set; } + internal bool RegEx { get; set; } + internal bool EnvVar { get; set; } + internal bool Updates { get; set; } = true; + internal string Prefix { get; set; } + internal string EverythingPath { get; set; } + internal bool ShowMore { get; set; } = true; + internal string CustomProgram { get; set; } = "notepad.exe"; + internal string CustomArg { get; set; } = "$P"; + internal LogLevel LoggingLevel { get; set; } = LogLevel.Error; // Get Filters from settings.toml public Dictionary Filters { get; } = []; + internal void Getfilters() { - Log.Info("User on Everything 1.4", GetType()); -#if DEBUG - if (Log > LogLevel.None) - Debugger.Write("2.Getting Filters..."); -#endif + Is1_4 = true; + if (LoggingLevel <= LogLevel.Info) + Log.Info("User on Everything 1.4, GettingFilters...", GetType()); + string[] strArr; try { strArr = File.ReadAllLines(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "settings.toml")); } catch (Exception e) { -#if DEBUG - if (Log > LogLevel.None) - Debugger.Write($"\r\nERROR: {e.Message}\r\n"); -#endif Log.Error($"Error reading settings.toml: {e.Message}", GetType()); return; } @@ -60,18 +56,9 @@ namespace Community.PowerToys.Run.Plugin.Everything if (kv[0].Contains(':')) Filters.TryAdd(kv[0].ToLowerInvariant(), kv[1] + (kv[1].EndsWith(';') ? ' ' : string.Empty)); } -#if DEBUG - if (Log > LogLevel.None) - Debugger.Write(Log > LogLevel.Debug ? string.Join(Environment.NewLine, Filters) + "\r\n" : string.Empty + " GettingFilters...Done"); -#endif + + if (LoggingLevel <= LogLevel.Info) + Log.Info(LoggingLevel < LogLevel.Debug ? string.Join(Environment.NewLine, Filters) : " GettingFilters...Done", GetType()); } } -#if DEBUG - public enum LogLevel - { - None, - Debug, - Verbose, - } -#endif } diff --git a/Update.cs b/Update/UpdateChecker.cs similarity index 72% rename from Update.cs rename to Update/UpdateChecker.cs index 335e66c..a87a262 100644 --- a/Update.cs +++ b/Update/UpdateChecker.cs @@ -8,28 +8,28 @@ using System.Text.Json; using System.Threading.Tasks; using System.Windows; using Community.PowerToys.Run.Plugin.Everything.Properties; +using NLog; +using Wox.Plugin.Logger; -namespace Community.PowerToys.Run.Plugin.Everything +namespace Community.PowerToys.Run.Plugin.Everything.Update { - internal sealed class Update + internal sealed class UpdateChecker { private readonly CompositeFormat updateAvailable = CompositeFormat.Parse(Resources.UpdateAvailable); - internal async Task UpdateAsync(Version v, Settings s) + + internal async Task Async(Version v, Settings s, UpdateSettings us, bool isArm) { string apiUrl = "https://api.github.com/repos/lin-ycv/EverythingPowerToys/releases/latest"; -#if DEBUG - if (s.Log > LogLevel.None) - Debugger.Write("1.Checking Update..."); -#endif + if (s.LoggingLevel <= LogLevel.Info) Log.Info("EPT: Checking Update...", GetType()); + try { using HttpClient httpClient = new(); httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0"); HttpResponseMessage response = await httpClient.GetAsync(apiUrl); -#if DEBUG - if (s.Log == LogLevel.Verbose) Debugger.Write($"\tResponse: {response.StatusCode}"); -#endif + if (s.LoggingLevel <= LogLevel.Debug) Log.Info($"EPT: Response: {response.StatusCode}", GetType()); + if (response.IsSuccessStatusCode) { using JsonDocument jsonDocument = await JsonDocument.ParseAsync(await response.Content.ReadAsStreamAsync()); @@ -37,19 +37,17 @@ namespace Community.PowerToys.Run.Plugin.Everything Version latest = Version.TryParse(root.GetProperty("tag_name").GetString().AsSpan(1), out var vNumber) ? vNumber : Version.Parse(root.GetProperty("tag_name").GetString()); - if (latest > v && latest.ToString() != s.Skip) + if (s.LoggingLevel <= LogLevel.Debug) Log.Info($"EPT:\n\tLastest: {latest}\n\tSkip: {us.Skip}", GetType()); + + if (latest > v && latest != us.Skip) { - MessageBoxResult mbox = MessageBox.Show(string.Format(CultureInfo.InvariantCulture, updateAvailable, v, latest), "Updater", MessageBoxButton.YesNoCancel); + MessageBoxResult mbox = MessageBox.Show(string.Format(CultureInfo.InvariantCulture, updateAvailable, v, latest), "EPT: Updater", MessageBoxButton.YesNoCancel); if (mbox == MessageBoxResult.Yes && root.TryGetProperty("assets", out JsonElement assets)) { string[] nameUrl = [string.Empty, string.Empty]; foreach (JsonElement asset in assets.EnumerateArray()) { -#if X64 - if (asset.TryGetProperty("browser_download_url", out JsonElement downUrl) && downUrl.ToString().EndsWith("x64.exe", StringComparison.OrdinalIgnoreCase)) -#elif ARM64 - if (asset.TryGetProperty("browser_download_url", out JsonElement downUrl) && downUrl.ToString().EndsWith("ARM64.exe", StringComparison.OrdinalIgnoreCase)) -#endif + if (asset.TryGetProperty("browser_download_url", out JsonElement downUrl) && downUrl.ToString().EndsWith(isArm ? "ARM64.exe" : "x64.exe", StringComparison.OrdinalIgnoreCase)) { nameUrl[0] = asset.GetProperty("name").ToString(); nameUrl[1] = downUrl.ToString(); @@ -62,6 +60,9 @@ namespace Community.PowerToys.Run.Plugin.Everything string fileName = Path.Combine(Path.GetTempPath(), nameUrl[0]); File.WriteAllBytes(fileName, fileContent); Process.Start(fileName); + + //foreach (Process pt in Process.GetProcessesByName("PowerToys")) + // pt.Kill(); } else { @@ -75,24 +76,19 @@ namespace Community.PowerToys.Run.Plugin.Everything } else if (mbox == MessageBoxResult.No) { - s.Skip = latest.ToString(); + us.Skip = latest; } } } } -#if RELEASE - catch - { } -#else catch (Exception e) { - if (s.Log > LogLevel.None) - Debugger.Write($"\r\nERROR: {e.Message}\r\n{e.StackTrace}\r\n"); - + if (s.LoggingLevel < LogLevel.Error) + Log.Exception($"EPT: Unable to check for update", e, GetType()); } - if (s.Log > LogLevel.None) - Debugger.Write(" Checking Update...Done"); -#endif + + if (s.LoggingLevel < LogLevel.Error) + Log.Info("EPT: Checking Update...Done", GetType()); } } } diff --git a/Update/UpdateSettings.cs b/Update/UpdateSettings.cs new file mode 100644 index 0000000..82a685d --- /dev/null +++ b/Update/UpdateSettings.cs @@ -0,0 +1,9 @@ +using System; + +namespace Community.PowerToys.Run.Plugin.Everything.Update +{ + public class UpdateSettings + { + public Version Skip { get; set; } = new Version(0, 0, 0, 0); + } +} diff --git a/plugin.json b/plugin.json index 95a18c6..16f1f52 100644 --- a/plugin.json +++ b/plugin.json @@ -4,7 +4,7 @@ "IsGlobal": true, "Name": "Everything", "Author": "Yu Chieh (Victor) Lin", - "Version": "0.87.0", + "Version": "0.0.2", "Language": "csharp", "Website": "https://github.com/Lin-ycv/EverythingPowerToys", "ExecuteFileName": "Community.PowerToys.Run.Plugin.Everything.dll",