diff --git a/PowerToys.sln b/PowerToys.sln index 6b4e70c89a..396ff8efa7 100644 --- a/PowerToys.sln +++ b/PowerToys.sln @@ -154,6 +154,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wox.Plugin.Folder", "src\mo EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Wox.Launcher", "src\modules\launcher\Wox.Launcher\Wox.Launcher.vcxproj", "{E364F67B-BB12-4E91-B639-355866EBCD8B}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wox.Plugin.Indexer", "src\modules\launcher\Plugins\Wox.Plugin.Indexer\Wox.Plugin.Indexer.csproj", "{63C3CEA8-51FE-472E-B97C-B58F8B17DD51}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -378,6 +380,14 @@ Global {E364F67B-BB12-4E91-B639-355866EBCD8B}.Release|Any CPU.ActiveCfg = Release|x64 {E364F67B-BB12-4E91-B639-355866EBCD8B}.Release|x64.ActiveCfg = Release|x64 {E364F67B-BB12-4E91-B639-355866EBCD8B}.Release|x64.Build.0 = Release|x64 + {63C3CEA8-51FE-472E-B97C-B58F8B17DD51}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {63C3CEA8-51FE-472E-B97C-B58F8B17DD51}.Debug|Any CPU.Build.0 = Debug|Any CPU + {63C3CEA8-51FE-472E-B97C-B58F8B17DD51}.Debug|x64.ActiveCfg = Debug|Any CPU + {63C3CEA8-51FE-472E-B97C-B58F8B17DD51}.Debug|x64.Build.0 = Debug|Any CPU + {63C3CEA8-51FE-472E-B97C-B58F8B17DD51}.Release|Any CPU.ActiveCfg = Release|Any CPU + {63C3CEA8-51FE-472E-B97C-B58F8B17DD51}.Release|Any CPU.Build.0 = Release|Any CPU + {63C3CEA8-51FE-472E-B97C-B58F8B17DD51}.Release|x64.ActiveCfg = Release|Any CPU + {63C3CEA8-51FE-472E-B97C-B58F8B17DD51}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -419,6 +429,7 @@ Global {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0} = {4AFC9975-2456-4C70-94A4-84073C1CED93} {787B8AA6-CA93-4C84-96FE-DF31110AD1C4} = {4AFC9975-2456-4C70-94A4-84073C1CED93} {E364F67B-BB12-4E91-B639-355866EBCD8B} = {C140A3EF-6DBF-4084-9D4C-4EB5A99FEE68} + {63C3CEA8-51FE-472E-B97C-B58F8B17DD51} = {4AFC9975-2456-4C70-94A4-84073C1CED93} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0} diff --git a/src/common/msi_to_msix_upgrade_lib/msi_to_msix_upgrade_lib.vcxproj b/src/common/msi_to_msix_upgrade_lib/msi_to_msix_upgrade_lib.vcxproj index baa4a66427..767a9d12c0 100644 --- a/src/common/msi_to_msix_upgrade_lib/msi_to_msix_upgrade_lib.vcxproj +++ b/src/common/msi_to_msix_upgrade_lib/msi_to_msix_upgrade_lib.vcxproj @@ -87,7 +87,7 @@ Use - Level4 + Level3 true true true @@ -129,7 +129,7 @@ Use - Level4 + Level3 true _DEBUG;_LIB;%(PreprocessorDefinitions) true diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Indexer/Images/WindowsIndexerImg.bmp b/src/modules/launcher/Plugins/Wox.Plugin.Indexer/Images/WindowsIndexerImg.bmp new file mode 100644 index 0000000000..78a015715e Binary files /dev/null and b/src/modules/launcher/Plugins/Wox.Plugin.Indexer/Images/WindowsIndexerImg.bmp differ diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Indexer/Main.cs b/src/modules/launcher/Plugins/Wox.Plugin.Indexer/Main.cs new file mode 100644 index 0000000000..652af40c40 --- /dev/null +++ b/src/modules/launcher/Plugins/Wox.Plugin.Indexer/Main.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Diagnostics; +using System.Text; +using System.Threading.Tasks; +using Wox.Plugin; +using System.IO; +using System.ComponentModel; +using Wox.Infrastructure.Storage; +using Wox.Plugin.Indexer.SearchHelper; +using Microsoft.Search.Interop; + +namespace Wox.Plugin.Indexer +{ + class Main : IPlugin, ISavable, IPluginI18n + { + + // This variable contains metadata about the Plugin + private PluginInitContext _context; + + // This variable contains information about the context menus + private Settings _settings; + + // Contains information about the plugin stored in json format + private PluginJsonStorage _storage; + + // To access Windows Search functionalities + private readonly WindowsSearchAPI _api = new WindowsSearchAPI(); + + // To save the configurations of plugins + public void Save() + { + _storage.Save(); + } + + // This function uses the Windows indexer and returns the list of results obtained + public List Query(Query query) + { + var results = new List(); + if (!string.IsNullOrEmpty(query.Search)) + { + var searchQuery = query.Search; + if (_settings.MaxSearchCount <= 0) + { + _settings.MaxSearchCount = 50; + } + + try + { + var searchResultsList = _api.Search(searchQuery, maxCount: _settings.MaxSearchCount).ToList(); + foreach (var searchResult in searchResultsList) + { + var path = searchResult.Path; + + string workingDir = null; + if (_settings.UseLocationAsWorkingDir) + workingDir = Path.GetDirectoryName(path); + + Result r = new Result(); + r.Title = Path.GetFileName(path); + r.SubTitle = path; + r.IcoPath = path; + r.Action = c => + { + bool hide; + try + { + Process.Start(new ProcessStartInfo + { + FileName = path, + UseShellExecute = true, + WorkingDirectory = workingDir + }); + hide = true; + } + catch (Win32Exception) + { + var name = $"Plugin: {_context.CurrentPluginMetadata.Name}"; + var msg = "Can't Open this file"; + _context.API.ShowMsg(name, msg, string.Empty); + hide = false; + } + return hide; + }; + r.ContextData = searchResult; + results.Add(r); + } + } + catch (Exception ex) + { + results.Add(new Result + { + // TODO: Localize the string + Title = "Windows indexer plugin is not running", + IcoPath = "Images\\WindowsIndexerImg.bmp" + }); + } + } + + return results; + } + + public void Init(PluginInitContext context) + { + // initialize the context of the plugin + _context = context; + _storage = new PluginJsonStorage(); + _settings = _storage.Load(); + } + + // TODO: Localize the strings + // Set the Plugin Title + public string GetTranslatedPluginTitle() + { + return "Windows Indexer Plugin"; + } + + // TODO: Localize the string + // Set the plugin Description + public string GetTranslatedPluginDescription() + { + return "Returns files and folders"; + } + + + } +} diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Indexer/Properties/AssemblyInfo.cs b/src/modules/launcher/Plugins/Wox.Plugin.Indexer/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..a6f591220c --- /dev/null +++ b/src/modules/launcher/Plugins/Wox.Plugin.Indexer/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Wox.Plugin.Indexer")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Wox.Plugin.Indexer")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("63c3cea8-51fe-472e-b97c-b58f8b17dd51")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Indexer/SearchHelper/SearchResult.cs b/src/modules/launcher/Plugins/Wox.Plugin.Indexer/SearchHelper/SearchResult.cs new file mode 100644 index 0000000000..fc4c0ed770 --- /dev/null +++ b/src/modules/launcher/Plugins/Wox.Plugin.Indexer/SearchHelper/SearchResult.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Wox.Plugin.Indexer.SearchHelper +{ + public class SearchResult + { + // Contains the Path of the file or folder + public string Path { get; set; } + } +} diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Indexer/SearchHelper/WindowsSearchAPI.cs b/src/modules/launcher/Plugins/Wox.Plugin.Indexer/SearchHelper/WindowsSearchAPI.cs new file mode 100644 index 0000000000..64875595a1 --- /dev/null +++ b/src/modules/launcher/Plugins/Wox.Plugin.Indexer/SearchHelper/WindowsSearchAPI.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.Data.OleDb; +using Microsoft.Search.Interop; + +namespace Wox.Plugin.Indexer.SearchHelper +{ + public class WindowsSearchAPI + { + public OleDbConnection conn; + public OleDbCommand command; + public OleDbDataReader WDSResults; + + public IEnumerable ExecuteQuery(ISearchQueryHelper queryHelper, string keyword) + { + // Generate SQL from our parameters, converting the userQuery from AQS->WHERE clause + string sqlQuery = queryHelper.GenerateSQLFromUserQuery(keyword); + + // --- Perform the query --- + // create an OleDbConnection object which connects to the indexer provider with the windows application + using (conn = new OleDbConnection(queryHelper.ConnectionString)) + { + // open the connection + conn.Open(); + + // now create an OleDB command object with the query we built above and the connection we just opened. + using (command = new OleDbCommand(sqlQuery, conn)) + { + // execute the command, which returns the results as an OleDbDataReader. + using (WDSResults = command.ExecuteReader()) + { + while (WDSResults.Read()) + { + // col 0 is our path in display format + Console.WriteLine("{0}", WDSResults.GetString(0)); + var result = new SearchResult { Path = WDSResults.GetString(0) }; + + yield return result; + } + } + + } + } + } + + + public void ModifyQueryHelper(ref ISearchQueryHelper queryHelper, string pattern) + { + // convert file pattern if it is not '*'. Don't create restriction for '*' as it includes all files. + if (pattern != "*") + { + pattern = pattern.Replace("*", "%"); + pattern = pattern.Replace("?", "_"); + + if (pattern.Contains("%") || pattern.Contains("_")) + { + queryHelper.QueryWhereRestrictions += " AND System.FileName LIKE '" + pattern + "' "; + } + else + { + // if there are no wildcards we can use a contains which is much faster as it uses the index + queryHelper.QueryWhereRestrictions += " AND Contains(System.FileName, '" + pattern + "') "; + } + } + } + + public void InitQueryHelper(out ISearchQueryHelper queryHelper, int maxCount) + { + // This uses the Microsoft.Search.Interop assembly + CSearchManager manager = new CSearchManager(); + + // SystemIndex catalog is the default catalog in Windows + ISearchCatalogManager catalogManager = manager.GetCatalog("SystemIndex"); + + // Get the ISearchQueryHelper which will help us to translate AQS --> SQL necessary to query the indexer + queryHelper = catalogManager.GetQueryHelper(); + + // Set the number of results we want. Don't set this property if all results are needed. + queryHelper.QueryMaxResults = maxCount; + + // Set list of columns we want to display, getting the path presently + queryHelper.QuerySelectColumns = "System.ItemPathDisplay"; + + // Set additional query restriction + queryHelper.QueryWhereRestrictions = "AND scope='file:'"; + + // Set sorting order + queryHelper.QuerySorting = "System.DateModified DESC"; + } + + public IEnumerable Search(string keyword, string pattern = "*", int maxCount = 100) + { + ISearchQueryHelper queryHelper; + InitQueryHelper(out queryHelper, maxCount); + ModifyQueryHelper(ref queryHelper, pattern); + return ExecuteQuery(queryHelper, keyword); + } + } +} diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Indexer/Settings.cs b/src/modules/launcher/Plugins/Wox.Plugin.Indexer/Settings.cs new file mode 100644 index 0000000000..6fc9bbbf94 --- /dev/null +++ b/src/modules/launcher/Plugins/Wox.Plugin.Indexer/Settings.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Wox.Plugin.Indexer +{ + public class Settings + { + public List ContextMenus = new List(); + public int MaxSearchCount { get; set; } = 100; + public bool UseLocationAsWorkingDir { get; set; } = false; + } + + public class ContextMenu + { + public string Name { get; set; } + public string Command { get; set; } + public string Argument { get; set; } + public string ImagePath { get; set; } + } +} diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Indexer/Wox.Plugin.Indexer.csproj b/src/modules/launcher/Plugins/Wox.Plugin.Indexer/Wox.Plugin.Indexer.csproj new file mode 100644 index 0000000000..1a7bdbe8c7 --- /dev/null +++ b/src/modules/launcher/Plugins/Wox.Plugin.Indexer/Wox.Plugin.Indexer.csproj @@ -0,0 +1,78 @@ + + + + + Debug + AnyCPU + {63C3CEA8-51FE-472E-B97C-B58F8B17DD51} + Library + Properties + Wox.Plugin.Indexer + Wox.Plugin.Indexer + v4.5.2 + 512 + true + + + + true + full + false + ..\..\..\..\..\x64\Debug\modules\launcher\Plugins\Wox.Plugin.Indexer\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + ..\..\..\..\..\x64\Release\modules\launcher\Plugins\Wox.Plugin.Indexer\ + TRACE + prompt + 4 + + + + ..\..\..\..\..\packages\tlbimp-Microsoft.Search.Interop.1.0.0\lib\net45\Microsoft.Search.Interop.dll + True + + + + + + + + + + + + + + + + + + + + + PreserveNewest + + + + + {4fd29318-a8ab-4d8f-aa47-60bc241b8da3} + Wox.Infrastructure + + + {8451ecdd-2ea4-4966-bb0a-7bbc40138e80} + Wox.Plugin + + + + + + PreserveNewest + + + + \ No newline at end of file diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Indexer/packages.config b/src/modules/launcher/Plugins/Wox.Plugin.Indexer/packages.config new file mode 100644 index 0000000000..6b77ac3b21 --- /dev/null +++ b/src/modules/launcher/Plugins/Wox.Plugin.Indexer/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/modules/launcher/Plugins/Wox.Plugin.Indexer/plugin.json b/src/modules/launcher/Plugins/Wox.Plugin.Indexer/plugin.json new file mode 100644 index 0000000000..741c57ddcc --- /dev/null +++ b/src/modules/launcher/Plugins/Wox.Plugin.Indexer/plugin.json @@ -0,0 +1,12 @@ +{ + "ID": "2140FC9819AD43A3A616E2735815C27C", + "ActionKeyword": "*", + "Name": "Windows Indexer", + "Description": "Search for files and folders", + "Author": "??", + "Version": "1.0.0", + "Language": "csharp", + "Website": "http://www.wox.one/plugin", + "ExecuteFileName": "Wox.Plugin.Indexer.dll", + "IcoPath": "Images\\WindowsIndexerImg.bmp" +} diff --git a/src/modules/launcher/Wox.Infrastructure/Wox.Infrastructure.csproj b/src/modules/launcher/Wox.Infrastructure/Wox.Infrastructure.csproj index 158a6848a4..0b0a7db25c 100644 --- a/src/modules/launcher/Wox.Infrastructure/Wox.Infrastructure.csproj +++ b/src/modules/launcher/Wox.Infrastructure/Wox.Infrastructure.csproj @@ -107,8 +107,8 @@ - xcopy /Y $(PkgPinyin4DotNet)\pinyindb\unicode_to_hanyu_pinyin.txt $(TargetDir)pinyindb\ - xcopy /Y $(PkgNLog_Schema)\content\NLog.xsd $(TargetDir) + xcopy /Y "$(PkgPinyin4DotNet)\pinyindb\unicode_to_hanyu_pinyin.txt" "$(TargetDir)pinyindb\" + xcopy /Y "$(PkgNLog_Schema)\content\NLog.xsd" "$(TargetDir)"