Compare commits
79 Commits
dependabot
...
yuleng/cli
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb8b016f28 | ||
|
|
dbb1f76906 | ||
|
|
99ee955dfb | ||
|
|
4d01062f76 | ||
|
|
d7d1e543ae | ||
|
|
272b725ff0 | ||
|
|
7884f4217a | ||
|
|
92014c81b9 | ||
|
|
d57096af20 | ||
|
|
f136a4fe04 | ||
|
|
87fa204fd6 | ||
|
|
6ebfac8eab | ||
|
|
582f3eb5c3 | ||
|
|
4f14070a1a | ||
|
|
76eb6eaac5 | ||
|
|
97f2868481 | ||
|
|
f3d3abc552 | ||
|
|
0a64561ed5 | ||
|
|
7f19817182 | ||
|
|
a33fd3c474 | ||
|
|
b712fa4d85 | ||
|
|
b66b044210 | ||
|
|
18919eaa40 | ||
|
|
e0854fbaf3 | ||
|
|
ba20da1611 | ||
|
|
35f2ed839e | ||
|
|
e20b5b9c51 | ||
|
|
c6a9ad2ad0 | ||
|
|
a67fc2d9b7 | ||
|
|
c78f6e52a0 | ||
|
|
9a55209d13 | ||
|
|
0cb6fe250b | ||
|
|
c0cb9417ad | ||
|
|
cd5027fa1a | ||
|
|
7da62cdb0a | ||
|
|
c46083dd8d | ||
|
|
65112a7b05 | ||
|
|
109c63ba33 | ||
|
|
6be6509c46 | ||
|
|
8a7933c0b2 | ||
|
|
6f5ea3bb95 | ||
|
|
fe985e7eea | ||
|
|
b3bf154fa5 | ||
|
|
ab553f9930 | ||
|
|
85b9191b7c | ||
|
|
cb174210cb | ||
|
|
f175a9c96a | ||
|
|
273d735a8b | ||
|
|
bcf0b685ac | ||
|
|
6a5e320749 | ||
|
|
83285e929a | ||
|
|
26108ff04b | ||
|
|
fed9e81fdc | ||
|
|
f998c38ac8 | ||
|
|
11083c9fb2 | ||
|
|
809601a33c | ||
|
|
2c9481e69a | ||
|
|
1d0917d06f | ||
|
|
4edfcee87e | ||
|
|
33cf6995fc | ||
|
|
81e251c2de | ||
|
|
f02b66c88d | ||
|
|
386fdcb1e9 | ||
|
|
8af6a99136 | ||
|
|
9699d8a802 | ||
|
|
5b16a8c945 | ||
|
|
fa2b7f6e5f | ||
|
|
f117bfc64e | ||
|
|
52bf042df2 | ||
|
|
2fc27b13b6 | ||
|
|
94b7e3eea2 | ||
|
|
c334f1d997 | ||
|
|
8e74eb2ba8 | ||
|
|
da9a08aa92 | ||
|
|
b893d633d9 | ||
|
|
a7bc09a87a | ||
|
|
6e9b3b1536 | ||
|
|
d973bcbcaa | ||
|
|
02fbb916a7 |
13
.github/actions/spell-check/allow/code.txt
vendored
@@ -51,6 +51,7 @@ resw
|
||||
resx
|
||||
srt
|
||||
Stereolithography
|
||||
taskmgr
|
||||
terabyte
|
||||
UYVY
|
||||
xbf
|
||||
@@ -336,6 +337,10 @@ MRUCMPPROC
|
||||
MRUINFO
|
||||
REGSTR
|
||||
|
||||
#Xaml
|
||||
NVI
|
||||
Storyboards
|
||||
|
||||
# Misc Win32 APIs and PInvokes
|
||||
DEFAULTTONEAREST
|
||||
INVOKEIDLIST
|
||||
@@ -360,7 +365,10 @@ FILESYSONLY
|
||||
URLIS
|
||||
WAITTIMEOUT
|
||||
DEFAULTTONEAREST
|
||||
|
||||
DWRITE
|
||||
LWIN
|
||||
VCENTER
|
||||
VREDRAW
|
||||
|
||||
# COM/WinRT interface prefixes and type fragments
|
||||
BAlt
|
||||
@@ -424,3 +432,6 @@ SHELLEXPERIENCEHOST
|
||||
SHELLHOST
|
||||
STARTMENUEXPERIENCEHOST
|
||||
WIDGETBOARD
|
||||
|
||||
# URIs
|
||||
actioncenter
|
||||
|
||||
212
.github/actions/spell-check/allow/zoomit.txt
vendored
@@ -1,23 +1,51 @@
|
||||
accelscroll
|
||||
acq
|
||||
ADDTO
|
||||
ADDTOOL
|
||||
adr
|
||||
Adr
|
||||
ALWAYSTIP
|
||||
APPLYTOSUBMENUS
|
||||
ARCHMASK
|
||||
archs
|
||||
AUDCLNT
|
||||
autocorr
|
||||
avx
|
||||
axisdefer
|
||||
axisflip
|
||||
axisstart
|
||||
backlight
|
||||
BEOS
|
||||
bfi
|
||||
BFIN
|
||||
bfly
|
||||
BGRX
|
||||
bitmaps
|
||||
bitrev
|
||||
blits
|
||||
Borgerding
|
||||
Borland
|
||||
breakc
|
||||
BREAKSCR
|
||||
BUFFERFLAGS
|
||||
bugzilla
|
||||
Cands
|
||||
capturepath
|
||||
cbs
|
||||
centiseconds
|
||||
cexp
|
||||
cfx
|
||||
cfy
|
||||
cgem
|
||||
cifx
|
||||
cify
|
||||
CLASSW
|
||||
coeffs
|
||||
colblocks
|
||||
constantbuffer
|
||||
coprime
|
||||
cpuid
|
||||
cpx
|
||||
CREATEDIBSECTION
|
||||
CREATESTRUCTW
|
||||
crossfades
|
||||
@@ -28,109 +56,216 @@ CTLCOLORDLG
|
||||
CTLCOLOREDIT
|
||||
CTLCOLORLISTBOX
|
||||
CTrim
|
||||
CVTEPI
|
||||
DBuffer
|
||||
dcl
|
||||
dct
|
||||
ddx
|
||||
ddy
|
||||
Deinterleave
|
||||
denoise
|
||||
denoised
|
||||
DEVSOURCE
|
||||
DFCS
|
||||
DIVSCALAR
|
||||
DJGPP
|
||||
dlg
|
||||
dlu
|
||||
dnn
|
||||
DONTCARE
|
||||
downsample
|
||||
DRAWITEM
|
||||
DRAWITEMSTRUCT
|
||||
droppedband
|
||||
Droppedband
|
||||
DSPs
|
||||
dsum
|
||||
dupburst
|
||||
dupsegments
|
||||
DWLP
|
||||
eband
|
||||
ebx
|
||||
ECX
|
||||
EDITCONTROL
|
||||
EDSP
|
||||
emmintrin
|
||||
EMX
|
||||
ENABLEHOOK
|
||||
endloop
|
||||
ENDOFSTREAM
|
||||
ener
|
||||
enh
|
||||
ettings
|
||||
expectedlock
|
||||
expf
|
||||
fabs
|
||||
fabsf
|
||||
facbuf
|
||||
fastscroll
|
||||
FDE
|
||||
ffast
|
||||
FIXDIV
|
||||
floorf
|
||||
fmadd
|
||||
fout
|
||||
fstride
|
||||
fxc
|
||||
GETCHANNELRECT
|
||||
GETCHECK
|
||||
GETCOUNT
|
||||
GETDISPINFO
|
||||
GETSCREENSAVEACTIVE
|
||||
GETSCREENSAVETIMEOUT
|
||||
GETTHUMBRECT
|
||||
GIFs
|
||||
glu
|
||||
groupshared
|
||||
gru
|
||||
hcfdark
|
||||
hcfwhitespace
|
||||
hlsl
|
||||
Hsieh
|
||||
hstride
|
||||
HTBOTTOMRIGHT
|
||||
HTHEME
|
||||
htol
|
||||
ICONINFORMATION
|
||||
ICONWARNING
|
||||
idct
|
||||
IDIn
|
||||
IDISHWND
|
||||
ifft
|
||||
igc
|
||||
ilog
|
||||
imad
|
||||
imax
|
||||
imin
|
||||
immintrin
|
||||
Inj
|
||||
interp
|
||||
inttypes
|
||||
ishl
|
||||
itof
|
||||
jumprecover
|
||||
kfft
|
||||
kheight
|
||||
kissfft
|
||||
KSDATAFORMAT
|
||||
ksize
|
||||
ktime
|
||||
lastg
|
||||
latestcapture
|
||||
ldx
|
||||
LEFTNOWORDWRAP
|
||||
legitjumps
|
||||
lenmem
|
||||
letterbox
|
||||
lld
|
||||
lldx
|
||||
llu
|
||||
llums
|
||||
logfont
|
||||
lookback
|
||||
lpc
|
||||
lpcnet
|
||||
LPNMHDR
|
||||
LPNMTTDISPINFO
|
||||
lround
|
||||
lte
|
||||
luma
|
||||
Luma
|
||||
maj
|
||||
manualdrop
|
||||
maskcache
|
||||
maxabs
|
||||
maxcorr
|
||||
MAXFACTORS
|
||||
maxperiod
|
||||
maxstep
|
||||
memalign
|
||||
memid
|
||||
memneeded
|
||||
MENUINFO
|
||||
MFSTARTUP
|
||||
mfxhw
|
||||
mic
|
||||
middledrop
|
||||
minperiod
|
||||
MIPSr
|
||||
MJPEG
|
||||
MMRESULT
|
||||
momentumreversal
|
||||
movc
|
||||
mrate
|
||||
mrt
|
||||
MULBYSCALAR
|
||||
MULC
|
||||
MWERKS
|
||||
mycfg
|
||||
narrowstrip
|
||||
nbak
|
||||
nbytes
|
||||
ncapture
|
||||
nchw
|
||||
ncm
|
||||
nduplicates
|
||||
nfft
|
||||
NHWC
|
||||
niterations
|
||||
nmonitor
|
||||
nnet
|
||||
NONCLIENTMETRICS
|
||||
NONOTIFY
|
||||
nonvle
|
||||
normf
|
||||
nredraw
|
||||
nstop
|
||||
nsubpixel
|
||||
ntorn
|
||||
numthreads
|
||||
nvw
|
||||
Octasic
|
||||
osc
|
||||
OSCE
|
||||
ovflw
|
||||
OWNERDRAW
|
||||
PBGRA
|
||||
periodictrap
|
||||
pillarbox
|
||||
pfdc
|
||||
pillarbox
|
||||
playhead
|
||||
pnmh
|
||||
pointerreuse
|
||||
PPW
|
||||
prereq
|
||||
PSHR
|
||||
pstdint
|
||||
PSWA
|
||||
pwfx
|
||||
QCONST
|
||||
qpc
|
||||
Qpc
|
||||
quantums
|
||||
qweight
|
||||
RCSEGMODEL
|
||||
RCZOOMITSCR
|
||||
readback
|
||||
READERF
|
||||
realcapture
|
||||
REFKNOWNFOLDERID
|
||||
relu
|
||||
reposted
|
||||
RETURNCMD
|
||||
rnn
|
||||
rnnoise
|
||||
rotateleft
|
||||
rsqrt
|
||||
rtcd
|
||||
RTEXT
|
||||
RTH
|
||||
rtvs
|
||||
SCALEIN
|
||||
SCALEOUT
|
||||
SCREENSAVE
|
||||
SCRNSAVE
|
||||
SCRNSAVECONFIGURE
|
||||
@@ -138,43 +273,80 @@ scrnsavw
|
||||
Scrnsavw
|
||||
scrollramp
|
||||
SCROLLSIZEGRIP
|
||||
selfie
|
||||
selftest
|
||||
SETBARCOLOR
|
||||
SETBKCOLOR
|
||||
SETDEFID
|
||||
SETRECT
|
||||
SETSCREENSAVETIMEOUT
|
||||
SETTIPSIDE
|
||||
sgem
|
||||
sgemv
|
||||
sgv
|
||||
SHAREMODE
|
||||
SHAREVIOLATION
|
||||
shortlist
|
||||
simde
|
||||
siv
|
||||
slowthenfast
|
||||
smallstart
|
||||
SNIPOCR
|
||||
softmax
|
||||
sqrtf
|
||||
SROUND
|
||||
srvs
|
||||
ssi
|
||||
startuprecovery
|
||||
stdint
|
||||
stf
|
||||
stopafter
|
||||
STREAMFLAGS
|
||||
SUBFROM
|
||||
subias
|
||||
submix
|
||||
sxx
|
||||
sxy
|
||||
symbian
|
||||
synthesising
|
||||
syy
|
||||
tallportal
|
||||
TBTS
|
||||
tci
|
||||
tcsicmp
|
||||
TEXTCALLBACK
|
||||
TEXTMETRIC
|
||||
tgsm
|
||||
THIRDPARTY
|
||||
tinystep
|
||||
tme
|
||||
toolbars
|
||||
TOOLINFO
|
||||
TRACKMOUSEEVENT
|
||||
TRIANGLELIST
|
||||
TTM
|
||||
TTN
|
||||
TWID
|
||||
UADD
|
||||
uav
|
||||
uavs
|
||||
uge
|
||||
Unadvise
|
||||
upscaled
|
||||
upscales
|
||||
USUB
|
||||
utof
|
||||
vad
|
||||
vaddq
|
||||
vaddvq
|
||||
valgrind
|
||||
Valin
|
||||
vandq
|
||||
vblank
|
||||
vcgeq
|
||||
vdup
|
||||
vectorizer
|
||||
VERTID
|
||||
VIDCAP
|
||||
vld
|
||||
vle
|
||||
@@ -184,6 +356,7 @@ vminq
|
||||
vmlal
|
||||
vmull
|
||||
vqaddq
|
||||
VSHR
|
||||
vshrn
|
||||
vsntprintf
|
||||
vsnwprintf
|
||||
@@ -194,7 +367,9 @@ WAVEFORMATEXTENSIBLE
|
||||
webcam
|
||||
Webcam
|
||||
webcams
|
||||
Wextra
|
||||
wfopen
|
||||
WGC
|
||||
wideportal
|
||||
wil
|
||||
WMU
|
||||
@@ -202,11 +377,46 @@ wrapjump
|
||||
wtol
|
||||
WTSSESSION
|
||||
WTSUn
|
||||
wxyz
|
||||
xchg
|
||||
xcorr
|
||||
XEnd
|
||||
Xfl
|
||||
Xiang
|
||||
Xiph
|
||||
xmmintrin
|
||||
xptr
|
||||
xshift
|
||||
XStart
|
||||
XStep
|
||||
xxxy
|
||||
xxyx
|
||||
xxyz
|
||||
xyw
|
||||
xywx
|
||||
xyxx
|
||||
xyxz
|
||||
xyzw
|
||||
xyzx
|
||||
xzwx
|
||||
xzxx
|
||||
Yfl
|
||||
YInternal
|
||||
yshift
|
||||
YUV
|
||||
yyyx
|
||||
yyzw
|
||||
yzw
|
||||
yzwy
|
||||
yzyy
|
||||
Zhou
|
||||
Zhu
|
||||
ZMBS
|
||||
zncc
|
||||
Zncc
|
||||
ZNCC
|
||||
zrh
|
||||
zwzz
|
||||
zyzw
|
||||
zzwz
|
||||
zzzw
|
||||
|
||||
8
.github/actions/spell-check/excludes.txt
vendored
@@ -105,13 +105,13 @@
|
||||
^src/common/ManagedCommon/ColorFormatHelper\.cs$
|
||||
^src/common/notifications/BackgroundActivatorDLL/cpp\.hint$
|
||||
^src/common/sysinternals/Eula/
|
||||
^doc/devdocs/modules/cmdpal/initial-sdk-spec/list-elements-mock-002\.pdn$
|
||||
^src/modules/cmdpal/Tests/Microsoft\.CommandPalette\.Extensions\.Toolkit\.UnitTests/FuzzyMatcherComparisonTests.cs$
|
||||
^src/modules/cmdpal/Tests/Microsoft\.CommandPalette\.Extensions\.Toolkit\.UnitTests/FuzzyMatcherDiacriticsTests.cs$
|
||||
^src/modules/cmdpal/doc/initial-sdk-spec/list-elements-mock-002\.pdn$
|
||||
^src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleMarkdownImagesPage\.cs$
|
||||
^src/modules/cmdpal/Microsoft\.CmdPal\.UI/Settings/InternalPage\.SampleData\.cs$
|
||||
^src/modules/cmdpal/Tests/Microsoft\.CmdPal\.Common\.UnitTests/.*\.TestData\.cs$
|
||||
^src/modules/cmdpal/Tests/Microsoft\.CmdPal\.Common\.UnitTests/Text/.*\.cs$
|
||||
^src/modules/cmdpal/Tests/Microsoft\.CommandPalette\.Extensions\.Toolkit\.UnitTests/FuzzyMatcherComparisonTests.cs$
|
||||
^src/modules/cmdpal/Tests/Microsoft\.CommandPalette\.Extensions\.Toolkit\.UnitTests/FuzzyMatcherDiacriticsTests.cs$
|
||||
^src/modules/colorPicker/ColorPickerUI/Shaders/GridShader\.cso$
|
||||
^src/modules/launcher/Plugins/Microsoft\.PowerToys\.Run\.Plugin\.TimeDate/Properties/
|
||||
^src/modules/MouseUtils/MouseJumpUI/MainForm\.resx$
|
||||
@@ -135,6 +135,8 @@
|
||||
^src/modules/previewpane/SvgPreviewHandler/SvgHTMLPreviewGenerator\.cs$
|
||||
^src/modules/previewpane/UnitTests-MarkdownPreviewHandler/HelperFiles/MarkdownWithHTMLImageTag\.txt$
|
||||
^src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/.*$
|
||||
^src/modules/ZoomIt/ZoomIt/rnnoise/
|
||||
^src/modules/ZoomIt/ZoomIt/selfie_segmentation\.onnx$
|
||||
^src/modules/ZoomIt/ZoomIt/ZoomIt\.idc$
|
||||
^src/Monaco/
|
||||
^tools/project_template/ModuleTemplate/resource\.h$
|
||||
|
||||
66
.github/actions/spell-check/expect.txt
vendored
@@ -25,6 +25,7 @@ advapi
|
||||
advfirewall
|
||||
AFeature
|
||||
affordances
|
||||
afterfx
|
||||
AFX
|
||||
agentskills
|
||||
AGGREGATABLE
|
||||
@@ -62,6 +63,7 @@ APPEXECLINK
|
||||
appext
|
||||
apphost
|
||||
APPLICATIONFRAMEHOST
|
||||
Applocal
|
||||
appmanifest
|
||||
APPMODEL
|
||||
APPNAME
|
||||
@@ -80,6 +82,7 @@ ARPPRODUCTICON
|
||||
ARRAYSIZE
|
||||
ARROWKEYS
|
||||
arrowshape
|
||||
artboard
|
||||
ARTIFACTSTAGINGDIRECTORY
|
||||
asf
|
||||
Ashcraft
|
||||
@@ -110,14 +113,12 @@ azman
|
||||
azureaiinference
|
||||
azureinference
|
||||
azureopenai
|
||||
Backlight
|
||||
backticks
|
||||
Badflags
|
||||
Badmode
|
||||
Badparam
|
||||
bbwe
|
||||
BCIE
|
||||
bck
|
||||
BESTEFFORT
|
||||
bezelled
|
||||
bhid
|
||||
@@ -156,6 +157,7 @@ breadcrumb
|
||||
Browsable
|
||||
BROWSEINFO
|
||||
bsd
|
||||
BSOD
|
||||
bthprops
|
||||
bti
|
||||
BTNFACE
|
||||
@@ -163,6 +165,7 @@ bugreport
|
||||
bugreportfile
|
||||
BUILDARCH
|
||||
BUILDNUMBER
|
||||
buildsystems
|
||||
buildtransitive
|
||||
builttoroam
|
||||
BUNDLEINFO
|
||||
@@ -200,6 +203,7 @@ checkmarks
|
||||
CHILDACTIVATE
|
||||
CHILDWINDOW
|
||||
CHOOSEFONT
|
||||
chu
|
||||
CIBUILD
|
||||
cidl
|
||||
CIELCh
|
||||
@@ -222,6 +226,7 @@ CLIPSIBLINGS
|
||||
closesocket
|
||||
clp
|
||||
CLSCTX
|
||||
CLSIDs
|
||||
clsids
|
||||
Clusion
|
||||
cmder
|
||||
@@ -332,6 +337,7 @@ dacl
|
||||
DAffine
|
||||
DAFFINETRANSFORM
|
||||
datareader
|
||||
Datasheet
|
||||
datatracker
|
||||
Dayof
|
||||
dbcc
|
||||
@@ -368,6 +374,7 @@ DEFPUSHBUTTON
|
||||
deinitialization
|
||||
DELA
|
||||
DELD
|
||||
deld
|
||||
DELETEDKEYIMAGE
|
||||
DELETESCANS
|
||||
DEMOTYPE
|
||||
@@ -392,6 +399,7 @@ DEVMODE
|
||||
DEVMODEW
|
||||
DEVNODES
|
||||
devpal
|
||||
devpackages
|
||||
DEVTYP
|
||||
dfx
|
||||
DIALOGEX
|
||||
@@ -435,9 +443,9 @@ drawingcolor
|
||||
dreamsofameaningfullife
|
||||
drivedetectionwarning
|
||||
DROPFILES
|
||||
DSPDLOG
|
||||
DSTINVERT
|
||||
DString
|
||||
DSVG
|
||||
dto
|
||||
DUMMYUNIONNAME
|
||||
dumpbin
|
||||
@@ -463,7 +471,6 @@ DWMWINDOWATTRIBUTE
|
||||
DWMWINDOWMAXIMIZEDCHANGE
|
||||
DWORDLONG
|
||||
dworigin
|
||||
DWRITE
|
||||
dxgi
|
||||
Dxva
|
||||
eab
|
||||
@@ -501,7 +508,6 @@ ETDT
|
||||
etl
|
||||
etw
|
||||
eula
|
||||
eurochange
|
||||
eventvwr
|
||||
evt
|
||||
EWXFORCE
|
||||
@@ -542,6 +548,7 @@ FFFF
|
||||
fffffffzzz
|
||||
FFh
|
||||
Figma
|
||||
figma
|
||||
FILEEXPLORER
|
||||
fileexploreraddons
|
||||
fileexplorerpreview
|
||||
@@ -631,9 +638,11 @@ gpu
|
||||
grabandmove
|
||||
GRABANDMOVEMODULEINTERFACE
|
||||
gradians
|
||||
GRC
|
||||
grctlext
|
||||
GRGX
|
||||
Gridcustomlayout
|
||||
gridlines
|
||||
GSM
|
||||
gtm
|
||||
guiddata
|
||||
@@ -720,6 +729,7 @@ hotkeys
|
||||
hotlight
|
||||
hotspot
|
||||
HPAINTBUFFER
|
||||
HPS
|
||||
HRAWINPUT
|
||||
HREDRAW
|
||||
hres
|
||||
@@ -783,6 +793,7 @@ imgflip
|
||||
inapp
|
||||
inbox
|
||||
INCONTACT
|
||||
indesign
|
||||
Indo
|
||||
inetcpl
|
||||
Infobar
|
||||
@@ -793,6 +804,7 @@ INITDIALOG
|
||||
INITGUID
|
||||
initialfile
|
||||
INITTOLOGFONTSTRUCT
|
||||
inkscape
|
||||
INLINEPREFIX
|
||||
inlines
|
||||
Inno
|
||||
@@ -836,6 +848,7 @@ issecret
|
||||
ISSEPARATOR
|
||||
issuecomment
|
||||
istep
|
||||
Italicise
|
||||
ith
|
||||
IUI
|
||||
IUWP
|
||||
@@ -843,6 +856,7 @@ IWIC
|
||||
jeli
|
||||
jfif
|
||||
jgeosdfsdsgmkedfgdfgdfgbkmhcgcflmi
|
||||
JIDEA
|
||||
jjw
|
||||
jobject
|
||||
JOBOBJECT
|
||||
@@ -854,6 +868,7 @@ Jsons
|
||||
jsonval
|
||||
jxr
|
||||
Kantai
|
||||
KBSC
|
||||
keybd
|
||||
KEYBDDATA
|
||||
KEYBDINPUT
|
||||
@@ -871,6 +886,7 @@ keynum
|
||||
keyremaps
|
||||
keyring
|
||||
keyvault
|
||||
kfull
|
||||
KILLFOCUS
|
||||
killrunner
|
||||
kmph
|
||||
@@ -974,12 +990,12 @@ lstrcmpi
|
||||
lstrcpyn
|
||||
lstrlen
|
||||
LTEXT
|
||||
LTM
|
||||
LTRREADING
|
||||
luid
|
||||
lusrmgr
|
||||
LVDS
|
||||
LWA
|
||||
LWIN
|
||||
LZero
|
||||
MAGTRANSFORM
|
||||
makeappx
|
||||
@@ -1029,6 +1045,7 @@ Mgmt
|
||||
Microwaved
|
||||
middleclickaction
|
||||
midl
|
||||
midtones
|
||||
mii
|
||||
MIIM
|
||||
mikeclayton
|
||||
@@ -1042,7 +1059,7 @@ MINMAXINFO
|
||||
minwindef
|
||||
Mip
|
||||
Miracast
|
||||
MIRACAST
|
||||
miracast
|
||||
mkdn
|
||||
mlcfg
|
||||
mmc
|
||||
@@ -1073,6 +1090,7 @@ MOVESIZEEND
|
||||
MOVESIZESTART
|
||||
MRM
|
||||
mru
|
||||
msaccess
|
||||
MSAL
|
||||
msc
|
||||
mscorlib
|
||||
@@ -1081,8 +1099,6 @@ msdata
|
||||
msdia
|
||||
MSDL
|
||||
MSGFLT
|
||||
MSHCTX
|
||||
MSHLFLAGS
|
||||
msiexec
|
||||
MSIFASTINSTALL
|
||||
MSIHANDLE
|
||||
@@ -1092,13 +1108,16 @@ msixbundle
|
||||
MSIXCA
|
||||
MSLLHOOKSTRUCT
|
||||
Mso
|
||||
mspub
|
||||
msrc
|
||||
msstore
|
||||
mstsc
|
||||
msvcp
|
||||
mswhql
|
||||
MT
|
||||
MTND
|
||||
multimonitor
|
||||
Multiplayer
|
||||
MULTIPLEUSE
|
||||
multizone
|
||||
muxc
|
||||
@@ -1141,7 +1160,6 @@ netcpl
|
||||
netframework
|
||||
netsetup
|
||||
netsh
|
||||
newcolor
|
||||
NEWDIALOGSTYLE
|
||||
NEWFILE
|
||||
NEWFILEHEADER
|
||||
@@ -1187,7 +1205,6 @@ nonclient
|
||||
NONCLIENTMETRICSW
|
||||
NONELEVATED
|
||||
nonspace
|
||||
nonstd
|
||||
NOOWNERZORDER
|
||||
NOPARENTNOTIFY
|
||||
NOPREFIX
|
||||
@@ -1202,6 +1219,7 @@ NORMALDISPLAY
|
||||
NORMALUSER
|
||||
NOSEARCH
|
||||
NOSENDCHANGING
|
||||
NOSIZE
|
||||
nosize
|
||||
notdefault
|
||||
NOTHOUSANDS
|
||||
@@ -1242,7 +1260,6 @@ OFN
|
||||
ofs
|
||||
OICI
|
||||
OICIIO
|
||||
oldcolor
|
||||
olditem
|
||||
oldpath
|
||||
oldtheme
|
||||
@@ -1309,6 +1326,7 @@ pchast
|
||||
PCIDLIST
|
||||
PCTSTR
|
||||
PCWSTR
|
||||
pdbs
|
||||
PDBs
|
||||
PDEVMODE
|
||||
PDFs
|
||||
@@ -1337,6 +1355,7 @@ phbm
|
||||
phbmp
|
||||
phicon
|
||||
Photoshop
|
||||
photoshop
|
||||
phwnd
|
||||
pici
|
||||
pidl
|
||||
@@ -1364,14 +1383,15 @@ POINTERID
|
||||
POINTERUPDATE
|
||||
Pokedex
|
||||
Pomodoro
|
||||
Popups
|
||||
popups
|
||||
POPUPWINDOW
|
||||
portfile
|
||||
POSITIONITEM
|
||||
POWERBROADCAST
|
||||
powerdisplay
|
||||
POWERDISPLAYMODULEINTERFACE
|
||||
powerocr
|
||||
powerpnt
|
||||
POWERRENAMECONTEXTMENU
|
||||
powerrenameinput
|
||||
POWERRENAMETEST
|
||||
@@ -1398,7 +1418,6 @@ Prefixer
|
||||
Premul
|
||||
prependpath
|
||||
prepopulate
|
||||
Prereq
|
||||
prevhost
|
||||
previewer
|
||||
PREVIEWHANDLERFRAMEINFO
|
||||
@@ -1427,6 +1446,7 @@ projectname
|
||||
PROPERTYKEY
|
||||
PROPVARIANT
|
||||
prot
|
||||
Prt
|
||||
PRTL
|
||||
prvpane
|
||||
psapi
|
||||
@@ -1472,6 +1492,7 @@ QUERYOPEN
|
||||
QUEUESYNC
|
||||
quickaccent
|
||||
quicklinks
|
||||
quickmask
|
||||
QUNS
|
||||
RAII
|
||||
randi
|
||||
@@ -1492,7 +1513,6 @@ READMODE
|
||||
READOBJECTS
|
||||
recents
|
||||
RECTDESTINATION
|
||||
rectp
|
||||
RECTSOURCE
|
||||
recursesubdirs
|
||||
recyclebin
|
||||
@@ -1580,14 +1600,15 @@ SAMESHORTCUTPREVIOUSLYMAPPED
|
||||
samsung
|
||||
sancov
|
||||
SAVEFAILED
|
||||
scanled
|
||||
schedtasks
|
||||
SCID
|
||||
SCL
|
||||
Scode
|
||||
SCREENFONTS
|
||||
screenruler
|
||||
screensaver
|
||||
screenshots
|
||||
Scrollback
|
||||
scrollviewer
|
||||
sddl
|
||||
SDKDDK
|
||||
@@ -1757,6 +1778,7 @@ STDAPI
|
||||
stdc
|
||||
stdcpp
|
||||
stdcpplatest
|
||||
stdext
|
||||
STDMETHODCALLTYPE
|
||||
STDMETHODIMP
|
||||
steamapps
|
||||
@@ -1879,6 +1901,7 @@ TNP
|
||||
Toggleable
|
||||
tontrager
|
||||
Toolhelp
|
||||
toolsets
|
||||
toolwindow
|
||||
TOPDOWNDIB
|
||||
TOUCHEVENTF
|
||||
@@ -1928,6 +1951,7 @@ ums
|
||||
uncompilable
|
||||
UNCPRIORITY
|
||||
UNDNAME
|
||||
ungroup
|
||||
UNICODETEXT
|
||||
unins
|
||||
uninsdeletekey
|
||||
@@ -1940,6 +1964,8 @@ UNLEN
|
||||
UNORM
|
||||
unparsable
|
||||
unremapped
|
||||
Unsend
|
||||
Unsubscribes
|
||||
untriaged
|
||||
unvirtualized
|
||||
unwide
|
||||
@@ -1969,11 +1995,11 @@ valuegenerator
|
||||
VARTYPE
|
||||
vbcscompiler
|
||||
vcamp
|
||||
VCENTER
|
||||
vcgtq
|
||||
VCINSTALLDIR
|
||||
vcp
|
||||
Vcpkg
|
||||
vcpkg
|
||||
vcpname
|
||||
VCRT
|
||||
vcruntime
|
||||
@@ -1996,6 +2022,7 @@ VIRTKEY
|
||||
VIRTUALDESK
|
||||
VISEGRADRELAY
|
||||
visiblecolorformats
|
||||
visio
|
||||
visualeffects
|
||||
vkey
|
||||
vmovl
|
||||
@@ -2005,7 +2032,6 @@ vorrq
|
||||
VOS
|
||||
vpaddlq
|
||||
vqsubq
|
||||
VREDRAW
|
||||
vreinterpretq
|
||||
VSC
|
||||
VSCBD
|
||||
@@ -2080,13 +2106,16 @@ winlogon
|
||||
winmd
|
||||
winml
|
||||
WINNT
|
||||
winproj
|
||||
winres
|
||||
winrt
|
||||
winsdk
|
||||
winsta
|
||||
WINTHRESHOLD
|
||||
WINVER
|
||||
winword
|
||||
winxamlmanager
|
||||
wireframes
|
||||
withinrafael
|
||||
Withscript
|
||||
wixproj
|
||||
@@ -2157,6 +2186,7 @@ xxxxxx
|
||||
ycombinator
|
||||
yinle
|
||||
yinyue
|
||||
yoko
|
||||
YVIRTUALSCREEN
|
||||
zamora
|
||||
Zenbook
|
||||
|
||||
13
.github/actions/spell-check/patterns.txt
vendored
@@ -18,6 +18,13 @@ MIcrosoftEdgeLauncherCsharp
|
||||
# marker for ignoring a comment to the end of the line
|
||||
// #no-spell-check.*$
|
||||
|
||||
# JavaScript regex literals that start with \b can be reported as "b..." words.
|
||||
# Example: /\bclass\s+.../
|
||||
^\s*/\\[b].{3,}?/[gim]*\s*(?:\)(?:;|$)|,$)
|
||||
|
||||
# GitHub API header token used in code (not natural language).
|
||||
\bx-ratelimit-reset\b
|
||||
|
||||
# Gaelic
|
||||
Gàidhlig
|
||||
|
||||
@@ -305,3 +312,9 @@ ms-windows-store://\S+
|
||||
|
||||
# ANSI color codes
|
||||
(?:\\(?:u00|x)1[Bb]|\\03[1-7]|\x1b|\\u\{1[Bb]\})\[\d+(?:;\d+)*m
|
||||
|
||||
# Special licenses text from RNNoise (BSD-style disclaimer: ``AS IS'')
|
||||
``AS IS''
|
||||
|
||||
# Old school moniker for macOS from RNNoise
|
||||
MacOS
|
||||
|
||||
2
.github/policies/resourceManagement.yml
vendored
@@ -163,7 +163,7 @@ configuration:
|
||||
association: Collaborator
|
||||
then:
|
||||
- addReply:
|
||||
reply: Hi! We've identified this issue as a duplicate of another one that already exists on this Issue Tracker. This specific instance is being closed in favor of tracking the concern over on the referenced thread. Thanks for your report!
|
||||
reply: We've identified this issue as a duplicate of an existing one and are closing this thread so discussion stays in one place.<br/><br/>Please see the comment above for the link to the original tracking issue, and feel free to subscribe there for updates.
|
||||
- closeIssue
|
||||
- removeLabel:
|
||||
label: Needs-Triage
|
||||
|
||||
377
.github/scripts/telemetry-pr-check.js
vendored
Normal file
@@ -0,0 +1,377 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Detects telemetry-event additions/modifications in a pull request and
|
||||
* posts (or updates) a PR comment when telemetry-related changes are found.
|
||||
*
|
||||
* This script is executed by .github/workflows/telemetry-pr-check.yml.
|
||||
* Keep both files aligned when changing trigger behavior, env usage, or messaging.
|
||||
*/
|
||||
|
||||
const fs = require('node:fs');
|
||||
const REVIEWER_LOGIN = 'chatasweetie';
|
||||
const REVIEWER_MENTION = `@${REVIEWER_LOGIN}`;
|
||||
|
||||
const COMMENT_MARKER = '<!-- telemetry-event-check -->';
|
||||
const COMMENT_BODY_WITH_PRIVACY_UPDATE = `${COMMENT_MARKER}
|
||||
Thank you for contributing to PowerToys. We've detected that this PR might include a new or modified telemetry event. After this PR is merged, please follow these next steps:
|
||||
|
||||
- [ ] Reach out to Jessica (${REVIEWER_MENTION}) to follow up on the next steps: https://aka.ms/pt-telemetry-process
|
||||
`;
|
||||
|
||||
const COMMENT_BODY_WITHOUT_PRIVACY_UPDATE = `${COMMENT_MARKER}
|
||||
Thank you for contributing to PowerToys. We've detected that this PR might include a new or modified telemetry event. Please ensure the following before merging:
|
||||
|
||||
- [ ] Add your telemetry events to [DATA_AND_PRIVACY](https://github.com/microsoft/PowerToys/blob/main/DATA_AND_PRIVACY.md).md within this PR.
|
||||
|
||||
- [ ] Reach out to Jessica (${REVIEWER_MENTION}) to follow up on the next steps: https://aka.ms/pt-telemetry-process`;
|
||||
|
||||
const TELEMETRY_PATH_PATTERNS = [
|
||||
/(^|\/)trace\.(h|hpp|cpp|cs)$/i,
|
||||
/(^|\/)telemetry\//i,
|
||||
/(^|\/)events\/.+event\.cs$/i,
|
||||
/^src\/common\/Telemetry\//i,
|
||||
/^src\/common\/ManagedTelemetry\//i,
|
||||
/^src\/runner\/trace\.(h|cpp)$/i,
|
||||
/^src\/settings-ui\/.+\/Telemetry\//i,
|
||||
];
|
||||
|
||||
const TELEMETRY_LINE_PATTERNS = [
|
||||
/TraceLoggingWriteWrapper\s*\(/,
|
||||
/\bTraceLoggingWrite\s*\(/,
|
||||
/\bTRACELOGGING_DEFINE_PROVIDER\b/,
|
||||
/\bTraceLoggingOptionProjectTelemetry\b/,
|
||||
/\bProjectTelemetryPrivacyDataTag\b/,
|
||||
/\bPROJECT_KEYWORD_MEASURE\b/,
|
||||
/\bRegisterProvider\s*\(/,
|
||||
/\bUnregisterProvider\s*\(/,
|
||||
/\bPowerToysTelemetry\.Log\.WriteEvent\s*\(/,
|
||||
/\bclass\s+\w+\s*:\s*EventBase\s*,\s*IEvent\b/,
|
||||
/\bclass\s+\w+\s*:\s*TelemetryBase\b/,
|
||||
/\bPartA_PrivTags\b/,
|
||||
/\[EventData\]/,
|
||||
/\bEventName\b/,
|
||||
];
|
||||
|
||||
function requireEnv(name) {
|
||||
const value = process.env[name];
|
||||
if (!value) {
|
||||
throw new Error(`Missing required environment variable: ${name}`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function validateRepository(repository) {
|
||||
if (!/^[^/]+\/[^/]+$/.test(repository)) {
|
||||
throw new Error(
|
||||
`GITHUB_REPOSITORY must be in owner/repo format, received: ${JSON.stringify(repository)}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function readEventPayload(eventPath) {
|
||||
let raw;
|
||||
try {
|
||||
raw = fs.readFileSync(eventPath, 'utf8');
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to read event payload at ${eventPath}: ${error.message}`);
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(raw);
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to parse JSON from ${eventPath}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
function resolvePullNumber(event) {
|
||||
const fromPullRequest = event?.pull_request?.number;
|
||||
const fromWorkflowDispatch = event?.inputs?.pr_number;
|
||||
const rawPullNumber = fromPullRequest ?? fromWorkflowDispatch;
|
||||
|
||||
if (rawPullNumber === undefined || rawPullNumber === null || rawPullNumber === '') {
|
||||
throw new Error(
|
||||
'Unable to determine pull request number from event payload. Expected pull_request.number or inputs.pr_number.'
|
||||
);
|
||||
}
|
||||
|
||||
const pullNumber = Number.parseInt(String(rawPullNumber), 10);
|
||||
if (!Number.isInteger(pullNumber) || pullNumber <= 0) {
|
||||
throw new Error(`Invalid pull request number: ${JSON.stringify(rawPullNumber)}`);
|
||||
}
|
||||
|
||||
return pullNumber;
|
||||
}
|
||||
|
||||
function isTelemetryPath(filePath) {
|
||||
return TELEMETRY_PATH_PATTERNS.some((pattern) => pattern.test(filePath));
|
||||
}
|
||||
|
||||
function changedLinesFromPatch(patch) {
|
||||
if (!patch) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return patch
|
||||
.split('\n')
|
||||
.filter((line) => {
|
||||
if (line.startsWith('+++') || line.startsWith('---')) {
|
||||
return false;
|
||||
}
|
||||
return line.startsWith('+') || line.startsWith('-');
|
||||
})
|
||||
.map((line) => line.slice(1));
|
||||
}
|
||||
|
||||
function hasTelemetryLineSignal(lines) {
|
||||
return lines.some((line) => TELEMETRY_LINE_PATTERNS.some((pattern) => pattern.test(line)));
|
||||
}
|
||||
|
||||
async function apiRequest(url, method = 'GET', body) {
|
||||
const token = requireEnv('GITHUB_TOKEN');
|
||||
let response;
|
||||
try {
|
||||
response = await fetch(url, {
|
||||
method,
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
Accept: 'application/vnd.github+json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: body ? JSON.stringify(body) : undefined,
|
||||
});
|
||||
} catch (error) {
|
||||
throw new Error(`Network error during ${method} ${url}: ${error.message}`);
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
const text = await response.text();
|
||||
const rateLimitReset = response.headers.get('x-ratelimit-reset');
|
||||
const rateLimitHint =
|
||||
response.status === 403 && rateLimitReset
|
||||
? ` (rate limit reset at epoch ${rateLimitReset})`
|
||||
: '';
|
||||
throw new Error(`${method} ${url} failed (${response.status})${rateLimitHint}: ${text}`);
|
||||
}
|
||||
|
||||
if (response.status === 204) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to parse JSON response for ${method} ${url}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function getAllPullFiles(apiBaseUrl, repository, pullNumber) {
|
||||
const files = [];
|
||||
let page = 1;
|
||||
|
||||
while (true) {
|
||||
const url = `${apiBaseUrl}/repos/${repository}/pulls/${pullNumber}/files?per_page=100&page=${page}`;
|
||||
const batch = await apiRequest(url);
|
||||
if (!Array.isArray(batch)) {
|
||||
throw new Error(`Unexpected response while listing PR files on page ${page}.`);
|
||||
}
|
||||
|
||||
if (batch.length === 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
files.push(...batch);
|
||||
|
||||
if (batch.length < 100) {
|
||||
break;
|
||||
}
|
||||
|
||||
page += 1;
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
async function getPullRequest(apiBaseUrl, repository, pullNumber) {
|
||||
const url = `${apiBaseUrl}/repos/${repository}/pulls/${pullNumber}`;
|
||||
const pullRequest = await apiRequest(url);
|
||||
if (!pullRequest || typeof pullRequest !== 'object') {
|
||||
throw new Error('Unexpected response while fetching pull request details.');
|
||||
}
|
||||
return pullRequest;
|
||||
}
|
||||
|
||||
async function ensureReviewerRequested(apiBaseUrl, repository, pullNumber, pullRequest) {
|
||||
const authorLogin = String(pullRequest?.user?.login || '').toLowerCase();
|
||||
const targetReviewer = REVIEWER_LOGIN.toLowerCase();
|
||||
|
||||
if (authorLogin === targetReviewer) {
|
||||
console.log(`Skipping reviewer request: ${REVIEWER_LOGIN} is the PR author.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const requestedReviewers = Array.isArray(pullRequest?.requested_reviewers)
|
||||
? pullRequest.requested_reviewers
|
||||
: [];
|
||||
const alreadyRequested = requestedReviewers.some(
|
||||
(reviewer) => String(reviewer?.login || '').toLowerCase() === targetReviewer
|
||||
);
|
||||
|
||||
if (alreadyRequested) {
|
||||
console.log(`Reviewer ${REVIEWER_LOGIN} is already requested.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const url = `${apiBaseUrl}/repos/${repository}/pulls/${pullNumber}/requested_reviewers`;
|
||||
try {
|
||||
await apiRequest(url, 'POST', { reviewers: [REVIEWER_LOGIN] });
|
||||
console.log(`Requested reviewer ${REVIEWER_LOGIN}.`);
|
||||
} catch (error) {
|
||||
// Reviewer request should not fail the telemetry guidance workflow.
|
||||
console.warn(
|
||||
`Unable to request reviewer ${REVIEWER_LOGIN}: ${error instanceof Error ? error.message : String(error)}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function findExistingTelemetryComment(apiBaseUrl, repository, pullNumber) {
|
||||
let page = 1;
|
||||
|
||||
while (true) {
|
||||
const commentsUrl = `${apiBaseUrl}/repos/${repository}/issues/${pullNumber}/comments?per_page=100&page=${page}`;
|
||||
const comments = await apiRequest(commentsUrl);
|
||||
|
||||
if (!Array.isArray(comments)) {
|
||||
throw new Error(`Unexpected response while listing issue comments on page ${page}.`);
|
||||
}
|
||||
|
||||
const existing = comments.find(
|
||||
(comment) => typeof comment.body === 'string' && comment.body.includes(COMMENT_MARKER)
|
||||
);
|
||||
if (existing) {
|
||||
return existing;
|
||||
}
|
||||
|
||||
if (comments.length < 100) {
|
||||
return null;
|
||||
}
|
||||
|
||||
page += 1;
|
||||
}
|
||||
}
|
||||
|
||||
function detectTelemetryChanges(files) {
|
||||
const matches = [];
|
||||
|
||||
for (const file of files) {
|
||||
const filename = file.filename || '';
|
||||
const telemetryPath = isTelemetryPath(filename);
|
||||
const changedLines = changedLinesFromPatch(file.patch);
|
||||
const telemetryLineSignal = hasTelemetryLineSignal(changedLines);
|
||||
|
||||
// Some large diffs omit patch content. If the file path is telemetry-centric,
|
||||
// treat it as a telemetry modification to avoid false negatives.
|
||||
const patchUnavailable = !file.patch && telemetryPath;
|
||||
|
||||
if (telemetryPath || telemetryLineSignal || patchUnavailable) {
|
||||
matches.push({
|
||||
filename,
|
||||
telemetryPath,
|
||||
telemetryLineSignal,
|
||||
patchUnavailable,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
function hasDataAndPrivacyChange(files) {
|
||||
return files.some((file) => {
|
||||
const filename = (file.filename || '').toLowerCase();
|
||||
return filename === 'data_and_privacy.md';
|
||||
});
|
||||
}
|
||||
|
||||
async function upsertPrComment(apiBaseUrl, repository, pullNumber, body) {
|
||||
const existing = await findExistingTelemetryComment(apiBaseUrl, repository, pullNumber);
|
||||
|
||||
if (existing) {
|
||||
const updateUrl = `${apiBaseUrl}/repos/${repository}/issues/comments/${existing.id}`;
|
||||
await apiRequest(updateUrl, 'PATCH', { body });
|
||||
console.log(`Updated existing telemetry comment (id: ${existing.id}).`);
|
||||
return;
|
||||
}
|
||||
|
||||
const createUrl = `${apiBaseUrl}/repos/${repository}/issues/${pullNumber}/comments`;
|
||||
await apiRequest(createUrl, 'POST', { body });
|
||||
console.log('Created telemetry comment on PR.');
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const eventPath = requireEnv('GITHUB_EVENT_PATH');
|
||||
const repository = requireEnv('GITHUB_REPOSITORY');
|
||||
const apiBaseUrl = process.env.GITHUB_API_URL || 'https://api.github.com';
|
||||
validateRepository(repository);
|
||||
|
||||
let parsedApiBaseUrl;
|
||||
try {
|
||||
parsedApiBaseUrl = new URL(apiBaseUrl);
|
||||
} catch {
|
||||
throw new Error(`Invalid GITHUB_API_URL: ${JSON.stringify(apiBaseUrl)}`);
|
||||
}
|
||||
|
||||
const event = readEventPayload(eventPath);
|
||||
const pullNumber = resolvePullNumber(event);
|
||||
|
||||
console.log(`Event name: ${process.env.GITHUB_EVENT_NAME || 'unknown'}`);
|
||||
console.log(`Repository: ${repository}`);
|
||||
console.log(`PR number: ${pullNumber}`);
|
||||
|
||||
const files = await getAllPullFiles(parsedApiBaseUrl.origin, repository, pullNumber);
|
||||
|
||||
if (files.length === 0) {
|
||||
console.log('No changed files found for PR; skipping telemetry comment update.');
|
||||
return;
|
||||
}
|
||||
|
||||
const matches = detectTelemetryChanges(files);
|
||||
const dataAndPrivacyChanged = hasDataAndPrivacyChange(files);
|
||||
|
||||
console.log(`Scanned ${files.length} changed files.`);
|
||||
console.log(`Telemetry matches found: ${matches.length}.`);
|
||||
console.log(`DATA_AND_PRIVACY.md changed: ${dataAndPrivacyChanged}.`);
|
||||
|
||||
if (matches.length === 0) {
|
||||
console.log('No telemetry-related additions/modifications detected.');
|
||||
return;
|
||||
}
|
||||
|
||||
for (const match of matches) {
|
||||
console.log(
|
||||
`- ${match.filename} (telemetryPath=${match.telemetryPath}, telemetryLineSignal=${match.telemetryLineSignal}, patchUnavailable=${match.patchUnavailable})`
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const pullRequest = await getPullRequest(parsedApiBaseUrl.origin, repository, pullNumber);
|
||||
await ensureReviewerRequested(parsedApiBaseUrl.origin, repository, pullNumber, pullRequest);
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
'Failed to fetch PR details or request reviewer; continuing to post telemetry guidance comment.'
|
||||
);
|
||||
console.warn(error instanceof Error ? error.stack || error.message : error);
|
||||
}
|
||||
|
||||
const commentBody = dataAndPrivacyChanged
|
||||
? COMMENT_BODY_WITH_PRIVACY_UPDATE
|
||||
: COMMENT_BODY_WITHOUT_PRIVACY_UPDATE;
|
||||
|
||||
await upsertPrComment(apiBaseUrl, repository, pullNumber, commentBody);
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error('Telemetry PR check failed.');
|
||||
console.error(error instanceof Error ? error.stack || error.message : error);
|
||||
process.exit(1);
|
||||
});
|
||||
8
.github/workflows/auto-label-issues.yml
vendored
@@ -88,8 +88,8 @@ jobs:
|
||||
'Product-Advanced Paste',
|
||||
'Product-Always On Top',
|
||||
'Product-Awake',
|
||||
'Product-ColorPicker',
|
||||
'Product-Command Not Found',
|
||||
'Product-Color Picker',
|
||||
'Product-CommandNotFound',
|
||||
'Product-Command Palette',
|
||||
'Product-CropAndLock',
|
||||
'Product-Environment Variables',
|
||||
@@ -98,7 +98,7 @@ jobs:
|
||||
'Product-File Locksmith',
|
||||
'Product-Find My Mouse',
|
||||
'Product-Grab And Move',
|
||||
'Product-Hosts',
|
||||
'Product-Hosts File Editor',
|
||||
'Product-Image Resizer',
|
||||
'Product-Keyboard Manager',
|
||||
'Product-LightSwitch',
|
||||
@@ -109,7 +109,7 @@ jobs:
|
||||
'Product-Mouse Without Borders',
|
||||
'Product-New+',
|
||||
'Product-Peek',
|
||||
'Product-Power Display',
|
||||
'Product-PowerDisplay',
|
||||
'Product-PowerRename',
|
||||
'Product-PowerToys Run',
|
||||
'Product-Quick Accent',
|
||||
|
||||
35
.github/workflows/telemetry-pr-check.yml
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
# NOTE: This workflow depends on .github/scripts/telemetry-pr-check.js for telemetry detection and PR comments.
|
||||
# Keep this workflow and script behavior in sync when making changes.
|
||||
name: Telemetry PR Check
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, reopened, synchronize, ready_for_review]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
pr_number:
|
||||
description: "Pull Request Number to test against"
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
concurrency:
|
||||
group: telemetry-pr-check-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
detect-telemetry-events:
|
||||
if: ${{ github.event.pull_request.draft == false }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Detect telemetry event changes and comment PR
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: node .github/scripts/telemetry-pr-check.js
|
||||
8
.gitignore
vendored
@@ -19,6 +19,9 @@
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
!**/rnnoise/
|
||||
!**/rnnoise/x86/
|
||||
!**/rnnoise/x86/**
|
||||
ARM64/
|
||||
bld/
|
||||
[Bb]in/
|
||||
@@ -370,3 +373,8 @@ installer/*/*.wxs.bk
|
||||
.squad-workstream
|
||||
.github/agents/**squad**.md
|
||||
.github/workflows/**squad**.yml
|
||||
|
||||
# vcpkg manifest mode installed packages
|
||||
vcpkg_installed/
|
||||
|
||||
deps/vcpkg/
|
||||
|
||||
6
.gitmodules
vendored
@@ -1,6 +0,0 @@
|
||||
[submodule "deps/spdlog"]
|
||||
path = deps/spdlog
|
||||
url = https://github.com/gabime/spdlog.git
|
||||
[submodule "deps/expected-lite"]
|
||||
path = deps/expected-lite
|
||||
url = https://github.com/martinmoene/expected-lite.git
|
||||
@@ -64,6 +64,10 @@
|
||||
"FancyZonesCLI.exe",
|
||||
"FancyZonesCLI.dll",
|
||||
|
||||
"cli\\fancyzones.exe",
|
||||
"cli\\imageresizer.exe",
|
||||
"cli\\filelocksmith.exe",
|
||||
|
||||
"PowerToys.GcodePreviewHandler.dll",
|
||||
"PowerToys.GcodePreviewHandler.exe",
|
||||
"PowerToys.GcodePreviewHandlerCpp.dll",
|
||||
@@ -212,6 +216,7 @@
|
||||
"WinUI3Apps\\PowerToys.NewPlus.ShellExtension.win10.dll",
|
||||
|
||||
"PowerAccent.Core.dll",
|
||||
"PowerAccent.Common.dll",
|
||||
"PowerToys.PowerAccent.dll",
|
||||
"PowerToys.PowerAccent.exe",
|
||||
"PowerToys.PowerAccentModuleInterface.dll",
|
||||
@@ -243,8 +248,12 @@
|
||||
"WinUI3Apps\\PowerToys.RegistryPreview.dll",
|
||||
"WinUI3Apps\\PowerToys.RegistryPreview.exe",
|
||||
|
||||
"PowerToys.ShortcutGuide.exe",
|
||||
"PowerToys.ShortcutGuideModuleInterface.dll",
|
||||
"WinUI3Apps\\PowerToys.ShortcutGuide.exe",
|
||||
"WinUI3Apps\\PowerToys.ShortcutGuide.dll",
|
||||
"WinUI3Apps\\PowerToys.ShortcutGuideModuleInterface.dll",
|
||||
"WinUI3Apps\\PowerToys.ShortcutGuide.IndexYmlGenerator.dll",
|
||||
"WinUI3Apps\\PowerToys.ShortcutGuide.IndexYmlGenerator.exe",
|
||||
"WinUI3Apps\\ShortcutGuide.CPPProject.dll",
|
||||
|
||||
"PowerToys.ZoomIt.exe",
|
||||
"PowerToys.ZoomItModuleInterface.dll",
|
||||
@@ -383,6 +392,11 @@
|
||||
"ColorCode.Core.dll",
|
||||
"Microsoft.SemanticKernel.Connectors.Ollama.dll",
|
||||
"OllamaSharp.dll",
|
||||
"WinUI3Apps\\Google.Apis.dll",
|
||||
"WinUI3Apps\\Google.Apis.Auth.dll",
|
||||
"WinUI3Apps\\Google.Apis.Core.dll",
|
||||
"WinUI3Apps\\Google.GenAI.dll",
|
||||
"WinUI3Apps\\YamlDotNet.dll",
|
||||
|
||||
"boost_regex-vc143-mt-gd-x32-1_87.dll",
|
||||
"boost_regex-vc143-mt-gd-x64-1_87.dll",
|
||||
|
||||
@@ -104,6 +104,10 @@ extends:
|
||||
# Have msbuild use the release nuget config profile
|
||||
additionalBuildOptions: /p:RestoreConfigFile="$(Build.SourcesDirectory)\.pipelines\release-nuget.config" /p:EnableCmdPalAOT=true
|
||||
beforeBuildSteps:
|
||||
# Install the Terrapin retrieval tool, which replaces vcpkg's download handler
|
||||
# to redirect it to a safe Microsoft-controlled location
|
||||
- template: .pipelines/v2/templates/steps-install-terrapin.yml@self
|
||||
|
||||
# Sets versions for all PowerToy created DLLs
|
||||
- pwsh: |-
|
||||
.pipelines/versionSetting.ps1 -versionNumber '${{ parameters.versionNumber }}' -DevEnvironment ''
|
||||
@@ -140,6 +144,10 @@ extends:
|
||||
signCertName: $(SigningSignCertName)
|
||||
useManagedIdentity: $(SigningUseManagedIdentity)
|
||||
clientId: $(SigningOriginalClientId)
|
||||
beforeBuildSteps:
|
||||
# Install the Terrapin retrieval tool, which replaces vcpkg's download handler
|
||||
# to redirect it to a safe Microsoft-controlled location
|
||||
- template: .pipelines/v2/templates/steps-install-terrapin.yml@self
|
||||
|
||||
- stage: Publish
|
||||
displayName: Publish
|
||||
|
||||
@@ -270,6 +270,34 @@ jobs:
|
||||
parameters:
|
||||
directory: $(build.sourcesdirectory)\src\modules\cmdpal
|
||||
|
||||
# --- vcpkg detection + binary cache --------------------------------------
|
||||
# PowerToys consumes spdlog (and, over time, other native deps) via vcpkg in
|
||||
# manifest mode. steps-install-vcpkg.yml prefers the vcpkg shipped with
|
||||
# Visual Studio (Microsoft.VisualStudio.Component.Vcpkg) and falls back to a
|
||||
# fresh clone of microsoft/vcpkg into deps/vcpkg if VS doesn't have it.
|
||||
# Either way it sets the VCPKG_ROOT pipeline variable; MSBuild integration
|
||||
# is wired globally from Cpp.Build.props (with vcpkg.targets in
|
||||
# Cpp.Build.targets) using the three-tier VcpkgRoot fallback
|
||||
# (env var > VS-shipped > deps/vcpkg runtime clone).
|
||||
#
|
||||
# Vcpkg's MSBuild integration runs `vcpkg install` once per project, so the
|
||||
# binary cache below saves ~3-5 minutes per triplet on cache hits.
|
||||
- template: .\steps-install-vcpkg.yml
|
||||
parameters:
|
||||
useVSPreview: ${{ parameters.useVSPreview }}
|
||||
|
||||
- ${{ if eq(parameters.enablePackageCaching, true) }}:
|
||||
- task: Cache@2
|
||||
displayName: 'Cache vcpkg binary archives'
|
||||
inputs:
|
||||
# Key on the inputs vcpkg uses to compute its package ABI: the manifest,
|
||||
# configuration, every overlay-port file, and the agent OS.
|
||||
key: '"vcpkg" | "$(Agent.OS)" | vcpkg.json | vcpkg-configuration.json | deps/vcpkg-overlays/**'
|
||||
restoreKeys: |
|
||||
"vcpkg" | "$(Agent.OS)"
|
||||
"vcpkg"
|
||||
path: $(LOCALAPPDATA)\vcpkg\archives
|
||||
|
||||
|
||||
|
||||
- ${{ parameters.beforeBuildSteps }}
|
||||
@@ -426,6 +454,18 @@ jobs:
|
||||
msbuildArchitecture: x64
|
||||
maximumCpuCount: true
|
||||
|
||||
# CLI shims: publish ONE Native AOT binary and stage one exe per command name into
|
||||
# <BuildPlatform>\<BuildConfiguration>\cli so the files exist BEFORE the signing step
|
||||
# (ESRPSigning_core.json) and the installer harvest. The shared script owns the command
|
||||
# list (single source of truth) and the drift validation; it is the same code path the
|
||||
# local installer build (tools/build/build-installer.ps1) uses.
|
||||
- pwsh: |-
|
||||
& "$(Build.SourcesDirectory)\tools\build\publish-cli-shims.ps1" `
|
||||
-Platform '$(BuildPlatform)' `
|
||||
-Configuration '$(BuildConfiguration)' `
|
||||
-OutDir "$(Build.SourcesDirectory)\$(BuildPlatform)\$(BuildConfiguration)\cli"
|
||||
displayName: Publish CLI shims (Native AOT)
|
||||
|
||||
### HACK: On ARM64 builds, building an app with Windows App SDK copies the x64 WebView2 dll instead of the ARM64 one. This task makes sure the right dll is used.
|
||||
- task: CopyFiles@2
|
||||
displayName: HACK Copy core WebView2 ARM64 dll to output directory
|
||||
|
||||
@@ -15,6 +15,9 @@ parameters:
|
||||
- name: signingIdentity
|
||||
type: object
|
||||
default: {}
|
||||
- name: beforeBuildSteps
|
||||
type: stepList
|
||||
default: []
|
||||
|
||||
jobs:
|
||||
- job: "BuildSDK"
|
||||
@@ -45,6 +48,8 @@ jobs:
|
||||
parameters:
|
||||
directory: $(build.sourcesdirectory)\src\modules\cmdpal
|
||||
|
||||
- ${{ parameters.beforeBuildSteps }}
|
||||
|
||||
- pwsh: |-
|
||||
& "$(build.sourcesdirectory)\src\modules\cmdpal\extensionsdk\nuget\BuildSDKHelper.ps1" -Configuration "Release" -BuildStep "build" -IsAzurePipelineBuild
|
||||
displayName: Build SDK
|
||||
|
||||
6
.pipelines/v2/templates/steps-install-terrapin.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
steps:
|
||||
- pwsh: |-
|
||||
nuget install -source "https://microsoft.pkgs.visualstudio.com/Dart/_packaging/PowerToysDependencies/nuget/v3/index.json" TerrapinRetrievalTool -Prerelease -OutputDirectory _trt -Config "$(Build.SourcesDirectory)\.pipelines\release-nuget.config"
|
||||
$TerrapinRetrievalToolPath = (Get-Item _trt\TerrapinRetrievalTool.*\win-x64\TerrapinRetrievalTool.exe).FullName
|
||||
Write-Host "##vso[task.setvariable variable=X_VCPKG_ASSET_SOURCES]x-script,${TerrapinRetrievalToolPath} -b https://vcpkg.storage.devpackages.microsoft.io/artifacts/ -a true -u None -p {url} -s {sha512} -d {dst};x-block-origin"
|
||||
displayName: Set up the Terrapin Retrieval Tool (vcpkg cache)
|
||||
41
.pipelines/v2/templates/steps-install-vcpkg.yml
Normal file
@@ -0,0 +1,41 @@
|
||||
# Adapted from microsoft/terminal build/pipelines/templates-v2/steps-install-vcpkg.yml.
|
||||
#
|
||||
# Detects vcpkg from (in order):
|
||||
# 1. The Visual Studio installation (Microsoft.VisualStudio.Component.Vcpkg,
|
||||
# declared in the repo-root .vsconfig).
|
||||
# 2. A local clone at deps/vcpkg, cloned and bootstrapped on demand.
|
||||
#
|
||||
# Sets the pipeline-scoped VCPKG_ROOT variable; the rest of the build
|
||||
# resolves vcpkg through it (see the three-tier VcpkgRoot fallback in
|
||||
# Cpp.Build.props). No repo-level vcpkg submodule required.
|
||||
parameters:
|
||||
- name: useVSPreview
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
steps:
|
||||
- pwsh: |-
|
||||
# vswhere -prerelease is opt-in via the useVSPreview parameter so CI on
|
||||
# stable VS doesn't accidentally pick up a Preview install when both
|
||||
# are present. Matches the existing useVSPreview plumbing for
|
||||
# verifyAndSetLatestVCToolsVersion.ps1.
|
||||
$vswhereArgs = @('-latest', '-requires', 'Microsoft.VisualStudio.Component.Vcpkg', '-property', 'installationPath')
|
||||
$useVSPreview = '${{ parameters.useVSPreview }}' -eq 'True'
|
||||
if ($useVSPreview) { $vswhereArgs = @('-prerelease') + $vswhereArgs }
|
||||
$VsInstallRoot = & 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe' @vswhereArgs
|
||||
If ([String]::IsNullOrEmpty($VsInstallRoot)) {
|
||||
Remove-Item -Recurse -Force deps/vcpkg -ErrorAction:Ignore
|
||||
git clone https://github.com/microsoft/vcpkg deps/vcpkg
|
||||
if ($LASTEXITCODE -ne 0) { throw "git clone vcpkg failed (exit $LASTEXITCODE)" }
|
||||
Push-Location deps/vcpkg
|
||||
& ./bootstrap-vcpkg.bat -disableMetrics
|
||||
if ($LASTEXITCODE -ne 0) { Pop-Location; throw "bootstrap-vcpkg failed (exit $LASTEXITCODE)" }
|
||||
$VcpkgRoot = $PWD
|
||||
Pop-Location
|
||||
Write-Host "Using vcpkg from local checkout ($VcpkgRoot)"
|
||||
} Else {
|
||||
$VcpkgRoot = Join-Path $VsInstallRoot 'VC\vcpkg'
|
||||
Write-Host "Using vcpkg from Visual Studio installation ($VcpkgRoot)"
|
||||
}
|
||||
Write-Host "##vso[task.setvariable variable=VCPKG_ROOT]$VcpkgRoot"
|
||||
displayName: Detect VS vcpkg or bootstrap locally
|
||||
@@ -48,6 +48,11 @@ foreach ($csprojFile in $csprojFilesArray) {
|
||||
continue
|
||||
}
|
||||
|
||||
# The PowerAccent.Common project does not target WinRT, so skip it
|
||||
if ($csprojFile -like '*PowerAccent.Common.csproj') {
|
||||
continue
|
||||
}
|
||||
|
||||
$importExists = Test-ImportSharedCsWinRTProps -filePath $csprojFile
|
||||
if (!$importExists) {
|
||||
Write-Output "$csprojFile need to import 'Common.Dotnet.CsWinRT.props'."
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
"Microsoft.VisualStudio.Component.VC.ATL.ARM64.Spectre",
|
||||
"Microsoft.VisualStudio.Component.VC.ATL",
|
||||
"Microsoft.VisualStudio.Component.VC.ATL.Spectre",
|
||||
"Microsoft.VisualStudio.Component.Vcpkg",
|
||||
"Microsoft.VisualStudio.ComponentGroup.WindowsAppSDK.Cs"
|
||||
]
|
||||
}
|
||||
@@ -39,7 +39,8 @@
|
||||
<PropertyGroup>
|
||||
<PreferredToolArchitecture>x64</PreferredToolArchitecture>
|
||||
<PreferredToolArchitecture Condition="'$(PROCESSOR_ARCHITECTURE)' == 'ARM64' or '$(PROCESSOR_ARCHITEW6432)' == 'ARM64'">arm64</PreferredToolArchitecture>
|
||||
<VcpkgEnabled>false</VcpkgEnabled>
|
||||
<!-- vcpkg.targets is imported via Cpp.Build.targets after Microsoft.Cpp.targets. -->
|
||||
<ForceImportAfterCppTargets>$(MSBuildThisFileDirectory)Cpp.Build.targets</ForceImportAfterCppTargets>
|
||||
<ReplaceWildcardsInProjectItems>true</ReplaceWildcardsInProjectItems>
|
||||
<ExternalIncludePath>$(MSBuildThisFileDirectory)deps;$(MSBuildThisFileDirectory)packages;$(ExternalIncludePath)</ExternalIncludePath>
|
||||
<!-- Enable control flow guard for C++ projects that don't consume any C++ files -->
|
||||
@@ -121,6 +122,48 @@
|
||||
<SpectreMitigation>Spectre</SpectreMitigation>
|
||||
</PropertyGroup>
|
||||
|
||||
<!--
|
||||
vcpkg integration. Set globally and loaded before Microsoft.Cpp.props (via
|
||||
ForceImportBeforeCppProps) so that vcpkg.props' ClCompile hook is in place
|
||||
before the C++ targets run. VcpkgRoot is resolved via the same three-tier
|
||||
fallback used by microsoft/terminal (env var → VS-shipped → deps/vcpkg).
|
||||
-->
|
||||
<PropertyGroup Label="vcpkg">
|
||||
<VcpkgEnabled>true</VcpkgEnabled>
|
||||
<VcpkgEnableManifest>true</VcpkgEnableManifest>
|
||||
<VcpkgManifestEnabled>true</VcpkgManifestEnabled>
|
||||
<VcpkgManifestRoot>$(MSBuildThisFileDirectory)</VcpkgManifestRoot>
|
||||
<VcpkgOSTarget>windows</VcpkgOSTarget>
|
||||
<VcpkgUseStatic>true</VcpkgUseStatic>
|
||||
<!--
|
||||
Force VcpkgConfiguration to follow $(Configuration). Without this,
|
||||
vcpkg.props infers VcpkgConfiguration from $(UseDebugLibraries), which
|
||||
Microsoft.Cpp.Default.props has already defaulted to 'false' by the
|
||||
time vcpkg.props is imported here (the PowerToys-wide Debug override
|
||||
below runs LATER). That would silently link the Release-built spdlog
|
||||
into Debug consumers and trigger LNK2038 (MT/MTd, _ITERATOR_DEBUG_LEVEL).
|
||||
-->
|
||||
<VcpkgConfiguration>$(Configuration)</VcpkgConfiguration>
|
||||
<!-- vcpkg validates triplets case-sensitively; PowerToys uses ARM64 capital-case. -->
|
||||
<VcpkgPlatformTarget Condition="'$(Platform)' == 'ARM64'">arm64</VcpkgPlatformTarget>
|
||||
<VcpkgApplocalDeps>false</VcpkgApplocalDeps>
|
||||
<VcpkgInstalledDir>$(MSBuildThisFileDirectory)vcpkg_installed\$(Platform)\</VcpkgInstalledDir>
|
||||
<VcpkgRoot Condition="'$(VcpkgRoot)' == ''">$(VCPKG_ROOT)</VcpkgRoot>
|
||||
<VcpkgRoot Condition="'$(VcpkgRoot)' == '' and '$(VsInstallRoot)' != ''">$(VsInstallRoot)\VC\vcpkg</VcpkgRoot>
|
||||
<VcpkgRoot Condition="'$(VcpkgRoot)' == '' or !Exists('$(VcpkgRoot)\vcpkg.exe')">$(MSBuildThisFileDirectory)deps\vcpkg</VcpkgRoot>
|
||||
<CAExcludePath>$(CAExcludePath);$(VcpkgInstalledDir)</CAExcludePath>
|
||||
<VCPkgLocalAppDataDisabled>true</VCPkgLocalAppDataDisabled>
|
||||
</PropertyGroup>
|
||||
<!-- Fail fast with an actionable message instead of opaque C1083 spdlog/spdlog.h errors. -->
|
||||
<Target Name="PowerToysEnsureVcpkgAvailable"
|
||||
BeforeTargets="PrepareForBuild"
|
||||
Condition="'$(MSBuildProjectExtension)' == '.vcxproj' and '$(VcpkgEnabled)' == 'true' and !Exists('$(VcpkgRoot)\scripts\buildsystems\msbuild\vcpkg.props')">
|
||||
<Error Text="PowerToys requires the 'vcpkg' Visual Studio component, but it was not found.%0A%0AOpen the Visual Studio Installer, click Modify on your VS install, search for 'vcpkg', enable 'C++ vcpkg package manager', and click Modify. (Visual Studio will also prompt you to install missing .vsconfig components when you open PowerToys.slnx.)%0A%0AIf you have vcpkg installed elsewhere, set the VCPKG_ROOT environment variable to its root before building.%0A%0ASearched: '$(VcpkgRoot)\scripts\buildsystems\msbuild\vcpkg.props'" />
|
||||
</Target>
|
||||
<Import Project="$(VcpkgRoot)\scripts\buildsystems\msbuild\vcpkg.props"
|
||||
Condition="'$(MSBuildProjectExtension)' == '.vcxproj' and Exists('$(VcpkgRoot)\scripts\buildsystems\msbuild\vcpkg.props')" />
|
||||
|
||||
|
||||
<!-- Debug/Release props -->
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
|
||||
16
Cpp.Build.targets
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project>
|
||||
<!--
|
||||
PowerToys global C++ post-targets. Wired in via
|
||||
<ForceImportAfterCppTargets> in Cpp.Build.props so MSBuild loads this
|
||||
file AFTER Microsoft.Cpp.targets for every .vcxproj.
|
||||
|
||||
Conditionally imports vcpkg.targets to hook ClCompile into vcpkg's
|
||||
VcpkgInstallManifestDependencies target so spdlog headers are
|
||||
auto-discovered on the include path and spdlog.lib is auto-linked.
|
||||
vcpkg.props is imported in Cpp.Build.props (before Microsoft.Cpp.props);
|
||||
vcpkg.targets needs the matching "after" hook here.
|
||||
-->
|
||||
<Import Project="$(VcpkgRoot)\scripts\buildsystems\msbuild\vcpkg.targets"
|
||||
Condition="'$(MSBuildProjectExtension)' == '.vcxproj' and '$(VcpkgEnabled)' == 'true' and Exists('$(VcpkgRoot)\scripts\buildsystems\msbuild\vcpkg.targets')" />
|
||||
</Project>
|
||||
@@ -41,34 +41,34 @@
|
||||
<PackageVersion Include="MessagePack" Version="3.1.3" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="10.0.102" />
|
||||
<PackageVersion Include="Microsoft.CommandPalette.Extensions" Version="0.9.260303001" />
|
||||
<PackageVersion Include="Microsoft.Data.Sqlite" Version="10.0.7" />
|
||||
<PackageVersion Include="Microsoft.Data.Sqlite" Version="10.0.8" />
|
||||
<!-- Including Microsoft.Bcl.AsyncInterfaces to force version, since it's used by Microsoft.SemanticKernel. -->
|
||||
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="10.0.7" />
|
||||
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="10.0.8" />
|
||||
<PackageVersion Include="Microsoft.Graphics.Win2D" Version="1.3.2" />
|
||||
<PackageVersion Include="Microsoft.Windows.CppWinRT" Version="2.0.250303.1" />
|
||||
<PackageVersion Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="3.1.16" />
|
||||
<PackageVersion Include="Microsoft.Extensions.AI" Version="9.9.1" />
|
||||
<PackageVersion Include="Microsoft.Extensions.AI.OpenAI" Version="9.9.1-preview.1.25474.6" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="10.0.7" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="10.0.7" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.7" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="10.0.7" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.7" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="10.0.7" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting.WindowsServices" Version="10.0.7" />
|
||||
<PackageVersion Include="Microsoft.Extensions.AI" Version="10.2.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.AI.OpenAI" Version="10.0.1-preview.1.25571.5" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="10.0.8" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="10.0.8" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.8" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="10.0.8" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.8" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="10.0.8" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting.WindowsServices" Version="10.0.8" />
|
||||
<PackageVersion Include="Microsoft.AI.Foundry.Local" Version="0.3.0" />
|
||||
<PackageVersion Include="Microsoft.SemanticKernel" Version="1.66.0" />
|
||||
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.OpenAI" Version="1.66.0" />
|
||||
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.AzureAIInference" Version="1.66.0-beta" />
|
||||
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.Google" Version="1.66.0-alpha" />
|
||||
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.MistralAI" Version="1.66.0-alpha" />
|
||||
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.Ollama" Version="1.66.0-alpha" />
|
||||
<PackageVersion Include="Microsoft.SemanticKernel" Version="1.71.0" />
|
||||
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.OpenAI" Version="1.71.0" />
|
||||
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.AzureAIInference" Version="1.71.0-beta" />
|
||||
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.Google" Version="1.71.0-alpha" />
|
||||
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.MistralAI" Version="1.71.0-alpha" />
|
||||
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.Ollama" Version="1.71.0-alpha" />
|
||||
<PackageVersion Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.2" />
|
||||
<PackageVersion Include="Microsoft.Web.WebView2" Version="1.0.3719.77" />
|
||||
<!-- Package Microsoft.Win32.SystemEvents added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.1. This is a dependency of System.Drawing.Common but the 8.0.1 version wasn't published to nuget. -->
|
||||
<PackageVersion Include="Microsoft.Win32.SystemEvents" Version="10.0.7" />
|
||||
<PackageVersion Include="Microsoft.Win32.SystemEvents" Version="10.0.8" />
|
||||
<PackageVersion Include="Microsoft.WindowsPackageManager.ComInterop" Version="1.10.340" />
|
||||
<PackageVersion Include="Microsoft.Windows.Compatibility" Version="10.0.7" />
|
||||
<PackageVersion Include="Microsoft.Windows.Compatibility" Version="10.0.8" />
|
||||
<PackageVersion Include="Microsoft.Windows.CsWin32" Version="0.3.269" />
|
||||
<!-- CsWinRT version needs to be set to have a WinRT.Runtime.dll at the same version contained inside the NET SDK we're currently building on CI. -->
|
||||
<!--
|
||||
@@ -90,11 +90,12 @@
|
||||
<PackageVersion Include="MSTest" Version="$(MSTestVersion)" />
|
||||
<PackageVersion Include="MSTest.TestFramework" Version="$(MSTestVersion)" />
|
||||
<PackageVersion Include="NJsonSchema" Version="11.4.0" />
|
||||
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageVersion Include="Newtonsoft.Json" Version="13.0.4" />
|
||||
<PackageVersion Include="NLog" Version="5.2.8" />
|
||||
<PackageVersion Include="NLog.Extensions.Logging" Version="5.3.8" />
|
||||
<PackageVersion Include="NLog.Schema" Version="5.2.8" />
|
||||
<PackageVersion Include="OpenAI" Version="2.5.0" />
|
||||
<PackageVersion Include="OpenAI" Version="2.7.0" />
|
||||
<PackageVersion Include="Polly.Core" Version="8.6.5" />
|
||||
<PackageVersion Include="ReverseMarkdown" Version="4.1.0" />
|
||||
<PackageVersion Include="RtfPipe" Version="2.0.7677.4303" />
|
||||
<PackageVersion Include="ScipBe.Common.Office.OneNote" Version="3.0.1" />
|
||||
@@ -105,28 +106,28 @@
|
||||
<PackageVersion Include="StreamJsonRpc" Version="2.21.69" />
|
||||
<PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
|
||||
<!-- Package System.CodeDom added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.1. This is a dependency of System.Management but the 8.0.1 version wasn't published to nuget. -->
|
||||
<PackageVersion Include="System.CodeDom" Version="10.0.7" />
|
||||
<PackageVersion Include="System.CodeDom" Version="10.0.8" />
|
||||
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
|
||||
<PackageVersion Include="System.ComponentModel.Composition" Version="10.0.7" />
|
||||
<PackageVersion Include="System.Configuration.ConfigurationManager" Version="10.0.7" />
|
||||
<PackageVersion Include="System.Data.OleDb" Version="10.0.7" />
|
||||
<PackageVersion Include="System.ComponentModel.Composition" Version="10.0.8" />
|
||||
<PackageVersion Include="System.Configuration.ConfigurationManager" Version="10.0.8" />
|
||||
<PackageVersion Include="System.Data.OleDb" Version="10.0.8" />
|
||||
<!-- Package System.Diagnostics.EventLog added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.1. This is a dependency of System.Data.OleDb but the 8.0.1 version wasn't published to nuget. -->
|
||||
<PackageVersion Include="System.Diagnostics.EventLog" Version="10.0.7" />
|
||||
<PackageVersion Include="System.Diagnostics.EventLog" Version="10.0.8" />
|
||||
<!-- Package System.Diagnostics.PerformanceCounter added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.11. -->
|
||||
<PackageVersion Include="System.Diagnostics.PerformanceCounter" Version="10.0.7" />
|
||||
<PackageVersion Include="System.ClientModel" Version="1.7.0" />
|
||||
<PackageVersion Include="System.Drawing.Common" Version="10.0.7" />
|
||||
<PackageVersion Include="System.Diagnostics.PerformanceCounter" Version="10.0.8" />
|
||||
<PackageVersion Include="System.ClientModel" Version="1.8.1" />
|
||||
<PackageVersion Include="System.Drawing.Common" Version="10.0.8" />
|
||||
<PackageVersion Include="System.IO.Abstractions" Version="22.0.13" />
|
||||
<PackageVersion Include="System.IO.Abstractions.TestingHelpers" Version="22.0.13" />
|
||||
<PackageVersion Include="System.Management" Version="10.0.7" />
|
||||
<PackageVersion Include="System.Management" Version="10.0.8" />
|
||||
<PackageVersion Include="System.Net.Http" Version="4.3.4" />
|
||||
<PackageVersion Include="System.Numerics.Tensors" Version="9.0.11" />
|
||||
<PackageVersion Include="System.Numerics.Tensors" Version="10.0.2" />
|
||||
<PackageVersion Include="System.Private.Uri" Version="4.3.2" />
|
||||
<PackageVersion Include="System.Reactive" Version="6.0.1" />
|
||||
<PackageVersion Include="System.Runtime.Caching" Version="10.0.7" />
|
||||
<PackageVersion Include="System.ServiceProcess.ServiceController" Version="10.0.7" />
|
||||
<PackageVersion Include="System.Text.Encoding.CodePages" Version="10.0.7" />
|
||||
<PackageVersion Include="System.Text.Json" Version="10.0.7" />
|
||||
<PackageVersion Include="System.Runtime.Caching" Version="10.0.8" />
|
||||
<PackageVersion Include="System.ServiceProcess.ServiceController" Version="10.0.8" />
|
||||
<PackageVersion Include="System.Text.Encoding.CodePages" Version="10.0.8" />
|
||||
<PackageVersion Include="System.Text.Json" Version="10.0.8" />
|
||||
<PackageVersion Include="System.Text.RegularExpressions" Version="4.3.1" />
|
||||
<PackageVersion Include="ToolGood.Words.Pinyin" Version="3.1.0.3" />
|
||||
<PackageVersion Include="UnicodeInformation" Version="2.6.0" />
|
||||
@@ -134,8 +135,8 @@
|
||||
<PackageVersion Include="UTF.Unknown" Version="2.6.0" />
|
||||
<PackageVersion Include="WinUIEx" Version="2.8.0" />
|
||||
<PackageVersion Include="WmiLight" Version="6.14.0" />
|
||||
<PackageVersion Include="WPF-UI" Version="3.0.5" />
|
||||
<PackageVersion Include="WyHash" Version="1.0.5" />
|
||||
<PackageVersion Include="YamlDotNet" Version="16.3.0" />
|
||||
<PackageVersion Include="WixToolset.Heat" Version="5.0.2" />
|
||||
<PackageVersion Include="WixToolset.Firewall.wixext" Version="5.0.2" />
|
||||
<PackageVersion Include="WixToolset.Util.wixext" Version="5.0.2" />
|
||||
|
||||
@@ -1600,5 +1600,5 @@ SOFTWARE.
|
||||
- UTF.Unknown
|
||||
- WinUIEx
|
||||
- WmiLight
|
||||
- WPF-UI
|
||||
- WyHash
|
||||
- YamlDotNet
|
||||
@@ -57,6 +57,7 @@
|
||||
<Project Path="src/common/UnitTests-CommonLib/UnitTests-CommonLib.vcxproj" Id="1a066c63-64b3-45f8-92fe-664e1cce8077" />
|
||||
<Project Path="src/common/UnitTests-CommonUtils/UnitTests-CommonUtils.vcxproj" Id="8b5cfb38-ccba-40a8-ad7a-89c57b070884" />
|
||||
<Project Path="src/common/updating/updating.vcxproj" Id="17da04df-e393-4397-9cf0-84dabe11032e" />
|
||||
<Project Path="src/common/updating/UnitTests/UpdatingUnitTests.vcxproj" Id="a1b2c3d4-e5f6-7890-abcd-ef1234567890" />
|
||||
<Project Path="src/common/version/version.vcxproj" Id="cc6e41ac-8174-4e8a-8d22-85dd7f4851df" />
|
||||
</Folder>
|
||||
<Folder Name="/common/interop/">
|
||||
@@ -67,10 +68,7 @@
|
||||
<Project Path="src/common/interop/PowerToys.Interop.vcxproj" Id="f055103b-f80b-4d0c-bf48-057c55620033" />
|
||||
</Folder>
|
||||
<Folder Name="/common/log/">
|
||||
<Project Path="src/common/logger/logger.vcxproj" Id="d9b8fc84-322a-4f9f-bbb9-20915c47ddfd">
|
||||
<BuildDependency Project="src/logging/logging.vcxproj" />
|
||||
</Project>
|
||||
<Project Path="src/logging/logging.vcxproj" Id="7e1e3f13-2bd6-3f75-a6a7-873a2b55c60f" />
|
||||
<Project Path="src/common/logger/logger.vcxproj" Id="d9b8fc84-322a-4f9f-bbb9-20915c47ddfd" />
|
||||
</Folder>
|
||||
<Folder Name="/common/notifications/">
|
||||
<Project Path="src/common/notifications/BackgroundActivator/BackgroundActivator.vcxproj" Id="0b593a6c-4143-4337-860e-db5710fb87db" />
|
||||
@@ -801,6 +799,14 @@
|
||||
<Project Path="src/modules/peek/peek/peek.vcxproj" Id="a1425b53-3d61-4679-8623-e64a0d3d0a48" />
|
||||
</Folder>
|
||||
<Folder Name="/modules/PowerAccent/">
|
||||
<Project Path="src/modules/poweraccent/PowerAccent.Common.UnitTests/PowerAccent.Common.UnitTests.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/poweraccent/PowerAccent.Common/PowerAccent.Common.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/poweraccent/PowerAccent.Core/PowerAccent.Core.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
@@ -989,9 +995,16 @@
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
</Folder>
|
||||
<Folder Name="/modules/shortcutguide/">
|
||||
<Project Path="src/modules/ShortcutGuide/ShortcutGuide/ShortcutGuide.vcxproj" Id="2edb3eb4-fa92-4bff-b2d8-566584837231" />
|
||||
<Project Path="src/modules/ShortcutGuide/ShortcutGuideModuleInterface/ShortcutGuideModuleInterface.vcxproj" Id="2d604c07-51fc-46bb-9eb7-75aecc7f5e81" />
|
||||
<Folder Name="/modules/ShortcutGuide/">
|
||||
<Project Path="src/modules/ShortcutGuide/ShortcutGuide.IndexYmlGenerator/ShortcutGuide.IndexYmlGenerator.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuide.Ui.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/ShortcutGuide/ShortcutGuideModuleInterface/ShortcutGuideModuleInterface.vcxproj" Id="e487304a-b1fb-4e6b-8e70-014051af5b99" />
|
||||
</Folder>
|
||||
<Folder Name="/modules/Workspaces/">
|
||||
<Project Path="src/modules/Workspaces/Workspaces.ModuleServices/Workspaces.ModuleServices.csproj">
|
||||
@@ -1125,4 +1138,14 @@
|
||||
</Project>
|
||||
<Project Path="src/Update/PowerToys.Update.vcxproj" Id="44ce9ae1-4390-42c5-bacc-0fd6b40aa203" />
|
||||
<Project Path="tools/project_template/ModuleTemplate/ModuleTemplateCompileTest.vcxproj" Id="64a80062-4d8b-4229-8a38-dfa1d7497749" />
|
||||
<Project Path="tools/CliShim/CliShim.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="tools/CliShim.UnitTests/CliShim.UnitTests.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
</Solution>
|
||||
|
||||
|
||||
|
||||
1
deps/expected-lite
vendored
7
deps/expected.props
vendored
@@ -1,7 +0,0 @@
|
||||
<Project>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)expected-lite\include\nonstd\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
</Project>
|
||||
1
deps/spdlog
vendored
94
deps/spdlog-msvc-fix/include/spdlog-msvc-fix.h
vendored
@@ -1,94 +0,0 @@
|
||||
// spdlog-msvc-fix.h
|
||||
//
|
||||
// Workaround for MSVC 14.51 (compiler version 19.51, _MSC_VER >= 1951) removing
|
||||
// stdext::checked_array_iterator. Force-included for all spdlog consumers via
|
||||
// deps/spdlog.props, because spdlog v1.8.5's bundled fmt format.h(357) still
|
||||
// references this type inside #if defined(_SECURE_SCL) && _SECURE_SCL -- a
|
||||
// branch entered in Debug builds where _ITERATOR_DEBUG_LEVEL > 0.
|
||||
//
|
||||
// On MSVC 14.50 and earlier, the type still exists in <iterator>, so this shim
|
||||
// is a no-op via the _MSC_VER guard. On MSVC 14.51+, it provides a minimal
|
||||
// pointer-backed substitute that satisfies the bundled fmt's usage:
|
||||
//
|
||||
// template <typename T> using checked_ptr = stdext::checked_array_iterator<T*>;
|
||||
// template <typename T> checked_ptr<T> make_checked(T* p, size_t size) {
|
||||
// return {p, size};
|
||||
// }
|
||||
// ... return make_checked(get_data(c) + size, n);
|
||||
//
|
||||
// When deps/spdlog is bumped past v1.14 (which ships fmt 10.2 and drops this
|
||||
// dependency), this shim and its <ForcedIncludeFiles> entry in deps/spdlog.props
|
||||
// can be deleted.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(__cplusplus) && defined(_MSC_VER) && _MSC_VER >= 1951
|
||||
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
|
||||
namespace stdext
|
||||
{
|
||||
template <typename _Ptr>
|
||||
class checked_array_iterator
|
||||
{
|
||||
_Ptr _Myarray = nullptr;
|
||||
std::size_t _Mysize = 0;
|
||||
std::size_t _Myindex = 0;
|
||||
|
||||
public:
|
||||
using iterator_category = std::random_access_iterator_tag;
|
||||
using value_type = std::remove_cv_t<std::remove_pointer_t<_Ptr>>;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = _Ptr;
|
||||
using reference = std::remove_pointer_t<_Ptr>&;
|
||||
|
||||
constexpr checked_array_iterator() = default;
|
||||
|
||||
constexpr checked_array_iterator(_Ptr arr, std::size_t size, std::size_t idx = 0) noexcept
|
||||
: _Myarray(arr), _Mysize(size), _Myindex(idx)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr reference operator*() const noexcept { return _Myarray[_Myindex]; }
|
||||
constexpr pointer operator->() const noexcept { return _Myarray + _Myindex; }
|
||||
constexpr reference operator[](difference_type n) const noexcept
|
||||
{
|
||||
return _Myarray[_Myindex + static_cast<std::size_t>(n)];
|
||||
}
|
||||
|
||||
constexpr checked_array_iterator& operator++() noexcept { ++_Myindex; return *this; }
|
||||
constexpr checked_array_iterator operator++(int) noexcept { auto t = *this; ++_Myindex; return t; }
|
||||
constexpr checked_array_iterator& operator--() noexcept { --_Myindex; return *this; }
|
||||
constexpr checked_array_iterator operator--(int) noexcept { auto t = *this; --_Myindex; return t; }
|
||||
|
||||
constexpr checked_array_iterator& operator+=(difference_type n) noexcept
|
||||
{
|
||||
_Myindex = static_cast<std::size_t>(static_cast<difference_type>(_Myindex) + n);
|
||||
return *this;
|
||||
}
|
||||
constexpr checked_array_iterator& operator-=(difference_type n) noexcept
|
||||
{
|
||||
_Myindex = static_cast<std::size_t>(static_cast<difference_type>(_Myindex) - n);
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend constexpr checked_array_iterator operator+(checked_array_iterator it, difference_type n) noexcept { it += n; return it; }
|
||||
friend constexpr checked_array_iterator operator+(difference_type n, checked_array_iterator it) noexcept { return it + n; }
|
||||
friend constexpr checked_array_iterator operator-(checked_array_iterator it, difference_type n) noexcept { it -= n; return it; }
|
||||
friend constexpr difference_type operator-(checked_array_iterator a, checked_array_iterator b) noexcept
|
||||
{
|
||||
return static_cast<difference_type>(a._Myindex) - static_cast<difference_type>(b._Myindex);
|
||||
}
|
||||
|
||||
friend constexpr bool operator==(checked_array_iterator a, checked_array_iterator b) noexcept { return a._Myindex == b._Myindex; }
|
||||
friend constexpr bool operator!=(checked_array_iterator a, checked_array_iterator b) noexcept { return !(a == b); }
|
||||
friend constexpr bool operator<(checked_array_iterator a, checked_array_iterator b) noexcept { return a._Myindex < b._Myindex; }
|
||||
friend constexpr bool operator>(checked_array_iterator a, checked_array_iterator b) noexcept { return b < a; }
|
||||
friend constexpr bool operator<=(checked_array_iterator a, checked_array_iterator b) noexcept { return !(b < a); }
|
||||
friend constexpr bool operator>=(checked_array_iterator a, checked_array_iterator b) noexcept { return !(a < b); }
|
||||
};
|
||||
} // namespace stdext
|
||||
|
||||
#endif // __cplusplus && _MSC_VER >= 1951
|
||||
19
deps/spdlog.props
vendored
@@ -1,9 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)spdlog\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>SPDLOG_WCHAR_TO_UTF8_SUPPORT;SPDLOG_COMPILED_LIB;SPDLOG_WCHAR_FILENAMES;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ForcedIncludeFiles>$(MSBuildThisFileDirectory)spdlog-msvc-fix\include\spdlog-msvc-fix.h;%(ForcedIncludeFiles)</ForcedIncludeFiles>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<!--
|
||||
SPDLOG_* preprocessor defines for spdlog consumers. The actual vcpkg
|
||||
integration (VcpkgEnabled, VcpkgRoot, triplet, manifest install) lives
|
||||
in Cpp.Build.props; this file just carries the defines that match how
|
||||
the pre-vcpkg in-tree build was configured.
|
||||
-->
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>SPDLOG_WCHAR_TO_UTF8_SUPPORT;SPDLOG_COMPILED_LIB;SPDLOG_WCHAR_FILENAMES;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
</Project>
|
||||
|
||||
16
deps/vcpkg-overlays/spdlog/msvc-14.51-stdext-checked-array-iterator.patch
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
--- a/include/spdlog/fmt/bundled/format.h
|
||||
+++ b/include/spdlog/fmt/bundled/format.h
|
||||
@@ -354,7 +354,12 @@ inline typename Container::value_type* get_data(Container& c) {
|
||||
return c.data();
|
||||
}
|
||||
|
||||
-#if defined(_SECURE_SCL) && _SECURE_SCL
|
||||
+// PowerToys: stdext::checked_array_iterator was deprecated in VS 2019 16.10
|
||||
+// and removed entirely in MSVC 14.51 (compiler 19.51, _MSC_VER >= 1951;
|
||||
+// see microsoft/STL STL4043). Skip the broken branch on those toolsets so the
|
||||
+// pointer-based fallback below is used instead. Drop this guard once
|
||||
+// deps/spdlog is bumped past v1.14 (which ships fmt 10.2 and removes this code).
|
||||
+#if defined(_SECURE_SCL) && _SECURE_SCL && (!defined(_MSC_VER) || _MSC_VER < 1951)
|
||||
// Make a checked iterator to avoid MSVC warnings.
|
||||
template <typename T> using checked_ptr = stdext::checked_array_iterator<T*>;
|
||||
template <typename T> checked_ptr<T> make_checked(T* p, size_t size) {
|
||||
43
deps/vcpkg-overlays/spdlog/portfile.cmake
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
# PowerToys overlay port for spdlog.
|
||||
#
|
||||
# Pinned to the same git commit that the deleted deps/spdlog submodule pointed
|
||||
# at, so this is a 1:1 submodule->vcpkg migration with no version change
|
||||
# (per the maintainer guidance: convert one submodule at a time, atomic
|
||||
# commit, don't also bump the version).
|
||||
#
|
||||
# A single hunk patch works around MSVC 14.51 STL4043 (removal of
|
||||
# stdext::checked_array_iterator) in spdlog's bundled fmt 7. Drop this overlay
|
||||
# (and switch to upstream vcpkg's spdlog port) once PowerToys bumps spdlog
|
||||
# past v1.14, which ships fmt 10.2 and removes the affected code path.
|
||||
|
||||
vcpkg_from_github(
|
||||
OUT_SOURCE_PATH SOURCE_PATH
|
||||
REPO gabime/spdlog
|
||||
REF 616866fcf40340ea25a8f218369bad810ef58e72
|
||||
SHA512 2076c527c7768627e6856b2f7ef663b185fd6251894cffd9299203d00f3d2de5696461060442dd72b96c9d3f0fd27f7f63ad2edfdf295e9b06c5fac6d6212faf
|
||||
HEAD_REF v1.x
|
||||
PATCHES
|
||||
msvc-14.51-stdext-checked-array-iterator.patch
|
||||
)
|
||||
|
||||
vcpkg_cmake_configure(
|
||||
SOURCE_PATH "${SOURCE_PATH}"
|
||||
OPTIONS
|
||||
-DSPDLOG_BUILD_EXAMPLE=OFF
|
||||
-DSPDLOG_BUILD_TESTS=OFF
|
||||
-DSPDLOG_BUILD_BENCH=OFF
|
||||
-DSPDLOG_FMT_EXTERNAL=OFF
|
||||
-DSPDLOG_WCHAR_SUPPORT=ON
|
||||
-DSPDLOG_WCHAR_FILENAMES=ON
|
||||
-DSPDLOG_NO_EXCEPTIONS=OFF
|
||||
-DSPDLOG_BUILD_SHARED=OFF
|
||||
)
|
||||
|
||||
vcpkg_cmake_install()
|
||||
vcpkg_cmake_config_fixup(PACKAGE_NAME spdlog CONFIG_PATH lib/cmake/spdlog)
|
||||
vcpkg_fixup_pkgconfig()
|
||||
vcpkg_copy_pdbs()
|
||||
|
||||
file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include")
|
||||
|
||||
vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/LICENSE")
|
||||
18
deps/vcpkg-overlays/spdlog/vcpkg.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "spdlog",
|
||||
"version-string": "1.8.5-pt-616866fc",
|
||||
"port-version": 0,
|
||||
"description": "Very fast, header-only/compiled, C++ logging library. PowerToys overlay pinned to gabime/spdlog@616866fc (the exact submodule commit before this migration), with a single-hunk patch that works around MSVC 14.51 removing stdext::checked_array_iterator (STL4043).",
|
||||
"homepage": "https://github.com/gabime/spdlog",
|
||||
"license": "MIT",
|
||||
"dependencies": [
|
||||
{
|
||||
"name": "vcpkg-cmake",
|
||||
"host": true
|
||||
},
|
||||
{
|
||||
"name": "vcpkg-cmake-config",
|
||||
"host": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -97,6 +97,10 @@ The Shell Process Debugging Tool is a Visual Studio extension that helps debug m
|
||||
- Check Event Viewer for application crashes related to `PowerToys.Settings.exe`
|
||||
- Crash dumps can be obtained from Event Viewer
|
||||
|
||||
### Debugging Command Palette
|
||||
Command Palette can be easily debugged using the solution filter in `src/modules/cmdpal/Command Palette.slnf`. This will open Command Palette as its own Visual Studio solution that can be run and debugged directly in Visual Studio without the need for the Shell Process Debugging Tool.
|
||||
|
||||
|
||||
## Troubleshooting Build Errors
|
||||
|
||||
### Missing Image Files or Corrupted Build State
|
||||
|
||||
|
Before Width: | Height: | Size: 19 KiB |
@@ -9,12 +9,14 @@
|
||||
[Pull Requests](https://github.com/microsoft/PowerToys/pulls?q=is%3Apr+is%3Aopen+label%3A%22Product-Shortcut+Guide%22+)
|
||||
|
||||
## Overview
|
||||
Shortcut Guide is a PowerToy that displays an overlay of available keyboard shortcuts when the Windows key is pressed and held. It provides a visual reference for Windows key combinations, helping users discover and utilize built-in Windows shortcuts.
|
||||
Shortcut Guide is a PowerToy that displays an overlay of available keyboard shortcuts when a user-set keyboard shortcut is pressed. It helps users discover and remember keyboard shortcuts for Windows and apps.
|
||||
|
||||
> [!NOTE]
|
||||
> The spec for the manifest files is in development and will be linked here once available.
|
||||
|
||||
## Usage
|
||||
- Press and hold the Windows key to display the overlay of available shortcuts
|
||||
- Press the hotkey again to dismiss the overlay
|
||||
- The overlay displays Windows shortcuts with their corresponding actions
|
||||
- Press the user-defined hotkey to display the overlay
|
||||
- Press the hotkey again or press ESC to dismiss the overlay
|
||||
|
||||
## Build and Debug Instructions
|
||||
|
||||
@@ -25,67 +27,89 @@ Shortcut Guide is a PowerToy that displays an overlay of available keyboard shor
|
||||
4. The executable is named PowerToys.ShortcutGuide.exe
|
||||
|
||||
### Debug
|
||||
1. Right-click the ShortcutGuide project and select 'Set as Startup Project'
|
||||
1. Right-click the ShortcutGuide.Ui project and select 'Set as Startup Project'
|
||||
2. Right-click the project again and select 'Debug'
|
||||
|
||||
## Code Structure
|
||||
> [!NOTE]
|
||||
> When run in debug mode, the window behaves differently than in release mode. It will not automatically close when loosing focus, it will be displayed on top of all other windows, and it is not hidden from the taskbar.
|
||||
|
||||

|
||||
## Project Structure
|
||||
|
||||
### Core Files
|
||||
The Shortcut Guide module consists of the following 4 projects:
|
||||
|
||||
#### [`dllmain.cpp`](/src/modules/shortcut_guide/dllmain.cpp)
|
||||
Contains DLL boilerplate code. Implements the PowertoyModuleIface, including enable/disable functionality and GPO policy handling. Captures hotkey events and starts the PowerToys.ShortcutGuide.exe process to display the shortcut guide window.
|
||||
### [`ShortcutGuide.Ui`](/src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuide.Ui.csproj
|
||||
|
||||
#### [`shortcut_guide.cpp`](/src/modules/shortcut_guide/shortcut_guide.cpp)
|
||||
Contains the module interface code. It initializes the settings values and the keyboard event listener. Defines the OverlayWindow class, which manages the overall logic and event handling for the PowerToys Shortcut Guide.
|
||||
This is the main UI project for the Shortcut Guide module. Upon startup it does the following tasks:
|
||||
|
||||
#### [`overlay_window.cpp`](/src/modules/shortcut_guide/overlay_window.cpp)
|
||||
Contains the code for loading the SVGs, creating and rendering of the overlay window. Manages and displays overlay windows with SVG graphics through two main classes:
|
||||
- D2DOverlaySVG: Handles loading, resizing, and manipulation of SVG graphics
|
||||
- D2DOverlayWindow: Manages the display and behavior of the overlay window
|
||||
1. Copies the built-in manifest files to the users manifest directory (overwriting existing files).
|
||||
2. Generate the `index.yml` manifest file.
|
||||
3. Populate the PowerToys shortcut manifest with the user-defined shortcuts.
|
||||
4. Starts the UI.
|
||||
|
||||
#### [`keyboard_state.cpp`](/src/modules/shortcut_guide/keyboard_state.cpp)
|
||||
Contains helper methods for checking the current state of the keyboard.
|
||||
### Related files in PowerToys.Interop
|
||||
|
||||
#### [`target_state.cpp`](/src/modules/shortcut_guide/target_state.cpp)
|
||||
State machine that handles the keyboard events. It's responsible for deciding when to show the overlay, when to suppress the Start menu (if the overlay is displayed long enough), etc. Handles state transitions and synchronization to ensure the overlay is shown or hidden appropriately based on user interactions.
|
||||
#### [`excluded_app.cpp`](/src/modules/ShortcutGuide/ShortcutGuide.CPPProject/excluded_app.cpp)
|
||||
|
||||
#### [`trace.cpp`](/src/modules/shortcut_guide/trace.cpp)
|
||||
Contains code for telemetry.
|
||||
This file contains one function with the following signature:
|
||||
|
||||
### Supporting Files
|
||||
```cpp
|
||||
__declspec(dllexport) bool IsCurrentWindowExcludedFromShortcutGuide()
|
||||
```
|
||||
|
||||
#### [`animation.cpp`](/src/modules/shortcut_guide/animation.cpp)
|
||||
Handles the timing and interpolation of animations. Calculates the current value of an animation based on elapsed time and a specified easing function.
|
||||
This function checks if the current window is excluded from the Shortcut Guide overlay. It returns `true` if the current window is excluded otherwise it returns `false`.
|
||||
|
||||
#### [`d2d_svg.cpp`](/src/modules/shortcut_guide/d2d_svg.cpp)
|
||||
Provides functionality for loading, resizing, recoloring, rendering, and manipulating SVG images using Direct2D.
|
||||
#### [`tasklist_positions.cpp`](/src/modules/ShortcutGuide/ShortcutGuide.CPPProject/tasklist_positions.cpp)
|
||||
|
||||
#### [`d2d_text.cpp`](/src/modules/shortcut_guide/d2d_text.cpp)
|
||||
Handles creation, resizing, alignment, and rendering of text using Direct2D and DirectWrite.
|
||||
This file contains helper functions to retrieve the positions of the taskbar buttons. It exports the following function:
|
||||
|
||||
#### [`d2d_window.cpp`](/src/modules/shortcut_guide/d2d_window.cpp)
|
||||
Manages a window using Direct2D and Direct3D for rendering. Handles window creation, resizing, rendering, and destruction.
|
||||
```cpp
|
||||
__declspec(dllexport) TasklistButton* get_buttons(HMONITOR monitor, int* size)
|
||||
```
|
||||
|
||||
#### [`native_event_waiter.cpp`](/src/modules/shortcut_guide/native_event_waiter.cpp)
|
||||
Waits for a named event and executes a specified action when the event is triggered. Uses a separate thread to handle event waiting and action execution.
|
||||
This function retrieves the positions of the taskbar buttons for a given monitor. It returns an array of `TasklistButton` structures (max 10), which contain the position and size of each button.
|
||||
|
||||
#### [`tasklist_positions.cpp`](/src/modules/shortcut_guide/tasklist_positions.cpp)
|
||||
Handles retrieving and updating the positions and information of taskbar buttons in Windows.
|
||||
`monitor` must be the monitor handle of the monitor containing the taskbar instance of which the buttons should be retrieved.
|
||||
|
||||
#### [`main.cpp`](/src/modules/shortcut_guide/main.cpp)
|
||||
The entry point for the PowerToys Shortcut Guide application. Handles initialization, ensures single instance execution, manages parent process termination, creates and displays the overlay window, and runs the main event loop.
|
||||
`size` will contain the resulting array size.
|
||||
|
||||
It determines the positions through Windows `FindWindowEx` function.
|
||||
For the primary taskbar it searches for:
|
||||
* A window called "Shell_TrayWnd"
|
||||
* that contains a window called "ReBarWindow32"
|
||||
* that contains a window called "MSTaskSwWClass"
|
||||
* that contains a window called "MSTaskListWClass"
|
||||
|
||||
For any secondary taskbar it searches for:
|
||||
* A window called "Shell_SecondaryTrayWnd"
|
||||
* that contains a window called "WorkerW"
|
||||
* that contains a window called "MSTaskListWClass"
|
||||
|
||||
It then enumerates all the button elements inside "MSTaskListWClass" while skipping such with a same name (which implies the user does not use combining taskbar buttons)
|
||||
|
||||
If this method fails, which it will for newer versions of Windows, it falls back to searching for:
|
||||
* A window called "Shell_TrayWnd" or "Shell_SecondaryTrayWnd"
|
||||
* that contains a window called "Windows.UI.Composition.DesktopWindowContentBridge"
|
||||
* that contains a window called "Windows.UI.Input.InputSite.WindowClass"
|
||||
* the first child element
|
||||
|
||||
It then enumerates all the button elements inside the selected while skipping such with a same name (which implies the user does not use combining taskbar buttons) and such that do not start with "Appid:" (which are not actual taskbar buttons related to apps, but others like the widgets or the search button).
|
||||
|
||||
### [`ShortcutGuide.IndexYmlGenerator`](/src/modules/ShortcutGuide/ShortcutGuide.IndexYmlGenerator/)
|
||||
|
||||
This application generates the `index.yml` manifest file.
|
||||
|
||||
It is a separate project so that its code can be easier ported to WinGet in the future.
|
||||
|
||||
### [`ShortcutGuideModuleInterface`](/src/modules/ShortcutGuide/ShortcutGuideModuleInterface/ShortcutGuideModuleInterface.vcxproj)
|
||||
|
||||
The module interface that handles opening and closing the user interface.
|
||||
|
||||
## Features and Limitations
|
||||
|
||||
- The overlay displays Windows shortcuts (Windows key combinations)
|
||||
- The module supports localization, but only for the Windows controls on the left side of the overlay
|
||||
- Currently the displayed shortcuts (Except the ones from PowerToys) are not localized.
|
||||
- It's currently rated as a P3 (lower priority) module
|
||||
|
||||
## Future Development
|
||||
|
||||
A community-contributed version 2 is in development that will support:
|
||||
- Application-specific shortcuts based on the active application
|
||||
- Additional shortcuts beyond Windows key combinations
|
||||
- PowerToys shortcuts
|
||||
- Implementing with WinGet to get new shortcut manifest files
|
||||
- Adding localization support for the built-in manifest files
|
||||
|
Before Width: | Height: | Size: 275 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 89 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 8.4 KiB |
|
Before Width: | Height: | Size: 258 KiB After Width: | Height: | Size: 134 KiB |
318
doc/specs/WinGet Manifest Keyboard Shortcuts schema.md
Normal file
@@ -0,0 +1,318 @@
|
||||
# WinGet Manifest Keyboard Shortcuts schema
|
||||
|
||||
## 1 What this spec is about
|
||||
|
||||
This spec provides an extension to the existing [WinGet manifest schema](https://github.com/microsoft/winget-pkgs/blob/master/doc/manifest/README.md) in form of an additional yaml file, that describes keyboard shortcuts the application provides.
|
||||
|
||||
These yaml files are saved on a per-user base and so called manifest interpreters can then display these manifests in a human-friendly version.
|
||||
|
||||
### 1.1 What this spec is not about
|
||||
|
||||
This spec does not provide a way to back up or save user-defined keyboard shortcuts.
|
||||
|
||||
## 2 Save location of manifests
|
||||
|
||||
### 2.1 WinGet
|
||||
|
||||
These files are saved online along with the other manifest files in the [WinGet Package repository](https://github.com/microsoft/winget-pkgs).
|
||||
|
||||
### 2.2 Locally
|
||||
|
||||
All manifests and one index file are saved locally under `%LocalAppData%/Microsoft/WinGet/KeyboardShortcuts`. All apps are allowed to add their manifest files there. In addition Package Managers (like WinGet) and manifest interpreters (like PowerToys Shortcut Guide) can control and add other manifests themselves.
|
||||
|
||||
#### 2.2.1 Downloading manifests
|
||||
|
||||
When WinGet or other package managers download a package, they should also download the corresponding keyboard shortcuts manifest file and save it in the local directory, given such a file exists in the WinGet repository.
|
||||
|
||||
The downloader is also responsible for updating the local `index.yaml` file, which contains all the information about the different manifest files that are saved in the same directory.
|
||||
|
||||
#### 2.2.2 Updating manifests
|
||||
|
||||
When a manifest interpreter starts, it should download the latest version of the manifests from the WinGet repository and save them in the local directory. If a manifest interpreter is not able to download the manifests or they do not exist, it should use the locally saved manifests.
|
||||
|
||||
The updater is also responsible for updating the local `index.yaml` file, which contains all the information about the different manifest files that are saved in the same directory.
|
||||
|
||||
> Note: WinGet must provide a way to update the keyboard shortcuts manifests given a package id.
|
||||
|
||||
### 2.3 File names
|
||||
|
||||
The file name of a keyboard shortcuts file is the WinGet package identifier, plus the locale of the strings of the file and at last the `.KBSC.yaml` file extension.
|
||||
|
||||
For example the package "test.bar" saves its manifest with `en-US` strings in `test.bar.en-US.KBSC.yaml`.
|
||||
|
||||
#### 2.3.1 No winget package available
|
||||
|
||||
If an application has no corresponding WinGet package its name starts with a plus (`+`) symbol.
|
||||
|
||||
### 2.4 Reserved namespaces
|
||||
|
||||
Every name starting with `+WindowsNT` is reserved for the Windows OS and its components.
|
||||
|
||||
## 3 File syntax
|
||||
|
||||
All relevant files are written in [YAML](https://yaml.org/spec).
|
||||
|
||||
> Note: A JSON schema will be provided as soon as the spec reaches a further step
|
||||
|
||||
### 3.1 Manifest Schema vNext Keyboard Shortcuts File
|
||||
|
||||
```
|
||||
PackageName: # The package unique identifier
|
||||
WindowFilter: # The filter of window processes to which the shortcuts apply to
|
||||
BackgroundProcess: # Optionally allows applying WindowFilter to background processes
|
||||
Shortcuts: # List of sections with keyboard shortcuts
|
||||
- SectionName: # Name of the category of shortcuts
|
||||
Properties: # List of shortcuts in the category
|
||||
- Name: # Name of the shortcut
|
||||
Description: # Optional description of the shortcut
|
||||
AdditionalInfo: # Optional additional information about the shortcut
|
||||
Recommended: # Optionally determines if the shortcut is displayed in a designated recommended area
|
||||
Shortcut: # An array of shortcuts that need to be pressed
|
||||
- Win: # Determines if the Windows Key is part of the shortcut
|
||||
Ctrl: # Determines if the Ctrl Key is part of the shortcut
|
||||
Shift: # Determines if the Shift Key is part of the shortcut
|
||||
Alt: # Determines if the Alt Key is part of the shortcut
|
||||
Keys: # Array of keys that need to be pressed
|
||||
```
|
||||
|
||||
Per Application/Package one or more Keyboard manifests can be declared. Every manifest must have a different locale and the same `PackageName`, `WindowFilter` and `BackgroundProcess` fields.
|
||||
|
||||
<details>
|
||||
<summary><b>PackageName</b> - The package unique identifier</summary>
|
||||
|
||||
Package identifier (see 2.1 for more information on the package identifier).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>WindowFilter</b> - The filter of window processes to which the shortcuts apply to</summary>
|
||||
|
||||
This field declares for which process name the shortcuts should be shown (To rephrase: For which processes the shortcut will have an effect if pressed). The value can be either an exact process executable name, for example `explorer.exe` or `chrome.exe`, or a single asterisk (`*`) to apply to any process. No other wildcard patterns are supported by this specification.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>BackgroundProcess</b> - Optionally allows applying WindowFilter to background processes.</summary>
|
||||
|
||||
**Optional field**
|
||||
|
||||
Defaults to `False`. Determines if WindowFilter should apply to background processes as well (Rephrased: When the process is running, the shortcuts will apply).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Shortcuts</b> - List of sections with keyboard shortcuts</summary>
|
||||
|
||||
List of different section (also called categories) of shortcuts.
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>SectionName</b> - Name of the category of shortcuts</summary>
|
||||
|
||||
Name of the section of shortcuts.
|
||||
|
||||
**Special sections**:
|
||||
|
||||
Special sections start with an identifier enclosed between `<` and `>`. This declares the category as a special display. If the interpreter of the manifest file can't understand the content this section should be left out.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Properties</b> - List of shortcuts in the category</summary>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Name</b> - Name of the shortcut</summary>
|
||||
|
||||
Name of the shortcut. This is the name that will be displayed in the interpreter.
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary><b>Description</b> - Optional description of the shortcut</summary>
|
||||
|
||||
Optional description of the shortcut. This is the description that will be displayed by the interpreter.
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>AdditionalInfo</b> - Optional additional information about the shortcut</summary>
|
||||
|
||||
Array of additional information about the shortcut. This is the additional information that will be displayed by the interpreter and are not part of this manifest.
|
||||
|
||||
**Example**:
|
||||
|
||||
For example, if the shortcut is only available on a certain Windows version, this information could be added here.
|
||||
```yaml
|
||||
AdditionalInfo:
|
||||
- MinWindowsVersion: "10.0.19041.0"
|
||||
```
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Shortcut</b> - An array of shortcuts that need to be pressed</summary>
|
||||
|
||||
An array of shortcuts that need to be pressed. This allows defining sequential shortcuts that need to be pressed in order to trigger the action.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Win</b> - Determines if the Windows Key is part of the shortcut</summary>
|
||||
|
||||
Refers to the left Windows Key on the keyboard.
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Ctrl</b> - Determines if the Ctrl Key is part of the shortcut</summary>
|
||||
|
||||
Refers to the left Ctrl Key on the keyboard.
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Shift</b> - Determines if the Shift Key is part of the shortcut</summary>
|
||||
|
||||
Refers to the left Shift Key on the keyboard.
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Alt</b> - Determines if the Alt Key is part of the shortcut</summary>
|
||||
|
||||
Refers to the left Alt Key on the keyboard.
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary><b>Recommended</b> - Optionally determines if the shortcut is displayed in a designated recommended area</summary>
|
||||
|
||||
**Optional field**
|
||||
|
||||
Defaults to `False`. Determines if the shortcut should be displayed in a designated recommended area. This is a visual hint for the user that this shortcut is important.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Keys</b> - Array of keys that need to be pressed</summary>
|
||||
|
||||
A string array of all the keys that need to be pressed. If a number is supplied, it should be read as a [KeyCode](https://learn.microsoft.com/windows/win32/inputdev/virtual-key-codes) and displayed accordingly (based on the Keyboard Layout of the user).
|
||||
|
||||
**Special keys**:
|
||||
|
||||
Special keys are enclosed between `<` and `>` and correspond to a key that should be displayed in a certain way. If the interpreter of the manifest file can't understand the content, the brackets should be left out.
|
||||
|
||||
|Name|Description|
|
||||
|----|-----------|
|
||||
|`<Office>`| Corresponds to the Office key on some Windows keyboards |
|
||||
|`<Copilot>`| Corresponds to the Copilot key on some Windows keyboards |
|
||||
|`<Left>`| Corresponds to the left arrow key |
|
||||
|`<Right>`| Corresponds to the right arrow key |
|
||||
|`<Up>`| Corresponds to the up arrow key |
|
||||
|`<Down>`| Corresponds to the down arrow key |
|
||||
|`<Enter>`| Corresponds to the Enter key |
|
||||
|`<Space>`| Corresponds to the Space key |
|
||||
|`<Tab>`| Corresponds to the Tab key |
|
||||
|`<Backspace>`| Corresponds to the Backspace key |
|
||||
|`<Delete>`| Corresponds to the Delete key |
|
||||
|`<Insert>`| Corresponds to the Insert key |
|
||||
|`<Home>`| Corresponds to the Home key |
|
||||
|`<End>`| Corresponds to the End key |
|
||||
|`<PrtScr>`| Corresponds to the Print Screen key |
|
||||
|`<Pause>`| Corresponds to the pause key |
|
||||
|`<PageUp>`| Corresponds to the Page Up key |
|
||||
|`<PageDown>`| Corresponds to the Page Down key |
|
||||
|`<Escape>`| Corresponds to the Escape key |
|
||||
|`<Arrow>`| Corresponds to either the left, right, up or down arrow key |
|
||||
|`<ArrowLR>`| Corresponds to either the left or right arrow key |
|
||||
|`<ArrowUD>`| Corresponds to either the up or down arrow key |
|
||||
|`<Underlined letter>`| Corresponds to any letter that is _underlined_ in the UI |
|
||||
|
||||
</details>
|
||||
|
||||
#### 3.2.2 Example
|
||||
|
||||
```yaml
|
||||
PackageName: Microsoft.PowerToys
|
||||
WindowFilter: "*"
|
||||
BackgroundProcess: True
|
||||
Shortcuts:
|
||||
- SectionName: General
|
||||
Properties:
|
||||
- Name: Advanced Paste
|
||||
Shortcut:
|
||||
- Win: True
|
||||
Ctrl: False
|
||||
Alt: False
|
||||
Shift: False
|
||||
Keys:
|
||||
- 86
|
||||
Description: Open Advanced Paste window
|
||||
- Name: Advanced Paste
|
||||
Shortcut:
|
||||
- Win: True
|
||||
Ctrl: True
|
||||
Alt: True
|
||||
Shift: False
|
||||
Keys:
|
||||
- 86
|
||||
Description: Paste as plain text directly
|
||||
|
||||
```
|
||||
|
||||
|
||||
### 3.2 `index.yaml` file
|
||||
|
||||
The `index.yaml` file is a file that contains all the information about the different manifest files that are saved in the same directory. This file is only available locally and is not saved in the WinGet repository as it is specific to the user.
|
||||
|
||||
```yaml
|
||||
DefaultShellName: # The package identifier of the default shell used in Windows
|
||||
Index: # List of all manifest files
|
||||
- WindowFilter: # The filter of window processes to which the shortcuts apply to
|
||||
BackgroundProcess: # Optionally allows applying WindowFilter to background processes
|
||||
Apps: # List of all manifest files for the filter
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary><b>DefaultShellName</b> - The package identifier of the default shell used in Windows</summary>
|
||||
|
||||
This declares the package identifier of the default shell used in Windows. Most commonly it is `+WindowsNT.Shell`. Although not enforced, only the shell declared in the registry key `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Shell` should be used here.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Index</b> - List of all manifest files</summary>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>WindowFilter</b> - The filter of window processes to which the shortcuts apply to</summary>
|
||||
|
||||
See the `WindowFilter` field in the manifest file for more information.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>BackgroundProcess</b> - Optionally allows applying WindowFilter to background processes</summary>
|
||||
|
||||
**Optional field**
|
||||
|
||||
See the `BackgroundProcess` field in the manifest file for more information.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Apps</b> - List of all the package identifiers applying for the filter</summary>
|
||||
</details>
|
||||
|
||||
#### 3.2.1 Example
|
||||
|
||||
```yaml
|
||||
DefaultShellName: "+WindowsNT.Shell"
|
||||
Index:
|
||||
- Filter: "*"
|
||||
BackgroundProcess: True
|
||||
Apps: ["+WindowsNT.Shell", "Microsoft.PowerToys"]
|
||||
- Filter: "explorer.exe"
|
||||
Apps: ["+WindowsNT.WindowsExplorer"]
|
||||
- Filter: "taskmgr.exe"
|
||||
Apps: ["+WindowsNT.TaskManager"]
|
||||
- Filter: "msedge.exe"
|
||||
Apps: ["+WindowsNT.Edge"]
|
||||
```
|
||||
@@ -79,3 +79,4 @@ Below are community created plugins that target a website or software. They are
|
||||
| [Linear](https://github.com/vednig/powertoys-linear) | [vednig](https://github.com/vednig) | Create Linear Issues directly from Powertoys Run |
|
||||
| [PerplexitySearchShortcut](https://github.com/0x6f677548/PowerToys-Run-PerplexitySearchShortcut) | [0x6f677548](https://github.com/0x6f677548) | Search Perplexity |
|
||||
| [SpeedTest](https://github.com/ruslanlap/PowerToysRun-SpeedTest) | [ruslanlap](https://github.com/ruslanlap) | One-command internet speed tests with real-time results, modern UI, and shareable links. |
|
||||
| [DiskAnalyzer](https://github.com/valley-soft/powertoys-diskanalyzer) | [ValleySoft](https://github.com/valley-soft) | Scan folders, find the largest files, and view drive space usage. |
|
||||
|
||||
@@ -8,9 +8,6 @@
|
||||
</Project>
|
||||
<Project Path="../src/common/Telemetry/EtwTrace/EtwTrace.vcxproj" Id="8f021b46-362b-485c-bfba-ccf83e820cbd" />
|
||||
<Project Path="../src/common/version/version.vcxproj" Id="cc6e41ac-8174-4e8a-8d22-85dd7f4851df" />
|
||||
<Project Path="../src/logging/logging.vcxproj" Id="7e1e3f13-2bd6-3f75-a6a7-873a2b55c60f">
|
||||
<Build Solution="Debug|ARM64" Project="false" />
|
||||
</Project>
|
||||
<Project Path="PowerToysSetupCustomActionsVNext/PowerToysSetupCustomActionsVNext.vcxproj" Id="b3a354b0-1e54-4b55-a962-fb5af9330c19">
|
||||
<Build Solution="Debug|ARM64" Project="false" />
|
||||
</Project>
|
||||
|
||||
72
installer/PowerToysSetupVNext/CliShims.wxs
Normal file
@@ -0,0 +1,72 @@
|
||||
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
|
||||
|
||||
<?include $(sys.CURRENTDIR)\Common.wxi?>
|
||||
|
||||
<!--
|
||||
CLI shims: tiny Native AOT launchers installed into the "cli" subfolder. Each shim
|
||||
forwards to a real PowerToys CLI (one level up, or under WinUI3Apps). The cli folder
|
||||
is appended to PATH so the shims are callable by name from a terminal.
|
||||
-->
|
||||
<Fragment>
|
||||
<DirectoryRef Id="CliFolder">
|
||||
<Component Id="CliShim_fancyzones_exe" Guid="63800E2A-21B9-4A92-AD25-9C1DC7593F4F" Bitness="always64">
|
||||
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
|
||||
<RegistryValue Type="string" Name="CliShim_fancyzones_exe" Value="" KeyPath="yes" />
|
||||
</RegistryKey>
|
||||
<File Source="$(var.BinDir)cli\fancyzones.exe" Id="CliShim_fancyzones.exe" Checksum="yes" />
|
||||
</Component>
|
||||
<Component Id="CliShim_imageresizer_exe" Guid="2BC274AA-FECD-4A85-BF70-06670A6B99A5" Bitness="always64">
|
||||
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
|
||||
<RegistryValue Type="string" Name="CliShim_imageresizer_exe" Value="" KeyPath="yes" />
|
||||
</RegistryKey>
|
||||
<File Source="$(var.BinDir)cli\imageresizer.exe" Id="CliShim_imageresizer.exe" Checksum="yes" />
|
||||
</Component>
|
||||
<Component Id="CliShim_filelocksmith_exe" Guid="2DD830DD-78D4-46EE-B23B-3EAB3D838EDF" Bitness="always64">
|
||||
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
|
||||
<RegistryValue Type="string" Name="CliShim_filelocksmith_exe" Value="" KeyPath="yes" />
|
||||
</RegistryKey>
|
||||
<File Source="$(var.BinDir)cli\filelocksmith.exe" Id="CliShim_filelocksmith.exe" Checksum="yes" />
|
||||
</Component>
|
||||
|
||||
<!--
|
||||
Append the cli folder to PATH (per-user vs per-machine, mirroring Core.wxs).
|
||||
MSI broadcasts WM_SETTINGCHANGE on commit, so Explorer and newly-opened terminals
|
||||
pick up the new PATH; terminals already open at install time must be reopened before
|
||||
the short command names (fancyzones / imageresizer / filelocksmith) resolve.
|
||||
-->
|
||||
<?if $(var.PerUser) = "true" ?>
|
||||
<Component Id="cli_env_path_user" Guid="87AA86E8-81E8-44CB-9519-FC70CF7BEA56" Bitness="always64">
|
||||
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
|
||||
<RegistryValue Type="string" Name="cli_env_path_user" Value="" KeyPath="yes" />
|
||||
</RegistryKey>
|
||||
<Environment Id="AddCliToUserPath" Name="PATH" Action="set" Part="last" System="no" Value="[CliFolder]" />
|
||||
</Component>
|
||||
<?else?>
|
||||
<Component Id="cli_env_path_machine" Guid="739F3916-2247-4F1B-8BBD-0949A5B870F7" Bitness="always64">
|
||||
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
|
||||
<RegistryValue Type="string" Name="cli_env_path_machine" Value="" KeyPath="yes" />
|
||||
</RegistryKey>
|
||||
<Environment Id="AddCliToMachinePath" Name="PATH" Action="set" Part="last" System="yes" Value="[CliFolder]" />
|
||||
</Component>
|
||||
<?endif?>
|
||||
</DirectoryRef>
|
||||
|
||||
<ComponentGroup Id="CliShimsComponentGroup">
|
||||
<Component Id="RemoveCliFolder" Guid="831C56D8-9FEB-41B2-8A8C-BD49487479BB" Directory="CliFolder">
|
||||
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
|
||||
<RegistryValue Type="string" Name="RemoveCliFolder" Value="" KeyPath="yes" />
|
||||
</RegistryKey>
|
||||
<RemoveFolder Id="RemoveFolderCliFolder" Directory="CliFolder" On="uninstall" />
|
||||
</Component>
|
||||
<ComponentRef Id="CliShim_fancyzones_exe" />
|
||||
<ComponentRef Id="CliShim_imageresizer_exe" />
|
||||
<ComponentRef Id="CliShim_filelocksmith_exe" />
|
||||
<?if $(var.PerUser) = "true" ?>
|
||||
<ComponentRef Id="cli_env_path_user" />
|
||||
<?else?>
|
||||
<ComponentRef Id="cli_env_path_machine" />
|
||||
<?endif?>
|
||||
</ComponentGroup>
|
||||
|
||||
</Fragment>
|
||||
</Wix>
|
||||
@@ -131,6 +131,7 @@ call powershell.exe -NonInteractive -executionpolicy Unrestricted -File $(MSBuil
|
||||
<Compile Include="Settings.wxs" />
|
||||
<Compile Include="ShortcutGuide.wxs" />
|
||||
<Compile Include="Tools.wxs" />
|
||||
<Compile Include="CliShims.wxs" />
|
||||
<Compile Include="MouseWithoutBorders.wxs" />
|
||||
<Compile Include="WinUI3Applications.wxs" />
|
||||
<Compile Include="MonacoSRC.wxs" />
|
||||
|
||||
@@ -67,6 +67,7 @@
|
||||
<ComponentGroupRef Id="DscResourcesComponentGroup" />
|
||||
<ComponentGroupRef Id="WindowsAppSDKComponentGroup" />
|
||||
<ComponentGroupRef Id="ToolComponentGroup" />
|
||||
<ComponentGroupRef Id="CliShimsComponentGroup" />
|
||||
<ComponentGroupRef Id="MonacoSRCHeatGenerated" />
|
||||
<ComponentGroupRef Id="WorkspacesComponentGroup" />
|
||||
<ComponentGroupRef Id="CmdPalComponentGroup" />
|
||||
@@ -299,6 +300,7 @@
|
||||
</Directory>
|
||||
</Directory>
|
||||
<Directory Id="ToolsFolder" Name="Tools" />
|
||||
<Directory Id="CliFolder" Name="cli" />
|
||||
</Directory>
|
||||
</StandardDirectory>
|
||||
<StandardDirectory Id="ProgramMenuFolder">
|
||||
|
||||
@@ -2,26 +2,39 @@
|
||||
|
||||
<?include $(sys.CURRENTDIR)\Common.wxi?>
|
||||
|
||||
<?define ShortcutGuideSvgFiles=?>
|
||||
<?define ShortcutGuideSvgFilesPath=$(var.BinDir)\Assets\ShortcutGuide\?>
|
||||
<?define ShortcutGuideAssetsFiles=?>
|
||||
<?define ShortcutGuideAssetsFilesPath=$(var.BinDir)WinUI3Apps\Assets\ShortcutGuide\?>
|
||||
<?define ShortcutGuideManifestsFiles=?>
|
||||
<?define ShortcutGuideManifestsFilesPath=$(var.BinDir)WinUI3Apps\Assets\ShortcutGuide\Manifests\?>
|
||||
|
||||
<Fragment>
|
||||
<!-- Shortcut guide files -->
|
||||
<DirectoryRef Id="BaseApplicationsAssetsFolder">
|
||||
<Directory Id="ShortcutGuideSvgsInstallFolder" Name="ShortcutGuide" />
|
||||
<DirectoryRef Id="WinUI3AppsAssetsFolder">
|
||||
<Directory Id="ShortcutGuideAssetsFolder" Name="ShortcutGuide">
|
||||
<Directory Id="ShortcutGuideManifestsFolder" Name="Manifests" />
|
||||
</Directory>
|
||||
</DirectoryRef>
|
||||
<DirectoryRef Id="ShortcutGuideSvgsInstallFolder" FileSource="$(var.ShortcutGuideSvgFilesPath)">
|
||||
<DirectoryRef Id="ShortcutGuideAssetsFolder" FileSource="$(var.ShortcutGuideAssetsFilesPath)">
|
||||
<!-- Generated by generateFileComponents.ps1 -->
|
||||
<!--ShortcutGuideSvgFiles_Component_Def-->
|
||||
<!--ShortcutGuideAssetsFiles_Component_Def-->
|
||||
</DirectoryRef>
|
||||
<DirectoryRef Id="ShortcutGuideManifestsFolder" FileSource="$(var.ShortcutGuideManifestsFilesPath)">
|
||||
<!-- Generated by generateFileComponents.ps1 -->
|
||||
<!--ShortcutGuideManifestsFiles_Component_Def-->
|
||||
</DirectoryRef>
|
||||
|
||||
<!-- Shortcut guide -->
|
||||
<ComponentGroup Id="ShortcutGuideComponentGroup">
|
||||
<Component Id="RemoveShortcutGuideFolder" Guid="AD1ABC55-B593-4A60-A86A-BA8C0ED493A5" Directory="ShortcutGuideSvgsInstallFolder">
|
||||
<ComponentGroup Id="ShortcutGuideComponentGroup" >
|
||||
<Component Id="RemoveShortcutGuideFolder" Guid="AD1ABC55-B593-4A60-A86A-BA8C0ED493A5" Directory="ShortcutGuideAssetsFolder" >
|
||||
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
|
||||
<RegistryValue Type="string" Name="RemoveShortcutGuideFolder" Value="" KeyPath="yes" />
|
||||
<RegistryValue Type="string" Name="RemoveShortcutGuideFolder" Value="" KeyPath="yes"/>
|
||||
</RegistryKey>
|
||||
<RemoveFolder Id="RemoveFolderShortcutGuideSvgsInstallFolder" Directory="ShortcutGuideSvgsInstallFolder" On="uninstall" />
|
||||
<RemoveFolder Id="RemoveFolderShortcutGuideAssetsInstallFolder" Directory="ShortcutGuideAssetsFolder" On="uninstall"/>
|
||||
</Component>
|
||||
<Component Id="RemoveShortcutGuideManifestsFolder" Guid="F47E2C3A-8D91-4B6F-A2E5-9C8D7F6A1B3E" Directory="ShortcutGuideManifestsFolder" >
|
||||
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
|
||||
<RegistryValue Type="string" Name="RemoveShortcutGuideManifestsFolder" Value="" KeyPath="yes"/>
|
||||
</RegistryKey>
|
||||
<RemoveFolder Id="RemoveFolderShortcutGuideManifestsInstallFolder" Directory="ShortcutGuideManifestsFolder" On="uninstall"/>
|
||||
</Component>
|
||||
</ComponentGroup>
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ Function Generate-FileList() {
|
||||
|
||||
$fileExclusionList = @("*.pdb", "*.lastcodeanalysissucceeded", "createdump.exe", "powertoys.exe")
|
||||
|
||||
$fileInclusionList = @("*.dll", "*.exe", "*.json", "*.msix", "*.png", "*.gif", "*.ico", "*.cur", "*.svg", "index.html", "reg.js", "gitignore.js", "srt.js", "monacoSpecialLanguages.js", "customTokenThemeRules.js", "*.pri")
|
||||
$fileInclusionList = @("*.dll", "*.exe", "*.json", "*.msix", "*.png", "*.gif", "*.ico", "*.cur", "*.svg", "index.html", "reg.js", "gitignore.js", "srt.js", "monacoSpecialLanguages.js", "customTokenThemeRules.js", "*.pri", "*.yml")
|
||||
|
||||
# MFC DLLs leak into the output via WindowsAppSDKSelfContained but no PowerToys binary imports them.
|
||||
# Verified with dumpbin /dependents across all 2176 binaries — zero consumers.
|
||||
@@ -112,6 +112,8 @@ Function Generate-FileComponents() {
|
||||
|
||||
foreach ($file in $fileList) {
|
||||
$fileTmp = $file -replace "-", "_"
|
||||
$fileTmp = $fileTmp -replace "[^A-Za-z0-9_.]", "_"
|
||||
if ($fileTmp -match "^[^A-Za-z_]") { $fileTmp = "_$fileTmp" }
|
||||
$componentDefs +=
|
||||
@"
|
||||
<File Id="$($fileListName)_File_$($fileTmp)" Source="`$(var.$($fileListName)Path)\$($file)" />`r`n
|
||||
@@ -397,8 +399,24 @@ Generate-FileComponents -fileListName "ValueGeneratorImagesCmpFiles" -wxsFilePat
|
||||
## Plugins
|
||||
|
||||
#ShortcutGuide
|
||||
Generate-FileList -fileDepsJson "" -fileListName ShortcutGuideSvgFiles -wxsFilePath $PSScriptRoot\ShortcutGuide.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\Assets\ShortcutGuide\"
|
||||
Generate-FileComponents -fileListName "ShortcutGuideSvgFiles" -wxsFilePath $PSScriptRoot\ShortcutGuide.wxs
|
||||
# Ensure manifest yml files are in the build output (the Build target's CopyToOutputDirectory
|
||||
# may not run reliably under -graph mode in solution builds).
|
||||
$sgManifestsSrc = "$PSScriptRoot..\..\..\src\modules\ShortcutGuide\ShortcutGuide.Ui\Assets\ShortcutGuide\Manifests"
|
||||
$sgManifestsDst = "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\ShortcutGuide\Manifests"
|
||||
Write-Host "ShortcutGuide manifests: src=$sgManifestsSrc exists=$(Test-Path $sgManifestsSrc)"
|
||||
Write-Host "ShortcutGuide manifests: dst=$sgManifestsDst exists=$(Test-Path $sgManifestsDst)"
|
||||
if (Test-Path $sgManifestsSrc) {
|
||||
New-Item -Path $sgManifestsDst -ItemType Directory -Force | Out-Null
|
||||
Copy-Item "$sgManifestsSrc\*.yml" -Destination $sgManifestsDst -Force
|
||||
$copied = (Get-ChildItem "$sgManifestsDst\*.yml" -ErrorAction SilentlyContinue).Count
|
||||
Write-Host "ShortcutGuide manifests: copied $copied yml files to build output"
|
||||
} else {
|
||||
Write-Host "WARNING: ShortcutGuide manifest source not found at $sgManifestsSrc"
|
||||
}
|
||||
Generate-FileList -fileDepsJson "" -fileListName ShortcutGuideAssetsFiles -wxsFilePath $PSScriptRoot\ShortcutGuide.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\ShortcutGuide\"
|
||||
Generate-FileComponents -fileListName "ShortcutGuideAssetsFiles" -wxsFilePath $PSScriptRoot\ShortcutGuide.wxs
|
||||
Generate-FileList -fileDepsJson "" -fileListName ShortcutGuideManifestsFiles -wxsFilePath $PSScriptRoot\ShortcutGuide.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\ShortcutGuide\Manifests\"
|
||||
Generate-FileComponents -fileListName "ShortcutGuideManifestsFiles" -wxsFilePath $PSScriptRoot\ShortcutGuide.wxs
|
||||
|
||||
#Settings
|
||||
Generate-FileList -fileDepsJson "" -fileListName SettingsV2AssetsFiles -wxsFilePath $PSScriptRoot\Settings.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\Settings\"
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
<PropertyGroup Label="Configuration">
|
||||
|
||||
</PropertyGroup>
|
||||
<Import Project="$(RepoRoot)deps\expected.props" />
|
||||
<PropertyGroup>
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
#include <common/updating/updating.h>
|
||||
#include <common/updating/updateState.h>
|
||||
#include <common/updating/installer.h>
|
||||
#include <common/updating/configBackup.h>
|
||||
#include <common/updating/updateLifecycle.h>
|
||||
|
||||
#include <common/utils/elevation.h>
|
||||
#include <common/utils/HttpClient.h>
|
||||
@@ -21,6 +23,8 @@
|
||||
#include <common/utils/resources.h>
|
||||
#include <common/utils/timeutil.h>
|
||||
|
||||
#include <wil/resource.h>
|
||||
|
||||
#include <common/SettingsAPI/settings_helpers.h>
|
||||
|
||||
#include <common/logger/logger.h>
|
||||
@@ -36,17 +40,59 @@ using namespace cmdArg;
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
void CleanupStaleTempUpdaters()
|
||||
{
|
||||
// Remove orphaned PowerToys.Update.*.exe files from previous runs
|
||||
try
|
||||
{
|
||||
std::error_code ec;
|
||||
const auto tempDir = fs::temp_directory_path();
|
||||
for (const auto& entry : fs::directory_iterator(tempDir, ec))
|
||||
{
|
||||
if (ec)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (!entry.is_regular_file())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto filename = entry.path().filename().wstring();
|
||||
if (filename.starts_with(L"PowerToys.Update.") && filename.ends_with(L".exe"))
|
||||
{
|
||||
// Skip our own file (current PID)
|
||||
const auto ownFilename = L"PowerToys.Update." + std::to_wstring(GetCurrentProcessId()) + L".exe";
|
||||
if (filename == ownFilename)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
fs::remove(entry.path(), ec);
|
||||
// Failure to delete is expected if another updater is still running
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Best-effort cleanup; don't block the update
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<fs::path> CopySelfToTempDir()
|
||||
{
|
||||
CleanupStaleTempUpdaters();
|
||||
|
||||
std::error_code error;
|
||||
auto dst_path = fs::temp_directory_path() / "PowerToys.Update.exe";
|
||||
auto dst_path = fs::temp_directory_path() / (L"PowerToys.Update." + std::to_wstring(GetCurrentProcessId()) + L".exe");
|
||||
fs::copy_file(get_module_filename(), dst_path, fs::copy_options::overwrite_existing, error);
|
||||
if (error)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return std::move(dst_path);
|
||||
return dst_path;
|
||||
}
|
||||
|
||||
std::optional<fs::path> ObtainInstaller(bool& isUpToDate)
|
||||
@@ -57,34 +103,9 @@ std::optional<fs::path> ObtainInstaller(bool& isUpToDate)
|
||||
|
||||
auto state = UpdateState::read();
|
||||
|
||||
const auto new_version_info = std::move(get_github_version_info_async()).get();
|
||||
if (std::holds_alternative<version_up_to_date>(*new_version_info))
|
||||
{
|
||||
isUpToDate = true;
|
||||
Logger::error("Invoked with -update_now argument, but no update was available");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (state.state == UpdateState::readyToDownload || state.state == UpdateState::errorDownloading)
|
||||
{
|
||||
if (!new_version_info)
|
||||
{
|
||||
Logger::error(L"Couldn't obtain github version info: {}", new_version_info.error());
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Cleanup old updates before downloading the latest
|
||||
updating::cleanup_updates();
|
||||
|
||||
auto downloaded_installer = std::move(download_new_version_async(std::get<new_version_download_info>(*new_version_info))).get();
|
||||
if (!downloaded_installer)
|
||||
{
|
||||
Logger::error("Couldn't download new installer");
|
||||
}
|
||||
|
||||
return downloaded_installer;
|
||||
}
|
||||
else if (state.state == UpdateState::readyToInstall)
|
||||
// Handle readyToInstall first — the installer is already on disk,
|
||||
// so we don't need a GitHub API call (which may fail if offline).
|
||||
if (state.state == UpdateState::readyToInstall)
|
||||
{
|
||||
fs::path installer{ get_pending_updates_path() / state.downloadedInstallerFilename };
|
||||
if (fs::is_regular_file(installer))
|
||||
@@ -97,12 +118,44 @@ std::optional<fs::path> ObtainInstaller(bool& isUpToDate)
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
else if (state.state == UpdateState::upToDate)
|
||||
|
||||
if (state.state == UpdateState::upToDate)
|
||||
{
|
||||
isUpToDate = true;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const auto new_version_info = std::move(get_github_version_info_async()).get();
|
||||
|
||||
// Check for error BEFORE dereferencing — the old code crashed here
|
||||
// when GitHub API was unreachable (new_version_info held an error string).
|
||||
if (!new_version_info)
|
||||
{
|
||||
Logger::error(L"Couldn't obtain github version info: {}", new_version_info.error());
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (std::holds_alternative<version_up_to_date>(*new_version_info))
|
||||
{
|
||||
isUpToDate = true;
|
||||
Logger::error("Invoked with -update_now argument, but no update was available");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (state.state == UpdateState::readyToDownload || state.state == UpdateState::errorDownloading)
|
||||
{
|
||||
// Cleanup old updates before downloading the latest
|
||||
updating::cleanup_updates();
|
||||
|
||||
auto downloaded_installer = std::move(download_new_version_async(std::get<new_version_download_info>(*new_version_info))).get();
|
||||
if (!downloaded_installer)
|
||||
{
|
||||
Logger::error("Couldn't download new installer");
|
||||
}
|
||||
|
||||
return downloaded_installer;
|
||||
}
|
||||
|
||||
Logger::error("Invoked with -update_now argument, but update state was invalid");
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -116,13 +169,32 @@ bool InstallNewVersionStage1(fs::path installer)
|
||||
|
||||
if (pt_main_window != nullptr)
|
||||
{
|
||||
SendMessageW(pt_main_window, WM_CLOSE, 0, 0);
|
||||
// Get the process that owns the tray window so we can wait for it to exit
|
||||
DWORD ptProcessId = 0;
|
||||
GetWindowThreadProcessId(pt_main_window, &ptProcessId);
|
||||
|
||||
// Use SendMessageTimeoutW to avoid blocking indefinitely if the
|
||||
// tray window thread is hung or unresponsive.
|
||||
DWORD_PTR result = 0;
|
||||
SendMessageTimeoutW(pt_main_window, WM_CLOSE, 0, 0, SMTO_ABORTIFHUNG, 5000, &result);
|
||||
|
||||
// Wait for PT to actually exit before launching installer.
|
||||
// Without this, the installer may find PT files locked.
|
||||
if (ptProcessId != 0)
|
||||
{
|
||||
wil::unique_handle ptProcess{ OpenProcess(SYNCHRONIZE, FALSE, ptProcessId) };
|
||||
if (ptProcess)
|
||||
{
|
||||
WaitForSingleObject(ptProcess.get(), 10000); // 10 second timeout
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::wstring arguments{ UPDATE_NOW_LAUNCH_STAGE2 };
|
||||
arguments += L" \"";
|
||||
arguments += installer.c_str();
|
||||
arguments += L"\"";
|
||||
// Pass the install directory so Stage 2 can relaunch PowerToys after install
|
||||
const std::wstring installDir = get_module_folderpath();
|
||||
|
||||
std::wstring arguments = updating::BuildStage2Arguments(
|
||||
UPDATE_NOW_LAUNCH_STAGE2, installer, fs::path(installDir));
|
||||
SHELLEXECUTEINFOW sei{ sizeof(sei) };
|
||||
sei.fMask = { SEE_MASK_FLAG_NO_UI | SEE_MASK_NOASYNC };
|
||||
sei.lpFile = copy_in_temp->c_str();
|
||||
@@ -190,9 +262,16 @@ int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
|
||||
LPWSTR* args = CommandLineToArgvW(GetCommandLineW(), &nArgs);
|
||||
if (!args || nArgs < 2)
|
||||
{
|
||||
if (args)
|
||||
{
|
||||
LocalFree(args);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// D3 fix: ensure args is freed on all exit paths
|
||||
auto freeArgs = wil::scope_exit([&] { LocalFree(args); });
|
||||
|
||||
std::wstring_view action{ args[1] };
|
||||
|
||||
std::filesystem::path logFilePath(PTSettingsHelper::get_root_save_folder_location());
|
||||
@@ -201,6 +280,11 @@ int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
|
||||
|
||||
if (action == UPDATE_NOW_LAUNCH_STAGE1)
|
||||
{
|
||||
// Backup config files before the update to protect against corruption
|
||||
Logger::info("Backing up config files before update");
|
||||
auto backupResult = updating::BackupConfigFiles(fs::path(PTSettingsHelper::get_root_save_folder_location()));
|
||||
Logger::info("Config backup complete: {} files backed up, {} errors", backupResult.filesBackedUp, backupResult.errors);
|
||||
|
||||
bool isUpToDate = false;
|
||||
auto installerPath = ObtainInstaller(isUpToDate);
|
||||
bool failed = !installerPath.has_value();
|
||||
@@ -217,6 +301,12 @@ int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
|
||||
}
|
||||
else if (action == UPDATE_NOW_LAUNCH_STAGE2)
|
||||
{
|
||||
if (nArgs < 3)
|
||||
{
|
||||
Logger::error("Stage 2 invoked without installer path argument");
|
||||
return 1;
|
||||
}
|
||||
|
||||
using namespace std::string_view_literals;
|
||||
const bool failed = !InstallNewVersionStage2(args[2]);
|
||||
if (failed)
|
||||
@@ -227,6 +317,39 @@ int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
|
||||
state.state = UpdateState::errorDownloading;
|
||||
});
|
||||
}
|
||||
|
||||
// Always check for corrupted configs after Stage 2, regardless
|
||||
// of install success/failure. A failed install may still corrupt configs.
|
||||
Logger::info("Checking for corrupted config files after update");
|
||||
auto restoreResult = updating::RestoreCorruptedConfigs(fs::path(PTSettingsHelper::get_root_save_folder_location()));
|
||||
Logger::info("Config restore check complete: {}/{} files restored, {} errors",
|
||||
restoreResult.filesRestored, restoreResult.filesChecked, restoreResult.errors);
|
||||
|
||||
if (!failed)
|
||||
{
|
||||
// Relaunch PowerToys from the install directory
|
||||
if (updating::CanRelaunchAfterUpdate(nArgs))
|
||||
{
|
||||
std::wstring ptExePath = updating::BuildPowerToysExePath(args[3]);
|
||||
|
||||
Logger::info(L"Relaunching PowerToys after update: {}", ptExePath);
|
||||
|
||||
SHELLEXECUTEINFOW sei{ sizeof(sei) };
|
||||
sei.fMask = { SEE_MASK_FLAG_NO_UI | SEE_MASK_NOASYNC };
|
||||
sei.lpFile = ptExePath.c_str();
|
||||
sei.nShow = SW_SHOWNORMAL;
|
||||
sei.lpParameters = UPDATE_REPORT_SUCCESS;
|
||||
|
||||
if (!ShellExecuteExW(&sei))
|
||||
{
|
||||
Logger::error(L"Failed to relaunch PowerToys after update");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::warn("Install directory not provided to Stage 2 - cannot relaunch PowerToys");
|
||||
}
|
||||
}
|
||||
return failed;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
<PropertyGroup Label="Configuration">
|
||||
|
||||
</PropertyGroup>
|
||||
<Import Project="$(RepoRoot)deps\expected.props" />
|
||||
<PropertyGroup>
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -73,7 +73,8 @@
|
||||
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<AdditionalOptions>%(AdditionalOptions) /bigobj</AdditionalOptions>
|
||||
<PreprocessorDefinitions>_WINRT_DLL;WINRT_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<!-- TODO: _SILENCE_EXPERIMENTAL_COROUTINE_DEPRECATION_WARNINGS: suppress VS 2026 STL hard error for <experimental/coroutine> until the code is ported to <coroutine> -->
|
||||
<PreprocessorDefinitions>_WINRT_DLL;WINRT_LEAN_AND_MEAN;_SILENCE_EXPERIMENTAL_COROUTINE_DEPRECATION_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>../../..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
|
||||
</ClCompile>
|
||||
|
||||
@@ -2,43 +2,35 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ManagedCommon
|
||||
{
|
||||
public static class IdRecoveryHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Fixes invalid IDs in the given list by assigning unique values.
|
||||
/// It ensures that all IDs are non-empty and unique, correcting any duplicates or empty IDs.
|
||||
/// Ensures that all items in the provided list have unique IDs. Duplicate IDs are replaced
|
||||
/// with the next available unique ID.
|
||||
/// </summary>
|
||||
/// <param name="items">The list of items that may contain invalid IDs.</param>
|
||||
/// <param name="items">The list of items that may contain duplicate IDs.</param>
|
||||
public static void RecoverInvalidIds<T>(IEnumerable<T> items)
|
||||
where T : class, IHasId
|
||||
{
|
||||
var idSet = new HashSet<int>();
|
||||
int newId = 0;
|
||||
var sortedItems = items.OrderBy(i => i.Id).ToList(); // Sort items by ID for consistent processing
|
||||
var seenIds = new HashSet<int>();
|
||||
int nextAvailableId = 0;
|
||||
|
||||
// Iterate through the list and fix invalid IDs
|
||||
foreach (var item in sortedItems)
|
||||
foreach (var item in items)
|
||||
{
|
||||
// If the ID is invalid or already exists in the set (duplicate), assign a new unique ID
|
||||
if (!idSet.Add(item.Id))
|
||||
// If this ID is already used, assign a new unique ID.
|
||||
if (!seenIds.Add(item.Id))
|
||||
{
|
||||
// Find the next available unique ID
|
||||
while (idSet.Contains(newId))
|
||||
// Find the next unused ID.
|
||||
while (!seenIds.Add(nextAvailableId))
|
||||
{
|
||||
newId++;
|
||||
nextAvailableId++;
|
||||
}
|
||||
|
||||
item.Id = newId;
|
||||
idSet.Add(newId); // Add the newly assigned ID to the set
|
||||
item.Id = nextAvailableId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>..\;..\utils;..\Telemetry;..\..\;..\..\..\deps\;..\..\..\deps\spdlog\include;..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.260126.7\include;$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>..\;..\utils;..\Telemetry;..\..\;..\..\..\deps\;..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.260126.7\include;$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<LanguageStandard>stdcpp23</LanguageStandard>
|
||||
<PreprocessorDefinitions>SPDLOG_WCHAR_TO_UTF8_SUPPORT;SPDLOG_HEADER_ONLY;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
|
||||
@@ -259,6 +259,10 @@ namespace winrt::PowerToys::Interop::implementation
|
||||
{
|
||||
return CommonSharedConstants::TERMINATE_POWER_DISPLAY_EVENT;
|
||||
}
|
||||
hstring Constants::AutoDisablePowerDisplayEvent()
|
||||
{
|
||||
return CommonSharedConstants::POWER_DISPLAY_AUTO_DISABLE_EVENT;
|
||||
}
|
||||
hstring Constants::RefreshPowerDisplayMonitorsEvent()
|
||||
{
|
||||
return CommonSharedConstants::REFRESH_POWER_DISPLAY_MONITORS_EVENT;
|
||||
|
||||
@@ -68,6 +68,7 @@ namespace winrt::PowerToys::Interop::implementation
|
||||
static hstring ShowCmdPalEvent();
|
||||
static hstring TogglePowerDisplayEvent();
|
||||
static hstring TerminatePowerDisplayEvent();
|
||||
static hstring AutoDisablePowerDisplayEvent();
|
||||
static hstring RefreshPowerDisplayMonitorsEvent();
|
||||
static hstring SettingsUpdatedPowerDisplayEvent();
|
||||
static hstring PowerDisplaySendSettingsTelemetryEvent();
|
||||
|
||||
@@ -65,6 +65,7 @@ namespace PowerToys
|
||||
static String ShowCmdPalEvent();
|
||||
static String TogglePowerDisplayEvent();
|
||||
static String TerminatePowerDisplayEvent();
|
||||
static String AutoDisablePowerDisplayEvent();
|
||||
static String RefreshPowerDisplayMonitorsEvent();
|
||||
static String SettingsUpdatedPowerDisplayEvent();
|
||||
static String PowerDisplaySendSettingsTelemetryEvent();
|
||||
|
||||
@@ -41,7 +41,6 @@
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
@@ -100,6 +99,7 @@
|
||||
<ClInclude Include="Constants.h">
|
||||
<DependentUpon>KeyboardListener.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="excluded_app.h" />
|
||||
<ClInclude Include="HotkeyManager.h">
|
||||
<DependentUpon>HotkeyManager.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
@@ -114,6 +114,7 @@
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="shared_constants.h" />
|
||||
<ClInclude Include="tasklist_positions.h" />
|
||||
<ClInclude Include="TwoWayPipeMessageIPCManaged.h">
|
||||
<DependentUpon>TwoWayPipeMessageIPCManaged.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
@@ -127,6 +128,7 @@
|
||||
<ClCompile Include="Constants.cpp">
|
||||
<DependentUpon>KeyboardListener.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="excluded_app.cpp" />
|
||||
<ClCompile Include="HotkeyManager.cpp">
|
||||
<DependentUpon>HotkeyManager.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
@@ -140,6 +142,7 @@
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="tasklist_positions.cpp" />
|
||||
<ClCompile Include="TwoWayPipeMessageIPCManaged.cpp">
|
||||
<DependentUpon>TwoWayPipeMessageIPCManaged.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
@@ -165,6 +168,9 @@
|
||||
<Midl Include="TwoWayPipeMessageIPCManaged.idl" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SettingsAPI\SettingsAPI.vcxproj">
|
||||
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\version\version.vcxproj">
|
||||
<Project>{cc6e41ac-8174-4e8a-8d22-85dd7f4851df}</Project>
|
||||
</ProjectReference>
|
||||
|
||||
@@ -54,6 +54,12 @@
|
||||
<ClInclude Include="Constants.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="excluded_app.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="tasklist_positions.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="keyboard_layout.cpp">
|
||||
@@ -83,6 +89,12 @@
|
||||
<ClCompile Include="Constants.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="excluded_app.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="tasklist_positions.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="interop.rc">
|
||||
|
||||
41
src/common/interop/excluded_app.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#include "pch.h"
|
||||
#include "excluded_app.h"
|
||||
#include <../utils/string_utils.h>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
__declspec(dllexport) bool IsCurrentWindowExcludedFromShortcutGuide()
|
||||
{
|
||||
PowerToysSettings::PowerToyValues settings = PowerToysSettings::PowerToyValues::load_from_settings_file(L"Shortcut Guide");
|
||||
auto settingsObject = settings.get_raw_json();
|
||||
std::wstring apps = settingsObject.GetNamedObject(L"properties").GetNamedObject(L"disabled_apps").GetNamedString(L"value").c_str();
|
||||
auto excludedUppercase = apps;
|
||||
CharUpperBuffW(excludedUppercase.data(), static_cast<DWORD>(excludedUppercase.length()));
|
||||
std::wstring_view view(excludedUppercase);
|
||||
view = left_trim<wchar_t>(trim<wchar_t>(view));
|
||||
|
||||
m_excludedApps.clear();
|
||||
|
||||
while (!view.empty())
|
||||
{
|
||||
auto pos = (std::min)(view.find_first_of(L"\r\n"), view.length());
|
||||
m_excludedApps.emplace_back(view.substr(0, pos));
|
||||
view.remove_prefix(pos);
|
||||
view = left_trim<wchar_t>(trim<wchar_t>(view));
|
||||
}
|
||||
|
||||
if (m_excludedApps.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (HWND foregroundApp{ GetForegroundWindow() })
|
||||
{
|
||||
auto processPath = get_process_path(foregroundApp);
|
||||
CharUpperBuffW(processPath.data(), static_cast<DWORD>(processPath.length()));
|
||||
|
||||
return check_excluded_app(foregroundApp, processPath, m_excludedApps);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
7
src/common/interop/excluded_app.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
extern "C"
|
||||
{
|
||||
std::vector<std::wstring> m_excludedApps;
|
||||
__declspec(dllexport) bool IsCurrentWindowExcludedFromShortcutGuide();
|
||||
}
|
||||
@@ -12,3 +12,29 @@
|
||||
#include <winrt/Windows.Foundation.Collections.h>
|
||||
#include <Windows.h>
|
||||
#include <Endpointvolume.h>
|
||||
#include <vector>
|
||||
#include <UIAutomation.h>
|
||||
#include <dxgi1_3.h>
|
||||
#include <d3d11_2.h>
|
||||
#include <d2d1_3.h>
|
||||
#include <d2d1_3helper.h>
|
||||
#include <d2d1helper.h>
|
||||
#include <dwrite.h>
|
||||
#include <dcomp.h>
|
||||
#include <dwmapi.h>
|
||||
#include <Shobjidl.h>
|
||||
#include <Shlwapi.h>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <functional>
|
||||
#include <condition_variable>
|
||||
#include <stdexcept>
|
||||
#include <tuple>
|
||||
#include <unordered_set>
|
||||
#include <filesystem>
|
||||
#include <common/utils/excluded_apps.h>
|
||||
#include <common/utils/process_path.h>
|
||||
#include <../SettingsAPI/settings_objects.h>
|
||||
|
||||
@@ -165,6 +165,7 @@ namespace CommonSharedConstants
|
||||
const wchar_t SETTINGS_UPDATED_POWER_DISPLAY_EVENT[] = L"Local\\PowerToysPowerDisplay-SettingsUpdatedEvent-2e4d6f8a-1c3b-5e7f-9a1d-4c6e8f0b2d3e";
|
||||
const wchar_t POWER_DISPLAY_SEND_SETTINGS_TELEMETRY_EVENT[] = L"Local\\PowerToysPowerDisplay-SettingsTelemetryEvent-8c4f2a1d-5e3b-7f9c-1a6d-3b8e5f2c9a7d";
|
||||
const wchar_t HOTKEY_UPDATED_POWER_DISPLAY_EVENT[] = L"Local\\PowerToysPowerDisplay-HotkeyUpdatedEvent-9d5f3a2b-7e1c-4b8a-6f3d-2a9e5c7b1d4f";
|
||||
const wchar_t POWER_DISPLAY_AUTO_DISABLE_EVENT[] = L"Local\\PowerToysPowerDisplay-AutoDisableEvent-1a7bd9af-d2e0-4e57-8879-0e1c353994d0";
|
||||
const wchar_t RESCAN_POWER_DISPLAY_MONITORS_EVENT[] = L"Local\\PowerToysPowerDisplay-RescanMonitorsEvent-7f3e8c5a-1d4b-4a9e-bc6f-5d8a2b9e3c4f";
|
||||
|
||||
// IPC Messages used in PowerDisplay (Named Pipe communication)
|
||||
|
||||
246
src/common/interop/tasklist_positions.cpp
Normal file
@@ -0,0 +1,246 @@
|
||||
#include "pch.h"
|
||||
#include "tasklist_positions.h"
|
||||
|
||||
// Tried my hardest adapting this to C#, but FindWindowW didn't work properly in C#. ~Noraa Junker
|
||||
|
||||
static winrt::com_ptr<IUIAutomation> automation;
|
||||
static winrt::com_ptr<IUIAutomationElement> element;
|
||||
static winrt::com_ptr<IUIAutomationCondition> true_condition;
|
||||
|
||||
extern "C"
|
||||
{
|
||||
HWND GetTaskbarHwndForCursorMonitor(HMONITOR monitor)
|
||||
{
|
||||
POINT pt;
|
||||
if (!GetCursorPos(&pt))
|
||||
return nullptr;
|
||||
|
||||
// Find the primary taskbar
|
||||
HWND primaryTaskbar = FindWindowW(L"Shell_TrayWnd", nullptr);
|
||||
if (primaryTaskbar)
|
||||
{
|
||||
MONITORINFO mi = { sizeof(mi) };
|
||||
if (GetWindowRect(primaryTaskbar, &mi.rcMonitor))
|
||||
{
|
||||
HMONITOR primaryMonitor = MonitorFromRect(&mi.rcMonitor, MONITOR_DEFAULTTONEAREST);
|
||||
if (primaryMonitor == monitor)
|
||||
return primaryTaskbar;
|
||||
}
|
||||
}
|
||||
|
||||
// Find the secondary taskbar(s)
|
||||
HWND secondaryTaskbar = nullptr;
|
||||
while ((secondaryTaskbar = FindWindowExW(nullptr, secondaryTaskbar, L"Shell_SecondaryTrayWnd", nullptr)) != nullptr)
|
||||
{
|
||||
MONITORINFO mi = { sizeof(mi) };
|
||||
RECT rc;
|
||||
if (GetWindowRect(secondaryTaskbar, &rc))
|
||||
{
|
||||
HMONITOR taskbarMonitor = MonitorFromRect(&rc, MONITOR_DEFAULTTONEAREST);
|
||||
if (monitor == taskbarMonitor)
|
||||
return secondaryTaskbar;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void update(HMONITOR monitor)
|
||||
{
|
||||
// Get HWND of the tasklist for the monitor under the cursor
|
||||
auto taskbar_hwnd = GetTaskbarHwndForCursorMonitor(monitor);
|
||||
if (!taskbar_hwnd)
|
||||
return;
|
||||
|
||||
wchar_t class_name[64] = {};
|
||||
GetClassNameW(taskbar_hwnd, class_name, 64);
|
||||
|
||||
HWND tasklist_hwnd = nullptr;
|
||||
|
||||
if (wcscmp(class_name, L"Shell_TrayWnd") == 0)
|
||||
{
|
||||
// Primary taskbar structure
|
||||
tasklist_hwnd = FindWindowExW(taskbar_hwnd, 0, L"ReBarWindow32", nullptr);
|
||||
if (!tasklist_hwnd)
|
||||
return;
|
||||
tasklist_hwnd = FindWindowExW(tasklist_hwnd, 0, L"MSTaskSwWClass", nullptr);
|
||||
if (!tasklist_hwnd)
|
||||
return;
|
||||
tasklist_hwnd = FindWindowExW(tasklist_hwnd, 0, L"MSTaskListWClass", nullptr);
|
||||
if (!tasklist_hwnd)
|
||||
return;
|
||||
}
|
||||
else if (wcscmp(class_name, L"Shell_SecondaryTrayWnd") == 0)
|
||||
{
|
||||
// Secondary taskbar structure
|
||||
HWND worker_hwnd = FindWindowExW(taskbar_hwnd, 0, L"WorkerW", nullptr);
|
||||
if (!worker_hwnd)
|
||||
return;
|
||||
tasklist_hwnd = FindWindowExW(worker_hwnd, 0, L"MSTaskListWClass", nullptr);
|
||||
if (!tasklist_hwnd)
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unknown taskbar type
|
||||
return;
|
||||
}
|
||||
|
||||
if (!automation)
|
||||
{
|
||||
winrt::check_hresult(CoCreateInstance(CLSID_CUIAutomation,
|
||||
nullptr,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
IID_IUIAutomation,
|
||||
automation.put_void()));
|
||||
winrt::check_hresult(automation->CreateTrueCondition(true_condition.put()));
|
||||
}
|
||||
element = nullptr;
|
||||
winrt::check_hresult(automation->ElementFromHandle(tasklist_hwnd, element.put()));
|
||||
}
|
||||
|
||||
void update_new(HMONITOR monitor)
|
||||
{
|
||||
// Get HWND of the tasklist for the monitor under the cursor
|
||||
auto taskbar_hwnd = GetTaskbarHwndForCursorMonitor(monitor);
|
||||
if (!taskbar_hwnd)
|
||||
return;
|
||||
|
||||
wchar_t class_name[64] = {};
|
||||
GetClassNameW(taskbar_hwnd, class_name, 64);
|
||||
|
||||
HWND tasklist_hwnd = nullptr;
|
||||
|
||||
if (wcscmp(class_name, L"Shell_TrayWnd") == 0 || wcscmp(class_name, L"Shell_SecondaryTrayWnd") == 0)
|
||||
{
|
||||
// Primary taskbar structure
|
||||
tasklist_hwnd = FindWindowExW(taskbar_hwnd, 0, L"Windows.UI.Composition.DesktopWindowContentBridge", nullptr);
|
||||
if (!tasklist_hwnd)
|
||||
return;
|
||||
tasklist_hwnd = FindWindowExW(tasklist_hwnd, 0, L"Windows.UI.Input.InputSite.WindowClass", nullptr);
|
||||
if (!tasklist_hwnd)
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unknown taskbar type
|
||||
return;
|
||||
}
|
||||
|
||||
if (!automation)
|
||||
{
|
||||
winrt::check_hresult(CoCreateInstance(CLSID_CUIAutomation,
|
||||
nullptr,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
IID_IUIAutomation,
|
||||
automation.put_void()));
|
||||
winrt::check_hresult(automation->CreateTrueCondition(true_condition.put()));
|
||||
}
|
||||
|
||||
winrt::com_ptr<IUIAutomationElement> tempElement;
|
||||
element = nullptr;
|
||||
winrt::check_hresult(automation->ElementFromHandle(tasklist_hwnd, tempElement.put()));
|
||||
|
||||
winrt::check_hresult(
|
||||
tempElement->FindFirst(TreeScope_Children, true_condition.get(), element.put()));
|
||||
}
|
||||
|
||||
bool update_buttons(std::vector<TasklistButton>& buttons)
|
||||
{
|
||||
if (!automation || !element)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
winrt::com_ptr<IUIAutomationElementArray> elements;
|
||||
if (element->FindAll(TreeScope_Children, true_condition.get(), elements.put()) < 0)
|
||||
return false;
|
||||
if (!elements)
|
||||
return false;
|
||||
int count;
|
||||
if (elements->get_Length(&count) < 0)
|
||||
return false;
|
||||
winrt::com_ptr<IUIAutomationElement> child;
|
||||
std::vector<TasklistButton> found_buttons;
|
||||
found_buttons.reserve(count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
child = nullptr;
|
||||
if (elements->GetElement(i, child.put()) < 0)
|
||||
return false;
|
||||
TasklistButton button = {};
|
||||
if (VARIANT var_rect; child->GetCurrentPropertyValue(UIA_BoundingRectanglePropertyId, &var_rect) >= 0)
|
||||
{
|
||||
if (var_rect.vt == (VT_R8 | VT_ARRAY))
|
||||
{
|
||||
LONG pos;
|
||||
double value;
|
||||
pos = 0;
|
||||
SafeArrayGetElement(var_rect.parray, &pos, &value);
|
||||
button.x = static_cast<long>(value);
|
||||
pos = 1;
|
||||
SafeArrayGetElement(var_rect.parray, &pos, &value);
|
||||
button.y = static_cast<long>(value);
|
||||
pos = 2;
|
||||
SafeArrayGetElement(var_rect.parray, &pos, &value);
|
||||
button.width = static_cast<long>(value);
|
||||
pos = 3;
|
||||
SafeArrayGetElement(var_rect.parray, &pos, &value);
|
||||
button.height = static_cast<long>(value);
|
||||
}
|
||||
VariantClear(&var_rect);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (BSTR automation_id; child->get_CurrentAutomationId(&automation_id) >= 0)
|
||||
{
|
||||
wcsncpy_s(button.name, automation_id, _countof(button.name));
|
||||
SysFreeString(automation_id);
|
||||
if (wcsncmp(button.name, L"Appid:", wcslen(L"Appid:")) != 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
found_buttons.push_back(button);
|
||||
}
|
||||
// assign keynums
|
||||
buttons.clear();
|
||||
for (auto& button : found_buttons)
|
||||
{
|
||||
if (buttons.empty())
|
||||
{
|
||||
button.keynum = 1;
|
||||
buttons.push_back(std::move(button));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (button.x < buttons.back().x || button.y < buttons.back().y) // skip 2nd row
|
||||
break;
|
||||
if (wcsncmp(button.name, buttons.back().name, _countof(button.name)) == 0)
|
||||
continue; // skip buttons from the same app
|
||||
button.keynum = buttons.back().keynum + 1;
|
||||
buttons.push_back(std::move(button));
|
||||
if (buttons.back().keynum == 10)
|
||||
break; // no more than 10 buttons
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
__declspec(dllexport) TasklistButton* get_buttons(HMONITOR monitor, int* size)
|
||||
{
|
||||
update(monitor);
|
||||
static std::vector<TasklistButton> buttons;
|
||||
update_buttons(buttons);
|
||||
*size = static_cast<int>(buttons.size());
|
||||
if (*size == 0)
|
||||
{
|
||||
// After a certain Windows update, the old method stopped working, try the new one
|
||||
update_new(monitor);
|
||||
update_buttons(buttons);
|
||||
*size = static_cast<int>(buttons.size());
|
||||
}
|
||||
return buttons.data();
|
||||
}
|
||||
}
|
||||
19
src/common/interop/tasklist_positions.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
struct TasklistButton
|
||||
{
|
||||
wchar_t name[256];
|
||||
int x;
|
||||
int y;
|
||||
int width;
|
||||
int height;
|
||||
int keynum;
|
||||
};
|
||||
|
||||
extern "C"
|
||||
{
|
||||
// Helper to get the taskbar HWND for the monitor under the cursor
|
||||
HWND GetTaskbarHwndForCursorMonitor(HMONITOR monitor);
|
||||
bool update_buttons(std::vector<TasklistButton>& buttons);
|
||||
__declspec(dllexport) TasklistButton* get_buttons(HMONITOR monitor, int* size);
|
||||
}
|
||||
@@ -70,9 +70,6 @@
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\logging\logging.vcxproj">
|
||||
<Project>{7e1e3f13-2bd6-3f75-a6a7-873a2b55c60f}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\version\version.vcxproj">
|
||||
<Project>{cc6e41ac-8174-4e8a-8d22-85dd7f4851df}</Project>
|
||||
</ProjectReference>
|
||||
|
||||
679
src/common/updating/UnitTests/UpdatingTests.cpp
Normal file
@@ -0,0 +1,679 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <common/updating/configBackup.h>
|
||||
#include <common/updating/updateLifecycle.h>
|
||||
|
||||
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace UpdatingUnitTests
|
||||
{
|
||||
// Helper to create a temp directory for test isolation.
|
||||
// Each instance gets a unique subdirectory to prevent test interference.
|
||||
class TempDir
|
||||
{
|
||||
public:
|
||||
TempDir()
|
||||
{
|
||||
wchar_t tempPath[MAX_PATH + 1];
|
||||
GetTempPathW(MAX_PATH, tempPath);
|
||||
static std::atomic<int> counter{0};
|
||||
m_path = fs::path(tempPath) / (L"PowerToysUpdateTests_" + std::to_wstring(counter++));
|
||||
|
||||
// Ensure clean state
|
||||
std::error_code ec;
|
||||
fs::remove_all(m_path, ec);
|
||||
fs::create_directories(m_path, ec);
|
||||
}
|
||||
|
||||
~TempDir()
|
||||
{
|
||||
std::error_code ec;
|
||||
fs::remove_all(m_path, ec);
|
||||
}
|
||||
|
||||
const fs::path& path() const { return m_path; }
|
||||
|
||||
// Write a file with the given content
|
||||
void WriteFile(const fs::path& relativePath, const std::string& content)
|
||||
{
|
||||
auto fullPath = m_path / relativePath;
|
||||
fs::create_directories(fullPath.parent_path());
|
||||
std::ofstream file(fullPath, std::ios::binary);
|
||||
file.write(content.data(), content.size());
|
||||
}
|
||||
|
||||
// Write a file with raw bytes (including null bytes for corruption testing)
|
||||
void WriteFileBytes(const fs::path& relativePath, const std::vector<char>& bytes)
|
||||
{
|
||||
auto fullPath = m_path / relativePath;
|
||||
fs::create_directories(fullPath.parent_path());
|
||||
std::ofstream file(fullPath, std::ios::binary);
|
||||
file.write(bytes.data(), bytes.size());
|
||||
}
|
||||
|
||||
// Read file content as string
|
||||
std::string ReadFile(const fs::path& relativePath)
|
||||
{
|
||||
auto fullPath = m_path / relativePath;
|
||||
std::ifstream file(fullPath, std::ios::binary);
|
||||
return std::string(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
|
||||
}
|
||||
|
||||
bool FileExists(const fs::path& relativePath)
|
||||
{
|
||||
return fs::exists(m_path / relativePath);
|
||||
}
|
||||
|
||||
private:
|
||||
fs::path m_path;
|
||||
};
|
||||
|
||||
TEST_CLASS(IsJsonFileCorruptedTests)
|
||||
{
|
||||
public:
|
||||
// Tests IsJsonFileCorrupted: valid JSON with no null bytes returns false.
|
||||
// Covers: configBackup.h IsJsonFileCorrupted — happy path, full file scan.
|
||||
TEST_METHOD(CleanJsonFileIsNotCorrupted)
|
||||
{
|
||||
TempDir dir;
|
||||
dir.WriteFile(L"settings.json", R"({"theme":"dark","startup":true})");
|
||||
|
||||
Assert::IsFalse(updating::IsJsonFileCorrupted(dir.path() / L"settings.json"));
|
||||
}
|
||||
|
||||
// Tests IsJsonFileCorrupted: zero-length file returns false (empty is not corrupted).
|
||||
// Covers: configBackup.h IsJsonFileCorrupted — file.read returns 0 bytes immediately.
|
||||
TEST_METHOD(EmptyFileIsNotCorrupted)
|
||||
{
|
||||
TempDir dir;
|
||||
dir.WriteFile(L"empty.json", "");
|
||||
|
||||
Assert::IsFalse(updating::IsJsonFileCorrupted(dir.path() / L"empty.json"));
|
||||
}
|
||||
|
||||
// Tests IsJsonFileCorrupted: file containing embedded null bytes returns true.
|
||||
// Covers: configBackup.h IsJsonFileCorrupted — null byte detection within buffer.
|
||||
TEST_METHOD(FileWithNullBytesIsCorrupted)
|
||||
{
|
||||
TempDir dir;
|
||||
std::vector<char> corrupted = { '{', '"', 'a', '"', ':', '\0', '\0', '\0', '}' };
|
||||
dir.WriteFileBytes(L"corrupted.json", corrupted);
|
||||
|
||||
Assert::IsTrue(updating::IsJsonFileCorrupted(dir.path() / L"corrupted.json"));
|
||||
}
|
||||
|
||||
// Tests IsJsonFileCorrupted: file entirely filled with 0x00 bytes returns true.
|
||||
// Reproduces the exact bug from #46179 where installer zeroed out JSON files.
|
||||
// Covers: configBackup.h IsJsonFileCorrupted — first byte is null.
|
||||
TEST_METHOD(FileFilledWithNullBytesIsCorrupted)
|
||||
{
|
||||
TempDir dir;
|
||||
std::vector<char> allNulls(1024, '\0');
|
||||
dir.WriteFileBytes(L"workspaces.json", allNulls);
|
||||
|
||||
Assert::IsTrue(updating::IsJsonFileCorrupted(dir.path() / L"workspaces.json"));
|
||||
}
|
||||
|
||||
// Tests IsJsonFileCorrupted: path that does not exist returns false.
|
||||
// Covers: configBackup.h IsJsonFileCorrupted — file.is_open() check.
|
||||
TEST_METHOD(NonExistentFileIsNotCorrupted)
|
||||
{
|
||||
TempDir dir;
|
||||
|
||||
Assert::IsFalse(updating::IsJsonFileCorrupted(dir.path() / L"missing.json"));
|
||||
}
|
||||
|
||||
// Tests IsJsonFileCorrupted: file larger than the 4096-byte read chunk
|
||||
// with no null bytes returns false.
|
||||
// Covers: configBackup.h IsJsonFileCorrupted — multi-chunk while loop.
|
||||
TEST_METHOD(LargeCleanFileIsNotCorrupted)
|
||||
{
|
||||
TempDir dir;
|
||||
std::string largeContent(8192, 'x');
|
||||
dir.WriteFile(L"large.json", largeContent);
|
||||
|
||||
Assert::IsFalse(updating::IsJsonFileCorrupted(dir.path() / L"large.json"));
|
||||
}
|
||||
|
||||
// Tests IsJsonFileCorrupted: null byte placed after the first 4096-byte
|
||||
// chunk boundary is still detected.
|
||||
// Covers: configBackup.h IsJsonFileCorrupted — second chunk scan.
|
||||
TEST_METHOD(NullByteAtEndOfLargeFileIsDetected)
|
||||
{
|
||||
TempDir dir;
|
||||
std::string content(5000, 'x');
|
||||
content[4999] = '\0';
|
||||
std::vector<char> bytes(content.begin(), content.end());
|
||||
dir.WriteFileBytes(L"sneaky.json", bytes);
|
||||
|
||||
Assert::IsTrue(updating::IsJsonFileCorrupted(dir.path() / L"sneaky.json"));
|
||||
}
|
||||
};
|
||||
|
||||
TEST_CLASS(BackupConfigFilesTests)
|
||||
{
|
||||
public:
|
||||
// Tests BackupConfigFiles: root-level .json files are copied to ConfigBackup.
|
||||
// Covers: configBackup.h BackupConfigFiles — root directory_iterator,
|
||||
// is_regular_file && extension == ".json" branch.
|
||||
// Setup: Two root-level JSON files.
|
||||
TEST_METHOD(BackupCopiesRootJsonFiles)
|
||||
{
|
||||
TempDir dir;
|
||||
dir.WriteFile(L"settings.json", R"({"theme":"dark"})");
|
||||
dir.WriteFile(L"UpdateState.json", R"({"state":0})");
|
||||
|
||||
updating::BackupConfigFiles(dir.path());
|
||||
|
||||
Assert::IsTrue(dir.FileExists(L"ConfigBackup\\settings.json"));
|
||||
Assert::IsTrue(dir.FileExists(L"ConfigBackup\\UpdateState.json"));
|
||||
Assert::AreEqual(std::string(R"({"theme":"dark"})"), dir.ReadFile(L"ConfigBackup\\settings.json"));
|
||||
}
|
||||
|
||||
// Tests BackupConfigFiles: .json files inside module subdirectories are
|
||||
// copied to ConfigBackup/<module>/.
|
||||
// Covers: configBackup.h BackupConfigFiles — is_directory branch,
|
||||
// module directory_iterator with extension filter.
|
||||
// Setup: Root JSON + two module directories with JSON files.
|
||||
TEST_METHOD(BackupCopiesModuleJsonFiles)
|
||||
{
|
||||
TempDir dir;
|
||||
dir.WriteFile(L"settings.json", R"({"theme":"dark"})");
|
||||
dir.WriteFile(L"FancyZones\\settings.json", R"({"zones":[]})");
|
||||
dir.WriteFile(L"Workspaces\\workspaces.json", R"({"workspaces":[]})");
|
||||
|
||||
updating::BackupConfigFiles(dir.path());
|
||||
|
||||
Assert::IsTrue(dir.FileExists(L"ConfigBackup\\FancyZones\\settings.json"));
|
||||
Assert::IsTrue(dir.FileExists(L"ConfigBackup\\Workspaces\\workspaces.json"));
|
||||
Assert::AreEqual(std::string(R"({"zones":[]})"),
|
||||
dir.ReadFile(L"ConfigBackup\\FancyZones\\settings.json"));
|
||||
}
|
||||
|
||||
// Tests BackupConfigFiles: non-.json files at root level are not copied.
|
||||
// Covers: configBackup.h BackupConfigFiles — extension filter excludes .log.
|
||||
// Setup: One JSON file + one .log file at root.
|
||||
TEST_METHOD(BackupSkipsNonJsonFiles)
|
||||
{
|
||||
TempDir dir;
|
||||
dir.WriteFile(L"settings.json", R"({"theme":"dark"})");
|
||||
dir.WriteFile(L"debug.log", "log data");
|
||||
|
||||
updating::BackupConfigFiles(dir.path());
|
||||
|
||||
Assert::IsTrue(dir.FileExists(L"ConfigBackup\\settings.json"));
|
||||
Assert::IsFalse(dir.FileExists(L"ConfigBackup\\debug.log"));
|
||||
}
|
||||
|
||||
// Tests BackupConfigFiles: the "Updates" directory is explicitly skipped.
|
||||
// Covers: configBackup.h BackupConfigFiles — dirName == L"Updates" continue.
|
||||
// Setup: Root JSON + Updates directory containing a file.
|
||||
TEST_METHOD(BackupSkipsUpdatesDirectory)
|
||||
{
|
||||
TempDir dir;
|
||||
dir.WriteFile(L"settings.json", R"({"theme":"dark"})");
|
||||
dir.WriteFile(L"Updates\\installer.exe", "fake exe");
|
||||
|
||||
updating::BackupConfigFiles(dir.path());
|
||||
|
||||
Assert::IsFalse(dir.FileExists(L"ConfigBackup\\Updates"));
|
||||
}
|
||||
|
||||
// Tests BackupConfigFiles: running backup twice overwrites the previous
|
||||
// backup with current file content.
|
||||
// Covers: configBackup.h BackupConfigFiles — fs::remove_all(backupDir) +
|
||||
// copy_options::overwrite_existing.
|
||||
// Setup: Backup, modify original, backup again.
|
||||
TEST_METHOD(BackupOverwritesPreviousBackup)
|
||||
{
|
||||
TempDir dir;
|
||||
dir.WriteFile(L"settings.json", R"({"version":1})");
|
||||
updating::BackupConfigFiles(dir.path());
|
||||
|
||||
// Update the original
|
||||
dir.WriteFile(L"settings.json", R"({"version":2})");
|
||||
updating::BackupConfigFiles(dir.path());
|
||||
|
||||
Assert::AreEqual(std::string(R"({"version":2})"), dir.ReadFile(L"ConfigBackup\\settings.json"));
|
||||
}
|
||||
|
||||
// Tests BackupConfigFiles: non-.json files inside module subdirectories
|
||||
// (e.g., FancyZones/zones.dat) should NOT be backed up.
|
||||
// Covers: configBackup.h BackupConfigFiles — extension filter in module loop.
|
||||
TEST_METHOD(BackupSkipsNonJsonFilesInModuleDirs)
|
||||
{
|
||||
TempDir dir;
|
||||
dir.WriteFile(L"settings.json", R"({})");
|
||||
dir.WriteFile(L"FancyZones\\settings.json", R"({"zones":[]})");
|
||||
dir.WriteFile(L"FancyZones\\zones.dat", "binary data");
|
||||
|
||||
updating::BackupConfigFiles(dir.path());
|
||||
|
||||
Assert::IsTrue(dir.FileExists(L"ConfigBackup\\FancyZones\\settings.json"));
|
||||
Assert::IsFalse(dir.FileExists(L"ConfigBackup\\FancyZones\\zones.dat"));
|
||||
}
|
||||
|
||||
// Tests BackupConfigFiles: empty root directory with no files produces
|
||||
// an empty ConfigBackup dir without errors.
|
||||
// Covers: configBackup.h BackupConfigFiles — empty directory_iterator.
|
||||
TEST_METHOD(BackupEmptyRootDirSucceeds)
|
||||
{
|
||||
TempDir dir;
|
||||
// Root dir exists but has no files
|
||||
|
||||
updating::BackupConfigFiles(dir.path());
|
||||
|
||||
Assert::IsTrue(dir.FileExists(L"ConfigBackup"));
|
||||
}
|
||||
};
|
||||
|
||||
TEST_CLASS(RestoreCorruptedConfigsTests)
|
||||
{
|
||||
public:
|
||||
// Tests RestoreCorruptedConfigs: corrupted root-level JSON file is restored
|
||||
// from the good backup copy.
|
||||
// Covers: configBackup.h RestoreCorruptedConfigs — root file restore branch,
|
||||
// fs::exists + IsJsonFileCorrupted + backup integrity check.
|
||||
// Setup: Good file -> backup -> corrupt original -> restore.
|
||||
TEST_METHOD(RestoreFixesCorruptedRootFile)
|
||||
{
|
||||
TempDir dir;
|
||||
const std::string goodContent = R"({"theme":"dark"})";
|
||||
dir.WriteFile(L"settings.json", goodContent);
|
||||
|
||||
// Backup
|
||||
updating::BackupConfigFiles(dir.path());
|
||||
|
||||
// Corrupt the original
|
||||
std::vector<char> corrupted(goodContent.size(), '\0');
|
||||
dir.WriteFileBytes(L"settings.json", corrupted);
|
||||
Assert::IsTrue(updating::IsJsonFileCorrupted(dir.path() / L"settings.json"));
|
||||
|
||||
// Restore
|
||||
updating::RestoreCorruptedConfigs(dir.path());
|
||||
|
||||
Assert::IsFalse(updating::IsJsonFileCorrupted(dir.path() / L"settings.json"));
|
||||
Assert::AreEqual(goodContent, dir.ReadFile(L"settings.json"));
|
||||
}
|
||||
|
||||
// Tests RestoreCorruptedConfigs: corrupted module-level JSON file is restored
|
||||
// from the good backup copy.
|
||||
// Covers: configBackup.h RestoreCorruptedConfigs — module directory branch,
|
||||
// moduleBackupEntry restore with integrity check.
|
||||
// Setup: Module file + root file -> backup -> corrupt module file -> restore.
|
||||
TEST_METHOD(RestoreFixesCorruptedModuleFile)
|
||||
{
|
||||
TempDir dir;
|
||||
const std::string goodContent = R"({"workspaces":[]})";
|
||||
dir.WriteFile(L"Workspaces\\workspaces.json", goodContent);
|
||||
dir.WriteFile(L"settings.json", R"({})");
|
||||
|
||||
updating::BackupConfigFiles(dir.path());
|
||||
|
||||
// Corrupt the module file
|
||||
std::vector<char> corrupted(goodContent.size(), '\0');
|
||||
dir.WriteFileBytes(L"Workspaces\\workspaces.json", corrupted);
|
||||
|
||||
updating::RestoreCorruptedConfigs(dir.path());
|
||||
|
||||
Assert::AreEqual(goodContent, dir.ReadFile(L"Workspaces\\workspaces.json"));
|
||||
}
|
||||
|
||||
// Tests RestoreCorruptedConfigs: clean (non-corrupted) files are NOT
|
||||
// overwritten by backup — preserves user changes made after backup.
|
||||
// Covers: configBackup.h RestoreCorruptedConfigs — IsJsonFileCorrupted
|
||||
// returns false, copy_file is skipped.
|
||||
// Setup: File -> backup -> modify (but keep valid) -> restore.
|
||||
TEST_METHOD(RestoreLeavesCleanFilesUntouched)
|
||||
{
|
||||
TempDir dir;
|
||||
dir.WriteFile(L"settings.json", R"({"version":1})");
|
||||
|
||||
updating::BackupConfigFiles(dir.path());
|
||||
|
||||
// Modify original (but keep it clean JSON)
|
||||
dir.WriteFile(L"settings.json", R"({"version":2})");
|
||||
|
||||
updating::RestoreCorruptedConfigs(dir.path());
|
||||
|
||||
// Should NOT have been restored since it's not corrupted
|
||||
Assert::AreEqual(std::string(R"({"version":2})"), dir.ReadFile(L"settings.json"));
|
||||
}
|
||||
|
||||
// Tests RestoreCorruptedConfigs: when no ConfigBackup directory exists,
|
||||
// restore silently does nothing (no crash, no data loss).
|
||||
// Covers: configBackup.h RestoreCorruptedConfigs — !fs::exists(backupDir)
|
||||
// early return.
|
||||
// Setup: File with no prior backup.
|
||||
TEST_METHOD(RestoreHandlesMissingBackupDirectory)
|
||||
{
|
||||
TempDir dir;
|
||||
dir.WriteFile(L"settings.json", R"({"theme":"dark"})");
|
||||
|
||||
// No backup was created - restore should silently do nothing
|
||||
updating::RestoreCorruptedConfigs(dir.path());
|
||||
|
||||
Assert::AreEqual(std::string(R"({"theme":"dark"})"), dir.ReadFile(L"settings.json"));
|
||||
}
|
||||
|
||||
// Tests RestoreCorruptedConfigs: end-to-end scenario with multiple modules,
|
||||
// some corrupted and some clean, verifying selective restore.
|
||||
// Covers: configBackup.h RestoreCorruptedConfigs — both root and module
|
||||
// branches, selective restore based on corruption status.
|
||||
// Setup: 4 modules -> backup -> corrupt 2 -> restore -> verify all 4.
|
||||
TEST_METHOD(FullBackupAndRestoreRoundTrip)
|
||||
{
|
||||
TempDir dir;
|
||||
|
||||
// Set up a realistic config structure
|
||||
dir.WriteFile(L"settings.json", R"({"startup":true,"theme":"dark"})");
|
||||
dir.WriteFile(L"FancyZones\\settings.json", R"({"zones":[{"id":1}]})");
|
||||
dir.WriteFile(L"Workspaces\\workspaces.json", R"({"workspaces":[{"name":"dev"}]})");
|
||||
dir.WriteFile(L"KeyboardManager\\default.json", R"({"remaps":[]})");
|
||||
|
||||
// Backup
|
||||
updating::BackupConfigFiles(dir.path());
|
||||
|
||||
// Corrupt some files (simulating #46179 scenario)
|
||||
dir.WriteFileBytes(L"Workspaces\\workspaces.json", std::vector<char>(100, '\0'));
|
||||
dir.WriteFileBytes(L"settings.json", std::vector<char>(50, '\0'));
|
||||
// Leave FancyZones and KBM clean
|
||||
|
||||
// Restore
|
||||
updating::RestoreCorruptedConfigs(dir.path());
|
||||
|
||||
// Corrupted files should be restored
|
||||
Assert::AreEqual(std::string(R"({"startup":true,"theme":"dark"})"), dir.ReadFile(L"settings.json"));
|
||||
Assert::AreEqual(std::string(R"({"workspaces":[{"name":"dev"}]})"), dir.ReadFile(L"Workspaces\\workspaces.json"));
|
||||
|
||||
// Clean files should be unchanged
|
||||
Assert::AreEqual(std::string(R"({"zones":[{"id":1}]})"), dir.ReadFile(L"FancyZones\\settings.json"));
|
||||
Assert::AreEqual(std::string(R"({"remaps":[]})"), dir.ReadFile(L"KeyboardManager\\default.json"));
|
||||
}
|
||||
|
||||
// Tests RestoreCorruptedConfigs: when the original file has been deleted
|
||||
// (not corrupted), restore should NOT recreate it from backup. The installer
|
||||
// may have intentionally removed obsolete config files.
|
||||
// Covers: configBackup.h RestoreCorruptedConfigs — fs::exists guard.
|
||||
TEST_METHOD(RestoreSkipsDeletedOriginals)
|
||||
{
|
||||
TempDir dir;
|
||||
dir.WriteFile(L"obsolete.json", R"({"old":true})");
|
||||
updating::BackupConfigFiles(dir.path());
|
||||
|
||||
// Installer deletes the file
|
||||
std::error_code ec;
|
||||
fs::remove(dir.path() / L"obsolete.json", ec);
|
||||
|
||||
updating::RestoreCorruptedConfigs(dir.path());
|
||||
|
||||
// Should NOT be recreated
|
||||
Assert::IsFalse(dir.FileExists(L"obsolete.json"));
|
||||
}
|
||||
|
||||
// Tests RestoreCorruptedConfigs: when the backup file itself is corrupted
|
||||
// (e.g., disk error during backup), restore should NOT copy corrupted
|
||||
// backup over the original — that would make things worse.
|
||||
// Covers: configBackup.h RestoreCorruptedConfigs — backup integrity check (B2 fix).
|
||||
TEST_METHOD(RestoreSkipsCorruptedBackup)
|
||||
{
|
||||
TempDir dir;
|
||||
dir.WriteFile(L"settings.json", R"({"theme":"dark"})");
|
||||
updating::BackupConfigFiles(dir.path());
|
||||
|
||||
// Corrupt BOTH the original AND the backup
|
||||
std::vector<char> nulls(50, '\0');
|
||||
dir.WriteFileBytes(L"settings.json", nulls);
|
||||
dir.WriteFileBytes(L"ConfigBackup\\settings.json", nulls);
|
||||
|
||||
updating::RestoreCorruptedConfigs(dir.path());
|
||||
|
||||
// Original should still be corrupted — we don't restore from bad backup
|
||||
Assert::IsTrue(updating::IsJsonFileCorrupted(dir.path() / L"settings.json"));
|
||||
}
|
||||
};
|
||||
|
||||
// Simulates what actually happens during a PowerToys upgrade:
|
||||
// 1. User has settings from normal use
|
||||
// 2. Updater backs up before install (Stage 1)
|
||||
// 3. Installer runs and corrupts some files (simulated)
|
||||
// 4. Updater restores corrupted files (Stage 2)
|
||||
// 5. PT relaunches and finds working configs
|
||||
TEST_CLASS(UpgradeSimulationTests)
|
||||
{
|
||||
public:
|
||||
// Tests full upgrade simulation: backup -> installer corrupts files -> restore.
|
||||
// Verifies that corrupted files are restored and clean files are untouched.
|
||||
// Covers: configBackup.h BackupConfigFiles + RestoreCorruptedConfigs —
|
||||
// end-to-end with 5 modules, 2 corrupted, 3 clean.
|
||||
// Setup: Realistic config structure with multiple modules.
|
||||
TEST_METHOD(SimulateUpgradeWithCorruption)
|
||||
{
|
||||
TempDir dir;
|
||||
|
||||
// === User's real config state before upgrade ===
|
||||
dir.WriteFile(L"settings.json",
|
||||
R"({"startup":true,"theme":"dark","run_elevated":false,"download_updates_automatically":true})");
|
||||
dir.WriteFile(L"FancyZones\\settings.json",
|
||||
R"({"zones":[{"id":1,"rect":{"x":0,"y":0,"w":960,"h":1080}}]})");
|
||||
dir.WriteFile(L"Workspaces\\workspaces.json",
|
||||
R"({"workspaces":[{"name":"dev","apps":["code","terminal"]}]})");
|
||||
dir.WriteFile(L"KeyboardManager\\default.json",
|
||||
R"({"remapKeys":{"inProcess":[{"original":"0x41","new":"0x42"}]}})");
|
||||
dir.WriteFile(L"MouseWithoutBorders\\settings.json",
|
||||
R"({"machineKey":"abc123","connectToAll":true})");
|
||||
|
||||
// Non-JSON files that should be left alone
|
||||
dir.WriteFile(L"update.log", "2026-04-11 update started");
|
||||
|
||||
// === Stage 1: Backup before killing PT ===
|
||||
updating::BackupConfigFiles(dir.path());
|
||||
|
||||
// Verify backup was created correctly
|
||||
Assert::IsTrue(dir.FileExists(L"ConfigBackup\\settings.json"));
|
||||
Assert::IsTrue(dir.FileExists(L"ConfigBackup\\FancyZones\\settings.json"));
|
||||
Assert::IsTrue(dir.FileExists(L"ConfigBackup\\Workspaces\\workspaces.json"));
|
||||
Assert::IsTrue(dir.FileExists(L"ConfigBackup\\KeyboardManager\\default.json"));
|
||||
Assert::IsTrue(dir.FileExists(L"ConfigBackup\\MouseWithoutBorders\\settings.json"));
|
||||
Assert::IsFalse(dir.FileExists(L"ConfigBackup\\update.log"));
|
||||
|
||||
// === Installer runs: some files get corrupted (the #46179 scenario) ===
|
||||
// Workspaces JSON filled with null bytes
|
||||
dir.WriteFileBytes(L"Workspaces\\workspaces.json", std::vector<char>(512, '\0'));
|
||||
// Main settings partially corrupted (null bytes injected)
|
||||
std::vector<char> partialCorrupt = { '{', '"', 's', '\0', '\0', '\0', '\0', '}' };
|
||||
dir.WriteFileBytes(L"settings.json", partialCorrupt);
|
||||
|
||||
// FancyZones, KBM, and MWB survive the install fine
|
||||
// (this is realistic - not all files get corrupted)
|
||||
|
||||
// === Stage 2: Restore after install completes ===
|
||||
updating::RestoreCorruptedConfigs(dir.path());
|
||||
|
||||
// === Verify: PT relaunches and finds working configs ===
|
||||
|
||||
// Corrupted files should be restored from backup
|
||||
Assert::IsFalse(updating::IsJsonFileCorrupted(dir.path() / L"settings.json"));
|
||||
Assert::IsFalse(updating::IsJsonFileCorrupted(dir.path() / L"Workspaces\\workspaces.json"));
|
||||
Assert::AreEqual(
|
||||
std::string(R"({"startup":true,"theme":"dark","run_elevated":false,"download_updates_automatically":true})"),
|
||||
dir.ReadFile(L"settings.json"));
|
||||
Assert::AreEqual(
|
||||
std::string(R"({"workspaces":[{"name":"dev","apps":["code","terminal"]}]})"),
|
||||
dir.ReadFile(L"Workspaces\\workspaces.json"));
|
||||
|
||||
// Clean files should be untouched (not overwritten with backup)
|
||||
Assert::AreEqual(
|
||||
std::string(R"({"zones":[{"id":1,"rect":{"x":0,"y":0,"w":960,"h":1080}}]})"),
|
||||
dir.ReadFile(L"FancyZones\\settings.json"));
|
||||
Assert::AreEqual(
|
||||
std::string(R"({"remapKeys":{"inProcess":[{"original":"0x41","new":"0x42"}]}})"),
|
||||
dir.ReadFile(L"KeyboardManager\\default.json"));
|
||||
Assert::AreEqual(
|
||||
std::string(R"({"machineKey":"abc123","connectToAll":true})"),
|
||||
dir.ReadFile(L"MouseWithoutBorders\\settings.json"));
|
||||
}
|
||||
|
||||
// Tests upgrade from an old version that has fewer modules than the new version.
|
||||
// Verifies that new module configs (created by the installer) are not touched
|
||||
// by restore, while corrupted old configs are restored.
|
||||
// Covers: configBackup.h RestoreCorruptedConfigs — module dir in root that
|
||||
// has no corresponding backup entry.
|
||||
// Setup: Old version with 1 module -> backup -> new installer adds module -> corrupt old -> restore.
|
||||
TEST_METHOD(SimulateUpgradeFromVeryOldVersion)
|
||||
{
|
||||
TempDir dir;
|
||||
|
||||
// Old version had fewer modules - only settings.json
|
||||
dir.WriteFile(L"settings.json", R"({"theme":"dark","powertoys_version":"v0.60.0"})");
|
||||
|
||||
// Backup
|
||||
updating::BackupConfigFiles(dir.path());
|
||||
|
||||
// New installer creates new module dirs that didn't exist before
|
||||
dir.WriteFile(L"NewModule\\settings.json", R"({"enabled":true})");
|
||||
|
||||
// Old settings get corrupted during upgrade
|
||||
dir.WriteFileBytes(L"settings.json", std::vector<char>(100, '\0'));
|
||||
|
||||
// Restore
|
||||
updating::RestoreCorruptedConfigs(dir.path());
|
||||
|
||||
// Old settings restored
|
||||
Assert::AreEqual(
|
||||
std::string(R"({"theme":"dark","powertoys_version":"v0.60.0"})"),
|
||||
dir.ReadFile(L"settings.json"));
|
||||
|
||||
// New module settings untouched (no backup existed for them)
|
||||
Assert::AreEqual(
|
||||
std::string(R"({"enabled":true})"),
|
||||
dir.ReadFile(L"NewModule\\settings.json"));
|
||||
}
|
||||
};
|
||||
|
||||
// Tests for the update lifecycle: argument passing between Stage 1 and Stage 2,
|
||||
// relaunch path construction, and the handoff that was broken in #42004/#43011/#44071.
|
||||
TEST_CLASS(UpdateLifecycleTests)
|
||||
{
|
||||
public:
|
||||
// Tests BuildStage2Arguments: output contains the stage 2 flag, installer path,
|
||||
// and install directory — all three components needed for Stage 2.
|
||||
// Covers: updateLifecycle.h BuildStage2Arguments — concatenation logic.
|
||||
// Setup: Typical paths with spaces (Program Files).
|
||||
TEST_METHOD(BuildStage2ArgumentsContainsInstallerAndInstallDir)
|
||||
{
|
||||
const auto args = updating::BuildStage2Arguments(
|
||||
L"-update_now_stage_2",
|
||||
L"C:\\Users\\test\\AppData\\Local\\PowerToys\\Updates\\powertoyssetup-x64.exe",
|
||||
L"C:\\Program Files\\PowerToys");
|
||||
|
||||
// Must contain the stage 2 flag
|
||||
Assert::IsTrue(args.find(L"-update_now_stage_2") != std::wstring::npos);
|
||||
// Must contain the installer path (quoted)
|
||||
Assert::IsTrue(args.find(L"powertoyssetup-x64.exe") != std::wstring::npos);
|
||||
// Must contain the install directory (quoted) — this was MISSING before our fix
|
||||
Assert::IsTrue(args.find(L"C:\\Program Files\\PowerToys") != std::wstring::npos);
|
||||
}
|
||||
|
||||
// Tests BuildStage2Arguments: both paths are wrapped in double quotes to
|
||||
// survive CommandLineToArgvW parsing when paths contain spaces.
|
||||
// Covers: updateLifecycle.h BuildStage2Arguments — quote wrapping.
|
||||
// Setup: Installer path with spaces.
|
||||
TEST_METHOD(BuildStage2ArgumentsQuotesBothPaths)
|
||||
{
|
||||
const auto args = updating::BuildStage2Arguments(
|
||||
L"-update_now_stage_2",
|
||||
L"C:\\path with spaces\\installer.exe",
|
||||
L"C:\\Program Files\\PowerToys");
|
||||
|
||||
// Count quotes — should have 4 (open/close for each path)
|
||||
size_t quoteCount = std::count(args.begin(), args.end(), L'"');
|
||||
Assert::AreEqual(size_t{ 4 }, quoteCount);
|
||||
}
|
||||
|
||||
// Tests BuildPowerToysExePath: appends "PowerToys.exe" to the install dir.
|
||||
// Covers: updateLifecycle.h BuildPowerToysExePath — fs::path / operator.
|
||||
// Setup: Standard install path without trailing backslash.
|
||||
TEST_METHOD(BuildPowerToysExePathAppendsExeName)
|
||||
{
|
||||
const auto path = updating::BuildPowerToysExePath(L"C:\\Program Files\\PowerToys");
|
||||
Assert::AreEqual(std::wstring(L"C:\\Program Files\\PowerToys\\PowerToys.exe"), path);
|
||||
}
|
||||
|
||||
// Tests BuildPowerToysExePath: trailing backslash does not produce double
|
||||
// backslash (e.g., "...PowerToys\\PowerToys.exe").
|
||||
// Covers: updateLifecycle.h BuildPowerToysExePath — fs::path normalizes separators.
|
||||
// Setup: Install path with trailing backslash.
|
||||
TEST_METHOD(BuildPowerToysExePathHandlesTrailingBackslash)
|
||||
{
|
||||
const auto path = updating::BuildPowerToysExePath(L"C:\\Program Files\\PowerToys\\");
|
||||
Assert::AreEqual(std::wstring(L"C:\\Program Files\\PowerToys\\PowerToys.exe"), path);
|
||||
}
|
||||
|
||||
// Tests BuildPowerToysExePath: empty string produces just "PowerToys.exe".
|
||||
// Covers: updateLifecycle.h BuildPowerToysExePath — fs::path with empty input.
|
||||
// Setup: Empty install directory string.
|
||||
TEST_METHOD(BuildPowerToysExePathHandlesEmptyString)
|
||||
{
|
||||
const auto path = updating::BuildPowerToysExePath(L"");
|
||||
Assert::AreEqual(std::wstring(L"PowerToys.exe"), path);
|
||||
}
|
||||
|
||||
// Tests CanRelaunchAfterUpdate: returns true when Stage 2 receives
|
||||
// the install directory (argCount >= 4), false otherwise.
|
||||
// This is the gate that prevents relaunch when using an old Stage 1
|
||||
// that didn't pass the install dir (#42004/#43011/#44071).
|
||||
// Covers: updateLifecycle.h CanRelaunchAfterUpdate.
|
||||
TEST_METHOD(CanRelaunchReflectsArgCount)
|
||||
{
|
||||
// Old Stage 1 (pre-fix): only passed action + installer = 3 args
|
||||
Assert::IsFalse(updating::CanRelaunchAfterUpdate(0));
|
||||
Assert::IsFalse(updating::CanRelaunchAfterUpdate(1));
|
||||
Assert::IsFalse(updating::CanRelaunchAfterUpdate(2));
|
||||
Assert::IsFalse(updating::CanRelaunchAfterUpdate(3));
|
||||
|
||||
// New Stage 1 (post-fix): passes action + installer + installDir = 4 args
|
||||
Assert::IsTrue(updating::CanRelaunchAfterUpdate(4));
|
||||
Assert::IsTrue(updating::CanRelaunchAfterUpdate(5));
|
||||
}
|
||||
|
||||
// Tests BuildStage2Arguments + CommandLineToArgvW round-trip: the exact
|
||||
// scenario where Stage 1 builds args and Windows parses them in Stage 2.
|
||||
// Verifies quoting is correct so paths with spaces survive the round trip.
|
||||
// Covers: updateLifecycle.h BuildStage2Arguments — quote correctness.
|
||||
// Setup: Realistic paths with spaces and version numbers.
|
||||
TEST_METHOD(Stage2ArgumentsCanBeRoundTrippedThroughCommandLineToArgvW)
|
||||
{
|
||||
const std::wstring installerPath = L"C:\\Users\\test user\\AppData\\Local\\PowerToys\\Updates\\powertoyssetup-0.86.0-x64.exe";
|
||||
const std::wstring installDir = L"C:\\Program Files\\PowerToys";
|
||||
|
||||
const auto args = updating::BuildStage2Arguments(L"-update_now_stage_2", installerPath, installDir);
|
||||
|
||||
// Simulate what Windows does: prepend a fake exe name and parse
|
||||
std::wstring commandLine = L"PowerToys.Update.exe " + args;
|
||||
|
||||
int argc = 0;
|
||||
LPWSTR* argv = CommandLineToArgvW(commandLine.c_str(), &argc);
|
||||
Assert::IsNotNull(argv);
|
||||
Assert::AreEqual(4, argc);
|
||||
Assert::AreEqual(std::wstring(L"-update_now_stage_2"), std::wstring(argv[1]));
|
||||
Assert::AreEqual(installerPath, std::wstring(argv[2]));
|
||||
Assert::AreEqual(installDir, std::wstring(argv[3]));
|
||||
|
||||
LocalFree(argv);
|
||||
}
|
||||
};
|
||||
}
|
||||
45
src/common/updating/UnitTests/UpdatingUnitTests.vcxproj
Normal file
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<ProjectGuid>{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>UpdatingUnitTests</RootNamespace>
|
||||
<ProjectSubType>NativeUnitTestProject</ProjectSubType>
|
||||
<ProjectName>Updating.UnitTests</ProjectName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseOfMfc>false</UseOfMfc>
|
||||
|
||||
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\tests\UpdatingUnitTests\</OutDir>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>..\;..\..\;..\..\..\;$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalLibraryDirectories>$(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="UpdatingTests.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader Condition="'$(UsePrecompiledHeaders)' != 'false'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
</Project>
|
||||
5
src/common/updating/UnitTests/pch.cpp
Normal file
@@ -0,0 +1,5 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
#include "pch.h"
|
||||
17
src/common/updating/UnitTests/pch.h
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
#ifndef PCH_H
|
||||
#define PCH_H
|
||||
|
||||
#include <atomic>
|
||||
#include <Windows.h>
|
||||
|
||||
// Suppressing 26466 - Don't use static_cast downcasts - in CppUnitTest.h
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 26466)
|
||||
#include "CppUnitTest.h"
|
||||
#pragma warning(pop)
|
||||
|
||||
#endif //PCH_H
|
||||
228
src/common/updating/configBackup.h
Normal file
@@ -0,0 +1,228 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
namespace updating
|
||||
{
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
struct BackupResult
|
||||
{
|
||||
int filesBackedUp{ 0 };
|
||||
int errors{ 0 };
|
||||
};
|
||||
|
||||
struct RestoreResult
|
||||
{
|
||||
int filesRestored{ 0 };
|
||||
int filesChecked{ 0 };
|
||||
int errors{ 0 };
|
||||
};
|
||||
|
||||
// Check if a JSON file is corrupted (contains null bytes, as seen in #46179)
|
||||
inline bool IsJsonFileCorrupted(const fs::path& filePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::ifstream file(filePath, std::ios::binary);
|
||||
if (!file.is_open())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr size_t c_readChunkSize{ 4096 };
|
||||
char buffer[c_readChunkSize];
|
||||
while (file.read(buffer, c_readChunkSize) || file.gcount() > 0)
|
||||
{
|
||||
const auto bytesRead = file.gcount();
|
||||
for (std::streamsize i = 0; i < bytesRead; ++i)
|
||||
{
|
||||
if (buffer[i] == '\0')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Backup all JSON config files before update to protect against corruption (#46179)
|
||||
inline BackupResult BackupConfigFiles(const fs::path& rootPath)
|
||||
{
|
||||
BackupResult result{};
|
||||
try
|
||||
{
|
||||
const fs::path backupDir = rootPath / L"ConfigBackup";
|
||||
|
||||
std::error_code ec;
|
||||
fs::remove_all(backupDir, ec);
|
||||
fs::create_directories(backupDir, ec);
|
||||
if (ec)
|
||||
{
|
||||
result.errors++;
|
||||
return result;
|
||||
}
|
||||
|
||||
for (const auto& entry : fs::directory_iterator(rootPath, ec))
|
||||
{
|
||||
if (ec)
|
||||
{
|
||||
result.errors++;
|
||||
break;
|
||||
}
|
||||
|
||||
if (entry.is_regular_file() && entry.path().extension() == L".json")
|
||||
{
|
||||
std::error_code copyEc;
|
||||
fs::copy_file(entry.path(), backupDir / entry.path().filename(), fs::copy_options::overwrite_existing, copyEc);
|
||||
if (copyEc)
|
||||
{
|
||||
result.errors++;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.filesBackedUp++;
|
||||
}
|
||||
}
|
||||
else if (entry.is_directory())
|
||||
{
|
||||
const auto dirName = entry.path().filename().wstring();
|
||||
if (dirName == L"ConfigBackup" || dirName == L"Updates")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto moduleBackup = backupDir / entry.path().filename();
|
||||
fs::create_directories(moduleBackup, ec);
|
||||
|
||||
std::error_code moduleEc;
|
||||
for (const auto& moduleEntry : fs::directory_iterator(entry.path(), moduleEc))
|
||||
{
|
||||
if (moduleEc)
|
||||
{
|
||||
result.errors++;
|
||||
break;
|
||||
}
|
||||
|
||||
if (moduleEntry.is_regular_file() && moduleEntry.path().extension() == L".json")
|
||||
{
|
||||
std::error_code copyEc;
|
||||
fs::copy_file(moduleEntry.path(), moduleBackup / moduleEntry.path().filename(), fs::copy_options::overwrite_existing, copyEc);
|
||||
if (copyEc)
|
||||
{
|
||||
result.errors++;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.filesBackedUp++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
result.errors++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Restore JSON configs from backup if corruption is detected after update.
|
||||
// Cleans up the backup directory afterward.
|
||||
inline RestoreResult RestoreCorruptedConfigs(const fs::path& rootPath)
|
||||
{
|
||||
RestoreResult result{};
|
||||
try
|
||||
{
|
||||
const fs::path backupDir = rootPath / L"ConfigBackup";
|
||||
|
||||
if (!fs::exists(backupDir))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
std::error_code ec;
|
||||
for (const auto& backupEntry : fs::directory_iterator(backupDir, ec))
|
||||
{
|
||||
if (ec)
|
||||
{
|
||||
result.errors++;
|
||||
break;
|
||||
}
|
||||
|
||||
if (backupEntry.is_regular_file() && backupEntry.path().extension() == L".json")
|
||||
{
|
||||
const auto originalPath = rootPath / backupEntry.path().filename();
|
||||
result.filesChecked++;
|
||||
if (fs::exists(originalPath) && IsJsonFileCorrupted(originalPath) && !IsJsonFileCorrupted(backupEntry.path()))
|
||||
{
|
||||
std::error_code copyEc;
|
||||
fs::copy_file(backupEntry.path(), originalPath, fs::copy_options::overwrite_existing, copyEc);
|
||||
if (copyEc)
|
||||
{
|
||||
result.errors++;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.filesRestored++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (backupEntry.is_directory())
|
||||
{
|
||||
const auto moduleDir = rootPath / backupEntry.path().filename();
|
||||
|
||||
std::error_code moduleEc;
|
||||
for (const auto& moduleBackupEntry : fs::directory_iterator(backupEntry.path(), moduleEc))
|
||||
{
|
||||
if (moduleEc)
|
||||
{
|
||||
result.errors++;
|
||||
break;
|
||||
}
|
||||
|
||||
if (moduleBackupEntry.is_regular_file() && moduleBackupEntry.path().extension() == L".json")
|
||||
{
|
||||
const auto originalModulePath = moduleDir / moduleBackupEntry.path().filename();
|
||||
result.filesChecked++;
|
||||
if (fs::exists(originalModulePath) && IsJsonFileCorrupted(originalModulePath) && !IsJsonFileCorrupted(moduleBackupEntry.path()))
|
||||
{
|
||||
std::error_code copyEc;
|
||||
fs::copy_file(moduleBackupEntry.path(), originalModulePath, fs::copy_options::overwrite_existing, copyEc);
|
||||
if (copyEc)
|
||||
{
|
||||
result.errors++;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.filesRestored++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up backup directory after restore check
|
||||
fs::remove_all(backupDir, ec);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
result.errors++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -24,8 +24,6 @@
|
||||
#include <regex>
|
||||
#include <charconv>
|
||||
|
||||
#include <expected.hpp>
|
||||
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
#include <winrt/Windows.Foundation.Collections.h>
|
||||
#include <winrt/Windows.ApplicationModel.h>
|
||||
|
||||
47
src/common/updating/updateLifecycle.h
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
|
||||
namespace updating
|
||||
{
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
// Build the command-line arguments for Stage 2.
|
||||
// Stage 1 passes the installer path and the PT install directory
|
||||
// so Stage 2 can run the installer and relaunch PowerToys afterward.
|
||||
// Note: paths containing embedded double-quote characters are not supported.
|
||||
// This is safe because install paths come from get_module_folderpath().
|
||||
inline std::wstring BuildStage2Arguments(
|
||||
const std::wstring& stage2Flag,
|
||||
const fs::path& installerPath,
|
||||
const fs::path& installDir)
|
||||
{
|
||||
std::wstring arguments{ stage2Flag };
|
||||
arguments += L" \"";
|
||||
arguments += installerPath.c_str();
|
||||
arguments += L"\" \"";
|
||||
arguments += installDir.c_str();
|
||||
arguments += L"\"";
|
||||
return arguments;
|
||||
}
|
||||
|
||||
// Build the full path to PowerToys.exe from the install directory.
|
||||
// Used by Stage 2 to relaunch PT after a successful update.
|
||||
inline std::wstring BuildPowerToysExePath(const std::wstring& installDir)
|
||||
{
|
||||
return (std::filesystem::path(installDir) / L"PowerToys.exe").wstring();
|
||||
}
|
||||
|
||||
// Determine whether Stage 2 has enough information to relaunch PT.
|
||||
// Returns true if the install directory argument was provided.
|
||||
inline bool CanRelaunchAfterUpdate(int argCount)
|
||||
{
|
||||
// args[0]=exe, args[1]=action, args[2]=installer, args[3]=installDir
|
||||
return argCount >= 4;
|
||||
}
|
||||
}
|
||||
@@ -87,11 +87,7 @@ namespace updating
|
||||
// If the current version starts with 0.0.*, it means we're on a local build from a farm and shouldn't check for updates.
|
||||
if constexpr (VERSION_MAJOR == 0 && VERSION_MINOR == 0)
|
||||
{
|
||||
#if USE_STD_EXPECTED
|
||||
co_return std::unexpected(LOCAL_BUILD_ERROR);
|
||||
#else
|
||||
co_return nonstd::make_unexpected(LOCAL_BUILD_ERROR);
|
||||
#endif
|
||||
}
|
||||
|
||||
try
|
||||
@@ -143,11 +139,7 @@ namespace updating
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
#if USE_STD_EXPECTED
|
||||
co_return std::unexpected(NETWORK_ERROR);
|
||||
#else
|
||||
co_return nonstd::make_unexpected(NETWORK_ERROR);
|
||||
#endif
|
||||
}
|
||||
#pragma warning(pop)
|
||||
|
||||
|
||||
@@ -5,14 +5,7 @@
|
||||
#include <filesystem>
|
||||
#include <variant>
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
//#if __MSVC_VERSION__ >= 1933 // MSVC begin to support std::unexpected in 19.33
|
||||
#if __has_include(<expected> ) // use the same way with excepted-lite to detect std::unexcepted, as using it as backup
|
||||
#include <expected>
|
||||
#define USE_STD_EXPECTED 1
|
||||
#else
|
||||
#include <expected.hpp>
|
||||
#define USE_STD_EXPECTED 0
|
||||
#endif
|
||||
|
||||
#include <common/version/helper.h>
|
||||
#include <wil/coroutine.h>
|
||||
@@ -31,12 +24,7 @@ namespace updating
|
||||
std::wstring installer_filename;
|
||||
};
|
||||
using github_version_info = std::variant<new_version_download_info, version_up_to_date>;
|
||||
|
||||
#if USE_STD_EXPECTED
|
||||
using github_version_result = std::expected<github_version_info, std::wstring>;
|
||||
#else
|
||||
using github_version_result = nonstd::expected<github_version_info, std::wstring>;
|
||||
#endif
|
||||
|
||||
wil::task<github_version_result> get_github_version_info_async(bool prerelease = false);
|
||||
wil::task<std::optional<std::filesystem::path>> download_new_version_async(new_version_download_info new_version);
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
<ProjectName>ApplicationUpdate</ProjectName>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<Import Project="..\..\..\deps\expected.props" />
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
|
||||
|
||||
@@ -1,164 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="16.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|ARM64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>ARM64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|ARM64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>ARM64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{7E1E3F13-2BD6-3F75-A6A7-873A2B55C60F}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectName>spdlog</ProjectName>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(RepoRoot)deps\spdlog.props" />
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
|
||||
<OutDir>$(RepoRoot)$(Platform)\$(Configuration)\</OutDir>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
|
||||
<DebugInformationFormat>None</DebugInformationFormat>
|
||||
<ExceptionHandling>Sync</ExceptionHandling>
|
||||
<InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<RuntimeTypeInfo>true</RuntimeTypeInfo>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<PreprocessorDefinitions>WIN32;_WINDOWS;SPDLOG_COMPILED_LIB;SPDLOG_WCHAR_FILENAMES;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ObjectFileName>$(IntDir)</ObjectFileName>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<EnableParallelCodeGeneration>true</EnableParallelCodeGeneration>
|
||||
</ClCompile>
|
||||
<Lib>
|
||||
<AdditionalOptions>%(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="$(RepoRoot)deps\spdlog\src\spdlog.cpp" />
|
||||
<ClCompile Include="$(RepoRoot)deps\spdlog\src\stdout_sinks.cpp" />
|
||||
<ClCompile Include="$(RepoRoot)deps\spdlog\src\color_sinks.cpp" />
|
||||
<ClCompile Include="$(RepoRoot)deps\spdlog\src\file_sinks.cpp" />
|
||||
<ClCompile Include="$(RepoRoot)deps\spdlog\src\async.cpp" />
|
||||
<ClCompile Include="$(RepoRoot)deps\spdlog\src\cfg.cpp" />
|
||||
<ClCompile Include="$(RepoRoot)deps\spdlog\src\fmt.cpp" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\async.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\async_logger-inl.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\async_logger.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\common-inl.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\common.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\formatter.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\fwd.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\logger-inl.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\logger.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\pattern_formatter-inl.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\pattern_formatter.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\spdlog-inl.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\spdlog.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\stopwatch.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\tweakme.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\version.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\details\backtracer-inl.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\details\backtracer.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\details\circular_q.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\details\console_globals.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\details\file_helper-inl.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\details\file_helper.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\details\fmt_helper.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\details\log_msg-inl.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\details\log_msg.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\details\log_msg_buffer-inl.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\details\log_msg_buffer.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\details\mpmc_blocking_q.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\details\null_mutex.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\details\os-inl.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\details\os.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\details\periodic_worker-inl.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\details\periodic_worker.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\details\registry-inl.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\details\registry.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\details\synchronous_factory.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\details\tcp_client-windows.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\details\tcp_client.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\details\thread_pool-inl.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\details\thread_pool.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\details\windows_include.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\sinks\android_sink.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\sinks\ansicolor_sink-inl.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\sinks\ansicolor_sink.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\sinks\base_sink-inl.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\sinks\base_sink.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\sinks\basic_file_sink-inl.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\sinks\basic_file_sink.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\sinks\daily_file_sink.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\sinks\dist_sink.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\sinks\dup_filter_sink.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\sinks\msvc_sink.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\sinks\null_sink.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\sinks\ostream_sink.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\sinks\ringbuffer_sink.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\sinks\rotating_file_sink-inl.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\sinks\rotating_file_sink.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\sinks\sink-inl.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\sinks\sink.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\sinks\stdout_color_sinks-inl.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\sinks\stdout_color_sinks.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\sinks\stdout_sinks-inl.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\sinks\stdout_sinks.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\sinks\syslog_sink.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\sinks\systemd_sink.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\sinks\tcp_sink.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\sinks\win_eventlog_sink.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\sinks\wincolor_sink-inl.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\sinks\wincolor_sink.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\fmt\bin_to_hex.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\fmt\chrono.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\fmt\fmt.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\fmt\ostr.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\fmt\bundled\chrono.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\fmt\bundled\color.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\fmt\bundled\compile.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\fmt\bundled\core.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\fmt\bundled\format-inl.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\fmt\bundled\format.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\fmt\bundled\locale.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\fmt\bundled\os.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\fmt\bundled\ostream.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\fmt\bundled\posix.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\fmt\bundled\printf.h" />
|
||||
<ClInclude Include="$(RepoRoot)deps\spdlog\include\spdlog\fmt\bundled\ranges.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -1,122 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="16.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<ClCompile Include="$(ProjectDir)..\..\deps\spdlog\src\spdlog.cpp" />
|
||||
<ClCompile Include="$(ProjectDir)..\..\deps\spdlog\src\stdout_sinks.cpp" />
|
||||
<ClCompile Include="$(ProjectDir)..\..\deps\spdlog\src\color_sinks.cpp" />
|
||||
<ClCompile Include="$(ProjectDir)..\..\deps\spdlog\src\file_sinks.cpp" />
|
||||
<ClCompile Include="$(ProjectDir)..\..\deps\spdlog\src\async.cpp" />
|
||||
<ClCompile Include="$(ProjectDir)..\..\deps\spdlog\src\cfg.cpp" />
|
||||
<ClCompile Include="$(ProjectDir)..\..\deps\spdlog\src\fmt.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\async.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\async_logger-inl.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\async_logger.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\common-inl.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\common.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\formatter.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\fwd.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\logger-inl.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\logger.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\pattern_formatter-inl.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\pattern_formatter.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\spdlog-inl.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\spdlog.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\stopwatch.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\tweakme.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\version.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\details\backtracer-inl.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\details\backtracer.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\details\circular_q.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\details\console_globals.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\details\file_helper-inl.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\details\file_helper.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\details\fmt_helper.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\details\log_msg-inl.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\details\log_msg.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\details\log_msg_buffer-inl.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\details\log_msg_buffer.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\details\mpmc_blocking_q.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\details\null_mutex.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\details\os-inl.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\details\os.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\details\periodic_worker-inl.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\details\periodic_worker.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\details\registry-inl.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\details\registry.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\details\synchronous_factory.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\details\tcp_client-windows.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\details\tcp_client.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\details\thread_pool-inl.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\details\thread_pool.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\details\windows_include.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\sinks\android_sink.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\sinks\ansicolor_sink-inl.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\sinks\ansicolor_sink.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\sinks\base_sink-inl.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\sinks\base_sink.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\sinks\basic_file_sink-inl.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\sinks\basic_file_sink.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\sinks\daily_file_sink.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\sinks\dist_sink.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\sinks\dup_filter_sink.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\sinks\msvc_sink.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\sinks\null_sink.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\sinks\ostream_sink.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\sinks\ringbuffer_sink.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\sinks\rotating_file_sink-inl.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\sinks\rotating_file_sink.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\sinks\sink-inl.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\sinks\sink.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\sinks\stdout_color_sinks-inl.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\sinks\stdout_color_sinks.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\sinks\stdout_sinks-inl.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\sinks\stdout_sinks.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\sinks\syslog_sink.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\sinks\systemd_sink.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\sinks\tcp_sink.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\sinks\win_eventlog_sink.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\sinks\wincolor_sink-inl.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\sinks\wincolor_sink.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\fmt\bin_to_hex.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\fmt\chrono.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\fmt\fmt.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\fmt\ostr.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\fmt\bundled\chrono.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\fmt\bundled\color.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\fmt\bundled\compile.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\fmt\bundled\core.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\fmt\bundled\format-inl.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\fmt\bundled\format.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\fmt\bundled\locale.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\fmt\bundled\os.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\fmt\bundled\ostream.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\fmt\bundled\posix.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\fmt\bundled\printf.h" />
|
||||
<ClInclude Include="$(ProjectDir)..\..\deps\spdlog\include\spdlog\fmt\bundled\ranges.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{CDF4BA23-560C-3A6F-8D1C-2F5ACA434329}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Header Files\spdlog">
|
||||
<UniqueIdentifier>{EFFE8123-D806-3145-8ABC-B48562A6C8F2}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Header Files\spdlog\details">
|
||||
<UniqueIdentifier>{C546A431-88F1-390F-B0F0-D9CAC274B7F5}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Header Files\spdlog\fmt">
|
||||
<UniqueIdentifier>{08320F28-6D0D-3217-B0B3-A98758C02C97}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Header Files\spdlog\fmt\bundled">
|
||||
<UniqueIdentifier>{C856528D-4506-3A62-B279-CBB4558CB61D}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Header Files\spdlog\sinks">
|
||||
<UniqueIdentifier>{A5EE33C4-AB64-38F0-BF4A-CCD02FFAB715}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{8B480F42-A230-3344-A387-2D050CFF7D9C}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -2,6 +2,8 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Text;
|
||||
|
||||
using AdvancedPaste.Helpers;
|
||||
using Windows.ApplicationModel.DataTransfer;
|
||||
|
||||
@@ -13,15 +15,26 @@ namespace AdvancedPaste.FuzzTests
|
||||
{
|
||||
public static void FuzzToJsonFromXmlOrCsv(ReadOnlySpan<byte> input)
|
||||
{
|
||||
// Decode the input bytes as UTF-8 text. `ReadOnlySpan<byte>.ToString()`
|
||||
// returns the type name (e.g. "System.ReadOnlySpan<Byte>[N]") rather
|
||||
// than the bytes, so an explicit decode is required to actually exercise
|
||||
// the helper with the provided input.
|
||||
string text = Encoding.UTF8.GetString(input);
|
||||
|
||||
var dataPackage = new DataPackage();
|
||||
dataPackage.SetText(text);
|
||||
|
||||
// Use GetAwaiter().GetResult() so any thrown exception surfaces with its
|
||||
// original type. `Task.Run(...).Result` wraps thrown exceptions in an
|
||||
// AggregateException, which would prevent the
|
||||
// `when (ex is ArgumentException)` filter below from matching.
|
||||
try
|
||||
{
|
||||
var dataPackage = new DataPackage();
|
||||
dataPackage.SetText(input.ToString());
|
||||
_ = Task.Run(async () => await JsonHelper.ToJsonFromXmlOrCsvAsync(dataPackage.GetView())).Result;
|
||||
_ = Task.Run(async () => await JsonHelper.ToJsonFromXmlOrCsvAsync(dataPackage.GetView())).GetAwaiter().GetResult();
|
||||
}
|
||||
catch (Exception ex) when (ex is ArgumentException)
|
||||
{
|
||||
// This is an example. It's important to filter out any *expected* exceptions from our code here.
|
||||
// It's important to filter out any *expected* exceptions from our code here.
|
||||
// However, catching all exceptions is considered an anti-pattern because it may suppress legitimate
|
||||
// issues, such as a NullReferenceException thrown by our code. In this case, we still re-throw
|
||||
// the exception, as the ToJsonFromXmlOrCsvAsync method is not expected to throw any exceptions.
|
||||
|
||||
@@ -57,7 +57,21 @@ namespace AdvancedPaste.Helpers
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var text = await clipboardData.GetTextAsync();
|
||||
string text;
|
||||
try
|
||||
{
|
||||
text = await clipboardData.GetTextAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// GetTextAsync goes through WinRT/COM and can fail for reasons outside
|
||||
// our control (e.g. clipboard contention, malformed payloads from other
|
||||
// apps). The contract for this helper is that it does not throw — any
|
||||
// failure to read clipboard text should be treated as "no text".
|
||||
Logger.LogError("Failed reading text from clipboard", ex);
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
string jsonText = string.Empty;
|
||||
|
||||
// If the text is already JSON, return it
|
||||
|
||||
@@ -28,7 +28,12 @@ namespace ipc
|
||||
|
||||
try
|
||||
{
|
||||
m_stream = std::ofstream(path);
|
||||
m_stream = std::ofstream(path, std::ios::binary);
|
||||
if (!m_stream.is_open())
|
||||
{
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
catch (...)
|
||||
|
||||
@@ -61,7 +61,7 @@ namespace winrt::PowerToys::FileLocksmithLib::Interop::implementation
|
||||
|
||||
com_array<hstring> NativeMethods::ReadPathsFromFile()
|
||||
{
|
||||
std::ifstream stream(paths_file());
|
||||
std::ifstream stream(paths_file(), std::ios::binary);
|
||||
|
||||
std::vector<std::wstring> result_cpp;
|
||||
std::wstring line;
|
||||
@@ -98,7 +98,7 @@ namespace winrt::PowerToys::FileLocksmithLib::Interop::implementation
|
||||
|
||||
bool NativeMethods::StartAsElevated(array_view<hstring const> paths)
|
||||
{
|
||||
std::ofstream stream(paths_file());
|
||||
std::ofstream stream(paths_file(), std::ios::binary);
|
||||
const WCHAR newline = L'\n';
|
||||
|
||||
for (uint32_t i = 0; i < paths.size(); i++)
|
||||
|
||||
@@ -84,7 +84,6 @@
|
||||
..\..\..\common;
|
||||
..\..\..\common\logger;
|
||||
..\..\..\common\utils;
|
||||
..\..\..\..\deps\spdlog\include;
|
||||
%(AdditionalIncludeDirectories)
|
||||
</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
|
||||
@@ -63,7 +63,6 @@
|
||||
$(RepoRoot)src\common\SettingsAPI;
|
||||
$(RepoRoot)src\common\Telemetry;
|
||||
$(RepoRoot)src\;
|
||||
..\..\..\..\deps\spdlog\include;
|
||||
./;
|
||||
%(AdditionalIncludeDirectories)
|
||||
</AdditionalIncludeDirectories>
|
||||
|
||||
@@ -196,11 +196,6 @@ internal static class Encryption
|
||||
return (uint)((hashValue[0] << 23) + (hashValue[1] << 16) + (hashValue[^1] << 8) + hashValue[2]);
|
||||
}
|
||||
|
||||
internal static string GetDebugInfo(string st)
|
||||
{
|
||||
return string.IsNullOrEmpty(st) ? st : ((byte)(Common.GetBytesU(st).Sum(value => value) % 256)).ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
internal static string CreateDefaultKey()
|
||||
{
|
||||
return CreateRandomKey();
|
||||
|
||||
@@ -62,7 +62,7 @@ internal static class Helper
|
||||
{
|
||||
Process p = Process.GetCurrentProcess();
|
||||
string procInfo = $"{p.PrivateMemorySize64 / 1024 / 1024}MB, {p.TotalProcessorTime}, {Environment.ProcessorCount}.";
|
||||
string threadStacks = $"{procInfo} {Thread.DumpThreadsStack()}";
|
||||
string threadStacks = $"{procInfo}\r\n{Thread.DumpThreadsStack()}\r\n";
|
||||
Logger.TelemetryLogTrace(threadStacks, SeverityLevel.Error);
|
||||
break;
|
||||
}
|
||||
@@ -379,7 +379,7 @@ internal static class Helper
|
||||
log += "=============================================================================================================================\r\n";
|
||||
log += $"{Application.ProductName} version {Application.ProductVersion}\r\n";
|
||||
|
||||
log += $"{Setting.Values.Username}/{Encryption.GetDebugInfo(Encryption.MyKey)}\r\n";
|
||||
log += $"{Setting.Values.Username}/{Logger.GetChecksum(Encryption.MyKey)}\r\n";
|
||||
log += $"{Common.MachineName}/{Common.MachineID}/{Common.DesMachineID}\r\n";
|
||||
log += $"Id: {Setting.Values.DeviceId}\r\n";
|
||||
log += $"Matrix: {string.Join(",", MachineStuff.MachineMatrix)}\r\n";
|
||||
@@ -428,9 +428,12 @@ internal static class Helper
|
||||
log += Setting.Values.LastPersonalizeLogonScr + "\r\n";
|
||||
log += "Name2IP =\r\n" + Setting.Values.Name2IP + "\r\n";
|
||||
|
||||
// note - this doesn't actually log the last 10 messages - it really concatenates the counts of
|
||||
// the first 10 unique messages that were logged, which isn't really very useful so we'll remove it
|
||||
/*
|
||||
log += "Last 10 trace messages:\r\n";
|
||||
|
||||
log += string.Join(Environment.NewLine, Logger.LogCounter.Select(item => $"({item.Value}): {item.Key}").Take(10));
|
||||
*/
|
||||
|
||||
log += "\r\n=============================================================================================================================";
|
||||
|
||||
|
||||
@@ -7,10 +7,11 @@ using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
@@ -30,29 +31,60 @@ namespace MouseWithoutBorders.Core;
|
||||
|
||||
internal static class Logger
|
||||
{
|
||||
internal static readonly string[] AllLogs = new string[MAX_LOG];
|
||||
private static readonly Lock AllLogsLock = new();
|
||||
internal static readonly ConcurrentDictionary<string, int> LogCounter = new();
|
||||
// keep a count of unique lines of text that get logged
|
||||
private static readonly ConcurrentDictionary<string, int> LogCounter = new();
|
||||
|
||||
// implements a simple ring buffer to store recent log entries in memory
|
||||
private const int MAX_LOG = 10000;
|
||||
private static readonly string[] AllLogs = new string[MAX_LOG];
|
||||
private static readonly Lock AllLogsLock = new();
|
||||
private static int allLogsIndex;
|
||||
|
||||
// used for throttling the number of exceptions that get logged
|
||||
// so that high-volume exceptions don't flood the logs
|
||||
private const int MaxLogExceptionPerHour = 1000;
|
||||
private static int lastHour;
|
||||
private static int exceptionCount;
|
||||
|
||||
// track some application statistics
|
||||
private static PackageMonitor lastPackageSent;
|
||||
private static PackageMonitor lastPackageReceived;
|
||||
|
||||
internal static void TelemetryLogTrace(string log, SeverityLevel severityLevel, bool flush = false)
|
||||
{
|
||||
int logCount = LogCounter.AddOrUpdate(log, 1, (key, value) => value + 1);
|
||||
var logCount = Logger.LogCounter.AddOrUpdate(log, 1, (key, value) => value + 1);
|
||||
Logger.Log(log);
|
||||
}
|
||||
|
||||
internal static void Log(Exception e, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "", [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0)
|
||||
private static void Log(string format, params object[] args)
|
||||
{
|
||||
Logger.Log(string.Format(CultureInfo.InvariantCulture, format, args));
|
||||
}
|
||||
|
||||
internal static void Log(string log, bool clearLog = false, [CallerMemberName] string memberName = "", [CallerFilePath] string sourceFilePath = "", [CallerLineNumber] int sourceLineNumber = 0)
|
||||
{
|
||||
log = DateTime.Now.ToString("MM/dd HH:mm:ss.fff", CultureInfo.InvariantCulture) + $"({Thread.CurrentThread.ManagedThreadId})" + log;
|
||||
|
||||
ManagedCommon.Logger.LogInfo(log, memberName, sourceFilePath, sourceLineNumber);
|
||||
lock (AllLogsLock)
|
||||
{
|
||||
if (clearLog)
|
||||
{
|
||||
allLogsIndex = 0;
|
||||
}
|
||||
|
||||
AllLogs[allLogsIndex] = log;
|
||||
allLogsIndex = (allLogsIndex + 1) % MAX_LOG;
|
||||
}
|
||||
}
|
||||
|
||||
internal static void Log(Exception e, [CallerMemberName] string memberName = "", [CallerFilePath] string sourceFilePath = "", [CallerLineNumber] int sourceLineNumber = 0)
|
||||
{
|
||||
if (e is not KnownException)
|
||||
{
|
||||
string exText = e.ToString();
|
||||
var exText = e.ToString();
|
||||
|
||||
Log($"!Exception!: {exText}", memberName, sourceFilePath, sourceLineNumber);
|
||||
Logger.Log($"!Exception!: {exText}", memberName, sourceFilePath, sourceLineNumber);
|
||||
|
||||
if (DateTime.UtcNow.Hour != lastHour)
|
||||
{
|
||||
@@ -71,172 +103,209 @@ internal static class Logger
|
||||
}
|
||||
}
|
||||
|
||||
private const string HeaderSENT =
|
||||
"Be{0},Ke{1},Mo{2},He{3},Mx{4},Tx{5},Im{6},By{7},Cl{8},Dr{9},De{10},Ed{11},Ie{12},Ni{13}";
|
||||
|
||||
private const string HeaderRECEIVED =
|
||||
"Be{0},Ke{1},Mo{2},He{3},Mx{4},Tx{5},Im{6},By{7},Cl{8},Dr{9},De{10},Ed{11},In{12},Ni{13},Pc{14}/{15}";
|
||||
|
||||
internal static void LogDebug(string log, bool clearLog = false, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "", [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0)
|
||||
{
|
||||
#if DEBUG
|
||||
Log(log, clearLog, memberName, sourceFilePath, sourceLineNumber);
|
||||
#endif
|
||||
}
|
||||
|
||||
internal static void Log(string log, bool clearLog = false, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "", [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0)
|
||||
{
|
||||
log = DateTime.Now.ToString("MM/dd HH:mm:ss.fff", CultureInfo.InvariantCulture) + $"({Thread.CurrentThread.ManagedThreadId})" + log;
|
||||
|
||||
ManagedCommon.Logger.LogInfo(log, memberName, sourceFilePath, sourceLineNumber);
|
||||
lock (AllLogsLock)
|
||||
{
|
||||
if (clearLog)
|
||||
{
|
||||
allLogsIndex = 0;
|
||||
}
|
||||
|
||||
AllLogs[allLogsIndex] = log;
|
||||
allLogsIndex = (allLogsIndex + 1) % MAX_LOG;
|
||||
}
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
internal static void LogDebug(string format, params object[] args)
|
||||
{
|
||||
#if DEBUG
|
||||
Logger.Log(format, args);
|
||||
#endif
|
||||
}
|
||||
|
||||
private static void Log(string format, params object[] args)
|
||||
{
|
||||
Logger.Log(string.Format(CultureInfo.InvariantCulture, format, args));
|
||||
}
|
||||
|
||||
private static PackageMonitor lastPackageSent;
|
||||
private static PackageMonitor lastPackageReceived;
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
internal static void LogAll()
|
||||
internal static void LogDebug(string log, bool clearLog = false, [CallerMemberName] string memberName = "", [CallerFilePath] string sourceFilePath = "", [CallerLineNumber] int sourceLineNumber = 0)
|
||||
{
|
||||
string log;
|
||||
Logger.Log(log, clearLog, memberName, sourceFilePath, sourceLineNumber);
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
internal static void LogStatistics()
|
||||
{
|
||||
if (!lastPackageSent.Equals(Package.PackageSent))
|
||||
{
|
||||
log = string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
"SENT:" + HeaderSENT,
|
||||
Package.PackageSent.Heartbeat,
|
||||
Package.PackageSent.Keyboard,
|
||||
Package.PackageSent.Mouse,
|
||||
Package.PackageSent.Hello,
|
||||
Package.PackageSent.Matrix,
|
||||
Package.PackageSent.ClipboardText,
|
||||
Package.PackageSent.ClipboardImage,
|
||||
Package.PackageSent.ByeBye,
|
||||
Package.PackageSent.Clipboard,
|
||||
Package.PackageSent.ClipboardDragDrop,
|
||||
Package.PackageSent.ClipboardDragDropEnd,
|
||||
Package.PackageSent.ExplorerDragDrop,
|
||||
Event.inputEventCount,
|
||||
Package.PackageSent.Nil);
|
||||
Log(log);
|
||||
lastPackageSent = Package.PackageSent; // Copy data
|
||||
var log =
|
||||
$"SENT:" +
|
||||
$"Be{Package.PackageSent.Heartbeat}," +
|
||||
$"Ke{Package.PackageSent.Keyboard}," +
|
||||
$"Mo{Package.PackageSent.Mouse}," +
|
||||
$"He{Package.PackageSent.Hello}," +
|
||||
$"Mx{Package.PackageSent.Matrix}," +
|
||||
$"Tx{Package.PackageSent.ClipboardText}," +
|
||||
$"Im{Package.PackageSent.ClipboardImage}," +
|
||||
$"By{Package.PackageSent.ByeBye}," +
|
||||
$"Cl{Package.PackageSent.Clipboard}," +
|
||||
$"Dr{Package.PackageSent.ClipboardDragDrop}," +
|
||||
$"De{Package.PackageSent.ClipboardDragDropEnd}," +
|
||||
$"Ed{Package.PackageSent.ExplorerDragDrop}," +
|
||||
$"Ie{Event.inputEventCount}," +
|
||||
$"Ni{Package.PackageSent.Nil}";
|
||||
Logger.Log(log);
|
||||
lastPackageSent = Package.PackageSent;
|
||||
}
|
||||
|
||||
if (!lastPackageReceived.Equals(Package.PackageReceived))
|
||||
{
|
||||
log = string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
"RECEIVED:" + HeaderRECEIVED,
|
||||
Package.PackageReceived.Heartbeat,
|
||||
Package.PackageReceived.Keyboard,
|
||||
Package.PackageReceived.Mouse,
|
||||
Package.PackageReceived.Hello,
|
||||
Package.PackageReceived.Matrix,
|
||||
Package.PackageReceived.ClipboardText,
|
||||
Package.PackageReceived.ClipboardImage,
|
||||
Package.PackageReceived.ByeBye,
|
||||
Package.PackageReceived.Clipboard,
|
||||
Package.PackageReceived.ClipboardDragDrop,
|
||||
Package.PackageReceived.ClipboardDragDropEnd,
|
||||
Package.PackageReceived.ExplorerDragDrop,
|
||||
Event.invalidPackageCount,
|
||||
Package.PackageReceived.Nil,
|
||||
Receiver.processedPackageCount,
|
||||
Receiver.skippedPackageCount);
|
||||
Log(log);
|
||||
var log =
|
||||
$"RECEIVED:" +
|
||||
$"Be{Package.PackageReceived.Heartbeat}," +
|
||||
$"Ke{Package.PackageReceived.Keyboard}," +
|
||||
$"Mo{Package.PackageReceived.Mouse}," +
|
||||
$"He{Package.PackageReceived.Hello}," +
|
||||
$"Mx{Package.PackageReceived.Matrix}," +
|
||||
$"Tx{Package.PackageReceived.ClipboardText}," +
|
||||
$"Im{Package.PackageReceived.ClipboardImage}," +
|
||||
$"By{Package.PackageReceived.ByeBye}," +
|
||||
$"Cl{Package.PackageReceived.Clipboard}," +
|
||||
$"Dr{Package.PackageReceived.ClipboardDragDrop}," +
|
||||
$"De{Package.PackageReceived.ClipboardDragDropEnd}," +
|
||||
$"Ed{Package.PackageReceived.ExplorerDragDrop}," +
|
||||
$"Ie{Event.invalidPackageCount}," +
|
||||
$"Ni{Package.PackageReceived.Nil}" +
|
||||
$"Pc{Receiver.processedPackageCount}/{Receiver.skippedPackageCount}";
|
||||
Logger.Log(log);
|
||||
lastPackageReceived = Package.PackageReceived;
|
||||
}
|
||||
}
|
||||
|
||||
internal static void GenerateLog()
|
||||
{
|
||||
int l = Setting.Values.DumpObjectsLevel;
|
||||
if (l is > 0 and < 10)
|
||||
{
|
||||
Logger.DumpObjects(l);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<ProcessThread> myThreads;
|
||||
|
||||
internal static void DumpObjects(int level)
|
||||
{
|
||||
try
|
||||
{
|
||||
string logFile = Path.Combine(Common.RunWithNoAdminRight ? Path.GetTempPath() : Path.GetDirectoryName(Application.ExecutablePath), "MagicMouse.log");
|
||||
|
||||
StringBuilder sb = new(1000000);
|
||||
string log;
|
||||
|
||||
myThreads = new List<ProcessThread>();
|
||||
|
||||
foreach (ProcessThread t in Process.GetCurrentProcess().Threads)
|
||||
{
|
||||
myThreads.Add(t);
|
||||
}
|
||||
|
||||
Logger.DumpProgramLogs(sb, level);
|
||||
Logger.DumpStaticTypes(sb, level);
|
||||
|
||||
log = string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
"{0} {1}\r\n{2}\r\n\r\n{3}",
|
||||
Application.ProductName,
|
||||
Application.ProductVersion,
|
||||
"Private Mem: " + (Process.GetCurrentProcess().PrivateMemorySize64 / 1024).ToString(CultureInfo.CurrentCulture) + "KB",
|
||||
sb.ToString());
|
||||
|
||||
if (!string.IsNullOrEmpty(Encryption.myKey))
|
||||
{
|
||||
log = log.Replace(Encryption.MyKey, Encryption.GetDebugInfo(Encryption.MyKey));
|
||||
}
|
||||
|
||||
log += Thread.DumpThreadsStack();
|
||||
log += $"\r\nCurrent process session: {Process.GetCurrentProcess().SessionId}, active console session: {NativeMethods.WTSGetActiveConsoleSessionId()}.";
|
||||
|
||||
File.WriteAllText(logFile, log);
|
||||
|
||||
if (Common.RunOnLogonDesktop || Common.RunOnScrSaverDesktop)
|
||||
{
|
||||
_ = MessageBox.Show("Dump file created: " + logFile, Application.ProductName);
|
||||
}
|
||||
else
|
||||
{
|
||||
Common.ShowToolTip("Dump file created: " + logFile + " and placed in the Clipboard.", 10000);
|
||||
Clipboard.SetText(logFile);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_ = MessageBox.Show(e.Message + "\r\n" + e.StackTrace, Application.ProductName);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void DumpProgramLogs(StringBuilder sb, int level)
|
||||
{
|
||||
_ = Logger.PrivateDump(sb, AllLogs, "[Program logs]\r\n===============\r\n", 0, level, false);
|
||||
_ = Logger.PrivateDump(sb, AllLogs, "[Program Logs]\r\n===============\r\n", 0, level, true);
|
||||
}
|
||||
|
||||
internal static string DumpObjects(int level)
|
||||
{
|
||||
var sb = new StringBuilder(1000000);
|
||||
|
||||
Logger.DumpProgramLogs(sb, level);
|
||||
Logger.DumpStaticTypes(sb, level);
|
||||
|
||||
var log =
|
||||
$"{Application.ProductName} {Application.ProductVersion}\r\n" +
|
||||
$"Private Mem: {Process.GetCurrentProcess().PrivateMemorySize64 / 1024}KB\r\n" +
|
||||
$"\r\n" +
|
||||
$"{sb}\r\n";
|
||||
|
||||
// obfuscate the current encryption key
|
||||
if (!string.IsNullOrEmpty(Encryption.myKey))
|
||||
{
|
||||
log = log.Replace(Encryption.MyKey, Logger.GetChecksum(Encryption.MyKey));
|
||||
}
|
||||
|
||||
log += Thread.DumpThreadsStack();
|
||||
log += "\r\n";
|
||||
log += $"Current process session: {Process.GetCurrentProcess().SessionId}\r\n";
|
||||
log += $"Active console session: {NativeMethods.WTSGetActiveConsoleSessionId()}";
|
||||
|
||||
return log;
|
||||
}
|
||||
|
||||
private static void DumpObject(StringBuilder sb, object obj, int level, Type t, int maxLevel)
|
||||
{
|
||||
if (t == typeof(Delegate))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj is PackageType or string or AddressFamily or ID or IPAddress)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var typeName = obj.GetType().ToString();
|
||||
if (typeName.EndsWith("type", StringComparison.CurrentCultureIgnoreCase)
|
||||
|| typeName.Contains("Cryptography")
|
||||
|| typeName.EndsWith("AsyncEventBits", StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var recurse = (obj is not null)
|
||||
&& (obj is not DATA)
|
||||
&& (obj.GetType().BaseType != typeof(ValueType))
|
||||
&& !obj.GetType().Namespace.Contains("System.Windows");
|
||||
var fi = t.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
|
||||
foreach (var f in fi)
|
||||
{
|
||||
if (f.GetValue(obj) != AllLogs)
|
||||
{
|
||||
_ = PrivateDump(sb, f.GetValue(obj), f.Name, level + 1, maxLevel, recurse);
|
||||
}
|
||||
}
|
||||
|
||||
if (obj is Dictionary<string, List<IPAddress>> dict)
|
||||
{
|
||||
foreach (var kvp in dict)
|
||||
{
|
||||
foreach (var ipAddress in kvp.Value)
|
||||
{
|
||||
_ = PrivateDump(sb, ipAddress, $"[{kvp.Key}]", level + 1, maxLevel, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (obj is Array arr)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (obj is string[] or int[] or uint[] or short[] or ushort[]
|
||||
or MachineInf[] or TcpClient[] or IPAddress[] or TcpSk[]
|
||||
or TcpServer[] or ProcessThread[] or Thread[])
|
||||
{
|
||||
for (var i = 0; i < arr.GetLength(0); i++)
|
||||
{
|
||||
_ = PrivateDump(sb, arr.GetValue(i), $"[{i}]", level + 1, maxLevel, true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_ = PrivateDump(sb, $"{typeName}: N/A", typeName, level + 1, maxLevel, true);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool PrivateDump(StringBuilder sb, object obj, string objName, int level, int maxLevel, bool recurse)
|
||||
{
|
||||
if (obj == null || ((maxLevel is >= 0) && (level >= maxLevel)) || obj is Cursor)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var objString = obj.ToString();
|
||||
var values = new string[7];
|
||||
values[0] = new string('-', Math.Max(level - 1, 0) * 2);
|
||||
values[1] = objName;
|
||||
/* values[2] = " "; */
|
||||
/* values[3] = t.FullName; */
|
||||
values[4] = " = ";
|
||||
values[5] = objName.Equals(nameof(Encryption.myKey), StringComparison.OrdinalIgnoreCase)
|
||||
? Logger.GetChecksum(objString)
|
||||
: objName.Equals("lastClipboardObject", StringComparison.OrdinalIgnoreCase)
|
||||
? string.Empty
|
||||
: objString
|
||||
.Replace("System.Windows.Forms.", string.Empty)
|
||||
.Replace("System.Net.Sockets.", string.Empty)
|
||||
.Replace("System.Security.Cryptography.", string.Empty)
|
||||
.Replace("System.Threading.", string.Empty)
|
||||
.Replace("System.ComponentModel.", string.Empty)
|
||||
.Replace("System.Runtime.", string.Empty)
|
||||
.Replace("System.Drawing.", string.Empty)
|
||||
.Replace("System.Object", "O")
|
||||
.Replace("System.Diagnostics.", string.Empty)
|
||||
.Replace("System.Collections.", string.Empty)
|
||||
.Replace("System.Drawing.", string.Empty)
|
||||
.Replace("System.Int", string.Empty)
|
||||
.Replace("System.EventHandler.", string.Empty);
|
||||
values[6] = "\r\n";
|
||||
_ = sb.Append(string.Concat(values).Replace(Common.BinaryName, "MM"));
|
||||
|
||||
var t = obj.GetType();
|
||||
if (!recurse || t.IsPrimitive)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Logger.DumpObject(sb, obj, level, t, maxLevel);
|
||||
return true;
|
||||
}
|
||||
|
||||
internal static void DumpStaticTypes(StringBuilder sb, int level)
|
||||
@@ -267,169 +336,7 @@ internal static class Logger
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool PrivateDump(StringBuilder sb, object obj, string objName, int level, int maxLevel, bool stop)
|
||||
{
|
||||
Type t;
|
||||
string padStr = string.Empty;
|
||||
string[] strArr;
|
||||
string objString;
|
||||
|
||||
if (obj == null || (maxLevel >= 0 && level >= maxLevel) || obj is Cursor)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < level; i++)
|
||||
{
|
||||
padStr += i < level - 1 ? "-" : padStr += string.Empty;
|
||||
}
|
||||
|
||||
objString = obj.ToString();
|
||||
t = obj.GetType();
|
||||
strArr = new string[7];
|
||||
strArr[0] = padStr;
|
||||
strArr[1] = objName;
|
||||
|
||||
// strArr[2] = " ";
|
||||
// strArr[3] = t.FullName;
|
||||
strArr[4] = " = ";
|
||||
strArr[5] = objName.Equals("myKey", StringComparison.OrdinalIgnoreCase)
|
||||
? Encryption.GetDebugInfo(objString)
|
||||
: objName.Equals("lastClipboardObject", StringComparison.OrdinalIgnoreCase)
|
||||
? string.Empty
|
||||
: objString
|
||||
.Replace("System.Windows.Forms.", string.Empty)
|
||||
.Replace("System.Net.Sockets.", string.Empty)
|
||||
.Replace("System.Security.Cryptography.", string.Empty)
|
||||
.Replace("System.Threading.", string.Empty)
|
||||
.Replace("System.ComponentModel.", string.Empty)
|
||||
.Replace("System.Runtime.", string.Empty)
|
||||
.Replace("System.Drawing.", string.Empty)
|
||||
.Replace("System.Object", "O")
|
||||
.Replace("System.Diagnostics.", string.Empty)
|
||||
.Replace("System.Collections.", string.Empty)
|
||||
.Replace("System.Drawing.", string.Empty)
|
||||
.Replace("System.Int", string.Empty)
|
||||
.Replace("System.EventHandler.", string.Empty);
|
||||
strArr[6] = "\r\n";
|
||||
_ = sb.Append(string.Concat(strArr).Replace(Common.BinaryName, "MM"));
|
||||
|
||||
if (stop || t.IsPrimitive)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Logger.DumpObject(sb, obj, level, t, maxLevel);
|
||||
return true;
|
||||
}
|
||||
|
||||
internal static void DumpObject(StringBuilder sb, object obj, int level, Type t, int maxLevel)
|
||||
{
|
||||
int i;
|
||||
bool stop;
|
||||
if (t == typeof(Delegate))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FieldInfo[] fi;
|
||||
string type;
|
||||
|
||||
if (obj is PackageType or string or AddressFamily or ID or IPAddress)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
type = obj.GetType().ToString();
|
||||
|
||||
if (type.EndsWith("type", StringComparison.CurrentCultureIgnoreCase) || type.Contains("Cryptography")
|
||||
|| type.EndsWith("AsyncEventBits", StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
stop = obj == null || obj is DATA || obj.GetType().BaseType == typeof(ValueType)
|
||||
|| obj.GetType().Namespace.Contains("System.Windows");
|
||||
fi = t.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
|
||||
|
||||
foreach (FieldInfo f in fi)
|
||||
{
|
||||
if (f.GetValue(obj) != AllLogs)
|
||||
{
|
||||
_ = PrivateDump(sb, f.GetValue(obj), f.Name, level + 1, maxLevel, stop);
|
||||
}
|
||||
}
|
||||
|
||||
if (obj is Dictionary<string, List<IPAddress>>)
|
||||
{
|
||||
Dictionary<string, List<IPAddress>> d = obj as Dictionary<string, List<IPAddress>>;
|
||||
|
||||
foreach (string k in d.Keys)
|
||||
{
|
||||
if (d.TryGetValue(k, out List<IPAddress> l))
|
||||
{
|
||||
foreach (IPAddress ip in l)
|
||||
{
|
||||
_ = PrivateDump(sb, ip, "[" + k + "]", level + 1, maxLevel, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (obj is Array)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (obj is MachineInf[])
|
||||
{
|
||||
MachineInf[] os = (MachineInf[])obj;
|
||||
|
||||
for (i = 0; i < os.GetLength(0); i++)
|
||||
{
|
||||
_ = PrivateDump(sb, os[i], "[" + i + "]", level + 1, maxLevel, false);
|
||||
}
|
||||
}
|
||||
else if (obj is int[] || obj is uint[])
|
||||
{
|
||||
int[] os = (int[])obj;
|
||||
|
||||
for (i = 0; i < os.GetLength(0); i++)
|
||||
{
|
||||
_ = PrivateDump(sb, os[i], "[" + i + "]", level + 1, maxLevel, false);
|
||||
}
|
||||
}
|
||||
else if (obj is short[] || obj is ushort[])
|
||||
{
|
||||
short[] os = (short[])obj;
|
||||
|
||||
for (i = 0; i < os.GetLength(0); i++)
|
||||
{
|
||||
_ = PrivateDump(sb, os[i], "[" + i + "]", level + 1, maxLevel, false);
|
||||
}
|
||||
}
|
||||
else if (obj is TcpClient[] || obj is IPAddress[] || obj is TcpSk[] || obj is string[]
|
||||
|| obj is TcpServer[]
|
||||
|| obj is ProcessThread[] || obj is Thread[])
|
||||
{
|
||||
object[] os = (object[])obj;
|
||||
|
||||
for (i = 0; i < os.GetLength(0); i++)
|
||||
{
|
||||
_ = PrivateDump(sb, os[i], "[" + i + "]", level + 1, maxLevel, false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_ = PrivateDump(sb, obj.GetType().ToString() + ": N/A", obj.GetType().ToString(), level + 1, maxLevel, false);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static void DumpType(StringBuilder sb, Type typeToDump, int level, int maxLevel)
|
||||
private static void DumpType(StringBuilder sb, Type typeToDump, int level, int maxLevel)
|
||||
{
|
||||
if ((typeToDump == typeof(Delegate))
|
||||
|| (typeToDump == typeof(PackageType))
|
||||
@@ -441,39 +348,45 @@ internal static class Logger
|
||||
return;
|
||||
}
|
||||
|
||||
var typeFullName = typeToDump.ToString();
|
||||
if (typeFullName.EndsWith("type", StringComparison.CurrentCultureIgnoreCase)
|
||||
|| typeFullName.Contains("Cryptography")
|
||||
|| typeFullName.EndsWith("AsyncEventBits", StringComparison.CurrentCultureIgnoreCase))
|
||||
var typeName = typeToDump.ToString();
|
||||
if (typeName.EndsWith("type", StringComparison.CurrentCultureIgnoreCase)
|
||||
|| typeName.Contains("Cryptography")
|
||||
|| typeName.EndsWith("AsyncEventBits", StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var stop = (typeToDump == null)
|
||||
|| (typeToDump == typeof(DATA))
|
||||
|| (typeToDump.BaseType == typeof(ValueType))
|
||||
|| typeToDump.Namespace.Contains("System.Windows");
|
||||
var recurse = (typeToDump is not null)
|
||||
&& (typeToDump != typeof(DATA))
|
||||
&& (typeToDump.BaseType != typeof(ValueType))
|
||||
&& !typeToDump.Namespace.Contains("System.Windows");
|
||||
|
||||
var fieldInfos = typeToDump.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
|
||||
foreach (var fieldInfo in fieldInfos)
|
||||
{
|
||||
if (fieldInfo.GetValue(null) != AllLogs)
|
||||
var fieldValue = fieldInfo.GetValue(null);
|
||||
if (fieldValue != AllLogs)
|
||||
{
|
||||
_ = Logger.PrivateDump(sb, fieldInfo.GetValue(null), fieldInfo.Name, level + 1, maxLevel, stop);
|
||||
_ = Logger.PrivateDump(sb, fieldValue, fieldInfo.Name, level + 1, maxLevel, recurse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates a basic checksum of the given string to be written to logs
|
||||
/// for quick verification without revealing the original sensitive value.
|
||||
/// </summary>
|
||||
internal static string GetChecksum(string st)
|
||||
{
|
||||
return string.IsNullOrEmpty(st)
|
||||
? st
|
||||
: ((byte)(Common.GetBytesU(st).Sum(value => value) % 256)).ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
internal static string GetStackTrace(StackTrace st)
|
||||
{
|
||||
string rv = string.Empty;
|
||||
|
||||
for (int i = 0; i < st.FrameCount; i++)
|
||||
{
|
||||
StackFrame sf = st.GetFrame(i);
|
||||
rv += sf.GetMethod() + " <= ";
|
||||
}
|
||||
|
||||
return rv;
|
||||
return string.Join(
|
||||
" <= ",
|
||||
st.GetFrames().Select(frame => frame.GetMethod().ToString()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,12 +10,11 @@
|
||||
// 2009-... modified by Truong Do (TruongDo).
|
||||
// 2023- Included in PowerToys.
|
||||
// </history>
|
||||
namespace MouseWithoutBorders.Class
|
||||
namespace MouseWithoutBorders.Core;
|
||||
|
||||
internal enum SeverityLevel
|
||||
{
|
||||
internal class SeverityLevel
|
||||
{
|
||||
internal static readonly SeverityLevel Information = new SeverityLevel();
|
||||
internal static readonly SeverityLevel Error = new SeverityLevel();
|
||||
internal static readonly SeverityLevel Warning = new SeverityLevel();
|
||||
}
|
||||
Information,
|
||||
Warning,
|
||||
Error,
|
||||
}
|
||||
@@ -68,7 +68,7 @@ internal sealed class Thread
|
||||
|
||||
internal static string DumpThreadsStack()
|
||||
{
|
||||
string stack = "\r\nMANAGED THREADS: " + threads.Count.ToString(CultureInfo.InvariantCulture) + "\r\n";
|
||||
string stack = "MANAGED THREADS: " + threads.Count.ToString(CultureInfo.InvariantCulture);
|
||||
stack += Logger.GetStackTrace(new StackTrace());
|
||||
return stack;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ using MouseWithoutBorders.Class;
|
||||
using MouseWithoutBorders.Core;
|
||||
using MouseWithoutBorders.Properties;
|
||||
|
||||
using Clipboard = MouseWithoutBorders.Core.Clipboard;
|
||||
using Timer = System.Windows.Forms.Timer;
|
||||
|
||||
[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.frmScreen.#ShowMouseWithoutBordersUiOnWinLogonDesktop(System.Boolean)", Justification = "Dotnet port with style preservation")]
|
||||
@@ -550,7 +551,7 @@ namespace MouseWithoutBorders
|
||||
|
||||
if (count % 20 == 0)
|
||||
{
|
||||
Logger.LogAll();
|
||||
Logger.LogStatistics();
|
||||
|
||||
// Need to review this code on why it is needed (moved from MoveToMyNeighbourIfNeeded(...))
|
||||
for (int i = 0; i < MachineStuff.MachineMatrix.Length; i++)
|
||||
@@ -1215,7 +1216,29 @@ namespace MouseWithoutBorders
|
||||
|
||||
private void MenuGenDumpFile_Click(object sender, EventArgs e)
|
||||
{
|
||||
Logger.GenerateLog();
|
||||
int l = Setting.Values.DumpObjectsLevel;
|
||||
if (l is > 0 and < 10)
|
||||
{
|
||||
try
|
||||
{
|
||||
string logFile = Path.Combine(Common.RunWithNoAdminRight ? Path.GetTempPath() : Path.GetDirectoryName(Application.ExecutablePath), "MagicMouse.log");
|
||||
var log = Logger.DumpObjects(l);
|
||||
File.WriteAllText(logFile, log);
|
||||
if (Common.RunOnLogonDesktop || Common.RunOnScrSaverDesktop)
|
||||
{
|
||||
_ = MessageBox.Show("Dump file created: " + logFile, Application.ProductName);
|
||||
}
|
||||
else
|
||||
{
|
||||
Common.ShowToolTip("Dump file created: " + logFile + " and placed in the Clipboard.", 10000);
|
||||
Clipboard.SetText(logFile);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_ = MessageBox.Show(ex.Message + "\r\n" + ex.StackTrace, Application.ProductName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void MainMenu_Opening(object sender, CancelEventArgs e)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[Program logs]
|
||||
[Program Logs]
|
||||
===============
|
||||
= System.String[]
|
||||
[Clipboard]
|
||||
@@ -199,15 +199,6 @@ HelperProcessName = PowerToys.MouseWithoutBordersHelper
|
||||
===============
|
||||
[Logger]
|
||||
===============
|
||||
AllLogsLock = Lock
|
||||
--_owningThreadId = 0
|
||||
--_state = 0
|
||||
--_recursionCount = 0
|
||||
--_spinCount = 22
|
||||
--_waiterStartTimeMs = 0
|
||||
--s_contentionCount = 0
|
||||
--s_maxSpinCount = 22
|
||||
--s_minSpinCountForAdaptiveSpin = -100
|
||||
LogCounter = Concurrent.ConcurrentDictionary`2[System.String,32]
|
||||
--_tables = Concurrent.ConcurrentDictionary`2+Tables[System.String,32]
|
||||
----_comparer = Generic.NonRandomizedStringEqualityComparer+OrdinalComparer
|
||||
@@ -228,6 +219,15 @@ LogCounter = Concurrent.ConcurrentDictionary`2[System.String,32]
|
||||
--_budget = ????????????
|
||||
--_growLockArray = True
|
||||
--_comparerIsDefaultForClasses = False
|
||||
AllLogsLock = Lock
|
||||
--_owningThreadId = 0
|
||||
--_state = 0
|
||||
--_recursionCount = 0
|
||||
--_spinCount = 22
|
||||
--_waiterStartTimeMs = 0
|
||||
--s_contentionCount = 0
|
||||
--s_maxSpinCount = 22
|
||||
--s_minSpinCountForAdaptiveSpin = -100
|
||||
allLogsIndex = 0
|
||||
lastHour = 0
|
||||
exceptionCount = 0
|
||||
@@ -263,8 +263,6 @@ lastPackageReceived = MouseWithoutBorders.Core.PackageMonitor
|
||||
--Nil = 0
|
||||
MAX_LOG = 10000
|
||||
MaxLogExceptionPerHour = 1000
|
||||
HeaderSENT = Be{0},Ke{1},Mo{2},He{3},Mx{4},Tx{5},Im{6},By{7},Cl{8},Dr{9},De{10},Ed{11},Ie{12},Ni{13}
|
||||
HeaderRECEIVED = Be{0},Ke{1},Mo{2},He{3},Mx{4},Tx{5},Im{6},By{7},Cl{8},Dr{9},De{10},Ed{11},In{12},Ni{13},Pc{14}/{15}
|
||||
[MachineStuff]
|
||||
===============
|
||||
McMatrixLock = Lock
|
||||
|
||||
@@ -3,8 +3,10 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
@@ -14,6 +16,94 @@ namespace MouseWithoutBorders.UnitTests.Core;
|
||||
|
||||
public static class LoggerTests
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class GetStackTraceTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void GetStackTraceShouldReturnCorrectValue()
|
||||
{
|
||||
// if we get the stack trace from the current test method with
|
||||
// "new StackTrace()" it's incredibly deep as it contains the
|
||||
// full MSTest call stack so we'll create a Task to capture a
|
||||
// much shallower stack trace from inside the task
|
||||
var stackTrace = default(StackTrace);
|
||||
Task.Run(() =>
|
||||
{
|
||||
void MyMethod1()
|
||||
{
|
||||
MyMethod2();
|
||||
}
|
||||
|
||||
void MyMethod2()
|
||||
{
|
||||
stackTrace = new StackTrace();
|
||||
}
|
||||
|
||||
MyMethod1();
|
||||
}).Wait();
|
||||
|
||||
/*
|
||||
// even with the above, the stack trace can still vary depending on the test runner host
|
||||
// being used (e.g. visual studio vs dotnet test) and the version of dotnet being used -
|
||||
// e.g.
|
||||
//
|
||||
// Void<GetStackTraceShouldReturnCorrectValue> g__MyMethod2| 2()
|
||||
// <= Void <GetStackTraceShouldReturnCorrectValue> g__MyMethod1 | 1()
|
||||
// <= Void <GetStackTraceShouldReturnCorrectValue> b__0()
|
||||
// <= Void RunFromThreadPoolDispatchLoop(System.Threading.Thread, System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
|
||||
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
// <= Void ExecuteWithThreadLocal(System.Threading.Tasks.Task ByRef, System.Threading.Thread)
|
||||
// ... etc ...
|
||||
//
|
||||
// versus
|
||||
//
|
||||
// Void<GetStackTraceShouldReturnCorrectValue> g__MyMethod2| 2()
|
||||
// <= Void <GetStackTraceShouldReturnCorrectValue> g__MyMethod1 | 1()
|
||||
// <= Void <GetStackTraceShouldReturnCorrectValue> b__0()
|
||||
// <= Void RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
|
||||
// ^^^^^^^^^^^
|
||||
// <= Void ExecuteWithThreadLocal(System.Threading.Tasks.Task ByRef, System.Threading.Thread)
|
||||
// ... etc ...
|
||||
//
|
||||
// so we're going to ignore everything after our task code.
|
||||
//
|
||||
// however, this is *still* inherently unstable due to compiler-generated uniqueness keys - e.g.
|
||||
//
|
||||
// Void <GetStackTraceShouldReturnCorrectValue>g__MyMethod1|2()
|
||||
// ^
|
||||
//
|
||||
// versus
|
||||
//
|
||||
// Void <GetStackTraceShouldReturnCorrectValue>g__MyMethod1|1()
|
||||
// ^
|
||||
// but we'll try to suppress the differences with a normalization regex
|
||||
// (which is giving diminishing returns on the value of this test, but
|
||||
// we'll persevere...)
|
||||
*/
|
||||
|
||||
string NormalizeStackTrace(string stackTrace, [CallerMemberName] string caller = "")
|
||||
{
|
||||
return string.Join(
|
||||
" <= ",
|
||||
stackTrace
|
||||
.Split(" <= ")
|
||||
.TakeWhile(line => line.Contains(caller, StringComparison.InvariantCulture))
|
||||
.Select(f => f.Split('|')[0])
|
||||
.ToList());
|
||||
}
|
||||
|
||||
var expected = NormalizeStackTrace(
|
||||
"Void <GetStackTraceShouldReturnCorrectValue>g__MyMethod2|2()" +
|
||||
" <= Void <GetStackTraceShouldReturnCorrectValue>g__MyMethod1|1()" +
|
||||
" <= Void <GetStackTraceShouldReturnCorrectValue>b__0()");
|
||||
|
||||
var actual = NormalizeStackTrace(
|
||||
Logger.GetStackTrace(stackTrace));
|
||||
|
||||
Assert.AreEqual(expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
[TestClass]
|
||||
public sealed class PrivateDumpTests
|
||||
{
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using ShortcutGuide.Helpers;
|
||||
using ShortcutGuide.Models;
|
||||
using YamlDotNet.Serialization;
|
||||
|
||||
// This class should be moved to WinGet in the future
|
||||
namespace ShortcutGuide.IndexYmlGenerator
|
||||
{
|
||||
public class IndexYmlGenerator
|
||||
{
|
||||
public static void Main()
|
||||
{
|
||||
CreateIndexYmlFile();
|
||||
}
|
||||
|
||||
// Todo: Exception handling
|
||||
public static void CreateIndexYmlFile()
|
||||
{
|
||||
string path = ManifestInterpreter.PathOfManifestFiles;
|
||||
if (File.Exists(Path.Combine(path, "index.yml")))
|
||||
{
|
||||
File.Delete(Path.Combine(path, "index.yml"));
|
||||
}
|
||||
|
||||
IndexFile indexFile = new() { };
|
||||
Dictionary<(string WindowFilter, bool BackgroundProcess), List<string>> processes = [];
|
||||
|
||||
foreach (string file in Directory.EnumerateFiles(path, "*.yml"))
|
||||
{
|
||||
string content = File.ReadAllText(file);
|
||||
Deserializer deserializer = new();
|
||||
ShortcutFile shortcutFile = deserializer.Deserialize<ShortcutFile>(content);
|
||||
if (processes.TryGetValue((shortcutFile.WindowFilter, shortcutFile.BackgroundProcess), out List<string>? apps))
|
||||
{
|
||||
if (apps.Contains(shortcutFile.PackageName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
apps.Add(shortcutFile.PackageName);
|
||||
continue;
|
||||
}
|
||||
|
||||
processes[(shortcutFile.WindowFilter, shortcutFile.BackgroundProcess)] = [shortcutFile.PackageName];
|
||||
}
|
||||
|
||||
indexFile.Index = processes.Select(item => new IndexFile.IndexItem
|
||||
{
|
||||
WindowFilter = item.Key.WindowFilter,
|
||||
BackgroundProcess = item.Key.BackgroundProcess,
|
||||
Apps = [.. item.Value],
|
||||
}).ToArray();
|
||||
|
||||
// Todo: Take the default shell name from the settings or environment variable, default to "+WindowsNT.Shell"
|
||||
indexFile.DefaultShellName = "+WindowsNT.Shell";
|
||||
|
||||
Serializer serializer = new();
|
||||
string yamlContent = serializer.Serialize(indexFile);
|
||||
File.WriteAllText(Path.Combine(path, "index.yml"), yamlContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<!-- Look at Directory.Build.props in root for common stuff as well -->
|
||||
<Import Project="..\..\..\Common.Dotnet.CsWinRT.props" />
|
||||
<Import Project="..\..\..\Common.SelfContained.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<RootNamespace>ShortcutGuide.IndexYmlGenerator</RootNamespace>
|
||||
<Nullable>enable</Nullable>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
<OutputPath>..\..\..\..\$(Platform)\$(Configuration)\WinUI3Apps</OutputPath>
|
||||
<AssemblyName>PowerToys.ShortcutGuide.IndexYmlGenerator</AssemblyName>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="YamlDotNet" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ShortcutGuide.Ui\ShortcutGuide.Ui.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 53 KiB |
@@ -0,0 +1,247 @@
|
||||
PackageName: +WindowsNT.Notepad
|
||||
Name: Notepad
|
||||
WindowFilter: "Notepad.exe"
|
||||
BackgroundProcess: false
|
||||
Shortcuts:
|
||||
- SectionName: File
|
||||
Properties:
|
||||
- Name: New tab
|
||||
Recommended: true
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- N
|
||||
- Name: New window
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: true
|
||||
Alt: false
|
||||
Keys:
|
||||
- N
|
||||
- Name: Open
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- O
|
||||
- Name: Save
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- S
|
||||
- Name: Save As
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: true
|
||||
Alt: false
|
||||
Keys:
|
||||
- S
|
||||
- Name: Save all
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: true
|
||||
Keys:
|
||||
- S
|
||||
- Name: Print
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- P
|
||||
- Name: Close tab
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- W
|
||||
- Name: Close window
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: true
|
||||
Alt: false
|
||||
Keys:
|
||||
- W
|
||||
- SectionName: Edit
|
||||
Properties:
|
||||
- Name: Undo
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- Z
|
||||
- Name: Redo
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: true
|
||||
Keys:
|
||||
- Z
|
||||
- Name: Cut
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- X
|
||||
- Name: Copy
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- C
|
||||
- Name: Paste
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- V
|
||||
- Name: Search with Bing
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- E
|
||||
- Name: Find
|
||||
Recommended: true
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- F
|
||||
- Name: Find next
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- F3
|
||||
- Name: Find previous
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: false
|
||||
Shift: true
|
||||
Alt: false
|
||||
Keys:
|
||||
- F3
|
||||
- Name: Replace
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- H
|
||||
- Name: Go to
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- G
|
||||
- Name: Select all
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- A
|
||||
- Name: Time/Date
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- F5
|
||||
- SectionName: View
|
||||
Properties:
|
||||
- Name: Zoom in
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- Plus
|
||||
- Name: Zoom out
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- Minus
|
||||
- Name: Reset zoom
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- 0
|
||||
- SectionName: Formatting
|
||||
Properties:
|
||||
- Name: Bold
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- B
|
||||
- Name: Italic
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- I
|
||||
- Name: Insert link
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- K
|
||||
- Name: Clear formatting
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- Space
|
||||
@@ -0,0 +1,773 @@
|
||||
PackageName: +WindowsNT.Shell
|
||||
WindowFilter: "*"
|
||||
BackgroundProcess: true
|
||||
Shortcuts:
|
||||
- SectionName: Desktop Shortcuts
|
||||
Properties:
|
||||
- Name: Close active window
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: true
|
||||
Keys:
|
||||
- F4
|
||||
- Name: Open shutdown box
|
||||
Description: When no windows are open
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: true
|
||||
Keys:
|
||||
- F4
|
||||
- Name: Cycle through open Windows
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: true
|
||||
Keys:
|
||||
- Esc
|
||||
- Name: Reveal typed password
|
||||
Description: On sign-in screen
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: true
|
||||
Keys:
|
||||
- F8
|
||||
- Name: Go back
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: true
|
||||
Keys:
|
||||
- "<Left>"
|
||||
- Name: Go forward
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: true
|
||||
Keys:
|
||||
- "<Right>"
|
||||
- Name: Move up one screen
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: true
|
||||
Keys:
|
||||
- "<Page Up>"
|
||||
- Name: Move down one screen
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: true
|
||||
Keys:
|
||||
- "<Page Down>"
|
||||
- Name: Window context menu
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: true
|
||||
Keys:
|
||||
- Space
|
||||
- Name: Switch between open apps
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: true
|
||||
Keys:
|
||||
- Tab
|
||||
Description: While pressing Tab multiple times
|
||||
- Name: Run command
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: true
|
||||
Keys:
|
||||
- "<Underlined letter>"
|
||||
Description: for the underlined letter in the app
|
||||
- Name: View open apps
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: true
|
||||
Keys:
|
||||
- Tab
|
||||
- Name: Change start menu size
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "<Arrow>"
|
||||
- Name: Move cursor
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "<ArrowLR>"
|
||||
Description: To the beginning or end of a word
|
||||
- Name: Switch keyboard layout
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: true
|
||||
Alt: false
|
||||
Keys:
|
||||
- ""
|
||||
- Name: Select block of text
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: true
|
||||
Alt: false
|
||||
Keys:
|
||||
- "<Arrow>"
|
||||
- Name: Open Task Manager
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: true
|
||||
Alt: false
|
||||
Keys:
|
||||
- Esc
|
||||
- Name: Enable/Disable Chinese IME
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- Space
|
||||
- Name: Open context menu
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: false
|
||||
Shift: true
|
||||
Alt: false
|
||||
Keys:
|
||||
- F10
|
||||
Description: For the selected item
|
||||
- SectionName: Virtual desktop
|
||||
Properties:
|
||||
- Name: Open task view
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- Tab
|
||||
- Name: Add a virtual desktop
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- D
|
||||
- Name: Close current desktop
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- F4
|
||||
- Name: Switch desktop
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "<ArrowLR>"
|
||||
Recommended: true
|
||||
- SectionName: "Windows key"
|
||||
Properties:
|
||||
- Name: Open start menu
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- ""
|
||||
- Name: Open Action Center
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "A"
|
||||
- Name: Open Date and Time
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: true
|
||||
Keys:
|
||||
- "D"
|
||||
- Name: Focus on the notification area
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "B"
|
||||
- Name: Open narrator
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "Enter"
|
||||
- Name: Open domain search
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "F"
|
||||
- Name: Open Quick Assist
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "Q"
|
||||
- Name: Wake up device
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: true
|
||||
Shift: true
|
||||
Alt: false
|
||||
Keys:
|
||||
- "B"
|
||||
Description: When black or a blank screen.
|
||||
- Name: Change input option
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "Space"
|
||||
Description: To next option
|
||||
- Name: Change input option
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "Space"
|
||||
Description: To previous option
|
||||
- Name: Display/Hide desktop
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "D"
|
||||
- Name: Minimize the active window
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "Down"
|
||||
Recommended: true
|
||||
- Name: Open file Explorer
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "E"
|
||||
- Name: Close Magnifier
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "Esc"
|
||||
- Name: Open Feedback Hub
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "F"
|
||||
Recommended: true
|
||||
- Name: Start IME reconversion
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "/"
|
||||
- Name: Open Game Bar
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "G"
|
||||
- Name: Open voice dictation
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "H"
|
||||
- Name: Minimize or restore all other windows
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "<Home>"
|
||||
- Name: Open Settings
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "I"
|
||||
- Name: Set focus to a Windows tip
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "J"
|
||||
- Name: Open Cast
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "K"
|
||||
- Name: Lock the device
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "L"
|
||||
- Name: Snap the window
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "<ArrowLR>"
|
||||
- Name: Minimize all windows
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "M"
|
||||
- Name: Zoom out Magnifier
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "-"
|
||||
- Name: Zoom in Magnifier
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "="
|
||||
- Name: Open notification center
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "N"
|
||||
- Name: Lock the device orientation
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "O"
|
||||
- Name: Open project Settings
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "P"
|
||||
- Name: Open Settings about Page
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "<Pause>"
|
||||
- Name: Open the emoji panel
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "."
|
||||
- Name: Open the emoji panel
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- ";"
|
||||
- Name: Capture a screenshot
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "<PrtScr>"
|
||||
Description: Save to the pictures folder
|
||||
- Name: Open search
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "Q"
|
||||
- Name: Open search
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "S"
|
||||
- Name: Open Run dialog
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "R"
|
||||
- Name: Restore window
|
||||
Description: If a window is snapped or maximized
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: true
|
||||
Alt: false
|
||||
Keys:
|
||||
- "<Down>"
|
||||
- Name: Make UWP app full screen
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: true
|
||||
Alt: false
|
||||
Keys:
|
||||
- "<Up>"
|
||||
- Name: Move window to monitor
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: true
|
||||
Alt: false
|
||||
Keys:
|
||||
- "<Arrow>"
|
||||
- Name: Open Snipping Tool
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: true
|
||||
Alt: false
|
||||
Keys:
|
||||
- "S"
|
||||
- Name: Stretch window
|
||||
Description: To the top and bottom of the screen
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: true
|
||||
Alt: false
|
||||
Keys:
|
||||
- "<Up>"
|
||||
- Name: Open task view
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "Tab"
|
||||
- Name: Open Accessibility Settings
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "U"
|
||||
- Name: Maximize the active window
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "<Up>"
|
||||
- Name: Open the clipboard history
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "V"
|
||||
- Name: Open widgets
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "W"
|
||||
- Name: Open Quick Link menu
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "X"
|
||||
- Name: Open snap layouts
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "Z"
|
||||
- SectionName: Clipboard
|
||||
Properties:
|
||||
- Name: Copy
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "C"
|
||||
- Name: Cut
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "X"
|
||||
- Name: Paste
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "V"
|
||||
- Name: Paste
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: false
|
||||
Shift: true
|
||||
Alt: false
|
||||
Keys:
|
||||
- "<Insert>"
|
||||
Description: Paste as plain text
|
||||
- SectionName: <TASKBAR1-9>Taskbar Shortcuts
|
||||
Properties:
|
||||
- Name: Open app in Taskbar
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "<TASKBAR1-9>"
|
||||
- Name: Open jump list
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: true
|
||||
Keys:
|
||||
- "<TASKBAR1-9>"
|
||||
- Name: Switch to last active window
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "<TASKBAR1-9>"
|
||||
- Name: Open as administrator
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: true
|
||||
Shift: true
|
||||
Alt: false
|
||||
Keys:
|
||||
- "<TASKBAR1-9>"
|
||||
- SectionName: Copilot key
|
||||
Properties:
|
||||
- Name: Open Copilot
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- '<Copilot>'
|
||||
Description: When copilot is available
|
||||
- Name: Open Windows search
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- '<Copilot>'
|
||||
Description: When copilot is not available
|
||||
- SectionName: Office key
|
||||
Properties:
|
||||
- Name: Open Word
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "<Office>"
|
||||
- "W"
|
||||
- Name: Open Excel
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "<Office>"
|
||||
- "X"
|
||||
- Name: Open PowerPoint
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "<Office>"
|
||||
- "P"
|
||||
- Name: Open Outlook
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "<Office>"
|
||||
- "O"
|
||||
- Name: Open Microsoft Teams
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "<Office>"
|
||||
- "T"
|
||||
- Name: Open OneNote
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "<Office>"
|
||||
- "N"
|
||||
- Name: Open OneDrive
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "<Office>"
|
||||
- "D"
|
||||
- Name: Open Yammer
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "<Office>"
|
||||
- "Y"
|
||||
- Name: Open LinkedIn
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "<Office>"
|
||||
- "L"
|
||||
@@ -0,0 +1,266 @@
|
||||
PackageName: +WindowsNT.WindowsExplorer
|
||||
WindowFilter: "explorer.exe"
|
||||
Name: File Explorer
|
||||
Shortcuts:
|
||||
- SectionName: General
|
||||
Properties:
|
||||
- Name: Open File Explorer
|
||||
Shortcut:
|
||||
- Win: true
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "E"
|
||||
Recommended: true
|
||||
- Name: Select the address bar
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: true
|
||||
Keys:
|
||||
- "D"
|
||||
- Name: Select the address bar
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "L"
|
||||
- Name: Select the address bar
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "F4"
|
||||
- Name: Select the search box
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "E"
|
||||
- Name: Select the search box
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "F3"
|
||||
- Name: Select the search box
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "F"
|
||||
- Name: Refresh the window
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "F5"
|
||||
- Name: Cycle through elements in the active window
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "F6"
|
||||
- Name: Maximize or restore the active window
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "F11"
|
||||
- SectionName: Navigation
|
||||
Properties:
|
||||
- Name: Navigate to the previous folder
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: true
|
||||
Keys:
|
||||
- "<Left>"
|
||||
- Name: Navigate to the previous folder
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "<Backspace>"
|
||||
- Name: Navigate to the next folder
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: true
|
||||
Keys:
|
||||
- "<Right>"
|
||||
- Name: Move up a level in the folder path
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: true
|
||||
Keys:
|
||||
- "<Up>"
|
||||
- SectionName: "Window management"
|
||||
Properties:
|
||||
- Name: Open a new window
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "N"
|
||||
- Name: Open a new tab
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "T"
|
||||
Recommended: true
|
||||
- Name: Close the current active tab
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "W"
|
||||
- Name: Move to the next tab
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "Tab"
|
||||
- Name: Move to the previous tab
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: true
|
||||
Alt: false
|
||||
Keys:
|
||||
- "Tab"
|
||||
- Name: Move to that tab number
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "Number (1-9)"
|
||||
- Name: Show/Hide the preview pane
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: true
|
||||
Keys:
|
||||
- "P"
|
||||
- Name: Show/Hide the details pane
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: false
|
||||
Shift: true
|
||||
Alt: true
|
||||
Keys:
|
||||
- "P"
|
||||
- Name: Resize all columns to fit text
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "+"
|
||||
- Name: Expand all folders
|
||||
Description: In the navigation pane
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: true
|
||||
Alt: false
|
||||
Keys:
|
||||
- "E"
|
||||
- SectionName: "File management"
|
||||
Properties:
|
||||
- Name: Display properties for the selected item
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: true
|
||||
Keys:
|
||||
- "Enter"
|
||||
- Name: Delete the selected item
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "<Delete>"
|
||||
- Name: Delete the selected item
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "D"
|
||||
- Name: Delete the selected item permanently
|
||||
Description: "This removes the item without sending it to the Recycle Bin"
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "D"
|
||||
- Name: Create a new folder
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: true
|
||||
Alt: false
|
||||
Keys:
|
||||
- "N"
|
||||
Recommended: true
|
||||
- Name: Rename the selected item
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: false
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "F2"
|
||||
- Name: Select multiple items
|
||||
Shortcut:
|
||||
- Win: false
|
||||
Ctrl: true
|
||||
Shift: false
|
||||
Alt: false
|
||||
Keys:
|
||||
- "<Arrow>"
|
||||
|
||||