[Refactor]Port C++/CX to C++/WinRT (#34198)

## Summary of the Pull Request
Removes all C++/CX code, replacing it with C++/WinRT.

## Detailed Description of the Pull Request / Additional comments
Removes all C++/CX code.
Renames interop namespaces to be better consumed by CsWinRT.
Standardizes all projects on net8.0-windows10.0.20348.0, which is a
requirement for C++/WinRT usage.
FileLocksmithLibInterop brought to stdcpplatest and static analysis
errors were corrected.
Removed now unneeded string conversion code from
FileLocksmithLibInterop.
Changed interop KeyboardHook to use a single hook across all instances.
Required because on C++/WinRT we don't have the .NET runtime to bind a
object instance to a delegate and be able to pass it to a C function
pointer argument (still no idea why this worked correctly on C++/CX to
be honest). This change actually makes us create less low level keyboard
hooks.
Changed some code that depended on arrays since WinRT/C++ returns null
instead of an empty array through the interface.

## Validation Steps Performed
Built and tested runtime.
This commit is contained in:
Jaime Bernardo
2024-08-08 15:26:43 +01:00
committed by GitHub
parent b16e82c837
commit fb5ed13386
136 changed files with 1511 additions and 1119 deletions

View File

@@ -15,6 +15,9 @@
<ProjectReference Include="..\ManagedTelemetry\Telemetry\ManagedTelemetry.csproj" />
<ProjectReference Include="..\..\settings-ui\Settings.UI.Library\Settings.UI.Library.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Windows.CsWinRT" />
</ItemGroup>
<!-- Experimentation is live, forcing inclusion -->
<ItemGroup Condition="'$(IsExperimentationLive)'!=''">

View File

@@ -2,13 +2,14 @@
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<TargetFramework>net8.0-windows10.0.20348.0</TargetFramework>
<RuntimeIdentifiers>win-x64;win-arm64</RuntimeIdentifiers>
<UseWPF>true</UseWPF>
<AssemblyName>PowerToys.Common.UI</AssemblyName>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Windows.CsWinRT" />
<PackageReference Include="ControlzEx" />
</ItemGroup>

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<TargetFramework>net8.0-windows10.0.20348.0</TargetFramework>
<RuntimeIdentifiers>win-x64;win-arm64</RuntimeIdentifiers>
<Description>PowerToys FilePreviewCommon</Description>
<AssemblyName>PowerToys.FilePreviewCommon</AssemblyName>

View File

@@ -7,7 +7,7 @@ using System.Diagnostics;
using System.Globalization;
using System.IO.Abstractions;
using System.Reflection;
using interop;
using PowerToys.Interop;
namespace ManagedCommon
{

View File

@@ -1,13 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<TargetFramework>net8.0-windows10.0.20348.0</TargetFramework>
<RuntimeIdentifiers>win-x64;win-arm64</RuntimeIdentifiers>
<Description>PowerToys ManagedCommon</Description>
<AssemblyName>PowerToys.ManagedCommon</AssemblyName>
</PropertyGroup>
<!-- See https://learn.microsoft.com/windows/apps/develop/platform/csharp-winrt/net-projection-from-cppwinrt-component for more info -->
<PropertyGroup>
<CsWinRTIncludes>PowerToys.Interop</CsWinRTIncludes>
<CsWinRTGeneratedFilesDir>$(OutDir)</CsWinRTGeneratedFilesDir>
<ErrorOnDuplicatePublishOutputFiles>false</ErrorOnDuplicatePublishOutputFiles>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Windows.CsWinRT" />
<PackageReference Include="System.IO.Abstractions" />
<PackageReference Include="System.Management" />
</ItemGroup>

View File

@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<TargetFramework>net8.0-windows10.0.20348.0</TargetFramework>
<RuntimeIdentifiers>win-x64;win-arm64</RuntimeIdentifiers>
<Description>PowerToys Telemetry</Description>
<AssemblyName>PowerToys.ManagedTelemetry</AssemblyName>

View File

@@ -0,0 +1,39 @@
#include "pch.h"
#include "CommonManaged.h"
#include "CommonManaged.g.cpp"
#include <common/version/version.h>
#include "../../modules/videoconference/VideoConferenceShared/MicrophoneDevice.h"
#include "../../modules/videoconference/VideoConferenceShared/VideoCaptureDeviceList.h"
namespace winrt::PowerToys::Interop::implementation
{
hstring CommonManaged::GetProductVersion()
{
return hstring{ get_product_version() };
}
winrt::Windows::Foundation::Collections::IVector<hstring> CommonManaged::GetAllActiveMicrophoneDeviceNames()
{
auto names = std::vector<winrt::hstring>();
for (const auto& device : MicrophoneDevice::getAllActive())
{
names.push_back(device->name().data());
}
return winrt::multi_threaded_vector(std::move(names));
}
winrt::Windows::Foundation::Collections::IVector<hstring> CommonManaged::GetAllVideoCaptureDeviceNames()
{
auto names = std::vector<winrt::hstring>();
VideoCaptureDeviceList vcdl;
vcdl.EnumerateDevices();
for (UINT32 i = 0; i < vcdl.Count(); ++i)
{
auto name = vcdl.GetDeviceName(i).data();
if (name != L"PowerToys VideoConference Mute")
{
names.push_back(name);
}
}
return winrt::multi_threaded_vector(std::move(names));
}
}

View File

@@ -0,0 +1,20 @@
#pragma once
#include "CommonManaged.g.h"
namespace winrt::PowerToys::Interop::implementation
{
struct CommonManaged : CommonManagedT<CommonManaged>
{
CommonManaged() = default;
static hstring GetProductVersion();
static winrt::Windows::Foundation::Collections::IVector<hstring> GetAllActiveMicrophoneDeviceNames();
static winrt::Windows::Foundation::Collections::IVector<hstring> GetAllVideoCaptureDeviceNames();
};
}
namespace winrt::PowerToys::Interop::factory_implementation
{
struct CommonManaged : CommonManagedT<CommonManaged, implementation::CommonManaged>
{
};
}

View File

@@ -0,0 +1,11 @@
namespace PowerToys
{
namespace Interop
{
[default_interface] static runtimeclass CommonManaged {
static String GetProductVersion();
static Windows.Foundation.Collections.IVector<String> GetAllActiveMicrophoneDeviceNames();
static Windows.Foundation.Collections.IVector<String> GetAllVideoCaptureDeviceNames();
}
}
}

View File

@@ -0,0 +1,146 @@
#include "pch.h"
#include "Constants.h"
#include "Constants.g.cpp"
#include "shared_constants.h"
#include <ShlObj.h>
namespace winrt::PowerToys::Interop::implementation
{
uint32_t Constants::VK_WIN_BOTH()
{
return CommonSharedConstants::VK_WIN_BOTH;
}
hstring Constants::AppDataPath()
{
PWSTR local_app_path;
winrt::check_hresult(SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, NULL, &local_app_path));
winrt::hstring result{ local_app_path };
CoTaskMemFree(local_app_path);
result = result + L"\\" + CommonSharedConstants::APPDATA_PATH;
return result;
}
hstring Constants::PowerLauncherSharedEvent()
{
return CommonSharedConstants::POWER_LAUNCHER_SHARED_EVENT;
}
hstring Constants::PowerLauncherCentralizedHookSharedEvent()
{
return CommonSharedConstants::POWER_LAUNCHER_CENTRALIZED_HOOK_SHARED_EVENT;
}
hstring Constants::RunSendSettingsTelemetryEvent()
{
return CommonSharedConstants::RUN_SEND_SETTINGS_TELEMETRY_EVENT;
}
hstring Constants::RunExitEvent()
{
return CommonSharedConstants::RUN_EXIT_EVENT;
}
hstring Constants::FZEExitEvent()
{
return CommonSharedConstants::FZE_EXIT_EVENT;
}
hstring Constants::FZEToggleEvent()
{
return CommonSharedConstants::FANCY_ZONES_EDITOR_TOGGLE_EVENT;
}
hstring Constants::ColorPickerSendSettingsTelemetryEvent()
{
return CommonSharedConstants::COLOR_PICKER_SEND_SETTINGS_TELEMETRY_EVENT;
}
hstring Constants::ShowColorPickerSharedEvent()
{
return CommonSharedConstants::SHOW_COLOR_PICKER_SHARED_EVENT;
}
hstring Constants::ShowAdvancedPasteSharedEvent()
{
return CommonSharedConstants::SHOW_ADVANCED_PASTE_SHARED_EVENT;
}
hstring Constants::AdvancedPasteMarkdownEvent()
{
return CommonSharedConstants::ADVANCED_PASTE_MARKDOWN_EVENT;
}
hstring Constants::AdvancedPasteJsonEvent()
{
return CommonSharedConstants::ADVANCED_PASTE_JSON_EVENT;
}
hstring Constants::ShowPowerOCRSharedEvent()
{
return CommonSharedConstants::SHOW_POWEROCR_SHARED_EVENT;
}
hstring Constants::MouseJumpShowPreviewEvent()
{
return CommonSharedConstants::MOUSE_JUMP_SHOW_PREVIEW_EVENT;
}
hstring Constants::AwakeExitEvent()
{
return CommonSharedConstants::AWAKE_EXIT_EVENT;
}
hstring Constants::ShowPeekEvent()
{
return CommonSharedConstants::SHOW_PEEK_SHARED_EVENT;
}
hstring Constants::PowerAccentExitEvent()
{
return CommonSharedConstants::POWERACCENT_EXIT_EVENT;
}
hstring Constants::ShortcutGuideTriggerEvent()
{
return CommonSharedConstants::SHORTCUT_GUIDE_TRIGGER_EVENT;
}
hstring Constants::RegistryPreviewTriggerEvent()
{
return CommonSharedConstants::REGISTRY_PREVIEW_TRIGGER_EVENT;
}
hstring Constants::MeasureToolTriggerEvent()
{
return CommonSharedConstants::MEASURE_TOOL_TRIGGER_EVENT;
}
hstring Constants::GcodePreviewResizeEvent()
{
return CommonSharedConstants::GCODE_PREVIEW_RESIZE_EVENT;
}
hstring Constants::QoiPreviewResizeEvent()
{
return CommonSharedConstants::QOI_PREVIEW_RESIZE_EVENT;
}
hstring Constants::DevFilesPreviewResizeEvent()
{
return CommonSharedConstants::DEV_FILES_PREVIEW_RESIZE_EVENT;
}
hstring Constants::MarkdownPreviewResizeEvent()
{
return CommonSharedConstants::MARKDOWN_PREVIEW_RESIZE_EVENT;
}
hstring Constants::PdfPreviewResizeEvent()
{
return CommonSharedConstants::PDF_PREVIEW_RESIZE_EVENT;
}
hstring Constants::SvgPreviewResizeEvent()
{
return CommonSharedConstants::SVG_PREVIEW_RESIZE_EVENT;
}
hstring Constants::ShowHostsSharedEvent()
{
return CommonSharedConstants::SHOW_HOSTS_EVENT;
}
hstring Constants::ShowHostsAdminSharedEvent()
{
return CommonSharedConstants::SHOW_HOSTS_ADMIN_EVENT;
}
hstring Constants::CropAndLockThumbnailEvent()
{
return CommonSharedConstants::CROP_AND_LOCK_THUMBNAIL_EVENT;
}
hstring Constants::CropAndLockReparentEvent()
{
return CommonSharedConstants::CROP_AND_LOCK_REPARENT_EVENT;
}
hstring Constants::ShowEnvironmentVariablesSharedEvent()
{
return CommonSharedConstants::SHOW_ENVIRONMENT_VARIABLES_EVENT;
}
hstring Constants::ShowEnvironmentVariablesAdminSharedEvent()
{
return CommonSharedConstants::SHOW_ENVIRONMENT_VARIABLES_ADMIN_EVENT;
}
}

View File

@@ -0,0 +1,50 @@
#pragma once
#include "Constants.g.h"
namespace winrt::PowerToys::Interop::implementation
{
struct Constants : ConstantsT<Constants>
{
Constants() = default;
static uint32_t VK_WIN_BOTH();
static hstring AppDataPath();
static hstring PowerLauncherSharedEvent();
static hstring PowerLauncherCentralizedHookSharedEvent();
static hstring RunSendSettingsTelemetryEvent();
static hstring RunExitEvent();
static hstring FZEExitEvent();
static hstring FZEToggleEvent();
static hstring ColorPickerSendSettingsTelemetryEvent();
static hstring ShowColorPickerSharedEvent();
static hstring ShowAdvancedPasteSharedEvent();
static hstring AdvancedPasteMarkdownEvent();
static hstring AdvancedPasteJsonEvent();
static hstring ShowPowerOCRSharedEvent();
static hstring MouseJumpShowPreviewEvent();
static hstring AwakeExitEvent();
static hstring ShowPeekEvent();
static hstring PowerAccentExitEvent();
static hstring ShortcutGuideTriggerEvent();
static hstring RegistryPreviewTriggerEvent();
static hstring MeasureToolTriggerEvent();
static hstring GcodePreviewResizeEvent();
static hstring QoiPreviewResizeEvent();
static hstring DevFilesPreviewResizeEvent();
static hstring MarkdownPreviewResizeEvent();
static hstring PdfPreviewResizeEvent();
static hstring SvgPreviewResizeEvent();
static hstring ShowHostsSharedEvent();
static hstring ShowHostsAdminSharedEvent();
static hstring CropAndLockThumbnailEvent();
static hstring CropAndLockReparentEvent();
static hstring ShowEnvironmentVariablesSharedEvent();
static hstring ShowEnvironmentVariablesAdminSharedEvent();
};
}
namespace winrt::PowerToys::Interop::factory_implementation
{
struct Constants : ConstantsT<Constants, implementation::Constants>
{
};
}

View File

@@ -0,0 +1,41 @@
namespace PowerToys
{
namespace Interop
{
[default_interface] static runtimeclass Constants {
static UInt32 VK_WIN_BOTH {get; };
static String AppDataPath();
static String PowerLauncherSharedEvent();
static String PowerLauncherCentralizedHookSharedEvent();
static String RunSendSettingsTelemetryEvent();
static String RunExitEvent();
static String FZEExitEvent();
static String FZEToggleEvent();
static String ColorPickerSendSettingsTelemetryEvent();
static String ShowColorPickerSharedEvent();
static String ShowAdvancedPasteSharedEvent();
static String AdvancedPasteMarkdownEvent();
static String AdvancedPasteJsonEvent();
static String ShowPowerOCRSharedEvent();
static String MouseJumpShowPreviewEvent();
static String AwakeExitEvent();
static String ShowPeekEvent();
static String PowerAccentExitEvent();
static String ShortcutGuideTriggerEvent();
static String RegistryPreviewTriggerEvent();
static String MeasureToolTriggerEvent();
static String GcodePreviewResizeEvent();
static String QoiPreviewResizeEvent();
static String DevFilesPreviewResizeEvent();
static String MarkdownPreviewResizeEvent();
static String PdfPreviewResizeEvent();
static String SvgPreviewResizeEvent();
static String ShowHostsSharedEvent();
static String ShowHostsAdminSharedEvent();
static String CropAndLockThumbnailEvent();
static String CropAndLockReparentEvent();
static String ShowEnvironmentVariablesSharedEvent();
static String ShowEnvironmentVariablesAdminSharedEvent();
}
}
}

View File

@@ -1,94 +1,89 @@
#include "pch.h"
#include "HotkeyManager.h"
#include "HotkeyManager.g.cpp"
using namespace interop;
HotkeyManager::HotkeyManager()
namespace winrt::PowerToys::Interop::implementation
{
keyboardEventCallback = gcnew KeyboardEventCallback(this, &HotkeyManager::KeyboardEventProc);
isActiveCallback = gcnew IsActiveCallback(this, &HotkeyManager::IsActiveProc);
filterKeyboardCallback = gcnew FilterKeyboardEvent(this, &HotkeyManager::FilterKeyboardProc);
keyboardHook = gcnew KeyboardHook(
keyboardEventCallback,
isActiveCallback,
filterKeyboardCallback);
hotkeys = gcnew Dictionary<HOTKEY_HANDLE, HotkeyCallback ^>();
pressedKeys = gcnew Hotkey();
keyboardHook->Start();
}
HotkeyManager::~HotkeyManager()
{
delete keyboardHook;
}
// When all Shortcut keys are pressed, fire the HotkeyCallback event.
void HotkeyManager::KeyboardEventProc(KeyboardEvent ^ /*ev*/)
{
// pressedKeys always stores the latest keyboard state
auto pressedKeysHandle = GetHotkeyHandle(pressedKeys);
if (hotkeys->ContainsKey(pressedKeysHandle))
HotkeyManager::HotkeyManager()
{
hotkeys[pressedKeysHandle]->Invoke();
// After invoking the hotkey send a dummy key to prevent Start Menu from activating
INPUT dummyEvent[1] = {};
dummyEvent[0].type = INPUT_KEYBOARD;
dummyEvent[0].ki.wVk = 0xFF;
dummyEvent[0].ki.dwFlags = KEYEVENTF_KEYUP;
SendInput(1, dummyEvent, sizeof(INPUT));
keyboardEventCallback = KeyboardEventCallback{ this, &HotkeyManager::KeyboardEventProc };
isActiveCallback = IsActiveCallback{ this, &HotkeyManager::IsActiveProc };
filterKeyboardCallback = FilterKeyboardEvent{ this, &HotkeyManager::FilterKeyboardProc };
keyboardHook = KeyboardHook{ keyboardEventCallback, isActiveCallback, filterKeyboardCallback };
keyboardHook.Start();
}
}
// Hotkeys are intended to be global, therefore they are always active no matter the
// context in which the keypress occurs.
bool HotkeyManager::IsActiveProc()
{
return true;
}
// When all Shortcut keys are pressed, fire the HotkeyCallback event.
void HotkeyManager::KeyboardEventProc(KeyboardEvent ev)
{
// pressedKeys always stores the latest keyboard state
auto pressedKeysHandle = GetHotkeyHandle(pressedKeys);
if (hotkeys.find(pressedKeysHandle) != hotkeys.end())
{
hotkeys[pressedKeysHandle]();
// KeyboardEvent callback is only fired for relevant key events.
bool HotkeyManager::FilterKeyboardProc(KeyboardEvent ^ ev)
{
// Updating the pressed keys here so we know if the keypress event should be propagated or not.
pressedKeys->Win = (GetAsyncKeyState(VK_LWIN) & 0x8000) || (GetAsyncKeyState(VK_RWIN) & 0x8000);
pressedKeys->Ctrl = GetAsyncKeyState(VK_CONTROL) & 0x8000;
pressedKeys->Alt = GetAsyncKeyState(VK_MENU) & 0x8000;
pressedKeys->Shift = GetAsyncKeyState(VK_SHIFT) & 0x8000;
pressedKeys->Key = static_cast<unsigned char>(ev->key);
// After invoking the hotkey send a dummy key to prevent Start Menu from activating
INPUT dummyEvent[1] = {};
dummyEvent[0].type = INPUT_KEYBOARD;
dummyEvent[0].ki.wVk = 0xFF;
dummyEvent[0].ki.dwFlags = KEYEVENTF_KEYUP;
SendInput(1, dummyEvent, sizeof(INPUT));
}
}
// Convert to hotkey handle
auto pressedKeysHandle = GetHotkeyHandle(pressedKeys);
// Check if any hotkey matches the pressed keys if the current key event is a key down event
if ((ev->message == WM_KEYDOWN || ev->message == WM_SYSKEYDOWN) && hotkeys->ContainsKey(pressedKeysHandle))
// Hotkeys are intended to be global, therefore they are always active no matter the
// context in which the keypress occurs.
bool HotkeyManager::IsActiveProc()
{
return true;
}
bool HotkeyManager::FilterKeyboardProc(KeyboardEvent ev)
{
// Updating the pressed keys here so we know if the keypress event should be propagated or not.
pressedKeys.Win = (GetAsyncKeyState(VK_LWIN) & 0x8000) || (GetAsyncKeyState(VK_RWIN) & 0x8000);
pressedKeys.Ctrl = GetAsyncKeyState(VK_CONTROL) & 0x8000;
pressedKeys.Alt = GetAsyncKeyState(VK_MENU) & 0x8000;
pressedKeys.Shift = GetAsyncKeyState(VK_SHIFT) & 0x8000;
pressedKeys.Key = static_cast<unsigned char>(ev.key);
return false;
}
// Convert to hotkey handle
auto pressedKeysHandle = GetHotkeyHandle(pressedKeys);
// NOTE: Replaces old hotkey if one already present.
HOTKEY_HANDLE HotkeyManager::RegisterHotkey(Hotkey ^ hotkey, HotkeyCallback ^ callback)
{
auto handle = GetHotkeyHandle(hotkey);
hotkeys[handle] = callback;
return handle;
}
// Check if any hotkey matches the pressed keys if the current key event is a key down event
if ((ev.message == WM_KEYDOWN || ev.message == WM_SYSKEYDOWN) && hotkeys.find(pressedKeysHandle)!=hotkeys.end())
{
return true;
}
void HotkeyManager::UnregisterHotkey(HOTKEY_HANDLE handle)
{
hotkeys->Remove(handle);
}
return false;
}
HOTKEY_HANDLE HotkeyManager::GetHotkeyHandle(Hotkey ^ hotkey)
{
HOTKEY_HANDLE handle = hotkey->Key;
handle |= hotkey->Win << 8;
handle |= hotkey->Ctrl << 9;
handle |= hotkey->Shift << 10;
handle |= hotkey->Alt << 11;
return handle;
uint16_t HotkeyManager::RegisterHotkey(winrt::PowerToys::Interop::Hotkey const& _hotkey, winrt::PowerToys::Interop::HotkeyCallback const& _callback)
{
auto handle = GetHotkeyHandle(_hotkey);
hotkeys[handle] = _callback;
return handle;
}
void HotkeyManager::UnregisterHotkey(uint16_t _handle)
{
auto iter = hotkeys.find(_handle);
if (iter != hotkeys.end()) {
hotkeys.erase(iter);
}
}
void HotkeyManager::Close()
{
}
uint16_t HotkeyManager::GetHotkeyHandle(Hotkey hotkey)
{
uint16_t handle = hotkey.Key;
handle |= hotkey.Win << 8;
handle |= hotkey.Ctrl << 9;
handle |= hotkey.Shift << 10;
handle |= hotkey.Alt << 11;
return handle;
}
}

View File

@@ -1,54 +1,33 @@
#pragma once
#include <Windows.h>
#include "KeyboardHook.h"
#include "HotkeyManager.g.h"
namespace interop
namespace winrt::PowerToys::Interop::implementation
{
public
ref struct Hotkey
struct HotkeyManager : HotkeyManagerT<HotkeyManager>
{
bool Win;
bool Ctrl;
bool Shift;
bool Alt;
unsigned char Key;
Hotkey()
{
Win = false;
Ctrl = false;
Shift = false;
Alt = false;
Key = 0;
}
};
public
delegate void HotkeyCallback();
typedef unsigned short HOTKEY_HANDLE;
public
ref class HotkeyManager
{
public:
HotkeyManager();
~HotkeyManager();
HOTKEY_HANDLE RegisterHotkey(Hotkey ^ hotkey, HotkeyCallback ^ callback);
void UnregisterHotkey(HOTKEY_HANDLE handle);
uint16_t RegisterHotkey(winrt::PowerToys::Interop::Hotkey const& _hotkey, winrt::PowerToys::Interop::HotkeyCallback const& _callback);
void UnregisterHotkey(uint16_t _handle);
void Close();
private:
KeyboardHook ^ keyboardHook;
Dictionary<HOTKEY_HANDLE, HotkeyCallback ^> ^ hotkeys;
Hotkey ^ pressedKeys;
KeyboardEventCallback ^ keyboardEventCallback;
IsActiveCallback ^ isActiveCallback;
FilterKeyboardEvent ^ filterKeyboardCallback;
KeyboardHook keyboardHook{ nullptr };
std::map<uint16_t, HotkeyCallback> hotkeys;
Hotkey pressedKeys{ };
KeyboardEventCallback keyboardEventCallback;
IsActiveCallback isActiveCallback;
FilterKeyboardEvent filterKeyboardCallback;
void KeyboardEventProc(KeyboardEvent ^ ev);
void KeyboardEventProc(KeyboardEvent ev);
bool IsActiveProc();
bool FilterKeyboardProc(KeyboardEvent ^ ev);
HOTKEY_HANDLE GetHotkeyHandle(Hotkey ^ hotkey);
bool FilterKeyboardProc(KeyboardEvent ev);
uint16_t GetHotkeyHandle(Hotkey hotkey);
};
}
namespace winrt::PowerToys::Interop::factory_implementation
{
struct HotkeyManager : HotkeyManagerT<HotkeyManager, implementation::HotkeyManager>
{
};
}

View File

@@ -0,0 +1,23 @@
namespace PowerToys
{
namespace Interop
{
struct Hotkey
{
Boolean Win;
Boolean Ctrl;
Boolean Shift;
Boolean Alt;
UInt8 Key;
};
delegate void HotkeyCallback();
[default_interface] runtimeclass HotkeyManager : Windows.Foundation.IClosable
{
HotkeyManager();
UInt16 RegisterHotkey(Hotkey _hotkey, HotkeyCallback _callback);
void UnregisterHotkey(UInt16 _handle);
}
}
}

View File

@@ -1,73 +1,99 @@
#include "pch.h"
#include "KeyboardHook.h"
#include <exception>
#include <msclr/marshal.h>
#include <msclr/marshal_cppstd.h>
#include "KeyboardHook.g.cpp"
#include <common/debug_control.h>
#include <common/utils/winapi_error.h>
using namespace interop;
using namespace System::Runtime::InteropServices;
using namespace System;
using namespace System::Diagnostics;
KeyboardHook::KeyboardHook(
KeyboardEventCallback ^ keyboardEventCallback,
IsActiveCallback ^ isActiveCallback,
FilterKeyboardEvent ^ filterKeyboardEvent)
namespace winrt::PowerToys::Interop::implementation
{
this->keyboardEventCallback = keyboardEventCallback;
this->isActiveCallback = isActiveCallback;
this->filterKeyboardEvent = filterKeyboardEvent;
}
std::mutex KeyboardHook::instancesMutex;
std::unordered_set<KeyboardHook*> KeyboardHook::instances;
KeyboardHook::~KeyboardHook()
{
// Unregister low level hook procedure
UnhookWindowsHookEx(hookHandle);
}
KeyboardHook::KeyboardHook(winrt::PowerToys::Interop::KeyboardEventCallback const& keyboardEventCallback, winrt::PowerToys::Interop::IsActiveCallback const& isActiveCallback, winrt::PowerToys::Interop::FilterKeyboardEvent const& filterKeyboardEvent)
{
this->keyboardEventCallback = keyboardEventCallback;
this->isActiveCallback = isActiveCallback;
this->filterKeyboardEvent = filterKeyboardEvent;
}
void KeyboardHook::Start()
{
hookProc = gcnew HookProcDelegate(this, &KeyboardHook::HookProc);
void KeyboardHook::Close()
{
std::unique_lock lock { instancesMutex };
auto iter = instances.find(this);
if (iter != instances.end())
{
instances.erase(iter);
}
if (instances.size() < 1 && hookHandle != nullptr)
{
if (UnhookWindowsHookEx(hookHandle))
{
hookHandle = nullptr;
}
}
}
void KeyboardHook::Start()
{
#if defined(DISABLE_LOWLEVEL_HOOKS_WHEN_DEBUGGED)
const bool hookDisabled = IsDebuggerPresent();
const bool hookDisabled = IsDebuggerPresent();
#else
const bool hookDisabled = false;
const bool hookDisabled = false;
#endif
if (!hookDisabled)
{
// register low level hook procedure
hookHandle = SetWindowsHookEx(
WH_KEYBOARD_LL,
(HOOKPROC)(void*)Marshal::GetFunctionPointerForDelegate(hookProc),
0,
0);
if (hookHandle == nullptr)
if (!hookDisabled)
{
DWORD errorCode = GetLastError();
show_last_error_message(L"SetWindowsHookEx", errorCode, L"PowerToys - Interop");
std::unique_lock lock { instancesMutex };
assert(instances.find(this) == instances.end());
// register low level hook procedure
instances.insert(this);
if (hookHandle == nullptr)
{
hookHandle = SetWindowsHookEx(
WH_KEYBOARD_LL,
HookProc,
0,
0);
if (hookHandle == nullptr)
{
DWORD errorCode = GetLastError();
show_last_error_message(L"SetWindowsHookEx", errorCode, L"PowerToys - Interop");
}
}
}
}
LRESULT KeyboardHook::HookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode == HC_ACTION)
{
std::vector<KeyboardHook*> instances_copy;
{
/* Use a copy of instances, to iterate through the copy without needing to maintain the lock */
std::unique_lock lock{ instancesMutex };
instances_copy.reserve(instances.size());
std::copy(instances.begin(), instances.end(), std::back_inserter(instances_copy));
}
for (auto const& s_instance : instances_copy)
{
if (s_instance->isActiveCallback())
{
KeyboardEvent ev;
ev.message = wParam;
ev.key = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam)->vkCode;
ev.dwExtraInfo = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam)->dwExtraInfo;
// Ignore the keyboard hook if the FilterkeyboardEvent returns false.
if ((s_instance->filterKeyboardEvent != nullptr && !s_instance->filterKeyboardEvent(ev)))
{
continue;
}
s_instance->keyboardEventCallback(ev);
return 1;
}
}
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
}
LRESULT __clrcall KeyboardHook::HookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode == HC_ACTION && isActiveCallback->Invoke())
{
KeyboardEvent ^ ev = gcnew KeyboardEvent();
ev->message = wParam;
ev->key = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam)->vkCode;
ev->dwExtraInfo = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam)->dwExtraInfo;
// Ignore the keyboard hook if the FilterkeyboardEvent returns false.
if ((filterKeyboardEvent != nullptr && !filterKeyboardEvent->Invoke(ev)))
{
return CallNextHookEx(hookHandle, nCode, wParam, lParam);
}
keyboardEventCallback->Invoke(ev);
return 1;
}
return CallNextHookEx(hookHandle, nCode, wParam, lParam);
}

