mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-02-23 19:49:43 +01:00
File search extension (#183)
Adds support for a basic Indexer-based file search extension. Co-authored-by: Mike Griese <migrie@microsoft.com>
This commit is contained in:
19
.github/actions/spell-check/expect.txt
vendored
19
.github/actions/spell-check/expect.txt
vendored
@@ -205,6 +205,7 @@ coclass
|
||||
codereview
|
||||
Codespaces
|
||||
COINIT
|
||||
colid
|
||||
colorconv
|
||||
colorformat
|
||||
colorhistory
|
||||
@@ -279,8 +280,12 @@ datareader
|
||||
datatracker
|
||||
dataversion
|
||||
Dayof
|
||||
DBID
|
||||
DBLCLKS
|
||||
DBLEPSILON
|
||||
DBPROP
|
||||
DBPROPIDSET
|
||||
DBPROPSET
|
||||
DCapture
|
||||
DCBA
|
||||
DCOM
|
||||
@@ -490,6 +495,7 @@ flac
|
||||
flaticon
|
||||
flyouts
|
||||
FMask
|
||||
fmtid
|
||||
FOF
|
||||
FOFX
|
||||
FOLDERID
|
||||
@@ -611,6 +617,7 @@ HREDRAW
|
||||
hres
|
||||
hresult
|
||||
hrgn
|
||||
HROW
|
||||
hsb
|
||||
HSCROLL
|
||||
hsi
|
||||
@@ -914,6 +921,8 @@ mscorlib
|
||||
msdata
|
||||
MSDL
|
||||
MSGFLT
|
||||
MSIDXS
|
||||
MSIDXSPROP
|
||||
msiexec
|
||||
MSIFASTINSTALL
|
||||
MSIHANDLE
|
||||
@@ -1057,6 +1066,7 @@ oldpath
|
||||
oldtheme
|
||||
oleaut
|
||||
OLECHAR
|
||||
openas
|
||||
opencode
|
||||
OPENFILENAME
|
||||
opensource
|
||||
@@ -1183,6 +1193,8 @@ previouscamera
|
||||
PREVIOUSINSTALLFOLDER
|
||||
PREVIOUSVERSIONSINSTALLED
|
||||
prevpane
|
||||
prg
|
||||
prgh
|
||||
prgms
|
||||
pri
|
||||
PRINTCLIENT
|
||||
@@ -1199,6 +1211,8 @@ programdata
|
||||
projectname
|
||||
PROPBAG
|
||||
PROPERTYKEY
|
||||
propkey
|
||||
Propset
|
||||
PROPVARIANT
|
||||
prvpane
|
||||
psapi
|
||||
@@ -1310,6 +1324,7 @@ RGBQUAD
|
||||
rgbs
|
||||
rgelt
|
||||
rgf
|
||||
rgh
|
||||
rgn
|
||||
rgs
|
||||
RIDEV
|
||||
@@ -1320,6 +1335,7 @@ RKey
|
||||
RNumber
|
||||
rop
|
||||
ROUNDSMALL
|
||||
ROWSETEXT
|
||||
rpcrt
|
||||
RRF
|
||||
rrr
|
||||
@@ -1361,6 +1377,7 @@ searchterm
|
||||
searchtext
|
||||
SEARCHUI
|
||||
secpol
|
||||
SEEMASKINVOKEIDLIST
|
||||
SENDCHANGE
|
||||
sendvirtualinput
|
||||
seperators
|
||||
@@ -1664,6 +1681,7 @@ uwp
|
||||
vabdq
|
||||
validmodulename
|
||||
valuegenerator
|
||||
VARENUM
|
||||
variantassignment
|
||||
vcamp
|
||||
vcdl
|
||||
@@ -1744,6 +1762,7 @@ webpage
|
||||
websites
|
||||
wekyb
|
||||
wgpocpl
|
||||
WHEREID
|
||||
Wholegrain
|
||||
wic
|
||||
wifi
|
||||
|
||||
@@ -707,6 +707,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Project Templates", "Projec
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NewPlus.ShellExtension.win10", "src\modules\NewPlus\NewShellExtensionContextMenu.win10\NewPlus.ShellExtension.win10.vcxproj", "{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.Ext.Indexer", "src\modules\cmdpal\Exts\Microsoft.CmdPal.Ext.Indexer\Microsoft.CmdPal.Ext.Indexer.csproj", "{453CBB73-A3CB-4D0B-8D24-6940B86FE21D}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.Ext.Shell", "src\modules\cmdpal\Exts\Microsoft.CmdPal.Ext.Shell\Microsoft.CmdPal.Ext.Shell.csproj", "{C0CE3B5E-16D3-495D-B335-CA791B660162}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.Ext.WindowWalker", "src\modules\cmdpal\Exts\Microsoft.CmdPal.Ext.WindowWalker\Microsoft.CmdPal.Ext.WindowWalker.csproj", "{3A9A7297-92C4-4F16-B6F9-8D4AB652C86C}"
|
||||
@@ -3359,6 +3361,24 @@ Global
|
||||
{D5E5F5EA-1B6C-4A73-88BE-304F36C9E4EE}.Release|x64.Build.0 = Release|x64
|
||||
{D5E5F5EA-1B6C-4A73-88BE-304F36C9E4EE}.Release|x86.ActiveCfg = Release|x64
|
||||
{D5E5F5EA-1B6C-4A73-88BE-304F36C9E4EE}.Release|x86.Build.0 = Release|x64
|
||||
{453CBB73-A3CB-4D0B-8D24-6940B86FE21D}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{453CBB73-A3CB-4D0B-8D24-6940B86FE21D}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{453CBB73-A3CB-4D0B-8D24-6940B86FE21D}.Debug|ARM64.Deploy.0 = Debug|ARM64
|
||||
{453CBB73-A3CB-4D0B-8D24-6940B86FE21D}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{453CBB73-A3CB-4D0B-8D24-6940B86FE21D}.Debug|x64.Build.0 = Debug|x64
|
||||
{453CBB73-A3CB-4D0B-8D24-6940B86FE21D}.Debug|x64.Deploy.0 = Debug|x64
|
||||
{453CBB73-A3CB-4D0B-8D24-6940B86FE21D}.Debug|x86.ActiveCfg = Debug|x64
|
||||
{453CBB73-A3CB-4D0B-8D24-6940B86FE21D}.Debug|x86.Build.0 = Debug|x64
|
||||
{453CBB73-A3CB-4D0B-8D24-6940B86FE21D}.Debug|x86.Deploy.0 = Debug|x64
|
||||
{453CBB73-A3CB-4D0B-8D24-6940B86FE21D}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{453CBB73-A3CB-4D0B-8D24-6940B86FE21D}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{453CBB73-A3CB-4D0B-8D24-6940B86FE21D}.Release|ARM64.Deploy.0 = Release|ARM64
|
||||
{453CBB73-A3CB-4D0B-8D24-6940B86FE21D}.Release|x64.ActiveCfg = Release|x64
|
||||
{453CBB73-A3CB-4D0B-8D24-6940B86FE21D}.Release|x64.Build.0 = Release|x64
|
||||
{453CBB73-A3CB-4D0B-8D24-6940B86FE21D}.Release|x64.Deploy.0 = Release|x64
|
||||
{453CBB73-A3CB-4D0B-8D24-6940B86FE21D}.Release|x86.ActiveCfg = Release|x64
|
||||
{453CBB73-A3CB-4D0B-8D24-6940B86FE21D}.Release|x86.Build.0 = Release|x64
|
||||
{453CBB73-A3CB-4D0B-8D24-6940B86FE21D}.Release|x86.Deploy.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -3627,6 +3647,7 @@ Global
|
||||
{B79B52FB-8B2E-4CF5-B0FE-37E3E981AC7A} = {071E18A4-A530-46B8-AB7D-B862EE55E24E}
|
||||
{89D0E199-B17A-418C-B2F8-7375B6708357} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
|
||||
{C0CE3B5E-16D3-495D-B335-CA791B660162} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
|
||||
{453CBB73-A3CB-4D0B-8D24-6940B86FE21D} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
|
||||
{02EA681E-C7D8-13C7-8484-4AC65E1B71E8} = {3846508C-77EB-4034-A702-F8BB263C4F79}
|
||||
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E} = {CA716AE6-FE5C-40AC-BB8F-2C87912687AC}
|
||||
{3A9A7297-92C4-4F16-B6F9-8D4AB652C86C} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Microsoft.CmdPal.Ext.Indexer.Data;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Properties;
|
||||
using Microsoft.CmdPal.Extensions.Helpers;
|
||||
using Windows.ApplicationModel.DataTransfer;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Indexer.Commands;
|
||||
|
||||
internal sealed partial class CopyPathCommand : InvokableCommand
|
||||
{
|
||||
private readonly IndexerItem _item;
|
||||
|
||||
internal CopyPathCommand(IndexerItem item)
|
||||
{
|
||||
this._item = item;
|
||||
this.Name = Resources.Indexer_Command_CopyPath;
|
||||
this.Icon = new("\uE8c8");
|
||||
}
|
||||
|
||||
public override CommandResult Invoke()
|
||||
{
|
||||
try
|
||||
{
|
||||
var dataPackage = new DataPackage();
|
||||
dataPackage.SetText(_item.FullPath);
|
||||
Clipboard.SetContent(dataPackage);
|
||||
Clipboard.Flush();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
return CommandResult.KeepOpen();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using ManagedCommon;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Data;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Properties;
|
||||
using Microsoft.CmdPal.Extensions.Helpers;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Indexer.Commands;
|
||||
|
||||
internal sealed partial class OpenFileCommand : InvokableCommand
|
||||
{
|
||||
private readonly IndexerItem _item;
|
||||
|
||||
internal OpenFileCommand(IndexerItem item)
|
||||
{
|
||||
this._item = item;
|
||||
this.Name = Resources.Indexer_Command_OpenFile;
|
||||
this.Icon = new("\uE8E5");
|
||||
}
|
||||
|
||||
public override CommandResult Invoke()
|
||||
{
|
||||
using (var process = new Process())
|
||||
{
|
||||
process.StartInfo.FileName = _item.FullPath;
|
||||
process.StartInfo.UseShellExecute = true;
|
||||
|
||||
try
|
||||
{
|
||||
process.Start();
|
||||
}
|
||||
catch (Win32Exception ex)
|
||||
{
|
||||
Logger.LogError($"Unable to open {_item.FullPath}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
return CommandResult.GoHome();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using ManagedCommon;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Data;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Properties;
|
||||
using Microsoft.CmdPal.Extensions.Helpers;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Indexer.Commands;
|
||||
|
||||
internal sealed partial class OpenInConsoleCommand : InvokableCommand
|
||||
{
|
||||
private readonly IndexerItem _item;
|
||||
|
||||
internal OpenInConsoleCommand(IndexerItem item)
|
||||
{
|
||||
this._item = item;
|
||||
this.Name = Resources.Indexer_Command_OpenPathInConsole;
|
||||
this.Icon = new("\uE756");
|
||||
}
|
||||
|
||||
public override CommandResult Invoke()
|
||||
{
|
||||
using (var process = new Process())
|
||||
{
|
||||
process.StartInfo.WorkingDirectory = Path.GetDirectoryName(_item.FullPath);
|
||||
process.StartInfo.FileName = "cmd.exe";
|
||||
|
||||
try
|
||||
{
|
||||
process.Start();
|
||||
}
|
||||
catch (Win32Exception ex)
|
||||
{
|
||||
Logger.LogError($"Unable to open {_item.FullPath}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
return CommandResult.GoHome();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using ManagedCommon;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Data;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Native;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Properties;
|
||||
using Microsoft.CmdPal.Extensions.Helpers;
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.UI.Shell;
|
||||
using Windows.Win32.UI.WindowsAndMessaging;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Indexer.Commands;
|
||||
|
||||
internal sealed partial class OpenPropertiesCommand : InvokableCommand
|
||||
{
|
||||
private readonly IndexerItem _item;
|
||||
|
||||
private static unsafe bool ShowFileProperties(string filename)
|
||||
{
|
||||
var propertiesPtr = Marshal.StringToHGlobalUni("properties");
|
||||
var filenamePtr = Marshal.StringToHGlobalUni(filename);
|
||||
|
||||
try
|
||||
{
|
||||
var filenamePCWSTR = new PCWSTR((char*)filenamePtr);
|
||||
var propertiesPCWSTR = new PCWSTR((char*)propertiesPtr);
|
||||
|
||||
var info = new SHELLEXECUTEINFOW
|
||||
{
|
||||
cbSize = (uint)Marshal.SizeOf<SHELLEXECUTEINFOW>(),
|
||||
lpVerb = propertiesPCWSTR,
|
||||
lpFile = filenamePCWSTR,
|
||||
nShow = (int)SHOW_WINDOW_CMD.SW_SHOW,
|
||||
fMask = NativeHelpers.SEEMASKINVOKEIDLIST,
|
||||
};
|
||||
|
||||
return PInvoke.ShellExecuteEx(ref info);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeHGlobal(filenamePtr);
|
||||
Marshal.FreeHGlobal(propertiesPtr);
|
||||
}
|
||||
}
|
||||
|
||||
internal OpenPropertiesCommand(IndexerItem item)
|
||||
{
|
||||
this._item = item;
|
||||
this.Name = Resources.Indexer_Command_OpenProperties;
|
||||
this.Icon = new("\uE90F");
|
||||
}
|
||||
|
||||
public override CommandResult Invoke()
|
||||
{
|
||||
try
|
||||
{
|
||||
ShowFileProperties(_item.FullPath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Error showing file properties: ", ex);
|
||||
}
|
||||
|
||||
return CommandResult.GoHome();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Data;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Native;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Properties;
|
||||
using Microsoft.CmdPal.Extensions.Helpers;
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.UI.Shell;
|
||||
using Windows.Win32.UI.WindowsAndMessaging;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Indexer.Commands;
|
||||
|
||||
internal sealed partial class OpenWithCommand : InvokableCommand
|
||||
{
|
||||
private readonly IndexerItem _item;
|
||||
|
||||
private static unsafe bool OpenWith(string filename)
|
||||
{
|
||||
var filenamePtr = Marshal.StringToHGlobalUni(filename);
|
||||
var verbPtr = Marshal.StringToHGlobalUni("openas");
|
||||
|
||||
try
|
||||
{
|
||||
var filenamePCWSTR = new PCWSTR((char*)filenamePtr);
|
||||
var verbPCWSTR = new PCWSTR((char*)verbPtr);
|
||||
|
||||
var info = new SHELLEXECUTEINFOW
|
||||
{
|
||||
cbSize = (uint)Marshal.SizeOf<SHELLEXECUTEINFOW>(),
|
||||
lpVerb = verbPCWSTR,
|
||||
lpFile = filenamePCWSTR,
|
||||
nShow = (int)SHOW_WINDOW_CMD.SW_SHOWNORMAL,
|
||||
fMask = NativeHelpers.SEEMASKINVOKEIDLIST,
|
||||
};
|
||||
|
||||
return PInvoke.ShellExecuteEx(ref info);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeHGlobal(filenamePtr);
|
||||
Marshal.FreeHGlobal(verbPtr);
|
||||
}
|
||||
}
|
||||
|
||||
internal OpenWithCommand(IndexerItem item)
|
||||
{
|
||||
this._item = item;
|
||||
this.Name = Resources.Indexer_Command_OpenWith;
|
||||
this.Icon = new("\uE7AC");
|
||||
}
|
||||
|
||||
public override CommandResult Invoke()
|
||||
{
|
||||
OpenWith(_item.FullPath);
|
||||
|
||||
return CommandResult.GoHome();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using ManagedCommon;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Data;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Properties;
|
||||
using Microsoft.CmdPal.Extensions.Helpers;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Indexer.Commands;
|
||||
|
||||
internal sealed partial class ShowFileInFolderCommand : InvokableCommand
|
||||
{
|
||||
private readonly IndexerItem _item;
|
||||
|
||||
internal ShowFileInFolderCommand(IndexerItem item)
|
||||
{
|
||||
this._item = item;
|
||||
this.Name = Resources.Indexer_Command_ShowInFolder;
|
||||
this.Icon = new("\uE838");
|
||||
}
|
||||
|
||||
public override CommandResult Invoke()
|
||||
{
|
||||
if (File.Exists(_item.FullPath))
|
||||
{
|
||||
try
|
||||
{
|
||||
var argument = "/select, \"" + _item.FullPath + "\"";
|
||||
Process.Start("explorer.exe", argument);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Invoke exception: ", ex);
|
||||
}
|
||||
}
|
||||
|
||||
return CommandResult.GoHome();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Indexer.Data;
|
||||
|
||||
internal sealed class IndexerItem
|
||||
{
|
||||
internal string FullPath { get; init; }
|
||||
|
||||
internal string FileName { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Microsoft.CmdPal.Ext.Indexer.Commands;
|
||||
using Microsoft.CmdPal.Extensions.Helpers;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Indexer.Data;
|
||||
|
||||
internal sealed partial class IndexerListItem : ListItem
|
||||
{
|
||||
private readonly IndexerItem _indexerItem;
|
||||
|
||||
public IndexerListItem(IndexerItem indexerItem)
|
||||
: base(new OpenFileCommand(indexerItem))
|
||||
{
|
||||
_indexerItem = indexerItem;
|
||||
Title = indexerItem.FileName;
|
||||
Subtitle = indexerItem.FullPath;
|
||||
|
||||
MoreCommands = [
|
||||
new CommandContextItem(new OpenWithCommand(indexerItem)),
|
||||
new CommandContextItem(new ShowFileInFolderCommand(indexerItem)),
|
||||
new CommandContextItem(new CopyPathCommand(indexerItem)),
|
||||
new CommandContextItem(new OpenInConsoleCommand(indexerItem)),
|
||||
new CommandContextItem(new OpenPropertiesCommand(indexerItem)),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using ManagedCommon;
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.System.Com;
|
||||
using Windows.Win32.System.Search;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Indexer.Indexer;
|
||||
|
||||
internal static class DataSourceManager
|
||||
{
|
||||
private static readonly Guid CLSIDCollatorDataSource = new("9E175B8B-F52A-11D8-B9A5-505054503030");
|
||||
|
||||
private static IDBInitialize _dataSource;
|
||||
|
||||
public static IDBInitialize GetDataSource()
|
||||
{
|
||||
if (_dataSource == null)
|
||||
{
|
||||
InitializeDataSource();
|
||||
}
|
||||
|
||||
return _dataSource;
|
||||
}
|
||||
|
||||
private static bool InitializeDataSource()
|
||||
{
|
||||
var hr = PInvoke.CoCreateInstance(CLSIDCollatorDataSource, null, CLSCTX.CLSCTX_INPROC_SERVER, typeof(IDBInitialize).GUID, out var dataSourceObj);
|
||||
if (hr != 0)
|
||||
{
|
||||
Logger.LogError("CoCreateInstance failed: " + hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dataSourceObj == null)
|
||||
{
|
||||
Logger.LogError("CoCreateInstance failed: dataSourceObj is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
_dataSource = (IDBInitialize)dataSourceObj;
|
||||
_dataSource.Initialize();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using Windows.Win32.Storage.IndexServer;
|
||||
using Windows.Win32.System.Com.StructuredStorage;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Indexer.Indexer.OleDB;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct DBPROP
|
||||
{
|
||||
#pragma warning disable SA1307 // Accessible fields should begin with upper-case letter
|
||||
public uint dwPropertyID;
|
||||
public uint dwOptions;
|
||||
public uint dwStatus;
|
||||
public DBID colid;
|
||||
public PROPVARIANT vValue;
|
||||
#pragma warning restore SA1307 // Accessible fields should begin with upper-case letter
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Indexer.Indexer.OleDB;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct DBPROPIDSET
|
||||
{
|
||||
#pragma warning disable SA1307 // Accessible fields should begin with upper-case letter
|
||||
public IntPtr rgPropertyIDs; // Pointer to array of property IDs
|
||||
public uint cPropertyIDs; // Number of properties in array
|
||||
public Guid guidPropertySet; // GUID of the property set
|
||||
#pragma warning restore SA1307 // Accessible fields should begin with upper-case letter
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Indexer.Indexer.OleDB;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct DBPROPSET
|
||||
{
|
||||
#pragma warning disable SA1307 // Accessible fields should begin with upper-case letter
|
||||
public IntPtr rgProperties; // Pointer to an array of DBPROP
|
||||
public uint cProperties; // Number of properties in the array
|
||||
public Guid guidPropertySet; // GUID of the property set
|
||||
#pragma warning restore SA1307 // Accessible fields should begin with upper-case letter
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Indexer.Indexer.OleDB;
|
||||
|
||||
[ComImport]
|
||||
[Guid("0c733a7c-2a1c-11ce-ade5-00aa0044773d")]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
public interface IRowset
|
||||
{
|
||||
[PreserveSig]
|
||||
int AddRefRows(
|
||||
uint cRows,
|
||||
[In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] IntPtr[] rghRows,
|
||||
[Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] uint[] rgRefCounts,
|
||||
[Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] int[] rgRowStatus);
|
||||
|
||||
[PreserveSig]
|
||||
int GetData(
|
||||
IntPtr hRow,
|
||||
IntPtr hAccessor,
|
||||
IntPtr pData);
|
||||
|
||||
[PreserveSig]
|
||||
int GetNextRows(
|
||||
IntPtr hReserved,
|
||||
long lRowsOffset,
|
||||
long cRows,
|
||||
out uint pcRowsObtained,
|
||||
out IntPtr prghRows);
|
||||
|
||||
[PreserveSig]
|
||||
int ReleaseRows(
|
||||
uint cRows,
|
||||
[In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] IntPtr[] rghRows,
|
||||
IntPtr rgRowOptions,
|
||||
[Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] uint[] rgRefCounts,
|
||||
[Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] int[] rgRowStatus);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Indexer.Indexer.OleDB;
|
||||
|
||||
[ComImport]
|
||||
[Guid("0C733A55-2A1C-11CE-ADE5-00AA0044773D")]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
public interface IRowsetInfo
|
||||
{
|
||||
[PreserveSig]
|
||||
int GetProperties(
|
||||
uint cPropertyIDSets,
|
||||
[In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] DBPROPIDSET[] rgPropertyIDSets,
|
||||
out ulong pcPropertySets,
|
||||
out IntPtr prgPropertySets);
|
||||
|
||||
[PreserveSig]
|
||||
int GetReferencedRowset(
|
||||
uint iOrdinal,
|
||||
[In] ref Guid riid,
|
||||
[Out, MarshalAs(UnmanagedType.Interface)] out object ppReferencedRowset);
|
||||
|
||||
[PreserveSig]
|
||||
int GetSpecification(
|
||||
[In] ref Guid riid,
|
||||
[Out, MarshalAs(UnmanagedType.Interface)] out object ppSpecification);
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using ManagedCommon;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Indexer.Utils;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Native;
|
||||
using Windows.Win32.System.Com;
|
||||
using Windows.Win32.System.Com.StructuredStorage;
|
||||
using Windows.Win32.UI.Shell.PropertiesSystem;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Indexer.Indexer;
|
||||
|
||||
internal sealed class SearchResult
|
||||
{
|
||||
public string ItemDisplayName { get; init; }
|
||||
|
||||
public string ItemUrl { get; init; }
|
||||
|
||||
public string LaunchUri { get; init; }
|
||||
|
||||
public bool IsFolder { get; init; }
|
||||
|
||||
public SearchResult(string name, string url, string filePath, bool isFolder)
|
||||
{
|
||||
ItemDisplayName = name;
|
||||
ItemUrl = url;
|
||||
IsFolder = isFolder;
|
||||
|
||||
if (LaunchUri == null || LaunchUri.Length == 0)
|
||||
{
|
||||
// Launch the file with the default app, so use the file path
|
||||
LaunchUri = filePath;
|
||||
}
|
||||
}
|
||||
|
||||
public static unsafe SearchResult Create(IPropertyStore propStore)
|
||||
{
|
||||
try
|
||||
{
|
||||
var key = NativeHelpers.PropertyKeys.PKEYItemNameDisplay;
|
||||
propStore.GetValue(&key, out var itemNameDisplay);
|
||||
|
||||
key = NativeHelpers.PropertyKeys.PKEYItemUrl;
|
||||
propStore.GetValue(&key, out var itemUrl);
|
||||
|
||||
key = NativeHelpers.PropertyKeys.PKEYKindText;
|
||||
propStore.GetValue(&key, out var kindText);
|
||||
|
||||
var filePath = GetFilePath(ref itemUrl);
|
||||
var isFolder = IsFoder(ref kindText);
|
||||
|
||||
// Create the actual result object
|
||||
var searchResult = new SearchResult(
|
||||
GetStringFromPropVariant(ref itemNameDisplay),
|
||||
GetStringFromPropVariant(ref itemUrl),
|
||||
filePath,
|
||||
isFolder);
|
||||
|
||||
return searchResult;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Failed to get property values from propStore.", ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsFoder(ref PROPVARIANT kindText)
|
||||
{
|
||||
var kindString = GetStringFromPropVariant(ref kindText);
|
||||
return string.Equals(kindString, "Folder", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private static string GetFilePath(ref PROPVARIANT itemUrl)
|
||||
{
|
||||
var filePath = GetStringFromPropVariant(ref itemUrl);
|
||||
filePath = UrlToFilePathConverter.Convert(filePath);
|
||||
return filePath;
|
||||
}
|
||||
|
||||
private static string GetStringFromPropVariant(ref PROPVARIANT propVariant)
|
||||
{
|
||||
if (propVariant.Anonymous.Anonymous.vt == VARENUM.VT_LPWSTR)
|
||||
{
|
||||
var pwszVal = propVariant.Anonymous.Anonymous.Anonymous.pwszVal;
|
||||
if (pwszVal != null)
|
||||
{
|
||||
return pwszVal.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Indexer.Indexer.Utils;
|
||||
|
||||
internal sealed class QueryStringBuilder
|
||||
{
|
||||
private const string Select = "SELECT";
|
||||
private const string Properties = "System.ItemUrl, System.ItemNameDisplay, path, System.Search.EntryID, System.Kind, System.KindText, System.Search.GatherTime, System.Search.QueryPropertyHits";
|
||||
private const string FromIndex = "FROM SystemIndex WHERE";
|
||||
private const string ScopeFileConditions = "SCOPE='file:'";
|
||||
private const string OrderConditions = "ORDER BY System.Search.Rank, System.DateModified, System.ItemNameDisplay DESC";
|
||||
private const string SelectQueryWithScope = Select + " " + Properties + " " + FromIndex + " (" + ScopeFileConditions + ")";
|
||||
private const string SelectQueryWithScopeAndOrderConditions = SelectQueryWithScope + " " + OrderConditions;
|
||||
|
||||
public static string GeneratePrimingQuery() => SelectQueryWithScopeAndOrderConditions;
|
||||
|
||||
public static string GenerateQuery(string searchText, uint whereId)
|
||||
{
|
||||
var queryStr = new StringBuilder(SelectQueryWithScope);
|
||||
|
||||
// Filter by item name display only
|
||||
if (!string.IsNullOrEmpty(searchText))
|
||||
{
|
||||
queryStr.Append(" AND (CONTAINS(System.ItemNameDisplay, '\"")
|
||||
.Append(searchText)
|
||||
.Append("*\"'))");
|
||||
}
|
||||
|
||||
// Always add reuse where to the query
|
||||
queryStr.Append(" AND ReuseWhere(")
|
||||
.Append(whereId.ToString(CultureInfo.InvariantCulture))
|
||||
.Append(") ")
|
||||
.Append(OrderConditions);
|
||||
|
||||
return queryStr.ToString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Indexer.Indexer.Utils;
|
||||
|
||||
public class UrlToFilePathConverter
|
||||
{
|
||||
public static string Convert(string url)
|
||||
{
|
||||
var result = url.Replace('/', '\\'); // replace all '/' to '\'
|
||||
|
||||
var fileProtocolString = "file:";
|
||||
var indexProtocolFound = url.IndexOf(fileProtocolString, StringComparison.CurrentCultureIgnoreCase);
|
||||
|
||||
if (indexProtocolFound != -1 && (indexProtocolFound + fileProtocolString.Length) < url.Length)
|
||||
{
|
||||
result = result[(indexProtocolFound + fileProtocolString.Length)..];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\..\..\..\Common.Dotnet.CsWinRT.props" />
|
||||
<PropertyGroup>
|
||||
<RootNamespace>Microsoft.CmdPal.Ext.Indexer</RootNamespace>
|
||||
|
||||
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\WinUI3Apps\CmdPal</OutputPath>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Windows.CsWin32">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<ProjectReference Include="..\..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
|
||||
|
||||
<ProjectReference Include="..\..\extensionsdk\Microsoft.CmdPal.Extensions.Helpers\Microsoft.CmdPal.Extensions.Helpers.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Properties\Resources.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using Windows.Win32.UI.Shell.PropertiesSystem;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Indexer.Native;
|
||||
|
||||
internal sealed class NativeHelpers
|
||||
{
|
||||
public const uint SEEMASKINVOKEIDLIST = 12;
|
||||
|
||||
internal static class PropertyKeys
|
||||
{
|
||||
public static readonly PROPERTYKEY PKEYItemNameDisplay = new() { fmtid = new System.Guid("B725F130-47EF-101A-A5F1-02608C9EEBAC"), pid = 10 };
|
||||
public static readonly PROPERTYKEY PKEYItemUrl = new() { fmtid = new System.Guid("49691C90-7E17-101A-A91C-08002B2ECDA9"), pid = 9 };
|
||||
public static readonly PROPERTYKEY PKEYKindText = new() { fmtid = new System.Guid("F04BEF95-C585-4197-A2B7-DF46FDC9EE6D"), pid = 100 };
|
||||
}
|
||||
|
||||
internal static class OleDb
|
||||
{
|
||||
public static readonly Guid DbGuidDefault = new("C8B521FB-5CF3-11CE-ADE5-00AA0044773D");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ManagedCommon;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Data;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Indexer;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Properties;
|
||||
using Microsoft.CmdPal.Extensions;
|
||||
using Microsoft.CmdPal.Extensions.Helpers;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Indexer;
|
||||
|
||||
internal sealed partial class IndexerPage : DynamicListPage, IDisposable
|
||||
{
|
||||
private readonly Lock _lockObject = new(); // Lock object for synchronization
|
||||
private readonly List<IListItem> _indexerListItems = [];
|
||||
|
||||
private SearchQuery _searchQuery = new();
|
||||
|
||||
private uint _queryCookie = 10;
|
||||
|
||||
public IndexerPage()
|
||||
{
|
||||
Icon = new("\ue729");
|
||||
Name = Resources.Indexer_Title;
|
||||
PlaceholderText = Resources.Indexer_PlaceholderText;
|
||||
|
||||
Logger.InitializeLogger("\\CmdPal\\Indexer\\Logs");
|
||||
}
|
||||
|
||||
public override void UpdateSearchText(string oldSearch, string newSearch)
|
||||
{
|
||||
if (oldSearch != newSearch)
|
||||
{
|
||||
_ = Task.Run(() =>
|
||||
{
|
||||
Logger.LogDebug($"Update {oldSearch} -> {newSearch}");
|
||||
StartQuery(newSearch);
|
||||
RaiseItemsChanged(0);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public override IListItem[] GetItems() => DoGetItems();
|
||||
|
||||
private void StartQuery(string query)
|
||||
{
|
||||
if (query == string.Empty)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Stopwatch stopwatch = new();
|
||||
stopwatch.Start();
|
||||
Query(query);
|
||||
stopwatch.Stop();
|
||||
Logger.LogDebug($"Query time: {stopwatch.ElapsedMilliseconds} ms, query: \"{query}\"");
|
||||
}
|
||||
|
||||
private IListItem[] DoGetItems()
|
||||
{
|
||||
if (string.IsNullOrEmpty(SearchText))
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
Stopwatch stopwatch = new();
|
||||
stopwatch.Start();
|
||||
|
||||
lock (_lockObject)
|
||||
{
|
||||
if (_searchQuery != null)
|
||||
{
|
||||
var cookie = _searchQuery.Cookie;
|
||||
if (cookie == _queryCookie)
|
||||
{
|
||||
SearchResult result;
|
||||
while (!_searchQuery.SearchResults.IsEmpty && _searchQuery.SearchResults.TryDequeue(out result))
|
||||
{
|
||||
_indexerListItems.Add(new IndexerListItem(new IndexerItem
|
||||
{
|
||||
FileName = result.ItemDisplayName,
|
||||
FullPath = result.LaunchUri,
|
||||
})
|
||||
{
|
||||
Icon = new(result.IsFolder ? "\uE838" : "\uE8E5"),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stopwatch.Stop();
|
||||
Logger.LogDebug($"Build ListItems: {stopwatch.ElapsedMilliseconds} ms, results: {_indexerListItems.Count}, query: \"{SearchText}\"");
|
||||
|
||||
return [.. _indexerListItems];
|
||||
}
|
||||
|
||||
private uint Query(string searchText)
|
||||
{
|
||||
if (searchText == string.Empty)
|
||||
{
|
||||
return _queryCookie;
|
||||
}
|
||||
|
||||
_queryCookie++;
|
||||
lock (_lockObject)
|
||||
{
|
||||
_searchQuery.CancelOutstandingQueries();
|
||||
_searchQuery.SearchResults.Clear();
|
||||
_indexerListItems.Clear();
|
||||
|
||||
// Just forward on to the helper with the right callback for feeding us results
|
||||
// Set up the binding for the items
|
||||
_searchQuery.Execute(searchText, _queryCookie);
|
||||
}
|
||||
|
||||
// unlock
|
||||
// Wait for the query executed event
|
||||
_searchQuery.WaitForQueryCompletedEvent();
|
||||
|
||||
return _queryCookie;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_searchQuery = null;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
@@ -6,6 +6,7 @@ using Microsoft.CmdPal.Common.Services;
|
||||
using Microsoft.CmdPal.Ext.Apps.Programs;
|
||||
using Microsoft.CmdPal.Ext.Bookmarks;
|
||||
using Microsoft.CmdPal.Ext.Calc;
|
||||
using Microsoft.CmdPal.Ext.Indexer;
|
||||
using Microsoft.CmdPal.Ext.Registry;
|
||||
using Microsoft.CmdPal.Ext.Settings;
|
||||
using Microsoft.CmdPal.Ext.Shell;
|
||||
@@ -87,6 +88,7 @@ public partial class App : Application
|
||||
services.AddSingleton<ICommandProvider, RegistryCommandsProvider>();
|
||||
services.AddSingleton<ICommandProvider, WindowWalkerCommandsProvider>();
|
||||
services.AddSingleton<ICommandProvider, WindowsSettingsCommandsProvider>();
|
||||
services.AddSingleton<ICommandProvider, IndexerCommandsProvider>();
|
||||
|
||||
// Models
|
||||
services.AddSingleton<TopLevelCommandManager>();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\..\..\Common.Dotnet.CsWinRT.props" />
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
@@ -85,6 +85,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Exts\Microsoft.CmdPal.Ext.Indexer\Microsoft.CmdPal.Ext.Indexer.csproj" />
|
||||
<ProjectReference Include="..\Microsoft.CmdPal.Common\Microsoft.CmdPal.Common.csproj" />
|
||||
<ProjectReference Include="..\Microsoft.CmdPal.UI.ViewModels\Microsoft.CmdPal.UI.ViewModels.csproj" />
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\..\..\Common.Dotnet.CsWinRT.props" />
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
@@ -89,6 +89,7 @@
|
||||
<ProjectReference Include="..\Exts\Microsoft.CmdPal.Ext.Bookmark\Microsoft.CmdPal.Ext.Bookmarks.csproj" />
|
||||
<ProjectReference Include="..\Exts\Microsoft.CmdPal.Ext.Calc\Microsoft.CmdPal.Ext.Calc.csproj" />
|
||||
<ProjectReference Include="..\Exts\Microsoft.CmdPal.Ext.CmdPalSettings\Microsoft.CmdPal.Ext.Settings.csproj" />
|
||||
<ProjectReference Include="..\Exts\Microsoft.CmdPal.Ext.Indexer\Microsoft.CmdPal.Ext.Indexer.csproj" />
|
||||
<ProjectReference Include="..\Exts\Microsoft.CmdPal.Ext.Registry\Microsoft.CmdPal.Ext.Registry.csproj" />
|
||||
<ProjectReference Include="..\Exts\Microsoft.CmdPal.Ext.Shell\Microsoft.CmdPal.Ext.Shell.csproj" />
|
||||
<ProjectReference Include="..\Exts\Microsoft.CmdPal.Ext.WindowsServices\Microsoft.CmdPal.Ext.WindowsServices.csproj" />
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Collections.ObjectModel;
|
||||
using Microsoft.CmdPal.Ext.Apps.Programs;
|
||||
using Microsoft.CmdPal.Ext.Bookmarks;
|
||||
using Microsoft.CmdPal.Ext.Calc;
|
||||
using Microsoft.CmdPal.Ext.Indexer;
|
||||
using Microsoft.CmdPal.Ext.Registry;
|
||||
using Microsoft.CmdPal.Ext.Settings;
|
||||
using Microsoft.CmdPal.Ext.Shell;
|
||||
@@ -50,10 +51,11 @@ public sealed class MainViewModel : IDisposable
|
||||
|
||||
public event TypedEventHandler<object, ICommand?>? GoToCommandRequested;
|
||||
|
||||
private readonly Dictionary<string, CommandAlias> _aliases = new();
|
||||
private readonly Dictionary<string, CommandAlias> _aliases = [];
|
||||
|
||||
internal MainViewModel()
|
||||
{
|
||||
BuiltInCommands.Add(new IndexerCommandsProvider());
|
||||
BuiltInCommands.Add(new BookmarksCommandProvider());
|
||||
BuiltInCommands.Add(new CalculatorCommandProvider());
|
||||
BuiltInCommands.Add(new SettingsCommandProvider());
|
||||
@@ -98,11 +100,6 @@ public sealed class MainViewModel : IDisposable
|
||||
handlers?.Invoke(this, null);
|
||||
}
|
||||
|
||||
private static string CreateHash(string? title, string? subtitle)
|
||||
{
|
||||
return title + subtitle;
|
||||
}
|
||||
|
||||
public IEnumerable<IListItem> AppItems => LoadedApps ? Apps.GetItems() : [];
|
||||
|
||||
// Okay this is definitely bad - Evaluating this re-wraps every app in the list with a new wrapper, holy fuck that's stupid
|
||||
@@ -120,10 +117,7 @@ public sealed class MainViewModel : IDisposable
|
||||
_reloadCommandProvider.Dispose();
|
||||
}
|
||||
|
||||
private void AddAlias(CommandAlias a)
|
||||
{
|
||||
_aliases.Add(a.SearchPrefix, a);
|
||||
}
|
||||
private void AddAlias(CommandAlias a) => _aliases.Add(a.SearchPrefix, a);
|
||||
|
||||
public bool CheckAlias(string searchText)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,496 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using ManagedCommon;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Indexer.OleDB;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Indexer.Utils;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Native;
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.System.Com;
|
||||
using Windows.Win32.System.Search;
|
||||
using Windows.Win32.UI.Shell.PropertiesSystem;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Indexer.Indexer;
|
||||
|
||||
internal sealed class SearchQuery : IDisposable
|
||||
{
|
||||
private readonly Lock _lockObject = new(); // Lock object for synchronization
|
||||
private readonly DBPROPIDSET dbPropIdSet;
|
||||
|
||||
private const uint QueryTimerThreshold = 85;
|
||||
private uint reuseWhereID;
|
||||
private EventWaitHandle queryCompletedEvent;
|
||||
private Timer queryTpTimer;
|
||||
private IRowset currentRowset;
|
||||
private IRowset reuseRowset;
|
||||
|
||||
public uint Cookie { get; set; }
|
||||
|
||||
public string SearchText { get; private set; }
|
||||
|
||||
public ConcurrentQueue<SearchResult> SearchResults { get; private set; } = [];
|
||||
|
||||
public SearchQuery()
|
||||
{
|
||||
dbPropIdSet = new DBPROPIDSET
|
||||
{
|
||||
rgPropertyIDs = Marshal.AllocCoTaskMem(sizeof(uint)), // Allocate memory for the property ID array
|
||||
cPropertyIDs = 1,
|
||||
guidPropertySet = new Guid("AA6EE6B0-E828-11D0-B23E-00AA0047FC01"), // DBPROPSET_MSIDXS_ROWSETEXT,
|
||||
};
|
||||
|
||||
// Copy the property ID into the allocated memory
|
||||
Marshal.WriteInt32(dbPropIdSet.rgPropertyIDs, 8); // MSIDXSPROP_WHEREID
|
||||
|
||||
Init();
|
||||
}
|
||||
|
||||
private void Init()
|
||||
{
|
||||
// Create all the objects we will want cached
|
||||
try
|
||||
{
|
||||
queryTpTimer = new Timer(QueryTimerCallback, this, Timeout.Infinite, Timeout.Infinite);
|
||||
if (queryTpTimer == null)
|
||||
{
|
||||
Logger.LogError("Failed to create query timer");
|
||||
return;
|
||||
}
|
||||
|
||||
queryCompletedEvent = new EventWaitHandle(false, EventResetMode.ManualReset);
|
||||
if (queryCompletedEvent == null)
|
||||
{
|
||||
Logger.LogError("Failed to create query completed event");
|
||||
return;
|
||||
}
|
||||
|
||||
// Execute a synchronous query on file items to prime the index and keep that handle around
|
||||
PrimeIndexAndCacheWhereId();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Exception at SearchUXQueryHelper Init", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void WaitForQueryCompletedEvent() => queryCompletedEvent.WaitOne();
|
||||
|
||||
public void CancelOutstandingQueries()
|
||||
{
|
||||
Logger.LogDebug("Cancel query " + SearchText);
|
||||
|
||||
// Are we currently doing work? If so, let's cancel
|
||||
lock (_lockObject)
|
||||
{
|
||||
if (queryTpTimer != null)
|
||||
{
|
||||
queryTpTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
queryTpTimer.Dispose();
|
||||
queryTpTimer = null;
|
||||
}
|
||||
|
||||
Init();
|
||||
}
|
||||
}
|
||||
|
||||
public void Execute(string searchText, uint cookie)
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
if (queryTpTimer != null)
|
||||
{
|
||||
// We cancel the outstanding query callback and queue a new one every time
|
||||
queryTpTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
SearchText = searchText;
|
||||
Cookie = cookie;
|
||||
|
||||
// Queue query
|
||||
var fireTime = DateTime.UtcNow.AddMilliseconds(QueryTimerThreshold);
|
||||
var dueTime = fireTime - DateTime.UtcNow;
|
||||
queryTpTimer.Change(dueTime, Timeout.InfiniteTimeSpan);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void QueryTimerCallback(object state)
|
||||
{
|
||||
var pQueryHelper = (SearchQuery)state;
|
||||
pQueryHelper.ExecuteSyncInternal();
|
||||
}
|
||||
|
||||
private void ExecuteSyncInternal()
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
var queryStr = QueryStringBuilder.GenerateQuery(SearchText, reuseWhereID);
|
||||
try
|
||||
{
|
||||
// We need to generate a search query string with the search text the user entered above
|
||||
if (currentRowset != null)
|
||||
{
|
||||
if (reuseRowset != null)
|
||||
{
|
||||
Marshal.ReleaseComObject(reuseRowset);
|
||||
}
|
||||
|
||||
// We have a previous rowset, this means the user is typing and we should store this
|
||||
// recapture the where ID from this so the next ExecuteSync call will be faster
|
||||
reuseRowset = currentRowset;
|
||||
reuseWhereID = GetReuseWhereId(reuseRowset);
|
||||
}
|
||||
|
||||
currentRowset = ExecuteCommand(queryStr);
|
||||
|
||||
SearchResults.Clear();
|
||||
FetchRows();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Error executing query", ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
queryCompletedEvent.Set();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool HandleRow(IGetRow getRow, nuint rowHandle)
|
||||
{
|
||||
object propertyStorePtr = null;
|
||||
|
||||
try
|
||||
{
|
||||
getRow.GetRowFromHROW(null, rowHandle, typeof(IPropertyStore).GUID, out propertyStorePtr);
|
||||
|
||||
var propertyStore = (IPropertyStore)propertyStorePtr;
|
||||
if (propertyStore == null)
|
||||
{
|
||||
Logger.LogError("Failed to get IPropertyStore interface");
|
||||
return false;
|
||||
}
|
||||
|
||||
var searchResult = SearchResult.Create(propertyStore);
|
||||
if (searchResult == null)
|
||||
{
|
||||
Logger.LogError("Failed to create search result");
|
||||
return false;
|
||||
}
|
||||
|
||||
SearchResults.Enqueue(searchResult);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Error handling row", ex);
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Ensure the COM object is released if not returned
|
||||
if (propertyStorePtr != null)
|
||||
{
|
||||
Marshal.ReleaseComObject(propertyStorePtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ulong FetchRows()
|
||||
{
|
||||
if (currentRowset == null)
|
||||
{
|
||||
Logger.LogError("No rowset to fetch rows from");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (currentRowset is not IGetRow getRow)
|
||||
{
|
||||
Logger.LogError("Rowset does not support IGetRow interface");
|
||||
return 0;
|
||||
}
|
||||
|
||||
long batchSize = 5000; // Number of rows to fetch in each batch
|
||||
ulong fetched = 0;
|
||||
uint rowCountReturned;
|
||||
var prghRows = IntPtr.Zero;
|
||||
|
||||
try
|
||||
{
|
||||
do
|
||||
{
|
||||
var res = currentRowset.GetNextRows(IntPtr.Zero, 0, batchSize, out rowCountReturned, out prghRows);
|
||||
if (res < 0)
|
||||
{
|
||||
Logger.LogError($"Error fetching rows: {res}");
|
||||
break;
|
||||
}
|
||||
|
||||
if (rowCountReturned == 0)
|
||||
{
|
||||
// No more rows to fetch
|
||||
break;
|
||||
}
|
||||
|
||||
// Marshal the row handles
|
||||
var rowHandles = new IntPtr[rowCountReturned];
|
||||
Marshal.Copy(prghRows, rowHandles, 0, (int)rowCountReturned);
|
||||
|
||||
for (var i = 0; i < rowCountReturned; i++)
|
||||
{
|
||||
var rowHandle = Marshal.ReadIntPtr(prghRows, i * IntPtr.Size);
|
||||
if (!HandleRow(getRow, (nuint)rowHandle))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
res = currentRowset.ReleaseRows(rowCountReturned, rowHandles, IntPtr.Zero, null, null);
|
||||
if (res != 0)
|
||||
{
|
||||
Logger.LogError($"Error releasing rows: {res}");
|
||||
break;
|
||||
}
|
||||
|
||||
fetched += rowCountReturned;
|
||||
|
||||
Marshal.FreeCoTaskMem(prghRows);
|
||||
prghRows = IntPtr.Zero;
|
||||
}
|
||||
while (rowCountReturned > 0);
|
||||
|
||||
return fetched;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Error fetching rows", ex);
|
||||
return 0;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (prghRows != IntPtr.Zero)
|
||||
{
|
||||
Marshal.FreeCoTaskMem(prghRows);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void PrimeIndexAndCacheWhereId()
|
||||
{
|
||||
var queryStr = QueryStringBuilder.GeneratePrimingQuery();
|
||||
var rowset = ExecuteCommand(queryStr);
|
||||
if (rowset != null)
|
||||
{
|
||||
if (reuseRowset != null)
|
||||
{
|
||||
Marshal.ReleaseComObject(reuseRowset);
|
||||
}
|
||||
|
||||
reuseRowset = rowset;
|
||||
reuseWhereID = GetReuseWhereId(reuseRowset);
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe IRowset ExecuteCommand(string queryStr)
|
||||
{
|
||||
object sessionPtr = null;
|
||||
object commandPtr = null;
|
||||
|
||||
try
|
||||
{
|
||||
var session = (IDBCreateSession)DataSourceManager.GetDataSource();
|
||||
session.CreateSession(null, typeof(IDBCreateCommand).GUID, out sessionPtr);
|
||||
if (sessionPtr == null)
|
||||
{
|
||||
Logger.LogError("CreateSession failed");
|
||||
return null;
|
||||
}
|
||||
|
||||
var createCommand = (IDBCreateCommand)sessionPtr;
|
||||
createCommand.CreateCommand(null, typeof(ICommandText).GUID, out commandPtr);
|
||||
if (commandPtr == null)
|
||||
{
|
||||
Logger.LogError("CreateCommand failed");
|
||||
return null;
|
||||
}
|
||||
|
||||
var commandText = (ICommandText)commandPtr;
|
||||
if (commandText == null)
|
||||
{
|
||||
Logger.LogError("Failed to get ICommandText interface");
|
||||
return null;
|
||||
}
|
||||
|
||||
commandText.SetCommandText(in NativeHelpers.OleDb.DbGuidDefault, queryStr);
|
||||
commandText.Execute(null, typeof(IRowset).GUID, null, null, out var rowsetPointer);
|
||||
|
||||
return rowsetPointer as IRowset;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Unexpected error.", ex);
|
||||
return null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Release the command pointer
|
||||
if (commandPtr != null)
|
||||
{
|
||||
Marshal.ReleaseComObject(commandPtr);
|
||||
}
|
||||
|
||||
// Release the session pointer
|
||||
if (sessionPtr != null)
|
||||
{
|
||||
Marshal.ReleaseComObject(sessionPtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IRowsetInfo GetRowsetInfo(IRowset rowset)
|
||||
{
|
||||
if (rowset == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var rowsetPtr = IntPtr.Zero;
|
||||
var rowsetInfoPtr = IntPtr.Zero;
|
||||
|
||||
try
|
||||
{
|
||||
// Get the IUnknown pointer for the IRowset object
|
||||
rowsetPtr = Marshal.GetIUnknownForObject(rowset);
|
||||
|
||||
// Query for IRowsetInfo interface
|
||||
var rowsetInfoGuid = typeof(IRowsetInfo).GUID;
|
||||
var res = Marshal.QueryInterface(rowsetPtr, in rowsetInfoGuid, out rowsetInfoPtr);
|
||||
if (res != 0)
|
||||
{
|
||||
Logger.LogError($"Error getting IRowsetInfo interface: {res}");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Marshal the interface pointer to the actual IRowsetInfo object
|
||||
var rowsetInfo = (IRowsetInfo)Marshal.GetObjectForIUnknown(rowsetInfoPtr);
|
||||
return rowsetInfo;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError($"Exception occurred while getting IRowsetInfo. ", ex);
|
||||
return null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Release the IRowsetInfo pointer if it was obtained
|
||||
if (rowsetInfoPtr != IntPtr.Zero)
|
||||
{
|
||||
Marshal.Release(rowsetInfoPtr); // Release the IRowsetInfo pointer
|
||||
}
|
||||
|
||||
// Release the IUnknown pointer for the IRowset object
|
||||
if (rowsetPtr != IntPtr.Zero)
|
||||
{
|
||||
Marshal.Release(rowsetPtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private DBPROP? GetPropset(IRowsetInfo rowsetInfo)
|
||||
{
|
||||
var prgPropSetsPtr = IntPtr.Zero;
|
||||
|
||||
try
|
||||
{
|
||||
ulong cPropertySets;
|
||||
var res = rowsetInfo.GetProperties(1, [dbPropIdSet], out cPropertySets, out prgPropSetsPtr);
|
||||
if (res != 0)
|
||||
{
|
||||
Logger.LogError($"Error getting properties: {res}");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (cPropertySets == 0 || prgPropSetsPtr == IntPtr.Zero)
|
||||
{
|
||||
Logger.LogError("No property sets returned");
|
||||
return null;
|
||||
}
|
||||
|
||||
var firstPropSetPtr = new IntPtr(prgPropSetsPtr.ToInt64());
|
||||
var propSet = Marshal.PtrToStructure<DBPROPSET>(firstPropSetPtr);
|
||||
if (propSet.cProperties == 0 || propSet.rgProperties == IntPtr.Zero)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var propPtr = new IntPtr(propSet.rgProperties.ToInt64());
|
||||
var prop = Marshal.PtrToStructure<DBPROP>(propPtr);
|
||||
return prop;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError($"Exception occurred while getting properties,", ex);
|
||||
return null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Free the property sets pointer returned by GetProperties, if necessary
|
||||
if (prgPropSetsPtr != IntPtr.Zero)
|
||||
{
|
||||
Marshal.FreeCoTaskMem(prgPropSetsPtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private uint GetReuseWhereId(IRowset rowset)
|
||||
{
|
||||
var rowsetInfo = GetRowsetInfo(rowset);
|
||||
if (rowsetInfo == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
var prop = GetPropset(rowsetInfo);
|
||||
if (prop == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (prop?.vValue.Anonymous.Anonymous.vt == VARENUM.VT_UI4)
|
||||
{
|
||||
var value = prop?.vValue.Anonymous.Anonymous.Anonymous.ulVal;
|
||||
return (uint)value;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
CancelOutstandingQueries();
|
||||
|
||||
// Free the allocated memory for rgPropertyIDs
|
||||
if (dbPropIdSet.rgPropertyIDs != IntPtr.Zero)
|
||||
{
|
||||
Marshal.FreeCoTaskMem(dbPropIdSet.rgPropertyIDs);
|
||||
}
|
||||
|
||||
if (reuseRowset != null)
|
||||
{
|
||||
Marshal.ReleaseComObject(reuseRowset);
|
||||
reuseRowset = null;
|
||||
}
|
||||
|
||||
if (currentRowset != null)
|
||||
{
|
||||
Marshal.ReleaseComObject(currentRowset);
|
||||
currentRowset = null;
|
||||
}
|
||||
|
||||
queryCompletedEvent?.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Microsoft.CmdPal.Ext.Indexer.Properties;
|
||||
using Microsoft.CmdPal.Extensions;
|
||||
using Microsoft.CmdPal.Extensions.Helpers;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Indexer;
|
||||
|
||||
public partial class IndexerCommandsProvider : CommandProvider
|
||||
{
|
||||
public IndexerCommandsProvider()
|
||||
{
|
||||
DisplayName = Resources.IndexerCommandsProvider_DisplayName;
|
||||
}
|
||||
|
||||
public override ICommandItem[] TopLevelCommands()
|
||||
{
|
||||
return [
|
||||
new CommandItem(new IndexerPage())
|
||||
{
|
||||
Title = Resources.Indexer_Title,
|
||||
Subtitle = Resources.Indexer_Subtitle,
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
DBID
|
||||
SHOW_WINDOW_CMD
|
||||
CoCreateInstance
|
||||
GetErrorInfo
|
||||
ICommandText
|
||||
IDBCreateCommand
|
||||
IDBCreateSession
|
||||
IDBInitialize
|
||||
IGetRow
|
||||
IPropertyStore
|
||||
ShellExecuteEx
|
||||
153
src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Indexer/Properties/Resources.Designer.cs
generated
Normal file
153
src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Indexer/Properties/Resources.Designer.cs
generated
Normal file
@@ -0,0 +1,153 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Indexer.Properties {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.CmdPal.Ext.Indexer.Properties.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Copy path.
|
||||
/// </summary>
|
||||
internal static string Indexer_Command_CopyPath {
|
||||
get {
|
||||
return ResourceManager.GetString("Indexer_Command_CopyPath", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Open.
|
||||
/// </summary>
|
||||
internal static string Indexer_Command_OpenFile {
|
||||
get {
|
||||
return ResourceManager.GetString("Indexer_Command_OpenFile", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Open path in console.
|
||||
/// </summary>
|
||||
internal static string Indexer_Command_OpenPathInConsole {
|
||||
get {
|
||||
return ResourceManager.GetString("Indexer_Command_OpenPathInConsole", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Properties.
|
||||
/// </summary>
|
||||
internal static string Indexer_Command_OpenProperties {
|
||||
get {
|
||||
return ResourceManager.GetString("Indexer_Command_OpenProperties", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Open with.
|
||||
/// </summary>
|
||||
internal static string Indexer_Command_OpenWith {
|
||||
get {
|
||||
return ResourceManager.GetString("Indexer_Command_OpenWith", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Show in folder.
|
||||
/// </summary>
|
||||
internal static string Indexer_Command_ShowInFolder {
|
||||
get {
|
||||
return ResourceManager.GetString("Indexer_Command_ShowInFolder", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Search for files and folders....
|
||||
/// </summary>
|
||||
internal static string Indexer_PlaceholderText {
|
||||
get {
|
||||
return ResourceManager.GetString("Indexer_PlaceholderText", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Search indexed files.
|
||||
/// </summary>
|
||||
internal static string Indexer_Subtitle {
|
||||
get {
|
||||
return ResourceManager.GetString("Indexer_Subtitle", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Indexer.
|
||||
/// </summary>
|
||||
internal static string Indexer_Title {
|
||||
get {
|
||||
return ResourceManager.GetString("Indexer_Title", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Indexer commands.
|
||||
/// </summary>
|
||||
internal static string IndexerCommandsProvider_DisplayName {
|
||||
get {
|
||||
return ResourceManager.GetString("IndexerCommandsProvider_DisplayName", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="IndexerCommandsProvider_DisplayName" xml:space="preserve">
|
||||
<value>Indexer commands</value>
|
||||
</data>
|
||||
<data name="Indexer_Command_CopyPath" xml:space="preserve">
|
||||
<value>Copy path</value>
|
||||
</data>
|
||||
<data name="Indexer_Command_OpenFile" xml:space="preserve">
|
||||
<value>Open</value>
|
||||
</data>
|
||||
<data name="Indexer_Command_OpenPathInConsole" xml:space="preserve">
|
||||
<value>Open path in console</value>
|
||||
</data>
|
||||
<data name="Indexer_Command_OpenProperties" xml:space="preserve">
|
||||
<value>Properties</value>
|
||||
</data>
|
||||
<data name="Indexer_Command_OpenWith" xml:space="preserve">
|
||||
<value>Open with</value>
|
||||
</data>
|
||||
<data name="Indexer_Command_ShowInFolder" xml:space="preserve">
|
||||
<value>Show in folder</value>
|
||||
</data>
|
||||
<data name="Indexer_PlaceholderText" xml:space="preserve">
|
||||
<value>Search for files and folders...</value>
|
||||
</data>
|
||||
<data name="Indexer_Subtitle" xml:space="preserve">
|
||||
<value>Search indexed files</value>
|
||||
</data>
|
||||
<data name="Indexer_Title" xml:space="preserve">
|
||||
<value>Indexer</value>
|
||||
</data>
|
||||
</root>
|
||||
Reference in New Issue
Block a user