2023-02-23 06:59:49 -08:00
// dllmain.cpp : Defines the entry point for the DLL application.
# include "pch.h"
2024-05-09 10:32:03 -04:00
# include "AdvancedPasteConstants.h"
2023-02-23 06:59:49 -08:00
# include <interface/powertoy_module_interface.h>
# include "trace.h"
# include "Generated Files/resource.h"
# include <common/logger/logger.h>
2024-05-09 10:32:03 -04:00
# include <common/SettingsAPI/settings_helpers.h>
2023-02-23 06:59:49 -08:00
# include <common/SettingsAPI/settings_objects.h>
# include <common/utils/resources.h>
# include <common/interop/shared_constants.h>
# include <common/utils/logger_helper.h>
# include <common/utils/winapi_error.h>
2024-05-09 10:32:03 -04:00
BOOL APIENTRY DllMain ( HMODULE /*hModule*/ , DWORD ul_reason_for_call , LPVOID /*lpReserved*/ )
2023-02-23 06:59:49 -08:00
{
switch ( ul_reason_for_call )
{
case DLL_PROCESS_ATTACH :
Trace : : RegisterProvider ( ) ;
break ;
case DLL_THREAD_ATTACH :
break ;
case DLL_THREAD_DETACH :
break ;
case DLL_PROCESS_DETACH :
Trace : : UnregisterProvider ( ) ;
break ;
}
return TRUE ;
}
namespace
{
const wchar_t JSON_KEY_PROPERTIES [ ] = L " properties " ;
const wchar_t JSON_KEY_WIN [ ] = L " win " ;
const wchar_t JSON_KEY_ALT [ ] = L " alt " ;
const wchar_t JSON_KEY_CTRL [ ] = L " ctrl " ;
const wchar_t JSON_KEY_SHIFT [ ] = L " shift " ;
const wchar_t JSON_KEY_CODE [ ] = L " code " ;
2024-05-09 10:32:03 -04:00
const wchar_t JSON_KEY_PASTE_AS_PLAIN_HOTKEY [ ] = L " paste-as-plain-hotkey " ;
const wchar_t JSON_KEY_ADVANCED_PASTE_UI_HOTKEY [ ] = L " advanced-paste-ui-hotkey " ;
const wchar_t JSON_KEY_PASTE_AS_MARKDOWN_HOTKEY [ ] = L " paste-as-markdown-hotkey " ;
const wchar_t JSON_KEY_PASTE_AS_JSON_HOTKEY [ ] = L " paste-as-json-hotkey " ;
const wchar_t JSON_KEY_SHOW_CUSTOM_PREVIEW [ ] = L " ShowCustomPreview " ;
const wchar_t JSON_KEY_VALUE [ ] = L " value " ;
2023-02-23 06:59:49 -08:00
}
2024-05-09 10:32:03 -04:00
class AdvancedPaste : public PowertoyModuleIface
2023-02-23 06:59:49 -08:00
{
private :
bool m_enabled = false ;
std : : wstring app_name ;
//contains the non localized key of the powertoy
std : : wstring app_key ;
HANDLE m_hProcess ;
// Time to wait for process to close after sending WM_CLOSE signal
static const int MAX_WAIT_MILLISEC = 10000 ;
2024-05-09 10:32:03 -04:00
Hotkey m_paste_as_plain_hotkey = { . win = true , . ctrl = true , . shift = false , . alt = true , . key = ' V ' } ;
Hotkey m_advanced_paste_ui_hotkey = { . win = true , . ctrl = false , . shift = true , . alt = false , . key = ' V ' } ;
Hotkey m_paste_as_markdown_hotkey { } ;
Hotkey m_paste_as_json_hotkey { } ;
2023-02-23 06:59:49 -08:00
2024-05-09 10:32:03 -04:00
bool m_preview_custom_format_output = true ;
2023-02-23 06:59:49 -08:00
2024-05-09 10:32:03 -04:00
// Handle to event used to invoke AdvancedPaste
HANDLE m_hShowUIEvent ;
HANDLE m_hPasteMarkdownEvent ;
HANDLE m_hPasteJsonEvent ;
Hotkey parse_single_hotkey ( const wchar_t * hotkey , const winrt : : Windows : : Data : : Json : : JsonObject & settingsObject )
2023-02-23 06:59:49 -08:00
{
2024-05-09 10:32:03 -04:00
try
2023-02-23 06:59:49 -08:00
{
2024-05-09 10:32:03 -04:00
Hotkey _temp_paste_as_plain ;
auto jsonHotkeyObject = settingsObject . GetNamedObject ( JSON_KEY_PROPERTIES ) . GetNamedObject ( hotkey ) ;
_temp_paste_as_plain . win = jsonHotkeyObject . GetNamedBoolean ( JSON_KEY_WIN ) ;
_temp_paste_as_plain . alt = jsonHotkeyObject . GetNamedBoolean ( JSON_KEY_ALT ) ;
_temp_paste_as_plain . shift = jsonHotkeyObject . GetNamedBoolean ( JSON_KEY_SHIFT ) ;
_temp_paste_as_plain . ctrl = jsonHotkeyObject . GetNamedBoolean ( JSON_KEY_CTRL ) ;
_temp_paste_as_plain . key = static_cast < unsigned char > ( jsonHotkeyObject . GetNamedNumber ( JSON_KEY_CODE ) ) ;
return _temp_paste_as_plain ;
}
catch ( . . . )
{
Logger : : error ( " Failed to initialize AdvancedPaste shortcut from settings. Value will keep unchanged. " ) ;
}
return { } ;
}
bool migrate_data_and_remove_data_file ( Hotkey & old_paste_as_plain_hotkey )
{
const wchar_t OLD_JSON_KEY_ACTIVATION_SHORTCUT [ ] = L " ActivationShortcut " ;
const wchar_t OLD_MODULE_KEY [ ] = L " PastePlain " ;
try
{
const std : : wstring save_file_location = PTSettingsHelper : : get_module_save_file_location ( OLD_MODULE_KEY ) ;
if ( std : : filesystem : : exists ( save_file_location ) )
2023-02-23 06:59:49 -08:00
{
2024-05-09 10:32:03 -04:00
PowerToysSettings : : PowerToyValues settings =
PowerToysSettings : : PowerToyValues : : load_from_settings_file ( OLD_MODULE_KEY ) ;
auto settingsObject = settings . get_raw_json ( ) ;
if ( settingsObject . GetView ( ) . Size ( ) )
{
old_paste_as_plain_hotkey = parse_single_hotkey ( OLD_JSON_KEY_ACTIVATION_SHORTCUT , settingsObject ) ;
std : : filesystem : : remove ( save_file_location ) ;
return true ;
}
2023-02-23 06:59:49 -08:00
}
}
2024-05-09 10:32:03 -04:00
catch ( std : : exception )
2023-02-23 06:59:49 -08:00
{
}
2024-05-09 10:32:03 -04:00
return false ;
}
void parse_hotkeys ( PowerToysSettings : : PowerToyValues & settings )
{
auto settingsObject = settings . get_raw_json ( ) ;
// Migrate Paste As PLain text shortcut
Hotkey old_paste_as_plain_hotkey ;
bool old_data_migrated = migrate_data_and_remove_data_file ( old_paste_as_plain_hotkey ) ;
if ( old_data_migrated )
{
m_paste_as_plain_hotkey = old_paste_as_plain_hotkey ;
// override settings file
json : : JsonObject new_hotkey_value ;
new_hotkey_value . SetNamedValue ( JSON_KEY_WIN , json : : value ( old_paste_as_plain_hotkey . win ) ) ;
new_hotkey_value . SetNamedValue ( JSON_KEY_ALT , json : : value ( old_paste_as_plain_hotkey . alt ) ) ;
new_hotkey_value . SetNamedValue ( JSON_KEY_SHIFT , json : : value ( old_paste_as_plain_hotkey . shift ) ) ;
new_hotkey_value . SetNamedValue ( JSON_KEY_CTRL , json : : value ( old_paste_as_plain_hotkey . ctrl ) ) ;
new_hotkey_value . SetNamedValue ( JSON_KEY_CODE , json : : value ( old_paste_as_plain_hotkey . key ) ) ;
if ( ! settingsObject . HasKey ( JSON_KEY_PROPERTIES ) )
{
settingsObject . SetNamedValue ( JSON_KEY_PROPERTIES , json : : JsonObject { } ) ;
}
settingsObject . GetNamedObject ( JSON_KEY_PROPERTIES ) . SetNamedValue ( JSON_KEY_PASTE_AS_PLAIN_HOTKEY , new_hotkey_value ) ;
json : : JsonObject ui_hotkey ;
ui_hotkey . SetNamedValue ( JSON_KEY_WIN , json : : value ( m_advanced_paste_ui_hotkey . win ) ) ;
ui_hotkey . SetNamedValue ( JSON_KEY_ALT , json : : value ( m_advanced_paste_ui_hotkey . alt ) ) ;
ui_hotkey . SetNamedValue ( JSON_KEY_SHIFT , json : : value ( m_advanced_paste_ui_hotkey . shift ) ) ;
ui_hotkey . SetNamedValue ( JSON_KEY_CTRL , json : : value ( m_advanced_paste_ui_hotkey . ctrl ) ) ;
ui_hotkey . SetNamedValue ( JSON_KEY_CODE , json : : value ( m_advanced_paste_ui_hotkey . key ) ) ;
settingsObject . GetNamedObject ( JSON_KEY_PROPERTIES ) . SetNamedValue ( JSON_KEY_ADVANCED_PASTE_UI_HOTKEY , ui_hotkey ) ;
settings . save_to_settings_file ( ) ;
}
else
2023-02-23 06:59:49 -08:00
{
2024-05-09 10:32:03 -04:00
if ( settingsObject . GetView ( ) . Size ( ) )
{
if ( settingsObject . GetNamedObject ( JSON_KEY_PROPERTIES ) . HasKey ( JSON_KEY_PASTE_AS_PLAIN_HOTKEY ) )
{
m_paste_as_plain_hotkey = parse_single_hotkey ( JSON_KEY_PASTE_AS_PLAIN_HOTKEY , settingsObject ) ;
}
if ( settingsObject . GetNamedObject ( JSON_KEY_PROPERTIES ) . HasKey ( JSON_KEY_ADVANCED_PASTE_UI_HOTKEY ) )
{
m_advanced_paste_ui_hotkey = parse_single_hotkey ( JSON_KEY_ADVANCED_PASTE_UI_HOTKEY , settingsObject ) ;
}
if ( settingsObject . GetNamedObject ( JSON_KEY_PROPERTIES ) . HasKey ( JSON_KEY_PASTE_AS_MARKDOWN_HOTKEY ) )
{
m_paste_as_markdown_hotkey = parse_single_hotkey ( JSON_KEY_PASTE_AS_MARKDOWN_HOTKEY , settingsObject ) ;
}
if ( settingsObject . GetNamedObject ( JSON_KEY_PROPERTIES ) . HasKey ( JSON_KEY_PASTE_AS_JSON_HOTKEY ) )
{
m_paste_as_json_hotkey = parse_single_hotkey ( JSON_KEY_PASTE_AS_JSON_HOTKEY , settingsObject ) ;
}
}
2023-02-23 06:59:49 -08:00
}
}
bool is_process_running ( )
{
return WaitForSingleObject ( m_hProcess , 0 ) = = WAIT_TIMEOUT ;
}
2024-05-09 10:32:03 -04:00
void launch_process ( const std : : wstring & arg = L " " )
2023-02-23 06:59:49 -08:00
{
2024-05-09 10:32:03 -04:00
Logger : : trace ( L " Starting AdvancedPaste process " ) ;
2023-02-23 06:59:49 -08:00
unsigned long powertoys_pid = GetCurrentProcessId ( ) ;
std : : wstring executable_args = L " " ;
executable_args . append ( std : : to_wstring ( powertoys_pid ) ) ;
2024-05-09 10:32:03 -04:00
executable_args + = L " " + arg ;
2023-02-23 06:59:49 -08:00
SHELLEXECUTEINFOW sei { sizeof ( sei ) } ;
sei . fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI } ;
2024-05-09 10:32:03 -04:00
sei . lpFile = L " WinUI3Apps \\ PowerToys.AdvancedPaste.exe " ;
2023-02-23 06:59:49 -08:00
sei . nShow = SW_SHOWNORMAL ;
sei . lpParameters = executable_args . data ( ) ;
if ( ShellExecuteExW ( & sei ) )
{
2024-05-09 10:32:03 -04:00
Logger : : trace ( " Successfully started the Advanced Paste process " ) ;
2023-02-23 06:59:49 -08:00
}
else
{
2024-05-09 10:32:03 -04:00
Logger : : error ( L " AdvancedPaste failed to start. {} " , get_last_error_or_default ( GetLastError ( ) ) ) ;
2023-02-23 06:59:49 -08:00
}
2024-05-09 10:32:03 -04:00
TerminateProcess ( m_hProcess , 1 ) ;
2023-02-23 06:59:49 -08:00
m_hProcess = sei . hProcess ;
}
// Load the settings file.
void init_settings ( )
{
try
{
// Load and parse the settings file for this PowerToy.
PowerToysSettings : : PowerToyValues settings =
PowerToysSettings : : PowerToyValues : : load_from_settings_file ( get_key ( ) ) ;
2024-05-09 10:32:03 -04:00
parse_hotkeys ( settings ) ;
auto settingsObject = settings . get_raw_json ( ) ;
if ( settingsObject . GetView ( ) . Size ( ) & & settingsObject . GetNamedObject ( JSON_KEY_PROPERTIES ) . HasKey ( JSON_KEY_SHOW_CUSTOM_PREVIEW ) )
{
m_preview_custom_format_output = settingsObject . GetNamedObject ( JSON_KEY_PROPERTIES ) . GetNamedObject ( JSON_KEY_SHOW_CUSTOM_PREVIEW ) . GetNamedBoolean ( JSON_KEY_VALUE ) ;
}
2023-02-23 06:59:49 -08:00
}
catch ( std : : exception & )
{
Logger : : warn ( L " An exception occurred while loading the settings file " ) ;
// Error while loading from the settings file. Let default values stay as they are.
}
}
2023-03-20 15:43:10 +01:00
void try_inject_modifier_key_up ( std : : vector < INPUT > & inputs , short modifier )
2023-02-23 06:59:49 -08:00
{
// Most significant bit is set if key is down
if ( ( GetAsyncKeyState ( static_cast < int > ( modifier ) ) & 0x8000 ) ! = 0 )
{
INPUT input_event = { } ;
input_event . type = INPUT_KEYBOARD ;
input_event . ki . wVk = modifier ;
input_event . ki . dwFlags = KEYEVENTF_KEYUP ;
inputs . push_back ( input_event ) ;
}
}
2023-03-03 12:26:10 +01:00
void try_inject_modifier_key_restore ( std : : vector < INPUT > & inputs , short modifier )
{
// Most significant bit is set if key is down
if ( ( GetAsyncKeyState ( static_cast < int > ( modifier ) ) & 0x8000 ) ! = 0 )
{
INPUT input_event = { } ;
input_event . type = INPUT_KEYBOARD ;
input_event . ki . wVk = modifier ;
inputs . push_back ( input_event ) ;
}
}
2023-02-23 06:59:49 -08:00
void try_to_paste_as_plain_text ( )
{
std : : wstring clipboard_text ;
{
// Read clipboard data begin
if ( ! OpenClipboard ( NULL ) )
{
DWORD errorCode = GetLastError ( ) ;
auto errorMessage = get_last_error_message ( errorCode ) ;
Logger : : error ( L " Couldn't open the clipboard to get the text. {} " , errorMessage . has_value ( ) ? errorMessage . value ( ) : L " " ) ;
2024-05-09 10:32:03 -04:00
Trace : : AdvancedPaste_Error ( errorCode , errorMessage . has_value ( ) ? errorMessage . value ( ) : L " " , L " read.OpenClipboard " ) ;
2023-02-23 06:59:49 -08:00
return ;
}
HANDLE h_clipboard_data = GetClipboardData ( CF_UNICODETEXT ) ;
if ( h_clipboard_data = = NULL )
{
DWORD errorCode = GetLastError ( ) ;
auto errorMessage = get_last_error_message ( errorCode ) ;
Logger : : error ( L " Failed to get clipboard data. {} " , errorMessage . has_value ( ) ? errorMessage . value ( ) : L " " ) ;
2024-05-09 10:32:03 -04:00
Trace : : AdvancedPaste_Error ( errorCode , errorMessage . has_value ( ) ? errorMessage . value ( ) : L " " , L " read.GetClipboardData " ) ;
2023-02-23 06:59:49 -08:00
CloseClipboard ( ) ;
return ;
}
2023-03-20 15:43:10 +01:00
wchar_t * pch_data = static_cast < wchar_t * > ( GlobalLock ( h_clipboard_data ) ) ;
2023-02-23 06:59:49 -08:00
2023-03-20 15:43:10 +01:00
if ( NULL = = pch_data )
2023-02-23 06:59:49 -08:00
{
DWORD errorCode = GetLastError ( ) ;
auto errorMessage = get_last_error_message ( errorCode ) ;
Logger : : error ( L " Couldn't lock the buffer to get the unformatted text from the clipboard. {} " , errorMessage . has_value ( ) ? errorMessage . value ( ) : L " " ) ;
2024-05-09 10:32:03 -04:00
Trace : : AdvancedPaste_Error ( errorCode , errorMessage . has_value ( ) ? errorMessage . value ( ) : L " " , L " read.GlobalLock " ) ;
2023-02-23 06:59:49 -08:00
CloseClipboard ( ) ;
return ;
}
clipboard_text = pch_data ;
GlobalUnlock ( h_clipboard_data ) ;
CloseClipboard ( ) ;
// Read clipboard data end
}
{
// Copy text to clipboard begin
UINT no_clipboard_history_or_roaming_format = 0 ;
// Get the format identifier for not adding the data to the clipboard history or roaming.
2023-03-20 15:43:10 +01:00
// https://learn.microsoft.com/windows/win32/dataxchg/clipboard-formats#cloud-clipboard-and-clipboard-history-formats
2023-02-23 06:59:49 -08:00
if ( 0 = = ( no_clipboard_history_or_roaming_format = RegisterClipboardFormat ( L " ExcludeClipboardContentFromMonitorProcessing " ) ) )
{
DWORD errorCode = GetLastError ( ) ;
auto errorMessage = get_last_error_message ( errorCode ) ;
Logger : : error ( L " Couldn't get the clipboard data format type that would allow excluding the data from the clipboard history / roaming. {} " , errorMessage . has_value ( ) ? errorMessage . value ( ) : L " " ) ;
2024-05-09 10:32:03 -04:00
Trace : : AdvancedPaste_Error ( errorCode , errorMessage . has_value ( ) ? errorMessage . value ( ) : L " " , L " write.RegisterClipboardFormat " ) ;
2023-02-23 06:59:49 -08:00
return ;
}
if ( ! OpenClipboard ( NULL ) )
{
DWORD errorCode = GetLastError ( ) ;
auto errorMessage = get_last_error_message ( errorCode ) ;
Logger : : error ( L " Couldn't open the clipboard to copy the unformatted text. {} " , errorMessage . has_value ( ) ? errorMessage . value ( ) : L " " ) ;
2024-05-09 10:32:03 -04:00
Trace : : AdvancedPaste_Error ( errorCode , errorMessage . has_value ( ) ? errorMessage . value ( ) : L " " , L " write.OpenClipboard " ) ;
2023-02-23 06:59:49 -08:00
return ;
}
HGLOBAL h_clipboard_data ;
if ( NULL = = ( h_clipboard_data = GlobalAlloc ( GMEM_MOVEABLE , ( clipboard_text . length ( ) + 1 ) * sizeof ( wchar_t ) ) ) )
{
DWORD errorCode = GetLastError ( ) ;
auto errorMessage = get_last_error_message ( errorCode ) ;
Logger : : error ( L " Couldn't allocate a buffer for the unformatted text. {} " , errorMessage . has_value ( ) ? errorMessage . value ( ) : L " " ) ;
2024-05-09 10:32:03 -04:00
Trace : : AdvancedPaste_Error ( errorCode , errorMessage . has_value ( ) ? errorMessage . value ( ) : L " " , L " write.GlobalAlloc " ) ;
2023-02-23 06:59:49 -08:00
CloseClipboard ( ) ;
return ;
}
wchar_t * pch_data = static_cast < wchar_t * > ( GlobalLock ( h_clipboard_data ) ) ;
if ( NULL = = pch_data )
{
DWORD errorCode = GetLastError ( ) ;
auto errorMessage = get_last_error_message ( errorCode ) ;
Logger : : error ( L " Couldn't lock the buffer to send the unformatted text to the clipboard. {} " , errorMessage . has_value ( ) ? errorMessage . value ( ) : L " " ) ;
GlobalFree ( h_clipboard_data ) ;
CloseClipboard ( ) ;
2024-05-09 10:32:03 -04:00
Trace : : AdvancedPaste_Error ( errorCode , errorMessage . has_value ( ) ? errorMessage . value ( ) : L " " , L " write.GlobalLock " ) ;
2023-02-23 06:59:49 -08:00
return ;
}
wcscpy_s ( pch_data , clipboard_text . length ( ) + 1 , clipboard_text . c_str ( ) ) ;
EmptyClipboard ( ) ;
if ( NULL = = SetClipboardData ( CF_UNICODETEXT , h_clipboard_data ) )
{
DWORD errorCode = GetLastError ( ) ;
auto errorMessage = get_last_error_message ( errorCode ) ;
Logger : : error ( L " Couldn't set the clipboard data to the unformatted text. {} " , errorMessage . has_value ( ) ? errorMessage . value ( ) : L " " ) ;
GlobalUnlock ( h_clipboard_data ) ;
GlobalFree ( h_clipboard_data ) ;
CloseClipboard ( ) ;
2024-05-09 10:32:03 -04:00
Trace : : AdvancedPaste_Error ( errorCode , errorMessage . has_value ( ) ? errorMessage . value ( ) : L " " , L " write.SetClipboardData " ) ;
2023-02-23 06:59:49 -08:00
return ;
}
// Don't show in history or allow data roaming.
SetClipboardData ( no_clipboard_history_or_roaming_format , 0 ) ;
CloseClipboard ( ) ;
// Copy text to clipboard end
}
{
// Clear kb state and send Ctrl+V begin
2023-03-20 15:43:10 +01:00
2023-02-23 06:59:49 -08:00
// we can assume that the last pressed key is...
// (1) not a modifier key and
// (2) marked as handled (so it never gets a key down input event).
// So, let's check which modifiers were pressed,
// and, if they were, inject a key up event for each of them
std : : vector < INPUT > inputs ;
try_inject_modifier_key_up ( inputs , VK_LCONTROL ) ;
try_inject_modifier_key_up ( inputs , VK_RCONTROL ) ;
try_inject_modifier_key_up ( inputs , VK_LWIN ) ;
try_inject_modifier_key_up ( inputs , VK_RWIN ) ;
try_inject_modifier_key_up ( inputs , VK_LSHIFT ) ;
try_inject_modifier_key_up ( inputs , VK_RSHIFT ) ;
try_inject_modifier_key_up ( inputs , VK_LMENU ) ;
try_inject_modifier_key_up ( inputs , VK_RMENU ) ;
// send Ctrl+V (key downs and key ups)
{
INPUT input_event = { } ;
input_event . type = INPUT_KEYBOARD ;
input_event . ki . wVk = VK_CONTROL ;
inputs . push_back ( input_event ) ;
}
{
INPUT input_event = { } ;
input_event . type = INPUT_KEYBOARD ;
input_event . ki . wVk = 0x56 ; // V
2023-03-03 09:52:49 +00:00
// Avoid triggering detection by the centralized keyboard hook. Allows using Control+V as activation.
input_event . ki . dwExtraInfo = CENTRALIZED_KEYBOARD_HOOK_DONT_TRIGGER_FLAG ;
2023-02-23 06:59:49 -08:00
inputs . push_back ( input_event ) ;
}
{
INPUT input_event = { } ;
input_event . type = INPUT_KEYBOARD ;
input_event . ki . wVk = 0x56 ; // V
input_event . ki . dwFlags = KEYEVENTF_KEYUP ;
2023-03-03 09:52:49 +00:00
// Avoid triggering detection by the centralized keyboard hook. Allows using Control+V as activation.
input_event . ki . dwExtraInfo = CENTRALIZED_KEYBOARD_HOOK_DONT_TRIGGER_FLAG ;
2023-02-23 06:59:49 -08:00
inputs . push_back ( input_event ) ;
}
{
INPUT input_event = { } ;
input_event . type = INPUT_KEYBOARD ;
input_event . ki . wVk = VK_CONTROL ;
input_event . ki . dwFlags = KEYEVENTF_KEYUP ;
inputs . push_back ( input_event ) ;
}
2023-03-03 12:26:10 +01:00
try_inject_modifier_key_restore ( inputs , VK_LCONTROL ) ;
try_inject_modifier_key_restore ( inputs , VK_RCONTROL ) ;
try_inject_modifier_key_restore ( inputs , VK_LWIN ) ;
try_inject_modifier_key_restore ( inputs , VK_RWIN ) ;
try_inject_modifier_key_restore ( inputs , VK_LSHIFT ) ;
try_inject_modifier_key_restore ( inputs , VK_RSHIFT ) ;
try_inject_modifier_key_restore ( inputs , VK_LMENU ) ;
try_inject_modifier_key_restore ( inputs , VK_RMENU ) ;
2024-02-20 16:01:40 +01:00
// After restoring the modifier keys send a dummy key to prevent Start Menu from activating
INPUT dummyEvent = { } ;
dummyEvent . type = INPUT_KEYBOARD ;
dummyEvent . ki . wVk = 0xFF ;
dummyEvent . ki . dwFlags = KEYEVENTF_KEYUP ;
inputs . push_back ( dummyEvent ) ;
2023-02-23 06:59:49 -08:00
auto uSent = SendInput ( static_cast < UINT > ( inputs . size ( ) ) , inputs . data ( ) , sizeof ( INPUT ) ) ;
if ( uSent ! = inputs . size ( ) )
{
DWORD errorCode = GetLastError ( ) ;
auto errorMessage = get_last_error_message ( errorCode ) ;
Logger : : error ( L " SendInput failed. Expected to send {} inputs and sent only {}. {} " , inputs . size ( ) , uSent , errorMessage . has_value ( ) ? errorMessage . value ( ) : L " " ) ;
2024-05-09 10:32:03 -04:00
Trace : : AdvancedPaste_Error ( errorCode , errorMessage . has_value ( ) ? errorMessage . value ( ) : L " " , L " input.SendInput " ) ;
2023-02-23 06:59:49 -08:00
return ;
}
// Clear kb state and send Ctrl+V end
}
2024-05-09 10:32:03 -04:00
}
void bring_process_to_front ( )
{
auto enum_windows = [ ] ( HWND hwnd , LPARAM param ) - > BOOL {
HANDLE process_handle = reinterpret_cast < HANDLE > ( param ) ;
DWORD window_process_id = 0 ;
GetWindowThreadProcessId ( hwnd , & window_process_id ) ;
if ( GetProcessId ( process_handle ) = = window_process_id )
{
SetForegroundWindow ( hwnd ) ;
return FALSE ;
}
return TRUE ;
} ;
EnumWindows ( enum_windows , ( LPARAM ) m_hProcess ) ;
2023-02-23 06:59:49 -08:00
}
public :
2024-05-09 10:32:03 -04:00
AdvancedPaste ( )
2023-02-23 06:59:49 -08:00
{
2024-05-09 10:32:03 -04:00
app_name = GET_RESOURCE_STRING ( IDS_ADVANCED_PASTE_NAME ) ;
app_key = AdvancedPasteConstants : : ModuleKey ;
LoggerHelpers : : init_logger ( app_key , L " ModuleInterface " , " AdvancedPaste " ) ;
m_hShowUIEvent = CreateDefaultEvent ( CommonSharedConstants : : SHOW_ADVANCED_PASTE_SHARED_EVENT ) ;
m_hPasteMarkdownEvent = CreateDefaultEvent ( CommonSharedConstants : : ADVANCED_PASTE_MARKDOWN_EVENT ) ;
m_hPasteJsonEvent = CreateDefaultEvent ( CommonSharedConstants : : ADVANCED_PASTE_JSON_EVENT ) ;
2023-02-23 06:59:49 -08:00
init_settings ( ) ;
}
2024-05-09 10:32:03 -04:00
~ AdvancedPaste ( )
2023-02-23 06:59:49 -08:00
{
if ( m_enabled )
{
}
m_enabled = false ;
}
// Destroy the powertoy and free memory
virtual void destroy ( ) override
{
2024-05-09 10:32:03 -04:00
Logger : : trace ( " AdvancedPaste::destroy() " ) ;
2023-02-23 06:59:49 -08:00
delete this ;
}
// Return the localized display name of the powertoy
virtual const wchar_t * get_name ( ) override
{
return app_name . c_str ( ) ;
}
// Return the non localized key of the powertoy, this will be cached by the runner
virtual const wchar_t * get_key ( ) override
{
return app_key . c_str ( ) ;
}
// Return the configured status for the gpo policy for the module
virtual powertoys_gpo : : gpo_rule_configured_t gpo_policy_enabled_configuration ( ) override
{
2024-05-09 10:32:03 -04:00
return powertoys_gpo : : getConfiguredAdvancedPasteEnabledValue ( ) ;
2023-02-23 06:59:49 -08:00
}
virtual bool get_config ( wchar_t * buffer , int * buffer_size ) override
{
HINSTANCE hinstance = reinterpret_cast < HINSTANCE > ( & __ImageBase ) ;
// Create a Settings object.
PowerToysSettings : : Settings settings ( hinstance , get_name ( ) ) ;
2024-05-09 10:32:03 -04:00
settings . set_description ( GET_RESOURCE_STRING ( IDS_ADVANCED_PASTE_SETTINGS_DESC ) ) ;
2023-02-23 06:59:49 -08:00
2024-05-09 10:32:03 -04:00
settings . set_overview_link ( L " https://aka.ms/PowerToysOverview_AdvancedPaste " ) ;
2023-02-23 06:59:49 -08:00
return settings . serialize_to_buffer ( buffer , buffer_size ) ;
}
virtual void call_custom_action ( const wchar_t * /*action*/ ) override
{
}
virtual void set_config ( const wchar_t * config ) override
{
try
{
// Parse the input JSON string.
PowerToysSettings : : PowerToyValues values =
PowerToysSettings : : PowerToyValues : : from_json_string ( config , get_key ( ) ) ;
2024-05-09 10:32:03 -04:00
parse_hotkeys ( values ) ;
auto settingsObject = values . get_raw_json ( ) ;
if ( settingsObject . GetView ( ) . Size ( ) & & settingsObject . GetNamedObject ( JSON_KEY_PROPERTIES ) . HasKey ( JSON_KEY_SHOW_CUSTOM_PREVIEW ) )
{
m_preview_custom_format_output = settingsObject . GetNamedObject ( JSON_KEY_PROPERTIES ) . GetNamedObject ( JSON_KEY_SHOW_CUSTOM_PREVIEW ) . GetNamedBoolean ( JSON_KEY_VALUE ) ;
}
// order of args matter
Trace : : AdvancedPaste_SettingsTelemetry ( m_paste_as_plain_hotkey ,
m_advanced_paste_ui_hotkey ,
m_paste_as_markdown_hotkey ,
m_paste_as_json_hotkey ,
m_preview_custom_format_output ) ;
2023-02-23 06:59:49 -08:00
// If you don't need to do any custom processing of the settings, proceed
// to persists the values calling:
values . save_to_settings_file ( ) ;
// Otherwise call a custom function to process the settings before saving them to disk:
// save_settings();
}
catch ( std : : exception & )
{
// Improper JSON.
}
}
virtual void enable ( )
{
2024-05-09 10:32:03 -04:00
Logger : : trace ( " AdvancedPaste::enable() " ) ;
Trace : : AdvancedPaste_Enable ( true ) ;
ResetEvent ( m_hShowUIEvent ) ;
ResetEvent ( m_hPasteMarkdownEvent ) ;
ResetEvent ( m_hPasteJsonEvent ) ;
2023-02-23 06:59:49 -08:00
m_enabled = true ;
2024-05-09 10:32:03 -04:00
launch_process ( ) ;
2023-02-23 06:59:49 -08:00
} ;
virtual void disable ( )
{
2024-05-09 10:32:03 -04:00
Logger : : trace ( " AdvancedPaste::disable() " ) ;
if ( m_enabled )
{
ResetEvent ( m_hShowUIEvent ) ;
ResetEvent ( m_hPasteMarkdownEvent ) ;
ResetEvent ( m_hPasteJsonEvent ) ;
TerminateProcess ( m_hProcess , 1 ) ;
Trace : : AdvancedPaste_Enable ( false ) ;
CloseHandle ( m_hProcess ) ;
m_hProcess = 0 ;
}
2023-02-23 06:59:49 -08:00
m_enabled = false ;
}
2024-05-09 10:32:03 -04:00
virtual bool on_hotkey ( size_t hotkeyId ) override
2023-02-23 06:59:49 -08:00
{
2024-05-09 10:32:03 -04:00
Logger : : trace ( L " AdvancedPaste hotkey pressed " ) ;
2023-02-23 06:59:49 -08:00
if ( m_enabled )
{
2024-05-09 10:32:03 -04:00
if ( ! is_process_running ( ) )
{
Logger : : trace ( L " Launching new process " ) ;
launch_process ( ) ;
Trace : : AdvancedPaste_Invoked ( L " AdvancedPasteUI " ) ;
}
// hotkeyId in same order as set by get_hotkeys
if ( hotkeyId = = 0 ) { // m_paste_as_plain_hotkey
Logger : : trace ( L " Paste as plain text hotkey pressed " ) ;
2023-02-23 06:59:49 -08:00
2024-05-09 10:32:03 -04:00
std : : thread ( [ = ] ( ) {
// hotkey work should be kept to a minimum, or Windows might deregister our low level keyboard hook.
// Move work to another thread.
try_to_paste_as_plain_text ( ) ;
} ) . detach ( ) ;
2023-02-23 06:59:49 -08:00
2024-05-09 10:32:03 -04:00
Trace : : AdvancedPaste_Invoked ( L " PastePlainTextDirect " ) ;
return true ;
}
if ( hotkeyId = = 1 ) { // m_advanced_paste_ui_hotkey
Logger : : trace ( L " Setting start up event " ) ;
bring_process_to_front ( ) ;
SetEvent ( m_hShowUIEvent ) ;
return true ;
}
if ( hotkeyId = = 2 ) { // m_paste_as_markdown_hotkey
Logger : : trace ( L " Starting paste as markdown directly " ) ;
SetEvent ( m_hPasteMarkdownEvent ) ;
return true ;
}
if ( hotkeyId = = 3 ) { // m_paste_as_json_hotkey
Logger : : trace ( L " Starting paste as json directly " ) ;
SetEvent ( m_hPasteJsonEvent ) ;
return true ;
}
2023-02-23 06:59:49 -08:00
}
return false ;
}
virtual size_t get_hotkeys ( Hotkey * hotkeys , size_t buffer_size ) override
{
2024-05-09 10:32:03 -04:00
if ( hotkeys & & buffer_size > = 4 )
2023-02-23 06:59:49 -08:00
{
2024-05-09 10:32:03 -04:00
hotkeys [ 0 ] = m_paste_as_plain_hotkey ;
hotkeys [ 1 ] = m_advanced_paste_ui_hotkey ;
hotkeys [ 2 ] = m_paste_as_markdown_hotkey ;
hotkeys [ 3 ] = m_paste_as_json_hotkey ;
2023-02-23 06:59:49 -08:00
}
2024-05-09 10:32:03 -04:00
return 4 ;
2023-02-23 06:59:49 -08:00
}
virtual bool is_enabled ( ) override
{
return m_enabled ;
}
} ;
extern " C " __declspec ( dllexport ) PowertoyModuleIface * __cdecl powertoy_create ( )
{
2024-05-09 10:32:03 -04:00
return new AdvancedPaste ( ) ;
2023-02-23 06:59:49 -08:00
}