Skip to content

Commit 9d7c9c1

Browse files
authored
[Screen Ruler] Better error handling and reuse D3D device (#20223)
* [Screen Ruler] simplify lines calculation * [Screen Ruler] Add inches and centimeters support * [Chore] prefer x64 toolset to avoid hitting C1076 * [Screen Ruler] Allow making screenshots in non-continuous mode * [Screen Ruler] Use single d3d device for all ops * [Screen Ruler] remove gpu mutex and clean up screen capturing * [Screen Ruler] handle and log DXGI initialization failure * [Screen Ruler] Add unhandled exception handler * [Screen Ruler] comment out Units of Measure setting * [Screen Ruler] introduce a separate device dedicated for capturing
1 parent feead9c commit 9d7c9c1

40 files changed

+958
-429
lines changed

.github/actions/spell-check/expect.txt

+6-5
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,6 @@ CSIDL
346346
csignal
347347
cso
348348
CSRW
349-
cstddef
350349
cstdint
351350
cstdlib
352351
cstring
@@ -402,6 +401,7 @@ DBLEPSILON
402401
DCapture
403402
DCBA
404403
DCOM
404+
dcommon
405405
dcomp
406406
dcompi
407407
DComposition
@@ -490,7 +490,6 @@ dreamsofameaningfullife
490490
drivedetectionwarning
491491
dshow
492492
dst
493-
DState
494493
DTo
495494
dutil
496495
DVASPECT
@@ -522,6 +521,7 @@ DWORDLONG
522521
dworigin
523522
dwrite
524523
dxgi
524+
dxgidebug
525525
dxgiformat
526526
dxguid
527527
ecount
@@ -811,6 +811,7 @@ ICapture
811811
icase
812812
ICEBLUE
813813
IClass
814+
IClosable
814815
ICollection
815816
IColor
816817
ICommand
@@ -881,7 +882,6 @@ IMAGERESIZERCONTEXTMENU
881882
IMAGERESIZEREXT
882883
imageresizerinput
883884
imageresizersettings
884-
TEXTEXTRACTOR
885885
imagingdevices
886886
IMain
887887
IMarkdown
@@ -1278,6 +1278,7 @@ Moq
12781278
MOUSEACTIVATE
12791279
MOUSEHWHEEL
12801280
MOUSEINPUT
1281+
MOUSELEAVE
12811282
MOUSEMOVE
12821283
MOUSEWHEEL
12831284
MOVESIZEEND
@@ -1632,7 +1633,6 @@ ptd
16321633
PTOKEN
16331634
PToy
16341635
ptr
1635-
ptrdiff
16361636
ptstr
16371637
PVOID
16381638
pwa
@@ -1746,6 +1746,7 @@ RIGHTSCROLLBAR
17461746
riid
17471747
riverar
17481748
RKey
1749+
RLO
17491750
RMENU
17501751
RNumber
17511752
roadmap
@@ -2063,6 +2064,7 @@ testhost
20632064
testprocess
20642065
TEXCOORD
20652066
textblock
2067+
TEXTEXTRACTOR
20662068
TEXTINCLUDE
20672069
THH
20682070
THICKFRAME
@@ -2272,7 +2274,6 @@ wchar
22722274
WClass
22732275
wcout
22742276
wcscat
2275-
wcschr
22762277
wcscmp
22772278
wcscpy
22782279
wcslen

Cpp.Build.props

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232

3333
<!-- C++ source compile-specific things for all configurations -->
3434
<PropertyGroup>
35+
<PreferredToolArchitecture>x64</PreferredToolArchitecture>
3536
<VcpkgEnabled>false</VcpkgEnabled>
3637
<ExternalIncludePath>$(MSBuildThisFileFullPath)\..\deps\;$(ExternalIncludePath)</ExternalIncludePath>
3738
</PropertyGroup>

installer/PowerToysSetup/Product.wxs

+1-1
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@
119119

120120
<?define ImageResizerSparsePackageAssets=LargeTile.png;SmallTile.png;SplashScreen.png;Square150x150Logo.png;Square44x44Logo.png;storelogo.png;Wide310x150Logo.png?>
121121

122-
<?define MeasureToolFiles=CoreMessagingXP.dll;dcompi.dll;dwmcorei.dll;DwmSceneI.dll;DWriteCore.dll;marshal.dll;Microsoft.DirectManipulation.dll;Microsoft.InputStateManager.dll;Microsoft.InteractiveExperiences.Projection.dll;Microsoft.Internal.FrameworkUdk.dll;Microsoft.UI.Composition.OSSupport.dll;Microsoft.UI.Input.dll;Microsoft.UI.Windowing.Core.dll;Microsoft.UI.Xaml.Controls.dll;Microsoft.UI.Xaml.Controls.pri;Microsoft.ui.xaml.dll;Microsoft.UI.Xaml.Internal.dll;Microsoft.UI.Xaml.Phone.dll;Microsoft.ui.xaml.resources.19h1.dll;Microsoft.ui.xaml.resources.common.dll;Microsoft.Web.WebView2.Core.dll;Microsoft.Windows.ApplicationModel.Resources.dll;Microsoft.Windows.ApplicationModel.WindowsAppRuntime.Projection.dll;Microsoft.Windows.AppLifecycle.Projection.dll;Microsoft.Windows.AppNotifications.Projection.dll;Microsoft.Windows.PushNotifications.Projection.dll;Microsoft.Windows.SDK.NET.dll;Microsoft.Windows.System.Projection.dll;Microsoft.WindowsAppRuntime.Bootstrap.dll;Microsoft.WindowsAppRuntime.Bootstrap.Net.dll;Microsoft.WindowsAppRuntime.dll;Microsoft.WindowsAppRuntime.Insights.Resource.dll;Microsoft.WindowsAppRuntime.Release.Net.dll;Microsoft.WinUI.dll;MRM.dll;PowerToys.ManagedCommon.dll;PowerToys.ManagedTelemetry.dll;PowerToys.MeasureToolCore.dll;PowerToys.MeasureToolUI.deps.json;PowerToys.MeasureToolUI.dll;PowerToys.MeasureToolUI.exe;PowerToys.MeasureToolUI.runtimeconfig.json;PushNotificationsLongRunningTask.ProxyStub.dll;resources.pri;System.CodeDom.dll;System.Management.dll;WindowsAppRuntime.png;WindowsAppSdk.AppxDeploymentExtensions.Desktop.dll;WinRT.Runtime.dll;WinUIEdit.dll;WinUIEx.dll;wuceffectsi.dll?>
122+
<?define MeasureToolFiles=CoreMessagingXP.dll;dcompi.dll;dwmcorei.dll;DwmSceneI.dll;DWriteCore.dll;marshal.dll;Microsoft.DirectManipulation.dll;Microsoft.InputStateManager.dll;Microsoft.InteractiveExperiences.Projection.dll;Microsoft.Internal.FrameworkUdk.dll;Microsoft.UI.Composition.OSSupport.dll;Microsoft.UI.Input.dll;Microsoft.UI.Windowing.Core.dll;Microsoft.UI.Xaml.Controls.dll;Microsoft.UI.Xaml.Controls.pri;Microsoft.ui.xaml.dll;Microsoft.UI.Xaml.Internal.dll;Microsoft.UI.Xaml.Phone.dll;Microsoft.ui.xaml.resources.19h1.dll;Microsoft.ui.xaml.resources.common.dll;Microsoft.Web.WebView2.Core.dll;Microsoft.Windows.ApplicationModel.Resources.dll;Microsoft.Windows.ApplicationModel.WindowsAppRuntime.Projection.dll;Microsoft.Windows.AppLifecycle.Projection.dll;Microsoft.Windows.AppNotifications.Projection.dll;Microsoft.Windows.PushNotifications.Projection.dll;Microsoft.Windows.SDK.NET.dll;Microsoft.Windows.System.Projection.dll;Microsoft.WindowsAppRuntime.Bootstrap.dll;Microsoft.WindowsAppRuntime.Bootstrap.Net.dll;Microsoft.WindowsAppRuntime.dll;Microsoft.WindowsAppRuntime.Insights.Resource.dll;Microsoft.WindowsAppRuntime.Release.Net.dll;Microsoft.WinUI.dll;MRM.dll;PowerToys.ManagedCommon.dll;PowerToys.Interop.dll;PowerToys.ManagedTelemetry.dll;PowerToys.MeasureToolCore.dll;PowerToys.MeasureToolUI.deps.json;PowerToys.MeasureToolUI.dll;PowerToys.MeasureToolUI.exe;PowerToys.MeasureToolUI.runtimeconfig.json;PushNotificationsLongRunningTask.ProxyStub.dll;resources.pri;System.CodeDom.dll;System.Management.dll;WindowsAppRuntime.png;WindowsAppSdk.AppxDeploymentExtensions.Desktop.dll;WinRT.Runtime.dll;WinUIEdit.dll;WinUIEx.dll;wuceffectsi.dll?>
123123

124124
<?define PowerRenameMicrosoftUIXamlAssetsInstallFiles=NoiseAsset_256x256_PNG.png?>
125125

src/common/utils/UnhandledExceptionHandler.h

+5-5
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ inline void LogStackTrace()
156156
Logger::error(L"Failed to capture context. {}", get_last_error_or_default(GetLastError()));
157157
return;
158158
}
159-
159+
160160
STACKFRAME64 stack;
161161
memset(&stack, 0, sizeof(STACKFRAME64));
162162