View File

@@ -1,48 +1,35 @@
#pragma once
#include "KeyboardHook.g.h"
#include <mutex>
#include <unordered_set>
#include <cinttypes>
using namespace System::Threading;
using namespace System::Collections::Generic;
namespace interop
namespace winrt::PowerToys::Interop::implementation
{
public
ref struct KeyboardEvent
struct KeyboardHook : KeyboardHookT<KeyboardHook>
{
WPARAM message;
int key;
uint64_t dwExtraInfo;
};
public
delegate void KeyboardEventCallback(KeyboardEvent ^ ev);
public
delegate bool IsActiveCallback();
public
delegate bool FilterKeyboardEvent(KeyboardEvent ^ ev);
public
ref class KeyboardHook
{
public:
KeyboardHook(
KeyboardEventCallback ^ keyboardEventCallback,
IsActiveCallback ^ isActiveCallback,
FilterKeyboardEvent ^ filterKeyboardEvent);
~KeyboardHook();
// KeyboardHook() = default;
KeyboardHook(winrt::PowerToys::Interop::KeyboardEventCallback const& keyboardEventCallback, winrt::PowerToys::Interop::IsActiveCallback const& isActiveCallback, winrt::PowerToys::Interop::FilterKeyboardEvent const& filterKeyboardEvent);
void Start();
void Close();
private:
delegate LRESULT HookProcDelegate(int nCode, WPARAM wParam, LPARAM lParam);
KeyboardEventCallback ^ keyboardEventCallback;
IsActiveCallback ^ isActiveCallback;
FilterKeyboardEvent ^ filterKeyboardEvent;
HHOOK hookHandle;
HookProcDelegate ^ hookProc;
winrt::PowerToys::Interop::KeyboardEventCallback keyboardEventCallback;
winrt::PowerToys::Interop::IsActiveCallback isActiveCallback;
winrt::PowerToys::Interop::FilterKeyboardEvent filterKeyboardEvent;
LRESULT __clrcall HookProc(int nCode, WPARAM wParam, LPARAM lParam);
// This class used to be C++/CX, which meant it ran on .NET runtime and was able to send function pointer for delegates as hook procedures for SetWindowsHookEx which kept an object reference.
// There doesn't seem to be a way to do this outside of the .NET runtime that allows us to get a proper C-style function.
// The alternative when porting to C++/winrt is to keep track of every instance and use a single proc instead of one per object. This should also make it lighter.
static std::mutex instancesMutex;
static std::unordered_set<KeyboardHook*> instances;
static inline HHOOK hookHandle = nullptr;
static LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam);
};
}
namespace winrt::PowerToys::Interop::factory_implementation
{
struct KeyboardHook : KeyboardHookT<KeyboardHook, implementation::KeyboardHook>
{
};
}

