Skip to content

Commit

Permalink
FancyZones: prevent WinHookEventProc reentrancy bugs by serializing e…
Browse files Browse the repository at this point in the history
…vents to FancyZones::WndProc
  • Loading branch information
yuyoyuppe committed May 5, 2020
1 parent 283bfde commit 57c4658
Show file tree
Hide file tree
Showing 8 changed files with 132 additions and 52 deletions.
44 changes: 7 additions & 37 deletions src/modules/fancyzones/dll/dllmain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <lib/trace.h>
#include <lib/Settings.h>
#include <lib/FancyZones.h>
#include <lib/FancyZonesWinHookEventIDs.h>

extern "C" IMAGE_DOS_HEADER __ImageBase;

Expand Down Expand Up @@ -74,6 +75,7 @@ class FancyZonesModule : public PowertoyModuleIface
{
if (!m_app)
{
initialize_winhook_event_ids();
Trace::FancyZones::EnableFancyZones(true);
m_app = MakeFancyZones(reinterpret_cast<HINSTANCE>(&__ImageBase), m_settings);

Expand Down Expand Up @@ -186,9 +188,6 @@ class FancyZonesModule : public PowertoyModuleIface

intptr_t HandleKeyboardHookEvent(LowlevelKeyboardEvent* 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<IFancyZonesSettings> m_settings;
Expand Down Expand Up @@ -242,14 +241,12 @@ intptr_t FancyZonesModule::HandleKeyboardHookEvent(LowlevelKeyboardEvent* data)

void FancyZonesModule::HandleWinHookEvent(WinHookEvent* data) noexcept
{
POINT ptScreen;
GetPhysicalCursorPos(&ptScreen);

auto fzCallback = m_app.as<IFancyZonesCallback>();
switch (data->event)
{
case EVENT_SYSTEM_MOVESIZESTART:
{
MoveSizeStart(data->hwnd, ptScreen);
fzCallback->HandleWinHookEvent(data);
if (!m_objectLocationWinEventHook)
{
m_objectLocationWinEventHook = SetWinEventHook(EVENT_OBJECT_LOCATIONCHANGE,
Expand All @@ -269,16 +266,13 @@ void FancyZonesModule::HandleWinHookEvent(WinHookEvent* data) noexcept
{
m_objectLocationWinEventHook = nullptr;
}
MoveSizeEnd(data->hwnd, ptScreen);
fzCallback->HandleWinHookEvent(data);
}
break;

case EVENT_OBJECT_LOCATIONCHANGE:
{
if (m_app.as<IFancyZonesCallback>()->InMoveSize())
{
MoveSizeUpdate(ptScreen);
}
fzCallback->HandleWinHookEvent(data);
}
break;

Expand All @@ -298,10 +292,7 @@ void FancyZonesModule::HandleWinHookEvent(WinHookEvent* data) noexcept
case EVENT_OBJECT_SHOW:
case EVENT_OBJECT_CREATE:
{
if (data->idObject == OBJID_WINDOW)
{
m_app.as<IFancyZonesCallback>()->WindowCreated(data->hwnd);
}
fzCallback->HandleWinHookEvent(data);
}
break;

Expand All @@ -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()
{
return new FancyZonesModule();
Expand Down
89 changes: 74 additions & 15 deletions src/modules/fancyzones/lib/FancyZones.cpp
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
#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 "lib/Settings.h"
#include "lib/ZoneWindow.h"
#include "lib/JsonHelpers.h"
#include "lib/ZoneSet.h"
#include "lib/WindowMoveHandler.h"
#include "lib/FancyZonesWinHookEventIDs.h"
#include "lib/util.h"
#include "trace.h"
#include "VirtualDesktopUtils.h"

#include <functional>
#include <common/window_helpers.h>
#include <lib/util.h>
#include <unordered_set>
#include <interface/win_hook_event_data.h>

enum class DisplayChangeType
{
Expand Down Expand Up @@ -72,14 +73,46 @@ struct FancyZones : public winrt::implements<FancyZones, IFancyZones, IFancyZone
MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen) noexcept
{
std::unique_lock writeLock(m_lock);
m_windowMoveHandler.MoveSizeUpdate(monitor, ptScreen, m_zoneWindowMap);
m_windowMoveHandler.MoveSizeUpdate(monitor, ptScreen, m_zoneWindowMap);
}
IFACEMETHODIMP_(void)
MoveSizeEnd(HWND window, POINT const& ptScreen) noexcept
{
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)
VirtualDesktopChanged() noexcept;
IFACEMETHODIMP_(void)
Expand Down Expand Up @@ -189,7 +222,7 @@ struct FancyZones : public winrt::implements<FancyZones, IFancyZones, IFancyZone
void MoveWindowsOnDisplayChange() noexcept;
void CycleActiveZoneSet(DWORD vkCode) noexcept;
bool OnSnapHotkey(DWORD vkCode) noexcept;

void RegisterVirtualDesktopUpdates(std::vector<GUID>& ids) noexcept;
void RegisterNewWorkArea(GUID virtualDesktopId, HMONITOR monitor) noexcept;
bool IsNewWorkArea(GUID virtualDesktopId, HMONITOR monitor) noexcept;
Expand All @@ -204,7 +237,7 @@ struct FancyZones : public winrt::implements<FancyZones, IFancyZones, IFancyZone
mutable std::shared_mutex m_lock;
HWND m_window{};
WindowMoveHandler m_windowMoveHandler;

std::map<HMONITOR, winrt::com_ptr<IZoneWindow>> m_zoneWindowMap; // Map of monitor to ZoneWindow (one per monitor)
winrt::com_ptr<IFancyZonesSettings> m_settings{};
GUID m_currentVirtualDesktopId{}; // UUID of the current virtual desktop. Is GUID_NULL until first VD switch per session.
Expand Down Expand Up @@ -263,8 +296,7 @@ FancyZones::Run() noexcept
.wait();

m_terminateVirtualDesktopTrackerEvent.reset(CreateEvent(nullptr, FALSE, FALSE, nullptr));
m_virtualDesktopTrackerThread.submit(OnThreadExecutor::task_t{ [&] {
VirtualDesktopUtils::HandleVirtualDesktopUpdates(m_window, WM_PRIV_VD_UPDATE, m_terminateVirtualDesktopTrackerEvent.get()); } });
m_virtualDesktopTrackerThread.submit(OnThreadExecutor::task_t{ [&] { VirtualDesktopUtils::HandleVirtualDesktopUpdates(m_window, WM_PRIV_VD_UPDATE, m_terminateVirtualDesktopTrackerEvent.get()); } });
}

// IFancyZones
Expand All @@ -289,8 +321,8 @@ FancyZones::Destroy() noexcept
IFACEMETHODIMP_(void)
FancyZones::VirtualDesktopChanged() noexcept
{
// VirtualDesktopChanged is called from another thread but results in new windows being created.
// Jump over to the UI thread to handle it.
// VirtualDesktopChanged is called from a reentrant WinHookProc function, therefore we must postpone the actual logic
// until we're in FancyZones::WndProc, which is not reentrant.
std::shared_lock readLock(m_lock);
PostMessage(m_window, WM_PRIV_VD_SWITCH, 0, 0);
}
Expand Down Expand Up @@ -557,6 +589,9 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa

default:
{
POINT ptScreen;
GetPhysicalCursorPos(&ptScreen);

if (message == WM_PRIV_VD_INIT)
{
OnDisplayChange(DisplayChangeType::Initialization);
Expand Down Expand Up @@ -587,6 +622,31 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa
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
{
return DefWindowProc(window, message, wparam, lparam);
Expand Down Expand Up @@ -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
{
auto thisRef = reinterpret_cast<FancyZones*>(GetWindowLongPtr(window, GWLP_USERDATA));
Expand Down
8 changes: 8 additions & 0 deletions src/modules/fancyzones/lib/FancyZones.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ interface IZoneWindow;
interface IFancyZonesSettings;
interface IZoneSet;

struct WinHookEvent;

interface __declspec(uuid("{50D3F0F5-736E-4186-BDF4-3D6BEE150C3A}")) IFancyZones : public IUnknown
{
/**
Expand Down Expand Up @@ -54,6 +56,12 @@ interface __declspec(uuid("{2CB37E8F-87E6-4AEC-B4B2-E0FDC873343F}")) IFancyZones
* Inform FancyZones that user has switched between virtual desktops.
*/
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
* zone insde active zone layout (if information about last zone, in which window was located
Expand Down
2 changes: 2 additions & 0 deletions src/modules/fancyzones/lib/FancyZonesLib.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="FancyZones.h" />
<ClInclude Include="FancyZonesWinHookEventIDs.h" />
<ClInclude Include="JsonHelpers.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="resource.h" />
Expand All @@ -109,6 +110,7 @@
</ItemGroup>
<ItemGroup>
<ClCompile Include="FancyZones.cpp" />
<ClCompile Include="FancyZonesWinHookEventIDs.cpp" />
<ClCompile Include="JsonHelpers.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
Expand Down
6 changes: 6 additions & 0 deletions src/modules/fancyzones/lib/FancyZonesLib.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@
<ClInclude Include="WindowMoveHandler.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="FancyZonesWinHookEventIDs.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp">
Expand Down Expand Up @@ -86,6 +89,9 @@
<ClCompile Include="WindowMoveHandler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="FancyZonesWinHookEventIDs.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="fancyzones.rc">
Expand Down
24 changes: 24 additions & 0 deletions src/modules/fancyzones/lib/FancyZonesWinHookEventIDs.cpp
Original file line number Diff line number Diff line change
@@ -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 changes: 9 additions & 0 deletions src/modules/fancyzones/lib/FancyZonesWinHookEventIDs.h
Original file line number Diff line number Diff line change
@@ -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();
2 changes: 2 additions & 0 deletions src/modules/fancyzones/lib/pch.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
#include <winrt/windows.foundation.h>
#include <psapi.h>
#include <shared_mutex>
#include <functional>
#include <unordered_set>

#pragma comment(lib, "windowsapp")

Expand Down

0 comments on commit 57c4658

Please sign in to comment.