Skip to content

Commit b02e53d

Browse files
authored
CmdPal: Update the shell provider to be run (#47642)
This PR updates the shell command provider to work (almost) exactly like run. The current shell provider is close, but not technically correct. It does enumerate files. Sure. But as it turns out, it doesn't enumerate things **exactly** correctly. It doesn't handle network paths super well. It doesn't handle NTFS file paths. Basically, there's a lot of weird edge cases in the way the run dialog enumerates file paths for suggestions. And the only way to match that is to just use the code from the old run dialog. This is code that is taken pretty verbatim from the new run dialog. Instead of trying to enumerate paths manually and shellexecuting command lines, We're using the actual APIs that the original run dialog used, more or less. They've been pretty much ported to C#. This should make us feel just as correct as the original run dialog did. And exactly the same as the new Run dialog. The one major change is the introduction of a static item at the top of the list for running the command that the user typed. This command is used to just immediately take whatever is in the search box and fire it off as the command the user typed. This is essentially what happens with the run dialog. When you press the button, we run the command in the text box. See: [The new Run dialog: faster, cleaner, and more capable - Windows Command Line](https://devblogs.microsoft.com/commandline/the-new-run-dialog-faster-cleaner-and-more-capable/) Honestly, most of this PR is just deleting the files we no longer need from the shell list provider and adding the tests from the OS side here. I also had to update CsWinRT for this.
1 parent 95ef94d commit b02e53d

40 files changed

Lines changed: 4162 additions & 1226 deletions

File tree

.github/actions/spell-check/allow/code.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,8 +353,15 @@ WINDOWPOS
353353
WINEVENTPROC
354354
WORKERW
355355
FULLSCREENAPP
356+
ACLO
357+
CACLI
358+
DOENVSUBST
359+
FILESYSONLY
360+
URLIS
361+
WAITTIMEOUT
356362
DEFAULTTONEAREST
357363

364+
358365
# COM/WinRT interface prefixes and type fragments
359366
BAlt
360367
BShift

Directory.Packages.props

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project>
1+
<Project>
22
<PropertyGroup>
33
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
44
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
@@ -69,7 +69,7 @@
6969
<PackageVersion Include="Microsoft.Win32.SystemEvents" Version="10.0.7" />
7070
<PackageVersion Include="Microsoft.WindowsPackageManager.ComInterop" Version="1.10.340" />
7171
<PackageVersion Include="Microsoft.Windows.Compatibility" Version="10.0.7" />
72-
<PackageVersion Include="Microsoft.Windows.CsWin32" Version="0.3.183" />
72+
<PackageVersion Include="Microsoft.Windows.CsWin32" Version="0.3.269" />
7373
<!-- CsWinRT version needs to be set to have a WinRT.Runtime.dll at the same version contained inside the NET SDK we're currently building on CI. -->
7474
<!--
7575
TODO: in Common.Dotnet.CsWinRT.props, on upgrade, verify RemoveCsWinRTPackageAnalyzer is no longer needed.

src/modules/cmdpal/Microsoft.CmdPal.Common/Services/IRunHistoryService.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ namespace Microsoft.CmdPal.Common.Services;
66

77
public interface IRunHistoryService
88
{
9+
long RunCommand(string commandLine, string workingDir, bool asAdmin, ulong hwnd);
10+
911
/// <summary>
1012
/// Gets the run history.
1113
/// </summary>
@@ -22,6 +24,13 @@ public interface IRunHistoryService
2224
/// </summary>
2325
/// <param name="item">The run history item to add.</param>
2426
void AddRunHistoryItem(string item);
27+
28+
/// <summary>
29+
/// Parses a command line into its components.
30+
/// </summary>
31+
ParseCommandlineResult ParseCommandline(string commandLine, string workingDirectory);
32+
33+
string QualifyCommandLineDirectory(string commandLine, string fullFilePath, string defaultDirectory);
2534
}
2635

2736
public interface ITelemetryService
@@ -31,4 +40,16 @@ public interface ITelemetryService
3140
void LogRunCommand(string command, bool asAdmin, bool success);
3241

3342
void LogOpenUri(string uri, bool isWeb, bool success);
43+
44+
void LogEvent(string eventName, IDictionary<string, object>? properties = null);
45+
}
46+
47+
public struct ParseCommandlineResult
48+
{
49+
public int Result; // HRESULT
50+
public bool IsUri;
51+
public string FilePath;
52+
public string Arguments;
53+
54+
public bool Success => Result == 0;
3455
}