View File

@@ -0,0 +1,23 @@
typedef UInt64 WPARAM;
namespace PowerToys
{
namespace Interop
{
struct KeyboardEvent
{
WPARAM message;
Int32 key;
UInt64 dwExtraInfo;
};
[version(1.0), uuid(7b820173-a54a-4a38-b810-810e05c14344)] delegate void KeyboardEventCallback(KeyboardEvent ev);
[version(1.0), uuid(e3a8bb91-0dfd-4d77-8472-75a47b7af8f8)] delegate Boolean IsActiveCallback();
[version(1.0), uuid(94b900ee-d9e8-4630-9064-737eeb9c18ad)] delegate Boolean FilterKeyboardEvent(KeyboardEvent ev);
[default_interface] runtimeclass KeyboardHook: Windows.Foundation.IClosable {
KeyboardHook(KeyboardEventCallback keyboardEventCallback, IsActiveCallback isActiveCallback, FilterKeyboardEvent filterKeyboardEvent);
void Start();
}
}
}

View File

@@ -0,0 +1,19 @@
#include "pch.h"
#include "LayoutMapManaged.h"
#include "LayoutMapManaged.g.cpp"
namespace winrt::PowerToys::Interop::implementation
{
hstring LayoutMapManaged::GetKeyName(uint32_t key)
{
return hstring{ _map->GetKeyName(key) };
}
uint32_t LayoutMapManaged::GetKeyValue(hstring const& name)
{
return _map->GetKeyFromName(std::wstring(name));
}
void LayoutMapManaged::Updatelayout()
{
_map->UpdateLayout();
}
}

