[PT Run] Run as user feature (#17283)

* run as different user

* fix tests

* Update src/modules/launcher/Plugins/Microsoft.Plugin.Shell/Main.cs

* fix typo
This commit is contained in:
Heiko
2022-04-04 11:37:08 +02:00
committed by GitHub
parent 12282a8dc7
commit e444881320
17 changed files with 205 additions and 45 deletions

View File

@@ -28,7 +28,7 @@ namespace Microsoft.Plugin.Indexer
File,
}
// Extensions for adding run as admin context menu item for applications
// Extensions for adding run as admin and run as other user context menu item for applications
private readonly string[] appExtensions = { ".exe", ".bat", ".appref-ms", ".lnk" };
public ContextMenuLoader(PluginInitContext context)
@@ -53,6 +53,7 @@ namespace Microsoft.Plugin.Indexer
if (CanFileBeRunAsAdmin(record.Path))
{
contextMenus.Add(CreateRunAsAdminContextMenu(record));
contextMenus.Add(CreateRunAsUserContextMenu(record));
}
contextMenus.Add(new ContextMenuResult
@@ -145,6 +146,34 @@ namespace Microsoft.Plugin.Indexer
};
}
// Function to add the context menu item to run as admin
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "We want to keep the process alive, and instead log the exception message")]
private static ContextMenuResult CreateRunAsUserContextMenu(SearchResult record)
{
return new ContextMenuResult
{
PluginName = Assembly.GetExecutingAssembly().GetName().Name,
Title = Properties.Resources.Microsoft_plugin_indexer_run_as_user,
Glyph = "\xE7EE",
FontFamily = "Segoe MDL2 Assets",
AcceleratorKey = Key.U,
AcceleratorModifiers = ModifierKeys.Control | ModifierKeys.Shift,
Action = _ =>
{
try
{
Task.Run(() => Helper.RunAsUser(record.Path));
return true;
}
catch (Exception e)
{
Log.Exception($"Failed to run {record.Path} as different user, {e.Message}", e, MethodBase.GetCurrentMethod().DeclaringType);
return false;
}
},
};
}
// Function to test if the file can be run as admin
private bool CanFileBeRunAsAdmin(string path)
{

View File

@@ -19,7 +19,7 @@ namespace Microsoft.Plugin.Indexer.Properties {
// 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", "16.0.0.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class Resources {
@@ -61,7 +61,7 @@ namespace Microsoft.Plugin.Indexer.Properties {
}
/// <summary>
/// Looks up a localized string similar to Disable drive detection warning.
/// Looks up a localized string similar to Disable non-indexed files warning.
/// </summary>
public static string disable_drive_detection_warning {
get {
@@ -186,6 +186,15 @@ namespace Microsoft.Plugin.Indexer.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Run as different user (Ctrl+Shift+U).
/// </summary>
public static string Microsoft_plugin_indexer_run_as_user {
get {
return ResourceManager.GetString("Microsoft_plugin_indexer_run_as_user", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Search.
/// </summary>

View File

@@ -162,4 +162,7 @@
<data name="disable_drive_detection_warning" xml:space="preserve">
<value>Disable non-indexed files warning</value>
</data>
</root>
<data name="Microsoft_plugin_indexer_run_as_user" xml:space="preserve">
<value>Run as different user (Ctrl+Shift+U)</value>
</data>
</root>

View File

@@ -466,10 +466,11 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs
List<ContextMenuResult> contextMenuResults = _pinnedWebpage.ContextMenus(string.Empty, mock.Object);
// Assert
Assert.AreEqual(3, contextMenuResults.Count);
Assert.AreEqual(4, contextMenuResults.Count);
Assert.AreEqual(Properties.Resources.wox_plugin_program_run_as_administrator, contextMenuResults[0].Title);
Assert.AreEqual(Properties.Resources.wox_plugin_program_open_containing_folder, contextMenuResults[1].Title);
Assert.AreEqual(Properties.Resources.wox_plugin_program_open_in_console, contextMenuResults[2].Title);
Assert.AreEqual(Properties.Resources.wox_plugin_program_run_as_user, contextMenuResults[1].Title);
Assert.AreEqual(Properties.Resources.wox_plugin_program_open_containing_folder, contextMenuResults[2].Title);
Assert.AreEqual(Properties.Resources.wox_plugin_program_open_in_console, contextMenuResults[3].Title);
}
[TestMethod]
@@ -497,10 +498,11 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs
List<ContextMenuResult> contextMenuResults = _chrome.ContextMenus(string.Empty, mock.Object);
// Assert
Assert.AreEqual(3, contextMenuResults.Count);
Assert.AreEqual(4, contextMenuResults.Count);
Assert.AreEqual(Properties.Resources.wox_plugin_program_run_as_administrator, contextMenuResults[0].Title);
Assert.AreEqual(Properties.Resources.wox_plugin_program_open_containing_folder, contextMenuResults[1].Title);
Assert.AreEqual(Properties.Resources.wox_plugin_program_open_in_console, contextMenuResults[2].Title);
Assert.AreEqual(Properties.Resources.wox_plugin_program_run_as_user, contextMenuResults[1].Title);
Assert.AreEqual(Properties.Resources.wox_plugin_program_open_containing_folder, contextMenuResults[2].Title);
Assert.AreEqual(Properties.Resources.wox_plugin_program_open_in_console, contextMenuResults[3].Title);
}
[TestMethod]
@@ -513,10 +515,11 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs
List<ContextMenuResult> contextMenuResults = _cmdRunCommand.ContextMenus(string.Empty, mock.Object);
// Assert
Assert.AreEqual(3, contextMenuResults.Count);
Assert.AreEqual(4, contextMenuResults.Count);
Assert.AreEqual(Properties.Resources.wox_plugin_program_run_as_administrator, contextMenuResults[0].Title);
Assert.AreEqual(Properties.Resources.wox_plugin_program_open_containing_folder, contextMenuResults[1].Title);
Assert.AreEqual(Properties.Resources.wox_plugin_program_open_in_console, contextMenuResults[2].Title);
Assert.AreEqual(Properties.Resources.wox_plugin_program_run_as_user, contextMenuResults[1].Title);
Assert.AreEqual(Properties.Resources.wox_plugin_program_open_containing_folder, contextMenuResults[2].Title);
Assert.AreEqual(Properties.Resources.wox_plugin_program_open_in_console, contextMenuResults[3].Title);
}
[TestMethod]
@@ -529,10 +532,11 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs
List<ContextMenuResult> contextMenuResults = _dummyAppRefApp.ContextMenus(string.Empty, mock.Object);
// Assert
Assert.AreEqual(3, contextMenuResults.Count);
Assert.AreEqual(4, contextMenuResults.Count);
Assert.AreEqual(Properties.Resources.wox_plugin_program_run_as_administrator, contextMenuResults[0].Title);
Assert.AreEqual(Properties.Resources.wox_plugin_program_open_containing_folder, contextMenuResults[1].Title);
Assert.AreEqual(Properties.Resources.wox_plugin_program_open_in_console, contextMenuResults[2].Title);
Assert.AreEqual(Properties.Resources.wox_plugin_program_run_as_user, contextMenuResults[1].Title);
Assert.AreEqual(Properties.Resources.wox_plugin_program_open_containing_folder, contextMenuResults[2].Title);
Assert.AreEqual(Properties.Resources.wox_plugin_program_open_in_console, contextMenuResults[3].Title);
}
[TestMethod]
@@ -545,10 +549,11 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs
List<ContextMenuResult> contextMenuResults = _dummyShortcutApp.ContextMenus(string.Empty, mock.Object);
// Assert
Assert.AreEqual(3, contextMenuResults.Count);
Assert.AreEqual(4, contextMenuResults.Count);
Assert.AreEqual(Properties.Resources.wox_plugin_program_run_as_administrator, contextMenuResults[0].Title);
Assert.AreEqual(Properties.Resources.wox_plugin_program_open_containing_folder, contextMenuResults[1].Title);
Assert.AreEqual(Properties.Resources.wox_plugin_program_open_in_console, contextMenuResults[2].Title);
Assert.AreEqual(Properties.Resources.wox_plugin_program_run_as_user, contextMenuResults[1].Title);
Assert.AreEqual(Properties.Resources.wox_plugin_program_open_containing_folder, contextMenuResults[2].Title);
Assert.AreEqual(Properties.Resources.wox_plugin_program_open_in_console, contextMenuResults[3].Title);
}
[TestMethod]

View File

@@ -158,6 +158,8 @@ namespace Microsoft.Plugin.Program.Programs
return true;
},
});
// We don't add context menu to 'run as different user', because UWP applications normally installed per user and not for all users.
}
contextMenus.Add(

View File

@@ -266,7 +266,24 @@ namespace Microsoft.Plugin.Program.Programs
AcceleratorModifiers = ModifierKeys.Control | ModifierKeys.Shift,
Action = _ =>
{
var info = GetProcessStartInfo(queryArguments, true);
var info = GetProcessStartInfo(queryArguments, RunAsType.Administrator);
Task.Run(() => Main.StartProcess(Process.Start, info));
return true;
},
});
contextMenus.Add(new ContextMenuResult
{
PluginName = Assembly.GetExecutingAssembly().GetName().Name,
Title = Properties.Resources.wox_plugin_program_run_as_user,
Glyph = "\xE7EE",
FontFamily = "Segoe MDL2 Assets",
AcceleratorKey = Key.U,
AcceleratorModifiers = ModifierKeys.Control | ModifierKeys.Shift,
Action = _ =>
{
var info = GetProcessStartInfo(queryArguments, RunAsType.OtherUser);
Task.Run(() => Main.StartProcess(Process.Start, info));
return true;
@@ -317,7 +334,7 @@ namespace Microsoft.Plugin.Program.Programs
return contextMenus;
}
private ProcessStartInfo GetProcessStartInfo(string programArguments, bool runAsAdmin = false)
private ProcessStartInfo GetProcessStartInfo(string programArguments, RunAsType runAs = RunAsType.None)
{
return new ProcessStartInfo
{
@@ -325,10 +342,17 @@ namespace Microsoft.Plugin.Program.Programs
WorkingDirectory = ParentDirectory,
UseShellExecute = true,
Arguments = programArguments,
Verb = runAsAdmin ? "runas" : string.Empty,
Verb = runAs == RunAsType.Administrator ? "runAs" : runAs == RunAsType.OtherUser ? "runAsUser" : string.Empty,
};
}
private enum RunAsType
{
None,
Administrator,
OtherUser,
}
public override string ToString()
{
return ExecutableName;

View File

@@ -19,7 +19,7 @@ namespace Microsoft.Plugin.Program.Properties {
// 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", "16.0.0.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class Resources {
@@ -203,5 +203,14 @@ namespace Microsoft.Plugin.Program.Properties {
return ResourceManager.GetString("wox_plugin_program_run_as_administrator", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Run as different user (Ctrl+Shift+U).
/// </summary>
public static string wox_plugin_program_run_as_user {
get {
return ResourceManager.GetString("wox_plugin_program_run_as_user", resourceCulture);
}
}
}
}

View File

@@ -165,4 +165,7 @@
<data name="powertoys_run_plugin_program_uwp_failed" xml:space="preserve">
<value>Can't start UWP</value>
</data>
<data name="wox_plugin_program_run_as_user" xml:space="preserve">
<value>Run as different user (Ctrl+Shift+U)</value>
</data>
</root>

View File

@@ -153,19 +153,29 @@ namespace Microsoft.Plugin.Shell
return history.ToList();
}
private ProcessStartInfo PrepareProcessStartInfo(string command, bool runAsAdministrator = false)
private ProcessStartInfo PrepareProcessStartInfo(string command, RunAsType runAs = RunAsType.None)
{
string trimmedCommand = command.Trim();
command = Environment.ExpandEnvironmentVariables(trimmedCommand);
var workingDirectory = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
var runAsAdministratorArg = !runAsAdministrator && !_settings.RunAsAdministrator ? string.Empty : "runas";
// Set runAsArg
string runAsVerbArg = string.Empty;
if (runAs == RunAsType.OtherUser)
{
runAsVerbArg = "runAsUser";
}
else if (runAs == RunAsType.Administrator || _settings.RunAsAdministrator)
{
runAsVerbArg = "runAs";
}
ProcessStartInfo info;
if (_settings.Shell == ExecutionShell.Cmd)
{
var arguments = _settings.LeaveShellOpen ? $"/k \"{command}\"" : $"/c \"{command}\" & pause";
info = ShellCommand.SetProcessStartInfo("cmd.exe", workingDirectory, arguments, runAsAdministratorArg);
info = ShellCommand.SetProcessStartInfo("cmd.exe", workingDirectory, arguments, runAsVerbArg);
}
else if (_settings.Shell == ExecutionShell.Powershell)
{
@@ -179,14 +189,14 @@ namespace Microsoft.Plugin.Shell
arguments = $"\"{command} ; Read-Host -Prompt \\\"Press Enter to continue\\\"\"";
}
info = ShellCommand.SetProcessStartInfo("powershell.exe", workingDirectory, arguments, runAsAdministratorArg);
info = ShellCommand.SetProcessStartInfo("powershell.exe", workingDirectory, arguments, runAsVerbArg);
}
else if (_settings.Shell == ExecutionShell.RunCommand)
{
// Open explorer if the path is a file or directory
if (Directory.Exists(command) || File.Exists(command))
{
info = ShellCommand.SetProcessStartInfo("explorer.exe", arguments: command, verb: runAsAdministratorArg);
info = ShellCommand.SetProcessStartInfo("explorer.exe", arguments: command, verb: runAsVerbArg);
}
else
{
@@ -197,16 +207,16 @@ namespace Microsoft.Plugin.Shell
if (ExistInPath(filename))
{
var arguments = parts[1];
info = ShellCommand.SetProcessStartInfo(filename, workingDirectory, arguments, runAsAdministratorArg);
info = ShellCommand.SetProcessStartInfo(filename, workingDirectory, arguments, runAsVerbArg);
}
else
{
info = ShellCommand.SetProcessStartInfo(command, verb: runAsAdministratorArg);
info = ShellCommand.SetProcessStartInfo(command, verb: runAsVerbArg);
}
}
else
{
info = ShellCommand.SetProcessStartInfo(command, verb: runAsAdministratorArg);
info = ShellCommand.SetProcessStartInfo(command, verb: runAsVerbArg);
}
}
}
@@ -222,6 +232,13 @@ namespace Microsoft.Plugin.Shell
return info;
}
private enum RunAsType
{
None,
Administrator,
OtherUser,
}
private void Execute(Func<ProcessStartInfo, Process> startProcess, ProcessStartInfo info)
{
try
@@ -326,7 +343,21 @@ namespace Microsoft.Plugin.Shell
AcceleratorModifiers = ModifierKeys.Control | ModifierKeys.Shift,
Action = c =>
{
Execute(Process.Start, PrepareProcessStartInfo(selectedResult.Title, true));
Execute(Process.Start, PrepareProcessStartInfo(selectedResult.Title, RunAsType.Administrator));
return true;
},
},
new ContextMenuResult
{
PluginName = Assembly.GetExecutingAssembly().GetName().Name,
Title = Properties.Resources.wox_plugin_cmd_run_as_user,
Glyph = "\xE7EE",
FontFamily = "Segoe MDL2 Assets",
AcceleratorKey = Key.U,
AcceleratorModifiers = ModifierKeys.Control | ModifierKeys.Shift,
Action = _ =>
{
Execute(Process.Start, PrepareProcessStartInfo(selectedResult.Title, RunAsType.OtherUser));
return true;
},
},

View File

@@ -19,7 +19,7 @@ namespace Microsoft.Plugin.Shell.Properties {
// 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", "16.0.0.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class Resources {
@@ -122,5 +122,14 @@ namespace Microsoft.Plugin.Shell.Properties {
return ResourceManager.GetString("wox_plugin_cmd_run_as_administrator", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Run as different user (Ctrl+Shift+U).
/// </summary>
public static string wox_plugin_cmd_run_as_user {
get {
return ResourceManager.GetString("wox_plugin_cmd_run_as_user", resourceCulture);
}
}
}
}

View File

@@ -138,4 +138,7 @@
<data name="wox_plugin_cmd_command_not_found" xml:space="preserve">
<value>Command not found</value>
</data>
<data name="wox_plugin_cmd_run_as_user" xml:space="preserve">
<value>Run as different user (Ctrl+Shift+U)</value>
</data>
</root>

View File

@@ -186,7 +186,7 @@ namespace Microsoft.Plugin.WindowWalker.Components
if (IsFullAccessDenied)
{
string killTree = killProcessTree ? " /t" : string.Empty;
Helper.OpenInShell("taskkill.exe", $"/pid {(int)ProcessID} /f{killTree}", null, true, true);
Helper.OpenInShell("taskkill.exe", $"/pid {(int)ProcessID} /f{killTree}", null, Helper.ShellRunAsType.Administrator, true);
}
else
{

View File

@@ -145,7 +145,7 @@ namespace Microsoft.PowerToys.Run.Plugin.Registry.Helper
Win32.Registry.SetValue(@"HKEY_Current_User\Software\Microsoft\Windows\CurrentVersion\Applets\Regedit", "LastKey", fullKey);
// -m => allow multi-instance (hidden start option)
Wox.Infrastructure.Helper.OpenInShell("regedit.exe", "-m", null, true);
Wox.Infrastructure.Helper.OpenInShell("regedit.exe", "-m", null, Wox.Infrastructure.Helper.ShellRunAsType.Administrator);
}
/// <summary>

View File

@@ -136,7 +136,7 @@ namespace Microsoft.PowerToys.Run.Plugin.System.Components
IcoPath = $"Images\\firmwareSettings.{iconTheme}.png",
Action = c =>
{
return ResultHelper.ExecuteCommand(confirmCommands, Resources.Microsoft_plugin_sys_uefi_confirmation, () => Helper.OpenInShell("shutdown", "/r /fw /t 0", null, true));
return ResultHelper.ExecuteCommand(confirmCommands, Resources.Microsoft_plugin_sys_uefi_confirmation, () => Helper.OpenInShell("shutdown", "/r /fw /t 0", null, Helper.ShellRunAsType.Administrator));
},
});
}

