2019-09-05 00:26:26 +08:00
# include "pch.h"
# include <WinSafer.h>
# include <Sddl.h>
# include <sstream>
# include <accctrl.h>
# include <aclapi.h>
2019-12-06 16:40:23 +08:00
2019-09-05 00:26:26 +08:00
# include "powertoy_module.h"
# include <common/two_way_pipe_message_ipc.h>
# include "tray_icon.h"
# include "general_settings.h"
2019-10-16 16:21:44 +08:00
# include "common/windows_colors.h"
2019-12-17 01:36:52 +08:00
# include "common/common.h"
# include "restart_elevated.h"
2019-09-05 00:26:26 +08:00
2019-12-06 16:40:23 +08:00
# include <common/json.h>
2020-05-06 01:02:31 +08:00
# include <common\settings_helpers.cpp>
2019-09-05 00:26:26 +08:00
2019-12-06 16:40:23 +08:00
# define BUFSIZE 1024
2019-09-05 00:26:26 +08:00
TwoWayPipeMessageIPC * current_settings_ipc = NULL ;
2019-12-11 22:39:05 +08:00
json : : JsonObject get_power_toys_settings ( )
{
json : : JsonObject result ;
for ( const auto & [ name , powertoy ] : modules ( ) )
{
try
{
result . SetNamedValue ( name , powertoy . json_config ( ) ) ;
}
catch ( . . . )
{
// TODO: handle malformed JSON.
}
2019-09-05 00:26:26 +08:00
}
2019-12-11 22:39:05 +08:00
return result ;
2019-09-05 00:26:26 +08:00
}
2019-12-11 22:39:05 +08:00
json : : JsonObject get_all_settings ( )
{
json : : JsonObject result ;
2019-12-06 16:40:23 +08:00
2020-04-21 15:30:12 +08:00
result . SetNamedValue ( L " general " , get_general_settings ( ) . to_json ( ) ) ;
2019-12-11 22:39:05 +08:00
result . SetNamedValue ( L " powertoys " , get_power_toys_settings ( ) ) ;
return result ;
2019-09-05 00:26:26 +08:00
}
2019-12-11 22:39:05 +08:00
void dispatch_json_action_to_module ( const json : : JsonObject & powertoys_configs )
{
for ( const auto & powertoy_element : powertoys_configs )
{
const std : : wstring name { powertoy_element . Key ( ) . c_str ( ) } ;
2019-12-17 01:36:52 +08:00
// Currently, there is only one custom action in the general settings screen,
// so it has to be the "restart as (non-)elevated" button.
if ( name = = L " general " )
{
if ( is_process_elevated ( ) )
{
schedule_restart_as_non_elevated ( ) ;
PostQuitMessage ( 0 ) ;
}
else
{
schedule_restart_as_elevated ( ) ;
PostQuitMessage ( 0 ) ;
}
}
else if ( modules ( ) . find ( name ) ! = modules ( ) . end ( ) )
2019-12-11 22:39:05 +08:00
{
const auto element = powertoy_element . Value ( ) . Stringify ( ) ;
2020-03-13 17:55:15 +08:00
modules ( ) . at ( name ) - > call_custom_action ( element . c_str ( ) ) ;
2019-12-11 22:39:05 +08:00
}
2019-09-05 00:26:26 +08:00
}
}
2019-12-11 22:39:05 +08:00
void send_json_config_to_module ( const std : : wstring & module_key , const std : : wstring & settings )
{
if ( modules ( ) . find ( module_key ) ! = modules ( ) . end ( ) )
{
2020-03-13 17:55:15 +08:00
modules ( ) . at ( module_key ) - > set_config ( settings . c_str ( ) ) ;
2019-12-11 22:39:05 +08:00
}
2019-09-05 00:26:26 +08:00
}
2019-12-11 22:39:05 +08:00
void dispatch_json_config_to_modules ( const json : : JsonObject & powertoys_configs )
{
for ( const auto & powertoy_element : powertoys_configs )
{
const auto element = powertoy_element . Value ( ) . Stringify ( ) ;
send_json_config_to_module ( powertoy_element . Key ( ) . c_str ( ) , element . c_str ( ) ) ;
}
2019-09-05 00:26:26 +08:00
} ;
2019-12-11 22:39:05 +08:00
void dispatch_received_json ( const std : : wstring & json_to_parse )
{
const json : : JsonObject j = json : : JsonObject : : Parse ( json_to_parse ) ;
for ( const auto & base_element : j )
{
const auto name = base_element . Key ( ) ;
const auto value = base_element . Value ( ) ;
if ( name = = L " general " )
{
apply_general_settings ( value . GetObjectW ( ) ) ;
if ( current_settings_ipc ! = nullptr )
{
const std : : wstring settings_string { get_all_settings ( ) . Stringify ( ) . c_str ( ) } ;
current_settings_ipc - > send ( settings_string ) ;
}
}
else if ( name = = L " powertoys " )
{
dispatch_json_config_to_modules ( value . GetObjectW ( ) ) ;
if ( current_settings_ipc ! = nullptr )
{
const std : : wstring settings_string { get_all_settings ( ) . Stringify ( ) . c_str ( ) } ;
current_settings_ipc - > send ( settings_string ) ;
}
}
else if ( name = = L " refresh " )
{
if ( current_settings_ipc ! = nullptr )
{
const std : : wstring settings_string { get_all_settings ( ) . Stringify ( ) . c_str ( ) } ;
current_settings_ipc - > send ( settings_string ) ;
}
}
else if ( name = = L " action " )
{
dispatch_json_action_to_module ( value . GetObjectW ( ) ) ;
}
2019-09-05 00:26:26 +08:00
}
2019-12-11 22:39:05 +08:00
return ;
2019-09-05 00:26:26 +08:00
}
2019-12-11 22:39:05 +08:00
void dispatch_received_json_callback ( PVOID data )
{
std : : wstring * msg = ( std : : wstring * ) data ;
dispatch_received_json ( * msg ) ;
delete msg ;
2019-09-05 00:26:26 +08:00
}
2019-12-11 22:39:05 +08:00
void receive_json_send_to_main_thread ( const std : : wstring & msg )
{
std : : wstring * copy = new std : : wstring ( msg ) ;
dispatch_run_on_main_ui_thread ( dispatch_received_json_callback , copy ) ;
2019-09-05 00:26:26 +08:00
}
2019-12-11 23:32:40 +08:00
// Try to run the Settings process with non-elevated privileges.
BOOL run_settings_non_elevated ( LPCWSTR executable_path , LPWSTR executable_args , PROCESS_INFORMATION * process_info )
2019-12-11 22:39:05 +08:00
{
HWND hwnd = GetShellWindow ( ) ;
if ( ! hwnd )
{
2019-12-11 23:32:40 +08:00
return false ;
2019-12-11 22:39:05 +08:00
}
2019-12-11 23:32:40 +08:00
2019-12-11 22:39:05 +08:00
DWORD pid ;
GetWindowThreadProcessId ( hwnd , & pid ) ;
2019-09-05 00:26:26 +08:00
2019-12-11 23:32:40 +08:00
winrt : : handle process { OpenProcess ( PROCESS_CREATE_PROCESS , FALSE , pid ) } ;
2019-12-11 22:39:05 +08:00
if ( ! process )
{
2019-12-11 23:32:40 +08:00
return false ;
2019-12-11 22:39:05 +08:00
}
2019-09-05 00:26:26 +08:00
2019-12-11 23:32:40 +08:00
SIZE_T size = 0 ;
2019-12-11 22:39:05 +08:00
InitializeProcThreadAttributeList ( nullptr , 1 , 0 , & size ) ;
2019-12-27 00:26:11 +08:00
auto pproc_buffer = std : : unique_ptr < char [ ] > { new ( std : : nothrow ) char [ size ] } ;
2019-12-11 23:32:40 +08:00
auto pptal = reinterpret_cast < PPROC_THREAD_ATTRIBUTE_LIST > ( pproc_buffer . get ( ) ) ;
2019-12-11 22:39:05 +08:00
if ( ! pptal )
{
2019-12-11 23:32:40 +08:00
return false ;
2019-12-11 22:39:05 +08:00
}
2019-09-05 00:26:26 +08:00
2019-12-11 22:39:05 +08:00
if ( ! InitializeProcThreadAttributeList ( pptal , 1 , 0 , & size ) )
{
2019-12-11 23:32:40 +08:00
return false ;
2019-12-11 22:39:05 +08:00
}
2019-09-05 00:26:26 +08:00
2019-12-11 22:39:05 +08:00
if ( ! UpdateProcThreadAttribute ( pptal ,
0 ,
PROC_THREAD_ATTRIBUTE_PARENT_PROCESS ,
& process ,
sizeof ( process ) ,
nullptr ,
nullptr ) )
{
2019-12-11 23:32:40 +08:00
return false ;
2019-12-11 22:39:05 +08:00
}
2019-09-05 00:26:26 +08:00
2019-12-11 23:32:40 +08:00
STARTUPINFOEX siex = { 0 } ;
2019-12-11 22:39:05 +08:00
siex . lpAttributeList = pptal ;
siex . StartupInfo . cb = sizeof ( siex ) ;
2019-12-11 23:32:40 +08:00
BOOL process_created = CreateProcessW ( executable_path ,
executable_args ,
nullptr ,
nullptr ,
FALSE ,
2020-05-03 18:17:06 +08:00
0 ,
2019-12-11 23:32:40 +08:00
nullptr ,
nullptr ,
& siex . StartupInfo ,
process_info ) ;
return process_created ;
}
2020-05-06 06:53:30 +08:00
// The following three helper functions determine if the user has a build version higher than or equal to 19h1, as that is a requirement for xaml islands
// Source : Microsoft-ui-xaml github
// Link: https://github.com/microsoft/microsoft-ui-xaml/blob/c045cde57c5c754683d674634a0baccda34d58c4/dev/dll/SharedHelpers.cpp
template < uint16_t APIVersion > bool IsAPIContractVxAvailable ( )
{
static bool isAPIContractVxAvailableInitialized = false ;
static bool isAPIContractVxAvailable = false ;
if ( ! isAPIContractVxAvailableInitialized )
{
isAPIContractVxAvailableInitialized = true ;
isAPIContractVxAvailable = winrt : : Windows : : Foundation : : Metadata : : ApiInformation : : IsApiContractPresent ( L " Windows.Foundation.UniversalApiContract " , APIVersion ) ;
}
return isAPIContractVxAvailable ;
}
bool IsAPIContractV8Available ( )
{
return IsAPIContractVxAvailable < 8 > ( ) ;
}
bool Is19H1OrHigher ( )
{
return IsAPIContractV8Available ( ) ;
}
// This function returns true if the build is 19h1 or higher, so that we deploy the new settings.
// It returns false otherwise.
bool use_new_settings ( )
{
return Is19H1OrHigher ( ) ;
}
2019-12-11 23:32:40 +08:00
DWORD g_settings_process_id = 0 ;
void run_settings_window ( )
{
PROCESS_INFORMATION process_info = { 0 } ;
HANDLE hToken = nullptr ;
// Arguments for calling the settings executable:
// "C:\powertoys_path\PowerToysSettings.exe" powertoys_pipe settings_pipe powertoys_pid settings_theme
// powertoys_pipe: PowerToys pipe server.
// settings_pipe : Settings pipe server.
// powertoys_pid : PowerToys process pid.
// settings_theme: pass "dark" to start the settings window in dark mode
// Arg 1: executable path.
std : : wstring executable_path = get_module_folderpath ( ) ;
2020-05-06 06:53:30 +08:00
if ( use_new_settings ( ) )
{
executable_path . append ( L " \\ SettingsUIRunner \\ Microsoft.PowerToys.Settings.UI.Runner.exe " ) ;
}
else
{
executable_path . append ( L " \\ PowerToysSettings.exe " ) ;
}
2019-12-11 23:32:40 +08:00
// Arg 2: pipe server. Generate unique names for the pipes, if getting a UUID is possible.
std : : wstring powertoys_pipe_name ( L " \\ \\ . \\ pipe \\ powertoys_runner_ " ) ;
std : : wstring settings_pipe_name ( L " \\ \\ . \\ pipe \\ powertoys_settings_ " ) ;
UUID temp_uuid ;
UuidCreate ( & temp_uuid ) ;
wchar_t * uuid_chars ;
UuidToString ( & temp_uuid , ( RPC_WSTR * ) & uuid_chars ) ;
if ( uuid_chars ! = nullptr )
2019-12-11 22:39:05 +08:00
{
2019-12-11 23:32:40 +08:00
powertoys_pipe_name + = std : : wstring ( uuid_chars ) ;
settings_pipe_name + = std : : wstring ( uuid_chars ) ;
RpcStringFree ( ( RPC_WSTR * ) & uuid_chars ) ;
uuid_chars = nullptr ;
}
// Arg 3: process pid.
DWORD powertoys_pid = GetCurrentProcessId ( ) ;
// Arg 4: settings theme.
2020-04-21 15:30:12 +08:00
const std : : wstring settings_theme_setting { get_general_settings ( ) . theme } ;
2019-12-11 23:32:40 +08:00
std : : wstring settings_theme ;
if ( settings_theme_setting = = L " dark " | | ( settings_theme_setting = = L " system " & & WindowsColors : : is_dark_mode ( ) ) )
{
settings_theme = L " dark " ;
}
2020-05-06 01:02:31 +08:00
// Arg 4: settings theme.
GeneralSettings save_settings = get_general_settings ( ) ;
bool isElevated { get_general_settings ( ) . isElevated } ;
std : : wstring settings_elevatedStatus ;
settings_elevatedStatus = isElevated ;
if ( isElevated )
{
settings_elevatedStatus = L " true " ;
}
else
{
settings_elevatedStatus = L " false " ;
}
2019-12-11 23:32:40 +08:00
std : : wstring executable_args = L " \" " ;
executable_args . append ( executable_path ) ;
executable_args . append ( L " \" " ) ;
executable_args . append ( powertoys_pipe_name ) ;
executable_args . append ( L " " ) ;
executable_args . append ( settings_pipe_name ) ;
executable_args . append ( L " " ) ;
executable_args . append ( std : : to_wstring ( powertoys_pid ) ) ;
executable_args . append ( L " " ) ;
executable_args . append ( settings_theme ) ;
2020-05-06 01:02:31 +08:00
executable_args . append ( L " " ) ;
executable_args . append ( settings_elevatedStatus ) ;
2019-12-11 23:32:40 +08:00
BOOL process_created = false ;
if ( is_process_elevated ( ) )
{
process_created = run_settings_non_elevated ( executable_path . c_str ( ) , executable_args . data ( ) , & process_info ) ;
}
if ( FALSE = = process_created )
{
// The runner is not elevated or we failed to create the process using the
// attribute list from Windows Explorer (this happens when PowerToys is executed
// as Administrator from a non-Administrator user or an error occur trying).
// In the second case the Settings process will run elevated.
STARTUPINFO startup_info = { sizeof ( startup_info ) } ;
if ( ! CreateProcessW ( executable_path . c_str ( ) ,
executable_args . data ( ) ,
nullptr ,
nullptr ,
FALSE ,
0 ,
nullptr ,
nullptr ,
& startup_info ,
& process_info ) )
{
goto LExit ;
}
2019-12-11 22:39:05 +08:00
}
2019-09-05 00:26:26 +08:00
2019-12-11 22:39:05 +08:00
if ( ! OpenProcessToken ( GetCurrentProcess ( ) , TOKEN_QUERY , & hToken ) )
{
goto LExit ;
}
2019-12-11 23:32:40 +08:00
2019-12-11 22:39:05 +08:00
current_settings_ipc = new TwoWayPipeMessageIPC ( powertoys_pipe_name , settings_pipe_name , receive_json_send_to_main_thread ) ;
current_settings_ipc - > start ( hToken ) ;
g_settings_process_id = process_info . dwProcessId ;
WaitForSingleObject ( process_info . hProcess , INFINITE ) ;
if ( WaitForSingleObject ( process_info . hProcess , INFINITE ) ! = WAIT_OBJECT_0 )
{
show_last_error_message ( L " Couldn't wait on the Settings Window to close. " , GetLastError ( ) ) ;
}
2019-09-05 00:26:26 +08:00
2019-12-11 22:39:05 +08:00
LExit :
2019-09-05 00:26:26 +08:00
2019-12-11 22:39:05 +08:00
if ( process_info . hProcess )
{
CloseHandle ( process_info . hProcess ) ;
}
2019-09-05 00:26:26 +08:00
2019-12-11 22:39:05 +08:00
if ( process_info . hThread )
{
CloseHandle ( process_info . hThread ) ;
}
2019-09-05 00:26:26 +08:00
2019-12-11 22:39:05 +08:00
if ( current_settings_ipc )
{
current_settings_ipc - > end ( ) ;
delete current_settings_ipc ;
2019-12-11 23:32:40 +08:00
current_settings_ipc = nullptr ;
2019-12-11 22:39:05 +08:00
}
2019-09-05 00:26:26 +08:00
2019-12-11 22:39:05 +08:00
if ( hToken )
{
CloseHandle ( hToken ) ;
}
g_settings_process_id = 0 ;
}
2020-05-06 05:02:55 +08:00
# define MAX_TITLE_LENGTH 100
2019-12-11 22:39:05 +08:00
void bring_settings_to_front ( )
{
auto callback = [ ] ( HWND hwnd , LPARAM data ) - > BOOL {
DWORD processId ;
if ( GetWindowThreadProcessId ( hwnd , & processId ) & & processId = = g_settings_process_id )
{
2020-05-06 05:02:55 +08:00
std : : wstring windowTitle = L " PowerToys Settings " ;
WCHAR title [ MAX_TITLE_LENGTH ] ;
int len = GetWindowTextW ( hwnd , title , MAX_TITLE_LENGTH ) ;
if ( len < = 0 )
{
return TRUE ;
}
if ( wcsncmp ( title , windowTitle . c_str ( ) , len ) = = 0 )
{
ShowWindow ( hwnd , SW_RESTORE ) ;
SetForegroundWindow ( hwnd ) ;
return FALSE ;
}
2019-12-11 22:39:05 +08:00
}
return TRUE ;
} ;
EnumWindows ( callback , 0 ) ;
2019-09-05 00:26:26 +08:00
}
2019-12-11 22:39:05 +08:00
void open_settings_window ( )
{
if ( g_settings_process_id ! = 0 )
{
bring_settings_to_front ( ) ;
}
else
{
std : : thread ( run_settings_window ) . detach ( ) ;
}
2019-09-05 00:26:26 +08:00
}
2020-05-06 05:13:52 +08:00
void close_settings_window ( )
{
if ( g_settings_process_id ! = 0 )
{
HANDLE proc = OpenProcess ( PROCESS_TERMINATE , false , g_settings_process_id ) ;
if ( proc ! = INVALID_HANDLE_VALUE )
{
TerminateProcess ( proc , 0 ) ;
}
}
}