View File

@@ -0,0 +1,24 @@
#pragma once
#include "LayoutMapManaged.g.h"
#include "keyboard_layout.h"
namespace winrt::PowerToys::Interop::implementation
{
struct LayoutMapManaged : LayoutMapManagedT<LayoutMapManaged>
{
LayoutMapManaged() = default;
hstring GetKeyName(uint32_t key);
uint32_t GetKeyValue(hstring const& name);
void Updatelayout();
private:
std::unique_ptr<LayoutMap> _map = std::make_unique<LayoutMap>();
};
}
namespace winrt::PowerToys::Interop::factory_implementation
{
struct LayoutMapManaged : LayoutMapManagedT<LayoutMapManaged, implementation::LayoutMapManaged>
{
};
}

View File

@@ -0,0 +1,12 @@
namespace PowerToys
{
namespace Interop
{
[default_interface] runtimeclass LayoutMapManaged {
LayoutMapManaged();
String GetKeyName(UInt32 key);
UInt32 GetKeyValue(String name);
void Updatelayout();
}
}
}

View File

@@ -0,0 +1,3 @@
EXPORTS
DllCanUnloadNow = WINRT_CanUnloadNow PRIVATE
DllGetActivationFactory = WINRT_GetActivationFactory PRIVATE

View File