@@ -238,14 +238,14 @@ inline LONG WINAPI UnhandledExceptionHandler(PEXCEPTION_POINTERS info)
238238
}
239239

240240
/* Handler to trap abort() calls */
241-
inline void AbortHandler(int signal_number)
241+
inline void AbortHandler(int /*signal_number*/)
242242
{
243243
Logger::error("--- ABORT");
244244
try
245245
{
246246
LogStackTrace();
247247
}
248-
catch(...)
248+
catch (...)
249249
{
250250
Logger::error("Failed to log stack trace on abort");
251251
Logger::flush();
@@ -271,9 +271,9 @@ inline void InitUnhandledExceptionHandler(void)
271271
// Global handler for unhandled exceptions
272272
SetUnhandledExceptionFilter(UnhandledExceptionHandler);
273273
// Handler for abort()
274-
signal(SIGABRT, &AbortHandler);
274+
signal(SIGABRT, &AbortHandler);
275275
}
276-
catch(...)
276+
catch (...)
277277
{
278278
Logger::error("Failed to init global unhandled exception handler");
279279
}

src/modules/MeasureTool/MeasureToolCore/BoundsToolOverlayUI.cpp

+32-32
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,16 @@ LRESULT CALLBACK BoundsToolWndProc(HWND window, UINT message, WPARAM wparam, LPA
3030
auto toolState = GetWindowParam<BoundsToolState*>(window);
3131
if (!toolState)
3232
break;
33-
const POINT cursorPos = convert::FromSystemToRelativeForDirect2D(window, toolState->commonState->cursorPosSystemSpace);
33+
const POINT cursorPos = convert::FromSystemToWindow(window, toolState->commonState->cursorPosSystemSpace);
3434

3535
D2D_POINT_2F newRegionStart = { .x = static_cast<float>(cursorPos.x), .y = static_cast<float>(cursorPos.y) };
3636
toolState->perScreen[window].currentRegionStart = newRegionStart;
3737
break;
3838
}
3939
case WM_CURSOR_LEFT_MONITOR:
4040
{
41+
for (; ShowCursor(true) < 0;)
42+
;
4143
auto toolState = GetWindowParam<BoundsToolState*>(window);
4244
if (!toolState)
4345
break;
@@ -59,12 +61,12 @@ LRESULT CALLBACK BoundsToolWndProc(HWND window, UINT message, WPARAM wparam, LPA
5961

6062
if (const bool shiftPress = GetKeyState(VK_SHIFT) & 0x8000; shiftPress)
6163
{
62-
const auto cursorPos = convert::FromSystemToRelativeForDirect2D(window, toolState->commonState->cursorPosSystemSpace);
64+
const auto cursorPos = convert::FromSystemToWindow(window, toolState->commonState->cursorPosSystemSpace);
6365

6466
D2D1_RECT_F rect;
6567
std::tie(rect.left, rect.right) = std::minmax(static_cast<float>(cursorPos.x), toolState->perScreen[window].currentRegionStart->x);
6668
std::tie(rect.top, rect.bottom) = std::minmax(static_cast<float>(cursorPos.y), toolState->perScreen[window].currentRegionStart->y);
67-
toolState->perScreen[window].measurements.push_back(rect);
69+
toolState->perScreen[window].measurements.push_back(Measurement{ rect });
6870
}
6971

7072
toolState->perScreen[window].currentRegionStart = std::nullopt;
@@ -97,45 +99,42 @@ LRESULT CALLBACK BoundsToolWndProc(HWND window, UINT message, WPARAM wparam, LPA
9799

98100
namespace
99101
{
100-
void DrawMeasurement(const D2D1_RECT_F rect,
102+
void DrawMeasurement(const Measurement& measurement,
101103
const bool alignTextBoxToCenter,
102104
const CommonState& commonState,
103105
HWND window,
104-
const D2DState& d2dState)
106+
const D2DState& d2dState,
107+
float mouseX,
108+
float mouseY)
105109
{
106110
const bool screenQuadrantAware = !alignTextBoxToCenter;
107-
const auto prevMode = d2dState.rt->GetAntialiasMode();
108-
d2dState.rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
109-
d2dState.rt->DrawRectangle(rect, d2dState.solidBrushes[Brush::line].get());
110-
d2dState.rt->SetAntialiasMode(prevMode);
111+
d2dState.ToggleAliasedLinesMode(true);
112+
d2dState.dxgiWindowState.rt->DrawRectangle(measurement.rect, d2dState.solidBrushes[Brush::line].get());
113+
d2dState.ToggleAliasedLinesMode(false);
111114

112115
OverlayBoxText text;
113-
const auto width = std::abs(rect.right - rect.left + 1);
114-
const auto height = std::abs(rect.top - rect.bottom + 1);
115-
const uint32_t textLen = swprintf_s(text.buffer.data(),
116-
text.buffer.size(),
117-
L"%.0f × %.0f",
118-
width,
119-
height);
120-
std::optional<size_t> crossSymbolPos = wcschr(text.buffer.data(), L' ') - text.buffer.data() + 1;
116+
const auto [crossSymbolPos, measureStringBufLen] =
117+
measurement.Print(text.buffer.data(),
118+
text.buffer.size(),
119+
true,
120+
true,
121+
commonState.units);
121122

122123
commonState.overlayBoxText.Access([&](OverlayBoxText& v) {
123124
v = text;
124125
});
125126

126-
float cornerX = rect.right;
127-
float cornerY = rect.bottom;
128127
if (alignTextBoxToCenter)
129128
{
130-
cornerX = rect.left + width / 2;
131-
cornerY = rect.top + height / 2;
129+
mouseX = measurement.rect.left + measurement.Width(Measurement::Unit::Pixel) / 2;
130+
mouseY = measurement.rect.top + measurement.Height(Measurement::Unit::Pixel) / 2;
132131
}
133132

134133
d2dState.DrawTextBox(text.buffer.data(),
135-
textLen,
134+
measureStringBufLen,
136135
crossSymbolPos,
137-
cornerX,
138-
cornerY,
136+
mouseX,
137+
mouseY,
139138
screenQuadrantAware,
140139
window);
141140
}
@@ -150,20 +149,21 @@ void DrawBoundsToolTick(const CommonState& commonState,
150149
if (it == end(toolState.perScreen))
151150
return;
152151

153-
d2dState.rt->Clear();
152+
d2dState.dxgiWindowState.rt->Clear();
154153

155154
const auto& perScreen = it->second;
156155
for (const auto& measure : perScreen.measurements)
157-
DrawMeasurement(measure, true, commonState, window, d2dState);
156+
DrawMeasurement(measure, true, commonState, window, d2dState, measure.rect.right, measure.rect.bottom);
158157

159158
if (!perScreen.currentRegionStart.has_value())
160159
return;
161160

162-
const auto cursorPos = convert::FromSystemToRelativeForDirect2D(window, commonState.cursorPosSystemSpace);
161+
const auto cursorPos = convert::FromSystemToWindow(window, commonState.cursorPosSystemSpace);
163162

164-
const D2D1_RECT_F rect{ .left = perScreen.currentRegionStart->x,
165-
.top = perScreen.currentRegionStart->y,
166-
.right = static_cast<float>(cursorPos.x),
167-
.bottom = static_cast<float>(cursorPos.y) };
168-
DrawMeasurement(rect, false, commonState, window, d2dState);
163+
D2D1_RECT_F rect;
164+
const float cursorX = static_cast<float>(cursorPos.x);
165+
const float cursorY = static_cast<float>(cursorPos.y);
166+
std::tie(rect.left, rect.right) = std::minmax(cursorX, perScreen.currentRegionStart->x);
167+
std::tie(rect.top, rect.bottom) = std::minmax(cursorY, perScreen.currentRegionStart->y);
168+
DrawMeasurement(Measurement{ rect }, false, commonState, window, d2dState, cursorX, cursorY);
169169
}

src/modules/MeasureTool/MeasureToolCore/CoordinateSystemConversion.h

+1-13
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,9 @@
66
namespace convert
77
{
88
// Converts a given point from multi-monitor coordinate system to the one relative to HWND
9-
inline POINT FromSystemToRelative(HWND window, POINT p)
9+
inline POINT FromSystemToWindow(HWND window, POINT p)
1010
{
1111
ScreenToClient(window, &p);
1212
return p;
1313
}
14-
15-
// Converts a given point from multi-monitor coordinate system to the one relative to HWND and also ready
16-
// to be used in Direct2D calls with AA mode set to aliased
17-
inline POINT FromSystemToRelativeForDirect2D(HWND window, POINT p)
18-
{
19-
ScreenToClient(window, &p);
20-
// Submitting DrawLine calls to Direct2D with thickness == 1.f and AA mode set to aliased causes
21-
// them to be drawn offset by [1,1] toward upper-left corner, so we must to compensate for that.
22-
++p.x;
23-
++p.y;
24-
return p;
25-
}
2614
}

0 commit comments

Comments
 (0)