diff --git a/.github/actions/spell-check/allow/names.txt b/.github/actions/spell-check/allow/names.txt
index b06f3391d4..fc153dd632 100644
--- a/.github/actions/spell-check/allow/names.txt
+++ b/.github/actions/spell-check/allow/names.txt
@@ -79,6 +79,7 @@ Gershaft
Giordani
Gokce
gordon
+Griese
grzhan
Guo
hanselman
@@ -197,6 +198,7 @@ cortana
dlnilsson
fancymouse
firefox
+fudan
gpt
Inkscape
Markdig
@@ -212,6 +214,7 @@ regedit
roslyn
Spotify
Vanara
+wangyi
WEX
windowwalker
winui
@@ -219,7 +222,9 @@ winuiex
wix
wordpad
WWL
+wyhash
xamlstyler
Xavalon
Xbox
Youdao
+zadjii
diff --git a/.github/actions/spell-check/excludes.txt b/.github/actions/spell-check/excludes.txt
index 912d72d472..21d7da66b8 100644
--- a/.github/actions/spell-check/excludes.txt
+++ b/.github/actions/spell-check/excludes.txt
@@ -96,10 +96,11 @@
^\Qdoc/devdocs/localization.md\E$
^\Qsrc/common/ManagedCommon/ColorFormatHelper.cs\E$
^\Qsrc/common/notifications/BackgroundActivatorDLL/cpp.hint\E$
+^\Qsrc/modules/cmdpal/doc/initial-sdk-spec/list-elements-mock-002.pdn\E$
^\Qsrc/modules/colorPicker/ColorPickerUI/Assets/ColorPicker/colorPicker.cur\E$
^\Qsrc/modules/colorPicker/ColorPickerUI/Shaders/GridShader.cso\E$
-^\Qsrc/modules/MouseUtils/MouseJumpUI/MainForm.resx\E$
^\Qsrc/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.SYSTEM_METRICS_INDEX.cs\E$
+^\Qsrc/modules/MouseUtils/MouseJumpUI/MainForm.resx\E$
^\Qsrc/modules/MouseWithoutBorders/App/Form/frmAbout.cs\E$
^\Qsrc/modules/MouseWithoutBorders/App/Form/frmInputCallback.resx\E$
^\Qsrc/modules/MouseWithoutBorders/App/Form/frmLogon.resx\E$
diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt
index 6907f937c5..6e4439e4a2 100644
--- a/.github/actions/spell-check/expect.txt
+++ b/.github/actions/spell-check/expect.txt
@@ -13,6 +13,8 @@ AColumn
acrt
ACTIVATEAPP
activationaction
+ACVS
+adaptivecards
ADDSTRING
ADDUNDORECORD
ADifferent
@@ -21,22 +23,21 @@ admx
advfirewall
AFeature
AFFINETRANSFORM
+affordances
AFX
AGGREGATABLE
-ahk
AHybrid
akv
-ALIGNRIGHT
ALarger
+ALIGNRIGHT
ALLAPPS
ALLCHILDREN
ALLINPUT
+Allman
ALLOWUNDO
ALLVIEW
ALPHATYPE
AModifier
-AMPROPERTY
-AMPROPSETID
amr
ANDSCANS
animatedvisuals
@@ -47,15 +48,19 @@ AOC
aocfnapldcnfbofgmbbllojgocaelgdd
APARTMENTTHREADED
APeriod
+apicontract
apidl
APIENTRY
APIIs
Apm
APPBARDATA
APPEXECLINK
+APPICONREFERENCE
APPLICATIONFRAMEHOST
appmanifest
+APPMODEL
APPNAME
+APPPUBLISHER
appref
appsettings
appwindow
@@ -124,6 +129,7 @@ BLURBEHIND
BLURREGION
bmi
bms
+BNDBk
BNumber
BODGY
BOOTSTRAPPERINSTALLFOLDER
@@ -144,6 +150,7 @@ bugreport
BUILDARCH
BUILDNUMBER
buildtransitive
+builttoroam
BVal
BValue
byapp
@@ -154,6 +161,7 @@ callbackptr
calpwstr
Cangjie
CANRENAME
+Cantarell
CAPTUREBLT
CAPTURECHANGED
CARETBLINKING
@@ -164,7 +172,7 @@ CCHFORMNAME
CCom
CContext
CDeclaration
-CDEF
+cdn
CElems
CENTERALIGN
certlm
@@ -191,9 +199,12 @@ CLIPCHILDREN
CLIPSIBLINGS
closesocket
CLSCTX
+CLSIDs
+Clsids
Clusion
cmder
CMDNOTFOUNDMODULEINTERFACE
+cmdpal
CMIC
CMINVOKECOMMANDINFO
CMINVOKECOMMANDINFOEX
@@ -205,16 +216,17 @@ coclass
codereview
Codespaces
COINIT
+colid
colorconv
colorformat
colorhistory
colorhistorylimit
COLORKEY
comctl
-comdef
comdlg
comexp
cominterop
+commandpalette
compmgmt
COMPOSITIONFULL
CONFIGW
@@ -224,6 +236,7 @@ CONOUT
contentfiles
CONTEXTHELP
CONTEXTMENUHANDLER
+contractversion
CONTROLL
CONTROLPARENT
copiedcolorrepresentation
@@ -232,13 +245,17 @@ COREWINDOW
cotaskmem
COULDNOT
countof
+cpcontrols
cph
cplusplus
CPower
+cppwinrt
createdump
CREATEPROCESS
CREATESCHEDULEDTASK
CREATESTRUCT
+CREATETHREAD
+CREATEWINDOW
CREATEWINDOWFAILED
CRECT
CRH
@@ -249,8 +266,10 @@ CSettings
cso
CSRW
CStyle
+cswin
CTest
CTEXT
+Ctl
CTLCOLORSTATIC
currentculture
CURRENTDIR
@@ -261,6 +280,7 @@ CUSTOMACTIONTEST
CVal
cvd
CVirtual
+CVS
cxfksword
CXSCREEN
CXSMICON
@@ -276,17 +296,24 @@ datareader
datatracker
dataversion
Dayof
+DBID
DBLCLKS
DBLEPSILON
+DBPROP
+DBPROPIDSET
+DBPROPSET
DCapture
DCBA
DCOM
DComposition
DCR
ddd
+DDEAPPLICATION
+DDECOMMAND
DDEIf
+DDEIFEXEC
+DDETOPIC
DDevice
-ddf
DDxgi
Deact
debugbreak
@@ -297,14 +324,18 @@ DEFAULTBOOTSTRAPPERINSTALLFOLDER
DEFAULTCOLOR
DEFAULTFLAGS
DEFAULTICON
+defaultlib
DEFAULTONLY
+DEFAULTTOFOLDER
DEFAULTTONEAREST
DEFAULTTONULL
DEFAULTTOPRIMARY
+DEFAULTTOSTAR
DEFERERASE
DEFPUSHBUTTON
deinitialization
DELA
+DELEGATEEXECUTE
DELETEDKEYIMAGE
DELETESCANS
deletethis
@@ -319,15 +350,11 @@ DESKTOPABSOLUTEPARSING
desktopshorcutinstalled
devblogs
devdocs
-devenum
devmgmt
DEVMODE
DEVMODEW
-DEVMON
-DEVSOURCE
-DGR
+devpal
DIALOGEX
-DIIRFLAG
dimm
DISABLEASACTIONKEY
DISABLENOSCROLL
@@ -336,8 +363,8 @@ DISPLAYCHANGE
DISPLAYCONFIG
DISPLAYFLAGS
DISPLAYFREQUENCY
-DISPLAYORIENTATION
displayname
+DISPLAYORIENTATION
divyan
Dlg
DLGFRAME
@@ -360,8 +387,9 @@ DRAWFRAME
drawingcolor
dreamsofameaningfullife
drivedetectionwarning
+Droid
DROPFILES
-dshow
+DROPTARGET
DSTINVERT
DSurface
DTexture
@@ -370,11 +398,7 @@ Dutil
DVASPECT
DVASPECTINFO
DVD
-DVH
-DVHD
dvr
-DVSD
-DVSL
DVTARGETDEVICE
dwl
dwm
@@ -395,7 +419,6 @@ dwrite
dxgi
easeofaccess
ecount
-EData
Edid
EDITKEYBOARD
EDITSHORTCUTS
@@ -418,7 +441,6 @@ epu
ERASEBKGND
EREOF
EResize
-ERole
ERRORIMAGE
ERRORTITLE
erwrite
@@ -455,22 +477,22 @@ exsb
exstyle
EXTENDEDKEY
EXTENDEDVERBS
+extensionsdk
EXTRALIGHT
EXTRINSICPROPERTIES
eyetracker
FANCYZONESDRAWLAYOUTTEST
FANCYZONESEDITOR
FARPROC
-fdw
fff
FILEEXPLORER
FILEFLAGS
FILEFLAGSMASK
-FILEINFOSIG
FILELOCKSMITH
FILELOCKSMITHCONTEXTMENU
FILELOCKSMITHEXT
FILELOCKSMITHLIBINTEROP
+filemgmt
FILEMUSTEXIST
FILEOP
FILEOPENDIALOGOPTIONS
@@ -479,16 +501,17 @@ FILESUBTYPE
FILESYSPATH
Filetime
FILEVERSION
-Filtergraph
Filterkeyboard
FILTERMODE
-Filterx
findfast
+Fira
FIXEDFILEINFO
FIXEDSYS
flac
+flaticon
flyouts
FMask
+fmtid
FOF
WANTNUKEWARNING
FOFX
@@ -500,6 +523,8 @@ FORCEMINIMIZE
FORMATDLGORD
formatetc
FORPARSING
+fpvm
+Fqc
FRAMECHANGED
frm
Froml
@@ -522,8 +547,8 @@ GETDESKWALLPAPER
GETDLGCODE
GETDPISCALEDSIZE
getfilesiginforedist
-GETICON
GETHOTKEY
+GETICON
GETMINMAXINFO
GETNONCLIENTMETRICS
GETPROPERTYSTOREFLAGS
@@ -532,6 +557,7 @@ GETSECKEY
GETSTICKYKEYS
GETTEXTLENGTH
GHND
+gifv
GMEM
GNumber
gpedit
@@ -540,6 +566,7 @@ GPOCA
gpp
gpu
gradians
+gsl
GSM
gtm
guiddata
@@ -565,7 +592,6 @@ hbr
HBRBACKGROUND
hbrush
hcblack
-HCERTSTORE
HCRYPTHASH
HCRYPTPROV
hcursor
@@ -591,6 +617,7 @@ HIMAGELIST
himl
hinst
HIWORD
+HKC
HKCC
HKCOMB
HKCR
@@ -604,6 +631,7 @@ HMD
hmenu
hmodule
hmonitor
+homies
homljgmgpmcbpjbnjpfijnhipfkiclkd
HORZRES
HORZSIZE
@@ -619,9 +647,11 @@ HREDRAW
hres
hresult
hrgn
+HROW
hsb
HSCROLL
hsi
+HSSH
HTCLIENT
hthumbnail
HTOUCHINPUT
@@ -641,11 +671,13 @@ HWNDPREV
hyjiacan
IAI
IBeam
+icf
ICONERROR
ICONLOCATION
idc
IDCANCEL
IDD
+idk
idl
idlist
IDOK
@@ -653,15 +685,16 @@ IDR
IDXGI
ietf
IEXPLORE
+iextn
IFACEMETHOD
IFACEMETHODIMP
IFile
+IGNOREBASECLASS
IGNOREUNKNOWN
+IGo
iid
Iindex
Ijwhost
-IKs
-iljxck
IMAGEHLP
IMAGERESIZERCONTEXTMENU
IMAGERESIZEREXT
@@ -669,6 +702,8 @@ imageresizerinput
imageresizersettings
imagingdevices
ime
+imgflip
+inbox
INCONTACT
Indo
inetcpl
@@ -678,6 +713,7 @@ Infotip
INITDIALOG
INITGUID
INITTOLOGFONTSTRUCT
+INLINEPREFIX
inorder
INPC
inproc
@@ -702,6 +738,8 @@ Inste
Interlop
INTRESOURCE
INVALIDARG
+INVALIDCALL
+INVALIDINDEX
invalidoperatioexception
ipcmanager
IPREVIEW
@@ -720,7 +758,6 @@ IUnknown
IUse
IWIC
iwr
-IYUV
jfif
jgeosdfsdsgmkedfgdfgdfgbkmhcgcflmi
jjw
@@ -739,6 +776,7 @@ KEYBOARDMANAGEREDITOR
KEYBOARDMANAGEREDITORLIBRARYWRAPPER
keyboardmanagerstate
keyboardmanagerui
+keyboardtester
KEYEVENTF
KEYIMAGE
keynum
@@ -747,18 +785,15 @@ keyvault
KILLFOCUS
killrunner
kmph
-KSPROPERTY
Kybd
lastcodeanalysissucceeded
Lastdevice
LASTEXITCODE
LAYOUTRTL
-lcb
LCIDTo
Lclean
Ldone
Ldr
-ldx
LEFTSCROLLBAR
LEFTTEXT
LError
@@ -766,6 +801,7 @@ LEVELID
LExit
lhwnd
LIBID
+libraryincludes
LIMITSIZE
LIMITTEXT
lindex
@@ -774,7 +810,6 @@ LINKOVERLAY
LINQTo
listview
LIVEZOOM
-lld
LLKH
llkhf
LMEM
@@ -782,6 +817,7 @@ LMENU
lnks
LOADFROMFILE
LOBYTE
+localappdata
LOCALDISPLAY
localpackage
LOCALSYSTEM
@@ -792,7 +828,6 @@ logon
LOGPIXELSX
LOGPIXELSY
longdate
-LONGLONG
LONGNAMES
lowlevel
LOWORD
@@ -824,10 +859,10 @@ LPTOP
lptpm
LPTR
LPTSTR
+lpv
LPW
lpwcx
lpwndpl
-lpv
LReader
LRESULT
LSTATUS
@@ -836,18 +871,18 @@ lstrcmpi
lstrcpyn
lstrlen
LTEXT
-LTRB
+LTk
LTRREADING
luid
LUMA
+LUQ
lusrmgr
LVal
+lvm
LWA
lwin
LZero
MAGTRANSFORM
-majortype
-makecab
MAKEINTRESOURCE
MAKEINTRESOURCEA
MAKEINTRESOURCEW
@@ -863,14 +898,16 @@ MARKDOWNPREVIEWHANDLERCPP
MAXIMIZEBOX
MAXSHORTCUTSIZE
maxversiontested
+MBM
MBR
MDICHILD
MDL
+mdpvm
mdtext
mdtxt
mdwn
-MEDIASUBTYPE
-mediatype
+meme
+memicmp
MENUITEMINFO
MENUITEMINFOW
MERGECOPY
@@ -878,9 +915,8 @@ MERGEPAINT
Metadatas
metafile
mfc
-mfplat
Mgmt
-mic
+Microwaved
midl
mii
mindaro
@@ -890,9 +926,9 @@ MINIMIZEEND
MINIMIZESTART
miniz
MINMAXINFO
+minwindef
Mip
Miracast
-mjpg
mkdn
mlcfg
mmc
@@ -900,6 +936,7 @@ mmcexe
MMdd
mmi
mmsys
+mobileredirect
mockapi
MODALFRAME
MODESPRUNED
@@ -927,11 +964,13 @@ msctls
msdata
MSDL
MSGFLT
+MSHCTX
+MSHLFLAGS
+MSIDXS
+MSIDXSPROP
msiexec
MSIFASTINSTALL
MSIHANDLE
-Msimg
-msiquery
MSIRESTARTMANAGERCONTROL
msixbundle
MSIXCA
@@ -948,6 +987,7 @@ MULTIPLEUSE
multizone
muxc
mvvm
+MVVMTK
MWBEx
MYICON
NAMECHANGE
@@ -988,6 +1028,8 @@ NEWPLUSCONTEXTMENU
NEWPLUSSHELLEXTENSIONWIN
newrow
newsgroups
+NGQt
+nicksnettravels
NIF
NLog
NLSTEXT
@@ -1008,6 +1050,7 @@ NOCRLF
nodeca
NODRAWCAPTION
NODRAWICON
+NOFIXUPS
NOINHERITLAYOUT
NOINTERFACE
NOINVERT
@@ -1021,12 +1064,15 @@ nonclient
NONCLIENTMETRICSW
NONELEVATED
NONINFRINGEMENT
+nonspace
nonstd
+NOOPEN
NOOWNERZORDER
NOPARENTNOTIFY
NOPREFIX
NOREDIRECTIONBITMAP
NOREDRAW
+NOREMAPCLSID
NOREMOVE
norename
NOREPEAT
@@ -1045,9 +1091,11 @@ NOTIFYICONDATAW
NOTIMPL
NOTOPMOST
NOTRACK
+NOTRUNCATE
NOTSRCCOPY
NOTSRCERASE
NOTXORPEN
+NOUSERSETTINGS
NOZORDER
NPH
npmjs
@@ -1057,6 +1105,7 @@ NTAPI
ntdll
NTSTATUS
NTSYSAPI
+NTZm
NULLCURSOR
nullonfailure
numberbox
@@ -1074,12 +1123,12 @@ oldpath
oldtheme
oleaut
OLECHAR
+openas
opencode
OPENFILENAME
opensource
openxmlformats
OPTIMIZEFORINVOKE
-ORAW
ORPHANEDDIALOGTITLE
ORSCANS
oss
@@ -1090,15 +1139,15 @@ OSVERSIONINFOEXW
OSVERSIONINFOW
osvi
OUTOFCONTEXT
-outpin
Outptr
outsettings
OVERLAPPEDWINDOW
-overlaywindow
Oversampling
OVERWRITEPROMPT
+OWMt
OWNDC
OWNERDRAWFIXED
+OWRj
Packagemanager
PACL
PAINTSTRUCT
@@ -1113,7 +1162,6 @@ PATCOPY
PATHMUSTEXIST
PATINVERT
PATPAINT
-PAUDIO
pbc
pbi
PBlob
@@ -1154,11 +1202,11 @@ phwnd
pici
pidl
PIDLIST
-PINDIR
pinfo
pinvoke
pipename
PKBDLLHOOKSTRUCT
+Playbadge
plib
ploc
ploca
@@ -1168,9 +1216,11 @@ PMAGTRANSFORM
PMSIHANDLE
pnid
PNMLINK
-Pnp
+Poc
+Podcasts
POINTERID
POINTERUPDATE
+Pokedex
Popups
POPUPWINDOW
POSITIONITEM
@@ -1183,7 +1233,6 @@ powertoysusersetup
Powrprof
ppenum
ppidl
-ppmt
pprm
pproc
ppshv
@@ -1198,13 +1247,15 @@ ppv
prc
Prefixer
prependpath
+prepopulate
prevhost
previewer
PREVIEWHANDLERFRAMEINFO
-previouscamera
PREVIOUSINSTALLFOLDER
PREVIOUSVERSIONSINSTALLED
prevpane
+prg
+prgh
prgms
pri
PRINTCLIENT
@@ -1220,10 +1271,9 @@ PRODUCTVERSION
Progman
programdata
projectname
-PROPBAG
PROPERTYKEY
+Propset
PROPVARIANT
-propvarutil
PRTL
prvpane
psapi
@@ -1249,6 +1299,7 @@ PTOKEN
PToy
ptstr
pui
+Puser
PWAs
pwcs
PWSTR
@@ -1264,10 +1315,13 @@ Quarternary
QUERYENDSESSION
QUERYOPEN
QUEUESYNC
+QUICKTIP
QUNS
+QXZ
RAII
RAlt
randi
+Rasterization
Rasterize
RAWINPUTDEVICE
RAWINPUTHEADER
@@ -1286,27 +1340,23 @@ RECTSOURCE
recyclebin
Redist
Reencode
-reencoded
REFCLSID
-REFGUID
REFIID
REGCLS
regfile
-REGFILTER
-REGFILTERPINS
+REGISTERCLASSEX
REGISTERCLASSFAILED
REGISTRYHEADER
registrypath
REGISTRYPREVIEWEXT
registryroot
regkey
-REGPINTYPES
regroot
-regsvr
REINSTALLMODE
reloadable
Relogger
remappings
+REMAPRUNDLL
REMAPSUCCESSFUL
REMAPUNSUCCESSFUL
Remotable
@@ -1333,6 +1383,7 @@ RGBQUAD
rgbs
rgelt
rgf
+rgh
rgn
rgs
RIDEV
@@ -1343,6 +1394,7 @@ RKey
RNumber
rop
ROUNDSMALL
+ROWSETEXT
rpcrt
RRF
rrr
@@ -1381,9 +1433,11 @@ SDDL
SDKDDK
sdns
searchterm
+searchtext
SEARCHUI
SECONDARYDISPLAY
secpol
+SEEMASKINVOKEIDLIST
SELCHANGE
SENDCHANGE
sendvirtualinput
@@ -1410,10 +1464,12 @@ settingsheader
settingshotkeycontrol
setvariable
SETWORKAREA
+SFBS
sfgao
SFGAOF
SHACF
SHANDLE
+sharepoint
sharpkeys
SHCNE
SHCNF
@@ -1425,14 +1481,17 @@ SHELLDLL
shellex
SHELLEXECUTEINFO
SHELLEXECUTEINFOW
+SHELLEXTENSION
+SHELLNEWVALUE
SHFILEINFO
SHFILEOPSTRUCT
SHGDN
SHGDNF
SHGFI
+SHGFIICON
+SHGFILARGEICON
shinfo
shlwapi
-shmem
SHNAMEMAPPING
shobjidl
SHORTCUTATLEAST
@@ -1471,7 +1530,6 @@ Sizename
SIZENESW
SIZENS
SIZENWSE
-sizeread
SIZEWE
SKEXP
SKIPOWNPROCESS
@@ -1485,6 +1543,8 @@ SNAPPROCESS
snwprintf
softline
SOURCECLIENTAREAONLY
+sourced
+sourcedoc
SOURCEHEADER
sourcesdirectory
spdisp
@@ -1492,6 +1552,8 @@ spdlog
spdo
spesi
splitwstring
+Spongebob
+spongebot
spsi
spsia
spsrm
@@ -1523,15 +1585,15 @@ STATSTG
stdafx
STDAPI
stdc
+stdcpp
stdcpplatest
STDMETHODCALLTYPE
STDMETHODIMP
STGC
STGM
STGMEDIUM
-sticpl
STICKYKEYS
-stl
+sticpl
storelogo
stprintf
streamjsonrpc
@@ -1540,8 +1602,6 @@ stringtable
stringval
Strm
strret
-strsafe
-strutil
stscanf
sttngs
Stubless
@@ -1550,7 +1610,6 @@ STYLECHANGING
subkeys
sublang
SUBMODULEUPDATE
-subquery
Superbar
sut
svchost
@@ -1565,7 +1624,6 @@ symbolrequestprod
SYMCACHE
SYMED
SYMOPT
-SYNCMFT
SYNCPAINT
SYSCHAR
SYSCOLORCHANGE
@@ -1621,7 +1679,10 @@ THH
THICKFRAME
THISCOMPONENT
THotkey
+throughs
+TIcon
TILEDWINDOW
+TILEINFO
TILLSON
timedate
timediff
@@ -1634,6 +1695,7 @@ tkconverters
TLayout
tlb
tlbimp
+tlc
TPMLEFTALIGN
TPMRETURNCMD
TMPVAR
@@ -1652,10 +1714,12 @@ trafficmanager
traies
transicc
TRAYMOUSEMESSAGE
+TResult
triaging
trl
trx
tsa
+TSender
TServer
tstoi
TStr
@@ -1671,6 +1735,8 @@ UAL
uap
UBR
UCallback
+ucrt
+ucrtd
udit
uefi
uesc
@@ -1685,6 +1751,7 @@ UNCPRIORITY
UNDNAME
unhiding
UNICODETEXT
+uninstalls
Uniquifies
unitconverter
unittests
@@ -1716,18 +1783,17 @@ uxtheme
vabdq
validmodulename
valuegenerator
+VARENUM
variantassignment
vcamp
-vcdl
+VCENTER
vcgtq
VCINSTALLDIR
Vcpkg
VCRT
-VCENTER
vcruntime
vcvars
VDesktop
-vdi
vdupq
VERBSONLY
VERBW
@@ -1738,19 +1804,17 @@ VERTSIZE
VFT
vget
vgetq
-vid
-VIDCAP
-VIDEOINFOHEADER
+videourl
viewmodel
-vih
VIRTKEY
VIRTUALDESK
VISEGRADRELAY
visiblecolorformats
Visibletrue
visualeffects
-VKey
+vkey
vmovl
+VMs
vorrq
VOS
vpaddlq
@@ -1776,6 +1840,7 @@ vswhere
Vtbl
WANTMAPPINGHANDLE
WANTPALM
+wasdk
wbem
WBounds
Wca
@@ -1785,17 +1850,20 @@ WClass
wcsicmp
wcsncpy
wcsnicmp
+WCT
WDA
wdm
wdp
wdupenv
webbrowsers
-webcam
webpage
websites
wekyb
wgpocpl
+WHEREID
+Wholegrain
WIC
+wic
wifi
wil
winapi
@@ -1830,7 +1898,6 @@ winsta
WINTHRESHOLD
WINVER
winxamlmanager
-wistd
withinrafael
Withscript
wixproj
@@ -1881,9 +1948,11 @@ wtoi
WTS
WTSAT
Wubi
-WVC
+WUX
Wwanpp
XAxis
+xclip
+xdoc
XDocument
XElement
xfd
@@ -1901,13 +1970,16 @@ XUP
XVIRTUALSCREEN
xxxxxx
YAxis
+ycombinator
Yeet
YIncrement
yinle
yinyue
+youtube
YPels
YResolution
YStr
+YTM
YVIRTUALSCREEN
ZEROINIT
zonable
@@ -1916,4 +1988,6 @@ Zoneszonabletester
Zoomin
zoomit
ZOOMITX
-zzz
+ZXk
+ZXNs
+zzz
\ No newline at end of file
diff --git a/.github/actions/spell-check/patterns.txt b/.github/actions/spell-check/patterns.txt
index f8c9761933..5a6f4785b1 100644
--- a/.github/actions/spell-check/patterns.txt
+++ b/.github/actions/spell-check/patterns.txt
@@ -232,6 +232,15 @@ _SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING
# ignore long runs of a single character:
\b([A-Za-z])\g{-1}{3,}\b
+# Amazon
+\bamazon\.com/[-\w]+/(?:dp/[0-9A-Z]+|)
+
+# imgur
+\bimgur\.com/[^.]+
+
+# Process Process (typename varname)
+Process Process
+
# ZoomIt menu items with accelerator keys
E&xit
-St&yle
+St&yle
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 89541c3a2e..66532cc074 100644
--- a/.gitignore
+++ b/.gitignore
@@ -224,7 +224,7 @@ ClientBin/
*.publishsettings
orleans.codegen.cs
-# Including strong name files can present a security risk
+# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
@@ -322,7 +322,7 @@ ImageResizer/tools/**
# OpenCover UI analysis results
OpenCover/
-# Azure Stream Analytics local run output
+# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
@@ -331,7 +331,7 @@ ASALocalRun/
# NVidia Nsight GPU debugger configuration file
*.nvuser
-# MFractors (Xamarin productivity tool) working folder
+# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Temp build files
diff --git a/.pipelines/ESRPSigning_cmdpal_msix_content.json b/.pipelines/ESRPSigning_cmdpal_msix_content.json
new file mode 100644
index 0000000000..6522fc56b3
--- /dev/null
+++ b/.pipelines/ESRPSigning_cmdpal_msix_content.json
@@ -0,0 +1,51 @@
+{
+ "Version": "1.0.0",
+ "UseMinimatch": false,
+ "SignBatches": [
+ {
+ "MatchedPath": [
+ "*.dll",
+ "*.exe"
+ ],
+ "SigningInfo": {
+ "Operations": [
+ {
+ "KeyCode": "CP-230012",
+ "OperationSetCode": "SigntoolSign",
+ "Parameters": [
+ {
+ "parameterName": "OpusName",
+ "parameterValue": "Microsoft"
+ },
+ {
+ "parameterName": "OpusInfo",
+ "parameterValue": "http://www.microsoft.com"
+ },
+ {
+ "parameterName": "FileDigest",
+ "parameterValue": "/fd \"SHA256\""
+ },
+ {
+ "parameterName": "PageHash",
+ "parameterValue": "/NPH"
+ },
+ {
+ "parameterName": "TimeStamp",
+ "parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
+ }
+ ],
+ "ToolName": "sign",
+ "ToolVersion": "1.0"
+ },
+ {
+ "KeyCode": "CP-230012",
+ "OperationSetCode": "SigntoolVerify",
+ "Parameters": [],
+ "ToolName": "sign",
+ "ToolVersion": "1.0"
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/.pipelines/ESRPSigning_core.json b/.pipelines/ESRPSigning_core.json
index 7b9af0a767..1b78856032 100644
--- a/.pipelines/ESRPSigning_core.json
+++ b/.pipelines/ESRPSigning_core.json
@@ -217,7 +217,10 @@
"PowerToys.ZoomItSettingsInterop.dll",
"WinUI3Apps\\PowerToys.Settings.dll",
- "WinUI3Apps\\PowerToys.Settings.exe"
+ "WinUI3Apps\\PowerToys.Settings.exe",
+
+ "PowerToys.CmdPalModuleInterface.dll",
+ "*Microsoft.CmdPal.UI_*.msix"
],
"SigningInfo": {
"Operations": [
diff --git a/.pipelines/ESRPSigning_sdk.json b/.pipelines/ESRPSigning_sdk.json
new file mode 100644
index 0000000000..066acf9e4e
--- /dev/null
+++ b/.pipelines/ESRPSigning_sdk.json
@@ -0,0 +1,51 @@
+{
+ "Version": "1.0.0",
+ "UseMinimatch": false,
+ "SignBatches": [
+ {
+ "MatchedPath": [
+ "Microsoft.CommandPalette.Extensions.dll",
+ "Microsoft.CommandPalette.Extensions.Toolkit.dll"
+ ],
+ "SigningInfo": {
+ "Operations": [
+ {
+ "KeyCode": "CP-230012",
+ "OperationSetCode": "SigntoolSign",
+ "Parameters": [
+ {
+ "parameterName": "OpusName",
+ "parameterValue": "Microsoft"
+ },
+ {
+ "parameterName": "OpusInfo",
+ "parameterValue": "http://www.microsoft.com"
+ },
+ {
+ "parameterName": "FileDigest",
+ "parameterValue": "/fd \"SHA256\""
+ },
+ {
+ "parameterName": "PageHash",
+ "parameterValue": "/NPH"
+ },
+ {
+ "parameterName": "TimeStamp",
+ "parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
+ }
+ ],
+ "ToolName": "sign",
+ "ToolVersion": "1.0"
+ },
+ {
+ "KeyCode": "CP-230012",
+ "OperationSetCode": "SigntoolVerify",
+ "Parameters": [],
+ "ToolName": "sign",
+ "ToolVersion": "1.0"
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/.pipelines/v2/release.yml b/.pipelines/v2/release.yml
index 3e0669bfd2..26819c5d14 100644
--- a/.pipelines/v2/release.yml
+++ b/.pipelines/v2/release.yml
@@ -20,6 +20,16 @@ parameters:
type: string
default: '0.0.1'
+ - name: cmdPalVersionNumber
+ displayName: "Command Palette Version Number"
+ type: string
+ default: '0.0.1'
+
+ - name: cmdPalSdkVersionNumber
+ displayName: "Command Palette SDK Version Number"
+ type: string
+ default: '0.0.1'
+
- name: buildConfigurations
displayName: "Build Configurations"
type: object
@@ -78,6 +88,7 @@ extends:
buildPlatforms: ${{ parameters.buildPlatforms }}
buildConfigurations: ${{ parameters.buildConfigurations }}
versionNumber: ${{ parameters.versionNumber }}
+ cmdPalVersionNumber: ${{ parameters.cmdPalVersionNumber }}
publishArtifacts: false # 1ES PT handles publication for us.
codeSign: true
runTests: false
@@ -95,7 +106,7 @@ extends:
beforeBuildSteps:
# Sets versions for all PowerToy created DLLs
- pwsh: |-
- .pipelines/versionSetting.ps1 -versionNumber '${{ parameters.versionNumber }}' -DevEnvironment ''
+ .pipelines/versionSetting.ps1 -versionNumber '${{ parameters.versionNumber }}' -DevEnvironment '' -cmdPalVersionNumber '${{ parameters.cmdPalVersionNumber }}'
displayName: Prepare versioning
# Prepare the localizations and telemetry config before the release build
@@ -107,6 +118,28 @@ extends:
move /Y "Microsoft.PowerToys.Telemetry.2.0.2\build\include\TelemetryBase.cs" "src\common\Telemetry\TelemetryBase.cs" || exit /b 1
displayName: Emplace telemetry files
+ - stage: Build_SDK
+ displayName: Build SDK
+ dependsOn: []
+ jobs:
+ - template: .pipelines/v2/templates/job-build-sdk.yml@self
+ parameters:
+ pool:
+ name: SHINE-INT-L
+ image: SHINE-VS17-Latest
+ os: windows
+ codeSign: true
+ sdkVersionNumber: ${{ parameters.cmdPalSdkVersionNumber }}
+ signingIdentity:
+ serviceName: $(SigningServiceName)
+ appId: $(SigningAppId)
+ tenantId: $(SigningTenantId)
+ akvName: $(SigningAKVName)
+ authCertName: $(SigningAuthCertName)
+ signCertName: $(SigningSignCertName)
+ useManagedIdentity: $(SigningUseManagedIdentity)
+ clientId: $(SigningOriginalClientId)
+
- stage: Publish
displayName: Publish
dependsOn: [Build]
diff --git a/.pipelines/v2/templates/job-build-project.yml b/.pipelines/v2/templates/job-build-project.yml
index 481534b687..6014d5b992 100644
--- a/.pipelines/v2/templates/job-build-project.yml
+++ b/.pipelines/v2/templates/job-build-project.yml
@@ -56,6 +56,9 @@ parameters:
- name: versionNumber
type: string
default: '0.0.1'
+ - name: cmdPalVersionNumber
+ type: string
+ default: '0.0.1'
- name: useLatestWinAppSDK
type: boolean
default: false
@@ -96,6 +99,7 @@ jobs:
${{ else }}:
OutputBuildPlatform: ${{ platform }}
variables:
+ MakeAppxPath: 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x86\MakeAppx.exe'
# Azure DevOps abhors a vacuum
# If these are blank, expansion will fail later on... which will result in direct substitution of the variable *names*
# later on. We'll just... set them to a single space and if we need to, check IsNullOrWhiteSpace.
@@ -237,6 +241,32 @@ jobs:
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
+ - task: CopyFiles@2
+ displayName: Stage SDK/build
+ inputs:
+ contents: |-
+ "**/cmdpal/extensionsdk/nuget/Microsoft.CommandPalette.Extensions.SDK.props"
+ "**/cmdpal/extensionsdk/nuget/Microsoft.CommandPalette.Extensions.SDK.targets"
+ flattenFolders: True
+ targetFolder: $(JobOutputDirectory)/sdk/build
+
+ - task: CopyFiles@2
+ displayName: Stage SDK/lib
+ inputs:
+ contents: |-
+ "**/Microsoft.CommandPalette.Extensions.Toolkit/$(BuildPlatform)/release/WinUI3Apps/CmdPal/Microsoft.CommandPalette.Extensions.Toolkit.dll"
+ "**/Microsoft.CommandPalette.Extensions.Toolkit/$(BuildPlatform)/release/WinUI3Apps/CmdPal/Microsoft.CommandPalette.Extensions.Toolkit.deps.json"
+ flattenFolders: True
+ targetFolder: $(JobOutputDirectory)/sdk/lib/net8.0-windows10.0.19041.0
+
+ - task: CopyFiles@2
+ displayName: Stage SDK/winmd
+ inputs:
+ contents: |-
+ "**/Microsoft.CommandPalette.Extensions/$(BuildPlatform)/release/Microsoft.CommandPalette.Extensions/Microsoft.CommandPalette.Extensions.winmd"
+ flattenFolders: True
+ targetFolder: $(JobOutputDirectory)/sdk/winmd
+
- task: VSBuild@1
displayName: Build BugReportTool
inputs:
@@ -304,7 +334,7 @@ jobs:
displayName: HACK Copy core WebView2 ARM64 dll to output directory
condition: eq(variables['BuildPlatform'],'arm64')
inputs:
- contents: packages/Microsoft.Web.WebView2.1.0.2739.15/runtimes/win-ARM64/native_uap/Microsoft.Web.WebView2.Core.dll
+ contents: packages/Microsoft.Web.WebView2.1.0.2903.40/runtimes/win-ARM64/native_uap/Microsoft.Web.WebView2.Core.dll
targetFolder: $(Build.SourcesDirectory)/ARM64/Release/WinUI3Apps/
flattenFolders: True
OverWrite: True
@@ -355,6 +385,33 @@ jobs:
!**\obj\**
- ${{ if eq(parameters.codeSign, true) }}:
+ - pwsh: |-
+ $Package = (Get-ChildItem -Recurse -Filter "Microsoft.CmdPal.UI_*.msix" | Select -First 1)
+ $PackageFilename = $Package.FullName
+ Write-Host "##vso[task.setvariable variable=CmdPalPackagePath]${PackageFilename}"
+ displayName: Locate the MSIX
+
+ - pwsh: |-
+ & "$(MakeAppxPath)" unpack /p "$(CmdPalPackagePath)" /d "$(JobOutputDirectory)/CmdPalPackageContents"
+ displayName: Unpack the MSIX for signing
+
+ - template: steps-esrp-signing.yml
+ parameters:
+ displayName: Sign CmdPal MSIX content
+ signingIdentity: ${{ parameters.signingIdentity }}
+ inputs:
+ FolderPath: '$(JobOutputDirectory)/CmdPalPackageContents'
+ signType: batchSigning
+ batchSignPolicyFile: '$(build.sourcesdirectory)\.pipelines\ESRPSigning_cmdpal_msix_content.json'
+ ciPolicyFile: '$(build.sourcesdirectory)\.pipelines\CIPolicy.xml'
+
+ - pwsh: |-
+ $outDir = New-Item -Type Directory "$(JobOutputDirectory)/_appx" -ErrorAction:Ignore
+ $PackageFilename = Join-Path $outDir.FullName (Split-Path -Leaf "$(CmdPalPackagePath)")
+ & "$(MakeAppxPath)" pack /h SHA256 /o /p $PackageFilename /d "$(JobOutputDirectory)/CmdPalPackageContents"
+ Copy-Item -Force $PackageFilename "$(CmdPalPackagePath)"
+ displayName: Re-pack the new CmdPal package after signing
+
- template: steps-esrp-signing.yml
parameters:
displayName: Sign Core PowerToys
@@ -430,7 +487,7 @@ jobs:
$machinePlat = "hash_machine_$(BuildPlatform).txt";
$combinedUserPath = $p + $userPlat;
$combinedMachinePath = $p + $machinePlat;
-
+
echo $p
echo $userPlat
@@ -440,7 +497,7 @@ jobs:
echo $machinePlat
echo $machineHash
echo $combinedMachinePath
-
+
$userHash | out-file -filepath $combinedUserPath
$machineHash | out-file -filepath $combinedMachinePath
displayName: Calculate file hashes
diff --git a/.pipelines/v2/templates/job-build-sdk.yml b/.pipelines/v2/templates/job-build-sdk.yml
new file mode 100644
index 0000000000..f8aa5dca3e
--- /dev/null
+++ b/.pipelines/v2/templates/job-build-sdk.yml
@@ -0,0 +1,91 @@
+parameters:
+ - name: buildConfigurations
+ type: object
+ default:
+ - Release
+ - name: codeSign
+ type: boolean
+ default: false
+ - name: pool
+ type: object
+ default: []
+ - name: signingIdentity
+ type: object
+ default: {}
+ - name: sdkVersionNumber
+ type: string
+ default: '0.0.1'
+
+jobs:
+- job: "BuildSDK"
+ ${{ if ne(length(parameters.pool), 0) }}:
+ pool: ${{ parameters.pool }}
+ displayName: Build SDK
+ timeoutInMinutes: 240
+ cancelTimeoutInMinutes: 1
+ templateContext: # Required when this template is hosted in 1ES PT
+ outputs:
+ - output: pipelineArtifact
+ artifactName: SDK
+ targetPath: $(Build.ArtifactStagingDirectory)
+ steps:
+ - checkout: self
+ clean: true
+ submodules: true
+ persistCredentials: True
+ fetchTags: false
+ fetchDepth: 1
+
+ - pwsh: |-
+ & "$(build.sourcesdirectory)\src\modules\cmdpal\extensionsdk\nuget\BuildSDKHelper.ps1" -Configuration "Release" -VersionOfSDK ${{ parameters.sdkVersionNumber }} -BuildStep "build" -IsAzurePipelineBuild
+ displayName: Build SDK
+
+ - ${{ if eq(parameters.codeSign, true) }}:
+ - template: steps-esrp-signing.yml
+ parameters:
+ displayName: Sign SDK
+ signingIdentity: ${{ parameters.signingIdentity }}
+ inputs:
+ FolderPath: 'src/modules'
+ signType: batchSigning
+ batchSignPolicyFile: '$(build.sourcesdirectory)\.pipelines\ESRPSigning_sdk.json'
+ ciPolicyFile: '$(build.sourcesdirectory)\.pipelines\CIPolicy.xml'
+
+ - pwsh: |-
+ & "$(build.sourcesdirectory)\src\modules\cmdpal\extensionsdk\nuget\BuildSDKHelper.ps1" -Configuration "Release" -VersionOfSDK ${{ parameters.sdkVersionNumber }} -BuildStep "pack" -IsAzurePipelineBuild
+ displayName: Pack SDK
+
+ - task: CopyFiles@2
+ displayName: Copy Nuget to Artifact Staging
+ inputs:
+ sourceFolder: "$(build.sourcesdirectory)/src/modules/cmdpal/extensionsdk/_build"
+ contents: '*.nupkg'
+ targetFolder: '$(Build.ArtifactStagingDirectory)'
+
+ - ${{ if eq(parameters.codeSign, true) }}:
+ - template: steps-esrp-signing.yml
+ parameters:
+ displayName: Sign NuGet packages
+ signingIdentity: ${{ parameters.signingIdentity }}
+ inputs:
+ FolderPath: $(Build.ArtifactStagingDirectory)
+ Pattern: '*.nupkg'
+ UseMinimatch: true
+ signConfigType: inlineSignParams
+ inlineOperation: >-
+ [
+ {
+ "KeyCode": "CP-401405",
+ "OperationCode": "NuGetSign",
+ "Parameters": {},
+ "ToolName": "sign",
+ "ToolVersion": "1.0"
+ },
+ {
+ "KeyCode": "CP-401405",
+ "OperationCode": "NuGetVerify",
+ "Parameters": {},
+ "ToolName": "sign",
+ "ToolVersion": "1.0"
+ }
+ ]
diff --git a/.pipelines/v2/templates/job-test-project.yml b/.pipelines/v2/templates/job-test-project.yml
index d5252ba23b..234f477fb6 100644
--- a/.pipelines/v2/templates/job-test-project.yml
+++ b/.pipelines/v2/templates/job-test-project.yml
@@ -105,4 +105,4 @@ jobs:
**\UITests-FancyZones.dll
**\UITests-FancyZonesEditor.dll
!**\obj\**
- !**\ref\**
+ !**\ref\**
\ No newline at end of file
diff --git a/.pipelines/v2/templates/steps-build-installer.yml b/.pipelines/v2/templates/steps-build-installer.yml
index a4eb61481c..8c3c89dbc0 100644
--- a/.pipelines/v2/templates/steps-build-installer.yml
+++ b/.pipelines/v2/templates/steps-build-installer.yml
@@ -87,6 +87,30 @@ steps:
dir $(build.sourcesdirectory)\extractedMsi
displayName: "${{replace(replace(parameters.buildUserInstaller,'True','👤'),'False','💻')}} Extract and verify MSI"
+ # Extract CmdPal msix package to check if its content is signed
+ - pwsh: |-
+ Write-Host "Extracting CmdPal MSIX package"
+
+ # Define the directory to search
+ $searchDir = "extractedMsi\File"
+
+ # Define the regex pattern for MSIX files
+ $pattern = '^Microsoft.CmdPal.UI.*\.msix$'
+
+ # Get all files in the directory and subdirectories
+ $msixFile = Get-ChildItem -Path $searchDir -Recurse -File | Where-Object {
+ $_.Name -match $pattern
+ }
+
+ Write-Host "MSIX file found: " $msixFile
+
+ $destinationDir = "$(build.sourcesdirectory)\extractedMsi\File\extractedCmdPalMsix"
+
+ Expand-Archive -Path $msixFile -DestinationPath $destinationDir
+ Get-ChildItem -Path $destinationDir -Recurse -File
+
+ displayName: ${{replace(replace(parameters.buildUserInstaller,'True','👤'),'False','💻')}} Extract CmdPal MSIX package
+
# Check if deps.json files don't reference different dll versions.
- pwsh: |-
& '.pipelines/verifyDepsJsonLibraryVersions.ps1' -targetDir '$(build.sourcesdirectory)\extractedMsi\File'
diff --git a/.pipelines/verifyDepsJsonLibraryVersions.ps1 b/.pipelines/verifyDepsJsonLibraryVersions.ps1
index e4f1733ac6..e85ca1d991 100644
--- a/.pipelines/verifyDepsJsonLibraryVersions.ps1
+++ b/.pipelines/verifyDepsJsonLibraryVersions.ps1
@@ -18,6 +18,11 @@ $totalFailures = 0
Get-ChildItem $targetDir -Recurse -Filter *.deps.json -Exclude *UITest*,MouseJump.Common.UnitTests*,*.FuzzTests* | ForEach-Object {
# Temporarily exclude All UI-Test, Fuzzer-Test projects because of Appium.WebDriver dependencies
$depsJsonFullFileName = $_.FullName
+
+ if ($depsJsonFullFileName -like "*CmdPal*") {
+ return
+ }
+
$depsJsonFileName = $_.Name
$depsJson = Get-Content $depsJsonFullFileName | ConvertFrom-Json
diff --git a/.pipelines/versionAndSignCheck.ps1 b/.pipelines/versionAndSignCheck.ps1
index 1970bd6adb..89cfbeea1c 100644
--- a/.pipelines/versionAndSignCheck.ps1
+++ b/.pipelines/versionAndSignCheck.ps1
@@ -22,7 +22,11 @@ $versionExceptions = @(
"TraceReloggerLib.dll",
"Microsoft.WindowsAppRuntime.Release.Net.dll",
"Microsoft.Windows.Widgets.Projection.dll",
- "WinRT.Host.Shim.dll") -join '|';
+ "WinRT.Host.Shim.dll",
+ "WyHash.dll",
+ "Microsoft.Recognizers.Text.DataTypes.TimexExpression.dll",
+ "ObjectModelCsProjection.dll",
+ "RendererCsProjection.dll") -join '|';
$nullVersionExceptions = @(
"codicon.ttf",
"e_sqlite3.dll",
@@ -43,12 +47,14 @@ $nullVersionExceptions = @(
"PushNotificationsLongRunningTask.ProxyStub.dll",
"WindowsAppSdk.AppxDeploymentExtensions.Desktop.dll",
"System.Diagnostics.EventLog.Messages.dll",
- "Microsoft.Windows.Widgets.dll") -join '|';
+ "Microsoft.Windows.Widgets.dll",
+ "AdaptiveCards.ObjectModel.WinUI3.dll",
+ "AdaptiveCards.Rendering.WinUI3.dll") -join '|';
$totalFailure = 0;
Write-Host $DirPath;
-if (-not (Test-Path $DirPath)) {
+if (-not (Test-Path $DirPath)) {
Write-Error "Folder does not exist!"
}
@@ -70,7 +76,7 @@ $items | ForEach-Object {
Write-Host "Version set to 1.0.0.0: " + $_.FullName
$totalFailure++;
}
- elseif ($_.VersionInfo.FileVersion -eq $null -and $_.Name -notmatch $nullVersionExceptions) {
+ elseif ($_.VersionInfo.FileVersion -eq $null -and $_.Name -notmatch $nullVersionExceptions) {
# These items are exceptions that actually a version not set.
Write-Host "Version not set: " + $_.FullName
$totalFailure++;
diff --git a/.pipelines/versionSetting.ps1 b/.pipelines/versionSetting.ps1
index 7221392195..bda3c47cc2 100644
--- a/.pipelines/versionSetting.ps1
+++ b/.pipelines/versionSetting.ps1
@@ -5,7 +5,10 @@ Param(
[Parameter(Mandatory=$True,Position=2)]
[AllowEmptyString()]
- [string]$DevEnvironment = "Local"
+ [string]$DevEnvironment = "Local",
+
+ [Parameter(Mandatory=$True,Position=3)]
+ [string]$cmdPalVersionNumber = "0.0.1"
)
Write-Host $PSScriptRoot
@@ -38,9 +41,20 @@ $verPropReadFileLocation = $verPropWriteFileLocation;
$verProps.Project.PropertyGroup.Version = $versionNumber;
$verProps.Project.PropertyGroup.DevEnvironment = $DevEnvironment;
-Write-Host "xml" $verProps.Project.PropertyGroup.Version
+Write-Host "xml" $verProps.Project.PropertyGroup.Version
$verProps.Save($verPropWriteFileLocation);
+
+#### The same thing as above, but for the CmdPal version
+$verPropWriteFileLocation = $PSScriptRoot + '/../src/CmdPalVersion.props';
+$verPropReadFileLocation = $verPropWriteFileLocation;
+[XML]$verProps = Get-Content $verPropReadFileLocation
+$verProps.Project.PropertyGroup.CmdPalVersion = $cmdPalVersionNumber;
+$verProps.Project.PropertyGroup.DevEnvironment = $DevEnvironment;
+Write-Host "xml" $verProps.Project.PropertyGroup.Version
+$verProps.Save($verPropWriteFileLocation);
+#######
+
# Set PowerRenameContextMenu package version in AppManifest.xml
$powerRenameContextMenuAppManifestWriteFileLocation = $PSScriptRoot + '/../src/modules/powerrename/PowerRenameContextMenu/AppxManifest.xml';
$powerRenameContextMenuAppManifestReadFileLocation = $powerRenameContextMenuAppManifestWriteFileLocation;
@@ -76,3 +90,12 @@ $newPlusContextMenuAppManifestReadFileLocation = $newPlusContextMenuAppManifestW
$newPlusContextMenuAppManifest.Package.Identity.Version = $versionNumber + '.0'
Write-Host "NewPlusContextMenu version" $newPlusContextMenuAppManifest.Package.Identity.Version
$newPlusContextMenuAppManifest.Save($newPlusContextMenuAppManifestWriteFileLocation);
+
+# Set package version in Package.appxmanifest
+$cmdPalAppManifestWriteFileLocation = $PSScriptRoot + '/../src/modules/cmdpal/Microsoft.CmdPal.UI/Package.appxmanifest';
+$cmdPalAppManifestReadFileLocation = $cmdPalAppManifestWriteFileLocation;
+
+[XML]$cmdPalAppManifest = Get-Content $cmdPalAppManifestReadFileLocation
+$cmdPalAppManifest.Package.Identity.Version = $cmdPalVersionNumber + '.0'
+Write-Host "CmdPal Package version: " $cmdPalAppManifest.Package.Identity.Version
+$cmdPalAppManifest.Save($cmdPalAppManifestWriteFileLocation);
diff --git a/.vsconfig b/.vsconfig
index bc5f1200fc..77ec8b0ffd 100644
--- a/.vsconfig
+++ b/.vsconfig
@@ -1,12 +1,13 @@
{
"version": "1.0",
- "components": [
+ "components": [
"Microsoft.VisualStudio.Component.CoreEditor",
"Microsoft.VisualStudio.Workload.CoreEditor",
"Microsoft.VisualStudio.Workload.NativeDesktop",
"Microsoft.VisualStudio.Workload.ManagedDesktop",
"Microsoft.VisualStudio.Workload.Universal",
"Microsoft.VisualStudio.Component.Windows10SDK.19041",
+ "Microsoft.VisualStudio.Component.Windows10SDK.20348",
"Microsoft.VisualStudio.Component.Windows10SDK.22621",
"Microsoft.VisualStudio.ComponentGroup.UWP.VC",
"Microsoft.VisualStudio.Component.UWP.VC.ARM64",
@@ -18,4 +19,4 @@
"Microsoft.VisualStudio.Component.VC.ATL.Spectre",
"Microsoft.VisualStudio.ComponentGroup.WindowsAppSDK.Cs"
]
-}
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 9698a4adb7..091364bd78 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -3,17 +3,21 @@
true
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
@@ -38,9 +42,10 @@
-
+
+
@@ -92,6 +97,7 @@
+
diff --git a/NOTICE.md b/NOTICE.md
index 138743ecfe..0b9cbb5fc2 100644
--- a/NOTICE.md
+++ b/NOTICE.md
@@ -1323,6 +1323,43 @@ EXHIBIT A -Mozilla Public License.
Original Code Source Code for Your Modifications.]
```
+## Utility: Command Palette
+
+### wyhash
+
+We use the WyHash NuGet package for calculating stable hashes for strings.
+
+**Source**: [https://github.com/wangyi-fudan/wyhash](https://github.com/wangyi-fudan/wyhash)
+
+### License
+
+```
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to
+```
+
## Utility: Registry Preview
### Monaco Editor
@@ -1355,17 +1392,22 @@ SOFTWARE.
## NuGet Packages used by PowerToys
+
+- AdaptiveCards.ObjectModel.WinUI3 2.0.0-beta
+- AdaptiveCards.Rendering.WinUI3 2.1.0-beta
+- AdaptiveCards.Templating 2.0.2
- Appium.WebDriver 4.4.5
- Azure.AI.OpenAI 1.0.0-beta.17
-- CommunityToolkit.Mvvm 8.2.2
-- CommunityToolkit.WinUI.Animations 8.0.240109
-- CommunityToolkit.WinUI.Collections 8.0.240109
-- CommunityToolkit.WinUI.Controls.Primitives 8.0.240109
-- CommunityToolkit.WinUI.Controls.Segmented 8.0.240109
-- CommunityToolkit.WinUI.Controls.SettingsControls 8.0.240109
-- CommunityToolkit.WinUI.Controls.Sizers 8.0.240109
-- CommunityToolkit.WinUI.Converters 8.0.240109
-- CommunityToolkit.WinUI.Extensions 8.0.240109
+- CommunityToolkit.Common 8.4.0
+- CommunityToolkit.Mvvm 8.4.0
+- CommunityToolkit.WinUI.Animations 8.2.250129-preview2
+- CommunityToolkit.WinUI.Collections 8.2.250129-preview2
+- CommunityToolkit.WinUI.Controls.Primitives 8.2.250129-preview2
+- CommunityToolkit.WinUI.Controls.Segmented 8.2.250129-preview2
+- CommunityToolkit.WinUI.Controls.SettingsControls 8.2.250129-preview2
+- CommunityToolkit.WinUI.Controls.Sizers 8.2.250129-preview2
+- CommunityToolkit.WinUI.Converters 8.2.250129-preview2
+- CommunityToolkit.WinUI.Extensions 8.2.250129-preview2
- CommunityToolkit.WinUI.UI.Controls.DataGrid 7.1.2
- CommunityToolkit.WinUI.UI.Controls.Markdown 7.1.2
- ControlzEx 6.0.0
@@ -1390,13 +1432,14 @@ SOFTWARE.
- Microsoft.NET.ILLink.Tasks (A)
- Microsoft.SemanticKernel 1.15.0
- Microsoft.Toolkit.Uwp.Notifications 7.1.2
-- Microsoft.Web.WebView2 1.0.2739.15
+- Microsoft.Web.WebView2 1.0.2903.40
- Microsoft.Win32.SystemEvents 9.0.3
- Microsoft.Windows.Compatibility 9.0.3
- Microsoft.Windows.CsWin32 0.2.46-beta
- Microsoft.Windows.CsWinRT 2.2.0
- Microsoft.Windows.SDK.BuildTools 10.0.22621.2428
- Microsoft.WindowsAppSDK 1.6.250205002
+- Microsoft.WindowsPackageManager.ComInterop 1.10.120-preview
- Microsoft.Xaml.Behaviors.WinUI.Managed 2.0.9
- Microsoft.Xaml.Behaviors.Wpf 1.1.39
- ModernWpfUI 0.9.4
@@ -1432,3 +1475,4 @@ SOFTWARE.
- UTF.Unknown 2.5.1
- WinUIEx 2.2.0
- WPF-UI 3.0.5
+- WyHash 1.0.5
diff --git a/PowerToys.sln b/PowerToys.sln
index 04b276b361..9b911b388b 100644
--- a/PowerToys.sln
+++ b/PowerToys.sln
@@ -620,10 +620,64 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EtwTrace", "src\common\Tele
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MouseWithoutBorders.UnitTests", "src\modules\MouseWithoutBorders\MouseWithoutBorders.UnitTests\MouseWithoutBorders.UnitTests.csproj", "{66614C26-314C-4B91-9071-76133422CFEF}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CommandPalette", "CommandPalette", "{3846508C-77EB-4034-A702-F8BB263C4F79}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Built-in Extensions", "Built-in Extensions", "{ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.Ext.Apps", "src\modules\cmdpal\Exts\Microsoft.CmdPal.Ext.Apps\Microsoft.CmdPal.Ext.Apps.csproj", "{6CE438DF-C245-4997-A360-0A0939E4BA34}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.Ext.Bookmarks", "src\modules\cmdpal\Exts\Microsoft.CmdPal.Ext.Bookmark\Microsoft.CmdPal.Ext.Bookmarks.csproj", "{E09AA983-C755-474F-83D6-A5CDF528C070}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.Ext.Calc", "src\modules\cmdpal\Exts\Microsoft.CmdPal.Ext.Calc\Microsoft.CmdPal.Ext.Calc.csproj", "{6D56B64D-FF1F-488F-AFED-9B9854A5D399}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.Ext.Registry", "src\modules\cmdpal\Exts\Microsoft.CmdPal.Ext.Registry\Microsoft.CmdPal.Ext.Registry.csproj", "{92EC89E4-9972-453A-8A1A-3A9E230C146A}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.Ext.WindowsServices", "src\modules\cmdpal\Exts\Microsoft.CmdPal.Ext.WindowsServices\Microsoft.CmdPal.Ext.WindowsServices.csproj", "{51939B4F-1F62-4BFF-A6A2-C08646E5BE95}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.Ext.WindowsSettings", "src\modules\cmdpal\Exts\Microsoft.CmdPal.Ext.WindowsSettings\Microsoft.CmdPal.Ext.WindowsSettings.csproj", "{D1160404-D3D1-497A-883A-4059C07C2273}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.Ext.WindowsTerminal", "src\modules\cmdpal\Exts\Microsoft.CmdPal.Ext.WindowsTerminal\Microsoft.CmdPal.Ext.WindowsTerminal.csproj", "{40F6D69D-E321-400F-A767-5628C7AE453D}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Extension SDK", "Extension SDK", "{F3D09629-59A2-4924-A4B9-D6BFAA2C1B49}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.CommandPalette.Extensions", "src\modules\cmdpal\extensionsdk\Microsoft.CommandPalette.Extensions\Microsoft.CommandPalette.Extensions.vcxproj", "{305DD37E-C85D-4B08-AAFE-7381FA890463}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CommandPalette.Extensions.Toolkit", "src\modules\cmdpal\extensionsdk\Microsoft.CommandPalette.Extensions.Toolkit\Microsoft.CommandPalette.Extensions.Toolkit.csproj", "{CA4D810F-C8F4-4B61-9DA9-71807E0B9F24}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.Common", "src\modules\cmdpal\Microsoft.CmdPal.Common\Microsoft.CmdPal.Common.csproj", "{14E62033-58D0-4A7D-8990-52F50A08BBBD}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.UI", "src\modules\cmdpal\Microsoft.Terminal.UI\Microsoft.Terminal.UI.vcxproj", "{6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sample Extensions", "Sample Extensions", "{071E18A4-A530-46B8-AB7D-B862EE55E24E}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProcessMonitorExtension", "src\modules\cmdpal\Exts\ProcessMonitorExtension\ProcessMonitorExtension.csproj", "{C846F7A7-792A-47D9-B0CB-417C900EE03D}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SamplePagesExtension", "src\modules\cmdpal\Exts\SamplePagesExtension\SamplePagesExtension.csproj", "{C831231F-891C-4572-9694-45062534B42A}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UI", "UI", "{7520A2FE-00A2-49B8-83ED-DB216E874C04}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.UI", "src\modules\cmdpal\Microsoft.CmdPal.UI\Microsoft.CmdPal.UI.csproj", "{8FBDABA4-40EE-4C0E-9BC8-2F6444A6EF90}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.UI.ViewModels", "src\modules\cmdpal\Microsoft.CmdPal.UI.ViewModels\Microsoft.CmdPal.UI.ViewModels.csproj", "{C66020D1-CB10-4CF7-8715-84C97FD5E5E2}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.Ext.ClipboardHistory", "src\modules\cmdpal\Exts\Microsoft.CmdPal.Ext.ClipboardHistory\Microsoft.CmdPal.Ext.ClipboardHistory.csproj", "{79775343-7A3D-445D-9104-3DD5B2893DF9}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CmdPalModuleInterface", "src\modules\cmdpal\CmdPalModuleInterface\CmdPalModuleInterface.vcxproj", "{0ADEB797-C8C7-4FFA-ACD5-2AF6CAD7ECD8}"
+EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkspacesCsharpLibrary", "src\modules\Workspaces\WorkspacesCsharpLibrary\WorkspacesCsharpLibrary.csproj", "{89D0E199-B17A-418C-B2F8-7375B6708357}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NewPlus.ShellExtension.win10", "src\modules\NewPlus\NewShellExtensionContextMenu.win10\NewPlus.ShellExtension.win10.vcxproj", "{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.Ext.Indexer", "src\modules\cmdpal\Exts\Microsoft.CmdPal.Ext.Indexer\Microsoft.CmdPal.Ext.Indexer.csproj", "{453CBB73-A3CB-4D0B-8D24-6940B86FE21D}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.Ext.Shell", "src\modules\cmdpal\Exts\Microsoft.CmdPal.Ext.Shell\Microsoft.CmdPal.Ext.Shell.csproj", "{C0CE3B5E-16D3-495D-B335-CA791B660162}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.Ext.WindowWalker", "src\modules\cmdpal\Exts\Microsoft.CmdPal.Ext.WindowWalker\Microsoft.CmdPal.Ext.WindowWalker.csproj", "{3A9A7297-92C4-4F16-B6F9-8D4AB652C86C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.Ext.WebSearch", "src\modules\cmdpal\Exts\Microsoft.CmdPal.Ext.WebSearch\Microsoft.CmdPal.Ext.WebSearch.csproj", "{605E914B-7232-4789-AF46-BF5D3DDFC14E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.Ext.WinGet", "src\modules\cmdpal\Exts\Microsoft.CmdPal.Ext.WinGet\Microsoft.CmdPal.Ext.WinGet.csproj", "{E81A7D20-9862-ABDB-0AAE-9BC5B517A9F9}"
+EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AdvancedPaste.UnitTests", "src\modules\AdvancedPaste\AdvancedPaste.UnitTests\AdvancedPaste.UnitTests.csproj", "{D5E5F5EA-1B6C-4A73-88BE-304F36C9E4EE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AdvancedPaste.FuzzTests", "src\modules\AdvancedPaste\AdvancedPaste.FuzzTests\AdvancedPaste.FuzzTests.csproj", "{7F5B9557-5878-4438-A721-3E28296BA193}"
@@ -636,6 +690,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ZoomItModuleInterface", "sr
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ZoomItSettingsInterop", "src\modules\ZoomIt\ZoomItSettingsInterop\ZoomItSettingsInterop.vcxproj", "{CA7D8106-30B9-4AEC-9D05-B69B31B8C461}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.Ext.TimeDate", "src\modules\cmdpal\Exts\Microsoft.CmdPal.Ext.TimeDate\Microsoft.CmdPal.Ext.TimeDate.csproj", "{DCC6BD67-17BB-47AA-B507-FB0FE43A7449}"
+EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UITestAutomation", "src\common\UITestAutomation\UITestAutomation.csproj", "{A558C25D-2007-498E-8B6F-43405AFAE9E2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeyboardManagerEditorUI", "src\modules\keyboardmanager\KeyboardManagerEditorUI\KeyboardManagerEditorUI.csproj", "{08F9155D-B6DC-46E5-9C83-AF60B655898B}"
@@ -648,6 +704,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hosts.UITests", "src\module
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RegistryPreview.FuzzTests", "src\modules\registrypreview\RegistryPreview.FuzzTests\RegistryPreview.FuzzTests.csproj", "{5702B3CC-8575-48D5-83D8-15BB42269CD3}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.Ext.System", "src\modules\cmdpal\Exts\Microsoft.CmdPal.Ext.System\Microsoft.CmdPal.Ext.System.csproj", "{64B88F02-CD88-4ED8-9624-989A800230F9}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM64 = Debug|ARM64
@@ -2200,6 +2258,154 @@ Global
{66614C26-314C-4B91-9071-76133422CFEF}.Release|ARM64.Build.0 = Release|ARM64
{66614C26-314C-4B91-9071-76133422CFEF}.Release|x64.ActiveCfg = Release|x64
{66614C26-314C-4B91-9071-76133422CFEF}.Release|x64.Build.0 = Release|x64
+ {6CE438DF-C245-4997-A360-0A0939E4BA34}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {6CE438DF-C245-4997-A360-0A0939E4BA34}.Debug|ARM64.Build.0 = Debug|ARM64
+ {6CE438DF-C245-4997-A360-0A0939E4BA34}.Debug|x64.ActiveCfg = Debug|x64
+ {6CE438DF-C245-4997-A360-0A0939E4BA34}.Debug|x64.Build.0 = Debug|x64
+ {6CE438DF-C245-4997-A360-0A0939E4BA34}.Release|ARM64.ActiveCfg = Release|ARM64
+ {6CE438DF-C245-4997-A360-0A0939E4BA34}.Release|ARM64.Build.0 = Release|ARM64
+ {6CE438DF-C245-4997-A360-0A0939E4BA34}.Release|x64.ActiveCfg = Release|x64
+ {6CE438DF-C245-4997-A360-0A0939E4BA34}.Release|x64.Build.0 = Release|x64
+ {E09AA983-C755-474F-83D6-A5CDF528C070}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {E09AA983-C755-474F-83D6-A5CDF528C070}.Debug|ARM64.Build.0 = Debug|ARM64
+ {E09AA983-C755-474F-83D6-A5CDF528C070}.Debug|x64.ActiveCfg = Debug|x64
+ {E09AA983-C755-474F-83D6-A5CDF528C070}.Debug|x64.Build.0 = Debug|x64
+ {E09AA983-C755-474F-83D6-A5CDF528C070}.Release|ARM64.ActiveCfg = Release|ARM64
+ {E09AA983-C755-474F-83D6-A5CDF528C070}.Release|ARM64.Build.0 = Release|ARM64
+ {E09AA983-C755-474F-83D6-A5CDF528C070}.Release|x64.ActiveCfg = Release|x64
+ {E09AA983-C755-474F-83D6-A5CDF528C070}.Release|x64.Build.0 = Release|x64
+ {6D56B64D-FF1F-488F-AFED-9B9854A5D399}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {6D56B64D-FF1F-488F-AFED-9B9854A5D399}.Debug|ARM64.Build.0 = Debug|ARM64
+ {6D56B64D-FF1F-488F-AFED-9B9854A5D399}.Debug|x64.ActiveCfg = Debug|x64
+ {6D56B64D-FF1F-488F-AFED-9B9854A5D399}.Debug|x64.Build.0 = Debug|x64
+ {6D56B64D-FF1F-488F-AFED-9B9854A5D399}.Release|ARM64.ActiveCfg = Release|ARM64
+ {6D56B64D-FF1F-488F-AFED-9B9854A5D399}.Release|ARM64.Build.0 = Release|ARM64
+ {6D56B64D-FF1F-488F-AFED-9B9854A5D399}.Release|x64.ActiveCfg = Release|x64
+ {6D56B64D-FF1F-488F-AFED-9B9854A5D399}.Release|x64.Build.0 = Release|x64
+ {92EC89E4-9972-453A-8A1A-3A9E230C146A}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {92EC89E4-9972-453A-8A1A-3A9E230C146A}.Debug|ARM64.Build.0 = Debug|ARM64
+ {92EC89E4-9972-453A-8A1A-3A9E230C146A}.Debug|x64.ActiveCfg = Debug|x64
+ {92EC89E4-9972-453A-8A1A-3A9E230C146A}.Debug|x64.Build.0 = Debug|x64
+ {92EC89E4-9972-453A-8A1A-3A9E230C146A}.Release|ARM64.ActiveCfg = Release|ARM64
+ {92EC89E4-9972-453A-8A1A-3A9E230C146A}.Release|ARM64.Build.0 = Release|ARM64
+ {92EC89E4-9972-453A-8A1A-3A9E230C146A}.Release|x64.ActiveCfg = Release|x64
+ {92EC89E4-9972-453A-8A1A-3A9E230C146A}.Release|x64.Build.0 = Release|x64
+ {51939B4F-1F62-4BFF-A6A2-C08646E5BE95}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {51939B4F-1F62-4BFF-A6A2-C08646E5BE95}.Debug|ARM64.Build.0 = Debug|ARM64
+ {51939B4F-1F62-4BFF-A6A2-C08646E5BE95}.Debug|x64.ActiveCfg = Debug|x64
+ {51939B4F-1F62-4BFF-A6A2-C08646E5BE95}.Debug|x64.Build.0 = Debug|x64
+ {51939B4F-1F62-4BFF-A6A2-C08646E5BE95}.Release|ARM64.ActiveCfg = Release|ARM64
+ {51939B4F-1F62-4BFF-A6A2-C08646E5BE95}.Release|ARM64.Build.0 = Release|ARM64
+ {51939B4F-1F62-4BFF-A6A2-C08646E5BE95}.Release|x64.ActiveCfg = Release|x64
+ {51939B4F-1F62-4BFF-A6A2-C08646E5BE95}.Release|x64.Build.0 = Release|x64
+ {D1160404-D3D1-497A-883A-4059C07C2273}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {D1160404-D3D1-497A-883A-4059C07C2273}.Debug|ARM64.Build.0 = Debug|ARM64
+ {D1160404-D3D1-497A-883A-4059C07C2273}.Debug|x64.ActiveCfg = Debug|x64
+ {D1160404-D3D1-497A-883A-4059C07C2273}.Debug|x64.Build.0 = Debug|x64
+ {D1160404-D3D1-497A-883A-4059C07C2273}.Release|ARM64.ActiveCfg = Release|ARM64
+ {D1160404-D3D1-497A-883A-4059C07C2273}.Release|ARM64.Build.0 = Release|ARM64
+ {D1160404-D3D1-497A-883A-4059C07C2273}.Release|x64.ActiveCfg = Release|x64
+ {D1160404-D3D1-497A-883A-4059C07C2273}.Release|x64.Build.0 = Release|x64
+ {40F6D69D-E321-400F-A767-5628C7AE453D}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {40F6D69D-E321-400F-A767-5628C7AE453D}.Debug|ARM64.Build.0 = Debug|ARM64
+ {40F6D69D-E321-400F-A767-5628C7AE453D}.Debug|x64.ActiveCfg = Debug|x64
+ {40F6D69D-E321-400F-A767-5628C7AE453D}.Debug|x64.Build.0 = Debug|x64
+ {40F6D69D-E321-400F-A767-5628C7AE453D}.Release|ARM64.ActiveCfg = Release|ARM64
+ {40F6D69D-E321-400F-A767-5628C7AE453D}.Release|ARM64.Build.0 = Release|ARM64
+ {40F6D69D-E321-400F-A767-5628C7AE453D}.Release|x64.ActiveCfg = Release|x64
+ {40F6D69D-E321-400F-A767-5628C7AE453D}.Release|x64.Build.0 = Release|x64
+ {305DD37E-C85D-4B08-AAFE-7381FA890463}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {305DD37E-C85D-4B08-AAFE-7381FA890463}.Debug|ARM64.Build.0 = Debug|ARM64
+ {305DD37E-C85D-4B08-AAFE-7381FA890463}.Debug|x64.ActiveCfg = Debug|x64
+ {305DD37E-C85D-4B08-AAFE-7381FA890463}.Debug|x64.Build.0 = Debug|x64
+ {305DD37E-C85D-4B08-AAFE-7381FA890463}.Release|ARM64.ActiveCfg = Release|ARM64
+ {305DD37E-C85D-4B08-AAFE-7381FA890463}.Release|ARM64.Build.0 = Release|ARM64
+ {305DD37E-C85D-4B08-AAFE-7381FA890463}.Release|x64.ActiveCfg = Release|x64
+ {305DD37E-C85D-4B08-AAFE-7381FA890463}.Release|x64.Build.0 = Release|x64
+ {CA4D810F-C8F4-4B61-9DA9-71807E0B9F24}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {CA4D810F-C8F4-4B61-9DA9-71807E0B9F24}.Debug|ARM64.Build.0 = Debug|ARM64
+ {CA4D810F-C8F4-4B61-9DA9-71807E0B9F24}.Debug|x64.ActiveCfg = Debug|x64
+ {CA4D810F-C8F4-4B61-9DA9-71807E0B9F24}.Debug|x64.Build.0 = Debug|x64
+ {CA4D810F-C8F4-4B61-9DA9-71807E0B9F24}.Release|ARM64.ActiveCfg = Release|ARM64
+ {CA4D810F-C8F4-4B61-9DA9-71807E0B9F24}.Release|ARM64.Build.0 = Release|ARM64
+ {CA4D810F-C8F4-4B61-9DA9-71807E0B9F24}.Release|x64.ActiveCfg = Release|x64
+ {CA4D810F-C8F4-4B61-9DA9-71807E0B9F24}.Release|x64.Build.0 = Release|x64
+ {14E62033-58D0-4A7D-8990-52F50A08BBBD}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {14E62033-58D0-4A7D-8990-52F50A08BBBD}.Debug|ARM64.Build.0 = Debug|ARM64
+ {14E62033-58D0-4A7D-8990-52F50A08BBBD}.Debug|x64.ActiveCfg = Debug|x64
+ {14E62033-58D0-4A7D-8990-52F50A08BBBD}.Debug|x64.Build.0 = Debug|x64
+ {14E62033-58D0-4A7D-8990-52F50A08BBBD}.Release|ARM64.ActiveCfg = Release|ARM64
+ {14E62033-58D0-4A7D-8990-52F50A08BBBD}.Release|ARM64.Build.0 = Release|ARM64
+ {14E62033-58D0-4A7D-8990-52F50A08BBBD}.Release|x64.ActiveCfg = Release|x64
+ {14E62033-58D0-4A7D-8990-52F50A08BBBD}.Release|x64.Build.0 = Release|x64
+ {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Debug|ARM64.Build.0 = Debug|ARM64
+ {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Debug|x64.ActiveCfg = Debug|x64
+ {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Debug|x64.Build.0 = Debug|x64
+ {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Release|ARM64.ActiveCfg = Release|ARM64
+ {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Release|ARM64.Build.0 = Release|ARM64
+ {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Release|x64.ActiveCfg = Release|x64
+ {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Release|x64.Build.0 = Release|x64
+ {C846F7A7-792A-47D9-B0CB-417C900EE03D}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {C846F7A7-792A-47D9-B0CB-417C900EE03D}.Debug|ARM64.Build.0 = Debug|ARM64
+ {C846F7A7-792A-47D9-B0CB-417C900EE03D}.Debug|ARM64.Deploy.0 = Debug|ARM64
+ {C846F7A7-792A-47D9-B0CB-417C900EE03D}.Debug|x64.ActiveCfg = Debug|x64
+ {C846F7A7-792A-47D9-B0CB-417C900EE03D}.Debug|x64.Build.0 = Debug|x64
+ {C846F7A7-792A-47D9-B0CB-417C900EE03D}.Debug|x64.Deploy.0 = Debug|x64
+ {C846F7A7-792A-47D9-B0CB-417C900EE03D}.Release|ARM64.ActiveCfg = Release|ARM64
+ {C846F7A7-792A-47D9-B0CB-417C900EE03D}.Release|ARM64.Build.0 = Release|ARM64
+ {C846F7A7-792A-47D9-B0CB-417C900EE03D}.Release|ARM64.Deploy.0 = Release|ARM64
+ {C846F7A7-792A-47D9-B0CB-417C900EE03D}.Release|x64.ActiveCfg = Release|x64
+ {C846F7A7-792A-47D9-B0CB-417C900EE03D}.Release|x64.Build.0 = Release|x64
+ {C846F7A7-792A-47D9-B0CB-417C900EE03D}.Release|x64.Deploy.0 = Release|x64
+ {C831231F-891C-4572-9694-45062534B42A}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {C831231F-891C-4572-9694-45062534B42A}.Debug|ARM64.Build.0 = Debug|ARM64
+ {C831231F-891C-4572-9694-45062534B42A}.Debug|ARM64.Deploy.0 = Debug|ARM64
+ {C831231F-891C-4572-9694-45062534B42A}.Debug|x64.ActiveCfg = Debug|x64
+ {C831231F-891C-4572-9694-45062534B42A}.Debug|x64.Build.0 = Debug|x64
+ {C831231F-891C-4572-9694-45062534B42A}.Debug|x64.Deploy.0 = Debug|x64
+ {C831231F-891C-4572-9694-45062534B42A}.Release|ARM64.ActiveCfg = Release|ARM64
+ {C831231F-891C-4572-9694-45062534B42A}.Release|ARM64.Build.0 = Release|ARM64
+ {C831231F-891C-4572-9694-45062534B42A}.Release|ARM64.Deploy.0 = Release|ARM64
+ {C831231F-891C-4572-9694-45062534B42A}.Release|x64.ActiveCfg = Release|x64
+ {C831231F-891C-4572-9694-45062534B42A}.Release|x64.Build.0 = Release|x64
+ {C831231F-891C-4572-9694-45062534B42A}.Release|x64.Deploy.0 = Release|x64
+ {8FBDABA4-40EE-4C0E-9BC8-2F6444A6EF90}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {8FBDABA4-40EE-4C0E-9BC8-2F6444A6EF90}.Debug|ARM64.Build.0 = Debug|ARM64
+ {8FBDABA4-40EE-4C0E-9BC8-2F6444A6EF90}.Debug|ARM64.Deploy.0 = Debug|ARM64
+ {8FBDABA4-40EE-4C0E-9BC8-2F6444A6EF90}.Debug|x64.ActiveCfg = Debug|x64
+ {8FBDABA4-40EE-4C0E-9BC8-2F6444A6EF90}.Debug|x64.Build.0 = Debug|x64
+ {8FBDABA4-40EE-4C0E-9BC8-2F6444A6EF90}.Debug|x64.Deploy.0 = Debug|x64
+ {8FBDABA4-40EE-4C0E-9BC8-2F6444A6EF90}.Release|ARM64.ActiveCfg = Release|ARM64
+ {8FBDABA4-40EE-4C0E-9BC8-2F6444A6EF90}.Release|ARM64.Build.0 = Release|ARM64
+ {8FBDABA4-40EE-4C0E-9BC8-2F6444A6EF90}.Release|ARM64.Deploy.0 = Release|ARM64
+ {8FBDABA4-40EE-4C0E-9BC8-2F6444A6EF90}.Release|x64.ActiveCfg = Release|x64
+ {8FBDABA4-40EE-4C0E-9BC8-2F6444A6EF90}.Release|x64.Build.0 = Release|x64
+ {8FBDABA4-40EE-4C0E-9BC8-2F6444A6EF90}.Release|x64.Deploy.0 = Release|x64
+ {C66020D1-CB10-4CF7-8715-84C97FD5E5E2}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {C66020D1-CB10-4CF7-8715-84C97FD5E5E2}.Debug|ARM64.Build.0 = Debug|ARM64
+ {C66020D1-CB10-4CF7-8715-84C97FD5E5E2}.Debug|x64.ActiveCfg = Debug|x64
+ {C66020D1-CB10-4CF7-8715-84C97FD5E5E2}.Debug|x64.Build.0 = Debug|x64
+ {C66020D1-CB10-4CF7-8715-84C97FD5E5E2}.Release|ARM64.ActiveCfg = Release|ARM64
+ {C66020D1-CB10-4CF7-8715-84C97FD5E5E2}.Release|ARM64.Build.0 = Release|ARM64
+ {C66020D1-CB10-4CF7-8715-84C97FD5E5E2}.Release|x64.ActiveCfg = Release|x64
+ {C66020D1-CB10-4CF7-8715-84C97FD5E5E2}.Release|x64.Build.0 = Release|x64
+ {79775343-7A3D-445D-9104-3DD5B2893DF9}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {79775343-7A3D-445D-9104-3DD5B2893DF9}.Debug|ARM64.Build.0 = Debug|ARM64
+ {79775343-7A3D-445D-9104-3DD5B2893DF9}.Debug|x64.ActiveCfg = Debug|x64
+ {79775343-7A3D-445D-9104-3DD5B2893DF9}.Debug|x64.Build.0 = Debug|x64
+ {79775343-7A3D-445D-9104-3DD5B2893DF9}.Release|ARM64.ActiveCfg = Release|ARM64
+ {79775343-7A3D-445D-9104-3DD5B2893DF9}.Release|ARM64.Build.0 = Release|ARM64
+ {79775343-7A3D-445D-9104-3DD5B2893DF9}.Release|x64.ActiveCfg = Release|x64
+ {79775343-7A3D-445D-9104-3DD5B2893DF9}.Release|x64.Build.0 = Release|x64
+ {0ADEB797-C8C7-4FFA-ACD5-2AF6CAD7ECD8}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {0ADEB797-C8C7-4FFA-ACD5-2AF6CAD7ECD8}.Debug|ARM64.Build.0 = Debug|ARM64
+ {0ADEB797-C8C7-4FFA-ACD5-2AF6CAD7ECD8}.Debug|x64.ActiveCfg = Debug|x64
+ {0ADEB797-C8C7-4FFA-ACD5-2AF6CAD7ECD8}.Debug|x64.Build.0 = Debug|x64
+ {0ADEB797-C8C7-4FFA-ACD5-2AF6CAD7ECD8}.Release|ARM64.ActiveCfg = Release|ARM64
+ {0ADEB797-C8C7-4FFA-ACD5-2AF6CAD7ECD8}.Release|ARM64.Build.0 = Release|ARM64
+ {0ADEB797-C8C7-4FFA-ACD5-2AF6CAD7ECD8}.Release|x64.ActiveCfg = Release|x64
+ {0ADEB797-C8C7-4FFA-ACD5-2AF6CAD7ECD8}.Release|x64.Build.0 = Release|x64
{89D0E199-B17A-418C-B2F8-7375B6708357}.Debug|ARM64.ActiveCfg = Debug|ARM64
{89D0E199-B17A-418C-B2F8-7375B6708357}.Debug|ARM64.Build.0 = Debug|ARM64
{89D0E199-B17A-418C-B2F8-7375B6708357}.Debug|x64.ActiveCfg = Debug|x64
@@ -2216,6 +2422,54 @@ Global
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Release|ARM64.Build.0 = Release|ARM64
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Release|x64.ActiveCfg = Release|x64
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Release|x64.Build.0 = Release|x64
+ {453CBB73-A3CB-4D0B-8D24-6940B86FE21D}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {453CBB73-A3CB-4D0B-8D24-6940B86FE21D}.Debug|ARM64.Build.0 = Debug|ARM64
+ {453CBB73-A3CB-4D0B-8D24-6940B86FE21D}.Debug|ARM64.Deploy.0 = Debug|ARM64
+ {453CBB73-A3CB-4D0B-8D24-6940B86FE21D}.Debug|x64.ActiveCfg = Debug|x64
+ {453CBB73-A3CB-4D0B-8D24-6940B86FE21D}.Debug|x64.Build.0 = Debug|x64
+ {453CBB73-A3CB-4D0B-8D24-6940B86FE21D}.Debug|x64.Deploy.0 = Debug|x64
+ {453CBB73-A3CB-4D0B-8D24-6940B86FE21D}.Release|ARM64.ActiveCfg = Release|ARM64
+ {453CBB73-A3CB-4D0B-8D24-6940B86FE21D}.Release|ARM64.Build.0 = Release|ARM64
+ {453CBB73-A3CB-4D0B-8D24-6940B86FE21D}.Release|ARM64.Deploy.0 = Release|ARM64
+ {453CBB73-A3CB-4D0B-8D24-6940B86FE21D}.Release|x64.ActiveCfg = Release|x64
+ {453CBB73-A3CB-4D0B-8D24-6940B86FE21D}.Release|x64.Build.0 = Release|x64
+ {453CBB73-A3CB-4D0B-8D24-6940B86FE21D}.Release|x64.Deploy.0 = Release|x64
+ {C0CE3B5E-16D3-495D-B335-CA791B660162}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {C0CE3B5E-16D3-495D-B335-CA791B660162}.Debug|ARM64.Build.0 = Debug|ARM64
+ {C0CE3B5E-16D3-495D-B335-CA791B660162}.Debug|x64.ActiveCfg = Debug|x64
+ {C0CE3B5E-16D3-495D-B335-CA791B660162}.Debug|x64.Build.0 = Debug|x64
+ {C0CE3B5E-16D3-495D-B335-CA791B660162}.Release|ARM64.ActiveCfg = Release|ARM64
+ {C0CE3B5E-16D3-495D-B335-CA791B660162}.Release|ARM64.Build.0 = Release|ARM64
+ {C0CE3B5E-16D3-495D-B335-CA791B660162}.Release|x64.ActiveCfg = Release|x64
+ {C0CE3B5E-16D3-495D-B335-CA791B660162}.Release|x64.Build.0 = Release|x64
+ {3A9A7297-92C4-4F16-B6F9-8D4AB652C86C}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {3A9A7297-92C4-4F16-B6F9-8D4AB652C86C}.Debug|ARM64.Build.0 = Debug|ARM64
+ {3A9A7297-92C4-4F16-B6F9-8D4AB652C86C}.Debug|x64.ActiveCfg = Debug|x64
+ {3A9A7297-92C4-4F16-B6F9-8D4AB652C86C}.Debug|x64.Build.0 = Debug|x64
+ {3A9A7297-92C4-4F16-B6F9-8D4AB652C86C}.Release|ARM64.ActiveCfg = Release|ARM64
+ {3A9A7297-92C4-4F16-B6F9-8D4AB652C86C}.Release|ARM64.Build.0 = Release|ARM64
+ {3A9A7297-92C4-4F16-B6F9-8D4AB652C86C}.Release|x64.ActiveCfg = Release|x64
+ {3A9A7297-92C4-4F16-B6F9-8D4AB652C86C}.Release|x64.Build.0 = Release|x64
+ {605E914B-7232-4789-AF46-BF5D3DDFC14E}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {605E914B-7232-4789-AF46-BF5D3DDFC14E}.Debug|ARM64.Build.0 = Debug|ARM64
+ {605E914B-7232-4789-AF46-BF5D3DDFC14E}.Debug|x64.ActiveCfg = Debug|x64
+ {605E914B-7232-4789-AF46-BF5D3DDFC14E}.Debug|x64.Build.0 = Debug|x64
+ {605E914B-7232-4789-AF46-BF5D3DDFC14E}.Release|ARM64.ActiveCfg = Release|ARM64
+ {605E914B-7232-4789-AF46-BF5D3DDFC14E}.Release|ARM64.Build.0 = Release|ARM64
+ {605E914B-7232-4789-AF46-BF5D3DDFC14E}.Release|x64.ActiveCfg = Release|x64
+ {605E914B-7232-4789-AF46-BF5D3DDFC14E}.Release|x64.Build.0 = Release|x64
+ {E81A7D20-9862-ABDB-0AAE-9BC5B517A9F9}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {E81A7D20-9862-ABDB-0AAE-9BC5B517A9F9}.Debug|ARM64.Build.0 = Debug|ARM64
+ {E81A7D20-9862-ABDB-0AAE-9BC5B517A9F9}.Debug|ARM64.Deploy.0 = Debug|ARM64
+ {E81A7D20-9862-ABDB-0AAE-9BC5B517A9F9}.Debug|x64.ActiveCfg = Debug|x64
+ {E81A7D20-9862-ABDB-0AAE-9BC5B517A9F9}.Debug|x64.Build.0 = Debug|x64
+ {E81A7D20-9862-ABDB-0AAE-9BC5B517A9F9}.Debug|x64.Deploy.0 = Debug|x64
+ {E81A7D20-9862-ABDB-0AAE-9BC5B517A9F9}.Release|ARM64.ActiveCfg = Release|ARM64
+ {E81A7D20-9862-ABDB-0AAE-9BC5B517A9F9}.Release|ARM64.Build.0 = Release|ARM64
+ {E81A7D20-9862-ABDB-0AAE-9BC5B517A9F9}.Release|ARM64.Deploy.0 = Release|ARM64
+ {E81A7D20-9862-ABDB-0AAE-9BC5B517A9F9}.Release|x64.ActiveCfg = Release|x64
+ {E81A7D20-9862-ABDB-0AAE-9BC5B517A9F9}.Release|x64.Build.0 = Release|x64
+ {E81A7D20-9862-ABDB-0AAE-9BC5B517A9F9}.Release|x64.Deploy.0 = Release|x64
{D5E5F5EA-1B6C-4A73-88BE-304F36C9E4EE}.Debug|ARM64.ActiveCfg = Debug|ARM64
{D5E5F5EA-1B6C-4A73-88BE-304F36C9E4EE}.Debug|ARM64.Build.0 = Debug|ARM64
{D5E5F5EA-1B6C-4A73-88BE-304F36C9E4EE}.Debug|x64.ActiveCfg = Debug|x64
@@ -2256,6 +2510,18 @@ Global
{CA7D8106-30B9-4AEC-9D05-B69B31B8C461}.Release|ARM64.Build.0 = Release|ARM64
{CA7D8106-30B9-4AEC-9D05-B69B31B8C461}.Release|x64.ActiveCfg = Release|x64
{CA7D8106-30B9-4AEC-9D05-B69B31B8C461}.Release|x64.Build.0 = Release|x64
+ {DCC6BD67-17BB-47AA-B507-FB0FE43A7449}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {DCC6BD67-17BB-47AA-B507-FB0FE43A7449}.Debug|ARM64.Build.0 = Debug|ARM64
+ {DCC6BD67-17BB-47AA-B507-FB0FE43A7449}.Debug|ARM64.Deploy.0 = Debug|ARM64
+ {DCC6BD67-17BB-47AA-B507-FB0FE43A7449}.Debug|x64.ActiveCfg = Debug|x64
+ {DCC6BD67-17BB-47AA-B507-FB0FE43A7449}.Debug|x64.Build.0 = Debug|x64
+ {DCC6BD67-17BB-47AA-B507-FB0FE43A7449}.Debug|x64.Deploy.0 = Debug|x64
+ {DCC6BD67-17BB-47AA-B507-FB0FE43A7449}.Release|ARM64.ActiveCfg = Release|ARM64
+ {DCC6BD67-17BB-47AA-B507-FB0FE43A7449}.Release|ARM64.Build.0 = Release|ARM64
+ {DCC6BD67-17BB-47AA-B507-FB0FE43A7449}.Release|ARM64.Deploy.0 = Release|ARM64
+ {DCC6BD67-17BB-47AA-B507-FB0FE43A7449}.Release|x64.ActiveCfg = Release|x64
+ {DCC6BD67-17BB-47AA-B507-FB0FE43A7449}.Release|x64.Build.0 = Release|x64
+ {DCC6BD67-17BB-47AA-B507-FB0FE43A7449}.Release|x64.Deploy.0 = Release|x64
{A558C25D-2007-498E-8B6F-43405AFAE9E2}.Debug|ARM64.ActiveCfg = Debug|ARM64
{A558C25D-2007-498E-8B6F-43405AFAE9E2}.Debug|ARM64.Build.0 = Debug|ARM64
{A558C25D-2007-498E-8B6F-43405AFAE9E2}.Debug|x64.ActiveCfg = Debug|x64
@@ -2304,6 +2570,18 @@ Global
{5702B3CC-8575-48D5-83D8-15BB42269CD3}.Release|ARM64.Build.0 = Release|ARM64
{5702B3CC-8575-48D5-83D8-15BB42269CD3}.Release|x64.ActiveCfg = Release|x64
{5702B3CC-8575-48D5-83D8-15BB42269CD3}.Release|x64.Build.0 = Release|x64
+ {64B88F02-CD88-4ED8-9624-989A800230F9}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {64B88F02-CD88-4ED8-9624-989A800230F9}.Debug|ARM64.Build.0 = Debug|ARM64
+ {64B88F02-CD88-4ED8-9624-989A800230F9}.Debug|x64.ActiveCfg = Debug|x64
+ {64B88F02-CD88-4ED8-9624-989A800230F9}.Debug|x64.Build.0 = Debug|x64
+ {64B88F02-CD88-4ED8-9624-989A800230F9}.Debug|x86.ActiveCfg = Debug|x64
+ {64B88F02-CD88-4ED8-9624-989A800230F9}.Debug|x86.Build.0 = Debug|x64
+ {64B88F02-CD88-4ED8-9624-989A800230F9}.Release|ARM64.ActiveCfg = Release|ARM64
+ {64B88F02-CD88-4ED8-9624-989A800230F9}.Release|ARM64.Build.0 = Release|ARM64
+ {64B88F02-CD88-4ED8-9624-989A800230F9}.Release|x64.ActiveCfg = Release|x64
+ {64B88F02-CD88-4ED8-9624-989A800230F9}.Release|x64.Build.0 = Release|x64
+ {64B88F02-CD88-4ED8-9624-989A800230F9}.Release|x86.ActiveCfg = Release|x64
+ {64B88F02-CD88-4ED8-9624-989A800230F9}.Release|x86.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -2531,20 +2809,49 @@ Global
{37D07516-4185-43A4-924F-3C7A5D95ECF6} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
{8F021B46-362B-485C-BFBA-CCF83E820CBD} = {8F62026A-294B-41C6-8839-87463613F216}
{66614C26-314C-4B91-9071-76133422CFEF} = {B6C42F16-73EB-477E-8B0D-4E6CF6C20AAC}
+ {3846508C-77EB-4034-A702-F8BB263C4F79} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
+ {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2} = {3846508C-77EB-4034-A702-F8BB263C4F79}
+ {6CE438DF-C245-4997-A360-0A0939E4BA34} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
+ {E09AA983-C755-474F-83D6-A5CDF528C070} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
+ {6D56B64D-FF1F-488F-AFED-9B9854A5D399} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
+ {92EC89E4-9972-453A-8A1A-3A9E230C146A} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
+ {51939B4F-1F62-4BFF-A6A2-C08646E5BE95} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
+ {D1160404-D3D1-497A-883A-4059C07C2273} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
+ {40F6D69D-E321-400F-A767-5628C7AE453D} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
+ {F3D09629-59A2-4924-A4B9-D6BFAA2C1B49} = {3846508C-77EB-4034-A702-F8BB263C4F79}
+ {305DD37E-C85D-4B08-AAFE-7381FA890463} = {F3D09629-59A2-4924-A4B9-D6BFAA2C1B49}
+ {CA4D810F-C8F4-4B61-9DA9-71807E0B9F24} = {F3D09629-59A2-4924-A4B9-D6BFAA2C1B49}
+ {14E62033-58D0-4A7D-8990-52F50A08BBBD} = {7520A2FE-00A2-49B8-83ED-DB216E874C04}
+ {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F} = {7520A2FE-00A2-49B8-83ED-DB216E874C04}
+ {071E18A4-A530-46B8-AB7D-B862EE55E24E} = {3846508C-77EB-4034-A702-F8BB263C4F79}
+ {C846F7A7-792A-47D9-B0CB-417C900EE03D} = {071E18A4-A530-46B8-AB7D-B862EE55E24E}
+ {C831231F-891C-4572-9694-45062534B42A} = {071E18A4-A530-46B8-AB7D-B862EE55E24E}
+ {7520A2FE-00A2-49B8-83ED-DB216E874C04} = {3846508C-77EB-4034-A702-F8BB263C4F79}
+ {8FBDABA4-40EE-4C0E-9BC8-2F6444A6EF90} = {7520A2FE-00A2-49B8-83ED-DB216E874C04}
+ {C66020D1-CB10-4CF7-8715-84C97FD5E5E2} = {7520A2FE-00A2-49B8-83ED-DB216E874C04}
+ {79775343-7A3D-445D-9104-3DD5B2893DF9} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
+ {0ADEB797-C8C7-4FFA-ACD5-2AF6CAD7ECD8} = {3846508C-77EB-4034-A702-F8BB263C4F79}
{89D0E199-B17A-418C-B2F8-7375B6708357} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E} = {CA716AE6-FE5C-40AC-BB8F-2C87912687AC}
+ {453CBB73-A3CB-4D0B-8D24-6940B86FE21D} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
+ {C0CE3B5E-16D3-495D-B335-CA791B660162} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
+ {3A9A7297-92C4-4F16-B6F9-8D4AB652C86C} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
+ {605E914B-7232-4789-AF46-BF5D3DDFC14E} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
+ {E81A7D20-9862-ABDB-0AAE-9BC5B517A9F9} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
{D5E5F5EA-1B6C-4A73-88BE-304F36C9E4EE} = {9873BA05-4C41-4819-9283-CF45D795431B}
{7F5B9557-5878-4438-A721-3E28296BA193} = {9873BA05-4C41-4819-9283-CF45D795431B}
{DD6E12FE-5509-4ABC-ACC2-3D6DC98A238C} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
{0A84F764-3A88-44CD-AA96-41BDBD48627B} = {DD6E12FE-5509-4ABC-ACC2-3D6DC98A238C}
{E4585179-2AC1-4D5F-A3FF-CFC5392F694C} = {DD6E12FE-5509-4ABC-ACC2-3D6DC98A238C}
{CA7D8106-30B9-4AEC-9D05-B69B31B8C461} = {DD6E12FE-5509-4ABC-ACC2-3D6DC98A238C}
+ {DCC6BD67-17BB-47AA-B507-FB0FE43A7449} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
{A558C25D-2007-498E-8B6F-43405AFAE9E2} = {1AFB6476-670D-4E80-A464-657E01DFF482}
{08F9155D-B6DC-46E5-9C83-AF60B655898B} = {38BDB927-829B-4C65-9CD9-93FB05D66D65}
{4382A954-179A-4078-92AF-715187DFFF50} = {38BDB927-829B-4C65-9CD9-93FB05D66D65}
{EBED240C-8702-452D-B764-6DB9DA9179AF} = {F05E590D-AD46-42BE-9C25-6A63ADD2E3EA}
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A0} = {F05E590D-AD46-42BE-9C25-6A63ADD2E3EA}
{5702B3CC-8575-48D5-83D8-15BB42269CD3} = {929C1324-22E8-4412-A9A8-80E85F3985A5}
+ {64B88F02-CD88-4ED8-9624-989A800230F9} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}
diff --git a/installer/PowerToysSetup/CmdPal.wxs b/installer/PowerToysSetup/CmdPal.wxs
new file mode 100644
index 0000000000..89a813979b
--- /dev/null
+++ b/installer/PowerToysSetup/CmdPal.wxs
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/installer/PowerToysSetup/PowerToysBootstrapper.wixproj b/installer/PowerToysSetup/PowerToysBootstrapper.wixproj
index 15a2000ca5..b2f5945dc2 100644
--- a/installer/PowerToysSetup/PowerToysBootstrapper.wixproj
+++ b/installer/PowerToysSetup/PowerToysBootstrapper.wixproj
@@ -17,6 +17,12 @@
$(DefineConstants);PerUser=false
+
+ $(DefineConstants);CIBuild=true
+
+
+ $(DefineConstants);CIBuild=false
+
Release
x64
diff --git a/installer/PowerToysSetup/PowerToysInstaller.wixproj b/installer/PowerToysSetup/PowerToysInstaller.wixproj
index 7ce39b82cf..f76d15b73a 100644
--- a/installer/PowerToysSetup/PowerToysInstaller.wixproj
+++ b/installer/PowerToysSetup/PowerToysInstaller.wixproj
@@ -1,9 +1,11 @@
-
+
+
- Version=$(Version);MonacoSRCHarvestPath=$(ProjectDir)..\..\x64\$(Configuration)\Assets\Monaco\monacoSRC
+ Version=$(Version);MonacoSRCHarvestPath=$(ProjectDir)..\..\x64\$(Configuration)\Assets\Monaco\monacoSRC;CmdPalVersion=$(CmdPalVersion)
Release
@@ -104,6 +113,7 @@ call powershell.exe -NonInteractive -executionpolicy Unrestricted -File $(MSBuil
+
@@ -188,4 +198,4 @@ call powershell.exe -NonInteractive -executionpolicy Unrestricted -File $(MSBuil
-
\ No newline at end of file
+
diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs
index 33dc8d0e55..2c99d84044 100644
--- a/installer/PowerToysSetup/Product.wxs
+++ b/installer/PowerToysSetup/Product.wxs
@@ -79,6 +79,10 @@
+
+
+
+
@@ -135,6 +139,7 @@
+
@@ -150,6 +155,9 @@
NOT Installed
+
+ NOT Installed
+
@@ -204,6 +212,10 @@
Property="LaunchPowerToys"
Value="[INSTALLFOLDER]" />
+
+
+
+
diff --git a/installer/PowerToysSetupCustomActions/CustomAction.cpp b/installer/PowerToysSetupCustomActions/CustomAction.cpp
index d0aca611fd..84c86af124 100644
--- a/installer/PowerToysSetupCustomActions/CustomAction.cpp
+++ b/installer/PowerToysSetupCustomActions/CustomAction.cpp
@@ -11,6 +11,7 @@
#include "../../src/common/updating/installer.h"
#include "../../src/common/version/version.h"
#include "../../src/common/Telemetry/EtwTrace/EtwTrace.h"
+#include "../../src/common/utils/package.h"
#include "../../src/common/utils/clean_video_conference.h"
#include
@@ -35,13 +36,13 @@ TRACELOGGING_DEFINE_PROVIDER(
TraceLoggingOptionProjectTelemetry());
const DWORD USERNAME_DOMAIN_LEN = DNLEN + UNLEN + 2; // Domain Name + '\' + User Name + '\0'
-const DWORD USERNAME_LEN = UNLEN + 1; // User Name + '\0'
+const DWORD USERNAME_LEN = UNLEN + 1; // User Name + '\0'
-static const wchar_t* POWERTOYS_EXE_COMPONENT = L"{A2C66D91-3485-4D00-B04D-91844E6B345B}";
-static const wchar_t* POWERTOYS_UPGRADE_CODE = L"{42B84BF7-5FBF-473B-9C8B-049DC16F7708}";
+static const wchar_t *POWERTOYS_EXE_COMPONENT = L"{A2C66D91-3485-4D00-B04D-91844E6B345B}";
+static const wchar_t *POWERTOYS_UPGRADE_CODE = L"{42B84BF7-5FBF-473B-9C8B-049DC16F7708}";
-constexpr inline const wchar_t* DataDiagnosticsRegKey = L"Software\\Classes\\PowerToys";
-constexpr inline const wchar_t* DataDiagnosticsRegValueName = L"AllowDataDiagnostics";
+constexpr inline const wchar_t *DataDiagnosticsRegKey = L"Software\\Classes\\PowerToys";
+constexpr inline const wchar_t *DataDiagnosticsRegValueName = L"AllowDataDiagnostics";
#define TraceLoggingWriteWrapper(provider, eventName, ...) \
if (isDataDiagnosticEnabled()) \
@@ -52,16 +53,16 @@ constexpr inline const wchar_t* DataDiagnosticsRegValueName = L"AllowDataDiagnos
trace.UpdateState(false); \
}
-static Shared::Trace::ETWTrace trace{ L"PowerToys_Installer" };
+static Shared::Trace::ETWTrace trace{L"PowerToys_Installer"};
inline bool isDataDiagnosticEnabled()
{
HKEY key{};
if (RegOpenKeyExW(HKEY_CURRENT_USER,
- DataDiagnosticsRegKey,
- 0,
- KEY_READ,
- &key) != ERROR_SUCCESS)
+ DataDiagnosticsRegKey,
+ 0,
+ KEY_READ,
+ &key) != ERROR_SUCCESS)
{
return false;
}
@@ -86,8 +87,7 @@ inline bool isDataDiagnosticEnabled()
return isDataDiagnosticsEnabled == 1;
}
-
-HRESULT getInstallFolder(MSIHANDLE hInstall, std::wstring& installationDir)
+HRESULT getInstallFolder(MSIHANDLE hInstall, std::wstring &installationDir)
{
DWORD len = 0;
wchar_t _[1];
@@ -116,13 +116,13 @@ BOOL IsLocalSystem()
// open process token
if (!OpenProcessToken(GetCurrentProcess(),
- TOKEN_QUERY,
- &hToken))
+ TOKEN_QUERY,
+ &hToken))
return FALSE;
// retrieve user SID
if (!GetTokenInformation(hToken, TokenUser, pTokenUser,
- sizeof(bTokenUser), &cbTokenUser))
+ sizeof(bTokenUser), &cbTokenUser))
{
CloseHandle(hToken);
return FALSE;
@@ -132,7 +132,7 @@ BOOL IsLocalSystem()
// allocate LocalSystem well-known SID
if (!AllocateAndInitializeSid(&siaNT, 1, SECURITY_LOCAL_SYSTEM_RID,
- 0, 0, 0, 0, 0, 0, 0, &pSystemSid))
+ 0, 0, 0, 0, 0, 0, 0, &pSystemSid))
return FALSE;
// compare the user SID from the token with the LocalSystem SID
@@ -194,7 +194,7 @@ static std::filesystem::path GetUserPowerShellModulesPath()
if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_Documents, 0, NULL, &myDocumentsBlockPtr)))
{
- const std::wstring myDocuments{ myDocumentsBlockPtr };
+ const std::wstring myDocuments{myDocumentsBlockPtr};
CoTaskMemFree(myDocumentsBlockPtr);
return std::filesystem::path(myDocuments) / "PowerShell" / "Modules";
}
@@ -227,10 +227,12 @@ UINT __stdcall LaunchPowerToysCA(MSIHANDLE hInstall)
BOOL isSystemUser = IsLocalSystem();
- if (isSystemUser) {
+ if (isSystemUser)
+ {
- auto action = [&commandLine](HANDLE userToken) {
- STARTUPINFO startupInfo{ .cb = sizeof(STARTUPINFO), .wShowWindow = SW_SHOWNORMAL };
+ auto action = [&commandLine](HANDLE userToken)
+ {
+ STARTUPINFO startupInfo{.cb = sizeof(STARTUPINFO), .wShowWindow = SW_SHOWNORMAL};
PROCESS_INFORMATION processInformation;
PVOID lpEnvironment = NULL;
@@ -269,7 +271,7 @@ UINT __stdcall LaunchPowerToysCA(MSIHANDLE hInstall)
}
else
{
- STARTUPINFO startupInfo{ .cb = sizeof(STARTUPINFO), .wShowWindow = SW_SHOWNORMAL };
+ STARTUPINFO startupInfo{.cb = sizeof(STARTUPINFO), .wShowWindow = SW_SHOWNORMAL};
PROCESS_INFORMATION processInformation;
@@ -313,7 +315,7 @@ UINT __stdcall CheckGPOCA(MSIHANDLE hInstall)
LPWSTR currentScope = nullptr;
hr = WcaGetProperty(L"InstallScope", ¤tScope);
- if (std::wstring{ currentScope } == L"perUser")
+ if (std::wstring{currentScope} == L"perUser")
{
if (powertoys_gpo::getDisablePerUserInstallationValue() == powertoys_gpo::gpo_rule_configured_enabled)
{
@@ -354,7 +356,7 @@ UINT __stdcall ApplyModulesRegistryChangeSetsCA(MSIHANDLE hInstall)
hr = getInstallFolder(hInstall, installationFolder);
ExitOnFailure(hr, "Failed to get installFolder.");
- for (const auto& changeSet : getAllOnByDefaultModulesChangeSets(installationFolder))
+ for (const auto &changeSet : getAllOnByDefaultModulesChangeSets(installationFolder))
{
if (!changeSet.apply())
{
@@ -382,7 +384,7 @@ UINT __stdcall UnApplyModulesRegistryChangeSetsCA(MSIHANDLE hInstall)
ExitOnFailure(hr, "Failed to initialize");
hr = getInstallFolder(hInstall, installationFolder);
ExitOnFailure(hr, "Failed to get installFolder.");
- for (const auto& changeSet : getAllModulesChangeSets(installationFolder))
+ for (const auto &changeSet : getAllModulesChangeSets(installationFolder))
{
changeSet.unApply();
}
@@ -396,8 +398,8 @@ LExit:
return WcaFinalize(er);
}
-const wchar_t* DSC_CONFIGURE_PSD1_NAME = L"Microsoft.PowerToys.Configure.psd1";
-const wchar_t* DSC_CONFIGURE_PSM1_NAME = L"Microsoft.PowerToys.Configure.psm1";
+const wchar_t *DSC_CONFIGURE_PSD1_NAME = L"Microsoft.PowerToys.Configure.psd1";
+const wchar_t *DSC_CONFIGURE_PSM1_NAME = L"Microsoft.PowerToys.Configure.psm1";
UINT __stdcall InstallDSCModuleCA(MSIHANDLE hInstall)
{
@@ -429,7 +431,7 @@ UINT __stdcall InstallDSCModuleCA(MSIHANDLE hInstall)
ExitOnFailure(hr, "Unable to create Powershell modules folder");
}
- for (const auto* filename : { DSC_CONFIGURE_PSD1_NAME, DSC_CONFIGURE_PSM1_NAME })
+ for (const auto *filename : {DSC_CONFIGURE_PSD1_NAME, DSC_CONFIGURE_PSM1_NAME})
{
fs::copy_file(fs::path(installationFolder) / "DSCModules" / filename, modulesPath / filename, fs::copy_options::overwrite_existing, errorCode);
@@ -477,7 +479,7 @@ UINT __stdcall UninstallDSCModuleCA(MSIHANDLE hInstall)
std::error_code errorCode;
- for (const auto* filename : { DSC_CONFIGURE_PSD1_NAME, DSC_CONFIGURE_PSM1_NAME })
+ for (const auto *filename : {DSC_CONFIGURE_PSD1_NAME, DSC_CONFIGURE_PSM1_NAME})
{
fs::remove(versionedModulePath / filename, errorCode);
@@ -488,7 +490,7 @@ UINT __stdcall UninstallDSCModuleCA(MSIHANDLE hInstall)
}
}
- for (const auto* modulePath : { &versionedModulePath, &powerToysModulePath })
+ for (const auto *modulePath : {&versionedModulePath, &powerToysModulePath})
{
fs::remove(*modulePath, errorCode);
@@ -535,7 +537,7 @@ UINT __stdcall InstallEmbeddedMSIXCA(MSIHANDLE hInstall)
using namespace winrt::Windows::Management::Deployment;
using namespace winrt::Windows::Foundation;
- Uri msix_uri{ msix_path.wstring() };
+ Uri msix_uri{msix_path.wstring()};
PackageManager pm;
auto result = pm.AddPackageAsync(msix_uri, nullptr, DeploymentOptions::None).get();
if (!result)
@@ -569,7 +571,7 @@ UINT __stdcall UninstallEmbeddedMSIXCA(MSIHANDLE hInstall)
hr = WcaInitialize(hInstall, "UninstallEmbeddedMSIXCA");
ExitOnFailure(hr, "Failed to initialize");
- for (const auto& p : pm.FindPackagesForUser({}, package_name, publisher))
+ for (const auto &p : pm.FindPackagesForUser({}, package_name, publisher))
{
auto result = pm.RemovePackageAsync(p.Id().FullName()).get();
if (result)
@@ -683,7 +685,6 @@ UINT __stdcall UninstallCommandNotFoundModuleCA(MSIHANDLE hInstall)
command += "-NoProfile -NonInteractive -NoLogo -WindowStyle Hidden -ExecutionPolicy Unrestricted -File \"" + winrt::to_string(installationFolder) + "\\WinUI3Apps\\Assets\\Settings\\Scripts\\DisableModule.ps1" + "\"";
#endif
-
system(command.c_str());
LExit:
@@ -738,10 +739,10 @@ UINT __stdcall RemoveScheduledTasksCA(MSIHANDLE hInstall)
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
- ITaskService* pService = nullptr;
- ITaskFolder* pTaskFolder = nullptr;
- IRegisteredTaskCollection* pTaskCollection = nullptr;
- ITaskFolder* pRootFolder = nullptr;
+ ITaskService *pService = nullptr;
+ ITaskFolder *pTaskFolder = nullptr;
+ IRegisteredTaskCollection *pTaskCollection = nullptr;
+ ITaskFolder *pRootFolder = nullptr;
LONG numTasks = 0;
hr = WcaInitialize(hInstall, "RemoveScheduledTasksCA");
@@ -754,10 +755,10 @@ UINT __stdcall RemoveScheduledTasksCA(MSIHANDLE hInstall)
// ------------------------------------------------------
// Create an instance of the Task Service.
hr = CoCreateInstance(CLSID_TaskScheduler,
- nullptr,
- CLSCTX_INPROC_SERVER,
- IID_ITaskService,
- reinterpret_cast(&pService));
+ nullptr,
+ CLSCTX_INPROC_SERVER,
+ IID_ITaskService,
+ reinterpret_cast(&pService));
ExitOnFailure(hr, "Failed to create an instance of ITaskService: %x", hr);
// Connect to the task service.
@@ -785,7 +786,7 @@ UINT __stdcall RemoveScheduledTasksCA(MSIHANDLE hInstall)
{
// Delete all the tasks found.
// If some tasks can't be deleted, the folder won't be deleted later and the user will still be notified.
- IRegisteredTask* pRegisteredTask = nullptr;
+ IRegisteredTask *pRegisteredTask = nullptr;
hr = pTaskCollection->get_Item(_variant_t(i + 1), &pRegisteredTask);
if (SUCCEEDED(hr))
{
@@ -861,8 +862,7 @@ UINT __stdcall TelemetryLogInstallSuccessCA(MSIHANDLE hInstall)
TraceLoggingWideString(get_product_version().c_str(), "Version"),
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
- TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE)
- );
+ TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
LExit:
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
@@ -1028,7 +1028,7 @@ UINT __stdcall DetectPrevInstallPathCA(MSIHANDLE hInstall)
try
{
- if (auto install_path = GetMsiPackageInstalledPath(std::wstring{ currentScope } == L"perUser"))
+ if (auto install_path = GetMsiPackageInstalledPath(std::wstring{currentScope} == L"perUser"))
{
MsiSetPropertyW(hInstall, L"PREVIOUSINSTALLFOLDER", install_path->data());
}
@@ -1040,6 +1040,47 @@ UINT __stdcall DetectPrevInstallPathCA(MSIHANDLE hInstall)
return WcaFinalize(er);
}
+UINT __stdcall InstallCmdPalPackageCA(MSIHANDLE hInstall)
+{
+ using namespace winrt::Windows::Foundation;
+ using namespace winrt::Windows::Management::Deployment;
+
+ HRESULT hr = S_OK;
+ UINT er = ERROR_SUCCESS;
+ std::wstring installationFolder;
+
+ hr = WcaInitialize(hInstall, "InstallCmdPalPackage");
+ hr = getInstallFolder(hInstall, installationFolder);
+
+ try
+ {
+ auto msix = package::FindMsixFile(installationFolder + L"\\WinUI3Apps\\CmdPal\\", false);
+ auto dependencies = package::FindMsixFile(installationFolder + L"\\WinUI3Apps\\CmdPal\\Dependencies\\", true);
+
+ if (!msix.empty())
+ {
+ auto msixPath = msix[0];
+
+ if (!package::RegisterPackage(msixPath, dependencies))
+ {
+ Logger::error(L"Failed to install CmdPal package");
+ er = ERROR_INSTALL_FAILURE;
+ }
+ }
+ }
+ catch (std::exception &e)
+ {
+ std::string errorMessage{"Exception thrown while trying to install CmdPal package: "};
+ errorMessage += e.what();
+ Logger::error(errorMessage);
+
+ er = ERROR_INSTALL_FAILURE;
+ }
+
+ er = er == ERROR_SUCCESS ? (SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE) : er;
+ return WcaFinalize(er);
+}
+
UINT __stdcall UnRegisterContextMenuPackagesCA(MSIHANDLE hInstall)
{
using namespace winrt::Windows::Foundation;
@@ -1053,54 +1094,20 @@ UINT __stdcall UnRegisterContextMenuPackagesCA(MSIHANDLE hInstall)
try
{
// Packages to unregister
- const std::vector packagesToRemoveDisplayName{ { L"PowerRenameContextMenu" }, { L"ImageResizerContextMenu" }, { L"FileLocksmithContextMenu" }, { L"NewPlusContextMenu" } };
+ const std::vector packagesToRemoveDisplayName{{L"PowerRenameContextMenu"}, {L"ImageResizerContextMenu"}, {L"FileLocksmithContextMenu"}, {L"NewPlusContextMenu"}, {L"Microsoft.CommandPalette"}};
- PackageManager packageManager;
-
- for (auto const& package : packageManager.FindPackages())
+ for (auto const &package : packagesToRemoveDisplayName)
{
- const auto& packageFullName = std::wstring{ package.Id().FullName() };
-
- for (const auto& packageToRemove : packagesToRemoveDisplayName)
+ if (!package::UnRegisterPackage(package))
{
- if (packageFullName.contains(packageToRemove))
- {
- auto deploymentOperation{ packageManager.RemovePackageAsync(packageFullName) };
- deploymentOperation.get();
-
- // Check the status of the operation
- if (deploymentOperation.Status() == AsyncStatus::Error)
- {
- auto deploymentResult{ deploymentOperation.GetResults() };
- auto errorCode = deploymentOperation.ErrorCode();
- auto errorText = deploymentResult.ErrorText();
-
- Logger::error(L"Unregister {} package failed. ErrorCode: {}, ErrorText: {}", packageFullName, std::to_wstring(errorCode), errorText);
-
- er = ERROR_INSTALL_FAILURE;
- }
- else if (deploymentOperation.Status() == AsyncStatus::Canceled)
- {
- Logger::error(L"Unregister {} package canceled.", packageFullName);
-
- er = ERROR_INSTALL_FAILURE;
- }
- else if (deploymentOperation.Status() == AsyncStatus::Completed)
- {
- Logger::info(L"Unregister {} package completed.", packageFullName);
- }
- else
- {
- Logger::debug(L"Unregister {} package started.", packageFullName);
- }
- }
-
+ Logger::error(L"Failed to unregister package: " + package);
+ er = ERROR_INSTALL_FAILURE;
}
}
}
- catch (std::exception& e)
+ catch (std::exception &e)
{
- std::string errorMessage{ "Exception thrown while trying to unregister sparse packages: " };
+ std::string errorMessage{"Exception thrown while trying to unregister sparse packages: "};
errorMessage += e.what();
Logger::error(errorMessage);
@@ -1128,7 +1135,7 @@ UINT __stdcall TerminateProcessesCA(MSIHANDLE hInstall)
}
processes.resize(bytes / sizeof(processes[0]));
- std::array processesToTerminate = {
+ std::array processesToTerminate = {
L"PowerToys.PowerLauncher.exe",
L"PowerToys.Settings.exe",
L"PowerToys.AdvancedPaste.exe",
@@ -1165,6 +1172,7 @@ UINT __stdcall TerminateProcessesCA(MSIHANDLE hInstall)
L"PowerToys.WorkspacesLauncherUI.exe",
L"PowerToys.WorkspacesEditor.exe",
L"PowerToys.WorkspacesWindowArranger.exe",
+ L"Microsoft.CmdPal.UI.exe",
L"PowerToys.ZoomIt.exe",
L"PowerToys.exe",
};
@@ -1177,7 +1185,7 @@ UINT __stdcall TerminateProcessesCA(MSIHANDLE hInstall)
}
wchar_t processName[MAX_PATH] = L"";
- HANDLE hProcess{ OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_TERMINATE, FALSE, procID) };
+ HANDLE hProcess{OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_TERMINATE, FALSE, procID)};
if (!hProcess)
{
continue;
@@ -1197,8 +1205,9 @@ UINT __stdcall TerminateProcessesCA(MSIHANDLE hInstall)
if (processName == processToTerminate)
{
const DWORD timeout = 500;
- auto windowEnumerator = [](HWND hwnd, LPARAM procIDPtr) -> BOOL {
- auto targetProcID = *reinterpret_cast(procIDPtr);
+ auto windowEnumerator = [](HWND hwnd, LPARAM procIDPtr) -> BOOL
+ {
+ auto targetProcID = *reinterpret_cast(procIDPtr);
DWORD windowProcID = 0;
GetWindowThreadProcessId(hwnd, &windowProcID);
if (windowProcID == targetProcID)
@@ -1224,15 +1233,15 @@ UINT __stdcall TerminateProcessesCA(MSIHANDLE hInstall)
void initSystemLogger()
{
static std::once_flag initLoggerFlag;
- std::call_once(initLoggerFlag, []() {
- WCHAR temp_path[MAX_PATH];
- auto ret = GetTempPath(MAX_PATH, temp_path);
+ std::call_once(initLoggerFlag, []()
+ {
+ WCHAR temp_path[MAX_PATH];
+ auto ret = GetTempPath(MAX_PATH, temp_path);
- if (ret)
- {
- Logger::init("PowerToysMSI", std::wstring{ temp_path } + L"\\PowerToysMSIInstaller", L"");
- }
- });
+ if (ret)
+ {
+ Logger::init("PowerToysMSI", std::wstring{ temp_path } + L"\\PowerToysMSIInstaller", L"");
+ } });
}
// DllMain - Initialize and cleanup WiX custom action utils.
diff --git a/installer/PowerToysSetupCustomActions/CustomAction.def b/installer/PowerToysSetupCustomActions/CustomAction.def
index d9ed0d0f04..e91060b764 100644
--- a/installer/PowerToysSetupCustomActions/CustomAction.def
+++ b/installer/PowerToysSetupCustomActions/CustomAction.def
@@ -18,6 +18,7 @@ EXPORTS
TerminateProcessesCA
InstallEmbeddedMSIXCA
InstallDSCModuleCA
+ InstallCmdPalPackageCA
UnApplyModulesRegistryChangeSetsCA
UnRegisterContextMenuPackagesCA
UninstallEmbeddedMSIXCA
diff --git a/installer/PowerToysSetupCustomActions/PowerToysSetupCustomActions.vcxproj b/installer/PowerToysSetupCustomActions/PowerToysSetupCustomActions.vcxproj
index e2de4a4065..09ed1ee31a 100644
--- a/installer/PowerToysSetupCustomActions/PowerToysSetupCustomActions.vcxproj
+++ b/installer/PowerToysSetupCustomActions/PowerToysSetupCustomActions.vcxproj
@@ -1,5 +1,6 @@
-
+
@@ -54,6 +55,7 @@
call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\AdvancedPaste.wxs"" ""$(ProjectDir)..\PowerToysSetup\AdvancedPaste.wxs.bk""""
call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\Awake.wxs"" ""$(ProjectDir)..\PowerToysSetup\Awake.wxs.bk""""
call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\BaseApplications.wxs"" ""$(ProjectDir)..\PowerToysSetup\BaseApplications.wxs.bk""""
+ call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\CmdPal.wxs"" ""$(ProjectDir)..\PowerToysSetup\CmdPal.wxs.bk""""
call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\ColorPicker.wxs"" ""$(ProjectDir)..\PowerToysSetup\ColorPicker.wxs.bk""""
call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\Core.wxs"" ""$(ProjectDir)..\PowerToysSetup\Core.wxs.bk""""
call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\EnvironmentVariables.wxs"" ""$(ProjectDir)..\PowerToysSetup\EnvironmentVariables.wxs.bk""""
diff --git a/src/CmdPalVersion.props b/src/CmdPalVersion.props
new file mode 100644
index 0000000000..e9e0e98130
--- /dev/null
+++ b/src/CmdPalVersion.props
@@ -0,0 +1,10 @@
+
+
+
+ 0.0.1
+ Local
+
+
+ SHA256
+
+
diff --git a/src/Common.Dotnet.AotCompatibility.props b/src/Common.Dotnet.AotCompatibility.props
index 9c9b3faa25..71c490fd6c 100644
--- a/src/Common.Dotnet.AotCompatibility.props
+++ b/src/Common.Dotnet.AotCompatibility.props
@@ -5,5 +5,8 @@
true
true
2
+
+
+ IL2081
diff --git a/src/Common.Dotnet.CsWinRT.props b/src/Common.Dotnet.CsWinRT.props
index f3c8c25ecc..e4731ce2fd 100644
--- a/src/Common.Dotnet.CsWinRT.props
+++ b/src/Common.Dotnet.CsWinRT.props
@@ -14,7 +14,7 @@
4
True
- CA1720;CA1859;CA2263;CA2022
+ CA1720;CA1859;CA2263;CA2022;MVVMTK0045;MVVMTK0049
diff --git a/src/codeAnalysis/GlobalSuppressions.cs b/src/codeAnalysis/GlobalSuppressions.cs
index 64e1ab9b16..c05e5f8820 100644
--- a/src/codeAnalysis/GlobalSuppressions.cs
+++ b/src/codeAnalysis/GlobalSuppressions.cs
@@ -12,10 +12,10 @@ using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1101:PrefixLocalCallsWithThis", Justification = "We follow the C# Core Coding Style which avoids using `this` unless absolutely necessary.")]
-[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1201:ElementsMustAppearInTheCorrectOrder", Justification = "It is not a priority and have hight impact in code changes.")]
-[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:ElementsMustBeOrderedByAccess", Justification = "It is not a priority and have hight impact in code changes.")]
-[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1203:ConstantsMustAppearBeforeFields", Justification = "It is not a priority and have hight impact in code changes.")]
-[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1204:StaticElementsMustAppearBeforeInstanceElements", Justification = "It is not a priority and have hight impact in code changes.")]
+[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1201:ElementsMustAppearInTheCorrectOrder", Justification = "It is not a priority and has high impact in code changes.")]
+[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:ElementsMustBeOrderedByAccess", Justification = "It is not a priority and has high impact in code changes.")]
+[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1203:ConstantsMustAppearBeforeFields", Justification = "It is not a priority and has high impact in code changes.")]
+[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1204:StaticElementsMustAppearBeforeInstanceElements", Justification = "It is not a priority and has high impact in code changes.")]
[assembly: SuppressMessage("StyleCop.CSharp.NamingRules", "SA1309:FieldNamesMustNotBeginWithUnderscore", Justification = "We follow the C# Core Coding Style which uses underscores as prefixes rather than using `this.`.")]
@@ -62,3 +62,9 @@ using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "", Scope = "namespaceanddescendants", Target = "MouseWithoutBorders")]
[assembly: SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "", Scope = "namespaceanddescendants", Target = "MouseWithoutBorders")]
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1649:File name should match first type name", Justification = "", Scope = "namespaceanddescendants", Target = "MouseWithoutBorders")]
+
+// AOT
+[assembly: SuppressMessage("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "MVVMTK0045:Using [ObservableProperty] on fields is not AOT compatible for WinRT", Justification = "Updated MVVM toolkit package introduced this.", Scope = "namespaceanddescendants", Target = "HostsUILib")]
+[assembly: SuppressMessage("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "MVVMTK0045:Using [ObservableProperty] on fields is not AOT compatible for WinRT", Justification = "Updated MVVM toolkit package introduced this.", Scope = "namespaceanddescendants", Target = "Peek.UI")]
+[assembly: SuppressMessage("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "MVVMTK0045:Using [ObservableProperty] on fields is not AOT compatible for WinRT", Justification = "Updated MVVM toolkit package introduced this.", Scope = "namespaceanddescendants", Target = "Peek.UI.Views")]
+[assembly: SuppressMessage("CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator", "MVVMTK0049:Using [INotifyPropertyChanged] is not AOT compatible for WinRT", Justification = "Updated MVVM toolkit package introduced this.", Scope = "type", Target = "~T:Peek.UI.Views.TitleBar")]
diff --git a/src/common/Common.UI/SettingsDeepLink.cs b/src/common/Common.UI/SettingsDeepLink.cs
index bb26fa71e6..d247e726a1 100644
--- a/src/common/Common.UI/SettingsDeepLink.cs
+++ b/src/common/Common.UI/SettingsDeepLink.cs
@@ -31,6 +31,7 @@ namespace Common.UI
Dashboard,
AdvancedPaste,
Workspaces,
+ CmdPal,
ZoomIt,
}
@@ -78,6 +79,8 @@ namespace Common.UI
return "AdvancedPaste";
case SettingsWindow.Workspaces:
return "Workspaces";
+ case SettingsWindow.CmdPal:
+ return "CmdPal";
case SettingsWindow.ZoomIt:
return "ZoomIt";
default:
diff --git a/src/common/GPOWrapper/GPOWrapper.cpp b/src/common/GPOWrapper/GPOWrapper.cpp
index 691a66b43f..62b5b49a9d 100644
--- a/src/common/GPOWrapper/GPOWrapper.cpp
+++ b/src/common/GPOWrapper/GPOWrapper.cpp
@@ -16,6 +16,10 @@ namespace winrt::PowerToys::GPOWrapper::implementation
{
return static_cast(powertoys_gpo::getConfiguredCmdNotFoundEnabledValue());
}
+ GpoRuleConfigured GPOWrapper::GetConfiguredCmdPalEnabledValue()
+ {
+ return static_cast(powertoys_gpo::getConfiguredCmdPalEnabledValue());
+ }
GpoRuleConfigured GPOWrapper::GetConfiguredColorPickerEnabledValue()
{
return static_cast(powertoys_gpo::getConfiguredColorPickerEnabledValue());
diff --git a/src/common/GPOWrapper/GPOWrapper.h b/src/common/GPOWrapper/GPOWrapper.h
index f3eb07680b..0d7783883b 100644
--- a/src/common/GPOWrapper/GPOWrapper.h
+++ b/src/common/GPOWrapper/GPOWrapper.h
@@ -10,6 +10,7 @@ namespace winrt::PowerToys::GPOWrapper::implementation
static GpoRuleConfigured GetConfiguredAlwaysOnTopEnabledValue();
static GpoRuleConfigured GetConfiguredAwakeEnabledValue();
static GpoRuleConfigured GetConfiguredCmdNotFoundEnabledValue();
+ static GpoRuleConfigured GetConfiguredCmdPalEnabledValue();
static GpoRuleConfigured GetConfiguredColorPickerEnabledValue();
static GpoRuleConfigured GetConfiguredCropAndLockEnabledValue();
static GpoRuleConfigured GetConfiguredFancyZonesEnabledValue();
diff --git a/src/common/GPOWrapper/GPOWrapper.idl b/src/common/GPOWrapper/GPOWrapper.idl
index b7aa8e22aa..1e3c3a19f5 100644
--- a/src/common/GPOWrapper/GPOWrapper.idl
+++ b/src/common/GPOWrapper/GPOWrapper.idl
@@ -14,6 +14,7 @@ namespace PowerToys
static GpoRuleConfigured GetConfiguredAlwaysOnTopEnabledValue();
static GpoRuleConfigured GetConfiguredAwakeEnabledValue();
static GpoRuleConfigured GetConfiguredCmdNotFoundEnabledValue();
+ static GpoRuleConfigured GetConfiguredCmdPalEnabledValue();
static GpoRuleConfigured GetConfiguredColorPickerEnabledValue();
static GpoRuleConfigured GetConfiguredCropAndLockEnabledValue();
static GpoRuleConfigured GetConfiguredFancyZonesEnabledValue();
diff --git a/src/common/ManagedCommon/ModuleType.cs b/src/common/ManagedCommon/ModuleType.cs
index 5b95af43d8..65b00d4b5a 100644
--- a/src/common/ManagedCommon/ModuleType.cs
+++ b/src/common/ManagedCommon/ModuleType.cs
@@ -10,6 +10,7 @@ namespace ManagedCommon
AlwaysOnTop,
Awake,
ColorPicker,
+ CmdPal,
CropAndLock,
EnvironmentVariables,
FancyZones,
diff --git a/src/common/interop/Constants.cpp b/src/common/interop/Constants.cpp
index 144fb728ce..62e6425f6f 100644
--- a/src/common/interop/Constants.cpp
+++ b/src/common/interop/Constants.cpp
@@ -187,4 +187,8 @@ namespace winrt::PowerToys::Interop::implementation
{
return CommonSharedConstants::TERMINATE_SETTINGS_SHARED_EVENT;
}
+ hstring Constants::ShowCmdPalEvent()
+ {
+ return CommonSharedConstants::CMDPAL_SHOW_EVENT;
+ }
}
diff --git a/src/common/interop/Constants.h b/src/common/interop/Constants.h
index b2a5fdef53..1b3a0f556c 100644
--- a/src/common/interop/Constants.h
+++ b/src/common/interop/Constants.h
@@ -50,6 +50,7 @@ namespace winrt::PowerToys::Interop::implementation
static hstring WorkspacesLaunchEditorEvent();
static hstring WorkspacesHotkeyEvent();
static hstring PowerToysRunnerTerminateSettingsEvent();
+ static hstring ShowCmdPalEvent();
};
}
diff --git a/src/common/interop/Constants.idl b/src/common/interop/Constants.idl
index e2a356d5ef..1de4b849ab 100644
--- a/src/common/interop/Constants.idl
+++ b/src/common/interop/Constants.idl
@@ -47,6 +47,7 @@ namespace PowerToys
static String WorkspacesLaunchEditorEvent();
static String WorkspacesHotkeyEvent();
static String PowerToysRunnerTerminateSettingsEvent();
+ static String ShowCmdPalEvent();
}
}
}
\ No newline at end of file
diff --git a/src/common/interop/shared_constants.h b/src/common/interop/shared_constants.h
index 1c4808ad6a..6853973806 100644
--- a/src/common/interop/shared_constants.h
+++ b/src/common/interop/shared_constants.h
@@ -128,6 +128,9 @@ namespace CommonSharedConstants
const wchar_t ZOOMIT_REFRESH_SETTINGS_EVENT[] = L"Local\\PowerToysZoomIt-RefreshSettingsEvent-f053a563-d519-4b0d-8152-a54489c13324";
const wchar_t ZOOMIT_EXIT_EVENT[] = L"Local\\PowerToysZoomIt-ExitEvent-36641ce6-df02-4eac-abea-a3fbf9138220";
+ // used from quick access window
+ const wchar_t CMDPAL_SHOW_EVENT[] = L"Local\\PowerToysCmdPal-ShowEvent-62336fcd-8611-4023-9b30-091a6af4cc5a";
+
// Max DWORD for key code to disable keys.
const DWORD VK_DISABLED = 0x100;
}
diff --git a/src/common/utils/gpo.h b/src/common/utils/gpo.h
index 479fb0ef23..5d79fa3b01 100644
--- a/src/common/utils/gpo.h
+++ b/src/common/utils/gpo.h
@@ -4,8 +4,10 @@
#include
#include
-namespace powertoys_gpo {
- enum gpo_rule_configured_t {
+namespace powertoys_gpo
+{
+ enum gpo_rule_configured_t
+ {
gpo_rule_configured_wrong_value = -3, // The policy is set to an unrecognized value
gpo_rule_configured_unavailable = -2, // Couldn't access registry
gpo_rule_configured_not_configured = -1, // Policy is not configured
@@ -53,6 +55,7 @@ namespace powertoys_gpo {
const std::wstring POLICY_CONFIGURE_ENABLED_SHORTCUT_GUIDE = L"ConfigureEnabledUtilityShortcutGuide";
const std::wstring POLICY_CONFIGURE_ENABLED_TEXT_EXTRACTOR = L"ConfigureEnabledUtilityTextExtractor";
const std::wstring POLICY_CONFIGURE_ENABLED_ADVANCED_PASTE = L"ConfigureEnabledUtilityAdvancedPaste";
+ const std::wstring POLICY_CONFIGURE_ENABLED_CMD_PAL = L"ConfigureEnabledUtilityCmdPal";
const std::wstring POLICY_CONFIGURE_ENABLED_ZOOM_IT = L"ConfigureEnabledUtilityZoomIt";
const std::wstring POLICY_CONFIGURE_ENABLED_REGISTRY_PREVIEW = L"ConfigureEnabledUtilityRegistryPreview";
const std::wstring POLICY_CONFIGURE_ENABLED_MOUSE_WITHOUT_BORDERS = L"ConfigureEnabledUtilityMouseWithoutBorders";
@@ -157,16 +160,17 @@ namespace powertoys_gpo {
machine_key_found = false;
}
- if(machine_key_found)
+ if (machine_key_found)
{
// If the path was found in the machine, we need to check if the value for the policy exists.
auto res = RegQueryValueExW(key, registry_value_name.c_str(), nullptr, nullptr, reinterpret_cast(&value), &valueSize);
RegCloseKey(key);
- if (res != ERROR_SUCCESS) {
+ if (res != ERROR_SUCCESS)
+ {
// Value not found on the path.
- machine_key_found=false;
+ machine_key_found = false;
}
}
@@ -175,7 +179,8 @@ namespace powertoys_gpo {
// If there's no value found on the machine scope, try to get it from the user scope.
if (auto res = RegOpenKeyExW(POLICIES_SCOPE_USER, POLICIES_PATH.c_str(), 0, KEY_READ, &key); res != ERROR_SUCCESS)
{
- if (res == ERROR_FILE_NOT_FOUND) {
+ if (res == ERROR_FILE_NOT_FOUND)
+ {
return gpo_rule_configured_not_configured;
}
return gpo_rule_configured_unavailable;
@@ -183,7 +188,8 @@ namespace powertoys_gpo {
auto res = RegQueryValueExW(key, registry_value_name.c_str(), nullptr, nullptr, reinterpret_cast(&value), &valueSize);
RegCloseKey(key);
- if (res != ERROR_SUCCESS) {
+ if (res != ERROR_SUCCESS)
+ {
return gpo_rule_configured_not_configured;
}
}
@@ -412,6 +418,11 @@ namespace powertoys_gpo {
return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_ADVANCED_PASTE);
}
+ inline gpo_rule_configured_t getConfiguredCmdPalEnabledValue()
+ {
+ return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_CMD_PAL);
+ }
+
inline gpo_rule_configured_t getConfiguredWorkspacesEnabledValue()
{
return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_WORKSPACES);
@@ -502,7 +513,7 @@ namespace powertoys_gpo {
}
inline gpo_rule_configured_t getRunPluginEnabledValue(std::string pluginID)
- {
+ {
if (pluginID == "" || pluginID == " ")
{
// this plugin id can't exist in the registry
@@ -511,7 +522,7 @@ namespace powertoys_gpo {
std::wstring plugin_id(pluginID.begin(), pluginID.end());
auto individual_plugin_setting = getPolicyListValue(POWER_LAUNCHER_INDIVIDUAL_PLUGIN_ENABLED_LIST_PATH, plugin_id);
-
+
if (individual_plugin_setting.has_value())
{
if (*individual_plugin_setting == L"0")
@@ -538,7 +549,7 @@ namespace powertoys_gpo {
{
// If no individual plugin policy exists, we check the policy with the setting for all plugins.
return getConfiguredValue(POLICY_CONFIGURE_ENABLED_POWER_LAUNCHER_ALL_PLUGINS);
- }
+ }
}
inline gpo_rule_configured_t getAllowedAdvancedPasteOnlineAIModelsValue()
@@ -602,7 +613,7 @@ namespace powertoys_gpo {
}
else
{
- return std::wstring ();
+ return std::wstring();
}
}
diff --git a/src/common/utils/package.h b/src/common/utils/package.h
index 7ca6ac37aa..4ca9bafd25 100644
--- a/src/common/utils/package.h
+++ b/src/common/utils/package.h
@@ -3,7 +3,10 @@
#include
#include
+#include
+#include
#include
+#include
#include
#include
@@ -13,6 +16,11 @@
#include "../version/version.h"
namespace package {
+
+ using namespace winrt::Windows::Foundation;
+ using namespace winrt::Windows::ApplicationModel;
+ using namespace winrt::Windows::Management::Deployment;
+
inline BOOL IsWin11OrGreater()
{
OSVERSIONINFOEX osvi{};
@@ -38,35 +46,35 @@ namespace package {
dwlConditionMask);
}
- inline bool IsPackageRegistered(std::wstring packageDisplayName)
+ inline std::optional GetRegisteredPackage(std::wstring packageDisplayName, bool checkVersion)
{
- using namespace winrt::Windows::Foundation;
- using namespace winrt::Windows::Management::Deployment;
-
PackageManager packageManager;
- for (auto const& package : packageManager.FindPackagesForUser({}))
+ for (const auto& package : packageManager.FindPackagesForUser({}))
{
const auto& packageFullName = std::wstring{ package.Id().FullName() };
const auto& packageVersion = package.Id().Version();
if (packageFullName.contains(packageDisplayName))
{
- if (packageVersion.Major == VERSION_MAJOR && packageVersion.Minor == VERSION_MINOR && packageVersion.Revision == VERSION_REVISION)
+ // If checkVersion is true, verify if the package has the same version as PowerToys.
+ if ((!checkVersion) || (packageVersion.Major == VERSION_MAJOR && packageVersion.Minor == VERSION_MINOR && packageVersion.Revision == VERSION_REVISION))
{
- return true;
+ return { package };
}
}
}
- return false;
+ return {};
}
- inline bool RegisterSparsePackage(std::wstring externalLocation, std::wstring sparsePkgPath)
+ inline bool IsPackageRegisteredWithPowerToysVersion(std::wstring packageDisplayName)
{
- using namespace winrt::Windows::Foundation;
- using namespace winrt::Windows::Management::Deployment;
+ return GetRegisteredPackage(packageDisplayName, true).has_value();
+ }
+ inline bool RegisterSparsePackage(const std::wstring& externalLocation, const std::wstring& sparsePkgPath)
+ {
try
{
Uri externalUri{ externalLocation };
@@ -115,4 +123,174 @@ namespace package {
return false;
}
}
+
+ inline bool UnRegisterPackage(const std::wstring& pkgDisplayName)
+ {
+ try
+ {
+ PackageManager packageManager;
+ const static auto packages = packageManager.FindPackages();
+
+ for (auto const& package : packages)
+ {
+ const auto& packageFullName = std::wstring{ package.Id().FullName() };
+
+ if (packageFullName.contains(pkgDisplayName))
+ {
+ auto deploymentOperation{ packageManager.RemovePackageAsync(packageFullName) };
+ deploymentOperation.get();
+
+ // Check the status of the operation
+ if (deploymentOperation.Status() == AsyncStatus::Error)
+ {
+ auto deploymentResult{ deploymentOperation.GetResults() };
+ auto errorCode = deploymentOperation.ErrorCode();
+ auto errorText = deploymentResult.ErrorText();
+
+ Logger::error(L"Unregister {} package failed. ErrorCode: {}, ErrorText: {}", packageFullName, std::to_wstring(errorCode), errorText);
+ }
+ else if (deploymentOperation.Status() == AsyncStatus::Canceled)
+ {
+ Logger::error(L"Unregister {} package canceled.", packageFullName);
+ }
+ else if (deploymentOperation.Status() == AsyncStatus::Completed)
+ {
+ Logger::info(L"Unregister {} package completed.", packageFullName);
+ }
+ else
+ {
+ Logger::debug(L"Unregister {} package started.", packageFullName);
+ }
+
+ break;
+ }
+ }
+ }
+ catch (std::exception& e)
+ {
+ Logger::error("Exception thrown while trying to unregister package: {}", e.what());
+ return false;
+ }
+
+ return true;
+ }
+
+ inline std::vector FindMsixFile(const std::wstring& directoryPath, bool recursive)
+ {
+ if (directoryPath.empty())
+ {
+ return {};
+ }
+
+ if (!std::filesystem::exists(directoryPath))
+ {
+ Logger::error(L"The directory '" + directoryPath + L"' does not exist.");
+ }
+
+ const std::regex pattern(R"(^.+\.(appx|msix|msixbundle)$)", std::regex_constants::icase);
+ std::vector matchedFiles;
+
+ try
+ {
+ if (recursive)
+ {
+ for (const auto& entry : std::filesystem::recursive_directory_iterator(directoryPath))
+ {
+ if (entry.is_regular_file())
+ {
+ const auto& fileName = entry.path().filename().string();
+ if (std::regex_match(fileName, pattern))
+ {
+ matchedFiles.push_back(entry.path());
+ }
+ }
+ }
+ }
+ else
+ {
+ for (const auto& entry : std::filesystem::directory_iterator(directoryPath))
+ {
+ if (entry.is_regular_file())
+ {
+ const auto& fileName = entry.path().filename().string();
+ if (std::regex_match(fileName, pattern))
+ {
+ matchedFiles.push_back(entry.path());
+ }
+ }
+ }
+ }
+ }
+ catch (const std::exception& ex)
+ {
+ Logger::error("An error occurred while searching for MSIX files: " + std::string(ex.what()));
+ }
+
+ return matchedFiles;
+ }
+
+ inline bool RegisterPackage(std::wstring pkgPath, std::vector dependencies)
+ {
+ try
+ {
+ Uri packageUri{ pkgPath };
+
+ PackageManager packageManager;
+
+ // Declare use of an external location
+ DeploymentOptions options = DeploymentOptions::ForceApplicationShutdown;
+
+ Collections::IVector uris = winrt::single_threaded_vector();
+ if (!dependencies.empty())
+ {
+ for (const auto& dependency : dependencies)
+ {
+ try
+ {
+ uris.Append(Uri(dependency));
+ }
+ catch (const winrt::hresult_error& ex)
+ {
+ Logger::error(L"Error creating Uri for dependency: %s", ex.message().c_str());
+ }
+ }
+ }
+
+ IAsyncOperationWithProgress deploymentOperation = packageManager.AddPackageAsync(packageUri, uris, options);
+ deploymentOperation.get();
+
+ // Check the status of the operation
+ if (deploymentOperation.Status() == AsyncStatus::Error)
+ {
+ auto deploymentResult{ deploymentOperation.GetResults() };
+ auto errorCode = deploymentOperation.ErrorCode();
+ auto errorText = deploymentResult.ErrorText();
+
+ Logger::error(L"Register {} package failed. ErrorCode: {}, ErrorText: {}", pkgPath, std::to_wstring(errorCode), errorText);
+ return false;
+ }
+ else if (deploymentOperation.Status() == AsyncStatus::Canceled)
+ {
+ Logger::error(L"Register {} package canceled.", pkgPath);
+ return false;
+ }
+ else if (deploymentOperation.Status() == AsyncStatus::Completed)
+ {
+ Logger::info(L"Register {} package completed.", pkgPath);
+ }
+ else
+ {
+ Logger::debug(L"Register {} package started.", pkgPath);
+ }
+
+ }
+ catch (std::exception& e)
+ {
+ Logger::error("Exception thrown while trying to register package: {}", e.what());
+
+ return false;
+ }
+
+ return true;
+ }
}
\ No newline at end of file
diff --git a/src/gpo/assets/PowerToys.admx b/src/gpo/assets/PowerToys.admx
index 7702b99b98..ba54f2728a 100644
--- a/src/gpo/assets/PowerToys.admx
+++ b/src/gpo/assets/PowerToys.admx
@@ -1,11 +1,11 @@
-
+
-
+
@@ -25,6 +25,7 @@
+
@@ -106,6 +107,16 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/gpo/assets/en-US/PowerToys.adml b/src/gpo/assets/en-US/PowerToys.adml
index 4a68d0069f..ca48535b3d 100644
--- a/src/gpo/assets/en-US/PowerToys.adml
+++ b/src/gpo/assets/en-US/PowerToys.adml
@@ -1,7 +1,7 @@
-
+
PowerToys
PowerToys
@@ -32,6 +32,7 @@
PowerToys version 0.86.0 or later
PowerToys version 0.88.0 or later
PowerToys version 0.89.0 or later
+ PowerToys version 0.90.0 or later
From PowerToys version 0.64.0 until PowerToys version 0.87.1
This policy configures the enabled state for all PowerToys utilities.
@@ -242,6 +243,7 @@ If you don't configure this policy, the user will be able to control the setting
Awake: Configure enabled state
Color Picker: Configure enabled state
Command Not Found: Configure enabled state
+ CmdPal: Configure enabled state
Crop And Lock: Configure enabled state
Environment Variables: Configure enabled state
FancyZones: Configure enabled state
diff --git a/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPaste.csproj b/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPaste.csproj
index b67ebf2880..fba18de07c 100644
--- a/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPaste.csproj
+++ b/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPaste.csproj
@@ -69,6 +69,10 @@
+
+
+
+
diff --git a/src/modules/EnvironmentVariables/EnvironmentVariables/EnvironmentVariables.csproj b/src/modules/EnvironmentVariables/EnvironmentVariables/EnvironmentVariables.csproj
index 7ee87d33a2..2dcbc1e237 100644
--- a/src/modules/EnvironmentVariables/EnvironmentVariables/EnvironmentVariables.csproj
+++ b/src/modules/EnvironmentVariables/EnvironmentVariables/EnvironmentVariables.csproj
@@ -2,7 +2,7 @@
-
+
WinExe
EnvironmentVariables
@@ -67,6 +67,10 @@
+
+
+
+
diff --git a/src/modules/FileLocksmith/FileLocksmithExt/PowerToysModule.cpp b/src/modules/FileLocksmith/FileLocksmithExt/PowerToysModule.cpp
index 78808a9428..ec755d99a3 100644
--- a/src/modules/FileLocksmith/FileLocksmithExt/PowerToysModule.cpp
+++ b/src/modules/FileLocksmith/FileLocksmithExt/PowerToysModule.cpp
@@ -83,7 +83,7 @@ public:
std::wstring path = get_module_folderpath(globals::instance);
std::wstring packageUri = path + L"\\FileLocksmithContextMenuPackage.msix";
- if (!package::IsPackageRegistered(constants::nonlocalizable::ContextMenuPackageName))
+ if (!package::IsPackageRegisteredWithPowerToysVersion(constants::nonlocalizable::ContextMenuPackageName))
{
package::RegisterSparsePackage(path, packageUri);
}
diff --git a/src/modules/FileLocksmith/FileLocksmithUI/FileLocksmithUI.csproj b/src/modules/FileLocksmith/FileLocksmithUI/FileLocksmithUI.csproj
index a39daec324..f4b28d3922 100644
--- a/src/modules/FileLocksmith/FileLocksmithUI/FileLocksmithUI.csproj
+++ b/src/modules/FileLocksmith/FileLocksmithUI/FileLocksmithUI.csproj
@@ -63,6 +63,10 @@
+
+
+
+
diff --git a/src/modules/Hosts/Hosts.Tests/Hosts.Tests.csproj b/src/modules/Hosts/Hosts.Tests/Hosts.Tests.csproj
index 8d332f9ea5..0f0a57ba6b 100644
--- a/src/modules/Hosts/Hosts.Tests/Hosts.Tests.csproj
+++ b/src/modules/Hosts/Hosts.Tests/Hosts.Tests.csproj
@@ -20,6 +20,8 @@
runtime
+
+
diff --git a/src/modules/Hosts/Hosts/Hosts.csproj b/src/modules/Hosts/Hosts/Hosts.csproj
index 7cfaecfb97..cf595dd44b 100644
--- a/src/modules/Hosts/Hosts/Hosts.csproj
+++ b/src/modules/Hosts/Hosts/Hosts.csproj
@@ -66,6 +66,10 @@
+
+
+
+
diff --git a/src/modules/Hosts/HostsUILib/ViewModels/MainViewModel.cs b/src/modules/Hosts/HostsUILib/ViewModels/MainViewModel.cs
index 42371e3361..60a036bdb8 100644
--- a/src/modules/Hosts/HostsUILib/ViewModels/MainViewModel.cs
+++ b/src/modules/Hosts/HostsUILib/ViewModels/MainViewModel.cs
@@ -202,7 +202,9 @@ namespace HostsUILib.ViewModels
}
_entries.CollectionChanged += Entries_CollectionChanged;
+#pragma warning disable IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code
Entries = new AdvancedCollectionView(_entries, true);
+#pragma warning restore IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code
Entries.SortDescriptions.Add(new SortDescription(nameof(Entry.Id), SortDirection.Ascending));
ApplyFilters();
OnPropertyChanged(nameof(Entries));
diff --git a/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj b/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj
index 226a1d6112..86d258854f 100644
--- a/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj
+++ b/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj
@@ -140,7 +140,7 @@
-
+
@@ -152,7 +152,7 @@
-
+
diff --git a/src/modules/MeasureTool/MeasureToolCore/packages.config b/src/modules/MeasureTool/MeasureToolCore/packages.config
index f391108479..61ff4b9f07 100644
--- a/src/modules/MeasureTool/MeasureToolCore/packages.config
+++ b/src/modules/MeasureTool/MeasureToolCore/packages.config
@@ -1,6 +1,6 @@
-
+
diff --git a/src/modules/MeasureTool/MeasureToolUI/MeasureToolUI.csproj b/src/modules/MeasureTool/MeasureToolUI/MeasureToolUI.csproj
index 8a34e06024..434ff088b2 100644
--- a/src/modules/MeasureTool/MeasureToolUI/MeasureToolUI.csproj
+++ b/src/modules/MeasureTool/MeasureToolUI/MeasureToolUI.csproj
@@ -13,7 +13,7 @@
app.manifest
true
x64;ARM64
- true
+ true
false
false
true
@@ -55,6 +55,8 @@
+
+
diff --git a/src/modules/NewPlus/NewShellExtensionContextMenu/new_utilities.h b/src/modules/NewPlus/NewShellExtensionContextMenu/new_utilities.h
index 580555f8a6..50f92562d2 100644
--- a/src/modules/NewPlus/NewShellExtensionContextMenu/new_utilities.h
+++ b/src/modules/NewPlus/NewShellExtensionContextMenu/new_utilities.h
@@ -155,7 +155,7 @@ namespace newplus::utilities
static const auto new_dll_path = get_module_folderpath(module_instance_handle);
auto new_package_uri = new_dll_path + L"\\" + constants::non_localizable::msix_package_name;
- if (!package::IsPackageRegistered(constants::non_localizable::context_menu_package_name))
+ if (!package::IsPackageRegisteredWithPowerToysVersion(constants::non_localizable::context_menu_package_name))
{
package::RegisterSparsePackage(new_dll_path, new_package_uri);
}
diff --git a/src/modules/cmdpal/.wt.json b/src/modules/cmdpal/.wt.json
new file mode 100644
index 0000000000..230329e876
--- /dev/null
+++ b/src/modules/cmdpal/.wt.json
@@ -0,0 +1,31 @@
+{
+ "$version": "1.0.0",
+ "snippets":
+ [
+ {
+ "input": "pwsh -c .\\doc\\initial-sdk-spec\\generate-interface.ps1 > .\\extensionsdk\\Microsoft.CommandPalette.Extensions\\Microsoft.CommandPalette.Extensions.idl",
+ "name": "Generate interface",
+ "description": "Generate the interface from the SDK spec\nThis drops it into Microsoft.CommandPalette.Extensions"
+ },
+ {
+ "input": "tasklist | findstr Extension",
+ "name": "List running extensions",
+ "description": "This will list all running extensions, as long as they have 'Extension' in the name (they should)"
+ },
+ {
+ "input": "for /F \"tokens=2\" %A in ('tasklist ^| findstr Extension') do taskkill /PID %A /F",
+ "name": "🚨 Terminate extensions 🚨",
+ "description": "Terminate anything with 'Extension' in the name"
+ },
+ {
+ "input": "start https://github.com/zadjii-msft/PowerToys/compare/main...zadjii-msft:PowerToys:{branch}?expand=1\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D\u001b[D",
+ "name": "New PR",
+ "description": "Create a new PR targeting the right fork.\nReplace {branch} with the actual branch you want to merge."
+ },
+ {
+ "input": "pushd .\\ExtensionTemplate\\ ; git archive -o ..\\Microsoft.CmdPal.UI.ViewModels\\Assets\\template.zip HEAD -- .\\TemplateCmdPalExtension\\ ; popd",
+ "name": "Update template project",
+ "description": "zips up the ExtensionTemplate into our assets. Run this in the cmdpal/ directory."
+ }
+ ]
+}
diff --git a/src/modules/cmdpal/CmdPalModuleInterface/CmdPalModuleInterface.rc b/src/modules/cmdpal/CmdPalModuleInterface/CmdPalModuleInterface.rc
new file mode 100644
index 0000000000..5fa3c8b90d
--- /dev/null
+++ b/src/modules/cmdpal/CmdPalModuleInterface/CmdPalModuleInterface.rc
@@ -0,0 +1,40 @@
+#include
+#include "resource.h"
+#include "../../../common/version/version.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+#include "winres.h"
+#undef APSTUDIO_READONLY_SYMBOLS
+
+1 VERSIONINFO
+FILEVERSION FILE_VERSION
+PRODUCTVERSION PRODUCT_VERSION
+FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+#ifdef _DEBUG
+FILEFLAGS VS_FF_DEBUG
+#else
+FILEFLAGS 0x0L
+#endif
+FILEOS VOS_NT_WINDOWS32
+FILETYPE VFT_DLL
+FILESUBTYPE VFT2_UNKNOWN
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0" // US English (0x0409), Unicode (0x04B0) charset
+ BEGIN
+ VALUE "CompanyName", COMPANY_NAME
+ VALUE "FileDescription", FILE_DESCRIPTION
+ VALUE "FileVersion", FILE_VERSION_STRING
+ VALUE "InternalName", INTERNAL_NAME
+ VALUE "LegalCopyright", COPYRIGHT_NOTE
+ VALUE "OriginalFilename", ORIGINAL_FILENAME
+ VALUE "ProductName", PRODUCT_NAME
+ VALUE "ProductVersion", PRODUCT_VERSION_STRING
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200 // US English (0x0409), Unicode (1200) charset
+ END
+END
diff --git a/src/modules/cmdpal/CmdPalModuleInterface/CmdPalModuleInterface.vcxproj b/src/modules/cmdpal/CmdPalModuleInterface/CmdPalModuleInterface.vcxproj
new file mode 100644
index 0000000000..4395e340fa
--- /dev/null
+++ b/src/modules/cmdpal/CmdPalModuleInterface/CmdPalModuleInterface.vcxproj
@@ -0,0 +1,87 @@
+
+
+
+
+ 17.0
+ Win32Proj
+ {0adeb797-c8c7-4ffa-acd5-2af6cad7ecd8}
+ CmdPalModuleInterface
+ 10.0
+ PowerToys.CmdPalModuleInterface
+
+
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+ ..\..\..\..\$(Platform)\$(Configuration)\
+
+
+
+ {d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}
+
+
+ {6955446d-23f7-4023-9bb3-8657f904af99}
+
+
+ {cc6e41ac-8174-4e8a-8d22-85dd7f4851df}
+
+
+
+
+ EXAMPLEPOWERTOY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
+ ..\..\..\common\inc;..\..\..\common\Telemetry;..\..\;..\..\..\;%(AdditionalIncludeDirectories)
+
+
+ $(OutDir)$(TargetName)$(TargetExt)
+
+
+
+
+
+
+
+
+
+ Create
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/cmdpal/CmdPalModuleInterface/CmdPalModuleInterface.vcxproj.filters b/src/modules/cmdpal/CmdPalModuleInterface/CmdPalModuleInterface.vcxproj.filters
new file mode 100644
index 0000000000..1b2723105d
--- /dev/null
+++ b/src/modules/cmdpal/CmdPalModuleInterface/CmdPalModuleInterface.vcxproj.filters
@@ -0,0 +1,33 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
+
+ Header Files
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/cmdpal/CmdPalModuleInterface/dllmain.cpp b/src/modules/cmdpal/CmdPalModuleInterface/dllmain.cpp
new file mode 100644
index 0000000000..419e66c648
--- /dev/null
+++ b/src/modules/cmdpal/CmdPalModuleInterface/dllmain.cpp
@@ -0,0 +1,268 @@
+// dllmain.cpp : Defines the entry point for the DLL application.
+#include "pch.h"
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+HINSTANCE g_hInst_cmdPal = 0;
+
+BOOL APIENTRY DllMain(HMODULE hInstance,
+ DWORD ul_reason_for_call,
+ LPVOID)
+{
+ switch (ul_reason_for_call)
+ {
+ case DLL_PROCESS_ATTACH:
+ g_hInst_cmdPal = hInstance;
+ break;
+ case DLL_THREAD_ATTACH:
+ case DLL_THREAD_DETACH:
+ case DLL_PROCESS_DETACH:
+ break;
+ }
+ return TRUE;
+}
+
+class CmdPal : public PowertoyModuleIface
+{
+private:
+ bool m_enabled = false;
+
+ std::wstring app_name;
+
+ //contains the non localized key of the powertoy
+ std::wstring app_key;
+
+ void LaunchApp()
+ {
+ auto package = package::GetRegisteredPackage(L"Microsoft.CommandPalette", false);
+
+ if (package.has_value())
+ {
+ auto getAppListEntriesOperation = package->GetAppListEntriesAsync();
+ auto appEntries = getAppListEntriesOperation.get();
+
+ if (appEntries.Size() > 0)
+ {
+ winrt::Windows::Foundation::IAsyncOperation launchOperation = appEntries.GetAt(0).LaunchAsync();
+ launchOperation.get();
+ }
+ else
+ {
+ Logger::error(L"No app entries found for the package.");
+ }
+ }
+ else
+ {
+ Logger::error(L"CmdPal package is not registered.");
+ }
+ }
+
+ std::vector GetProcessesIdByName(const std::wstring& processName)
+ {
+ std::vector processIds;
+ HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+
+ if (snapshot != INVALID_HANDLE_VALUE)
+ {
+ PROCESSENTRY32 processEntry;
+ processEntry.dwSize = sizeof(PROCESSENTRY32);
+
+ if (Process32First(snapshot, &processEntry))
+ {
+ do
+ {
+ if (_wcsicmp(processEntry.szExeFile, processName.c_str()) == 0)
+ {
+ processIds.push_back(processEntry.th32ProcessID);
+ }
+ } while (Process32Next(snapshot, &processEntry));
+ }
+
+ CloseHandle(snapshot);
+ }
+
+ return processIds;
+ }
+
+ void TerminateCmdPal()
+ {
+ auto processIds = GetProcessesIdByName(L"Microsoft.CmdPal.UI.exe");
+
+ if (processIds.size() == 0)
+ {
+ Logger::trace(L"Nothing To PROCESS_TERMINATE");
+ return;
+ }
+
+ for (DWORD pid : processIds)
+ {
+ HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
+
+ if (hProcess != NULL)
+ {
+ TerminateProcess(hProcess, 0);
+ CloseHandle(hProcess);
+ }
+ }
+ }
+
+public:
+ CmdPal()
+ {
+ app_name = L"CmdPal";
+ app_key = L"CmdPal";
+ LoggerHelpers::init_logger(app_key, L"ModuleInterface", "CmdPal");
+ }
+
+ ~CmdPal()
+ {
+ if (m_enabled)
+ {
+ }
+ m_enabled = false;
+ }
+
+ // Destroy the powertoy and free memory
+ virtual void destroy() override
+ {
+ Logger::trace("CmdPal::destroy()");
+ TerminateCmdPal();
+ delete this;
+ }
+
+ // Return the localized display name of the powertoy
+ virtual const wchar_t* get_name() override
+ {
+ return app_name.c_str();
+ }
+
+ // Return the non localized key of the powertoy, this will be cached by the runner
+ virtual const wchar_t* get_key() override
+ {
+ return app_key.c_str();
+ }
+
+ // Return the configured status for the gpo policy for the module
+ virtual powertoys_gpo::gpo_rule_configured_t gpo_policy_enabled_configuration() override
+ {
+ return powertoys_gpo::getConfiguredCmdPalEnabledValue();
+ }
+
+ virtual bool get_config(wchar_t* buffer, int* buffer_size) override
+ {
+ HINSTANCE hinstance = reinterpret_cast(&__ImageBase);
+
+ // Create a Settings object.
+ PowerToysSettings::Settings settings(hinstance, get_name());
+
+ return settings.serialize_to_buffer(buffer, buffer_size);
+ }
+
+ virtual void call_custom_action(const wchar_t* /*action*/) override
+ {
+ }
+
+ virtual void set_config(const wchar_t* config) override
+ {
+ try
+ {
+ // Parse the input JSON string.
+ PowerToysSettings::PowerToyValues values =
+ PowerToysSettings::PowerToyValues::from_json_string(config, get_key());
+
+ // If you don't need to do any custom processing of the settings, proceed
+ // to persists the values calling:
+ values.save_to_settings_file();
+ // Otherwise call a custom function to process the settings before saving them to disk:
+ // save_settings();
+ }
+ catch (std::exception&)
+ {
+ // Improper JSON.
+ }
+ }
+
+ virtual void enable()
+ {
+ Logger::trace("CmdPal::enable()");
+
+ m_enabled = true;
+
+ try
+ {
+ if (!package::GetRegisteredPackage(L"Microsoft.CommandPalette", false).has_value())
+ {
+ Logger::info(L"CmdPal not installed. Installing...");
+
+ std::wstring installationFolder = get_module_folderpath();
+#if _DEBUG
+ std::wstring archSubdir = L"x64";
+#ifdef _M_ARM64
+ archSubdir = L"ARM64";
+#endif
+ auto msix = package::FindMsixFile(installationFolder + L"\\WinUI3Apps\\CmdPal\\AppPackages\\Microsoft.CmdPal.UI_0.0.1.0_Debug_Test\\", false);
+ auto dependencies = package::FindMsixFile(installationFolder + L"\\WinUI3Apps\\CmdPal\\AppPackages\\Microsoft.CmdPal.UI_0.0.1.0_Debug_Test\\Dependencies\\" + archSubdir + L"\\", true);
+#else
+ auto msix = package::FindMsixFile(installationFolder + L"\\WinUI3Apps\\CmdPal\\", false);
+ auto dependencies = package::FindMsixFile(installationFolder + L"\\WinUI3Apps\\CmdPal\\Dependencies\\", true);
+#endif
+
+ if (!msix.empty())
+ {
+ auto msixPath = msix[0];
+
+ if (!package::RegisterPackage(msixPath, dependencies))
+ {
+ Logger::error(L"Failed to install CmdPal package");
+ }
+ }
+ }
+ }
+ catch (std::exception& e)
+ {
+ std::string errorMessage{ "Exception thrown while trying to install CmdPal package: " };
+ errorMessage += e.what();
+ Logger::error(errorMessage);
+ }
+
+ LaunchApp();
+ }
+
+ virtual void disable()
+ {
+ Logger::trace("CmdPal::disable()");
+ TerminateCmdPal();
+
+ m_enabled = false;
+ }
+
+ virtual bool on_hotkey(size_t) override
+ {
+ return false;
+ }
+
+ virtual size_t get_hotkeys(Hotkey*, size_t) override
+ {
+ return 0;
+ }
+
+ virtual bool is_enabled() override
+ {
+ return m_enabled;
+ }
+};
+
+extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create()
+{
+ return new CmdPal();
+}
diff --git a/src/modules/cmdpal/CmdPalModuleInterface/packages.config b/src/modules/cmdpal/CmdPalModuleInterface/packages.config
new file mode 100644
index 0000000000..09bfc449e2
--- /dev/null
+++ b/src/modules/cmdpal/CmdPalModuleInterface/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/cmdpal/CmdPalModuleInterface/pch.cpp b/src/modules/cmdpal/CmdPalModuleInterface/pch.cpp
new file mode 100644
index 0000000000..64b7eef6d6
--- /dev/null
+++ b/src/modules/cmdpal/CmdPalModuleInterface/pch.cpp
@@ -0,0 +1,5 @@
+// pch.cpp: source file corresponding to the pre-compiled header
+
+#include "pch.h"
+
+// When you are using pre-compiled headers, this source file is necessary for compilation to succeed.
diff --git a/src/modules/cmdpal/CmdPalModuleInterface/pch.h b/src/modules/cmdpal/CmdPalModuleInterface/pch.h
new file mode 100644
index 0000000000..30600d2e25
--- /dev/null
+++ b/src/modules/cmdpal/CmdPalModuleInterface/pch.h
@@ -0,0 +1,16 @@
+// pch.h: This is a precompiled header file.
+// Files listed below are compiled only once, improving build performance for future builds.
+// This also affects IntelliSense performance, including code completion and many code browsing features.
+// However, files listed here are ALL re-compiled if any one of them is updated between builds.
+// Do not add files here that you will be updating frequently as this negates the performance advantage.
+
+#ifndef PCH_H
+#define PCH_H
+
+#include
+
+#include
+#include
+#include
+
+#endif //PCH_H
diff --git a/src/modules/cmdpal/CmdPalModuleInterface/resource.h b/src/modules/cmdpal/CmdPalModuleInterface/resource.h
new file mode 100644
index 0000000000..483f62d2bc
--- /dev/null
+++ b/src/modules/cmdpal/CmdPalModuleInterface/resource.h
@@ -0,0 +1,13 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by AlwaysOnTopModuleInterface.rc
+
+//////////////////////////////
+// Non-localizable
+
+#define FILE_DESCRIPTION "PowerToys Command Palette Module"
+#define INTERNAL_NAME "PowerToys.CmdPalModuleInterface"
+#define ORIGINAL_FILENAME "PowerToys.CmdPalModuleInterface.dll"
+
+// Non-localizable
+//////////////////////////////
diff --git a/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/Directory.Build.props b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/Directory.Build.props
new file mode 100644
index 0000000000..9eb15ca3cf
--- /dev/null
+++ b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/Directory.Build.props
@@ -0,0 +1,10 @@
+
+
+ x64;ARM64
+ true
+ Recommended
+ <_SkipUpgradeNetAnalyzersNuGetWarning>true
+ direct
+ $(Platform)
+
+
diff --git a/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/Directory.Packages.props b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/Directory.Packages.props
new file mode 100644
index 0000000000..4e4102de90
--- /dev/null
+++ b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/Directory.Packages.props
@@ -0,0 +1,16 @@
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension.sln b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension.sln
new file mode 100644
index 0000000000..47823e3856
--- /dev/null
+++ b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension.sln
@@ -0,0 +1,43 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.13.35507.96 d17.13
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TemplateCmdPalExtension", "TemplateCmdPalExtension\TemplateCmdPalExtension.csproj", "{79F86DE5-70B1-4EC1-9832-DF428B55E466}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|ARM64 = Debug|ARM64
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|ARM64 = Release|ARM64
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {79F86DE5-70B1-4EC1-9832-DF428B55E466}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {79F86DE5-70B1-4EC1-9832-DF428B55E466}.Debug|ARM64.Build.0 = Debug|ARM64
+ {79F86DE5-70B1-4EC1-9832-DF428B55E466}.Debug|ARM64.Deploy.0 = Debug|ARM64
+ {79F86DE5-70B1-4EC1-9832-DF428B55E466}.Debug|x64.ActiveCfg = Debug|x64
+ {79F86DE5-70B1-4EC1-9832-DF428B55E466}.Debug|x64.Build.0 = Debug|x64
+ {79F86DE5-70B1-4EC1-9832-DF428B55E466}.Debug|x64.Deploy.0 = Debug|x64
+ {79F86DE5-70B1-4EC1-9832-DF428B55E466}.Debug|x86.ActiveCfg = Debug|x86
+ {79F86DE5-70B1-4EC1-9832-DF428B55E466}.Debug|x86.Build.0 = Debug|x86
+ {79F86DE5-70B1-4EC1-9832-DF428B55E466}.Debug|x86.Deploy.0 = Debug|x86
+ {79F86DE5-70B1-4EC1-9832-DF428B55E466}.Release|ARM64.ActiveCfg = Release|ARM64
+ {79F86DE5-70B1-4EC1-9832-DF428B55E466}.Release|ARM64.Build.0 = Release|ARM64
+ {79F86DE5-70B1-4EC1-9832-DF428B55E466}.Release|ARM64.Deploy.0 = Release|ARM64
+ {79F86DE5-70B1-4EC1-9832-DF428B55E466}.Release|x64.ActiveCfg = Release|x64
+ {79F86DE5-70B1-4EC1-9832-DF428B55E466}.Release|x64.Build.0 = Release|x64
+ {79F86DE5-70B1-4EC1-9832-DF428B55E466}.Release|x64.Deploy.0 = Release|x64
+ {79F86DE5-70B1-4EC1-9832-DF428B55E466}.Release|x86.ActiveCfg = Release|x86
+ {79F86DE5-70B1-4EC1-9832-DF428B55E466}.Release|x86.Build.0 = Release|x86
+ {79F86DE5-70B1-4EC1-9832-DF428B55E466}.Release|x86.Deploy.0 = Release|x86
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {CEDBC581-5818-4350-BC8A-A1ECE687D357}
+ EndGlobalSection
+EndGlobal
diff --git a/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/Assets/LockScreenLogo.scale-200.png b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/Assets/LockScreenLogo.scale-200.png
new file mode 100644
index 0000000000..7440f0d4bf
Binary files /dev/null and b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/Assets/LockScreenLogo.scale-200.png differ
diff --git a/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/Assets/SplashScreen.scale-200.png b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/Assets/SplashScreen.scale-200.png
new file mode 100644
index 0000000000..32f486a867
Binary files /dev/null and b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/Assets/SplashScreen.scale-200.png differ
diff --git a/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/Assets/Square150x150Logo.scale-200.png b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/Assets/Square150x150Logo.scale-200.png
new file mode 100644
index 0000000000..53ee3777ea
Binary files /dev/null and b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/Assets/Square150x150Logo.scale-200.png differ
diff --git a/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/Assets/Square44x44Logo.scale-200.png b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/Assets/Square44x44Logo.scale-200.png
new file mode 100644
index 0000000000..f713bba67f
Binary files /dev/null and b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/Assets/Square44x44Logo.scale-200.png differ
diff --git a/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/Assets/Square44x44Logo.targetsize-24_altform-unplated.png b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/Assets/Square44x44Logo.targetsize-24_altform-unplated.png
new file mode 100644
index 0000000000..dc9f5bea0c
Binary files /dev/null and b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/Assets/Square44x44Logo.targetsize-24_altform-unplated.png differ
diff --git a/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/Assets/StoreLogo.png b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/Assets/StoreLogo.png
new file mode 100644
index 0000000000..a4586f26bd
Binary files /dev/null and b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/Assets/StoreLogo.png differ
diff --git a/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/Assets/Wide310x150Logo.scale-200.png b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/Assets/Wide310x150Logo.scale-200.png
new file mode 100644
index 0000000000..8b4a5d0dd5
Binary files /dev/null and b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/Assets/Wide310x150Logo.scale-200.png differ
diff --git a/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/Package.appxmanifest b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/Package.appxmanifest
new file mode 100644
index 0000000000..625ee4dfec
--- /dev/null
+++ b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/Package.appxmanifest
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+
+ TemplateDisplayName
+ A Lone Developer
+ Assets\StoreLogo.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/Pages/TemplateCmdPalExtensionPage.cs b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/Pages/TemplateCmdPalExtensionPage.cs
new file mode 100644
index 0000000000..93da2b191a
--- /dev/null
+++ b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/Pages/TemplateCmdPalExtensionPage.cs
@@ -0,0 +1,25 @@
+// 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 Microsoft.CommandPalette.Extensions;
+using Microsoft.CommandPalette.Extensions.Toolkit;
+
+namespace TemplateCmdPalExtension;
+
+internal sealed partial class TemplateCmdPalExtensionPage : ListPage
+{
+ public TemplateCmdPalExtensionPage()
+ {
+ Icon = IconHelpers.FromRelativePath("Assets\\StoreLogo.png");
+ Title = "TemplateDisplayName";
+ Name = "Open";
+ }
+
+ public override IListItem[] GetItems()
+ {
+ return [
+ new ListItem(new NoOpCommand()) { Title = "TODO: Implement your extension here" }
+ ];
+ }
+}
diff --git a/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/Program.cs b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/Program.cs
new file mode 100644
index 0000000000..7325c7c960
--- /dev/null
+++ b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/Program.cs
@@ -0,0 +1,36 @@
+// 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;
+using System.Threading;
+using Microsoft.CommandPalette.Extensions;
+
+namespace TemplateCmdPalExtension;
+
+public class Program
+{
+ [MTAThread]
+ public static void Main(string[] args)
+ {
+ if (args.Length > 0 && args[0] == "-RegisterProcessAsComServer")
+ {
+ using ExtensionServer server = new();
+ var extensionDisposedEvent = new ManualResetEvent(false);
+ var extensionInstance = new TemplateCmdPalExtension(extensionDisposedEvent);
+
+ // We are instantiating an extension instance once above, and returning it every time the callback in RegisterExtension below is called.
+ // This makes sure that only one instance of SampleExtension is alive, which is returned every time the host asks for the IExtension object.
+ // If you want to instantiate a new instance each time the host asks, create the new instance inside the delegate.
+ server.RegisterExtension(() => extensionInstance);
+
+ // This will make the main thread wait until the event is signalled by the extension class.
+ // Since we have single instance of the extension object, we exit as soon as it is disposed.
+ extensionDisposedEvent.WaitOne();
+ }
+ else
+ {
+ Console.WriteLine("Not being launched as a Extension... exiting.");
+ }
+ }
+}
diff --git a/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/Properties/PublishProfiles/win-arm64.pubxml b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/Properties/PublishProfiles/win-arm64.pubxml
new file mode 100644
index 0000000000..79690066ff
--- /dev/null
+++ b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/Properties/PublishProfiles/win-arm64.pubxml
@@ -0,0 +1,15 @@
+
+
+
+
+ FileSystem
+ ARM64
+ win-arm64
+ bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\
+ true
+ False
+ True
+
+
diff --git a/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/Properties/PublishProfiles/win-x64.pubxml b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/Properties/PublishProfiles/win-x64.pubxml
new file mode 100644
index 0000000000..c53b5882ae
--- /dev/null
+++ b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/Properties/PublishProfiles/win-x64.pubxml
@@ -0,0 +1,15 @@
+
+
+
+
+ FileSystem
+ x64
+ win-x64
+ bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\
+ true
+ False
+ True
+
+
diff --git a/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/Properties/launchSettings.json b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/Properties/launchSettings.json
new file mode 100644
index 0000000000..9e90cf7012
--- /dev/null
+++ b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/Properties/launchSettings.json
@@ -0,0 +1,11 @@
+{
+ "profiles": {
+ "TemplateCmdPalExtension (Package)": {
+ "commandName": "MsixPackage",
+ "doNotLaunchApp": true
+ },
+ "TemplateCmdPalExtension (Unpackaged)": {
+ "commandName": "Project"
+ }
+ }
+}
diff --git a/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/TemplateCmdPalExtension.cs b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/TemplateCmdPalExtension.cs
new file mode 100644
index 0000000000..ac6659e3b4
--- /dev/null
+++ b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/TemplateCmdPalExtension.cs
@@ -0,0 +1,36 @@
+// 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;
+using System.Runtime.InteropServices;
+using System.Threading;
+using Microsoft.CommandPalette.Extensions;
+
+namespace TemplateCmdPalExtension;
+
+[ComVisible(true)]
+[Guid("FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF")]
+[ComDefaultInterface(typeof(IExtension))]
+public sealed partial class TemplateCmdPalExtension : IExtension, IDisposable
+{
+ private readonly ManualResetEvent _extensionDisposedEvent;
+
+ private readonly TemplateCmdPalExtensionCommandsProvider _provider = new();
+
+ public TemplateCmdPalExtension(ManualResetEvent extensionDisposedEvent)
+ {
+ this._extensionDisposedEvent = extensionDisposedEvent;
+ }
+
+ public object? GetProvider(ProviderType providerType)
+ {
+ return providerType switch
+ {
+ ProviderType.Commands => _provider,
+ _ => null,
+ };
+ }
+
+ public void Dispose() => this._extensionDisposedEvent.Set();
+}
diff --git a/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/TemplateCmdPalExtension.csproj b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/TemplateCmdPalExtension.csproj
new file mode 100644
index 0000000000..6b58eb0e05
--- /dev/null
+++ b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/TemplateCmdPalExtension.csproj
@@ -0,0 +1,67 @@
+
+
+ WinExe
+ TemplateCmdPalExtension
+ app.manifest
+
+ 10.0.22621.57
+ net9.0-windows10.0.22621.0
+ 10.0.19041.0
+ 10.0.19041.0
+ win-x64;win-arm64
+
+ win-$(Platform).pubxml
+ true
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+ true
+ true
+ 2
+
+ IL2081
+
+ true
+ true
+
+
diff --git a/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/TemplateCmdPalExtensionCommandsProvider.cs b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/TemplateCmdPalExtensionCommandsProvider.cs
new file mode 100644
index 0000000000..45acf71856
--- /dev/null
+++ b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/TemplateCmdPalExtensionCommandsProvider.cs
@@ -0,0 +1,28 @@
+// 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 Microsoft.CommandPalette.Extensions;
+using Microsoft.CommandPalette.Extensions.Toolkit;
+
+namespace TemplateCmdPalExtension;
+
+public partial class TemplateCmdPalExtensionCommandsProvider : CommandProvider
+{
+ private readonly ICommandItem[] _commands;
+
+ public TemplateCmdPalExtensionCommandsProvider()
+ {
+ DisplayName = "TemplateDisplayName";
+ Icon = IconHelpers.FromRelativePath("Assets\\StoreLogo.png");
+ _commands = [
+ new CommandItem(new TemplateCmdPalExtensionPage()) { Title = DisplayName },
+ ];
+ }
+
+ public override ICommandItem[] TopLevelCommands()
+ {
+ return _commands;
+ }
+
+}
diff --git a/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/app.manifest b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/app.manifest
new file mode 100644
index 0000000000..930a1b5cfa
--- /dev/null
+++ b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/TemplateCmdPalExtension/app.manifest
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PerMonitorV2
+
+
+
\ No newline at end of file
diff --git a/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/nuget.config b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/nuget.config
new file mode 100644
index 0000000000..e6a17ffdfe
--- /dev/null
+++ b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/nuget.config
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/AllAppsCommandProvider.cs b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/AllAppsCommandProvider.cs
new file mode 100644
index 0000000000..c90b45373f
--- /dev/null
+++ b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/AllAppsCommandProvider.cs
@@ -0,0 +1,59 @@
+// 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.Linq;
+using Microsoft.CmdPal.Ext.Apps.Properties;
+using Microsoft.CommandPalette.Extensions;
+using Microsoft.CommandPalette.Extensions.Toolkit;
+
+namespace Microsoft.CmdPal.Ext.Apps;
+
+public partial class AllAppsCommandProvider : CommandProvider
+{
+ public static readonly AllAppsPage Page = new();
+
+ private readonly CommandItem _listItem;
+
+ public AllAppsCommandProvider()
+ {
+ Id = "AllApps";
+ DisplayName = Resources.installed_apps;
+ Icon = IconHelpers.FromRelativePath("Assets\\AllApps.svg");
+ Settings = AllAppsSettings.Instance.Settings;
+
+ _listItem = new(Page) { Subtitle = Resources.search_installed_apps };
+ }
+
+ public override ICommandItem[] TopLevelCommands() => [_listItem];
+
+ public ICommandItem? LookupApp(string displayName)
+ {
+ var items = Page.GetItems();
+
+ // We're going to do this search in two directions:
+ // First, is this name a substring of any app...
+ var nameMatches = items.Where(i => i.Title.Contains(displayName));
+
+ // ... Then, does any app have this name as a substring ...
+ // Only get one of these - "Terminal Preview" contains both "Terminal" and "Terminal Preview", so just take the best one
+ var appMatches = items.Where(i => displayName.Contains(i.Title)).OrderByDescending(i => i.Title.Length).Take(1);
+
+ // ... Now, combine those two
+ var both = nameMatches.Concat(appMatches);
+
+ if (both.Count() == 1)
+ {
+ return both.First();
+ }
+ else if (nameMatches.Count() == 1 && appMatches.Count() == 1)
+ {
+ if (nameMatches.First() == appMatches.First())
+ {
+ return nameMatches.First();
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/AllAppsPage.cs b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/AllAppsPage.cs
new file mode 100644
index 0000000000..ed724794fd
--- /dev/null
+++ b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/AllAppsPage.cs
@@ -0,0 +1,119 @@
+// 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.Diagnostics;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using ManagedCommon;
+using Microsoft.CmdPal.Ext.Apps.Programs;
+using Microsoft.CmdPal.Ext.Apps.Properties;
+using Microsoft.CommandPalette.Extensions;
+using Microsoft.CommandPalette.Extensions.Toolkit;
+
+namespace Microsoft.CmdPal.Ext.Apps;
+
+public sealed partial class AllAppsPage : ListPage
+{
+ private readonly Lock _listLock = new();
+ private AppListItem[] allAppsSection = [];
+
+ public AllAppsPage()
+ {
+ this.Name = Resources.all_apps;
+ this.Icon = IconHelpers.FromRelativePath("Assets\\AllApps.svg");
+ this.ShowDetails = true;
+ this.IsLoading = true;
+ this.PlaceholderText = Resources.search_installed_apps_placeholder;
+
+ Task.Run(() =>
+ {
+ lock (_listLock)
+ {
+ BuildListItems();
+ }
+ });
+ }
+
+ public override IListItem[] GetItems()
+ {
+ if (allAppsSection.Length == 0 || AppCache.Instance.Value.ShouldReload())
+ {
+ lock (_listLock)
+ {
+ BuildListItems();
+ }
+ }
+
+ return allAppsSection;
+ }
+
+ private void BuildListItems()
+ {
+ this.IsLoading = true;
+
+ Stopwatch stopwatch = new();
+ stopwatch.Start();
+
+ List apps = GetPrograms();
+
+ this.allAppsSection = apps
+ .Select((app) => new AppListItem(app, true))
+ .ToArray();
+
+ this.IsLoading = false;
+
+ AppCache.Instance.Value.ResetReloadFlag();
+
+ stopwatch.Stop();
+ Logger.LogTrace($"{nameof(AllAppsPage)}.{nameof(BuildListItems)} took: {stopwatch.ElapsedMilliseconds} ms");
+ }
+
+ internal List GetPrograms()
+ {
+ IEnumerable uwpResults = AppCache.Instance.Value.UWPs
+ .Where((application) => application.Enabled)
+ .Select(app =>
+ new AppItem()
+ {
+ Name = app.Name,
+ Subtitle = app.Description,
+ Type = UWPApplication.Type(),
+ IcoPath = app.LogoType != LogoType.Error ? app.LogoPath : string.Empty,
+ DirPath = app.Location,
+ UserModelId = app.UserModelId,
+ IsPackaged = true,
+ Commands = app.GetCommands(),
+ });
+
+ IEnumerable win32Results = AppCache.Instance.Value.Win32s
+ .Where((application) => application.Enabled && application.Valid)
+ .Select(app =>
+ {
+ string icoPath = string.IsNullOrEmpty(app.IcoPath) ?
+ (app.AppType == Win32Program.ApplicationType.InternetShortcutApplication ?
+ app.IcoPath :
+ app.FullPath) :
+ app.IcoPath;
+
+ // icoPath = icoPath.EndsWith(".lnk", System.StringComparison.InvariantCultureIgnoreCase) ? (icoPath + ",0") : icoPath;
+ icoPath = icoPath.EndsWith(".lnk", System.StringComparison.InvariantCultureIgnoreCase) ?
+ app.FullPath :
+ icoPath;
+ return new AppItem()
+ {
+ Name = app.Name,
+ Subtitle = app.Description,
+ Type = app.Type(),
+ IcoPath = icoPath,
+ ExePath = !string.IsNullOrEmpty(app.LnkFilePath) ? app.LnkFilePath : app.FullPath,
+ DirPath = app.Location,
+ Commands = app.GetCommands(),
+ };
+ });
+
+ return uwpResults.Concat(win32Results).OrderBy(app => app.Name).ToList();
+ }
+}
diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/AppCache.cs b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/AppCache.cs
new file mode 100644
index 0000000000..cb80c5e8c5
--- /dev/null
+++ b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/AppCache.cs
@@ -0,0 +1,90 @@
+// 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;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.CmdPal.Ext.Apps.Programs;
+using Microsoft.CmdPal.Ext.Apps.Storage;
+using Microsoft.CmdPal.Ext.Apps.Utils;
+
+namespace Microsoft.CmdPal.Ext.Apps;
+
+public sealed class AppCache : IDisposable
+{
+ private Win32ProgramFileSystemWatchers _win32ProgramRepositoryHelper;
+
+ private PackageRepository _packageRepository;
+
+ private Win32ProgramRepository _win32ProgramRepository;
+
+ private bool _disposed;
+
+ public IList Win32s => _win32ProgramRepository.Items;
+
+ public IList UWPs => _packageRepository.Items;
+
+ public static readonly Lazy Instance = new(() => new());
+
+ public AppCache()
+ {
+ _win32ProgramRepositoryHelper = new Win32ProgramFileSystemWatchers();
+ _win32ProgramRepository = new Win32ProgramRepository(_win32ProgramRepositoryHelper.FileSystemWatchers.Cast().ToList(), AllAppsSettings.Instance, _win32ProgramRepositoryHelper.PathsToWatch);
+
+ _packageRepository = new PackageRepository(new PackageCatalogWrapper());
+
+ var a = Task.Run(() =>
+ {
+ _win32ProgramRepository.IndexPrograms();
+ });
+
+ var b = Task.Run(() =>
+ {
+ _packageRepository.IndexPrograms();
+ UpdateUWPIconPath(ThemeHelper.GetCurrentTheme());
+ });
+
+ Task.WaitAll(a, b);
+
+ AllAppsSettings.Instance.LastIndexTime = DateTime.Today;
+ }
+
+ private void UpdateUWPIconPath(Theme theme)
+ {
+ if (_packageRepository != null)
+ {
+ foreach (UWPApplication app in _packageRepository)
+ {
+ app.UpdateLogoPath(theme);
+ }
+ }
+ }
+
+ public bool ShouldReload() => _packageRepository.ShouldReload() || _win32ProgramRepository.ShouldReload();
+
+ public void ResetReloadFlag()
+ {
+ _packageRepository.ResetReloadFlag();
+ _win32ProgramRepository.ResetReloadFlag();
+ }
+
+ public void Dispose()
+ {
+ Dispose(disposing: true);
+ GC.SuppressFinalize(this);
+ }
+
+ private void Dispose(bool disposing)
+ {
+ if (!_disposed)
+ {
+ if (disposing)
+ {
+ _win32ProgramRepositoryHelper?.Dispose();
+ _disposed = true;
+ }
+ }
+ }
+}
diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/AppItem.cs b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/AppItem.cs
new file mode 100644
index 0000000000..4bb26bed67
--- /dev/null
+++ b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/AppItem.cs
@@ -0,0 +1,34 @@
+// 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 Microsoft.CmdPal.Ext.Apps.Programs;
+using Microsoft.CommandPalette.Extensions.Toolkit;
+
+namespace Microsoft.CmdPal.Ext.Apps;
+
+internal sealed class AppItem
+{
+ public string Name { get; set; } = string.Empty;
+
+ public string Subtitle { get; set; } = string.Empty;
+
+ public string Type { get; set; } = string.Empty;
+
+ public string IcoPath { get; set; } = string.Empty;
+
+ public string ExePath { get; set; } = string.Empty;
+
+ public string DirPath { get; set; } = string.Empty;
+
+ public string UserModelId { get; set; } = string.Empty;
+
+ public bool IsPackaged { get; set; }
+
+ public List? Commands { get; set; }
+
+ public AppItem()
+ {
+ }
+}
diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/AppListItem.cs b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/AppListItem.cs
new file mode 100644
index 0000000000..1ab8df76ef
--- /dev/null
+++ b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/AppListItem.cs
@@ -0,0 +1,104 @@
+// 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;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Microsoft.CommandPalette.Extensions;
+using Microsoft.CommandPalette.Extensions.Toolkit;
+using Windows.Storage.Streams;
+
+namespace Microsoft.CmdPal.Ext.Apps.Programs;
+
+internal sealed partial class AppListItem : ListItem
+{
+ private readonly AppItem _app;
+ private static readonly Tag _appTag = new("App");
+
+ private readonly Lazy _details;
+ private readonly Lazy _icon;
+
+ public override IDetails? Details { get => _details.Value; set => base.Details = value; }
+
+ public override IIconInfo? Icon { get => _icon.Value; set => base.Icon = value; }
+
+ public AppListItem(AppItem app, bool useThumbnails)
+ : base(new AppCommand(app))
+ {
+ _app = app;
+ Title = app.Name;
+ Subtitle = app.Subtitle;
+ Tags = [_appTag];
+ MoreCommands = _app.Commands!.ToArray();
+
+ _details = new Lazy(() => BuildDetails());
+ _icon = new Lazy(() =>
+ {
+ var t = FetchIcon(useThumbnails);
+ t.Wait();
+ return t.Result;
+ });
+ }
+
+ private Details BuildDetails()
+ {
+ var metadata = new List();
+ metadata.Add(new DetailsElement() { Key = "Type", Data = new DetailsTags() { Tags = [new Tag(_app.Type)] } });
+ if (!_app.IsPackaged)
+ {
+ metadata.Add(new DetailsElement() { Key = "Path", Data = new DetailsLink() { Text = _app.ExePath } });
+ }
+
+ return new Details()
+ {
+ Title = this.Title,
+ HeroImage = this.Icon ?? new IconInfo(string.Empty),
+ Metadata = metadata.ToArray(),
+ };
+ }
+
+ public async Task FetchIcon(bool useThumbnails)
+ {
+ IconInfo? icon = null;
+ if (_app.IsPackaged)
+ {
+ icon = new IconInfo(_app.IcoPath);
+ if (_details.IsValueCreated)
+ {
+ _details.Value.HeroImage = icon;
+ }
+
+ return icon;
+ }
+
+ if (useThumbnails)
+ {
+ try
+ {
+ var stream = await ThumbnailHelper.GetThumbnail(_app.ExePath);
+ if (stream != null)
+ {
+ var data = new IconData(RandomAccessStreamReference.CreateFromStream(stream));
+ icon = new IconInfo(data, data);
+ }
+ }
+ catch
+ {
+ }
+
+ icon = icon ?? new IconInfo(_app.IcoPath);
+ }
+ else
+ {
+ icon = new IconInfo(_app.IcoPath);
+ }
+
+ if (_details.IsValueCreated)
+ {
+ _details.Value.HeroImage = icon;
+ }
+
+ return icon;
+ }
+}
diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Assets/AllApps.png b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Assets/AllApps.png
new file mode 100644
index 0000000000..34ca984532
Binary files /dev/null and b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Assets/AllApps.png differ
diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Assets/AllApps.svg b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Assets/AllApps.svg
new file mode 100644
index 0000000000..3fcd53bc89
--- /dev/null
+++ b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Assets/AllApps.svg
@@ -0,0 +1,29 @@
+
diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Microsoft.CmdPal.Ext.Apps.csproj b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Microsoft.CmdPal.Ext.Apps.csproj
new file mode 100644
index 0000000000..24782335ed
--- /dev/null
+++ b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Microsoft.CmdPal.Ext.Apps.csproj
@@ -0,0 +1,52 @@
+
+
+
+ Microsoft.CmdPal.Ext.Apps
+ enable
+ $(SolutionDir)$(Platform)\$(Configuration)\WinUI3Apps\CmdPal
+ false
+ false
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/NativeMethods.txt b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/NativeMethods.txt
new file mode 100644
index 0000000000..c0c94348c5
--- /dev/null
+++ b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/NativeMethods.txt
@@ -0,0 +1,19 @@
+GetPhysicallyInstalledSystemMemory
+GlobalMemoryStatusEx
+GetSystemInfo
+CoCreateInstance
+SetForegroundWindow
+IsIconic
+RegisterHotKey
+SetWindowLongPtr
+CallWindowProc
+ShowWindow
+SetForegroundWindow
+SetFocus
+SetActiveWindow
+MonitorFromWindow
+GetMonitorInfo
+SHCreateStreamOnFileEx
+CoAllowSetForegroundWindow
+SHCreateStreamOnFileEx
+SHLoadIndirectString
\ No newline at end of file
diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/AppxFactory.cs b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/AppxFactory.cs
new file mode 100644
index 0000000000..420128ef29
--- /dev/null
+++ b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/AppxFactory.cs
@@ -0,0 +1,14 @@
+// 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.Runtime.InteropServices;
+
+namespace Microsoft.CmdPal.Ext.Apps.Programs;
+
+// Reference : https://stackoverflow.com/questions/32122679/getting-icon-of-modern-windows-app-from-a-desktop-application
+[Guid("5842a140-ff9f-4166-8f5c-62f5b7b0c781")]
+[ComImport]
+public class AppxFactory
+{
+}
diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/AppxPackageHelper.cs b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/AppxPackageHelper.cs
new file mode 100644
index 0000000000..83a9fbb146
--- /dev/null
+++ b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/AppxPackageHelper.cs
@@ -0,0 +1,45 @@
+// 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.Runtime.InteropServices;
+using Windows.Win32.System.Com;
+using static Microsoft.CmdPal.Ext.Apps.Utils.Native;
+
+namespace Microsoft.CmdPal.Ext.Apps.Programs;
+
+public static class AppxPackageHelper
+{
+ private static readonly IAppxFactory AppxFactory = (IAppxFactory)new AppxFactory();
+
+ // This function returns a list of attributes of applications
+ internal static IEnumerable GetAppsFromManifest(IStream stream)
+ {
+ var reader = AppxFactory.CreateManifestReader(stream);
+ var manifestApps = reader.GetApplications();
+
+ while (manifestApps.GetHasCurrent())
+ {
+ var manifestApp = manifestApps.GetCurrent();
+ var hr = manifestApp.GetStringValue("AppListEntry", out var appListEntry);
+ _ = CheckHRAndReturnOrThrow(hr, appListEntry);
+ if (appListEntry != "none")
+ {
+ yield return manifestApp;
+ }
+
+ manifestApps.MoveNext();
+ }
+ }
+
+ internal static T CheckHRAndReturnOrThrow(HRESULT hr, T result)
+ {
+ if (hr != HRESULT.S_OK)
+ {
+ Marshal.ThrowExceptionForHR((int)hr);
+ }
+
+ return result;
+ }
+}
diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IApplicationActivationManager.cs b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IApplicationActivationManager.cs
new file mode 100644
index 0000000000..32fb3f2890
--- /dev/null
+++ b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IApplicationActivationManager.cs
@@ -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.
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace Microsoft.CmdPal.Ext.Apps.Programs;
+
+// Reference : https://github.com/MicrosoftEdge/edge-launcher/blob/108e63df0b4cb5cd9d5e45aa7a264690851ec51d/MIcrosoftEdgeLauncherCsharp/Program.cs
+[Flags]
+public enum ActivateOptions
+{
+ None = 0x00000000,
+ DesignMode = 0x00000001,
+ NoErrorUI = 0x00000002,
+ NoSplashScreen = 0x00000004,
+}
+
+// ApplicationActivationManager
+[ComImport]
+[Guid("2e941141-7f97-4756-ba1d-9decde894a3d")]
+[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+public interface IApplicationActivationManager
+{
+ IntPtr ActivateApplication([In] string appUserModelId, [In] string arguments, [In] ActivateOptions options, [Out] out uint processId);
+
+ IntPtr ActivateForFile([In] string appUserModelId, [In] IntPtr /*IShellItemArray* */ itemArray, [In] string verb, [Out] out uint processId);
+
+ IntPtr ActivateForProtocol([In] string appUserModelId, [In] IntPtr /* IShellItemArray* */itemArray, [Out] out uint processId);
+}
+
+// Application Activation Manager Class
+[ComImport]
+[Guid("45BA127D-10A8-46EA-8AB7-56EA9078943C")]
+public class ApplicationActivationManager : IApplicationActivationManager
+{
+ [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)/*, PreserveSig*/]
+ public extern IntPtr ActivateApplication([In] string appUserModelId, [In] string arguments, [In] ActivateOptions options, [Out] out uint processId);
+
+ [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
+ public extern IntPtr ActivateForFile([In] string appUserModelId, [In] IntPtr /*IShellItemArray* */ itemArray, [In] string verb, [Out] out uint processId);
+
+ [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
+ public extern IntPtr ActivateForProtocol([In] string appUserModelId, [In] IntPtr /* IShellItemArray* */itemArray, [Out] out uint processId);
+}
diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IAppxFactory.cs b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IAppxFactory.cs
new file mode 100644
index 0000000000..7af82b74ab
--- /dev/null
+++ b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IAppxFactory.cs
@@ -0,0 +1,20 @@
+// 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.Runtime.InteropServices;
+using Windows.Win32.System.Com;
+
+namespace Microsoft.CmdPal.Ext.Apps.Programs;
+
+[Guid("BEB94909-E451-438B-B5A7-D79E767B75D8")]
+[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+public interface IAppxFactory
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "Implements COM Interface")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Implements COM Interface")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Implements COM Interface")]
+ void _VtblGap0_2(); // skip 2 methods
+
+ internal IAppxManifestReader CreateManifestReader(IStream inputStream);
+}
diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestApplication.cs b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestApplication.cs
new file mode 100644
index 0000000000..1ca12d3c29
--- /dev/null
+++ b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestApplication.cs
@@ -0,0 +1,19 @@
+// 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.Runtime.InteropServices;
+using static Microsoft.CmdPal.Ext.Apps.Utils.Native;
+
+namespace Microsoft.CmdPal.Ext.Apps.Programs;
+
+[Guid("5DA89BF4-3773-46BE-B650-7E744863B7E8")]
+[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+public interface IAppxManifestApplication
+{
+ [PreserveSig]
+ HRESULT GetStringValue([MarshalAs(UnmanagedType.LPWStr)] string name, [MarshalAs(UnmanagedType.LPWStr)] out string value);
+
+ [PreserveSig]
+ HRESULT GetAppUserModelId([MarshalAs(UnmanagedType.LPWStr)] out string value);
+}
diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestApplicationsEnumerator.cs b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestApplicationsEnumerator.cs
new file mode 100644
index 0000000000..f7152a0813
--- /dev/null
+++ b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestApplicationsEnumerator.cs
@@ -0,0 +1,19 @@
+// 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;
+using System.Runtime.InteropServices;
+
+namespace Microsoft.CmdPal.Ext.Apps.Programs;
+
+[Guid("9EB8A55A-F04B-4D0D-808D-686185D4847A")]
+[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+public interface IAppxManifestApplicationsEnumerator
+{
+ IAppxManifestApplication GetCurrent();
+
+ bool GetHasCurrent();
+
+ bool MoveNext();
+}
diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestProperties.cs b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestProperties.cs
new file mode 100644
index 0000000000..4c61e6f069
--- /dev/null
+++ b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestProperties.cs
@@ -0,0 +1,19 @@
+// 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.Runtime.InteropServices;
+using static Microsoft.CmdPal.Ext.Apps.Utils.Native;
+
+namespace Microsoft.CmdPal.Ext.Apps.Programs;
+
+[Guid("03FAF64D-F26F-4B2C-AAF7-8FE7789B8BCA")]
+[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+public interface IAppxManifestProperties
+{
+ [PreserveSig]
+ HRESULT GetBoolValue([MarshalAs(UnmanagedType.LPWStr)] string name, out bool value);
+
+ [PreserveSig]
+ HRESULT GetStringValue([MarshalAs(UnmanagedType.LPWStr)] string name, [MarshalAs(UnmanagedType.LPWStr)] out string value);
+}
diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestReader.cs b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestReader.cs
new file mode 100644
index 0000000000..20c7fb62f6
--- /dev/null
+++ b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestReader.cs
@@ -0,0 +1,27 @@
+// 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;
+using System.Runtime.InteropServices;
+
+namespace Microsoft.CmdPal.Ext.Apps.Programs;
+
+[Guid("4E1BD148-55A0-4480-A3D1-15544710637C")]
+[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+public interface IAppxManifestReader
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "Implements COM Interface")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Implements COM Interface")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Implements COM Interface")]
+ void _VtblGap0_1(); // skip 1 method
+
+ IAppxManifestProperties GetProperties();
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "Implements COM Interface")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Implements COM Interface")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Implements COM Interface")]
+ void _VtblGap1_5(); // skip 5 methods
+
+ IAppxManifestApplicationsEnumerator GetApplications();
+}
diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IPackage.cs b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IPackage.cs
new file mode 100644
index 0000000000..84021d5970
--- /dev/null
+++ b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IPackage.cs
@@ -0,0 +1,20 @@
+// 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.
+
+namespace Microsoft.CmdPal.Ext.Apps.Programs;
+
+public interface IPackage
+{
+ string Name { get; }
+
+ string FullName { get; }
+
+ string FamilyName { get; }
+
+ bool IsFramework { get; }
+
+ bool IsDevelopmentMode { get; }
+
+ string InstalledLocation { get; }
+}
diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IPackageManager.cs b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IPackageManager.cs
new file mode 100644
index 0000000000..d7a3383a32
--- /dev/null
+++ b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IPackageManager.cs
@@ -0,0 +1,12 @@
+// 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;
+
+namespace Microsoft.CmdPal.Ext.Apps.Programs;
+
+public interface IPackageManager
+{
+ IEnumerable FindPackagesForCurrentUser();
+}
diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IProgram.cs b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IProgram.cs
new file mode 100644
index 0000000000..2350cf6ae6
--- /dev/null
+++ b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IProgram.cs
@@ -0,0 +1,18 @@
+// 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.
+
+namespace Microsoft.CmdPal.Ext.Apps.Programs;
+
+public interface IProgram
+{
+ string UniqueIdentifier { get; set; }
+
+ string Name { get; }
+
+ string Description { get; set; }
+
+ string Location { get; }
+
+ bool Enabled { get; set; }
+}
diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/LogoType.cs b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/LogoType.cs
new file mode 100644
index 0000000000..2de4ef92cc
--- /dev/null
+++ b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/LogoType.cs
@@ -0,0 +1,12 @@
+// 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.
+
+namespace Microsoft.CmdPal.Ext.Apps.Programs;
+
+public enum LogoType
+{
+ Error,
+ Colored,
+ HighContrast,
+}
diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/PackageManagerWrapper.cs b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/PackageManagerWrapper.cs
new file mode 100644
index 0000000000..be70a0ba95
--- /dev/null
+++ b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/PackageManagerWrapper.cs
@@ -0,0 +1,34 @@
+// 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.Linq;
+using System.Security.Principal;
+using Windows.Management.Deployment;
+
+namespace Microsoft.CmdPal.Ext.Apps.Programs;
+
+public class PackageManagerWrapper : IPackageManager
+{
+ private readonly PackageManager _packageManager;
+
+ public PackageManagerWrapper()
+ {
+ _packageManager = new PackageManager();
+ }
+
+ public IEnumerable FindPackagesForCurrentUser()
+ {
+ var user = WindowsIdentity.GetCurrent().User;
+
+ if (user != null)
+ {
+ var pkgs = _packageManager.FindPackagesForUser(user.Value);
+
+ return pkgs.Select(PackageWrapper.GetWrapperFromPackage).Where(package => package != null);
+ }
+
+ return Enumerable.Empty();
+ }
+}
diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/PackageWrapper.cs b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/PackageWrapper.cs
new file mode 100644
index 0000000000..2de128c05c
--- /dev/null
+++ b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/PackageWrapper.cs
@@ -0,0 +1,75 @@
+// 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;
+using System.IO;
+using Windows.Foundation.Metadata;
+using Package = Windows.ApplicationModel.Package;
+
+namespace Microsoft.CmdPal.Ext.Apps.Programs;
+
+public class PackageWrapper : IPackage
+{
+ public string Name { get; } = string.Empty;
+
+ public string FullName { get; } = string.Empty;
+
+ public string FamilyName { get; } = string.Empty;
+
+ public bool IsFramework { get; }
+
+ public bool IsDevelopmentMode { get; }
+
+ public string InstalledLocation { get; } = string.Empty;
+
+ public PackageWrapper()
+ {
+ }
+
+ public PackageWrapper(string name, string fullName, string familyName, bool isFramework, bool isDevelopmentMode, string installedLocation)
+ {
+ Name = name;
+ FullName = fullName;
+ FamilyName = familyName;
+ IsFramework = isFramework;
+ IsDevelopmentMode = isDevelopmentMode;
+ InstalledLocation = installedLocation;
+ }
+
+ private static readonly Lazy IsPackageDotInstallationPathAvailable = new(() =>
+ ApiInformation.IsPropertyPresent(typeof(Package).FullName, nameof(Package.InstalledLocation.Path)));
+
+ public static PackageWrapper GetWrapperFromPackage(Package package)
+ {
+ ArgumentNullException.ThrowIfNull(package);
+
+ string path;
+ try
+ {
+ path = IsPackageDotInstallationPathAvailable.Value ? GetInstalledPath(package) : package.InstalledLocation.Path;
+ }
+ catch (Exception e) when (e is ArgumentException || e is FileNotFoundException || e is DirectoryNotFoundException)
+ {
+ return new PackageWrapper(
+ package.Id.Name,
+ package.Id.FullName,
+ package.Id.FamilyName,
+ package.IsFramework,
+ package.IsDevelopmentMode,
+ string.Empty);
+ }
+
+ return new PackageWrapper(
+ package.Id.Name,
+ package.Id.FullName,
+ package.Id.FamilyName,
+ package.IsFramework,
+ package.IsDevelopmentMode,
+ path);
+ }
+
+ // This is a separate method so the reference to .InstalledPath won't be loaded in API versions which do not support this API (e.g. older then Build 19041)
+ private static string GetInstalledPath(Package package)
+ => package.InstalledLocation.Path;
+}
diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/ProgramSource.cs b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/ProgramSource.cs
new file mode 100644
index 0000000000..d22b42772e
--- /dev/null
+++ b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/ProgramSource.cs
@@ -0,0 +1,24 @@
+// 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.
+
+namespace Microsoft.CmdPal.Ext.Apps.Programs;
+
+///
+/// Contains user added folder location contents as well as all user disabled applications
+///
+///
+/// Win32 class applications set UniqueIdentifier using their full file path
+/// UWP class applications set UniqueIdentifier using their Application User Model ID
+/// Custom user added program sources set UniqueIdentifier using their location
+///
+public class ProgramSource
+{
+ public string Location { get; set; } = string.Empty;
+
+ public string Name { get; set; } = string.Empty;
+
+ public bool Enabled { get; set; } = true;
+
+ public string UniqueIdentifier { get; set; } = string.Empty;
+}
diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/UWP.cs b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/UWP.cs
new file mode 100644
index 0000000000..beba2b185a
--- /dev/null
+++ b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/UWP.cs
@@ -0,0 +1,203 @@
+// 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;
+using System.Collections.Generic;
+using System.IO.Abstractions;
+using System.Linq;
+using System.Xml.Linq;
+using Microsoft.CmdPal.Ext.Apps.Utils;
+using Windows.Win32;
+using Windows.Win32.Foundation;
+using Windows.Win32.System.Com;
+using static Microsoft.CmdPal.Ext.Apps.Utils.Native;
+
+namespace Microsoft.CmdPal.Ext.Apps.Programs;
+
+[Serializable]
+public partial class UWP
+{
+ private static readonly IPath Path = new FileSystem().Path;
+
+ private static readonly Dictionary _versionFromNamespace = new()
+ {
+ { "http://schemas.microsoft.com/appx/manifest/foundation/windows10", PackageVersion.Windows10 },
+ { "http://schemas.microsoft.com/appx/2013/manifest", PackageVersion.Windows81 },
+ { "http://schemas.microsoft.com/appx/2010/manifest", PackageVersion.Windows8 },
+ };
+
+ public string Name { get; }
+
+ public string FullName { get; }
+
+ public string FamilyName { get; }
+
+ public string Location { get; set; } = string.Empty;
+
+ // Localized path based on windows display language
+ public string LocationLocalized { get; set; } = string.Empty;
+
+ public IList Apps { get; private set; } = new List();
+
+ public PackageVersion Version { get; set; }
+
+ public static IPackageManager PackageManagerWrapper { get; set; } = new PackageManagerWrapper();
+
+ public UWP(IPackage package)
+ {
+ ArgumentNullException.ThrowIfNull(package);
+
+ Name = package.Name;
+ FullName = package.FullName;
+ FamilyName = package.FamilyName;
+ }
+
+ public void InitializeAppInfo(string installedLocation)
+ {
+ Location = installedLocation;
+ LocationLocalized = ShellLocalization.Instance.GetLocalizedPath(installedLocation);
+ var path = Path.Combine(installedLocation, "AppxManifest.xml");
+
+ var namespaces = XmlNamespaces(path);
+ InitPackageVersion(namespaces);
+
+ const uint noAttribute = 0x80;
+
+ var access = (uint)STGM.READ;
+ var hResult = PInvoke.SHCreateStreamOnFileEx(path, access, noAttribute, false, null, out IStream stream);
+
+ // S_OK
+ if (hResult == 0)
+ {
+ Apps = AppxPackageHelper.GetAppsFromManifest(stream).Select(appInManifest => new UWPApplication(appInManifest, this)).Where(a =>
+ {
+ var valid =
+ !string.IsNullOrEmpty(a.UserModelId) &&
+ !string.IsNullOrEmpty(a.DisplayName) &&
+ a.AppListEntry != "none";
+
+ return valid;
+ }).ToList();
+ }
+ else
+ {
+ Apps = Array.Empty();
+ }
+ }
+
+ // http://www.hanselman.com/blog/GetNamespacesFromAnXMLDocumentWithXPathDocumentAndLINQToXML.aspx
+ private static string[] XmlNamespaces(string path)
+ {
+ var z = XDocument.Load(path);
+ if (z.Root != null)
+ {
+ var namespaces = z.Root.Attributes().
+ Where(a => a.IsNamespaceDeclaration).
+ GroupBy(
+ a => a.Name.Namespace == XNamespace.None ? string.Empty : a.Name.LocalName,
+ a => XNamespace.Get(a.Value)).Select(
+ g => g.First().ToString()).ToArray();
+ return namespaces;
+ }
+ else
+ {
+ return Array.Empty();
+ }
+ }
+
+ private void InitPackageVersion(string[] namespaces)
+ {
+ foreach (var n in _versionFromNamespace.Keys.Where(namespaces.Contains))
+ {
+ Version = _versionFromNamespace[n];
+ return;
+ }
+
+ Version = PackageVersion.Unknown;
+ }
+
+ public static UWPApplication[] All()
+ {
+ var windows10 = new Version(10, 0);
+ var support = Environment.OSVersion.Version.Major >= windows10.Major;
+ if (support)
+ {
+ var applications = CurrentUserPackages().AsParallel().SelectMany(p =>
+ {
+ UWP u;
+ try
+ {
+ u = new UWP(p);
+ u.InitializeAppInfo(p.InstalledLocation);
+ }
+ catch (Exception )
+ {
+ return Array.Empty();
+ }
+
+ return u.Apps;
+ });
+
+ var updatedListWithoutDisabledApps = applications
+ .Where(t1 => AllAppsSettings.Instance.DisabledProgramSources.All(x => x.UniqueIdentifier != t1.UniqueIdentifier))
+ .Select(x => x);
+
+ return updatedListWithoutDisabledApps.ToArray();
+ }
+ else
+ {
+ return Array.Empty();
+ }
+ }
+
+ private static IEnumerable CurrentUserPackages()
+ {
+ return PackageManagerWrapper.FindPackagesForCurrentUser().Where(p =>
+ {
+ try
+ {
+ var f = p.IsFramework;
+ var path = p.InstalledLocation;
+ return !f && !string.IsNullOrEmpty(path);
+ }
+ catch (Exception )
+ {
+ return false;
+ }
+ });
+ }
+
+ public override string ToString()
+ {
+ return FamilyName;
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1309:Use ordinal string comparison", Justification = "Using CurrentCultureIgnoreCase since this is used with FamilyName")]
+ public override bool Equals(object? obj)
+ {
+ if (obj is UWP uwp)
+ {
+ // Using CurrentCultureIgnoreCase since this is used with FamilyName
+ return FamilyName.Equals(uwp.FamilyName, StringComparison.CurrentCultureIgnoreCase);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ public override int GetHashCode()
+ {
+ // Using CurrentCultureIgnoreCase since this is used with FamilyName
+ return FamilyName.GetHashCode(StringComparison.CurrentCultureIgnoreCase);
+ }
+
+ public enum PackageVersion
+ {
+ Windows10,
+ Windows81,
+ Windows8,
+ Unknown,
+ }
+}
diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/UWPApplication.cs b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/UWPApplication.cs
new file mode 100644
index 0000000000..6a4cf94480
--- /dev/null
+++ b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/UWPApplication.cs
@@ -0,0 +1,608 @@
+// 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;
+using System.Collections.Generic;
+using System.IO.Abstractions;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Xml;
+using Microsoft.CmdPal.Ext.Apps.Commands;
+using Microsoft.CmdPal.Ext.Apps.Properties;
+using Microsoft.CmdPal.Ext.Apps.Utils;
+using Microsoft.CommandPalette.Extensions;
+using Microsoft.CommandPalette.Extensions.Toolkit;
+using static Microsoft.CmdPal.Ext.Apps.Utils.Native;
+using PackageVersion = Microsoft.CmdPal.Ext.Apps.Programs.UWP.PackageVersion;
+
+namespace Microsoft.CmdPal.Ext.Apps.Programs;
+
+[Serializable]
+public class UWPApplication : IProgram
+{
+ private static readonly IFileSystem FileSystem = new FileSystem();
+ private static readonly IPath Path = FileSystem.Path;
+ private static readonly IFile File = FileSystem.File;
+
+ public string AppListEntry { get; set; } = string.Empty;
+
+ public string UniqueIdentifier { get; set; }
+
+ public string DisplayName { get; set; }
+
+ public string Description { get; set; }
+
+ public string UserModelId { get; set; }
+
+ public string BackgroundColor { get; set; }
+
+ public string EntryPoint { get; set; }
+
+ public string Name => DisplayName;
+
+ public string Location => Package.Location;
+
+ // Localized path based on windows display language
+ public string LocationLocalized => Package.LocationLocalized;
+
+ public bool Enabled { get; set; }
+
+ public bool CanRunElevated { get; set; }
+
+ public string LogoPath { get; set; } = string.Empty;
+
+ public LogoType LogoType { get; set; }
+
+ public UWP Package { get; set; }
+
+ private string logoUri;
+
+ private const string ContrastWhite = "contrast-white";
+
+ private const string ContrastBlack = "contrast-black";
+
+ // Function to set the subtitle based on the Type of application
+ public static string Type()
+ {
+ return Resources.packaged_application;
+ }
+
+ public List GetCommands()
+ {
+ List commands = new List();
+
+ if (CanRunElevated)
+ {
+ commands.Add(
+ new CommandContextItem(
+ new RunAsAdminCommand(UniqueIdentifier, string.Empty, true)));
+
+ // We don't add context menu to 'run as different user', because UWP applications normally installed per user and not for all users.
+ }
+
+ commands.Add(
+ new CommandContextItem(
+ new OpenPathCommand(Location)
+ {
+ Name = Resources.open_containing_folder,
+ Icon = new("\ue838"),
+ }));
+
+ commands.Add(
+ new CommandContextItem(
+ new OpenInConsoleCommand(Package.Location)));
+
+ return commands;
+ }
+
+ public UWPApplication(IAppxManifestApplication manifestApp, UWP package)
+ {
+ ArgumentNullException.ThrowIfNull(manifestApp);
+
+ var hr = manifestApp.GetAppUserModelId(out var tmpUserModelId);
+ UserModelId = AppxPackageHelper.CheckHRAndReturnOrThrow(hr, tmpUserModelId);
+
+ hr = manifestApp.GetAppUserModelId(out var tmpUniqueIdentifier);
+ UniqueIdentifier = AppxPackageHelper.CheckHRAndReturnOrThrow(hr, tmpUniqueIdentifier);
+
+ hr = manifestApp.GetStringValue("DisplayName", out var tmpDisplayName);
+ DisplayName = AppxPackageHelper.CheckHRAndReturnOrThrow(hr, tmpDisplayName);
+
+ hr = manifestApp.GetStringValue("Description", out var tmpDescription);
+ Description = AppxPackageHelper.CheckHRAndReturnOrThrow(hr, tmpDescription);
+
+ hr = manifestApp.GetStringValue("BackgroundColor", out var tmpBackgroundColor);
+ BackgroundColor = AppxPackageHelper.CheckHRAndReturnOrThrow(hr, tmpBackgroundColor);
+
+ hr = manifestApp.GetStringValue("EntryPoint", out var tmpEntryPoint);
+ EntryPoint = AppxPackageHelper.CheckHRAndReturnOrThrow(hr, tmpEntryPoint);
+
+ Package = package ?? throw new ArgumentNullException(nameof(package));
+
+ DisplayName = ResourceFromPri(package.FullName, DisplayName);
+ Description = ResourceFromPri(package.FullName, Description);
+ logoUri = LogoUriFromManifest(manifestApp);
+
+ Enabled = true;
+ CanRunElevated = IfApplicationCanRunElevated();
+ }
+
+ private bool IfApplicationCanRunElevated()
+ {
+ if (EntryPoint == "Windows.FullTrustApplication")
+ {
+ return true;
+ }
+ else
+ {
+ var manifest = Package.Location + "\\AppxManifest.xml";
+ if (File.Exists(manifest))
+ {
+ try
+ {
+ // Check the manifest to verify if the Trust Level for the application is "mediumIL"
+ var file = File.ReadAllText(manifest);
+ var xmlDoc = new XmlDocument();
+ xmlDoc.LoadXml(file);
+ var xmlRoot = xmlDoc.DocumentElement;
+ var namespaceManager = new XmlNamespaceManager(xmlDoc.NameTable);
+ namespaceManager.AddNamespace("uap10", "http://schemas.microsoft.com/appx/manifest/uap/windows10/10");
+ var trustLevelNode = xmlRoot?.SelectSingleNode("//*[local-name()='Application' and @uap10:TrustLevel]", namespaceManager); // According to https://learn.microsoft.com/windows/apps/desktop/modernize/grant-identity-to-nonpackaged-apps#create-a-package-manifest-for-the-sparse-package and https://learn.microsoft.com/uwp/schemas/appxpackage/uapmanifestschema/element-application#attributes
+
+ if (trustLevelNode?.Attributes?["uap10:TrustLevel"]?.Value == "mediumIL")
+ {
+ return true;
+ }
+ }
+ catch (Exception)
+ {
+ }
+ }
+ }
+
+ return false;
+ }
+
+ internal string ResourceFromPri(string packageFullName, string resourceReference)
+ {
+ const string prefix = "ms-resource:";
+
+ // Using OrdinalIgnoreCase since this is used internally
+ if (!string.IsNullOrWhiteSpace(resourceReference) && resourceReference.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
+ {
+ // magic comes from @talynone
+ // https://github.com/talynone/Wox.Plugin.WindowsUniversalAppLauncher/blob/master/StoreAppLauncher/Helpers/NativeApiHelper.cs#L139-L153
+ var key = resourceReference.Substring(prefix.Length);
+ string parsed;
+ var parsedFallback = string.Empty;
+
+ // Using Ordinal/OrdinalIgnoreCase since these are used internally
+ if (key.StartsWith("//", StringComparison.Ordinal))
+ {
+ parsed = prefix + key;
+ }
+ else if (key.StartsWith('/'))
+ {
+ parsed = prefix + "//" + key;
+ }
+ else if (key.Contains("resources", StringComparison.OrdinalIgnoreCase))
+ {
+ parsed = prefix + key;
+ }
+ else
+ {
+ parsed = prefix + "///resources/" + key;
+
+ // e.g. for Windows Terminal version >= 1.12 DisplayName and Description resources are not in the 'resources' subtree
+ parsedFallback = prefix + "///" + key;
+ }
+
+ var outBuffer = new StringBuilder(128);
+ var source = $"@{{{packageFullName}? {parsed}}}";
+ var capacity = (uint)outBuffer.Capacity;
+ var hResult = SHLoadIndirectString(source, outBuffer, capacity, IntPtr.Zero);
+ if (hResult != HRESULT.S_OK)
+ {
+ if (!string.IsNullOrEmpty(parsedFallback))
+ {
+ var sourceFallback = $"@{{{packageFullName}? {parsedFallback}}}";
+ hResult = SHLoadIndirectString(sourceFallback, outBuffer, capacity, IntPtr.Zero);
+ if (hResult == HRESULT.S_OK)
+ {
+ var loaded = outBuffer.ToString();
+ if (!string.IsNullOrEmpty(loaded))
+ {
+ return loaded;
+ }
+ else
+ {
+ return string.Empty;
+ }
+ }
+ }
+
+ // https://github.com/Wox-launcher/Wox/issues/964
+ // known hresult 2147942522:
+ // 'Microsoft Corporation' violates pattern constraint of '\bms-resource:.{1,256}'.
+ // for
+ // Microsoft.MicrosoftOfficeHub_17.7608.23501.0_x64__8wekyb3d8bbwe: ms-resource://Microsoft.MicrosoftOfficeHub/officehubintl/AppManifest_GetOffice_Description
+ // Microsoft.BingFoodAndDrink_3.0.4.336_x64__8wekyb3d8bbwe: ms-resource:AppDescription
+ return string.Empty;
+ }
+ else
+ {
+ var loaded = outBuffer.ToString();
+ if (!string.IsNullOrEmpty(loaded))
+ {
+ return loaded;
+ }
+ else
+ {
+ return string.Empty;
+ }
+ }
+ }
+ else
+ {
+ return resourceReference;
+ }
+ }
+
+ private static readonly Dictionary _logoKeyFromVersion = new Dictionary
+ {
+ { PackageVersion.Windows10, "Square44x44Logo" },
+ { PackageVersion.Windows81, "Square30x30Logo" },
+ { PackageVersion.Windows8, "SmallLogo" },
+ };
+
+ internal string LogoUriFromManifest(IAppxManifestApplication app)
+ {
+ if (_logoKeyFromVersion.TryGetValue(Package.Version, out var key))
+ {
+ var hr = app.GetStringValue(key, out var logoUriFromApp);
+ _ = AppxPackageHelper.CheckHRAndReturnOrThrow(hr, logoUriFromApp);
+ return logoUriFromApp;
+ }
+ else
+ {
+ return string.Empty;
+ }
+ }
+
+ public void UpdateLogoPath(Theme theme)
+ {
+ LogoPathFromUri(logoUri, theme);
+ }
+
+ // scale factors on win10: https://learn.microsoft.com/windows/uwp/controls-and-patterns/tiles-and-notifications-app-assets#asset-size-tables,
+ private static readonly Dictionary> _scaleFactors = new Dictionary>
+ {
+ { PackageVersion.Windows10, new List { 100, 125, 150, 200, 400 } },
+ { PackageVersion.Windows81, new List { 100, 120, 140, 160, 180 } },
+ { PackageVersion.Windows8, new List { 100 } },
+ };
+
+ private bool SetScaleIcons(string path, string colorscheme, bool highContrast = false)
+ {
+ var extension = Path.GetExtension(path);
+ if (extension != null)
+ {
+ var end = path.Length - extension.Length;
+ var prefix = path.Substring(0, end);
+ var paths = new List { };
+
+ if (!highContrast)
+ {
+ paths.Add(path);
+ }
+
+ if (_scaleFactors.TryGetValue(Package.Version, out var factors))
+ {
+ foreach (var factor in factors)
+ {
+ if (highContrast)
+ {
+ paths.Add($"{prefix}.scale-{factor}_{colorscheme}{extension}");
+ paths.Add($"{prefix}.{colorscheme}_scale-{factor}{extension}");
+ }
+ else
+ {
+ paths.Add($"{prefix}.scale-{factor}{extension}");
+ }
+ }
+ }
+
+ var selectedIconPath = paths.FirstOrDefault(File.Exists);
+ if (!string.IsNullOrEmpty(selectedIconPath))
+ {
+ LogoPath = selectedIconPath;
+ if (highContrast)
+ {
+ LogoType = LogoType.HighContrast;
+ }
+ else
+ {
+ LogoType = LogoType.Colored;
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private bool SetTargetSizeIcon(string path, string colorscheme, bool highContrast = false)
+ {
+ var extension = Path.GetExtension(path);
+ if (extension != null)
+ {
+ var end = path.Length - extension.Length;
+ var prefix = path.Substring(0, end);
+ var paths = new List { };
+ const int appIconSize = 36;
+ var targetSizes = new List { 16, 24, 30, 36, 44, 60, 72, 96, 128, 180, 256 }.AsParallel();
+ var pathFactorPairs = new Dictionary();
+
+ foreach (var factor in targetSizes)
+ {
+ if (highContrast)
+ {
+ var suffixThemePath = $"{prefix}.targetsize-{factor}_{colorscheme}{extension}";
+ var prefixThemePath = $"{prefix}.{colorscheme}_targetsize-{factor}{extension}";
+ paths.Add(suffixThemePath);
+ paths.Add(prefixThemePath);
+ pathFactorPairs.Add(suffixThemePath, factor);
+ pathFactorPairs.Add(prefixThemePath, factor);
+ }
+ else
+ {
+ var simplePath = $"{prefix}.targetsize-{factor}{extension}";
+ var altformUnPlatedPath = $"{prefix}.targetsize-{factor}_altform-unplated{extension}";
+ paths.Add(simplePath);
+ paths.Add(altformUnPlatedPath);
+ pathFactorPairs.Add(simplePath, factor);
+ pathFactorPairs.Add(altformUnPlatedPath, factor);
+ }
+ }
+
+ var selectedIconPath = paths.OrderBy(x => Math.Abs(pathFactorPairs.GetValueOrDefault(x) - appIconSize)).FirstOrDefault(File.Exists);
+ if (!string.IsNullOrEmpty(selectedIconPath))
+ {
+ LogoPath = selectedIconPath;
+ if (highContrast)
+ {
+ LogoType = LogoType.HighContrast;
+ }
+ else
+ {
+ LogoType = LogoType.Colored;
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private bool SetColoredIcon(string path, string colorscheme)
+ {
+ var isSetColoredScaleIcon = SetScaleIcons(path, colorscheme);
+ if (isSetColoredScaleIcon)
+ {
+ return true;
+ }
+
+ var isSetColoredTargetIcon = SetTargetSizeIcon(path, colorscheme);
+ if (isSetColoredTargetIcon)
+ {
+ return true;
+ }
+
+ var isSetHighContrastScaleIcon = SetScaleIcons(path, colorscheme, true);
+ if (isSetHighContrastScaleIcon)
+ {
+ return true;
+ }
+
+ var isSetHighContrastTargetIcon = SetTargetSizeIcon(path, colorscheme, true);
+ if (isSetHighContrastTargetIcon)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ private bool SetHighContrastIcon(string path, string colorscheme)
+ {
+ var isSetHighContrastScaleIcon = SetScaleIcons(path, colorscheme, true);
+ if (isSetHighContrastScaleIcon)
+ {
+ return true;
+ }
+
+ var isSetHighContrastTargetIcon = SetTargetSizeIcon(path, colorscheme, true);
+ if (isSetHighContrastTargetIcon)
+ {
+ return true;
+ }
+
+ var isSetColoredScaleIcon = SetScaleIcons(path, colorscheme);
+ if (isSetColoredScaleIcon)
+ {
+ return true;
+ }
+
+ var isSetColoredTargetIcon = SetTargetSizeIcon(path, colorscheme);
+ if (isSetColoredTargetIcon)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ internal void LogoPathFromUri(string uri, Theme theme)
+ {
+ // all https://learn.microsoft.com/windows/uwp/controls-and-patterns/tiles-and-notifications-app-assets
+ // windows 10 https://msdn.microsoft.com/library/windows/apps/dn934817.aspx
+ // windows 8.1 https://msdn.microsoft.com/library/windows/apps/hh965372.aspx#target_size
+ // windows 8 https://msdn.microsoft.com/library/windows/apps/br211475.aspx
+ string path;
+ bool isLogoUriSet;
+
+ // Using Ordinal since this is used internally with uri
+ if (uri.Contains('\\', StringComparison.Ordinal))
+ {
+ path = Path.Combine(Package.Location, uri);
+ }
+ else
+ {
+ // for C:\Windows\MiracastView etc
+ path = Path.Combine(Package.Location, "Assets", uri);
+ }
+
+ switch (theme)
+ {
+ case Theme.HighContrastBlack:
+ case Theme.HighContrastOne:
+ case Theme.HighContrastTwo:
+ isLogoUriSet = SetHighContrastIcon(path, ContrastBlack);
+ break;
+ case Theme.HighContrastWhite:
+ isLogoUriSet = SetHighContrastIcon(path, ContrastWhite);
+ break;
+ case Theme.Light:
+ isLogoUriSet = SetColoredIcon(path, ContrastWhite);
+ break;
+ default:
+ isLogoUriSet = SetColoredIcon(path, ContrastBlack);
+ break;
+ }
+
+ if (!isLogoUriSet)
+ {
+ LogoPath = string.Empty;
+ LogoType = LogoType.Error;
+ }
+ }
+
+ /*
+ public ImageSource Logo()
+ {
+ if (LogoType == LogoType.Colored)
+ {
+ var logo = ImageFromPath(LogoPath);
+ var platedImage = PlatedImage(logo);
+ return platedImage;
+ }
+ else
+ {
+ return ImageFromPath(LogoPath);
+ }
+ }
+
+ private const int _dpiScale100 = 96;
+
+ private ImageSource PlatedImage(BitmapImage image)
+ {
+ if (!string.IsNullOrEmpty(BackgroundColor))
+ {
+ string currentBackgroundColor;
+ if (BackgroundColor == "transparent")
+ {
+ // Using InvariantCulture since this is internal
+ currentBackgroundColor = SystemParameters.WindowGlassBrush.ToString(CultureInfo.InvariantCulture);
+ }
+ else
+ {
+ currentBackgroundColor = BackgroundColor;
+ }
+
+ var padding = 8;
+ var width = image.Width + (2 * padding);
+ var height = image.Height + (2 * padding);
+ var x = 0;
+ var y = 0;
+
+ var group = new DrawingGroup();
+ var converted = ColorConverter.ConvertFromString(currentBackgroundColor);
+ if (converted != null)
+ {
+ var color = (Color)converted;
+ var brush = new SolidColorBrush(color);
+ var pen = new Pen(brush, 1);
+ var backgroundArea = new Rect(0, 0, width, height);
+ var rectangleGeometry = new RectangleGeometry(backgroundArea, 8, 8);
+ var rectDrawing = new GeometryDrawing(brush, pen, rectangleGeometry);
+ group.Children.Add(rectDrawing);
+
+ var imageArea = new Rect(x + padding, y + padding, image.Width, image.Height);
+ var imageDrawing = new ImageDrawing(image, imageArea);
+ group.Children.Add(imageDrawing);
+
+ // http://stackoverflow.com/questions/6676072/get-system-drawing-bitmap-of-a-wpf-area-using-visualbrush
+ var visual = new DrawingVisual();
+ var context = visual.RenderOpen();
+ context.DrawDrawing(group);
+ context.Close();
+
+ var bitmap = new RenderTargetBitmap(
+ Convert.ToInt32(width),
+ Convert.ToInt32(height),
+ _dpiScale100,
+ _dpiScale100,
+ PixelFormats.Pbgra32);
+
+ bitmap.Render(visual);
+
+ return bitmap;
+ }
+ else
+ {
+ ProgramLogger.Exception($"Unable to convert background string {BackgroundColor} to color for {Package.Location}", new InvalidOperationException(), GetType(), Package.Location);
+
+ return new BitmapImage(new Uri(Constant.ErrorIcon));
+ }
+ }
+ else
+ {
+ // todo use windows theme as background
+ return image;
+ }
+ }
+
+ private BitmapImage ImageFromPath(string path)
+ {
+ if (File.Exists(path))
+ {
+ var memoryStream = new MemoryStream();
+ using (var fileStream = File.OpenRead(path))
+ {
+ fileStream.CopyTo(memoryStream);
+ memoryStream.Position = 0;
+
+ var image = new BitmapImage();
+ image.BeginInit();
+ image.StreamSource = memoryStream;
+ image.EndInit();
+ return image;
+ }
+ }
+ else
+ {
+ // ProgramLogger.Exception($"Unable to get logo for {UserModelId} from {path} and located in {Package.Location}", new FileNotFoundException(), GetType(), path);
+ return new BitmapImage(new Uri(ImageLoader.ErrorIconPath));
+ }
+ }
+ */
+
+ public override string ToString()
+ {
+ return $"{DisplayName}: {Description}";
+ }
+}
diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/Win32Program.cs b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/Win32Program.cs
new file mode 100644
index 0000000000..48dfaa2f7e
--- /dev/null
+++ b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/Win32Program.cs
@@ -0,0 +1,847 @@
+// 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;
+using System.Collections.Generic;
+using System.ComponentModel.Design;
+using System.Diagnostics;
+using System.Globalization;
+using System.IO;
+using System.IO.Abstractions;
+using System.Linq;
+using System.Reflection;
+using System.Security;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using System.Windows.Input;
+using Microsoft.CmdPal.Ext.Apps.Commands;
+using Microsoft.CmdPal.Ext.Apps.Properties;
+using Microsoft.CmdPal.Ext.Apps.Utils;
+using Microsoft.CommandPalette.Extensions.Toolkit;
+using Microsoft.Win32;
+
+namespace Microsoft.CmdPal.Ext.Apps.Programs;
+
+[Serializable]
+public class Win32Program : IProgram
+{
+ public static readonly Win32Program InvalidProgram = new Win32Program { Valid = false, Enabled = false };
+
+ private static readonly IFileSystem FileSystem = new FileSystem();
+ private static readonly IPath Path = FileSystem.Path;
+ private static readonly IFile File = FileSystem.File;
+ private static readonly IDirectory Directory = FileSystem.Directory;
+
+ public string Name { get; set; } = string.Empty;
+
+ // Localized name based on windows display language
+ public string NameLocalized { get; set; } = string.Empty;
+
+ public string UniqueIdentifier { get; set; } = string.Empty;
+
+ public string IcoPath { get; set; } = string.Empty;
+
+ public string Description { get; set; } = string.Empty;
+
+ // Path of app executable or lnk target executable
+ public string FullPath { get; set; } = string.Empty;
+
+ // Localized path based on windows display language
+ public string FullPathLocalized { get; set; } = string.Empty;
+
+ public string ParentDirectory { get; set; } = string.Empty;
+
+ public string ExecutableName { get; set; } = string.Empty;
+
+ // Localized executable name based on windows display language
+ public string ExecutableNameLocalized { get; set; } = string.Empty;
+
+ // Path to the lnk file on LnkProgram
+ public string LnkFilePath { get; set; } = string.Empty;
+
+ public string LnkResolvedExecutableName { get; set; } = string.Empty;
+
+ // Localized path based on windows display language
+ public string LnkResolvedExecutableNameLocalized { get; set; } = string.Empty;
+
+ public bool Valid { get; set; }
+
+ public bool Enabled { get; set; }
+
+ public bool HasArguments => !string.IsNullOrEmpty(Arguments);
+
+ public string Arguments { get; set; } = string.Empty;
+
+ public string Location => ParentDirectory;
+
+ public ApplicationType AppType { get; set; }
+
+ // Wrappers for File Operations
+ public static IFileVersionInfoWrapper FileVersionInfoWrapper { get; set; } = new FileVersionInfoWrapper();
+
+ public static IFile FileWrapper { get; set; } = new FileSystem().File;
+
+ private const string ShortcutExtension = "lnk";
+ private const string ApplicationReferenceExtension = "appref-ms";
+ private const string InternetShortcutExtension = "url";
+ private static readonly HashSet ExecutableApplicationExtensions = new HashSet(StringComparer.OrdinalIgnoreCase) { "exe", "bat", "bin", "com", "cpl", "msc", "msi", "cmd", "ps1", "job", "msp", "mst", "sct", "ws", "wsh", "wsf" };
+
+ private const string ProxyWebApp = "_proxy.exe";
+ private const string AppIdArgument = "--app-id";
+
+ public enum ApplicationType
+ {
+ WebApplication = 0,
+ InternetShortcutApplication = 1,
+ Win32Application = 2,
+ ShortcutApplication = 3,
+ ApprefApplication = 4,
+ RunCommand = 5,
+ Folder = 6,
+ GenericFile = 7,
+ }
+
+ public bool IsWebApplication()
+ {
+ // To Filter PWAs when the user searches for the main application
+ // All Chromium based applications contain the --app-id argument
+ // Reference : https://codereview.chromium.org/399045
+ // Using Ordinal IgnoreCase since this is used internally
+ return !string.IsNullOrEmpty(FullPath) &&
+ !string.IsNullOrEmpty(Arguments) &&
+ FullPath.Contains(ProxyWebApp, StringComparison.OrdinalIgnoreCase) &&
+ Arguments.Contains(AppIdArgument, StringComparison.OrdinalIgnoreCase);
+ }
+
+ // Condition to Filter pinned Web Applications or PWAs when searching for the main application
+ public bool FilterWebApplication(string query)
+ {
+ // If the app is not a web application, then do not filter it
+ if (!IsWebApplication())
+ {
+ return false;
+ }
+
+ var subqueries = query?.Split() ?? Array.Empty();
+ var nameContainsQuery = false;
+ var pathContainsQuery = false;
+
+ // check if any space separated query is a part of the app name or path name
+ foreach (var subquery in subqueries)
+ {
+ // Using OrdinalIgnoreCase since these are used internally
+ if (FullPath.Contains(subquery, StringComparison.OrdinalIgnoreCase))
+ {
+ pathContainsQuery = true;
+ }
+
+ if (Name.Contains(subquery, StringComparison.OrdinalIgnoreCase))
+ {
+ nameContainsQuery = true;
+ }
+ }
+
+ return pathContainsQuery && !nameContainsQuery;
+ }
+
+ // Function to set the subtitle based on the Type of application
+ public string Type()
+ {
+ switch (AppType)
+ {
+ case ApplicationType.Win32Application:
+ case ApplicationType.ShortcutApplication:
+ case ApplicationType.ApprefApplication:
+ return Resources.application;
+ case ApplicationType.InternetShortcutApplication:
+ return Resources.internet_shortcut_application;
+ case ApplicationType.WebApplication:
+ return Resources.web_application;
+ case ApplicationType.RunCommand:
+ return Resources.run_command;
+ case ApplicationType.Folder:
+ return Resources.folder;
+ case ApplicationType.GenericFile:
+ return Resources.file;
+ default:
+ return string.Empty;
+ }
+ }
+
+ public bool QueryEqualsNameForRunCommands(string query)
+ {
+ if (query != null && AppType == ApplicationType.RunCommand)
+ {
+ // Using OrdinalIgnoreCase since this is used internally
+ if (!query.Equals(Name, StringComparison.OrdinalIgnoreCase) && !query.Equals(ExecutableName, StringComparison.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public List GetCommands()
+ {
+ List commands = new List();
+
+ if (AppType != ApplicationType.InternetShortcutApplication && AppType != ApplicationType.Folder && AppType != ApplicationType.GenericFile)
+ {
+ commands.Add(new CommandContextItem(
+ new RunAsAdminCommand(!string.IsNullOrEmpty(LnkFilePath) ? LnkFilePath : FullPath, ParentDirectory, false)));
+
+ commands.Add(new CommandContextItem(
+ new RunAsUserCommand(!string.IsNullOrEmpty(LnkFilePath) ? LnkFilePath : FullPath, ParentDirectory)));
+ }
+
+ commands.Add(new CommandContextItem(
+ new OpenPathCommand(ParentDirectory)));
+
+ commands.Add(new CommandContextItem(
+ new OpenInConsoleCommand(ParentDirectory)));
+
+ return commands;
+ }
+
+ public override string ToString()
+ {
+ return ExecutableName;
+ }
+
+ private static Win32Program CreateWin32Program(string path)
+ {
+ try
+ {
+ var parentDir = Directory.GetParent(path);
+
+ return new Win32Program
+ {
+ Name = Path.GetFileNameWithoutExtension(path),
+ ExecutableName = Path.GetFileName(path),
+ IcoPath = path,
+
+ // Using InvariantCulture since this is user facing
+ FullPath = path,
+ UniqueIdentifier = path,
+ ParentDirectory = parentDir is null ? string.Empty : parentDir.FullName,
+ Description = string.Empty,
+ Valid = true,
+ Enabled = true,
+ AppType = ApplicationType.Win32Application,
+
+ // Localized name, path and executable based on windows display language
+ NameLocalized = ShellLocalization.Instance.GetLocalizedName(path),
+ FullPathLocalized = ShellLocalization.Instance.GetLocalizedPath(path),
+ ExecutableNameLocalized = Path.GetFileName(ShellLocalization.Instance.GetLocalizedPath(path)),
+ };
+ }
+ catch (Exception e) when (e is SecurityException || e is UnauthorizedAccessException)
+ {
+ return InvalidProgram;
+ }
+ catch (Exception)
+ {
+ return InvalidProgram;
+ }
+ }
+
+ private static readonly Regex InternetShortcutURLPrefixes = new Regex(@"^steam:\/\/(rungameid|run|open)\/|^com\.epicgames\.launcher:\/\/apps\/", RegexOptions.Compiled);
+
+ // This function filters Internet Shortcut programs
+ private static Win32Program InternetShortcutProgram(string path)
+ {
+ try
+ {
+ // We don't want to read the whole file if we don't need to
+ var lines = FileWrapper.ReadLines(path);
+ var iconPath = string.Empty;
+ var urlPath = string.Empty;
+ var validApp = false;
+
+ const string urlPrefix = "URL=";
+ const string iconFilePrefix = "IconFile=";
+
+ foreach (var line in lines)
+ {
+ // Using OrdinalIgnoreCase since this is used internally
+ if (line.StartsWith(urlPrefix, StringComparison.OrdinalIgnoreCase))
+ {
+ urlPath = line.Substring(urlPrefix.Length);
+
+ if (!Uri.TryCreate(urlPath, UriKind.RelativeOrAbsolute, out var _))
+ {
+ return InvalidProgram;
+ }
+
+ // To filter out only those steam shortcuts which have 'run' or 'rungameid' as the hostname
+ if (InternetShortcutURLPrefixes.Match(urlPath).Success)
+ {
+ validApp = true;
+ }
+ }
+ else if (line.StartsWith(iconFilePrefix, StringComparison.OrdinalIgnoreCase))
+ {
+ iconPath = line.Substring(iconFilePrefix.Length);
+ }
+
+ // If we resolved an urlPath & and an iconPath quit reading the file
+ if (!string.IsNullOrEmpty(urlPath) && !string.IsNullOrEmpty(iconPath))
+ {
+ break;
+ }
+ }
+
+ if (!validApp)
+ {
+ return InvalidProgram;
+ }
+
+ try
+ {
+ var parentDir = Directory.GetParent(path);
+
+ return new Win32Program
+ {
+ Name = Path.GetFileNameWithoutExtension(path),
+ ExecutableName = Path.GetFileName(path),
+ IcoPath = iconPath,
+ FullPath = urlPath,
+ UniqueIdentifier = path,
+ ParentDirectory = parentDir is null ? string.Empty : parentDir.FullName,
+ Valid = true,
+ Enabled = true,
+ AppType = ApplicationType.InternetShortcutApplication,
+ };
+ }
+ catch (Exception e) when (e is SecurityException || e is UnauthorizedAccessException)
+ {
+ return InvalidProgram;
+ }
+ }
+ catch (Exception)
+ {
+ return InvalidProgram;
+ }
+ }
+
+ private static Win32Program LnkProgram(string path)
+ {
+ try
+ {
+ var program = CreateWin32Program(path);
+ var shellLinkHelper = new ShellLinkHelper();
+ var target = shellLinkHelper.RetrieveTargetPath(path);
+
+ if (!string.IsNullOrEmpty(target))
+ {
+ if (!(File.Exists(target) || Directory.Exists(target)))
+ {
+ // If the link points nowhere, consider it invalid.
+ return InvalidProgram;
+ }
+
+ program.LnkFilePath = program.FullPath;
+ program.LnkResolvedExecutableName = Path.GetFileName(target);
+ program.LnkResolvedExecutableNameLocalized = Path.GetFileName(ShellLocalization.Instance.GetLocalizedPath(target));
+
+ // Using CurrentCulture since this is user facing
+ program.FullPath = Path.GetFullPath(target);
+ program.FullPathLocalized = ShellLocalization.Instance.GetLocalizedPath(target);
+
+ program.Arguments = shellLinkHelper.Arguments;
+
+ // A .lnk could be a (Chrome) PWA, set correct AppType
+ program.AppType = program.IsWebApplication()
+ ? ApplicationType.WebApplication
+ : GetAppTypeFromPath(target);
+
+ var description = shellLinkHelper.Description;
+ if (!string.IsNullOrEmpty(description))
+ {
+ program.Description = description;
+ }
+ else
+ {
+ var info = FileVersionInfoWrapper.GetVersionInfo(target);
+ if (!string.IsNullOrEmpty(info?.FileDescription))
+ {
+ program.Description = info.FileDescription;
+ }
+ }
+ }
+
+ return program;
+ }
+ catch (System.IO.FileLoadException)
+ {
+ return InvalidProgram;
+ }
+
+ // Only do a catch all in production. This is so make developer aware of any unhandled exception and add the exception handling in.
+ // Error caused likely due to trying to get the description of the program
+ catch (Exception)
+ {
+ return InvalidProgram;
+ }
+ }
+
+ private static Win32Program ExeProgram(string path)
+ {
+ try
+ {
+ var program = CreateWin32Program(path);
+ var info = FileVersionInfoWrapper.GetVersionInfo(path);
+ if (!string.IsNullOrEmpty(info?.FileDescription))
+ {
+ program.Description = info.FileDescription;
+ }
+
+ return program;
+ }
+ catch (Exception e) when (e is SecurityException || e is UnauthorizedAccessException)
+ {
+ return InvalidProgram;
+ }
+ catch (FileNotFoundException)
+ {
+ return InvalidProgram;
+ }
+ catch (Exception)
+ {
+ return InvalidProgram;
+ }
+ }
+
+ // Function to get the application type, given the path to the application
+ public static ApplicationType GetAppTypeFromPath(string path)
+ {
+ ArgumentNullException.ThrowIfNull(path);
+
+ var extension = Extension(path);
+
+ // Using OrdinalIgnoreCase since these are used internally with paths
+ if (ExecutableApplicationExtensions.Contains(extension))
+ {
+ return ApplicationType.Win32Application;
+ }
+ else if (extension.Equals(ShortcutExtension, StringComparison.OrdinalIgnoreCase))
+ {
+ return ApplicationType.ShortcutApplication;
+ }
+ else if (extension.Equals(ApplicationReferenceExtension, StringComparison.OrdinalIgnoreCase))
+ {
+ return ApplicationType.ApprefApplication;
+ }
+ else if (extension.Equals(InternetShortcutExtension, StringComparison.OrdinalIgnoreCase))
+ {
+ return ApplicationType.InternetShortcutApplication;
+ }
+ else if (string.IsNullOrEmpty(extension) && System.IO.Directory.Exists(path))
+ {
+ return ApplicationType.Folder;
+ }
+
+ return ApplicationType.GenericFile;
+ }
+
+ // Function to get the Win32 application, given the path to the application
+ public static Win32Program? GetAppFromPath(string path)
+ {
+ ArgumentNullException.ThrowIfNull(path);
+
+ Win32Program? app;
+ switch (GetAppTypeFromPath(path))
+ {
+ case ApplicationType.Win32Application:
+ app = ExeProgram(path);
+ break;
+ case ApplicationType.ShortcutApplication:
+ app = LnkProgram(path);
+ break;
+ case ApplicationType.ApprefApplication:
+ app = CreateWin32Program(path);
+ app.AppType = ApplicationType.ApprefApplication;
+ break;
+ case ApplicationType.InternetShortcutApplication:
+ app = InternetShortcutProgram(path);
+ break;
+ case ApplicationType.WebApplication:
+ case ApplicationType.RunCommand:
+ case ApplicationType.Folder:
+ case ApplicationType.GenericFile:
+ default:
+ app = null;
+ break;
+ }
+
+ // if the app is valid, only then return the application, else return null
+ return app?.Valid == true
+ ? app
+ : null;
+ }
+
+ private static IEnumerable ProgramPaths(string directory, IList suffixes, bool recursiveSearch = true)
+ {
+ if (!Directory.Exists(directory))
+ {
+ return Array.Empty();
+ }
+
+ var files = new List();
+ var folderQueue = new Queue();
+ folderQueue.Enqueue(directory);
+
+ // Keep track of already visited directories to avoid cycles.
+ var alreadyVisited = new HashSet();
+
+ do
+ {
+ var currentDirectory = folderQueue.Dequeue();
+
+ if (alreadyVisited.Contains(currentDirectory))
+ {
+ continue;
+ }
+
+ alreadyVisited.Add(currentDirectory);
+
+ try
+ {
+ foreach (var suffix in suffixes)
+ {
+ try
+ {
+ files.AddRange(Directory.EnumerateFiles(currentDirectory, $"*.{suffix}", SearchOption.TopDirectoryOnly));
+ }
+ catch (DirectoryNotFoundException)
+ {
+ }
+ }
+ }
+ catch (Exception e) when (e is SecurityException || e is UnauthorizedAccessException)
+ {
+ }
+ catch (Exception)
+ {
+ }
+
+ try
+ {
+ // If the search is set to be non-recursive, then do not enqueue the child directories.
+ if (!recursiveSearch)
+ {
+ continue;
+ }
+
+ foreach (var childDirectory in Directory.EnumerateDirectories(currentDirectory, "*", new EnumerationOptions()
+ {
+ // https://learn.microsoft.com/dotnet/api/system.io.enumerationoptions?view=net-6.0
+ // Exclude directories with the Reparse Point file attribute, to avoid loops due to symbolic links / directory junction / mount points.
+ AttributesToSkip = FileAttributes.Hidden | FileAttributes.System | FileAttributes.ReparsePoint,
+ RecurseSubdirectories = false,
+ }))
+ {
+ folderQueue.Enqueue(childDirectory);
+ }
+ }
+ catch (Exception e) when (e is SecurityException || e is UnauthorizedAccessException)
+ {
+ }
+ catch (Exception)
+ {
+ }
+ }
+ while (folderQueue.Count > 0);
+
+ return files;
+ }
+
+ private static string Extension(string path)
+ {
+ // Using InvariantCulture since this is user facing
+ var extension = Path.GetExtension(path)?.ToLowerInvariant();
+
+ return !string.IsNullOrEmpty(extension)
+ ? extension.Substring(1)
+ : string.Empty;
+ }
+
+ private static IEnumerable CustomProgramPaths(IEnumerable sources, IList suffixes)
+ => sources?.Where(programSource => Directory.Exists(programSource.Location) && programSource.Enabled)
+ .SelectMany(programSource => ProgramPaths(programSource.Location, suffixes))
+ .ToList() ?? Enumerable.Empty();
+
+ // Function to obtain the list of applications, the locations of which have been added to the env variable PATH
+ private static List PathEnvironmentProgramPaths(IList suffixes)
+ {
+ // To get all the locations stored in the PATH env variable
+ var pathEnvVariable = Environment.GetEnvironmentVariable("PATH");
+ var searchPaths = pathEnvVariable?.Split(Path.PathSeparator);
+ var toFilterAllPaths = new List();
+ var isRecursiveSearch = true;
+
+ if (searchPaths is not null)
+ {
+ foreach (var path in searchPaths)
+ {
+ if (path.Length > 0)
+ {
+ // to expand any environment variables present in the path
+ var directory = Environment.ExpandEnvironmentVariables(path);
+ var paths = ProgramPaths(directory, suffixes, !isRecursiveSearch);
+ toFilterAllPaths.AddRange(paths);
+ }
+ }
+ }
+
+ return toFilterAllPaths;
+ }
+
+ private static List IndexPath(IList suffixes, List indexLocations)
+ => indexLocations
+ .SelectMany(indexLocation => ProgramPaths(indexLocation, suffixes))
+ .ToList();
+
+ private static List StartMenuProgramPaths(IList suffixes)
+ {
+ var directory1 = Environment.GetFolderPath(Environment.SpecialFolder.StartMenu);
+ var directory2 = Environment.GetFolderPath(Environment.SpecialFolder.CommonStartMenu);
+ var indexLocation = new List() { directory1, directory2 };
+
+ return IndexPath(suffixes, indexLocation);
+ }
+
+ private static List DesktopProgramPaths(IList suffixes)
+ {
+ var directory1 = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
+ var directory2 = Environment.GetFolderPath(Environment.SpecialFolder.CommonDesktopDirectory);
+
+ var indexLocation = new List() { directory1, directory2 };
+
+ return IndexPath(suffixes, indexLocation);
+ }
+
+ private static List RegistryAppProgramPaths(IList suffixes)
+ {
+ // https://msdn.microsoft.com/library/windows/desktop/ee872121
+ const string appPaths = @"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths";
+ var paths = new List();
+ using (var root = Registry.LocalMachine.OpenSubKey(appPaths))
+ {
+ if (root != null)
+ {
+ paths.AddRange(GetPathsFromRegistry(root));
+ }
+ }
+
+ using (var root = Registry.CurrentUser.OpenSubKey(appPaths))
+ {
+ if (root != null)
+ {
+ paths.AddRange(GetPathsFromRegistry(root));
+ }
+ }
+
+ return paths
+ .Where(path => suffixes.Any(suffix => path.EndsWith(suffix, StringComparison.InvariantCultureIgnoreCase)))
+ .Select(ExpandEnvironmentVariables)
+ .Where(path => path is not null)
+ .ToList();
+ }
+
+ private static IEnumerable GetPathsFromRegistry(RegistryKey root)
+ => root
+ .GetSubKeyNames()
+ .Select(x => GetPathFromRegistrySubkey(root, x));
+
+ private static string GetPathFromRegistrySubkey(RegistryKey root, string subkey)
+ {
+ var path = string.Empty;
+ try
+ {
+ using (var key = root.OpenSubKey(subkey))
+ {
+ if (key == null)
+ {
+ return string.Empty;
+ }
+
+ var defaultValue = string.Empty;
+ path = key.GetValue(defaultValue) as string;
+ }
+
+ if (string.IsNullOrEmpty(path))
+ {
+ return string.Empty;
+ }
+
+ // fix path like this: ""\"C:\\folder\\executable.exe\""
+ return path = path.Trim('"', ' ');
+ }
+ catch (Exception e) when (e is SecurityException || e is UnauthorizedAccessException)
+ {
+ return string.Empty;
+ }
+ }
+
+ private static string ExpandEnvironmentVariables(string path) =>
+ !string.IsNullOrEmpty(path)
+ ? Environment.ExpandEnvironmentVariables(path)
+ : string.Empty;
+
+ // Overriding the object.GetHashCode() function to aid in removing duplicates while adding and removing apps from the concurrent dictionary storage
+ public override int GetHashCode()
+ => Win32ProgramEqualityComparer.Default.GetHashCode(this);
+
+ public override bool Equals(object? obj)
+ => obj is Win32Program win32Program && Win32ProgramEqualityComparer.Default.Equals(this, win32Program);
+
+ private sealed class Win32ProgramEqualityComparer : IEqualityComparer
+ {
+ public static readonly Win32ProgramEqualityComparer Default = new Win32ProgramEqualityComparer();
+
+ public bool Equals(Win32Program? app1, Win32Program? app2)
+ {
+ if (app1 == null && app2 == null)
+ {
+ return true;
+ }
+
+ return app1 != null
+ && app2 != null
+ && (app1.Name?.ToUpperInvariant(), app1.ExecutableName?.ToUpperInvariant(), app1.FullPath?.ToUpperInvariant())
+ .Equals((app2.Name?.ToUpperInvariant(), app2.ExecutableName?.ToUpperInvariant(), app2.FullPath?.ToUpperInvariant()));
+ }
+
+ public int GetHashCode(Win32Program obj)
+ => (obj.Name?.ToUpperInvariant(), obj.ExecutableName?.ToUpperInvariant(), obj.FullPath?.ToUpperInvariant()).GetHashCode();
+ }
+
+ public static List DeduplicatePrograms(IEnumerable programs)
+ => new HashSet(programs, Win32ProgramEqualityComparer.Default).ToList();
+
+ private static Win32Program GetProgramFromPath(string path)
+ {
+ var extension = Extension(path);
+ if (ExecutableApplicationExtensions.Contains(extension))
+ {
+ return ExeProgram(path);
+ }
+
+ switch (extension)
+ {
+ case ShortcutExtension:
+ return LnkProgram(path);
+ case ApplicationReferenceExtension:
+ return CreateWin32Program(path);
+ case InternetShortcutExtension:
+ return InternetShortcutProgram(path);
+ default:
+ return InvalidProgram;
+ }
+ }
+
+ private static bool TryGetIcoPathForRunCommandProgram(Win32Program program, out string? icoPath)
+ {
+ icoPath = null;
+
+ if (program.AppType != ApplicationType.RunCommand)
+ {
+ return false;
+ }
+
+ if (string.IsNullOrEmpty(program.FullPath))
+ {
+ return false;
+ }
+
+ // https://msdn.microsoft.com/library/windows/desktop/ee872121
+ try
+ {
+ var redirectionPath = ReparsePoint.GetTarget(program.FullPath);
+ if (string.IsNullOrEmpty(redirectionPath))
+ {
+ return false;
+ }
+
+ icoPath = ExpandEnvironmentVariables(redirectionPath);
+ return true;
+ }
+ catch (IOException)
+ {
+ }
+
+ icoPath = null;
+ return false;
+ }
+
+ private static Win32Program GetRunCommandProgramFromPath(string path)
+ {
+ var program = GetProgramFromPath(path);
+ if (program.Valid)
+ {
+ program.AppType = ApplicationType.RunCommand;
+
+ if (TryGetIcoPathForRunCommandProgram(program, out var icoPath))
+ {
+ program.IcoPath = icoPath ?? string.Empty;
+ }
+ }
+
+ return program;
+ }
+
+ public static IList All(AllAppsSettings settings)
+ {
+ ArgumentNullException.ThrowIfNull(settings);
+
+ try
+ {
+ // Set an initial size to an expected size to prevent multiple hashSet resizes
+ const int defaultHashsetSize = 1000;
+
+ // Multiple paths could have the same programPaths and we don't want to resolve / lookup them multiple times
+ var paths = new HashSet(defaultHashsetSize);
+ var runCommandPaths = new HashSet(defaultHashsetSize);
+
+ // Parallelize multiple sources, and priority based on paths which most likely contain .lnks which are formatted
+ var sources = new (bool IsEnabled, Func> GetPaths)[]
+ {
+ (true, () => CustomProgramPaths(settings.ProgramSources, settings.ProgramSuffixes)),
+ (settings.EnableStartMenuSource, () => StartMenuProgramPaths(settings.ProgramSuffixes)),
+ (settings.EnableDesktopSource, () => DesktopProgramPaths(settings.ProgramSuffixes)),
+ (settings.EnableRegistrySource, () => RegistryAppProgramPaths(settings.ProgramSuffixes)),
+ };
+
+ // Run commands are always set as AppType "RunCommand"
+ var runCommandSources = new (bool IsEnabled, Func> GetPaths)[]
+ {
+ (settings.EnablePathEnvironmentVariableSource, () => PathEnvironmentProgramPaths(settings.RunCommandSuffixes)),
+ };
+
+ var disabledProgramsList = settings.DisabledProgramSources;
+
+ // Get all paths but exclude all normal .Executables
+ paths.UnionWith(sources
+ .AsParallel()
+ .SelectMany(source => source.IsEnabled ? source.GetPaths() : Enumerable.Empty())
+ .Where(programPath => disabledProgramsList.All(x => x.UniqueIdentifier != programPath))
+ .Where(path => !ExecutableApplicationExtensions.Contains(Extension(path))));
+ runCommandPaths.UnionWith(runCommandSources
+ .AsParallel()
+ .SelectMany(source => source.IsEnabled ? source.GetPaths() : Enumerable.Empty())
+ .Where(programPath => disabledProgramsList.All(x => x.UniqueIdentifier != programPath)));
+
+ var programs = paths.AsParallel().Select(source => GetProgramFromPath(source));
+ var runCommandPrograms = runCommandPaths.AsParallel().Select(source => GetRunCommandProgramFromPath(source));
+
+ return DeduplicatePrograms(programs.Concat(runCommandPrograms).Where(program => program?.Valid == true));
+ }
+ catch (Exception)
+ {
+ return Array.Empty();
+ }
+ }
+}
diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Bookmark/AddBookmarkForm.cs b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Bookmark/AddBookmarkForm.cs
new file mode 100644
index 0000000000..9b3f54a21f
--- /dev/null
+++ b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Bookmark/AddBookmarkForm.cs
@@ -0,0 +1,107 @@
+// 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;
+using System.IO;
+using System.Text.Json;
+using System.Text.Json.Nodes;
+using Microsoft.CmdPal.Ext.Bookmarks.Properties;
+using Microsoft.CommandPalette.Extensions.Toolkit;
+using Windows.Foundation;
+
+namespace Microsoft.CmdPal.Ext.Bookmarks;
+
+internal sealed partial class AddBookmarkForm : FormContent
+{
+ internal event TypedEventHandler