@@ -1,6 +1,6 @@
<?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')" />
<PropertyGroup>
<AssemblyTitle>PowerToys.Interop</AssemblyTitle>
</PropertyGroup>
@@ -25,16 +25,25 @@
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<ProjectGuid>{F055103B-F80B-4D0C-BF48-057C55620033}</ProjectGuid>
<TargetFramework>net8.0-windows</TargetFramework>
<Keyword>ManagedCProj</Keyword>
<RootNamespace>PowerToysInterop</RootNamespace>
<RootNamespace>PowerToys.Interop</RootNamespace>
<ProjectName>PowerToys.Interop</ProjectName>
<CppWinRTOptimized>true</CppWinRTOptimized>
<CppWinRTRootNamespaceAutoMerge>true</CppWinRTRootNamespaceAutoMerge>
<CppWinRTGenerateWindowsMetadata>true</CppWinRTGenerateWindowsMetadata>
<MinimalCoreWin>true</MinimalCoreWin>
<DefaultLanguage>en-US</DefaultLanguage>
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
<AppContainerApplication>false</AppContainerApplication>
<AppxPackage>false</AppxPackage>
<ApplicationType>Windows Store</ApplicationType>
<ApplicationTypeRevision>10.0</ApplicationTypeRevision>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<CLRSupport>NetCore</CLRSupport>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
@@ -55,93 +64,122 @@
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
<ClCompile>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<UseDebugLibraries>true</UseDebugLibraries>
<LinkIncremental>true</LinkIncremental>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
<ClCompile>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<LinkIncremental>false</LinkIncremental>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup>
<ClCompile>
<PreprocessorDefinitions>PowerToysInterop;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
<PreprocessorDefinitions>_WINRT_DLL;WINRT_LEAN_AND_MEAN;PowerToysInterop;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(SolutionDir)src\common\interop;../../;../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
<OmitDefaultLibName>false</OmitDefaultLibName>
<AdditionalOptions>/Zc:twoPhase- </AdditionalOptions>
<LanguageStandard>stdcpp17</LanguageStandard>
<AdditionalOptions>%(AdditionalOptions) /bigobj /Zc:twoPhase- </AdditionalOptions>
</ClCompile>
<Link>
<AdditionalDependencies>WindowsApp.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>WindowsApp.lib;shell32.lib;advapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<SubSystem>Console</SubSystem>
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
<ModuleDefinitionFile>PowerToys.Interop.def</ModuleDefinitionFile>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<AssemblyVersionFiles Include="Generated Files\AssemblyInfo.cpp" />
</ItemGroup>
<Target Name="GenerateAssemblyInfo" BeforeTargets="PrepareForBuild">
<ItemGroup>
<HeaderLines Include="#include &quot;../pch.h&quot;" />
<HeaderLines Include="using namespace System%3b" />
<HeaderLines Include="using namespace System::Reflection%3b" />
<HeaderLines Include="using namespace System::Runtime::CompilerServices%3b" />
<HeaderLines Include="using namespace System::Runtime::InteropServices%3b" />
<HeaderLines Include="using namespace System::Security::Permissions%3b" />
<HeaderLines Include="[assembly: AssemblyTitleAttribute(L&quot;$(AssemblyTitle)&quot;)]%3b" />
<HeaderLines Include="[assembly: AssemblyDescriptionAttribute(&quot;&quot;)]%3b" />
<HeaderLines Include="[assembly: AssemblyConfigurationAttribute(&quot;&quot;)]%3b" />
<HeaderLines Include="[assembly: AssemblyCompanyAttribute(&quot;$(AssemblyCompany)&quot;)]%3b" />
<HeaderLines Include="[assembly: AssemblyCopyrightAttribute(&quot;$(AssemblyCopyright)&quot;)]%3b" />
<HeaderLines Include="[assembly: AssemblyProductAttribute(&quot;$(AssemblyTitle)&quot;)]%3b" />
<HeaderLines Include="[assembly: AssemblyTrademarkAttribute(&quot;&quot;)]%3b" />
<HeaderLines Include="[assembly: AssemblyCultureAttribute(&quot;&quot;)]%3b" />
<HeaderLines Include="[assembly: AssemblyVersionAttribute(&quot;$(Version).0&quot;)]%3b" />
<HeaderLines Include="[assembly: ComVisible(false)]%3b" />
<HeaderLines Include="[assembly:CLSCompliantAttribute(true)]%3b" />
</ItemGroup>
<WriteLinesToFile File="Generated Files\AssemblyInfo.cpp" Lines="@(HeaderLines)" Overwrite="true" Encoding="Unicode" WriteOnlyWhenDifferent="true" />
</Target>
<ItemGroup>
<ClInclude Include="..\..\modules\videoconference\VideoConferenceShared\MicrophoneDevice.h" />
<ClInclude Include="..\..\modules\videoconference\VideoConferenceShared\VideoCaptureDeviceList.h" />
<ClInclude Include="HotkeyManager.h" />
<ClInclude Include="KeyboardHook.h" />
<ClInclude Include="async_message_queue.h" />
<ClInclude Include="CommonManaged.h">
<DependentUpon>CommonManaged.idl</DependentUpon>
</ClInclude>
<ClInclude Include="Constants.h">
<DependentUpon>KeyboardListener.idl</DependentUpon>
</ClInclude>
<ClInclude Include="HotkeyManager.h">
<DependentUpon>HotkeyManager.idl</DependentUpon>
</ClInclude>
<ClInclude Include="KeyboardHook.h">
<DependentUpon>KeyboardHook.idl</DependentUpon>
</ClInclude>
<ClInclude Include="keyboard_layout.h" />
<ClInclude Include="keyboard_layout_impl.h" />
<ClInclude Include="LayoutMapManaged.h">
<DependentUpon>LayoutMapManaged.idl</DependentUpon>
</ClInclude>
<ClInclude Include="pch.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="shared_constants.h" />
<ClInclude Include="TwoWayPipeMessageIPCManaged.h">
<DependentUpon>TwoWayPipeMessageIPCManaged.idl</DependentUpon>
</ClInclude>
<ClInclude Include="two_way_pipe_message_ipc.h" />
<ClInclude Include="two_way_pipe_message_ipc_impl.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\modules\videoconference\VideoConferenceShared\MicrophoneDevice.cpp">
<CompileAsManaged>false</CompileAsManaged>
<ClCompile Include="..\..\modules\videoconference\VideoConferenceShared\MicrophoneDevice.cpp" />
<ClCompile Include="..\..\modules\videoconference\VideoConferenceShared\VideoCaptureDeviceList.cpp" />
<ClCompile Include="CommonManaged.cpp">
<DependentUpon>CommonManaged.idl</DependentUpon>
</ClCompile>
<ClCompile Include="..\..\modules\videoconference\VideoConferenceShared\VideoCaptureDeviceList.cpp">
<CompileAsManaged>false</CompileAsManaged>
<ClCompile Include="Constants.cpp">
<DependentUpon>KeyboardListener.idl</DependentUpon>
</ClCompile>
<ClCompile Include="Generated Files\AssemblyInfo.cpp" />
<ClCompile Include="HotkeyManager.cpp" />
<ClCompile Include="interop.cpp" />
<ClCompile Include="KeyboardHook.cpp" />
<ClCompile Include="keyboard_layout.cpp">
<CompileAsManaged>false</CompileAsManaged>
<ClCompile Include="HotkeyManager.cpp">
<DependentUpon>HotkeyManager.idl</DependentUpon>
</ClCompile>
<ClCompile Include="two_way_pipe_message_ipc.cpp">
<CompileAsManaged>false</CompileAsManaged>
<ClCompile Include="KeyboardHook.cpp">
<DependentUpon>KeyboardHook.idl</DependentUpon>
</ClCompile>
<ClCompile Include="keyboard_layout.cpp" />
<ClCompile Include="LayoutMapManaged.cpp">
<DependentUpon>LayoutMapManaged.idl</DependentUpon>
</ClCompile>
<ClCompile Include="pch.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="TwoWayPipeMessageIPCManaged.cpp">
<DependentUpon>TwoWayPipeMessageIPCManaged.idl</DependentUpon>
</ClCompile>
<ClCompile Include="two_way_pipe_message_ipc.cpp" />
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="interop.rc" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
<None Include="PowerToys.Interop.def" />
</ItemGroup>
<ItemGroup>
<None Include="PropertySheet.props" />
</ItemGroup>
<ItemGroup>
<Midl Include="CommonManaged.idl" />
<Midl Include="Constants.idl" />
<Midl Include="HotkeyManager.idl" />
<Midl Include="KeyboardHook.idl" />
<Midl Include="LayoutMapManaged.idl" />
<Midl Include="TwoWayPipeMessageIPCManaged.idl" />
</ItemGroup>
<ImportGroup Label="ExtensionTargets" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<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')" />
</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.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>

