diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt
index 319a31be1f..8fa1d0ed37 100644
--- a/.github/actions/spell-check/expect.txt
+++ b/.github/actions/spell-check/expect.txt
@@ -848,11 +848,15 @@ hinstance
hitinfo
HIWORD
hk
-HKCR
+HKCC
HKCU
+HKCR
+HKE
hkey
HKL
HKLM
+HKPD
+HKU
HLOCAL
HLSL
hmenu
@@ -1161,6 +1165,7 @@ Kybd
LAlt
lambson
lamotile
+langword
Lastdevice
LASTEXITCODE
laute
@@ -1197,6 +1202,7 @@ linkid
Linkmenu
linq
LINQTo
+Linux
listbox
listview
llkhf
@@ -1765,6 +1771,7 @@ REFCLSID
refcount
REFIID
REGCLS
+regedit
regex
REGISTERCLASSFAILED
registrypath
@@ -2092,6 +2099,7 @@ SYSCOLORCHANGE
SYSCOMMAND
SYSDEADCHAR
SYSICONINDEX
+sysinfo
SYSKEY
syskeydown
syskeyup
@@ -2516,4 +2524,4 @@ zoneset
ZONESETCHANGE
Zoneszonabletester
Zoomusingmagnifier
-zzz
+zzz
\ No newline at end of file
diff --git a/.github/actions/spell-check/patterns.txt b/.github/actions/spell-check/patterns.txt
index e5192cd0c3..893dd915cc 100644
--- a/.github/actions/spell-check/patterns.txt
+++ b/.github/actions/spell-check/patterns.txt
@@ -32,3 +32,16 @@ TestCase\("[^"]+"
\\testapp
\\tests
\\tools
+
+# plugin.json
+^ "ID": "[0-9A-F]{32}",$
+
+# UnitTests
+\[DataRow\(.*\)\]
+
+# Id info inside markdown file (registry.md)
+^\|\s+ID\s+\|\s*\`[0-9A-F]{32}\`
+
+# marker for ignoring a comment to the end of the line
+^.*/\* #no-spell-check-line \*/.*$
+// #no-spell-check.*$
\ No newline at end of file
diff --git a/.pipelines/pipeline.user.windows.yml b/.pipelines/pipeline.user.windows.yml
index 883ec2d7f3..1e00f2bb48 100644
--- a/.pipelines/pipeline.user.windows.yml
+++ b/.pipelines/pipeline.user.windows.yml
@@ -118,6 +118,8 @@ build:
- 'modules\launcher\Plugins\Microsoft.Plugin.Program\Wox.Infrastructure.dll'
- 'modules\launcher\Plugins\Microsoft.Plugin.Program\Wox.Plugin.dll'
- 'modules\launcher\Plugins\Microsoft.Plugin.Program\Telemetry.dll'
+ - 'modules\launcher\Plugins\Microsoft.PowerToys.Run.Plugin.Registry\Microsoft.PowerToys.Run.Plugin.Registry.dll'
+ - 'modules\launcher\Plugins\Microsoft.PowerToys.Run.Plugin.Registry\ManagedTelemetry.dll'
- 'modules\launcher\Plugins\Microsoft.Plugin.Shell\Microsoft.Plugin.Shell.dll'
- 'modules\launcher\Plugins\Microsoft.Plugin.Shell\Wox.Infrastructure.dll'
- 'modules\launcher\Plugins\Microsoft.Plugin.Shell\Wox.Plugin.dll'
diff --git a/PowerToys.sln b/PowerToys.sln
index cca19d731f..c31302780d 100644
--- a/PowerToys.sln
+++ b/PowerToys.sln
@@ -305,6 +305,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Telemetry", "Telemetry", "{
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerToys.Common.UI", "src\common\Microsoft.PowerToys.Common.UI\Microsoft.PowerToys.Common.UI.csproj", "{C3A17DCA-217B-462C-BB0C-BE086AF80081}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerToys.Run.Plugin.Registry", "src\modules\launcher\Plugins\Microsoft.PowerToys.Run.Plugin.Registry\Microsoft.PowerToys.Run.Plugin.Registry.csproj", "{4BABF3FE-3451-42FD-873F-3C332E18DCEF}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerToys.Run.Plugin.Registry.UnitTests", "src\modules\launcher\Plugins\Microsoft.PowerToys.Run.Plugin.Registry.UnitTest\Microsoft.PowerToys.Run.Plugin.Registry.UnitTests.csproj", "{0648DF05-5DDA-4BE1-B5F2-584926EBDB65}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -619,6 +623,14 @@ Global
{C3A17DCA-217B-462C-BB0C-BE086AF80081}.Debug|x64.Build.0 = Debug|x64
{C3A17DCA-217B-462C-BB0C-BE086AF80081}.Release|x64.ActiveCfg = Release|x64
{C3A17DCA-217B-462C-BB0C-BE086AF80081}.Release|x64.Build.0 = Release|x64
+ {4BABF3FE-3451-42FD-873F-3C332E18DCEF}.Debug|x64.ActiveCfg = Debug|x64
+ {4BABF3FE-3451-42FD-873F-3C332E18DCEF}.Debug|x64.Build.0 = Debug|x64
+ {4BABF3FE-3451-42FD-873F-3C332E18DCEF}.Release|x64.ActiveCfg = Release|x64
+ {4BABF3FE-3451-42FD-873F-3C332E18DCEF}.Release|x64.Build.0 = Release|x64
+ {0648DF05-5DDA-4BE1-B5F2-584926EBDB65}.Debug|x64.ActiveCfg = Debug|x64
+ {0648DF05-5DDA-4BE1-B5F2-584926EBDB65}.Debug|x64.Build.0 = Debug|x64
+ {0648DF05-5DDA-4BE1-B5F2-584926EBDB65}.Release|x64.ActiveCfg = Release|x64
+ {0648DF05-5DDA-4BE1-B5F2-584926EBDB65}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -711,6 +723,8 @@ Global
{B39DC643-4663-475E-B329-03F0C9918D48} = {1AFB6476-670D-4E80-A464-657E01DFF482}
{8F62026A-294B-41C6-8839-87463613F216} = {1AFB6476-670D-4E80-A464-657E01DFF482}
{C3A17DCA-217B-462C-BB0C-BE086AF80081} = {1AFB6476-670D-4E80-A464-657E01DFF482}
+ {4BABF3FE-3451-42FD-873F-3C332E18DCEF} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
+ {0648DF05-5DDA-4BE1-B5F2-584926EBDB65} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}
diff --git a/doc/devdocs/modules/launcher/plugins/registry.md b/doc/devdocs/modules/launcher/plugins/registry.md
new file mode 100644
index 0000000000..d74672fb99
--- /dev/null
+++ b/doc/devdocs/modules/launcher/plugins/registry.md
@@ -0,0 +1,101 @@
+# Registry Plugin
+
+The registry plugin allows users to search the Windows registry.
+
+## Special functions (differ from the regular functions)
+
+* Support full base keys and short base keys (e.g. `HKLM` for `HKEY_LOCALE_MACHINE`).
+* Show count of subkeys and count of values in the second result line.
+* Search for value names and value data inside a registry key (syntax: `[RegistryKey]\\[ValueName]` and `[RegistryKey]\\[ValueData]`)
+
+## The Windows Registry
+
+The registry contains all settings for the Windows operating system and many settings of the installed (Windows only) programs.
+
+*Note: Linux and macOS program ports typical store the settings in it's own configuration files and not in the Windows registry.*
+
+For more information about the Windows registry, see [the official documentation](https://docs.microsoft.com/en-us/windows/win32/sysinfo/registry).
+
+For advanced information about the Windows registry, see [Windows registry information for advanced users](https://docs.microsoft.com/en-us/troubleshoot/windows-server/performance/windows-registry-advanced-users).
+
+## Score
+
+The score is currently not set on the results.
+
+## Important for developers
+
+### General
+
+* The assembly name is cached into `_assemblyName` (to avoid to many calls of `Assembly.GetExecutingAssembly()`)
+
+### Results
+
+* All results override the visible search result via `QueryTextDisplay` to avoid problems with short registry base keys (e.g. `HKLM`).
+* The length of a `Title` and `Subtitle` is automatic truncated, when it is to long.
+
+## Microsoft.Plugin.Registry project
+
+### Important plugin values (meta-data)
+
+| Name | Value |
+| --------------- | --------------------------------------------- |
+| ActionKeyword | `:` |
+| ExecuteFileName | `Microsoft.PowerToys.Run.Plugin.Registry.dll` |
+| ID | `303417D927BF4C97BCFFC78A123BE0C8` |
+
+### Interfaces used by this plugin
+
+The plugin use only these interfaces (all inside the `Main.cs`):
+
+* `Wox.Plugin.IPlugin`
+* `Wox.Plugin.IContextMenu`
+* `Wox.Plugin.IPluginI18n`
+* `System.IDisposable`
+
+### Program files
+
+| File | Content |
+| ------------------------------------ | ------------------------------------------------------------------------ |
+| `Classes\RegistryEntry.cs` | Wrapper class for a registry key with a possible exception on access |
+| `Constants\KeyName.cs` | Static used short registry key names (to avoid code and string doubling) |
+| `Constants\MaxTextLength.cs` | Contain all maximum text lengths (for truncating) |
+| `Enumeration\TruncateSide.cs` | Contain the possible truncate sides |
+| `Helper\ContextMenuHelper.cs` | All functions to build the context menu (for each result entry) |
+| `Helper\QueryHelper.cs` | All functions to analyze the search query |
+| `Helper\RegistryHelper.cs` | All functions to search into the Windows registry (via `Win32.Registry`) |
+| `Helper\ResultHelper.cs` | All functions to convert internal results into WOX results |
+| `Helper\ValueHelper.cs` | All functions to convert values into human readable values |
+| `Images\reg.dark.png` | Symbol for the results for the dark theme |
+| `Images\reg.light.png` | Symbol for the results for the light theme |
+| `Properties\Resources.Designer.resx` | File that contain all translatable keys |
+| `Properties\Resources.resx` | File that contain all translatable strings in the neutral language |
+| `GlobalSuppressions.cs` | Code suppressions (no real file, linked via *.csproj) |
+| `Main.cs` | Main class, the only place that implement the WOX interfaces |
+| `plugin.json` | All meta-data for this plugin |
+| `StyleCop.json` | Code style (no real file, linked via *.csproj) |
+
+### Important project values (*.csproj)
+
+| Name | Value |
+| --------------- | ------------------------------------------------------------------------------ |
+| TargetFramework | `netcoreapp3.1` (means .NET Core 3.1) |
+| LangVersion | `8.0` (mean C# 8.0) |
+| Platforms | `x64` |
+| Nullable | `true` |
+| Output | `..\..\..\..\..\x64\Debug\modules\launcher\Plugins\Microsoft.Plugin.Registry\` |
+| RootNamespace | `Microsoft.PowerToys.Run.Plugin.Registry` |
+| AssemblyName | `Microsoft.PowerToys.Run.Plugin.Registry` |
+
+### Project dependencies
+
+#### Packages
+
+| Package | Version |
+| ------------------------------------------------------------------------------------- | ------- |
+| [`Microsoft.CodeAnalysis.FxCopAnalyzers`](https://github.com/dotnet/roslyn-analyzers) | 3.3.0 |
+| [`StyleCop.Analyzers`](https://github.com/DotNetAnalyzers/StyleCopAnalyzers) | 1.1.118 |
+
+#### Projects
+
+* `Wox.Infrastructure`
+* `Wox.Plugin`
diff --git a/doc/devdocs/modules/launcher/readme.md b/doc/devdocs/modules/launcher/readme.md
index da77c753e8..69af0edc46 100644
--- a/doc/devdocs/modules/launcher/readme.md
+++ b/doc/devdocs/modules/launcher/readme.md
@@ -9,7 +9,8 @@
- [Folder](/doc/devdocs/modules/launcher/plugins/folder.md)
- [Indexer](/doc/devdocs/modules/launcher/plugins/indexer.md)
- [Program](/doc/devdocs/modules/launcher/plugins/program.md)
+ - [Registry](/doc/devdocs/modules/launcher/plugins/registry.md)
- [Shell](/doc/devdocs/modules/launcher/plugins/shell.md)
- [Sys](/doc/devdocs/modules/launcher/plugins/sys.md)
- [Uri](/doc/devdocs/modules/launcher/plugins/uri.md)
- - [Window Walker](/doc/devdocs/modules/launcher/plugins/windowwalker.md)
\ No newline at end of file
+ - [Window Walker](/doc/devdocs/modules/launcher/plugins/windowwalker.md)
diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry.UnitTest/Constants/KeyNameTest.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry.UnitTest/Constants/KeyNameTest.cs
new file mode 100644
index 0000000000..ce2bb3792b
--- /dev/null
+++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry.UnitTest/Constants/KeyNameTest.cs
@@ -0,0 +1,27 @@
+// 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.PowerToys.Run.Plugin.Registry.Constants;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace Microsoft.PowerToys.Run.Plugin.Registry.UnitTest.Constants
+{
+ [TestClass]
+ public sealed class KeyNameTest
+ {
+ [TestMethod]
+ [DataRow("HKEY", KeyName.FirstPart)]
+ [DataRow("HKEY_", KeyName.FirstPartUnderscore)]
+ [DataRow("HKCR", KeyName.ClassRootShort)]
+ [DataRow("HKCC", KeyName.CurrentConfigShort)]
+ [DataRow("HKCU", KeyName.CurrentUserShort)]
+ [DataRow("HKLM", KeyName.LocalMachineShort)]
+ [DataRow("HKPD", KeyName.PerformanceDataShort)]
+ [DataRow("HKU", KeyName.UsersShort)]
+ public void TestConstants(string shortName, string baseName)
+ {
+ Assert.AreEqual(shortName, baseName);
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry.UnitTest/Helper/QueryHelperTest.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry.UnitTest/Helper/QueryHelperTest.cs
new file mode 100644
index 0000000000..9a1247907a
--- /dev/null
+++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry.UnitTest/Helper/QueryHelperTest.cs
@@ -0,0 +1,41 @@
+// 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.PowerToys.Run.Plugin.Registry.Helper;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace Microsoft.PowerToys.Run.Plugin.Registry.UnitTest.Helper
+{
+ [TestClass]
+ public sealed class QueryHelperTest
+ {
+ [TestMethod]
+ [DataRow(@"HKLM", false, @"HKLM", "")]
+ [DataRow(@"HKLM\", false, @"HKLM\", "")]
+ [DataRow(@"HKLM\\", true, @"HKLM", "")]
+ [DataRow(@"HKLM\\Test", true, @"HKLM", "Test")]
+ [DataRow(@"HKLM\Test\\TestTest", true, @"HKLM\Test", "TestTest")]
+ [DataRow(@"HKLM\Test\\\TestTest", true, @"HKLM\Test", @"\TestTest")]
+ public void GetQueryPartsTest(string query, bool expectedHasValueName, string expectedQueryKey, string expectedQueryValueName)
+ {
+ var hasValueName = QueryHelper.GetQueryParts(query, out var queryKey, out var queryValueName);
+
+ Assert.AreEqual(expectedHasValueName, hasValueName);
+ Assert.AreEqual(expectedQueryKey, queryKey);
+ Assert.AreEqual(expectedQueryValueName, queryValueName);
+ }
+
+ [TestMethod]
+ [DataRow(@"HKCR\*\OpenWithList", @"HKEY_CLASSES_ROOT\*\OpenWithList")]
+ [DataRow(@"HKCU\Control Panel\Accessibility", @"HKEY_CURRENT_USER\Control Panel\Accessibility")]
+ [DataRow(@"HKLM\HARDWARE\UEFI", @"HKEY_LOCAL_MACHINE\HARDWARE\UEFI")]
+ [DataRow(@"HKU\.DEFAULT\Environment", @"HKEY_USERS\.DEFAULT\Environment")]
+ [DataRow(@"HKCC\System\CurrentControlSet\Control", @"HKEY_CURRENT_CONFIG\System\CurrentControlSet\Control")]
+ [DataRow(@"HKPD\???", @"HKEY_PERFORMANCE_DATA\???")]
+ public void GetShortBaseKeyTest(string registryKeyShort, string registryKeyFull)
+ {
+ Assert.AreEqual(registryKeyShort, QueryHelper.GetKeyWithShortBaseKey(registryKeyFull));
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry.UnitTest/Helper/RegistryHelperTest.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry.UnitTest/Helper/RegistryHelperTest.cs
new file mode 100644
index 0000000000..8855d3e23e
--- /dev/null
+++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry.UnitTest/Helper/RegistryHelperTest.cs
@@ -0,0 +1,73 @@
+// 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.Collections;
+using System.Linq;
+using Microsoft.PowerToys.Run.Plugin.Registry.Helper;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace Microsoft.PowerToys.Run.Plugin.Registry.UnitTest.Helper
+{
+ [TestClass]
+ public sealed class RegistryHelperTest
+ {
+ [TestMethod]
+ [DataRow(@"HKCC\System\CurrentControlSet\Control", "HKEY_CURRENT_CONFIG")]
+ [DataRow(@"HKCR\*\OpenWithList", "HKEY_CLASSES_ROOT")]
+ [DataRow(@"HKCU\Control Panel\Accessibility", "HKEY_CURRENT_USER")]
+ [DataRow(@"HKLM\HARDWARE\UEFI", "HKEY_LOCAL_MACHINE")]
+ [DataRow(@"HKPD\???", "HKEY_PERFORMANCE_DATA")]
+ [DataRow(@"HKU\.DEFAULT\Environment", "HKEY_USERS")]
+ public void GetRegistryBaseKeyTestOnlyOneBaseKey(string query, string expectedBaseKey)
+ {
+ var (baseKeyList, _) = RegistryHelper.GetRegistryBaseKey(query);
+ Assert.IsTrue(baseKeyList.Count() == 1);
+ Assert.AreEqual(expectedBaseKey, baseKeyList.FirstOrDefault().Name);
+ }
+
+ [TestMethod]
+ public void GetRegistryBaseKeyTestMoreThanOneBaseKey()
+ {
+ var (baseKeyList, _) = RegistryHelper.GetRegistryBaseKey("HKC\\Control Panel\\Accessibility"); /* #no-spell-check-line */
+
+ Assert.IsTrue(baseKeyList.Count() > 1);
+
+ var list = baseKeyList.Select(found => found.Name);
+ Assert.IsTrue(list.Contains("HKEY_CLASSES_ROOT"));
+ Assert.IsTrue(list.Contains("HKEY_CURRENT_CONFIG"));
+ Assert.IsTrue(list.Contains("HKEY_CURRENT_USER"));
+ }
+
+ [TestMethod]
+ [DataRow(@"HKCR\*\OpenWithList", @"*\OpenWithList")]
+ [DataRow(@"HKCU\Control Panel\Accessibility", @"Control Panel\Accessibility")]
+ [DataRow(@"HKLM\HARDWARE\UEFI", @"HARDWARE\UEFI")]
+ [DataRow(@"HKU\.DEFAULT\Environment", @".DEFAULT\Environment")]
+ [DataRow(@"HKCC\System\CurrentControlSet\Control", @"System\CurrentControlSet\Control")]
+ [DataRow(@"HKPD\???", @"???")]
+ public void GetRegistryBaseKeyTestSubKey(string query, string expectedSubKey)
+ {
+ var (_, subKey) = RegistryHelper.GetRegistryBaseKey(query);
+ Assert.AreEqual(expectedSubKey, subKey);
+ }
+
+ [TestMethod]
+ public void GetAllBaseKeysTest()
+ {
+ var list = RegistryHelper.GetAllBaseKeys();
+
+ CollectionAssert.AllItemsAreNotNull((ICollection)list);
+ CollectionAssert.AllItemsAreUnique((ICollection)list);
+
+ var keys = list.Select(found => found.Key).ToList() as ICollection;
+
+ CollectionAssert.Contains(keys, Win32.Registry.ClassesRoot);
+ CollectionAssert.Contains(keys, Win32.Registry.CurrentConfig);
+ CollectionAssert.Contains(keys, Win32.Registry.CurrentUser);
+ CollectionAssert.Contains(keys, Win32.Registry.LocalMachine);
+ CollectionAssert.Contains(keys, Win32.Registry.PerformanceData);
+ CollectionAssert.Contains(keys, Win32.Registry.Users);
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry.UnitTest/Helper/ResultHelperTest.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry.UnitTest/Helper/ResultHelperTest.cs
new file mode 100644
index 0000000000..16b3c47a5e
--- /dev/null
+++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry.UnitTest/Helper/ResultHelperTest.cs
@@ -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.
+
+using Microsoft.PowerToys.Run.Plugin.Registry.Helper;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace Microsoft.PowerToys.Run.Plugin.Registry.UnitTest.Helper
+{
+ [TestClass]
+ public sealed class ResultHelperTest
+ {
+ [TestMethod]
+ [DataRow(@"HKEY_CLASSES_ROOT\*\OpenWithList", @"HKEY_CLASSES_ROOT\*\OpenWithList")]
+ [DataRow(@"HKEY_CURRENT_USER\Control Panel\Accessibility", @"HKEY_CURRENT_USER\Control Panel\Accessibility")]
+ [DataRow(@"HKEY_LOCAL_MACHINE\HARDWARE\UEFI", @"HKEY_LOCAL_MACHINE\HARDWARE\UEFI")]
+ [DataRow(@"HKEY_USERS\.DEFAULT\Environment", @"HKEY_USERS\.DEFAULT\Environment")]
+ [DataRow(@"HKCC\System\CurrentControlSet\Control", @"HKEY_CURRENT_CONFIG\System\CurrentControlSet\Control")]
+ [DataRow(@"HKEY_PERFORMANCE_DATA\???", @"HKEY_PERFORMANCE_DATA\???")]
+ [DataRow(@"HKCR\*\shell\Open with VS Code\command", @"HKEY_CLASSES_ROOT\*\shell\Open with VS Code\command")]
+ [DataRow(@"...ndows\CurrentVersion\Explorer\StartupApproved", @"HKEY_CURRENT_USER\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved")]
+ [DataRow(@"...p\Upgrade\NetworkDriverBackup\Control\Network", @"HKEY_LOCAL_MACHINE\SYSTEM\Setup\Upgrade\NetworkDriverBackup\Control\Network")]
+ [DataRow(@"...anel\International\User Profile System Backup", @"HKEY_USERS\.DEFAULT\Control Panel\International\User Profile System Backup")]
+ [DataRow(@"...stem\CurrentControlSet\Control\Print\Printers", @"HKEY_CURRENT_CONFIG\System\CurrentControlSet\Control\Print\Printers")]
+ public void GetTruncatedTextTest(string registryKeyShort, string registryKeyFull)
+ {
+ Assert.AreEqual(registryKeyShort, ResultHelper.GetTruncatedText(registryKeyFull, 45));
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry.UnitTest/Microsoft.PowerToys.Run.Plugin.Registry.UnitTests.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry.UnitTest/Microsoft.PowerToys.Run.Plugin.Registry.UnitTests.csproj
new file mode 100644
index 0000000000..19a76d6aa8
--- /dev/null
+++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry.UnitTest/Microsoft.PowerToys.Run.Plugin.Registry.UnitTests.csproj
@@ -0,0 +1,65 @@
+
+
+
+ netcoreapp3.1
+ x64
+ en-US
+ 8.0
+ enable
+ false
+
+
+
+ true
+ DEBUG;TRACE
+ full
+ x64
+ prompt
+ MinimumRecommendedRules.ruleset
+ 4
+ false
+ true
+
+
+
+ TRACE
+ true
+ pdbonly
+ x64
+ prompt
+ MinimumRecommendedRules.ruleset
+ 4
+ true
+
+
+
+
+
+
+
+
+
+
+ GlobalSuppressions.cs
+
+
+ StyleCop.json
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Classes/RegistryEntry.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Classes/RegistryEntry.cs
new file mode 100644
index 0000000000..691f1dd3b5
--- /dev/null
+++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Classes/RegistryEntry.cs
@@ -0,0 +1,113 @@
+// 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 Microsoft.PowerToys.Run.Plugin.Registry.Helper;
+using Microsoft.Win32;
+
+namespace Microsoft.PowerToys.Run.Plugin.Registry.Classes
+{
+ ///
+ /// A entry of the registry.
+ ///
+ internal class RegistryEntry
+ {
+ ///
+ /// Gets the full path to a registry key.
+ ///
+ internal string KeyPath { get; }
+
+ ///
+ /// Gets the registry key for this entry.
+ ///
+ internal RegistryKey? Key { get; }
+
+ ///
+ /// Gets a possible exception that was occurred when try to open this registry key (e.g. ).
+ ///
+ internal Exception? Exception { get; }
+
+ ///
+ /// Gets the name of the current selected registry value.
+ ///
+ internal string? ValueName { get; }
+
+ ///
+ /// Gets the value of the current selected registry value.
+ ///
+ internal object? ValueData { get; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The full path to the registry key for this entry.
+ /// A exception that was occurred when try to access this registry key.
+ internal RegistryEntry(string keyPath, Exception exception)
+ {
+ KeyPath = keyPath;
+ Exception = exception;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The for this entry.
+ internal RegistryEntry(RegistryKey key)
+ {
+ KeyPath = key.Name;
+ Key = key;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The for this entry.
+ /// The value name of the current selected registry value.
+ /// The value of the current selected registry value.
+ internal RegistryEntry(RegistryKey key, string valueName, object value)
+ {
+ KeyPath = key.Name;
+ Key = key;
+ ValueName = valueName;
+ ValueData = value;
+ }
+
+ ///
+ /// Return the registry key.
+ ///
+ /// A registry key.
+ internal string GetRegistryKey()
+ {
+ return $"{Key?.Name ?? KeyPath}";
+ }
+
+ ///
+ /// Return the value name with the complete registry key.
+ ///
+ /// A value name with the complete registry key.
+ internal string GetValueNameWithKey()
+ {
+ return $"{Key?.Name ?? KeyPath}\\\\{ValueName?.ToString() ?? string.Empty}";
+ }
+
+ ///
+ /// Return the value data of a value name inside a registry key.
+ ///
+ /// A value data.
+ internal string GetValueData()
+ {
+ if (Key is null)
+ {
+ return KeyPath;
+ }
+
+ if (string.IsNullOrEmpty(ValueName))
+ {
+ return Key.Name;
+ }
+
+ return ValueHelper.GetValue(Key, ValueName);
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Constants/KeyName.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Constants/KeyName.cs
new file mode 100644
index 0000000000..1aea08bfc3
--- /dev/null
+++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Constants/KeyName.cs
@@ -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.
+
+namespace Microsoft.PowerToys.Run.Plugin.Registry.Constants
+{
+ ///
+ /// This class contains names for important registry keys
+ ///
+ internal static class KeyName
+ {
+ ///
+ /// The first name part of each base key without the underscore
+ ///
+ internal const string FirstPart = "HKEY";
+
+ ///
+ /// The first name part of each base key follow by a underscore
+ ///
+ internal const string FirstPartUnderscore = "HKEY_";
+
+ ///
+ /// The short name for the base key HKEY_CLASSES_ROOT (see )
+ ///
+ internal const string ClassRootShort = "HKCR";
+
+ ///
+ /// The short name for the base key HKEY_CURRENT_CONFIG (see )
+ ///
+ internal const string CurrentConfigShort = "HKCC";
+
+ ///
+ /// The short name for the base key HKEY_CURRENT_USER (see )
+ ///
+ internal const string CurrentUserShort = "HKCU";
+
+ ///
+ /// The short name for the base key HKEY_LOCAL_MACHINE (see )
+ ///
+ internal const string LocalMachineShort = "HKLM";
+
+ ///
+ /// The short name for the base key HKEY_PERFORMANCE_DATA (see )
+ ///
+ internal const string PerformanceDataShort = "HKPD";
+
+ ///
+ /// The short name for the base key HKEY_USERS (see )
+ ///
+ internal const string UsersShort = "HKU";
+ }
+}
diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Constants/MaxTextLength.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Constants/MaxTextLength.cs
new file mode 100644
index 0000000000..280460de9a
--- /dev/null
+++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Constants/MaxTextLength.cs
@@ -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.
+
+namespace Microsoft.PowerToys.Run.Plugin.Registry.Constants
+{
+ ///
+ /// This class contain all maximum text length.
+ ///
+ public static class MaxTextLength
+ {
+ ///
+ /// The maximum length for the title text length with two context menu symbols on the right.
+ ///
+ internal const int MaximumTitleLengthWithTwoSymbols = 44;
+
+ ///
+ /// The maximum length for the title text length with three context menu symbols on the right.
+ ///
+ internal const int MaximumTitleLengthWithThreeSymbols = 40;
+
+ ///
+ /// The maximum length for the sub-title text length with two context menu symbols on the right.
+ ///
+ internal const int MaximumSubTitleLengthWithTwoSymbols = 85;
+
+ ///
+ /// The maximum length for the sub-title text length with three context menu symbols on the right.
+ ///
+ internal const int MaximumSubTitleLengthWithThreeSymbols = 78;
+ }
+}
diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Enumerations/TruncateSide.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Enumerations/TruncateSide.cs
new file mode 100644
index 0000000000..10be1f2da6
--- /dev/null
+++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Enumerations/TruncateSide.cs
@@ -0,0 +1,22 @@
+// 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.PowerToys.Run.Plugin.Registry.Enumerations
+{
+ ///
+ /// The truncate side for a to long text
+ ///
+ internal enum TruncateSide
+ {
+ ///
+ /// Truncate a text only from the right side
+ ///
+ OnlyFromLeft,
+
+ ///
+ /// Truncate a text only from the left side
+ ///
+ OnlyFromRight,
+ }
+}
diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Helper/ContextMenuHelper.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Helper/ContextMenuHelper.cs
new file mode 100644
index 0000000000..9572d6b677
--- /dev/null
+++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Helper/ContextMenuHelper.cs
@@ -0,0 +1,140 @@
+// 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.Windows;
+using System.Windows.Input;
+using Microsoft.PowerToys.Run.Plugin.Registry.Classes;
+using Microsoft.PowerToys.Run.Plugin.Registry.Properties;
+using Wox.Plugin;
+using Wox.Plugin.Logger;
+
+namespace Microsoft.PowerToys.Run.Plugin.Registry.Helper
+{
+ ///
+ /// Helper class to easier work with context menu entries
+ ///
+ internal static class ContextMenuHelper
+ {
+ ///
+ /// Return a list with all context menu entries for the given
+ /// Symbols taken from
+ ///
+ /// The result for the context menu entires
+ /// The name of the this assembly
+ /// A list with context menu entries
+ internal static List GetContextMenu(Result result, string assemblyName)
+ {
+ if (!(result?.ContextData is RegistryEntry entry))
+ {
+ return new List(0);
+ }
+
+ var list = new List();
+
+ if (string.IsNullOrEmpty(entry.ValueName))
+ {
+ list.Add(new ContextMenuResult
+ {
+ AcceleratorKey = Key.C,
+ AcceleratorModifiers = ModifierKeys.Control,
+ Action = _ => TryToCopyToClipBoard(entry.GetRegistryKey()),
+ FontFamily = "Segoe MDL2 Assets",
+ Glyph = "\xE8C8", // E8C8 => Symbol: Copy
+ PluginName = assemblyName,
+ Title = $"{Resources.CopyKeyNamePath} (Ctrl+C)",
+ });
+ }
+ else
+ {
+ list.Add(new ContextMenuResult
+ {
+ AcceleratorKey = Key.C,
+ AcceleratorModifiers = ModifierKeys.Control | ModifierKeys.Shift,
+ Action = _ => TryToCopyToClipBoard(entry.GetValueData()),
+ FontFamily = "Segoe MDL2 Assets",
+ Glyph = "\xF413", // F413 => Symbol: CopyTo
+ PluginName = assemblyName,
+ Title = $"{Resources.CopyValueData} (Ctrl+Shift+C)",
+ });
+
+ list.Add(new ContextMenuResult
+ {
+ AcceleratorKey = Key.C,
+ AcceleratorModifiers = ModifierKeys.Control,
+ Action = _ => TryToCopyToClipBoard(entry.GetValueNameWithKey()),
+ FontFamily = "Segoe MDL2 Assets",
+ Glyph = "\xE8C8", // E8C8 => Symbol: Copy
+ PluginName = assemblyName,
+ Title = $"{Resources.CopyValueName} (Ctrl+C)",
+ });
+ }
+
+ list.Add(new ContextMenuResult
+ {
+ AcceleratorKey = Key.Enter,
+ Action = _ => TryToOpenInRegistryEditor(entry),
+ FontFamily = "Segoe MDL2 Assets",
+ Glyph = "\xE8A7", // E8A7 => Symbol: OpenInNewWindow
+ PluginName = assemblyName,
+ Title = $"{Resources.OpenKeyInRegistryEditor} (Enter)",
+ });
+
+ return list;
+ }
+
+ #pragma warning disable CA1031 // Do not catch general exception types
+
+ ///
+ /// Open the Windows registry editor and jump to registry key inside the given key (inside the
+ ///
+ /// The to jump in
+ /// if the registry editor was successful open, otherwise
+ internal static bool TryToOpenInRegistryEditor(in RegistryEntry entry)
+ {
+ try
+ {
+ RegistryHelper.OpenRegistryKey(entry.Key?.Name ?? entry.KeyPath);
+ return true;
+ }
+ catch (System.ComponentModel.Win32Exception)
+ {
+ MessageBox.Show(
+ Resources.OpenInRegistryEditorAccessExceptionText,
+ Resources.OpenInRegistryEditorAccessExceptionTitle,
+ MessageBoxButton.OK,
+ MessageBoxImage.Error);
+ return false;
+ }
+ catch (Exception exception)
+ {
+ Log.Exception("Error on opening Windows registry editor", exception, typeof(Main));
+ return false;
+ }
+ }
+
+ ///
+ /// Copy the given text to the clipboard
+ ///
+ /// The text to copy to the clipboard
+ /// The text successful copy to the clipboard, otherwise
+ private static bool TryToCopyToClipBoard(in string text)
+ {
+ try
+ {
+ Clipboard.Clear();
+ Clipboard.SetText(text);
+ return true;
+ }
+ catch (Exception exception)
+ {
+ Log.Exception("Can't copy to clipboard", exception, typeof(Main));
+ return false;
+ }
+ }
+
+ #pragma warning restore CA1031 // Do not catch general exception types
+ }
+}
diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Helper/QueryHelper.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Helper/QueryHelper.cs
new file mode 100644
index 0000000000..3e37f3b193
--- /dev/null
+++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Helper/QueryHelper.cs
@@ -0,0 +1,98 @@
+// 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.Linq;
+using Microsoft.PowerToys.Run.Plugin.Registry.Constants;
+
+namespace Microsoft.PowerToys.Run.Plugin.Registry.Helper
+{
+ ///
+ /// Helper class to easier work with queries
+ ///
+ internal static class QueryHelper
+ {
+ ///
+ /// The character to distinguish if the search query contain multiple parts (typically "\\")
+ ///
+ internal const string QuerySplitCharacter = "\\\\";
+
+ ///
+ /// A list that contain short names of all registry base keys
+ ///
+ private static readonly IReadOnlyDictionary _shortBaseKeys = new Dictionary(6)
+ {
+ { Win32.Registry.ClassesRoot.Name, KeyName.ClassRootShort },
+ { Win32.Registry.CurrentConfig.Name, KeyName.CurrentConfigShort },
+ { Win32.Registry.CurrentUser.Name, KeyName.CurrentUserShort },
+ { Win32.Registry.LocalMachine.Name, KeyName.LocalMachineShort },
+ { Win32.Registry.PerformanceData.Name, KeyName.PerformanceDataShort },
+ { Win32.Registry.Users.Name, KeyName.UsersShort },
+ };
+
+ ///
+ /// Return the parts of a given query
+ ///
+ /// The query that could contain parts
+ /// The key part of the query
+ /// The value name part of the query
+ /// when the query search for a key and a value name, otherwise
+ internal static bool GetQueryParts(in string query, out string queryKey, out string queryValueName)
+ {
+ if (!query.Contains(QuerySplitCharacter, StringComparison.InvariantCultureIgnoreCase))
+ {
+ queryKey = query;
+ queryValueName = string.Empty;
+ return false;
+ }
+
+ var querySplit = query.Split(QuerySplitCharacter);
+
+ queryKey = querySplit.FirstOrDefault();
+ queryValueName = querySplit.LastOrDefault();
+ return true;
+ }
+
+ ///
+ /// Return a registry key with a long base key
+ ///
+ /// A registry key with a short base key
+ /// A registry key with a long base key
+ internal static string GetKeyWithLongBaseKey(in string registryKey)
+ {
+ foreach (var shortName in _shortBaseKeys)
+ {
+ if (!registryKey.StartsWith(shortName.Value, StringComparison.InvariantCultureIgnoreCase))
+ {
+ continue;
+ }
+
+ return registryKey.Replace(shortName.Value, shortName.Key, StringComparison.InvariantCultureIgnoreCase);
+ }
+
+ return registryKey;
+ }
+
+ ///
+ /// Return a registry key with a short base key (useful to reduce the text length of a registry key)
+ ///
+ /// A registry key with a full base key
+ /// A registry key with a short base key
+ internal static string GetKeyWithShortBaseKey(in string registryKey)
+ {
+ foreach (var shortName in _shortBaseKeys)
+ {
+ if (!registryKey.StartsWith(shortName.Key, StringComparison.InvariantCultureIgnoreCase))
+ {
+ continue;
+ }
+
+ return registryKey.Replace(shortName.Key, shortName.Value, StringComparison.InvariantCultureIgnoreCase);
+ }
+
+ return registryKey;
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Helper/RegistryHelper.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Helper/RegistryHelper.cs
new file mode 100644
index 0000000000..5354ef835c
--- /dev/null
+++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Helper/RegistryHelper.cs
@@ -0,0 +1,239 @@
+// 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.Collections.ObjectModel;
+using System.Diagnostics;
+using System.Linq;
+using Microsoft.PowerToys.Run.Plugin.Registry.Classes;
+using Microsoft.PowerToys.Run.Plugin.Registry.Constants;
+using Microsoft.PowerToys.Run.Plugin.Registry.Properties;
+using Microsoft.Win32;
+
+namespace Microsoft.PowerToys.Run.Plugin.Registry.Helper
+{
+#pragma warning disable CA1031 // Do not catch general exception types
+
+ ///
+ /// Helper class to easier work with the registry
+ ///
+ internal static class RegistryHelper
+ {
+ ///
+ /// A list that contain all registry base keys in a long/full version and in a short version (e.g HKLM = HKEY_LOCAL_MACHINE)
+ ///
+ private static readonly IReadOnlyDictionary _baseKeys = new Dictionary(12)
+ {
+ { KeyName.ClassRootShort, Win32.Registry.ClassesRoot },
+ { Win32.Registry.ClassesRoot.Name, Win32.Registry.ClassesRoot },
+ { KeyName.CurrentConfigShort, Win32.Registry.CurrentConfig },
+ { Win32.Registry.CurrentConfig.Name, Win32.Registry.CurrentConfig },
+ { KeyName.CurrentUserShort, Win32.Registry.CurrentUser },
+ { Win32.Registry.CurrentUser.Name, Win32.Registry.CurrentUser },
+ { KeyName.LocalMachineShort, Win32.Registry.LocalMachine },
+ { Win32.Registry.LocalMachine.Name, Win32.Registry.LocalMachine },
+ { KeyName.PerformanceDataShort, Win32.Registry.PerformanceData },
+ { Win32.Registry.PerformanceData.Name, Win32.Registry.PerformanceData },
+ { KeyName.UsersShort, Win32.Registry.Users },
+ { Win32.Registry.Users.Name, Win32.Registry.Users },
+ };
+
+ ///
+ /// Try to find registry base keys based on the given query
+ ///
+ /// The query to search
+ /// A combination of a list of base and the sub keys
+ internal static (IEnumerable? baseKey, string subKey) GetRegistryBaseKey(in string query)
+ {
+ if (string.IsNullOrWhiteSpace(query))
+ {
+ return (null, string.Empty);
+ }
+
+ var baseKey = query.Split('\\').FirstOrDefault();
+ var subKey = query.Replace(baseKey, string.Empty, StringComparison.InvariantCultureIgnoreCase).TrimStart('\\');
+
+ var baseKeyResult = _baseKeys
+ .Where(found => found.Key.StartsWith(baseKey, StringComparison.InvariantCultureIgnoreCase))
+ .Select(found => found.Value)
+ .Distinct();
+
+ return (baseKeyResult, subKey);
+ }
+
+ ///
+ /// Return a list of all registry base key
+ ///
+ /// A list with all registry base keys
+ internal static ICollection GetAllBaseKeys()
+ {
+ return new Collection
+ {
+ new RegistryEntry(Win32.Registry.ClassesRoot),
+ new RegistryEntry(Win32.Registry.CurrentConfig),
+ new RegistryEntry(Win32.Registry.CurrentUser),
+ new RegistryEntry(Win32.Registry.LocalMachine),
+ new RegistryEntry(Win32.Registry.PerformanceData),
+ new RegistryEntry(Win32.Registry.Users),
+ };
+ }
+
+ ///
+ /// Search for the given sub-key path in the given registry base key
+ ///
+ /// The base
+ /// The path of the registry sub-key
+ /// A list with all found registry keys
+ internal static ICollection SearchForSubKey(in RegistryKey baseKey, in string subKeyPath)
+ {
+ if (string.IsNullOrEmpty(subKeyPath))
+ {
+ return FindSubKey(baseKey, string.Empty);
+ }
+
+ var subKeysNames = subKeyPath.Split('\\');
+ var index = 0;
+ var subKey = baseKey;
+
+ ICollection result;
+
+ do
+ {
+ result = FindSubKey(subKey, subKeysNames.ElementAtOrDefault(index));
+
+ if (result.Count == 0)
+ {
+ return FindSubKey(subKey, string.Empty);
+ }
+
+ if (result.Count == 1 && index < subKeysNames.Length)
+ {
+ subKey = result.First().Key;
+ }
+
+ if (result.Count > 1 || subKey == null)
+ {
+ break;
+ }
+
+ index++;
+ }
+ while (index < subKeysNames.Length);
+
+ return result;
+ }
+
+ ///
+ /// Return a human readable summary of a given
+ ///
+ /// The for the summary
+ /// A human readable summary
+ internal static string GetSummary(in RegistryKey key)
+ {
+ return $"{Resources.SubKeys} {key.SubKeyCount} - {Resources.Values} {key.ValueCount}";
+ }
+
+ ///
+ /// Open a given registry key in the registry editor
+ ///
+ /// The registry key to open
+ internal static void OpenRegistryKey(in string fullKey)
+ {
+ // it's impossible to directly open a key via command-line option, so we must override the last remember key
+ Win32.Registry.SetValue(@"HKEY_Current_User\Software\Microsoft\Windows\CurrentVersion\Applets\Regedit", "LastKey", fullKey);
+
+ var processStartInfo = new ProcessStartInfo
+ {
+ // -m => allow multi-instance (hidden start option)
+ Arguments = "-m",
+
+ FileName = "regedit.exe",
+
+ // Start as administrator
+ Verb = "runas",
+
+ // Start as administrator will not work without this
+ UseShellExecute = true,
+ };
+
+ Process.Start(processStartInfo);
+ }
+
+ ///
+ /// Try to find the given registry sub-key in the given registry parent-key
+ ///
+ /// The parent-key, also the root to start the search
+ /// The sub-key to find
+ /// A list with all found registry sub-keys
+ private static ICollection FindSubKey(in RegistryKey parentKey, in string searchSubKey)
+ {
+ var list = new Collection();
+
+ try
+ {
+ foreach (var subKey in parentKey.GetSubKeyNames().OrderBy(found => found))
+ {
+ if (!subKey.StartsWith(searchSubKey, StringComparison.InvariantCultureIgnoreCase))
+ {
+ continue;
+ }
+
+ if (string.Equals(subKey, searchSubKey, StringComparison.InvariantCultureIgnoreCase))
+ {
+ list.Add(new RegistryEntry(parentKey.OpenSubKey(subKey, RegistryKeyPermissionCheck.ReadSubTree)));
+ return list;
+ }
+
+ try
+ {
+ list.Add(new RegistryEntry(parentKey.OpenSubKey(subKey, RegistryKeyPermissionCheck.ReadSubTree)));
+ }
+ catch (Exception exception)
+ {
+ list.Add(new RegistryEntry($"{parentKey.Name}\\{subKey}", exception));
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ list.Add(new RegistryEntry(parentKey.Name, ex));
+ }
+
+ return list;
+ }
+
+ ///
+ /// Return a list with a registry sub-keys of the given registry parent-key
+ ///
+ /// The registry parent-key
+ /// (optional) The maximum count of the results
+ /// A list with all found registry sub-keys
+ private static ICollection GetAllSubKeys(in RegistryKey parentKey, in int maxCount = 50)
+ {
+ var list = new Collection();
+
+ try
+ {
+ foreach (var subKey in parentKey.GetSubKeyNames())
+ {
+ if (list.Count >= maxCount)
+ {
+ break;
+ }
+
+ list.Add(new RegistryEntry(parentKey));
+ }
+ }
+ catch (Exception exception)
+ {
+ list.Add(new RegistryEntry(parentKey.Name, exception));
+ }
+
+ return list;
+ }
+ }
+
+ #pragma warning restore CA1031 // Do not catch general exception types
+}
diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Helper/ResultHelper.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Helper/ResultHelper.cs
new file mode 100644
index 0000000000..b6f1b050f8
--- /dev/null
+++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Helper/ResultHelper.cs
@@ -0,0 +1,219 @@
+// 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.Linq;
+using Microsoft.PowerToys.Run.Plugin.Registry.Classes;
+using Microsoft.PowerToys.Run.Plugin.Registry.Constants;
+using Microsoft.PowerToys.Run.Plugin.Registry.Enumerations;
+using Microsoft.PowerToys.Run.Plugin.Registry.Properties;
+using Microsoft.Win32;
+using Wox.Plugin;
+
+namespace Microsoft.PowerToys.Run.Plugin.Registry.Helper
+{
+ ///
+ /// Helper class to easier work with results
+ ///
+ internal static class ResultHelper
+ {
+ #pragma warning disable CA1031 // Do not catch general exception types
+
+ ///
+ /// Return a list with s, based on the given list
+ ///
+ /// The original result list to convert
+ /// The path to the icon of each entry
+ /// A list with
+ internal static List GetResultList(in IEnumerable list, in string iconPath)
+ {
+ var resultList = new List();
+
+ foreach (var entry in list)
+ {
+ var result = new Result
+ {
+ IcoPath = iconPath,
+ };
+
+ if (entry.Exception is null && !(entry.Key is null))
+ {
+ // when key contains keys or fields
+ result.QueryTextDisplay = entry.Key.Name;
+ result.SubTitle = RegistryHelper.GetSummary(entry.Key);
+ result.Title = GetTruncatedText(entry.Key.Name, MaxTextLength.MaximumTitleLengthWithTwoSymbols);
+ }
+ else if (entry.Key is null && !(entry.Exception is null))
+ {
+ // on error (e.g access denied)
+ result.QueryTextDisplay = entry.KeyPath;
+ result.SubTitle = GetTruncatedText(entry.Exception.Message, MaxTextLength.MaximumSubTitleLengthWithTwoSymbols, TruncateSide.OnlyFromRight);
+ result.Title = GetTruncatedText(entry.KeyPath, MaxTextLength.MaximumTitleLengthWithTwoSymbols);
+ }
+ else
+ {
+ result.QueryTextDisplay = entry.KeyPath;
+ result.Title = GetTruncatedText(entry.KeyPath, MaxTextLength.MaximumTitleLengthWithTwoSymbols);
+ }
+
+ result.Action = (_) => ContextMenuHelper.TryToOpenInRegistryEditor(entry);
+ result.ContextData = entry;
+ result.ToolTipData = new ToolTipData(Resources.RegistryKey, $"{Resources.KeyName}\t{result.Title}");
+
+ resultList.Add(result);
+ }
+
+ return resultList;
+ }
+
+ ///
+ /// Return a list with s, based on the given
+ ///
+ /// The that should contain entries for the list
+ /// The path to the icon of each entry
+ /// (optional) When not filter the list for the given value name and value
+ /// A list with
+ internal static List GetValuesFromKey(in RegistryKey? key, in string iconPath, string searchValue = "")
+ {
+ if (key is null)
+ {
+ return new List(0);
+ }
+
+ ICollection> valueList = new List>(key.ValueCount);
+
+ var resultList = new List();
+
+ try
+ {
+ var valueNames = key.GetValueNames();
+
+ try
+ {
+ foreach (var valueName in valueNames)
+ {
+ valueList.Add(KeyValuePair.Create(valueName, key.GetValue(valueName)));
+ }
+ }
+ catch (Exception valueException)
+ {
+ var registryEntry = new RegistryEntry(key.Name, valueException);
+
+ resultList.Add(new Result
+ {
+ ContextData = registryEntry,
+ IcoPath = iconPath,
+ SubTitle = GetTruncatedText(valueException.Message, MaxTextLength.MaximumSubTitleLengthWithThreeSymbols, TruncateSide.OnlyFromRight),
+ Title = GetTruncatedText(key.Name, MaxTextLength.MaximumTitleLengthWithThreeSymbols),
+ ToolTipData = new ToolTipData(valueException.Message, valueException.ToString()),
+ Action = (_) => ContextMenuHelper.TryToOpenInRegistryEditor(registryEntry),
+ QueryTextDisplay = key.Name,
+ });
+ }
+
+ if (!string.IsNullOrEmpty(searchValue))
+ {
+ var filteredValueName = valueList.Where(found => found.Key.Contains(searchValue, StringComparison.InvariantCultureIgnoreCase));
+ var filteredValueList = valueList.Where(found => found.Value.ToString()?.Contains(searchValue, StringComparison.InvariantCultureIgnoreCase) ?? false);
+
+ valueList = filteredValueName.Concat(filteredValueList).Distinct().ToList();
+ }
+
+ foreach (var valueEntry in valueList.OrderBy(found => found.Key))
+ {
+ var valueName = valueEntry.Key;
+ if (string.IsNullOrEmpty(valueName))
+ {
+ valueName = "(Default)";
+ }
+
+ var registryEntry = new RegistryEntry(key, valueEntry.Key, valueEntry.Value);
+
+ resultList.Add(new Result
+ {
+ ContextData = registryEntry,
+ IcoPath = iconPath,
+ SubTitle = GetTruncatedText(GetSubTileForRegistryValue(key, valueEntry), MaxTextLength.MaximumSubTitleLengthWithThreeSymbols, TruncateSide.OnlyFromRight),
+ Title = GetTruncatedText(valueName, MaxTextLength.MaximumTitleLengthWithThreeSymbols),
+ ToolTipData = new ToolTipData(Resources.RegistryValue, GetToolTipTextForRegistryValue(key, valueEntry)),
+ Action = (_) => ContextMenuHelper.TryToOpenInRegistryEditor(registryEntry),
+
+ // Avoid user handling interrupt when move up/down inside the results of a registry key
+ QueryTextDisplay = $"{key.Name}{QueryHelper.QuerySplitCharacter}",
+ });
+ }
+ }
+ catch (Exception exception)
+ {
+ var registryEntry = new RegistryEntry(key.Name, exception);
+
+ resultList.Add(new Result
+ {
+ ContextData = registryEntry,
+ IcoPath = iconPath,
+ SubTitle = GetTruncatedText(exception.Message, MaxTextLength.MaximumSubTitleLengthWithThreeSymbols, TruncateSide.OnlyFromRight),
+ Title = GetTruncatedText(key.Name, MaxTextLength.MaximumTitleLengthWithThreeSymbols),
+ ToolTipData = new ToolTipData(exception.Message, exception.ToString()),
+ Action = (_) => ContextMenuHelper.TryToOpenInRegistryEditor(registryEntry),
+ QueryTextDisplay = key.Name,
+ });
+ }
+
+ return resultList;
+ }
+
+#pragma warning restore CA1031 // Do not catch general exception types
+
+ ///
+ /// Return a truncated name
+ ///
+ /// The text to truncate
+ /// The maximum length of the text
+ /// (optional) The side of the truncate
+ /// A truncated text with a maximum length
+ internal static string GetTruncatedText(string text, in int maxLength, TruncateSide truncateSide = TruncateSide.OnlyFromLeft)
+ {
+ if (truncateSide == TruncateSide.OnlyFromLeft)
+ {
+ if (text.Length > maxLength)
+ {
+ text = QueryHelper.GetKeyWithShortBaseKey(text);
+ }
+
+ return text.Length > maxLength ? $"...{text[^maxLength..]}" : text;
+ }
+ else
+ {
+ return text.Length > maxLength ? $"{text[0..maxLength]}..." : text;
+ }
+ }
+
+ ///
+ /// Return the tool-tip text for a registry value
+ ///
+ /// The registry key for the tool-tip
+ /// The value name and value of the registry value
+ /// A tool-tip text
+ private static string GetToolTipTextForRegistryValue(RegistryKey key, KeyValuePair valueEntry)
+ {
+ return $"{Resources.KeyName}\t{key.Name}{Environment.NewLine}"
+ + $"{Resources.Name}\t{valueEntry.Key}{Environment.NewLine}"
+ + $"{Resources.Type}\t{ValueHelper.GetType(key, valueEntry.Key)}{Environment.NewLine}"
+ + $"{Resources.Value}\t{ValueHelper.GetValue(key, valueEntry.Key)}";
+ }
+
+ ///
+ /// Return the sub-title text for a registry value
+ ///
+ /// The registry key for the sub-title
+ /// The value name and value of the registry value
+ /// A sub-title text
+ private static string GetSubTileForRegistryValue(RegistryKey key, KeyValuePair valueEntry)
+ {
+ return $"{Resources.Type} {ValueHelper.GetType(key, valueEntry.Key)}"
+ + $" - {Resources.Value} {ValueHelper.GetValue(key, valueEntry.Key, 50)}";
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Helper/ValueHelper.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Helper/ValueHelper.cs
new file mode 100644
index 0000000000..d3bf3a9790
--- /dev/null
+++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Helper/ValueHelper.cs
@@ -0,0 +1,63 @@
+// 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.Linq;
+using Microsoft.PowerToys.Run.Plugin.Registry.Properties;
+using Microsoft.Win32;
+
+namespace Microsoft.PowerToys.Run.Plugin.Registry.Helper
+{
+ ///
+ /// Helper class to easier work with values of a
+ ///
+ internal static class ValueHelper
+ {
+ ///
+ /// Return a human readable value data, of the given value name inside the given
+ ///
+ /// The that should contain the value name.
+ /// The name of the value.
+ /// The maximum length for the human readable value.
+ /// A human readable value data.
+ internal static string GetValue(in RegistryKey key, in string valueName, int maxLength = int.MaxValue)
+ {
+ var unformattedValue = key.GetValue(valueName);
+
+ var valueData = key.GetValueKind(valueName) switch
+ {
+ RegistryValueKind.DWord => $"0x{unformattedValue:X8} ({(uint)(int)unformattedValue})",
+ RegistryValueKind.QWord => $"0x{unformattedValue:X16} ({(ulong)(long)unformattedValue})",
+ RegistryValueKind.Binary => (unformattedValue as byte[]).Aggregate(string.Empty, (current, singleByte) => $"{current} {singleByte:X2}"),
+ _ => $"{unformattedValue}",
+ };
+
+ return valueData.Length > maxLength
+ ? $"{valueData.Substring(0, maxLength)}..."
+ : valueData;
+ }
+
+ ///
+ /// Return the registry type name of a given value name inside a given
+ ///
+ /// The that should contain the value name
+ /// The name of the value
+ /// A registry type name
+ internal static object GetType(RegistryKey key, string valueName)
+ {
+ return key.GetValueKind(valueName) switch
+ {
+ RegistryValueKind.None => Resources.RegistryValueKindNone,
+ RegistryValueKind.Unknown => Resources.RegistryValueKindUnknown,
+ RegistryValueKind.String => "REG_SZ",
+ RegistryValueKind.ExpandString => "REG_EXPAND_SZ",
+ RegistryValueKind.MultiString => "REG_MULTI_SZ",
+ RegistryValueKind.Binary => "REG_BINARY",
+ RegistryValueKind.DWord => "REG_DWORD",
+ RegistryValueKind.QWord => "REG_QWORD",
+ _ => throw new ArgumentOutOfRangeException(nameof(valueName)),
+ };
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Images/reg.dark.png b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Images/reg.dark.png
new file mode 100644
index 0000000000..562b3c1887
Binary files /dev/null and b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Images/reg.dark.png differ
diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Images/reg.light.png b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Images/reg.light.png
new file mode 100644
index 0000000000..1a59d7e23e
Binary files /dev/null and b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Images/reg.light.png differ
diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Main.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Main.cs
new file mode 100644
index 0000000000..7a1310d55d
--- /dev/null
+++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Main.cs
@@ -0,0 +1,173 @@
+// 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.Linq;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using ManagedCommon;
+using Microsoft.PowerToys.Run.Plugin.Registry.Classes;
+using Microsoft.PowerToys.Run.Plugin.Registry.Helper;
+using Microsoft.PowerToys.Run.Plugin.Registry.Properties;
+using Wox.Plugin;
+
+[assembly: InternalsVisibleTo("Microsoft.PowerToys.Run.Plugin.Registry.UnitTests")]
+
+namespace Microsoft.PowerToys.Run.Plugin.Registry
+{
+ ///
+ /// Main class of this plugin that implement all used interfaces
+ ///
+ public class Main : IPlugin, IContextMenu, IPluginI18n, IDisposable
+ {
+ ///
+ /// The name of this assembly
+ ///
+ private readonly string _assemblyName;
+
+ ///
+ /// The initial context for this plugin (contains API and meta-data)
+ ///
+ private PluginInitContext? _context;
+
+ ///
+ /// The path to the icon for each result
+ ///
+ private string _defaultIconPath;
+
+ ///
+ /// Indicate that the plugin is disposed
+ ///
+ private bool _disposed;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public Main()
+ {
+ _assemblyName = Assembly.GetExecutingAssembly().GetName().Name ?? GetTranslatedPluginTitle();
+ _defaultIconPath = "Images/reg.light.png";
+ }
+
+ ///
+ /// Initialize the plugin with the given
+ ///
+ /// The for this plugin
+ public void Init(PluginInitContext context)
+ {
+ _context = context ?? throw new ArgumentNullException(nameof(context));
+ _context.API.ThemeChanged += OnThemeChanged;
+ UpdateIconPath(_context.API.GetCurrentTheme());
+ }
+
+ ///
+ /// Return a filtered list, based on the given query
+ ///
+ /// The query to filter the list
+ /// A filtered list, can be empty when nothing was found
+ public List Query(Query query)
+ {
+ if (query?.Search is null)
+ {
+ return new List(0);
+ }
+
+ var searchForValueName = QueryHelper.GetQueryParts(query.Search, out var queryKey, out var queryValueName);
+
+ var (baseKeyList, subKey) = RegistryHelper.GetRegistryBaseKey(queryKey);
+ if (baseKeyList is null)
+ {
+ // no base key found
+ return ResultHelper.GetResultList(RegistryHelper.GetAllBaseKeys(), _defaultIconPath);
+ }
+ else if (baseKeyList.Count() > 1)
+ {
+ // more than one base key was found -> show results
+ return ResultHelper.GetResultList(baseKeyList.Select(found => new RegistryEntry(found)), _defaultIconPath);
+ }
+
+ // only one base key was found -> start search for the sub-key
+ var list = RegistryHelper.SearchForSubKey(baseKeyList.First(), subKey);
+
+ // when only one sub-key was found and a user search for values ("\\")
+ // show the filtered list of values of one sub-key
+ if (searchForValueName && list.Count == 1)
+ {
+ return ResultHelper.GetValuesFromKey(list.First().Key, _defaultIconPath, queryValueName);
+ }
+
+ return ResultHelper.GetResultList(list, _defaultIconPath);
+ }
+
+ ///
+ /// Return a list context menu entries for a given (shown at the right side of the result)
+ ///
+ /// The for the list with context menu entries
+ /// A list context menu entries
+ public List LoadContextMenus(Result selectedResult)
+ {
+ return ContextMenuHelper.GetContextMenu(selectedResult, _assemblyName);
+ }
+
+ ///
+ /// Change all theme-based elements (typical called when the plugin theme has changed)
+ ///
+ /// The old
+ /// The new
+ private void OnThemeChanged(Theme oldtheme, Theme newTheme)
+ {
+ UpdateIconPath(newTheme);
+ }
+
+ ///
+ /// Update all icons (typical called when the plugin theme has changed)
+ ///
+ /// The new for the icons
+ private void UpdateIconPath(Theme theme)
+ {
+ _defaultIconPath = theme == Theme.Light || theme == Theme.HighContrastWhite
+ ? "Images/reg.light.png"
+ : "Images/reg.dark.png";
+ }
+
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ ///
+ /// Wrapper method for that dispose additional objects and events form the plugin itself
+ ///
+ /// Indicate that the plugin is disposed
+ protected virtual void Dispose(bool disposing)
+ {
+ if (_disposed || !disposing)
+ {
+ return;
+ }
+
+ if (!(_context is null))
+ {
+ _context.API.ThemeChanged -= OnThemeChanged;
+ }
+
+ _disposed = true;
+ }
+
+ ///
+ public string GetTranslatedPluginTitle()
+ {
+ return Resources.PluginTitle;
+ }
+
+ ///
+ public string GetTranslatedPluginDescription()
+ {
+ return Resources.PluginDescription;
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Microsoft.PowerToys.Run.Plugin.Registry.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Microsoft.PowerToys.Run.Plugin.Registry.csproj
new file mode 100644
index 0000000000..cddfe836c6
--- /dev/null
+++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Microsoft.PowerToys.Run.Plugin.Registry.csproj
@@ -0,0 +1,105 @@
+
+
+
+ netcoreapp3.1
+ Microsoft.PowerToys.Run.Plugin.Registry
+ Microsoft.PowerToys.Run.Plugin.Registry
+ false
+ false
+ x64
+ en-US
+ 8.0
+ enable
+
+
+
+ true
+ ..\..\..\..\..\x64\Debug\modules\launcher\Plugins\Microsoft.PowerToys.Run.Plugin.Registry\
+ DEBUG;TRACE
+ full
+ x64
+ prompt
+ MinimumRecommendedRules.ruleset
+ 4
+ false
+ true
+
+
+
+ ..\..\..\..\..\x64\Release\modules\launcher\Plugins\Microsoft.PowerToys.Run.Plugin.Registry\
+ TRACE
+ true
+ pdbonly
+ x64
+ prompt
+ MinimumRecommendedRules.ruleset
+ 4
+ true
+
+
+
+
+
+
+
+
+
+
+ GlobalSuppressions.cs
+
+
+ StyleCop.json
+
+
+
+
+
+ false
+
+
+ false
+
+
+
+
+
+ PreserveNewest
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+
+
diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Properties/Resources.Designer.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Properties/Resources.Designer.cs
new file mode 100644
index 0000000000..60f6e5aa33
--- /dev/null
+++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Properties/Resources.Designer.cs
@@ -0,0 +1,225 @@
+//------------------------------------------------------------------------------
+//
+// 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.
+//
+//------------------------------------------------------------------------------
+
+namespace Microsoft.PowerToys.Run.Plugin.Registry.Properties {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // 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", "16.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() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [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.PowerToys.Run.Plugin.Registry.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Copy key name (path).
+ ///
+ internal static string CopyKeyNamePath {
+ get {
+ return ResourceManager.GetString("CopyKeyNamePath", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Copy value data.
+ ///
+ internal static string CopyValueData {
+ get {
+ return ResourceManager.GetString("CopyValueData", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Copy value name.
+ ///
+ internal static string CopyValueName {
+ get {
+ return ResourceManager.GetString("CopyValueName", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Key name:.
+ ///
+ internal static string KeyName {
+ get {
+ return ResourceManager.GetString("KeyName", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Name:.
+ ///
+ internal static string Name {
+ get {
+ return ResourceManager.GetString("Name", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to You do not have enough rights to open the Windows registry editor.
+ ///
+ internal static string OpenInRegistryEditorAccessExceptionText {
+ get {
+ return ResourceManager.GetString("OpenInRegistryEditorAccessExceptionText", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Error on open registry editor.
+ ///
+ internal static string OpenInRegistryEditorAccessExceptionTitle {
+ get {
+ return ResourceManager.GetString("OpenInRegistryEditorAccessExceptionTitle", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Open key in registry editor.
+ ///
+ internal static string OpenKeyInRegistryEditor {
+ get {
+ return ResourceManager.GetString("OpenKeyInRegistryEditor", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Search inside the Windows Registry.
+ ///
+ internal static string PluginDescription {
+ get {
+ return ResourceManager.GetString("PluginDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Registry Plugin.
+ ///
+ internal static string PluginTitle {
+ get {
+ return ResourceManager.GetString("PluginTitle", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Registry key.
+ ///
+ internal static string RegistryKey {
+ get {
+ return ResourceManager.GetString("RegistryKey", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Registry value.
+ ///
+ internal static string RegistryValue {
+ get {
+ return ResourceManager.GetString("RegistryValue", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to No data type.
+ ///
+ internal static string RegistryValueKindNone {
+ get {
+ return ResourceManager.GetString("RegistryValueKindNone", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Unsupported data type.
+ ///
+ internal static string RegistryValueKindUnknown {
+ get {
+ return ResourceManager.GetString("RegistryValueKindUnknown", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Subkeys:.
+ ///
+ internal static string SubKeys {
+ get {
+ return ResourceManager.GetString("SubKeys", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Type:.
+ ///
+ internal static string Type {
+ get {
+ return ResourceManager.GetString("Type", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Value:.
+ ///
+ internal static string Value {
+ get {
+ return ResourceManager.GetString("Value", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Values:.
+ ///
+ internal static string Values {
+ get {
+ return ResourceManager.GetString("Values", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Properties/Resources.resx b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Properties/Resources.resx
new file mode 100644
index 0000000000..7a97ac5ec7
--- /dev/null
+++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Properties/Resources.resx
@@ -0,0 +1,182 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Registry Plugin
+
+
+ Search inside the Windows Registry
+ "this built into Windows the OS. translate accordingly, https://docs.microsoft.com/de-de/troubleshoot/windows-server/performance/windows-registry-advanced-users is an example of it translated in German"
+
+
+ Copy key name (path)
+ 'The maximum size of a key name is 255 characters.'
+
+
+ Key name:
+ 'The maximum size of a key name is 255 characters.'
+
+
+ Name:
+
+
+ Copy value name
+
+
+ Open key in registry editor
+ "registry editor" is the name of the OS built-in application
+
+
+ You do not have enough rights to open the Windows registry editor
+ "registry editor" is the name of the OS built-in application
+
+
+ Error on open registry editor
+ "registry editor" is the name of the OS built-in application
+
+
+ Subkeys:
+
+
+ Values:
+
+
+ Registry key
+
+
+ Registry value
+
+
+ Type:
+ See https://docs.microsoft.com/en-us/windows/win32/sysinfo/registry-value-types for proper context of how to translate 'type'
+
+
+ Value:
+ See https://docs.microsoft.com/en-us/windows/win32/sysinfo/registry-value-types for proper context of how to translate 'value'
+
+
+ No data type
+
+
+ Unsupported data type
+
+
+ Copy value data
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/plugin.json b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/plugin.json
new file mode 100644
index 0000000000..0ad383e35d
--- /dev/null
+++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/plugin.json
@@ -0,0 +1,12 @@
+{
+ "ID": "303417D927BF4C97BCFFC78A123BE0C8",
+ "ActionKeyword": ":",
+ "Name": "Registry",
+ "Description": "Search inside the Windows Registry",
+ "Author": "TobiasSekan",
+ "Version": "1.0.0",
+ "Language": "csharp",
+ "Website": "https://aka.ms/powertoys",
+ "ExecuteFileName": "Microsoft.PowerToys.Run.Plugin.Registry.dll",
+ "IcoPath": "Images\\reg.dark.png"
+}