mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-03 17:56:44 +02:00
FZ: warn w/ a toast if an elevated window cannot be dragged and offer learning more
This commit is contained in:
committed by
Andrey Nekrasov
parent
c2e219b446
commit
60fa6071b9
@@ -29,6 +29,9 @@
|
||||
<ProjectName>common</ProjectName>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<ImportGroup Label="Shared">
|
||||
<Import Project="..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.190716.2\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.190716.2\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
@@ -164,7 +167,16 @@
|
||||
<ClCompile Include="window_helpers.cpp" />
|
||||
<ClCompile Include="winstore.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</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.ImplementationLibrary.1.0.190716.2\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.190716.2\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -145,10 +145,10 @@ void notifications::register_background_toast_handler()
|
||||
}
|
||||
}
|
||||
|
||||
void notifications::show_toast(std::wstring_view message)
|
||||
void notifications::show_toast(std::wstring message, toast_params params)
|
||||
{
|
||||
// The toast won't be actually activated in the background, since it doesn't have any buttons
|
||||
show_toast_with_activations(message, {}, {});
|
||||
show_toast_with_activations(std::move(message), {}, {}, std::move(params));
|
||||
}
|
||||
|
||||
inline void xml_escape(std::wstring data)
|
||||
@@ -182,13 +182,13 @@ inline void xml_escape(std::wstring data)
|
||||
data.swap(buffer);
|
||||
}
|
||||
|
||||
void notifications::show_toast_with_activations(std::wstring_view message, std::wstring_view background_handler_id, std::vector<button_t> buttons)
|
||||
void notifications::show_toast_with_activations(std::wstring message, std::wstring_view background_handler_id, std::vector<action_t> actions, toast_params params)
|
||||
{
|
||||
// DO NOT LOCALIZE any string in this function, because they're XML tags and a subject to
|
||||
// https://docs.microsoft.com/en-us/windows/uwp/design/shell/tiles-and-notifications/toast-xml-schema
|
||||
|
||||
std::wstring toast_xml;
|
||||
toast_xml.reserve(1024);
|
||||
toast_xml.reserve(2048);
|
||||
std::wstring title{ L"PowerToys" };
|
||||
if (winstore::running_as_packaged())
|
||||
{
|
||||
@@ -200,28 +200,78 @@ void notifications::show_toast_with_activations(std::wstring_view message, std::
|
||||
toast_xml += L"</text><text>";
|
||||
toast_xml += message;
|
||||
toast_xml += L"</text></binding></visual><actions>";
|
||||
for (size_t i = 0; i < size(actions); ++i)
|
||||
{
|
||||
std::visit(overloaded{
|
||||
[&](const snooze_button& b) {
|
||||
const bool has_durations = !b.durations.empty() && size(b.durations) <= 5;
|
||||
std::wstring selection_id = L"snoozeTime";
|
||||
selection_id += static_cast<wchar_t>(L'0' + i);
|
||||
if (has_durations)
|
||||
{
|
||||
toast_xml += LR"(<input id=")";
|
||||
toast_xml += selection_id;
|
||||
toast_xml += LR"(" type="selection" defaultInput=")";
|
||||
toast_xml += std::to_wstring(b.durations[0].minutes);
|
||||
toast_xml += LR"(">)";
|
||||
for (const auto& duration : b.durations)
|
||||
{
|
||||
toast_xml += LR"(<selection id=")";
|
||||
toast_xml += std::to_wstring(duration.minutes);
|
||||
toast_xml += LR"(" content=")";
|
||||
toast_xml += duration.label;
|
||||
toast_xml += LR"("/>)";
|
||||
}
|
||||
toast_xml += LR"(</input>)";
|
||||
}
|
||||
},
|
||||
[](const auto&) {} },
|
||||
actions[i]);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < size(buttons); ++i)
|
||||
for (size_t i = 0; i < size(actions); ++i)
|
||||
{
|
||||
std::visit(overloaded{
|
||||
[&](const link_button& b) {
|
||||
toast_xml += LR"(<action activationType="protocol" arguments=")";
|
||||
toast_xml += LR"(<action activationType="protocol" )";
|
||||
if (b.context_menu)
|
||||
{
|
||||
toast_xml += LR"(placement="contextMenu" )";
|
||||
}
|
||||
toast_xml += LR"(arguments=")";
|
||||
toast_xml += b.url;
|
||||
toast_xml += LR"(" content=")";
|
||||
toast_xml += b.label;
|
||||
toast_xml += LR"("/>)";
|
||||
toast_xml += LR"(" />)";
|
||||
},
|
||||
[&](const background_activated_button& b) {
|
||||
toast_xml += LR"(<action activationType="background" arguments=")";
|
||||
toast_xml += LR"(<action activationType="background" )";
|
||||
if (b.context_menu)
|
||||
{
|
||||
toast_xml += LR"(placement="contextMenu" )";
|
||||
}
|
||||
toast_xml += LR"(arguments=")";
|
||||
toast_xml += L"button_id=" + std::to_wstring(i); // pass the button ID
|
||||
toast_xml += L"&handler=";
|
||||
toast_xml += background_handler_id;
|
||||
toast_xml += LR"(" content=")";
|
||||
toast_xml += b.label;
|
||||
toast_xml += LR"("/>)";
|
||||
toast_xml += LR"(" />)";
|
||||
},
|
||||
},
|
||||
buttons[i]);
|
||||
[&](const snooze_button& b) {
|
||||
const bool has_durations = !b.durations.empty() && size(b.durations) <= 5;
|
||||
std::wstring selection_id = L"snoozeTime";
|
||||
selection_id += static_cast<wchar_t>(L'0' + i);
|
||||
toast_xml += LR"(<action activationType="system" arguments="snooze" )";
|
||||
if (has_durations)
|
||||
{
|
||||
toast_xml += LR"(hint-inputId=")";
|
||||
toast_xml += selection_id;
|
||||
toast_xml += '"';
|
||||
}
|
||||
toast_xml += LR"( content="" />)";
|
||||
} },
|
||||
actions[i]);
|
||||
}
|
||||
toast_xml += L"</actions></toast>";
|
||||
|
||||
@@ -232,5 +282,22 @@ void notifications::show_toast_with_activations(std::wstring_view message, std::
|
||||
|
||||
const auto notifier = winstore::running_as_packaged() ? ToastNotificationManager::ToastNotificationManager::CreateToastNotifier() :
|
||||
ToastNotificationManager::ToastNotificationManager::CreateToastNotifier(WIN32_AUMID);
|
||||
|
||||
// Set a tag-related params if it has a valid length
|
||||
if (params.tag.has_value() && params.tag->length() < 64)
|
||||
{
|
||||
notification.Tag(*params.tag);
|
||||
if (!params.resend_if_scheduled)
|
||||
{
|
||||
for (const auto& scheduled_toast : notifier.GetScheduledToastNotifications())
|
||||
{
|
||||
if (scheduled_toast.Tag() == *params.tag)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
notifier.Show(notification);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <variant>
|
||||
#include <optional>
|
||||
|
||||
namespace notifications
|
||||
{
|
||||
@@ -12,19 +14,38 @@ namespace notifications
|
||||
|
||||
void run_desktop_app_activator_loop();
|
||||
|
||||
struct snooze_duration
|
||||
{
|
||||
std::wstring label;
|
||||
int minutes;
|
||||
};
|
||||
|
||||
struct snooze_button
|
||||
{
|
||||
std::vector<snooze_duration> durations;
|
||||
};
|
||||
|
||||
struct link_button
|
||||
{
|
||||
std::wstring_view label;
|
||||
std::wstring_view url;
|
||||
std::wstring label;
|
||||
std::wstring url;
|
||||
bool context_menu = false;
|
||||
};
|
||||
|
||||
struct background_activated_button
|
||||
{
|
||||
std::wstring_view label;
|
||||
std::wstring label;
|
||||
bool context_menu = false;
|
||||
};
|
||||
|
||||
using button_t = std::variant<link_button, background_activated_button>;
|
||||
struct toast_params
|
||||
{
|
||||
std::optional<std::wstring_view> tag;
|
||||
bool resend_if_scheduled = true;
|
||||
};
|
||||
|
||||
void show_toast(std::wstring_view plaintext_message);
|
||||
void show_toast_with_activations(std::wstring_view plaintext_message, std::wstring_view background_handler_id, std::vector<button_t> buttons);
|
||||
using action_t = std::variant<link_button, background_activated_button, snooze_button>;
|
||||
|
||||
void show_toast(std::wstring plaintext_message, toast_params params = {});
|
||||
void show_toast_with_activations(std::wstring plaintext_message, std::wstring_view background_handler_id, std::vector<action_t> actions, toast_params params = {});
|
||||
}
|
||||
|
||||
71
src/common/notifications/fancyzones_notifications.h
Normal file
71
src/common/notifications/fancyzones_notifications.h
Normal file
@@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <Windows.h>
|
||||
#include <limits>
|
||||
|
||||
#include "../timeutil.h"
|
||||
namespace
|
||||
{
|
||||
const inline wchar_t CANT_DRAG_ELEVATED_DONT_SHOW_AGAIN_REGISTRY_PATH[] = LR"(SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\DontShowMeThisDialogAgain\{e16ea82f-6d94-4f30-bb02-d6d911588afd})";
|
||||
const inline int64_t disable_interval_in_days = 30;
|
||||
}
|
||||
|
||||
inline bool disable_cant_drag_elevated_warning()
|
||||
{
|
||||
HKEY key{};
|
||||
if (RegCreateKeyExW(HKEY_CURRENT_USER,
|
||||
CANT_DRAG_ELEVATED_DONT_SHOW_AGAIN_REGISTRY_PATH,
|
||||
0,
|
||||
nullptr,
|
||||
REG_OPTION_NON_VOLATILE,
|
||||
KEY_ALL_ACCESS,
|
||||
nullptr,
|
||||
&key,
|
||||
nullptr) != ERROR_SUCCESS)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const auto now = timeutil::now();
|
||||
const size_t buf_size = sizeof(now);
|
||||
if (RegSetValueExW(key, nullptr, 0, REG_QWORD, reinterpret_cast<const BYTE*>(&now), sizeof(now)) != ERROR_SUCCESS)
|
||||
{
|
||||
RegCloseKey(key);
|
||||
return false;
|
||||
}
|
||||
RegCloseKey(key);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool is_cant_drag_elevated_warning_disabled()
|
||||
{
|
||||
HKEY key{};
|
||||
if (RegOpenKeyExW(HKEY_CURRENT_USER,
|
||||
CANT_DRAG_ELEVATED_DONT_SHOW_AGAIN_REGISTRY_PATH,
|
||||
0,
|
||||
KEY_READ,
|
||||
&key) != ERROR_SUCCESS)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
std::wstring buffer(std::numeric_limits<time_t>::digits10 + 2, L'\0');
|
||||
time_t last_disabled_time{};
|
||||
DWORD time_size = static_cast<DWORD>(sizeof(last_disabled_time));
|
||||
if (RegGetValueW(
|
||||
key,
|
||||
nullptr,
|
||||
nullptr,
|
||||
RRF_RT_REG_QWORD,
|
||||
nullptr,
|
||||
&last_disabled_time,
|
||||
&time_size) != ERROR_SUCCESS)
|
||||
{
|
||||
RegCloseKey(key);
|
||||
return false;
|
||||
}
|
||||
RegCloseKey(key);
|
||||
return timeutil::diff::in_days(timeutil::now(), last_disabled_time) < disable_interval_in_days;
|
||||
return false;
|
||||
}
|
||||
4
src/common/packages.config
Normal file
4
src/common/packages.config
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.190716.2" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "window_helpers.h"
|
||||
#include "pch.h"
|
||||
#include <wil/Resource.h>
|
||||
|
||||
|
||||
HWND CreateMsgWindow(_In_ HINSTANCE hInst, _In_ WNDPROC pfnWndProc, _In_ void* p)
|
||||
{
|
||||
@@ -27,4 +29,32 @@ HWND CreateMsgWindow(_In_ HINSTANCE hInst, _In_ WNDPROC pfnWndProc, _In_ void* p
|
||||
}
|
||||
|
||||
return hwnd;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
bool elevated = false;
|
||||
|
||||
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,7 @@
|
||||
#pragma once
|
||||
#include "common.h"
|
||||
|
||||
HWND CreateMsgWindow(_In_ HINSTANCE hInst, _In_ WNDPROC pfnWndProc, _In_ void* p);
|
||||
HWND CreateMsgWindow(_In_ HINSTANCE hInst, _In_ WNDPROC pfnWndProc, _In_ void* p);
|
||||
|
||||
// If HWND is already dead, we assume it wasn't elevated
|
||||
bool IsProcessOfWindowElevated(HWND window);
|
||||
Reference in New Issue
Block a user