View File

@@ -18,12 +18,6 @@
<ClInclude Include="pch.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="KeyboardHook.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="HotkeyManager.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="resource.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -36,23 +30,38 @@
<ClInclude Include="shared_constants.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Constants.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="keyboard_layout.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="keyboard_layout_impl.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="LayoutMapManaged.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CommonManaged.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="two_way_pipe_message_ipc.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="two_way_pipe_message_ipc_impl.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="async_message_queue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="HotkeyManager.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Constants.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="interop.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Generated Files\AssemblyInfo.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="KeyboardHook.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="HotkeyManager.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="two_way_pipe_message_ipc.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="keyboard_layout.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@@ -62,6 +71,30 @@
<ClCompile Include="..\..\modules\videoconference\VideoConferenceShared\VideoCaptureDeviceList.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="pch.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Constants.cpp">
<Filter>Header Files</Filter>
</ClCompile>
<ClCompile Include="LayoutMapManaged.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CommonManaged.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="two_way_pipe_message_ipc.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="HotkeyManager.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Constants.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="interop.rc">
@@ -69,9 +102,33 @@
</ResourceCompile>
</ItemGroup>
<ItemGroup>
<Natvis Include="$(MSBuildThisFileDirectory)..\..\natvis\wil.natvis" />
<None Include="packages.config" />
<None Include="PowerToys.Interop.def">
<Filter>Source Files</Filter>
</None>
<None Include="PropertySheet.props" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
<Midl Include="HotkeyManager.idl">
<Filter>Source Files</Filter>
</Midl>
<Midl Include="Constants.idl">
<Filter>Source Files</Filter>
</Midl>
<Midl Include="LayoutMapManaged.idl">
<Filter>Source Files</Filter>
</Midl>
<Midl Include="CommonManaged.idl">
<Filter>Source Files</Filter>
</Midl>
<Midl Include="KeyboardHook.idl">
<Filter>Source Files</Filter>
</Midl>
<Midl Include="TwoWayPipeMessageIPCManaged.idl">
<Filter>Source Files</Filter>
</Midl>
</ItemGroup>
<ItemGroup>
<Natvis Include="$(MSBuildThisFileDirectory)..\..\natvis\wil.natvis" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ImportGroup Label="PropertySheets" />
<PropertyGroup Label="UserMacros" />
<!--
To customize common C++/WinRT project properties:
* right-click the project node
* expand the Common Properties item
* select the C++/WinRT property page
For more advanced scenarios, and complete documentation, please see:
https://github.com/Microsoft/cppwinrt/tree/master/nuget
-->
<PropertyGroup />
<ItemDefinitionGroup />
</Project>