src/modules/cmdpal/Microsoft.CmdPal.Common/Services/Sanitizer/ErrorReportSanitizer.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) Microsoft Corporation
1+
// Copyright (c) Microsoft Corporation
22
// The Microsoft Corporation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

@@ -22,8 +22,7 @@ private static void OnGuardrailTriggered(GuardrailEventArgs eventArgs)
2222
private static IEnumerable<ISanitizationRuleProvider> BuildProviders()
2323
{
2424
// Order matters
25-
return
26-
[
25+
return (ISanitizationRuleProvider[])[
2726
new PiiRuleProvider(),
2827
new UrlRuleProvider(),
2928
new NetworkRuleProvider(),

src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Models/ExtensionWrapper.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ public async Task<IEnumerable<T>> GetListOfProvidersAsync<T>()
192192
}
193193
else if (supportedProviders is T singleProviderSupported)
194194
{
195-
return [singleProviderSupported];
195+
return (T[])[singleProviderSupported];
196196
}
197197

198198
return Enumerable.Empty<T>();

src/modules/cmdpal/Microsoft.CmdPal.UI/Events/RunEvents.cs

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,5 +76,210 @@ public CmdPalOpenUri(string uri, bool isWeb, bool success)
7676
}
7777
}
7878

79+
[EventData]
80+
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
81+
public class CmdPalRunBuildListPathResolution : EventBase, IEvent
82+
{
83+
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
84+
85+
public string NewSearch { get; set; }
86+
87+
public string CorrectedSearchText { get; set; }
88+
89+
public string Expanded { get; set; }
90+
91+
public bool WithLeadingTilde { get; set; }
92+
93+
public bool CouldResolvePath { get; set; }
94+
95+
public bool IsFile { get; set; }
96+
97+
public long DurationMs { get; set; }
98+
99+
public int Result { get; set; }
100+
101+
public CmdPalRunBuildListPathResolution(
102+
string newSearch,
103+
string correctedSearchText,
104+
string expanded,
105+
bool withLeadingTilde,
106+
bool couldResolvePath,
107+
bool isFile,
108+
long durationMs,
109+
int result)
110+
{
111+
EventName = "CmdPal_Run_BuildListPathResolution";
112+
NewSearch = newSearch;
113+
CorrectedSearchText = correctedSearchText;
114+
Expanded = expanded;
115+
WithLeadingTilde = withLeadingTilde;
116+
CouldResolvePath = couldResolvePath;
117+
IsFile = isFile;
118+
DurationMs = durationMs;
119+
Result = result;
120+
}
121+
}
122+
123+
[EventData]
124+
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
125+
public class CmdPalRunCreatePathItemsResolvedPath : EventBase, IEvent
126+
{
127+
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
128+
129+
public string FullFilePath { get; set; }
130+
131+
public string SearchText { get; set; }
132+
133+
public string DirectoryPath { get; set; }
134+
135+
public CmdPalRunCreatePathItemsResolvedPath(string fullFilePath, string searchText, string directoryPath)
136+
{
137+
EventName = "CmdPal_Run_CreatePathItemsResolvedPath";
138+
FullFilePath = fullFilePath;
139+
SearchText = searchText;
140+
DirectoryPath = directoryPath;
141+
}
142+
}
143+
144+
[EventData]
145+
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
146+
public class CmdPalRunCreatePathItemsFiltered : EventBase, IEvent
147+
{
148+
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
149+
150+
public string Dir { get; set; }
151+
152+
public string FuzzyString { get; set; }
153+
154+
public int FilteredCount { get; set; }
155+
156+
public CmdPalRunCreatePathItemsFiltered(string dir, string fuzzyString, int filteredCount)
157+
{
158+
EventName = "CmdPal_Run_CreatePathItemsFiltered";
159+
Dir = dir;
160+
FuzzyString = fuzzyString;
161+
FilteredCount = filteredCount;
162+
}
163+
}
164+
165+
[EventData]
166+
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
167+
public class CmdPalRunCreatePathItemsChangedDirectory : EventBase, IEvent
168+
{
169+
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
170+
171+
public string OldDir { get; set; }
172+
173+
public string NewDir { get; set; }
174+
175+
public CmdPalRunCreatePathItemsChangedDirectory(string oldDir, string newDir)
176+
{
177+
EventName = "CmdPal_Run_CreatePathItemsChangedDirectory";
178+
OldDir = oldDir;
179+
NewDir = newDir;
180+
}
181+
}
182+
183+
[EventData]
184+
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
185+
public class CmdPalRunBuildItemsForDirectory : EventBase, IEvent
186+
{
187+
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
188+
189+
public string Dir { get; set; }
190+
191+
public int FileCount { get; set; }
192+
193+
public CmdPalRunBuildItemsForDirectory(string dir, int fileCount)
194+
{
195+
EventName = "CmdPal_Run_BuildItemsForDirectory";
196+
Dir = dir;
197+
FileCount = fileCount;
198+
}
199+
}
200+
201+
[EventData]
202+
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
203+
public class CmdPalRunLoadHistory : EventBase, IEvent
204+
{
205+
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
206+
207+
public int ItemsToLoad { get; set; }
208+
209+
public int ItemsLoaded { get; set; }
210+
211+
public long DurationMs { get; set; }
212+
213+
public CmdPalRunLoadHistory(int itemsToLoad, int itemsLoaded, long durationMs)
214+
{
215+
EventName = "CmdPal_Run_LoadHistory";
216+
ItemsToLoad = itemsToLoad;
217+
ItemsLoaded = itemsLoaded;
218+
DurationMs = durationMs;
219+
}
220+
}
221+
222+
[EventData]
223+
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
224+
public class CmdPalRunLoadHistoryItem : EventBase, IEvent
225+
{
226+
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
227+
228+
public string Type { get; set; }
229+
230+
public bool TimedOut { get; set; }
231+
232+
public long TotalMs { get; set; }
233+
234+
public long ParseMs { get; set; }
235+
236+
public bool IsUri { get; set; }
237+
238+
public string Target { get; set; }
239+
240+
public string Args { get; set; }
241+
242+
public int ParseResult { get; set; }
243+
244+
public CmdPalRunLoadHistoryItem(
245+
string type,
246+
bool timedOut,
247+
long totalMs,
248+
long parseMs,
249+
bool isUri,
250+
string target,
251+
string args,
252+
int parseResult)
253+
{
254+
EventName = "CmdPal_Run_LoadHistoryItem";
255+
Type = type;
256+
TimedOut = timedOut;
257+
TotalMs = totalMs;
258+
ParseMs = parseMs;
259+
IsUri = isUri;
260+
Target = target;
261+
Args = args;
262+
ParseResult = parseResult;
263+
}
264+
}
265+
266+
[EventData]
267+
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
268+
public class CmdPalGenericLogEvent : EventBase, IEvent
269+
{
270+
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
271+
272+
public string Name { get; set; }
273+
274+
public string Message { get; set; }
275+
276+
public CmdPalGenericLogEvent(string name, string message)
277+
{
278+
EventName = "CmdPal_GenericLogEvent";
279+
Name = name;
280+
Message = message;
281+
}
282+
}
283+
79284
#pragma warning restore SA1649 // File name should match first type name
80285
#pragma warning restore SA1402 // File may only contain a single type

0 commit comments

Comments
 (0)