View File

@@ -153,7 +153,7 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsTerminal
try
{
string path = "shell:AppsFolder\\" + id;
Helper.OpenInShell(path, TerminalHelper.GetArguments(profile, _openNewTab), runAsAdmin: true);
Helper.OpenInShell(path, TerminalHelper.GetArguments(profile, _openNewTab), runAs: Helper.ShellRunAsType.Administrator);
}
#pragma warning disable CA1031 // Do not catch general exception types
catch (Exception ex)

View File

@@ -101,7 +101,7 @@ namespace Wox.Infrastructure
{
FileName = path,
WorkingDirectory = Path.GetDirectoryName(path),
Verb = "runas",
Verb = "runAs",
UseShellExecute = true,
};
@@ -115,6 +115,28 @@ namespace Wox.Infrastructure
}
}
// Function to run as other user for context menu items
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Suppressing this to enable FxCop. We are logging the exception, and going forward general exceptions should not be caught")]
public static void RunAsUser(string path)
{
var info = new ProcessStartInfo
{
FileName = path,
WorkingDirectory = Path.GetDirectoryName(path),
Verb = "runAsUser",
UseShellExecute = true,
};
try
{
Process.Start(info);
}
catch (System.Exception ex)
{
Log.Exception($"Unable to Run {path} as different user : {ex.Message}", ex, MethodBase.GetCurrentMethod().DeclaringType);
}
}
public static Process OpenInConsole(string path)
{
var processStartInfo = new ProcessStartInfo
@@ -126,7 +148,7 @@ namespace Wox.Infrastructure
return Process.Start(processStartInfo);
}
public static bool OpenInShell(string path, string arguments = null, string workingDir = null, bool runAsAdmin = false, bool runWithHiddenWindow = false)
public static bool OpenInShell(string path, string arguments = null, string workingDir = null, ShellRunAsType runAs = ShellRunAsType.None, bool runWithHiddenWindow = false)
{
using (var process = new Process())
{
@@ -134,13 +156,16 @@ namespace Wox.Infrastructure
process.StartInfo.WorkingDirectory = string.IsNullOrWhiteSpace(workingDir) ? string.Empty : workingDir;
process.StartInfo.Arguments = string.IsNullOrWhiteSpace(arguments) ? string.Empty : arguments;
process.StartInfo.WindowStyle = runWithHiddenWindow ? ProcessWindowStyle.Hidden : ProcessWindowStyle.Normal;
process.StartInfo.UseShellExecute = true;
if (runAsAdmin)
if (runAs == ShellRunAsType.Administrator)
{
process.StartInfo.Verb = "RunAs";
}
process.StartInfo.UseShellExecute = true;
else if (runAs == ShellRunAsType.OtherUser)
{
process.StartInfo.Verb = "RunAsUser";
}
try
{
@@ -154,5 +179,12 @@ namespace Wox.Infrastructure
}
}
}
public enum ShellRunAsType
{
None,
Administrator,
OtherUser,
}
}
}