View File

@@ -0,0 +1,37 @@
#include "pch.h"
#include "TwoWayPipeMessageIPCManaged.h"
#include "TwoWayPipeMessageIPCManaged.g.cpp"
#include "two_way_pipe_message_ipc_impl.h"
#include <functional>
namespace winrt::PowerToys::Interop::implementation
{
TwoWayPipeMessageIPCManaged::TwoWayPipeMessageIPCManaged(hstring const& inputPipeName, hstring const& outputPipeName, winrt::PowerToys::Interop::TwoWayPipeIPCReadCallback const& _callback)
{
this->_callback = _callback;
if (_callback != nullptr)
{
_internalReadCallback = [this](const std::wstring& msg) {
this->_callback(msg);
};
}
_pipe = new TwoWayPipeMessageIPC(std::wstring{ inputPipeName }, std::wstring{ outputPipeName }, _internalReadCallback);
}
void TwoWayPipeMessageIPCManaged::Send(hstring const& msg)
{
_pipe->send(std::wstring{ msg });
}
void TwoWayPipeMessageIPCManaged::Start()
{
_pipe->start(nullptr);
}
void TwoWayPipeMessageIPCManaged::End()
{
_pipe->end();
}
void TwoWayPipeMessageIPCManaged::Close()
{
delete _pipe;
}
}

View File

@@ -0,0 +1,28 @@
#pragma once
#include "TwoWayPipeMessageIPCManaged.g.h"
#include "two_way_pipe_message_ipc.h"
namespace winrt::PowerToys::Interop::implementation
{
struct TwoWayPipeMessageIPCManaged : TwoWayPipeMessageIPCManagedT<TwoWayPipeMessageIPCManaged>
{
TwoWayPipeMessageIPCManaged() = default;
TwoWayPipeMessageIPCManaged(hstring const& inputPipeName, hstring const& outputPipeName, winrt::PowerToys::Interop::TwoWayPipeIPCReadCallback const& _callback);
void Send(hstring const& msg);
void Start();
void End();
void Close();
private:
TwoWayPipeMessageIPC* _pipe;
TwoWayPipeIPCReadCallback _callback;
std::function<void(const std::wstring& msg)> _internalReadCallback;
};
}
namespace winrt::PowerToys::Interop::factory_implementation
{
struct TwoWayPipeMessageIPCManaged : TwoWayPipeMessageIPCManagedT<TwoWayPipeMessageIPCManaged, implementation::TwoWayPipeMessageIPCManaged>
{
};
}

View File

@@ -0,0 +1,14 @@
namespace PowerToys
{
namespace Interop
{
delegate void TwoWayPipeIPCReadCallback(String message);
[default_interface] runtimeclass TwoWayPipeMessageIPCManaged : Windows.Foundation.IClosable
{
TwoWayPipeMessageIPCManaged(String inputPipeName, String outputPipeName, TwoWayPipeIPCReadCallback _callback);
void Send(String msg);
void Start();
void End();
}
}
}

View File

