mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-05 10:46:33 +02:00
Add the Command Palette module (#37908)
Windows Command Palette ("CmdPal") is the next iteration of PowerToys Run. With extensibility at its core, the Command Palette is your one-stop launcher to start _anything_.
By default, CmdPal is bound to <kbd>Win+Alt+Space</kbd>.


----
This brings the current preview version of CmdPal into the upstream PowerToys repo. There are still lots of bugs to work out, but it's reached the state we're ready to start sharing it with the world. From here, we can further collaborate with the community on the features that are important, and ensuring that we've got a most robust API to enable developers to build whatever extensions they want.
Most of the built-in PT Run modules have already been ported to CmdPal's extension API. Those include:
* Installed apps
* Shell commands
* File search (powered by the indexer)
* Windows Registry search
* Web search
* Windows Terminal Profiles
* Windows Services
* Windows settings
There are a couple new extensions built-in
* You can now search for packages on `winget` and install them right from the palette. This also powers searching for extensions for the palette
* The calculator has an entirely new implementation. This is currently less feature complete than the original PT Run one - we're looking forward to updating it to be more complete for future ingestion in Windows
* "Bookmarks" allow you to save shortcuts to files, folders, and webpages as top-level commands in the palette.
We've got a bunch of other samples too, in this repo and elsewhere
### PowerToys specific notes
CmdPal will eventually graduate out of PowerToys to live as its own application, which is why it's implemented just a little differently than most other modules. Enabling CmdPal will install its `msix` package.
The CI was minorly changed to support CmdPal version numbers independent of PowerToys itself. It doesn't make sense for us to start CmdPal at v0.90, and in the future, we want to be able to rev CmdPal independently of PT itself.
Closes #3200, closes #3600, closes #7770, closes #34273, closes #36471, closes #20976, closes #14495
-----
TODOs et al
**Blocking:**
- [ ] Images and descriptions in Settings and OOBE need to be properly defined, as mentioned before
- [ ] Niels is on it
- [x] Doesn't start properly from PowerToys unless the fix PR is merged.
- https://github.com/zadjii-msft/PowerToys/pull/556 merged
- [x] I seem to lose focus a lot when I press on some limits, like between the search bar and the results.
- This is https://github.com/zadjii-msft/PowerToys/issues/427
- [x] Turned off an extension like Calculator and it was still working.
- Need to get rid of that toggle, it doesn't do anything currently
- [x] `ListViewModel.<FetchItems>` crash
- Pretty confident that was fixed in https://github.com/zadjii-msft/PowerToys/pull/553
**Not blocking / improvements:**
- Show the shortcut through settings, as mentioned before, or create a button that would open CmdPalette settings.
- When PowerToys starts, CmdPalette is always shown if enabled. That's weird when just starting PowerToys/ logging in to the computer with PowerToys auto-start activated. I think this should at least be a setting.
- Needing to double press a result for it to do the default action seems quirky. If one is already selected, I think just pressing should be enough for it to do the action.
- This is currently a setting, though we're thinking of changing the setting even more: https://github.com/zadjii-msft/PowerToys/issues/392
- There's no URI extension. Was surprised when typing a URL that it only proposed a web search.
- [x] There's no System commands extension. Was expecting to be able to quickly restart the computer by typing restart but it wasn't there.
- This is in PR https://github.com/zadjii-msft/PowerToys/pull/452
---------
Co-authored-by: joadoumie <98557455+joadoumie@users.noreply.github.com>
Co-authored-by: Jordi Adoumie <jordiadoumie@microsoft.com>
Co-authored-by: Mike Griese <zadjii@gmail.com>
Co-authored-by: Niels Laute <niels.laute@live.nl>
Co-authored-by: Michael Hawker <24302614+michael-hawker@users.noreply.github.com>
Co-authored-by: Stefan Markovic <57057282+stefansjfw@users.noreply.github.com>
Co-authored-by: Seraphima <zykovas91@gmail.com>
Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>
Co-authored-by: Kristen Schau <47155823+krschau@users.noreply.github.com>
Co-authored-by: Eric Johnson <ericjohnson327@gmail.com>
Co-authored-by: Ethan Fang <ethanfang@microsoft.com>
Co-authored-by: Yu Leng (from Dev Box) <yuleng@microsoft.com>
Co-authored-by: Clint Rutkas <clint@rutkas.com>
This commit is contained in:
@@ -0,0 +1,217 @@
|
||||
// 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.Globalization;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Text;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.System.Helpers;
|
||||
|
||||
/// <summary>
|
||||
/// This class holds all available results
|
||||
/// </summary>
|
||||
internal static class Commands
|
||||
{
|
||||
internal const int EWXLOGOFF = 0x00000000;
|
||||
internal const int EWXSHUTDOWN = 0x00000001;
|
||||
internal const int EWXREBOOT = 0x00000002;
|
||||
internal const int EWXFORCE = 0x00000004;
|
||||
internal const int EWXPOWEROFF = 0x00000008;
|
||||
internal const int EWXFORCEIFHUNG = 0x00000010;
|
||||
|
||||
// Cache for network interface information to save query time
|
||||
private const int UpdateCacheIntervalSeconds = 5;
|
||||
private static List<NetworkConnectionProperties> networkPropertiesCache = new List<NetworkConnectionProperties>();
|
||||
private static DateTime timeOfLastNetworkQuery;
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list with all system command results
|
||||
/// </summary>
|
||||
/// <param name="isUefi">Value indicating if the system is booted in uefi mode</param>
|
||||
/// <param name="splitRecycleBinResults">Value indicating if we should show two results for Recycle Bin.</param>
|
||||
/// <param name="confirmCommands">A value indicating if the user should confirm the system commands</param>
|
||||
/// <param name="emptyRBSuccessMessage">Show a success message after empty Recycle Bin.</param>
|
||||
/// <returns>A list of all results</returns>
|
||||
public static List<IListItem> GetSystemCommands(bool isUefi, bool splitRecycleBinResults, bool confirmCommands, bool emptyRBSuccessMessage)
|
||||
{
|
||||
var results = new List<IListItem>();
|
||||
results.AddRange(new[]
|
||||
{
|
||||
new ListItem(new ExecuteCommandConfirmation(Resources.Microsoft_plugin_command_name_shutdown, confirmCommands, Resources.Microsoft_plugin_sys_shutdown_computer_confirmation, () => OpenInShellHelper.OpenInShell("shutdown", "/s /hybrid /t 0")))
|
||||
{
|
||||
Title = Resources.Microsoft_plugin_sys_shutdown_computer,
|
||||
Subtitle = Resources.Microsoft_plugin_sys_shutdown_computer_description,
|
||||
Icon = Icons.ShutdownIcon,
|
||||
},
|
||||
new ListItem(new ExecuteCommandConfirmation(Resources.Microsoft_plugin_command_name_restart, confirmCommands, Resources.Microsoft_plugin_sys_restart_computer_confirmation, () => OpenInShellHelper.OpenInShell("shutdown", "/g /t 0")))
|
||||
{
|
||||
Title = Resources.Microsoft_plugin_sys_restart_computer,
|
||||
Subtitle = Resources.Microsoft_plugin_sys_restart_computer_description,
|
||||
Icon = Icons.RestartIcon,
|
||||
},
|
||||
new ListItem(new ExecuteCommandConfirmation(Resources.Microsoft_plugin_command_name_signout, confirmCommands, Resources.Microsoft_plugin_sys_sign_out_confirmation, () => NativeMethods.ExitWindowsEx(EWXLOGOFF, 0)))
|
||||
{
|
||||
Title = Resources.Microsoft_plugin_sys_sign_out,
|
||||
Subtitle = Resources.Microsoft_plugin_sys_sign_out_description,
|
||||
Icon = Icons.LogoffIcon,
|
||||
},
|
||||
new ListItem(new ExecuteCommandConfirmation(Resources.Microsoft_plugin_command_name_lock, confirmCommands, Resources.Microsoft_plugin_sys_lock_confirmation, () => NativeMethods.LockWorkStation()))
|
||||
{
|
||||
Title = Resources.Microsoft_plugin_sys_lock,
|
||||
Subtitle = Resources.Microsoft_plugin_sys_lock_description,
|
||||
Icon = Icons.LockIcon,
|
||||
},
|
||||
new ListItem(new ExecuteCommandConfirmation(Resources.Microsoft_plugin_command_name_sleep, confirmCommands, Resources.Microsoft_plugin_sys_sleep_confirmation, () => NativeMethods.SetSuspendState(false, true, true)))
|
||||
{
|
||||
Title = Resources.Microsoft_plugin_sys_sleep,
|
||||
Subtitle = Resources.Microsoft_plugin_sys_sleep_description,
|
||||
Icon = Icons.SleepIcon,
|
||||
},
|
||||
new ListItem(new ExecuteCommandConfirmation(Resources.Microsoft_plugin_command_name_hibernate, confirmCommands, Resources.Microsoft_plugin_sys_hibernate_confirmation, () => NativeMethods.SetSuspendState(true, true, true)))
|
||||
{
|
||||
Title = Resources.Microsoft_plugin_sys_hibernate,
|
||||
Subtitle = Resources.Microsoft_plugin_sys_hibernate_description,
|
||||
Icon = Icons.SleepIcon, // Icon change needed
|
||||
},
|
||||
});
|
||||
|
||||
// Show Recycle Bin results based on setting.
|
||||
if (splitRecycleBinResults)
|
||||
{
|
||||
results.AddRange(new[]
|
||||
{
|
||||
new ListItem(new OpenInShellCommand(Resources.Microsoft_plugin_command_name_empty, "explorer.exe", "shell:RecycleBinFolder"))
|
||||
{
|
||||
Title = Resources.Microsoft_plugin_sys_RecycleBinOpen,
|
||||
Subtitle = Resources.Microsoft_plugin_sys_RecycleBin_description,
|
||||
Icon = Icons.RecycleBinIcon,
|
||||
},
|
||||
new ListItem(new EmptyRecycleBinConfirmation(emptyRBSuccessMessage))
|
||||
{
|
||||
Title = Resources.Microsoft_plugin_sys_RecycleBinEmptyResult,
|
||||
Subtitle = Resources.Microsoft_plugin_sys_RecycleBinEmpty_description,
|
||||
Icon = Icons.RecycleBinIcon,
|
||||
},
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
results.Add(
|
||||
new ListItem(new OpenInShellCommand(Resources.Microsoft_plugin_command_name_empty, "explorer.exe", "shell:RecycleBinFolder"))
|
||||
{
|
||||
Title = Resources.Microsoft_plugin_sys_RecycleBin,
|
||||
Subtitle = Resources.Microsoft_plugin_sys_RecycleBin_description,
|
||||
Icon = Icons.RecycleBinIcon,
|
||||
});
|
||||
}
|
||||
|
||||
// UEFI command/result. It is only available on systems booted in UEFI mode.
|
||||
if (isUefi)
|
||||
{
|
||||
results.Add(new ListItem(new ExecuteCommandConfirmation(Resources.Microsoft_plugin_command_name_reboot, confirmCommands, Resources.Microsoft_plugin_sys_uefi_confirmation, () => OpenInShellHelper.OpenInShell("shutdown", "/r /fw /t 0", null, OpenInShellHelper.ShellRunAsType.Administrator)))
|
||||
{
|
||||
Title = Resources.Microsoft_plugin_sys_uefi,
|
||||
Subtitle = Resources.Microsoft_plugin_sys_uefi_description,
|
||||
Icon = Icons.FirmwareSettingsIcon,
|
||||
});
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of all ip and mac results
|
||||
/// </summary>
|
||||
/// <param name="manager">The tSettingsManager instance</param>
|
||||
/// <returns>The list of available results</returns>
|
||||
public static List<IListItem> GetNetworkConnectionResults(SettingsManager manager)
|
||||
{
|
||||
var results = new List<IListItem>();
|
||||
|
||||
// We update the cache only if the last query is older than 'updateCacheIntervalSeconds' seconds
|
||||
DateTime timeOfLastNetworkQueryBefore = timeOfLastNetworkQuery;
|
||||
timeOfLastNetworkQuery = DateTime.Now; // Set time of last query to this query
|
||||
if ((timeOfLastNetworkQuery - timeOfLastNetworkQueryBefore).TotalSeconds >= UpdateCacheIntervalSeconds)
|
||||
{
|
||||
networkPropertiesCache = NetworkConnectionProperties.GetList();
|
||||
}
|
||||
|
||||
CompositeFormat sysIpv4DescriptionCompositeFormate = CompositeFormat.Parse(Resources.Microsoft_plugin_sys_ip4_description);
|
||||
CompositeFormat sysMacDescriptionCompositeFormate = CompositeFormat.Parse(Resources.Microsoft_plugin_sys_mac_description);
|
||||
var hideDisconnectedNetworkInfo = manager.HideDisconnectedNetworkInfo;
|
||||
|
||||
foreach (NetworkConnectionProperties intInfo in networkPropertiesCache)
|
||||
{
|
||||
if (hideDisconnectedNetworkInfo)
|
||||
{
|
||||
if (intInfo.State != OperationalStatus.Up)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(intInfo.IPv4))
|
||||
{
|
||||
results.Add(new ListItem(new CopyTextCommand(intInfo.GetConnectionDetails()))
|
||||
{
|
||||
Title = intInfo.IPv4,
|
||||
Subtitle = string.Format(CultureInfo.InvariantCulture, sysIpv4DescriptionCompositeFormate, intInfo.ConnectionName),
|
||||
Icon = Icons.NetworkAdapterIcon,
|
||||
Details = new Details() { Title = Resources.Microsoft_plugin_ext_connection_details, Body = intInfo.GetConnectionDetails() },
|
||||
});
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(intInfo.IPv6Primary))
|
||||
{
|
||||
results.Add(new ListItem(new CopyTextCommand(intInfo.GetConnectionDetails()))
|
||||
{
|
||||
Title = intInfo.IPv6Primary,
|
||||
Subtitle = string.Format(CultureInfo.InvariantCulture, sysIpv4DescriptionCompositeFormate, intInfo.ConnectionName),
|
||||
Icon = Icons.NetworkAdapterIcon,
|
||||
Details = new Details() { Title = Resources.Microsoft_plugin_ext_connection_details, Body = intInfo.GetConnectionDetails() },
|
||||
});
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(intInfo.PhysicalAddress))
|
||||
{
|
||||
results.Add(new ListItem(new CopyTextCommand(intInfo.GetAdapterDetails()))
|
||||
{
|
||||
Title = intInfo.PhysicalAddress,
|
||||
Subtitle = string.Format(CultureInfo.InvariantCulture, sysMacDescriptionCompositeFormate, intInfo.Adapter, intInfo.ConnectionName),
|
||||
Icon = Icons.NetworkAdapterIcon,
|
||||
Details = new Details() { Title = Resources.Microsoft_plugin_ext_connection_details, Body = intInfo.GetConnectionDetails() },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
public static List<IListItem> GetAllCommands(SettingsManager manager)
|
||||
{
|
||||
var list = new List<IListItem>();
|
||||
var listLock = new object();
|
||||
|
||||
// Network (ip and mac) results are slow with many network cards and returned delayed.
|
||||
// On global queries the first word/part has to be 'ip', 'mac' or 'address' for network results
|
||||
var networkConnectionResults = Commands.GetNetworkConnectionResults(manager);
|
||||
|
||||
var isBootedInUefiMode = Win32Helpers.GetSystemFirmwareType() == FirmwareType.Uefi;
|
||||
|
||||
var separateEmptyRB = manager.ShowSeparateResultForEmptyRecycleBin;
|
||||
var confirmSystemCommands = manager.ShowDialogToConfirmCommand;
|
||||
var showSuccessOnEmptyRB = manager.ShowSuccessMessageAfterEmptyingRecycleBin;
|
||||
|
||||
// normal system commands are fast and can be returned immediately
|
||||
var systemCommands = Commands.GetSystemCommands(isBootedInUefiMode, separateEmptyRB, confirmSystemCommands, showSuccessOnEmptyRB);
|
||||
list.AddRange(systemCommands);
|
||||
list.AddRange(networkConnectionResults);
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// 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.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.System.Helpers;
|
||||
|
||||
public static partial class Icons
|
||||
{
|
||||
public static IconInfo FirmwareSettingsIcon { get; } = IconHelpers.FromRelativePaths("Microsoft.CmdPal.Ext.System\\Assets\\logoff.light.png", "Microsoft.CmdPal.Ext.System\\Assets\\logoff.dark.png");
|
||||
|
||||
public static IconInfo LockIcon { get; } = new IconInfo("\uE72E");
|
||||
|
||||
public static IconInfo LogoffIcon { get; } = IconHelpers.FromRelativePaths("Microsoft.CmdPal.Ext.System\\Assets\\logoff.light.png", "Microsoft.CmdPal.Ext.System\\Assets\\logoff.dark.png");
|
||||
|
||||
public static IconInfo NetworkAdapterIcon { get; } = new IconInfo("\uEDA3");
|
||||
|
||||
public static IconInfo RecycleBinIcon { get; } = new IconInfo("\uE74D");
|
||||
|
||||
public static IconInfo RestartIcon { get; } = new IconInfo("\uE777");
|
||||
|
||||
public static IconInfo ShutdownIcon { get; } = new IconInfo("\uE7E8");
|
||||
|
||||
public static IconInfo SleepIcon { get; } = IconHelpers.FromRelativePaths("Microsoft.CmdPal.Ext.System\\Assets\\sleep.light.png", "Microsoft.CmdPal.Ext.System\\Assets\\sleep.dark.png");
|
||||
}
|
||||
@@ -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.System.Helpers;
|
||||
|
||||
public sealed partial class MessageBoxHelper
|
||||
{
|
||||
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
|
||||
private static extern int MessageBox(IntPtr hWnd, string text, string caption, int type);
|
||||
|
||||
public static MessageBoxResult Show(string text, string caption, IconType iconType, MessageBoxType type)
|
||||
{
|
||||
return (MessageBoxResult)MessageBox(IntPtr.Zero, text, caption, (int)type | (int)iconType);
|
||||
}
|
||||
|
||||
public enum IconType
|
||||
{
|
||||
Error = 0x00000010,
|
||||
Help = 0x00000020,
|
||||
Warning = 0x00000030,
|
||||
Info = 0x00000040,
|
||||
}
|
||||
|
||||
public enum MessageBoxType
|
||||
{
|
||||
OK = 0x00000000,
|
||||
}
|
||||
|
||||
public enum MessageBoxResult
|
||||
{
|
||||
OK = 1,
|
||||
Cancel = 2,
|
||||
Abort = 3,
|
||||
Retry = 4,
|
||||
Ignore = 5,
|
||||
Yes = 6,
|
||||
No = 7,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
// 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.CodeAnalysis;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.System.Helpers;
|
||||
|
||||
[SuppressMessage("Interoperability", "CA1401:P/Invokes should not be visible", Justification = "We want plugins to share this NativeMethods class, instead of each one creating its own.")]
|
||||
public sealed class Native
|
||||
{
|
||||
public enum HRESULT : uint
|
||||
{
|
||||
/// <summary>
|
||||
/// Operation successful.
|
||||
/// </summary>
|
||||
S_OK = 0x00000000,
|
||||
|
||||
/// <summary>
|
||||
/// Operation successful. (negative condition/no operation)
|
||||
/// </summary>
|
||||
S_FALSE = 0x00000001,
|
||||
|
||||
/// <summary>
|
||||
/// Not implemented.
|
||||
/// </summary>
|
||||
E_NOTIMPL = 0x80004001,
|
||||
|
||||
/// <summary>
|
||||
/// No such interface supported.
|
||||
/// </summary>
|
||||
E_NOINTERFACE = 0x80004002,
|
||||
|
||||
/// <summary>
|
||||
/// Pointer that is not valid.
|
||||
/// </summary>
|
||||
E_POINTER = 0x80004003,
|
||||
|
||||
/// <summary>
|
||||
/// Operation aborted.
|
||||
/// </summary>
|
||||
E_ABORT = 0x80004004,
|
||||
|
||||
/// <summary>
|
||||
/// Unspecified failure.
|
||||
/// </summary>
|
||||
E_FAIL = 0x80004005,
|
||||
|
||||
/// <summary>
|
||||
/// Unexpected failure.
|
||||
/// </summary>
|
||||
E_UNEXPECTED = 0x8000FFFF,
|
||||
|
||||
/// <summary>
|
||||
/// General access denied error.
|
||||
/// </summary>
|
||||
E_ACCESSDENIED = 0x80070005,
|
||||
|
||||
/// <summary>
|
||||
/// Handle that is not valid.
|
||||
/// </summary>
|
||||
E_HANDLE = 0x80070006,
|
||||
|
||||
/// <summary>
|
||||
/// Failed to allocate necessary memory.
|
||||
/// </summary>
|
||||
E_OUTOFMEMORY = 0x8007000E,
|
||||
|
||||
/// <summary>
|
||||
/// One or more arguments are not valid.
|
||||
/// </summary>
|
||||
E_INVALIDARG = 0x80070057,
|
||||
|
||||
/// <summary>
|
||||
/// The operation was canceled by the user. (Error source 7 means Win32.)
|
||||
/// </summary>
|
||||
/// <SeeAlso href="https://learn.microsoft.com/windows/win32/debug/system-error-codes--1000-1299-"/>
|
||||
/// <SeeAlso href="https://en.wikipedia.org/wiki/HRESULT"/>
|
||||
E_CANCELLED = 0x800704C7,
|
||||
}
|
||||
|
||||
public static class ShellItemTypeConstants
|
||||
{
|
||||
/// <summary>
|
||||
/// Guid for type IShellItem.
|
||||
/// </summary>
|
||||
public static readonly Guid ShellItemGuid = new("43826d1e-e718-42ee-bc55-a1e261c37bfe");
|
||||
|
||||
/// <summary>
|
||||
/// Guid for type IShellItem2.
|
||||
/// </summary>
|
||||
public static readonly Guid ShellItem2Guid = new("7E9FB0D3-919F-4307-AB2E-9B1860310C93");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The following are ShellItem DisplayName types.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum SIGDN : uint
|
||||
{
|
||||
NORMALDISPLAY = 0,
|
||||
PARENTRELATIVEPARSING = 0x80018001,
|
||||
PARENTRELATIVEFORADDRESSBAR = 0x8001c001,
|
||||
DESKTOPABSOLUTEPARSING = 0x80028000,
|
||||
PARENTRELATIVEEDITING = 0x80031001,
|
||||
DESKTOPABSOLUTEEDITING = 0x8004c000,
|
||||
FILESYSPATH = 0x80058000,
|
||||
URL = 0x80068000,
|
||||
}
|
||||
|
||||
[ComImport]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
[Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe")]
|
||||
public interface IShellItem
|
||||
{
|
||||
void BindToHandler(
|
||||
nint pbc,
|
||||
[MarshalAs(UnmanagedType.LPStruct)] Guid bhid,
|
||||
[MarshalAs(UnmanagedType.LPStruct)] Guid riid,
|
||||
out nint ppv);
|
||||
|
||||
void GetParent(out IShellItem ppsi);
|
||||
|
||||
void GetDisplayName(SIGDN sigdnName, [MarshalAs(UnmanagedType.LPWStr)] out string ppszName);
|
||||
|
||||
void GetAttributes(uint sfgaoMask, out uint psfgaoAttribs);
|
||||
|
||||
void Compare(IShellItem psi, uint hint, out int piOrder);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see href="https://learn.microsoft.com/windows/win32/stg/stgm-constants">see all STGM values</see>
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum STGM : long
|
||||
{
|
||||
READ = 0x00000000L,
|
||||
WRITE = 0x00000001L,
|
||||
READWRITE = 0x00000002L,
|
||||
CREATE = 0x00001000L,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
// 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 SuppressMessageAttribute = System.Diagnostics.CodeAnalysis.SuppressMessageAttribute;
|
||||
|
||||
#pragma warning disable SA1649, CA1051, CA1707, CA1028, CA1714, CA1069, SA1402
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.System.Helpers;
|
||||
|
||||
[SuppressMessage("Interoperability", "CA1401:P/Invokes should not be visible", Justification = "We want plugins to share this NativeMethods class, instead of each one creating its own.")]
|
||||
public static class NativeMethods
|
||||
{
|
||||
[DllImport("kernel32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool CloseHandle(IntPtr hObject);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool GetFirmwareType(ref FirmwareType FirmwareType);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool ExitWindowsEx(uint uFlags, uint dwReason);
|
||||
|
||||
[DllImport("user32")]
|
||||
public static extern void LockWorkStation();
|
||||
|
||||
[DllImport("Powrprof.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool SetSuspendState(bool hibernate, bool forceCritical, bool disableWakeEvent);
|
||||
|
||||
[DllImport("Shell32.dll", CharSet = CharSet.Unicode)]
|
||||
public static extern uint SHEmptyRecycleBin(IntPtr hWnd, uint dwFlags);
|
||||
}
|
||||
|
||||
public enum HRESULT : uint
|
||||
{
|
||||
/// <summary>
|
||||
/// Operation successful.
|
||||
/// </summary>
|
||||
S_OK = 0x00000000,
|
||||
|
||||
/// <summary>
|
||||
/// Operation successful. (negative condition/no operation)
|
||||
/// </summary>
|
||||
S_FALSE = 0x00000001,
|
||||
|
||||
/// <summary>
|
||||
/// Not implemented.
|
||||
/// </summary>
|
||||
E_NOTIMPL = 0x80004001,
|
||||
|
||||
/// <summary>
|
||||
/// No such interface supported.
|
||||
/// </summary>
|
||||
E_NOINTERFACE = 0x80004002,
|
||||
|
||||
/// <summary>
|
||||
/// Pointer that is not valid.
|
||||
/// </summary>
|
||||
E_POINTER = 0x80004003,
|
||||
|
||||
/// <summary>
|
||||
/// Operation aborted.
|
||||
/// </summary>
|
||||
E_ABORT = 0x80004004,
|
||||
|
||||
/// <summary>
|
||||
/// Unspecified failure.
|
||||
/// </summary>
|
||||
E_FAIL = 0x80004005,
|
||||
|
||||
/// <summary>
|
||||
/// Unexpected failure.
|
||||
/// </summary>
|
||||
E_UNEXPECTED = 0x8000FFFF,
|
||||
|
||||
/// <summary>
|
||||
/// General access denied error.
|
||||
/// </summary>
|
||||
E_ACCESSDENIED = 0x80070005,
|
||||
|
||||
/// <summary>
|
||||
/// Handle that is not valid.
|
||||
/// </summary>
|
||||
E_HANDLE = 0x80070006,
|
||||
|
||||
/// <summary>
|
||||
/// Failed to allocate necessary memory.
|
||||
/// </summary>
|
||||
E_OUTOFMEMORY = 0x8007000E,
|
||||
|
||||
/// <summary>
|
||||
/// One or more arguments are not valid.
|
||||
/// </summary>
|
||||
E_INVALIDARG = 0x80070057,
|
||||
|
||||
/// <summary>
|
||||
/// The operation was canceled by the user. (Error source 7 means Win32.)
|
||||
/// </summary>
|
||||
/// <SeeAlso href="https://learn.microsoft.com/windows/win32/debug/system-error-codes--1000-1299-"/>
|
||||
/// <SeeAlso href="https://en.wikipedia.org/wiki/HRESULT"/>
|
||||
E_CANCELLED = 0x800704C7,
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// <see href="https://learn.microsoft.com/windows/win32/api/winnt/ne-winnt-firmware_type">see learn.microsoft.com</see>
|
||||
/// </remarks>
|
||||
public enum FirmwareType
|
||||
{
|
||||
Unknown = 0,
|
||||
Bios = 1,
|
||||
Uefi = 2,
|
||||
Max = 3,
|
||||
}
|
||||
|
||||
public delegate bool EnumWindowsProc(IntPtr hwnd, IntPtr lParam);
|
||||
@@ -0,0 +1,320 @@
|
||||
// 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.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.System.Helpers;
|
||||
|
||||
/// <summary>
|
||||
/// This class represents the information for a network connection/interface
|
||||
/// </summary>
|
||||
internal sealed class NetworkConnectionProperties
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name of the adapter
|
||||
/// </summary>
|
||||
internal string Adapter { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the physical address (MAC) of the adapter
|
||||
/// </summary>
|
||||
internal string PhysicalAddress { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating the interface type
|
||||
/// </summary>
|
||||
internal NetworkInterfaceType Type { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the speed of the adapter as unformatted value (Static information form the adapter device)
|
||||
/// </summary>
|
||||
internal long Speed { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating the operational state of the adapter
|
||||
/// </summary>
|
||||
internal OperationalStatus State { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the network connection
|
||||
/// </summary>
|
||||
internal string ConnectionName { get; private set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a string with the suffix of the connection
|
||||
/// </summary>
|
||||
internal string Suffix { get; private set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the IPv4 address
|
||||
/// </summary>
|
||||
internal string IPv4 { get; private set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the IPv4 subnet mask
|
||||
/// </summary>
|
||||
internal string IPv4Mask { get; private set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the primarily used IPv6 address
|
||||
/// </summary>
|
||||
internal string IPv6Primary { get; private set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the global IPv6 address
|
||||
/// </summary>
|
||||
internal string IPv6Global { get; private set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the temporary IPv6 address
|
||||
/// </summary>
|
||||
internal string IPv6Temporary { get; private set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the link local IPv6 address
|
||||
/// </summary>
|
||||
internal string IPv6LinkLocal { get; private set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the site local IPv6 address
|
||||
/// </summary>
|
||||
internal string IPv6SiteLocal { get; private set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unique local IPv6 address
|
||||
/// </summary>
|
||||
internal string IPv6UniqueLocal { get; private set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of gateway IPs as string
|
||||
/// </summary>
|
||||
internal List<IPAddress> Gateways { get; private set; } = new List<IPAddress>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of DHCP server IPs as string
|
||||
/// </summary>
|
||||
internal IPAddressCollection? DhcpServers { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of DNS server IPs as string
|
||||
/// </summary>
|
||||
internal IPAddressCollection? DnsServers { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of WINS server IPs as string
|
||||
/// </summary>
|
||||
internal IPAddressCollection? WinsServers { get; private set; }
|
||||
|
||||
private static readonly CompositeFormat MicrosoftPluginSysGbps = CompositeFormat.Parse(Resources.Microsoft_plugin_sys_Gbps);
|
||||
private static readonly CompositeFormat MicrosoftPluginSysMbps = CompositeFormat.Parse(Resources.Microsoft_plugin_sys_Mbps);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NetworkConnectionProperties"/> class.
|
||||
/// This private constructor is used when we crete the list of adapter (properties) by calling <see cref="NetworkConnectionProperties.GetList()"/>.
|
||||
/// </summary>
|
||||
/// <param name="networkInterface">Network interface of the connection</param>
|
||||
private NetworkConnectionProperties(NetworkInterface networkInterface)
|
||||
{
|
||||
// Setting adapter properties
|
||||
Adapter = networkInterface.Description;
|
||||
PhysicalAddress = networkInterface.GetPhysicalAddress().ToString();
|
||||
Type = networkInterface.NetworkInterfaceType;
|
||||
Speed = networkInterface.Speed;
|
||||
State = networkInterface.OperationalStatus;
|
||||
|
||||
// Connection properties
|
||||
ConnectionName = networkInterface.Name;
|
||||
if (State == OperationalStatus.Up)
|
||||
{
|
||||
Suffix = networkInterface.GetIPProperties().DnsSuffix;
|
||||
SetIpProperties(networkInterface.GetIPProperties());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a list with all network adapters and their properties
|
||||
/// </summary>
|
||||
/// <returns>List containing all network adapters</returns>
|
||||
internal static List<NetworkConnectionProperties> GetList()
|
||||
{
|
||||
var interfaces = NetworkInterface.GetAllNetworkInterfaces()
|
||||
.Where(x => x.NetworkInterfaceType != NetworkInterfaceType.Loopback && x.GetPhysicalAddress() != null)
|
||||
.Select(i => new NetworkConnectionProperties(i))
|
||||
.OrderByDescending(i => i.IPv4) // list IPv4 first
|
||||
.ThenBy(i => i.IPv6Primary) // then IPv6
|
||||
.ToList();
|
||||
return interfaces;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a formatted string with the adapter details
|
||||
/// </summary>
|
||||
/// <returns>String with the details</returns>
|
||||
internal string GetAdapterDetails()
|
||||
{
|
||||
return $"{Resources.Microsoft_plugin_sys_AdapterName}: {Adapter}" +
|
||||
$"\n{Resources.Microsoft_plugin_sys_PhysicalAddress}: {PhysicalAddress}" +
|
||||
$"\n{Resources.Microsoft_plugin_sys_Speed}: {GetFormattedSpeedValue(Speed)}" +
|
||||
$"\n{Resources.Microsoft_plugin_sys_Type}: {GetAdapterTypeAsString(Type)}" +
|
||||
$"\n{Resources.Microsoft_plugin_sys_State}: " + (State == OperationalStatus.Up ? Resources.Microsoft_plugin_sys_Connected : Resources.Microsoft_plugin_sys_Disconnected) +
|
||||
$"\n{Resources.Microsoft_plugin_sys_ConnectionName}: {ConnectionName}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a formatted string with the connection details
|
||||
/// </summary>
|
||||
/// <returns>String with the details</returns>
|
||||
internal string GetConnectionDetails()
|
||||
{
|
||||
return $"{Resources.Microsoft_plugin_sys_ConnectionName}: {ConnectionName}" +
|
||||
$"\n{Resources.Microsoft_plugin_sys_State}: " + (State == OperationalStatus.Up ? Resources.Microsoft_plugin_sys_Connected : Resources.Microsoft_plugin_sys_Disconnected) +
|
||||
$"\n{Resources.Microsoft_plugin_sys_Type}: {GetAdapterTypeAsString(Type)}" +
|
||||
$"\n{Resources.Microsoft_plugin_sys_Suffix}: {Suffix}" +
|
||||
CreateIpInfoForDetailsText($"{Resources.Microsoft_plugin_sys_Ip4Address}: ", IPv4) +
|
||||
CreateIpInfoForDetailsText($"{Resources.Microsoft_plugin_sys_Ip4SubnetMask}: ", IPv4Mask) +
|
||||
CreateIpInfoForDetailsText($"{Resources.Microsoft_plugin_sys_Ip6Address}:\n\t", IPv6Global) +
|
||||
CreateIpInfoForDetailsText($"{Resources.Microsoft_plugin_sys_Ip6Temp}:\n\t", IPv6Temporary) +
|
||||
CreateIpInfoForDetailsText($"{Resources.Microsoft_plugin_sys_Ip6Link}:\n\t", IPv6LinkLocal) +
|
||||
CreateIpInfoForDetailsText($"{Resources.Microsoft_plugin_sys_Ip6Site}:\n\t", IPv6SiteLocal) +
|
||||
CreateIpInfoForDetailsText($"{Resources.Microsoft_plugin_sys_Ip6Unique}:\n\t", IPv6UniqueLocal) +
|
||||
CreateIpInfoForDetailsText($"{Resources.Microsoft_plugin_sys_Gateways}:\n\t", Gateways) +
|
||||
CreateIpInfoForDetailsText($"{Resources.Microsoft_plugin_sys_Dhcp}:\n\t", DhcpServers == null ? string.Empty : DhcpServers) +
|
||||
CreateIpInfoForDetailsText($"{Resources.Microsoft_plugin_sys_Dns}:\n\t", DnsServers == null ? string.Empty : DnsServers) +
|
||||
CreateIpInfoForDetailsText($"{Resources.Microsoft_plugin_sys_Wins}:\n\t", WinsServers == null ? string.Empty : WinsServers) +
|
||||
$"\n\n{Resources.Microsoft_plugin_sys_AdapterName}: {Adapter}" +
|
||||
$"\n{Resources.Microsoft_plugin_sys_PhysicalAddress}: {PhysicalAddress}" +
|
||||
$"\n{Resources.Microsoft_plugin_sys_Speed}: {GetFormattedSpeedValue(Speed)}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the ip address properties of the <see cref="NetworkConnectionProperties"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="properties">Element of the type <see cref="IPInterfaceProperties"/>.</param>
|
||||
private void SetIpProperties(IPInterfaceProperties properties)
|
||||
{
|
||||
DateTime t = DateTime.Now;
|
||||
|
||||
UnicastIPAddressInformationCollection ipList = properties.UnicastAddresses;
|
||||
GatewayIPAddressInformationCollection gwList = properties.GatewayAddresses;
|
||||
DhcpServers = properties.DhcpServerAddresses;
|
||||
DnsServers = properties.DnsAddresses;
|
||||
WinsServers = properties.WinsServersAddresses;
|
||||
|
||||
for (var i = 0; i < ipList.Count; i++)
|
||||
{
|
||||
IPAddress ip = ipList[i].Address;
|
||||
|
||||
if (ip.AddressFamily == AddressFamily.InterNetwork)
|
||||
{
|
||||
IPv4 = ip.ToString();
|
||||
IPv4Mask = ipList[i].IPv4Mask.ToString();
|
||||
}
|
||||
else if (ip.AddressFamily == AddressFamily.InterNetworkV6)
|
||||
{
|
||||
if (string.IsNullOrEmpty(IPv6Primary))
|
||||
{
|
||||
IPv6Primary = ip.ToString();
|
||||
}
|
||||
|
||||
if (ip.IsIPv6LinkLocal)
|
||||
{
|
||||
IPv6LinkLocal = ip.ToString();
|
||||
}
|
||||
else if (ip.IsIPv6SiteLocal)
|
||||
{
|
||||
IPv6SiteLocal = ip.ToString();
|
||||
}
|
||||
else if (ip.IsIPv6UniqueLocal)
|
||||
{
|
||||
IPv6UniqueLocal = ip.ToString();
|
||||
}
|
||||
else if (ipList[i].SuffixOrigin == SuffixOrigin.Random)
|
||||
{
|
||||
IPv6Temporary = ip.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
IPv6Global = ip.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < gwList.Count; i++)
|
||||
{
|
||||
Gateways.Add(gwList[i].Address);
|
||||
}
|
||||
|
||||
Debug.Print($"time for getting ips: {DateTime.Now - t}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the interface type as string
|
||||
/// </summary>
|
||||
/// <param name="type">The type to convert</param>
|
||||
/// <returns>A string indicating the interface type</returns>
|
||||
private string GetAdapterTypeAsString(NetworkInterfaceType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case NetworkInterfaceType.Wman:
|
||||
case NetworkInterfaceType.Wwanpp:
|
||||
case NetworkInterfaceType.Wwanpp2:
|
||||
return Resources.Microsoft_plugin_sys_MobileBroadband;
|
||||
case NetworkInterfaceType.Wireless80211:
|
||||
return Resources.Microsoft_plugin_sys_WirelessLan;
|
||||
case NetworkInterfaceType.Loopback:
|
||||
return Resources.Microsoft_plugin_sys_Loopback;
|
||||
case NetworkInterfaceType.Tunnel:
|
||||
return Resources.Microsoft_plugin_sys_TunnelConnection;
|
||||
case NetworkInterfaceType.Unknown:
|
||||
return Resources.Microsoft_plugin_sys_Unknown;
|
||||
default:
|
||||
return Resources.Microsoft_plugin_sys_Cable;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the speed as formatted text value
|
||||
/// </summary>
|
||||
/// <param name="speed">The adapter speed as <see langword="long"/>.</param>
|
||||
/// <returns>A formatted string like `100 MB/s`</returns>
|
||||
private static string GetFormattedSpeedValue(long speed)
|
||||
{
|
||||
return (speed >= 1000000000) ? string.Format(CultureInfo.InvariantCulture, MicrosoftPluginSysGbps, speed / 1000000000) : string.Format(CultureInfo.InvariantCulture, MicrosoftPluginSysMbps, speed / 1000000);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns IP info or an empty string
|
||||
/// </summary>
|
||||
/// <param name="title">Descriptive header for the information.</param>
|
||||
/// <param name="property">IP value as <see cref="string"/> or <see cref="List{String}"/>.</param>
|
||||
/// <returns>Formatted string or an empty string.</returns>
|
||||
/// <exception cref="ArgumentException">If the parameter <paramref name="property"/> is not of the type <see cref="string"/> or <see cref="List{String}"/>.</exception>
|
||||
private static string CreateIpInfoForDetailsText(string title, dynamic property)
|
||||
{
|
||||
switch (property)
|
||||
{
|
||||
case string:
|
||||
return $"\n{title}{property}";
|
||||
case List<string> listString:
|
||||
return listString.Count == 0 ? string.Empty : $"\n{title}{string.Join("\n\t", property)}";
|
||||
case List<IPAddress> listIP:
|
||||
return listIP.Count == 0 ? string.Empty : $"\n{title}{string.Join("\n\t", property)}";
|
||||
case IPAddressCollection collectionIP:
|
||||
return collectionIP.Count == 0 ? string.Empty : $"\n{title}{string.Join("\n\t", property)}";
|
||||
case null:
|
||||
return string.Empty;
|
||||
default:
|
||||
throw new ArgumentException($"'{property}' is not of type 'string', 'List<string>', 'List<IPAddress>' or 'IPAddressCollection'.", nameof(property));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.System.Helpers;
|
||||
|
||||
public static partial class OpenInShellHelper
|
||||
{
|
||||
public static bool OpenInShell(string path, string? arguments = null, string? workingDir = null, ShellRunAsType runAs = ShellRunAsType.None, bool runWithHiddenWindow = false)
|
||||
{
|
||||
using var process = new Process();
|
||||
process.StartInfo.FileName = path;
|
||||
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 (runAs == ShellRunAsType.Administrator)
|
||||
{
|
||||
process.StartInfo.Verb = "RunAs";
|
||||
}
|
||||
else if (runAs == ShellRunAsType.OtherUser)
|
||||
{
|
||||
process.StartInfo.Verb = "RunAsUser";
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
process.Start();
|
||||
return true;
|
||||
}
|
||||
catch (Win32Exception ex)
|
||||
{
|
||||
ExtensionHost.LogMessage(new LogMessage() { Message = $"Unable to open {path}: {ex.Message}" });
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public enum ShellRunAsType
|
||||
{
|
||||
None,
|
||||
Administrator,
|
||||
OtherUser,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// 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.Threading.Tasks;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using Windows.System;
|
||||
using static Microsoft.CmdPal.Ext.System.Helpers.MessageBoxHelper;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.System.Helpers;
|
||||
|
||||
internal static class ResultHelper
|
||||
{
|
||||
public static bool ExecutingEmptyRecycleBinTask { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Method to process the empty recycle bin command in a separate task
|
||||
/// </summary>
|
||||
public static void EmptyRecycleBinTask(bool settingEmptyRBSuccesMsg)
|
||||
{
|
||||
ExecutingEmptyRecycleBinTask = true;
|
||||
|
||||
// https://learn.microsoft.com/windows/win32/api/shellapi/nf-shellapi-shemptyrecyclebina/
|
||||
// http://www.pinvoke.net/default.aspx/shell32/SHEmptyRecycleBin.html/
|
||||
// If the recycle bin is already empty, it will return -2147418113 (0x8000FFFF (E_UNEXPECTED))
|
||||
// If the user canceled the deletion task it will return 2147943623 (0x800704C7 (E_CANCELLED - The operation was canceled by the user.))
|
||||
// On success it will return 0 (S_OK)
|
||||
var result = NativeMethods.SHEmptyRecycleBin(IntPtr.Zero, 0);
|
||||
if (result == (uint)HRESULT.E_UNEXPECTED)
|
||||
{
|
||||
_ = MessageBoxHelper.Show(Resources.Microsoft_plugin_sys_RecycleBin_IsEmpty, "Plugin: " + Resources.Microsoft_plugin_sys_plugin_name, IconType.Info, MessageBoxType.OK);
|
||||
}
|
||||
else if (result != (uint)HRESULT.S_OK && result != (uint)HRESULT.E_CANCELLED)
|
||||
{
|
||||
var errorDesc = Win32Helpers.MessageFromHResult((int)result);
|
||||
var name = "Plugin: " + Resources.Microsoft_plugin_sys_plugin_name;
|
||||
var message = $"{Resources.Microsoft_plugin_sys_RecycleBin_ErrorMsg} {errorDesc}";
|
||||
|
||||
ExtensionHost.LogMessage(new LogMessage() { Message = message + " - Please refer to https://msdn.microsoft.com/library/windows/desktop/aa378137 for more information." });
|
||||
|
||||
_ = MessageBoxHelper.Show(message, name, IconType.Error, MessageBoxType.OK);
|
||||
}
|
||||
|
||||
if (result == (uint)HRESULT.S_OK && settingEmptyRBSuccesMsg)
|
||||
{
|
||||
_ = MessageBoxHelper.Show(Resources.Microsoft_plugin_sys_RecycleBin_EmptySuccessMessage, "Plugin: " + Resources.Microsoft_plugin_sys_plugin_name, IconType.Info, MessageBoxType.OK);
|
||||
}
|
||||
|
||||
ExecutingEmptyRecycleBinTask = false;
|
||||
}
|
||||
}
|
||||
@@ -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.IO;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.System.Helpers;
|
||||
|
||||
public class SettingsManager : JsonSettingsManager
|
||||
{
|
||||
private static readonly string _namespace = "system";
|
||||
|
||||
private static string Namespaced(string propertyName) => $"{_namespace}.{propertyName}";
|
||||
|
||||
private readonly ToggleSetting _showDialogToConfirmCommand = new(
|
||||
Namespaced(nameof(ShowDialogToConfirmCommand)),
|
||||
Resources.confirm_system_commands,
|
||||
Resources.confirm_system_commands,
|
||||
false); // TODO -- double check default value
|
||||
|
||||
private readonly ToggleSetting _showSuccessMessageAfterEmptyingRecycleBin = new(
|
||||
Namespaced(nameof(ShowSuccessMessageAfterEmptyingRecycleBin)),
|
||||
Resources.Microsoft_plugin_sys_RecycleBin_ShowEmptySuccessMessage,
|
||||
Resources.Microsoft_plugin_sys_RecycleBin_ShowEmptySuccessMessage,
|
||||
false); // TODO -- double check default value
|
||||
|
||||
private readonly ToggleSetting _showSeparateResultForEmptyRecycleBin = new(
|
||||
Namespaced(nameof(ShowSeparateResultForEmptyRecycleBin)),
|
||||
Resources.Microsoft_plugin_sys_RecycleBin_ShowEmptySeparate,
|
||||
Resources.Microsoft_plugin_sys_RecycleBin_ShowEmptySeparate,
|
||||
true); // TODO -- double check default value
|
||||
|
||||
private readonly ToggleSetting _hideDisconnectedNetworkInfo = new(
|
||||
Namespaced(nameof(HideDisconnectedNetworkInfo)),
|
||||
Resources.Microsoft_plugin_ext_settings_hideDisconnectedNetworkInfo,
|
||||
Resources.Microsoft_plugin_ext_settings_hideDisconnectedNetworkInfo,
|
||||
true); // TODO -- double check default value
|
||||
|
||||
public bool ShowDialogToConfirmCommand => _showDialogToConfirmCommand.Value;
|
||||
|
||||
public bool ShowSuccessMessageAfterEmptyingRecycleBin => _showSuccessMessageAfterEmptyingRecycleBin.Value;
|
||||
|
||||
public bool ShowSeparateResultForEmptyRecycleBin => _showSeparateResultForEmptyRecycleBin.Value;
|
||||
|
||||
public bool HideDisconnectedNetworkInfo => _hideDisconnectedNetworkInfo.Value;
|
||||
|
||||
internal static string SettingsJsonPath()
|
||||
{
|
||||
var directory = Utilities.BaseSettingsPath("Microsoft.CmdPal");
|
||||
Directory.CreateDirectory(directory);
|
||||
|
||||
// now, the state is just next to the exe
|
||||
return Path.Combine(directory, "settings.json");
|
||||
}
|
||||
|
||||
public SettingsManager()
|
||||
{
|
||||
FilePath = SettingsJsonPath();
|
||||
|
||||
Settings.Add(_showDialogToConfirmCommand);
|
||||
Settings.Add(_showSuccessMessageAfterEmptyingRecycleBin);
|
||||
Settings.Add(_showSeparateResultForEmptyRecycleBin);
|
||||
Settings.Add(_hideDisconnectedNetworkInfo);
|
||||
|
||||
// Load settings from file upon initialization
|
||||
LoadSettings();
|
||||
|
||||
Settings.SettingsChanged += (s, a) => this.SaveSettings();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// 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.System.Helpers;
|
||||
|
||||
internal sealed class SystemPluginContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the type of the result
|
||||
/// </summary>
|
||||
public ResultContextType Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the context data for the command/results
|
||||
/// </summary>
|
||||
public string Data { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an additional result name for searching
|
||||
/// </summary>
|
||||
public string SearchTag { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
internal enum ResultContextType
|
||||
{
|
||||
Command, // Reserved for later usage
|
||||
NetworkAdapterInfo,
|
||||
RecycleBinCommand,
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
// 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.System.Helpers;
|
||||
|
||||
public static class Win32Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Detects the type of system firmware which is equal to the boot type by calling the method <see cref="NativeMethods.GetFirmwareType"/>.
|
||||
/// </summary>
|
||||
/// <returns>Firmware type like Uefi or Bios.</returns>
|
||||
public static FirmwareType GetSystemFirmwareType()
|
||||
{
|
||||
FirmwareType firmwareType = default;
|
||||
_ = NativeMethods.GetFirmwareType(ref firmwareType);
|
||||
return firmwareType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the last Win32 Error code thrown by a native method if enabled for this method.
|
||||
/// </summary>
|
||||
/// <returns>The error code as int value.</returns>
|
||||
public static int GetLastError()
|
||||
{
|
||||
return Marshal.GetLastPInvokeError();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validate that the handle is not null and close it.
|
||||
/// </summary>
|
||||
/// <param name="handle">Handle to close.</param>
|
||||
/// <returns>Zero if native method fails and nonzero if the native method succeeds.</returns>
|
||||
public static bool CloseHandleIfNotNull(IntPtr handle)
|
||||
{
|
||||
if (handle == IntPtr.Zero)
|
||||
{
|
||||
// Return true if there is nothing to close.
|
||||
return true;
|
||||
}
|
||||
|
||||
return NativeMethods.CloseHandle(handle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the description for an HRESULT error code.
|
||||
/// </summary>
|
||||
/// <param name="hr">The HRESULT number</param>
|
||||
/// <returns>A string containing the description.</returns>
|
||||
public static string MessageFromHResult(int hr)
|
||||
{
|
||||
return Marshal.GetExceptionForHR(hr)?.Message ?? string.Empty;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user