diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt
index e45a8fb517..9c97c8dba7 100644
--- a/.github/actions/spell-check/expect.txt
+++ b/.github/actions/spell-check/expect.txt
@@ -423,6 +423,7 @@ DARKPURPLE
DARKTEAL
DARKYELLOW
datareader
+datatracker
Datavalue
dataversion
DATAW
@@ -519,7 +520,7 @@ dllexport
dllhost
dllmain
DNLEN
-Dns
+dns
DONOTROUND
DONTVALIDATEPATH
dotnet
@@ -790,7 +791,10 @@ graphql
GSM
gtm
gui
+guiddata
guiddef
+guidgenerator
+GUIDv
GUITHREADINFO
GValue
gwl
@@ -862,6 +866,7 @@ HOLDESC
HOMEPATH
homljgmgpmcbpjbnjpfijnhipfkiclkd
HOOKPROC
+Horvalds
Hostbackdropbrush
hotkeycontrol
hotkeys
@@ -916,6 +921,7 @@ IDR
idx
IDXGI
IEnum
+ietf
IExec
IEXPLORE
Iface
@@ -956,6 +962,7 @@ INPUTHARDWARE
INPUTKEYBOARD
INPUTLANGCHANGED
INPUTMOUSE
+inputparser
INPUTSINK
INPUTTYPE
INSTALLDESKTOPSHORTCUT
@@ -1428,6 +1435,7 @@ odbccp
Oem
officehubintl
ofs
+oid
oldcolor
olditem
oldnewthing
@@ -1760,6 +1768,7 @@ roslyn
roundf
ROUNDSMALL
Rpc
+rpcrt
RRF
rrr
RSAT
@@ -2172,6 +2181,7 @@ uxtheme
UYVY
vabdq
validmodulename
+valuegenerator
Vanara
variantassignment
vcamp
diff --git a/.pipelines/ESRPSigning_core.json b/.pipelines/ESRPSigning_core.json
index 7d793997df..7bcafc06bc 100644
--- a/.pipelines/ESRPSigning_core.json
+++ b/.pipelines/ESRPSigning_core.json
@@ -131,6 +131,7 @@
"modules\\launcher\\Plugins\\TimeDate\\Microsoft.PowerToys.Run.Plugin.TimeDate.dll",
"modules\\launcher\\Plugins\\WebSearch\\Community.PowerToys.Run.Plugin.WebSearch.dll",
"modules\\launcher\\Plugins\\WindowsTerminal\\Microsoft.PowerToys.Run.Plugin.WindowsTerminal.dll",
+ "modules\\launcher\\Plugins\\ValueGenerator\\Community.PowerToys.Run.Plugin.ValueGenerator.dll",
"modules\\MeasureTool\\PowerToys.MeasureToolModuleInterface.dll",
"modules\\MeasureTool\\PowerToys.MeasureToolCore.dll",
diff --git a/.pipelines/ci/templates/build-powertoys-steps.yml b/.pipelines/ci/templates/build-powertoys-steps.yml
index 0da849f2bb..9fb7023624 100644
--- a/.pipelines/ci/templates/build-powertoys-steps.yml
+++ b/.pipelines/ci/templates/build-powertoys-steps.yml
@@ -231,6 +231,7 @@ steps:
**\Microsoft.Interop.Tests.dll
**\ImageResizer.Test.dll
**\Community.PowerToys.Run.Plugin.UnitConverter.UnitTest.dll
+ **\Community.PowerToys.Run.Plugin.ValueGenerator.UnitTests.dll
**\Microsoft.Plugin.Folder.UnitTests.dll
**\Microsoft.Plugin.Program.UnitTests.dll
**\Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest.dll
diff --git a/PowerToys.sln b/PowerToys.sln
index d82533f297..f636803aa1 100644
--- a/PowerToys.sln
+++ b/PowerToys.sln
@@ -140,6 +140,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PowerLauncher", "src\module
{A2D583F0-B70C-4462-B1F0-8E81AFB7BA85} = {A2D583F0-B70C-4462-B1F0-8E81AFB7BA85}
{BB23A474-5058-4F75-8FA3-5FE3DE53CDF4} = {BB23A474-5058-4F75-8FA3-5FE3DE53CDF4}
{C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0} = {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}
+ {D095BE44-1F2E-463E-A494-121892A75EA2} = {D095BE44-1F2E-463E-A494-121892A75EA2}
{F8B870EB-D5F5-45BA-9CF7-A5C459818820} = {F8B870EB-D5F5-45BA-9CF7-A5C459818820}
{FD8EB419-FF9C-4D88-BB6F-BF6CED37747B} = {FD8EB419-FF9C-4D88-BB6F-BF6CED37747B}
{FDB3555B-58EF-4AE6-B5F1-904719637AB4} = {FDB3555B-58EF-4AE6-B5F1-904719637AB4}
@@ -525,6 +526,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FilePreviewCommon", "src\co
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerToys.Run.Plugin.PowerToys", "src\modules\launcher\Plugins\Microsoft.PowerToys.Run.Plugin.PowerToys\Microsoft.PowerToys.Run.Plugin.PowerToys.csproj", "{500DED3E-CFB5-4ED5-ACC6-02B3D6DC336D}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Community.PowerToys.Run.Plugin.ValueGenerator", "src\modules\launcher\Plugins\Community.PowerToys.Run.Plugin.ValueGenerator\Community.PowerToys.Run.Plugin.ValueGenerator.csproj", "{D095BE44-1F2E-463E-A494-121892A75EA2}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Community.PowerToys.Run.Plugin.ValueGenerator.UnitTests", "src\modules\launcher\Plugins\Community.PowerToys.Run.Plugin.ValueGenerator.UnitTests\Community.PowerToys.Run.Plugin.ValueGenerator.UnitTests.csproj", "{90F9FA90-2C20-4004-96E6-F3B78151F5A5}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM64 = Debug|ARM64
@@ -2247,6 +2252,30 @@ Global
{500DED3E-CFB5-4ED5-ACC6-02B3D6DC336D}.Release|x64.Build.0 = Release|x64
{500DED3E-CFB5-4ED5-ACC6-02B3D6DC336D}.Release|x86.ActiveCfg = Release|x64
{500DED3E-CFB5-4ED5-ACC6-02B3D6DC336D}.Release|x86.Build.0 = Release|x64
+ {D095BE44-1F2E-463E-A494-121892A75EA2}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {D095BE44-1F2E-463E-A494-121892A75EA2}.Debug|ARM64.Build.0 = Debug|ARM64
+ {D095BE44-1F2E-463E-A494-121892A75EA2}.Debug|x64.ActiveCfg = Debug|x64
+ {D095BE44-1F2E-463E-A494-121892A75EA2}.Debug|x64.Build.0 = Debug|x64
+ {D095BE44-1F2E-463E-A494-121892A75EA2}.Debug|x86.ActiveCfg = Debug|x64
+ {D095BE44-1F2E-463E-A494-121892A75EA2}.Debug|x86.Build.0 = Debug|x64
+ {D095BE44-1F2E-463E-A494-121892A75EA2}.Release|ARM64.ActiveCfg = Release|ARM64
+ {D095BE44-1F2E-463E-A494-121892A75EA2}.Release|ARM64.Build.0 = Release|ARM64
+ {D095BE44-1F2E-463E-A494-121892A75EA2}.Release|x64.ActiveCfg = Release|x64
+ {D095BE44-1F2E-463E-A494-121892A75EA2}.Release|x64.Build.0 = Release|x64
+ {D095BE44-1F2E-463E-A494-121892A75EA2}.Release|x86.ActiveCfg = Release|x64
+ {D095BE44-1F2E-463E-A494-121892A75EA2}.Release|x86.Build.0 = Release|x64
+ {90F9FA90-2C20-4004-96E6-F3B78151F5A5}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {90F9FA90-2C20-4004-96E6-F3B78151F5A5}.Debug|ARM64.Build.0 = Debug|ARM64
+ {90F9FA90-2C20-4004-96E6-F3B78151F5A5}.Debug|x64.ActiveCfg = Debug|x64
+ {90F9FA90-2C20-4004-96E6-F3B78151F5A5}.Debug|x64.Build.0 = Debug|x64
+ {90F9FA90-2C20-4004-96E6-F3B78151F5A5}.Debug|x86.ActiveCfg = Debug|x64
+ {90F9FA90-2C20-4004-96E6-F3B78151F5A5}.Debug|x86.Build.0 = Debug|x64
+ {90F9FA90-2C20-4004-96E6-F3B78151F5A5}.Release|ARM64.ActiveCfg = Release|ARM64
+ {90F9FA90-2C20-4004-96E6-F3B78151F5A5}.Release|ARM64.Build.0 = Release|ARM64
+ {90F9FA90-2C20-4004-96E6-F3B78151F5A5}.Release|x64.ActiveCfg = Release|x64
+ {90F9FA90-2C20-4004-96E6-F3B78151F5A5}.Release|x64.Build.0 = Release|x64
+ {90F9FA90-2C20-4004-96E6-F3B78151F5A5}.Release|x86.ActiveCfg = Release|x64
+ {90F9FA90-2C20-4004-96E6-F3B78151F5A5}.Release|x86.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -2436,6 +2465,8 @@ Global
{929C1324-22E8-4412-A9A8-80E85F3985A5} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
{9EBAA524-0EDA-470B-95D4-39383285CBB2} = {1AFB6476-670D-4E80-A464-657E01DFF482}
{500DED3E-CFB5-4ED5-ACC6-02B3D6DC336D} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
+ {D095BE44-1F2E-463E-A494-121892A75EA2} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
+ {90F9FA90-2C20-4004-96E6-F3B78151F5A5} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}
diff --git a/doc/devdocs/modules/launcher/plugins/community.valuegenerator.md b/doc/devdocs/modules/launcher/plugins/community.valuegenerator.md
new file mode 100644
index 0000000000..ddecae0bee
--- /dev/null
+++ b/doc/devdocs/modules/launcher/plugins/community.valuegenerator.md
@@ -0,0 +1,52 @@
+# Value Generator Plugin
+
+The Value Generator plugin is used to generate hashes for strings, to calculate base64 encodings and to generate GUIDs versions 1, 3, 4 and 5.
+
+
+
+### [`IComputeRequest`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/IComputeRequest.cs)
+- Interface for a request for computation
+- the `bool Compute()` method must populate the `IsSuccessful` and one of the `Result` and `ErrorMessage` fields
+- The result of `string ResultToString()` will be used for the Result's title
+- The `Description` field will be used for the Result's subtitle
+
+### [`HashRequest`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Hashing/HashRequest.cs)
+- Implements IComputeRequest
+- Supports the hashing algorithms from System.Security.Cryptography:
+ - MD5
+ - SHA1
+ - SHA256
+ - SHA384
+ - SHA512
+- If other algorithms are added to System.Security.Cryptography, they can be added to the `_algorithms` dictionary. [`InputParser.ParseInput()`](#inputparser) will need to return a `HashRequest` for the algorithm in the query
+
+### [`Base64Request`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Base64/Base64Request.cs)
+- Implements IComputeRequest
+- `Compute()` will populate `Result` with the base64 encoding of the byte array passed in the constructor
+
+### [`GUIDRequest`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/GUID/GUIDRequest.cs)
+- Implements IComputeRequest
+- Uses the [`GUIDGenerator`](#guidgenerator) class to generate or compute the requested GUID
+
+### [`GUIDGenerator`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/GUID/GUIDGenerator.cs)
+- Utility class for generating or calculating GUIDs
+- Generating GUID versions 1 and 4 is done using builtin APIs. [`UuidCreateSequential`](https://learn.microsoft.com/en-us/windows/win32/api/rpcdce/nf-rpcdce-uuidcreatesequential) for version 1 and `System.Guid.NewGuid()` for version 4
+- Versions 3 and 5 take two parameters, a namespace and a name
+- The namespace must be a valid GUID or one of the [predefined ones](https://datatracker.ietf.org/doc/html/rfc4122#appendix-C)
+- The `PredefinedNamespaces` dictionary contains aliases for the predefined namespaces
+- The name can be any string
+
+### [`InputParser`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/InputParser.cs)
+- It is responsible only for parsing the query from the user
+- Based on the user query, the `ParseInput()` method must return an object that implements the `IComputeRequest` interface or it must throw one of `FormatException` or `ArgumentException`
+- Throwing an `ArgumentException` should signal the fact the query contains a mistake that the user can fix (eg. an unsupported hash function, an invalid GUID version, an invalid namespace, etc)
+> The error message will be shown to the user and no log message will be created
+- Throwing a `FormatException` should signal either:
+ - that the query may become valid, and so it does not make sense to show an error just yet (eg. the query does not contain a request yet, a hash request without a string to hash)
+ - that the query is completely invalid
+> The error message will not be shown to the user but a log message will be created
+
+### Adding a new value generator
+1. To add a new value generator, create a folder under `/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/` and inside it add a class that implements `IComputeRequest`.
+2. Add any utility classes that are specific to the new generator inside the same folder to keep them separated from the other generators.
+3. Modify the `InputParser.ParseInput()` to handle a request for the new generator and return an instance of the class you created in step 1
\ No newline at end of file
diff --git a/doc/images/launcher/plugins/community.valuegenerator.png b/doc/images/launcher/plugins/community.valuegenerator.png
new file mode 100644
index 0000000000..37d564565a
Binary files /dev/null and b/doc/images/launcher/plugins/community.valuegenerator.png differ
diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs
index 61a48a7e08..721ef997c6 100644
--- a/installer/PowerToysSetup/Product.wxs
+++ b/installer/PowerToysSetup/Product.wxs
@@ -597,6 +597,9 @@
+
+
+
diff --git a/installer/PowerToysSetup/Resources.wxs b/installer/PowerToysSetup/Resources.wxs
index a4dd7067c3..2724e73992 100644
--- a/installer/PowerToysSetup/Resources.wxs
+++ b/installer/PowerToysSetup/Resources.wxs
@@ -11,7 +11,7 @@
-
+
@@ -393,6 +393,15 @@
+
+
+
+
+
+
@@ -474,6 +483,7 @@
+
diff --git a/installer/PowerToysSetup/Run.wxs b/installer/PowerToysSetup/Run.wxs
index 3186c17c31..6cf803e36e 100644
--- a/installer/PowerToysSetup/Run.wxs
+++ b/installer/PowerToysSetup/Run.wxs
@@ -105,6 +105,11 @@
+
+
+
+
+
@@ -319,6 +324,16 @@
+
+
+
+
+
+
+
+
+
+
@@ -365,6 +380,8 @@
+
+
diff --git a/installer/PowerToysSetup/generateAllFileComponents.ps1 b/installer/PowerToysSetup/generateAllFileComponents.ps1
index e435ff8bdb..108d725f12 100644
--- a/installer/PowerToysSetup/generateAllFileComponents.ps1
+++ b/installer/PowerToysSetup/generateAllFileComponents.ps1
@@ -203,6 +203,11 @@ Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson ""$
Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson """" -fileListName PowerToysImagesCmpFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath ""$PSScriptRoot..\..\..\$platform\Release\modules\launcher\Plugins\PowerToys\Images"""
Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""PowerToysCmpFiles"" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot"
Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""PowerToysImagesCmpFiles"" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot"
+###ValueGenerator
+Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson ""$PSScriptRoot..\..\..\$platform\Release\modules\launcher\Plugins\ValueGenerator\Community.PowerToys.Run.Plugin.ValueGenerator.deps.json"" -fileListName ValueGeneratorCmpFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1"
+Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson """" -fileListName ValueGeneratorImagesCmpFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath ""$PSScriptRoot..\..\..\$platform\Release\modules\launcher\Plugins\ValueGenerator\Images"""
+Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""ValueGeneratorCmpFiles"" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot"
+Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""ValueGeneratorImagesCmpFiles"" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot"
## Plugins
#ShortcutGuide
diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator.UnitTests/Community.PowerToys.Run.Plugin.ValueGenerator.UnitTests.csproj b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator.UnitTests/Community.PowerToys.Run.Plugin.ValueGenerator.UnitTests.csproj
new file mode 100644
index 0000000000..ba684821bd
--- /dev/null
+++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator.UnitTests/Community.PowerToys.Run.Plugin.ValueGenerator.UnitTests.csproj
@@ -0,0 +1,19 @@
+
+
+
+ net7.0-windows
+ enable
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator.UnitTests/GUIDGeneratorTests.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator.UnitTests/GUIDGeneratorTests.cs
new file mode 100644
index 0000000000..f15fcc9b33
--- /dev/null
+++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator.UnitTests/GUIDGeneratorTests.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.Linq;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace Community.PowerToys.Run.Plugin.ValueGenerator.UnitTests
+{
+ [TestClass]
+ public class GUIDGeneratorTests
+ {
+ [TestMethod]
+ public void GUIDv1Generator()
+ {
+ var guidRequest = new GUID.GUIDRequest(1);
+ guidRequest.Compute();
+ var guid = guidRequest.Result;
+
+ Assert.IsNotNull(guid);
+ Assert.AreEqual(0x1000, GetGUIDVersion(guid));
+ }
+
+ [TestMethod]
+ public void GUIDv3Generator()
+ {
+ var guidRequest = new GUID.GUIDRequest(3, "ns:DNS", "abc");
+ guidRequest.Compute();
+ var guid = guidRequest.Result;
+
+ Assert.IsNotNull(guid);
+ Assert.AreEqual(0x3000, GetGUIDVersion(guid));
+ }
+
+ [TestMethod]
+ public void GUIDv4Generator()
+ {
+ var guidRequest = new GUID.GUIDRequest(4);
+ guidRequest.Compute();
+ var guid = guidRequest.Result;
+
+ Assert.IsNotNull(guid);
+ Assert.AreEqual(0x4000, GetGUIDVersion(guid));
+ }
+
+ [TestMethod]
+ public void GUIDv5Generator()
+ {
+ var guidRequest = new GUID.GUIDRequest(5, "ns:DNS", "abc");
+ guidRequest.Compute();
+ var guid = guidRequest.Result;
+
+ Assert.IsNotNull(guid);
+ Assert.AreEqual(0x5000, GetGUIDVersion(guid));
+ }
+
+ [DataTestMethod]
+ [DataRow(3, "ns:DNS", "abc", "5bd670ce-29c8-3369-a8a1-10ce44c7259e")]
+ [DataRow(3, "ns:URL", "abc", "874a8cb4-4e91-3055-a476-3d3e2ffe375f")]
+ [DataRow(3, "ns:OID", "abc", "5557cd36-6b67-38ac-83fe-825f5905fc15")]
+ [DataRow(3, "ns:X500", "abc", "589392cb-93e1-392c-a846-367c45ed1ecc")]
+ [DataRow(3, "589392cb-93e1-392c-a846-367c45ed1ecc", "abc", "f55f77d2-feed-378e-aa47-2f716b07aaad")]
+ [DataRow(3, "abc", "abc", null)]
+ [DataRow(3, "", "abc", null)]
+ [DataRow(3, null, "abc", null)]
+ [DataRow(3, "abc", "", null)]
+ [DataRow(3, "abc", null, null)]
+ [DataRow(5, "ns:DNS", "abc", "6cb8e707-0fc5-5f55-88d4-d4fed43e64a8")]
+ [DataRow(5, "ns:URL", "abc", "68661508-f3c4-55b4-945d-ae2b4dfe5db4")]
+ [DataRow(5, "ns:OID", "abc", "7697a46f-b283-5da3-8e7c-62c11c03dd9e")]
+ [DataRow(5, "ns:X500", "abc", "53e882a6-63b1-578b-8bf1-8f0878cfa6b7")]
+ [DataRow(5, "589392cb-93e1-392c-a846-367c45ed1ecc", "abc", "396466f5-96f4-57e2-aea1-78e1bb1bacc6")]
+ [DataRow(5, "abc", "abc", null)]
+ [DataRow(5, "", "abc", null)]
+ [DataRow(5, null, "abc", null)]
+ [DataRow(5, "abc", "", null)]
+ [DataRow(5, "abc", null, null)]
+ public void GUIDv3Andv5(int version, string namespaceName, string name, string expectedResult)
+ {
+ var expectException = false;
+ if (namespaceName == null)
+ {
+ expectException = true;
+ }
+
+ string[] predefinedNamespaces = new string[] { "ns:DNS", "ns:URL", "ns:OID", "ns:X500" };
+ if (namespaceName != null &&
+ !Guid.TryParse(namespaceName, out _) &&
+ !predefinedNamespaces.Contains(namespaceName))
+ {
+ expectException = true;
+ }
+
+ if (name == null)
+ {
+ expectException = true;
+ }
+
+ try
+ {
+ var guidRequest = new GUID.GUIDRequest(version, namespaceName, name);
+
+ guidRequest.Compute();
+
+ if (expectException)
+ {
+ Assert.Fail("GUID generator should have thrown an exception");
+ }
+
+ if (namespaceName != string.Empty)
+ {
+ Assert.IsTrue(guidRequest.IsSuccessful);
+ Assert.AreEqual(expectedResult, guidRequest.ResultToString());
+ }
+ else
+ {
+ Assert.IsFalse(guidRequest.IsSuccessful);
+ }
+ }
+ catch (AssertFailedException)
+ {
+ throw;
+ }
+ catch
+ {
+ if (!expectException)
+ {
+ throw;
+ }
+ }
+ }
+
+ private static short GetGUIDVersion(byte[] guid)
+ {
+ var time_hi_and_version = BitConverter.ToInt16(guid.AsSpan()[6..8]);
+ return (short)(time_hi_and_version & 0xF000);
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator.UnitTests/InputParserTests.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator.UnitTests/InputParserTests.cs
new file mode 100644
index 0000000000..e17437556d
--- /dev/null
+++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator.UnitTests/InputParserTests.cs
@@ -0,0 +1,92 @@
+// 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 System.Text.RegularExpressions;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Wox.Plugin;
+
+namespace Community.PowerToys.Run.Plugin.ValueGenerator.UnitTests
+{
+ [TestClass]
+ public class InputParserTests
+ {
+ [DataTestMethod]
+ [DataRow("md5 abc", typeof(Hashing.HashRequest))]
+ [DataRow("sha1 abc", typeof(Hashing.HashRequest))]
+ [DataRow("sha256 abc", typeof(Hashing.HashRequest))]
+ [DataRow("sha384 abc", typeof(Hashing.HashRequest))]
+ [DataRow("sha512 abc", typeof(Hashing.HashRequest))]
+ [DataRow("sha1111 abc", null)]
+ [DataRow("uuid", typeof(GUID.GUIDRequest))]
+ [DataRow("guidv3 ns:DNS abc", typeof(GUID.GUIDRequest))]
+ [DataRow("guidv2 ns:DNS abc", null)]
+ [DataRow("uUiD5 ns:URL abc", typeof(GUID.GUIDRequest))]
+ [DataRow("Guidvv ns:DNS abc", null)]
+ [DataRow("guidv4", typeof(GUID.GUIDRequest))]
+ [DataRow("base64 abc", typeof(Base64.Base64Request))]
+ [DataRow("base99 abc", null)]
+ [DataRow("base64s abc", null)]
+ public void ParserTest(string input, Type? expectedRequestType)
+ {
+ var parser = new InputParser();
+ var query = new Query(input);
+
+ var expectException = false;
+ string? command = null;
+ if (query.Terms.Count == 0)
+ {
+ expectException = true;
+ }
+ else
+ {
+ command = query.Terms[0];
+ }
+
+ if (command != null && !CommandIsKnown(command))
+ {
+ expectException = true;
+ }
+
+ try
+ {
+ IComputeRequest request = parser.ParseInput(query);
+ if (expectException)
+ {
+ Assert.Fail("Parser should have thrown an exception");
+ }
+
+ Assert.IsNotNull(request);
+
+ Assert.AreEqual(expectedRequestType, request.GetType());
+ }
+ catch (AssertFailedException)
+ {
+ throw;
+ }
+ catch
+ {
+ if (!expectException)
+ {
+ throw;
+ }
+ }
+ }
+
+ private static bool CommandIsKnown(string command)
+ {
+ string[] hashes = new string[] { "md5", "sha1", "sha256", "sha384", "sha512", "base64" };
+
+ if (hashes.Contains(command.ToLowerInvariant()))
+ {
+ return true;
+ }
+
+ Regex regex = new Regex("^(guid|uuid)([1345]{0,1}|v[1345]{1})$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
+
+ return regex.IsMatch(command);
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Base64/Base64Request.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Base64/Base64Request.cs
new file mode 100644
index 0000000000..b613e48606
--- /dev/null
+++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Base64/Base64Request.cs
@@ -0,0 +1,50 @@
+// 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.Text;
+using Wox.Plugin.Logger;
+
+namespace Community.PowerToys.Run.Plugin.ValueGenerator.Base64
+{
+ public class Base64Request : IComputeRequest
+ {
+ public byte[] Result { get; set; }
+
+ public string Description => "Base64 Encoding";
+
+ public bool IsSuccessful { get; set; }
+
+ public string ErrorMessage { get; set; }
+
+ private byte[] DataToEncode { get; set; }
+
+ public Base64Request(byte[] dataToEncode)
+ {
+ DataToEncode = dataToEncode ?? throw new ArgumentNullException(nameof(dataToEncode));
+ }
+
+ public bool Compute()
+ {
+ IsSuccessful = true;
+ try
+ {
+ Result = Encoding.UTF8.GetBytes(System.Convert.ToBase64String(DataToEncode));
+ }
+ catch (Exception e)
+ {
+ Log.Exception(e.Message, e, GetType());
+ ErrorMessage = e.Message;
+ IsSuccessful = false;
+ }
+
+ return IsSuccessful;
+ }
+
+ public string ResultToString()
+ {
+ return Encoding.UTF8.GetString(Result);
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Community.PowerToys.Run.Plugin.ValueGenerator.csproj b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Community.PowerToys.Run.Plugin.ValueGenerator.csproj
new file mode 100644
index 0000000000..0298e66f7a
--- /dev/null
+++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Community.PowerToys.Run.Plugin.ValueGenerator.csproj
@@ -0,0 +1,74 @@
+
+
+
+
+ net7.0-windows
+ {D095BE44-1F2E-463E-A494-121892A75EA2}
+ true
+ $(Version).0
+ Community.PowerToys.Run.Plugin.ValueGenerator
+ Community.PowerToys.Run.Plugin.ValueGenerator
+ false
+ false
+ true
+ ..\..\..\..\..\$(Platform)\$(Configuration)\modules\launcher\Plugins\ValueGenerator\
+
+
+
+ true
+ DEBUG;TRACE
+ full
+ false
+
+
+
+ TRACE
+ true
+ pdbonly
+
+
+
+
+ false
+
+
+ false
+
+
+
+
+
+ PreserveNewest
+
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+
+
+
+ PublicResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+
diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/GUID/GUIDGenerator.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/GUID/GUIDGenerator.cs
new file mode 100644
index 0000000000..6479972f13
--- /dev/null
+++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/GUID/GUIDGenerator.cs
@@ -0,0 +1,119 @@
+// 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.Net;
+using System.Security.Cryptography;
+using System.Text;
+using Wox.Plugin.Common.Win32;
+
+namespace Community.PowerToys.Run.Plugin.ValueGenerator.GUID
+{
+ internal sealed class GUIDGenerator
+ {
+ // As defined in https://datatracker.ietf.org/doc/html/rfc4122#appendix-C
+ public static readonly Dictionary PredefinedNamespaces = new Dictionary()
+ {
+ { "ns:dns", new Guid(0x6ba7b810, 0x9dad, 0x11d1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8) },
+ { "ns:url", new Guid(0x6ba7b811, 0x9dad, 0x11d1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8) },
+ { "ns:oid", new Guid(0x6ba7b812, 0x9dad, 0x11d1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8) },
+ { "ns:x500", new Guid(0x6ba7b814, 0x9dad, 0x11d1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8) },
+ };
+
+ public static Guid V1()
+ {
+ GUIDDATA guiddata;
+
+ int uuidCreateResult = NativeMethods.UuidCreateSequential(out guiddata);
+
+ if (uuidCreateResult != Win32Constants.RPC_S_OK && uuidCreateResult != Win32Constants.RPC_S_UUID_LOCAL_ONLY)
+ {
+ throw new InvalidOperationException("Failed to create GUID version 1");
+ }
+
+ return new Guid(guiddata.Data1, guiddata.Data2, guiddata.Data3, guiddata.Data4);
+ }
+
+ public static Guid V3(Guid uuidNamespace, string uuidName)
+ {
+ return V3AndV5(uuidNamespace, uuidName, 3);
+ }
+
+ public static Guid V4()
+ {
+ return Guid.NewGuid();
+ }
+
+ public static Guid V5(Guid uuidNamespace, string uuidName)
+ {
+ return V3AndV5(uuidNamespace, uuidName, 5);
+ }
+
+ private static Guid V3AndV5(Guid uuidNamespace, string uuidName, short version)
+ {
+ byte[] namespaceBytes = uuidNamespace.ToByteArray();
+ byte[] networkEndianNamespaceBytes = namespaceBytes;
+
+ // Convert time_low, time_mid and time_hi_and_version to network order
+ int time_low = IPAddress.HostToNetworkOrder(BitConverter.ToInt32(networkEndianNamespaceBytes.AsSpan()[0..4]));
+ short time_mid = IPAddress.HostToNetworkOrder(BitConverter.ToInt16(networkEndianNamespaceBytes.AsSpan()[4..6]));
+ short time_hi_and_version = IPAddress.HostToNetworkOrder(BitConverter.ToInt16(networkEndianNamespaceBytes.AsSpan()[6..8]));
+
+ Buffer.BlockCopy(BitConverter.GetBytes(time_low), 0, networkEndianNamespaceBytes, 0, 4);
+ Buffer.BlockCopy(BitConverter.GetBytes(time_mid), 0, networkEndianNamespaceBytes, 4, 2);
+ Buffer.BlockCopy(BitConverter.GetBytes(time_hi_and_version), 0, networkEndianNamespaceBytes, 6, 2);
+
+ byte[] nameBytes = Encoding.ASCII.GetBytes(uuidName);
+
+ byte[] namespaceAndNameBytes = new byte[networkEndianNamespaceBytes.Length + nameBytes.Length];
+ Buffer.BlockCopy(networkEndianNamespaceBytes, 0, namespaceAndNameBytes, 0, namespaceBytes.Length);
+ Buffer.BlockCopy(nameBytes, 0, namespaceAndNameBytes, networkEndianNamespaceBytes.Length, nameBytes.Length);
+
+ byte[] hash;
+ if (version == 3)
+ {
+#pragma warning disable CA5351 // Do Not Use Broken Cryptographic Algorithms
+ hash = MD5.HashData(namespaceAndNameBytes);
+#pragma warning restore CA5351 // Do Not Use Broken Cryptographic Algorithms
+ }
+ else if (version == 5)
+ {
+#pragma warning disable CA5350 // Do Not Use Weak Cryptographic Algorithms
+ hash = SHA1.HashData(namespaceAndNameBytes);
+#pragma warning restore CA5350 // Do Not Use Weak Cryptographic Algorithms
+ }
+ else
+ {
+ throw new InvalidOperationException($"GUID version {version} does not exist");
+ }
+
+ byte[] result = new byte[16];
+
+ // Copy first 16-bytes of the hash into our Uuid result
+ Buffer.BlockCopy(hash, 0, result, 0, 16);
+
+ // Convert put time_low, time_mid and time_hi_and_version back to host order
+ time_low = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(result.AsSpan()[0..4]));
+ time_mid = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(result.AsSpan()[4..6]));
+ time_hi_and_version = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(result.AsSpan()[6..8]));
+
+ // Set version 'version' in time_hi_and_version field according to https://datatracker.ietf.org/doc/html/rfc4122#section-4.1.3
+ time_hi_and_version &= 0x0FFF;
+#pragma warning disable CS0675 // Bitwise-or operator used on a sign-extended operand
+ time_hi_and_version = (short)(time_hi_and_version | (version << 12));
+#pragma warning restore CS0675 // Bitwise-or operator used on a sign-extended operand
+
+ Buffer.BlockCopy(BitConverter.GetBytes(time_low), 0, result, 0, 4);
+ Buffer.BlockCopy(BitConverter.GetBytes(time_mid), 0, result, 4, 2);
+ Buffer.BlockCopy(BitConverter.GetBytes(time_hi_and_version), 0, result, 6, 2);
+
+ // Set upper two bits to "10"
+ result[8] &= 0x3F;
+ result[8] |= 0x80;
+
+ return new Guid(result);
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/GUID/GUIDRequest.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/GUID/GUIDRequest.cs
new file mode 100644
index 0000000000..00d30bba90
--- /dev/null
+++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/GUID/GUIDRequest.cs
@@ -0,0 +1,148 @@
+// 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.Security.Cryptography;
+using Wox.Plugin.Logger;
+
+namespace Community.PowerToys.Run.Plugin.ValueGenerator.GUID
+{
+ public class GUIDRequest : IComputeRequest
+ {
+ public byte[] Result { get; set; }
+
+ public bool IsSuccessful { get; set; }
+
+ public string ErrorMessage { get; set; }
+
+ private int Version { get; set; }
+
+ public string Description
+ {
+ get
+ {
+ switch (Version)
+ {
+ case 1:
+ return "Version 1: Time base GUID";
+ case 3:
+ case 5:
+ string hashAlgorithm;
+ if (Version == 3)
+ {
+ hashAlgorithm = HashAlgorithmName.MD5.ToString();
+ }
+ else
+ {
+ hashAlgorithm = HashAlgorithmName.SHA1.ToString();
+ }
+
+ return $"Version {Version} ({hashAlgorithm}): Namespace and name based GUID.";
+ case 4:
+ return "Version 4: Randomly generated GUID";
+ default:
+ return string.Empty;
+ }
+ }
+ }
+
+ private Guid? GuidNamespace { get; set; }
+
+ private string GuidName { get; set; }
+
+ private Guid GuidResult { get; set; }
+
+ private static readonly string NullNamespaceError = $"The first parameter needs to be a valid GUID or one of: {string.Join(", ", GUIDGenerator.PredefinedNamespaces.Keys)}";
+
+ public GUIDRequest(int version, string guidNamespace = null, string name = null)
+ {
+ Version = version;
+
+ if (Version < 1 || Version > 5 || Version == 2)
+ {
+ throw new ArgumentException("Unsupported GUID version. Supported versions are 1, 3, 4 and 5");
+ }
+
+ if (version == 3 || version == 5)
+ {
+ if (guidNamespace == null)
+ {
+ throw new ArgumentNullException(null, NullNamespaceError);
+ }
+
+ Guid guid;
+ if (GUIDGenerator.PredefinedNamespaces.TryGetValue(guidNamespace.ToLowerInvariant(), out guid))
+ {
+ GuidNamespace = guid;
+ }
+ else if (Guid.TryParse(guidNamespace, out guid))
+ {
+ GuidNamespace = guid;
+ }
+ else
+ {
+ throw new ArgumentNullException(null, NullNamespaceError);
+ }
+
+ if (name == null)
+ {
+ throw new ArgumentNullException(nameof(name));
+ }
+ else
+ {
+ GuidName = name;
+ }
+ }
+ else
+ {
+ GuidNamespace = null;
+ }
+
+ ErrorMessage = null;
+ }
+
+ public bool Compute()
+ {
+ IsSuccessful = true;
+ try
+ {
+ switch (Version)
+ {
+ case 1:
+ GuidResult = GUIDGenerator.V1();
+ break;
+ case 3:
+ GuidResult = GUIDGenerator.V3(GuidNamespace.Value, GuidName);
+ break;
+ case 4:
+ GuidResult = GUIDGenerator.V4();
+ break;
+ case 5:
+ GuidResult = GUIDGenerator.V5(GuidNamespace.Value, GuidName);
+ break;
+ }
+
+ Result = GuidResult.ToByteArray();
+ }
+ catch (InvalidOperationException e)
+ {
+ Log.Exception(e.Message, e, GetType());
+ ErrorMessage = e.Message;
+ IsSuccessful = false;
+ }
+
+ return IsSuccessful;
+ }
+
+ public string ResultToString()
+ {
+ if (!IsSuccessful)
+ {
+ return ErrorMessage;
+ }
+
+ return GuidResult.ToString();
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Hashing/HashRequest.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Hashing/HashRequest.cs
new file mode 100644
index 0000000000..352372285f
--- /dev/null
+++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Hashing/HashRequest.cs
@@ -0,0 +1,81 @@
+// 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.Security.Cryptography;
+using System.Text;
+using Wox.Plugin.Logger;
+
+namespace Community.PowerToys.Run.Plugin.ValueGenerator.Hashing
+{
+ public class HashRequest : IComputeRequest
+ {
+ public byte[] Result { get; set; }
+
+ public bool IsSuccessful { get; set; }
+
+ public string ErrorMessage { get; set; }
+
+ public string Description
+ {
+ get
+ {
+ return $"{AlgorithmName}({Encoding.UTF8.GetString(DataToHash)})";
+ }
+ }
+
+ public HashAlgorithmName AlgorithmName { get; set; }
+
+ private byte[] DataToHash { get; set; }
+
+ private static Dictionary _algorithms = new Dictionary()
+ {
+#pragma warning disable CA5351 // Do Not Use Broken Cryptographic Algorithms
+ { HashAlgorithmName.MD5, MD5.Create() },
+#pragma warning restore CA5351 // Do Not Use Broken Cryptographic Algorithms
+
+#pragma warning disable CA5350 // Do Not Use Weak Cryptographic Algorithms
+ { HashAlgorithmName.SHA1, SHA1.Create() },
+#pragma warning restore CA5350 // Do Not Use Weak Cryptographic Algorithms
+ { HashAlgorithmName.SHA256, SHA256.Create() },
+ { HashAlgorithmName.SHA384, SHA384.Create() },
+ { HashAlgorithmName.SHA512, SHA512.Create() },
+ };
+
+ public HashRequest(HashAlgorithmName algorithmName, byte[] dataToHash)
+ {
+ AlgorithmName = algorithmName;
+ DataToHash = dataToHash ?? throw new ArgumentNullException(nameof(dataToHash));
+ }
+
+ public bool Compute()
+ {
+ if (DataToHash == null)
+ {
+ ErrorMessage = "Null data passed to hash request";
+ Log.Exception(ErrorMessage, new InvalidOperationException(ErrorMessage), GetType());
+ IsSuccessful = false;
+ }
+ else
+ {
+ Result = _algorithms[AlgorithmName].ComputeHash(DataToHash);
+ IsSuccessful = true;
+ }
+
+ return IsSuccessful;
+ }
+
+ public string ResultToString()
+ {
+ StringBuilder sb = new StringBuilder();
+ foreach (var b in Result)
+ {
+ sb.Append(b.ToString("X2", null));
+ }
+
+ return sb.ToString();
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/IComputeRequest.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/IComputeRequest.cs
new file mode 100644
index 0000000000..c24d041ac7
--- /dev/null
+++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/IComputeRequest.cs
@@ -0,0 +1,23 @@
+// 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;
+
+namespace Community.PowerToys.Run.Plugin.ValueGenerator
+{
+ public interface IComputeRequest
+ {
+ public byte[] Result { get; set; }
+
+ public string Description { get; }
+
+ public bool IsSuccessful { get; set; }
+
+ public string ErrorMessage { get; set; }
+
+ bool Compute();
+
+ public string ResultToString();
+ }
+}
diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Images/ValueGenerator.dark.png b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Images/ValueGenerator.dark.png
new file mode 100644
index 0000000000..b94d83c300
Binary files /dev/null and b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Images/ValueGenerator.dark.png differ
diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Images/ValueGenerator.light.png b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Images/ValueGenerator.light.png
new file mode 100644
index 0000000000..e63953a560
Binary files /dev/null and b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Images/ValueGenerator.light.png differ
diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Images/Warning.dark.png b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Images/Warning.dark.png
new file mode 100644
index 0000000000..f033c78fda
Binary files /dev/null and b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Images/Warning.dark.png differ
diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Images/Warning.light.png b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Images/Warning.light.png
new file mode 100644
index 0000000000..37dd08c87e
Binary files /dev/null and b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Images/Warning.light.png differ
diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/InputParser.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/InputParser.cs
new file mode 100644
index 0000000000..394fbf8752
--- /dev/null
+++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/InputParser.cs
@@ -0,0 +1,132 @@
+// 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.Security.Cryptography;
+using System.Text;
+using Community.PowerToys.Run.Plugin.ValueGenerator.Base64;
+using Community.PowerToys.Run.Plugin.ValueGenerator.GUID;
+using Community.PowerToys.Run.Plugin.ValueGenerator.Hashing;
+using Wox.Plugin;
+using Wox.Plugin.Logger;
+
+namespace Community.PowerToys.Run.Plugin.ValueGenerator
+{
+ public class InputParser
+ {
+ public IComputeRequest ParseInput(Query query)
+ {
+ IComputeRequest request;
+
+ if (query.Terms.Count == 0)
+ {
+ throw new FormatException("Empty request");
+ }
+
+ string command = query.Terms[0];
+
+ if (command.ToLower(null) == "md5")
+ {
+ int commandIndex = query.RawUserQuery.IndexOf(command, StringComparison.InvariantCultureIgnoreCase);
+ string content = query.RawUserQuery.Substring(commandIndex + command.Length).Trim();
+
+ if (content == string.Empty)
+ {
+ throw new FormatException("Empty hash request");
+ }
+
+ Log.Debug($"Will calculate MD5 hash for: {content}", GetType());
+ request = new HashRequest(HashAlgorithmName.MD5, Encoding.UTF8.GetBytes(content));
+ }
+ else if (command.StartsWith("sha", StringComparison.InvariantCultureIgnoreCase))
+ {
+ int commandIndex = query.RawUserQuery.IndexOf(command, StringComparison.InvariantCultureIgnoreCase);
+ string content = query.RawUserQuery.Substring(commandIndex + command.Length).Trim();
+ HashAlgorithmName algorithmName;
+
+ switch (command.Substring(3))
+ {
+ case "1":
+ algorithmName = HashAlgorithmName.SHA1;
+ break;
+
+ case "256":
+ algorithmName = HashAlgorithmName.SHA256;
+ break;
+
+ case "384":
+ algorithmName = HashAlgorithmName.SHA384;
+ break;
+
+ case "512":
+ algorithmName = HashAlgorithmName.SHA512;
+ break;
+ default:
+ throw new ArgumentException("Unknown SHA variant. Supported variants: SHA1, SHA256, SHA384, SHA512");
+ }
+
+ if (content == string.Empty)
+ {
+ throw new FormatException("Empty hash request");
+ }
+
+ Log.Debug($"Will calculate {algorithmName} hash for: {content}", GetType());
+ request = new HashRequest(algorithmName, Encoding.UTF8.GetBytes(content));
+ }
+ else if (command.StartsWith("guid", StringComparison.InvariantCultureIgnoreCase) ||
+ command.StartsWith("uuid", StringComparison.InvariantCultureIgnoreCase))
+ {
+ string content = query.Search.Substring(command.Length).Trim();
+
+ // Default to version 4
+ int version = 4;
+ string versionQuery = command.Substring(4);
+
+ if (versionQuery.Length > 0)
+ {
+ if (versionQuery.StartsWith("v", StringComparison.InvariantCultureIgnoreCase))
+ {
+ versionQuery = versionQuery.Substring(1);
+ }
+
+ if (!int.TryParse(versionQuery, null, out version))
+ {
+ throw new ArgumentException("Could not determine requested GUID version");
+ }
+ }
+
+ if (version == 3 || version == 5)
+ {
+ string[] sParameters = content.Split(" ");
+
+ if (sParameters.Length != 2)
+ {
+ throw new ArgumentException("GUID versions 3 and 5 require 2 parameters - a namespace GUID and a name");
+ }
+
+ string namespaceParameter = sParameters[0];
+ string nameParameter = sParameters[1];
+
+ request = new GUIDRequest(version, namespaceParameter, nameParameter);
+ }
+ else
+ {
+ request = new GUIDRequest(version);
+ }
+ }
+ else if (command.ToLower(null) == "base64")
+ {
+ int commandIndex = query.RawUserQuery.IndexOf(command, StringComparison.InvariantCultureIgnoreCase);
+ string content = query.RawUserQuery.Substring(commandIndex + command.Length).Trim();
+ request = new Base64Request(Encoding.UTF8.GetBytes(content));
+ }
+ else
+ {
+ throw new FormatException($"Invalid Query: {query.RawUserQuery}");
+ }
+
+ return request;
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Main.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Main.cs
new file mode 100644
index 0000000000..50d304c8d6
--- /dev/null
+++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Main.cs
@@ -0,0 +1,156 @@
+// 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.Runtime.InteropServices;
+using System.Threading;
+using System.Windows;
+using Community.PowerToys.Run.Plugin.ValueGenerator.Properties;
+using ManagedCommon;
+using Wox.Plugin;
+using Wox.Plugin.Logger;
+
+namespace Community.PowerToys.Run.Plugin.ValueGenerator
+{
+ public class Main : IPlugin, IPluginI18n, IDisposable
+ {
+ public string Name => Resources.plugin_name;
+
+ public string Description => Resources.plugin_description;
+
+ private PluginInitContext _context;
+ private static bool _isLightTheme = true;
+ private bool _disposed;
+ private static InputParser _inputParser = new InputParser();
+
+ public void Init(PluginInitContext context)
+ {
+ context = context ?? throw new ArgumentNullException(paramName: nameof(context));
+
+ _context = context;
+ _context.API.ThemeChanged += OnThemeChanged;
+ UpdateIconPath(_context.API.GetCurrentTheme());
+ }
+
+ public void Dispose()
+ {
+ Dispose(disposing: true);
+ GC.SuppressFinalize(this);
+ }
+
+ private void OnThemeChanged(Theme currentTheme, Theme newTheme)
+ {
+ UpdateIconPath(newTheme);
+ }
+
+ private static void UpdateIconPath(Theme theme)
+ {
+ if (theme == Theme.Light || theme == Theme.HighContrastWhite)
+ {
+ _isLightTheme = true;
+ }
+ else
+ {
+ _isLightTheme = false;
+ }
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!_disposed)
+ {
+ if (disposing)
+ {
+ if (_context != null && _context.API != null)
+ {
+ _context.API.ThemeChanged -= OnThemeChanged;
+ }
+
+ _disposed = true;
+ }
+ }
+ }
+
+ public string GetTranslatedPluginDescription()
+ {
+ return Resources.plugin_description;
+ }
+
+ public string GetTranslatedPluginTitle()
+ {
+ return Resources.plugin_name;
+ }
+
+ public List Query(Query query)
+ {
+ if (query == null)
+ {
+ throw new ArgumentNullException(paramName: nameof(query));
+ }
+
+ var results = new List();
+ try
+ {
+ IComputeRequest computeRequest = _inputParser.ParseInput(query);
+ results.Add(GetResult(computeRequest));
+ }
+ catch (ArgumentException e)
+ {
+ results.Add(GetErrorResult(e.Message));
+ }
+ catch (FormatException e)
+ {
+ Log.Debug(GetTranslatedPluginTitle() + ": " + e.Message, GetType());
+ }
+
+ return results;
+ }
+
+ private Result GetResult(IComputeRequest request)
+ {
+ request.Compute();
+
+ return new Result
+ {
+ ContextData = request.Result,
+ Title = request.ResultToString(),
+ IcoPath = _isLightTheme ? "Images/ValueGenerator.dark.png" : "Images/ValueGenerator.light.png",
+ Score = 300,
+ SubTitle = request.Description,
+ Action = c =>
+ {
+ var ret = false;
+ var thread = new Thread(() =>
+ {
+ try
+ {
+ Clipboard.SetText(request.ResultToString());
+ ret = true;
+ }
+ catch (ExternalException)
+ {
+ MessageBox.Show(Properties.Resources.copy_failed);
+ }
+ });
+ thread.SetApartmentState(ApartmentState.STA);
+ thread.Start();
+ thread.Join();
+ return ret;
+ },
+ };
+ }
+
+ private Result GetErrorResult(string errorMessage)
+ {
+ return new Result
+ {
+ Title = Resources.error_title,
+ SubTitle = errorMessage,
+ IcoPath = _isLightTheme ? "Images/Warning.light.png" : "Images/Warning.dark.png",
+ Action = _ => { return true; },
+ };
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Properties/Resources.Designer.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Properties/Resources.Designer.cs
new file mode 100644
index 0000000000..88e011f1bf
--- /dev/null
+++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Properties/Resources.Designer.cs
@@ -0,0 +1,108 @@
+//------------------------------------------------------------------------------
+//
+// 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 Community.PowerToys.Run.Plugin.ValueGenerator.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", "17.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ public 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)]
+ public static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Community.PowerToys.Run.Plugin.ValueGenerator.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)]
+ public static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Copy failed.
+ ///
+ public static string copy_failed {
+ get {
+ return ResourceManager.GetString("copy_failed", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Copy {0} to clipboard.
+ ///
+ public static string copy_to_clipboard {
+ get {
+ return ResourceManager.GetString("copy_to_clipboard", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Value Generator Error.
+ ///
+ public static string error_title {
+ get {
+ return ResourceManager.GetString("error_title", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to A plugin to calculate hashes and generate values..
+ ///
+ public static string plugin_description {
+ get {
+ return ResourceManager.GetString("plugin_description", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Value Generator.
+ ///
+ public static string plugin_name {
+ get {
+ return ResourceManager.GetString("plugin_name", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Properties/Resources.resx b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Properties/Resources.resx
new file mode 100644
index 0000000000..5c07f6d5b8
--- /dev/null
+++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Properties/Resources.resx
@@ -0,0 +1,135 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 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
+
+
+ Copy failed
+
+
+ Copy {0} to clipboard
+
+
+ Value Generator Error
+
+
+ A plugin to calculate hashes and generate values.
+
+
+ Value Generator
+
+
\ No newline at end of file
diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/plugin.json b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/plugin.json
new file mode 100644
index 0000000000..085efe787b
--- /dev/null
+++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/plugin.json
@@ -0,0 +1,14 @@
+{
+ "ID": "a26b1bb4dbd911edafa10242ac120002",
+ "ActionKeyword": "#",
+ "isGlobal": false,
+ "Name": "ValueGenerator",
+ "Description": "A plugin to calculate hashes and generate values",
+ "Author": "IHorvalds",
+ "Version": "1.0.0",
+ "Language": "csharp",
+ "Website": "https://github/com/IHorvalds",
+ "IcoPathDark": "Images\\ValueGenerator.dark.png",
+ "IcoPathLight": "Images\\ValueGenerator.light.png",
+ "ExecuteFileName": "Community.PowerToys.Run.Plugin.ValueGenerator.dll"
+}
\ No newline at end of file
diff --git a/src/modules/launcher/Wox.Plugin/Common/Win32/NativeMethods.cs b/src/modules/launcher/Wox.Plugin/Common/Win32/NativeMethods.cs
index 432d7ea5f5..4e34eac2cc 100644
--- a/src/modules/launcher/Wox.Plugin/Common/Win32/NativeMethods.cs
+++ b/src/modules/launcher/Wox.Plugin/Common/Win32/NativeMethods.cs
@@ -118,6 +118,9 @@ namespace Wox.Plugin.Common.Win32
[DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern int SHCreateItemFromParsingName([MarshalAs(UnmanagedType.LPWStr)] string path, IntPtr pbc, ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out IShellItem shellItem);
+
+ [DllImport("rpcrt4.dll")]
+ public static extern int UuidCreateSequential(out GUIDDATA Uuid);
}
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "These are the names used by win32.")]
@@ -143,6 +146,16 @@ namespace Wox.Plugin.Common.Win32
/// Closes the window
///
public const int SC_CLOSE = 0xF060;
+
+ ///
+ /// RPC call succeeded
+ ///
+ public const int RPC_S_OK = 0;
+
+ ///
+ /// The UUID is guaranteed to be unique to this computer only.
+ ///
+ public const int RPC_S_UUID_LOCAL_ONLY = 0x720;
}
public static class ShellItemTypeConstants
@@ -618,6 +631,16 @@ namespace Wox.Plugin.Common.Win32
AllAccess = StandardRightsRequired | Synchronize | 0xFFFF,
}
+ [StructLayout(LayoutKind.Sequential)]
+ public struct GUIDDATA
+ {
+ public int Data1;
+ public short Data2;
+ public short Data3;
+ [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst = 8)]
+ public byte[] Data4;
+ }
+
///
/// Contains information about the placement of a window on the screen.
///
diff --git a/src/modules/launcher/Wox.Plugin/Query.cs b/src/modules/launcher/Wox.Plugin/Query.cs
index b4752e2892..c8bc934508 100644
--- a/src/modules/launcher/Wox.Plugin/Query.cs
+++ b/src/modules/launcher/Wox.Plugin/Query.cs
@@ -44,6 +44,18 @@ namespace Wox.Plugin
}
}
+ ///
+ /// Gets the query as entered by the user.
+ /// You should only use this property if you need to process the raw text directly.
+ ///
+ public string RawUserQuery
+ {
+ get
+ {
+ return _query;
+ }
+ }
+
private string _search;
///