@@ -4,8 +4,8 @@
using System;
using System.Threading;
using interop;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using PowerToys.Interop;
namespace Microsoft.Interop.Tests
{

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<TargetFramework>net8.0-windows10.0.20348.0</TargetFramework>
<IsPackable>false</IsPackable>
<RuntimeIdentifiers>win-x64;win-arm64</RuntimeIdentifiers>
@@ -46,7 +46,15 @@
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<!-- See https://learn.microsoft.com/windows/apps/develop/platform/csharp-winrt/net-projection-from-cppwinrt-component for more info -->
<PropertyGroup>
<CsWinRTIncludes>PowerToys.Interop</CsWinRTIncludes>
<CsWinRTGeneratedFilesDir>$(OutDir)</CsWinRTGeneratedFilesDir>
<ErrorOnDuplicatePublishOutputFiles>false</ErrorOnDuplicatePublishOutputFiles>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Windows.CsWinRT" />
<PackageReference Include="MSTest" />
</ItemGroup>

View File

@@ -1,299 +0,0 @@
#include "pch.h"
#include <msclr/marshal.h>
#include <msclr/marshal_cppstd.h>
#include <functional>
#include "keyboard_layout.h"
#include "two_way_pipe_message_ipc.h"
#include "shared_constants.h"
// We cannot use C++/WinRT APIs when compiled with /clr (we'll get a runtime crash). os-detect API is used
// in both native C++ and C++/CX.
// We also cannot compile it as a library, since we use different cppruntime linkage in C++/CX and native C++.
// Therefore the simplest way is to compile these functions as native using the pragmas below.
#pragma managed(push, off)
#include "../utils/os-detect.h"
// TODO: move to a separate library in common
#include "../../modules/videoconference/VideoConferenceShared/MicrophoneDevice.h"
#include "../../modules/videoconference/VideoConferenceShared/VideoCaptureDeviceList.h"
#pragma managed(pop)
#include <common/version/version.h>
using namespace System;
using namespace System::Runtime::InteropServices;
using System::Collections::Generic::List;
// https://learn.microsoft.com/cpp/dotnet/how-to-wrap-native-class-for-use-by-csharp?view=vs-2019
namespace interop
{
public
ref class LayoutMapManaged
{
public:
LayoutMapManaged() :
_map(new LayoutMap) {}
~LayoutMapManaged()
{
delete _map;
}
String ^ GetKeyName(DWORD key)
{
return gcnew String(_map->GetKeyName(key).c_str());
}
DWORD GetKeyValue(String ^ name)
{
return _map->GetKeyFromName(msclr::interop::marshal_as<std::wstring>(name));
}
void Updatelayout()
{
_map->UpdateLayout();
}
protected:
!LayoutMapManaged()
{
delete _map;
}
private:
LayoutMap* _map;
};
public
ref class TwoWayPipeMessageIPCManaged
{
public:
delegate void ReadCallback(String ^ message);
TwoWayPipeMessageIPCManaged(String ^ inputPipeName, String ^ outputPipeName, ReadCallback ^ callback)
{
_wrapperCallback = gcnew InternalReadCallback(this, &TwoWayPipeMessageIPCManaged::ReadCallbackHelper);
_callback = callback;
TwoWayPipeMessageIPC::callback_function cb = nullptr;
if (callback != nullptr)
{
cb = (TwoWayPipeMessageIPC::callback_function)(void*)Marshal::GetFunctionPointerForDelegate(_wrapperCallback);
}
_pipe = new TwoWayPipeMessageIPC(
msclr::interop::marshal_as<std::wstring>(inputPipeName),
msclr::interop::marshal_as<std::wstring>(outputPipeName),
cb);
}
~TwoWayPipeMessageIPCManaged()
{
delete _pipe;
}
void Send(String ^ msg)
{
_pipe->send(msclr::interop::marshal_as<std::wstring>(msg));
}
void Start()
{
_pipe->start(nullptr);
}
void End()
{
_pipe->end();
}
protected:
!TwoWayPipeMessageIPCManaged()
{
delete _pipe;
}
private:
delegate void InternalReadCallback(const std::wstring& msg);
TwoWayPipeMessageIPC* _pipe;
ReadCallback ^ _callback;
InternalReadCallback ^ _wrapperCallback;
void ReadCallbackHelper(const std::wstring& msg)
{
_callback(gcnew String(msg.c_str()));
}
};
public
ref class CommonManaged
{
public:
static String ^ GetProductVersion() {
return gcnew String(get_product_version().c_str());
}
static List<String ^> ^ GetAllActiveMicrophoneDeviceNames() {
auto names = gcnew List<String ^>();
for (const auto& device : MicrophoneDevice::getAllActive())
{
names->Add(gcnew String(device->name().data()));
}
return names;
}
static List<String ^> ^
GetAllVideoCaptureDeviceNames() {
auto names = gcnew List<String ^>();
VideoCaptureDeviceList vcdl;
vcdl.EnumerateDevices();
for (UINT32 i = 0; i < vcdl.Count(); ++i)
{
auto name = gcnew String(vcdl.GetDeviceName(i).data());
if (name != L"PowerToys VideoConference Mute")
{
names->Add(name);
}
}
return names;
}
};
public
ref class Constants
{
public:
literal int VK_WIN_BOTH = CommonSharedConstants::VK_WIN_BOTH;
static String ^ AppDataPath() {
auto localPath = Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData);
auto powerToysPath = gcnew String(CommonSharedConstants::APPDATA_PATH);
return System::IO::Path::Combine(localPath, powerToysPath);
}
static String ^ PowerLauncherSharedEvent() {
return gcnew String(CommonSharedConstants::POWER_LAUNCHER_SHARED_EVENT);
}
static String ^ PowerLauncherCentralizedHookSharedEvent() {
return gcnew String(CommonSharedConstants::POWER_LAUNCHER_CENTRALIZED_HOOK_SHARED_EVENT);
}
static String ^ RunSendSettingsTelemetryEvent() {
return gcnew String(CommonSharedConstants::RUN_SEND_SETTINGS_TELEMETRY_EVENT);
}
static String ^ RunExitEvent() {
return gcnew String(CommonSharedConstants::RUN_EXIT_EVENT);
}
static String ^ FZEExitEvent() {
return gcnew String(CommonSharedConstants::FZE_EXIT_EVENT);
}
static String ^ FZEToggleEvent() {
return gcnew String(CommonSharedConstants::FANCY_ZONES_EDITOR_TOGGLE_EVENT);
}
static String ^ ColorPickerSendSettingsTelemetryEvent() {
return gcnew String(CommonSharedConstants::COLOR_PICKER_SEND_SETTINGS_TELEMETRY_EVENT);
}
static String ^ ShowColorPickerSharedEvent() {
return gcnew String(CommonSharedConstants::SHOW_COLOR_PICKER_SHARED_EVENT);
}
static String ^ ShowAdvancedPasteSharedEvent() {
return gcnew String(CommonSharedConstants::SHOW_ADVANCED_PASTE_SHARED_EVENT);
}
static String ^ AdvancedPasteMarkdownEvent() {
return gcnew String(CommonSharedConstants::ADVANCED_PASTE_MARKDOWN_EVENT);
}
static String ^ AdvancedPasteJsonEvent() {
return gcnew String(CommonSharedConstants::ADVANCED_PASTE_JSON_EVENT);
}
static String ^ ShowPowerOCRSharedEvent() {
return gcnew String(CommonSharedConstants::SHOW_POWEROCR_SHARED_EVENT);
}
static String ^ MouseJumpShowPreviewEvent() {
return gcnew String(CommonSharedConstants::MOUSE_JUMP_SHOW_PREVIEW_EVENT);
}
static String ^ AwakeExitEvent() {
return gcnew String(CommonSharedConstants::AWAKE_EXIT_EVENT);
}
static String^ ShowPeekEvent() {
return gcnew String(CommonSharedConstants::SHOW_PEEK_SHARED_EVENT);
}
static String ^ PowerAccentExitEvent() {
return gcnew String(CommonSharedConstants::POWERACCENT_EXIT_EVENT);
}
static String ^ ShortcutGuideTriggerEvent() {
return gcnew String(CommonSharedConstants::SHORTCUT_GUIDE_TRIGGER_EVENT);
}
static String ^ RegistryPreviewTriggerEvent() {
return gcnew String(CommonSharedConstants::REGISTRY_PREVIEW_TRIGGER_EVENT);
}
static String ^ MeasureToolTriggerEvent() {
return gcnew String(CommonSharedConstants::MEASURE_TOOL_TRIGGER_EVENT);
}
static String ^ GcodePreviewResizeEvent() {
return gcnew String(CommonSharedConstants::GCODE_PREVIEW_RESIZE_EVENT);
}
static String ^ QoiPreviewResizeEvent() {
return gcnew String(CommonSharedConstants::QOI_PREVIEW_RESIZE_EVENT);
}
static String ^ DevFilesPreviewResizeEvent() {
return gcnew String(CommonSharedConstants::DEV_FILES_PREVIEW_RESIZE_EVENT);
}
static String ^ MarkdownPreviewResizeEvent() {
return gcnew String(CommonSharedConstants::MARKDOWN_PREVIEW_RESIZE_EVENT);
}
static String ^ PdfPreviewResizeEvent() {
return gcnew String(CommonSharedConstants::PDF_PREVIEW_RESIZE_EVENT);
}
static String ^ SvgPreviewResizeEvent() {
return gcnew String(CommonSharedConstants::SVG_PREVIEW_RESIZE_EVENT);
}
static String ^ ShowHostsSharedEvent() {
return gcnew String(CommonSharedConstants::SHOW_HOSTS_EVENT);
}
static String ^ ShowHostsAdminSharedEvent() {
return gcnew String(CommonSharedConstants::SHOW_HOSTS_ADMIN_EVENT);
}
static String ^ CropAndLockThumbnailEvent() {
return gcnew String(CommonSharedConstants::CROP_AND_LOCK_THUMBNAIL_EVENT);
}
static String ^ CropAndLockReparentEvent() {
return gcnew String(CommonSharedConstants::CROP_AND_LOCK_REPARENT_EVENT);
}
static String ^ ShowEnvironmentVariablesSharedEvent() {
return gcnew String(CommonSharedConstants::SHOW_ENVIRONMENT_VARIABLES_EVENT);
}
static String ^ ShowEnvironmentVariablesAdminSharedEvent() {
return gcnew String(CommonSharedConstants::SHOW_ENVIRONMENT_VARIABLES_ADMIN_EVENT);
}
};
}

View File

@@ -0,0 +1 @@
#include "pch.h"

View File

@@ -3,13 +3,12 @@
// This also affects IntelliSense performance, including code completion and many code browsing features.
// However, files listed here are ALL re-compiled if any one of them is updated between builds.
// Do not add files here that you will be updating frequently as this negates the performance advantage.
#ifndef PCH_H
#define PCH_H
#pragma once
#define WIN32_LEAN_AND_MEAN
// add headers that you want to pre-compile here
#include <unknwn.h>
#include <winrt/base.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <Windows.h>
#include <Endpointvolume.h>
#endif //PCH_H

View File

@@ -1,8 +1,9 @@
#pragma once
#include <functional>
class TwoWayPipeMessageIPC
{
public:
typedef void (*callback_function)(const std::wstring&);
typedef std::function<void(const std::wstring&)>callback_function;
TwoWayPipeMessageIPC(
std::wstring _input_pipe_name,
std::wstring _output_pipe_name,