mirror of
https://github.com/microsoft/PowerToys.git
synced 2024-12-12 18:29:24 +08:00
FancyZones: prevent WinHookEventProc reentrancy bugs by serializing events to FancyZones::WndProc
This commit is contained in:
parent
283bfde3d9
commit
57c4658021
@ -10,6 +10,7 @@
|
|||||||
#include <lib/trace.h>
|
#include <lib/trace.h>
|
||||||
#include <lib/Settings.h>
|
#include <lib/Settings.h>
|
||||||
#include <lib/FancyZones.h>
|
#include <lib/FancyZones.h>
|
||||||
|
#include <lib/FancyZonesWinHookEventIDs.h>
|
||||||
|
|
||||||
extern "C" IMAGE_DOS_HEADER __ImageBase;
|
extern "C" IMAGE_DOS_HEADER __ImageBase;
|
||||||
|
|
||||||
@ -74,6 +75,7 @@ public:
|
|||||||
{
|
{
|
||||||
if (!m_app)
|
if (!m_app)
|
||||||
{
|
{
|
||||||
|
initialize_winhook_event_ids();
|
||||||
Trace::FancyZones::EnableFancyZones(true);
|
Trace::FancyZones::EnableFancyZones(true);
|
||||||
m_app = MakeFancyZones(reinterpret_cast<HINSTANCE>(&__ImageBase), m_settings);
|
m_app = MakeFancyZones(reinterpret_cast<HINSTANCE>(&__ImageBase), m_settings);
|
||||||
|
|
||||||
@ -186,9 +188,6 @@ private:
|
|||||||
|
|
||||||
intptr_t HandleKeyboardHookEvent(LowlevelKeyboardEvent* data) noexcept;
|
intptr_t HandleKeyboardHookEvent(LowlevelKeyboardEvent* data) noexcept;
|
||||||
void HandleWinHookEvent(WinHookEvent* data) noexcept;
|
void HandleWinHookEvent(WinHookEvent* data) noexcept;
|
||||||
void MoveSizeStart(HWND window, POINT const& ptScreen) noexcept;
|
|
||||||
void MoveSizeEnd(HWND window, POINT const& ptScreen) noexcept;
|
|
||||||
void MoveSizeUpdate(POINT const& ptScreen) noexcept;
|
|
||||||
|
|
||||||
winrt::com_ptr<IFancyZones> m_app;
|
winrt::com_ptr<IFancyZones> m_app;
|
||||||
winrt::com_ptr<IFancyZonesSettings> m_settings;
|
winrt::com_ptr<IFancyZonesSettings> m_settings;
|
||||||
@ -242,14 +241,12 @@ intptr_t FancyZonesModule::HandleKeyboardHookEvent(LowlevelKeyboardEvent* data)
|
|||||||
|
|
||||||
void FancyZonesModule::HandleWinHookEvent(WinHookEvent* data) noexcept
|
void FancyZonesModule::HandleWinHookEvent(WinHookEvent* data) noexcept
|
||||||
{
|
{
|
||||||
POINT ptScreen;
|
auto fzCallback = m_app.as<IFancyZonesCallback>();
|
||||||
GetPhysicalCursorPos(&ptScreen);
|
|
||||||
|
|
||||||
switch (data->event)
|
switch (data->event)
|
||||||
{
|
{
|
||||||
case EVENT_SYSTEM_MOVESIZESTART:
|
case EVENT_SYSTEM_MOVESIZESTART:
|
||||||
{
|
{
|
||||||
MoveSizeStart(data->hwnd, ptScreen);
|
fzCallback->HandleWinHookEvent(data);
|
||||||
if (!m_objectLocationWinEventHook)
|
if (!m_objectLocationWinEventHook)
|
||||||
{
|
{
|
||||||
m_objectLocationWinEventHook = SetWinEventHook(EVENT_OBJECT_LOCATIONCHANGE,
|
m_objectLocationWinEventHook = SetWinEventHook(EVENT_OBJECT_LOCATIONCHANGE,
|
||||||
@ -269,16 +266,13 @@ void FancyZonesModule::HandleWinHookEvent(WinHookEvent* data) noexcept
|
|||||||
{
|
{
|
||||||
m_objectLocationWinEventHook = nullptr;
|
m_objectLocationWinEventHook = nullptr;
|
||||||
}
|
}
|
||||||
MoveSizeEnd(data->hwnd, ptScreen);
|
fzCallback->HandleWinHookEvent(data);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EVENT_OBJECT_LOCATIONCHANGE:
|
case EVENT_OBJECT_LOCATIONCHANGE:
|
||||||
{
|
{
|
||||||
if (m_app.as<IFancyZonesCallback>()->InMoveSize())
|
fzCallback->HandleWinHookEvent(data);
|
||||||
{
|
|
||||||
MoveSizeUpdate(ptScreen);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -298,10 +292,7 @@ void FancyZonesModule::HandleWinHookEvent(WinHookEvent* data) noexcept
|
|||||||
case EVENT_OBJECT_SHOW:
|
case EVENT_OBJECT_SHOW:
|
||||||
case EVENT_OBJECT_CREATE:
|
case EVENT_OBJECT_CREATE:
|
||||||
{
|
{
|
||||||
if (data->idObject == OBJID_WINDOW)
|
fzCallback->HandleWinHookEvent(data);
|
||||||
{
|
|
||||||
m_app.as<IFancyZonesCallback>()->WindowCreated(data->hwnd);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -310,27 +301,6 @@ void FancyZonesModule::HandleWinHookEvent(WinHookEvent* data) noexcept
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FancyZonesModule::MoveSizeStart(HWND window, POINT const& ptScreen) noexcept
|
|
||||||
{
|
|
||||||
if (auto monitor = MonitorFromPoint(ptScreen, MONITOR_DEFAULTTONULL))
|
|
||||||
{
|
|
||||||
m_app.as<IFancyZonesCallback>()->MoveSizeStart(window, monitor, ptScreen);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FancyZonesModule::MoveSizeEnd(HWND window, POINT const& ptScreen) noexcept
|
|
||||||
{
|
|
||||||
m_app.as<IFancyZonesCallback>()->MoveSizeEnd(window, ptScreen);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FancyZonesModule::MoveSizeUpdate(POINT const& ptScreen) noexcept
|
|
||||||
{
|
|
||||||
if (auto monitor = MonitorFromPoint(ptScreen, MONITOR_DEFAULTTONULL))
|
|
||||||
{
|
|
||||||
m_app.as<IFancyZonesCallback>()->MoveSizeUpdate(monitor, ptScreen);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create()
|
extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create()
|
||||||
{
|
{
|
||||||
return new FancyZonesModule();
|
return new FancyZonesModule();
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
#include "common/dpi_aware.h"
|
|
||||||
#include "common/on_thread_executor.h"
|
#include <common/dpi_aware.h>
|
||||||
|
#include <common/on_thread_executor.h>
|
||||||
|
#include <common/window_helpers.h>
|
||||||
|
|
||||||
#include "FancyZones.h"
|
#include "FancyZones.h"
|
||||||
#include "lib/Settings.h"
|
#include "lib/Settings.h"
|
||||||
@ -8,13 +10,12 @@
|
|||||||
#include "lib/JsonHelpers.h"
|
#include "lib/JsonHelpers.h"
|
||||||
#include "lib/ZoneSet.h"
|
#include "lib/ZoneSet.h"
|
||||||
#include "lib/WindowMoveHandler.h"
|
#include "lib/WindowMoveHandler.h"
|
||||||
|
#include "lib/FancyZonesWinHookEventIDs.h"
|
||||||
|
#include "lib/util.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
#include "VirtualDesktopUtils.h"
|
#include "VirtualDesktopUtils.h"
|
||||||
|
|
||||||
#include <functional>
|
#include <interface/win_hook_event_data.h>
|
||||||
#include <common/window_helpers.h>
|
|
||||||
#include <lib/util.h>
|
|
||||||
#include <unordered_set>
|
|
||||||
|
|
||||||
enum class DisplayChangeType
|
enum class DisplayChangeType
|
||||||
{
|
{
|
||||||
@ -80,6 +81,38 @@ public:
|
|||||||
std::unique_lock writeLock(m_lock);
|
std::unique_lock writeLock(m_lock);
|
||||||
m_windowMoveHandler.MoveSizeEnd(window, ptScreen, m_zoneWindowMap);
|
m_windowMoveHandler.MoveSizeEnd(window, ptScreen, m_zoneWindowMap);
|
||||||
}
|
}
|
||||||
|
IFACEMETHODIMP_(void)
|
||||||
|
HandleWinHookEvent(const WinHookEvent* data) noexcept
|
||||||
|
{
|
||||||
|
const auto wparam = reinterpret_cast<WPARAM>(data->hwnd);
|
||||||
|
const LONG lparam = 0;
|
||||||
|
std::shared_lock readLock(m_lock);
|
||||||
|
switch (data->event)
|
||||||
|
{
|
||||||
|
case EVENT_SYSTEM_MOVESIZESTART:
|
||||||
|
PostMessageW(m_window, WM_PRIV_MOVESIZESTART, wparam, lparam);
|
||||||
|
break;
|
||||||
|
case EVENT_SYSTEM_MOVESIZEEND:
|
||||||
|
PostMessageW(m_window, WM_PRIV_MOVESIZEEND, wparam, lparam);
|
||||||
|
break;
|
||||||
|
case EVENT_OBJECT_LOCATIONCHANGE:
|
||||||
|
PostMessageW(m_window, WM_PRIV_LOCATIONCHANGE, wparam, lparam);
|
||||||
|
break;
|
||||||
|
case EVENT_OBJECT_NAMECHANGE:
|
||||||
|
PostMessageW(m_window, WM_PRIV_NAMECHANGE, wparam, lparam);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EVENT_OBJECT_UNCLOAKED:
|
||||||
|
case EVENT_OBJECT_SHOW:
|
||||||
|
case EVENT_OBJECT_CREATE:
|
||||||
|
if (data->idObject == OBJID_WINDOW)
|
||||||
|
{
|
||||||
|
PostMessageW(m_window, WM_PRIV_WINDOWCREATED, wparam, lparam);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
IFACEMETHODIMP_(void)
|
IFACEMETHODIMP_(void)
|
||||||
VirtualDesktopChanged() noexcept;
|
VirtualDesktopChanged() noexcept;
|
||||||
IFACEMETHODIMP_(void)
|
IFACEMETHODIMP_(void)
|
||||||
@ -263,8 +296,7 @@ FancyZones::Run() noexcept
|
|||||||
.wait();
|
.wait();
|
||||||
|
|
||||||
m_terminateVirtualDesktopTrackerEvent.reset(CreateEvent(nullptr, FALSE, FALSE, nullptr));
|
m_terminateVirtualDesktopTrackerEvent.reset(CreateEvent(nullptr, FALSE, FALSE, nullptr));
|
||||||
m_virtualDesktopTrackerThread.submit(OnThreadExecutor::task_t{ [&] {
|
m_virtualDesktopTrackerThread.submit(OnThreadExecutor::task_t{ [&] { VirtualDesktopUtils::HandleVirtualDesktopUpdates(m_window, WM_PRIV_VD_UPDATE, m_terminateVirtualDesktopTrackerEvent.get()); } });
|
||||||
VirtualDesktopUtils::HandleVirtualDesktopUpdates(m_window, WM_PRIV_VD_UPDATE, m_terminateVirtualDesktopTrackerEvent.get()); } });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IFancyZones
|
// IFancyZones
|
||||||
@ -289,8 +321,8 @@ FancyZones::Destroy() noexcept
|
|||||||
IFACEMETHODIMP_(void)
|
IFACEMETHODIMP_(void)
|
||||||
FancyZones::VirtualDesktopChanged() noexcept
|
FancyZones::VirtualDesktopChanged() noexcept
|
||||||
{
|
{
|
||||||
// VirtualDesktopChanged is called from another thread but results in new windows being created.
|
// VirtualDesktopChanged is called from a reentrant WinHookProc function, therefore we must postpone the actual logic
|
||||||
// Jump over to the UI thread to handle it.
|
// until we're in FancyZones::WndProc, which is not reentrant.
|
||||||
std::shared_lock readLock(m_lock);
|
std::shared_lock readLock(m_lock);
|
||||||
PostMessage(m_window, WM_PRIV_VD_SWITCH, 0, 0);
|
PostMessage(m_window, WM_PRIV_VD_SWITCH, 0, 0);
|
||||||
}
|
}
|
||||||
@ -557,6 +589,9 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
|
POINT ptScreen;
|
||||||
|
GetPhysicalCursorPos(&ptScreen);
|
||||||
|
|
||||||
if (message == WM_PRIV_VD_INIT)
|
if (message == WM_PRIV_VD_INIT)
|
||||||
{
|
{
|
||||||
OnDisplayChange(DisplayChangeType::Initialization);
|
OnDisplayChange(DisplayChangeType::Initialization);
|
||||||
@ -587,6 +622,31 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa
|
|||||||
m_terminateEditorEvent.release();
|
m_terminateEditorEvent.release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (message == WM_PRIV_MOVESIZESTART)
|
||||||
|
{
|
||||||
|
auto hwnd = reinterpret_cast<HWND>(wparam);
|
||||||
|
if (auto monitor = MonitorFromPoint(ptScreen, MONITOR_DEFAULTTONULL))
|
||||||
|
{
|
||||||
|
MoveSizeStart(hwnd, monitor, ptScreen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (message == WM_PRIV_MOVESIZEEND)
|
||||||
|
{
|
||||||
|
auto hwnd = reinterpret_cast<HWND>(wparam);
|
||||||
|
MoveSizeEnd(hwnd, ptScreen);
|
||||||
|
}
|
||||||
|
else if (message == WM_PRIV_LOCATIONCHANGE && InMoveSize())
|
||||||
|
{
|
||||||
|
if (auto monitor = MonitorFromPoint(ptScreen, MONITOR_DEFAULTTONULL))
|
||||||
|
{
|
||||||
|
MoveSizeUpdate(monitor, ptScreen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (message == WM_PRIV_WINDOWCREATED)
|
||||||
|
{
|
||||||
|
auto hwnd = reinterpret_cast<HWND>(wparam);
|
||||||
|
WindowCreated(hwnd);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return DefWindowProc(window, message, wparam, lparam);
|
return DefWindowProc(window, message, wparam, lparam);
|
||||||
@ -672,7 +732,6 @@ void FancyZones::AddZoneWindow(HMONITOR monitor, PCWSTR deviceId) noexcept
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
LRESULT CALLBACK FancyZones::s_WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lparam) noexcept
|
LRESULT CALLBACK FancyZones::s_WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lparam) noexcept
|
||||||
{
|
{
|
||||||
auto thisRef = reinterpret_cast<FancyZones*>(GetWindowLongPtr(window, GWLP_USERDATA));
|
auto thisRef = reinterpret_cast<FancyZones*>(GetWindowLongPtr(window, GWLP_USERDATA));
|
||||||
|
@ -4,6 +4,8 @@ interface IZoneWindow;
|
|||||||
interface IFancyZonesSettings;
|
interface IFancyZonesSettings;
|
||||||
interface IZoneSet;
|
interface IZoneSet;
|
||||||
|
|
||||||
|
struct WinHookEvent;
|
||||||
|
|
||||||
interface __declspec(uuid("{50D3F0F5-736E-4186-BDF4-3D6BEE150C3A}")) IFancyZones : public IUnknown
|
interface __declspec(uuid("{50D3F0F5-736E-4186-BDF4-3D6BEE150C3A}")) IFancyZones : public IUnknown
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
@ -54,6 +56,12 @@ interface __declspec(uuid("{2CB37E8F-87E6-4AEC-B4B2-E0FDC873343F}")) IFancyZones
|
|||||||
* Inform FancyZones that user has switched between virtual desktops.
|
* Inform FancyZones that user has switched between virtual desktops.
|
||||||
*/
|
*/
|
||||||
IFACEMETHOD_(void, VirtualDesktopChanged)() = 0;
|
IFACEMETHOD_(void, VirtualDesktopChanged)() = 0;
|
||||||
|
/**
|
||||||
|
* Callback from WinEventHook to FancyZones
|
||||||
|
*
|
||||||
|
* @param data Handle of window being moved or resized.
|
||||||
|
*/
|
||||||
|
IFACEMETHOD_(void, HandleWinHookEvent)(const WinHookEvent* data){};
|
||||||
/**
|
/**
|
||||||
* Inform FancyZones that new window is created. FancyZones will try to assign it to the
|
* Inform FancyZones that new window is created. FancyZones will try to assign it to the
|
||||||
* zone insde active zone layout (if information about last zone, in which window was located
|
* zone insde active zone layout (if information about last zone, in which window was located
|
||||||
|
@ -95,6 +95,7 @@
|
|||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="FancyZones.h" />
|
<ClInclude Include="FancyZones.h" />
|
||||||
|
<ClInclude Include="FancyZonesWinHookEventIDs.h" />
|
||||||
<ClInclude Include="JsonHelpers.h" />
|
<ClInclude Include="JsonHelpers.h" />
|
||||||
<ClInclude Include="pch.h" />
|
<ClInclude Include="pch.h" />
|
||||||
<ClInclude Include="resource.h" />
|
<ClInclude Include="resource.h" />
|
||||||
@ -109,6 +110,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="FancyZones.cpp" />
|
<ClCompile Include="FancyZones.cpp" />
|
||||||
|
<ClCompile Include="FancyZonesWinHookEventIDs.cpp" />
|
||||||
<ClCompile Include="JsonHelpers.cpp" />
|
<ClCompile Include="JsonHelpers.cpp" />
|
||||||
<ClCompile Include="pch.cpp">
|
<ClCompile Include="pch.cpp">
|
||||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||||
|
@ -51,6 +51,9 @@
|
|||||||
<ClInclude Include="WindowMoveHandler.h">
|
<ClInclude Include="WindowMoveHandler.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="FancyZonesWinHookEventIDs.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="pch.cpp">
|
<ClCompile Include="pch.cpp">
|
||||||
@ -86,6 +89,9 @@
|
|||||||
<ClCompile Include="WindowMoveHandler.cpp">
|
<ClCompile Include="WindowMoveHandler.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="FancyZonesWinHookEventIDs.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ResourceCompile Include="fancyzones.rc">
|
<ResourceCompile Include="fancyzones.rc">
|
||||||
|
24
src/modules/fancyzones/lib/FancyZonesWinHookEventIDs.cpp
Normal file
24
src/modules/fancyzones/lib/FancyZonesWinHookEventIDs.cpp
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
#include "FancyZonesWinHookEventIDs.h"
|
||||||
|
|
||||||
|
UINT WM_PRIV_MOVESIZESTART;
|
||||||
|
UINT WM_PRIV_MOVESIZEEND;
|
||||||
|
UINT WM_PRIV_LOCATIONCHANGE;
|
||||||
|
UINT WM_PRIV_NAMECHANGE;
|
||||||
|
UINT WM_PRIV_WINDOWCREATED;
|
||||||
|
|
||||||
|
std::once_flag init_flag;
|
||||||
|
|
||||||
|
void initialize_winhook_event_ids()
|
||||||
|
{
|
||||||
|
std::call_once(init_flag, [&] {
|
||||||
|
WM_PRIV_MOVESIZESTART = RegisterWindowMessage(L"{f48def23-df42-4c0f-a13d-3eb4a9e204d4}");
|
||||||
|
WM_PRIV_MOVESIZEEND = RegisterWindowMessage(L"{805d643c-804d-4728-b533-907d760ebaf0}");
|
||||||
|
WM_PRIV_LOCATIONCHANGE = RegisterWindowMessage(L"{d56c5ee7-58e5-481c-8c4f-8844cf4d0347}");
|
||||||
|
WM_PRIV_NAMECHANGE = RegisterWindowMessage(L"{b7b30c61-bfa0-4d95-bcde-fc4f2cbf6d76}");
|
||||||
|
WM_PRIV_WINDOWCREATED = RegisterWindowMessage(L"{bdb10669-75da-480a-9ec4-eeebf09a02d7}");
|
||||||
|
});
|
||||||
|
}
|
9
src/modules/fancyzones/lib/FancyZonesWinHookEventIDs.h
Normal file
9
src/modules/fancyzones/lib/FancyZonesWinHookEventIDs.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
extern UINT WM_PRIV_MOVESIZESTART;
|
||||||
|
extern UINT WM_PRIV_MOVESIZEEND;
|
||||||
|
extern UINT WM_PRIV_LOCATIONCHANGE;
|
||||||
|
extern UINT WM_PRIV_NAMECHANGE;
|
||||||
|
extern UINT WM_PRIV_WINDOWCREATED;
|
||||||
|
|
||||||
|
void initialize_winhook_event_ids();
|
@ -17,6 +17,8 @@
|
|||||||
#include <winrt/windows.foundation.h>
|
#include <winrt/windows.foundation.h>
|
||||||
#include <psapi.h>
|
#include <psapi.h>
|
||||||
#include <shared_mutex>
|
#include <shared_mutex>
|
||||||
|
#include <functional>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
#pragma comment(lib, "windowsapp")
|
#pragma comment(lib, "windowsapp")
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user