View File

@@ -291,11 +291,12 @@ namespace Wox.Test.Plugins
List<ContextMenuResult> contextMenuItems = contextMenuLoader.LoadContextMenus(result);
// Assert
Assert.AreEqual(4, contextMenuItems.Count);
Assert.AreEqual(5, contextMenuItems.Count);
Assert.AreEqual(Microsoft.Plugin.Indexer.Properties.Resources.Microsoft_plugin_indexer_open_containing_folder, contextMenuItems[0].Title);
Assert.AreEqual(Microsoft.Plugin.Indexer.Properties.Resources.Microsoft_plugin_indexer_run_as_administrator, contextMenuItems[1].Title);
Assert.AreEqual(Microsoft.Plugin.Indexer.Properties.Resources.Microsoft_plugin_indexer_copy_path, contextMenuItems[2].Title);
Assert.AreEqual(Microsoft.Plugin.Indexer.Properties.Resources.Microsoft_plugin_indexer_open_in_console, contextMenuItems[3].Title);
Assert.AreEqual(Microsoft.Plugin.Indexer.Properties.Resources.Microsoft_plugin_indexer_run_as_user, contextMenuItems[2].Title);
Assert.AreEqual(Microsoft.Plugin.Indexer.Properties.Resources.Microsoft_plugin_indexer_copy_path, contextMenuItems[3].Title);
Assert.AreEqual(Microsoft.Plugin.Indexer.Properties.Resources.Microsoft_plugin_indexer_open_in_console, contextMenuItems[4].Title);
}
[DataTestMethod]