mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-07-05 01:50:26 +02:00
Compare commits
40 Commits
shawn/Pyth
...
dev/vanzue
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c897105b42 | ||
|
|
29858c6782 | ||
|
|
aa0dd19b64 | ||
|
|
39afe4f196 | ||
|
|
5b6645ac27 | ||
|
|
c67f5d52f1 | ||
|
|
ce7cfee3f3 | ||
|
|
b354f56a72 | ||
|
|
737b092022 | ||
|
|
8c5fb85abb | ||
|
|
098244d7f2 | ||
|
|
8b94d8c233 | ||
|
|
9f1caa5ff2 | ||
|
|
fdc61e5dff | ||
|
|
dbc30045b5 | ||
|
|
a7587a9af1 | ||
|
|
fe236fb20c | ||
|
|
78df0c6b9c | ||
|
|
5fb5c91703 | ||
|
|
e120eca6c9 | ||
|
|
591371bbba | ||
|
|
0bce23fff0 | ||
|
|
4bdb9de7d6 | ||
|
|
75354e676e | ||
|
|
95294419ba | ||
|
|
a3a07e3a0e | ||
|
|
ac38d9abaf | ||
|
|
210124510a | ||
|
|
7f055d11b5 | ||
|
|
f891ca2f27 | ||
|
|
f6b97cc16d | ||
|
|
56611d07ff | ||
|
|
278667834f | ||
|
|
bdf3bff18d | ||
|
|
a100d9b352 | ||
|
|
7f5b5d57ad | ||
|
|
63b6ccc2c5 | ||
|
|
c005996b0c | ||
|
|
0303d59d86 | ||
|
|
dee6a62a68 |
1
.github/actions/spell-check/expect.txt
vendored
1
.github/actions/spell-check/expect.txt
vendored
@@ -1801,6 +1801,7 @@ tlb
|
|||||||
tlbimp
|
tlbimp
|
||||||
tlc
|
tlc
|
||||||
tmain
|
tmain
|
||||||
|
tml
|
||||||
TNP
|
TNP
|
||||||
Toolhelp
|
Toolhelp
|
||||||
toolwindow
|
toolwindow
|
||||||
|
|||||||
@@ -118,7 +118,7 @@
|
|||||||
<MSBuildCacheIgnoredInputPatterns>$(MSBuildCacheIgnoredInputPatterns);$(PackagesConfigFile)</MSBuildCacheIgnoredInputPatterns>
|
<MSBuildCacheIgnoredInputPatterns>$(MSBuildCacheIgnoredInputPatterns);$(PackagesConfigFile)</MSBuildCacheIgnoredInputPatterns>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(MSBuildCacheEnabled)' == 'true' and '$(MSBuildCachePackageRoot)' == ''">
|
<PropertyGroup Condition="'$(MSBuildCacheEnabled)' == 'true' AND '$(MSBuildCachePackageRoot)' == ''">
|
||||||
<PackagesConfigContents>$([System.IO.File]::ReadAllText("$(PackagesConfigFile)"))</PackagesConfigContents>
|
<PackagesConfigContents>$([System.IO.File]::ReadAllText("$(PackagesConfigFile)"))</PackagesConfigContents>
|
||||||
<MSBuildCachePackageVersion>$([System.Text.RegularExpressions.Regex]::Match($(PackagesConfigContents), 'Microsoft.MSBuildCache.*?version="(.*?)"').Groups[1].Value)</MSBuildCachePackageVersion>
|
<MSBuildCachePackageVersion>$([System.Text.RegularExpressions.Regex]::Match($(PackagesConfigContents), 'Microsoft.MSBuildCache.*?version="(.*?)"').Groups[1].Value)</MSBuildCachePackageVersion>
|
||||||
<MSBuildCachePackageRoot>$(MSBuildThisFileDirectory)packages\$(MSBuildCachePackageName).$(MSBuildCachePackageVersion)</MSBuildCachePackageRoot>
|
<MSBuildCachePackageRoot>$(MSBuildThisFileDirectory)packages\$(MSBuildCachePackageName).$(MSBuildCachePackageVersion)</MSBuildCachePackageRoot>
|
||||||
|
|||||||
@@ -82,6 +82,7 @@
|
|||||||
<Project Path="src/common/Telemetry/EtwTrace/EtwTrace.vcxproj" Id="8f021b46-362b-485c-bfba-ccf83e820cbd" />
|
<Project Path="src/common/Telemetry/EtwTrace/EtwTrace.vcxproj" Id="8f021b46-362b-485c-bfba-ccf83e820cbd" />
|
||||||
</Folder>
|
</Folder>
|
||||||
<Folder Name="/common/utils/">
|
<Folder Name="/common/utils/">
|
||||||
|
<Project Path="src/common/utils/utils.vcxproj" Id="e8470e15-88c4-4c4b-b872-7f1a8f8e7d7e" />
|
||||||
<File Path="src/common/utils/appMutex.h" />
|
<File Path="src/common/utils/appMutex.h" />
|
||||||
<File Path="src/common/utils/color.h" />
|
<File Path="src/common/utils/color.h" />
|
||||||
<File Path="src/common/utils/com_object_factory.h" />
|
<File Path="src/common/utils/com_object_factory.h" />
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
</Project>
|
</Project>
|
||||||
<Project Path="../src/common/Telemetry/EtwTrace/EtwTrace.vcxproj" Id="8f021b46-362b-485c-bfba-ccf83e820cbd" />
|
<Project Path="../src/common/Telemetry/EtwTrace/EtwTrace.vcxproj" Id="8f021b46-362b-485c-bfba-ccf83e820cbd" />
|
||||||
<Project Path="../src/common/version/version.vcxproj" Id="cc6e41ac-8174-4e8a-8d22-85dd7f4851df" />
|
<Project Path="../src/common/version/version.vcxproj" Id="cc6e41ac-8174-4e8a-8d22-85dd7f4851df" />
|
||||||
|
<Project Path="../src/common/utils/utils.vcxproj" Id="e8470e15-88c4-4c4b-b872-7f1a8f8e7d7e" />
|
||||||
<Project Path="../src/logging/logging.vcxproj" Id="7e1e3f13-2bd6-3f75-a6a7-873a2b55c60f">
|
<Project Path="../src/logging/logging.vcxproj" Id="7e1e3f13-2bd6-3f75-a6a7-873a2b55c60f">
|
||||||
<Build Solution="Debug|ARM64" Project="false" />
|
<Build Solution="Debug|ARM64" Project="false" />
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -162,6 +162,9 @@
|
|||||||
<ProjectReference Include="..\..\src\common\Telemetry\EtwTrace\EtwTrace.vcxproj">
|
<ProjectReference Include="..\..\src\common\Telemetry\EtwTrace\EtwTrace.vcxproj">
|
||||||
<Project>{8f021b46-362b-485c-bfba-ccf83e820cbd}</Project>
|
<Project>{8f021b46-362b-485c-bfba-ccf83e820cbd}</Project>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\..\src\common\utils\utils.vcxproj">
|
||||||
|
<Project>{e8470e15-88c4-4c4b-b872-7f1a8f8e7d7e}</Project>
|
||||||
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
<ImportGroup Label="ExtensionTargets">
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
|||||||
@@ -47,6 +47,9 @@
|
|||||||
<ProjectReference Include="..\common\SettingsAPI\SettingsAPI.vcxproj">
|
<ProjectReference Include="..\common\SettingsAPI\SettingsAPI.vcxproj">
|
||||||
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\common\utils\utils.vcxproj">
|
||||||
|
<Project>{e8470e15-88c4-4c4b-b872-7f1a8f8e7d7e}</Project>
|
||||||
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="resource.h" />
|
<ClInclude Include="resource.h" />
|
||||||
|
|||||||
@@ -53,6 +53,9 @@
|
|||||||
<ProjectReference Include="..\common\updating\updating.vcxproj">
|
<ProjectReference Include="..\common\updating\updating.vcxproj">
|
||||||
<Project>{17da04df-e393-4397-9cf0-84dabe11032e}</Project>
|
<Project>{17da04df-e393-4397-9cf0-84dabe11032e}</Project>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\common\utils\utils.vcxproj">
|
||||||
|
<Project>{e8470e15-88c4-4c4b-b872-7f1a8f8e7d7e}</Project>
|
||||||
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="resource.h" />
|
<ClInclude Include="resource.h" />
|
||||||
|
|||||||
@@ -113,6 +113,9 @@
|
|||||||
<ProjectReference Include="..\..\common\version\version.vcxproj">
|
<ProjectReference Include="..\..\common\version\version.vcxproj">
|
||||||
<Project>{cc6e41ac-8174-4e8a-8d22-85dd7f4851df}</Project>
|
<Project>{cc6e41ac-8174-4e8a-8d22-85dd7f4851df}</Project>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\..\common\utils\utils.vcxproj">
|
||||||
|
<Project>{e8470e15-88c4-4c4b-b872-7f1a8f8e7d7e}</Project>
|
||||||
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
<ImportGroup Label="ExtensionTargets">
|
<ImportGroup Label="ExtensionTargets">
|
||||||
@@ -125,4 +128,4 @@
|
|||||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
<Error Condition="!Exists('..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
<Error Condition="!Exists('..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||||
</Target>
|
</Target>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,7 +1,24 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<Import Project="..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
|
<Import Project="..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||||
|
<ItemGroup Label="ProjectConfigurations">
|
||||||
|
<ProjectConfiguration Include="Debug|x64">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|x64">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Debug|ARM64">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>ARM64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|ARM64">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>ARM64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Globals">
|
<PropertyGroup Label="Globals">
|
||||||
<VCProjectVersion>16.0</VCProjectVersion>
|
<VCProjectVersion>16.0</VCProjectVersion>
|
||||||
<ProjectGuid>{6955446D-23F7-4023-9BB3-8657F904AF99}</ProjectGuid>
|
<ProjectGuid>{6955446D-23F7-4023-9BB3-8657F904AF99}</ProjectGuid>
|
||||||
@@ -50,6 +67,9 @@
|
|||||||
<ProjectReference Include="..\logger\logger.vcxproj">
|
<ProjectReference Include="..\logger\logger.vcxproj">
|
||||||
<Project>{d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}</Project>
|
<Project>{d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}</Project>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\utils\utils.vcxproj">
|
||||||
|
<Project>{e8470e15-88c4-4c4b-b872-7f1a8f8e7d7e}</Project>
|
||||||
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
<Import Project="..\..\..\deps\spdlog.props" />
|
<Import Project="..\..\..\deps\spdlog.props" />
|
||||||
@@ -65,4 +85,4 @@
|
|||||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
<Error Condition="!Exists('..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
<Error Condition="!Exists('..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||||
</Target>
|
</Target>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -168,6 +168,9 @@
|
|||||||
<ProjectReference Include="..\version\version.vcxproj">
|
<ProjectReference Include="..\version\version.vcxproj">
|
||||||
<Project>{cc6e41ac-8174-4e8a-8d22-85dd7f4851df}</Project>
|
<Project>{cc6e41ac-8174-4e8a-8d22-85dd7f4851df}</Project>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\utils\utils.vcxproj">
|
||||||
|
<Project>{e8470e15-88c4-4c4b-b872-7f1a8f8e7d7e}</Project>
|
||||||
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ImportGroup Label="ExtensionTargets" />
|
<ImportGroup Label="ExtensionTargets" />
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
|||||||
62
src/common/utils/EventLocker.cpp
Normal file
62
src/common/utils/EventLocker.cpp
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "EventLocker.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
EventLocker::EventLocker(HANDLE handle) :
|
||||||
|
eventHandle(handle)
|
||||||
|
{
|
||||||
|
if (eventHandle)
|
||||||
|
{
|
||||||
|
SetEvent(eventHandle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<EventLocker> EventLocker::Get(std::wstring eventName)
|
||||||
|
{
|
||||||
|
EventLocker locker(std::move(eventName));
|
||||||
|
if (!locker.eventHandle)
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::optional<EventLocker>(std::move(locker));
|
||||||
|
}
|
||||||
|
|
||||||
|
EventLocker::EventLocker(EventLocker&& other) noexcept :
|
||||||
|
eventHandle(other.eventHandle)
|
||||||
|
{
|
||||||
|
other.eventHandle = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
EventLocker& EventLocker::operator=(EventLocker&& other) noexcept
|
||||||
|
{
|
||||||
|
if (this != &other)
|
||||||
|
{
|
||||||
|
eventHandle = other.eventHandle;
|
||||||
|
other.eventHandle = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
EventLocker::~EventLocker()
|
||||||
|
{
|
||||||
|
if (eventHandle)
|
||||||
|
{
|
||||||
|
ResetEvent(eventHandle);
|
||||||
|
CloseHandle(eventHandle);
|
||||||
|
eventHandle = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EventLocker::EventLocker(std::wstring eventName)
|
||||||
|
{
|
||||||
|
eventHandle = CreateEvent(nullptr, true, false, eventName.c_str());
|
||||||
|
if (!eventHandle)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetEvent(eventHandle);
|
||||||
|
}
|
||||||
@@ -1,61 +1,26 @@
|
|||||||
#include <windows.h>
|
#pragma once
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
class EventLocker
|
class EventLocker
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
EventLocker(HANDLE h)
|
explicit EventLocker(HANDLE handle);
|
||||||
{
|
|
||||||
eventHandle = h;
|
|
||||||
SetEvent(eventHandle);
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::optional<EventLocker> Get(std::wstring eventName)
|
static std::optional<EventLocker> Get(std::wstring eventName);
|
||||||
{
|
|
||||||
EventLocker locker(eventName);
|
|
||||||
if (!locker.eventHandle)
|
|
||||||
{
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
return locker;
|
EventLocker(EventLocker&) = delete;
|
||||||
}
|
EventLocker& operator=(EventLocker&) = delete;
|
||||||
|
|
||||||
EventLocker(EventLocker& e) = delete;
|
EventLocker(EventLocker&& other) noexcept;
|
||||||
EventLocker& operator=(EventLocker& e) = delete;
|
EventLocker& operator=(EventLocker&& other) noexcept;
|
||||||
|
|
||||||
EventLocker(EventLocker&& e) noexcept
|
~EventLocker();
|
||||||
{
|
|
||||||
this->eventHandle = e.eventHandle;
|
|
||||||
e.eventHandle = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
EventLocker& operator=(EventLocker&& e) noexcept
|
|
||||||
{
|
|
||||||
this->eventHandle = e.eventHandle;
|
|
||||||
e.eventHandle = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
~EventLocker()
|
|
||||||
{
|
|
||||||
if (eventHandle)
|
|
||||||
{
|
|
||||||
ResetEvent(eventHandle);
|
|
||||||
CloseHandle(eventHandle);
|
|
||||||
eventHandle = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private:
|
private:
|
||||||
EventLocker(std::wstring eventName)
|
explicit EventLocker(std::wstring eventName);
|
||||||
{
|
|
||||||
eventHandle = CreateEvent(nullptr, true, false, eventName.c_str());
|
|
||||||
if (!eventHandle)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SetEvent(eventHandle);
|
HANDLE eventHandle = nullptr;
|
||||||
}
|
|
||||||
|
|
||||||
HANDLE eventHandle;
|
|
||||||
};
|
};
|
||||||
|
|||||||
71
src/common/utils/EventWaiter.cpp
Normal file
71
src/common/utils/EventWaiter.cpp
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "EventWaiter.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
EventWaiter::EventWaiter(const std::wstring& name, std::function<void(DWORD)> callback)
|
||||||
|
{
|
||||||
|
// Create localExitThreadEvent and localWaitingEvent for capturing. We cannot capture 'this' as we implement move constructor.
|
||||||
|
const auto localExitThreadEvent = exitThreadEvent = CreateEvent(nullptr, false, false, nullptr);
|
||||||
|
const HANDLE localWaitingEvent = waitingEvent = CreateEvent(nullptr, false, false, name.c_str());
|
||||||
|
|
||||||
|
std::thread([=]() {
|
||||||
|
HANDLE events[2] = { localWaitingEvent, localExitThreadEvent };
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
const auto waitResult = WaitForMultipleObjects(2, events, false, INFINITE);
|
||||||
|
if (waitResult == WAIT_OBJECT_0 + 1)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (waitResult == WAIT_FAILED)
|
||||||
|
{
|
||||||
|
callback(GetLastError());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (waitResult == WAIT_OBJECT_0)
|
||||||
|
{
|
||||||
|
callback(ERROR_SUCCESS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
EventWaiter::EventWaiter(EventWaiter&& other) noexcept :
|
||||||
|
exitThreadEvent(other.exitThreadEvent),
|
||||||
|
waitingEvent(other.waitingEvent)
|
||||||
|
{
|
||||||
|
other.exitThreadEvent = nullptr;
|
||||||
|
other.waitingEvent = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
EventWaiter& EventWaiter::operator=(EventWaiter&& other) noexcept
|
||||||
|
{
|
||||||
|
if (this != &other)
|
||||||
|
{
|
||||||
|
exitThreadEvent = other.exitThreadEvent;
|
||||||
|
waitingEvent = other.waitingEvent;
|
||||||
|
other.exitThreadEvent = nullptr;
|
||||||
|
other.waitingEvent = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
EventWaiter::~EventWaiter()
|
||||||
|
{
|
||||||
|
if (exitThreadEvent)
|
||||||
|
{
|
||||||
|
SetEvent(exitThreadEvent);
|
||||||
|
CloseHandle(exitThreadEvent);
|
||||||
|
exitThreadEvent = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (waitingEvent)
|
||||||
|
{
|
||||||
|
CloseHandle(waitingEvent);
|
||||||
|
waitingEvent = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,73 +8,15 @@
|
|||||||
class EventWaiter
|
class EventWaiter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
EventWaiter() {}
|
EventWaiter() = default;
|
||||||
EventWaiter(const std::wstring& name, std::function<void(DWORD)> callback)
|
EventWaiter(const std::wstring& name, std::function<void(DWORD)> callback);
|
||||||
{
|
|
||||||
// Create localExitThreadEvent and localWaitingEvent for capturing. We cannot capture 'this' as we implement move constructor.
|
|
||||||
auto localExitThreadEvent = exitThreadEvent = CreateEvent(nullptr, false, false, nullptr);
|
|
||||||
HANDLE localWaitingEvent = waitingEvent = CreateEvent(nullptr, false, false, name.c_str());
|
|
||||||
std::thread([=]() {
|
|
||||||
HANDLE events[2] = { localWaitingEvent, localExitThreadEvent };
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
auto waitResult = WaitForMultipleObjects(2, events, false, INFINITE);
|
|
||||||
if (waitResult == WAIT_OBJECT_0 + 1)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (waitResult == WAIT_FAILED)
|
|
||||||
{
|
|
||||||
callback(GetLastError());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (waitResult == WAIT_OBJECT_0)
|
|
||||||
{
|
|
||||||
callback(ERROR_SUCCESS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).detach();
|
|
||||||
}
|
|
||||||
|
|
||||||
EventWaiter(EventWaiter&) = delete;
|
EventWaiter(EventWaiter&) = delete;
|
||||||
EventWaiter& operator=(EventWaiter&) = delete;
|
EventWaiter& operator=(EventWaiter&) = delete;
|
||||||
|
EventWaiter(EventWaiter&& other) noexcept;
|
||||||
EventWaiter(EventWaiter&& a) noexcept
|
EventWaiter& operator=(EventWaiter&& other) noexcept;
|
||||||
{
|
~EventWaiter();
|
||||||
this->exitThreadEvent = a.exitThreadEvent;
|
|
||||||
this->waitingEvent = a.waitingEvent;
|
|
||||||
|
|
||||||
a.exitThreadEvent = nullptr;
|
|
||||||
a.waitingEvent = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
EventWaiter& operator=(EventWaiter&& a) noexcept
|
|
||||||
{
|
|
||||||
this->exitThreadEvent = a.exitThreadEvent;
|
|
||||||
this->waitingEvent = a.waitingEvent;
|
|
||||||
|
|
||||||
a.exitThreadEvent = nullptr;
|
|
||||||
a.waitingEvent = nullptr;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
~EventWaiter()
|
|
||||||
{
|
|
||||||
if (exitThreadEvent)
|
|
||||||
{
|
|
||||||
SetEvent(exitThreadEvent);
|
|
||||||
CloseHandle(exitThreadEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (waitingEvent)
|
|
||||||
{
|
|
||||||
CloseHandle(waitingEvent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
HANDLE exitThreadEvent = nullptr;
|
HANDLE exitThreadEvent = nullptr;
|
||||||
HANDLE waitingEvent = nullptr;
|
HANDLE waitingEvent = nullptr;
|
||||||
};
|
};
|
||||||
|
|||||||
60
src/common/utils/HDropIterator.cpp
Normal file
60
src/common/utils/HDropIterator.cpp
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "HDropIterator.h"
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
HDropIterator::HDropIterator(IDataObject* dataObject)
|
||||||
|
{
|
||||||
|
FORMATETC formatetc{
|
||||||
|
CF_HDROP,
|
||||||
|
nullptr,
|
||||||
|
DVASPECT_CONTENT,
|
||||||
|
-1,
|
||||||
|
TYMED_HGLOBAL
|
||||||
|
};
|
||||||
|
|
||||||
|
if (dataObject && SUCCEEDED(dataObject->GetData(&formatetc, &m_medium)))
|
||||||
|
{
|
||||||
|
_listCount = DragQueryFile(static_cast<HDROP>(m_medium.hGlobal), 0xFFFFFFFF, nullptr, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_medium = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HDropIterator::~HDropIterator()
|
||||||
|
{
|
||||||
|
if (m_medium.tymed)
|
||||||
|
{
|
||||||
|
ReleaseStgMedium(&m_medium);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HDropIterator::First()
|
||||||
|
{
|
||||||
|
_current = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HDropIterator::Next()
|
||||||
|
{
|
||||||
|
++_current;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HDropIterator::IsDone() const
|
||||||
|
{
|
||||||
|
return _current >= _listCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
LPTSTR HDropIterator::CurrentItem() const
|
||||||
|
{
|
||||||
|
const UINT cch = DragQueryFile(static_cast<HDROP>(m_medium.hGlobal), _current, nullptr, 0) + 1;
|
||||||
|
LPTSTR path = static_cast<LPTSTR>(malloc(sizeof(TCHAR) * cch));
|
||||||
|
if (!path)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
DragQueryFile(static_cast<HDROP>(m_medium.hGlobal), _current, path, cch);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
@@ -1,67 +1,21 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <objidl.h>
|
||||||
#include <shellapi.h>
|
#include <shellapi.h>
|
||||||
|
|
||||||
class HDropIterator
|
class HDropIterator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
HDropIterator(IDataObject* pDataObject)
|
explicit HDropIterator(IDataObject* dataObject);
|
||||||
{
|
~HDropIterator();
|
||||||
_current = 0;
|
|
||||||
_listCount = 0;
|
|
||||||
|
|
||||||
FORMATETC formatetc = {
|
void First();
|
||||||
CF_HDROP,
|
void Next();
|
||||||
NULL,
|
[[nodiscard]] bool IsDone() const;
|
||||||
DVASPECT_CONTENT,
|
[[nodiscard]] LPTSTR CurrentItem() const;
|
||||||
-1,
|
|
||||||
TYMED_HGLOBAL
|
|
||||||
};
|
|
||||||
|
|
||||||
if (SUCCEEDED(pDataObject->GetData(&formatetc, &m_medium)))
|
|
||||||
{
|
|
||||||
_listCount = DragQueryFile(static_cast<HDROP>(m_medium.hGlobal), 0xFFFFFFFF, NULL, 0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_medium = {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
~HDropIterator()
|
|
||||||
{
|
|
||||||
if (m_medium.tymed)
|
|
||||||
{
|
|
||||||
ReleaseStgMedium(&m_medium);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void First()
|
|
||||||
{
|
|
||||||
_current = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Next()
|
|
||||||
{
|
|
||||||
_current++;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsDone() const
|
|
||||||
{
|
|
||||||
return _current >= _listCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
LPTSTR CurrentItem() const
|
|
||||||
{
|
|
||||||
UINT cch = DragQueryFile(static_cast<HDROP>(m_medium.hGlobal), _current, NULL, 0) + 1;
|
|
||||||
LPTSTR pszPath = static_cast<LPTSTR>(malloc(sizeof(TCHAR) * cch));
|
|
||||||
|
|
||||||
DragQueryFile(static_cast<HDROP>(m_medium.hGlobal), _current, pszPath, cch);
|
|
||||||
|
|
||||||
return pszPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
UINT _listCount;
|
UINT _listCount = 0;
|
||||||
STGMEDIUM m_medium;
|
STGMEDIUM m_medium{};
|
||||||
UINT _current;
|
UINT _current = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
73
src/common/utils/HttpClient.cpp
Normal file
73
src/common/utils/HttpClient.cpp
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "HttpClient.h"
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
HttpClient::HttpClient()
|
||||||
|
{
|
||||||
|
auto headers = m_client.DefaultRequestHeaders();
|
||||||
|
headers.UserAgent().TryParseAdd(USER_AGENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::future<std::wstring> HttpClient::request(const winrt::Windows::Foundation::Uri& url)
|
||||||
|
{
|
||||||
|
auto response = co_await m_client.GetAsync(url);
|
||||||
|
(void)response.EnsureSuccessStatusCode();
|
||||||
|
auto body = co_await response.Content().ReadAsStringAsync();
|
||||||
|
co_return std::wstring(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::future<void> HttpClient::download(const winrt::Windows::Foundation::Uri& url, const std::wstring& dstFilePath)
|
||||||
|
{
|
||||||
|
auto response = co_await m_client.GetAsync(url);
|
||||||
|
(void)response.EnsureSuccessStatusCode();
|
||||||
|
auto fileStream = co_await storage::Streams::FileRandomAccessStream::OpenAsync(
|
||||||
|
dstFilePath.c_str(),
|
||||||
|
storage::FileAccessMode::ReadWrite,
|
||||||
|
storage::StorageOpenOptions::AllowReadersAndWriters,
|
||||||
|
storage::Streams::FileOpenDisposition::CreateAlways);
|
||||||
|
co_await response.Content().WriteToStreamAsync(fileStream);
|
||||||
|
fileStream.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::future<void> HttpClient::download(const winrt::Windows::Foundation::Uri& url,
|
||||||
|
const std::wstring& dstFilePath,
|
||||||
|
const std::function<void(float)>& progressUpdateCallback)
|
||||||
|
{
|
||||||
|
auto response = co_await m_client.GetAsync(url, winrt::Windows::Web::Http::HttpCompletionOption::ResponseHeadersRead);
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
|
const uint64_t totalBytes = response.Content().Headers().ContentLength().GetUInt64();
|
||||||
|
auto contentStream = co_await response.Content().ReadAsInputStreamAsync();
|
||||||
|
|
||||||
|
uint64_t totalBytesRead = 0;
|
||||||
|
storage::Streams::Buffer buffer(8192);
|
||||||
|
auto fileStream = co_await storage::Streams::FileRandomAccessStream::OpenAsync(
|
||||||
|
dstFilePath.c_str(),
|
||||||
|
storage::FileAccessMode::ReadWrite,
|
||||||
|
storage::StorageOpenOptions::AllowReadersAndWriters,
|
||||||
|
storage::Streams::FileOpenDisposition::CreateAlways);
|
||||||
|
|
||||||
|
co_await contentStream.ReadAsync(buffer, buffer.Capacity(), storage::Streams::InputStreamOptions::None);
|
||||||
|
while (buffer.Length() > 0)
|
||||||
|
{
|
||||||
|
co_await fileStream.WriteAsync(buffer);
|
||||||
|
totalBytesRead += buffer.Length();
|
||||||
|
if (progressUpdateCallback)
|
||||||
|
{
|
||||||
|
const float percentage = static_cast<float>(totalBytesRead) / totalBytes;
|
||||||
|
progressUpdateCallback(percentage);
|
||||||
|
}
|
||||||
|
|
||||||
|
co_await contentStream.ReadAsync(buffer, buffer.Capacity(), storage::Streams::InputStreamOptions::None);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (progressUpdateCallback)
|
||||||
|
{
|
||||||
|
progressUpdateCallback(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fileStream.Close();
|
||||||
|
contentStream.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,63 +15,13 @@ namespace http
|
|||||||
class HttpClient
|
class HttpClient
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
HttpClient()
|
HttpClient();
|
||||||
{
|
|
||||||
auto headers = m_client.DefaultRequestHeaders();
|
|
||||||
headers.UserAgent().TryParseAdd(USER_AGENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::future<std::wstring> request(const winrt::Windows::Foundation::Uri& url)
|
std::future<std::wstring> request(const winrt::Windows::Foundation::Uri& url);
|
||||||
{
|
std::future<void> download(const winrt::Windows::Foundation::Uri& url, const std::wstring& dstFilePath);
|
||||||
auto response = co_await m_client.GetAsync(url);
|
std::future<void> download(const winrt::Windows::Foundation::Uri& url,
|
||||||
(void)response.EnsureSuccessStatusCode();
|
const std::wstring& dstFilePath,
|
||||||
auto body = co_await response.Content().ReadAsStringAsync();
|
const std::function<void(float)>& progressUpdateCallback);
|
||||||
co_return std::wstring(body);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::future<void> download(const winrt::Windows::Foundation::Uri& url, const std::wstring& dstFilePath)
|
|
||||||
{
|
|
||||||
auto response = co_await m_client.GetAsync(url);
|
|
||||||
(void)response.EnsureSuccessStatusCode();
|
|
||||||
auto file_stream = co_await storage::Streams::FileRandomAccessStream::OpenAsync(dstFilePath.c_str(), storage::FileAccessMode::ReadWrite, storage::StorageOpenOptions::AllowReadersAndWriters, storage::Streams::FileOpenDisposition::CreateAlways);
|
|
||||||
co_await response.Content().WriteToStreamAsync(file_stream);
|
|
||||||
file_stream.Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::future<void> download(const winrt::Windows::Foundation::Uri& url, const std::wstring& dstFilePath, const std::function<void(float)>& progressUpdateCallback)
|
|
||||||
{
|
|
||||||
auto response = co_await m_client.GetAsync(url, HttpCompletionOption::ResponseHeadersRead);
|
|
||||||
response.EnsureSuccessStatusCode();
|
|
||||||
|
|
||||||
uint64_t totalBytes = response.Content().Headers().ContentLength().GetUInt64();
|
|
||||||
auto contentStream = co_await response.Content().ReadAsInputStreamAsync();
|
|
||||||
|
|
||||||
uint64_t totalBytesRead = 0;
|
|
||||||
storage::Streams::Buffer buffer(8192);
|
|
||||||
auto fileStream = co_await storage::Streams::FileRandomAccessStream::OpenAsync(dstFilePath.c_str(), storage::FileAccessMode::ReadWrite, storage::StorageOpenOptions::AllowReadersAndWriters, storage::Streams::FileOpenDisposition::CreateAlways);
|
|
||||||
|
|
||||||
co_await contentStream.ReadAsync(buffer, buffer.Capacity(), storage::Streams::InputStreamOptions::None);
|
|
||||||
while (buffer.Length() > 0)
|
|
||||||
{
|
|
||||||
co_await fileStream.WriteAsync(buffer);
|
|
||||||
totalBytesRead += buffer.Length();
|
|
||||||
if (progressUpdateCallback)
|
|
||||||
{
|
|
||||||
float percentage = static_cast<float>(totalBytesRead) / totalBytes;
|
|
||||||
progressUpdateCallback(percentage);
|
|
||||||
}
|
|
||||||
|
|
||||||
co_await contentStream.ReadAsync(buffer, buffer.Capacity(), storage::Streams::InputStreamOptions::None);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (progressUpdateCallback)
|
|
||||||
{
|
|
||||||
progressUpdateCallback(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
fileStream.Close();
|
|
||||||
contentStream.Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
winrt::Windows::Web::Http::HttpClient m_client;
|
winrt::Windows::Web::Http::HttpClient m_client;
|
||||||
|
|||||||
13
src/common/utils/MsWindowsSettings.cpp
Normal file
13
src/common/utils/MsWindowsSettings.cpp
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "MsWindowsSettings.h"
|
||||||
|
|
||||||
|
bool GetAnimationsEnabled()
|
||||||
|
{
|
||||||
|
BOOL enabled = 0;
|
||||||
|
const auto result = SystemParametersInfo(SPI_GETCLIENTAREAANIMATION, 0, &enabled, 0);
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
Logger::error("SystemParametersInfo SPI_GETCLIENTAREAANIMATION failed.");
|
||||||
|
}
|
||||||
|
return enabled != 0;
|
||||||
|
}
|
||||||
@@ -1,13 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
inline bool GetAnimationsEnabled()
|
#include <common/logger/logger.h>
|
||||||
{
|
#include <windows.h>
|
||||||
BOOL enabled = 0;
|
|
||||||
BOOL fResult;
|
bool GetAnimationsEnabled();
|
||||||
fResult = SystemParametersInfo(SPI_GETCLIENTAREAANIMATION, 0, &enabled, 0);
|
|
||||||
if (!fResult)
|
|
||||||
{
|
|
||||||
Logger::error("SystemParametersInfo SPI_GETCLIENTAREAANIMATION failed.");
|
|
||||||
}
|
|
||||||
return enabled;
|
|
||||||
}
|
|
||||||
|
|||||||
83
src/common/utils/MsiUtils.cpp
Normal file
83
src/common/utils/MsiUtils.cpp
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "MsiUtils.h"
|
||||||
|
|
||||||
|
std::optional<std::wstring> GetMsiPackageInstalledPath(bool perUser)
|
||||||
|
{
|
||||||
|
constexpr size_t guid_length = 39;
|
||||||
|
wchar_t productId[guid_length];
|
||||||
|
const std::wstring upgradeCode = perUser ? POWER_TOYS_UPGRADE_CODE_USER : POWER_TOYS_UPGRADE_CODE;
|
||||||
|
if (const bool found = ERROR_SUCCESS == MsiEnumRelatedProductsW(upgradeCode.c_str(), 0, 0, productId); !found)
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const bool installed = INSTALLSTATE_DEFAULT == MsiQueryProductStateW(productId); !installed)
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD bufferSize = MAX_PATH;
|
||||||
|
wchar_t buffer[MAX_PATH];
|
||||||
|
if (ERROR_SUCCESS == MsiGetProductInfoW(productId, INSTALLPROPERTY_INSTALLLOCATION, buffer, &bufferSize) && bufferSize)
|
||||||
|
{
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD packagePathSize = 0;
|
||||||
|
|
||||||
|
if (ERROR_SUCCESS != MsiGetProductInfoW(productId, INSTALLPROPERTY_LOCALPACKAGE, nullptr, &packagePathSize))
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring packagePath(++packagePathSize, L'\0');
|
||||||
|
|
||||||
|
if (ERROR_SUCCESS != MsiGetProductInfoW(productId, INSTALLPROPERTY_LOCALPACKAGE, packagePath.data(), &packagePathSize))
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
packagePath.resize(size(packagePath) - 1); // trim additional \0 which we got from MsiGetProductInfoW
|
||||||
|
|
||||||
|
wchar_t path[MAX_PATH];
|
||||||
|
DWORD pathSize = MAX_PATH;
|
||||||
|
MsiGetComponentPathW(productId, POWERTOYS_EXE_COMPONENT, path, &pathSize);
|
||||||
|
if (!pathSize)
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
PathCchRemoveFileSpec(path, pathSize);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring GetMsiPackagePath()
|
||||||
|
{
|
||||||
|
std::wstring packagePath;
|
||||||
|
wchar_t productString[39];
|
||||||
|
if (const bool found = ERROR_SUCCESS == MsiEnumRelatedProductsW(POWER_TOYS_UPGRADE_CODE, 0, 0, productString); !found)
|
||||||
|
{
|
||||||
|
return packagePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const bool installed = INSTALLSTATE_DEFAULT == MsiQueryProductStateW(productString); !installed)
|
||||||
|
{
|
||||||
|
return packagePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD packagePathSize = 0;
|
||||||
|
|
||||||
|
if (const bool hasPackagePath = ERROR_SUCCESS == MsiGetProductInfoW(productString, INSTALLPROPERTY_LOCALPACKAGE, nullptr, &packagePathSize); !hasPackagePath)
|
||||||
|
{
|
||||||
|
return packagePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
packagePath = std::wstring(++packagePathSize, L'\0');
|
||||||
|
if (const bool gotPackagePath = ERROR_SUCCESS == MsiGetProductInfoW(productString, INSTALLPROPERTY_LOCALPACKAGE, packagePath.data(), &packagePathSize); !gotPackagePath)
|
||||||
|
{
|
||||||
|
packagePath.clear();
|
||||||
|
return packagePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
packagePath.resize(size(packagePath) - 1); // trim additional \0 which we got from MsiGetProductInfoW
|
||||||
|
|
||||||
|
return packagePath;
|
||||||
|
}
|
||||||
@@ -16,82 +16,5 @@ namespace // Strings in this namespace should not be localized
|
|||||||
const inline wchar_t POWERTOYS_EXE_COMPONENT[] = L"{A2C66D91-3485-4D00-B04D-91844E6B345B}";
|
const inline wchar_t POWERTOYS_EXE_COMPONENT[] = L"{A2C66D91-3485-4D00-B04D-91844E6B345B}";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::wstring> GetMsiPackageInstalledPath(bool perUser)
|
std::optional<std::wstring> GetMsiPackageInstalledPath(bool perUser);
|
||||||
{
|
std::wstring GetMsiPackagePath();
|
||||||
constexpr size_t guid_length = 39;
|
|
||||||
wchar_t product_ID[guid_length];
|
|
||||||
std::wstring upgradeCode = (perUser ? POWER_TOYS_UPGRADE_CODE_USER : POWER_TOYS_UPGRADE_CODE);
|
|
||||||
if (const bool found = ERROR_SUCCESS == MsiEnumRelatedProductsW(upgradeCode.c_str(), 0, 0, product_ID); !found)
|
|
||||||
{
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (const bool installed = INSTALLSTATE_DEFAULT == MsiQueryProductStateW(product_ID); !installed)
|
|
||||||
{
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD buf_size = MAX_PATH;
|
|
||||||
wchar_t buf[MAX_PATH];
|
|
||||||
if (ERROR_SUCCESS == MsiGetProductInfoW(product_ID, INSTALLPROPERTY_INSTALLLOCATION, buf, &buf_size) && buf_size)
|
|
||||||
{
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD package_path_size = 0;
|
|
||||||
|
|
||||||
if (ERROR_SUCCESS != MsiGetProductInfoW(product_ID, INSTALLPROPERTY_LOCALPACKAGE, nullptr, &package_path_size))
|
|
||||||
{
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
std::wstring package_path(++package_path_size, L'\0');
|
|
||||||
|
|
||||||
if (ERROR_SUCCESS != MsiGetProductInfoW(product_ID, INSTALLPROPERTY_LOCALPACKAGE, package_path.data(), &package_path_size))
|
|
||||||
{
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
package_path.resize(size(package_path) - 1); // trim additional \0 which we got from MsiGetProductInfoW
|
|
||||||
|
|
||||||
wchar_t path[MAX_PATH];
|
|
||||||
DWORD path_size = MAX_PATH;
|
|
||||||
MsiGetComponentPathW(product_ID, POWERTOYS_EXE_COMPONENT, path, &path_size);
|
|
||||||
if (!path_size)
|
|
||||||
{
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
PathCchRemoveFileSpec(path, path_size);
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::wstring GetMsiPackagePath()
|
|
||||||
{
|
|
||||||
std::wstring package_path;
|
|
||||||
wchar_t GUID_product_string[39];
|
|
||||||
if (const bool found = ERROR_SUCCESS == MsiEnumRelatedProductsW(POWER_TOYS_UPGRADE_CODE, 0, 0, GUID_product_string); !found)
|
|
||||||
{
|
|
||||||
return package_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (const bool installed = INSTALLSTATE_DEFAULT == MsiQueryProductStateW(GUID_product_string); !installed)
|
|
||||||
{
|
|
||||||
return package_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD package_path_size = 0;
|
|
||||||
|
|
||||||
if (const bool has_package_path = ERROR_SUCCESS == MsiGetProductInfoW(GUID_product_string, INSTALLPROPERTY_LOCALPACKAGE, nullptr, &package_path_size); !has_package_path)
|
|
||||||
{
|
|
||||||
return package_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
package_path = std::wstring(++package_path_size, L'\0');
|
|
||||||
if (const bool got_package_path = ERROR_SUCCESS == MsiGetProductInfoW(GUID_product_string, INSTALLPROPERTY_LOCALPACKAGE, package_path.data(), &package_path_size); !got_package_path)
|
|
||||||
{
|
|
||||||
package_path = {};
|
|
||||||
return package_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
package_path.resize(size(package_path) - 1); // trim additional \0 which we got from MsiGetProductInfoW
|
|
||||||
|
|
||||||
return package_path;
|
|
||||||
}
|
|
||||||
|
|||||||
57
src/common/utils/OnThreadExecutor.cpp
Normal file
57
src/common/utils/OnThreadExecutor.cpp
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "OnThreadExecutor.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
OnThreadExecutor::OnThreadExecutor() :
|
||||||
|
_worker_thread([this] { worker_thread(); })
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
OnThreadExecutor::~OnThreadExecutor()
|
||||||
|
{
|
||||||
|
_shutdown_request = true;
|
||||||
|
_task_cv.notify_one();
|
||||||
|
if (_worker_thread.joinable())
|
||||||
|
{
|
||||||
|
_worker_thread.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::future<void> OnThreadExecutor::submit(task_t task)
|
||||||
|
{
|
||||||
|
auto future = task.get_future();
|
||||||
|
{
|
||||||
|
std::lock_guard lock{ _task_mutex };
|
||||||
|
_task_queue.emplace(std::move(task));
|
||||||
|
}
|
||||||
|
_task_cv.notify_one();
|
||||||
|
return future;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnThreadExecutor::cancel()
|
||||||
|
{
|
||||||
|
std::lock_guard lock{ _task_mutex };
|
||||||
|
std::queue<task_t> emptyQueue;
|
||||||
|
std::swap(_task_queue, emptyQueue);
|
||||||
|
_task_cv.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnThreadExecutor::worker_thread()
|
||||||
|
{
|
||||||
|
while (!_shutdown_request)
|
||||||
|
{
|
||||||
|
task_t task;
|
||||||
|
{
|
||||||
|
std::unique_lock task_lock{ _task_mutex };
|
||||||
|
_task_cv.wait(task_lock, [this] { return !_task_queue.empty() || _shutdown_request; });
|
||||||
|
if (_shutdown_request)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
task = std::move(_task_queue.front());
|
||||||
|
_task_queue.pop();
|
||||||
|
}
|
||||||
|
task();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,8 @@
|
|||||||
#include <functional>
|
#include <functional>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
// OnThreadExecutor allows its caller to off-load some work to a persistently running background thread.
|
// OnThreadExecutor allows its caller to off-load some work to a persistently running background thread.
|
||||||
// This might come in handy if you use the API which sets thread-wide global state and the state needs
|
// This might come in handy if you use the API which sets thread-wide global state and the state needs
|
||||||
@@ -15,58 +17,18 @@ class OnThreadExecutor final
|
|||||||
public:
|
public:
|
||||||
using task_t = std::packaged_task<void()>;
|
using task_t = std::packaged_task<void()>;
|
||||||
|
|
||||||
OnThreadExecutor() :
|
OnThreadExecutor();
|
||||||
_shutdown_request{ false },
|
~OnThreadExecutor();
|
||||||
_worker_thread{ [this] { worker_thread(); } }
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
~OnThreadExecutor()
|
std::future<void> submit(task_t task);
|
||||||
{
|
void cancel();
|
||||||
_shutdown_request = true;
|
|
||||||
_task_cv.notify_one();
|
|
||||||
_worker_thread.join();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::future<void> submit(task_t task)
|
|
||||||
{
|
|
||||||
auto future = task.get_future();
|
|
||||||
std::lock_guard lock{ _task_mutex };
|
|
||||||
_task_queue.emplace(std::move(task));
|
|
||||||
_task_cv.notify_one();
|
|
||||||
return future;
|
|
||||||
}
|
|
||||||
|
|
||||||
void cancel()
|
|
||||||
{
|
|
||||||
std::lock_guard lock{ _task_mutex };
|
|
||||||
_task_queue = {};
|
|
||||||
_task_cv.notify_one();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void worker_thread()
|
void worker_thread();
|
||||||
{
|
|
||||||
while (!_shutdown_request)
|
|
||||||
{
|
|
||||||
task_t task;
|
|
||||||
{
|
|
||||||
std::unique_lock task_lock{ _task_mutex };
|
|
||||||
_task_cv.wait(task_lock, [this] { return !_task_queue.empty() || _shutdown_request; });
|
|
||||||
if (_shutdown_request)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
task = std::move(_task_queue.front());
|
|
||||||
_task_queue.pop();
|
|
||||||
}
|
|
||||||
task();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::mutex _task_mutex;
|
std::mutex _task_mutex;
|
||||||
std::condition_variable _task_cv;
|
std::condition_variable _task_cv;
|
||||||
std::atomic_bool _shutdown_request;
|
std::atomic_bool _shutdown_request{ false };
|
||||||
std::queue<std::packaged_task<void()>> _task_queue;
|
std::queue<std::packaged_task<void()>> _task_queue;
|
||||||
std::thread _worker_thread;
|
std::thread _worker_thread;
|
||||||
};
|
};
|
||||||
|
|||||||
40
src/common/utils/ProcessWaiter.cpp
Normal file
40
src/common/utils/ProcessWaiter.cpp
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "ProcessWaiter.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace ProcessWaiter
|
||||||
|
{
|
||||||
|
void OnProcessTerminate(std::wstring parent_pid, std::function<void(DWORD)> callback)
|
||||||
|
{
|
||||||
|
DWORD pid = 0;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
pid = std::stoul(parent_pid);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
callback(ERROR_INVALID_PARAMETER);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::thread([pid, callback = std::move(callback)]() mutable {
|
||||||
|
wil::unique_handle process{ OpenProcess(SYNCHRONIZE, FALSE, pid) };
|
||||||
|
if (process)
|
||||||
|
{
|
||||||
|
if (WaitForSingleObject(process.get(), INFINITE) == WAIT_OBJECT_0)
|
||||||
|
{
|
||||||
|
callback(ERROR_SUCCESS);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
callback(GetLastError());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
callback(GetLastError());
|
||||||
|
}
|
||||||
|
}).detach();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,32 +1,11 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <Windows.h>
|
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <Windows.h>
|
||||||
|
|
||||||
namespace ProcessWaiter
|
namespace ProcessWaiter
|
||||||
{
|
{
|
||||||
void OnProcessTerminate(std::wstring parent_pid, std::function<void(DWORD)> callback)
|
void OnProcessTerminate(std::wstring parent_pid, std::function<void(DWORD)> callback);
|
||||||
{
|
|
||||||
DWORD pid = std::stol(parent_pid);
|
|
||||||
std::thread([=]() {
|
|
||||||
HANDLE process = OpenProcess(SYNCHRONIZE, FALSE, pid);
|
|
||||||
if (process != nullptr)
|
|
||||||
{
|
|
||||||
if (WaitForSingleObject(process, INFINITE) == WAIT_OBJECT_0)
|
|
||||||
{
|
|
||||||
CloseHandle(process);
|
|
||||||
callback(ERROR_SUCCESS);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
CloseHandle(process);
|
|
||||||
callback(GetLastError());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
callback(GetLastError());
|
|
||||||
}
|
|
||||||
}).detach();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
277
src/common/utils/UnhandledExceptionHandler.cpp
Normal file
277
src/common/utils/UnhandledExceptionHandler.cpp
Normal file
@@ -0,0 +1,277 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "UnhandledExceptionHandler.h"
|
||||||
|
|
||||||
|
#include <DbgHelp.h>
|
||||||
|
#include <atomic>
|
||||||
|
#include <csignal>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#include "winapi_error.h"
|
||||||
|
#include "../logger/logger.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
std::atomic_bool processingException{ false };
|
||||||
|
|
||||||
|
const char* exceptionDescription(const DWORD code)
|
||||||
|
{
|
||||||
|
switch (code)
|
||||||
|
{
|
||||||
|
case EXCEPTION_ACCESS_VIOLATION:
|
||||||
|
return "EXCEPTION_ACCESS_VIOLATION";
|
||||||
|
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
|
||||||
|
return "EXCEPTION_ARRAY_BOUNDS_EXCEEDED";
|
||||||
|
case EXCEPTION_BREAKPOINT:
|
||||||
|
return "EXCEPTION_BREAKPOINT";
|
||||||
|
case EXCEPTION_DATATYPE_MISALIGNMENT:
|
||||||
|
return "EXCEPTION_DATATYPE_MISALIGNMENT";
|
||||||
|
case EXCEPTION_FLT_DENORMAL_OPERAND:
|
||||||
|
return "EXCEPTION_FLT_DENORMAL_OPERAND";
|
||||||
|
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
|
||||||
|
return "EXCEPTION_FLT_DIVIDE_BY_ZERO";
|
||||||
|
case EXCEPTION_FLT_INEXACT_RESULT:
|
||||||
|
return "EXCEPTION_FLT_INEXACT_RESULT";
|
||||||
|
case EXCEPTION_FLT_INVALID_OPERATION:
|
||||||
|
return "EXCEPTION_FLT_INVALID_OPERATION";
|
||||||
|
case EXCEPTION_FLT_OVERFLOW:
|
||||||
|
return "EXCEPTION_FLT_OVERFLOW";
|
||||||
|
case EXCEPTION_FLT_STACK_CHECK:
|
||||||
|
return "EXCEPTION_FLT_STACK_CHECK";
|
||||||
|
case EXCEPTION_FLT_UNDERFLOW:
|
||||||
|
return "EXCEPTION_FLT_UNDERFLOW";
|
||||||
|
case EXCEPTION_ILLEGAL_INSTRUCTION:
|
||||||
|
return "EXCEPTION_ILLEGAL_INSTRUCTION";
|
||||||
|
case EXCEPTION_IN_PAGE_ERROR:
|
||||||
|
return "EXCEPTION_IN_PAGE_ERROR";
|
||||||
|
case EXCEPTION_INT_DIVIDE_BY_ZERO:
|
||||||
|
return "EXCEPTION_INT_DIVIDE_BY_ZERO";
|
||||||
|
case EXCEPTION_INT_OVERFLOW:
|
||||||
|
return "EXCEPTION_INT_OVERFLOW";
|
||||||
|
case EXCEPTION_INVALID_DISPOSITION:
|
||||||
|
return "EXCEPTION_INVALID_DISPOSITION";
|
||||||
|
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
|
||||||
|
return "EXCEPTION_NONCONTINUABLE_EXCEPTION";
|
||||||
|
case EXCEPTION_PRIV_INSTRUCTION:
|
||||||
|
return "EXCEPTION_PRIV_INSTRUCTION";
|
||||||
|
case EXCEPTION_SINGLE_STEP:
|
||||||
|
return "EXCEPTION_SINGLE_STEP";
|
||||||
|
case EXCEPTION_STACK_OVERFLOW:
|
||||||
|
return "EXCEPTION_STACK_OVERFLOW";
|
||||||
|
default:
|
||||||
|
return "UNKNOWN EXCEPTION";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetFilenameStart(wchar_t* path)
|
||||||
|
{
|
||||||
|
int pos = 0;
|
||||||
|
int found = 0;
|
||||||
|
if (path != nullptr)
|
||||||
|
{
|
||||||
|
while (path[pos] != L'\0' && pos < MAX_PATH)
|
||||||
|
{
|
||||||
|
if (path[pos] == L'\\')
|
||||||
|
{
|
||||||
|
found = pos + 1;
|
||||||
|
}
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring GetModuleName(HANDLE process, const STACKFRAME64& stack)
|
||||||
|
{
|
||||||
|
static wchar_t modulePath[MAX_PATH]{};
|
||||||
|
memset(&modulePath[0], '\0', sizeof(modulePath));
|
||||||
|
|
||||||
|
const DWORD64 moduleBase = SymGetModuleBase64(process, stack.AddrPC.Offset);
|
||||||
|
if (!moduleBase)
|
||||||
|
{
|
||||||
|
Logger::error(L"Failed to get a module. {}", get_last_error_or_default(GetLastError()));
|
||||||
|
return std::wstring();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!GetModuleFileNameW(reinterpret_cast<HINSTANCE>(moduleBase), modulePath, MAX_PATH))
|
||||||
|
{
|
||||||
|
Logger::error(L"Failed to get a module path. {}", get_last_error_or_default(GetLastError()));
|
||||||
|
return std::wstring();
|
||||||
|
}
|
||||||
|
|
||||||
|
const int start = GetFilenameStart(modulePath);
|
||||||
|
return std::wstring(modulePath, start);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring GetName(HANDLE process, const STACKFRAME64& stack)
|
||||||
|
{
|
||||||
|
static IMAGEHLP_SYMBOL64* pSymbol = static_cast<IMAGEHLP_SYMBOL64*>(malloc(sizeof(IMAGEHLP_SYMBOL64) + MAX_PATH * sizeof(TCHAR)));
|
||||||
|
if (!pSymbol)
|
||||||
|
{
|
||||||
|
return std::wstring();
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(pSymbol, '\0', sizeof(*pSymbol) + MAX_PATH);
|
||||||
|
pSymbol->MaxNameLength = MAX_PATH;
|
||||||
|
pSymbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
|
||||||
|
|
||||||
|
DWORD64 displacement = 0;
|
||||||
|
if (!SymGetSymFromAddr64(process, stack.AddrPC.Offset, &displacement, pSymbol))
|
||||||
|
{
|
||||||
|
Logger::error(L"Failed to get a symbol. {}", get_last_error_or_default(GetLastError()));
|
||||||
|
return std::wstring();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string str = pSymbol->Name;
|
||||||
|
return std::wstring(str.begin(), str.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring GetLine(HANDLE process, const STACKFRAME64& stack)
|
||||||
|
{
|
||||||
|
static IMAGEHLP_LINE64 line{};
|
||||||
|
|
||||||
|
memset(&line, '\0', sizeof(IMAGEHLP_LINE64));
|
||||||
|
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
|
||||||
|
line.LineNumber = 0;
|
||||||
|
|
||||||
|
DWORD displacement = 0;
|
||||||
|
if (!SymGetLineFromAddr64(process, stack.AddrPC.Offset, &displacement, &line))
|
||||||
|
{
|
||||||
|
return std::wstring();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string fileName(line.FileName);
|
||||||
|
return L"(" + std::wstring(fileName.begin(), fileName.end()) + L":" + std::to_wstring(line.LineNumber) + L")";
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogStackTrace()
|
||||||
|
{
|
||||||
|
CONTEXT context;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
RtlCaptureContext(&context);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
Logger::error(L"Failed to capture context. {}", get_last_error_or_default(GetLastError()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
STACKFRAME64 stack;
|
||||||
|
memset(&stack, 0, sizeof(STACKFRAME64));
|
||||||
|
|
||||||
|
HANDLE process = GetCurrentProcess();
|
||||||
|
HANDLE thread = GetCurrentThread();
|
||||||
|
|
||||||
|
#ifdef _M_ARM64
|
||||||
|
stack.AddrPC.Offset = context.Pc;
|
||||||
|
stack.AddrStack.Offset = context.Sp;
|
||||||
|
stack.AddrFrame.Offset = context.Fp;
|
||||||
|
#else
|
||||||
|
stack.AddrPC.Offset = context.Rip;
|
||||||
|
stack.AddrStack.Offset = context.Rsp;
|
||||||
|
stack.AddrFrame.Offset = context.Rbp;
|
||||||
|
#endif
|
||||||
|
stack.AddrPC.Mode = AddrModeFlat;
|
||||||
|
stack.AddrStack.Mode = AddrModeFlat;
|
||||||
|
stack.AddrFrame.Mode = AddrModeFlat;
|
||||||
|
|
||||||
|
std::wstringstream ss;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
const BOOL result = StackWalk64(
|
||||||
|
#ifdef _M_ARM64
|
||||||
|
IMAGE_FILE_MACHINE_ARM64,
|
||||||
|
#else
|
||||||
|
IMAGE_FILE_MACHINE_AMD64,
|
||||||
|
#endif
|
||||||
|
process,
|
||||||
|
thread,
|
||||||
|
&stack,
|
||||||
|
&context,
|
||||||
|
NULL,
|
||||||
|
SymFunctionTableAccess64,
|
||||||
|
SymGetModuleBase64,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ss << GetModuleName(process, stack) << "!" << GetName(process, stack) << GetLine(process, stack) << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger::error(L"STACK TRACE\r\n{}", ss.str());
|
||||||
|
Logger::flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LONG WINAPI UnhandledExceptionHandler(PEXCEPTION_POINTERS info)
|
||||||
|
{
|
||||||
|
bool expected = false;
|
||||||
|
if (!processingException.compare_exchange_strong(expected, true))
|
||||||
|
{
|
||||||
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto guard = wil::scope_exit([]() noexcept { processingException = false; });
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const char* description = "Exception code not available";
|
||||||
|
if (info != nullptr && info->ExceptionRecord != nullptr && info->ExceptionRecord->ExceptionCode != 0)
|
||||||
|
{
|
||||||
|
description = exceptionDescription(info->ExceptionRecord->ExceptionCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger::error(description);
|
||||||
|
LogStackTrace();
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
Logger::error("Failed to log stack trace");
|
||||||
|
Logger::flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbortHandler(int /*signal_number*/)
|
||||||
|
{
|
||||||
|
Logger::error("--- ABORT");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
LogStackTrace();
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
Logger::error("Failed to log stack trace on abort");
|
||||||
|
Logger::flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitSymbols()
|
||||||
|
{
|
||||||
|
// Preload symbols so they will be available in case of out-of-memory exception
|
||||||
|
SymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_UNDNAME);
|
||||||
|
HANDLE process = GetCurrentProcess();
|
||||||
|
if (!SymInitialize(process, NULL, TRUE))
|
||||||
|
{
|
||||||
|
Logger::error(L"Failed to initialize symbol handler. {}", get_last_error_or_default(GetLastError()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitUnhandledExceptionHandler()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
InitSymbols();
|
||||||
|
SetUnhandledExceptionFilter(UnhandledExceptionHandler);
|
||||||
|
signal(SIGABRT, &AbortHandler);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
Logger::error("Failed to init global unhandled exception handler");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,280 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#include <DbgHelp.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <sstream>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#include "winapi_error.h"
|
LONG WINAPI UnhandledExceptionHandler(PEXCEPTION_POINTERS info);
|
||||||
#include "../logger/logger.h"
|
void AbortHandler(int signal_number);
|
||||||
|
void InitSymbols();
|
||||||
static BOOLEAN processingException = FALSE;
|
void InitUnhandledExceptionHandler();
|
||||||
|
|
||||||
static inline const char* exceptionDescription(const DWORD& code)
|
|
||||||
{
|
|
||||||
switch (code)
|
|
||||||
{
|
|
||||||
case EXCEPTION_ACCESS_VIOLATION:
|
|
||||||
return "EXCEPTION_ACCESS_VIOLATION";
|
|
||||||
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
|
|
||||||
return "EXCEPTION_ARRAY_BOUNDS_EXCEEDED";
|
|
||||||
case EXCEPTION_BREAKPOINT:
|
|
||||||
return "EXCEPTION_BREAKPOINT";
|
|
||||||
case EXCEPTION_DATATYPE_MISALIGNMENT:
|
|
||||||
return "EXCEPTION_DATATYPE_MISALIGNMENT";
|
|
||||||
case EXCEPTION_FLT_DENORMAL_OPERAND:
|
|
||||||
return "EXCEPTION_FLT_DENORMAL_OPERAND";
|
|
||||||
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
|
|
||||||
return "EXCEPTION_FLT_DIVIDE_BY_ZERO";
|
|
||||||
case EXCEPTION_FLT_INEXACT_RESULT:
|
|
||||||
return "EXCEPTION_FLT_INEXACT_RESULT";
|
|
||||||
case EXCEPTION_FLT_INVALID_OPERATION:
|
|
||||||
return "EXCEPTION_FLT_INVALID_OPERATION";
|
|
||||||
case EXCEPTION_FLT_OVERFLOW:
|
|
||||||
return "EXCEPTION_FLT_OVERFLOW";
|
|
||||||
case EXCEPTION_FLT_STACK_CHECK:
|
|
||||||
return "EXCEPTION_FLT_STACK_CHECK";
|
|
||||||
case EXCEPTION_FLT_UNDERFLOW:
|
|
||||||
return "EXCEPTION_FLT_UNDERFLOW";
|
|
||||||
case EXCEPTION_ILLEGAL_INSTRUCTION:
|
|
||||||
return "EXCEPTION_ILLEGAL_INSTRUCTION";
|
|
||||||
case EXCEPTION_IN_PAGE_ERROR:
|
|
||||||
return "EXCEPTION_IN_PAGE_ERROR";
|
|
||||||
case EXCEPTION_INT_DIVIDE_BY_ZERO:
|
|
||||||
return "EXCEPTION_INT_DIVIDE_BY_ZERO";
|
|
||||||
case EXCEPTION_INT_OVERFLOW:
|
|
||||||
return "EXCEPTION_INT_OVERFLOW";
|
|
||||||
case EXCEPTION_INVALID_DISPOSITION:
|
|
||||||
return "EXCEPTION_INVALID_DISPOSITION";
|
|
||||||
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
|
|
||||||
return "EXCEPTION_NONCONTINUABLE_EXCEPTION";
|
|
||||||
case EXCEPTION_PRIV_INSTRUCTION:
|
|
||||||
return "EXCEPTION_PRIV_INSTRUCTION";
|
|
||||||
case EXCEPTION_SINGLE_STEP:
|
|
||||||
return "EXCEPTION_SINGLE_STEP";
|
|
||||||
case EXCEPTION_STACK_OVERFLOW:
|
|
||||||
return "EXCEPTION_STACK_OVERFLOW";
|
|
||||||
default:
|
|
||||||
return "UNKNOWN EXCEPTION";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the index of the last backslash in the file path */
|
|
||||||
inline int GetFilenameStart(wchar_t* path)
|
|
||||||
{
|
|
||||||
int pos = 0;
|
|
||||||
int found = 0;
|
|
||||||
if (path != NULL)
|
|
||||||
{
|
|
||||||
while (path[pos] != L'\0' && pos < MAX_PATH)
|
|
||||||
{
|
|
||||||
if (path[pos] == L'\\')
|
|
||||||
{
|
|
||||||
found = pos + 1;
|
|
||||||
}
|
|
||||||
++pos;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return found;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::wstring GetModuleName(HANDLE process, const STACKFRAME64& stack)
|
|
||||||
{
|
|
||||||
static wchar_t modulePath[MAX_PATH]{};
|
|
||||||
const size_t size = sizeof(modulePath);
|
|
||||||
memset(&modulePath[0], '\0', size);
|
|
||||||
|
|
||||||
DWORD64 moduleBase = SymGetModuleBase64(process, stack.AddrPC.Offset);
|
|
||||||
if (!moduleBase)
|
|
||||||
{
|
|
||||||
Logger::error(L"Failed to get a module. {}", get_last_error_or_default(GetLastError()));
|
|
||||||
return std::wstring();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!GetModuleFileNameW(reinterpret_cast<HINSTANCE>(moduleBase), modulePath, MAX_PATH))
|
|
||||||
{
|
|
||||||
Logger::error(L"Failed to get a module path. {}", get_last_error_or_default(GetLastError()));
|
|
||||||
return std::wstring();
|
|
||||||
}
|
|
||||||
|
|
||||||
const int start = GetFilenameStart(modulePath);
|
|
||||||
return std::wstring(modulePath, start);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::wstring GetName(HANDLE process, const STACKFRAME64& stack)
|
|
||||||
{
|
|
||||||
static IMAGEHLP_SYMBOL64* pSymbol = static_cast<IMAGEHLP_SYMBOL64*>(malloc(sizeof(IMAGEHLP_SYMBOL64) + MAX_PATH * sizeof(TCHAR)));
|
|
||||||
if (!pSymbol)
|
|
||||||
{
|
|
||||||
return std::wstring();
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(pSymbol, '\0', sizeof(*pSymbol) + MAX_PATH);
|
|
||||||
pSymbol->MaxNameLength = MAX_PATH;
|
|
||||||
pSymbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
|
|
||||||
|
|
||||||
DWORD64 dw64Displacement = 0;
|
|
||||||
if (!SymGetSymFromAddr64(process, stack.AddrPC.Offset, &dw64Displacement, pSymbol))
|
|
||||||
{
|
|
||||||
Logger::error(L"Failed to get a symbol. {}", get_last_error_or_default(GetLastError()));
|
|
||||||
return std::wstring();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string str = pSymbol->Name;
|
|
||||||
return std::wstring(str.begin(), str.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::wstring GetLine(HANDLE process, const STACKFRAME64& stack)
|
|
||||||
{
|
|
||||||
static IMAGEHLP_LINE64 line{};
|
|
||||||
|
|
||||||
memset(&line, '\0', sizeof(IMAGEHLP_LINE64));
|
|
||||||
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
|
|
||||||
line.LineNumber = 0;
|
|
||||||
|
|
||||||
DWORD dwDisplacement = 0;
|
|
||||||
if (!SymGetLineFromAddr64(process, stack.AddrPC.Offset, &dwDisplacement, &line))
|
|
||||||
{
|
|
||||||
return std::wstring();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string fileName(line.FileName);
|
|
||||||
return L"(" + std::wstring(fileName.begin(), fileName.end()) + L":" + std::to_wstring(line.LineNumber) + L")";
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void LogStackTrace()
|
|
||||||
{
|
|
||||||
CONTEXT context;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
RtlCaptureContext(&context);
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
Logger::error(L"Failed to capture context. {}", get_last_error_or_default(GetLastError()));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
STACKFRAME64 stack;
|
|
||||||
memset(&stack, 0, sizeof(STACKFRAME64));
|
|
||||||
|
|
||||||
HANDLE process = GetCurrentProcess();
|
|
||||||
HANDLE thread = GetCurrentThread();
|
|
||||||
|
|
||||||
#ifdef _M_ARM64
|
|
||||||
stack.AddrPC.Offset = context.Pc;
|
|
||||||
stack.AddrStack.Offset = context.Sp;
|
|
||||||
stack.AddrFrame.Offset = context.Fp;
|
|
||||||
#else
|
|
||||||
stack.AddrPC.Offset = context.Rip;
|
|
||||||
stack.AddrStack.Offset = context.Rsp;
|
|
||||||
stack.AddrFrame.Offset = context.Rbp;
|
|
||||||
#endif
|
|
||||||
stack.AddrPC.Mode = AddrModeFlat;
|
|
||||||
stack.AddrStack.Mode = AddrModeFlat;
|
|
||||||
stack.AddrFrame.Mode = AddrModeFlat;
|
|
||||||
|
|
||||||
BOOL result = false;
|
|
||||||
std::wstringstream ss;
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
result = StackWalk64(
|
|
||||||
#ifdef _M_ARM64
|
|
||||||
IMAGE_FILE_MACHINE_ARM64,
|
|
||||||
#else
|
|
||||||
IMAGE_FILE_MACHINE_AMD64,
|
|
||||||
#endif
|
|
||||||
process,
|
|
||||||
thread,
|
|
||||||
&stack,
|
|
||||||
&context,
|
|
||||||
NULL,
|
|
||||||
SymFunctionTableAccess64,
|
|
||||||
SymGetModuleBase64,
|
|
||||||
NULL);
|
|
||||||
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
ss << GetModuleName(process, stack) << "!" << GetName(process, stack) << GetLine(process, stack) << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger::error(L"STACK TRACE\r\n{}", ss.str());
|
|
||||||
Logger::flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline LONG WINAPI UnhandledExceptionHandler(PEXCEPTION_POINTERS info)
|
|
||||||
{
|
|
||||||
if (!processingException)
|
|
||||||
{
|
|
||||||
bool headerLogged = false;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
const char* exDescription = "Exception code not available";
|
|
||||||
processingException = true;
|
|
||||||
if (info != NULL && info->ExceptionRecord != NULL && info->ExceptionRecord->ExceptionCode != NULL)
|
|
||||||
{
|
|
||||||
exDescription = exceptionDescription(info->ExceptionRecord->ExceptionCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
headerLogged = true;
|
|
||||||
Logger::error(exDescription);
|
|
||||||
LogStackTrace();
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
Logger::error("Failed to log stack trace");
|
|
||||||
Logger::flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
processingException = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return EXCEPTION_CONTINUE_SEARCH;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Handler to trap abort() calls */
|
|
||||||
inline void AbortHandler(int /*signal_number*/)
|
|
||||||
{
|
|
||||||
Logger::error("--- ABORT");
|
|
||||||
try
|
|
||||||
{
|
|
||||||
LogStackTrace();
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
Logger::error("Failed to log stack trace on abort");
|
|
||||||
Logger::flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void InitSymbols()
|
|
||||||
{
|
|
||||||
// Preload symbols so they will be available in case of out-of-memory exception
|
|
||||||
SymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_UNDNAME);
|
|
||||||
HANDLE process = GetCurrentProcess();
|
|
||||||
if (!SymInitialize(process, NULL, TRUE))
|
|
||||||
{
|
|
||||||
Logger::error(L"Failed to initialize symbol handler. {}", get_last_error_or_default(GetLastError()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void InitUnhandledExceptionHandler(void)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
InitSymbols();
|
|
||||||
// Global handler for unhandled exceptions
|
|
||||||
SetUnhandledExceptionFilter(UnhandledExceptionHandler);
|
|
||||||
// Handler for abort()
|
|
||||||
signal(SIGABRT, &AbortHandler);
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
Logger::error("Failed to init global unhandled exception handler");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
14
src/common/utils/appMutex.cpp
Normal file
14
src/common/utils/appMutex.cpp
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "appMutex.h"
|
||||||
|
|
||||||
|
wil::unique_mutex_nothrow createAppMutex(const std::wstring& mutexName)
|
||||||
|
{
|
||||||
|
wil::unique_mutex_nothrow result{ CreateMutexW(nullptr, TRUE, mutexName.c_str()) };
|
||||||
|
|
||||||
|
if (GetLastError() == ERROR_ALREADY_EXISTS)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
@@ -14,9 +14,4 @@ namespace
|
|||||||
constexpr inline wchar_t POWERTOYS_BOOTSTRAPPER_MUTEX_NAME[] = L"Local\\PowerToys_Bootstrapper_InstanceMutex";
|
constexpr inline wchar_t POWERTOYS_BOOTSTRAPPER_MUTEX_NAME[] = L"Local\\PowerToys_Bootstrapper_InstanceMutex";
|
||||||
}
|
}
|
||||||
|
|
||||||
inline wil::unique_mutex_nothrow createAppMutex(const std::wstring& mutexName)
|
wil::unique_mutex_nothrow createAppMutex(const std::wstring& mutexName);
|
||||||
{
|
|
||||||
wil::unique_mutex_nothrow result{ CreateMutexW(nullptr, TRUE, mutexName.c_str()) };
|
|
||||||
|
|
||||||
return GetLastError() == ERROR_ALREADY_EXISTS ? wil::unique_mutex_nothrow{} : std::move(result);
|
|
||||||
}
|
|
||||||
|
|||||||
18
src/common/utils/clean_video_conference.cpp
Normal file
18
src/common/utils/clean_video_conference.cpp
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "clean_video_conference.h"
|
||||||
|
|
||||||
|
void clean_video_conference()
|
||||||
|
{
|
||||||
|
// 31AD75E9-8C3A-49C8-B9ED-5880D6B4A764 is the CLSID GUID for the 64 video conference mute driver.
|
||||||
|
// 31AD75E9-8C3A-49C8-B9ED-5880D6B4A732 is the CLSID GUID for the 32 video conference mute driver.
|
||||||
|
// 860BB310-5D01-11D0-BD3B-00A0C911CE86 is the CLSID GUID for CLSID_VideoInputDeviceCategory.
|
||||||
|
|
||||||
|
// Unregister the 64 bit driver CLSID:
|
||||||
|
RegDeleteTreeW(HKEY_CLASSES_ROOT, L"CLSID\\{31AD75E9-8C3A-49C8-B9ED-5880D6B4A764}");
|
||||||
|
// Unregister the 64 bit driver CLSID from Video Input Devices:
|
||||||
|
RegDeleteTreeW(HKEY_CLASSES_ROOT, L"CLSID\\{860BB310-5D01-11D0-BD3B-00A0C911CE86}\\Instance\\{31AD75E9-8C3A-49C8-B9ED-5880D6B4A764}");
|
||||||
|
// Unregister the 32 bit driver CLSID:
|
||||||
|
RegDeleteTreeW(HKEY_LOCAL_MACHINE, L"Software\\WOW6432Node\\Classes\\CLSID\\{31AD75E9-8C3A-49C8-B9ED-5880D6B4A732}");
|
||||||
|
// Unregister the 32 bit driver CLSID from Video Input Devices:
|
||||||
|
RegDeleteTreeW(HKEY_LOCAL_MACHINE, L"Software\\WOW6432Node\\Classes\\CLSID\\{860BB310-5D01-11D0-BD3B-00A0C911CE86}\\Instance\\{31AD75E9-8C3A-49C8-B9ED-5880D6B4A732}");
|
||||||
|
}
|
||||||
@@ -1,18 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// Video Conference Mute was a utility we deprecated. However, this required a manual user disable of the module to remove the camera registration, so we include the disable code here to be able to clean up.
|
#include <windows.h>
|
||||||
void clean_video_conference()
|
|
||||||
{
|
|
||||||
// 31AD75E9-8C3A-49C8-B9ED-5880D6B4A764 is the CLSID GUID for the 64 video conference mute driver.
|
|
||||||
// 31AD75E9-8C3A-49C8-B9ED-5880D6B4A732 is the CLSID GUID for the 32 video conference mute driver.
|
|
||||||
// 860BB310-5D01-11D0-BD3B-00A0C911CE86 is the CLSID GUID for CLSID_VideoInputDeviceCategory.
|
|
||||||
|
|
||||||
// Unregister the 64 bit driver CLSID:
|
// Video Conference Mute was a utility we deprecated. However, this required a manual user disable of the module to remove the camera registration, so we include the disable code here to be able to clean up.
|
||||||
RegDeleteTreeW(HKEY_CLASSES_ROOT, L"CLSID\\{31AD75E9-8C3A-49C8-B9ED-5880D6B4A764}");
|
void clean_video_conference();
|
||||||
// Unregister the 64 bit driver CLSID from Video Input Devices:
|
|
||||||
RegDeleteTreeW(HKEY_CLASSES_ROOT, L"CLSID\\{860BB310-5D01-11D0-BD3B-00A0C911CE86}\\Instance\\{31AD75E9-8C3A-49C8-B9ED-5880D6B4A764}");
|
|
||||||
// Unregister the 32 bit driver CLSID:
|
|
||||||
RegDeleteTreeW(HKEY_LOCAL_MACHINE, L"Software\\WOW6432Node\\Classes\\CLSID\\{31AD75E9-8C3A-49C8-B9ED-5880D6B4A732}");
|
|
||||||
// Unregister the 32 bit driver CLSID from Video Input Devices:
|
|
||||||
RegDeleteTreeW(HKEY_LOCAL_MACHINE, L"Software\\WOW6432Node\\Classes\\CLSID\\{860BB310-5D01-11D0-BD3B-00A0C911CE86}\\Instance\\{31AD75E9-8C3A-49C8-B9ED-5880D6B4A732}");
|
|
||||||
}
|
|
||||||
|
|||||||
50
src/common/utils/color.cpp
Normal file
50
src/common/utils/color.cpp
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "color.h"
|
||||||
|
|
||||||
|
bool checkValidRGB(std::wstring_view hex, uint8_t* R, uint8_t* G, uint8_t* B)
|
||||||
|
{
|
||||||
|
if (hex.length() != 7)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
hex = hex.substr(1, 6); // remove #
|
||||||
|
for (const auto& c : hex)
|
||||||
|
{
|
||||||
|
if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F')))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (swscanf_s(hex.data(), L"%2hhx%2hhx%2hhx", R, G, B) != 3)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkValidARGB(std::wstring_view hex, uint8_t* A, uint8_t* R, uint8_t* G, uint8_t* B)
|
||||||
|
{
|
||||||
|
if (hex.length() != 9)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
hex = hex.substr(1, 8); // remove #
|
||||||
|
for (const auto& c : hex)
|
||||||
|
{
|
||||||
|
if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F')))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (swscanf_s(hex.data(), L"%2hhx%2hhx%2hhx%2hhx", A, R, G, B) != 4)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
@@ -1,41 +1,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
// helper function to get the RGB from a #FFFFFF string.
|
// helper function to get the RGB from a #FFFFFF string.
|
||||||
inline bool checkValidRGB(std::wstring_view hex, uint8_t* R, uint8_t* G, uint8_t* B)
|
bool checkValidRGB(std::wstring_view hex, uint8_t* R, uint8_t* G, uint8_t* B);
|
||||||
{
|
|
||||||
if (hex.length() != 7)
|
|
||||||
return false;
|
|
||||||
hex = hex.substr(1, 6); // remove #
|
|
||||||
for (auto& c : hex)
|
|
||||||
{
|
|
||||||
if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F')))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (swscanf_s(hex.data(), L"%2hhx%2hhx%2hhx", R, G, B) != 3)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// helper function to get the ARGB from a #FFFFFFFF string.
|
// helper function to get the ARGB from a #FFFFFFFF string.
|
||||||
inline bool checkValidARGB(std::wstring_view hex, uint8_t* A, uint8_t* R, uint8_t* G, uint8_t* B)
|
bool checkValidARGB(std::wstring_view hex, uint8_t* A, uint8_t* R, uint8_t* G, uint8_t* B);
|
||||||
{
|
|
||||||
if (hex.length() != 9)
|
|
||||||
return false;
|
|
||||||
hex = hex.substr(1, 8); // remove #
|
|
||||||
for (auto& c : hex)
|
|
||||||
{
|
|
||||||
if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F')))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (swscanf_s(hex.data(), L"%2hhx%2hhx%2hhx%2hhx", A, R, G, B) != 4)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|||||||
506
src/common/utils/elevation.cpp
Normal file
506
src/common/utils/elevation.cpp
Normal file
@@ -0,0 +1,506 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "elevation.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
std::wstring GetErrorString(HRESULT handle)
|
||||||
|
{
|
||||||
|
_com_error err(handle);
|
||||||
|
return err.ErrorMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FindDesktopFolderView(REFIID riid, void** ppv)
|
||||||
|
{
|
||||||
|
CComPtr<IShellWindows> spShellWindows;
|
||||||
|
auto result = spShellWindows.CoCreateInstance(CLSID_ShellWindows);
|
||||||
|
if (result != S_OK || spShellWindows == nullptr)
|
||||||
|
{
|
||||||
|
Logger::warn(L"Failed to create instance. {}", GetErrorString(result));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CComVariant vtLoc(CSIDL_DESKTOP);
|
||||||
|
CComVariant vtEmpty;
|
||||||
|
long lhwnd;
|
||||||
|
CComPtr<IDispatch> spdisp;
|
||||||
|
result = spShellWindows->FindWindowSW(&vtLoc, &vtEmpty, SWC_DESKTOP, &lhwnd, SWFO_NEEDDISPATCH, &spdisp);
|
||||||
|
if (result != S_OK || spdisp == nullptr)
|
||||||
|
{
|
||||||
|
Logger::warn(L"Failed to find the window. {}", GetErrorString(result));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CComPtr<IShellBrowser> spBrowser;
|
||||||
|
result = CComQIPtr<IServiceProvider>(spdisp)->QueryService(SID_STopLevelBrowser, IID_PPV_ARGS(&spBrowser));
|
||||||
|
if (result != S_OK || spBrowser == nullptr)
|
||||||
|
{
|
||||||
|
Logger::warn(L"Failed to query service. {}", GetErrorString(result));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CComPtr<IShellView> spView;
|
||||||
|
result = spBrowser->QueryActiveShellView(&spView);
|
||||||
|
if (result != S_OK || spView == nullptr)
|
||||||
|
{
|
||||||
|
Logger::warn(L"Failed to query active shell window. {}", GetErrorString(result));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = spView->QueryInterface(riid, ppv);
|
||||||
|
if (result != S_OK || ppv == nullptr || *ppv == nullptr)
|
||||||
|
{
|
||||||
|
Logger::warn(L"Failed to query interface. {}", GetErrorString(result));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GetDesktopAutomationObject(REFIID riid, void** ppv)
|
||||||
|
{
|
||||||
|
CComPtr<IShellView> spsv;
|
||||||
|
|
||||||
|
// Desktop may not be available on startup
|
||||||
|
auto attempts = 5;
|
||||||
|
for (auto i = 1; i <= attempts; i++)
|
||||||
|
{
|
||||||
|
if (FindDesktopFolderView(IID_PPV_ARGS(&spsv)))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger::warn(L"FindDesktopFolderView() failed attempt {}", i);
|
||||||
|
|
||||||
|
if (i == attempts)
|
||||||
|
{
|
||||||
|
Logger::warn(L"FindDesktopFolderView() max attempts reached");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Sleep(3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
CComPtr<IDispatch> spdispView;
|
||||||
|
auto result = spsv->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&spdispView));
|
||||||
|
if (result != S_OK || spdispView == nullptr)
|
||||||
|
{
|
||||||
|
Logger::warn(L"spsv->GetItemObject() failed. {}", GetErrorString(result));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SUCCEEDED(spdispView->QueryInterface(riid, ppv));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShellExecuteFromExplorer(LPCWSTR file, LPCWSTR parameters, LPCWSTR directory)
|
||||||
|
{
|
||||||
|
CComPtr<IShellFolderViewDual> spFolderView;
|
||||||
|
if (!GetDesktopAutomationObject(IID_PPV_ARGS(&spFolderView)))
|
||||||
|
{
|
||||||
|
Logger::warn(L"GetDesktopAutomationObject() failed.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CComPtr<IDispatch> spdispShell;
|
||||||
|
auto result = spFolderView->get_Application(&spdispShell);
|
||||||
|
if (result != S_OK || spdispShell == nullptr)
|
||||||
|
{
|
||||||
|
Logger::warn(L"spFolderView->get_Application() failed. {}", GetErrorString(result));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CComQIPtr<IShellDispatch2> shell(spdispShell);
|
||||||
|
if (shell == nullptr)
|
||||||
|
{
|
||||||
|
Logger::warn(L"IShellDispatch2 is nullptr");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CComVariant args(parameters ? parameters : L"");
|
||||||
|
CComVariant dir(directory ? directory : L"");
|
||||||
|
CComVariant operation(L"open");
|
||||||
|
CComVariant show(SW_SHOWNORMAL);
|
||||||
|
result = shell->ShellExecute(CComBSTR(file), args, dir, operation, show);
|
||||||
|
if (result != S_OK)
|
||||||
|
{
|
||||||
|
Logger::warn(L"IShellDispatch2::ShellExecute() failed. {}", GetErrorString(result));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_process_elevated(const bool use_cached_value)
|
||||||
|
{
|
||||||
|
auto detection_func = []() {
|
||||||
|
HANDLE token = nullptr;
|
||||||
|
bool elevated = false;
|
||||||
|
|
||||||
|
if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))
|
||||||
|
{
|
||||||
|
TOKEN_ELEVATION elevation;
|
||||||
|
DWORD size;
|
||||||
|
if (GetTokenInformation(token, TokenElevation, &elevation, sizeof(elevation), &size))
|
||||||
|
{
|
||||||
|
elevated = (elevation.TokenIsElevated != 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token)
|
||||||
|
{
|
||||||
|
CloseHandle(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
return elevated;
|
||||||
|
};
|
||||||
|
static const bool cached_value = detection_func();
|
||||||
|
return use_cached_value ? cached_value : detection_func();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool drop_elevated_privileges()
|
||||||
|
{
|
||||||
|
HANDLE token = nullptr;
|
||||||
|
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_DEFAULT | WRITE_OWNER, &token))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PSID medium_sid = NULL;
|
||||||
|
if (!::ConvertStringSidToSid(SDDL_ML_MEDIUM, &medium_sid))
|
||||||
|
{
|
||||||
|
CloseHandle(token);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
TOKEN_MANDATORY_LABEL tml = {};
|
||||||
|
tml.Label.Attributes = SE_GROUP_INTEGRITY;
|
||||||
|
tml.Label.Sid = medium_sid;
|
||||||
|
DWORD dwLen = sizeof(TOKEN_MANDATORY_LABEL) + ::GetLengthSid(medium_sid);
|
||||||
|
BOOL res = ::SetTokenInformation(token, TokenIntegrityLevel, &tml, dwLen);
|
||||||
|
LocalFree(medium_sid);
|
||||||
|
CloseHandle(token);
|
||||||
|
return res == TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
HANDLE run_as_different_user(const std::wstring& file, const std::wstring& params, const wchar_t* workingDir, const bool showWindow)
|
||||||
|
{
|
||||||
|
Logger::info(L"run_as_different_user with params={}", params);
|
||||||
|
SHELLEXECUTEINFOW exec_info = { 0 };
|
||||||
|
exec_info.cbSize = sizeof(SHELLEXECUTEINFOW);
|
||||||
|
exec_info.lpVerb = L"runAsUser";
|
||||||
|
exec_info.lpFile = file.c_str();
|
||||||
|
exec_info.lpParameters = params.c_str();
|
||||||
|
exec_info.hwnd = 0;
|
||||||
|
exec_info.fMask = SEE_MASK_NOCLOSEPROCESS;
|
||||||
|
exec_info.lpDirectory = workingDir;
|
||||||
|
exec_info.hInstApp = 0;
|
||||||
|
exec_info.nShow = showWindow ? SW_SHOWDEFAULT : SW_HIDE; // may have limited effect
|
||||||
|
return ShellExecuteExW(&exec_info) ? exec_info.hProcess : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
HANDLE run_elevated(const std::wstring& file, const std::wstring& params, const wchar_t* workingDir, const bool showWindow)
|
||||||
|
{
|
||||||
|
Logger::info(L"run_elevated with params={}", params);
|
||||||
|
SHELLEXECUTEINFOW exec_info = { 0 };
|
||||||
|
exec_info.cbSize = sizeof(SHELLEXECUTEINFOW);
|
||||||
|
exec_info.lpVerb = L"runas";
|
||||||
|
exec_info.lpFile = file.c_str();
|
||||||
|
exec_info.lpParameters = params.c_str();
|
||||||
|
exec_info.hwnd = 0;
|
||||||
|
exec_info.fMask = SEE_MASK_NOCLOSEPROCESS;
|
||||||
|
exec_info.lpDirectory = workingDir;
|
||||||
|
exec_info.hInstApp = 0;
|
||||||
|
exec_info.nShow = showWindow ? SW_SHOWDEFAULT : SW_HIDE; // may have limited effect
|
||||||
|
BOOL result = ShellExecuteExW(&exec_info);
|
||||||
|
return result ? exec_info.hProcess : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool run_non_elevated(const std::wstring& file, const std::wstring& params, DWORD* returnPid, const wchar_t* workingDir, const bool showWindow)
|
||||||
|
{
|
||||||
|
Logger::info(L"run_non_elevated with params={}", params);
|
||||||
|
auto executable_args = L"\"" + file + L"\"";
|
||||||
|
if (!params.empty())
|
||||||
|
{
|
||||||
|
executable_args += L" " + params;
|
||||||
|
}
|
||||||
|
|
||||||
|
HWND hwnd = GetShellWindow();
|
||||||
|
if (!hwnd)
|
||||||
|
{
|
||||||
|
if (GetLastError() == ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
Logger::warn(L"GetShellWindow() returned null. Shell window is not available");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger::error(L"GetShellWindow() failed. {}", get_last_error_or_default(GetLastError()));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
DWORD pid;
|
||||||
|
GetWindowThreadProcessId(hwnd, &pid);
|
||||||
|
|
||||||
|
winrt::handle process{ OpenProcess(PROCESS_CREATE_PROCESS, FALSE, pid) };
|
||||||
|
if (!process)
|
||||||
|
{
|
||||||
|
Logger::error(L"OpenProcess() failed. {}", get_last_error_or_default(GetLastError()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SIZE_T size = 0;
|
||||||
|
InitializeProcThreadAttributeList(nullptr, 1, 0, &size);
|
||||||
|
auto pproc_buffer = std::make_unique<char[]>(size);
|
||||||
|
auto pptal = reinterpret_cast<PPROC_THREAD_ATTRIBUTE_LIST>(pproc_buffer.get());
|
||||||
|
if (!pptal)
|
||||||
|
{
|
||||||
|
Logger::error(L"pptal failed to initialize. {}", get_last_error_or_default(GetLastError()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!InitializeProcThreadAttributeList(pptal, 1, 0, &size))
|
||||||
|
{
|
||||||
|
Logger::error(L"InitializeProcThreadAttributeList() failed. {}", get_last_error_or_default(GetLastError()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
HANDLE process_handle = process.get();
|
||||||
|
if (!UpdateProcThreadAttribute(pptal,
|
||||||
|
0,
|
||||||
|
PROC_THREAD_ATTRIBUTE_PARENT_PROCESS,
|
||||||
|
&process_handle,
|
||||||
|
sizeof(process_handle),
|
||||||
|
nullptr,
|
||||||
|
nullptr))
|
||||||
|
{
|
||||||
|
Logger::error(L"UpdateProcThreadAttribute() failed. {}", get_last_error_or_default(GetLastError()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
STARTUPINFOEX siex = { 0 };
|
||||||
|
siex.lpAttributeList = pptal;
|
||||||
|
siex.StartupInfo.cb = sizeof(siex);
|
||||||
|
PROCESS_INFORMATION pi = { 0 };
|
||||||
|
auto dwCreationFlags = EXTENDED_STARTUPINFO_PRESENT;
|
||||||
|
|
||||||
|
if (!showWindow)
|
||||||
|
{
|
||||||
|
siex.StartupInfo.dwFlags = STARTF_USESHOWWINDOW;
|
||||||
|
siex.StartupInfo.wShowWindow = SW_HIDE;
|
||||||
|
dwCreationFlags = CREATE_NO_WINDOW;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring cmdLine = executable_args;
|
||||||
|
auto succeeded = CreateProcessW(file.c_str(),
|
||||||
|
&cmdLine[0],
|
||||||
|
nullptr,
|
||||||
|
nullptr,
|
||||||
|
FALSE,
|
||||||
|
dwCreationFlags,
|
||||||
|
nullptr,
|
||||||
|
workingDir,
|
||||||
|
&siex.StartupInfo,
|
||||||
|
&pi);
|
||||||
|
|
||||||
|
if (succeeded)
|
||||||
|
{
|
||||||
|
if (returnPid)
|
||||||
|
{
|
||||||
|
*returnPid = pi.dwProcessId;
|
||||||
|
}
|
||||||
|
if (pi.hProcess)
|
||||||
|
{
|
||||||
|
CloseHandle(pi.hProcess);
|
||||||
|
}
|
||||||
|
if (pi.hThread)
|
||||||
|
{
|
||||||
|
CloseHandle(pi.hThread);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DeleteProcThreadAttributeList(pptal);
|
||||||
|
return succeeded;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RunNonElevatedEx(const std::wstring& file, const std::wstring& params, const std::wstring& working_dir)
|
||||||
|
{
|
||||||
|
bool success = false;
|
||||||
|
HRESULT co_init = E_FAIL;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
co_init = CoInitialize(nullptr);
|
||||||
|
success = ShellExecuteFromExplorer(file.c_str(), params.c_str(), working_dir.c_str());
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
if (SUCCEEDED(co_init))
|
||||||
|
{
|
||||||
|
CoUninitialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<ProcessInfo> RunNonElevatedFailsafe(const std::wstring& file, const std::wstring& params, const std::wstring& working_dir, DWORD handleAccess)
|
||||||
|
{
|
||||||
|
bool launched = RunNonElevatedEx(file, params, working_dir);
|
||||||
|
if (!launched)
|
||||||
|
{
|
||||||
|
Logger::warn(L"RunNonElevatedEx() failed. Trying fallback");
|
||||||
|
std::wstring action_runner_path = get_module_folderpath() + L"\\PowerToys.ActionRunner.exe";
|
||||||
|
std::wstring newParams = L"-run-non-elevated -target \"" + file + L"\" " + params;
|
||||||
|
launched = run_non_elevated(action_runner_path, newParams, nullptr, working_dir.c_str());
|
||||||
|
if (launched)
|
||||||
|
{
|
||||||
|
Logger::trace(L"Started {}", file);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger::warn(L"Failed to start {}", file);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto handles = getProcessHandlesByName(std::filesystem::path{ file }.filename().wstring(), PROCESS_QUERY_INFORMATION | SYNCHRONIZE | handleAccess);
|
||||||
|
if (handles.empty())
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProcessInfo result;
|
||||||
|
result.processID = GetProcessId(handles[0].get());
|
||||||
|
result.processHandle = std::move(handles[0]);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool run_same_elevation(const std::wstring& file, const std::wstring& params, DWORD* returnPid, const wchar_t* workingDir)
|
||||||
|
{
|
||||||
|
auto executable_args = L"\"" + file + L"\"";
|
||||||
|
if (!params.empty())
|
||||||
|
{
|
||||||
|
executable_args += L" " + params;
|
||||||
|
}
|
||||||
|
|
||||||
|
STARTUPINFO si = { sizeof(STARTUPINFO) };
|
||||||
|
PROCESS_INFORMATION pi = { 0 };
|
||||||
|
|
||||||
|
auto succeeded = CreateProcessW(file.c_str(),
|
||||||
|
&executable_args[0],
|
||||||
|
nullptr,
|
||||||
|
nullptr,
|
||||||
|
FALSE,
|
||||||
|
0,
|
||||||
|
nullptr,
|
||||||
|
workingDir,
|
||||||
|
&si,
|
||||||
|
&pi);
|
||||||
|
|
||||||
|
if (succeeded)
|
||||||
|
{
|
||||||
|
if (pi.hProcess)
|
||||||
|
{
|
||||||
|
if (returnPid)
|
||||||
|
{
|
||||||
|
*returnPid = GetProcessId(pi.hProcess);
|
||||||
|
}
|
||||||
|
CloseHandle(pi.hProcess);
|
||||||
|
}
|
||||||
|
if (pi.hThread)
|
||||||
|
{
|
||||||
|
CloseHandle(pi.hThread);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return succeeded;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool check_user_is_admin()
|
||||||
|
{
|
||||||
|
auto freeMemory = [](PSID pSID, PTOKEN_GROUPS pGroupInfo) {
|
||||||
|
if (pSID)
|
||||||
|
{
|
||||||
|
FreeSid(pSID);
|
||||||
|
}
|
||||||
|
if (pGroupInfo)
|
||||||
|
{
|
||||||
|
GlobalFree(pGroupInfo);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
HANDLE hToken;
|
||||||
|
DWORD dwSize = 0;
|
||||||
|
PTOKEN_GROUPS pGroupInfo;
|
||||||
|
SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_NT_AUTHORITY;
|
||||||
|
PSID pSID = NULL;
|
||||||
|
|
||||||
|
// Open a handle to the access token for the calling process.
|
||||||
|
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call GetTokenInformation to get the buffer size.
|
||||||
|
if (!GetTokenInformation(hToken, TokenGroups, NULL, dwSize, &dwSize))
|
||||||
|
{
|
||||||
|
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate the buffer.
|
||||||
|
pGroupInfo = static_cast<PTOKEN_GROUPS>(GlobalAlloc(GPTR, dwSize));
|
||||||
|
|
||||||
|
// Call GetTokenInformation again to get the group information.
|
||||||
|
if (!GetTokenInformation(hToken, TokenGroups, pGroupInfo, dwSize, &dwSize))
|
||||||
|
{
|
||||||
|
freeMemory(pSID, pGroupInfo);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a SID for the BUILTIN\\Administrators group.
|
||||||
|
if (!AllocateAndInitializeSid(&SIDAuth, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pSID))
|
||||||
|
{
|
||||||
|
freeMemory(pSID, pGroupInfo);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop through the group SIDs looking for the administrator SID.
|
||||||
|
for (DWORD i = 0; i < pGroupInfo->GroupCount; ++i)
|
||||||
|
{
|
||||||
|
if (EqualSid(pSID, pGroupInfo->Groups[i].Sid))
|
||||||
|
{
|
||||||
|
freeMemory(pSID, pGroupInfo);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
freeMemory(pSID, pGroupInfo);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsProcessOfWindowElevated(HWND window)
|
||||||
|
{
|
||||||
|
DWORD pid = 0;
|
||||||
|
GetWindowThreadProcessId(window, &pid);
|
||||||
|
if (!pid)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
wil::unique_handle hProcess{ OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION,
|
||||||
|
FALSE,
|
||||||
|
pid) };
|
||||||
|
|
||||||
|
wil::unique_handle token;
|
||||||
|
|
||||||
|
if (OpenProcessToken(hProcess.get(), TOKEN_QUERY, &token))
|
||||||
|
{
|
||||||
|
TOKEN_ELEVATION elevation;
|
||||||
|
DWORD size;
|
||||||
|
if (GetTokenInformation(token.get(), TokenElevation, &elevation, sizeof(elevation), &size))
|
||||||
|
{
|
||||||
|
return elevation.TokenIsElevated != 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
@@ -14,6 +14,8 @@
|
|||||||
#include <winrt/base.h>
|
#include <winrt/base.h>
|
||||||
#include <winrt/Windows.Foundation.Collections.h>
|
#include <winrt/Windows.Foundation.Collections.h>
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
||||||
@@ -22,376 +24,23 @@
|
|||||||
#include <common/utils/process_path.h>
|
#include <common/utils/process_path.h>
|
||||||
#include <common/utils/processApi.h>
|
#include <common/utils/processApi.h>
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
inline std::wstring GetErrorString(HRESULT handle)
|
|
||||||
{
|
|
||||||
_com_error err(handle);
|
|
||||||
return err.ErrorMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool FindDesktopFolderView(REFIID riid, void** ppv)
|
|
||||||
{
|
|
||||||
CComPtr<IShellWindows> spShellWindows;
|
|
||||||
auto result = spShellWindows.CoCreateInstance(CLSID_ShellWindows);
|
|
||||||
if (result != S_OK || spShellWindows == nullptr)
|
|
||||||
{
|
|
||||||
Logger::warn(L"Failed to create instance. {}", GetErrorString(result));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
CComVariant vtLoc(CSIDL_DESKTOP);
|
|
||||||
CComVariant vtEmpty;
|
|
||||||
long lhwnd;
|
|
||||||
CComPtr<IDispatch> spdisp;
|
|
||||||
result = spShellWindows->FindWindowSW(
|
|
||||||
&vtLoc, &vtEmpty, SWC_DESKTOP, &lhwnd, SWFO_NEEDDISPATCH, &spdisp);
|
|
||||||
|
|
||||||
if (result != S_OK || spdisp == nullptr)
|
|
||||||
{
|
|
||||||
Logger::warn(L"Failed to find the window. {}", GetErrorString(result));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
CComPtr<IShellBrowser> spBrowser;
|
|
||||||
result = CComQIPtr<IServiceProvider>(spdisp)->QueryService(SID_STopLevelBrowser,
|
|
||||||
IID_PPV_ARGS(&spBrowser));
|
|
||||||
if (result != S_OK || spBrowser == nullptr)
|
|
||||||
{
|
|
||||||
Logger::warn(L"Failed to query service. {}", GetErrorString(result));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
CComPtr<IShellView> spView;
|
|
||||||
result = spBrowser->QueryActiveShellView(&spView);
|
|
||||||
if (result != S_OK || spView == nullptr)
|
|
||||||
{
|
|
||||||
Logger::warn(L"Failed to query active shell window. {}", GetErrorString(result));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = spView->QueryInterface(riid, ppv);
|
|
||||||
if (result != S_OK || ppv == nullptr || *ppv == nullptr)
|
|
||||||
{
|
|
||||||
Logger::warn(L"Failed to query interface. {}", GetErrorString(result));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool GetDesktopAutomationObject(REFIID riid, void** ppv)
|
|
||||||
{
|
|
||||||
CComPtr<IShellView> spsv;
|
|
||||||
|
|
||||||
// Desktop may not be available on startup
|
|
||||||
auto attempts = 5;
|
|
||||||
for (auto i = 1; i <= attempts; i++)
|
|
||||||
{
|
|
||||||
if (FindDesktopFolderView(IID_PPV_ARGS(&spsv)))
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger::warn(L"FindDesktopFolderView() failed attempt {}", i);
|
|
||||||
|
|
||||||
if (i == attempts)
|
|
||||||
{
|
|
||||||
Logger::warn(L"FindDesktopFolderView() max attempts reached");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Sleep(3000);
|
|
||||||
}
|
|
||||||
|
|
||||||
CComPtr<IDispatch> spdispView;
|
|
||||||
auto result = spsv->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&spdispView));
|
|
||||||
if (result != S_OK)
|
|
||||||
{
|
|
||||||
Logger::warn(L"GetItemObject() failed. {}", GetErrorString(result));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = spdispView->QueryInterface(riid, ppv);
|
|
||||||
if (result != S_OK)
|
|
||||||
{
|
|
||||||
Logger::warn(L"QueryInterface() failed. {}", GetErrorString(result));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool ShellExecuteFromExplorer(
|
|
||||||
PCWSTR pszFile,
|
|
||||||
PCWSTR pszParameters = nullptr,
|
|
||||||
PCWSTR workingDir = L"")
|
|
||||||
{
|
|
||||||
CComPtr<IShellFolderViewDual> spFolderView;
|
|
||||||
if (!GetDesktopAutomationObject(IID_PPV_ARGS(&spFolderView)))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
CComPtr<IDispatch> spdispShell;
|
|
||||||
auto result = spFolderView->get_Application(&spdispShell);
|
|
||||||
if (result != S_OK)
|
|
||||||
{
|
|
||||||
Logger::warn(L"get_Application() failed. {}", GetErrorString(result));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
CComQIPtr<IShellDispatch2>(spdispShell)
|
|
||||||
->ShellExecuteW(CComBSTR(pszFile),
|
|
||||||
CComVariant(pszParameters ? pszParameters : L""),
|
|
||||||
CComVariant(workingDir),
|
|
||||||
CComVariant(L""),
|
|
||||||
CComVariant(SW_SHOWNORMAL));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns true if the current process is running with elevated privileges
|
// Returns true if the current process is running with elevated privileges
|
||||||
inline bool is_process_elevated(const bool use_cached_value = true)
|
bool is_process_elevated(const bool use_cached_value = true);
|
||||||
{
|
|
||||||
auto detection_func = []() {
|
|
||||||
HANDLE token = nullptr;
|
|
||||||
bool elevated = false;
|
|
||||||
|
|
||||||
if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))
|
|
||||||
{
|
|
||||||
TOKEN_ELEVATION elevation;
|
|
||||||
DWORD size;
|
|
||||||
if (GetTokenInformation(token, TokenElevation, &elevation, sizeof(elevation), &size))
|
|
||||||
{
|
|
||||||
elevated = (elevation.TokenIsElevated != 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (token)
|
|
||||||
{
|
|
||||||
CloseHandle(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
return elevated;
|
|
||||||
};
|
|
||||||
static const bool cached_value = detection_func();
|
|
||||||
return use_cached_value ? cached_value : detection_func();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Drops the elevated privileges if present
|
// Drops the elevated privileges if present
|
||||||
inline bool drop_elevated_privileges()
|
bool drop_elevated_privileges();
|
||||||
{
|
|
||||||
HANDLE token = nullptr;
|
|
||||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_DEFAULT | WRITE_OWNER, &token))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
PSID medium_sid = NULL;
|
// Run command as different user, returns process handle or null on failure
|
||||||
if (!::ConvertStringSidToSid(SDDL_ML_MEDIUM, &medium_sid))
|
HANDLE run_as_different_user(const std::wstring& file, const std::wstring& params, const wchar_t* workingDir = nullptr, const bool showWindow = true);
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
TOKEN_MANDATORY_LABEL label = { 0 };
|
// Run command as elevated user, returns process handle or null on failure
|
||||||
label.Label.Attributes = SE_GROUP_INTEGRITY;
|
HANDLE run_elevated(const std::wstring& file, const std::wstring& params, const wchar_t* workingDir = nullptr, const bool showWindow = true);
|
||||||
label.Label.Sid = medium_sid;
|
|
||||||
DWORD size = static_cast<DWORD>(sizeof(TOKEN_MANDATORY_LABEL) + ::GetLengthSid(medium_sid));
|
|
||||||
|
|
||||||
BOOL result = SetTokenInformation(token, TokenIntegrityLevel, &label, size);
|
|
||||||
LocalFree(medium_sid);
|
|
||||||
CloseHandle(token);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run command as different user, returns true if succeeded
|
|
||||||
inline HANDLE run_as_different_user(const std::wstring& file, const std::wstring& params, const wchar_t* workingDir = nullptr, const bool showWindow = true)
|
|
||||||
{
|
|
||||||
Logger::info(L"run_elevated with params={}", params);
|
|
||||||
SHELLEXECUTEINFOW exec_info = { 0 };
|
|
||||||
exec_info.cbSize = sizeof(SHELLEXECUTEINFOW);
|
|
||||||
exec_info.lpVerb = L"runAsUser";
|
|
||||||
exec_info.lpFile = file.c_str();
|
|
||||||
exec_info.lpParameters = params.c_str();
|
|
||||||
exec_info.hwnd = 0;
|
|
||||||
exec_info.fMask = SEE_MASK_NOCLOSEPROCESS;
|
|
||||||
exec_info.lpDirectory = workingDir;
|
|
||||||
exec_info.hInstApp = 0;
|
|
||||||
if (showWindow)
|
|
||||||
{
|
|
||||||
exec_info.nShow = SW_SHOWDEFAULT;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// might have limited success, but only option with ShellExecuteExW
|
|
||||||
exec_info.nShow = SW_HIDE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ShellExecuteExW(&exec_info) ? exec_info.hProcess : nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run command as elevated user, returns true if succeeded
|
|
||||||
inline HANDLE run_elevated(const std::wstring& file, const std::wstring& params, const wchar_t* workingDir = nullptr, const bool showWindow = true)
|
|
||||||
{
|
|
||||||
Logger::info(L"run_elevated with params={}", params);
|
|
||||||
SHELLEXECUTEINFOW exec_info = { 0 };
|
|
||||||
exec_info.cbSize = sizeof(SHELLEXECUTEINFOW);
|
|
||||||
exec_info.lpVerb = L"runas";
|
|
||||||
exec_info.lpFile = file.c_str();
|
|
||||||
exec_info.lpParameters = params.c_str();
|
|
||||||
exec_info.hwnd = 0;
|
|
||||||
exec_info.fMask = SEE_MASK_NOCLOSEPROCESS;
|
|
||||||
exec_info.lpDirectory = workingDir;
|
|
||||||
exec_info.hInstApp = 0;
|
|
||||||
|
|
||||||
if (showWindow)
|
|
||||||
{
|
|
||||||
exec_info.nShow = SW_SHOWDEFAULT;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// might have limited success, but only option with ShellExecuteExW
|
|
||||||
exec_info.nShow = SW_HIDE;
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOL result = ShellExecuteExW(&exec_info);
|
|
||||||
|
|
||||||
return result ? exec_info.hProcess : nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run command as non-elevated user, returns true if succeeded, puts the process id into returnPid if returnPid != NULL
|
// Run command as non-elevated user, returns true if succeeded, puts the process id into returnPid if returnPid != NULL
|
||||||
inline bool run_non_elevated(const std::wstring& file, const std::wstring& params, DWORD* returnPid, const wchar_t* workingDir = nullptr, const bool showWindow = true)
|
bool run_non_elevated(const std::wstring& file, const std::wstring& params, DWORD* returnPid, const wchar_t* workingDir = nullptr, const bool showWindow = true);
|
||||||
{
|
|
||||||
Logger::info(L"run_non_elevated with params={}", params);
|
|
||||||
auto executable_args = L"\"" + file + L"\"";
|
|
||||||
if (!params.empty())
|
|
||||||
{
|
|
||||||
executable_args += L" " + params;
|
|
||||||
}
|
|
||||||
|
|
||||||
HWND hwnd = GetShellWindow();
|
// Try running through the shell's automation object
|
||||||
if (!hwnd)
|
bool RunNonElevatedEx(const std::wstring& file, const std::wstring& params, const std::wstring& working_dir);
|
||||||
{
|
|
||||||
if (GetLastError() == ERROR_SUCCESS)
|
|
||||||
{
|
|
||||||
Logger::warn(L"GetShellWindow() returned null. Shell window is not available");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Logger::error(L"GetShellWindow() failed. {}", get_last_error_or_default(GetLastError()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
DWORD pid;
|
|
||||||
GetWindowThreadProcessId(hwnd, &pid);
|
|
||||||
|
|
||||||
winrt::handle process{ OpenProcess(PROCESS_CREATE_PROCESS, FALSE, pid) };
|
|
||||||
if (!process)
|
|
||||||
{
|
|
||||||
Logger::error(L"OpenProcess() failed. {}", get_last_error_or_default(GetLastError()));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
SIZE_T size = 0;
|
|
||||||
|
|
||||||
InitializeProcThreadAttributeList(nullptr, 1, 0, &size);
|
|
||||||
auto pproc_buffer = std::make_unique<char[]>(size);
|
|
||||||
auto pptal = reinterpret_cast<PPROC_THREAD_ATTRIBUTE_LIST>(pproc_buffer.get());
|
|
||||||
if (!pptal)
|
|
||||||
{
|
|
||||||
Logger::error(L"pptal failed to initialize. {}", get_last_error_or_default(GetLastError()));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!InitializeProcThreadAttributeList(pptal, 1, 0, &size))
|
|
||||||
{
|
|
||||||
Logger::error(L"InitializeProcThreadAttributeList() failed. {}", get_last_error_or_default(GetLastError()));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
HANDLE process_handle = process.get();
|
|
||||||
if (!UpdateProcThreadAttribute(pptal,
|
|
||||||
0,
|
|
||||||
PROC_THREAD_ATTRIBUTE_PARENT_PROCESS,
|
|
||||||
&process_handle,
|
|
||||||
sizeof(process_handle),
|
|
||||||
nullptr,
|
|
||||||
nullptr))
|
|
||||||
{
|
|
||||||
Logger::error(L"UpdateProcThreadAttribute() failed. {}", get_last_error_or_default(GetLastError()));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
STARTUPINFOEX siex = { 0 };
|
|
||||||
siex.lpAttributeList = pptal;
|
|
||||||
siex.StartupInfo.cb = sizeof(siex);
|
|
||||||
PROCESS_INFORMATION pi = { 0 };
|
|
||||||
auto dwCreationFlags = EXTENDED_STARTUPINFO_PRESENT;
|
|
||||||
|
|
||||||
if (!showWindow)
|
|
||||||
{
|
|
||||||
siex.StartupInfo.dwFlags = STARTF_USESHOWWINDOW;
|
|
||||||
siex.StartupInfo.wShowWindow = SW_HIDE;
|
|
||||||
dwCreationFlags = CREATE_NO_WINDOW;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto succeeded = CreateProcessW(file.c_str(),
|
|
||||||
&executable_args[0],
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
FALSE,
|
|
||||||
dwCreationFlags,
|
|
||||||
nullptr,
|
|
||||||
workingDir,
|
|
||||||
&siex.StartupInfo,
|
|
||||||
&pi);
|
|
||||||
if (succeeded)
|
|
||||||
{
|
|
||||||
if (pi.hProcess)
|
|
||||||
{
|
|
||||||
if (returnPid)
|
|
||||||
{
|
|
||||||
*returnPid = GetProcessId(pi.hProcess);
|
|
||||||
}
|
|
||||||
|
|
||||||
CloseHandle(pi.hProcess);
|
|
||||||
}
|
|
||||||
if (pi.hThread)
|
|
||||||
{
|
|
||||||
CloseHandle(pi.hThread);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Logger::error(L"CreateProcessW() failed. {}", get_last_error_or_default(GetLastError()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return succeeded;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool RunNonElevatedEx(const std::wstring& file, const std::wstring& params, const std::wstring& working_dir)
|
|
||||||
{
|
|
||||||
bool success = false;
|
|
||||||
HRESULT co_init = E_FAIL;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
co_init = CoInitialize(nullptr);
|
|
||||||
success = ShellExecuteFromExplorer(file.c_str(), params.c_str(), working_dir.c_str());
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
if (SUCCEEDED(co_init))
|
|
||||||
{
|
|
||||||
CoUninitialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ProcessInfo
|
struct ProcessInfo
|
||||||
{
|
{
|
||||||
@@ -399,172 +48,16 @@ struct ProcessInfo
|
|||||||
DWORD processID = {};
|
DWORD processID = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
inline std::optional<ProcessInfo> RunNonElevatedFailsafe(const std::wstring& file, const std::wstring& params, const std::wstring& working_dir, DWORD handleAccess = 0)
|
// Fallback to ActionRunner when shell route fails and return process info if available
|
||||||
{
|
std::optional<ProcessInfo> RunNonElevatedFailsafe(const std::wstring& file, const std::wstring& params, const std::wstring& working_dir, DWORD handleAccess = 0);
|
||||||
bool launched = RunNonElevatedEx(file, params, working_dir);
|
|
||||||
if (!launched)
|
|
||||||
{
|
|
||||||
Logger::warn(L"RunNonElevatedEx() failed. Trying fallback");
|
|
||||||
std::wstring action_runner_path = get_module_folderpath() + L"\\PowerToys.ActionRunner.exe";
|
|
||||||
std::wstring newParams = fmt::format(L"-run-non-elevated -target \"{}\" {}", file, params);
|
|
||||||
launched = run_non_elevated(action_runner_path, newParams, nullptr, working_dir.c_str());
|
|
||||||
if (launched)
|
|
||||||
{
|
|
||||||
Logger::trace(L"Started {}", file);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Logger::warn(L"Failed to start {}", file);
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto handles = getProcessHandlesByName(std::filesystem::path{ file }.filename().wstring(), PROCESS_QUERY_INFORMATION | SYNCHRONIZE | handleAccess);
|
|
||||||
|
|
||||||
if (handles.empty())
|
|
||||||
return std::nullopt;
|
|
||||||
|
|
||||||
ProcessInfo result;
|
|
||||||
result.processID = GetProcessId(handles[0].get());
|
|
||||||
result.processHandle = std::move(handles[0]);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run command with the same elevation, returns true if succeeded
|
// Run command with the same elevation, returns true if succeeded
|
||||||
inline bool run_same_elevation(const std::wstring& file, const std::wstring& params, DWORD* returnPid, const wchar_t* workingDir = nullptr)
|
bool run_same_elevation(const std::wstring& file, const std::wstring& params, DWORD* returnPid, const wchar_t* workingDir = nullptr);
|
||||||
{
|
|
||||||
auto executable_args = L"\"" + file + L"\"";
|
|
||||||
if (!params.empty())
|
|
||||||
{
|
|
||||||
executable_args += L" " + params;
|
|
||||||
}
|
|
||||||
|
|
||||||
STARTUPINFO si = { sizeof(STARTUPINFO) };
|
|
||||||
PROCESS_INFORMATION pi = { 0 };
|
|
||||||
|
|
||||||
auto succeeded = CreateProcessW(file.c_str(),
|
|
||||||
&executable_args[0],
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
FALSE,
|
|
||||||
0,
|
|
||||||
nullptr,
|
|
||||||
workingDir,
|
|
||||||
&si,
|
|
||||||
&pi);
|
|
||||||
|
|
||||||
if (succeeded)
|
|
||||||
{
|
|
||||||
if (pi.hProcess)
|
|
||||||
{
|
|
||||||
if (returnPid)
|
|
||||||
{
|
|
||||||
*returnPid = GetProcessId(pi.hProcess);
|
|
||||||
}
|
|
||||||
|
|
||||||
CloseHandle(pi.hProcess);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pi.hThread)
|
|
||||||
{
|
|
||||||
CloseHandle(pi.hThread);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return succeeded;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns true if the current process is running from administrator account
|
// Returns true if the current process is running from administrator account
|
||||||
// The function returns true in case of error since we want to return false
|
// The function returns true in case of error since we want to return false
|
||||||
// only in case of a positive verification that the user is not an admin.
|
// only in case of a positive verification that the user is not an admin.
|
||||||
inline bool check_user_is_admin()
|
bool check_user_is_admin();
|
||||||
{
|
|
||||||
auto freeMemory = [](PSID pSID, PTOKEN_GROUPS pGroupInfo) {
|
|
||||||
if (pSID)
|
|
||||||
{
|
|
||||||
FreeSid(pSID);
|
|
||||||
}
|
|
||||||
if (pGroupInfo)
|
|
||||||
{
|
|
||||||
GlobalFree(pGroupInfo);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
HANDLE hToken;
|
bool IsProcessOfWindowElevated(HWND window);
|
||||||
DWORD dwSize = 0;
|
|
||||||
PTOKEN_GROUPS pGroupInfo;
|
|
||||||
SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_NT_AUTHORITY;
|
|
||||||
PSID pSID = NULL;
|
|
||||||
|
|
||||||
// Open a handle to the access token for the calling process.
|
|
||||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call GetTokenInformation to get the buffer size.
|
|
||||||
if (!GetTokenInformation(hToken, TokenGroups, NULL, dwSize, &dwSize))
|
|
||||||
{
|
|
||||||
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allocate the buffer.
|
|
||||||
pGroupInfo = static_cast<PTOKEN_GROUPS>(GlobalAlloc(GPTR, dwSize));
|
|
||||||
|
|
||||||
// Call GetTokenInformation again to get the group information.
|
|
||||||
if (!GetTokenInformation(hToken, TokenGroups, pGroupInfo, dwSize, &dwSize))
|
|
||||||
{
|
|
||||||
freeMemory(pSID, pGroupInfo);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a SID for the BUILTIN\Administrators group.
|
|
||||||
if (!AllocateAndInitializeSid(&SIDAuth, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pSID))
|
|
||||||
{
|
|
||||||
freeMemory(pSID, pGroupInfo);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loop through the group SIDs looking for the administrator SID.
|
|
||||||
for (DWORD i = 0; i < pGroupInfo->GroupCount; ++i)
|
|
||||||
{
|
|
||||||
if (EqualSid(pSID, pGroupInfo->Groups[i].Sid))
|
|
||||||
{
|
|
||||||
freeMemory(pSID, pGroupInfo);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
freeMemory(pSID, pGroupInfo);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool IsProcessOfWindowElevated(HWND window)
|
|
||||||
{
|
|
||||||
DWORD pid = 0;
|
|
||||||
GetWindowThreadProcessId(window, &pid);
|
|
||||||
if (!pid)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
wil::unique_handle hProcess{ OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION,
|
|
||||||
FALSE,
|
|
||||||
pid) };
|
|
||||||
|
|
||||||
wil::unique_handle token;
|
|
||||||
|
|
||||||
if (OpenProcessToken(hProcess.get(), TOKEN_QUERY, &token))
|
|
||||||
{
|
|
||||||
TOKEN_ELEVATION elevation;
|
|
||||||
DWORD size;
|
|
||||||
if (GetTokenInformation(token.get(), TokenElevation, &elevation, sizeof(elevation), &size))
|
|
||||||
{
|
|
||||||
return elevation.TokenIsElevated != 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|||||||
63
src/common/utils/excluded_apps.cpp
Normal file
63
src/common/utils/excluded_apps.cpp
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "excluded_apps.h"
|
||||||
|
|
||||||
|
bool find_app_name_in_path(const std::wstring& where, const std::vector<std::wstring>& what)
|
||||||
|
{
|
||||||
|
for (const auto& row : what)
|
||||||
|
{
|
||||||
|
const auto pos = where.rfind(row);
|
||||||
|
const auto last_slash = where.rfind('\\');
|
||||||
|
// Check that row occurs in where, and its last occurrence contains the first character after the last backslash.
|
||||||
|
if (pos != std::wstring::npos && pos <= last_slash + 1 && pos + row.length() > last_slash)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool find_folder_in_path(const std::wstring& where, const std::vector<std::wstring>& what)
|
||||||
|
{
|
||||||
|
for (const auto& row : what)
|
||||||
|
{
|
||||||
|
if (where.rfind(row) != std::wstring::npos)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool check_excluded_app_with_title(const HWND& hwnd, const std::vector<std::wstring>& excludedApps)
|
||||||
|
{
|
||||||
|
WCHAR title[MAX_TITLE_LENGTH];
|
||||||
|
const int len = GetWindowTextW(hwnd, title, MAX_TITLE_LENGTH);
|
||||||
|
if (len <= 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring titleStr(title);
|
||||||
|
CharUpperBuffW(titleStr.data(), static_cast<DWORD>(titleStr.length()));
|
||||||
|
|
||||||
|
for (const auto& app : excludedApps)
|
||||||
|
{
|
||||||
|
if (titleStr.contains(app))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool check_excluded_app(const HWND& hwnd, const std::wstring& processPath, const std::vector<std::wstring>& excludedApps)
|
||||||
|
{
|
||||||
|
bool res = find_app_name_in_path(processPath, excludedApps);
|
||||||
|
|
||||||
|
if (!res)
|
||||||
|
{
|
||||||
|
res = check_excluded_app_with_title(hwnd, excludedApps);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
@@ -1,67 +1,15 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
// Checks if a process path is included in a list of strings.
|
// Checks if a process path is included in a list of strings.
|
||||||
inline bool find_app_name_in_path(const std::wstring& where, const std::vector<std::wstring>& what)
|
bool find_app_name_in_path(const std::wstring& where, const std::vector<std::wstring>& what);
|
||||||
{
|
|
||||||
for (const auto& row : what)
|
|
||||||
{
|
|
||||||
const auto pos = where.rfind(row);
|
|
||||||
const auto last_slash = where.rfind('\\');
|
|
||||||
//Check that row occurs in where, and its last occurrence contains in itself the first character after the last backslash.
|
|
||||||
if (pos != std::wstring::npos && pos <= last_slash + 1 && pos + row.length() > last_slash)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool find_folder_in_path(const std::wstring& where, const std::vector<std::wstring>& what)
|
bool find_folder_in_path(const std::wstring& where, const std::vector<std::wstring>& what);
|
||||||
{
|
|
||||||
for (const auto& row : what)
|
|
||||||
{
|
|
||||||
const auto pos = where.rfind(row);
|
|
||||||
if (pos != std::wstring::npos)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define MAX_TITLE_LENGTH 255
|
#define MAX_TITLE_LENGTH 255
|
||||||
inline bool check_excluded_app_with_title(const HWND& hwnd, const std::vector<std::wstring>& excludedApps)
|
bool check_excluded_app_with_title(const HWND& hwnd, const std::vector<std::wstring>& excludedApps);
|
||||||
{
|
|
||||||
WCHAR title[MAX_TITLE_LENGTH];
|
|
||||||
int len = GetWindowTextW(hwnd, title, MAX_TITLE_LENGTH);
|
|
||||||
if (len <= 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::wstring titleStr(title);
|
bool check_excluded_app(const HWND& hwnd, const std::wstring& processPath, const std::vector<std::wstring>& excludedApps);
|
||||||
CharUpperBuffW(titleStr.data(), static_cast<DWORD>(titleStr.length()));
|
|
||||||
|
|
||||||
for (const auto& app : excludedApps)
|
|
||||||
{
|
|
||||||
if (titleStr.contains(app))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool check_excluded_app(const HWND& hwnd, const std::wstring& processPath, const std::vector<std::wstring>& excludedApps)
|
|
||||||
{
|
|
||||||
bool res = find_app_name_in_path(processPath, excludedApps);
|
|
||||||
|
|
||||||
if (!res)
|
|
||||||
{
|
|
||||||
res = check_excluded_app_with_title(hwnd, excludedApps);
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|||||||
100
src/common/utils/exec.cpp
Normal file
100
src/common/utils/exec.cpp
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "exec.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
std::optional<std::string> exec_and_read_output(const std::wstring_view command, DWORD timeout_ms)
|
||||||
|
{
|
||||||
|
SECURITY_ATTRIBUTES saAttr{ sizeof(saAttr) };
|
||||||
|
saAttr.bInheritHandle = false;
|
||||||
|
|
||||||
|
constexpr size_t bufferSize = 4096;
|
||||||
|
// We must use a named pipe for async I/O
|
||||||
|
char pipename[MAX_PATH + 1];
|
||||||
|
if (!GetTempFileNameA(R"(\\.\pipe\)", "tmp", 1, pipename))
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
wil::unique_handle readPipe{
|
||||||
|
CreateNamedPipeA(pipename, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, PIPE_UNLIMITED_INSTANCES, bufferSize, bufferSize, 0, &saAttr)
|
||||||
|
};
|
||||||
|
|
||||||
|
saAttr.bInheritHandle = true;
|
||||||
|
wil::unique_handle writePipe{
|
||||||
|
CreateFileA(pipename, GENERIC_WRITE, 0, &saAttr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr)
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!readPipe || !writePipe)
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
PROCESS_INFORMATION piProcInfo{};
|
||||||
|
STARTUPINFOW siStartInfo{ sizeof(siStartInfo) };
|
||||||
|
|
||||||
|
siStartInfo.hStdError = writePipe.get();
|
||||||
|
siStartInfo.hStdOutput = writePipe.get();
|
||||||
|
siStartInfo.dwFlags |= STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
|
||||||
|
siStartInfo.wShowWindow = SW_HIDE;
|
||||||
|
|
||||||
|
std::wstring cmdLine{ command };
|
||||||
|
if (!CreateProcessW(nullptr,
|
||||||
|
cmdLine.data(),
|
||||||
|
nullptr,
|
||||||
|
nullptr,
|
||||||
|
true,
|
||||||
|
NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE,
|
||||||
|
nullptr,
|
||||||
|
nullptr,
|
||||||
|
&siStartInfo,
|
||||||
|
&piProcInfo))
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
// Child process inherited the write end of the pipe, we can close it now
|
||||||
|
writePipe.reset();
|
||||||
|
|
||||||
|
auto closeProcessHandles = wil::scope_exit([&] {
|
||||||
|
CloseHandle(piProcInfo.hThread);
|
||||||
|
CloseHandle(piProcInfo.hProcess);
|
||||||
|
});
|
||||||
|
|
||||||
|
std::string childOutput;
|
||||||
|
bool processExited = false;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
char buffer[bufferSize];
|
||||||
|
DWORD gotBytes = 0;
|
||||||
|
wil::unique_handle ioEvent{ CreateEventW(nullptr, true, false, nullptr) };
|
||||||
|
OVERLAPPED overlapped{ .hEvent = ioEvent.get() };
|
||||||
|
ReadFile(readPipe.get(), buffer, sizeof(buffer), nullptr, &overlapped);
|
||||||
|
|
||||||
|
const std::array<HANDLE, 2> handlesToWait = { overlapped.hEvent, piProcInfo.hProcess };
|
||||||
|
switch (WaitForMultipleObjects(1 + !processExited, handlesToWait.data(), false, timeout_ms))
|
||||||
|
{
|
||||||
|
case WAIT_OBJECT_0 + 1:
|
||||||
|
if (!processExited)
|
||||||
|
{
|
||||||
|
// When the process exits, we can reduce timeout and read the rest of the output w/o possibly big timeout
|
||||||
|
timeout_ms = 1000;
|
||||||
|
processExited = true;
|
||||||
|
closeProcessHandles.reset();
|
||||||
|
}
|
||||||
|
[[fallthrough]];
|
||||||
|
case WAIT_OBJECT_0:
|
||||||
|
if (GetOverlappedResultEx(readPipe.get(), &overlapped, &gotBytes, timeout_ms, true))
|
||||||
|
{
|
||||||
|
childOutput += std::string_view{ buffer, gotBytes };
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
[[fallthrough]];
|
||||||
|
default:
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exit:
|
||||||
|
CancelIo(readPipe.get());
|
||||||
|
return childOutput;
|
||||||
|
}
|
||||||
@@ -15,94 +15,4 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
inline std::optional<std::string> exec_and_read_output(const std::wstring_view command, DWORD timeout_ms = 30000)
|
std::optional<std::string> exec_and_read_output(const std::wstring_view command, DWORD timeout_ms = 30000);
|
||||||
{
|
|
||||||
SECURITY_ATTRIBUTES saAttr{ sizeof(saAttr) };
|
|
||||||
saAttr.bInheritHandle = false;
|
|
||||||
|
|
||||||
constexpr size_t bufferSize = 4096;
|
|
||||||
// We must use a named pipe for async I/O
|
|
||||||
char pipename[MAX_PATH + 1];
|
|
||||||
if (!GetTempFileNameA(R"(\\.\pipe\)", "tmp", 1, pipename))
|
|
||||||
{
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
wil::unique_handle readPipe{ CreateNamedPipeA(pipename, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, PIPE_UNLIMITED_INSTANCES, bufferSize, bufferSize, 0, &saAttr) };
|
|
||||||
|
|
||||||
saAttr.bInheritHandle = true;
|
|
||||||
wil::unique_handle writePipe{ CreateFileA(pipename, GENERIC_WRITE, 0, &saAttr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr) };
|
|
||||||
|
|
||||||
if (!readPipe || !writePipe)
|
|
||||||
{
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
PROCESS_INFORMATION piProcInfo{};
|
|
||||||
STARTUPINFOW siStartInfo{ sizeof(siStartInfo) };
|
|
||||||
|
|
||||||
siStartInfo.hStdError = writePipe.get();
|
|
||||||
siStartInfo.hStdOutput = writePipe.get();
|
|
||||||
siStartInfo.dwFlags |= STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
|
|
||||||
siStartInfo.wShowWindow = SW_HIDE;
|
|
||||||
|
|
||||||
std::wstring cmdLine{ command };
|
|
||||||
if (!CreateProcessW(nullptr,
|
|
||||||
cmdLine.data(),
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
true,
|
|
||||||
NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE,
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
&siStartInfo,
|
|
||||||
&piProcInfo))
|
|
||||||
{
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
// Child process inherited the write end of the pipe, we can close it now
|
|
||||||
writePipe.reset();
|
|
||||||
|
|
||||||
auto closeProcessHandles = wil::scope_exit([&] {
|
|
||||||
CloseHandle(piProcInfo.hThread);
|
|
||||||
CloseHandle(piProcInfo.hProcess);
|
|
||||||
});
|
|
||||||
|
|
||||||
std::string childOutput;
|
|
||||||
bool processExited = false;
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
char buffer[bufferSize];
|
|
||||||
DWORD gotBytes = 0;
|
|
||||||
wil::unique_handle IOEvent{ CreateEventW(nullptr, true, false, nullptr) };
|
|
||||||
OVERLAPPED overlapped{ .hEvent = IOEvent.get() };
|
|
||||||
ReadFile(readPipe.get(), buffer, sizeof(buffer), nullptr, &overlapped);
|
|
||||||
|
|
||||||
const std::array<HANDLE, 2> handlesToWait = { overlapped.hEvent, piProcInfo.hProcess };
|
|
||||||
switch (WaitForMultipleObjects(1 + !processExited, handlesToWait.data(), false, timeout_ms))
|
|
||||||
{
|
|
||||||
case WAIT_OBJECT_0 + 1:
|
|
||||||
if (!processExited)
|
|
||||||
{
|
|
||||||
// When the process exits, we can reduce timeout and read the rest of the output w/o possibly big timeout
|
|
||||||
timeout_ms = 1000;
|
|
||||||
processExited = true;
|
|
||||||
closeProcessHandles.reset();
|
|
||||||
}
|
|
||||||
[[fallthrough]];
|
|
||||||
case WAIT_OBJECT_0:
|
|
||||||
if (GetOverlappedResultEx(readPipe.get(), &overlapped, &gotBytes, timeout_ms, true))
|
|
||||||
{
|
|
||||||
childOutput += std::string_view{ buffer, gotBytes };
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// Timeout
|
|
||||||
[[fallthrough]];
|
|
||||||
default:
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exit:
|
|
||||||
CancelIo(readPipe.get());
|
|
||||||
return childOutput;
|
|
||||||
}
|
|
||||||
|
|||||||
12
src/common/utils/game_mode.cpp
Normal file
12
src/common/utils/game_mode.cpp
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "game_mode.h"
|
||||||
|
|
||||||
|
bool detect_game_mode()
|
||||||
|
{
|
||||||
|
QUERY_USER_NOTIFICATION_STATE notification_state;
|
||||||
|
if (SHQueryUserNotificationState(¬ification_state) != S_OK)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return notification_state == QUNS_RUNNING_D3D_FULL_SCREEN;
|
||||||
|
}
|
||||||
@@ -1,12 +1,4 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <shellapi.h>
|
#include <shellapi.h>
|
||||||
|
|
||||||
inline bool detect_game_mode()
|
bool detect_game_mode();
|
||||||
{
|
|
||||||
QUERY_USER_NOTIFICATION_STATE notification_state;
|
|
||||||
if (SHQueryUserNotificationState(¬ification_state) != S_OK)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return (notification_state == QUNS_RUNNING_D3D_FULL_SCREEN);
|
|
||||||
}
|
|
||||||
|
|||||||
236
src/common/utils/gpo.cpp
Normal file
236
src/common/utils/gpo.cpp
Normal file
@@ -0,0 +1,236 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "gpo.h"
|
||||||
|
|
||||||
|
namespace powertoys_gpo
|
||||||
|
{
|
||||||
|
std::optional<std::wstring> readRegistryStringValue(HKEY hRootKey, const std::wstring& subKey, const std::wstring& value_name, const bool is_multi_line_text)
|
||||||
|
{
|
||||||
|
// Set value type
|
||||||
|
DWORD reg_value_type = REG_SZ;
|
||||||
|
DWORD reg_flags = RRF_RT_REG_SZ;
|
||||||
|
if (is_multi_line_text)
|
||||||
|
{
|
||||||
|
reg_value_type = REG_MULTI_SZ;
|
||||||
|
reg_flags = RRF_RT_REG_MULTI_SZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD string_buffer_capacity;
|
||||||
|
// Request required buffer capacity / string length
|
||||||
|
if (RegGetValueW(hRootKey, subKey.c_str(), value_name.c_str(), reg_flags, ®_value_type, NULL, &string_buffer_capacity) != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
else if (string_buffer_capacity == 0)
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegGetValueW overshoots sometimes. Use a buffer first to not have characters past the string end.
|
||||||
|
wchar_t* temp_buffer = new wchar_t[string_buffer_capacity / sizeof(wchar_t) + 1];
|
||||||
|
// Read string
|
||||||
|
if (RegGetValueW(hRootKey, subKey.c_str(), value_name.c_str(), reg_flags, ®_value_type, temp_buffer, &string_buffer_capacity) != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
delete[] temp_buffer;
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert buffer to std::wstring
|
||||||
|
std::wstring string_value = L"";
|
||||||
|
if (reg_value_type == REG_MULTI_SZ)
|
||||||
|
{
|
||||||
|
// If it is REG_MULTI_SZ handle this way
|
||||||
|
wchar_t* currentString = temp_buffer;
|
||||||
|
while (*currentString != L'\0')
|
||||||
|
{
|
||||||
|
// If first entry then assign the string, else add to the string
|
||||||
|
string_value = (string_value == L"") ? currentString : (string_value + L"\r\n" + currentString);
|
||||||
|
currentString += wcslen(currentString) + 1; // Move to the next string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If it is REG_SZ handle this way
|
||||||
|
string_value = temp_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete buffer, return string value
|
||||||
|
delete[] temp_buffer;
|
||||||
|
return string_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
gpo_rule_configured_t getConfiguredValue(const std::wstring& registry_value_name)
|
||||||
|
{
|
||||||
|
HKEY key{};
|
||||||
|
DWORD value = 0xFFFFFFFE;
|
||||||
|
DWORD valueSize = sizeof(value);
|
||||||
|
|
||||||
|
bool machine_key_found = true;
|
||||||
|
if (auto res = RegOpenKeyExW(POLICIES_SCOPE_MACHINE, POLICIES_PATH.c_str(), 0, KEY_READ, &key); res != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
machine_key_found = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (machine_key_found)
|
||||||
|
{
|
||||||
|
// If the path was found in the machine, we need to check if the value for the policy exists.
|
||||||
|
auto res = RegQueryValueExW(key, registry_value_name.c_str(), nullptr, nullptr, reinterpret_cast<LPBYTE>(&value), &valueSize);
|
||||||
|
|
||||||
|
RegCloseKey(key);
|
||||||
|
|
||||||
|
if (res != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
// Value not found on the path.
|
||||||
|
machine_key_found = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!machine_key_found)
|
||||||
|
{
|
||||||
|
// If there's no value found on the machine scope, try to get it from the user scope.
|
||||||
|
if (auto res = RegOpenKeyExW(POLICIES_SCOPE_USER, POLICIES_PATH.c_str(), 0, KEY_READ, &key); res != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
if (res == ERROR_FILE_NOT_FOUND)
|
||||||
|
{
|
||||||
|
return gpo_rule_configured_not_configured;
|
||||||
|
}
|
||||||
|
return gpo_rule_configured_unavailable;
|
||||||
|
}
|
||||||
|
auto res = RegQueryValueExW(key, registry_value_name.c_str(), nullptr, nullptr, reinterpret_cast<LPBYTE>(&value), &valueSize);
|
||||||
|
RegCloseKey(key);
|
||||||
|
|
||||||
|
if (res != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
return gpo_rule_configured_not_configured;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (value)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
return gpo_rule_configured_disabled;
|
||||||
|
case 1:
|
||||||
|
return gpo_rule_configured_enabled;
|
||||||
|
default:
|
||||||
|
return gpo_rule_configured_wrong_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::wstring> getPolicyListValue(const std::wstring& registry_list_path, const std::wstring& registry_list_value_name)
|
||||||
|
{
|
||||||
|
// This function returns the value of an entry of a policy list. The user scope is only checked, if the list is not enabled for the machine to not mix the lists.
|
||||||
|
|
||||||
|
HKEY key{};
|
||||||
|
|
||||||
|
// Try to read from the machine list.
|
||||||
|
bool machine_list_found = false;
|
||||||
|
if (RegOpenKeyExW(POLICIES_SCOPE_MACHINE, registry_list_path.c_str(), 0, KEY_READ, &key) == ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
machine_list_found = true;
|
||||||
|
RegCloseKey(key);
|
||||||
|
|
||||||
|
// If the path exists in the machine registry, we try to read the value.
|
||||||
|
auto regValueData = readRegistryStringValue(POLICIES_SCOPE_MACHINE, registry_list_path, registry_list_value_name);
|
||||||
|
|
||||||
|
if (regValueData.has_value())
|
||||||
|
{
|
||||||
|
// Return the value from the machine list.
|
||||||
|
return *regValueData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no list exists for machine, we try to read from the user list.
|
||||||
|
if (!machine_list_found)
|
||||||
|
{
|
||||||
|
if (RegOpenKeyExW(POLICIES_SCOPE_USER, registry_list_path.c_str(), 0, KEY_READ, &key) == ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
RegCloseKey(key);
|
||||||
|
|
||||||
|
// If the path exists in the user registry, we try to read the value.
|
||||||
|
auto regValueData = readRegistryStringValue(POLICIES_SCOPE_USER, registry_list_path, registry_list_value_name);
|
||||||
|
|
||||||
|
if (regValueData.has_value())
|
||||||
|
{
|
||||||
|
// Return the value from the user list.
|
||||||
|
return *regValueData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No list exists for machine and user, or no value was found in the list, or an error ocurred while reading the value.
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
gpo_rule_configured_t getUtilityEnabledValue(const std::wstring& utility_name)
|
||||||
|
{
|
||||||
|
auto individual_value = getConfiguredValue(utility_name);
|
||||||
|
|
||||||
|
if (individual_value == gpo_rule_configured_disabled || individual_value == gpo_rule_configured_enabled)
|
||||||
|
{
|
||||||
|
return individual_value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return getConfiguredValue(POLICY_CONFIGURE_ENABLED_GLOBAL_ALL_UTILITIES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gpo_rule_configured_t getRunPluginEnabledValue(std::string pluginID)
|
||||||
|
{
|
||||||
|
if (pluginID == "" || pluginID == " ")
|
||||||
|
{
|
||||||
|
// this plugin id can't exist in the registry
|
||||||
|
return gpo_rule_configured_not_configured;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring plugin_id(pluginID.begin(), pluginID.end());
|
||||||
|
auto individual_plugin_setting = getPolicyListValue(POWER_LAUNCHER_INDIVIDUAL_PLUGIN_ENABLED_LIST_PATH, plugin_id);
|
||||||
|
|
||||||
|
if (individual_plugin_setting.has_value())
|
||||||
|
{
|
||||||
|
if (*individual_plugin_setting == L"0")
|
||||||
|
{
|
||||||
|
// force disabled
|
||||||
|
return gpo_rule_configured_disabled;
|
||||||
|
}
|
||||||
|
else if (*individual_plugin_setting == L"1")
|
||||||
|
{
|
||||||
|
// force enabled
|
||||||
|
return gpo_rule_configured_enabled;
|
||||||
|
}
|
||||||
|
else if (*individual_plugin_setting == L"2")
|
||||||
|
{
|
||||||
|
// user takes control
|
||||||
|
return gpo_rule_configured_not_configured;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return gpo_rule_configured_wrong_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If no individual plugin policy exists, we check the policy with the setting for all plugins.
|
||||||
|
return getConfiguredValue(POLICY_CONFIGURE_ENABLED_POWER_LAUNCHER_ALL_PLUGINS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring getConfiguredMwbPolicyDefinedIpMappingRules()
|
||||||
|
{
|
||||||
|
// Important: HKLM has priority over HKCU
|
||||||
|
auto mapping_rules = readRegistryStringValue(HKEY_LOCAL_MACHINE, POLICIES_PATH, POLICY_MWB_POLICY_DEFINED_IP_MAPPING_RULES, true);
|
||||||
|
if (!mapping_rules.has_value())
|
||||||
|
{
|
||||||
|
mapping_rules = readRegistryStringValue(HKEY_CURRENT_USER, POLICIES_PATH, POLICY_MWB_POLICY_DEFINED_IP_MAPPING_RULES, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// return value
|
||||||
|
if (mapping_rules.has_value())
|
||||||
|
{
|
||||||
|
return mapping_rules.value();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return std::wstring();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -105,176 +105,10 @@ namespace powertoys_gpo
|
|||||||
|
|
||||||
// Methods used for reading the registry
|
// Methods used for reading the registry
|
||||||
#pragma region ReadRegistryMethods
|
#pragma region ReadRegistryMethods
|
||||||
inline std::optional<std::wstring> readRegistryStringValue(HKEY hRootKey, const std::wstring& subKey, const std::wstring& value_name, const bool is_multi_line_text = false)
|
std::optional<std::wstring> readRegistryStringValue(HKEY hRootKey, const std::wstring& subKey, const std::wstring& value_name, const bool is_multi_line_text = false);
|
||||||
{
|
gpo_rule_configured_t getConfiguredValue(const std::wstring& registry_value_name);
|
||||||
// Set value type
|
std::optional<std::wstring> getPolicyListValue(const std::wstring& registry_list_path, const std::wstring& registry_list_value_name);
|
||||||
DWORD reg_value_type = REG_SZ;
|
gpo_rule_configured_t getUtilityEnabledValue(const std::wstring& utility_name);
|
||||||
DWORD reg_flags = RRF_RT_REG_SZ;
|
|
||||||
if (is_multi_line_text)
|
|
||||||
{
|
|
||||||
reg_value_type = REG_MULTI_SZ;
|
|
||||||
reg_flags = RRF_RT_REG_MULTI_SZ;
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD string_buffer_capacity;
|
|
||||||
// Request required buffer capacity / string length
|
|
||||||
if (RegGetValueW(hRootKey, subKey.c_str(), value_name.c_str(), reg_flags, ®_value_type, NULL, &string_buffer_capacity) != ERROR_SUCCESS)
|
|
||||||
{
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
else if (string_buffer_capacity == 0)
|
|
||||||
{
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegGetValueW overshoots sometimes. Use a buffer first to not have characters past the string end.
|
|
||||||
wchar_t* temp_buffer = new wchar_t[string_buffer_capacity / sizeof(wchar_t) + 1];
|
|
||||||
// Read string
|
|
||||||
if (RegGetValueW(hRootKey, subKey.c_str(), value_name.c_str(), reg_flags, ®_value_type, temp_buffer, &string_buffer_capacity) != ERROR_SUCCESS)
|
|
||||||
{
|
|
||||||
delete temp_buffer;
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert buffer to std::wstring
|
|
||||||
std::wstring string_value = L"";
|
|
||||||
if (reg_value_type == REG_MULTI_SZ)
|
|
||||||
{
|
|
||||||
// If it is REG_MULTI_SZ handle this way
|
|
||||||
wchar_t* currentString = temp_buffer;
|
|
||||||
while (*currentString != L'\0')
|
|
||||||
{
|
|
||||||
// If first entry then assign the string, else add to the string
|
|
||||||
string_value = (string_value == L"") ? currentString : (string_value + L"\r\n" + currentString);
|
|
||||||
currentString += wcslen(currentString) + 1; // Move to the next string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// If it is REG_SZ handle this way
|
|
||||||
string_value = temp_buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
// delete buffer, return string value
|
|
||||||
delete temp_buffer;
|
|
||||||
return string_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline gpo_rule_configured_t getConfiguredValue(const std::wstring& registry_value_name)
|
|
||||||
{
|
|
||||||
HKEY key{};
|
|
||||||
DWORD value = 0xFFFFFFFE;
|
|
||||||
DWORD valueSize = sizeof(value);
|
|
||||||
|
|
||||||
bool machine_key_found = true;
|
|
||||||
if (auto res = RegOpenKeyExW(POLICIES_SCOPE_MACHINE, POLICIES_PATH.c_str(), 0, KEY_READ, &key); res != ERROR_SUCCESS)
|
|
||||||
{
|
|
||||||
machine_key_found = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (machine_key_found)
|
|
||||||
{
|
|
||||||
// If the path was found in the machine, we need to check if the value for the policy exists.
|
|
||||||
auto res = RegQueryValueExW(key, registry_value_name.c_str(), nullptr, nullptr, reinterpret_cast<LPBYTE>(&value), &valueSize);
|
|
||||||
|
|
||||||
RegCloseKey(key);
|
|
||||||
|
|
||||||
if (res != ERROR_SUCCESS)
|
|
||||||
{
|
|
||||||
// Value not found on the path.
|
|
||||||
machine_key_found = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!machine_key_found)
|
|
||||||
{
|
|
||||||
// If there's no value found on the machine scope, try to get it from the user scope.
|
|
||||||
if (auto res = RegOpenKeyExW(POLICIES_SCOPE_USER, POLICIES_PATH.c_str(), 0, KEY_READ, &key); res != ERROR_SUCCESS)
|
|
||||||
{
|
|
||||||
if (res == ERROR_FILE_NOT_FOUND)
|
|
||||||
{
|
|
||||||
return gpo_rule_configured_not_configured;
|
|
||||||
}
|
|
||||||
return gpo_rule_configured_unavailable;
|
|
||||||
}
|
|
||||||
auto res = RegQueryValueExW(key, registry_value_name.c_str(), nullptr, nullptr, reinterpret_cast<LPBYTE>(&value), &valueSize);
|
|
||||||
RegCloseKey(key);
|
|
||||||
|
|
||||||
if (res != ERROR_SUCCESS)
|
|
||||||
{
|
|
||||||
return gpo_rule_configured_not_configured;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (value)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
return gpo_rule_configured_disabled;
|
|
||||||
case 1:
|
|
||||||
return gpo_rule_configured_enabled;
|
|
||||||
default:
|
|
||||||
return gpo_rule_configured_wrong_value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::optional<std::wstring> getPolicyListValue(const std::wstring& registry_list_path, const std::wstring& registry_list_value_name)
|
|
||||||
{
|
|
||||||
// This function returns the value of an entry of a policy list. The user scope is only checked, if the list is not enabled for the machine to not mix the lists.
|
|
||||||
|
|
||||||
HKEY key{};
|
|
||||||
|
|
||||||
// Try to read from the machine list.
|
|
||||||
bool machine_list_found = false;
|
|
||||||
if (RegOpenKeyExW(POLICIES_SCOPE_MACHINE, registry_list_path.c_str(), 0, KEY_READ, &key) == ERROR_SUCCESS)
|
|
||||||
{
|
|
||||||
machine_list_found = true;
|
|
||||||
RegCloseKey(key);
|
|
||||||
|
|
||||||
// If the path exists in the machine registry, we try to read the value.
|
|
||||||
auto regValueData = readRegistryStringValue(POLICIES_SCOPE_MACHINE, registry_list_path, registry_list_value_name);
|
|
||||||
|
|
||||||
if (regValueData.has_value())
|
|
||||||
{
|
|
||||||
// Return the value from the machine list.
|
|
||||||
return *regValueData;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no list exists for machine, we try to read from the user list.
|
|
||||||
if (!machine_list_found)
|
|
||||||
{
|
|
||||||
if (RegOpenKeyExW(POLICIES_SCOPE_USER, registry_list_path.c_str(), 0, KEY_READ, &key) == ERROR_SUCCESS)
|
|
||||||
{
|
|
||||||
RegCloseKey(key);
|
|
||||||
|
|
||||||
// If the path exists in the user registry, we try to read the value.
|
|
||||||
auto regValueData = readRegistryStringValue(POLICIES_SCOPE_USER, registry_list_path, registry_list_value_name);
|
|
||||||
|
|
||||||
if (regValueData.has_value())
|
|
||||||
{
|
|
||||||
// Return the value from the user list.
|
|
||||||
return *regValueData;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// No list exists for machine and user, or no value was found in the list, or an error ocurred while reading the value.
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline gpo_rule_configured_t getUtilityEnabledValue(const std::wstring& utility_name)
|
|
||||||
{
|
|
||||||
auto individual_value = getConfiguredValue(utility_name);
|
|
||||||
|
|
||||||
if (individual_value == gpo_rule_configured_disabled || individual_value == gpo_rule_configured_enabled)
|
|
||||||
{
|
|
||||||
return individual_value;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return getConfiguredValue(POLICY_CONFIGURE_ENABLED_GLOBAL_ALL_UTILITIES);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#pragma endregion ReadRegistryMethods
|
#pragma endregion ReadRegistryMethods
|
||||||
|
|
||||||
// Utility enabled state policies
|
// Utility enabled state policies
|
||||||
@@ -544,45 +378,7 @@ namespace powertoys_gpo
|
|||||||
return getConfiguredValue(POLICY_CONFIGURE_RUN_AT_STARTUP);
|
return getConfiguredValue(POLICY_CONFIGURE_RUN_AT_STARTUP);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline gpo_rule_configured_t getRunPluginEnabledValue(std::string pluginID)
|
gpo_rule_configured_t getRunPluginEnabledValue(std::string pluginID);
|
||||||
{
|
|
||||||
if (pluginID == "" || pluginID == " ")
|
|
||||||
{
|
|
||||||
// this plugin id can't exist in the registry
|
|
||||||
return gpo_rule_configured_not_configured;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::wstring plugin_id(pluginID.begin(), pluginID.end());
|
|
||||||
auto individual_plugin_setting = getPolicyListValue(POWER_LAUNCHER_INDIVIDUAL_PLUGIN_ENABLED_LIST_PATH, plugin_id);
|
|
||||||
|
|
||||||
if (individual_plugin_setting.has_value())
|
|
||||||
{
|
|
||||||
if (*individual_plugin_setting == L"0")
|
|
||||||
{
|
|
||||||
// force disabled
|
|
||||||
return gpo_rule_configured_disabled;
|
|
||||||
}
|
|
||||||
else if (*individual_plugin_setting == L"1")
|
|
||||||
{
|
|
||||||
// force enabled
|
|
||||||
return gpo_rule_configured_enabled;
|
|
||||||
}
|
|
||||||
else if (*individual_plugin_setting == L"2")
|
|
||||||
{
|
|
||||||
// user takes control
|
|
||||||
return gpo_rule_configured_not_configured;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return gpo_rule_configured_wrong_value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// If no individual plugin policy exists, we check the policy with the setting for all plugins.
|
|
||||||
return getConfiguredValue(POLICY_CONFIGURE_ENABLED_POWER_LAUNCHER_ALL_PLUGINS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline gpo_rule_configured_t getAllowedAdvancedPasteOnlineAIModelsValue()
|
inline gpo_rule_configured_t getAllowedAdvancedPasteOnlineAIModelsValue()
|
||||||
{
|
{
|
||||||
@@ -664,25 +460,7 @@ namespace powertoys_gpo
|
|||||||
return getConfiguredValue(POLICY_MWB_DISABLE_USER_DEFINED_IP_MAPPING_RULES);
|
return getConfiguredValue(POLICY_MWB_DISABLE_USER_DEFINED_IP_MAPPING_RULES);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::wstring getConfiguredMwbPolicyDefinedIpMappingRules()
|
std::wstring getConfiguredMwbPolicyDefinedIpMappingRules();
|
||||||
{
|
|
||||||
// Important: HKLM has priority over HKCU
|
|
||||||
auto mapping_rules = readRegistryStringValue(HKEY_LOCAL_MACHINE, POLICIES_PATH, POLICY_MWB_POLICY_DEFINED_IP_MAPPING_RULES, true);
|
|
||||||
if (!mapping_rules.has_value())
|
|
||||||
{
|
|
||||||
mapping_rules = readRegistryStringValue(HKEY_CURRENT_USER, POLICIES_PATH, POLICY_MWB_POLICY_DEFINED_IP_MAPPING_RULES, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// return value
|
|
||||||
if (mapping_rules.has_value())
|
|
||||||
{
|
|
||||||
return mapping_rules.value();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return std::wstring();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline gpo_rule_configured_t getConfiguredNewPlusHideTemplateFilenameExtensionValue()
|
inline gpo_rule_configured_t getConfiguredNewPlusHideTemplateFilenameExtensionValue()
|
||||||
{
|
{
|
||||||
|
|||||||
50
src/common/utils/json.cpp
Normal file
50
src/common/utils/json.cpp
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "json.h"
|
||||||
|
|
||||||
|
namespace json
|
||||||
|
{
|
||||||
|
std::optional<JsonObject> from_file(std::wstring_view file_name)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
std::ifstream file(file_name.data(), std::ios::binary);
|
||||||
|
if (file.is_open())
|
||||||
|
{
|
||||||
|
using iterator = std::istreambuf_iterator<char>;
|
||||||
|
std::string objStr{ iterator{ file }, iterator{} };
|
||||||
|
return JsonValue::Parse(winrt::to_hstring(objStr)).GetObjectW();
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void to_file(std::wstring_view file_name, const JsonObject& obj)
|
||||||
|
{
|
||||||
|
std::wstring objStr{ obj.Stringify().c_str() };
|
||||||
|
std::ofstream{ file_name.data(), std::ios::binary } << winrt::to_string(objStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has(const json::JsonObject& o, std::wstring_view name, const json::JsonValueType type)
|
||||||
|
{
|
||||||
|
return o.HasKey(name) && o.GetNamedValue(name).ValueType() == type;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonValue value(const bool boolean)
|
||||||
|
{
|
||||||
|
return json::JsonValue::CreateBooleanValue(boolean);
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonValue value(JsonObject valueObject)
|
||||||
|
{
|
||||||
|
return valueObject.as<JsonValue>();
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonValue value(JsonValue valueObject)
|
||||||
|
{
|
||||||
|
return valueObject; // identity function overload for convenience
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,38 +11,11 @@ namespace json
|
|||||||
{
|
{
|
||||||
using namespace winrt::Windows::Data::Json;
|
using namespace winrt::Windows::Data::Json;
|
||||||
|
|
||||||
inline std::optional<JsonObject> from_file(std::wstring_view file_name)
|
std::optional<JsonObject> from_file(std::wstring_view file_name);
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
std::ifstream file(file_name.data(), std::ios::binary);
|
|
||||||
if (file.is_open())
|
|
||||||
{
|
|
||||||
using isbi = std::istreambuf_iterator<char>;
|
|
||||||
std::string obj_str{ isbi{ file }, isbi{} };
|
|
||||||
return JsonValue::Parse(winrt::to_hstring(obj_str)).GetObjectW();
|
|
||||||
}
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void to_file(std::wstring_view file_name, const JsonObject& obj)
|
void to_file(std::wstring_view file_name, const JsonObject& obj);
|
||||||
{
|
|
||||||
std::wstring obj_str{ obj.Stringify().c_str() };
|
|
||||||
std::ofstream{ file_name.data(), std::ios::binary } << winrt::to_string(obj_str);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool has(
|
bool has(const json::JsonObject& o, std::wstring_view name, const json::JsonValueType type = JsonValueType::Object);
|
||||||
const json::JsonObject& o,
|
|
||||||
std::wstring_view name,
|
|
||||||
const json::JsonValueType type = JsonValueType::Object)
|
|
||||||
{
|
|
||||||
return o.HasKey(name) && o.GetNamedValue(name).ValueType() == type;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline std::enable_if_t<std::is_arithmetic_v<T>, JsonValue> value(const T arithmetic)
|
inline std::enable_if_t<std::is_arithmetic_v<T>, JsonValue> value(const T arithmetic)
|
||||||
@@ -56,20 +29,11 @@ namespace json
|
|||||||
return json::JsonValue::CreateStringValue(s);
|
return json::JsonValue::CreateStringValue(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline JsonValue value(const bool boolean)
|
JsonValue value(const bool boolean);
|
||||||
{
|
|
||||||
return json::JsonValue::CreateBooleanValue(boolean);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline JsonValue value(JsonObject value)
|
JsonValue value(JsonObject value);
|
||||||
{
|
|
||||||
return value.as<JsonValue>();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline JsonValue value(JsonValue value)
|
JsonValue value(JsonValue value);
|
||||||
{
|
|
||||||
return value; // identity function overload for convenience
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T, typename D = std::optional<T>>
|
template<typename T, typename D = std::optional<T>>
|
||||||
requires std::constructible_from<std::optional<T>, D>
|
requires std::constructible_from<std::optional<T>, D>
|
||||||
|
|||||||
19
src/common/utils/language_helper.cpp
Normal file
19
src/common/utils/language_helper.cpp
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "language_helper.h"
|
||||||
|
|
||||||
|
namespace LanguageHelpers
|
||||||
|
{
|
||||||
|
std::wstring load_language()
|
||||||
|
{
|
||||||
|
std::filesystem::path languageJsonFilePath(PTSettingsHelper::get_root_save_folder_location() + L"\\language.json");
|
||||||
|
|
||||||
|
auto langJson = json::from_file(languageJsonFilePath.c_str());
|
||||||
|
if (!langJson.has_value())
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring language = langJson->GetNamedString(L"language", L"").c_str();
|
||||||
|
return language;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,17 +6,5 @@
|
|||||||
|
|
||||||
namespace LanguageHelpers
|
namespace LanguageHelpers
|
||||||
{
|
{
|
||||||
inline std::wstring load_language()
|
std::wstring load_language();
|
||||||
{
|
|
||||||
std::filesystem::path languageJsonFilePath(PTSettingsHelper::get_root_save_folder_location() + L"\\language.json");
|
|
||||||
|
|
||||||
auto langJson = json::from_file(languageJsonFilePath.c_str());
|
|
||||||
if (!langJson.has_value())
|
|
||||||
{
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::wstring language = langJson->GetNamedString(L"language", L"").c_str();
|
|
||||||
return language;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
96
src/common/utils/logger_helper.cpp
Normal file
96
src/common/utils/logger_helper.cpp
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "logger_helper.h"
|
||||||
|
|
||||||
|
namespace LoggerHelpers
|
||||||
|
{
|
||||||
|
std::filesystem::path get_log_folder_path(std::wstring_view appPath)
|
||||||
|
{
|
||||||
|
std::filesystem::path logFolderPath(appPath);
|
||||||
|
logFolderPath.append(LogSettings::logPath);
|
||||||
|
logFolderPath.append(get_product_version());
|
||||||
|
return logFolderPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool delete_old_log_folder(const std::filesystem::path& logFolderPath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
std::filesystem::remove_all(logFolderPath);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (std::filesystem::filesystem_error& e)
|
||||||
|
{
|
||||||
|
Logger::error("Failed to delete old log folder: {}", e.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool dir_exists(std::filesystem::path dir)
|
||||||
|
{
|
||||||
|
std::error_code err;
|
||||||
|
auto entry = std::filesystem::directory_entry(dir, err);
|
||||||
|
if (err.value())
|
||||||
|
{
|
||||||
|
Logger::error("Failed to create directory entry. {}", err.message());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry.exists();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool delete_other_versions_log_folders(std::wstring_view appPath, const std::filesystem::path& currentVersionLogFolder)
|
||||||
|
{
|
||||||
|
bool result = true;
|
||||||
|
std::filesystem::path logFolderPath(appPath);
|
||||||
|
logFolderPath.append(LogSettings::logPath);
|
||||||
|
|
||||||
|
if (!dir_exists(logFolderPath))
|
||||||
|
{
|
||||||
|
Logger::trace("Directory {} does not exist", logFolderPath.string());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::error_code err;
|
||||||
|
auto folders = std::filesystem::directory_iterator(logFolderPath, err);
|
||||||
|
if (err.value())
|
||||||
|
{
|
||||||
|
Logger::error("Failed to create directory iterator for {}. {}", logFolderPath.string(), err.message());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& dir : folders)
|
||||||
|
{
|
||||||
|
if (dir != currentVersionLogFolder)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
std::filesystem::remove_all(dir);
|
||||||
|
}
|
||||||
|
catch (std::filesystem::filesystem_error& e)
|
||||||
|
{
|
||||||
|
Logger::error("Failed to delete previous version log folder: {}", e.what());
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_logger(std::wstring moduleName, std::wstring internalPath, std::string loggerName)
|
||||||
|
{
|
||||||
|
std::filesystem::path rootFolder(PTSettingsHelper::get_module_save_folder_location(moduleName));
|
||||||
|
rootFolder.append(internalPath);
|
||||||
|
|
||||||
|
auto currentFolder = rootFolder;
|
||||||
|
currentFolder.append(LogSettings::logPath);
|
||||||
|
currentFolder.append(get_product_version());
|
||||||
|
|
||||||
|
auto logsPath = currentFolder;
|
||||||
|
logsPath.append(L"log.log");
|
||||||
|
Logger::init(loggerName, logsPath.wstring(), PTSettingsHelper::get_log_settings_file_location());
|
||||||
|
|
||||||
|
delete_other_versions_log_folders(rootFolder.wstring(), currentFolder);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,99 +1,19 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include <string>
|
||||||
#include <common/version/version.h>
|
#include <common/version/version.h>
|
||||||
#include <common/SettingsAPI/settings_helpers.h>
|
#include <common/SettingsAPI/settings_helpers.h>
|
||||||
|
|
||||||
namespace LoggerHelpers
|
namespace LoggerHelpers
|
||||||
{
|
{
|
||||||
inline std::filesystem::path get_log_folder_path(std::wstring_view appPath)
|
std::filesystem::path get_log_folder_path(std::wstring_view appPath);
|
||||||
{
|
|
||||||
std::filesystem::path logFolderPath(appPath);
|
|
||||||
logFolderPath.append(LogSettings::logPath);
|
|
||||||
logFolderPath.append(get_product_version());
|
|
||||||
return logFolderPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool delete_old_log_folder(const std::filesystem::path& logFolderPath)
|
bool delete_old_log_folder(const std::filesystem::path& logFolderPath);
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
std::filesystem::remove_all(logFolderPath);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (std::filesystem::filesystem_error& e)
|
|
||||||
{
|
|
||||||
Logger::error("Failed to delete old log folder: {}", e.what());
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
bool dir_exists(std::filesystem::path dir);
|
||||||
}
|
|
||||||
|
|
||||||
inline bool dir_exists(std::filesystem::path dir)
|
bool delete_other_versions_log_folders(std::wstring_view appPath, const std::filesystem::path& currentVersionLogFolder);
|
||||||
{
|
|
||||||
std::error_code err;
|
|
||||||
auto entry = std::filesystem::directory_entry(dir, err);
|
|
||||||
if (err.value())
|
|
||||||
{
|
|
||||||
Logger::error("Failed to create directory entry. {}", err.message());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return entry.exists();
|
void init_logger(std::wstring moduleName, std::wstring internalPath, std::string loggerName);
|
||||||
}
|
|
||||||
|
|
||||||
inline bool delete_other_versions_log_folders(std::wstring_view appPath, const std::filesystem::path& currentVersionLogFolder)
|
|
||||||
{
|
|
||||||
bool result = true;
|
|
||||||
std::filesystem::path logFolderPath(appPath);
|
|
||||||
logFolderPath.append(LogSettings::logPath);
|
|
||||||
|
|
||||||
if (!dir_exists(logFolderPath))
|
|
||||||
{
|
|
||||||
Logger::trace("Directory {} does not exist", logFolderPath.string());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::error_code err;
|
|
||||||
auto folders = std::filesystem::directory_iterator(logFolderPath, err);
|
|
||||||
if (err.value())
|
|
||||||
{
|
|
||||||
Logger::error("Failed to create directory iterator for {}. {}", logFolderPath.string(), err.message());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& dir : folders)
|
|
||||||
{
|
|
||||||
if (dir != currentVersionLogFolder)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
std::filesystem::remove_all(dir);
|
|
||||||
}
|
|
||||||
catch (std::filesystem::filesystem_error& e)
|
|
||||||
{
|
|
||||||
Logger::error("Failed to delete previous version log folder: {}", e.what());
|
|
||||||
result = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void init_logger(std::wstring moduleName, std::wstring internalPath, std::string loggerName)
|
|
||||||
{
|
|
||||||
std::filesystem::path rootFolder(PTSettingsHelper::get_module_save_folder_location(moduleName));
|
|
||||||
rootFolder.append(internalPath);
|
|
||||||
|
|
||||||
auto currentFolder = rootFolder;
|
|
||||||
currentFolder.append(LogSettings::logPath);
|
|
||||||
currentFolder.append(get_product_version());
|
|
||||||
|
|
||||||
auto logsPath = currentFolder;
|
|
||||||
logsPath.append(L"log.log");
|
|
||||||
Logger::init(loggerName, logsPath.wstring(), PTSettingsHelper::get_log_settings_file_location());
|
|
||||||
|
|
||||||
delete_other_versions_log_folders(rootFolder.wstring(), currentFolder);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
318
src/common/utils/modulesRegistry.cpp
Normal file
318
src/common/utils/modulesRegistry.cpp
Normal file
@@ -0,0 +1,318 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "modulesRegistry.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
registry::ChangeSet createPreviewChangeSet(registry::shellex::PreviewHandlerType type,
|
||||||
|
bool perUser,
|
||||||
|
const wchar_t* clsid,
|
||||||
|
std::wstring dllName,
|
||||||
|
std::wstring handlerClsid,
|
||||||
|
std::wstring handlerDisplayName,
|
||||||
|
std::vector<std::wstring> extensions,
|
||||||
|
std::wstring perceivedType = L"",
|
||||||
|
std::wstring fileKindType = L"")
|
||||||
|
{
|
||||||
|
using namespace registry::shellex;
|
||||||
|
|
||||||
|
return generatePreviewHandler(type,
|
||||||
|
perUser,
|
||||||
|
clsid,
|
||||||
|
get_std_product_version(),
|
||||||
|
std::move(dllName),
|
||||||
|
std::move(handlerClsid),
|
||||||
|
std::move(handlerDisplayName),
|
||||||
|
std::move(extensions),
|
||||||
|
std::move(perceivedType),
|
||||||
|
std::move(fileKindType));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
registry::ChangeSet getSvgPreviewHandlerChangeSet(const std::wstring& installationDir, bool perUser)
|
||||||
|
{
|
||||||
|
return createPreviewChangeSet(
|
||||||
|
registry::shellex::PreviewHandlerType::preview,
|
||||||
|
perUser,
|
||||||
|
L"{FCDD4EED-41AA-492F-8A84-31A1546226E0}",
|
||||||
|
(fs::path{ installationDir } / LR"d(PowerToys.SvgPreviewHandlerCpp.dll)d").wstring(),
|
||||||
|
L"SvgPreviewHandler",
|
||||||
|
L"Svg Preview Handler",
|
||||||
|
NonLocalizable::ExtSVG);
|
||||||
|
}
|
||||||
|
|
||||||
|
registry::ChangeSet getMdPreviewHandlerChangeSet(const std::wstring& installationDir, bool perUser)
|
||||||
|
{
|
||||||
|
return createPreviewChangeSet(
|
||||||
|
registry::shellex::PreviewHandlerType::preview,
|
||||||
|
perUser,
|
||||||
|
L"{60789D87-9C3C-44AF-B18C-3DE2C2820ED3}",
|
||||||
|
(fs::path{ installationDir } / LR"d(PowerToys.MarkdownPreviewHandlerCpp.dll)d").wstring(),
|
||||||
|
L"MarkdownPreviewHandler",
|
||||||
|
L"Markdown Preview Handler",
|
||||||
|
NonLocalizable::ExtMarkdown);
|
||||||
|
}
|
||||||
|
|
||||||
|
registry::ChangeSet getMonacoPreviewHandlerChangeSet(const std::wstring& installationDir, bool perUser)
|
||||||
|
{
|
||||||
|
std::vector<std::wstring> extensions;
|
||||||
|
|
||||||
|
std::vector<std::wstring> exclusions;
|
||||||
|
exclusions.insert(exclusions.end(), NonLocalizable::ExtMarkdown.begin(), NonLocalizable::ExtMarkdown.end());
|
||||||
|
exclusions.insert(exclusions.end(), NonLocalizable::ExtSVG.begin(), NonLocalizable::ExtSVG.end());
|
||||||
|
exclusions.insert(exclusions.end(), NonLocalizable::ExtNoNoNo.begin(), NonLocalizable::ExtNoNoNo.end());
|
||||||
|
|
||||||
|
std::wstring languagesFilePath = fs::path{ installationDir } / NonLocalizable::MONACO_LANGUAGES_FILE_NAME;
|
||||||
|
auto jsonValue = json::from_file(languagesFilePath);
|
||||||
|
|
||||||
|
if (jsonValue)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto list = jsonValue->GetNamedArray(NonLocalizable::ListID);
|
||||||
|
for (uint32_t i = 0; i < list.Size(); ++i)
|
||||||
|
{
|
||||||
|
auto entry = list.GetObjectAt(i);
|
||||||
|
if (entry.HasKey(NonLocalizable::ExtensionsID))
|
||||||
|
{
|
||||||
|
auto extensionsList = entry.GetNamedArray(NonLocalizable::ExtensionsID);
|
||||||
|
|
||||||
|
for (uint32_t j = 0; j < extensionsList.Size(); ++j)
|
||||||
|
{
|
||||||
|
auto extension = extensionsList.GetStringAt(j);
|
||||||
|
|
||||||
|
bool isExcluded = false;
|
||||||
|
for (const auto& excluded : exclusions)
|
||||||
|
{
|
||||||
|
if (std::wstring{ extension } == excluded)
|
||||||
|
{
|
||||||
|
isExcluded = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isExcluded)
|
||||||
|
{
|
||||||
|
extensions.emplace_back(extension);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return createPreviewChangeSet(
|
||||||
|
registry::shellex::PreviewHandlerType::preview,
|
||||||
|
perUser,
|
||||||
|
L"{D8034CFA-F34B-41FE-AD45-62FCBB52A6DA}",
|
||||||
|
(fs::path{ installationDir } / LR"d(PowerToys.MonacoPreviewHandlerCpp.dll)d").wstring(),
|
||||||
|
L"MonacoPreviewHandler",
|
||||||
|
L"Monaco Preview Handler",
|
||||||
|
std::move(extensions));
|
||||||
|
}
|
||||||
|
|
||||||
|
registry::ChangeSet getPdfPreviewHandlerChangeSet(const std::wstring& installationDir, bool perUser)
|
||||||
|
{
|
||||||
|
return createPreviewChangeSet(
|
||||||
|
registry::shellex::PreviewHandlerType::preview,
|
||||||
|
perUser,
|
||||||
|
L"{A5A41CC7-02CB-41D4-8C9B-9087040D6098}",
|
||||||
|
(fs::path{ installationDir } / LR"d(PowerToys.PdfPreviewHandlerCpp.dll)d").wstring(),
|
||||||
|
L"PdfPreviewHandler",
|
||||||
|
L"Pdf Preview Handler",
|
||||||
|
NonLocalizable::ExtPDF);
|
||||||
|
}
|
||||||
|
|
||||||
|
registry::ChangeSet getGcodePreviewHandlerChangeSet(const std::wstring& installationDir, bool perUser)
|
||||||
|
{
|
||||||
|
return createPreviewChangeSet(
|
||||||
|
registry::shellex::PreviewHandlerType::preview,
|
||||||
|
perUser,
|
||||||
|
L"{A0257634-8812-4CE8-AF11-FA69ACAEAFAE}",
|
||||||
|
(fs::path{ installationDir } / LR"d(PowerToys.GcodePreviewHandlerCpp.dll)d").wstring(),
|
||||||
|
L"GcodePreviewHandler",
|
||||||
|
L"G-code Preview Handler",
|
||||||
|
NonLocalizable::ExtGCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
registry::ChangeSet getBgcodePreviewHandlerChangeSet(const std::wstring& installationDir, bool perUser)
|
||||||
|
{
|
||||||
|
return createPreviewChangeSet(
|
||||||
|
registry::shellex::PreviewHandlerType::preview,
|
||||||
|
perUser,
|
||||||
|
L"{0e6d5bdd-d5f8-4692-a089-8bb88cdd37f4}",
|
||||||
|
(fs::path{ installationDir } / LR"d(PowerToys.BgcodePreviewHandlerCpp.dll)d").wstring(),
|
||||||
|
L"BgcodePreviewHandler",
|
||||||
|
L"Binary G-code Preview Handler",
|
||||||
|
NonLocalizable::ExtBGCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
registry::ChangeSet getQoiPreviewHandlerChangeSet(const std::wstring& installationDir, bool perUser)
|
||||||
|
{
|
||||||
|
return createPreviewChangeSet(
|
||||||
|
registry::shellex::PreviewHandlerType::preview,
|
||||||
|
perUser,
|
||||||
|
L"{729B72CD-B72E-4FE9-BCBF-E954B33FE699}",
|
||||||
|
(fs::path{ installationDir } / LR"d(PowerToys.QoiPreviewHandlerCpp.dll)d").wstring(),
|
||||||
|
L"QoiPreviewHandler",
|
||||||
|
L"Qoi Preview Handler",
|
||||||
|
NonLocalizable::ExtQOI);
|
||||||
|
}
|
||||||
|
|
||||||
|
registry::ChangeSet getSvgThumbnailHandlerChangeSet(const std::wstring& installationDir, bool perUser)
|
||||||
|
{
|
||||||
|
return createPreviewChangeSet(
|
||||||
|
registry::shellex::PreviewHandlerType::thumbnail,
|
||||||
|
perUser,
|
||||||
|
L"{10144713-1526-46C9-88DA-1FB52807A9FF}",
|
||||||
|
(fs::path{ installationDir } / LR"d(PowerToys.SvgThumbnailProviderCpp.dll)d").wstring(),
|
||||||
|
L"SvgThumbnailProvider",
|
||||||
|
L"Svg Thumbnail Provider",
|
||||||
|
NonLocalizable::ExtSVG,
|
||||||
|
L"image",
|
||||||
|
L"Picture");
|
||||||
|
}
|
||||||
|
|
||||||
|
registry::ChangeSet getPdfThumbnailHandlerChangeSet(const std::wstring& installationDir, bool perUser)
|
||||||
|
{
|
||||||
|
return createPreviewChangeSet(
|
||||||
|
registry::shellex::PreviewHandlerType::thumbnail,
|
||||||
|
perUser,
|
||||||
|
L"{D8BB9942-93BD-412D-87E4-33FAB214DC1A}",
|
||||||
|
(fs::path{ installationDir } / LR"d(PowerToys.PdfThumbnailProviderCpp.dll)d").wstring(),
|
||||||
|
L"PdfThumbnailProvider",
|
||||||
|
L"Pdf Thumbnail Provider",
|
||||||
|
NonLocalizable::ExtPDF);
|
||||||
|
}
|
||||||
|
|
||||||
|
registry::ChangeSet getGcodeThumbnailHandlerChangeSet(const std::wstring& installationDir, bool perUser)
|
||||||
|
{
|
||||||
|
return createPreviewChangeSet(
|
||||||
|
registry::shellex::PreviewHandlerType::thumbnail,
|
||||||
|
perUser,
|
||||||
|
L"{F2847CBE-CD03-4C83-A359-1A8052C1B9D5}",
|
||||||
|
(fs::path{ installationDir } / LR"d(PowerToys.GcodeThumbnailProviderCpp.dll)d").wstring(),
|
||||||
|
L"GcodeThumbnailProvider",
|
||||||
|
L"G-code Thumbnail Provider",
|
||||||
|
NonLocalizable::ExtGCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
registry::ChangeSet getBgcodeThumbnailHandlerChangeSet(const std::wstring& installationDir, bool perUser)
|
||||||
|
{
|
||||||
|
return createPreviewChangeSet(
|
||||||
|
registry::shellex::PreviewHandlerType::thumbnail,
|
||||||
|
perUser,
|
||||||
|
L"{5c93a1e4-99d0-4fb3-991c-6c296a27be21}",
|
||||||
|
(fs::path{ installationDir } / LR"d(PowerToys.BgcodeThumbnailProviderCpp.dll)d").wstring(),
|
||||||
|
L"BgcodeThumbnailProvider",
|
||||||
|
L"Binary G-code Thumbnail Provider",
|
||||||
|
NonLocalizable::ExtBGCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
registry::ChangeSet getStlThumbnailHandlerChangeSet(const std::wstring& installationDir, bool perUser)
|
||||||
|
{
|
||||||
|
return createPreviewChangeSet(
|
||||||
|
registry::shellex::PreviewHandlerType::thumbnail,
|
||||||
|
perUser,
|
||||||
|
L"{77257004-6F25-4521-B602-50ECC6EC62A6}",
|
||||||
|
(fs::path{ installationDir } / LR"d(PowerToys.StlThumbnailProviderCpp.dll)d").wstring(),
|
||||||
|
L"StlThumbnailProvider",
|
||||||
|
L"Stl Thumbnail Provider",
|
||||||
|
NonLocalizable::ExtSTL);
|
||||||
|
}
|
||||||
|
|
||||||
|
registry::ChangeSet getQoiThumbnailHandlerChangeSet(const std::wstring& installationDir, bool perUser)
|
||||||
|
{
|
||||||
|
return createPreviewChangeSet(
|
||||||
|
registry::shellex::PreviewHandlerType::thumbnail,
|
||||||
|
perUser,
|
||||||
|
L"{AD856B15-D25E-4008-AFB7-AFAA55586188}",
|
||||||
|
(fs::path{ installationDir } / LR"d(PowerToys.QoiThumbnailProviderCpp.dll)d").wstring(),
|
||||||
|
L"QoiThumbnailProvider",
|
||||||
|
L"Qoi Thumbnail Provider",
|
||||||
|
NonLocalizable::ExtQOI,
|
||||||
|
L"image",
|
||||||
|
L"Picture");
|
||||||
|
}
|
||||||
|
|
||||||
|
registry::ChangeSet getRegistryPreviewSetDefaultAppChangeSet(const std::wstring& installationDir, bool perUser)
|
||||||
|
{
|
||||||
|
const HKEY scope = perUser ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
|
||||||
|
|
||||||
|
std::vector<registry::ValueChange> changes;
|
||||||
|
|
||||||
|
std::wstring appName = L"Registry Preview";
|
||||||
|
std::wstring fullAppName = L"PowerToys.RegistryPreview";
|
||||||
|
std::wstring registryKeyPrefix = L"Software\\Classes\\";
|
||||||
|
|
||||||
|
std::wstring appPath = installationDir + L"\\WinUI3Apps\\PowerToys.RegistryPreview.exe";
|
||||||
|
std::wstring command = appPath + L" \"----ms-protocol:ms-encodedlaunch:App?ContractId=Windows.File&Verb=open&File=%1\"";
|
||||||
|
|
||||||
|
changes.push_back({ scope, registryKeyPrefix + fullAppName + L"\\Application", L"ApplicationName", appName });
|
||||||
|
changes.push_back({ scope, registryKeyPrefix + fullAppName + L"\\DefaultIcon", std::nullopt, appPath });
|
||||||
|
changes.push_back({ scope, registryKeyPrefix + fullAppName + L"\\shell\\open\\command", std::nullopt, command });
|
||||||
|
changes.push_back({ scope, registryKeyPrefix + L".reg\\OpenWithProgIDs", fullAppName, L"" });
|
||||||
|
|
||||||
|
return { changes };
|
||||||
|
}
|
||||||
|
|
||||||
|
registry::ChangeSet getRegistryPreviewChangeSet(const std::wstring& installationDir, bool perUser)
|
||||||
|
{
|
||||||
|
const HKEY scope = perUser ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
|
||||||
|
|
||||||
|
std::vector<registry::ValueChange> changes;
|
||||||
|
|
||||||
|
std::wstring command = installationDir;
|
||||||
|
command.append(L"\\WinUI3Apps\\PowerToys.RegistryPreview.exe \"%1\"");
|
||||||
|
changes.push_back({ scope, L"Software\\Classes\\regfile\\shell\\preview\\command", std::nullopt, command });
|
||||||
|
|
||||||
|
std::wstring iconPath = installationDir;
|
||||||
|
iconPath.append(L"\\WinUI3Apps\\Assets\\RegistryPreview\\RegistryPreview.ico");
|
||||||
|
changes.push_back({ scope, L"Software\\Classes\\regfile\\shell\\preview", L"icon", iconPath });
|
||||||
|
|
||||||
|
return { changes };
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<registry::ChangeSet> getAllOnByDefaultModulesChangeSets(const std::wstring& installationDir)
|
||||||
|
{
|
||||||
|
constexpr bool perUser = true;
|
||||||
|
return {
|
||||||
|
getSvgPreviewHandlerChangeSet(installationDir, perUser),
|
||||||
|
getMdPreviewHandlerChangeSet(installationDir, perUser),
|
||||||
|
getMonacoPreviewHandlerChangeSet(installationDir, perUser),
|
||||||
|
getGcodePreviewHandlerChangeSet(installationDir, perUser),
|
||||||
|
getBgcodePreviewHandlerChangeSet(installationDir, perUser),
|
||||||
|
getQoiPreviewHandlerChangeSet(installationDir, perUser),
|
||||||
|
getSvgThumbnailHandlerChangeSet(installationDir, perUser),
|
||||||
|
getGcodeThumbnailHandlerChangeSet(installationDir, perUser),
|
||||||
|
getBgcodeThumbnailHandlerChangeSet(installationDir, perUser),
|
||||||
|
getStlThumbnailHandlerChangeSet(installationDir, perUser),
|
||||||
|
getQoiThumbnailHandlerChangeSet(installationDir, perUser),
|
||||||
|
getRegistryPreviewChangeSet(installationDir, perUser)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<registry::ChangeSet> getAllModulesChangeSets(const std::wstring& installationDir)
|
||||||
|
{
|
||||||
|
constexpr bool perUser = true;
|
||||||
|
return {
|
||||||
|
getSvgPreviewHandlerChangeSet(installationDir, perUser),
|
||||||
|
getMdPreviewHandlerChangeSet(installationDir, perUser),
|
||||||
|
getMonacoPreviewHandlerChangeSet(installationDir, perUser),
|
||||||
|
getPdfPreviewHandlerChangeSet(installationDir, perUser),
|
||||||
|
getGcodePreviewHandlerChangeSet(installationDir, perUser),
|
||||||
|
getBgcodePreviewHandlerChangeSet(installationDir, perUser),
|
||||||
|
getQoiPreviewHandlerChangeSet(installationDir, perUser),
|
||||||
|
getSvgThumbnailHandlerChangeSet(installationDir, perUser),
|
||||||
|
getPdfThumbnailHandlerChangeSet(installationDir, perUser),
|
||||||
|
getGcodeThumbnailHandlerChangeSet(installationDir, perUser),
|
||||||
|
getBgcodeThumbnailHandlerChangeSet(installationDir, perUser),
|
||||||
|
getStlThumbnailHandlerChangeSet(installationDir, perUser),
|
||||||
|
getQoiThumbnailHandlerChangeSet(installationDir, perUser),
|
||||||
|
getRegistryPreviewChangeSet(installationDir, perUser),
|
||||||
|
getRegistryPreviewSetDefaultAppChangeSet(installationDir, perUser)
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -25,309 +25,36 @@ namespace NonLocalizable
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
inline registry::ChangeSet getSvgPreviewHandlerChangeSet(const std::wstring installationDir, const bool perUser)
|
registry::ChangeSet getSvgPreviewHandlerChangeSet(const std::wstring& installationDir, bool perUser);
|
||||||
{
|
|
||||||
using namespace registry::shellex;
|
|
||||||
return generatePreviewHandler(PreviewHandlerType::preview,
|
|
||||||
perUser,
|
|
||||||
L"{FCDD4EED-41AA-492F-8A84-31A1546226E0}",
|
|
||||||
get_std_product_version(),
|
|
||||||
(fs::path{ installationDir } /
|
|
||||||
LR"d(PowerToys.SvgPreviewHandlerCpp.dll)d")
|
|
||||||
.wstring(),
|
|
||||||
L"SvgPreviewHandler",
|
|
||||||
L"Svg Preview Handler",
|
|
||||||
NonLocalizable::ExtSVG);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline registry::ChangeSet getMdPreviewHandlerChangeSet(const std::wstring installationDir, const bool perUser)
|
registry::ChangeSet getMdPreviewHandlerChangeSet(const std::wstring& installationDir, bool perUser);
|
||||||
{
|
|
||||||
using namespace registry::shellex;
|
|
||||||
return generatePreviewHandler(PreviewHandlerType::preview,
|
|
||||||
perUser,
|
|
||||||
L"{60789D87-9C3C-44AF-B18C-3DE2C2820ED3}",
|
|
||||||
get_std_product_version(),
|
|
||||||
(fs::path{ installationDir } / LR"d(PowerToys.MarkdownPreviewHandlerCpp.dll)d").wstring(),
|
|
||||||
L"MarkdownPreviewHandler",
|
|
||||||
L"Markdown Preview Handler",
|
|
||||||
NonLocalizable::ExtMarkdown);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline registry::ChangeSet getMonacoPreviewHandlerChangeSet(const std::wstring installationDir, const bool perUser)
|
registry::ChangeSet getMonacoPreviewHandlerChangeSet(const std::wstring& installationDir, bool perUser);
|
||||||
{
|
|
||||||
using namespace registry::shellex;
|
|
||||||
|
|
||||||
// Set up a list of extensions for the preview handler to take over
|
registry::ChangeSet getPdfPreviewHandlerChangeSet(const std::wstring& installationDir, bool perUser);
|
||||||
std::vector<std::wstring> extensions;
|
|
||||||
|
|
||||||
// Set up a list of extensions that Monaco support but the preview handler shouldn't take over
|
registry::ChangeSet getGcodePreviewHandlerChangeSet(const std::wstring& installationDir, bool perUser);
|
||||||
std::vector<std::wstring> ExtExclusions;
|
|
||||||
ExtExclusions.insert(ExtExclusions.end(), NonLocalizable::ExtMarkdown.begin(), NonLocalizable::ExtMarkdown.end());
|
|
||||||
ExtExclusions.insert(ExtExclusions.end(), NonLocalizable::ExtSVG.begin(), NonLocalizable::ExtSVG.end());
|
|
||||||
ExtExclusions.insert(ExtExclusions.end(), NonLocalizable::ExtNoNoNo.begin(), NonLocalizable::ExtNoNoNo.end());
|
|
||||||
bool IsExcluded = false;
|
|
||||||
|
|
||||||
std::wstring languagesFilePath = fs::path{ installationDir } / NonLocalizable::MONACO_LANGUAGES_FILE_NAME;
|
registry::ChangeSet getBgcodePreviewHandlerChangeSet(const std::wstring& installationDir, bool perUser);
|
||||||
auto json = json::from_file(languagesFilePath);
|
|
||||||
|
|
||||||
if (json)
|
registry::ChangeSet getQoiPreviewHandlerChangeSet(const std::wstring& installationDir, bool perUser);
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
auto list = json->GetNamedArray(NonLocalizable::ListID);
|
|
||||||
for (uint32_t i = 0; i < list.Size(); ++i)
|
|
||||||
{
|
|
||||||
auto entry = list.GetObjectAt(i);
|
|
||||||
if (entry.HasKey(NonLocalizable::ExtensionsID))
|
|
||||||
{
|
|
||||||
auto extensionsList = entry.GetNamedArray(NonLocalizable::ExtensionsID);
|
|
||||||
|
|
||||||
for (uint32_t j = 0; j < extensionsList.Size(); ++j)
|
registry::ChangeSet getSvgThumbnailHandlerChangeSet(const std::wstring& installationDir, bool perUser);
|
||||||
{
|
|
||||||
auto extension = extensionsList.GetStringAt(j);
|
|
||||||
|
|
||||||
// Ignore extensions in the exclusion list
|
registry::ChangeSet getPdfThumbnailHandlerChangeSet(const std::wstring& installationDir, bool perUser);
|
||||||
IsExcluded = false;
|
|
||||||
|
|
||||||
for (std::wstring k : ExtExclusions)
|
registry::ChangeSet getGcodeThumbnailHandlerChangeSet(const std::wstring& installationDir, bool perUser);
|
||||||
{
|
|
||||||
if (std::wstring{ extension } == k)
|
|
||||||
{
|
|
||||||
IsExcluded = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (IsExcluded)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
extensions.push_back(std::wstring{ extension });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return generatePreviewHandler(PreviewHandlerType::preview,
|
registry::ChangeSet getBgcodeThumbnailHandlerChangeSet(const std::wstring& installationDir, bool perUser);
|
||||||
perUser,
|
|
||||||
L"{D8034CFA-F34B-41FE-AD45-62FCBB52A6DA}",
|
|
||||||
get_std_product_version(),
|
|
||||||
(fs::path{ installationDir } / LR"d(PowerToys.MonacoPreviewHandlerCpp.dll)d").wstring(),
|
|
||||||
L"MonacoPreviewHandler",
|
|
||||||
L"Monaco Preview Handler",
|
|
||||||
extensions);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline registry::ChangeSet getPdfPreviewHandlerChangeSet(const std::wstring installationDir, const bool perUser)
|
registry::ChangeSet getStlThumbnailHandlerChangeSet(const std::wstring& installationDir, bool perUser);
|
||||||
{
|
|
||||||
using namespace registry::shellex;
|
|
||||||
return generatePreviewHandler(PreviewHandlerType::preview,
|
|
||||||
perUser,
|
|
||||||
L"{A5A41CC7-02CB-41D4-8C9B-9087040D6098}",
|
|
||||||
get_std_product_version(),
|
|
||||||
(fs::path{ installationDir } / LR"d(PowerToys.PdfPreviewHandlerCpp.dll)d").wstring(),
|
|
||||||
L"PdfPreviewHandler",
|
|
||||||
L"Pdf Preview Handler",
|
|
||||||
NonLocalizable::ExtPDF);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline registry::ChangeSet getGcodePreviewHandlerChangeSet(const std::wstring installationDir, const bool perUser)
|
registry::ChangeSet getQoiThumbnailHandlerChangeSet(const std::wstring& installationDir, bool perUser);
|
||||||
{
|
|
||||||
using namespace registry::shellex;
|
|
||||||
return generatePreviewHandler(PreviewHandlerType::preview,
|
|
||||||
perUser,
|
|
||||||
L"{A0257634-8812-4CE8-AF11-FA69ACAEAFAE}",
|
|
||||||
get_std_product_version(),
|
|
||||||
(fs::path{ installationDir } / LR"d(PowerToys.GcodePreviewHandlerCpp.dll)d").wstring(),
|
|
||||||
L"GcodePreviewHandler",
|
|
||||||
L"G-code Preview Handler",
|
|
||||||
NonLocalizable::ExtGCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline registry::ChangeSet getBgcodePreviewHandlerChangeSet(const std::wstring installationDir, const bool perUser)
|
registry::ChangeSet getRegistryPreviewSetDefaultAppChangeSet(const std::wstring& installationDir, bool perUser);
|
||||||
{
|
|
||||||
using namespace registry::shellex;
|
|
||||||
return generatePreviewHandler(PreviewHandlerType::preview,
|
|
||||||
perUser,
|
|
||||||
L"{0e6d5bdd-d5f8-4692-a089-8bb88cdd37f4}",
|
|
||||||
get_std_product_version(),
|
|
||||||
(fs::path{ installationDir } / LR"d(PowerToys.BgcodePreviewHandlerCpp.dll)d").wstring(),
|
|
||||||
L"BgcodePreviewHandler",
|
|
||||||
L"Binary G-code Preview Handler",
|
|
||||||
NonLocalizable::ExtBGCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline registry::ChangeSet getQoiPreviewHandlerChangeSet(const std::wstring installationDir, const bool perUser)
|
registry::ChangeSet getRegistryPreviewChangeSet(const std::wstring& installationDir, bool perUser);
|
||||||
{
|
|
||||||
using namespace registry::shellex;
|
|
||||||
return generatePreviewHandler(PreviewHandlerType::preview,
|
|
||||||
perUser,
|
|
||||||
L"{729B72CD-B72E-4FE9-BCBF-E954B33FE699}",
|
|
||||||
get_std_product_version(),
|
|
||||||
(fs::path{ installationDir } / LR"d(PowerToys.QoiPreviewHandlerCpp.dll)d").wstring(),
|
|
||||||
L"QoiPreviewHandler",
|
|
||||||
L"Qoi Preview Handler",
|
|
||||||
NonLocalizable::ExtQOI);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline registry::ChangeSet getSvgThumbnailHandlerChangeSet(const std::wstring installationDir, const bool perUser)
|
std::vector<registry::ChangeSet> getAllOnByDefaultModulesChangeSets(const std::wstring& installationDir);
|
||||||
{
|
|
||||||
using namespace registry::shellex;
|
|
||||||
return generatePreviewHandler(PreviewHandlerType::thumbnail,
|
|
||||||
perUser,
|
|
||||||
L"{10144713-1526-46C9-88DA-1FB52807A9FF}",
|
|
||||||
get_std_product_version(),
|
|
||||||
(fs::path{ installationDir } / LR"d(PowerToys.SvgThumbnailProviderCpp.dll)d").wstring(),
|
|
||||||
L"SvgThumbnailProvider",
|
|
||||||
L"Svg Thumbnail Provider",
|
|
||||||
NonLocalizable::ExtSVG,
|
|
||||||
L"image",
|
|
||||||
L"Picture");
|
|
||||||
}
|
|
||||||
|
|
||||||
inline registry::ChangeSet getPdfThumbnailHandlerChangeSet(const std::wstring installationDir, const bool perUser)
|
std::vector<registry::ChangeSet> getAllModulesChangeSets(const std::wstring& installationDir);
|
||||||
{
|
|
||||||
using namespace registry::shellex;
|
|
||||||
return generatePreviewHandler(PreviewHandlerType::thumbnail,
|
|
||||||
perUser,
|
|
||||||
L"{D8BB9942-93BD-412D-87E4-33FAB214DC1A}",
|
|
||||||
get_std_product_version(),
|
|
||||||
(fs::path{ installationDir } / LR"d(PowerToys.PdfThumbnailProviderCpp.dll)d").wstring(),
|
|
||||||
L"PdfThumbnailProvider",
|
|
||||||
L"Pdf Thumbnail Provider",
|
|
||||||
NonLocalizable::ExtPDF);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline registry::ChangeSet getGcodeThumbnailHandlerChangeSet(const std::wstring installationDir, const bool perUser)
|
|
||||||
{
|
|
||||||
using namespace registry::shellex;
|
|
||||||
return generatePreviewHandler(PreviewHandlerType::thumbnail,
|
|
||||||
perUser,
|
|
||||||
L"{F2847CBE-CD03-4C83-A359-1A8052C1B9D5}",
|
|
||||||
get_std_product_version(),
|
|
||||||
(fs::path{ installationDir } / LR"d(PowerToys.GcodeThumbnailProviderCpp.dll)d").wstring(),
|
|
||||||
L"GcodeThumbnailProvider",
|
|
||||||
L"G-code Thumbnail Provider",
|
|
||||||
NonLocalizable::ExtGCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline registry::ChangeSet getBgcodeThumbnailHandlerChangeSet(const std::wstring installationDir, const bool perUser)
|
|
||||||
{
|
|
||||||
using namespace registry::shellex;
|
|
||||||
return generatePreviewHandler(PreviewHandlerType::thumbnail,
|
|
||||||
perUser,
|
|
||||||
L"{5c93a1e4-99d0-4fb3-991c-6c296a27be21}",
|
|
||||||
get_std_product_version(),
|
|
||||||
(fs::path{ installationDir } / LR"d(PowerToys.BgcodeThumbnailProviderCpp.dll)d").wstring(),
|
|
||||||
L"BgcodeThumbnailProvider",
|
|
||||||
L"Binary G-code Thumbnail Provider",
|
|
||||||
NonLocalizable::ExtBGCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline registry::ChangeSet getStlThumbnailHandlerChangeSet(const std::wstring installationDir, const bool perUser)
|
|
||||||
{
|
|
||||||
using namespace registry::shellex;
|
|
||||||
return generatePreviewHandler(PreviewHandlerType::thumbnail,
|
|
||||||
perUser,
|
|
||||||
L"{77257004-6F25-4521-B602-50ECC6EC62A6}",
|
|
||||||
get_std_product_version(),
|
|
||||||
(fs::path{ installationDir } / LR"d(PowerToys.StlThumbnailProviderCpp.dll)d").wstring(),
|
|
||||||
L"StlThumbnailProvider",
|
|
||||||
L"Stl Thumbnail Provider",
|
|
||||||
NonLocalizable::ExtSTL);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline registry::ChangeSet getQoiThumbnailHandlerChangeSet(const std::wstring installationDir, const bool perUser)
|
|
||||||
{
|
|
||||||
using namespace registry::shellex;
|
|
||||||
return generatePreviewHandler(PreviewHandlerType::thumbnail,
|
|
||||||
perUser,
|
|
||||||
L"{AD856B15-D25E-4008-AFB7-AFAA55586188}",
|
|
||||||
get_std_product_version(),
|
|
||||||
(fs::path{ installationDir } / LR"d(PowerToys.QoiThumbnailProviderCpp.dll)d").wstring(),
|
|
||||||
L"QoiThumbnailProvider",
|
|
||||||
L"Qoi Thumbnail Provider",
|
|
||||||
NonLocalizable::ExtQOI,
|
|
||||||
L"image",
|
|
||||||
L"Picture");
|
|
||||||
}
|
|
||||||
|
|
||||||
inline registry::ChangeSet getRegistryPreviewSetDefaultAppChangeSet(const std::wstring installationDir, const bool perUser)
|
|
||||||
{
|
|
||||||
const HKEY scope = perUser ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
|
|
||||||
|
|
||||||
using vec_t = std::vector<registry::ValueChange>;
|
|
||||||
vec_t changes;
|
|
||||||
|
|
||||||
std::wstring appName = L"Registry Preview";
|
|
||||||
std::wstring fullAppName = L"PowerToys.RegistryPreview";
|
|
||||||
std::wstring registryKeyPrefix = L"Software\\Classes\\";
|
|
||||||
|
|
||||||
std::wstring appPath = installationDir + L"\\WinUI3Apps\\PowerToys.RegistryPreview.exe";
|
|
||||||
std::wstring command = appPath + L" \"----ms-protocol:ms-encodedlaunch:App?ContractId=Windows.File&Verb=open&File=%1\"";
|
|
||||||
|
|
||||||
changes.push_back({ scope, registryKeyPrefix + fullAppName + L"\\" + L"Application", L"ApplicationName", appName });
|
|
||||||
changes.push_back({ scope, registryKeyPrefix + fullAppName + L"\\" + L"DefaultIcon", std::nullopt, appPath });
|
|
||||||
changes.push_back({ scope, registryKeyPrefix + fullAppName + L"\\" + L"shell\\open\\command", std::nullopt, command });
|
|
||||||
changes.push_back({ scope, registryKeyPrefix + L".reg\\OpenWithProgIDs", fullAppName, L"" });
|
|
||||||
|
|
||||||
return { changes };
|
|
||||||
}
|
|
||||||
|
|
||||||
inline registry::ChangeSet getRegistryPreviewChangeSet(const std::wstring installationDir,const bool perUser)
|
|
||||||
{
|
|
||||||
const HKEY scope = perUser ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
|
|
||||||
|
|
||||||
using vec_t = std::vector<registry::ValueChange>;
|
|
||||||
vec_t changes;
|
|
||||||
|
|
||||||
std::wstring command = installationDir;
|
|
||||||
command.append(L"\\WinUI3Apps\\PowerToys.RegistryPreview.exe \"%1\"");
|
|
||||||
changes.push_back({ scope, L"Software\\Classes\\regfile\\shell\\preview\\command", std::nullopt, command });
|
|
||||||
|
|
||||||
std::wstring icon_path = installationDir;
|
|
||||||
icon_path.append(L"\\WinUI3Apps\\Assets\\RegistryPreview\\RegistryPreview.ico");
|
|
||||||
changes.push_back({ scope, L"Software\\Classes\\regfile\\shell\\preview", L"icon", icon_path });
|
|
||||||
|
|
||||||
return { changes };
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::vector<registry::ChangeSet> getAllOnByDefaultModulesChangeSets(const std::wstring installationDir)
|
|
||||||
{
|
|
||||||
constexpr bool PER_USER = true;
|
|
||||||
return { getSvgPreviewHandlerChangeSet(installationDir, PER_USER),
|
|
||||||
getMdPreviewHandlerChangeSet(installationDir, PER_USER),
|
|
||||||
getMonacoPreviewHandlerChangeSet(installationDir, PER_USER),
|
|
||||||
getGcodePreviewHandlerChangeSet(installationDir, PER_USER),
|
|
||||||
getBgcodePreviewHandlerChangeSet(installationDir, PER_USER),
|
|
||||||
getQoiPreviewHandlerChangeSet(installationDir, PER_USER),
|
|
||||||
getSvgThumbnailHandlerChangeSet(installationDir, PER_USER),
|
|
||||||
getGcodeThumbnailHandlerChangeSet(installationDir, PER_USER),
|
|
||||||
getBgcodeThumbnailHandlerChangeSet(installationDir, PER_USER),
|
|
||||||
getStlThumbnailHandlerChangeSet(installationDir, PER_USER),
|
|
||||||
getQoiThumbnailHandlerChangeSet(installationDir, PER_USER),
|
|
||||||
getRegistryPreviewChangeSet(installationDir, PER_USER) };
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::vector<registry::ChangeSet> getAllModulesChangeSets(const std::wstring installationDir)
|
|
||||||
{
|
|
||||||
constexpr bool PER_USER = true;
|
|
||||||
return { getSvgPreviewHandlerChangeSet(installationDir, PER_USER),
|
|
||||||
getMdPreviewHandlerChangeSet(installationDir, PER_USER),
|
|
||||||
getMonacoPreviewHandlerChangeSet(installationDir, PER_USER),
|
|
||||||
getPdfPreviewHandlerChangeSet(installationDir, PER_USER),
|
|
||||||
getGcodePreviewHandlerChangeSet(installationDir, PER_USER),
|
|
||||||
getBgcodePreviewHandlerChangeSet(installationDir, PER_USER),
|
|
||||||
getQoiPreviewHandlerChangeSet(installationDir, PER_USER),
|
|
||||||
getSvgThumbnailHandlerChangeSet(installationDir, PER_USER),
|
|
||||||
getPdfThumbnailHandlerChangeSet(installationDir, PER_USER),
|
|
||||||
getGcodeThumbnailHandlerChangeSet(installationDir, PER_USER),
|
|
||||||
getBgcodeThumbnailHandlerChangeSet(installationDir, PER_USER),
|
|
||||||
getStlThumbnailHandlerChangeSet(installationDir, PER_USER),
|
|
||||||
getQoiThumbnailHandlerChangeSet(installationDir, PER_USER),
|
|
||||||
getRegistryPreviewChangeSet(installationDir, PER_USER),
|
|
||||||
getRegistryPreviewSetDefaultAppChangeSet(installationDir, PER_USER) };
|
|
||||||
}
|
|
||||||
|
|||||||
417
src/common/utils/package.cpp
Normal file
417
src/common/utils/package.cpp
Normal file
@@ -0,0 +1,417 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "package.h"
|
||||||
|
#include <common/utils/winapi_error.h>
|
||||||
|
|
||||||
|
namespace package
|
||||||
|
{
|
||||||
|
using namespace winrt::Windows::Foundation;
|
||||||
|
using namespace winrt::Windows::ApplicationModel;
|
||||||
|
using namespace winrt::Windows::Management::Deployment;
|
||||||
|
|
||||||
|
BOOL IsWin11OrGreater()
|
||||||
|
{
|
||||||
|
OSVERSIONINFOEX osvi{};
|
||||||
|
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
|
||||||
|
osvi.dwMajorVersion = HIBYTE(_WIN32_WINNT_WINTHRESHOLD); // 10
|
||||||
|
osvi.dwMinorVersion = LOBYTE(_WIN32_WINNT_WINTHRESHOLD); // 0
|
||||||
|
osvi.dwBuildNumber = 22000; // Windows 11 RTM build
|
||||||
|
|
||||||
|
DWORDLONG mask = 0;
|
||||||
|
BYTE op = VER_GREATER_EQUAL;
|
||||||
|
VER_SET_CONDITION(mask, VER_MAJORVERSION, op);
|
||||||
|
VER_SET_CONDITION(mask, VER_MINORVERSION, op);
|
||||||
|
VER_SET_CONDITION(mask, VER_BUILDNUMBER, op);
|
||||||
|
|
||||||
|
return VerifyVersionInfo(&osvi,
|
||||||
|
VER_MAJORVERSION | VER_MINORVERSION | VER_BUILDNUMBER,
|
||||||
|
mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
ComInitializer::ComInitializer(DWORD coInitFlags) : _initialized(false)
|
||||||
|
{
|
||||||
|
const HRESULT hr = CoInitializeEx(nullptr, coInitFlags);
|
||||||
|
_initialized = SUCCEEDED(hr);
|
||||||
|
}
|
||||||
|
|
||||||
|
ComInitializer::~ComInitializer()
|
||||||
|
{
|
||||||
|
if (_initialized)
|
||||||
|
{
|
||||||
|
CoUninitialize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ComInitializer::Succeeded() const
|
||||||
|
{
|
||||||
|
return _initialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int compare_versions(const PackageVersion& a, const PACKAGE_VERSION& b)
|
||||||
|
{
|
||||||
|
if (a.Major != b.Major) return (a.Major < b.Major) ? -1 : 1;
|
||||||
|
if (a.Minor != b.Minor) return (a.Minor < b.Minor) ? -1 : 1;
|
||||||
|
if (a.Build != b.Build) return (a.Build < b.Build) ? -1 : 1;
|
||||||
|
if (a.Revision != b.Revision) return (a.Revision < b.Revision) ? -1 : 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GetPackageNameAndVersionFromAppx(const std::wstring& appxPath,
|
||||||
|
std::wstring& outName,
|
||||||
|
PACKAGE_VERSION& outVersion)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ComInitializer comInit;
|
||||||
|
if (!comInit.Succeeded())
|
||||||
|
{
|
||||||
|
Logger::error(L"COM initialization failed.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Microsoft::WRL::ComPtr<IAppxFactory> factory;
|
||||||
|
Microsoft::WRL::ComPtr<IStream> stream;
|
||||||
|
Microsoft::WRL::ComPtr<IAppxPackageReader> reader;
|
||||||
|
Microsoft::WRL::ComPtr<IAppxManifestReader> manifest;
|
||||||
|
Microsoft::WRL::ComPtr<IAppxManifestPackageId> packageId;
|
||||||
|
|
||||||
|
HRESULT hr = CoCreateInstance(__uuidof(AppxFactory), nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&factory));
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
Logger::error(L"CoCreateInstance(AppxFactory) failed. {}", get_last_error_or_default(hr));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = SHCreateStreamOnFileEx(appxPath.c_str(), STGM_READ | STGM_SHARE_DENY_WRITE, FILE_ATTRIBUTE_NORMAL, FALSE, nullptr, &stream);
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
Logger::error(L"SHCreateStreamOnFileEx failed. {}", get_last_error_or_default(hr));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = factory->CreatePackageReader(stream.Get(), &reader);
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
Logger::error(L"CreatePackageReader failed. {}", get_last_error_or_default(hr));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = reader->GetManifest(&manifest);
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
Logger::error(L"GetManifest failed. {}", get_last_error_or_default(hr));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = manifest->GetPackageId(&packageId);
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
Logger::error(L"GetPackageId failed. {}", get_last_error_or_default(hr));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LPWSTR name = nullptr;
|
||||||
|
hr = packageId->GetName(&name);
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
Logger::error(L"GetName failed. {}", get_last_error_or_default(hr));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT64 ver64 = 0;
|
||||||
|
hr = packageId->GetVersion(&ver64);
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
CoTaskMemFree(name);
|
||||||
|
Logger::error(L"GetVersion failed. {}", get_last_error_or_default(hr));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
outName = std::wstring(name);
|
||||||
|
CoTaskMemFree(name);
|
||||||
|
|
||||||
|
outVersion.Major = static_cast<UINT16>((ver64 >> 48) & 0xFFFF);
|
||||||
|
outVersion.Minor = static_cast<UINT16>((ver64 >> 32) & 0xFFFF);
|
||||||
|
outVersion.Build = static_cast<UINT16>((ver64 >> 16) & 0xFFFF);
|
||||||
|
outVersion.Revision = static_cast<UINT16>(ver64 & 0xFFFF);
|
||||||
|
|
||||||
|
Logger::info(L"Package name: {}, version: {}.{}.{}.{}, appxPath: {}",
|
||||||
|
outName,
|
||||||
|
outVersion.Major,
|
||||||
|
outVersion.Minor,
|
||||||
|
outVersion.Build,
|
||||||
|
outVersion.Revision,
|
||||||
|
appxPath);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (const std::exception& ex)
|
||||||
|
{
|
||||||
|
Logger::error(L"Standard exception: {}", winrt::to_hstring(ex.what()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
Logger::error(L"Unknown or non-standard exception occurred.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<Package> GetRegisteredPackage(std::wstring packageDisplayName, bool checkVersion)
|
||||||
|
{
|
||||||
|
PackageManager packageManager;
|
||||||
|
for (const auto& pkg : packageManager.FindPackagesForUser({}))
|
||||||
|
{
|
||||||
|
const auto& fullName = std::wstring{ pkg.Id().FullName() };
|
||||||
|
if (fullName.find(packageDisplayName) != std::wstring::npos)
|
||||||
|
{
|
||||||
|
if (!checkVersion)
|
||||||
|
{
|
||||||
|
return pkg;
|
||||||
|
}
|
||||||
|
const auto& ver = pkg.Id().Version();
|
||||||
|
if (ver.Major == VERSION_MAJOR && ver.Minor == VERSION_MINOR && ver.Revision == VERSION_REVISION)
|
||||||
|
{
|
||||||
|
return pkg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsPackageRegisteredWithPowerToysVersion(std::wstring packageDisplayName)
|
||||||
|
{
|
||||||
|
return GetRegisteredPackage(packageDisplayName, true).has_value();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RegisterSparsePackage(const std::wstring& externalLocation, const std::wstring& sparsePkgPath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Uri externalUri{ externalLocation };
|
||||||
|
Uri packageUri{ sparsePkgPath };
|
||||||
|
|
||||||
|
PackageManager packageManager;
|
||||||
|
AddPackageOptions options;
|
||||||
|
options.ExternalLocationUri(externalUri);
|
||||||
|
options.ForceUpdateFromAnyVersion(true);
|
||||||
|
|
||||||
|
auto deploymentOperation = packageManager.AddPackageByUriAsync(packageUri, options);
|
||||||
|
deploymentOperation.get();
|
||||||
|
|
||||||
|
if (deploymentOperation.Status() == AsyncStatus::Error)
|
||||||
|
{
|
||||||
|
auto deploymentResult{ deploymentOperation.GetResults() };
|
||||||
|
auto errorCode = deploymentOperation.ErrorCode();
|
||||||
|
auto errorText = deploymentResult.ErrorText();
|
||||||
|
Logger::error(L"Register {} package failed. ErrorCode: {}, ErrorText: {}", sparsePkgPath, std::to_wstring(errorCode), errorText);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (deploymentOperation.Status() == AsyncStatus::Canceled)
|
||||||
|
{
|
||||||
|
Logger::error(L"Register {} package canceled.", sparsePkgPath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (deploymentOperation.Status() == AsyncStatus::Completed)
|
||||||
|
{
|
||||||
|
Logger::info(L"Register {} package completed.", sparsePkgPath);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger::debug(L"Register {} package started.", sparsePkgPath);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (std::exception& e)
|
||||||
|
{
|
||||||
|
Logger::error("Exception thrown while trying to register package: {}", e.what());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UnRegisterPackage(const std::wstring& pkgDisplayName)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
PackageManager packageManager;
|
||||||
|
const auto packages = packageManager.FindPackagesForUser({});
|
||||||
|
|
||||||
|
bool any = false;
|
||||||
|
for (auto const& pkg : packages)
|
||||||
|
{
|
||||||
|
const auto& fullName = std::wstring{ pkg.Id().FullName() };
|
||||||
|
if (fullName.find(pkgDisplayName) != std::wstring::npos)
|
||||||
|
{
|
||||||
|
any = true;
|
||||||
|
auto op = packageManager.RemovePackageAsync(fullName);
|
||||||
|
op.get();
|
||||||
|
|
||||||
|
if (op.Status() == AsyncStatus::Error)
|
||||||
|
{
|
||||||
|
auto res = op.GetResults();
|
||||||
|
auto code = op.ErrorCode();
|
||||||
|
auto text = res.ErrorText();
|
||||||
|
Logger::error(L"Unregister {} package failed. ErrorCode: {}, ErrorText: {}", fullName, std::to_wstring(code), text);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (op.Status() == AsyncStatus::Canceled)
|
||||||
|
{
|
||||||
|
Logger::error(L"Unregister {} package canceled.", fullName);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (op.Status() == AsyncStatus::Completed)
|
||||||
|
{
|
||||||
|
Logger::info(L"Unregister {} package completed.", fullName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger::debug(L"Unregister {} package started.", fullName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If nothing matched, treat as success (nothing to remove)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (std::exception& e)
|
||||||
|
{
|
||||||
|
Logger::error("Exception thrown while trying to unregister package: {}", e.what());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::wstring> FindMsixFile(const std::wstring& directoryPath, bool recursive)
|
||||||
|
{
|
||||||
|
std::vector<std::wstring> results;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (recursive)
|
||||||
|
{
|
||||||
|
for (const auto& entry : std::filesystem::recursive_directory_iterator(directoryPath))
|
||||||
|
{
|
||||||
|
if (!entry.is_regular_file()) continue;
|
||||||
|
auto ext = entry.path().extension().wstring();
|
||||||
|
std::transform(ext.begin(), ext.end(), ext.begin(), ::towlower);
|
||||||
|
if (ext == L".msix" || ext == L".msixbundle")
|
||||||
|
{
|
||||||
|
results.push_back(entry.path().wstring());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (const auto& entry : std::filesystem::directory_iterator(directoryPath))
|
||||||
|
{
|
||||||
|
if (!entry.is_regular_file()) continue;
|
||||||
|
auto ext = entry.path().extension().wstring();
|
||||||
|
std::transform(ext.begin(), ext.end(), ext.begin(), ::towlower);
|
||||||
|
if (ext == L".msix" || ext == L".msixbundle")
|
||||||
|
{
|
||||||
|
results.push_back(entry.path().wstring());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
Logger::error(L"FindMsixFile error: {}", winrt::to_hstring(e.what()));
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsPackageSatisfied(const std::wstring& appxPath)
|
||||||
|
{
|
||||||
|
std::wstring targetName;
|
||||||
|
PACKAGE_VERSION targetVersion{};
|
||||||
|
if (!GetPackageNameAndVersionFromAppx(appxPath, targetName, targetVersion))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PackageManager pm;
|
||||||
|
for (const auto& pkg : pm.FindPackagesForUser({}))
|
||||||
|
{
|
||||||
|
if (std::wstring{ pkg.Id().Name() } == targetName)
|
||||||
|
{
|
||||||
|
auto v = pkg.Id().Version();
|
||||||
|
if (compare_versions(v, targetVersion) >= 0)
|
||||||
|
{
|
||||||
|
Logger::info(L"Package {} is satisfied. Installed version {}.{}.{}.{} >= target {}.{}.{}.{}, appxPath: {}",
|
||||||
|
targetName,
|
||||||
|
v.Major, v.Minor, v.Build, v.Revision,
|
||||||
|
targetVersion.Major, targetVersion.Minor, targetVersion.Build, targetVersion.Revision,
|
||||||
|
appxPath);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger::info(L"Package {} is not satisfied. Target version: {}.{}.{}.{}; appxPath: {}",
|
||||||
|
targetName,
|
||||||
|
targetVersion.Major, targetVersion.Minor, targetVersion.Build, targetVersion.Revision,
|
||||||
|
appxPath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RegisterPackage(std::wstring pkgPath, std::vector<std::wstring> dependencies)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Uri packageUri{ pkgPath };
|
||||||
|
PackageManager packageManager;
|
||||||
|
|
||||||
|
DeploymentOptions options = DeploymentOptions::ForceTargetApplicationShutdown;
|
||||||
|
Collections::IVector<Uri> uris = winrt::single_threaded_vector<Uri>();
|
||||||
|
|
||||||
|
for (const auto& dep : dependencies)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (IsPackageSatisfied(dep))
|
||||||
|
{
|
||||||
|
Logger::info(L"Dependency already satisfied: {}", dep);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
uris.Append(Uri(dep));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const winrt::hresult_error& ex)
|
||||||
|
{
|
||||||
|
Logger::error(L"Error creating Uri for dependency: %s", ex.message().c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto op = packageManager.AddPackageAsync(packageUri, uris, options);
|
||||||
|
op.get();
|
||||||
|
|
||||||
|
if (op.Status() == AsyncStatus::Error)
|
||||||
|
{
|
||||||
|
auto res = op.GetResults();
|
||||||
|
auto code = op.ErrorCode();
|
||||||
|
auto text = res.ErrorText();
|
||||||
|
Logger::error(L"Register {} package failed. ErrorCode: {}, ErrorText: {}", pkgPath, std::to_wstring(code), text);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (op.Status() == AsyncStatus::Canceled)
|
||||||
|
{
|
||||||
|
Logger::error(L"Register {} package canceled.", pkgPath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (op.Status() == AsyncStatus::Completed)
|
||||||
|
{
|
||||||
|
Logger::info(L"Register {} package completed.", pkgPath);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger::debug(L"Register {} package started.", pkgPath);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (std::exception& e)
|
||||||
|
{
|
||||||
|
Logger::error("Exception thrown while trying to register package: {}", e.what());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -15,9 +15,6 @@
|
|||||||
#include <winrt/Windows.Foundation.h>
|
#include <winrt/Windows.Foundation.h>
|
||||||
#include <winrt/Windows.Management.Deployment.h>
|
#include <winrt/Windows.Management.Deployment.h>
|
||||||
|
|
||||||
#include "../logger/logger.h"
|
|
||||||
#include "../version/version.h"
|
|
||||||
|
|
||||||
namespace package
|
namespace package
|
||||||
{
|
{
|
||||||
using namespace winrt::Windows::Foundation;
|
using namespace winrt::Windows::Foundation;
|
||||||
@@ -25,30 +22,7 @@ namespace package
|
|||||||
using namespace winrt::Windows::Management::Deployment;
|
using namespace winrt::Windows::Management::Deployment;
|
||||||
using Microsoft::WRL::ComPtr;
|
using Microsoft::WRL::ComPtr;
|
||||||
|
|
||||||
inline BOOL IsWin11OrGreater()
|
BOOL IsWin11OrGreater();
|
||||||
{
|
|
||||||
OSVERSIONINFOEX osvi{};
|
|
||||||
DWORDLONG dwlConditionMask = 0;
|
|
||||||
byte op = VER_GREATER_EQUAL;
|
|
||||||
|
|
||||||
// Initialize the OSVERSIONINFOEX structure.
|
|
||||||
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
|
|
||||||
osvi.dwMajorVersion = HIBYTE(_WIN32_WINNT_WINTHRESHOLD);
|
|
||||||
osvi.dwMinorVersion = LOBYTE(_WIN32_WINNT_WINTHRESHOLD);
|
|
||||||
// Windows 11 build number
|
|
||||||
osvi.dwBuildNumber = 22000;
|
|
||||||
|
|
||||||
// Initialize the condition mask.
|
|
||||||
VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, op);
|
|
||||||
VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, op);
|
|
||||||
VER_SET_CONDITION(dwlConditionMask, VER_BUILDNUMBER, op);
|
|
||||||
|
|
||||||
// Perform the test.
|
|
||||||
return VerifyVersionInfo(
|
|
||||||
&osvi,
|
|
||||||
VER_MAJORVERSION | VER_MINORVERSION | VER_BUILDNUMBER,
|
|
||||||
dwlConditionMask);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct PACKAGE_VERSION
|
struct PACKAGE_VERSION
|
||||||
{
|
{
|
||||||
@@ -61,412 +35,30 @@ namespace package
|
|||||||
class ComInitializer
|
class ComInitializer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit ComInitializer(DWORD coInitFlags = COINIT_MULTITHREADED) :
|
explicit ComInitializer(DWORD coInitFlags = COINIT_MULTITHREADED);
|
||||||
_initialized(false)
|
~ComInitializer();
|
||||||
{
|
bool Succeeded() const;
|
||||||
const HRESULT hr = CoInitializeEx(nullptr, coInitFlags);
|
|
||||||
_initialized = SUCCEEDED(hr);
|
|
||||||
}
|
|
||||||
|
|
||||||
~ComInitializer()
|
|
||||||
{
|
|
||||||
if (_initialized)
|
|
||||||
{
|
|
||||||
CoUninitialize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Succeeded() const { return _initialized; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _initialized;
|
bool _initialized;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline bool GetPackageNameAndVersionFromAppx(
|
bool GetPackageNameAndVersionFromAppx(
|
||||||
const std::wstring& appxPath,
|
const std::wstring& appxPath,
|
||||||
std::wstring& outName,
|
std::wstring& outName,
|
||||||
PACKAGE_VERSION& outVersion)
|
PACKAGE_VERSION& outVersion);
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ComInitializer comInit;
|
|
||||||
if (!comInit.Succeeded())
|
|
||||||
{
|
|
||||||
Logger::error(L"COM initialization failed.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ComPtr<IAppxFactory> factory;
|
std::optional<Package> GetRegisteredPackage(std::wstring packageDisplayName, bool checkVersion);
|
||||||
ComPtr<IStream> stream;
|
|
||||||
ComPtr<IAppxPackageReader> reader;
|
|
||||||
ComPtr<IAppxManifestReader> manifest;
|
|
||||||
ComPtr<IAppxManifestPackageId> packageId;
|
|
||||||
|
|
||||||
HRESULT hr = CoCreateInstance(__uuidof(AppxFactory), nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&factory));
|
bool IsPackageRegisteredWithPowerToysVersion(std::wstring packageDisplayName);
|
||||||
if (FAILED(hr))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
hr = SHCreateStreamOnFileEx(appxPath.c_str(), STGM_READ | STGM_SHARE_DENY_WRITE, FILE_ATTRIBUTE_NORMAL, FALSE, nullptr, &stream);
|
bool RegisterSparsePackage(const std::wstring& externalLocation, const std::wstring& sparsePkgPath);
|
||||||
if (FAILED(hr))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
hr = factory->CreatePackageReader(stream.Get(), &reader);
|
bool UnRegisterPackage(const std::wstring& pkgDisplayName);
|
||||||
if (FAILED(hr))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
hr = reader->GetManifest(&manifest);
|
std::vector<std::wstring> FindMsixFile(const std::wstring& directoryPath, bool recursive);
|
||||||
if (FAILED(hr))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
hr = manifest->GetPackageId(&packageId);
|
bool IsPackageSatisfied(const std::wstring& appxPath);
|
||||||
if (FAILED(hr))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
LPWSTR name = nullptr;
|
bool RegisterPackage(std::wstring pkgPath, std::vector<std::wstring> dependencies);
|
||||||
hr = packageId->GetName(&name);
|
|
||||||
if (FAILED(hr))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
UINT64 version = 0;
|
|
||||||
hr = packageId->GetVersion(&version);
|
|
||||||
if (FAILED(hr))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
outName = std::wstring(name);
|
|
||||||
CoTaskMemFree(name);
|
|
||||||
|
|
||||||
outVersion.Major = static_cast<UINT16>((version >> 48) & 0xFFFF);
|
|
||||||
outVersion.Minor = static_cast<UINT16>((version >> 32) & 0xFFFF);
|
|
||||||
outVersion.Build = static_cast<UINT16>((version >> 16) & 0xFFFF);
|
|
||||||
outVersion.Revision = static_cast<UINT16>(version & 0xFFFF);
|
|
||||||
|
|
||||||
Logger::info(L"Package name: {}, version: {}.{}.{}.{}, appxPath: {}",
|
|
||||||
outName,
|
|
||||||
outVersion.Major,
|
|
||||||
outVersion.Minor,
|
|
||||||
outVersion.Build,
|
|
||||||
outVersion.Revision,
|
|
||||||
appxPath);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (const std::exception& ex)
|
|
||||||
{
|
|
||||||
Logger::error(L"Standard exception: {}", winrt::to_hstring(ex.what()));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
Logger::error(L"Unknown or non-standard exception occurred.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::optional<Package> GetRegisteredPackage(std::wstring packageDisplayName, bool checkVersion)
|
|
||||||
{
|
|
||||||
PackageManager packageManager;
|
|
||||||
|
|
||||||
for (const auto& package : packageManager.FindPackagesForUser({}))
|
|
||||||
{
|
|
||||||
const auto& packageFullName = std::wstring{ package.Id().FullName() };
|
|
||||||
const auto& packageVersion = package.Id().Version();
|
|
||||||
|
|
||||||
if (packageFullName.contains(packageDisplayName))
|
|
||||||
{
|
|
||||||
// If checkVersion is true, verify if the package has the same version as PowerToys.
|
|
||||||
if ((!checkVersion) || (packageVersion.Major == VERSION_MAJOR && packageVersion.Minor == VERSION_MINOR && packageVersion.Revision == VERSION_REVISION))
|
|
||||||
{
|
|
||||||
return { package };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool IsPackageRegisteredWithPowerToysVersion(std::wstring packageDisplayName)
|
|
||||||
{
|
|
||||||
return GetRegisteredPackage(packageDisplayName, true).has_value();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool RegisterSparsePackage(const std::wstring& externalLocation, const std::wstring& sparsePkgPath)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Uri externalUri{ externalLocation };
|
|
||||||
Uri packageUri{ sparsePkgPath };
|
|
||||||
|
|
||||||
PackageManager packageManager;
|
|
||||||
|
|
||||||
// Declare use of an external location
|
|
||||||
AddPackageOptions options;
|
|
||||||
options.ExternalLocationUri(externalUri);
|
|
||||||
options.ForceUpdateFromAnyVersion(true);
|
|
||||||
|
|
||||||
IAsyncOperationWithProgress<DeploymentResult, DeploymentProgress> deploymentOperation = packageManager.AddPackageByUriAsync(packageUri, options);
|
|
||||||
deploymentOperation.get();
|
|
||||||
|
|
||||||
// Check the status of the operation
|
|
||||||
if (deploymentOperation.Status() == AsyncStatus::Error)
|
|
||||||
{
|
|
||||||
auto deploymentResult{ deploymentOperation.GetResults() };
|
|
||||||
auto errorCode = deploymentOperation.ErrorCode();
|
|
||||||
auto errorText = deploymentResult.ErrorText();
|
|
||||||
|
|
||||||
Logger::error(L"Register {} package failed. ErrorCode: {}, ErrorText: {}", sparsePkgPath, std::to_wstring(errorCode), errorText);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (deploymentOperation.Status() == AsyncStatus::Canceled)
|
|
||||||
{
|
|
||||||
Logger::error(L"Register {} package canceled.", sparsePkgPath);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (deploymentOperation.Status() == AsyncStatus::Completed)
|
|
||||||
{
|
|
||||||
Logger::info(L"Register {} package completed.", sparsePkgPath);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Logger::debug(L"Register {} package started.", sparsePkgPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (std::exception& e)
|
|
||||||
{
|
|
||||||
Logger::error("Exception thrown while trying to register package: {}", e.what());
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool UnRegisterPackage(const std::wstring& pkgDisplayName)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
PackageManager packageManager;
|
|
||||||
const static auto packages = packageManager.FindPackagesForUser({});
|
|
||||||
|
|
||||||
for (auto const& package : packages)
|
|
||||||
{
|
|
||||||
const auto& packageFullName = std::wstring{ package.Id().FullName() };
|
|
||||||
|
|
||||||
if (packageFullName.contains(pkgDisplayName))
|
|
||||||
{
|
|
||||||
auto deploymentOperation{ packageManager.RemovePackageAsync(packageFullName) };
|
|
||||||
deploymentOperation.get();
|
|
||||||
|
|
||||||
// Check the status of the operation
|
|
||||||
if (deploymentOperation.Status() == AsyncStatus::Error)
|
|
||||||
{
|
|
||||||
auto deploymentResult{ deploymentOperation.GetResults() };
|
|
||||||
auto errorCode = deploymentOperation.ErrorCode();
|
|
||||||
auto errorText = deploymentResult.ErrorText();
|
|
||||||
|
|
||||||
Logger::error(L"Unregister {} package failed. ErrorCode: {}, ErrorText: {}", packageFullName, std::to_wstring(errorCode), errorText);
|
|
||||||
}
|
|
||||||
else if (deploymentOperation.Status() == AsyncStatus::Canceled)
|
|
||||||
{
|
|
||||||
Logger::error(L"Unregister {} package canceled.", packageFullName);
|
|
||||||
}
|
|
||||||
else if (deploymentOperation.Status() == AsyncStatus::Completed)
|
|
||||||
{
|
|
||||||
Logger::info(L"Unregister {} package completed.", packageFullName);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Logger::debug(L"Unregister {} package started.", packageFullName);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (std::exception& e)
|
|
||||||
{
|
|
||||||
Logger::error("Exception thrown while trying to unregister package: {}", e.what());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::vector<std::wstring> FindMsixFile(const std::wstring& directoryPath, bool recursive)
|
|
||||||
{
|
|
||||||
if (directoryPath.empty())
|
|
||||||
{
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!std::filesystem::exists(directoryPath))
|
|
||||||
{
|
|
||||||
Logger::error(L"The directory '" + directoryPath + L"' does not exist.");
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::regex pattern(R"(^.+\.(appx|msix|msixbundle)$)", std::regex_constants::icase);
|
|
||||||
std::vector<std::wstring> matchedFiles;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (recursive)
|
|
||||||
{
|
|
||||||
for (const auto& entry : std::filesystem::recursive_directory_iterator(directoryPath))
|
|
||||||
{
|
|
||||||
if (entry.is_regular_file())
|
|
||||||
{
|
|
||||||
const auto& fileName = entry.path().filename().string();
|
|
||||||
if (std::regex_match(fileName, pattern))
|
|
||||||
{
|
|
||||||
matchedFiles.push_back(entry.path());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (const auto& entry : std::filesystem::directory_iterator(directoryPath))
|
|
||||||
{
|
|
||||||
if (entry.is_regular_file())
|
|
||||||
{
|
|
||||||
const auto& fileName = entry.path().filename().string();
|
|
||||||
if (std::regex_match(fileName, pattern))
|
|
||||||
{
|
|
||||||
matchedFiles.push_back(entry.path());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (const std::exception& ex)
|
|
||||||
{
|
|
||||||
Logger::error("An error occurred while searching for MSIX files: " + std::string(ex.what()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return matchedFiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool IsPackageSatisfied(const std::wstring& appxPath)
|
|
||||||
{
|
|
||||||
std::wstring targetName;
|
|
||||||
PACKAGE_VERSION targetVersion{};
|
|
||||||
|
|
||||||
if (!GetPackageNameAndVersionFromAppx(appxPath, targetName, targetVersion))
|
|
||||||
{
|
|
||||||
Logger::error(L"Failed to get package name and version from appx: " + appxPath);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
PackageManager pm;
|
|
||||||
|
|
||||||
for (const auto& package : pm.FindPackagesForUser({}))
|
|
||||||
{
|
|
||||||
const auto& id = package.Id();
|
|
||||||
if (std::wstring(id.Name()) == targetName)
|
|
||||||
{
|
|
||||||
const auto& version = id.Version();
|
|
||||||
|
|
||||||
if (version.Major > targetVersion.Major ||
|
|
||||||
(version.Major == targetVersion.Major && version.Minor > targetVersion.Minor) ||
|
|
||||||
(version.Major == targetVersion.Major && version.Minor == targetVersion.Minor && version.Build > targetVersion.Build) ||
|
|
||||||
(version.Major == targetVersion.Major && version.Minor == targetVersion.Minor && version.Build == targetVersion.Build && version.Revision >= targetVersion.Revision))
|
|
||||||
{
|
|
||||||
Logger::info(
|
|
||||||
L"Package {} is already satisfied with version {}.{}.{}.{}; target version {}.{}.{}.{}; appxPath: {}",
|
|
||||||
id.Name(),
|
|
||||||
version.Major,
|
|
||||||
version.Minor,
|
|
||||||
version.Build,
|
|
||||||
version.Revision,
|
|
||||||
targetVersion.Major,
|
|
||||||
targetVersion.Minor,
|
|
||||||
targetVersion.Build,
|
|
||||||
targetVersion.Revision,
|
|
||||||
appxPath);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger::info(
|
|
||||||
L"Package {} is not satisfied. Target version: {}.{}.{}.{}; appxPath: {}",
|
|
||||||
targetName,
|
|
||||||
targetVersion.Major,
|
|
||||||
targetVersion.Minor,
|
|
||||||
targetVersion.Build,
|
|
||||||
targetVersion.Revision,
|
|
||||||
appxPath);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool RegisterPackage(std::wstring pkgPath, std::vector<std::wstring> dependencies)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Uri packageUri{ pkgPath };
|
|
||||||
|
|
||||||
PackageManager packageManager;
|
|
||||||
|
|
||||||
// Declare use of an external location
|
|
||||||
DeploymentOptions options = DeploymentOptions::ForceTargetApplicationShutdown;
|
|
||||||
|
|
||||||
Collections::IVector<Uri> uris = winrt::single_threaded_vector<Uri>();
|
|
||||||
if (!dependencies.empty())
|
|
||||||
{
|
|
||||||
for (const auto& dependency : dependencies)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (IsPackageSatisfied(dependency))
|
|
||||||
{
|
|
||||||
Logger::info(L"Dependency already satisfied: {}", dependency);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
uris.Append(Uri(dependency));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (const winrt::hresult_error& ex)
|
|
||||||
{
|
|
||||||
Logger::error(L"Error creating Uri for dependency: %s", ex.message().c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IAsyncOperationWithProgress<DeploymentResult, DeploymentProgress> deploymentOperation = packageManager.AddPackageAsync(packageUri, uris, options);
|
|
||||||
deploymentOperation.get();
|
|
||||||
|
|
||||||
// Check the status of the operation
|
|
||||||
if (deploymentOperation.Status() == AsyncStatus::Error)
|
|
||||||
{
|
|
||||||
auto deploymentResult{ deploymentOperation.GetResults() };
|
|
||||||
auto errorCode = deploymentOperation.ErrorCode();
|
|
||||||
auto errorText = deploymentResult.ErrorText();
|
|
||||||
|
|
||||||
Logger::error(L"Register {} package failed. ErrorCode: {}, ErrorText: {}", pkgPath, std::to_wstring(errorCode), errorText);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (deploymentOperation.Status() == AsyncStatus::Canceled)
|
|
||||||
{
|
|
||||||
Logger::error(L"Register {} package canceled.", pkgPath);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (deploymentOperation.Status() == AsyncStatus::Completed)
|
|
||||||
{
|
|
||||||
Logger::info(L"Register {} package completed.", pkgPath);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Logger::debug(L"Register {} package started.", pkgPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (std::exception& e)
|
|
||||||
{
|
|
||||||
Logger::error("Exception thrown while trying to register package: {}", e.what());
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
5
src/common/utils/packages.config
Normal file
5
src/common/utils/packages.config
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
|
||||||
|
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.231216.1" targetFramework="native" />
|
||||||
|
</packages>
|
||||||
1
src/common/utils/pch.cpp
Normal file
1
src/common/utils/pch.cpp
Normal file
@@ -0,0 +1 @@
|
|||||||
|
#include "pch.h"
|
||||||
38
src/common/utils/pch.h
Normal file
38
src/common/utils/pch.h
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <shellapi.h>
|
||||||
|
#include <sddl.h>
|
||||||
|
#include <shldisp.h>
|
||||||
|
#include <shlobj.h>
|
||||||
|
#include <Shlwapi.h>
|
||||||
|
#include <exdisp.h>
|
||||||
|
#include <atlbase.h>
|
||||||
|
#include <comdef.h>
|
||||||
|
#include <appxpackaging.h>
|
||||||
|
|
||||||
|
#include <winrt/base.h>
|
||||||
|
#include <winrt/Windows.Foundation.h>
|
||||||
|
#include <winrt/Windows.Foundation.Collections.h>
|
||||||
|
#include <winrt/Windows.ApplicationModel.h>
|
||||||
|
#include <winrt/Windows.Management.Deployment.h>
|
||||||
|
#include <wrl/client.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <optional>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <regex>
|
||||||
|
#include <fstream>
|
||||||
|
#include <exception>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include <wil/result.h>
|
||||||
|
#include <wil/com.h>
|
||||||
|
#include <wil/resource.h>
|
||||||
|
|
||||||
|
#include <common/logger/logger.h>
|
||||||
|
#include <common/version/version.h>
|
||||||
44
src/common/utils/processApi.cpp
Normal file
44
src/common/utils/processApi.cpp
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "processApi.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
std::vector<wil::unique_process_handle> getProcessHandlesByName(std::wstring_view processName, DWORD handleAccess)
|
||||||
|
{
|
||||||
|
std::vector<wil::unique_process_handle> result;
|
||||||
|
DWORD bytesRequired = 0;
|
||||||
|
std::vector<DWORD> processIds;
|
||||||
|
processIds.resize(4096 / sizeof(processIds[0]));
|
||||||
|
auto processIdSize = static_cast<DWORD>(processIds.size() * sizeof(processIds[0]));
|
||||||
|
EnumProcesses(processIds.data(), processIdSize, &bytesRequired);
|
||||||
|
while (bytesRequired == processIdSize)
|
||||||
|
{
|
||||||
|
processIdSize *= 2;
|
||||||
|
processIds.resize(processIdSize / sizeof(processIds[0]));
|
||||||
|
EnumProcesses(processIds.data(), processIdSize, &bytesRequired);
|
||||||
|
}
|
||||||
|
processIds.resize(bytesRequired / sizeof(processIds[0]));
|
||||||
|
|
||||||
|
handleAccess |= PROCESS_QUERY_LIMITED_INFORMATION;
|
||||||
|
for (const DWORD processId : processIds)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
wil::unique_process_handle hProcess{ OpenProcess(handleAccess, FALSE, processId) };
|
||||||
|
wchar_t name[MAX_PATH + 1];
|
||||||
|
DWORD length = MAX_PATH;
|
||||||
|
if (!hProcess || !QueryFullProcessImageNameW(hProcess.get(), 0, name, &length))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (processName == PathFindFileNameW(name))
|
||||||
|
{
|
||||||
|
result.push_back(std::move(hProcess));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
@@ -6,42 +6,4 @@
|
|||||||
#include <Psapi.h>
|
#include <Psapi.h>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
inline std::vector<wil::unique_process_handle> getProcessHandlesByName(const std::wstring_view processName, DWORD handleAccess)
|
std::vector<wil::unique_process_handle> getProcessHandlesByName(std::wstring_view processName, DWORD handleAccess);
|
||||||
{
|
|
||||||
std::vector<wil::unique_process_handle> result;
|
|
||||||
DWORD bytesRequired;
|
|
||||||
std::vector<DWORD> processIds;
|
|
||||||
processIds.resize(4096 / sizeof(processIds[0]));
|
|
||||||
auto processIdSize = static_cast<DWORD>(size(processIds) * sizeof(processIds[0]));
|
|
||||||
EnumProcesses(processIds.data(), processIdSize, &bytesRequired);
|
|
||||||
while (bytesRequired == processIdSize)
|
|
||||||
{
|
|
||||||
processIdSize *= 2;
|
|
||||||
processIds.resize(processIdSize / sizeof(processIds[0]));
|
|
||||||
EnumProcesses(processIds.data(), processIdSize, &bytesRequired);
|
|
||||||
}
|
|
||||||
processIds.resize(bytesRequired / sizeof(processIds[0]));
|
|
||||||
|
|
||||||
handleAccess |= PROCESS_QUERY_LIMITED_INFORMATION;
|
|
||||||
for (const DWORD processId : processIds)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
wil::unique_process_handle hProcess{ OpenProcess(handleAccess, FALSE, processId) };
|
|
||||||
wchar_t name[MAX_PATH + 1];
|
|
||||||
DWORD length = MAX_PATH;
|
|
||||||
if (!hProcess || !QueryFullProcessImageNameW(hProcess.get(), 0, name, &length))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (processName == PathFindFileNameW(name))
|
|
||||||
{
|
|
||||||
result.push_back(std::move(hProcess));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|||||||
111
src/common/utils/process_path.cpp
Normal file
111
src/common/utils/process_path.cpp
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "process_path.h"
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
std::wstring get_process_path(DWORD pid) noexcept
|
||||||
|
{
|
||||||
|
wil::unique_handle process{ OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, TRUE, pid) };
|
||||||
|
std::wstring name;
|
||||||
|
if (process)
|
||||||
|
{
|
||||||
|
name.resize(MAX_PATH);
|
||||||
|
DWORD nameLength = static_cast<DWORD>(name.length());
|
||||||
|
if (QueryFullProcessImageNameW(process.get(), 0, name.data(), &nameLength) == 0)
|
||||||
|
{
|
||||||
|
nameLength = 0;
|
||||||
|
}
|
||||||
|
name.resize(nameLength);
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring get_process_path(HWND window) noexcept
|
||||||
|
{
|
||||||
|
const static std::wstring appFrameHost = L"ApplicationFrameHost.exe";
|
||||||
|
|
||||||
|
DWORD pid{};
|
||||||
|
GetWindowThreadProcessId(window, &pid);
|
||||||
|
auto name = get_process_path(pid);
|
||||||
|
|
||||||
|
if (name.length() >= appFrameHost.length() &&
|
||||||
|
name.compare(name.length() - appFrameHost.length(), appFrameHost.length(), appFrameHost) == 0)
|
||||||
|
{
|
||||||
|
DWORD newPid = pid;
|
||||||
|
|
||||||
|
EnumChildWindows(
|
||||||
|
window,
|
||||||
|
[](HWND hwnd, LPARAM param) -> BOOL {
|
||||||
|
auto newPidPtr = reinterpret_cast<DWORD*>(param);
|
||||||
|
DWORD childPid;
|
||||||
|
GetWindowThreadProcessId(hwnd, &childPid);
|
||||||
|
if (childPid != *newPidPtr)
|
||||||
|
{
|
||||||
|
*newPidPtr = childPid;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
},
|
||||||
|
reinterpret_cast<LPARAM>(&newPid));
|
||||||
|
|
||||||
|
if (newPid != pid)
|
||||||
|
{
|
||||||
|
return get_process_path(newPid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring get_process_path_waiting_uwp(HWND window)
|
||||||
|
{
|
||||||
|
const static std::wstring appFrameHost = L"ApplicationFrameHost.exe";
|
||||||
|
|
||||||
|
int attempt = 0;
|
||||||
|
auto processPath = get_process_path(window);
|
||||||
|
|
||||||
|
while (++attempt < 30 && processPath.length() >= appFrameHost.length() &&
|
||||||
|
processPath.compare(processPath.length() - appFrameHost.length(), appFrameHost.length(), appFrameHost) == 0)
|
||||||
|
{
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||||
|
processPath = get_process_path(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
return processPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring get_module_filename(HMODULE mod)
|
||||||
|
{
|
||||||
|
wchar_t buffer[MAX_PATH + 1];
|
||||||
|
DWORD actualLength = GetModuleFileNameW(mod, buffer, MAX_PATH);
|
||||||
|
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
|
||||||
|
{
|
||||||
|
const DWORD longPathLength = 0xFFFF;
|
||||||
|
std::wstring longFilename(longPathLength, L'\0');
|
||||||
|
actualLength = GetModuleFileNameW(mod, longFilename.data(), longPathLength);
|
||||||
|
return longFilename.substr(0, actualLength);
|
||||||
|
}
|
||||||
|
return { buffer, actualLength };
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring get_module_folderpath(HMODULE mod, bool removeFilename)
|
||||||
|
{
|
||||||
|
wchar_t buffer[MAX_PATH + 1];
|
||||||
|
DWORD actualLength = GetModuleFileNameW(mod, buffer, MAX_PATH);
|
||||||
|
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
|
||||||
|
{
|
||||||
|
const DWORD longPathLength = 0xFFFF;
|
||||||
|
std::wstring longFilename(longPathLength, L'\0');
|
||||||
|
actualLength = GetModuleFileNameW(mod, longFilename.data(), longPathLength);
|
||||||
|
PathRemoveFileSpecW(longFilename.data());
|
||||||
|
longFilename.resize(std::wcslen(longFilename.data()));
|
||||||
|
longFilename.shrink_to_fit();
|
||||||
|
return longFilename;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (removeFilename)
|
||||||
|
{
|
||||||
|
PathRemoveFileSpecW(buffer);
|
||||||
|
}
|
||||||
|
return { buffer, static_cast<uint64_t>(lstrlenW(buffer)) };
|
||||||
|
}
|
||||||
@@ -7,116 +7,13 @@
|
|||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
// Get the executable path or module name for modern apps
|
// Get the executable path or module name for modern apps
|
||||||
inline std::wstring get_process_path(DWORD pid) noexcept
|
std::wstring get_process_path(DWORD pid) noexcept;
|
||||||
{
|
|
||||||
auto process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, TRUE, pid);
|
|
||||||
std::wstring name;
|
|
||||||
if (process != INVALID_HANDLE_VALUE)
|
|
||||||
{
|
|
||||||
name.resize(MAX_PATH);
|
|
||||||
DWORD name_length = static_cast<DWORD>(name.length());
|
|
||||||
if (QueryFullProcessImageNameW(process, 0, name.data(), &name_length) == 0)
|
|
||||||
{
|
|
||||||
name_length = 0;
|
|
||||||
}
|
|
||||||
name.resize(name_length);
|
|
||||||
CloseHandle(process);
|
|
||||||
}
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the executable path or module name for modern apps
|
// Get the executable path or module name for modern apps
|
||||||
inline std::wstring get_process_path(HWND window) noexcept
|
std::wstring get_process_path(HWND window) noexcept;
|
||||||
{
|
|
||||||
const static std::wstring app_frame_host = L"ApplicationFrameHost.exe";
|
|
||||||
|
|
||||||
DWORD pid{};
|
std::wstring get_process_path_waiting_uwp(HWND window);
|
||||||
GetWindowThreadProcessId(window, &pid);
|
|
||||||
auto name = get_process_path(pid);
|
|
||||||
|
|
||||||
if (name.length() >= app_frame_host.length() &&
|
std::wstring get_module_filename(HMODULE mod = nullptr);
|
||||||
name.compare(name.length() - app_frame_host.length(), app_frame_host.length(), app_frame_host) == 0)
|
|
||||||
{
|
|
||||||
// It is a UWP app. We will enumerate the windows and look for one created
|
|
||||||
// by something with a different PID
|
|
||||||
DWORD new_pid = pid;
|
|
||||||
|
|
||||||
EnumChildWindows(
|
std::wstring get_module_folderpath(HMODULE mod = nullptr, bool removeFilename = true);
|
||||||
window, [](HWND hwnd, LPARAM param) -> BOOL {
|
|
||||||
auto new_pid_ptr = reinterpret_cast<DWORD*>(param);
|
|
||||||
DWORD pid;
|
|
||||||
GetWindowThreadProcessId(hwnd, &pid);
|
|
||||||
if (pid != *new_pid_ptr)
|
|
||||||
{
|
|
||||||
*new_pid_ptr = pid;
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
reinterpret_cast<LPARAM>(&new_pid));
|
|
||||||
|
|
||||||
// If we have a new pid, get the new name.
|
|
||||||
if (new_pid != pid)
|
|
||||||
{
|
|
||||||
return get_process_path(new_pid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::wstring get_process_path_waiting_uwp(HWND window)
|
|
||||||
{
|
|
||||||
const static std::wstring appFrameHost = L"ApplicationFrameHost.exe";
|
|
||||||
|
|
||||||
int attempt = 0;
|
|
||||||
auto processPath = get_process_path(window);
|
|
||||||
|
|
||||||
while (++attempt < 30 && processPath.length() >= appFrameHost.length() &&
|
|
||||||
processPath.compare(processPath.length() - appFrameHost.length(), appFrameHost.length(), appFrameHost) == 0)
|
|
||||||
{
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
|
||||||
processPath = get_process_path(window);
|
|
||||||
}
|
|
||||||
|
|
||||||
return processPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::wstring get_module_filename(HMODULE mod = nullptr)
|
|
||||||
{
|
|
||||||
wchar_t buffer[MAX_PATH + 1];
|
|
||||||
DWORD actual_length = GetModuleFileNameW(mod, buffer, MAX_PATH);
|
|
||||||
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
|
|
||||||
{
|
|
||||||
const DWORD long_path_length = 0xFFFF; // should be always enough
|
|
||||||
std::wstring long_filename(long_path_length, L'\0');
|
|
||||||
actual_length = GetModuleFileNameW(mod, long_filename.data(), long_path_length);
|
|
||||||
return long_filename.substr(0, actual_length);
|
|
||||||
}
|
|
||||||
return { buffer, actual_length };
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::wstring get_module_folderpath(HMODULE mod = nullptr, const bool removeFilename = true)
|
|
||||||
{
|
|
||||||
wchar_t buffer[MAX_PATH + 1];
|
|
||||||
DWORD actual_length = GetModuleFileNameW(mod, buffer, MAX_PATH);
|
|
||||||
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
|
|
||||||
{
|
|
||||||
const DWORD long_path_length = 0xFFFF; // should be always enough
|
|
||||||
std::wstring long_filename(long_path_length, L'\0');
|
|
||||||
actual_length = GetModuleFileNameW(mod, long_filename.data(), long_path_length);
|
|
||||||
PathRemoveFileSpecW(long_filename.data());
|
|
||||||
long_filename.resize(std::wcslen(long_filename.data()));
|
|
||||||
long_filename.shrink_to_fit();
|
|
||||||
return long_filename;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (removeFilename)
|
|
||||||
{
|
|
||||||
PathRemoveFileSpecW(buffer);
|
|
||||||
}
|
|
||||||
return { buffer, static_cast<uint64_t>(lstrlenW(buffer))};
|
|
||||||
}
|
|
||||||
|
|||||||
398
src/common/utils/registry.cpp
Normal file
398
src/common/utils/registry.cpp
Normal file
@@ -0,0 +1,398 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "registry.h"
|
||||||
|
|
||||||
|
namespace registry
|
||||||
|
{
|
||||||
|
namespace install_scope
|
||||||
|
{
|
||||||
|
const InstallScope get_current_install_scope()
|
||||||
|
{
|
||||||
|
// Open HKLM key
|
||||||
|
HKEY perMachineKey{};
|
||||||
|
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
||||||
|
INSTALL_SCOPE_REG_KEY,
|
||||||
|
0,
|
||||||
|
KEY_READ,
|
||||||
|
&perMachineKey) != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
// Open HKCU key
|
||||||
|
HKEY perUserKey{};
|
||||||
|
if (RegOpenKeyExW(HKEY_CURRENT_USER,
|
||||||
|
INSTALL_SCOPE_REG_KEY,
|
||||||
|
0,
|
||||||
|
KEY_READ,
|
||||||
|
&perUserKey) != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
// both keys are missing
|
||||||
|
return InstallScope::PerMachine;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DWORD dataSize{};
|
||||||
|
if (RegGetValueW(
|
||||||
|
perUserKey,
|
||||||
|
nullptr,
|
||||||
|
L"InstallScope",
|
||||||
|
RRF_RT_REG_SZ,
|
||||||
|
nullptr,
|
||||||
|
nullptr,
|
||||||
|
&dataSize) != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
// HKCU key is missing
|
||||||
|
RegCloseKey(perUserKey);
|
||||||
|
return InstallScope::PerMachine;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring data;
|
||||||
|
data.resize(dataSize / sizeof(wchar_t));
|
||||||
|
|
||||||
|
if (RegGetValueW(
|
||||||
|
perUserKey,
|
||||||
|
nullptr,
|
||||||
|
L"InstallScope",
|
||||||
|
RRF_RT_REG_SZ,
|
||||||
|
nullptr,
|
||||||
|
&data[0],
|
||||||
|
&dataSize) != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
// HKCU key is missing
|
||||||
|
RegCloseKey(perUserKey);
|
||||||
|
return InstallScope::PerMachine;
|
||||||
|
}
|
||||||
|
RegCloseKey(perUserKey);
|
||||||
|
|
||||||
|
if (data.contains(L"perUser"))
|
||||||
|
{
|
||||||
|
return InstallScope::PerUser;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return InstallScope::PerMachine;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
const wchar_t* getScopeName(HKEY scope)
|
||||||
|
{
|
||||||
|
if (scope == HKEY_LOCAL_MACHINE)
|
||||||
|
{
|
||||||
|
return L"HKLM";
|
||||||
|
}
|
||||||
|
else if (scope == HKEY_CURRENT_USER)
|
||||||
|
{
|
||||||
|
return L"HKCU";
|
||||||
|
}
|
||||||
|
else if (scope == HKEY_CLASSES_ROOT)
|
||||||
|
{
|
||||||
|
return L"HKCR";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return L"HK??";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring ValueChange::toString() const
|
||||||
|
{
|
||||||
|
using namespace detail;
|
||||||
|
|
||||||
|
std::wstring value_str;
|
||||||
|
std::visit(overloaded{ [&](DWORD value) {
|
||||||
|
std::wostringstream oss;
|
||||||
|
oss << value;
|
||||||
|
value_str = oss.str();
|
||||||
|
},
|
||||||
|
[&](const std::wstring& value) { value_str = value; } },
|
||||||
|
value);
|
||||||
|
|
||||||
|
return fmt::format(L"{}\\{}\\{}:{}", detail::getScopeName(scope), path, name ? *name : L"Default", value_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ValueChange::isApplied() const
|
||||||
|
{
|
||||||
|
HKEY key{};
|
||||||
|
if (auto res = RegOpenKeyExW(scope, path.c_str(), 0, KEY_READ, &key); res != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
Logger::info(L"isApplied of {}: RegOpenKeyExW failed: {}", toString(), get_last_error_or_default(res));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
detail::on_exit closeKey{ [key] { RegCloseKey(key); } };
|
||||||
|
|
||||||
|
const DWORD expectedType = valueTypeToWinapiType(value);
|
||||||
|
|
||||||
|
DWORD retrievedType{};
|
||||||
|
wchar_t buffer[VALUE_BUFFER_SIZE];
|
||||||
|
DWORD valueSize = sizeof(buffer);
|
||||||
|
if (auto res = RegQueryValueExW(key,
|
||||||
|
name.has_value() ? name->c_str() : nullptr,
|
||||||
|
0,
|
||||||
|
&retrievedType,
|
||||||
|
reinterpret_cast<LPBYTE>(&buffer),
|
||||||
|
&valueSize);
|
||||||
|
res != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
Logger::info(L"isApplied of {}: RegQueryValueExW failed: {}", toString(), get_last_error_or_default(res));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expectedType != retrievedType)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const auto retrievedValue = bufferToValue(buffer, valueSize, retrievedType))
|
||||||
|
{
|
||||||
|
return value == retrievedValue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ValueChange::apply() const
|
||||||
|
{
|
||||||
|
HKEY key{};
|
||||||
|
|
||||||
|
if (auto res = RegCreateKeyExW(scope, path.c_str(), 0, nullptr, REG_OPTION_NON_VOLATILE, KEY_WRITE, nullptr, &key, nullptr); res !=
|
||||||
|
ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
Logger::error(L"apply of {}: RegCreateKeyExW failed: {}", toString(), get_last_error_or_default(res));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
detail::on_exit closeKey{ [key] { RegCloseKey(key); } };
|
||||||
|
|
||||||
|
wchar_t buffer[VALUE_BUFFER_SIZE];
|
||||||
|
DWORD valueSize;
|
||||||
|
DWORD valueType;
|
||||||
|
|
||||||
|
valueToBuffer(value, buffer, valueSize, valueType);
|
||||||
|
if (auto res = RegSetValueExW(key,
|
||||||
|
name.has_value() ? name->c_str() : nullptr,
|
||||||
|
0,
|
||||||
|
valueType,
|
||||||
|
reinterpret_cast<BYTE*>(buffer),
|
||||||
|
valueSize);
|
||||||
|
res != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
Logger::error(L"apply of {}: RegSetValueExW failed: {}", toString(), get_last_error_or_default(res));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ValueChange::unApply() const
|
||||||
|
{
|
||||||
|
HKEY key{};
|
||||||
|
if (auto res = RegOpenKeyExW(scope, path.c_str(), 0, KEY_ALL_ACCESS, &key); res != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
Logger::error(L"unApply of {}: RegOpenKeyExW failed: {}", toString(), get_last_error_or_default(res));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
detail::on_exit closeKey{ [key] { RegCloseKey(key); } };
|
||||||
|
|
||||||
|
// delete the value itself
|
||||||
|
if (auto res = RegDeleteKeyValueW(scope, path.c_str(), name.has_value() ? name->c_str() : nullptr); res != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
Logger::error(L"unApply of {}: RegDeleteKeyValueW failed: {}", toString(), get_last_error_or_default(res));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the path doesn't contain anything and delete it if so
|
||||||
|
DWORD nValues = 0;
|
||||||
|
DWORD maxValueLen = 0;
|
||||||
|
const auto ok =
|
||||||
|
RegQueryInfoKeyW(
|
||||||
|
key, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, &nValues, nullptr, &maxValueLen, nullptr, nullptr) ==
|
||||||
|
ERROR_SUCCESS;
|
||||||
|
|
||||||
|
if (ok && (!nValues || !maxValueLen))
|
||||||
|
{
|
||||||
|
RegDeleteTreeW(scope, path.c_str());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD ValueChange::valueTypeToWinapiType(const value_t& v)
|
||||||
|
{
|
||||||
|
return std::visit(
|
||||||
|
[](auto&& arg) {
|
||||||
|
using T = std::decay_t<decltype(arg)>;
|
||||||
|
if constexpr (std::is_same_v<T, DWORD>)
|
||||||
|
return REG_DWORD;
|
||||||
|
else if constexpr (std::is_same_v<T, std::wstring>)
|
||||||
|
return REG_SZ;
|
||||||
|
else
|
||||||
|
static_assert(always_false_v<T>, "support for this registry type is not implemented");
|
||||||
|
},
|
||||||
|
v);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ValueChange::valueToBuffer(const value_t& value, wchar_t buffer[VALUE_BUFFER_SIZE], DWORD& valueSize, DWORD& type)
|
||||||
|
{
|
||||||
|
using detail::overloaded;
|
||||||
|
|
||||||
|
std::visit(overloaded{ [&](DWORD value) {
|
||||||
|
*reinterpret_cast<DWORD*>(buffer) = value;
|
||||||
|
type = REG_DWORD;
|
||||||
|
valueSize = sizeof(value);
|
||||||
|
},
|
||||||
|
[&](const std::wstring& value) {
|
||||||
|
assert(value.size() < VALUE_BUFFER_SIZE);
|
||||||
|
value.copy(buffer, value.size());
|
||||||
|
type = REG_SZ;
|
||||||
|
valueSize = static_cast<DWORD>(sizeof(wchar_t) * value.size());
|
||||||
|
} },
|
||||||
|
value);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<ValueChange::value_t> ValueChange::bufferToValue(const wchar_t buffer[VALUE_BUFFER_SIZE],
|
||||||
|
const DWORD valueSize,
|
||||||
|
const DWORD type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case REG_DWORD:
|
||||||
|
return *reinterpret_cast<const DWORD*>(buffer);
|
||||||
|
case REG_SZ:
|
||||||
|
{
|
||||||
|
if (!valueSize)
|
||||||
|
{
|
||||||
|
return std::wstring{};
|
||||||
|
}
|
||||||
|
std::wstring result{ buffer, valueSize / sizeof(wchar_t) };
|
||||||
|
while (result[result.size() - 1] == L'\0')
|
||||||
|
{
|
||||||
|
result.resize(result.size() - 1);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ChangeSet::isApplied() const
|
||||||
|
{
|
||||||
|
for (const auto& c : changes)
|
||||||
|
{
|
||||||
|
if (c.required && !c.isApplied())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ChangeSet::apply() const
|
||||||
|
{
|
||||||
|
bool ok = true;
|
||||||
|
for (const auto& c : changes)
|
||||||
|
{
|
||||||
|
ok = (c.apply()||!c.required) && ok;
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ChangeSet::unApply() const
|
||||||
|
{
|
||||||
|
bool ok = true;
|
||||||
|
for (const auto& c : changes)
|
||||||
|
{
|
||||||
|
ok = (c.unApply()||!c.required) && ok;
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace shellex
|
||||||
|
{
|
||||||
|
registry::ChangeSet generatePreviewHandler(const PreviewHandlerType handlerType,
|
||||||
|
const bool perUser,
|
||||||
|
std::wstring handlerClsid,
|
||||||
|
std::wstring powertoysVersion,
|
||||||
|
std::wstring fullPathToHandler,
|
||||||
|
std::wstring className,
|
||||||
|
std::wstring displayName,
|
||||||
|
std::vector<std::wstring> fileTypes,
|
||||||
|
std::wstring perceivedType,
|
||||||
|
std::wstring fileKindType)
|
||||||
|
{
|
||||||
|
const HKEY scope = perUser ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
|
||||||
|
|
||||||
|
std::wstring clsidPath = L"Software\\Classes\\CLSID";
|
||||||
|
clsidPath += L'\\';
|
||||||
|
clsidPath += handlerClsid;
|
||||||
|
|
||||||
|
std::wstring inprocServerPath = clsidPath;
|
||||||
|
inprocServerPath += L'\\';
|
||||||
|
inprocServerPath += L"InprocServer32";
|
||||||
|
|
||||||
|
std::wstring assemblyKeyValue;
|
||||||
|
if (const auto lastDotPos = className.rfind(L'.'); lastDotPos != std::wstring::npos)
|
||||||
|
{
|
||||||
|
assemblyKeyValue = L"PowerToys." + className.substr(lastDotPos + 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
assemblyKeyValue = L"PowerToys." + className;
|
||||||
|
}
|
||||||
|
|
||||||
|
assemblyKeyValue += L", Version=";
|
||||||
|
assemblyKeyValue += powertoysVersion;
|
||||||
|
assemblyKeyValue += L", Culture=neutral";
|
||||||
|
|
||||||
|
std::wstring versionPath = inprocServerPath;
|
||||||
|
versionPath += L'\\';
|
||||||
|
versionPath += powertoysVersion;
|
||||||
|
|
||||||
|
using vec_t = std::vector<registry::ValueChange>;
|
||||||
|
// TODO: verify that we actually need all of those
|
||||||
|
vec_t changes = { { scope, clsidPath, L"DisplayName", displayName },
|
||||||
|
{ scope, clsidPath, std::nullopt, className },
|
||||||
|
{ scope, inprocServerPath, std::nullopt, fullPathToHandler },
|
||||||
|
{ scope, inprocServerPath, L"Assembly", assemblyKeyValue },
|
||||||
|
{ scope, inprocServerPath, L"Class", className },
|
||||||
|
{ scope, inprocServerPath, L"ThreadingModel", L"Apartment" } };
|
||||||
|
|
||||||
|
for (const auto& fileType : fileTypes)
|
||||||
|
{
|
||||||
|
std::wstring fileTypePath = L"Software\\Classes\\" + fileType;
|
||||||
|
std::wstring fileAssociationPath = fileTypePath + L"\\shellex\\";
|
||||||
|
fileAssociationPath += handlerType == PreviewHandlerType::preview ? IPREVIEW_HANDLER_CLSID : ITHUMBNAIL_PROVIDER_CLSID;
|
||||||
|
changes.push_back({ scope, fileAssociationPath, std::nullopt, handlerClsid });
|
||||||
|
if (!fileKindType.empty())
|
||||||
|
{
|
||||||
|
// Registering a file type as a kind needs to be done at the HKEY_LOCAL_MACHINE level.
|
||||||
|
// Make it optional as well so that we don't fail registering the handler if we can't write to HKEY_LOCAL_MACHINE.
|
||||||
|
std::wstring kindMapPath = L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\KindMap";
|
||||||
|
changes.push_back({ HKEY_LOCAL_MACHINE, kindMapPath, fileType, fileKindType, false});
|
||||||
|
}
|
||||||
|
if (!perceivedType.empty())
|
||||||
|
{
|
||||||
|
changes.push_back({ scope, fileTypePath, L"PerceivedType", perceivedType });
|
||||||
|
}
|
||||||
|
if (handlerType == PreviewHandlerType::preview && fileType == L".reg")
|
||||||
|
{
|
||||||
|
// this regfile registry key has precedence over Software\Classes\.reg for .reg files
|
||||||
|
std::wstring regfilePath = L"Software\\Classes\\regfile\\shellex\\" + IPREVIEW_HANDLER_CLSID + L"\\";
|
||||||
|
changes.push_back({ scope, regfilePath, std::nullopt, handlerClsid });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handlerType == PreviewHandlerType::preview)
|
||||||
|
{
|
||||||
|
const std::wstring previewHostClsid = L"{6d2b5079-2f0b-48dd-ab7f-97cec514d30b}";
|
||||||
|
const std::wstring previewHandlerListPath = LR"(Software\Microsoft\Windows\CurrentVersion\PreviewHandlers)";
|
||||||
|
|
||||||
|
changes.push_back({ scope, clsidPath, L"AppID", previewHostClsid });
|
||||||
|
changes.push_back({ scope, previewHandlerListPath, handlerClsid, displayName });
|
||||||
|
}
|
||||||
|
|
||||||
|
return registry::ChangeSet{ .changes = std::move(changes) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -217,226 +217,28 @@ namespace registry
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
std::wstring toString() const
|
std::wstring toString() const;
|
||||||
{
|
bool isApplied() const;
|
||||||
using namespace detail;
|
bool apply() const;
|
||||||
|
bool unApply() const;
|
||||||
std::wstring value_str;
|
|
||||||
std::visit(overloaded{ [&](DWORD value) {
|
|
||||||
std::wostringstream oss;
|
|
||||||
oss << value;
|
|
||||||
value_str = oss.str();
|
|
||||||
},
|
|
||||||
[&](const std::wstring& value) { value_str = value; } },
|
|
||||||
value);
|
|
||||||
|
|
||||||
return fmt::format(L"{}\\{}\\{}:{}", detail::getScopeName(scope), path, name ? *name : L"Default", value_str);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isApplied() const
|
|
||||||
{
|
|
||||||
HKEY key{};
|
|
||||||
if (auto res = RegOpenKeyExW(scope, path.c_str(), 0, KEY_READ, &key); res != ERROR_SUCCESS)
|
|
||||||
{
|
|
||||||
Logger::info(L"isApplied of {}: RegOpenKeyExW failed: {}", toString(), get_last_error_or_default(res));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
detail::on_exit closeKey{ [key] { RegCloseKey(key); } };
|
|
||||||
|
|
||||||
const DWORD expectedType = valueTypeToWinapiType(value);
|
|
||||||
|
|
||||||
DWORD retrievedType{};
|
|
||||||
wchar_t buffer[VALUE_BUFFER_SIZE];
|
|
||||||
DWORD valueSize = sizeof(buffer);
|
|
||||||
if (auto res = RegQueryValueExW(key,
|
|
||||||
name.has_value() ? name->c_str() : nullptr,
|
|
||||||
0,
|
|
||||||
&retrievedType,
|
|
||||||
reinterpret_cast<LPBYTE>(&buffer),
|
|
||||||
&valueSize);
|
|
||||||
res != ERROR_SUCCESS)
|
|
||||||
{
|
|
||||||
Logger::info(L"isApplied of {}: RegQueryValueExW failed: {}", toString(), get_last_error_or_default(res));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (expectedType != retrievedType)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (const auto retrievedValue = bufferToValue(buffer, valueSize, retrievedType))
|
|
||||||
{
|
|
||||||
return value == retrievedValue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool apply() const
|
|
||||||
{
|
|
||||||
HKEY key{};
|
|
||||||
|
|
||||||
if (auto res = RegCreateKeyExW(scope, path.c_str(), 0, nullptr, REG_OPTION_NON_VOLATILE, KEY_WRITE, nullptr, &key, nullptr); res !=
|
|
||||||
ERROR_SUCCESS)
|
|
||||||
{
|
|
||||||
Logger::error(L"apply of {}: RegCreateKeyExW failed: {}", toString(), get_last_error_or_default(res));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
detail::on_exit closeKey{ [key] { RegCloseKey(key); } };
|
|
||||||
|
|
||||||
wchar_t buffer[VALUE_BUFFER_SIZE];
|
|
||||||
DWORD valueSize;
|
|
||||||
DWORD valueType;
|
|
||||||
|
|
||||||
valueToBuffer(value, buffer, valueSize, valueType);
|
|
||||||
if (auto res = RegSetValueExW(key,
|
|
||||||
name.has_value() ? name->c_str() : nullptr,
|
|
||||||
0,
|
|
||||||
valueType,
|
|
||||||
reinterpret_cast<BYTE*>(buffer),
|
|
||||||
valueSize);
|
|
||||||
res != ERROR_SUCCESS)
|
|
||||||
{
|
|
||||||
Logger::error(L"apply of {}: RegSetValueExW failed: {}", toString(), get_last_error_or_default(res));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool unApply() const
|
|
||||||
{
|
|
||||||
HKEY key{};
|
|
||||||
if (auto res = RegOpenKeyExW(scope, path.c_str(), 0, KEY_ALL_ACCESS, &key); res != ERROR_SUCCESS)
|
|
||||||
{
|
|
||||||
Logger::error(L"unApply of {}: RegOpenKeyExW failed: {}", toString(), get_last_error_or_default(res));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
detail::on_exit closeKey{ [key] { RegCloseKey(key); } };
|
|
||||||
|
|
||||||
// delete the value itself
|
|
||||||
if (auto res = RegDeleteKeyValueW(scope, path.c_str(), name.has_value() ? name->c_str() : nullptr); res != ERROR_SUCCESS)
|
|
||||||
{
|
|
||||||
Logger::error(L"unApply of {}: RegDeleteKeyValueW failed: {}", toString(), get_last_error_or_default(res));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the path doesn't contain anything and delete it if so
|
|
||||||
DWORD nValues = 0;
|
|
||||||
DWORD maxValueLen = 0;
|
|
||||||
const auto ok =
|
|
||||||
RegQueryInfoKeyW(
|
|
||||||
key, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, &nValues, nullptr, &maxValueLen, nullptr, nullptr) ==
|
|
||||||
ERROR_SUCCESS;
|
|
||||||
|
|
||||||
if (ok && (!nValues || !maxValueLen))
|
|
||||||
{
|
|
||||||
RegDeleteTreeW(scope, path.c_str());
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool requiresElevation() const { return scope == HKEY_LOCAL_MACHINE; }
|
bool requiresElevation() const { return scope == HKEY_LOCAL_MACHINE; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static DWORD valueTypeToWinapiType(const value_t& v)
|
static DWORD valueTypeToWinapiType(const value_t& v);
|
||||||
{
|
static void valueToBuffer(const value_t& value, wchar_t buffer[VALUE_BUFFER_SIZE], DWORD& valueSize, DWORD& type);
|
||||||
return std::visit(
|
|
||||||
[](auto&& arg) {
|
|
||||||
using T = std::decay_t<decltype(arg)>;
|
|
||||||
if constexpr (std::is_same_v<T, DWORD>)
|
|
||||||
return REG_DWORD;
|
|
||||||
else if constexpr (std::is_same_v<T, std::wstring>)
|
|
||||||
return REG_SZ;
|
|
||||||
else
|
|
||||||
static_assert(always_false_v<T>, "support for this registry type is not implemented");
|
|
||||||
},
|
|
||||||
v);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void valueToBuffer(const value_t& value, wchar_t buffer[VALUE_BUFFER_SIZE], DWORD& valueSize, DWORD& type)
|
|
||||||
{
|
|
||||||
using detail::overloaded;
|
|
||||||
|
|
||||||
std::visit(overloaded{ [&](DWORD value) {
|
|
||||||
*reinterpret_cast<DWORD*>(buffer) = value;
|
|
||||||
type = REG_DWORD;
|
|
||||||
valueSize = sizeof(value);
|
|
||||||
},
|
|
||||||
[&](const std::wstring& value) {
|
|
||||||
assert(value.size() < VALUE_BUFFER_SIZE);
|
|
||||||
value.copy(buffer, value.size());
|
|
||||||
type = REG_SZ;
|
|
||||||
valueSize = static_cast<DWORD>(sizeof(wchar_t) * value.size());
|
|
||||||
} },
|
|
||||||
value);
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::optional<value_t> bufferToValue(const wchar_t buffer[VALUE_BUFFER_SIZE],
|
static std::optional<value_t> bufferToValue(const wchar_t buffer[VALUE_BUFFER_SIZE],
|
||||||
const DWORD valueSize,
|
const DWORD valueSize,
|
||||||
const DWORD type)
|
const DWORD type);
|
||||||
{
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case REG_DWORD:
|
|
||||||
return *reinterpret_cast<const DWORD*>(buffer);
|
|
||||||
case REG_SZ:
|
|
||||||
{
|
|
||||||
if (!valueSize)
|
|
||||||
{
|
|
||||||
return std::wstring{};
|
|
||||||
}
|
|
||||||
std::wstring result{ buffer, valueSize / sizeof(wchar_t) };
|
|
||||||
while (result[result.size() - 1] == L'\0')
|
|
||||||
{
|
|
||||||
result.resize(result.size() - 1);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ChangeSet
|
struct ChangeSet
|
||||||
{
|
{
|
||||||
std::vector<ValueChange> changes;
|
std::vector<ValueChange> changes;
|
||||||
|
|
||||||
bool isApplied() const
|
bool isApplied() const;
|
||||||
{
|
bool apply() const;
|
||||||
for (const auto& c : changes)
|
bool unApply() const;
|
||||||
{
|
|
||||||
if (c.required && !c.isApplied())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool apply() const
|
|
||||||
{
|
|
||||||
bool ok = true;
|
|
||||||
for (const auto& c : changes)
|
|
||||||
{
|
|
||||||
ok = (c.apply()||!c.required) && ok;
|
|
||||||
}
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool unApply() const
|
|
||||||
{
|
|
||||||
bool ok = true;
|
|
||||||
for (const auto& c : changes)
|
|
||||||
{
|
|
||||||
ok = (c.unApply()||!c.required) && ok;
|
|
||||||
}
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const inline std::wstring DOTNET_COMPONENT_CATEGORY_CLSID = L"{62C8FE65-4EBB-45E7-B440-6E39B2CDBF29}";
|
const inline std::wstring DOTNET_COMPONENT_CATEGORY_CLSID = L"{62C8FE65-4EBB-45E7-B440-6E39B2CDBF29}";
|
||||||
@@ -451,7 +253,7 @@ namespace registry
|
|||||||
thumbnail
|
thumbnail
|
||||||
};
|
};
|
||||||
|
|
||||||
inline registry::ChangeSet generatePreviewHandler(const PreviewHandlerType handlerType,
|
registry::ChangeSet generatePreviewHandler(const PreviewHandlerType handlerType,
|
||||||
const bool perUser,
|
const bool perUser,
|
||||||
std::wstring handlerClsid,
|
std::wstring handlerClsid,
|
||||||
std::wstring powertoysVersion,
|
std::wstring powertoysVersion,
|
||||||
@@ -460,80 +262,6 @@ namespace registry
|
|||||||
std::wstring displayName,
|
std::wstring displayName,
|
||||||
std::vector<std::wstring> fileTypes,
|
std::vector<std::wstring> fileTypes,
|
||||||
std::wstring perceivedType = L"",
|
std::wstring perceivedType = L"",
|
||||||
std::wstring fileKindType = L"")
|
std::wstring fileKindType = L"");
|
||||||
{
|
|
||||||
const HKEY scope = perUser ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
|
|
||||||
|
|
||||||
std::wstring clsidPath = L"Software\\Classes\\CLSID";
|
|
||||||
clsidPath += L'\\';
|
|
||||||
clsidPath += handlerClsid;
|
|
||||||
|
|
||||||
std::wstring inprocServerPath = clsidPath;
|
|
||||||
inprocServerPath += L'\\';
|
|
||||||
inprocServerPath += L"InprocServer32";
|
|
||||||
|
|
||||||
std::wstring assemblyKeyValue;
|
|
||||||
if (const auto lastDotPos = className.rfind(L'.'); lastDotPos != std::wstring::npos)
|
|
||||||
{
|
|
||||||
assemblyKeyValue = L"PowerToys." + className.substr(lastDotPos + 1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
assemblyKeyValue = L"PowerToys." + className;
|
|
||||||
}
|
|
||||||
|
|
||||||
assemblyKeyValue += L", Version=";
|
|
||||||
assemblyKeyValue += powertoysVersion;
|
|
||||||
assemblyKeyValue += L", Culture=neutral";
|
|
||||||
|
|
||||||
std::wstring versionPath = inprocServerPath;
|
|
||||||
versionPath += L'\\';
|
|
||||||
versionPath += powertoysVersion;
|
|
||||||
|
|
||||||
using vec_t = std::vector<registry::ValueChange>;
|
|
||||||
// TODO: verify that we actually need all of those
|
|
||||||
vec_t changes = { { scope, clsidPath, L"DisplayName", displayName },
|
|
||||||
{ scope, clsidPath, std::nullopt, className },
|
|
||||||
{ scope, inprocServerPath, std::nullopt, fullPathToHandler },
|
|
||||||
{ scope, inprocServerPath, L"Assembly", assemblyKeyValue },
|
|
||||||
{ scope, inprocServerPath, L"Class", className },
|
|
||||||
{ scope, inprocServerPath, L"ThreadingModel", L"Apartment" } };
|
|
||||||
|
|
||||||
for (const auto& fileType : fileTypes)
|
|
||||||
{
|
|
||||||
std::wstring fileTypePath = L"Software\\Classes\\" + fileType;
|
|
||||||
std::wstring fileAssociationPath = fileTypePath + L"\\shellex\\";
|
|
||||||
fileAssociationPath += handlerType == PreviewHandlerType::preview ? IPREVIEW_HANDLER_CLSID : ITHUMBNAIL_PROVIDER_CLSID;
|
|
||||||
changes.push_back({ scope, fileAssociationPath, std::nullopt, handlerClsid });
|
|
||||||
if (!fileKindType.empty())
|
|
||||||
{
|
|
||||||
// Registering a file type as a kind needs to be done at the HKEY_LOCAL_MACHINE level.
|
|
||||||
// Make it optional as well so that we don't fail registering the handler if we can't write to HKEY_LOCAL_MACHINE.
|
|
||||||
std::wstring kindMapPath = L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\KindMap";
|
|
||||||
changes.push_back({ HKEY_LOCAL_MACHINE, kindMapPath, fileType, fileKindType, false});
|
|
||||||
}
|
|
||||||
if (!perceivedType.empty())
|
|
||||||
{
|
|
||||||
changes.push_back({ scope, fileTypePath, L"PerceivedType", perceivedType });
|
|
||||||
}
|
|
||||||
if (handlerType == PreviewHandlerType::preview && fileType == L".reg")
|
|
||||||
{
|
|
||||||
// this regfile registry key has precedence over Software\Classes\.reg for .reg files
|
|
||||||
std::wstring regfilePath = L"Software\\Classes\\regfile\\shellex\\" + IPREVIEW_HANDLER_CLSID + L"\\";
|
|
||||||
changes.push_back({ scope, regfilePath, std::nullopt, handlerClsid });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (handlerType == PreviewHandlerType::preview)
|
|
||||||
{
|
|
||||||
const std::wstring previewHostClsid = L"{6d2b5079-2f0b-48dd-ab7f-97cec514d30b}";
|
|
||||||
const std::wstring previewHandlerListPath = LR"(Software\Microsoft\Windows\CurrentVersion\PreviewHandlers)";
|
|
||||||
|
|
||||||
changes.push_back({ scope, clsidPath, L"AppID", previewHostClsid });
|
|
||||||
changes.push_back({ scope, previewHandlerListPath, handlerClsid, displayName });
|
|
||||||
}
|
|
||||||
|
|
||||||
return registry::ChangeSet{ .changes = std::move(changes) };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
201
src/common/utils/resources.cpp
Normal file
201
src/common/utils/resources.cpp
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "resources.h"
|
||||||
|
#include <atlstr.h>
|
||||||
|
#include <common/utils/language_helper.h>
|
||||||
|
|
||||||
|
std::wstring get_english_fallback_string(UINT resource_id, HINSTANCE instance)
|
||||||
|
{
|
||||||
|
// Try to load en-us string as the first fallback.
|
||||||
|
WORD english_language = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
|
||||||
|
|
||||||
|
ATL::CStringW english_string;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!english_string.LoadStringW(instance, resource_id, english_language))
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::wstring(english_string);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring get_resource_string_language_override(UINT resource_id, HINSTANCE instance)
|
||||||
|
{
|
||||||
|
static std::wstring language = LanguageHelpers::load_language();
|
||||||
|
unsigned lang = LANG_ENGLISH;
|
||||||
|
unsigned sublang = SUBLANG_ENGLISH_US;
|
||||||
|
|
||||||
|
if (!language.empty())
|
||||||
|
{
|
||||||
|
// Language list taken from Resources.wxs
|
||||||
|
if (language == L"ar-SA")
|
||||||
|
{
|
||||||
|
lang = LANG_ARABIC;
|
||||||
|
sublang = SUBLANG_ARABIC_SAUDI_ARABIA;
|
||||||
|
}
|
||||||
|
else if (language == L"cs-CZ")
|
||||||
|
{
|
||||||
|
lang = LANG_CZECH;
|
||||||
|
sublang = SUBLANG_CZECH_CZECH_REPUBLIC;
|
||||||
|
}
|
||||||
|
else if (language == L"de-DE")
|
||||||
|
{
|
||||||
|
lang = LANG_GERMAN;
|
||||||
|
sublang = SUBLANG_GERMAN;
|
||||||
|
}
|
||||||
|
else if (language == L"en-US")
|
||||||
|
{
|
||||||
|
lang = LANG_ENGLISH;
|
||||||
|
sublang = SUBLANG_ENGLISH_US;
|
||||||
|
}
|
||||||
|
else if (language == L"es-ES")
|
||||||
|
{
|
||||||
|
lang = LANG_SPANISH;
|
||||||
|
sublang = SUBLANG_SPANISH;
|
||||||
|
}
|
||||||
|
else if (language == L"fa-IR")
|
||||||
|
{
|
||||||
|
lang = LANG_PERSIAN;
|
||||||
|
sublang = SUBLANG_PERSIAN_IRAN;
|
||||||
|
}
|
||||||
|
else if (language == L"fr-FR")
|
||||||
|
{
|
||||||
|
lang = LANG_FRENCH;
|
||||||
|
sublang = SUBLANG_FRENCH;
|
||||||
|
}
|
||||||
|
else if (language == L"he-IL")
|
||||||
|
{
|
||||||
|
lang = LANG_HEBREW;
|
||||||
|
sublang = SUBLANG_HEBREW_ISRAEL;
|
||||||
|
}
|
||||||
|
else if (language == L"hu-HU")
|
||||||
|
{
|
||||||
|
lang = LANG_HUNGARIAN;
|
||||||
|
sublang = SUBLANG_HUNGARIAN_HUNGARY;
|
||||||
|
}
|
||||||
|
else if (language == L"it-IT")
|
||||||
|
{
|
||||||
|
lang = LANG_ITALIAN;
|
||||||
|
sublang = SUBLANG_ITALIAN;
|
||||||
|
}
|
||||||
|
else if (language == L"ja-JP")
|
||||||
|
{
|
||||||
|
lang = LANG_JAPANESE;
|
||||||
|
sublang = SUBLANG_JAPANESE_JAPAN;
|
||||||
|
}
|
||||||
|
else if (language == L"ko-KR")
|
||||||
|
{
|
||||||
|
lang = LANG_KOREAN;
|
||||||
|
sublang = SUBLANG_KOREAN;
|
||||||
|
}
|
||||||
|
else if (language == L"nl-NL")
|
||||||
|
{
|
||||||
|
lang = LANG_DUTCH;
|
||||||
|
sublang = SUBLANG_DUTCH;
|
||||||
|
}
|
||||||
|
else if (language == L"pl-PL")
|
||||||
|
{
|
||||||
|
lang = LANG_POLISH;
|
||||||
|
sublang = SUBLANG_POLISH_POLAND;
|
||||||
|
}
|
||||||
|
else if (language == L"pt-BR")
|
||||||
|
{
|
||||||
|
lang = LANG_PORTUGUESE;
|
||||||
|
sublang = SUBLANG_PORTUGUESE_BRAZILIAN;
|
||||||
|
}
|
||||||
|
else if (language == L"pt-PT")
|
||||||
|
{
|
||||||
|
lang = LANG_PORTUGUESE;
|
||||||
|
sublang = SUBLANG_PORTUGUESE;
|
||||||
|
}
|
||||||
|
else if (language == L"ru-RU")
|
||||||
|
{
|
||||||
|
lang = LANG_RUSSIAN;
|
||||||
|
sublang = SUBLANG_RUSSIAN_RUSSIA;
|
||||||
|
}
|
||||||
|
else if (language == L"sv-SE")
|
||||||
|
{
|
||||||
|
lang = LANG_SWEDISH;
|
||||||
|
sublang = SUBLANG_SWEDISH;
|
||||||
|
}
|
||||||
|
else if (language == L"tr-TR")
|
||||||
|
{
|
||||||
|
lang = LANG_TURKISH;
|
||||||
|
sublang = SUBLANG_TURKISH_TURKEY;
|
||||||
|
}
|
||||||
|
else if (language == L"uk-UA")
|
||||||
|
{
|
||||||
|
lang = LANG_UKRAINIAN;
|
||||||
|
sublang = SUBLANG_UKRAINIAN_UKRAINE;
|
||||||
|
}
|
||||||
|
else if (language == L"zh-CN")
|
||||||
|
{
|
||||||
|
lang = LANG_CHINESE_SIMPLIFIED;
|
||||||
|
sublang = SUBLANG_CHINESE_SIMPLIFIED;
|
||||||
|
}
|
||||||
|
else if (language == L"zh-TW")
|
||||||
|
{
|
||||||
|
lang = LANG_CHINESE_TRADITIONAL;
|
||||||
|
sublang = SUBLANG_CHINESE_TRADITIONAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
WORD languageID = MAKELANGID(lang, sublang);
|
||||||
|
ATL::CStringW result;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!result.LoadStringW(instance, resource_id, languageID))
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result.IsEmpty())
|
||||||
|
{
|
||||||
|
return std::wstring(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring get_resource_string(UINT resource_id, HINSTANCE instance, const wchar_t* fallback)
|
||||||
|
{
|
||||||
|
// Try to load en-us string as the first fallback.
|
||||||
|
std::wstring english_string = get_english_fallback_string(resource_id, instance);
|
||||||
|
|
||||||
|
std::wstring language_override_resource = get_resource_string_language_override(resource_id, instance);
|
||||||
|
|
||||||
|
if (!language_override_resource.empty())
|
||||||
|
{
|
||||||
|
return language_override_resource;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wchar_t* text_ptr;
|
||||||
|
auto length = LoadStringW(instance, resource_id, reinterpret_cast<wchar_t*>(&text_ptr), 0);
|
||||||
|
if (length == 0)
|
||||||
|
{
|
||||||
|
if (!english_string.empty())
|
||||||
|
{
|
||||||
|
return std::wstring(english_string);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return { text_ptr, static_cast<std::size_t>(length) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,210 +2,20 @@
|
|||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <atlstr.h>
|
|
||||||
|
|
||||||
#include <common/utils/language_helper.h>
|
|
||||||
|
|
||||||
|
|
||||||
inline std::wstring get_english_fallback_string(UINT resource_id, HINSTANCE instance)
|
|
||||||
{
|
|
||||||
// Try to load en-us string as the first fallback.
|
|
||||||
WORD english_language = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
|
|
||||||
|
|
||||||
ATL::CStringW english_string;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!english_string.LoadStringW(instance, resource_id, english_language))
|
|
||||||
{
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::wstring(english_string);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::wstring get_resource_string_language_override(UINT resource_id, HINSTANCE instance)
|
|
||||||
{
|
|
||||||
static std::wstring language = LanguageHelpers::load_language();
|
|
||||||
unsigned lang = LANG_ENGLISH;
|
|
||||||
unsigned sublang = SUBLANG_ENGLISH_US;
|
|
||||||
|
|
||||||
if (!language.empty())
|
|
||||||
{
|
|
||||||
// Language list taken from Resources.wxs
|
|
||||||
if (language == L"ar-SA")
|
|
||||||
{
|
|
||||||
lang = LANG_ARABIC;
|
|
||||||
sublang = SUBLANG_ARABIC_SAUDI_ARABIA;
|
|
||||||
}
|
|
||||||
else if (language == L"cs-CZ")
|
|
||||||
{
|
|
||||||
lang = LANG_CZECH;
|
|
||||||
sublang = SUBLANG_CZECH_CZECH_REPUBLIC;
|
|
||||||
}
|
|
||||||
else if (language == L"de-DE")
|
|
||||||
{
|
|
||||||
lang = LANG_GERMAN;
|
|
||||||
sublang = SUBLANG_GERMAN;
|
|
||||||
}
|
|
||||||
else if (language == L"en-US")
|
|
||||||
{
|
|
||||||
lang = LANG_ENGLISH;
|
|
||||||
sublang = SUBLANG_ENGLISH_US;
|
|
||||||
}
|
|
||||||
else if (language == L"es-ES")
|
|
||||||
{
|
|
||||||
lang = LANG_SPANISH;
|
|
||||||
sublang = SUBLANG_SPANISH;
|
|
||||||
}
|
|
||||||
else if (language == L"fa-IR")
|
|
||||||
{
|
|
||||||
lang = LANG_PERSIAN;
|
|
||||||
sublang = SUBLANG_PERSIAN_IRAN;
|
|
||||||
}
|
|
||||||
else if (language == L"fr-FR")
|
|
||||||
{
|
|
||||||
lang = LANG_FRENCH;
|
|
||||||
sublang = SUBLANG_FRENCH;
|
|
||||||
}
|
|
||||||
else if (language == L"he-IL")
|
|
||||||
{
|
|
||||||
lang = LANG_HEBREW;
|
|
||||||
sublang = SUBLANG_HEBREW_ISRAEL;
|
|
||||||
}
|
|
||||||
else if (language == L"hu-HU")
|
|
||||||
{
|
|
||||||
lang = LANG_HUNGARIAN;
|
|
||||||
sublang = SUBLANG_HUNGARIAN_HUNGARY;
|
|
||||||
}
|
|
||||||
else if (language == L"it-IT")
|
|
||||||
{
|
|
||||||
lang = LANG_ITALIAN;
|
|
||||||
sublang = SUBLANG_ITALIAN;
|
|
||||||
}
|
|
||||||
else if (language == L"ja-JP")
|
|
||||||
{
|
|
||||||
lang = LANG_JAPANESE;
|
|
||||||
sublang = SUBLANG_JAPANESE_JAPAN;
|
|
||||||
}
|
|
||||||
else if (language == L"ko-KR")
|
|
||||||
{
|
|
||||||
lang = LANG_KOREAN;
|
|
||||||
sublang = SUBLANG_KOREAN;
|
|
||||||
}
|
|
||||||
else if (language == L"nl-NL")
|
|
||||||
{
|
|
||||||
lang = LANG_DUTCH;
|
|
||||||
sublang = SUBLANG_DUTCH;
|
|
||||||
}
|
|
||||||
else if (language == L"pl-PL")
|
|
||||||
{
|
|
||||||
lang = LANG_POLISH;
|
|
||||||
sublang = SUBLANG_POLISH_POLAND;
|
|
||||||
}
|
|
||||||
else if (language == L"pt-BR")
|
|
||||||
{
|
|
||||||
lang = LANG_PORTUGUESE;
|
|
||||||
sublang = SUBLANG_PORTUGUESE_BRAZILIAN;
|
|
||||||
}
|
|
||||||
else if (language == L"pt-PT")
|
|
||||||
{
|
|
||||||
lang = LANG_PORTUGUESE;
|
|
||||||
sublang = SUBLANG_PORTUGUESE;
|
|
||||||
}
|
|
||||||
else if (language == L"ru-RU")
|
|
||||||
{
|
|
||||||
lang = LANG_RUSSIAN;
|
|
||||||
sublang = SUBLANG_RUSSIAN_RUSSIA;
|
|
||||||
}
|
|
||||||
else if (language == L"sv-SE")
|
|
||||||
{
|
|
||||||
lang = LANG_SWEDISH;
|
|
||||||
sublang = SUBLANG_SWEDISH;
|
|
||||||
}
|
|
||||||
else if (language == L"tr-TR")
|
|
||||||
{
|
|
||||||
lang = LANG_TURKISH;
|
|
||||||
sublang = SUBLANG_TURKISH_TURKEY;
|
|
||||||
}
|
|
||||||
else if (language == L"uk-UA")
|
|
||||||
{
|
|
||||||
lang = LANG_UKRAINIAN;
|
|
||||||
sublang = SUBLANG_UKRAINIAN_UKRAINE;
|
|
||||||
}
|
|
||||||
else if (language == L"zh-CN")
|
|
||||||
{
|
|
||||||
lang = LANG_CHINESE_SIMPLIFIED;
|
|
||||||
sublang = SUBLANG_CHINESE_SIMPLIFIED;
|
|
||||||
}
|
|
||||||
else if (language == L"zh-TW")
|
|
||||||
{
|
|
||||||
lang = LANG_CHINESE_TRADITIONAL;
|
|
||||||
sublang = SUBLANG_CHINESE_TRADITIONAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
WORD languageID = MAKELANGID(lang, sublang);
|
|
||||||
ATL::CStringW result;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!result.LoadStringW(instance, resource_id, languageID))
|
|
||||||
{
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!result.IsEmpty())
|
|
||||||
{
|
|
||||||
return std::wstring(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get a string from the resource file
|
|
||||||
inline std::wstring get_resource_string(UINT resource_id, HINSTANCE instance, const wchar_t* fallback)
|
|
||||||
{
|
|
||||||
// Try to load en-us string as the first fallback.
|
|
||||||
std::wstring english_string = get_english_fallback_string(resource_id, instance);
|
|
||||||
|
|
||||||
std::wstring language_override_resource = get_resource_string_language_override(resource_id, instance);
|
|
||||||
|
|
||||||
if (!language_override_resource.empty())
|
|
||||||
{
|
|
||||||
return language_override_resource;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
wchar_t* text_ptr;
|
|
||||||
auto length = LoadStringW(instance, resource_id, reinterpret_cast<wchar_t*>(&text_ptr), 0);
|
|
||||||
if (length == 0)
|
|
||||||
{
|
|
||||||
if (!english_string.empty())
|
|
||||||
{
|
|
||||||
return std::wstring(english_string);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return fallback;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return { text_ptr, static_cast<std::size_t>(length) };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" IMAGE_DOS_HEADER __ImageBase;
|
extern "C" IMAGE_DOS_HEADER __ImageBase;
|
||||||
|
|
||||||
|
// Non-localizable - Load English fallback string
|
||||||
|
std::wstring get_english_fallback_string(UINT resource_id, HINSTANCE instance);
|
||||||
|
|
||||||
|
// Non-localizable - Load string with language override
|
||||||
|
std::wstring get_resource_string_language_override(UINT resource_id, HINSTANCE instance);
|
||||||
|
|
||||||
|
// Localizable - Load resource string with fallback support
|
||||||
|
std::wstring get_resource_string(UINT resource_id, HINSTANCE instance, const wchar_t* fallback);
|
||||||
|
|
||||||
// Wrapper for getting a string from the resource file. Returns the resource id text when fails.
|
// Wrapper for getting a string from the resource file. Returns the resource id text when fails.
|
||||||
#define GET_RESOURCE_STRING(resource_id) get_resource_string(resource_id, reinterpret_cast<HINSTANCE>(&__ImageBase), L#resource_id)
|
#define GET_RESOURCE_STRING(resource_id) get_resource_string(resource_id, reinterpret_cast<HINSTANCE>(&__ImageBase), L#resource_id)
|
||||||
#define GET_RESOURCE_STRING_FALLBACK(resource_id, fallback) get_resource_string(resource_id, reinterpret_cast<HINSTANCE>(&__ImageBase), fallback)
|
#define GET_RESOURCE_STRING_FALLBACK(resource_id, fallback) get_resource_string(resource_id, reinterpret_cast<HINSTANCE>(&__ImageBase), fallback)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
270
src/common/utils/shell_ext_registration.cpp
Normal file
270
src/common/utils/shell_ext_registration.cpp
Normal file
@@ -0,0 +1,270 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "shell_ext_registration.h"
|
||||||
|
|
||||||
|
#include "../logger/logger.h"
|
||||||
|
|
||||||
|
namespace runtime_shell_ext
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
struct unique_hkey
|
||||||
|
{
|
||||||
|
HKEY h{ nullptr };
|
||||||
|
unique_hkey() = default;
|
||||||
|
explicit unique_hkey(HKEY handle) : h(handle) {}
|
||||||
|
~unique_hkey() { if (h) RegCloseKey(h); }
|
||||||
|
unique_hkey(const unique_hkey&) = delete;
|
||||||
|
unique_hkey& operator=(const unique_hkey&) = delete;
|
||||||
|
unique_hkey(unique_hkey&& other) noexcept : h(other.h) { other.h = nullptr; }
|
||||||
|
unique_hkey& operator=(unique_hkey&& other) noexcept
|
||||||
|
{
|
||||||
|
if (this != &other)
|
||||||
|
{
|
||||||
|
if (h)
|
||||||
|
{
|
||||||
|
RegCloseKey(h);
|
||||||
|
}
|
||||||
|
h = other.h;
|
||||||
|
other.h = nullptr;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
HKEY get() const { return h; }
|
||||||
|
HKEY* put()
|
||||||
|
{
|
||||||
|
if (h)
|
||||||
|
{
|
||||||
|
RegCloseKey(h);
|
||||||
|
h = nullptr;
|
||||||
|
}
|
||||||
|
return &h;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::wstring base_dir_from_module(HMODULE moduleInstance)
|
||||||
|
{
|
||||||
|
wchar_t buf[MAX_PATH];
|
||||||
|
if (GetModuleFileNameW(moduleInstance, buf, MAX_PATH))
|
||||||
|
{
|
||||||
|
PathRemoveFileSpecW(buf);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
return L"";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring pick_existing_dll(const std::wstring& base, const std::vector<std::wstring>& candidates)
|
||||||
|
{
|
||||||
|
for (const auto& rel : candidates)
|
||||||
|
{
|
||||||
|
std::wstring full = base + L"\\" + rel;
|
||||||
|
if (GetFileAttributesW(full.c_str()) != INVALID_FILE_ATTRIBUTES)
|
||||||
|
{
|
||||||
|
return full;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!candidates.empty())
|
||||||
|
{
|
||||||
|
return base + L"\\" + candidates.front();
|
||||||
|
}
|
||||||
|
return L"";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sentinel_exists(const Spec& spec)
|
||||||
|
{
|
||||||
|
unique_hkey key;
|
||||||
|
if (RegOpenKeyExW(HKEY_CURRENT_USER, spec.sentinelKey.c_str(), 0, KEY_READ, key.put()) != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
DWORD value = 0;
|
||||||
|
DWORD size = sizeof(value);
|
||||||
|
return RegQueryValueExW(key.get(), spec.sentinelValue.c_str(), nullptr, nullptr, reinterpret_cast<LPBYTE>(&value), &size) == ERROR_SUCCESS && value == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_sentinel(const Spec& spec)
|
||||||
|
{
|
||||||
|
unique_hkey key;
|
||||||
|
if (RegCreateKeyExW(HKEY_CURRENT_USER, spec.sentinelKey.c_str(), 0, nullptr, 0, KEY_WRITE, nullptr, key.put(), nullptr) == ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
DWORD one = 1;
|
||||||
|
RegSetValueExW(key.get(), spec.sentinelValue.c_str(), 0, REG_DWORD, reinterpret_cast<const BYTE*>(&one), sizeof(one));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_inproc_server(const Spec& spec, const std::wstring& dllPath)
|
||||||
|
{
|
||||||
|
using namespace std::string_literals;
|
||||||
|
std::wstring clsidRoot = L"Software\\Classes\\CLSID\\"s + spec.clsid;
|
||||||
|
std::wstring inprocKey = clsidRoot + L"\\InprocServer32";
|
||||||
|
{
|
||||||
|
unique_hkey key;
|
||||||
|
if (RegCreateKeyExW(HKEY_CURRENT_USER, clsidRoot.c_str(), 0, nullptr, 0, KEY_WRITE, nullptr, key.put(), nullptr) == ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
if (!spec.friendlyName.empty())
|
||||||
|
{
|
||||||
|
RegSetValueExW(key.get(), nullptr, 0, REG_SZ, reinterpret_cast<const BYTE*>(spec.friendlyName.c_str()), static_cast<DWORD>((spec.friendlyName.size() + 1) * sizeof(wchar_t)));
|
||||||
|
}
|
||||||
|
if (spec.writeOptInEmptyValue)
|
||||||
|
{
|
||||||
|
const wchar_t* optIn = L"ContextMenuOptIn";
|
||||||
|
const wchar_t empty = L'\0';
|
||||||
|
RegSetValueExW(key.get(), optIn, 0, REG_SZ, reinterpret_cast<const BYTE*>(&empty), sizeof(empty));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unique_hkey key;
|
||||||
|
if (RegCreateKeyExW(HKEY_CURRENT_USER, inprocKey.c_str(), 0, nullptr, 0, KEY_WRITE, nullptr, key.put(), nullptr) == ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
RegSetValueExW(key.get(), nullptr, 0, REG_SZ, reinterpret_cast<const BYTE*>(dllPath.c_str()), static_cast<DWORD>((dllPath.size() + 1) * sizeof(wchar_t)));
|
||||||
|
if (spec.writeThreadingModel)
|
||||||
|
{
|
||||||
|
const wchar_t* tm = L"Apartment";
|
||||||
|
RegSetValueExW(key.get(), L"ThreadingModel", 0, REG_SZ, reinterpret_cast<const BYTE*>(tm), static_cast<DWORD>((wcslen(tm) + 1) * sizeof(wchar_t)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring read_inproc_server(const Spec& spec)
|
||||||
|
{
|
||||||
|
using namespace std::string_literals;
|
||||||
|
std::wstring inprocKey = L"Software\\Classes\\CLSID\\"s + spec.clsid + L"\\InprocServer32";
|
||||||
|
unique_hkey key;
|
||||||
|
if (RegOpenKeyExW(HKEY_CURRENT_USER, inprocKey.c_str(), 0, KEY_READ, key.put()) != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
return L"";
|
||||||
|
}
|
||||||
|
wchar_t buf[MAX_PATH];
|
||||||
|
DWORD size = sizeof(buf);
|
||||||
|
if (RegQueryValueExW(key.get(), nullptr, nullptr, nullptr, reinterpret_cast<LPBYTE>(buf), &size) == ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
return std::wstring(buf);
|
||||||
|
}
|
||||||
|
return L"";
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_default_value_key(const std::wstring& keyPath, const std::wstring& value)
|
||||||
|
{
|
||||||
|
unique_hkey key;
|
||||||
|
if (RegCreateKeyExW(HKEY_CURRENT_USER, keyPath.c_str(), 0, nullptr, 0, KEY_WRITE, nullptr, key.put(), nullptr) == ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
RegSetValueExW(key.get(), nullptr, 0, REG_SZ, reinterpret_cast<const BYTE*>(value.c_str()), static_cast<DWORD>((value.size() + 1) * sizeof(wchar_t)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool representative_association_exists(const Spec& spec)
|
||||||
|
{
|
||||||
|
using namespace std::string_literals;
|
||||||
|
if (spec.representativeSystemExt.empty() || spec.systemFileAssocHandlerName.empty())
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
std::wstring keyPath = L"Software\\Classes\\SystemFileAssociations\\"s + spec.representativeSystemExt + L"\\ShellEx\\ContextMenuHandlers\\" + spec.systemFileAssocHandlerName;
|
||||||
|
unique_hkey key;
|
||||||
|
return RegOpenKeyExW(HKEY_CURRENT_USER, keyPath.c_str(), 0, KEY_READ, key.put()) == ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EnsureRegistered(const Spec& spec, HMODULE moduleInstance)
|
||||||
|
{
|
||||||
|
using namespace std::string_literals;
|
||||||
|
auto base = detail::base_dir_from_module(moduleInstance);
|
||||||
|
auto dllPath = detail::pick_existing_dll(base, spec.dllFileCandidates);
|
||||||
|
if (dllPath.empty())
|
||||||
|
{
|
||||||
|
Logger::error(L"Runtime registration: cannot locate dll path for CLSID {}", spec.clsid);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool exists = detail::sentinel_exists(spec);
|
||||||
|
bool repaired = false;
|
||||||
|
if (exists)
|
||||||
|
{
|
||||||
|
auto current = detail::read_inproc_server(spec);
|
||||||
|
if (_wcsicmp(current.c_str(), dllPath.c_str()) != 0)
|
||||||
|
{
|
||||||
|
detail::write_inproc_server(spec, dllPath);
|
||||||
|
repaired = true;
|
||||||
|
}
|
||||||
|
if (!detail::representative_association_exists(spec))
|
||||||
|
{
|
||||||
|
repaired = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!exists)
|
||||||
|
{
|
||||||
|
detail::write_inproc_server(spec, dllPath);
|
||||||
|
}
|
||||||
|
if (!exists || repaired)
|
||||||
|
{
|
||||||
|
for (const auto& path : spec.contextMenuHandlerKeyPaths)
|
||||||
|
{
|
||||||
|
detail::write_default_value_key(path, spec.clsid);
|
||||||
|
}
|
||||||
|
for (const auto& path : spec.extraAssociationPaths)
|
||||||
|
{
|
||||||
|
detail::write_default_value_key(path, spec.clsid);
|
||||||
|
}
|
||||||
|
for (const auto& ext : spec.systemFileAssocExtensions)
|
||||||
|
{
|
||||||
|
using namespace std::string_literals;
|
||||||
|
auto baseKey = L"Software\\Classes\\SystemFileAssociations\\"s + ext + L"\\ShellEx\\ContextMenuHandlers\\" + spec.systemFileAssocHandlerName;
|
||||||
|
detail::write_default_value_key(baseKey, spec.clsid);
|
||||||
|
}
|
||||||
|
if (spec.logRepairs)
|
||||||
|
{
|
||||||
|
Logger::info(L"Runtime shell extension registration repaired {}", spec.clsid);
|
||||||
|
}
|
||||||
|
detail::write_sentinel(spec);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger::trace(L"Runtime shell extension registration already up to date for {}", spec.clsid);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Unregister(const Spec& spec)
|
||||||
|
{
|
||||||
|
using namespace std::string_literals;
|
||||||
|
|
||||||
|
for (const auto& path : spec.contextMenuHandlerKeyPaths)
|
||||||
|
{
|
||||||
|
RegDeleteTreeW(HKEY_CURRENT_USER, path.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& path : spec.extraAssociationPaths)
|
||||||
|
{
|
||||||
|
RegDeleteTreeW(HKEY_CURRENT_USER, path.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!spec.systemFileAssocExtensions.empty() && !spec.systemFileAssocHandlerName.empty())
|
||||||
|
{
|
||||||
|
for (const auto& ext : spec.systemFileAssocExtensions)
|
||||||
|
{
|
||||||
|
std::wstring keyPath = L"Software\\Classes\\SystemFileAssociations\\";
|
||||||
|
keyPath += ext;
|
||||||
|
keyPath += L"\\ShellEx\\ContextMenuHandlers\\";
|
||||||
|
keyPath += spec.systemFileAssocHandlerName;
|
||||||
|
RegDeleteTreeW(HKEY_CURRENT_USER, keyPath.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!spec.clsid.empty())
|
||||||
|
{
|
||||||
|
std::wstring clsidRoot = L"Software\\Classes\\CLSID\\"s + spec.clsid;
|
||||||
|
RegDeleteTreeW(HKEY_CURRENT_USER, clsidRoot.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!spec.sentinelKey.empty() && !spec.sentinelValue.empty())
|
||||||
|
{
|
||||||
|
HKEY key{};
|
||||||
|
if (RegOpenKeyExW(HKEY_CURRENT_USER, spec.sentinelKey.c_str(), 0, KEY_SET_VALUE, &key) == ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
RegDeleteValueW(key, spec.sentinelValue.c_str());
|
||||||
|
RegCloseKey(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger::info(L"Runtime shell extension unregistered for {}", spec.clsid);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,3 @@
|
|||||||
// Shared runtime shell extension registration utility for PowerToys modules.
|
|
||||||
// Provides a generic EnsureRegistered function so individual modules only need
|
|
||||||
// to supply a specification (CLSID, sentinel, handler key paths, etc.).
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -9,258 +5,28 @@
|
|||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <shlwapi.h>
|
#include <shlwapi.h>
|
||||||
|
|
||||||
#include "../logger/logger.h"
|
|
||||||
|
|
||||||
namespace runtime_shell_ext
|
namespace runtime_shell_ext
|
||||||
{
|
{
|
||||||
struct Spec
|
struct Spec
|
||||||
{
|
{
|
||||||
// Mandatory
|
// Mandatory
|
||||||
std::wstring clsid; // e.g. {GUID}
|
std::wstring clsid; // e.g. {GUID}
|
||||||
std::wstring sentinelKey; // e.g. Software\\Microsoft\\PowerToys\\ModuleName
|
std::wstring sentinelKey; // e.g. Software\\Microsoft\\PowerToys\\ModuleName
|
||||||
std::wstring sentinelValue; // e.g. ContextMenuRegistered
|
std::wstring sentinelValue; // e.g. ContextMenuRegistered
|
||||||
std::vector<std::wstring> dllFileCandidates; // relative filenames (pick first existing)
|
std::vector<std::wstring> dllFileCandidates; // relative filenames (pick first existing)
|
||||||
std::vector<std::wstring> contextMenuHandlerKeyPaths; // full HKCU relative paths where default value = CLSID
|
std::vector<std::wstring> contextMenuHandlerKeyPaths; // full HKCU relative paths where default value = CLSID
|
||||||
|
|
||||||
// Optional
|
// Optional
|
||||||
std::wstring friendlyName; // if non-empty written as default under CLSID root
|
std::wstring friendlyName; // if non-empty written as default under CLSID root
|
||||||
bool writeOptInEmptyValue = true; // write ContextMenuOptIn="" under CLSID root (legacy pattern)
|
bool writeOptInEmptyValue = true; // write ContextMenuOptIn="" under CLSID root (legacy pattern)
|
||||||
bool writeThreadingModel = true; // write Apartment threading model
|
bool writeThreadingModel = true; // write Apartment threading model
|
||||||
std::vector<std::wstring> extraAssociationPaths; // additional key paths (DragDropHandlers etc.) default=CLSID
|
std::vector<std::wstring> extraAssociationPaths; // additional key paths (DragDropHandlers etc.) default=CLSID
|
||||||
std::vector<std::wstring> systemFileAssocExtensions; // e.g. .png -> Software\\Classes\\SystemFileAssociations\\.png\\ShellEx\\ContextMenuHandlers\\<HandlerName>
|
std::vector<std::wstring> systemFileAssocExtensions; // e.g. .png -> Software\\Classes\\SystemFileAssociations\\.png\\ShellEx\\ContextMenuHandlers\\<HandlerName>
|
||||||
std::wstring systemFileAssocHandlerName; // e.g. ImageResizer
|
std::wstring systemFileAssocHandlerName; // e.g. ImageResizer
|
||||||
std::wstring representativeSystemExt; // used to decide if associations need repair (.png)
|
std::wstring representativeSystemExt; // used to decide if associations need repair (.png)
|
||||||
bool logRepairs = true;
|
bool logRepairs = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace detail
|
bool EnsureRegistered(const Spec& spec, HMODULE moduleInstance);
|
||||||
{
|
bool Unregister(const Spec& spec);
|
||||||
// Minimal RAII wrapper for HKEY
|
|
||||||
struct unique_hkey
|
|
||||||
{
|
|
||||||
HKEY h{ nullptr };
|
|
||||||
unique_hkey() = default;
|
|
||||||
explicit unique_hkey(HKEY handle) : h(handle) {}
|
|
||||||
~unique_hkey() { if (h) RegCloseKey(h); }
|
|
||||||
unique_hkey(const unique_hkey&) = delete;
|
|
||||||
unique_hkey& operator=(const unique_hkey&) = delete;
|
|
||||||
unique_hkey(unique_hkey&& other) noexcept : h(other.h) { other.h = nullptr; }
|
|
||||||
unique_hkey& operator=(unique_hkey&& other) noexcept { if (this != &other) { if (h) RegCloseKey(h); h = other.h; other.h = nullptr; } return *this; }
|
|
||||||
HKEY get() const { return h; }
|
|
||||||
HKEY* put() { if (h) { RegCloseKey(h); h = nullptr; } return &h; }
|
|
||||||
};
|
|
||||||
inline std::wstring base_dir_from_module(HMODULE h)
|
|
||||||
{
|
|
||||||
wchar_t buf[MAX_PATH];
|
|
||||||
if (GetModuleFileNameW(h, buf, MAX_PATH))
|
|
||||||
{
|
|
||||||
PathRemoveFileSpecW(buf);
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
return L"";
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::wstring pick_existing_dll(const std::wstring& base, const std::vector<std::wstring>& candidates)
|
|
||||||
{
|
|
||||||
for (const auto& rel : candidates)
|
|
||||||
{
|
|
||||||
std::wstring full = base + L"\\" + rel;
|
|
||||||
if (GetFileAttributesW(full.c_str()) != INVALID_FILE_ATTRIBUTES)
|
|
||||||
{
|
|
||||||
return full;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!candidates.empty())
|
|
||||||
{
|
|
||||||
return base + L"\\" + candidates.front();
|
|
||||||
}
|
|
||||||
return L"";
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool sentinel_exists(const Spec& spec)
|
|
||||||
{
|
|
||||||
unique_hkey key;
|
|
||||||
if (RegOpenKeyExW(HKEY_CURRENT_USER, spec.sentinelKey.c_str(), 0, KEY_READ, key.put()) != ERROR_SUCCESS)
|
|
||||||
return false;
|
|
||||||
DWORD v = 0; DWORD sz = sizeof(v);
|
|
||||||
return RegQueryValueExW(key.get(), spec.sentinelValue.c_str(), nullptr, nullptr, reinterpret_cast<LPBYTE>(&v), &sz) == ERROR_SUCCESS && v == 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void write_sentinel(const Spec& spec)
|
|
||||||
{
|
|
||||||
unique_hkey key;
|
|
||||||
if (RegCreateKeyExW(HKEY_CURRENT_USER, spec.sentinelKey.c_str(), 0, nullptr, 0, KEY_WRITE, nullptr, key.put(), nullptr) == ERROR_SUCCESS)
|
|
||||||
{
|
|
||||||
DWORD one = 1;
|
|
||||||
RegSetValueExW(key.get(), spec.sentinelValue.c_str(), 0, REG_DWORD, reinterpret_cast<const BYTE*>(&one), sizeof(one));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void write_inproc_server(const Spec& spec, const std::wstring& dllPath)
|
|
||||||
{
|
|
||||||
using namespace std::string_literals;
|
|
||||||
std::wstring clsidRoot = L"Software\\Classes\\CLSID\\"s + spec.clsid;
|
|
||||||
std::wstring inprocKey = clsidRoot + L"\\InprocServer32";
|
|
||||||
{
|
|
||||||
unique_hkey key;
|
|
||||||
if (RegCreateKeyExW(HKEY_CURRENT_USER, clsidRoot.c_str(), 0, nullptr, 0, KEY_WRITE, nullptr, key.put(), nullptr) == ERROR_SUCCESS)
|
|
||||||
{
|
|
||||||
if (!spec.friendlyName.empty())
|
|
||||||
{
|
|
||||||
RegSetValueExW(key.get(), nullptr, 0, REG_SZ, reinterpret_cast<const BYTE*>(spec.friendlyName.c_str()), static_cast<DWORD>((spec.friendlyName.size() + 1) * sizeof(wchar_t)));
|
|
||||||
}
|
|
||||||
if (spec.writeOptInEmptyValue)
|
|
||||||
{
|
|
||||||
const wchar_t* optIn = L"ContextMenuOptIn";
|
|
||||||
const wchar_t empty = L'\0';
|
|
||||||
RegSetValueExW(key.get(), optIn, 0, REG_SZ, reinterpret_cast<const BYTE*>(&empty), sizeof(empty));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
unique_hkey key;
|
|
||||||
if (RegCreateKeyExW(HKEY_CURRENT_USER, inprocKey.c_str(), 0, nullptr, 0, KEY_WRITE, nullptr, key.put(), nullptr) == ERROR_SUCCESS)
|
|
||||||
{
|
|
||||||
RegSetValueExW(key.get(), nullptr, 0, REG_SZ, reinterpret_cast<const BYTE*>(dllPath.c_str()), static_cast<DWORD>((dllPath.size() + 1) * sizeof(wchar_t)));
|
|
||||||
if (spec.writeThreadingModel)
|
|
||||||
{
|
|
||||||
const wchar_t* tm = L"Apartment";
|
|
||||||
RegSetValueExW(key.get(), L"ThreadingModel", 0, REG_SZ, reinterpret_cast<const BYTE*>(tm), static_cast<DWORD>((wcslen(tm) + 1) * sizeof(wchar_t)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::wstring read_inproc_server(const Spec& spec)
|
|
||||||
{
|
|
||||||
using namespace std::string_literals;
|
|
||||||
std::wstring inprocKey = L"Software\\Classes\\CLSID\\"s + spec.clsid + L"\\InprocServer32";
|
|
||||||
unique_hkey key;
|
|
||||||
if (RegOpenKeyExW(HKEY_CURRENT_USER, inprocKey.c_str(), 0, KEY_READ, key.put()) != ERROR_SUCCESS)
|
|
||||||
return L"";
|
|
||||||
wchar_t buf[MAX_PATH]; DWORD sz = sizeof(buf);
|
|
||||||
if (RegQueryValueExW(key.get(), nullptr, nullptr, nullptr, reinterpret_cast<LPBYTE>(buf), &sz) == ERROR_SUCCESS)
|
|
||||||
return std::wstring(buf);
|
|
||||||
return L"";
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void write_default_value_key(const std::wstring& keyPath, const std::wstring& value)
|
|
||||||
{
|
|
||||||
unique_hkey key;
|
|
||||||
if (RegCreateKeyExW(HKEY_CURRENT_USER, keyPath.c_str(), 0, nullptr, 0, KEY_WRITE, nullptr, key.put(), nullptr) == ERROR_SUCCESS)
|
|
||||||
{
|
|
||||||
RegSetValueExW(key.get(), nullptr, 0, REG_SZ, reinterpret_cast<const BYTE*>(value.c_str()), static_cast<DWORD>((value.size() + 1) * sizeof(wchar_t)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool representative_association_exists(const Spec& spec)
|
|
||||||
{
|
|
||||||
using namespace std::string_literals;
|
|
||||||
if (spec.representativeSystemExt.empty() || spec.systemFileAssocHandlerName.empty())
|
|
||||||
return true;
|
|
||||||
std::wstring keyPath = L"Software\\Classes\\SystemFileAssociations\\"s + spec.representativeSystemExt + L"\\ShellEx\\ContextMenuHandlers\\" + spec.systemFileAssocHandlerName;
|
|
||||||
unique_hkey key;
|
|
||||||
return RegOpenKeyExW(HKEY_CURRENT_USER, keyPath.c_str(), 0, KEY_READ, key.put()) == ERROR_SUCCESS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool EnsureRegistered(const Spec& spec, HMODULE moduleInstance)
|
|
||||||
{
|
|
||||||
using namespace std::string_literals;
|
|
||||||
auto base = detail::base_dir_from_module(moduleInstance);
|
|
||||||
auto dllPath = detail::pick_existing_dll(base, spec.dllFileCandidates);
|
|
||||||
if (dllPath.empty())
|
|
||||||
{
|
|
||||||
Logger::error(L"Runtime registration: cannot locate dll path for CLSID {}", spec.clsid);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
bool exists = detail::sentinel_exists(spec);
|
|
||||||
bool repaired = false;
|
|
||||||
if (exists)
|
|
||||||
{
|
|
||||||
auto current = detail::read_inproc_server(spec);
|
|
||||||
if (_wcsicmp(current.c_str(), dllPath.c_str()) != 0)
|
|
||||||
{
|
|
||||||
detail::write_inproc_server(spec, dllPath);
|
|
||||||
repaired = true;
|
|
||||||
}
|
|
||||||
if (!detail::representative_association_exists(spec))
|
|
||||||
{
|
|
||||||
repaired = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!exists)
|
|
||||||
{
|
|
||||||
detail::write_inproc_server(spec, dllPath);
|
|
||||||
}
|
|
||||||
if (!exists || repaired)
|
|
||||||
{
|
|
||||||
for (const auto& path : spec.contextMenuHandlerKeyPaths)
|
|
||||||
{
|
|
||||||
detail::write_default_value_key(path, spec.clsid);
|
|
||||||
}
|
|
||||||
for (const auto& path : spec.extraAssociationPaths)
|
|
||||||
{
|
|
||||||
detail::write_default_value_key(path, spec.clsid);
|
|
||||||
}
|
|
||||||
if (!spec.systemFileAssocExtensions.empty() && !spec.systemFileAssocHandlerName.empty())
|
|
||||||
{
|
|
||||||
for (const auto& ext : spec.systemFileAssocExtensions)
|
|
||||||
{
|
|
||||||
std::wstring path = L"Software\\Classes\\SystemFileAssociations\\"s + ext + L"\\ShellEx\\ContextMenuHandlers\\" + spec.systemFileAssocHandlerName;
|
|
||||||
detail::write_default_value_key(path, spec.clsid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!exists)
|
|
||||||
{
|
|
||||||
detail::write_sentinel(spec);
|
|
||||||
Logger::info(L"Runtime registration completed for CLSID {}", spec.clsid);
|
|
||||||
}
|
|
||||||
else if (repaired && spec.logRepairs)
|
|
||||||
{
|
|
||||||
Logger::info(L"Runtime registration repaired for CLSID {}", spec.clsid);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool Unregister(const Spec& spec)
|
|
||||||
{
|
|
||||||
using namespace std::string_literals;
|
|
||||||
// Remove handler key paths
|
|
||||||
for (const auto& path : spec.contextMenuHandlerKeyPaths)
|
|
||||||
{
|
|
||||||
RegDeleteTreeW(HKEY_CURRENT_USER, path.c_str());
|
|
||||||
}
|
|
||||||
// Remove extra association paths (e.g., drag & drop handlers)
|
|
||||||
for (const auto& path : spec.extraAssociationPaths)
|
|
||||||
{
|
|
||||||
RegDeleteTreeW(HKEY_CURRENT_USER, path.c_str());
|
|
||||||
}
|
|
||||||
// Remove per-extension system file association handler keys
|
|
||||||
if (!spec.systemFileAssocExtensions.empty() && !spec.systemFileAssocHandlerName.empty())
|
|
||||||
{
|
|
||||||
for (const auto& ext : spec.systemFileAssocExtensions)
|
|
||||||
{
|
|
||||||
std::wstring keyPath = L"Software\\Classes\\SystemFileAssociations\\"s + ext + L"\\ShellEx\\ContextMenuHandlers\\" + spec.systemFileAssocHandlerName;
|
|
||||||
RegDeleteTreeW(HKEY_CURRENT_USER, keyPath.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Remove CLSID branch
|
|
||||||
if (!spec.clsid.empty())
|
|
||||||
{
|
|
||||||
std::wstring clsidRoot = L"Software\\Classes\\CLSID\\"s + spec.clsid;
|
|
||||||
RegDeleteTreeW(HKEY_CURRENT_USER, clsidRoot.c_str());
|
|
||||||
}
|
|
||||||
// Remove sentinel value (not deleting entire key to avoid disturbing other values)
|
|
||||||
if (!spec.sentinelKey.empty() && !spec.sentinelValue.empty())
|
|
||||||
{
|
|
||||||
HKEY hKey{};
|
|
||||||
if (RegOpenKeyExW(HKEY_CURRENT_USER, spec.sentinelKey.c_str(), 0, KEY_SET_VALUE, &hKey) == ERROR_SUCCESS)
|
|
||||||
{
|
|
||||||
RegDeleteValueW(hKey, spec.sentinelValue.c_str());
|
|
||||||
RegCloseKey(hKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Logger::info(L"Successfully unregistered CLSID {}", spec.clsid);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
11
src/common/utils/string_utils.cpp
Normal file
11
src/common/utils/string_utils.cpp
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "string_utils.h"
|
||||||
|
|
||||||
|
std::string unwide(const std::wstring& wide)
|
||||||
|
{
|
||||||
|
std::string result(wide.length(), 0);
|
||||||
|
std::transform(begin(wide), end(wide), result.begin(), [](const wchar_t c) {
|
||||||
|
return static_cast<char>(c);
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
@@ -55,11 +55,4 @@ inline void replace_chars(std::basic_string<CharT>& s,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::string unwide(const std::wstring& wide)
|
std::string unwide(const std::wstring& wide);
|
||||||
{
|
|
||||||
std::string result(wide.length(), 0);
|
|
||||||
std::transform(begin(wide), end(wide), result.begin(), [](const wchar_t c) {
|
|
||||||
return static_cast<char>(c);
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|||||||
60
src/common/utils/timeutil.cpp
Normal file
60
src/common/utils/timeutil.cpp
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "timeutil.h"
|
||||||
|
|
||||||
|
namespace timeutil
|
||||||
|
{
|
||||||
|
std::string format_as_local(const char* format_string, const time_t time)
|
||||||
|
{
|
||||||
|
char timeBuf[256] = {};
|
||||||
|
tm localtime{};
|
||||||
|
localtime_s(&localtime, &time);
|
||||||
|
std::strftime(timeBuf, sizeof(timeBuf), format_string, &localtime);
|
||||||
|
return timeBuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring to_string(const time_t time)
|
||||||
|
{
|
||||||
|
return std::to_wstring(static_cast<uint64_t>(time));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::time_t> from_string(const std::wstring& s)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
uint64_t value = std::stoull(s);
|
||||||
|
return static_cast<std::time_t>(value);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::time_t now()
|
||||||
|
{
|
||||||
|
return winrt::clock::to_time_t(winrt::clock::now());
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace diff
|
||||||
|
{
|
||||||
|
int64_t in_seconds(const std::time_t to, const std::time_t from)
|
||||||
|
{
|
||||||
|
return static_cast<int64_t>(std::difftime(to, from));
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t in_minutes(const std::time_t to, const std::time_t from)
|
||||||
|
{
|
||||||
|
return static_cast<int64_t>(std::difftime(to, from) / 60);
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t in_hours(const std::time_t to, const std::time_t from)
|
||||||
|
{
|
||||||
|
return static_cast<int64_t>(std::difftime(to, from) / 3600);
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t in_days(const std::time_t to, const std::time_t from)
|
||||||
|
{
|
||||||
|
return static_cast<int64_t>(std::difftime(to, from) / (3600 * 24LL));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,58 +9,22 @@
|
|||||||
|
|
||||||
namespace timeutil
|
namespace timeutil
|
||||||
{
|
{
|
||||||
inline std::string format_as_local(const char* format_string, const time_t time)
|
std::string format_as_local(const char* format_string, const time_t time);
|
||||||
{
|
|
||||||
char timeBuf[256] = {};
|
|
||||||
tm localtime{};
|
|
||||||
localtime_s(&localtime, &time);
|
|
||||||
std::strftime(timeBuf, sizeof(timeBuf), format_string, &localtime);
|
|
||||||
return timeBuf;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::wstring to_string(const time_t time)
|
std::wstring to_string(const time_t time);
|
||||||
{
|
|
||||||
return std::to_wstring(static_cast<uint64_t>(time));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::optional<std::time_t> from_string(const std::wstring& s)
|
std::optional<std::time_t> from_string(const std::wstring& s);
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
uint64_t i = std::stoull(s);
|
|
||||||
return static_cast<std::time_t>(i);
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::time_t now()
|
std::time_t now();
|
||||||
{
|
|
||||||
return winrt::clock::to_time_t(winrt::clock::now());
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace diff
|
namespace diff
|
||||||
{
|
{
|
||||||
inline int64_t in_seconds(const std::time_t to, const std::time_t from)
|
int64_t in_seconds(std::time_t to, std::time_t from);
|
||||||
{
|
|
||||||
return static_cast<int64_t>(std::difftime(to, from));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int64_t in_minutes(const std::time_t to, const std::time_t from)
|
int64_t in_minutes(std::time_t to, std::time_t from);
|
||||||
{
|
|
||||||
return static_cast<int64_t>(std::difftime(to, from) / 60);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int64_t in_hours(const std::time_t to, const std::time_t from)
|
int64_t in_hours(std::time_t to, std::time_t from);
|
||||||
{
|
|
||||||
return static_cast<int64_t>(std::difftime(to, from) / 3600);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int64_t in_days(const std::time_t to, const std::time_t from)
|
int64_t in_days(std::time_t to, std::time_t from);
|
||||||
{
|
|
||||||
return static_cast<int64_t>(std::difftime(to, from) / (3600 * 24LL));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
152
src/common/utils/utils.vcxproj
Normal file
152
src/common/utils/utils.vcxproj
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||||
|
<ItemGroup Label="ProjectConfigurations">
|
||||||
|
<ProjectConfiguration Include="Debug|x64">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|x64">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Debug|ARM64">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>ARM64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|ARM64">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>ARM64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
</ItemGroup>
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<VCProjectVersion>16.0</VCProjectVersion>
|
||||||
|
<Keyword>Win32Proj</Keyword>
|
||||||
|
<ProjectGuid>{e8470e15-88c4-4c4b-b872-7f1a8f8e7d7e}</ProjectGuid>
|
||||||
|
<RootNamespace>utils</RootNamespace>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
|
<PropertyGroup Label="Configuration">
|
||||||
|
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||||
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
|
<OutDir>..\..\..\$(Platform)\$(Configuration)\</OutDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||||
|
<Import Project="..\..\..\deps\spdlog.props" />
|
||||||
|
<ImportGroup Label="ExtensionSettings">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="Shared">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<PropertyGroup Label="UserMacros" />
|
||||||
|
<ItemDefinitionGroup>
|
||||||
|
<ClCompile>
|
||||||
|
<AdditionalIncludeDirectories>..\\..\\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<PreprocessorDefinitions>_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<ConformanceMode>true</ConformanceMode>
|
||||||
|
</ClCompile>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="appMutex.h" />
|
||||||
|
<ClInclude Include="clean_video_conference.h" />
|
||||||
|
<ClInclude Include="color.h" />
|
||||||
|
<ClInclude Include="com_object_factory.h" />
|
||||||
|
<ClInclude Include="elevation.h" />
|
||||||
|
<ClInclude Include="EventLocker.h" />
|
||||||
|
<ClInclude Include="EventWaiter.h" />
|
||||||
|
<ClInclude Include="excluded_apps.h" />
|
||||||
|
<ClInclude Include="exec.h" />
|
||||||
|
<ClInclude Include="game_mode.h" />
|
||||||
|
<ClInclude Include="gpo.h" />
|
||||||
|
<ClInclude Include="HDropIterator.h" />
|
||||||
|
<ClInclude Include="HttpClient.h" />
|
||||||
|
<ClInclude Include="json.h" />
|
||||||
|
<ClInclude Include="language_helper.h" />
|
||||||
|
<ClInclude Include="logger_helper.h" />
|
||||||
|
<ClInclude Include="modulesRegistry.h" />
|
||||||
|
<ClInclude Include="MsiUtils.h" />
|
||||||
|
<ClInclude Include="MsWindowsSettings.h" />
|
||||||
|
<ClInclude Include="OnThreadExecutor.h" />
|
||||||
|
<ClInclude Include="os-detect.h" />
|
||||||
|
<ClInclude Include="package.h" />
|
||||||
|
<ClInclude Include="pch.h" />
|
||||||
|
<ClInclude Include="process_path.h" />
|
||||||
|
<ClInclude Include="processApi.h" />
|
||||||
|
<ClInclude Include="ProcessWaiter.h" />
|
||||||
|
<ClInclude Include="registry.h" />
|
||||||
|
<ClInclude Include="resources.h" />
|
||||||
|
<ClInclude Include="serialized.h" />
|
||||||
|
<ClInclude Include="shell_ext_registration.h" />
|
||||||
|
<ClInclude Include="string_utils.h" />
|
||||||
|
<ClInclude Include="timeutil.h" />
|
||||||
|
<ClInclude Include="UnhandledExceptionHandler.h" />
|
||||||
|
<ClInclude Include="winapi_error.h" />
|
||||||
|
<ClInclude Include="window.h" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="pch.cpp">
|
||||||
|
<PrecompiledHeader Condition="'$(UsePrecompiledHeaders)' != 'false'">Create</PrecompiledHeader>
|
||||||
|
<PrecompiledHeader Condition="'$(UsePrecompiledHeaders)' == 'false'">NotUsing</PrecompiledHeader>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="EventLocker.cpp" />
|
||||||
|
<ClCompile Include="EventWaiter.cpp" />
|
||||||
|
<ClCompile Include="HDropIterator.cpp" />
|
||||||
|
<ClCompile Include="HttpClient.cpp" />
|
||||||
|
<ClCompile Include="MsWindowsSettings.cpp" />
|
||||||
|
<ClCompile Include="MsiUtils.cpp" />
|
||||||
|
<ClCompile Include="OnThreadExecutor.cpp" />
|
||||||
|
<ClCompile Include="ProcessWaiter.cpp" />
|
||||||
|
<ClCompile Include="UnhandledExceptionHandler.cpp" />
|
||||||
|
<ClCompile Include="appMutex.cpp" />
|
||||||
|
<ClCompile Include="clean_video_conference.cpp" />
|
||||||
|
<ClCompile Include="color.cpp" />
|
||||||
|
<ClCompile Include="excluded_apps.cpp" />
|
||||||
|
<ClCompile Include="exec.cpp" />
|
||||||
|
<ClCompile Include="game_mode.cpp" />
|
||||||
|
<ClCompile Include="package.cpp" />
|
||||||
|
<ClCompile Include="elevation.cpp" />
|
||||||
|
<ClCompile Include="resources.cpp" />
|
||||||
|
<ClCompile Include="gpo.cpp" />
|
||||||
|
<ClCompile Include="json.cpp" />
|
||||||
|
<ClCompile Include="language_helper.cpp" />
|
||||||
|
<ClCompile Include="logger_helper.cpp" />
|
||||||
|
<ClCompile Include="modulesRegistry.cpp" />
|
||||||
|
<ClCompile Include="processApi.cpp" />
|
||||||
|
<ClCompile Include="process_path.cpp" />
|
||||||
|
<ClCompile Include="registry.cpp" />
|
||||||
|
<ClCompile Include="shell_ext_registration.cpp" />
|
||||||
|
<ClCompile Include="string_utils.cpp" />
|
||||||
|
<ClCompile Include="timeutil.cpp" />
|
||||||
|
<ClCompile Include="winapi_error.cpp" />
|
||||||
|
<ClCompile Include="window.cpp" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\logging\logging.vcxproj">
|
||||||
|
<Project>{7e1e3f13-2bd6-3f75-a6a7-873a2b55c60f}</Project>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\logger\logger.vcxproj">
|
||||||
|
<Project>{d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}</Project>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\version\version.vcxproj">
|
||||||
|
<Project>{cc6e41ac-8174-4e8a-8d22-85dd7f4851df}</Project>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="packages.config" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
<Import Project="..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||||
|
<Import Project="..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||||
|
</ImportGroup>
|
||||||
|
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||||
|
<PropertyGroup>
|
||||||
|
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Error Condition="!Exists('..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||||
|
</Target>
|
||||||
|
</Project>
|
||||||
43
src/common/utils/winapi_error.cpp
Normal file
43
src/common/utils/winapi_error.cpp
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "winapi_error.h"
|
||||||
|
|
||||||
|
std::optional<std::wstring> get_last_error_message(DWORD dw)
|
||||||
|
{
|
||||||
|
std::optional<std::wstring> message;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const auto msg = std::system_category().message(dw);
|
||||||
|
message.emplace(begin(msg), end(msg));
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring get_last_error_or_default(DWORD dw)
|
||||||
|
{
|
||||||
|
auto message = get_last_error_message(dw);
|
||||||
|
return message.has_value() ? *message : L"";
|
||||||
|
}
|
||||||
|
|
||||||
|
void show_last_error_message(const wchar_t* functionName, DWORD dw, const wchar_t* errorTitle)
|
||||||
|
{
|
||||||
|
const auto systemMessage = get_last_error_message(dw);
|
||||||
|
if (!systemMessage.has_value())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LPWSTR lpDisplayBuf = static_cast<LPWSTR>(LocalAlloc(LMEM_ZEROINIT, (systemMessage->size() + lstrlenW(functionName) + 40) * sizeof(WCHAR)));
|
||||||
|
if (lpDisplayBuf != nullptr)
|
||||||
|
{
|
||||||
|
StringCchPrintfW(lpDisplayBuf,
|
||||||
|
LocalSize(lpDisplayBuf) / sizeof(WCHAR),
|
||||||
|
L"%s: %s (%d)",
|
||||||
|
functionName,
|
||||||
|
systemMessage->c_str(),
|
||||||
|
dw);
|
||||||
|
MessageBoxW(nullptr, lpDisplayBuf, errorTitle, MB_OK | MB_ICONERROR);
|
||||||
|
LocalFree(lpDisplayBuf);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,43 +8,8 @@
|
|||||||
#include <system_error>
|
#include <system_error>
|
||||||
#include <strsafe.h>
|
#include <strsafe.h>
|
||||||
|
|
||||||
inline std::optional<std::wstring> get_last_error_message(const DWORD dw)
|
std::optional<std::wstring> get_last_error_message(DWORD dw);
|
||||||
{
|
|
||||||
std::optional<std::wstring> message;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
const auto msg = std::system_category().message(dw);
|
|
||||||
message.emplace(begin(msg), end(msg));
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::wstring get_last_error_or_default(const DWORD dw)
|
std::wstring get_last_error_or_default(DWORD dw);
|
||||||
{
|
|
||||||
auto message = get_last_error_message(dw);
|
|
||||||
return message.has_value() ? *message : L"";
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void show_last_error_message(const wchar_t* functionName, DWORD dw, const wchar_t* errorTitle)
|
void show_last_error_message(const wchar_t* functionName, DWORD dw, const wchar_t* errorTitle);
|
||||||
{
|
|
||||||
const auto system_message = get_last_error_message(dw);
|
|
||||||
if (!system_message.has_value())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
LPWSTR lpDisplayBuf = static_cast<LPWSTR>(LocalAlloc(LMEM_ZEROINIT, (system_message->size() + lstrlenW(functionName) + 40) * sizeof(WCHAR)));
|
|
||||||
if (lpDisplayBuf != NULL)
|
|
||||||
{
|
|
||||||
StringCchPrintfW(lpDisplayBuf,
|
|
||||||
LocalSize(lpDisplayBuf) / sizeof(WCHAR),
|
|
||||||
L"%s: %s (%d)",
|
|
||||||
functionName,
|
|
||||||
system_message->c_str(),
|
|
||||||
dw);
|
|
||||||
MessageBoxW(NULL, lpDisplayBuf, errorTitle, MB_OK | MB_ICONERROR);
|
|
||||||
LocalFree(lpDisplayBuf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
56
src/common/utils/window.cpp
Normal file
56
src/common/utils/window.cpp
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "window.h"
|
||||||
|
|
||||||
|
int run_message_loop(bool until_idle,
|
||||||
|
std::optional<uint32_t> timeout_ms,
|
||||||
|
std::unordered_map<DWORD, std::function<void()>> wm_app_msg_callbacks)
|
||||||
|
{
|
||||||
|
MSG msg{};
|
||||||
|
bool stop = false;
|
||||||
|
UINT_PTR timerId = 0;
|
||||||
|
if (timeout_ms.has_value())
|
||||||
|
{
|
||||||
|
timerId = SetTimer(nullptr, 0, *timeout_ms, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!stop && (until_idle ? PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE) : GetMessageW(&msg, nullptr, 0, 0)))
|
||||||
|
{
|
||||||
|
TranslateMessage(&msg);
|
||||||
|
DispatchMessageW(&msg);
|
||||||
|
stop = until_idle && !PeekMessageW(&msg, nullptr, 0, 0, PM_NOREMOVE);
|
||||||
|
stop = stop || (msg.message == WM_TIMER && msg.wParam == timerId);
|
||||||
|
|
||||||
|
if (auto it = wm_app_msg_callbacks.find(msg.message); it != end(wm_app_msg_callbacks))
|
||||||
|
{
|
||||||
|
it->second();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeout_ms.has_value())
|
||||||
|
{
|
||||||
|
KillTimer(nullptr, timerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return static_cast<int>(msg.wParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_system_window(HWND hwnd, const char* class_name)
|
||||||
|
{
|
||||||
|
constexpr std::array system_classes = { "SysListView32", "WorkerW", "Shell_TrayWnd", "Shell_SecondaryTrayWnd", "Progman" };
|
||||||
|
const std::array system_hwnds = { GetDesktopWindow(), GetShellWindow() };
|
||||||
|
for (auto system_hwnd : system_hwnds)
|
||||||
|
{
|
||||||
|
if (hwnd == system_hwnd)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const auto system_class : system_classes)
|
||||||
|
{
|
||||||
|
if (!strcmp(system_class, class_name))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
@@ -10,60 +10,12 @@
|
|||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
// Initializes and runs windows message loop
|
// Initializes and runs windows message loop
|
||||||
inline int run_message_loop(const bool until_idle = false,
|
int run_message_loop(bool until_idle = false,
|
||||||
const std::optional<uint32_t> timeout_ms = {},
|
std::optional<uint32_t> timeout_ms = {},
|
||||||
std::unordered_map<DWORD, std::function<void()>> wm_app_msg_callbacks = {})
|
std::unordered_map<DWORD, std::function<void()>> wm_app_msg_callbacks = {});
|
||||||
{
|
|
||||||
MSG msg{};
|
|
||||||
bool stop = false;
|
|
||||||
UINT_PTR timerId = 0;
|
|
||||||
if (timeout_ms.has_value())
|
|
||||||
{
|
|
||||||
timerId = SetTimer(nullptr, 0, *timeout_ms, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (!stop && (until_idle ? PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE) : GetMessageW(&msg, nullptr, 0, 0)))
|
|
||||||
{
|
|
||||||
TranslateMessage(&msg);
|
|
||||||
DispatchMessageW(&msg);
|
|
||||||
stop = until_idle && !PeekMessageW(&msg, nullptr, 0, 0, PM_NOREMOVE);
|
|
||||||
stop = stop || (msg.message == WM_TIMER && msg.wParam == timerId);
|
|
||||||
|
|
||||||
if (auto it = wm_app_msg_callbacks.find(msg.message); it != end(wm_app_msg_callbacks))
|
|
||||||
it->second();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (timeout_ms.has_value())
|
|
||||||
{
|
|
||||||
KillTimer(nullptr, timerId);
|
|
||||||
}
|
|
||||||
|
|
||||||
return static_cast<int>(msg.wParam);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if window is part of the shell or the taskbar.
|
// Check if window is part of the shell or the taskbar.
|
||||||
inline bool is_system_window(HWND hwnd, const char* class_name)
|
bool is_system_window(HWND hwnd, const char* class_name);
|
||||||
{
|
|
||||||
// We compare the HWND against HWND of the desktop and shell windows,
|
|
||||||
// we also filter out some window class names know to belong to the taskbar.
|
|
||||||
constexpr std::array system_classes = { "SysListView32", "WorkerW", "Shell_TrayWnd", "Shell_SecondaryTrayWnd", "Progman" };
|
|
||||||
const std::array system_hwnds = { GetDesktopWindow(), GetShellWindow() };
|
|
||||||
for (auto system_hwnd : system_hwnds)
|
|
||||||
{
|
|
||||||
if (hwnd == system_hwnd)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const auto system_class : system_classes)
|
|
||||||
{
|
|
||||||
if (!strcmp(system_class, class_name))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline T GetWindowCreateParam(LPARAM lparam)
|
inline T GetWindowCreateParam(LPARAM lparam)
|
||||||
|
|||||||
@@ -1,18 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project DefaultTargets="Build" ToolsVersion="16.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project DefaultTargets="Build" ToolsVersion="16.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<ItemGroup Label="ProjectConfigurations">
|
<ItemGroup Label="ProjectConfigurations">
|
||||||
<ProjectConfiguration Include="Debug|Win32">
|
|
||||||
<Configuration>Debug</Configuration>
|
|
||||||
<Platform>Win32</Platform>
|
|
||||||
</ProjectConfiguration>
|
|
||||||
<ProjectConfiguration Include="Debug|x64">
|
<ProjectConfiguration Include="Debug|x64">
|
||||||
<Configuration>Debug</Configuration>
|
<Configuration>Debug</Configuration>
|
||||||
<Platform>x64</Platform>
|
<Platform>x64</Platform>
|
||||||
</ProjectConfiguration>
|
</ProjectConfiguration>
|
||||||
<ProjectConfiguration Include="Release|Win32">
|
|
||||||
<Configuration>Release</Configuration>
|
|
||||||
<Platform>Win32</Platform>
|
|
||||||
</ProjectConfiguration>
|
|
||||||
<ProjectConfiguration Include="Release|x64">
|
<ProjectConfiguration Include="Release|x64">
|
||||||
<Configuration>Release</Configuration>
|
<Configuration>Release</Configuration>
|
||||||
<Platform>x64</Platform>
|
<Platform>x64</Platform>
|
||||||
|
|||||||
@@ -62,6 +62,9 @@
|
|||||||
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
|
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
|
||||||
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\..\..\common\utils\utils.vcxproj">
|
||||||
|
<Project>{e8470e15-88c4-4c4b-b872-7f1a8f8e7d7e}</Project>
|
||||||
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="AdvancedPaste.base.rc" />
|
<None Include="AdvancedPaste.base.rc" />
|
||||||
@@ -84,4 +87,4 @@
|
|||||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||||
</Target>
|
</Target>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -157,6 +157,9 @@
|
|||||||
<ProjectReference Include="..\..\..\common\Themes\Themes.vcxproj">
|
<ProjectReference Include="..\..\..\common\Themes\Themes.vcxproj">
|
||||||
<Project>{98537082-0fdb-40de-abd8-0dc5a4269bab}</Project>
|
<Project>{98537082-0fdb-40de-abd8-0dc5a4269bab}</Project>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\..\..\common\utils\utils.vcxproj">
|
||||||
|
<Project>{e8470e15-88c4-4c4b-b872-7f1a8f8e7d7e}</Project>
|
||||||
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
<Import Project="..\..\..\..\deps\spdlog.props" />
|
<Import Project="..\..\..\..\deps\spdlog.props" />
|
||||||
@@ -174,4 +177,4 @@
|
|||||||
<Error Condition="!Exists('..\..\..\..\packages\robmikh.common.0.0.23-beta\build\native\robmikh.common.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\robmikh.common.0.0.23-beta\build\native\robmikh.common.targets'))" />
|
<Error Condition="!Exists('..\..\..\..\packages\robmikh.common.0.0.23-beta\build\native\robmikh.common.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\robmikh.common.0.0.23-beta\build\native\robmikh.common.targets'))" />
|
||||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||||
</Target>
|
</Target>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -97,6 +97,9 @@
|
|||||||
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
|
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
|
||||||
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\..\..\common\utils\utils.vcxproj">
|
||||||
|
<Project>{e8470e15-88c4-4c4b-b872-7f1a8f8e7d7e}</Project>
|
||||||
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
<ImportGroup Label="ExtensionTargets">
|
<ImportGroup Label="ExtensionTargets">
|
||||||
@@ -115,4 +118,4 @@
|
|||||||
<Error Condition="!Exists('..\..\..\..\packages\robmikh.common.0.0.23-beta\build\native\robmikh.common.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\robmikh.common.0.0.23-beta\build\native\robmikh.common.targets'))" />
|
<Error Condition="!Exists('..\..\..\..\packages\robmikh.common.0.0.23-beta\build\native\robmikh.common.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\robmikh.common.0.0.23-beta\build\native\robmikh.common.targets'))" />
|
||||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||||
</Target>
|
</Target>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -103,6 +103,9 @@
|
|||||||
<ProjectReference Include="..\..\..\common\version\version.vcxproj">
|
<ProjectReference Include="..\..\..\common\version\version.vcxproj">
|
||||||
<Project>{cc6e41ac-8174-4e8a-8d22-85dd7f4851df}</Project>
|
<Project>{cc6e41ac-8174-4e8a-8d22-85dd7f4851df}</Project>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\..\..\common\utils\utils.vcxproj">
|
||||||
|
<Project>{e8470e15-88c4-4c4b-b872-7f1a8f8e7d7e}</Project>
|
||||||
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
<Import Project="..\..\..\..\deps\spdlog.props" />
|
<Import Project="..\..\..\..\deps\spdlog.props" />
|
||||||
@@ -116,4 +119,4 @@
|
|||||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||||
</Target>
|
</Target>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -79,6 +79,11 @@
|
|||||||
<PrecompiledHeader Condition="'$(UsePrecompiledHeaders)' != 'false'">Create</PrecompiledHeader>
|
<PrecompiledHeader Condition="'$(UsePrecompiledHeaders)' != 'false'">Create</PrecompiledHeader>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\..\common\utils\utils.vcxproj">
|
||||||
|
<Project>{e8470e15-88c4-4c4b-b872-7f1a8f8e7d7e}</Project>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -70,6 +70,9 @@
|
|||||||
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
|
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
|
||||||
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\..\..\common\utils\utils.vcxproj">
|
||||||
|
<Project>{e8470e15-88c4-4c4b-b872-7f1a8f8e7d7e}</Project>
|
||||||
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="HostsModuleInterface.base.rc" />
|
<None Include="HostsModuleInterface.base.rc" />
|
||||||
|
|||||||
@@ -206,6 +206,9 @@
|
|||||||
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
|
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
|
||||||
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\..\..\common\utils\utils.vcxproj">
|
||||||
|
<Project>{e8470e15-88c4-4c4b-b872-7f1a8f8e7d7e}</Project>
|
||||||
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config" />
|
||||||
@@ -222,4 +225,4 @@
|
|||||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||||
</Target>
|
</Target>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -109,11 +109,20 @@
|
|||||||
<ProjectReference Include="..\..\..\common\Telemetry\EtwTrace\EtwTrace.vcxproj">
|
<ProjectReference Include="..\..\..\common\Telemetry\EtwTrace\EtwTrace.vcxproj">
|
||||||
<Project>{8f021b46-362b-485c-bfba-ccf83e820cbd}</Project>
|
<Project>{8f021b46-362b-485c-bfba-ccf83e820cbd}</Project>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\..\..\common\utils\utils.vcxproj">
|
||||||
|
<Project>{e8470e15-88c4-4c4b-b872-7f1a8f8e7d7e}</Project>
|
||||||
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||||
<Import Project="..\..\..\..\deps\spdlog.props" />
|
<Import Project="..\..\..\..\deps\spdlog.props" />
|
||||||
<ImportGroup Label="ExtensionTargets">
|
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
<PropertyGroup>
|
||||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||||
</ImportGroup>
|
</PropertyGroup>
|
||||||
</Project>
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||||
|
</Target>
|
||||||
|
</Project>
|
||||||
|
|||||||
@@ -66,6 +66,9 @@
|
|||||||
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
|
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
|
||||||
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\..\..\common\utils\utils.vcxproj">
|
||||||
|
<Project>{e8470e15-88c4-4c4b-b872-7f1a8f8e7d7e}</Project>
|
||||||
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ResourceCompile Include="MeasureToolModuleInterface.rc" />
|
<ResourceCompile Include="MeasureToolModuleInterface.rc" />
|
||||||
@@ -85,4 +88,4 @@
|
|||||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||||
</Target>
|
</Target>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -129,6 +129,9 @@
|
|||||||
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
|
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
|
||||||
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\..\..\common\utils\utils.vcxproj">
|
||||||
|
<Project>{e8470e15-88c4-4c4b-b872-7f1a8f8e7d7e}</Project>
|
||||||
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ResourceCompile Include="FindMyMouse.rc" />
|
<ResourceCompile Include="FindMyMouse.rc" />
|
||||||
|
|||||||
@@ -106,6 +106,9 @@
|
|||||||
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
|
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
|
||||||
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\..\..\common\utils\utils.vcxproj">
|
||||||
|
<Project>{e8470e15-88c4-4c4b-b872-7f1a8f8e7d7e}</Project>
|
||||||
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config" />
|
||||||
|
|||||||
@@ -101,6 +101,9 @@
|
|||||||
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
|
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
|
||||||
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\..\..\common\utils\utils.vcxproj">
|
||||||
|
<Project>{e8470e15-88c4-4c4b-b872-7f1a8f8e7d7e}</Project>
|
||||||
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ResourceCompile Include="MouseJump.rc" />
|
<ResourceCompile Include="MouseJump.rc" />
|
||||||
@@ -120,4 +123,4 @@
|
|||||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||||
</Target>
|
</Target>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -107,6 +107,9 @@
|
|||||||
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
|
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
|
||||||
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\..\..\common\utils\utils.vcxproj">
|
||||||
|
<Project>{e8470e15-88c4-4c4b-b872-7f1a8f8e7d7e}</Project>
|
||||||
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config" />
|
||||||
|
|||||||
@@ -56,6 +56,9 @@
|
|||||||
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
|
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
|
||||||
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\..\..\common\utils\utils.vcxproj">
|
||||||
|
<Project>{e8470e15-88c4-4c4b-b872-7f1a8f8e7d7e}</Project>
|
||||||
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ResourceCompile Include="MouseWithoutBordersModuleInterface.rc" />
|
<ResourceCompile Include="MouseWithoutBordersModuleInterface.rc" />
|
||||||
@@ -77,4 +80,4 @@
|
|||||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||||
</Target>
|
</Target>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -134,6 +134,9 @@
|
|||||||
<ProjectReference Include="..\..\..\common\Themes\Themes.vcxproj">
|
<ProjectReference Include="..\..\..\common\Themes\Themes.vcxproj">
|
||||||
<Project>{98537082-0fdb-40de-abd8-0dc5a4269bab}</Project>
|
<Project>{98537082-0fdb-40de-abd8-0dc5a4269bab}</Project>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\..\..\common\utils\utils.vcxproj">
|
||||||
|
<Project>{e8470e15-88c4-4c4b-b872-7f1a8f8e7d7e}</Project>
|
||||||
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="dll.def" />
|
<None Include="dll.def" />
|
||||||
|
|||||||
@@ -182,6 +182,9 @@ MakeAppx.exe pack /d . /p $(OutDir)NewPlusPackage.msix /nv</Command>
|
|||||||
<ProjectReference Include="..\..\..\common\version\version.vcxproj">
|
<ProjectReference Include="..\..\..\common\version\version.vcxproj">
|
||||||
<Project>{cc6e41ac-8174-4e8a-8d22-85dd7f4851df}</Project>
|
<Project>{cc6e41ac-8174-4e8a-8d22-85dd7f4851df}</Project>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\..\..\common\utils\utils.vcxproj">
|
||||||
|
<Project>{e8470e15-88c4-4c4b-b872-7f1a8f8e7d7e}</Project>
|
||||||
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ResourceCompile Include="Generated Files/new.rc" />
|
<ResourceCompile Include="Generated Files/new.rc" />
|
||||||
@@ -242,4 +245,4 @@ MakeAppx.exe pack /d . /p $(OutDir)NewPlusPackage.msix /nv</Command>
|
|||||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||||
</Target>
|
</Target>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -60,6 +60,9 @@
|
|||||||
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
|
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
|
||||||
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\..\..\common\utils\utils.vcxproj">
|
||||||
|
<Project>{e8470e15-88c4-4c4b-b872-7f1a8f8e7d7e}</Project>
|
||||||
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="PowerOCR.base.rc" />
|
<None Include="PowerOCR.base.rc" />
|
||||||
|
|||||||
@@ -164,6 +164,9 @@
|
|||||||
<ProjectReference Include="..\..\..\common\Themes\Themes.vcxproj">
|
<ProjectReference Include="..\..\..\common\Themes\Themes.vcxproj">
|
||||||
<Project>{98537082-0fdb-40de-abd8-0dc5a4269bab}</Project>
|
<Project>{98537082-0fdb-40de-abd8-0dc5a4269bab}</Project>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\..\..\common\utils\utils.vcxproj">
|
||||||
|
<Project>{e8470e15-88c4-4c4b-b872-7f1a8f8e7d7e}</Project>
|
||||||
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="Resources.resx" />
|
<EmbeddedResource Include="Resources.resx" />
|
||||||
|
|||||||
@@ -66,6 +66,9 @@
|
|||||||
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
|
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
|
||||||
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\..\..\common\utils\utils.vcxproj">
|
||||||
|
<Project>{e8470e15-88c4-4c4b-b872-7f1a8f8e7d7e}</Project>
|
||||||
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="Resources.resx" />
|
<EmbeddedResource Include="Resources.resx" />
|
||||||
|
|||||||
@@ -158,6 +158,9 @@
|
|||||||
<ProjectReference Include="..\..\..\common\Telemetry\EtwTrace\EtwTrace.vcxproj">
|
<ProjectReference Include="..\..\..\common\Telemetry\EtwTrace\EtwTrace.vcxproj">
|
||||||
<Project>{8f021b46-362b-485c-bfba-ccf83e820cbd}</Project>
|
<Project>{8f021b46-362b-485c-bfba-ccf83e820cbd}</Project>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\..\..\common\utils\utils.vcxproj">
|
||||||
|
<Project>{E8470E15-88C4-4C4B-B872-7F1A8F8E7D7E}</Project>
|
||||||
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\WorkspacesLib\WorkspacesLib.vcxproj">
|
<ProjectReference Include="..\WorkspacesLib\WorkspacesLib.vcxproj">
|
||||||
<Project>{b31fcc55-b5a4-4ea7-b414-2dceae6af332}</Project>
|
<Project>{b31fcc55-b5a4-4ea7-b414-2dceae6af332}</Project>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
@@ -185,4 +188,4 @@
|
|||||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||||
</Target>
|
</Target>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -74,6 +74,9 @@
|
|||||||
<ProjectReference Include="..\..\..\common\logger\logger.vcxproj">
|
<ProjectReference Include="..\..\..\common\logger\logger.vcxproj">
|
||||||
<Project>{d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}</Project>
|
<Project>{d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}</Project>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\..\..\common\utils\utils.vcxproj">
|
||||||
|
<Project>{e8470e15-88c4-4c4b-b872-7f1a8f8e7d7e}</Project>
|
||||||
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config" />
|
||||||
|
|||||||
@@ -150,6 +150,9 @@
|
|||||||
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
|
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
|
||||||
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\..\..\common\utils\utils.vcxproj">
|
||||||
|
<Project>{E8470E15-88C4-4C4B-B872-7F1A8F8E7D7E}</Project>
|
||||||
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\WorkspacesLib\WorkspacesLib.vcxproj">
|
<ProjectReference Include="..\WorkspacesLib\WorkspacesLib.vcxproj">
|
||||||
<Project>{b31fcc55-b5a4-4ea7-b414-2dceae6af332}</Project>
|
<Project>{b31fcc55-b5a4-4ea7-b414-2dceae6af332}</Project>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
@@ -177,4 +180,4 @@
|
|||||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||||
</Target>
|
</Target>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
#include <common/utils/gpo.h>
|
#include <common/utils/gpo.h>
|
||||||
#include <common/utils/logger_helper.h>
|
#include <common/utils/logger_helper.h>
|
||||||
#include <common/utils/UnhandledExceptionHandler.h>
|
#include <common/utils/UnhandledExceptionHandler.h>
|
||||||
|
#include <common/utils/winapi_error.h>
|
||||||
#include <WorkspacesLib/utils.h>
|
#include <WorkspacesLib/utils.h>
|
||||||
|
|
||||||
const std::wstring moduleName = L"Workspaces\\WorkspacesSnapshotTool";
|
const std::wstring moduleName = L"Workspaces\\WorkspacesSnapshotTool";
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user