[PT Run] Registry plugin (#7951)

This commit is contained in:
Tobias Sekan 2021-01-20 19:15:45 +01:00 committed by GitHub
parent da33d22c85
commit a434d6047f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 2053 additions and 3 deletions

View file

@ -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

View file

@ -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.*$

View file

@ -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'

View file

@ -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}

View file

@ -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`

View file

@ -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)
- [Window Walker](/doc/devdocs/modules/launcher/plugins/windowwalker.md)

View file

@ -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);
}
}
}

View file

@ -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));
}
}
}

View file

@ -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);
}
}
}

View file

@ -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));
}
}
}

View file

@ -0,0 +1,65 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<Platforms>x64</Platforms>
<NeutralLanguage>en-US</NeutralLanguage>
<LangVersion>8.0</LangVersion>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<WarningLevel>4</WarningLevel>
<Optimize>false</Optimize>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<WarningLevel>4</WarningLevel>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
<PackageReference Include="MSTest.TestAdapter" Version="2.1.1" />
<PackageReference Include="MSTest.TestFramework" Version="2.1.1" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\..\..\codeAnalysis\GlobalSuppressions.cs">
<Link>GlobalSuppressions.cs</Link>
</Compile>
<AdditionalFiles Include="..\..\..\..\codeAnalysis\StyleCop.json">
<Link>StyleCop.json</Link>
</AdditionalFiles>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="3.3.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Microsoft.PowerToys.Run.Plugin.Registry\Microsoft.PowerToys.Run.Plugin.Registry.csproj" />
</ItemGroup>
</Project>

View file

@ -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
{
/// <summary>
/// A entry of the registry.
/// </summary>
internal class RegistryEntry
{
/// <summary>
/// Gets the full path to a registry key.
/// </summary>
internal string KeyPath { get; }
/// <summary>
/// Gets the registry key for this entry.
/// </summary>
internal RegistryKey? Key { get; }
/// <summary>
/// Gets a possible exception that was occurred when try to open this registry key (e.g. <see cref="UnauthorizedAccessException"/>).
/// </summary>
internal Exception? Exception { get; }
/// <summary>
/// Gets the name of the current selected registry value.
/// </summary>
internal string? ValueName { get; }
/// <summary>
/// Gets the value of the current selected registry value.
/// </summary>
internal object? ValueData { get; }
/// <summary>
/// Initializes a new instance of the <see cref="RegistryEntry"/> class.
/// </summary>
/// <param name="keyPath">The full path to the registry key for this entry.</param>
/// <param name="exception">A exception that was occurred when try to access this registry key.</param>
internal RegistryEntry(string keyPath, Exception exception)
{
KeyPath = keyPath;
Exception = exception;
}
/// <summary>
/// Initializes a new instance of the <see cref="RegistryEntry"/> class.
/// </summary>
/// <param name="key">The <see cref="RegistryKey"/> for this entry.</param>
internal RegistryEntry(RegistryKey key)
{
KeyPath = key.Name;
Key = key;
}
/// <summary>
/// Initializes a new instance of the <see cref="RegistryEntry"/> class.
/// </summary>
/// <param name="key">The <see cref="RegistryKey"/> for this entry.</param>
/// <param name="valueName">The value name of the current selected registry value.</param>
/// <param name="value">The value of the current selected registry value.</param>
internal RegistryEntry(RegistryKey key, string valueName, object value)
{
KeyPath = key.Name;
Key = key;
ValueName = valueName;
ValueData = value;
}
/// <summary>
/// Return the registry key.
/// </summary>
/// <returns>A registry key.</returns>
internal string GetRegistryKey()
{
return $"{Key?.Name ?? KeyPath}";
}
/// <summary>
/// Return the value name with the complete registry key.
/// </summary>
/// <returns>A value name with the complete registry key.</returns>
internal string GetValueNameWithKey()
{
return $"{Key?.Name ?? KeyPath}\\\\{ValueName?.ToString() ?? string.Empty}";
}
/// <summary>
/// Return the value data of a value name inside a registry key.
/// </summary>
/// <returns>A value data.</returns>
internal string GetValueData()
{
if (Key is null)
{
return KeyPath;
}
if (string.IsNullOrEmpty(ValueName))
{
return Key.Name;
}
return ValueHelper.GetValue(Key, ValueName);
}
}
}

View file

@ -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
{
/// <summary>
/// This class contains names for important registry keys
/// </summary>
internal static class KeyName
{
/// <summary>
/// The first name part of each base key without the underscore
/// </summary>
internal const string FirstPart = "HKEY";
/// <summary>
/// The first name part of each base key follow by a underscore
/// </summary>
internal const string FirstPartUnderscore = "HKEY_";
/// <summary>
/// The short name for the base key HKEY_CLASSES_ROOT (see <see cref="Win32.Registry.ClassesRoot"/>)
/// </summary>
internal const string ClassRootShort = "HKCR";
/// <summary>
/// The short name for the base key HKEY_CURRENT_CONFIG (see <see cref="Win32.Registry.CurrentConfig"/>)
/// </summary>
internal const string CurrentConfigShort = "HKCC";
/// <summary>
/// The short name for the base key HKEY_CURRENT_USER (see <see cref="Win32.Registry.CurrentUser"/>)
/// </summary>
internal const string CurrentUserShort = "HKCU";
/// <summary>
/// The short name for the base key HKEY_LOCAL_MACHINE (see <see cref="Win32.Registry.LocalMachine"/>)
/// </summary>
internal const string LocalMachineShort = "HKLM";
/// <summary>
/// The short name for the base key HKEY_PERFORMANCE_DATA (see <see cref="Win32.Registry.PerformanceData"/>)
/// </summary>
internal const string PerformanceDataShort = "HKPD";
/// <summary>
/// The short name for the base key HKEY_USERS (see <see cref="Win32.Registry.Users"/>)
/// </summary>
internal const string UsersShort = "HKU";
}
}

View file

@ -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
{
/// <summary>
/// This class contain all maximum text length.
/// </summary>
public static class MaxTextLength
{
/// <summary>
/// The maximum length for the title text length with two context menu symbols on the right.
/// </summary>
internal const int MaximumTitleLengthWithTwoSymbols = 44;
/// <summary>
/// The maximum length for the title text length with three context menu symbols on the right.
/// </summary>
internal const int MaximumTitleLengthWithThreeSymbols = 40;
/// <summary>
/// The maximum length for the sub-title text length with two context menu symbols on the right.
/// </summary>
internal const int MaximumSubTitleLengthWithTwoSymbols = 85;
/// <summary>
/// The maximum length for the sub-title text length with three context menu symbols on the right.
/// </summary>
internal const int MaximumSubTitleLengthWithThreeSymbols = 78;
}
}

View file

@ -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
{
/// <summary>
/// The truncate side for a to long text
/// </summary>
internal enum TruncateSide
{
/// <summary>
/// Truncate a text only from the right side
/// </summary>
OnlyFromLeft,
/// <summary>
/// Truncate a text only from the left side
/// </summary>
OnlyFromRight,
}
}

View file

@ -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
{
/// <summary>
/// Helper class to easier work with context menu entries
/// </summary>
internal static class ContextMenuHelper
{
/// <summary>
/// Return a list with all context menu entries for the given <see cref="Result"/>
/// <para>Symbols taken from <see href="https://docs.microsoft.com/en-us/windows/uwp/design/style/segoe-ui-symbol-font"/></para>
/// </summary>
/// <param name="result">The result for the context menu entires</param>
/// <param name="assemblyName">The name of the this assembly</param>
/// <returns>A list with context menu entries</returns>
internal static List<ContextMenuResult> GetContextMenu(Result result, string assemblyName)
{
if (!(result?.ContextData is RegistryEntry entry))
{
return new List<ContextMenuResult>(0);
}
var list = new List<ContextMenuResult>();
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
/// <summary>
/// Open the Windows registry editor and jump to registry key inside the given key (inside the <see cref="RegistryEntry"/>
/// </summary>
/// <param name="entry">The <see cref="RegistryEntry"/> to jump in</param>
/// <returns><see langword="true"/> if the registry editor was successful open, otherwise <see langword="false"/></returns>
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;
}
}
/// <summary>
/// Copy the given text to the clipboard
/// </summary>
/// <param name="text">The text to copy to the clipboard</param>
/// <returns><see langword="true"/>The text successful copy to the clipboard, otherwise <see langword="false"/></returns>
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
}
}

View file

@ -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
{
/// <summary>
/// Helper class to easier work with queries
/// </summary>
internal static class QueryHelper
{
/// <summary>
/// The character to distinguish if the search query contain multiple parts (typically "\\")
/// </summary>
internal const string QuerySplitCharacter = "\\\\";
/// <summary>
/// A list that contain short names of all registry base keys
/// </summary>
private static readonly IReadOnlyDictionary<string, string> _shortBaseKeys = new Dictionary<string, string>(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 },
};
/// <summary>
/// Return the parts of a given query
/// </summary>
/// <param name="query">The query that could contain parts</param>
/// <param name="queryKey">The key part of the query</param>
/// <param name="queryValueName">The value name part of the query</param>
/// <returns><see langword="true"/> when the query search for a key and a value name, otherwise <see langword="false"/></returns>
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;
}
/// <summary>
/// Return a registry key with a long base key
/// </summary>
/// <param name="registryKey">A registry key with a short base key</param>
/// <returns>A registry key with a long base key</returns>
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;
}
/// <summary>
/// Return a registry key with a short base key (useful to reduce the text length of a registry key)
/// </summary>
/// <param name="registryKey">A registry key with a full base key</param>
/// <returns>A registry key with a short base key</returns>
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;
}
}
}

View file

@ -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
/// <summary>
/// Helper class to easier work with the registry
/// </summary>
internal static class RegistryHelper
{
/// <summary>
/// A list that contain all registry base keys in a long/full version and in a short version (e.g HKLM = HKEY_LOCAL_MACHINE)
/// </summary>
private static readonly IReadOnlyDictionary<string, RegistryKey> _baseKeys = new Dictionary<string, RegistryKey>(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 },
};
/// <summary>
/// Try to find registry base keys based on the given query
/// </summary>
/// <param name="query">The query to search</param>
/// <returns>A combination of a list of base <see cref="RegistryKey"/> and the sub keys</returns>
internal static (IEnumerable<RegistryKey>? 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);
}
/// <summary>
/// Return a list of all registry base key
/// </summary>
/// <returns>A list with all registry base keys</returns>
internal static ICollection<RegistryEntry> GetAllBaseKeys()
{
return new Collection<RegistryEntry>
{
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),
};
}
/// <summary>
/// Search for the given sub-key path in the given registry base key
/// </summary>
/// <param name="baseKey">The base <see cref="RegistryKey"/></param>
/// <param name="subKeyPath">The path of the registry sub-key</param>
/// <returns>A list with all found registry keys</returns>
internal static ICollection<RegistryEntry> 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<RegistryEntry> 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;
}
/// <summary>
/// Return a human readable summary of a given <see cref="RegistryKey"/>
/// </summary>
/// <param name="key">The <see cref="RegistryKey"/> for the summary</param>
/// <returns>A human readable summary</returns>
internal static string GetSummary(in RegistryKey key)
{
return $"{Resources.SubKeys} {key.SubKeyCount} - {Resources.Values} {key.ValueCount}";
}
/// <summary>
/// Open a given registry key in the registry editor
/// </summary>
/// <param name="fullKey">The registry key to open</param>
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);
}
/// <summary>
/// Try to find the given registry sub-key in the given registry parent-key
/// </summary>
/// <param name="parentKey">The parent-key, also the root to start the search</param>
/// <param name="searchSubKey">The sub-key to find</param>
/// <returns>A list with all found registry sub-keys</returns>
private static ICollection<RegistryEntry> FindSubKey(in RegistryKey parentKey, in string searchSubKey)
{
var list = new Collection<RegistryEntry>();
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;
}
/// <summary>
/// Return a list with a registry sub-keys of the given registry parent-key
/// </summary>
/// <param name="parentKey">The registry parent-key</param>
/// <param name="maxCount">(optional) The maximum count of the results</param>
/// <returns>A list with all found registry sub-keys</returns>
private static ICollection<RegistryEntry> GetAllSubKeys(in RegistryKey parentKey, in int maxCount = 50)
{
var list = new Collection<RegistryEntry>();
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
}

View file

@ -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
{
/// <summary>
/// Helper class to easier work with results
/// </summary>
internal static class ResultHelper
{
#pragma warning disable CA1031 // Do not catch general exception types
/// <summary>
/// Return a list with <see cref="Result"/>s, based on the given list
/// </summary>
/// <param name="list">The original result list to convert</param>
/// <param name="iconPath">The path to the icon of each entry</param>
/// <returns>A list with <see cref="Result"/></returns>
internal static List<Result> GetResultList(in IEnumerable<RegistryEntry> list, in string iconPath)
{
var resultList = new List<Result>();
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;
}
/// <summary>
/// Return a list with <see cref="Result"/>s, based on the given <see cref="RegistryKey"/>
/// </summary>
/// <param name="key">The <see cref="RegistryKey"/> that should contain entries for the list</param>
/// <param name="iconPath">The path to the icon of each entry</param>
/// <param name="searchValue">(optional) When not <see cref="string.Empty"/> filter the list for the given value name and value</param>
/// <returns>A list with <see cref="Result"/></returns>
internal static List<Result> GetValuesFromKey(in RegistryKey? key, in string iconPath, string searchValue = "")
{
if (key is null)
{
return new List<Result>(0);
}
ICollection<KeyValuePair<string, object>> valueList = new List<KeyValuePair<string, object>>(key.ValueCount);
var resultList = new List<Result>();
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
/// <summary>
/// Return a truncated name
/// </summary>
/// <param name="text">The text to truncate</param>
/// <param name="maxLength">The maximum length of the text</param>
/// <param name="truncateSide">(optional) The side of the truncate</param>
/// <returns>A truncated text with a maximum length</returns>
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;
}
}
/// <summary>
/// Return the tool-tip text for a registry value
/// </summary>
/// <param name="key">The registry key for the tool-tip</param>
/// <param name="valueEntry">The value name and value of the registry value</param>
/// <returns>A tool-tip text</returns>
private static string GetToolTipTextForRegistryValue(RegistryKey key, KeyValuePair<string, object> 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)}";
}
/// <summary>
/// Return the sub-title text for a registry value
/// </summary>
/// <param name="key">The registry key for the sub-title</param>
/// <param name="valueEntry">The value name and value of the registry value</param>
/// <returns>A sub-title text</returns>
private static string GetSubTileForRegistryValue(RegistryKey key, KeyValuePair<string, object> valueEntry)
{
return $"{Resources.Type} {ValueHelper.GetType(key, valueEntry.Key)}"
+ $" - {Resources.Value} {ValueHelper.GetValue(key, valueEntry.Key, 50)}";
}
}
}

View file

@ -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
{
/// <summary>
/// Helper class to easier work with values of a <see cref="RegistryKey"/>
/// </summary>
internal static class ValueHelper
{
/// <summary>
/// Return a human readable value data, of the given value name inside the given <see cref="RegistryKey"/>
/// </summary>
/// <param name="key">The <see cref="RegistryKey"/> that should contain the value name.</param>
/// <param name="valueName">The name of the value.</param>
/// <param name="maxLength">The maximum length for the human readable value.</param>
/// <returns>A human readable value data.</returns>
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;
}
/// <summary>
/// Return the registry type name of a given value name inside a given <see cref="RegistryKey"/>
/// </summary>
/// <param name="key">The <see cref="RegistryKey"/> that should contain the value name</param>
/// <param name="valueName">The name of the value</param>
/// <returns>A registry type name</returns>
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)),
};
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View file

@ -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
{
/// <summary>
/// Main class of this plugin that implement all used interfaces
/// </summary>
public class Main : IPlugin, IContextMenu, IPluginI18n, IDisposable
{
/// <summary>
/// The name of this assembly
/// </summary>
private readonly string _assemblyName;
/// <summary>
/// The initial context for this plugin (contains API and meta-data)
/// </summary>
private PluginInitContext? _context;
/// <summary>
/// The path to the icon for each result
/// </summary>
private string _defaultIconPath;
/// <summary>
/// Indicate that the plugin is disposed
/// </summary>
private bool _disposed;
/// <summary>
/// Initializes a new instance of the <see cref="Main"/> class.
/// </summary>
public Main()
{
_assemblyName = Assembly.GetExecutingAssembly().GetName().Name ?? GetTranslatedPluginTitle();
_defaultIconPath = "Images/reg.light.png";
}
/// <summary>
/// Initialize the plugin with the given <see cref="PluginInitContext"/>
/// </summary>
/// <param name="context">The <see cref="PluginInitContext"/> for this plugin</param>
public void Init(PluginInitContext context)
{
_context = context ?? throw new ArgumentNullException(nameof(context));
_context.API.ThemeChanged += OnThemeChanged;
UpdateIconPath(_context.API.GetCurrentTheme());
}
/// <summary>
/// Return a filtered list, based on the given query
/// </summary>
/// <param name="query">The query to filter the list</param>
/// <returns>A filtered list, can be empty when nothing was found</returns>
public List<Result> Query(Query query)
{
if (query?.Search is null)
{
return new List<Result>(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);
}
/// <summary>
/// Return a list context menu entries for a given <see cref="Result"/> (shown at the right side of the result)
/// </summary>
/// <param name="selectedResult">The <see cref="Result"/> for the list with context menu entries</param>
/// <returns>A list context menu entries</returns>
public List<ContextMenuResult> LoadContextMenus(Result selectedResult)
{
return ContextMenuHelper.GetContextMenu(selectedResult, _assemblyName);
}
/// <summary>
/// Change all theme-based elements (typical called when the plugin theme has changed)
/// </summary>
/// <param name="oldtheme">The old <see cref="Theme"/></param>
/// <param name="newTheme">The new <see cref="Theme"/></param>
private void OnThemeChanged(Theme oldtheme, Theme newTheme)
{
UpdateIconPath(newTheme);
}
/// <summary>
/// Update all icons (typical called when the plugin theme has changed)
/// </summary>
/// <param name="theme">The new <see cref="Theme"/> for the icons</param>
private void UpdateIconPath(Theme theme)
{
_defaultIconPath = theme == Theme.Light || theme == Theme.HighContrastWhite
? "Images/reg.light.png"
: "Images/reg.dark.png";
}
/// <inheritdoc/>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Wrapper method for <see cref="Dispose"/> that dispose additional objects and events form the plugin itself
/// </summary>
/// <param name="disposing">Indicate that the plugin is disposed</param>
protected virtual void Dispose(bool disposing)
{
if (_disposed || !disposing)
{
return;
}
if (!(_context is null))
{
_context.API.ThemeChanged -= OnThemeChanged;
}
_disposed = true;
}
/// <inheritdoc/>
public string GetTranslatedPluginTitle()
{
return Resources.PluginTitle;
}
/// <inheritdoc/>
public string GetTranslatedPluginDescription()
{
return Resources.PluginDescription;
}
}
}

View file

@ -0,0 +1,105 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace>Microsoft.PowerToys.Run.Plugin.Registry</RootNamespace>
<AssemblyName>Microsoft.PowerToys.Run.Plugin.Registry</AssemblyName>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<Platforms>x64</Platforms>
<NeutralLanguage>en-US</NeutralLanguage>
<LangVersion>8.0</LangVersion>
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>..\..\..\..\..\x64\Debug\modules\launcher\Plugins\Microsoft.PowerToys.Run.Plugin.Registry\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<WarningLevel>4</WarningLevel>
<Optimize>false</Optimize>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<OutputPath>..\..\..\..\..\x64\Release\modules\launcher\Plugins\Microsoft.PowerToys.Run.Plugin.Registry\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<WarningLevel>4</WarningLevel>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Languages\**" />
<EmbeddedResource Remove="Languages\**" />
<None Remove="Languages\**" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\..\..\codeAnalysis\GlobalSuppressions.cs">
<Link>GlobalSuppressions.cs</Link>
</Compile>
<AdditionalFiles Include="..\..\..\..\codeAnalysis\StyleCop.json">
<Link>StyleCop.json</Link>
</AdditionalFiles>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Wox.Infrastructure\Wox.Infrastructure.csproj">
<Private>false</Private>
</ProjectReference>
<ProjectReference Include="..\..\Wox.Plugin\Wox.Plugin.csproj">
<Private>false</Private>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="plugin.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="3.3.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<None Update="Images\reg.dark.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Images\reg.light.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View file

@ -0,0 +1,225 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Microsoft.PowerToys.Run.Plugin.Registry.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "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() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.PowerToys.Run.Plugin.Registry.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to Copy key name (path).
/// </summary>
internal static string CopyKeyNamePath {
get {
return ResourceManager.GetString("CopyKeyNamePath", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Copy value data.
/// </summary>
internal static string CopyValueData {
get {
return ResourceManager.GetString("CopyValueData", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Copy value name.
/// </summary>
internal static string CopyValueName {
get {
return ResourceManager.GetString("CopyValueName", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Key name:.
/// </summary>
internal static string KeyName {
get {
return ResourceManager.GetString("KeyName", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Name:.
/// </summary>
internal static string Name {
get {
return ResourceManager.GetString("Name", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You do not have enough rights to open the Windows registry editor.
/// </summary>
internal static string OpenInRegistryEditorAccessExceptionText {
get {
return ResourceManager.GetString("OpenInRegistryEditorAccessExceptionText", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Error on open registry editor.
/// </summary>
internal static string OpenInRegistryEditorAccessExceptionTitle {
get {
return ResourceManager.GetString("OpenInRegistryEditorAccessExceptionTitle", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Open key in registry editor.
/// </summary>
internal static string OpenKeyInRegistryEditor {
get {
return ResourceManager.GetString("OpenKeyInRegistryEditor", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Search inside the Windows Registry.
/// </summary>
internal static string PluginDescription {
get {
return ResourceManager.GetString("PluginDescription", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Registry Plugin.
/// </summary>
internal static string PluginTitle {
get {
return ResourceManager.GetString("PluginTitle", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Registry key.
/// </summary>
internal static string RegistryKey {
get {
return ResourceManager.GetString("RegistryKey", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Registry value.
/// </summary>
internal static string RegistryValue {
get {
return ResourceManager.GetString("RegistryValue", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to No data type.
/// </summary>
internal static string RegistryValueKindNone {
get {
return ResourceManager.GetString("RegistryValueKindNone", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Unsupported data type.
/// </summary>
internal static string RegistryValueKindUnknown {
get {
return ResourceManager.GetString("RegistryValueKindUnknown", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Subkeys:.
/// </summary>
internal static string SubKeys {
get {
return ResourceManager.GetString("SubKeys", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Type:.
/// </summary>
internal static string Type {
get {
return ResourceManager.GetString("Type", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Value:.
/// </summary>
internal static string Value {
get {
return ResourceManager.GetString("Value", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Values:.
/// </summary>
internal static string Values {
get {
return ResourceManager.GetString("Values", resourceCulture);
}
}
}
}

View file

@ -0,0 +1,182 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="PluginTitle" xml:space="preserve">
<value>Registry Plugin</value>
</data>
<data name="PluginDescription" xml:space="preserve">
<value>Search inside the Windows Registry</value>
<comment>"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"</comment>
</data>
<data name="CopyKeyNamePath" xml:space="preserve">
<value>Copy key name (path)</value>
<comment>'The maximum size of a key name is 255 characters.'</comment>
</data>
<data name="KeyName" xml:space="preserve">
<value>Key name:</value>
<comment>'The maximum size of a key name is 255 characters.'</comment>
</data>
<data name="Name" xml:space="preserve">
<value>Name:</value>
</data>
<data name="CopyValueName" xml:space="preserve">
<value>Copy value name</value>
</data>
<data name="OpenKeyInRegistryEditor" xml:space="preserve">
<value>Open key in registry editor</value>
<comment>"registry editor" is the name of the OS built-in application</comment>
</data>
<data name="OpenInRegistryEditorAccessExceptionText" xml:space="preserve">
<value>You do not have enough rights to open the Windows registry editor</value>
<comment>"registry editor" is the name of the OS built-in application </comment>
</data>
<data name="OpenInRegistryEditorAccessExceptionTitle" xml:space="preserve">
<value>Error on open registry editor</value>
<comment>"registry editor" is the name of the OS built-in application</comment>
</data>
<data name="SubKeys" xml:space="preserve">
<value>Subkeys:</value>
</data>
<data name="Values" xml:space="preserve">
<value>Values:</value>
</data>
<data name="RegistryKey" xml:space="preserve">
<value>Registry key</value>
</data>
<data name="RegistryValue" xml:space="preserve">
<value>Registry value</value>
</data>
<data name="Type" xml:space="preserve">
<value>Type:</value>
<comment>See https://docs.microsoft.com/en-us/windows/win32/sysinfo/registry-value-types for proper context of how to translate 'type'</comment>
</data>
<data name="Value" xml:space="preserve">
<value>Value:</value>
<comment>See https://docs.microsoft.com/en-us/windows/win32/sysinfo/registry-value-types for proper context of how to translate 'value'</comment>
</data>
<data name="RegistryValueKindNone" xml:space="preserve">
<value>No data type</value>
</data>
<data name="RegistryValueKindUnknown" xml:space="preserve">
<value>Unsupported data type</value>
</data>
<data name="CopyValueData" xml:space="preserve">
<value>Copy value data</value>
</data>
</root>

View file

@ -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"
}