diff --git a/.config/configuration.vsEnterprise.winget b/.config/configuration.vsEnterprise.winget index 4d19d37137..08c9983562 100644 --- a/.config/configuration.vsEnterprise.winget +++ b/.config/configuration.vsEnterprise.winget @@ -2,14 +2,14 @@ # Reference: https://github.com/microsoft/PowerToys/blob/main/doc/devdocs/readme.md#compiling-powertoys properties: resources: - - resource: Microsoft.Windows.Developer/DeveloperMode + - resource: Microsoft.Windows.Settings/WindowsSettings directives: description: Enable Developer Mode allowPrerelease: true # Requires elevation for the set operation securityContext: elevated settings: - Ensure: Present + DeveloperMode: true - resource: Microsoft.WinGet.DSC/WinGetPackage id: vsPackage directives: diff --git a/.config/configuration.vsProfessional.winget b/.config/configuration.vsProfessional.winget index 78fb1c13d5..4d7e4a31c3 100644 --- a/.config/configuration.vsProfessional.winget +++ b/.config/configuration.vsProfessional.winget @@ -2,14 +2,14 @@ # Reference: https://github.com/microsoft/PowerToys/blob/main/doc/devdocs/readme.md#compiling-powertoys properties: resources: - - resource: Microsoft.Windows.Developer/DeveloperMode + - resource: Microsoft.Windows.Settings/WindowsSettings directives: description: Enable Developer Mode allowPrerelease: true # Requires elevation for the set operation securityContext: elevated settings: - Ensure: Present + DeveloperMode: true - resource: Microsoft.WinGet.DSC/WinGetPackage id: vsPackage directives: diff --git a/.config/configuration.winget b/.config/configuration.winget index 456eca47f2..2016dcdc33 100644 --- a/.config/configuration.winget +++ b/.config/configuration.winget @@ -2,14 +2,14 @@ # Reference: https://github.com/microsoft/PowerToys/blob/main/doc/devdocs/readme.md#compiling-powertoys properties: resources: - - resource: Microsoft.Windows.Developer/DeveloperMode + - resource: Microsoft.Windows.Settings/WindowsSettings directives: description: Enable Developer Mode allowPrerelease: true # Requires elevation for the set operation securityContext: elevated settings: - Ensure: Present + DeveloperMode: true - resource: Microsoft.WinGet.DSC/WinGetPackage id: vsPackage directives: diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index e55d081c91..5e5ea03e7e 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -12,7 +12,7 @@ body: attributes: label: Microsoft PowerToys version placeholder: X.XX.X - description: Hover over system tray icon or look at Settings + description: Hover over the system tray icon or look at Settings validations: required: true @@ -20,7 +20,7 @@ body: type: dropdown attributes: label: Installation method - description: How / Where was PowerToys installed from? + description: How / where was PowerToys installed from? multiple: true options: - GitHub diff --git a/.github/ISSUE_TEMPLATE/documentation-issue.yml b/.github/ISSUE_TEMPLATE/documentation-issue.yml index 151fc5a1f7..583cf54811 100644 --- a/.github/ISSUE_TEMPLATE/documentation-issue.yml +++ b/.github/ISSUE_TEMPLATE/documentation-issue.yml @@ -6,7 +6,7 @@ labels: body: - type: textarea attributes: - label: Provide a description of requested docs changes + label: Describe the requested doc changes placeholder: Briefly describe which document needs to be corrected and why. validations: required: true diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index d7d092dbca..a2c7db9cc5 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -13,7 +13,7 @@ body: - type: textarea attributes: label: Scenario when this would be used? - placeholder: What is the scenario this would be used? Why is this important to your workflow as a power user? + placeholder: What is the scenario this would be used in? Why is this important to your workflow as a power user? validations: required: true - type: textarea diff --git a/.github/ISSUE_TEMPLATE/translation_issue.yml b/.github/ISSUE_TEMPLATE/translation_issue.yml index ffddacb9aa..1fdefbff8b 100644 --- a/.github/ISSUE_TEMPLATE/translation_issue.yml +++ b/.github/ISSUE_TEMPLATE/translation_issue.yml @@ -14,7 +14,7 @@ body: attributes: label: Microsoft PowerToys version placeholder: 0.70.0 - description: Hover over system tray icon or look at Settings + description: Hover over the system tray icon or look at Settings validations: required: true - type: dropdown @@ -65,7 +65,7 @@ body: - type: textarea attributes: label: ❌ Actual phrase(s) - placeholder: What is there? Please include a screenshot as that is extremely helpful. + placeholder: What is there? Please include a screenshot, as that is extremely helpful. validations: required: true - type: textarea diff --git a/.github/actions/spell-check/allow/names.txt b/.github/actions/spell-check/allow/names.txt index eeda5b9ae1..f570a231af 100644 --- a/.github/actions/spell-check/allow/names.txt +++ b/.github/actions/spell-check/allow/names.txt @@ -46,8 +46,8 @@ betsegaw bricelam bsky CCcat -chenmy chemwolf +chenmy Chinh chrdavis Chrzan @@ -65,8 +65,8 @@ Deondre DHowett ductdo Essey -Feng ethanfangg +Feng ferraridavide foxmsft frankychen @@ -77,6 +77,7 @@ Galaxi Garside Gershaft Giordani +Gleb Gokce gordon Griese @@ -90,12 +91,15 @@ Hemmerlein hlaueriksson Horvalds Howett +hotkidfamily htcfreek Huynh Ionut jamrobot Jaswal +Jaylyn jefflord +Jeremic Jordi jyuwono kai @@ -105,6 +109,7 @@ Kantarci Karthick kaylacinnamon kevinguo +Khmyznikov Krigun Lambson Laute @@ -127,6 +132,8 @@ Naro nathancartlidge Nemeth nielslaute +Noraa +noraajunker oldnewthing onegreatworld palenshus @@ -146,11 +153,13 @@ ricardosantos riri ritchielawrence robmikh +ruslanlap Russinovich Rutkas ryanbodrug saahmedm sachaple +Sameerjs Santossio Schoen Sekan @@ -166,9 +175,11 @@ Tadele talynone Taras TBM +Teutsch tilovell Triet urnotdfs +vednig waaverecords wang Whuihuan @@ -187,9 +198,6 @@ zhaopy zhaoqpcn Zoltan Zykova -Sameerjs -ruslanlap -vednig # OTHERS @@ -216,6 +224,7 @@ openai Quickime regedit roslyn +Skia Spotify Vanara wangyi diff --git a/.github/actions/spell-check/excludes.txt b/.github/actions/spell-check/excludes.txt index 21d7da66b8..f5710e72ce 100644 --- a/.github/actions/spell-check/excludes.txt +++ b/.github/actions/spell-check/excludes.txt @@ -92,6 +92,7 @@ ^\.github/actions/spell-check/ ^\.gitmodules$ ^\Q.github/workflows/spelling2.yml\E$ +^\Q.pipelines/272MSSharedLibSN2048.snk\E$ ^\Q.pipelines/ESRPSigning_core.json\E$ ^\Qdoc/devdocs/localization.md\E$ ^\Qsrc/common/ManagedCommon/ColorFormatHelper.cs\E$ @@ -120,7 +121,9 @@ ^src/modules/MouseWithoutBorders/App/Form/.*\.resx$ ^src/modules/MouseWithoutBorders/App/Helper/.*\.resx$ ^src/modules/previewpane/UnitTests-MarkdownPreviewHandler/HelperFiles/MarkdownWithHTMLImageTag\.txt$ +^src/modules/ZoomIt/ZoomIt/ZoomIt\.idc$ ^src/Monaco/ ^src/common/sysinternals/Eula/ ^tools/Verification scripts/Check preview handler registration\.ps1$ ignore$ +^src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/.*$ diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index f9d35feab1..a081d3fcd1 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -14,6 +14,7 @@ AColumn acrt ACTIVATEAPP activationaction +ACTIVATEOPTIONS ACVS adaptivecards ADate @@ -68,6 +69,7 @@ appwiz APSTUDIO AQS ARandom +Arash ARCHITEW ARemapped ARPINSTALLLOCATION @@ -154,6 +156,7 @@ builttoroam BVal BValue byapp +BYCOMMAND BYPOSITION CALCRECT CALG @@ -189,6 +192,7 @@ CImage cla CLASSDC CLASSNOTAVAILABLE +cleanmgr clickable clickonce CLIENTEDGE @@ -200,6 +204,7 @@ CLIPSIBLINGS closesocket clp CLSCTX +CLSCTXLOCALSERVER clsids Clusion cmder @@ -273,6 +278,7 @@ currentculture CURRENTDIR CURSORINFO cursorpos +CURSORSHOWING customaction CUSTOMACTIONTEST CUSTOMFORMATPLACEHOLDER @@ -327,7 +333,6 @@ DEFAULTTONULL DEFAULTTOPRIMARY DEFERERASE DEFPUSHBUTTON -DEFT deinitialization DELA DELETEDKEYIMAGE @@ -341,7 +346,9 @@ DESELECTOTHERS DESIGNINFO DESKTOPABSOLUTEEDITING DESKTOPABSOLUTEPARSING +DESKTOPHORZRES desktopshorcutinstalled +DESKTOPVERTRES devblogs devdocs devmgmt @@ -352,6 +359,7 @@ DFX DIALOGEX digicert dimm +DINORMAL DISABLEASACTIONKEY DISABLENOSCROLL diskmgmt @@ -395,6 +403,7 @@ DVASPECTINFO DVD dvr DVTARGETDEVICE +dwflags dwl dwm dwmapi @@ -469,6 +478,7 @@ EXPCMDFLAGS EXPCMDSTATE explr exppowertoys +exprtk exptas exsb exstyle @@ -482,6 +492,7 @@ FANCYZONESDRAWLAYOUTTEST FANCYZONESEDITOR FARPROC fff +FFFF FILEEXPLORER FILEFLAGS FILEFLAGSMASK @@ -600,6 +611,7 @@ helptext HGFE hglobal hhk +HHmmssfff hhx Hiber Hiberboot @@ -608,6 +620,7 @@ hicon HIDEREADONLY HIDEWINDOW Hif +hightlight HIMAGELIST himl hinst @@ -669,7 +682,6 @@ IBeam icf ICONERROR ICONLOCATION -idc IDCANCEL IDD idk @@ -680,7 +692,6 @@ IDR IDXGI ietf IEXPLORE -iextn IFACEMETHOD IFACEMETHODIMP IFile @@ -719,7 +730,6 @@ INPUTMOUSE INPUTSINK INPUTTYPE INSTALLDESKTOPSHORTCUT -INSTALLDIR installdir INSTALLFOLDER INSTALLFOLDERTOBOOTSTRAPPERINSTALLFOLDER @@ -745,6 +755,7 @@ isocpp iss issecret ISSEPARATOR +istep ith ITHUMBNAIL IUI @@ -755,6 +766,7 @@ iwr jfif jgeosdfsdsgmkedfgdfgdfgbkmhcgcflmi jjw +JLO jobject jpe jpnime @@ -788,6 +800,7 @@ LCIDTo Lclean Ldone Ldr +LEFTALIGN LEFTSCROLLBAR LEFTTEXT LError @@ -810,11 +823,11 @@ LMENU lnks LOADFROMFILE LOBYTE -localappdata LOCALDISPLAY localpackage LOCALSYSTEM LOCATIONCHANGE +LOCKTYPE LOGFONT LOGFONTW logon @@ -910,6 +923,7 @@ metafile mfc Mgmt Microwaved +middleclickaction midl mii mindaro @@ -975,7 +989,6 @@ msrc msstore mst msvcp -msvsmon MTND MULTIPLEUSE multizone @@ -1153,6 +1166,7 @@ PARTIALCONFIRMATIONDIALOGTITLE PATCOPY PATHMUSTEXIST PATINVERT +partow PATPAINT pbc pbi @@ -1251,6 +1265,7 @@ prg prgh prgms pri +primaryclickaction PRINTCLIENT printmanagement prm @@ -1272,6 +1287,7 @@ prvpane psapi pscid PSECURITY +psexec psfgao psfi PSMODULEPATH @@ -1282,7 +1298,6 @@ pstm PStr pstream pstrm -pswd PSYSTEM psz ptb @@ -1427,6 +1442,7 @@ SDKDDK sdns searchterm SEARCHUI +secondaryclickaction SECONDARYDISPLAY secpol securestring @@ -1474,16 +1490,12 @@ SHELLDLL shellex SHELLEXECUTEINFO SHELLEXECUTEINFOW -SHELLEXTENSION SHELLICONSIZE -SHELLNEWVALUE SHFILEINFO SHFILEOPSTRUCT SHGDN SHGDNF SHGFI -SHGFIICON -SHGFILARGEICON SHIL shinfo shlwapi @@ -1515,9 +1527,9 @@ SICHINT SIDs siex sigdn +Signedness SIGNINGSCENARIO signtool -Signtool SINGLEKEY sipolicy SIZEBOX @@ -1533,6 +1545,7 @@ SLGP sln SMALLICON smartphone +smileys SMTO SNAPPROCESS snk @@ -1588,6 +1601,7 @@ steamapps STGC STGM STGMEDIUM +STGMREAD STICKYKEYS sticpl storelogo @@ -1664,6 +1678,7 @@ TDefault TDevice telephon templatenamespace +TESTONLY testprocess TEXCOORD TEXTBOXNEWLINE @@ -1716,6 +1731,7 @@ trx tsa TSender TServer +tskill tstoi TStr tweakme @@ -1779,7 +1795,6 @@ uxtheme vabdq validmodulename valuegenerator -VARENUM variantassignment vcamp VCENTER @@ -1875,6 +1890,7 @@ WINDOWPOSCHANGING WINDOWSBUILDNUMBER windowssearch windowssettings +windowsterminal WINDOWSTYLES WINDOWSTYLESICON winerror @@ -1949,6 +1965,7 @@ Wwanpp XAxis xclip xcopy +XDeployment XDocument XElement xfd @@ -1984,6 +2001,7 @@ Zoneszonabletester Zoomin zoomit ZOOMITX +Zorder ZXk ZXNs zzz @@ -1995,3 +2013,10 @@ culori Evercoder LCh CIELCh +CLSCTXINPROCALL +IIDI +irow +lcid +ppwsz +rguid +VARTYPE diff --git a/.github/actions/spell-check/patterns.txt b/.github/actions/spell-check/patterns.txt index 5a6f4785b1..6bd99fb83e 100644 --- a/.github/actions/spell-check/patterns.txt +++ b/.github/actions/spell-check/patterns.txt @@ -243,4 +243,4 @@ Process Process # ZoomIt menu items with accelerator keys E&xit -St&yle \ No newline at end of file +St&yle diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 0cff106acb..2db246d63b 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -5,9 +5,9 @@ ## PR Checklist - [ ] **Closes:** #xxx -- [ ] **Communication:** I've discussed this with core contributors already. If work hasn't been agreed, this work might be rejected +- [ ] **Communication:** I've discussed this with core contributors already. If the work hasn't been agreed, this work might be rejected - [ ] **Tests:** Added/updated and all pass -- [ ] **Localization:** All end user facing strings can be localized +- [ ] **Localization:** All end-user-facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries @@ -16,7 +16,7 @@ - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx - + ## Detailed Description of the Pull Request / Additional comments diff --git a/.github/workflows/package-submissions.yml b/.github/workflows/package-submissions.yml index b03c18b78b..a2d401faa4 100644 --- a/.github/workflows/package-submissions.yml +++ b/.github/workflows/package-submissions.yml @@ -1,5 +1,4 @@ name: WinGet submission on release -# based off of https://github.com/nushell/nushell/blob/main/.github/workflows/winget-submission.yml on: workflow_dispatch: @@ -9,23 +8,31 @@ on: jobs: winget: name: Publish winget package + + # winget-create is only supported on Windows runs-on: windows-latest + + # winget-create will read the following environment variable to access the GitHub token needed for submitting a PR + # See https://aka.ms/winget-create-token + env: + WINGET_CREATE_GITHUB_TOKEN: ${{ secrets.PT_WINGET }} + + # Only submit stable releases + if: ${{ !github.event.release.prerelease }} steps: - name: Submit Microsoft.PowerToys package to Windows Package Manager Community Repository run: | + # Get installer info from GitHub release event + $assets = '${{ toJSON(github.event.release.assets) }}' | ConvertFrom-Json + $x64UserInstallerUrl = $assets | Where-Object -Property name -match 'PowerToysUserSetup.*x64' | Select -ExpandProperty browser_download_url + $x64MachineInstallerUrl = $assets | Where-Object -Property name -match 'PowerToysSetup.*x64' | Select -ExpandProperty browser_download_url + $arm64UserInstallerUrl = $assets | Where-Object -Property name -match 'PowerToysUserSetup.*arm64' | Select -ExpandProperty browser_download_url + $arm64MachineInstallerUrl = $assets | Where-Object -Property name -match 'PowerToysSetup.*arm64' | Select -ExpandProperty browser_download_url + $packageVersion = (${{ toJSON(github.event.release.tag_name) }}).Trim('v') - $wingetPackage = "Microsoft.PowerToys" - $gitToken = "${{ secrets.PT_WINGET }}" - - $github = Invoke-RestMethod -uri "https://api.github.com/repos/Microsoft/PowerToys/releases" - - $targetRelease = $github | Where-Object -Property name -match 'Release'| Select -First 1 - $installerUserX64Url = $targetRelease | Select -ExpandProperty assets -First 1 | Where-Object -Property name -match 'PowerToysUserSetup.*x64' | Select -ExpandProperty browser_download_url - $installerMachineX64Url = $targetRelease | Select -ExpandProperty assets -First 1 | Where-Object -Property name -match 'PowerToysSetup.*x64' | Select -ExpandProperty browser_download_url - $installerUserArmUrl = $targetRelease | Select -ExpandProperty assets -First 1 | Where-Object -Property name -match 'PowerToysUserSetup.*arm64' | Select -ExpandProperty browser_download_url - $installerMachineArmUrl = $targetRelease | Select -ExpandProperty assets -First 1 | Where-Object -Property name -match 'PowerToysSetup.*arm64' | Select -ExpandProperty browser_download_url - $ver = $targetRelease.tag_name -ireplace '^v' - - # getting latest wingetcreate file - iwr https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe - .\wingetcreate.exe update $wingetPackage -s -v $ver -u "$installerUserX64Url|user" "$installerMachineX64Url|machine" "$installerUserArmUrl|user" "$installerMachineArmUrl|machine" -t $gitToken + # Update package using wingetcreate + curl.exe -JLO https://aka.ms/wingetcreate/latest + .\wingetcreate.exe update Microsoft.PowerToys ` + --version $packageVersion ` + --urls "$x64UserInstallerUrl|user" "$x64MachineInstallerUrl|machine" "$arm64UserInstallerUrl|user" "$arm64MachineInstallerUrl|machine" ` + --submit diff --git a/.github/workflows/spelling2.yml b/.github/workflows/spelling2.yml index 03bb66fff6..cd2b6d5b0f 100644 --- a/.github/workflows/spelling2.yml +++ b/.github/workflows/spelling2.yml @@ -93,7 +93,7 @@ jobs: steps: - name: check-spelling id: spelling - uses: check-spelling/check-spelling@67debf50669c7fc76fc8f5d7f996384535a72b77 # v0.0.24 + uses: check-spelling/check-spelling@c635c2f3f714eec2fcf27b643a1919b9a811ef2e # v0.0.25 with: config: .github/actions/spell-check suppress_push_for_open_pull_request: ${{ github.actor != 'dependabot[bot]' && 1 }} @@ -156,7 +156,7 @@ jobs: if: (success() || failure()) && needs.spelling.outputs.followup && github.event_name == 'push' steps: - name: comment - uses: check-spelling/check-spelling@67debf50669c7fc76fc8f5d7f996384535a72b77 # v0.0.24 + uses: check-spelling/check-spelling@c635c2f3f714eec2fcf27b643a1919b9a811ef2e # v0.0.25 with: config: .github/actions/spell-check checkout: true @@ -175,7 +175,7 @@ jobs: if: (success() || failure()) && needs.spelling.outputs.followup && contains(github.event_name, 'pull_request') steps: - name: comment - uses: check-spelling/check-spelling@67debf50669c7fc76fc8f5d7f996384535a72b77 # v0.0.24 + uses: check-spelling/check-spelling@c635c2f3f714eec2fcf27b643a1919b9a811ef2e # v0.0.25 with: config: .github/actions/spell-check checkout: true @@ -202,7 +202,7 @@ jobs: cancel-in-progress: false steps: - name: apply spelling updates - uses: check-spelling/check-spelling@67debf50669c7fc76fc8f5d7f996384535a72b77 # v0.0.24 + uses: check-spelling/check-spelling@c635c2f3f714eec2fcf27b643a1919b9a811ef2e # v0.0.25 with: experimental_apply_changes_via_bot: ${{ github.repository_owner != 'microsoft' && 1 }} checkout: true diff --git a/.pipelines/ESRPSigning_core.json b/.pipelines/ESRPSigning_core.json index 615b5633bf..b0b2a42655 100644 --- a/.pipelines/ESRPSigning_core.json +++ b/.pipelines/ESRPSigning_core.json @@ -3,226 +3,227 @@ "UseMinimatch": false, "SignBatches": [ { - "MatchedPath": [ - "*.resources.dll", - - "WinUI3Apps\\Assets\\Settings\\Scripts\\*.ps1", + "MatchedPath": [ + "*.resources.dll", - "PowerToys.ActionRunner.exe", - "PowerToys.Update.exe", - "PowerToys.BackgroundActivatorDLL.dll", - "Notifications.dll", - "os-detection.dll", - "PowerToys.exe", - "PowerToys.FilePreviewCommon.dll", - "PowerToys.Interop.dll", - "Tools\\PowerToys.BugReportTool.exe", - "StylesReportTool\\PowerToys.StylesReportTool.exe", - "Telemetry.dll", - "PowerToys.ManagedTelemetry.dll", - "PowerToys.ManagedCommon.dll", - "PowerToys.Common.UI.dll", - "PowerToys.Settings.UI.Lib.dll", - "PowerToys.GPOWrapper.dll", - "PowerToys.GPOWrapperProjection.dll", - "PowerToys.AllExperiments.dll", + "WinUI3Apps\\Assets\\Settings\\Scripts\\*.ps1", - "PowerToys.AlwaysOnTop.exe", - "PowerToys.AlwaysOnTopModuleInterface.dll", + "PowerToys.ActionRunner.exe", + "PowerToys.Update.exe", + "PowerToys.BackgroundActivatorDLL.dll", + "Notifications.dll", + "os-detection.dll", + "PowerToys.exe", + "PowerToys.FilePreviewCommon.dll", + "PowerToys.Interop.dll", + "Tools\\PowerToys.BugReportTool.exe", + "StylesReportTool\\PowerToys.StylesReportTool.exe", + "Telemetry.dll", + "CalculatorEngineCommon.dll", + "PowerToys.ManagedTelemetry.dll", + "PowerToys.ManagedCommon.dll", + "PowerToys.Common.UI.dll", + "PowerToys.Settings.UI.Lib.dll", + "PowerToys.GPOWrapper.dll", + "PowerToys.GPOWrapperProjection.dll", + "PowerToys.AllExperiments.dll", - "PowerToys.CmdNotFoundModuleInterface.dll", - "PowerToys.CmdNotFound.dll", + "PowerToys.AlwaysOnTop.exe", + "PowerToys.AlwaysOnTopModuleInterface.dll", - "PowerToys.ColorPicker.dll", - "PowerToys.ColorPickerUI.dll", - "PowerToys.ColorPickerUI.exe", + "PowerToys.CmdNotFoundModuleInterface.dll", + "PowerToys.CmdNotFound.dll", - "PowerToys.CropAndLockModuleInterface.dll", - "PowerToys.CropAndLock.exe", + "PowerToys.ColorPicker.dll", + "PowerToys.ColorPickerUI.dll", + "PowerToys.ColorPickerUI.exe", - "PowerToys.PowerOCRModuleInterface.dll", - "PowerToys.PowerOCR.dll", - "PowerToys.PowerOCR.exe", + "PowerToys.CropAndLockModuleInterface.dll", + "PowerToys.CropAndLock.exe", - "PowerToys.AdvancedPasteModuleInterface.dll", - "WinUI3Apps\\PowerToys.AdvancedPaste.exe", - "WinUI3Apps\\PowerToys.AdvancedPaste.dll", + "PowerToys.PowerOCRModuleInterface.dll", + "PowerToys.PowerOCR.dll", + "PowerToys.PowerOCR.exe", - "PowerToys.AwakeModuleInterface.dll", - "PowerToys.Awake.exe", - "PowerToys.Awake.dll", + "PowerToys.AdvancedPasteModuleInterface.dll", + "WinUI3Apps\\PowerToys.AdvancedPaste.exe", + "WinUI3Apps\\PowerToys.AdvancedPaste.dll", - "fancyzones.dll", - "PowerToys.FancyZonesEditor.exe", - "PowerToys.FancyZonesEditor.dll", - "PowerToys.FancyZonesEditorCommon.dll", - "PowerToys.FancyZonesModuleInterface.dll", - "PowerToys.FancyZones.exe", + "PowerToys.AwakeModuleInterface.dll", + "PowerToys.Awake.exe", + "PowerToys.Awake.dll", - "PowerToys.GcodePreviewHandler.dll", - "PowerToys.GcodePreviewHandler.exe", - "PowerToys.GcodePreviewHandlerCpp.dll", - "PowerToys.GcodeThumbnailProvider.dll", - "PowerToys.GcodeThumbnailProvider.exe", - "PowerToys.GcodeThumbnailProviderCpp.dll", - "PowerToys.ManagedTelemetry.dll", - "PowerToys.MarkdownPreviewHandler.dll", - "PowerToys.MarkdownPreviewHandler.exe", - "PowerToys.MarkdownPreviewHandlerCpp.dll", - "PowerToys.MonacoPreviewHandler.dll", - "PowerToys.MonacoPreviewHandler.exe", - "PowerToys.MonacoPreviewHandlerCpp.dll", - "PowerToys.PdfPreviewHandler.dll", - "PowerToys.PdfPreviewHandler.exe", - "PowerToys.PdfPreviewHandlerCpp.dll", - "PowerToys.PdfThumbnailProvider.dll", - "PowerToys.PdfThumbnailProvider.exe", - "PowerToys.PdfThumbnailProviderCpp.dll", - "PowerToys.powerpreview.dll", - "PowerToys.PreviewHandlerCommon.dll", - "PowerToys.QoiPreviewHandler.dll", - "PowerToys.QoiPreviewHandler.exe", - "PowerToys.QoiPreviewHandlerCpp.dll", - "PowerToys.QoiThumbnailProvider.dll", - "PowerToys.QoiThumbnailProvider.exe", - "PowerToys.QoiThumbnailProviderCpp.dll", - "PowerToys.StlThumbnailProvider.dll", - "PowerToys.StlThumbnailProvider.exe", - "PowerToys.StlThumbnailProviderCpp.dll", - "PowerToys.SvgPreviewHandler.dll", - "PowerToys.SvgPreviewHandler.exe", - "PowerToys.SvgPreviewHandlerCpp.dll", - "PowerToys.SvgThumbnailProvider.dll", - "PowerToys.SvgThumbnailProvider.exe", - "PowerToys.SvgThumbnailProviderCpp.dll", + "fancyzones.dll", + "PowerToys.FancyZonesEditor.exe", + "PowerToys.FancyZonesEditor.dll", + "PowerToys.FancyZonesEditorCommon.dll", + "PowerToys.FancyZonesModuleInterface.dll", + "PowerToys.FancyZones.exe", - "WinUI3Apps\\PowerToys.HostsModuleInterface.dll", - "WinUI3Apps\\PowerToys.HostsUILib.dll", - "WinUI3Apps\\PowerToys.Hosts.dll", - "WinUI3Apps\\PowerToys.Hosts.exe", + "PowerToys.GcodePreviewHandler.dll", + "PowerToys.GcodePreviewHandler.exe", + "PowerToys.GcodePreviewHandlerCpp.dll", + "PowerToys.GcodeThumbnailProvider.dll", + "PowerToys.GcodeThumbnailProvider.exe", + "PowerToys.GcodeThumbnailProviderCpp.dll", + "PowerToys.ManagedTelemetry.dll", + "PowerToys.MarkdownPreviewHandler.dll", + "PowerToys.MarkdownPreviewHandler.exe", + "PowerToys.MarkdownPreviewHandlerCpp.dll", + "PowerToys.MonacoPreviewHandler.dll", + "PowerToys.MonacoPreviewHandler.exe", + "PowerToys.MonacoPreviewHandlerCpp.dll", + "PowerToys.PdfPreviewHandler.dll", + "PowerToys.PdfPreviewHandler.exe", + "PowerToys.PdfPreviewHandlerCpp.dll", + "PowerToys.PdfThumbnailProvider.dll", + "PowerToys.PdfThumbnailProvider.exe", + "PowerToys.PdfThumbnailProviderCpp.dll", + "PowerToys.powerpreview.dll", + "PowerToys.PreviewHandlerCommon.dll", + "PowerToys.QoiPreviewHandler.dll", + "PowerToys.QoiPreviewHandler.exe", + "PowerToys.QoiPreviewHandlerCpp.dll", + "PowerToys.QoiThumbnailProvider.dll", + "PowerToys.QoiThumbnailProvider.exe", + "PowerToys.QoiThumbnailProviderCpp.dll", + "PowerToys.StlThumbnailProvider.dll", + "PowerToys.StlThumbnailProvider.exe", + "PowerToys.StlThumbnailProviderCpp.dll", + "PowerToys.SvgPreviewHandler.dll", + "PowerToys.SvgPreviewHandler.exe", + "PowerToys.SvgPreviewHandlerCpp.dll", + "PowerToys.SvgThumbnailProvider.dll", + "PowerToys.SvgThumbnailProvider.exe", + "PowerToys.SvgThumbnailProviderCpp.dll", - "WinUI3Apps\\PowerToys.FileLocksmithLib.Interop.dll", - "WinUI3Apps\\PowerToys.FileLocksmithExt.dll", - "WinUI3Apps\\PowerToys.FileLocksmithUI.exe", - "WinUI3Apps\\PowerToys.FileLocksmithUI.dll", - "WinUI3Apps\\PowerToys.FileLocksmithContextMenu.dll", - "FileLocksmithContextMenuPackage.msix", + "WinUI3Apps\\PowerToys.HostsModuleInterface.dll", + "WinUI3Apps\\PowerToys.HostsUILib.dll", + "WinUI3Apps\\PowerToys.Hosts.dll", + "WinUI3Apps\\PowerToys.Hosts.exe", - "WinUI3Apps\\Peek.Common.dll", - "WinUI3Apps\\Peek.FilePreviewer.dll", - "WinUI3Apps\\Powertoys.Peek.UI.dll", - "WinUI3Apps\\Powertoys.Peek.UI.exe", - "WinUI3Apps\\Powertoys.Peek.dll", + "WinUI3Apps\\PowerToys.FileLocksmithLib.Interop.dll", + "WinUI3Apps\\PowerToys.FileLocksmithExt.dll", + "WinUI3Apps\\PowerToys.FileLocksmithUI.exe", + "WinUI3Apps\\PowerToys.FileLocksmithUI.dll", + "WinUI3Apps\\PowerToys.FileLocksmithContextMenu.dll", + "FileLocksmithContextMenuPackage.msix", - "WinUI3Apps\\PowerToys.EnvironmentVariablesModuleInterface.dll", - "WinUI3Apps\\PowerToys.EnvironmentVariablesUILib.dll", - "WinUI3Apps\\PowerToys.EnvironmentVariables.dll", - "WinUI3Apps\\PowerToys.EnvironmentVariables.exe", + "WinUI3Apps\\Peek.Common.dll", + "WinUI3Apps\\Peek.FilePreviewer.dll", + "WinUI3Apps\\Powertoys.Peek.UI.dll", + "WinUI3Apps\\Powertoys.Peek.UI.exe", + "WinUI3Apps\\Powertoys.Peek.dll", - "PowerToys.ImageResizer.exe", - "PowerToys.ImageResizer.dll", - "PowerToys.ImageResizerExt.dll", - "PowerToys.ImageResizerContextMenu.dll", - "ImageResizerContextMenuPackage.msix", + "WinUI3Apps\\PowerToys.EnvironmentVariablesModuleInterface.dll", + "WinUI3Apps\\PowerToys.EnvironmentVariablesUILib.dll", + "WinUI3Apps\\PowerToys.EnvironmentVariables.dll", + "WinUI3Apps\\PowerToys.EnvironmentVariables.exe", - "PowerToys.KeyboardManager.dll", - "KeyboardManagerEditor\\PowerToys.KeyboardManagerEditor.exe", - "KeyboardManagerEngine\\PowerToys.KeyboardManagerEngine.exe", - "PowerToys.KeyboardManagerEditorLibraryWrapper.dll", + "PowerToys.ImageResizer.exe", + "PowerToys.ImageResizer.dll", + "PowerToys.ImageResizerExt.dll", + "PowerToys.ImageResizerContextMenu.dll", + "ImageResizerContextMenuPackage.msix", - "PowerToys.Launcher.dll", - "PowerToys.PowerLauncher.dll", - "PowerToys.PowerLauncher.exe", - "PowerToys.PowerLauncher.Telemetry.dll", - "Wox.dll", - "Wox.Infrastructure.dll", - "Wox.Plugin.dll", - "RunPlugins\\Calculator\\Microsoft.PowerToys.Run.Plugin.Calculator.dll", - "RunPlugins\\Folder\\Microsoft.Plugin.Folder.dll", - "RunPlugins\\Indexer\\Microsoft.Plugin.Indexer.dll", - "RunPlugins\\OneNote\\Microsoft.PowerToys.Run.Plugin.OneNote.dll", - "RunPlugins\\History\\Microsoft.PowerToys.Run.Plugin.History.dll", - "RunPlugins\\PowerToys\\Microsoft.PowerToys.Run.Plugin.PowerToys.dll", - "RunPlugins\\Program\\Microsoft.Plugin.Program.dll", - "RunPlugins\\Registry\\Microsoft.PowerToys.Run.Plugin.Registry.dll", - "RunPlugins\\WindowsSettings\\Microsoft.PowerToys.Run.Plugin.WindowsSettings.dll", - "RunPlugins\\Shell\\Microsoft.Plugin.Shell.dll", - "RunPlugins\\Uri\\Microsoft.Plugin.Uri.dll", - "RunPlugins\\WindowWalker\\Microsoft.Plugin.WindowWalker.dll", - "RunPlugins\\UnitConverter\\Community.PowerToys.Run.Plugin.UnitConverter.dll", - "RunPlugins\\VSCodeWorkspaces\\Community.PowerToys.Run.Plugin.VSCodeWorkspaces.dll", - "RunPlugins\\Service\\Microsoft.PowerToys.Run.Plugin.Service.dll", - "RunPlugins\\System\\Microsoft.PowerToys.Run.Plugin.System.dll", - "RunPlugins\\TimeDate\\Microsoft.PowerToys.Run.Plugin.TimeDate.dll", - "RunPlugins\\ValueGenerator\\Community.PowerToys.Run.Plugin.ValueGenerator.dll", - "RunPlugins\\WebSearch\\Community.PowerToys.Run.Plugin.WebSearch.dll", - "RunPlugins\\WindowsTerminal\\Microsoft.PowerToys.Run.Plugin.WindowsTerminal.dll", - - "WinUI3Apps\\PowerToys.MeasureToolModuleInterface.dll", - "WinUI3Apps\\PowerToys.MeasureToolCore.dll", - "WinUI3Apps\\PowerToys.MeasureToolUI.dll", - "WinUI3Apps\\PowerToys.MeasureToolUI.exe", + "PowerToys.KeyboardManager.dll", + "KeyboardManagerEditor\\PowerToys.KeyboardManagerEditor.exe", + "KeyboardManagerEngine\\PowerToys.KeyboardManagerEngine.exe", + "PowerToys.KeyboardManagerEditorLibraryWrapper.dll", - "PowerToys.FindMyMouse.dll", - "PowerToys.MouseHighlighter.dll", - "PowerToys.MouseJump.dll", - "PowerToys.MouseJump.Common.dll", - "PowerToys.MousePointerCrosshairs.dll", - "PowerToys.MouseJumpUI.dll", - "PowerToys.MouseJumpUI.exe", + "PowerToys.Launcher.dll", + "PowerToys.PowerLauncher.dll", + "PowerToys.PowerLauncher.exe", + "PowerToys.PowerLauncher.Telemetry.dll", + "Wox.dll", + "Wox.Infrastructure.dll", + "Wox.Plugin.dll", + "RunPlugins\\Calculator\\Microsoft.PowerToys.Run.Plugin.Calculator.dll", + "RunPlugins\\Folder\\Microsoft.Plugin.Folder.dll", + "RunPlugins\\Indexer\\Microsoft.Plugin.Indexer.dll", + "RunPlugins\\OneNote\\Microsoft.PowerToys.Run.Plugin.OneNote.dll", + "RunPlugins\\History\\Microsoft.PowerToys.Run.Plugin.History.dll", + "RunPlugins\\PowerToys\\Microsoft.PowerToys.Run.Plugin.PowerToys.dll", + "RunPlugins\\Program\\Microsoft.Plugin.Program.dll", + "RunPlugins\\Registry\\Microsoft.PowerToys.Run.Plugin.Registry.dll", + "RunPlugins\\WindowsSettings\\Microsoft.PowerToys.Run.Plugin.WindowsSettings.dll", + "RunPlugins\\Shell\\Microsoft.Plugin.Shell.dll", + "RunPlugins\\Uri\\Microsoft.Plugin.Uri.dll", + "RunPlugins\\WindowWalker\\Microsoft.Plugin.WindowWalker.dll", + "RunPlugins\\UnitConverter\\Community.PowerToys.Run.Plugin.UnitConverter.dll", + "RunPlugins\\VSCodeWorkspaces\\Community.PowerToys.Run.Plugin.VSCodeWorkspaces.dll", + "RunPlugins\\Service\\Microsoft.PowerToys.Run.Plugin.Service.dll", + "RunPlugins\\System\\Microsoft.PowerToys.Run.Plugin.System.dll", + "RunPlugins\\TimeDate\\Microsoft.PowerToys.Run.Plugin.TimeDate.dll", + "RunPlugins\\ValueGenerator\\Community.PowerToys.Run.Plugin.ValueGenerator.dll", + "RunPlugins\\WebSearch\\Community.PowerToys.Run.Plugin.WebSearch.dll", + "RunPlugins\\WindowsTerminal\\Microsoft.PowerToys.Run.Plugin.WindowsTerminal.dll", - "PowerToys.MouseWithoutBorders.dll", - "PowerToys.MouseWithoutBorders.exe", - "PowerToys.MouseWithoutBordersModuleInterface.dll", - "PowerToys.MouseWithoutBordersService.dll", - "PowerToys.MouseWithoutBordersService.exe", - "PowerToys.MouseWithoutBordersHelper.dll", - "PowerToys.MouseWithoutBordersHelper.exe", + "WinUI3Apps\\PowerToys.MeasureToolModuleInterface.dll", + "WinUI3Apps\\PowerToys.MeasureToolCore.dll", + "WinUI3Apps\\PowerToys.MeasureToolUI.dll", + "WinUI3Apps\\PowerToys.MeasureToolUI.exe", - "WinUI3Apps\\PowerToys.NewPlus.ShellExtension.dll", - "WinUI3Apps\\NewPlusPackage.msix", - "WinUI3Apps\\PowerToys.NewPlus.ShellExtension.win10.dll", + "PowerToys.FindMyMouse.dll", + "PowerToys.MouseHighlighter.dll", + "PowerToys.MouseJump.dll", + "PowerToys.MouseJump.Common.dll", + "PowerToys.MousePointerCrosshairs.dll", + "PowerToys.MouseJumpUI.dll", + "PowerToys.MouseJumpUI.exe", - "PowerAccent.Core.dll", - "PowerToys.PowerAccent.dll", - "PowerToys.PowerAccent.exe", - "PowerToys.PowerAccentModuleInterface.dll", - "PowerToys.PowerAccentKeyboardService.dll", + "PowerToys.MouseWithoutBorders.dll", + "PowerToys.MouseWithoutBorders.exe", + "PowerToys.MouseWithoutBordersModuleInterface.dll", + "PowerToys.MouseWithoutBordersService.dll", + "PowerToys.MouseWithoutBordersService.exe", + "PowerToys.MouseWithoutBordersHelper.dll", + "PowerToys.MouseWithoutBordersHelper.exe", - "WinUI3Apps\\PowerToys.PowerRenameExt.dll", - "WinUI3Apps\\PowerToys.PowerRename.exe", - "WinUI3Apps\\PowerToys.PowerRenameContextMenu.dll", - "WinUI3Apps\\PowerRenameContextMenuPackage.msix", + "WinUI3Apps\\PowerToys.NewPlus.ShellExtension.dll", + "WinUI3Apps\\NewPlusPackage.msix", + "WinUI3Apps\\PowerToys.NewPlus.ShellExtension.win10.dll", - "PowerToys.WorkspacesSnapshotTool.exe", - "PowerToys.WorkspacesLauncher.exe", - "PowerToys.WorkspacesWindowArranger.exe", - "PowerToys.WorkspacesEditor.exe", - "PowerToys.WorkspacesEditor.dll", - "PowerToys.WorkspacesLauncherUI.exe", - "PowerToys.WorkspacesLauncherUI.dll", - "PowerToys.WorkspacesModuleInterface.dll", - "PowerToys.WorkspacesCsharpLibrary.dll", + "PowerAccent.Core.dll", + "PowerToys.PowerAccent.dll", + "PowerToys.PowerAccent.exe", + "PowerToys.PowerAccentModuleInterface.dll", + "PowerToys.PowerAccentKeyboardService.dll", - "WinUI3Apps\\PowerToys.RegistryPreviewExt.dll", - "WinUI3Apps\\PowerToys.RegistryPreviewUILib.dll", - "WinUI3Apps\\PowerToys.RegistryPreview.dll", - "WinUI3Apps\\PowerToys.RegistryPreview.exe", + "WinUI3Apps\\PowerToys.PowerRenameExt.dll", + "WinUI3Apps\\PowerToys.PowerRename.exe", + "WinUI3Apps\\PowerToys.PowerRenameContextMenu.dll", + "WinUI3Apps\\PowerRenameContextMenuPackage.msix", - "PowerToys.ShortcutGuide.exe", - "PowerToys.ShortcutGuideModuleInterface.dll", + "PowerToys.WorkspacesSnapshotTool.exe", + "PowerToys.WorkspacesLauncher.exe", + "PowerToys.WorkspacesWindowArranger.exe", + "PowerToys.WorkspacesEditor.exe", + "PowerToys.WorkspacesEditor.dll", + "PowerToys.WorkspacesLauncherUI.exe", + "PowerToys.WorkspacesLauncherUI.dll", + "PowerToys.WorkspacesModuleInterface.dll", + "PowerToys.WorkspacesCsharpLibrary.dll", - "PowerToys.ZoomIt.exe", - "PowerToys.ZoomItModuleInterface.dll", - "PowerToys.ZoomItSettingsInterop.dll", + "WinUI3Apps\\PowerToys.RegistryPreviewExt.dll", + "WinUI3Apps\\PowerToys.RegistryPreviewUILib.dll", + "WinUI3Apps\\PowerToys.RegistryPreview.dll", + "WinUI3Apps\\PowerToys.RegistryPreview.exe", - "WinUI3Apps\\PowerToys.Settings.dll", - "WinUI3Apps\\PowerToys.Settings.exe", + "PowerToys.ShortcutGuide.exe", + "PowerToys.ShortcutGuideModuleInterface.dll", - "PowerToys.CmdPalModuleInterface.dll", - "CmdPalKeyboardService.dll", - "*Microsoft.CmdPal.UI_*.msix" - ], + "PowerToys.ZoomIt.exe", + "PowerToys.ZoomItModuleInterface.dll", + "PowerToys.ZoomItSettingsInterop.dll", + + "WinUI3Apps\\PowerToys.Settings.dll", + "WinUI3Apps\\PowerToys.Settings.exe", + + "PowerToys.CmdPalModuleInterface.dll", + "CmdPalKeyboardService.dll", + "*Microsoft.CmdPal.UI_*.msix" + ], "SigningInfo": { "Operations": [ { diff --git a/.pipelines/UpdateVersions.ps1 b/.pipelines/UpdateVersions.ps1 index a1bc5bef9a..0be3e3d30b 100644 --- a/.pipelines/UpdateVersions.ps1 +++ b/.pipelines/UpdateVersions.ps1 @@ -126,16 +126,15 @@ Get-ChildItem -Path $rootPath -Recurse packages.config | ForEach-Object { } # Update Directory.Packages.props file -$propsFile = [System.IO.Path]::Combine($rootPath,"Directory.Packages.props") -if (Test-Path $propsFile) { - $file = Read-FileWithEncoding -Path $propsFile +Get-ChildItem -Path $rootPath -Recurse "Directory.Packages.props" | ForEach-Object { + $file = Read-FileWithEncoding -Path $_.FullName $content = $file.Content if ($content -match '' $oldVersionString = '' $content = $content -replace $oldVersionString, $newVersionString - Write-FileWithEncoding -Path $propsFile -Content $content -Encoding $file.encoding - Write-Host "Modified " $propsFile + Write-FileWithEncoding -Path $_.FullName -Content $content -Encoding $file.encoding + Write-Host "Modified " $_.FullName } } @@ -144,8 +143,8 @@ Get-ChildItem -Path $rootPath -Recurse *.vcxproj | ForEach-Object { $file = Read-FileWithEncoding -Path $_.FullName $content = $file.Content if ($content -match '\\Microsoft.WindowsAppSDK.') { - $newVersionString = '\Microsoft.WindowsAppSDK.' + $WinAppSDKVersion + '\' - $oldVersionString = '\\Microsoft.WindowsAppSDK.[-.0-9a-zA-Z]*\\' + $newVersionString = '\Microsoft.WindowsAppSDK.' + $WinAppSDKVersion + $oldVersionString = '\\Microsoft.WindowsAppSDK.(?=[-.0-9a-zA-Z]*\d)[-.0-9a-zA-Z]*' #positive lookahead for at least a digit $content = $content -replace $oldVersionString, $newVersionString Write-FileWithEncoding -Path $_.FullName -Content $content -Encoding $file.encoding Write-Host "Modified " $_.FullName diff --git a/.pipelines/applyXamlStyling.ps1 b/.pipelines/applyXamlStyling.ps1 index 7cb7b4a4b0..1facedc569 100644 --- a/.pipelines/applyXamlStyling.ps1 +++ b/.pipelines/applyXamlStyling.ps1 @@ -41,6 +41,9 @@ Write-Output "" Write-Output "Restoring dotnet tools..." dotnet tool restore --disable-parallel --no-cache +# Use Regex syntax +$PathExcludes = "(\\obj\\)|(\\bin\\)|(\\x64\\)|(\\Generated Files\\PowerRenameXAML\\)|(\\RegistryPreviewUILib\\Controls\\HexBox\\)" + if (-not $Passive) { # Look for unstaged changed files by default @@ -87,7 +90,7 @@ if (-not $Passive) } Write-Output "Running Git Diff: $gitDiffCommand" - $files = Invoke-Expression $gitDiffCommand | Select-String -Pattern "\.xaml$" + $files = Invoke-Expression $gitDiffCommand | Select-String -Pattern "\.xaml$" | Where-Object { $_ -notmatch $PathExcludes } if (-not $Passive -and -not $Main -and -not $Unstaged -and -not $Staged -and -not $LastCommit) { @@ -107,7 +110,7 @@ if (-not $Passive) else { Write-Output "Checking all files (passively)" - $files = Get-ChildItem -Path "$PSScriptRoot\..\src\*.xaml" -Recurse | Select-Object -ExpandProperty FullName | Where-Object { $_ -notmatch "(\\obj\\)|(\\bin\\)|(\\x64\\)|(\\Generated Files\\PowerRenameXAML\\)" } + $files = Get-ChildItem -Path "$PSScriptRoot\..\src\*.xaml" -Recurse | Select-Object -ExpandProperty FullName | Where-Object { $_ -notmatch $PathExcludes } if ($files.count -gt 0) { diff --git a/.pipelines/v2/templates/job-build-project.yml b/.pipelines/v2/templates/job-build-project.yml index 48dd3b2222..5ddacf5435 100644 --- a/.pipelines/v2/templates/job-build-project.yml +++ b/.pipelines/v2/templates/job-build-project.yml @@ -53,6 +53,9 @@ parameters: - name: runTests type: boolean default: true + - name: buildTests + type: boolean + default: true - name: useVSPreview type: boolean default: false @@ -99,7 +102,7 @@ jobs: ${{ else }}: OutputBuildPlatform: ${{ platform }} variables: - MakeAppxPath: 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x86\MakeAppx.exe' + MakeAppxPath: 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.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. @@ -110,7 +113,7 @@ jobs: JobOutputArtifactName: build-$(BuildPlatform)-$(BuildConfiguration)${{ parameters.artifactStem }} NUGET_RESTORE_MSBUILD_ARGS: /p:Platform=$(BuildPlatform) # Required for nuget to work due to self contained NODE_OPTIONS: --max_old_space_size=16384 - ${{ if eq(parameters.runTests, true) }}: + ${{ if or(eq(parameters.runTests, true), eq(parameters.buildTests, true)) }}: MSBuildMainBuildTargets: Build;Test ${{ else }}: MSBuildMainBuildTargets: Build @@ -336,6 +339,7 @@ jobs: /p:VCRTForwarders-IncludeDebugCRT=false /p:PowerToysRoot=$(Build.SourcesDirectory) /p:PublishProfile=InstallationPublishProfile.pubxml + /p:TargetFramework=net9.0-windows10.0.26100.0 /bl:$(LogOutputDirectory)\publish-${{ join('_',split(project, '/')) }}.binlog $(RestoreAdditionalProjectSourcesArg) platform: $(BuildPlatform) @@ -398,9 +402,7 @@ jobs: testAssemblyVer2: | **\KeyboardManagerEngineTest.dll **\KeyboardManagerEditorTest.dll - **\UnitTests-CommonLib.dll - **\PowerRenameUnitTests.dll - **\UnitTests-FancyZones.dll + **\*UnitTest*.dll !**\obj\** - pwsh: |- @@ -578,7 +580,7 @@ jobs: displayName: Stage GPO files # Running the tests may result in future jobs consuming artifacts out of this build - - ${{ if eq(parameters.runTests, true) }}: + - ${{ if or(eq(parameters.runTests, true), eq(parameters.buildTests, true)) }}: - task: CopyFiles@2 displayName: Stage entire build output inputs: diff --git a/.pipelines/v2/templates/job-publish-symbols-using-symbolrequestprod-api.yml b/.pipelines/v2/templates/job-publish-symbols-using-symbolrequestprod-api.yml index 967b7ba4eb..6b214be612 100644 --- a/.pipelines/v2/templates/job-publish-symbols-using-symbolrequestprod-api.yml +++ b/.pipelines/v2/templates/job-publish-symbols-using-symbolrequestprod-api.yml @@ -68,7 +68,7 @@ jobs: pwsh: true ScriptType: InlineScript Inline: |- - $AzToken = (Get-AzAccessToken -ResourceUrl api://30471ccf-0966-45b9-a979-065dbedb24c1).Token + $AzToken = (Get-AzAccessToken -AsSecureString -ResourceUrl api://30471ccf-0966-45b9-a979-065dbedb24c1).Token | ConvertFrom-SecureString -AsPlainText Write-Host "##vso[task.setvariable variable=SymbolAccessToken;issecret=true]$AzToken" diff --git a/.pipelines/v2/templates/job-test-project.yml b/.pipelines/v2/templates/job-test-project.yml index 234f477fb6..ab682cd5a5 100644 --- a/.pipelines/v2/templates/job-test-project.yml +++ b/.pipelines/v2/templates/job-test-project.yml @@ -15,20 +15,29 @@ parameters: jobs: - job: Test${{ parameters.platform }}${{ parameters.configuration }} displayName: Test ${{ parameters.platform }} ${{ parameters.configuration }} + timeoutInMinutes: 300 variables: - BuildPlatform: ${{ parameters.platform }} + ${{ if or(eq(parameters.platform, 'x64Win10'), eq(parameters.platform, 'x64Win11')) }}: + BuildPlatform: x64 + ${{ else }}: + BuildPlatform: ${{ parameters.platform }} + TestPlatform: ${{ parameters.platform }} BuildConfiguration: ${{ parameters.configuration }} SrcPath: $(Build.Repository.LocalPath) - TestArtifactsName: build-${{ parameters.platform }}-${{ parameters.configuration }}${{ parameters.inputArtifactStem }} + TestArtifactsName: build-${{ variables.BuildPlatform }}-${{ parameters.configuration }}${{ parameters.inputArtifactStem }} pool: ${{ if eq(variables['System.CollectionId'], 'cb55739e-4afe-46a3-970f-1b49d8ee7564') }}: ${{ if ne(parameters.platform, 'ARM64') }}: name: SHINE-INT-Testing-x64 + ${{ if eq(parameters.platform, 'x64Win11') }}: + demands: ImageOverride -equals SHINE-W11-Testing ${{ else }}: name: SHINE-INT-Testing-arm64 ${{ else }}: ${{ if ne(parameters.platform, 'ARM64') }}: name: SHINE-OSS-Testing-x64 + ${{ if eq(parameters.platform, 'x64Win11') }}: + demands: ImageOverride -equals SHINE-W11-Testing ${{ else }}: name: SHINE-OSS-Testing-arm64 steps: @@ -101,8 +110,13 @@ jobs: vsTestVersion: 'toolsInstaller' uiTests: true rerunFailedTests: true + # Since UITests-FancyZonesEditor.dll is generated in both UITests-FancyZonesEditor and UITests-FancyZones, removed one to avoid duplicate test runs testAssemblyVer2: | - **\UITests-FancyZones.dll - **\UITests-FancyZonesEditor.dll + **\*UITest*.dll !**\obj\** - !**\ref\** \ No newline at end of file + !**\ref\** + !**\UITests-FancyZones\**\UITests-FancyZonesEditor.dll + + + env: + platform: '$(TestPlatform)' diff --git a/.pipelines/v2/templates/pipeline-ci-build.yml b/.pipelines/v2/templates/pipeline-ci-build.yml index 37e06ae4f2..cc7a67e613 100644 --- a/.pipelines/v2/templates/pipeline-ci-build.yml +++ b/.pipelines/v2/templates/pipeline-ci-build.yml @@ -60,16 +60,4 @@ stages: useLatestWinAppSDK: ${{ parameters.useLatestWinAppSDK }} ${{ if eq(parameters.useLatestWinAppSDK, true) }}: winAppSDKVersionNumber: ${{ parameters.winAppSDKVersionNumber }} - useExperimentalVersion: ${{ parameters.useExperimentalVersion }} - - - ${{ if and(eq(parameters.runTests, true), not(and(eq(platform, 'arm64'), eq(variables['System.PullRequest.IsFork'], true)))) }}: - - stage: Test_${{ platform }} - displayName: Test ${{ platform }} - dependsOn: - - Build_${{platform}} - jobs: - - template: job-test-project.yml - parameters: - platform: ${{ platform }} - configuration: Release - useLatestWebView2: ${{ parameters.useLatestWebView2 }} + useExperimentalVersion: ${{ parameters.useExperimentalVersion }} \ No newline at end of file diff --git a/.pipelines/v2/templates/pipeline-ui-tests-automation.yml b/.pipelines/v2/templates/pipeline-ui-tests-automation.yml new file mode 100644 index 0000000000..4db1005f3b --- /dev/null +++ b/.pipelines/v2/templates/pipeline-ui-tests-automation.yml @@ -0,0 +1,84 @@ +variables: + - name: runCodesignValidationInjectionBG + value: false + - name: EnablePipelineCache + value: true + - ${{ if eq(parameters.enableMsBuildCaching, true) }}: + - name: EnablePipelineCache + value: true + +parameters: + - name: buildPlatforms + type: object + default: + - x64 + - arm64 + - name: enableMsBuildCaching + type: boolean + default: false + - name: useVSPreview + type: boolean + default: false + - name: useLatestWebView2 + type: boolean + default: false + +stages: + - ${{ each platform in parameters.buildPlatforms }}: + - stage: Build_${{ platform }} + displayName: Build ${{ platform }} + dependsOn: [] + jobs: + - template: job-build-project.yml + parameters: + pool: + ${{ if eq(variables['System.CollectionId'], 'cb55739e-4afe-46a3-970f-1b49d8ee7564') }}: + name: SHINE-INT-L + ${{ else }}: + name: SHINE-OSS-L + ${{ if eq(parameters.useVSPreview, true) }}: + demands: ImageOverride -equals SHINE-VS17-Preview + buildPlatforms: + - ${{ platform }} + buildConfigurations: [Release] + enablePackageCaching: true + enableMsBuildCaching: ${{ parameters.enableMsBuildCaching }} + runTests: false + buildTests: true + useVSPreview: ${{ parameters.useVSPreview }} + + - ${{ if eq(platform, 'x64') }}: + - stage: Test_x64Win10 + displayName: Test x64Win10 + dependsOn: + - Build_${{platform}} + jobs: + - template: job-test-project.yml + parameters: + platform: x64Win10 + configuration: Release + useLatestWebView2: ${{ parameters.useLatestWebView2 }} + + - ${{ if eq(platform, 'x64') }}: + - stage: Test_x64Win11 + displayName: Test x64Win11 + dependsOn: + - Build_${{platform}} + jobs: + - template: job-test-project.yml + parameters: + platform: x64Win11 + configuration: Release + useLatestWebView2: ${{ parameters.useLatestWebView2 }} + + - ${{ if ne(platform, 'x64') }}: + - stage: Test_${{ platform }} + displayName: Test ${{ platform }} + dependsOn: + - Build_${{platform}} + jobs: + - template: job-test-project.yml + parameters: + platform: ${{ platform }} + configuration: Release + useLatestWebView2: ${{ parameters.useLatestWebView2 }} diff --git a/.pipelines/verifyAndSetLatestVCToolsVersion.ps1 b/.pipelines/verifyAndSetLatestVCToolsVersion.ps1 index a47e6a2292..4ebf3a3eec 100644 --- a/.pipelines/verifyAndSetLatestVCToolsVersion.ps1 +++ b/.pipelines/verifyAndSetLatestVCToolsVersion.ps1 @@ -1,7 +1,28 @@ $VSInstances = ([xml](& 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe' -latest -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -include packages -format xml)) $VSPackages = $VSInstances.instances.instance.packages.package -$LatestVCPackage = ($VSInstances.instances.instance.packages.package | ? { $_.id -eq "Microsoft.VisualCpp.Tools.Core" }) +$LatestVCPackage = ($VSPackages | ? { $_.id -eq "Microsoft.VisualCpp.Tools.Core" }) $LatestVCToolsVersion = $LatestVCPackage.version; + +$VSRoot = (& 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe' -latest -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property 'resolvedInstallationPath') +$VCToolsRoot = Join-Path $VSRoot "VC\Tools\MSVC" + +# We have observed a few instances where the VC tools package version actually +# differs from the version on the files themselves. We might as well check +# whether the version we just found _actually exists_ before we use it. +# We'll use whichever highest version exists. +$PackageVCToolPath = Join-Path $VCToolsRoot $LatestVCToolsVersion +If ($Null -Eq (Get-Item $PackageVCToolPath -ErrorAction:Ignore)) { + $VCToolsVersions = Get-ChildItem $VCToolsRoot | ForEach-Object { + [Version]$_.Name + } | Sort -Descending + $LatestActualVCToolsVersion = $VCToolsVersions | Select -First 1 + + If ([Version]$LatestVCToolsVersion -Ne $LatestActualVCToolsVersion) { + Write-Output "VC Tools Mismatch: Directory = $LatestActualVCToolsVersion, Package = $LatestVCToolsVersion" + $LatestVCToolsVersion = $LatestActualVCToolsVersion.ToString(3) + } +} + Write-Output "Latest VCToolsVersion: $LatestVCToolsVersion" Write-Output "Updating VCToolsVersion environment variable for job" Write-Output "##vso[task.setvariable variable=VCToolsVersion]$LatestVCToolsVersion" diff --git a/.pipelines/verifyDepsJsonLibraryVersions.ps1 b/.pipelines/verifyDepsJsonLibraryVersions.ps1 index e85ca1d991..085e1e439a 100644 --- a/.pipelines/verifyDepsJsonLibraryVersions.ps1 +++ b/.pipelines/verifyDepsJsonLibraryVersions.ps1 @@ -92,4 +92,3 @@ if ($totalFailures -gt 0) { Write-Host -ForegroundColor Green "All " $referencedFileVersionsPerDll.keys.Count " libraries are mentioned with the same version across the dependencies.`r`n" exit 0 - diff --git a/.pipelines/verifyNoticeMdAgainstNugetPackages.ps1 b/.pipelines/verifyNoticeMdAgainstNugetPackages.ps1 index ebef8412a7..e3120836c8 100644 --- a/.pipelines/verifyNoticeMdAgainstNugetPackages.ps1 +++ b/.pipelines/verifyNoticeMdAgainstNugetPackages.ps1 @@ -72,9 +72,57 @@ $returnList = [System.Collections.Generic.HashSet[string]]($totalList) -join "`r Write-Host $returnList +# Extract the current package list from NOTICE.md +$noticePattern = "## NuGet Packages used by PowerToys\s*((?:\r?\n- .+)+)" +$noticeMatch = [regex]::Match($noticeFile, $noticePattern) + +if ($noticeMatch.Success) { + $currentNoticePackageList = $noticeMatch.Groups[1].Value.Trim() +} else { + Write-Warning "Warning: Could not find 'NuGet Packages used by PowerToys' section in NOTICE.md" + $currentNoticePackageList = "" +} + if (!$noticeFile.Trim().EndsWith($returnList.Trim())) { Write-Host -ForegroundColor Red "Notice.md does not match NuGet list." + + # Show detailed differences + $generatedPackages = $returnList -split "`r`n|`n" | Where-Object { $_.Trim() -ne "" } | Sort-Object + $noticePackages = $currentNoticePackageList -split "`r`n|`n" | Where-Object { $_.Trim() -ne "" } | ForEach-Object { $_.Trim() } | Sort-Object + + Write-Host "" + Write-Host -ForegroundColor Cyan "=== DETAILED DIFFERENCE ANALYSIS ===" + Write-Host "" + + # Find packages in proj file list but not in NOTICE.md + $missingFromNotice = $generatedPackages | Where-Object { $noticePackages -notcontains $_ } + if ($missingFromNotice.Count -gt 0) { + Write-Host -ForegroundColor Red "MissingFromNotice:" + foreach ($pkg in $missingFromNotice) { + Write-Host -ForegroundColor Red " $pkg" + } + Write-Host "" + } + + # Find packages in NOTICE.md but not in proj file list + $extraInNotice = $noticePackages | Where-Object { $generatedPackages -notcontains $_ } + if ($extraInNotice.Count -gt 0) { + Write-Host -ForegroundColor Yellow "ExtraInNotice:" + foreach ($pkg in $extraInNotice) { + Write-Host -ForegroundColor Yellow " $pkg" + } + Write-Host "" + } + + # Show counts for summary + Write-Host -ForegroundColor Cyan "Summary:" + Write-Host " Proj file list has $($generatedPackages.Count) packages" + Write-Host " NOTICE.md has $($noticePackages.Count) packages" + Write-Host " MissingFromNotice: $($missingFromNotice.Count) packages" + Write-Host " ExtraInNotice: $($extraInNotice.Count) packages" + Write-Host "" + exit 1 } diff --git a/.pipelines/versionAndSignCheck.ps1 b/.pipelines/versionAndSignCheck.ps1 index 89cfbeea1c..78031ac7e0 100644 --- a/.pipelines/versionAndSignCheck.ps1 +++ b/.pipelines/versionAndSignCheck.ps1 @@ -28,6 +28,8 @@ $versionExceptions = @( "ObjectModelCsProjection.dll", "RendererCsProjection.dll") -join '|'; $nullVersionExceptions = @( + "SkiaSharp.Views.WinUI.Native.dll", + "libSkiaSharp.dll", "codicon.ttf", "e_sqlite3.dll", "getfilesiginforedist.dll", diff --git a/.vsconfig b/.vsconfig index 77ec8b0ffd..90abacd81c 100644 --- a/.vsconfig +++ b/.vsconfig @@ -9,6 +9,7 @@ "Microsoft.VisualStudio.Component.Windows10SDK.19041", "Microsoft.VisualStudio.Component.Windows10SDK.20348", "Microsoft.VisualStudio.Component.Windows10SDK.22621", + "Microsoft.VisualStudio.Component.Windows10SDK.26100", "Microsoft.VisualStudio.ComponentGroup.UWP.VC", "Microsoft.VisualStudio.Component.UWP.VC.ARM64", "Microsoft.VisualStudio.Component.VC.Runtimes.ARM64.Spectre", diff --git a/COMMUNITY.md b/COMMUNITY.md index ccfa61decf..5f9a0a57ea 100644 --- a/COMMUNITY.md +++ b/COMMUNITY.md @@ -6,8 +6,8 @@ Names are in alphabetical order based on first name. ## High impact community members -### [@Aaron-Junker](https://github.com/Aaron-Junker) - [Aaron Junker](https://aaron-junker.github.io) -Aaron has helped triaging, discussing, and creating a substantial number of issues and contributed features/fixes. Aaron was the primary person for helping build the File Explorer preview pane handler for developer files. +### [@Noraa-Junker](https://github.com/Noraa-Junker) - [Noraa Junker](https://noraajunker.ch) +Noraa has helped triaging, discussing, and creating a substantial number of issues and contributed features/fixes. Noraa was the primary person for helping build the File Explorer preview pane handler for developer files. ### [@cgaarden](https://github.com/cgaarden) - [Christian Gaarden Gaardmark](https://www.onegreatworld.com) Christian contributed New+ utility @@ -21,7 +21,7 @@ Connor was the creator of Workspaces and helped create Command Palette (PowerToy ### [@damienleroy](https://github.com/damienleroy) - [Damien Leroy](https://www.linkedin.com/in/Damien-Leroy-b2734416a/) Damien has helped out by developing and contributing the Quick Accent utility. -### [@daverayment ](https://github.com/daverayment) - [David Rayment](https://www.linkedin.com/in/david-rayment-168b5251/) +### [@daverayment](https://github.com/daverayment) - [David Rayment](https://www.linkedin.com/in/david-rayment-168b5251/) Dave has helped improve the experience inside of Peek by adding in new features and fixing bugs. ### [@davidegiacometti](https://github.com/davidegiacometti) - [Davide Giacometti](https://www.linkedin.com/in/davidegiacometti/) @@ -117,10 +117,6 @@ PowerRename is from Chris's SmartRename and icon rendering for SVGs in File Expl PowerToys Awake is a tool to keep your computer awake. -### [@Niels9001](https://github.com/niels9001/) - [Niels Laute](https://nielslaute.com/) - -Niels has helped drive large sums of our update toward a new [consistent and modern UX](https://github.com/microsoft/PowerToys/issues/891). This includes the [launcher work](https://github.com/microsoft/PowerToys/issues/44), color picker UX update and [icon design](https://github.com/microsoft/PowerToys/issues/1118). - ### [@randyrants](https://github.com/randyrants) - [Randy Santossio](https://www.randyrants.com) Randy contributed Registry Preview and some very early conversations about keyboard remapping. @@ -184,11 +180,9 @@ ZoomIt source code was originally implemented by [Sysinternals](https://sysinter ## PowerToys core team -- [@crutkas](https://github.com/crutkas/) - Clint Rutkas - Lead - [@cinnamon-msft](https://github.com/cinnamon-msft) - Kayla Cinnamon - Lead -- [@nguyen-dows](https://github.com/nguyen-dows) - Christopher Nguyen - Product Manager - [@craigloewen-msft](https://github.com/craigloewen-msft) - Craig Loewen - Product Manager -- [@zhiwei-ms](https://github.com/zhiwei-ms) - Zhiwei Yu - Product Manager +- [@niels9001](https://github.com/niels9001/) - Niels Laute - Product Manager - [@dhowett](https://github.com/dhowett) - Dustin Howett - Dev lead - [@yeelam-gordon](https://github.com/yeelam-gordon) - Gordon Lam - Dev lead - [@jamrobot](https://github.com/jamrobot) - Jerry Xu - Dev lead @@ -204,6 +198,13 @@ ZoomIt source code was originally implemented by [Sysinternals](https://sysinter - [@zhaopy536](https://github.com/zhaopy536) - Peiyao Zhao - Dev - [@wang563681252](https://github.com/wang563681252) - Zhaopeng Wang - Dev - [@vanzue](https://github.com/vanzue) - Kai Tao - Dev +- [@zadjii-msft](https://github.com/zadjii-msft) - Mike Griese - Dev +- [@khmyznikov](https://github.com/khmyznikov) - Gleb Khmyznikov - Dev +- [@chatasweetie](https://github.com/chatasweetie) - Jessica Earley-Cha - Dev +- [@MichaelJolley](https://github.com/MichaelJolley) - Michael Jolley - Dev +- [@Jaylyn-Barbee](https://github.com/Jaylyn-Barbee) - Jaylyn Barbee - Dev +- [@zateutsch](https://github.com/zateutsch) - Zach Teutsch - Dev +- [@crutkas](https://github.com/crutkas/) - Clint Rutkas - Overhead ## Former PowerToys core team members @@ -211,6 +212,7 @@ ZoomIt source code was originally implemented by [Sysinternals](https://sysinter - [@ethanfangg](https://github.com/ethanfangg) - Ethan Fang - Product Manager - [@plante-msft](https://github.com/plante-msft) - Connor Plante - Product Manager - [@joadoumie](https://github.com/joadoumie) - Jordi Adoumie - Product Manager +- [@nguyen-dows](https://github.com/nguyen-dows) - Christopher Nguyen - Product Manager - [@enricogior](https://github.com/enricogior) - Enrico Giordani - Dev Lead - [@bzoz](https://github.com/bzoz) - Bartosz Sosnowski - Dev - [@ivan100sic](https://github.com/ivan100sic) - Ivan Stošić - Dev diff --git a/Cpp.Build.props b/Cpp.Build.props index 06c22b45bf..5a4538f940 100644 --- a/Cpp.Build.props +++ b/Cpp.Build.props @@ -96,8 +96,8 @@ - 10.0.22621.0 - 10.0.22621.0 + 10.0.26100.0 + 10.0.26100.0 10.0.19041.0 diff --git a/Directory.Packages.props b/Directory.Packages.props index 083a66e5a9..8651672751 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -31,31 +31,31 @@ - + - + - - - - - + + + + + - + - - + + - - + + @@ -69,31 +69,33 @@ + + - + - - - + + + - + - + - - + + - + - - - - + + + + diff --git a/NOTICE.md b/NOTICE.md index 21d51192c4..60b480d6c0 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -79,6 +79,43 @@ For more information, please refer to ### Calculator +#### exprtk + +We use the exprtk library (exprtk.hpp) to evaluate mathematical expressions. + +**Source**: [https://github.com/ArashPartow/exprtk](https://github.com/ArashPartow/exprtk) + +``` +MIT License + +Copyright (c) 1999-2024 Arash Partow + +https://www.partow.net/programming/exprtk/index.html + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +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 OR COPYRIGHT HOLDERS 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. +``` + +## Utility: PowerToys Run Built-in Extensions + +### Calculator + #### Mages We use the Mages NuGet package for calculating the result of expression. @@ -807,30 +844,25 @@ DEALINGS IN THE SOFTWARE. **Source**: https://github.com/kuba--/zip -This is free and unencumbered software released into the public domain. +All Rights Reserved. -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. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -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 above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. -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 +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 OR COPYRIGHT HOLDERS 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. ## Utility: Measure tool @@ -1395,6 +1427,37 @@ EXHIBIT A -Mozilla Public License. ## Utility: Registry Preview +### HexBox.WinUI + +We use HexBox.WinUI to show a preview of binary values. + +**Source**: https://github.com/hotkidfamily/HexBox.WinUI + +``` +MIT License + +Copyright (c) 2019 Filip Jeremic +Copyright (c) 2024~2025 hotkidfamily@gmail.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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 OR COPYRIGHT HOLDERS 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. +``` + ### Monaco Editor **Source**: https://github.com/Microsoft/monaco-editor @@ -1425,6 +1488,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ``` + ## NuGet Packages used by PowerToys - AdaptiveCards.ObjectModel.WinUI3 2.0.0-beta @@ -1453,26 +1517,26 @@ SOFTWARE. - Mages 3.0.0 - Markdig.Signed 0.34.0 - MessagePack 3.1.3 -- Microsoft.Bcl.AsyncInterfaces 9.0.5 +- Microsoft.Bcl.AsyncInterfaces 9.0.6 - Microsoft.CodeAnalysis.NetAnalyzers 9.0.0 -- Microsoft.Data.Sqlite 9.0.5 +- Microsoft.Data.Sqlite 9.0.6 - Microsoft.Diagnostics.Tracing.TraceEvent 3.1.16 - Microsoft.DotNet.ILCompiler (A) -- Microsoft.Extensions.DependencyInjection 9.0.5 -- Microsoft.Extensions.Hosting 9.0.5 -- Microsoft.Extensions.Hosting.WindowsServices 9.0.5 -- Microsoft.Extensions.Logging 9.0.5 -- Microsoft.Extensions.Logging.Abstractions 9.0.5 +- Microsoft.Extensions.DependencyInjection 9.0.6 +- Microsoft.Extensions.Hosting 9.0.6 +- Microsoft.Extensions.Hosting.WindowsServices 9.0.6 +- Microsoft.Extensions.Logging 9.0.6 +- Microsoft.Extensions.Logging.Abstractions 9.0.6 - Microsoft.NET.ILLink.Tasks (A) - Microsoft.SemanticKernel 1.15.0 - Microsoft.Toolkit.Uwp.Notifications 7.1.2 - Microsoft.Web.WebView2 1.0.2903.40 -- Microsoft.Win32.SystemEvents 9.0.5 -- Microsoft.Windows.Compatibility 9.0.5 -- Microsoft.Windows.CsWin32 0.2.46-beta +- Microsoft.Win32.SystemEvents 9.0.6 +- Microsoft.Windows.Compatibility 9.0.6 +- Microsoft.Windows.CsWin32 0.3.183 - Microsoft.Windows.CsWinRT 2.2.0 -- Microsoft.Windows.SDK.BuildTools 10.0.22621.2428 -- Microsoft.WindowsAppSDK 1.7.250401001 +- Microsoft.Windows.SDK.BuildTools 10.0.26100.4188 +- Microsoft.WindowsAppSDK 1.7.250513003 - Microsoft.WindowsPackageManager.ComInterop 1.10.340 - Microsoft.Xaml.Behaviors.WinUI.Managed 2.0.9 - Microsoft.Xaml.Behaviors.Wpf 1.1.39 @@ -1485,27 +1549,28 @@ SOFTWARE. - ReverseMarkdown 4.1.0 - ScipBe.Common.Office.OneNote 3.0.1 - SharpCompress 0.37.2 +- SkiaSharp.Views.WinUI 2.88.9 - StreamJsonRpc 2.21.69 - StyleCop.Analyzers 1.2.0-beta.556 -- System.CodeDom 9.0.5 +- System.CodeDom 9.0.6 - System.CommandLine 2.0.0-beta4.22272.1 -- System.ComponentModel.Composition 9.0.5 -- System.Configuration.ConfigurationManager 9.0.5 -- System.Data.OleDb 9.0.5 -- System.Data.SqlClient 4.8.6 -- System.Diagnostics.EventLog 9.0.5 -- System.Diagnostics.PerformanceCounter 9.0.5 -- System.Drawing.Common 9.0.5 +- System.ComponentModel.Composition 9.0.6 +- System.Configuration.ConfigurationManager 9.0.6 +- System.Data.OleDb 9.0.6 +- System.Data.SqlClient 4.9.0 +- System.Diagnostics.EventLog 9.0.6 +- System.Diagnostics.PerformanceCounter 9.0.6 +- System.Drawing.Common 9.0.6 - System.IO.Abstractions 22.0.13 - System.IO.Abstractions.TestingHelpers 22.0.13 -- System.Management 9.0.5 +- System.Management 9.0.6 - System.Net.Http 4.3.4 - System.Private.Uri 4.3.2 - System.Reactive 6.0.1 -- System.Runtime.Caching 9.0.5 -- System.ServiceProcess.ServiceController 9.0.5 -- System.Text.Encoding.CodePages 9.0.5 -- System.Text.Json 9.0.5 +- System.Runtime.Caching 9.0.6 +- System.ServiceProcess.ServiceController 9.0.6 +- System.Text.Encoding.CodePages 9.0.6 +- System.Text.Json 9.0.6 - System.Text.RegularExpressions 4.3.1 - UnicodeInformation 2.6.0 - UnitsNet 5.56.0 diff --git a/PowerToys.sln b/PowerToys.sln index 201c0fbefe..f81a222668 100644 --- a/PowerToys.sln +++ b/PowerToys.sln @@ -604,6 +604,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WindowProperties", "WindowP EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WorkspacesLib", "src\modules\Workspaces\WorkspacesLib\WorkspacesLib.vcxproj", "{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WorkspacesLibUnitTests", "src\modules\Workspaces\WorkspacesLib.UnitTests\WorkspacesLibUnitTests.vcxproj", "{A85D4D9F-9A39-4B5D-8B5A-9F2D5C9A8B4C}" + ProjectSection(ProjectDependencies) = postProject + {B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332} = {B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332} + EndProjectSection +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkspacesLauncherUI", "src\modules\Workspaces\WorkspacesLauncherUI\WorkspacesLauncherUI.csproj", "{9C53CC25-0623-4569-95BC-B05410675EE3}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WorkspacesModuleInterface", "src\modules\Workspaces\WorkspacesModuleInterface\WorkspacesModuleInterface.vcxproj", "{45285DF2-9742-4ECA-9AC9-58951FC26489}" @@ -706,10 +711,20 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RegistryPreview.FuzzTests", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.Ext.System", "src\modules\cmdpal\ext\Microsoft.CmdPal.Ext.System\Microsoft.CmdPal.Ext.System.csproj", "{64B88F02-CD88-4ED8-9624-989A800230F9}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FancyZones.FuzzTests", "src\modules\fancyzones\FancyZones.FuzzTests\FancyZones.FuzzTests.csproj", "{0217E86E-3476-9946-DE8E-9D200CEBD47A}" +EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CmdPalKeyboardService", "src\modules\cmdpal\CmdPalKeyboardService\CmdPalKeyboardService.vcxproj", "{5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerRename.FuzzingTest", "src\modules\powerrename\PowerRename.FuzzingTest\PowerRename.FuzzingTest.vcxproj", "{2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MouseUtils.UITests", "src\modules\MouseUtils\MouseUtils.UITests\MouseUtils.UITests.csproj", "{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkspacesEditorUITest", "src\modules\Workspaces\WorkspacesEditorUITest\WorkspacesEditorUITest.csproj", "{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CalculatorEngineCommon", "src\common\CalculatorEngineCommon\CalculatorEngineCommon.vcxproj", "{2CF78CF7-8FEB-4BE1-9591-55FA25B48FC6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ManagedCsWin32", "src\common\ManagedCsWin32\ManagedCsWin32.csproj", "{14AFD976-B4D2-49D0-9E6C-AA93CC061B8A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM64 = Debug|ARM64 @@ -2198,6 +2213,14 @@ Global {B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Release|ARM64.Build.0 = Release|ARM64 {B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Release|x64.ActiveCfg = Release|x64 {B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Release|x64.Build.0 = Release|x64 + {A85D4D9F-9A39-4B5D-8B5A-9F2D5C9A8B4C}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {A85D4D9F-9A39-4B5D-8B5A-9F2D5C9A8B4C}.Debug|ARM64.Build.0 = Debug|ARM64 + {A85D4D9F-9A39-4B5D-8B5A-9F2D5C9A8B4C}.Debug|x64.ActiveCfg = Debug|x64 + {A85D4D9F-9A39-4B5D-8B5A-9F2D5C9A8B4C}.Debug|x64.Build.0 = Debug|x64 + {A85D4D9F-9A39-4B5D-8B5A-9F2D5C9A8B4C}.Release|ARM64.ActiveCfg = Release|ARM64 + {A85D4D9F-9A39-4B5D-8B5A-9F2D5C9A8B4C}.Release|ARM64.Build.0 = Release|ARM64 + {A85D4D9F-9A39-4B5D-8B5A-9F2D5C9A8B4C}.Release|x64.ActiveCfg = Release|x64 + {A85D4D9F-9A39-4B5D-8B5A-9F2D5C9A8B4C}.Release|x64.Build.0 = Release|x64 {9C53CC25-0623-4569-95BC-B05410675EE3}.Debug|ARM64.ActiveCfg = Debug|ARM64 {9C53CC25-0623-4569-95BC-B05410675EE3}.Debug|ARM64.Build.0 = Debug|ARM64 {9C53CC25-0623-4569-95BC-B05410675EE3}.Debug|x64.ActiveCfg = Debug|x64 @@ -2582,6 +2605,30 @@ Global {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 + {4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Debug|ARM64.Build.0 = Debug|ARM64 + {4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Debug|x64.ActiveCfg = Debug|x64 + {4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Debug|x64.Build.0 = Debug|x64 + {4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Release|ARM64.ActiveCfg = Release|ARM64 + {4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Release|ARM64.Build.0 = Release|ARM64 + {4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Release|x64.ActiveCfg = Release|x64 + {4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Release|x64.Build.0 = Release|x64 + {43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Debug|ARM64.Build.0 = Debug|ARM64 + {43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Debug|x64.ActiveCfg = Debug|x64 + {43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Debug|x64.Build.0 = Debug|x64 + {43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Release|ARM64.ActiveCfg = Release|ARM64 + {43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Release|ARM64.Build.0 = Release|ARM64 + {43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Release|x64.ActiveCfg = Release|x64 + {43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Release|x64.Build.0 = Release|x64 + {0217E86E-3476-9946-DE8E-9D200CEBD47A}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {0217E86E-3476-9946-DE8E-9D200CEBD47A}.Debug|ARM64.Build.0 = Debug|ARM64 + {0217E86E-3476-9946-DE8E-9D200CEBD47A}.Debug|x64.ActiveCfg = Debug|x64 + {0217E86E-3476-9946-DE8E-9D200CEBD47A}.Debug|x64.Build.0 = Debug|x64 + {0217E86E-3476-9946-DE8E-9D200CEBD47A}.Release|ARM64.ActiveCfg = Release|ARM64 + {0217E86E-3476-9946-DE8E-9D200CEBD47A}.Release|ARM64.Build.0 = Release|ARM64 + {0217E86E-3476-9946-DE8E-9D200CEBD47A}.Release|x64.ActiveCfg = Release|x64 + {0217E86E-3476-9946-DE8E-9D200CEBD47A}.Release|x64.Build.0 = Release|x64 {5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2}.Debug|ARM64.ActiveCfg = Debug|ARM64 {5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2}.Debug|ARM64.Build.0 = Debug|ARM64 {5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2}.Debug|x64.ActiveCfg = Debug|x64 @@ -2596,6 +2643,22 @@ Global {2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}.Release|ARM64.ActiveCfg = Release|ARM64 {2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}.Release|x64.ActiveCfg = Release|x64 {2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}.Release|x64.Build.0 = Release|x64 + {2CF78CF7-8FEB-4BE1-9591-55FA25B48FC6}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {2CF78CF7-8FEB-4BE1-9591-55FA25B48FC6}.Debug|ARM64.Build.0 = Debug|ARM64 + {2CF78CF7-8FEB-4BE1-9591-55FA25B48FC6}.Debug|x64.ActiveCfg = Debug|x64 + {2CF78CF7-8FEB-4BE1-9591-55FA25B48FC6}.Debug|x64.Build.0 = Debug|x64 + {2CF78CF7-8FEB-4BE1-9591-55FA25B48FC6}.Release|ARM64.ActiveCfg = Release|ARM64 + {2CF78CF7-8FEB-4BE1-9591-55FA25B48FC6}.Release|ARM64.Build.0 = Release|ARM64 + {2CF78CF7-8FEB-4BE1-9591-55FA25B48FC6}.Release|x64.ActiveCfg = Release|x64 + {2CF78CF7-8FEB-4BE1-9591-55FA25B48FC6}.Release|x64.Build.0 = Release|x64 + {14AFD976-B4D2-49D0-9E6C-AA93CC061B8A}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {14AFD976-B4D2-49D0-9E6C-AA93CC061B8A}.Debug|ARM64.Build.0 = Debug|ARM64 + {14AFD976-B4D2-49D0-9E6C-AA93CC061B8A}.Debug|x64.ActiveCfg = Debug|x64 + {14AFD976-B4D2-49D0-9E6C-AA93CC061B8A}.Debug|x64.Build.0 = Debug|x64 + {14AFD976-B4D2-49D0-9E6C-AA93CC061B8A}.Release|ARM64.ActiveCfg = Release|ARM64 + {14AFD976-B4D2-49D0-9E6C-AA93CC061B8A}.Release|ARM64.Build.0 = Release|ARM64 + {14AFD976-B4D2-49D0-9E6C-AA93CC061B8A}.Release|x64.ActiveCfg = Release|x64 + {14AFD976-B4D2-49D0-9E6C-AA93CC061B8A}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2735,7 +2798,7 @@ Global {25C91A4E-BA4E-467A-85CD-8B62545BF674} = {A50C70A6-2DA0-4027-B90E-B1A40755A8A5} {6AB6A2D6-F859-4A82-9184-0BD29C9F07D1} = {A50C70A6-2DA0-4027-B90E-B1A40755A8A5} {212AD910-8488-4036-BE20-326931B75FB2} = {4AFC9975-2456-4C70-94A4-84073C1CED93} - {7AC943C9-52E8-44CF-9083-744D8049667B} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC} + {7AC943C9-52E8-44CF-9083-744D8049667B} = {322566EF-20DC-43A6-B9F8-616AF942579A} {54A93AF7-60C7-4F6C-99D2-FBB1F75F853A} = {7AC943C9-52E8-44CF-9083-744D8049667B} {92C39820-9F84-4529-BC7D-22AAE514D63B} = {7AC943C9-52E8-44CF-9083-744D8049667B} {515554D1-D004-4F7F-A107-2211FC0F6B2C} = {7AC943C9-52E8-44CF-9083-744D8049667B} @@ -2815,6 +2878,7 @@ Global {BE126CBB-AE12-406A-9837-A05ACFCA57A7} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF} {14CB58B7-D280-4A7A-95DE-4B2DF14EA000} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF} {B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF} + {A85D4D9F-9A39-4B5D-8B5A-9F2D5C9A8B4C} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF} {9C53CC25-0623-4569-95BC-B05410675EE3} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF} {45285DF2-9742-4ECA-9AC9-58951FC26489} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF} {3D63307B-9D27-44FD-B033-B26F39245B85} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF} @@ -2866,8 +2930,13 @@ Global {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} + {4E0AE3A4-2EE0-44D7-A2D0-8769977254A1} = {322566EF-20DC-43A6-B9F8-616AF942579A} + {43E779F3-D83C-48B1-BA8D-1912DBD76FC9} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF} + {0217E86E-3476-9946-DE8E-9D200CEBD47A} = {D1D6BC88-09AE-4FB4-AD24-5DED46A791DD} {5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2} = {3846508C-77EB-4034-A702-F8BB263C4F79} {2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E} = {89E20BCE-EB9C-46C8-8B50-E01A82E6FDC3} + {2CF78CF7-8FEB-4BE1-9591-55FA25B48FC6} = {1AFB6476-670D-4E80-A464-657E01DFF482} + {14AFD976-B4D2-49D0-9E6C-AA93CC061B8A} = {1AFB6476-670D-4E80-A464-657E01DFF482} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0} diff --git a/README.md b/README.md index 44c1a800c0..232cf6fd82 100644 --- a/README.md +++ b/README.md @@ -53,10 +53,10 @@ This is our preferred method. ### Via Microsoft Store -Install from the [Microsoft Store's PowerToys page][microsoft-store-link]. You must be using the [new Microsoft Store](https://blogs.windows.com/windowsExperience/2021/06/24/building-a-new-open-microsoft-store-on-windows-11/) which is available for both Windows 11 and Windows 10. +Install from the [Microsoft Store's PowerToys page][microsoft-store-link]. You must be using the [new Microsoft Store](https://blogs.windows.com/windowsExperience/2021/06/24/building-a-new-open-microsoft-store-on-windows-11/), which is available for both Windows 11 and Windows 10. ### Via WinGet -Download PowerToys from [WinGet][winget-link]. Updating PowerToys via winget will respect current PowerToys installation scope. To install PowerToys, run the following command from the command line / PowerShell: +Download PowerToys from [WinGet][winget-link]. Updating PowerToys via winget will respect the current PowerToys installation scope. To install PowerToys, run the following command from the command line / PowerShell: #### User scope installer [default] ```powershell @@ -79,7 +79,7 @@ There is a collection of [third-party plugins](./doc/thirdPartyRunPlugins.md) cr ## Contributing -This project welcomes contributions of all types. Besides coding features / bug fixes, other ways to assist include spec writing, design, documentation, and finding bugs. We are excited to work with the power user community to build a set of tools for helping you get the most out of Windows. +This project welcomes contributions of all types. Besides coding features / bug fixes, other ways to assist include spec writing, design, documentation, and finding bugs. We are excited to work with the power user community to build a set of tools for helping you get the most out of Windows. We ask that **before you start work on a feature that you would like to contribute**, please read our [Contributor's Guide](CONTRIBUTING.md). We would be happy to work with you to figure out the best approach, provide guidance and mentorship throughout feature development, and help avoid any wasted or duplicate effort. @@ -99,7 +99,7 @@ In this release, we focused on new features, stability, and automation. **✨Highlights** - - We focused on greatly improving Command Palette's performance and fixing a large amount of bugs. Some new features we've added are: + - We focused on greatly improving the Command Palette's performance and fixing a large number of bugs. Some new features we've added are: - Added the ability for Command Palette to search any file using a fallback command. - Added the ability to make the Command Palette global hotkey a low-level keyboard hook. - Added open URL fallback command for the WebSearch extension, enabling users to directly open URLs in the browser from Command Palette. @@ -117,17 +117,17 @@ In this release, we focused on new features, stability, and automation. ### Command Not Found - - Updated the WinGet Command Not Found script to only enable the experimental features if they actually exist. + - Updated the WinGet Command Not Found script to only enable the experimental features if they exist. ### Command Palette - Updated bug template to include Command Palette module. - Fixed an issue where the toast window was not scaled for DPI, causing layout issues under display scaling. - - Fixed an issue where Up/Down keyboard navigation didn't move selection when caret was at position 0, and add continuous navigation like PT Run v1. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! + - Fixed an issue where Up/Down keyboard navigation didn't move selection when the caret was at position 0, and added continuous navigation like PT Run v1. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! - Updated the Time and Date extension code to simplify it and improve clarity. - Fixed an issue where capitalization in the command causes failure when trying to go to the mouse pointer, resolved by adjusting the command to lowercase. - - Added open URL fallback command for the WebSearch extension, enabling users to directly open URLs in the browser from Command Palette. Thanks [@htcfreek](https://github.com/htcfreek)! - - Added setting to enable/disable system tray icon in CmdPal and align terminology with Windows 11. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! + - Added open URL fallback command for the WebSearch extension, enabling users to directly open URLs in the browser from the Command Palette. Thanks [@htcfreek](https://github.com/htcfreek)! + - Added setting to enable/disable system tray icon in CmdPal and aligned terminology with Windows 11. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! - Fixed an alias update issue by removing the old alias when a new one is set. - Resolved GitHub casing conflict by migrating Exts and exts into a new ext directory, ensuring consistent structure across platforms and preventing path fragmentation. - Fix an issue where the 'Create New Extension' command generated empty file names. @@ -155,9 +155,9 @@ In this release, we focused on new features, stability, and automation. - Refactored CmdPal classes to improve JSON serialization and introduced new serialization contexts for better performance and maintainability. - Added support for ahead-of-time (AoT) compilation. - Added retry mechanism for CmdPal launch. - - Removed some unused files from CmdPal.Common to simplify codebase and facilitate marking it as AoT-compatible. + - Removed some unused files from CmdPal.Common to simplify the codebase and facilitate marking it as AoT-compatible. - Fixed a bug where a race condition in the update of SearchText caused the cursor in the input box to automatically jump to the end of the line, ensuring SearchText is only updated after it has actually been changed. - - Added support for searching any file in fallback command. + - Added support for searching any file in the fallback command. - Cleaned up AoT-related code to prevent duplicate operations during testing. - Reduced CmdPal load time by parallelizing extension startup and adding timeouts to prevent misbehaving extensions from blocking others. - Enhanced UI behavior by dismissing the details pane when the list gets emptied, avoiding inconsistent visual states. @@ -179,7 +179,7 @@ In this release, we focused on new features, stability, and automation. ### Keyboard Manager - - Fixed an issue where a modifier key, when set without specifying left or right, would get stuck due to incorrect key handling, by tracking the pressed keys and sending the correct key accordingly. Thanks [@mantaionut](https://github.com/mantaionut)! + - Fixed an issue where a modifier key, when set without specifying left or right, would get stuck due to incorrect key handling by tracking the pressed keys and sending the correct key accordingly. Thanks [@mantaionut](https://github.com/mantaionut)! ### PowerRename @@ -187,7 +187,7 @@ In this release, we focused on new features, stability, and automation. ### PowerToys Run - - Added support for custom formats in the "Time and Date" plugin and improves error messages for invalid input formats. Thanks [@htcfreek](https://github.com/htcfreek)! + - Added support for custom formats in the "Time and Date" plugin and improved error messages for invalid input formats. Thanks [@htcfreek](https://github.com/htcfreek)! - Fix two crashes: one for WFT on very early dates and another for calculating the week of the month on very late dates (e.g., 31.12.9999), and reorder UI settings. Thanks [@htcfreek](https://github.com/htcfreek)! - Fix an issue where capitalization in the command causes failure when trying to go to the mouse pointer, resolved by adjusting the command to lowercase. - Added version details to plugin error messages for 'Loading error' and 'Init error'. Thanks [@htcfreek](https://github.com/htcfreek)! @@ -254,7 +254,7 @@ For [v0.92][github-next-release-work], we'll work on the items below: - New UI Automation tests - Working on installer upgrades - Upgrading Keyboard Manager's editor UI - - Stability / bug fixes + - Stability, bug fixes ## PowerToys Community @@ -266,7 +266,7 @@ This project has adopted the [Microsoft Open Source Code of Conduct][oss-conduct ## Privacy Statement -The application logs basic diagnostic data (telemetry). For more information on privacy and what we collect, see our [PowerToys Data and Privacy documentation](https://aka.ms/powertoys-data-and-privacy-documentation). +The application logs basic diagnostic data (telemetry). For more privacy information and what we collect, see our [PowerToys Data and Privacy documentation](https://aka.ms/powertoys-data-and-privacy-documentation). [oss-CLA]: https://cla.opensource.microsoft.com [oss-conduct-code]: CODE_OF_CONDUCT.md diff --git a/deps/cziplib b/deps/cziplib index 7a57414261..81314fff0a 160000 --- a/deps/cziplib +++ b/deps/cziplib @@ -1 +1 @@ -Subproject commit 7a57414261361ca991ff8053881343eb6bb6f205 +Subproject commit 81314fff0a882b72a9ad321e7a3311660125b56e diff --git a/doc/devdocs/UITests.md b/doc/devdocs/UITests.md index b1b91a632c..d02eeb6993 100644 --- a/doc/devdocs/UITests.md +++ b/doc/devdocs/UITests.md @@ -21,67 +21,74 @@ - Create a new project and add the following references to the project file. Change the OutputPath to your own module's path. ``` - - Library - - false - - - - ..\..\..\..\$(Platform)\$(Configuration)\tests\KeyboardManagerUITests\ - - - - - - - + + + + + + {4E0AE3A4-2EE0-44D7-A2D0-8769977254A0} + PowerToys.Hosts.UITests + PowerToys.Hosts.UITests + false + true + enable + Library + + + false + + + $(SolutionDir)$(Platform)\$(Configuration)\tests\Hosts.UITests\ + + + + + + + + ``` - Inherit your test class from UITestBase. >Set Scope: The default scope starts from the PowerToys settings UI. If you want to start from your own module, set the constructor as shown below: >Specify Scope: ``` - [TestClass] - public class RunFancyZonesTest : UITestBase - { - public RunFancyZonesTest() - : base(PowerToysModule.FancyZone) - { - } - } + [TestClass] + public class HostModuleTests : UITestBase + { + public HostModuleTests() + : base(PowerToysModule.Hosts, WindowSize.Small_Vertical) + { + } + } ``` -- Then you can start using session to perform the UI operations. +- Then you can start performing the UI operations. **Example** ``` -using Microsoft.PowerToys.UITest; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace UITests_KeyboardManager +[TestMethod("Hosts.Basic.EmptyViewShouldWork")] +[TestCategory("Hosts File Editor #4")] +public void TestEmptyView() { - [TestClass] - public class RunKeyboardManagerUITests : UITestBase - { - [TestMethod] - public void OpenKeyboardManagerEditor() - { - // Open KeyboardManagerEditor - this.Session.Find + + + + + + + + + (new(commandViewModel.Model)); + } + } } diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Settings/ExtensionPage.xaml b/src/modules/cmdpal/Microsoft.CmdPal.UI/Settings/ExtensionPage.xaml index dc297df841..ab221daaa9 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Settings/ExtensionPage.xaml +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Settings/ExtensionPage.xaml @@ -1,4 +1,4 @@ - + - + + IsOn="{x:Bind IsDirectAlias, Mode=TwoWay}" /> diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Strings/en-us/Resources.resw b/src/modules/cmdpal/Microsoft.CmdPal.UI/Strings/en-us/Resources.resw index 6c63eeff16..11f1106250 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Strings/en-us/Resources.resw +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Strings/en-us/Resources.resw @@ -412,4 +412,16 @@ Right-click to remove the key combination, thereby deactivating the shortcut. More + + Settings + + + Exit + + + Direct + + + Indirect + \ No newline at end of file diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Styles/TextBlock.xaml b/src/modules/cmdpal/Microsoft.CmdPal.UI/Styles/TextBlock.xaml new file mode 100644 index 0000000000..6160585127 --- /dev/null +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Styles/TextBlock.xaml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/app.manifest b/src/modules/cmdpal/Microsoft.CmdPal.UI/app.manifest index 0cd29eccd5..f5dd7ff036 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/app.manifest +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/app.manifest @@ -13,6 +13,7 @@ + true/PM PerMonitorV2 diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/rd.xml b/src/modules/cmdpal/Microsoft.CmdPal.UI/rd.xml new file mode 100644 index 0000000000..f8dc5641af --- /dev/null +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/rd.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/modules/cmdpal/Microsoft.Terminal.UI/Microsoft.Terminal.UI.vcxproj b/src/modules/cmdpal/Microsoft.Terminal.UI/Microsoft.Terminal.UI.vcxproj index a3c282b7f1..4f13f17ed6 100644 --- a/src/modules/cmdpal/Microsoft.Terminal.UI/Microsoft.Terminal.UI.vcxproj +++ b/src/modules/cmdpal/Microsoft.Terminal.UI/Microsoft.Terminal.UI.vcxproj @@ -1,9 +1,9 @@ - + ..\..\..\..\ - $(PathToRoot)packages\Microsoft.WindowsAppSDK.1.7.250401001 + $(PathToRoot)packages\Microsoft.WindowsAppSDK.1.7.250513003 @@ -19,7 +19,7 @@ false Windows Store 10.0 - 10.0.22621.0 + 10.0.26100.0 10.0.19041.0 diff --git a/src/modules/cmdpal/README.md b/src/modules/cmdpal/README.md index 518a54b78f..b9e0a42f61 100644 --- a/src/modules/cmdpal/README.md +++ b/src/modules/cmdpal/README.md @@ -1,6 +1,6 @@ # ![cmdpal logo](./Microsoft.CmdPal.UI/Assets/Stable/StoreLogo.scale-100.png) Command Palette -Windows Command Palette ("CmdPal") is the next iteration of PowerToys Run. With extensibility at it's core, the Command Palette is your one-stop launcher to start _anything_. +Windows Command Palette ("CmdPal") is the next iteration of PowerToys Run. With extensibility at its core, the Command Palette is your one-stop launcher to start _anything_. By default, CmdPal is bound to Win+Alt+Space. @@ -39,8 +39,8 @@ Projects of interest are: [Initial SDK Spec]: ./doc/initial-sdk-spec/initial-sdk-spec.md -[generic samples]: ./Exts/SamplePagesExtension -[real samples]: ./Exts/ProcessMonitorExtension +[generic samples]: ./ext/SamplePagesExtension +[real samples]: ./ext/ProcessMonitorExtension [real extensions that we've "shipped" already]: https://github.com/zadjii/CmdPalExtensions/blob/main/src/extensions diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/AllAppsSettings.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/AllAppsSettings.cs index 91d5cd5736..63a40614aa 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/AllAppsSettings.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/AllAppsSettings.cs @@ -71,7 +71,7 @@ public class AllAppsSettings : JsonSettingsManager internal static string SettingsJsonPath() { - string directory = Utilities.BaseSettingsPath("Microsoft.CmdPal"); + var directory = Utilities.BaseSettingsPath("Microsoft.CmdPal"); Directory.CreateDirectory(directory); // now, the state is just next to the exe diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/AppCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/AppCommand.cs index cb57086cb3..d2fe194830 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/AppCommand.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/AppCommand.cs @@ -8,7 +8,12 @@ using System.Threading.Tasks; using ManagedCommon; using Microsoft.CmdPal.Ext.Apps.Programs; using Microsoft.CmdPal.Ext.Apps.Properties; +using Microsoft.CmdPal.Ext.Apps.Utils; using Microsoft.CommandPalette.Extensions.Toolkit; +using Windows.Services.Maps; +using Windows.Win32; +using Windows.Win32.System.Com; +using Windows.Win32.UI.Shell; using WyHash; namespace Microsoft.CmdPal.Ext.Apps; @@ -27,26 +32,31 @@ internal sealed partial class AppCommand : InvokableCommand internal static async Task StartApp(string aumid) { - var appManager = new ApplicationActivationManager(); - const ActivateOptions noFlags = ActivateOptions.None; await Task.Run(() => { - try + unsafe { - appManager.ActivateApplication(aumid, /*queryArguments*/ string.Empty, noFlags, out var unusedPid); - } - catch (System.Exception ex) - { - Logger.LogError(ex.Message); + IApplicationActivationManager* appManager = null; + try + { + PInvoke.CoCreateInstance(typeof(ApplicationActivationManager).GUID, null, CLSCTX.CLSCTX_INPROC_SERVER, out appManager).ThrowOnFailure(); + using var handle = new SafeComHandle((IntPtr)appManager); + appManager->ActivateApplication( + aumid, + string.Empty, + ACTIVATEOPTIONS.AO_NONE, + out var unusedPid); + } + catch (System.Exception ex) + { + Logger.LogError(ex.Message); + } } }).ConfigureAwait(false); } internal static async Task StartExe(string path) { - var appManager = new ApplicationActivationManager(); - - // const ActivateOptions noFlags = ActivateOptions.None; await Task.Run(() => { try diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Commands/CopyPathCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Commands/CopyPathCommand.cs new file mode 100644 index 0000000000..30ad044f37 --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Commands/CopyPathCommand.cs @@ -0,0 +1,49 @@ +// 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.Globalization; +using System.Text; +using ManagedCommon; +using Microsoft.CmdPal.Ext.Apps.Properties; +using Microsoft.CommandPalette.Extensions.Toolkit; + +namespace Microsoft.CmdPal.Ext.Apps.Commands; + +internal sealed partial class CopyPathCommand : InvokableCommand +{ + private static readonly IconInfo TheIcon = new("\ue8c8"); + + private readonly string _target; + + public CopyPathCommand(string target) + { + Name = Resources.copy_path; + Icon = TheIcon; + + _target = target; + } + + private static readonly CompositeFormat CopyFailedFormat = CompositeFormat.Parse(Resources.copy_failed); + + public override CommandResult Invoke() + { + try + { + ClipboardHelper.SetText(_target); + } + catch (Exception ex) + { + Logger.LogError("Copy failed: " + ex.Message); + return CommandResult.ShowToast( + new ToastArgs + { + Message = string.Format(CultureInfo.CurrentCulture, CopyFailedFormat, ex.Message), + Result = CommandResult.KeepOpen(), + }); + } + + return CommandResult.ShowToast(Resources.copied_to_clipboard); + } +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Microsoft.CmdPal.Ext.Apps.csproj b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Microsoft.CmdPal.Ext.Apps.csproj index 24782335ed..9ead1da687 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Microsoft.CmdPal.Ext.Apps.csproj +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Microsoft.CmdPal.Ext.Apps.csproj @@ -1,11 +1,13 @@  + Microsoft.CmdPal.Ext.Apps enable $(SolutionDir)$(Platform)\$(Configuration)\WinUI3Apps\CmdPal false false + true @@ -18,7 +20,7 @@ - + @@ -49,4 +51,9 @@ Resources.Designer.cs + + + + + diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/NativeMethods.json b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/NativeMethods.json new file mode 100644 index 0000000000..b1156c41b7 --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/NativeMethods.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://aka.ms/CsWin32.schema.json", + "allowMarshaling": false, + "comInterop": { + "preserveSigMethods": [ "*" ] + } +} \ No newline at end of file diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/NativeMethods.txt b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/NativeMethods.txt index c0c94348c5..017871d42f 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/NativeMethods.txt +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/NativeMethods.txt @@ -1,19 +1,20 @@ -GetPhysicallyInstalledSystemMemory -GlobalMemoryStatusEx -GetSystemInfo +IStream CoCreateInstance -SetForegroundWindow -IsIconic -RegisterHotKey -SetWindowLongPtr -CallWindowProc -ShowWindow -SetForegroundWindow -SetFocus -SetActiveWindow -MonitorFromWindow -GetMonitorInfo +IApplicationActivationManager +ApplicationActivationManager SHCreateStreamOnFileEx -CoAllowSetForegroundWindow -SHCreateStreamOnFileEx -SHLoadIndirectString \ No newline at end of file +SHCreateItemFromParsingName +IShellItem +ISequentialStream +SHLoadIndirectString +IAppxFactory +AppxFactory +IAppxManifestReader +IAppxManifestApplicationsEnumerator +IAppxManifestApplication +IAppxManifestProperties +IShellLinkW +ShellLink +IPersistFile +CoTaskMemFree +IUnknown diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/AppxFactory.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/AppxFactory.cs deleted file mode 100644 index 420128ef29..0000000000 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/AppxFactory.cs +++ /dev/null @@ -1,14 +0,0 @@ -// 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/ext/Microsoft.CmdPal.Ext.Apps/Programs/AppxPackageHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/AppxPackageHelper.cs index 83a9fbb146..ef81410898 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/AppxPackageHelper.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/AppxPackageHelper.cs @@ -2,42 +2,75 @@ // 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.Runtime.InteropServices; +using ManagedCommon; +using Microsoft.CmdPal.Ext.Apps.Utils; +using Microsoft.UI.Xaml.Controls; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.Storage.Packaging.Appx; 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) + internal static unsafe List GetAppsFromManifest(IStream* stream) { - var reader = AppxFactory.CreateManifestReader(stream); - var manifestApps = reader.GetApplications(); + PInvoke.CoCreateInstance(typeof(AppxFactory).GUID, null, CLSCTX.CLSCTX_INPROC_SERVER, out IAppxFactory* appxFactory).ThrowOnFailure(); + using var handle = new SafeComHandle((IntPtr)appxFactory); - while (manifestApps.GetHasCurrent()) + IAppxManifestReader* reader = null; + IAppxManifestApplicationsEnumerator* manifestApps = null; + var result = new List(); + + appxFactory->CreateManifestReader(stream, &reader); + using var readerHandle = new SafeComHandle((IntPtr)reader); + reader->GetApplications(&manifestApps); + using var manifestAppsHandle = new SafeComHandle((IntPtr)manifestApps); + + while (true) { - var manifestApp = manifestApps.GetCurrent(); - var hr = manifestApp.GetStringValue("AppListEntry", out var appListEntry); - _ = CheckHRAndReturnOrThrow(hr, appListEntry); - if (appListEntry != "none") + manifestApps->GetHasCurrent(out var hasCurrent); + if (hasCurrent == false) { - yield return manifestApp; + break; } - manifestApps.MoveNext(); - } - } + IAppxManifestApplication* manifestApp = null; - internal static T CheckHRAndReturnOrThrow(HRESULT hr, T result) - { - if (hr != HRESULT.S_OK) - { - Marshal.ThrowExceptionForHR((int)hr); + try + { + manifestApps->GetCurrent(&manifestApp).ThrowOnFailure(); + + var hr = manifestApp->GetStringValue("AppListEntry", out var appListEntryPtr); + var appListEntry = ComFreeHelper.GetStringAndFree(hr, appListEntryPtr); + + if (appListEntry != "none") + { + result.Add((IntPtr)manifestApp); + } + else if (manifestApp != null) + { + manifestApp->Release(); + } + } + catch (Exception ex) + { + if (manifestApp != null) + { + manifestApp->Release(); + } + + Logger.LogError($"Failed to get current application from manifest: {ex.Message}"); + } + + manifestApps->MoveNext(out var hasNext); + if (hasNext == false) + { + break; + } } return result; diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IApplicationActivationManager.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IApplicationActivationManager.cs deleted file mode 100644 index 32fb3f2890..0000000000 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IApplicationActivationManager.cs +++ /dev/null @@ -1,47 +0,0 @@ -// 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/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxFactory.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxFactory.cs deleted file mode 100644 index 7af82b74ab..0000000000 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxFactory.cs +++ /dev/null @@ -1,20 +0,0 @@ -// 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/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestApplication.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestApplication.cs deleted file mode 100644 index 1ca12d3c29..0000000000 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestApplication.cs +++ /dev/null @@ -1,19 +0,0 @@ -// 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/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestApplicationsEnumerator.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestApplicationsEnumerator.cs deleted file mode 100644 index f7152a0813..0000000000 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestApplicationsEnumerator.cs +++ /dev/null @@ -1,19 +0,0 @@ -// 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/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestProperties.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestProperties.cs deleted file mode 100644 index 4c61e6f069..0000000000 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestProperties.cs +++ /dev/null @@ -1,19 +0,0 @@ -// 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/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestReader.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestReader.cs deleted file mode 100644 index 20c7fb62f6..0000000000 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestReader.cs +++ /dev/null @@ -1,27 +0,0 @@ -// 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/ext/Microsoft.CmdPal.Ext.Apps/Programs/ReparsePoint.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/ReparsePoint.cs index 1e3463f77c..bdbce1bfc3 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/ReparsePoint.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/ReparsePoint.cs @@ -3,20 +3,19 @@ // See the LICENSE file in the project root for more information. using System; -using System.ComponentModel; using System.IO; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; - +using ManagedCsWin32; using Microsoft.Win32.SafeHandles; -using Windows.Storage.Streams; namespace Microsoft.CmdPal.Ext.Apps.Programs; /// /// Provides access to NTFS reparse points in .Net. /// -public static class ReparsePoint +public static partial class ReparsePoint { #pragma warning disable SA1310 // Field names should not contain underscore @@ -35,118 +34,6 @@ public static class ReparsePoint private const int E_INVALID_PROTOCOL_FORMAT = unchecked((int)0x83760002); #pragma warning restore SA1310 // Field names should not contain underscore - [Flags] - private enum FileAccessType : uint - { - DELETE = 0x00010000, - READ_CONTROL = 0x00020000, - WRITE_DAC = 0x00040000, - WRITE_OWNER = 0x00080000, - SYNCHRONIZE = 0x00100000, - - STANDARD_RIGHTS_REQUIRED = 0x000F0000, - - STANDARD_RIGHTS_READ = READ_CONTROL, - STANDARD_RIGHTS_WRITE = READ_CONTROL, - STANDARD_RIGHTS_EXECUTE = READ_CONTROL, - - STANDARD_RIGHTS_ALL = 0x001F0000, - - SPECIFIC_RIGHTS_ALL = 0x0000FFFF, - - ACCESS_SYSTEM_SECURITY = 0x01000000, - - MAXIMUM_ALLOWED = 0x02000000, - - GENERIC_READ = 0x80000000, - GENERIC_WRITE = 0x40000000, - GENERIC_EXECUTE = 0x20000000, - GENERIC_ALL = 0x10000000, - - FILE_READ_DATA = 0x0001, - FILE_WRITE_DATA = 0x0002, - FILE_APPEND_DATA = 0x0004, - FILE_READ_EA = 0x0008, - FILE_WRITE_EA = 0x0010, - FILE_EXECUTE = 0x0020, - FILE_READ_ATTRIBUTES = 0x0080, - FILE_WRITE_ATTRIBUTES = 0x0100, - - FILE_ALL_ACCESS = - STANDARD_RIGHTS_REQUIRED | - SYNCHRONIZE - | 0x1FF, - - FILE_GENERIC_READ = - STANDARD_RIGHTS_READ | - FILE_READ_DATA | - FILE_READ_ATTRIBUTES | - FILE_READ_EA | - SYNCHRONIZE, - - FILE_GENERIC_WRITE = - STANDARD_RIGHTS_WRITE | - FILE_WRITE_DATA | - FILE_WRITE_ATTRIBUTES | - FILE_WRITE_EA | - FILE_APPEND_DATA | - SYNCHRONIZE, - - FILE_GENERIC_EXECUTE = - STANDARD_RIGHTS_EXECUTE | - FILE_READ_ATTRIBUTES | - FILE_EXECUTE | - SYNCHRONIZE, - } - - [Flags] - private enum FileShareType : uint - { - None = 0x00000000, - Read = 0x00000001, - Write = 0x00000002, - Delete = 0x00000004, - } - - private enum CreationDisposition : uint - { - New = 1, - CreateAlways = 2, - OpenExisting = 3, - OpenAlways = 4, - TruncateExisting = 5, - } - - [Flags] - private enum FileAttributes : uint - { - Readonly = 0x00000001, - Hidden = 0x00000002, - System = 0x00000004, - Directory = 0x00000010, - Archive = 0x00000020, - Device = 0x00000040, - Normal = 0x00000080, - Temporary = 0x00000100, - SparseFile = 0x00000200, - ReparsePoint = 0x00000400, - Compressed = 0x00000800, - Offline = 0x00001000, - NotContentIndexed = 0x00002000, - Encrypted = 0x00004000, - Write_Through = 0x80000000, - Overlapped = 0x40000000, - NoBuffering = 0x20000000, - RandomAccess = 0x10000000, - SequentialScan = 0x08000000, - DeleteOnClose = 0x04000000, - BackupSemantics = 0x02000000, - PosixSemantics = 0x01000000, - OpenReparsePoint = 0x00200000, - OpenNoRecall = 0x00100000, - FirstPipeInstance = 0x00080000, - } - private enum AppExecutionAliasReparseTagBufferLayoutVersion : uint { Invalid = 0, @@ -195,27 +82,6 @@ public static class ReparsePoint public AppExecutionAliasReparseTagBufferLayoutVersion Version; } - [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] - private static extern bool DeviceIoControl( - IntPtr hDevice, - uint dwIoControlCode, - IntPtr inBuffer, - int nInBufferSize, - IntPtr outBuffer, - int nOutBufferSize, - out int pBytesReturned, - IntPtr lpOverlapped); - - [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] - private static extern IntPtr CreateFile( - string lpFileName, - FileAccessType dwDesiredAccess, - FileShareType dwShareMode, - IntPtr lpSecurityAttributes, - CreationDisposition dwCreationDisposition, - FileAttributes dwFlagsAndAttributes, - IntPtr hTemplateFile); - /// /// Gets the target of the specified reparse point. /// @@ -229,13 +95,13 @@ public static class ReparsePoint public static string? GetTarget(string reparsePoint) { using (SafeFileHandle reparsePointHandle = new SafeFileHandle( - CreateFile( + Kernel32.CreateFile( reparsePoint, FileAccessType.FILE_READ_ATTRIBUTES | FileAccessType.FILE_READ_EA, FileShareType.Delete | FileShareType.Read | FileShareType.Write, IntPtr.Zero, CreationDisposition.OpenExisting, - FileAttributes.OpenReparsePoint, + ManagedCsWin32.FileAttributes.OpenReparsePoint, IntPtr.Zero), true)) { @@ -253,7 +119,7 @@ public static class ReparsePoint for (var i = 0; i < 2; ++i) { int bytesReturned; - var result = DeviceIoControl( + var result = Kernel32.DeviceIoControl( reparsePointHandle.DangerousGetHandle(), FSCTL_GET_REPARSE_POINT, IntPtr.Zero, @@ -284,15 +150,18 @@ public static class ReparsePoint ThrowLastWin32Error("Unable to get information about reparse point."); } - AppExecutionAliasReparseTagHeader aliasReparseHeader = Marshal.PtrToStructure(outBuffer); - - if (aliasReparseHeader.ReparseTag == IO_REPARSE_TAG_APPEXECLINK) + unsafe { - var metadata = AppExecutionAliasMetadata.FromPersistedRepresentationIntPtr( - outBuffer, - aliasReparseHeader.Version); + var aliasReparseHeader = Unsafe.Read((void*)outBuffer); - return metadata.ExePath; + if (aliasReparseHeader.ReparseTag == IO_REPARSE_TAG_APPEXECLINK) + { + var metadata = AppExecutionAliasMetadata.FromPersistedRepresentationIntPtr( + outBuffer, + aliasReparseHeader.Version); + + return metadata.ExePath; + } } return null; @@ -319,61 +188,65 @@ public static class ReparsePoint public static AppExecutionAliasMetadata FromPersistedRepresentationIntPtr(IntPtr reparseDataBufferPtr, AppExecutionAliasReparseTagBufferLayoutVersion version) { - var dataOffset = Marshal.SizeOf(); - var dataBufferPtr = reparseDataBufferPtr + dataOffset; - - string? packageFullName = null; - string? packageFamilyName = null; - string? aumid = null; - string? exePath = null; - - VerifyVersion(version); - - switch (version) + unsafe { - case AppExecutionAliasReparseTagBufferLayoutVersion.Initial: - packageFullName = Marshal.PtrToStringUni(dataBufferPtr); - if (packageFullName is not null) - { - dataBufferPtr += Encoding.Unicode.GetByteCount(packageFullName) + Encoding.Unicode.GetByteCount("\0"); - aumid = Marshal.PtrToStringUni(dataBufferPtr); + var dataOffset = Unsafe.SizeOf(); - if (aumid is not null) + var dataBufferPtr = reparseDataBufferPtr + dataOffset; + + string? packageFullName = null; + string? packageFamilyName = null; + string? aumid = null; + string? exePath = null; + + VerifyVersion(version); + + switch (version) + { + case AppExecutionAliasReparseTagBufferLayoutVersion.Initial: + packageFullName = Marshal.PtrToStringUni(dataBufferPtr); + if (packageFullName is not null) { - dataBufferPtr += Encoding.Unicode.GetByteCount(aumid) + Encoding.Unicode.GetByteCount("\0"); - exePath = Marshal.PtrToStringUni(dataBufferPtr); + dataBufferPtr += Encoding.Unicode.GetByteCount(packageFullName) + Encoding.Unicode.GetByteCount("\0"); + aumid = Marshal.PtrToStringUni(dataBufferPtr); + + if (aumid is not null) + { + dataBufferPtr += Encoding.Unicode.GetByteCount(aumid) + Encoding.Unicode.GetByteCount("\0"); + exePath = Marshal.PtrToStringUni(dataBufferPtr); + } } - } - break; + break; - case AppExecutionAliasReparseTagBufferLayoutVersion.PackageFamilyName: - case AppExecutionAliasReparseTagBufferLayoutVersion.MultiAppTypeSupport: - packageFamilyName = Marshal.PtrToStringUni(dataBufferPtr); + case AppExecutionAliasReparseTagBufferLayoutVersion.PackageFamilyName: + case AppExecutionAliasReparseTagBufferLayoutVersion.MultiAppTypeSupport: + packageFamilyName = Marshal.PtrToStringUni(dataBufferPtr); - if (packageFamilyName is not null) - { - dataBufferPtr += Encoding.Unicode.GetByteCount(packageFamilyName) + Encoding.Unicode.GetByteCount("\0"); - aumid = Marshal.PtrToStringUni(dataBufferPtr); - - if (aumid is not null) + if (packageFamilyName is not null) { - dataBufferPtr += Encoding.Unicode.GetByteCount(aumid) + Encoding.Unicode.GetByteCount("\0"); + dataBufferPtr += Encoding.Unicode.GetByteCount(packageFamilyName) + Encoding.Unicode.GetByteCount("\0"); + aumid = Marshal.PtrToStringUni(dataBufferPtr); - exePath = Marshal.PtrToStringUni(dataBufferPtr); + if (aumid is not null) + { + dataBufferPtr += Encoding.Unicode.GetByteCount(aumid) + Encoding.Unicode.GetByteCount("\0"); + + exePath = Marshal.PtrToStringUni(dataBufferPtr); + } } - } - break; + break; + } + + return new AppExecutionAliasMetadata + { + PackageFullName = packageFullName ?? string.Empty, + PackageFamilyName = packageFamilyName ?? string.Empty, + Aumid = aumid ?? string.Empty, + ExePath = exePath ?? string.Empty, + }; } - - return new AppExecutionAliasMetadata - { - PackageFullName = packageFullName ?? string.Empty, - PackageFamilyName = packageFamilyName ?? string.Empty, - Aumid = aumid ?? string.Empty, - ExePath = exePath ?? string.Empty, - }; } private static void VerifyVersion(AppExecutionAliasReparseTagBufferLayoutVersion version) diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/UWP.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/UWP.cs index e115911001..15d7c079db 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/UWP.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/UWP.cs @@ -3,17 +3,19 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.IO.Abstractions; using System.Linq; +using System.Threading.Tasks; using System.Xml.Linq; using ManagedCommon; using Microsoft.CmdPal.Ext.Apps.Utils; using Microsoft.CommandPalette.Extensions.Toolkit; using Windows.Win32; using Windows.Win32.Foundation; +using Windows.Win32.Storage.Packaging.Appx; using Windows.Win32.System.Com; -using static Microsoft.CmdPal.Ext.Apps.Utils.Native; namespace Microsoft.CmdPal.Ext.Apps.Programs; @@ -55,7 +57,7 @@ public partial class UWP FamilyName = package.FamilyName; } - public void InitializeAppInfo(string installedLocation) + public unsafe void InitializeAppInfo(string installedLocation) { Location = installedLocation; LocationLocalized = ShellLocalization.Instance.GetLocalizedPath(installedLocation); @@ -65,26 +67,31 @@ public partial class UWP 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) + const uint STGMREAD = 0x00000000; + try { - Apps = AppxPackageHelper.GetAppsFromManifest(stream).Select(appInManifest => new UWPApplication(appInManifest, this)).Where(a => + IStream* stream = null; + PInvoke.SHCreateStreamOnFileEx(path, STGMREAD, noAttribute, false, null, &stream).ThrowOnFailure(); + using var streamHandle = new SafeComHandle((IntPtr)stream); + + Apps = AppxPackageHelper.GetAppsFromManifest(stream).Select(appInManifest => + { + using var appHandle = new SafeComHandle(appInManifest); + return new UWPApplication((IAppxManifestApplication*)appInManifest, this); + }).Where(a => { var valid = - !string.IsNullOrEmpty(a.UserModelId) && - !string.IsNullOrEmpty(a.DisplayName) && - a.AppListEntry != "none"; - + !string.IsNullOrEmpty(a.UserModelId) && + !string.IsNullOrEmpty(a.DisplayName) && + a.AppListEntry != "none"; return valid; }).ToList(); } - else + catch (Exception ex) { Apps = Array.Empty(); + Logger.LogError($"Failed to initialize UWP app info for {Name} ({FullName}): {ex.Message}"); + return; } } @@ -123,35 +130,36 @@ public partial class UWP { 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 ex) - { - Logger.LogError(ex.Message); - 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 + if (!support) { return Array.Empty(); } + + var appsBag = new ConcurrentBag(); + + Parallel.ForEach(CurrentUserPackages(), p => + { + try + { + var u = new UWP(p); + u.InitializeAppInfo(p.InstalledLocation); + + foreach (var app in u.Apps) + { + if (AllAppsSettings.Instance.DisabledProgramSources.All(x => x.UniqueIdentifier != app.UniqueIdentifier)) + { + appsBag.Add(app); + } + } + } + catch (Exception ex) + { + Logger.LogError(ex.Message); + } + }); + + return appsBag.ToArray(); } private static IEnumerable CurrentUserPackages() diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/UWPApplication.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/UWPApplication.cs index c38c05d7b5..0915ea05dc 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/UWPApplication.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/UWPApplication.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.IO.Abstractions; using System.Linq; +using System.Runtime.InteropServices; using System.Text; using System.Xml; using ManagedCommon; @@ -13,7 +14,9 @@ using Microsoft.CmdPal.Ext.Apps.Commands; using Microsoft.CmdPal.Ext.Apps.Properties; using Microsoft.CmdPal.Ext.Apps.Utils; using Microsoft.CommandPalette.Extensions.Toolkit; -using static Microsoft.CmdPal.Ext.Apps.Utils.Native; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.Storage.Packaging.Appx; using PackageVersion = Microsoft.CmdPal.Ext.Apps.Programs.UWP.PackageVersion; using Theme = Microsoft.CmdPal.Ext.Apps.Utils.Theme; @@ -82,6 +85,10 @@ public class UWPApplication : IProgram // 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 CopyPathCommand(Location))); + commands.Add( new CommandContextItem( new OpenPathCommand(Location) @@ -97,27 +104,27 @@ public class UWPApplication : IProgram return commands; } - public UWPApplication(IAppxManifestApplication manifestApp, UWP package) + internal unsafe UWPApplication(IAppxManifestApplication* manifestApp, UWP package) { ArgumentNullException.ThrowIfNull(manifestApp); - var hr = manifestApp.GetAppUserModelId(out var tmpUserModelId); - UserModelId = AppxPackageHelper.CheckHRAndReturnOrThrow(hr, tmpUserModelId); + var hr = manifestApp->GetAppUserModelId(out var tmpUserModelIdPtr); + UserModelId = ComFreeHelper.GetStringAndFree(hr, tmpUserModelIdPtr); - hr = manifestApp.GetAppUserModelId(out var tmpUniqueIdentifier); - UniqueIdentifier = AppxPackageHelper.CheckHRAndReturnOrThrow(hr, tmpUniqueIdentifier); + manifestApp->GetAppUserModelId(out var tmpUniqueIdentifierPtr); + UniqueIdentifier = ComFreeHelper.GetStringAndFree(hr, tmpUniqueIdentifierPtr); - hr = manifestApp.GetStringValue("DisplayName", out var tmpDisplayName); - DisplayName = AppxPackageHelper.CheckHRAndReturnOrThrow(hr, tmpDisplayName); + manifestApp->GetStringValue("DisplayName", out var tmpDisplayNamePtr); + DisplayName = ComFreeHelper.GetStringAndFree(hr, tmpDisplayNamePtr); - hr = manifestApp.GetStringValue("Description", out var tmpDescription); - Description = AppxPackageHelper.CheckHRAndReturnOrThrow(hr, tmpDescription); + manifestApp->GetStringValue("Description", out var tmpDescriptionPtr); + Description = ComFreeHelper.GetStringAndFree(hr, tmpDescriptionPtr); - hr = manifestApp.GetStringValue("BackgroundColor", out var tmpBackgroundColor); - BackgroundColor = AppxPackageHelper.CheckHRAndReturnOrThrow(hr, tmpBackgroundColor); + manifestApp->GetStringValue("BackgroundColor", out var tmpBackgroundColorPtr); + BackgroundColor = ComFreeHelper.GetStringAndFree(hr, tmpBackgroundColorPtr); - hr = manifestApp.GetStringValue("EntryPoint", out var tmpEntryPoint); - EntryPoint = AppxPackageHelper.CheckHRAndReturnOrThrow(hr, tmpEntryPoint); + manifestApp->GetStringValue("EntryPoint", out var tmpEntryPointPtr); + EntryPoint = ComFreeHelper.GetStringAndFree(hr, tmpEntryPointPtr); Package = package ?? throw new ArgumentNullException(nameof(package)); @@ -166,7 +173,7 @@ public class UWPApplication : IProgram return false; } - internal string ResourceFromPri(string packageFullName, string resourceReference) + internal unsafe string ResourceFromPri(string packageFullName, string resourceReference) { const string prefix = "ms-resource:"; @@ -200,30 +207,8 @@ public class UWPApplication : IProgram 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)) { - 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}'. @@ -232,17 +217,39 @@ public class UWPApplication : IProgram // Microsoft.BingFoodAndDrink_3.0.4.336_x64__8wekyb3d8bbwe: ms-resource:AppDescription return string.Empty; } - else + + var capacity = 1024U; + PWSTR outBuffer = new PWSTR((char*)(void*)Marshal.AllocHGlobal((int)capacity * sizeof(char))); + var source = $"@{{{packageFullName}? {parsed}}}"; + + try { + PInvoke.SHLoadIndirectString(source, outBuffer.AsSpan()).ThrowOnFailure(); + var loaded = outBuffer.ToString(); - if (!string.IsNullOrEmpty(loaded)) + return string.IsNullOrEmpty(loaded) ? string.Empty : loaded; + } + catch (Exception) + { + try { - return loaded; + var sourceFallback = $"@{{{packageFullName}?{parsedFallback}}}"; + PInvoke.SHLoadIndirectString(sourceFallback, outBuffer.AsSpan()).ThrowOnFailure(); + var loaded = outBuffer.ToString(); + return string.IsNullOrEmpty(loaded) ? string.Empty : loaded; } - else + catch (Exception) { + // ProgramLogger.Exception($"Unable to load resource {resourceReference} from {packageFullName}", new InvalidOperationException(), GetType(), packageFullName); return string.Empty; } + finally + { + } + } + finally + { + Marshal.FreeHGlobal((IntPtr)outBuffer.Value); } } else @@ -258,13 +265,12 @@ public class UWPApplication : IProgram { PackageVersion.Windows8, "SmallLogo" }, }; - internal string LogoUriFromManifest(IAppxManifestApplication app) + internal unsafe 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; + var hr = app->GetStringValue(key, out var logoUriFromAppPtr); + return ComFreeHelper.GetStringAndFree(hr, logoUriFromAppPtr); } else { @@ -349,7 +355,7 @@ public class UWPApplication : IProgram 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 targetSizes = new List { 16, 24, 30, 36, 44, 60, 72, 96, 128, 180, 256 }; var pathFactorPairs = new Dictionary(); foreach (var factor in targetSizes) diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/Win32Program.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/Win32Program.cs index 11c9be6be5..1ccf797d35 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/Win32Program.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/Win32Program.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel.Design; using System.Diagnostics; @@ -197,6 +198,9 @@ public class Win32Program : IProgram new RunAsUserCommand(!string.IsNullOrEmpty(LnkFilePath) ? LnkFilePath : FullPath, ParentDirectory))); } + commands.Add(new CommandContextItem( + new CopyPathCommand(FullPath))); + commands.Add(new CommandContextItem( new OpenPathCommand(ParentDirectory))); @@ -841,18 +845,69 @@ public class Win32Program : IProgram 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 pathBag = new ConcurrentBag(); - var programs = paths.AsParallel().Select(source => GetProgramFromPath(source)); - var runCommandPrograms = runCommandPaths.AsParallel().Select(source => GetRunCommandProgramFromPath(source)); + Parallel.ForEach(sources, source => + { + if (!source.IsEnabled) + { + return; + } + + foreach (var path in source.GetPaths()) + { + if (disabledProgramsList.All(x => x.UniqueIdentifier != path) && + !ExecutableApplicationExtensions.Contains(Extension(path))) + { + pathBag.Add(path); + } + } + }); + + paths.UnionWith(pathBag); + + var runCommandPathBag = new ConcurrentBag(); + + Parallel.ForEach(runCommandSources, source => + { + if (!source.IsEnabled) + { + return; + } + + foreach (var path in source.GetPaths()) + { + if (disabledProgramsList.All(x => x.UniqueIdentifier != path)) + { + runCommandPathBag.Add(path); + } + } + }); + + runCommandPaths.UnionWith(runCommandPathBag); + + var programsList = new ConcurrentBag(); + Parallel.ForEach(paths, source => + { + var program = GetProgramFromPath(source); + if (program != null) + { + programsList.Add(program); + } + }); + + var runCommandProgramsList = new ConcurrentBag(); + Parallel.ForEach(runCommandPaths, source => + { + var program = GetRunCommandProgramFromPath(source); + if (program != null) + { + runCommandProgramsList.Add(program); + } + }); + + var programs = programsList.ToList(); + var runCommandPrograms = runCommandProgramsList.ToList(); return DeduplicatePrograms(programs.Concat(runCommandPrograms).Where(program => program?.Valid == true)); } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Properties/Resources.Designer.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Properties/Resources.Designer.cs index 33531ba62f..cc07ac86b4 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Properties/Resources.Designer.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Properties/Resources.Designer.cs @@ -78,6 +78,33 @@ namespace Microsoft.CmdPal.Ext.Apps.Properties { } } + /// + /// Looks up a localized string similar to Copied to clipboard!. + /// + internal static string copied_to_clipboard { + get { + return ResourceManager.GetString("copied_to_clipboard", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Copy failed ({0}). Please try again.. + /// + internal static string copy_failed { + get { + return ResourceManager.GetString("copy_failed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Copy path. + /// + internal static string copy_path { + get { + return ResourceManager.GetString("copy_path", resourceCulture); + } + } + /// /// Looks up a localized string similar to Include apps found on the desktop. /// diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Properties/Resources.resx b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Properties/Resources.resx index 98212f1066..ce4fb79689 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Properties/Resources.resx +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Properties/Resources.resx @@ -163,12 +163,22 @@ Open location + + Copy path + Run as administrator Run as different user + + Copy failed ({0}). Please try again. + {0} is the error message + + + Copied to clipboard! + Include apps found in the Start Menu diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/ListRepository`1.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/ListRepository`1.cs index 653663b7e1..74877bf841 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/ListRepository`1.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/ListRepository`1.cs @@ -69,11 +69,6 @@ public class ListRepository : IRepository, IEnumerable } } - public ParallelQuery AsParallel() - { - return _items.Values.AsParallel(); - } - public bool Contains(T item) { if (item is not null) diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/Win32ProgramRepository.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/Win32ProgramRepository.cs index 9592599b17..b8e2064048 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/Win32ProgramRepository.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/Win32ProgramRepository.cs @@ -6,8 +6,8 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; using System.IO; -using System.IO.Abstractions; using System.Threading.Tasks; using ManagedCommon; using Microsoft.CmdPal.Ext.Apps.Programs; @@ -15,11 +15,9 @@ using Win32Program = Microsoft.CmdPal.Ext.Apps.Programs.Win32Program; namespace Microsoft.CmdPal.Ext.Apps.Storage; +[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] internal sealed partial class Win32ProgramRepository : ListRepository, IProgramRepository { - private static readonly IFileSystem FileSystem = new FileSystem(); - private static readonly IPath Path = FileSystem.Path; - private const string LnkExtension = ".lnk"; private const string UrlExtension = ".url"; diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/ComFreeHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/ComFreeHelper.cs new file mode 100644 index 0000000000..5f11987f76 --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/ComFreeHelper.cs @@ -0,0 +1,35 @@ +// 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 Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.System.Com; + +namespace Microsoft.CmdPal.Ext.Apps.Utils; + +public static class ComFreeHelper +{ + internal static unsafe string GetStringAndFree(HRESULT hr, PWSTR ptr) + { + hr.ThrowOnFailure(); + try + { + return ptr.ToString(); + } + finally + { + PInvoke.CoTaskMemFree(ptr); + } + } + + public static unsafe void ComObjectRelease(T* comPtr) + where T : unmanaged + { + if (comPtr != null) + { + ((IUnknown*)comPtr)->Release(); + } + } +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/Native.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/Native.cs deleted file mode 100644 index 37918160fe..0000000000 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/Native.cs +++ /dev/null @@ -1,157 +0,0 @@ -// 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.Diagnostics.CodeAnalysis; -using System.Runtime.InteropServices; -using System.Text; - -namespace Microsoft.CmdPal.Ext.Apps.Utils; - -[SuppressMessage("Interoperability", "CA1401:P/Invokes should not be visible", Justification = "We want plugins to share this NativeMethods class, instead of each one creating its own.")] -public sealed class Native -{ - [DllImport("shlwapi.dll", CharSet = CharSet.Unicode)] - public static extern int SHLoadIndirectString(string pszSource, StringBuilder pszOutBuf, int cchOutBuf, nint ppvReserved); - - [DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = true)] - public static extern int SHCreateItemFromParsingName([MarshalAs(UnmanagedType.LPWStr)] string path, nint pbc, ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out IShellItem shellItem); - - [DllImport("shlwapi.dll", CharSet = CharSet.Unicode)] - public static extern HRESULT SHCreateStreamOnFileEx(string fileName, STGM grfMode, uint attributes, bool create, System.Runtime.InteropServices.ComTypes.IStream reserved, out System.Runtime.InteropServices.ComTypes.IStream stream); - - [DllImport("shlwapi.dll", CharSet = CharSet.Unicode)] - public static extern HRESULT SHLoadIndirectString(string pszSource, StringBuilder pszOutBuf, uint cchOutBuf, nint ppvReserved); - - public enum HRESULT : uint - { - /// - /// Operation successful. - /// - S_OK = 0x00000000, - - /// - /// Operation successful. (negative condition/no operation) - /// - S_FALSE = 0x00000001, - - /// - /// Not implemented. - /// - E_NOTIMPL = 0x80004001, - - /// - /// No such interface supported. - /// - E_NOINTERFACE = 0x80004002, - - /// - /// Pointer that is not valid. - /// - E_POINTER = 0x80004003, - - /// - /// Operation aborted. - /// - E_ABORT = 0x80004004, - - /// - /// Unspecified failure. - /// - E_FAIL = 0x80004005, - - /// - /// Unexpected failure. - /// - E_UNEXPECTED = 0x8000FFFF, - - /// - /// General access denied error. - /// - E_ACCESSDENIED = 0x80070005, - - /// - /// Handle that is not valid. - /// - E_HANDLE = 0x80070006, - - /// - /// Failed to allocate necessary memory. - /// - E_OUTOFMEMORY = 0x8007000E, - - /// - /// One or more arguments are not valid. - /// - E_INVALIDARG = 0x80070057, - - /// - /// The operation was canceled by the user. (Error source 7 means Win32.) - /// - /// - /// - E_CANCELLED = 0x800704C7, - } - - public static class ShellItemTypeConstants - { - /// - /// Guid for type IShellItem. - /// - public static readonly Guid ShellItemGuid = new("43826d1e-e718-42ee-bc55-a1e261c37bfe"); - - /// - /// Guid for type IShellItem2. - /// - public static readonly Guid ShellItem2Guid = new("7E9FB0D3-919F-4307-AB2E-9B1860310C93"); - } - - /// - /// The following are ShellItem DisplayName types. - /// - [Flags] - public enum SIGDN : uint - { - NORMALDISPLAY = 0, - PARENTRELATIVEPARSING = 0x80018001, - PARENTRELATIVEFORADDRESSBAR = 0x8001c001, - DESKTOPABSOLUTEPARSING = 0x80028000, - PARENTRELATIVEEDITING = 0x80031001, - DESKTOPABSOLUTEEDITING = 0x8004c000, - FILESYSPATH = 0x80058000, - URL = 0x80068000, - } - - [ComImport] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - [Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe")] - public interface IShellItem - { - void BindToHandler( - nint pbc, - [MarshalAs(UnmanagedType.LPStruct)] Guid bhid, - [MarshalAs(UnmanagedType.LPStruct)] Guid riid, - out nint ppv); - - void GetParent(out IShellItem ppsi); - - void GetDisplayName(SIGDN sigdnName, [MarshalAs(UnmanagedType.LPWStr)] out string ppszName); - - void GetAttributes(uint sfgaoMask, out uint psfgaoAttribs); - - void Compare(IShellItem psi, uint hint, out int piOrder); - } - - /// - /// see all STGM values - /// - [Flags] - public enum STGM : long - { - READ = 0x00000000L, - WRITE = 0x00000001L, - READWRITE = 0x00000002L, - CREATE = 0x00001000L, - } -} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/SafeComHandle.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/SafeComHandle.cs new file mode 100644 index 0000000000..86db1caf33 --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/SafeComHandle.cs @@ -0,0 +1,30 @@ +// 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.Utils; + +public partial class SafeComHandle : SafeHandle +{ + public SafeComHandle() + : base(IntPtr.Zero, ownsHandle: true) + { + } + + public SafeComHandle(IntPtr handle) + : base(IntPtr.Zero, ownsHandle: true) + { + SetHandle(handle); + } + + public override bool IsInvalid => handle == IntPtr.Zero; + + protected override bool ReleaseHandle() + { + var count = Marshal.Release(handle); + return true; + } +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/ShellLinkHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/ShellLinkHelper.cs index 1bb0e10580..543abf5dcf 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/ShellLinkHelper.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/ShellLinkHelper.cs @@ -3,124 +3,17 @@ // See the LICENSE file in the project root for more information. using System; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.ComTypes; using System.Text; using ManagedCommon; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.System.Com; +using Windows.Win32.UI.Shell; namespace Microsoft.CmdPal.Ext.Apps.Utils; public class ShellLinkHelper : IShellLinkHelper { - [Flags] - private enum SLGP_FLAGS - { - SLGP_SHORTPATH = 0x1, - SLGP_UNCPRIORITY = 0x2, - SLGP_RAWPATH = 0x4, - } - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Matching COM")] - private struct WIN32_FIND_DATAW - { - public uint dwFileAttributes; - public long ftCreationTime; - public long ftLastAccessTime; - public long ftLastWriteTime; - public uint nFileSizeHigh; - public uint nFileSizeLow; - public uint dwReserved0; - public uint dwReserved1; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] - public string cFileName; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)] - public string cAlternateFileName; - } - - [Flags] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Implements COM Interface")] - public enum SLR_FLAGS - { - SLR_NO_UI = 0x1, - SLR_ANY_MATCH = 0x2, - SLR_UPDATE = 0x4, - SLR_NOUPDATE = 0x8, - SLR_NOSEARCH = 0x10, - SLR_NOTRACK = 0x20, - SLR_NOLINKINFO = 0x40, - SLR_INVOKE_MSI = 0x80, - } - - // Reference : http://www.pinvoke.net/default.aspx/Interfaces.IShellLinkW - - // The IShellLink interface allows Shell links to be created, modified, and resolved - [ComImport] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - [Guid("000214F9-0000-0000-C000-000000000046")] - private interface IShellLinkW - { - /// Retrieves the path and file name of a Shell link object - void GetPath([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxPath, ref WIN32_FIND_DATAW pfd, SLGP_FLAGS fFlags); - - /// Retrieves the list of item identifiers for a Shell link object - void GetIDList(out nint ppidl); - - /// Sets the pointer to an item identifier list (PIDL) for a Shell link object. - void SetIDList(nint pidl); - - /// Retrieves the description string for a Shell link object - void GetDescription([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, int cchMaxName); - - /// Sets the description for a Shell link object. The description can be any application-defined string - void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName); - - /// Retrieves the name of the working directory for a Shell link object - void GetWorkingDirectory([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath); - - /// Sets the name of the working directory for a Shell link object - void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir); - - /// Retrieves the command-line arguments associated with a Shell link object - void GetArguments([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cchMaxPath); - - /// Sets the command-line arguments for a Shell link object - void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs); - - /// Retrieves the hot key for a Shell link object - void GetHotkey(out short pwHotkey); - - /// Sets a hot key for a Shell link object - void SetHotkey(short wHotkey); - - /// Retrieves the show command for a Shell link object - void GetShowCmd(out int piShowCmd); - - /// Sets the show command for a Shell link object. The show command sets the initial show state of the window. - void SetShowCmd(int iShowCmd); - - /// Retrieves the location (path and index) of the icon for a Shell link object - void GetIconLocation([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, int cchIconPath, out int piIcon); - - /// Sets the location (path and index) of the icon for a Shell link object - void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon); - - /// Sets the relative path to the Shell link object - void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, int dwReserved); - - /// Attempts to find the target of a Shell link, even if it has been moved or renamed - void Resolve(ref nint hwnd, SLR_FLAGS fFlags); - - /// Sets the path and file name of a Shell link object - void SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile); - } - - [ComImport] - [Guid("00021401-0000-0000-C000-000000000046")] - private class ShellLink - { - } - // Contains the description of the app public string Description { get; set; } = string.Empty; @@ -130,60 +23,63 @@ public class ShellLinkHelper : IShellLinkHelper public bool HasArguments { get; set; } // Retrieve the target path using Shell Link - public string RetrieveTargetPath(string path) + public unsafe string RetrieveTargetPath(string path) { - var link = new ShellLink(); - const int STGM_READ = 0; - - try - { - ((IPersistFile)link).Load(path, STGM_READ); - } - catch (System.IO.FileNotFoundException ex) - { - Logger.LogError(ex.Message); - return string.Empty; - } - - var hwnd = default(nint); - ((IShellLinkW)link).Resolve(ref hwnd, 0); - + var target = string.Empty; const int MAX_PATH = 260; - var buffer = new StringBuilder(MAX_PATH); + IShellLinkW* link = null; - var data = default(WIN32_FIND_DATAW); - ((IShellLinkW)link).GetPath(buffer, buffer.Capacity, ref data, SLGP_FLAGS.SLGP_SHORTPATH); - var target = buffer.ToString(); + PInvoke.CoCreateInstance(typeof(ShellLink).GUID, null, CLSCTX.CLSCTX_INPROC_SERVER, out link).ThrowOnFailure(); + using var linkHandle = new SafeComHandle((IntPtr)link); + + const int STGMREAD = 0; + + IPersistFile* persistFile = null; + Guid iid = typeof(IPersistFile).GUID; + ((IUnknown*)link)->QueryInterface(&iid, (void**)&persistFile); + if (persistFile != null) + { + using var persistFileHandle = new SafeComHandle((IntPtr)persistFile); + try + { + persistFile->Load(path, STGMREAD); + } + catch (System.IO.FileNotFoundException) + { + // Log.Exception($"Failed to load {path}, {e.Message}", e, GetType()); + return string.Empty; + } + } + + var hwnd = HWND.Null; + const uint SLR_NO_UI = 0x1; + link->Resolve(hwnd, SLR_NO_UI); + + var buffer = stackalloc char[MAX_PATH]; + + var hr = link->GetPath((PWSTR)buffer, MAX_PATH, null, 0x1); + + target = hr.Succeeded ? new string(buffer) : string.Empty; // To set the app description if (!string.IsNullOrEmpty(target)) { - buffer = new StringBuilder(MAX_PATH); - try - { - ((IShellLinkW)link).GetDescription(buffer, MAX_PATH); - Description = buffer.ToString(); - } - catch (Exception ex) - { - Logger.LogError(ex.Message); - Description = string.Empty; - } + var descBuffer = stackalloc char[MAX_PATH]; + var desHr = link->GetDescription(descBuffer, MAX_PATH); + Description = desHr.Succeeded ? new string(descBuffer) : string.Empty; - var argumentBuffer = new StringBuilder(MAX_PATH); - ((IShellLinkW)link).GetArguments(argumentBuffer, argumentBuffer.Capacity); - Arguments = argumentBuffer.ToString(); + var argsBuffer = stackalloc char[MAX_PATH]; + var argHr = link->GetArguments(argsBuffer, MAX_PATH); + + Arguments = argHr.Succeeded ? new string(argsBuffer) : string.Empty; // Set variable to true if the program takes in any arguments - if (argumentBuffer.Length != 0) + if (Arguments.Length != 0) { HasArguments = true; } } - // To release unmanaged memory - Marshal.ReleaseComObject(link); - return target; } } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/ShellLocalization.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/ShellLocalization.cs index 0a3337e0a8..87e152b7e0 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/ShellLocalization.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/ShellLocalization.cs @@ -4,7 +4,8 @@ using System; using System.Collections.Concurrent; using System.IO; -using static Microsoft.CmdPal.Ext.Apps.Utils.Native; +using Windows.Win32; +using Windows.Win32.UI.Shell; namespace Microsoft.CmdPal.Ext.Apps.Utils; @@ -23,7 +24,7 @@ public class ShellLocalization /// /// Path to the shell item (e. g. shortcut 'File Explorer.lnk'). /// The localized name as string or . - public string GetLocalizedName(string path) + public unsafe string GetLocalizedName(string path) { var lowerInvariantPath = path.ToLowerInvariant(); @@ -33,18 +34,29 @@ public class ShellLocalization return value; } - var shellItemType = ShellItemTypeConstants.ShellItemGuid; - var retCode = SHCreateItemFromParsingName(path, nint.Zero, ref shellItemType, out var shellItem); - if (retCode != 0) + void* shellItemPtrVoid = null; + try + { + var retCode = PInvoke.SHCreateItemFromParsingName(path, null, typeof(IShellItem).GUID, out shellItemPtrVoid).ThrowOnFailure(); + using var shellItemHandle = new SafeComHandle((IntPtr)shellItemPtrVoid); + IShellItem* shellItemPtr = (IShellItem*)shellItemPtrVoid; + + var hr = shellItemPtr->GetDisplayName(SIGDN.SIGDN_NORMALDISPLAY, out var filenamePtr); + + var filename = ComFreeHelper.GetStringAndFree(hr, filenamePtr); + + if (filename == null) + { + return string.Empty; + } + + _ = _localizationCache.TryAdd(lowerInvariantPath, filename); + return filename; + } + catch (Exception) { return string.Empty; } - - shellItem.GetDisplayName(SIGDN.NORMALDISPLAY, out var filename); - - _ = _localizationCache.TryAdd(lowerInvariantPath, filename); - - return filename; } /// diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/CalculateEngine.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/CalculateEngine.cs index 0d6f9536db..35e53b0e95 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/CalculateEngine.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/CalculateEngine.cs @@ -6,20 +6,20 @@ using System; using System.Collections.Generic; using System.Globalization; using System.Text.RegularExpressions; - -using Mages.Core; +using CalculatorEngineCommon; +using Windows.Foundation.Collections; namespace Microsoft.CmdPal.Ext.Calc.Helper; public static class CalculateEngine { - private static readonly Engine _magesEngine = new Engine(new Configuration + private static readonly PropertySet _constants = new() { - Scope = new Dictionary - { - { "e", Math.E }, // e is not contained in the default mages engine - }, - }); + { "pi", Math.PI }, + { "e", Math.E }, + }; + + private static readonly Calculator _calculator = new Calculator(_constants); public const int RoundingDigits = 10; @@ -68,28 +68,26 @@ public static class CalculateEngine // Expand conversions between trig units input = CalculateHelper.ExpandTrigConversions(input, trigMode); - var result = _magesEngine.Interpret(input); + var result = _calculator.EvaluateExpression(input); // This could happen for some incorrect queries, like pi(2) - if (result == null) + if (result == "NaN") { error = Properties.Resources.calculator_expression_not_complete; return default; } - result = TransformResult(result); - if (result is string) - { - error = result as string; - return default; - } - - if (string.IsNullOrEmpty(result?.ToString())) + if (string.IsNullOrEmpty(result)) { return default; } var decimalResult = Convert.ToDecimal(result, cultureInfo); + + // Remove trailing zeros from the decimal string representation (e.g., "1.2300" -> "1.23") + // This is necessary because the value extracted from exprtk may contain unnecessary trailing zeros. + var formatted = decimalResult.ToString("G29", cultureInfo); + decimalResult = Convert.ToDecimal(formatted, cultureInfo); var roundedResult = Round(decimalResult); return new CalculateResult() @@ -103,25 +101,4 @@ public static class CalculateEngine { return Math.Round(value, RoundingDigits, MidpointRounding.AwayFromZero); } - - private static dynamic TransformResult(object result) - { - if (result.ToString() == "NaN") - { - return Properties.Resources.calculator_not_a_number; - } - - if (result is Function) - { - return Properties.Resources.calculator_expression_not_complete; - } - - if (result is double[,]) - { - // '[10,10]' is interpreted as array by mages engine - return Properties.Resources.calculator_double_array_returned; - } - - return result; - } } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/CalculateHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/CalculateHelper.cs index acab3d7b96..deb441cfe2 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/CalculateHelper.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/CalculateHelper.cs @@ -35,7 +35,7 @@ public static class CalculateHelper { if (string.IsNullOrWhiteSpace(input)) { - throw new ArgumentNullException(paramName: nameof(input)); + return false; } if (!RegValidExpressChar.IsMatch(input)) diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/NumberTranslator.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/NumberTranslator.cs index 8de77ebdae..6930e2fa23 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/NumberTranslator.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/NumberTranslator.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Globalization; using System.Text; using System.Text.RegularExpressions; @@ -63,46 +64,61 @@ public class NumberTranslator return Translate(input, targetCulture, sourceCulture, splitRegexForTarget); } + private static string ConvertBaseLiteral(string token, CultureInfo cultureTo) + { + var prefixes = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "0x", 16 }, + { "0b", 2 }, + { "0o", 8 }, + }; + + foreach (var (prefix, numberBase) in prefixes) + { + if (token.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) + { + try + { + var num = Convert.ToInt64(token.Substring(prefix.Length), numberBase); + return num.ToString(cultureTo); + } + catch + { + return null; // fallback + } + } + } + + return null; + } + private static string Translate(string input, CultureInfo cultureFrom, CultureInfo cultureTo, Regex splitRegex) { var outputBuilder = new StringBuilder(); - var hexRegex = new Regex(@"(?:(0x[\da-fA-F]+))"); - var hexTokens = hexRegex.Split(input); + // Match numbers in hexadecimal (0x..), binary (0b..), or octal (0o..) format, + // and convert them to decimal form for compatibility with ExprTk (which only supports decimal input). + var baseNumberRegex = new Regex(@"(0[xX][\da-fA-F]+|0[bB][0-9]+|0[oO][0-9]+)"); - foreach (var hexToken in hexTokens) + var tokens = baseNumberRegex.Split(input); + + foreach (var token in tokens) { - if (hexToken.StartsWith("0x", StringComparison.InvariantCultureIgnoreCase)) - { - // Mages engine has issues processing large hex number (larger than 7 hex digits + 0x prefix = 9 characters). So we convert it to decimal and pass it to the engine. - if (hexToken.Length > 9) - { - try - { - var num = Convert.ToInt64(hexToken, 16); - var numStr = num.ToString(cultureFrom); - outputBuilder.Append(numStr); - } - catch (Exception) - { - outputBuilder.Append(hexToken); - } - } - else - { - outputBuilder.Append(hexToken); - } + // Currently, we only convert base literals (hexadecimal, binary, octal) to decimal. + var converted = ConvertBaseLiteral(token, cultureTo); + if (converted != null) + { + outputBuilder.Append(converted); continue; } - var tokens = splitRegex.Split(hexToken); - foreach (var token in tokens) + foreach (var inner in splitRegex.Split(token)) { var leadingZeroCount = 0; // Count leading zero characters. - foreach (var c in token) + foreach (var c in inner) { if (c != '0') { @@ -113,7 +129,7 @@ public class NumberTranslator } // number is all zero characters. no need to add zero characters at the end. - if (token.Length == leadingZeroCount) + if (inner.Length == leadingZeroCount) { leadingZeroCount = 0; } @@ -121,9 +137,9 @@ public class NumberTranslator decimal number; outputBuilder.Append( - decimal.TryParse(token, NumberStyles.Number, cultureFrom, out number) + decimal.TryParse(inner, NumberStyles.Number, cultureFrom, out number) ? (new string('0', leadingZeroCount) + number.ToString(cultureTo)) - : token.Replace(cultureFrom.TextInfo.ListSeparator, cultureTo.TextInfo.ListSeparator)); + : inner.Replace(cultureFrom.TextInfo.ListSeparator, cultureTo.TextInfo.ListSeparator)); } } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/QueryHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/QueryHelper.cs index 6b1e62ae03..df6a8a153d 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/QueryHelper.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/QueryHelper.cs @@ -23,6 +23,9 @@ public static partial class QueryHelper CultureInfo inputCulture = settings.InputUseEnglishFormat ? new CultureInfo("en-us") : CultureInfo.CurrentCulture; CultureInfo outputCulture = settings.OutputUseEnglishFormat ? new CultureInfo("en-us") : CultureInfo.CurrentCulture; + // In case the user pastes a query with a leading = + query = query.TrimStart('='); + // Happens if the user has only typed the action key so far if (string.IsNullOrEmpty(query)) { @@ -32,6 +35,11 @@ public static partial class QueryHelper NumberTranslator translator = NumberTranslator.Create(inputCulture, new CultureInfo("en-US")); var input = translator.Translate(query.Normalize(NormalizationForm.FormKC)); + if (string.IsNullOrWhiteSpace(input)) + { + return ErrorHandler.OnError(isFallbackSearch, query, Properties.Resources.calculator_expression_empty); + } + if (!CalculateHelper.InputValid(input)) { return null; @@ -57,11 +65,6 @@ public static partial class QueryHelper return ResultHelper.CreateResult(result.RoundedResult, inputCulture, outputCulture, query, handleSave); } - catch (Mages.Core.ParseException) - { - // Invalid input - return ErrorHandler.OnError(isFallbackSearch, query, Properties.Resources.calculator_expression_not_complete); - } catch (OverflowException) { // Result to big to convert to decimal diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Microsoft.CmdPal.Ext.Calc.csproj b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Microsoft.CmdPal.Ext.Calc.csproj index 59f5ccbc70..fc84428a76 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Microsoft.CmdPal.Ext.Calc.csproj +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Microsoft.CmdPal.Ext.Calc.csproj @@ -9,15 +9,25 @@ Microsoft.CmdPal.Ext.Calc.pri + + CalculatorEngineCommon + $(OutDir) + + + - - + + + PreserveNewest + + + PreserveNewest + - Resources.resx diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Properties/Resources.Designer.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Properties/Resources.Designer.cs index 8cd385be32..8759c1209f 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Properties/Resources.Designer.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Properties/Resources.Designer.cs @@ -132,6 +132,15 @@ namespace Microsoft.CmdPal.Ext.Calc.Properties { } } + /// + /// Looks up a localized string similar to Please enter an expression. + /// + public static string calculator_expression_empty { + get { + return ResourceManager.GetString("calculator_expression_empty", resourceCulture); + } + } + /// /// Looks up a localized string similar to Expression wrong or incomplete. /// diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Properties/Resources.resx b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Properties/Resources.resx index 3c50d3a1c5..dba2ba2067 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Properties/Resources.resx +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Properties/Resources.resx @@ -199,4 +199,7 @@ Copy binary + + Please enter an expression + \ No newline at end of file diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.ClipboardHistory/Helpers/ClipboardHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.ClipboardHistory/Helpers/ClipboardHelper.cs index bbfec3c491..100d23e9f0 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.ClipboardHistory/Helpers/ClipboardHelper.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.ClipboardHistory/Helpers/ClipboardHelper.cs @@ -30,6 +30,8 @@ internal static class ClipboardHelper (StandardDataFormats.Bitmap, ClipboardFormat.Image), ]; + private static readonly ClipboardThreadQueue ClipboardThreadQueue = new ClipboardThreadQueue(); + internal static async Task GetAvailableClipboardFormatsAsync(DataPackageView clipboardData) { var availableClipboardFormats = DataFormats.Aggregate( @@ -58,9 +60,12 @@ internal static class ClipboardHelper try { // Clipboard.SetContentWithOptions(output, null); - Clipboard.SetContent(output); - Flush(); - ExtensionHost.LogMessage(new LogMessage() { Message = "Copied text to clipboard" }); + ClipboardThreadQueue.EnqueueTask(() => + { + Clipboard.SetContent(output); + Flush(); + ExtensionHost.LogMessage(new LogMessage() { Message = "Copied text to clipboard" }); + }); } catch (COMException ex) { @@ -74,27 +79,32 @@ internal static class ClipboardHelper // TODO(stefan): For some reason Flush() fails from time to time when directly activated via hotkey. // Calling inside a loop makes it work. // Exception is: The operation is not permitted because the calling application is not the owner of the data on the clipboard. - const int maxAttempts = 5; - for (var i = 1; i <= maxAttempts; i++) + ClipboardThreadQueue.EnqueueTask(() => { - try + const int maxAttempts = 5; + + for (var i = 1; i <= maxAttempts; i++) { - Task.Run(Clipboard.Flush).Wait(); - return true; - } - catch (Exception ex) - { - if (i == maxAttempts) + try { - ExtensionHost.LogMessage(new LogMessage() + Task.Run(Clipboard.Flush).Wait(); + return; + } + catch (Exception ex) + { + if (i == maxAttempts) { - Message = $"{nameof(Clipboard)}.{nameof(Flush)}() failed: {ex}", - }); + ExtensionHost.LogMessage(new LogMessage() + { + Message = $"{nameof(Clipboard)}.{nameof(Flush)}() failed: {ex}", + }); + } } } - } + }); - return false; + // We cannot get the real result of the Flush() call here, as it is executed in a different thread. + return true; } private static async Task FlushAsync() => await Task.Run(Flush); @@ -105,7 +115,7 @@ internal static class ClipboardHelper DataPackage output = new(); output.SetStorageItems([storageFile]); - Clipboard.SetContent(output); + ClipboardThreadQueue.EnqueueTask(() => Clipboard.SetContent(output)); await FlushAsync(); } @@ -118,7 +128,7 @@ internal static class ClipboardHelper { DataPackage output = new(); output.SetBitmap(image); - Clipboard.SetContentWithOptions(output, null); + ClipboardThreadQueue.EnqueueTask(() => Clipboard.SetContentWithOptions(output, null)); Flush(); } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.ClipboardHistory/Helpers/ClipboardThreadScheduler.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.ClipboardHistory/Helpers/ClipboardThreadScheduler.cs new file mode 100644 index 0000000000..0f36f66453 --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.ClipboardHistory/Helpers/ClipboardThreadScheduler.cs @@ -0,0 +1,74 @@ +// 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.Concurrent; +using System.Threading; +using Microsoft.CommandPalette.Extensions.Toolkit; + +namespace Microsoft.CmdPal.Ext.ClipboardHistory.Helpers; + +public partial class ClipboardThreadQueue : IDisposable +{ + private readonly Thread _thread; + private readonly ConcurrentQueue _taskQueue = new ConcurrentQueue(); + private readonly AutoResetEvent _taskAvailable = new AutoResetEvent(false); + private readonly CancellationTokenSource cancellationToken = new CancellationTokenSource(); + + public ClipboardThreadQueue() + { + _thread = new Thread(() => + { + var hr = NativeMethods.CoInitialize(IntPtr.Zero); + if (hr != 0) + { + ExtensionHost.LogMessage($"CoInitialize failed with HRESULT: {hr}"); + } + + while (true) + { + _taskAvailable.WaitOne(); + + if (cancellationToken.IsCancellationRequested) + { + break; + } + + while (_taskQueue.TryDequeue(out var task)) + { + try + { + task(); + } + catch (Exception ex) + { + ExtensionHost.LogMessage($"Error executing task in ClipboardThreadQueue: {ex.Message}"); + } + } + } + + NativeMethods.CoUninitialize(); + }); + + _thread.SetApartmentState(ApartmentState.STA); + _thread.IsBackground = true; + _thread.Start(); + } + + public void EnqueueTask(Action task) + { + _taskQueue.Enqueue(task); + _taskAvailable.Set(); + } + + public void Dispose() + { + cancellationToken.Cancel(); + _taskAvailable.Set(); + _thread.Join(); // Wait for the thread to finish processing tasks + + _taskAvailable.Dispose(); + GC.SuppressFinalize(this); + } +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.ClipboardHistory/Helpers/NativeMethods.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.ClipboardHistory/Helpers/NativeMethods.cs index f4b6089229..1e0a46f030 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.ClipboardHistory/Helpers/NativeMethods.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.ClipboardHistory/Helpers/NativeMethods.cs @@ -8,7 +8,7 @@ using Windows.Foundation; namespace Microsoft.CmdPal.Ext.ClipboardHistory.Helpers; -internal static class NativeMethods +public static partial class NativeMethods { [StructLayout(LayoutKind.Sequential)] [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Matching Native Structure")] @@ -98,4 +98,10 @@ internal static class NativeMethods [DllImport("user32.dll")] internal static extern bool GetCursorPos(out PointInter lpPoint); + + [LibraryImport("ole32.dll")] + internal static partial int CoInitialize(IntPtr pvReserved); + + [LibraryImport("ole32.dll")] + internal static partial void CoUninitialize(); } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Actions/ActionRuntimeFactory.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Actions/ActionRuntimeFactory.cs new file mode 100644 index 0000000000..6fd708f265 --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Actions/ActionRuntimeFactory.cs @@ -0,0 +1,38 @@ +// 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; +using ManagedCsWin32; +using WinRT; + +namespace Microsoft.CmdPal.Ext.Indexer.Data; + +internal static class ActionRuntimeFactory +{ + private const string ActionRuntimeClsidStr = "C36FEF7E-35F3-4192-9F2C-AF1FD425FB85"; + + // typeof(Windows.AI.Actions.IActionRuntime).GUID + private static readonly Guid IActionRuntimeIID = Guid.Parse("206EFA2C-C909-508A-B4B0-9482BE96DB9C"); + + public static unsafe global::Windows.AI.Actions.ActionRuntime CreateActionRuntime() + { + IntPtr abiPtr = default; + try + { + Guid classId = Guid.Parse(ActionRuntimeClsidStr); + Guid iid = IActionRuntimeIID; + + var hresult = Ole32.CoCreateInstance(ref Unsafe.AsRef(in classId), IntPtr.Zero, CLSCTX.LocalServer, ref iid, out abiPtr); + Marshal.ThrowExceptionForHR((int)hresult); + + return MarshalInterface.FromAbi(abiPtr); + } + finally + { + MarshalInspectable.DisposeAbi(abiPtr); + } + } +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Assets/Actions.png b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Assets/Actions.png new file mode 100644 index 0000000000..6aaddfda85 Binary files /dev/null and b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Assets/Actions.png differ diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/ExecuteActionCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/ExecuteActionCommand.cs new file mode 100644 index 0000000000..2a3c895e9b --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/ExecuteActionCommand.cs @@ -0,0 +1,43 @@ +// 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.Tasks; +using Microsoft.CommandPalette.Extensions.Toolkit; +using Windows.AI.Actions.Hosting; + +namespace Microsoft.CmdPal.Ext.Indexer.Commands; + +internal sealed partial class ExecuteActionCommand : InvokableCommand +{ + private readonly ActionInstance actionInstance; + + internal ExecuteActionCommand(ActionInstance actionInstance) + { + this.actionInstance = actionInstance; + this.Name = actionInstance.DisplayInfo.Description; + this.Icon = new IconInfo(actionInstance.Definition.IconFullPath); + } + + public override CommandResult Invoke() + { + var task = Task.Run(InvokeAsync); + task.Wait(); + + return task.Result; + } + + private async Task InvokeAsync() + { + try + { + await actionInstance.InvokeAsync(); + return CommandResult.GoHome(); + } + catch (Exception ex) + { + return CommandResult.ShowToast("Failed to invoke action " + actionInstance.Definition.Id + ": " + ex.Message); + } + } +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/OpenPropertiesCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/OpenPropertiesCommand.cs index a6611cf6b3..d07bbdca80 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/OpenPropertiesCommand.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/OpenPropertiesCommand.cs @@ -5,13 +5,11 @@ using System; using System.Runtime.InteropServices; using ManagedCommon; +using ManagedCsWin32; using Microsoft.CmdPal.Ext.Indexer.Data; -using Microsoft.CmdPal.Ext.Indexer.Native; +using Microsoft.CmdPal.Ext.Indexer.Indexer.Utils; using Microsoft.CmdPal.Ext.Indexer.Properties; using Microsoft.CommandPalette.Extensions.Toolkit; -using Windows.Win32; -using Windows.Win32.Foundation; -using Windows.Win32.UI.Shell; using Windows.Win32.UI.WindowsAndMessaging; namespace Microsoft.CmdPal.Ext.Indexer.Commands; @@ -27,19 +25,16 @@ internal sealed partial class OpenPropertiesCommand : InvokableCommand try { - var filenamePCWSTR = new PCWSTR((char*)filenamePtr); - var propertiesPCWSTR = new PCWSTR((char*)propertiesPtr); - - var info = new SHELLEXECUTEINFOW + var info = new Shell32.SHELLEXECUTEINFOW { - cbSize = (uint)Marshal.SizeOf(), - lpVerb = propertiesPCWSTR, - lpFile = filenamePCWSTR, - nShow = (int)SHOW_WINDOW_CMD.SW_SHOW, - fMask = NativeHelpers.SEEMASKINVOKEIDLIST, + CbSize = (uint)sizeof(Shell32.SHELLEXECUTEINFOW), + LpVerb = propertiesPtr, + LpFile = filenamePtr, + Show = (int)SHOW_WINDOW_CMD.SW_SHOW, + FMask = NativeHelpers.SEEMASKINVOKEIDLIST, }; - return PInvoke.ShellExecuteEx(ref info); + return Shell32.ShellExecuteEx(ref info); } finally { diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/OpenWithCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/OpenWithCommand.cs index a9c431d32c..2c1875d3d7 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/OpenWithCommand.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/OpenWithCommand.cs @@ -3,13 +3,11 @@ // See the LICENSE file in the project root for more information. using System.Runtime.InteropServices; +using ManagedCsWin32; using Microsoft.CmdPal.Ext.Indexer.Data; -using Microsoft.CmdPal.Ext.Indexer.Native; +using Microsoft.CmdPal.Ext.Indexer.Indexer.Utils; using Microsoft.CmdPal.Ext.Indexer.Properties; using Microsoft.CommandPalette.Extensions.Toolkit; -using Windows.Win32; -using Windows.Win32.Foundation; -using Windows.Win32.UI.Shell; using Windows.Win32.UI.WindowsAndMessaging; namespace Microsoft.CmdPal.Ext.Indexer.Commands; @@ -25,19 +23,16 @@ internal sealed partial class OpenWithCommand : InvokableCommand try { - var filenamePCWSTR = new PCWSTR((char*)filenamePtr); - var verbPCWSTR = new PCWSTR((char*)verbPtr); - - var info = new SHELLEXECUTEINFOW + var info = new Shell32.SHELLEXECUTEINFOW { - cbSize = (uint)Marshal.SizeOf(), - lpVerb = verbPCWSTR, - lpFile = filenamePCWSTR, - nShow = (int)SHOW_WINDOW_CMD.SW_SHOWNORMAL, - fMask = NativeHelpers.SEEMASKINVOKEIDLIST, + CbSize = (uint)sizeof(Shell32.SHELLEXECUTEINFOW), + LpVerb = verbPtr, + LpFile = filenamePtr, + Show = (int)SHOW_WINDOW_CMD.SW_SHOWNORMAL, + FMask = NativeHelpers.SEEMASKINVOKEIDLIST, }; - return PInvoke.ShellExecuteEx(ref info); + return Shell32.ShellExecuteEx(ref info); } finally { diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Data/IndexerListItem.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Data/IndexerListItem.cs index 57d399b668..f2bfbc1c1a 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Data/IndexerListItem.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Data/IndexerListItem.cs @@ -4,8 +4,11 @@ using System.Collections.Generic; using Microsoft.CmdPal.Ext.Indexer.Commands; +using Microsoft.CmdPal.Ext.Indexer.Pages; using Microsoft.CmdPal.Ext.Indexer.Properties; +using Microsoft.CommandPalette.Extensions; using Microsoft.CommandPalette.Extensions.Toolkit; +using Windows.Foundation.Metadata; namespace Microsoft.CmdPal.Ext.Indexer.Data; @@ -38,9 +41,24 @@ internal sealed partial class IndexerListItem : ListItem } } - MoreCommands = [ + IContextItem[] moreCommands = [ ..context, - new CommandContextItem(new OpenWithCommand(indexerItem)), + new CommandContextItem(new OpenWithCommand(indexerItem))]; + + if (ApiInformation.IsApiContractPresent("Windows.AI.Actions.ActionsContract", 4)) + { + var actionsListContextItem = new ActionsListContextItem(indexerItem.FullPath); + if (actionsListContextItem.AnyActions()) + { + moreCommands = [ + .. moreCommands, + actionsListContextItem + ]; + } + } + + MoreCommands = [ + .. moreCommands, new CommandContextItem(new ShowFileInFolderCommand(indexerItem.FullPath) { Name = Resources.Indexer_Command_ShowInFolder }), new CommandContextItem(new CopyPathCommand(indexerItem)), new CommandContextItem(new OpenInConsoleCommand(indexerItem)), diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/DataSourceManager.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/DataSourceManager.cs index e186251825..7b9f9bd45b 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/DataSourceManager.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/DataSourceManager.cs @@ -3,17 +3,15 @@ // See the LICENSE file in the project root for more information. using System; +using System.Runtime.CompilerServices; using ManagedCommon; -using Windows.Win32; -using Windows.Win32.System.Com; -using Windows.Win32.System.Search; +using ManagedCsWin32; +using Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch; namespace Microsoft.CmdPal.Ext.Indexer.Indexer; internal static class DataSourceManager { - private static readonly Guid CLSIDCollatorDataSource = new("9E175B8B-F52A-11D8-B9A5-505054503030"); - private static IDBInitialize _dataSource; public static IDBInitialize GetDataSource() @@ -28,20 +26,18 @@ internal static class DataSourceManager private static bool InitializeDataSource() { - var hr = PInvoke.CoCreateInstance(CLSIDCollatorDataSource, null, CLSCTX.CLSCTX_INPROC_SERVER, typeof(IDBInitialize).GUID, out var dataSourceObj); - if (hr != 0) + var riid = typeof(IDBInitialize).GUID; + + try { - Logger.LogError("CoCreateInstance failed: " + hr); + _dataSource = ComHelper.CreateComInstance(ref Unsafe.AsRef(in CLSID.CollatorDataSource), CLSCTX.InProcServer); + } + catch (Exception e) + { + Logger.LogError($"Failed to create datasource. ex: {e.Message}"); return false; } - if (dataSourceObj == null) - { - Logger.LogError("CoCreateInstance failed: dataSourceObj is null"); - return false; - } - - _dataSource = (IDBInitialize)dataSourceObj; _dataSource.Initialize(); return true; diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/OleDB/DBPROP.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/OleDB/DBPROP.cs index d4be1b967f..054e3d504c 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/OleDB/DBPROP.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/OleDB/DBPROP.cs @@ -3,8 +3,8 @@ // See the LICENSE file in the project root for more information. using System.Runtime.InteropServices; +using Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch; using Windows.Win32.Storage.IndexServer; -using Windows.Win32.System.Com.StructuredStorage; namespace Microsoft.CmdPal.Ext.Indexer.Indexer.OleDB; @@ -16,6 +16,6 @@ internal struct DBPROP public uint dwOptions; public uint dwStatus; public DBID colid; - public PROPVARIANT vValue; + public PropVariant vValue; #pragma warning restore SA1307 // Accessible fields should begin with upper-case letter } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/OleDB/IRowset.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/OleDB/IRowset.cs index 3127e2e563..75502f56c2 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/OleDB/IRowset.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/OleDB/IRowset.cs @@ -4,40 +4,38 @@ using System; using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; namespace Microsoft.CmdPal.Ext.Indexer.Indexer.OleDB; -[ComImport] [Guid("0c733a7c-2a1c-11ce-ade5-00aa0044773d")] -[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] -public interface IRowset +[GeneratedComInterface] +public partial interface IRowset { - [PreserveSig] - int AddRefRows( + void AddRefRows( uint cRows, - [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] IntPtr[] rghRows, - [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] uint[] rgRefCounts, - [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] int[] rgRowStatus); + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] IntPtr[] rghRows, + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] uint[] rgRefCounts, + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] int[] rgRowStatus); - [PreserveSig] - int GetData( + void GetData( IntPtr hRow, IntPtr hAccessor, IntPtr pData); - [PreserveSig] - int GetNextRows( + void GetNextRows( IntPtr hReserved, long lRowsOffset, long cRows, out uint pcRowsObtained, out IntPtr prghRows); - [PreserveSig] - int ReleaseRows( + void ReleaseRows( uint cRows, - [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] IntPtr[] rghRows, + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] IntPtr[] rghRows, IntPtr rgRowOptions, - [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] uint[] rgRefCounts, - [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] int[] rgRowStatus); + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] uint[] rgRefCounts, + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] int[] rgRowStatus); + + void RestartPosition(nuint hReserved); } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/OleDB/IRowsetInfo.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/OleDB/IRowsetInfo.cs index 5c891c8036..bf1406179b 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/OleDB/IRowsetInfo.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/OleDB/IRowsetInfo.cs @@ -4,29 +4,29 @@ using System; using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; namespace Microsoft.CmdPal.Ext.Indexer.Indexer.OleDB; -[ComImport] [Guid("0C733A55-2A1C-11CE-ADE5-00AA0044773D")] -[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] -public interface IRowsetInfo +[GeneratedComInterface] +public partial interface IRowsetInfo { [PreserveSig] int GetProperties( uint cPropertyIDSets, - [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] DBPROPIDSET[] rgPropertyIDSets, + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] DBPROPIDSET[] rgPropertyIDSets, out ulong pcPropertySets, out IntPtr prgPropertySets); [PreserveSig] int GetReferencedRowset( uint iOrdinal, - [In] ref Guid riid, - [Out, MarshalAs(UnmanagedType.Interface)] out object ppReferencedRowset); + ref Guid riid, + [MarshalAs(UnmanagedType.Interface)] out object ppReferencedRowset); [PreserveSig] int GetSpecification( - [In] ref Guid riid, - [Out, MarshalAs(UnmanagedType.Interface)] out object ppSpecification); + ref Guid riid, + [MarshalAs(UnmanagedType.Interface)] out object ppSpecification); } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SearchQuery.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SearchQuery.cs index abe12396ea..8fa972f302 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SearchQuery.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SearchQuery.cs @@ -4,16 +4,15 @@ using System; using System.Collections.Concurrent; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; using ManagedCommon; +using ManagedCsWin32; using Microsoft.CmdPal.Ext.Indexer.Indexer.OleDB; +using Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch; using Microsoft.CmdPal.Ext.Indexer.Indexer.Utils; -using Microsoft.CmdPal.Ext.Indexer.Native; -using Windows.Win32; -using Windows.Win32.System.Com; -using Windows.Win32.System.Search; -using Windows.Win32.UI.Shell.PropertiesSystem; +using static Microsoft.CmdPal.Ext.Indexer.Indexer.Utils.NativeHelpers; namespace Microsoft.CmdPal.Ext.Indexer.Indexer; @@ -120,11 +119,6 @@ internal sealed partial class SearchQuery : IDisposable // We need to generate a search query string with the search text the user entered above if (currentRowset != null) { - if (reuseRowset != null) - { - Marshal.ReleaseComObject(reuseRowset); - } - // We have a previous rowset, this means the user is typing and we should store this // recapture the where ID from this so the next ExecuteSync call will be faster reuseRowset = currentRowset; @@ -148,13 +142,10 @@ internal sealed partial class SearchQuery : IDisposable private bool HandleRow(IGetRow getRow, nuint rowHandle) { - object propertyStorePtr = null; - try { - getRow.GetRowFromHROW(null, rowHandle, typeof(IPropertyStore).GUID, out propertyStorePtr); + getRow.GetRowFromHROW(null, rowHandle, ref Unsafe.AsRef(in IID.IPropertyStore), out var propertyStore); - var propertyStore = (IPropertyStore)propertyStorePtr; if (propertyStore == null) { Logger.LogError("Failed to get IPropertyStore interface"); @@ -176,14 +167,6 @@ internal sealed partial class SearchQuery : IDisposable Logger.LogError("Error handling row", ex); return false; } - finally - { - // Ensure the COM object is released if not returned - if (propertyStorePtr != null) - { - Marshal.ReleaseComObject(propertyStorePtr); - } - } } public bool FetchRows(int offset, int limit) @@ -194,16 +177,17 @@ internal sealed partial class SearchQuery : IDisposable return false; } - if (currentRowset is not IGetRow) + IGetRow getRow = null; + + try + { + getRow = (IGetRow)currentRowset; + } + catch (Exception) { Logger.LogInfo("Reset the current rowset"); ExecuteSyncInternal(); - } - - if (currentRowset is not IGetRow getRow) - { - Logger.LogError("Rowset does not support IGetRow interface"); - return false; + getRow = (IGetRow)currentRowset; } uint rowCountReturned; @@ -211,12 +195,7 @@ internal sealed partial class SearchQuery : IDisposable try { - var res = currentRowset.GetNextRows(IntPtr.Zero, offset, limit, out rowCountReturned, out prghRows); - if (res < 0) - { - Logger.LogError($"Error fetching rows: {res}"); - return false; - } + currentRowset.GetNextRows(IntPtr.Zero, offset, limit, out rowCountReturned, out prghRows); if (rowCountReturned == 0) { @@ -237,11 +216,7 @@ internal sealed partial class SearchQuery : IDisposable } } - res = currentRowset.ReleaseRows(rowCountReturned, rowHandles, IntPtr.Zero, null, null); - if (res != 0) - { - Logger.LogError($"Error releasing rows: {res}"); - } + currentRowset.ReleaseRows(rowCountReturned, rowHandles, IntPtr.Zero, null, null); Marshal.FreeCoTaskMem(prghRows); prghRows = IntPtr.Zero; @@ -268,11 +243,6 @@ internal sealed partial class SearchQuery : IDisposable var rowset = ExecuteCommand(queryStr); if (rowset != null) { - if (reuseRowset != null) - { - Marshal.ReleaseComObject(reuseRowset); - } - reuseRowset = rowset; reuseWhereID = GetReuseWhereId(reuseRowset); } @@ -280,110 +250,50 @@ internal sealed partial class SearchQuery : IDisposable private unsafe IRowset ExecuteCommand(string queryStr) { - object sessionPtr = null; - object commandPtr = null; + if (string.IsNullOrEmpty(queryStr)) + { + return null; + } try { var session = (IDBCreateSession)DataSourceManager.GetDataSource(); - session.CreateSession(null, typeof(IDBCreateCommand).GUID, out sessionPtr); - if (sessionPtr == null) + var guid = typeof(IDBCreateCommand).GUID; + session.CreateSession(IntPtr.Zero, ref guid, out var ppDBSession); + + if (ppDBSession == null) { Logger.LogError("CreateSession failed"); return null; } - var createCommand = (IDBCreateCommand)sessionPtr; - createCommand.CreateCommand(null, typeof(ICommandText).GUID, out commandPtr); - if (commandPtr == null) - { - Logger.LogError("CreateCommand failed"); - return null; - } + var createCommand = (IDBCreateCommand)ppDBSession; + guid = typeof(ICommandText).GUID; + createCommand.CreateCommand(IntPtr.Zero, ref guid, out ICommandText commandText); - var commandText = (ICommandText)commandPtr; if (commandText == null) { Logger.LogError("Failed to get ICommandText interface"); return null; } - commandText.SetCommandText(in NativeHelpers.OleDb.DbGuidDefault, queryStr); - commandText.Execute(null, typeof(IRowset).GUID, null, null, out var rowsetPointer); + var riid = NativeHelpers.OleDb.DbGuidDefault; - return rowsetPointer as IRowset; + var irowSetRiid = typeof(IRowset).GUID; + + commandText.SetCommandText(ref riid, queryStr); + commandText.Execute(null, ref irowSetRiid, null, out var pcRowsAffected, out var rowsetPointer); + + return rowsetPointer; } catch (Exception ex) { Logger.LogError("Unexpected error.", ex); return null; } - finally - { - // Release the command pointer - if (commandPtr != null) - { - Marshal.ReleaseComObject(commandPtr); - } - - // Release the session pointer - if (sessionPtr != null) - { - Marshal.ReleaseComObject(sessionPtr); - } - } } - private IRowsetInfo GetRowsetInfo(IRowset rowset) - { - if (rowset == null) - { - return null; - } - - var rowsetPtr = IntPtr.Zero; - var rowsetInfoPtr = IntPtr.Zero; - - try - { - // Get the IUnknown pointer for the IRowset object - rowsetPtr = Marshal.GetIUnknownForObject(rowset); - - // Query for IRowsetInfo interface - var rowsetInfoGuid = typeof(IRowsetInfo).GUID; - var res = Marshal.QueryInterface(rowsetPtr, in rowsetInfoGuid, out rowsetInfoPtr); - if (res != 0) - { - Logger.LogError($"Error getting IRowsetInfo interface: {res}"); - return null; - } - - // Marshal the interface pointer to the actual IRowsetInfo object - var rowsetInfo = (IRowsetInfo)Marshal.GetObjectForIUnknown(rowsetInfoPtr); - return rowsetInfo; - } - catch (Exception ex) - { - Logger.LogError($"Exception occurred while getting IRowsetInfo. ", ex); - return null; - } - finally - { - // Release the IRowsetInfo pointer if it was obtained - if (rowsetInfoPtr != IntPtr.Zero) - { - Marshal.Release(rowsetInfoPtr); // Release the IRowsetInfo pointer - } - - // Release the IUnknown pointer for the IRowset object - if (rowsetPtr != IntPtr.Zero) - { - Marshal.Release(rowsetPtr); - } - } - } - - private DBPROP? GetPropset(IRowsetInfo rowsetInfo) + private unsafe DBPROP? GetPropset(IRowsetInfo rowsetInfo) { var prgPropSetsPtr = IntPtr.Zero; @@ -403,16 +313,15 @@ internal sealed partial class SearchQuery : IDisposable return null; } - var firstPropSetPtr = new IntPtr(prgPropSetsPtr.ToInt64()); - var propSet = Marshal.PtrToStructure(firstPropSetPtr); + var firstPropSetPtr = (DBPROPSET*)prgPropSetsPtr.ToInt64(); + var propSet = *firstPropSetPtr; if (propSet.cProperties == 0 || propSet.rgProperties == IntPtr.Zero) { return null; } - var propPtr = new IntPtr(propSet.rgProperties.ToInt64()); - var prop = Marshal.PtrToStructure(propPtr); - return prop; + var propPtr = (DBPROP*)propSet.rgProperties.ToInt64(); + return *propPtr; } catch (Exception ex) { @@ -431,7 +340,8 @@ internal sealed partial class SearchQuery : IDisposable private uint GetReuseWhereId(IRowset rowset) { - var rowsetInfo = GetRowsetInfo(rowset); + var rowsetInfo = (IRowsetInfo)rowset; + if (rowsetInfo == null) { return 0; @@ -443,9 +353,9 @@ internal sealed partial class SearchQuery : IDisposable return 0; } - if (prop?.vValue.Anonymous.Anonymous.vt == VARENUM.VT_UI4) + if (prop?.vValue.VarType == VarEnum.VT_UI4) { - var value = prop?.vValue.Anonymous.Anonymous.Anonymous.ulVal; + var value = prop?.vValue._ulong; return (uint)value; } @@ -462,18 +372,6 @@ internal sealed partial class SearchQuery : IDisposable Marshal.FreeCoTaskMem(dbPropIdSet.rgPropertyIDs); } - if (reuseRowset != null) - { - Marshal.ReleaseComObject(reuseRowset); - reuseRowset = null; - } - - if (currentRowset != null) - { - Marshal.ReleaseComObject(currentRowset); - currentRowset = null; - } - queryCompletedEvent?.Dispose(); } } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SearchResult.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SearchResult.cs index 1840338b73..b44e9ab11b 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SearchResult.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SearchResult.cs @@ -3,12 +3,10 @@ // See the LICENSE file in the project root for more information. using System; +using System.Runtime.InteropServices; using ManagedCommon; +using Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch; using Microsoft.CmdPal.Ext.Indexer.Indexer.Utils; -using Microsoft.CmdPal.Ext.Indexer.Native; -using Windows.Win32.System.Com; -using Windows.Win32.System.Com.StructuredStorage; -using Windows.Win32.UI.Shell.PropertiesSystem; namespace Microsoft.CmdPal.Ext.Indexer.Indexer; @@ -39,14 +37,9 @@ internal sealed class SearchResult { try { - var key = NativeHelpers.PropertyKeys.PKEYItemNameDisplay; - propStore.GetValue(&key, out var itemNameDisplay); - - key = NativeHelpers.PropertyKeys.PKEYItemUrl; - propStore.GetValue(&key, out var itemUrl); - - key = NativeHelpers.PropertyKeys.PKEYKindText; - propStore.GetValue(&key, out var kindText); + propStore.GetValue(NativeHelpers.PropertyKeys.PKEYItemNameDisplay, out var itemNameDisplay); + propStore.GetValue(NativeHelpers.PropertyKeys.PKEYItemUrl, out var itemUrl); + propStore.GetValue(NativeHelpers.PropertyKeys.PKEYKindText, out var kindText); var filePath = GetFilePath(ref itemUrl); var isFolder = IsFoder(ref kindText); @@ -67,28 +60,34 @@ internal sealed class SearchResult } } - private static bool IsFoder(ref PROPVARIANT kindText) + private static bool IsFoder(ref PropVariant kindText) { var kindString = GetStringFromPropVariant(ref kindText); return string.Equals(kindString, "Folder", StringComparison.OrdinalIgnoreCase); } - private static string GetFilePath(ref PROPVARIANT itemUrl) + private static string GetFilePath(ref PropVariant itemUrl) { var filePath = GetStringFromPropVariant(ref itemUrl); filePath = UrlToFilePathConverter.Convert(filePath); return filePath; } - private static string GetStringFromPropVariant(ref PROPVARIANT propVariant) + private static string GetStringFromPropVariant(ref PropVariant propVariant) { - if (propVariant.Anonymous.Anonymous.vt == VARENUM.VT_LPWSTR) + if (propVariant.VarType == System.Runtime.InteropServices.VarEnum.VT_LPWSTR) { - var pwszVal = propVariant.Anonymous.Anonymous.Anonymous.pwszVal; - if (pwszVal != null) + var pwszVal = propVariant._ptr; + + if (pwszVal == IntPtr.Zero) { - return pwszVal.ToString(); + return string.Empty; } + + // convert to string + var str = Marshal.PtrToStringUni(pwszVal); + + return str; } return string.Empty; diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchCatalogManager.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchCatalogManager.cs deleted file mode 100644 index bc2b855844..0000000000 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchCatalogManager.cs +++ /dev/null @@ -1,18 +0,0 @@ -// 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.Indexer.Indexer.SystemSearch; - -[CoClass(typeof(CSearchCatalogManagerClass))] -[Guid("AB310581-AC80-11D1-8DF3-00C04FB6EF50")] -[ComImport] -[System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1715:Identifiers should have correct prefix", Justification = "Using original name from type lib")] -[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1302:Interface names should begin with I", Justification = "Using original name from type lib")] -[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Using original name from type lib")] -public interface CSearchCatalogManager : ISearchCatalogManager -{ -} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchCatalogManagerClass.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchCatalogManagerClass.cs deleted file mode 100644 index 6f64e518a0..0000000000 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchCatalogManagerClass.cs +++ /dev/null @@ -1,131 +0,0 @@ -// 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.Indexer.Indexer.SystemSearch; - -[ComConversionLoss] -[Guid("AAB49DD5-AD0B-40AE-B654-AE8976BF6BD2")] -[ClassInterface((short)0)] -[ComImport] -[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1212:Property accessors should follow order", Justification = "The order of the property accessors must match the order in which the methods were defined in the vtable")] -public class CSearchCatalogManagerClass : ISearchCatalogManager, CSearchCatalogManager -{ - [DispId(1610678272)] - public virtual extern string Name - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.LPWStr)] - get; - } - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public virtual extern IntPtr GetParameter([MarshalAs(UnmanagedType.LPWStr), In] string pszName); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public virtual extern void SetParameter([MarshalAs(UnmanagedType.LPWStr), In] string pszName, [In] ref object pValue); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public virtual extern void GetCatalogStatus( - out object pStatus, - out object pPausedReason); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public virtual extern void Reset(); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public virtual extern void Reindex(); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public virtual extern void ReindexMatchingURLs([MarshalAs(UnmanagedType.LPWStr), In] string pszPattern); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public virtual extern void ReindexSearchRoot([MarshalAs(UnmanagedType.LPWStr), In] string pszRoot); - - [DispId(1610678280)] - public virtual extern uint ConnectTimeout - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - get; - } - - [DispId(1610678282)] - public virtual extern uint DataTimeout - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - get; - } - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public virtual extern int NumberOfItems(); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public virtual extern void NumberOfItemsToIndex( - out int plIncrementalCount, - out int plNotificationQueue, - out int plHighPriorityQueue); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.LPWStr)] - public virtual extern string URLBeingIndexed(); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public virtual extern uint GetURLIndexingState([MarshalAs(UnmanagedType.LPWStr), In] string psz); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.Interface)] - public virtual extern object GetPersistentItemsChangedSink(); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public virtual extern void RegisterViewForNotification( - [MarshalAs(UnmanagedType.LPWStr), In] string pszView, - [MarshalAs(UnmanagedType.Interface), In] object pViewChangedSink, - out uint pdwCookie); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public virtual extern void GetItemsChangedSink( - [MarshalAs(UnmanagedType.Interface), In] object pISearchNotifyInlineSite, - [In] ref Guid riid, - out IntPtr ppv, - out Guid pGUIDCatalogResetSignature, - out Guid pGUIDCheckPointSignature, - out uint pdwLastCheckPointNumber); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public virtual extern void UnregisterViewForNotification([In] uint dwCookie); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public virtual extern void SetExtensionClusion([MarshalAs(UnmanagedType.LPWStr), In] string pszExtension, [In] int fExclude); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.Interface)] - public virtual extern object EnumerateExcludedExtensions(); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.Interface)] - public virtual extern CSearchQueryHelper GetQueryHelper(); - - [DispId(1610678295)] - public virtual extern int DiacriticSensitivity - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - get; - } - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.Interface)] - public virtual extern object GetCrawlScopeManager(); -} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchManager.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchManager.cs deleted file mode 100644 index 210bb3437e..0000000000 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchManager.cs +++ /dev/null @@ -1,18 +0,0 @@ -// 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.Indexer.Indexer.SystemSearch; - -[CoClass(typeof(CSearchManagerClass))] -[Guid("AB310581-AC80-11D1-8DF3-00C04FB6EF69")] -[ComImport] -[System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1715:Identifiers should have correct prefix", Justification = "Using original name from type lib")] -[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1302:Interface names should begin with I", Justification = "Using original name from type lib")] -[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Using original name from type lib")] -public interface CSearchManager : ISearchManager -{ -} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchManagerClass.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchManagerClass.cs deleted file mode 100644 index 19a8067bb7..0000000000 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchManagerClass.cs +++ /dev/null @@ -1,90 +0,0 @@ -// 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.Indexer.Indexer.SystemSearch; - -[Guid("7D096C5F-AC08-4F1F-BEB7-5C22C517CE39")] -[TypeLibType(2)] -[ClassInterface((short)0)] -[ComConversionLoss] -[ComImport] -public class CSearchManagerClass : ISearchManager, CSearchManager -{ - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public virtual extern void GetIndexerVersionStr([MarshalAs(UnmanagedType.LPWStr)] out string ppszVersionString); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public virtual extern void GetIndexerVersion(out uint pdwMajor, out uint pdwMinor); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public virtual extern IntPtr GetParameter([MarshalAs(UnmanagedType.LPWStr), In] string pszName); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public virtual extern void SetParameter([MarshalAs(UnmanagedType.LPWStr), In] string pszName, [In] ref object pValue); - - [DispId(1610678276)] - public virtual extern string ProxyName - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.LPWStr)] - get; - } - - [DispId(1610678277)] - public virtual extern string BypassList - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.LPWStr)] - get; - } - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public virtual extern void SetProxy( - [In] object sUseProxy, - [In] int fLocalByPassProxy, - [In] uint dwPortNumber, - [MarshalAs(UnmanagedType.LPWStr), In] string pszProxyName, - [MarshalAs(UnmanagedType.LPWStr), In] string pszByPassList); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.Interface)] - public virtual extern CSearchCatalogManager GetCatalog([MarshalAs(UnmanagedType.LPWStr), In] string pszCatalog); - - [DispId(1610678280)] - public virtual extern string UserAgent - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.LPWStr)] - get; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: MarshalAs(UnmanagedType.LPWStr)] - [param: In] - set; - } - - [DispId(1610678282)] - public virtual extern object UseProxy - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - get; - } - - [DispId(1610678283)] - public virtual extern int LocalBypass - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - get; - } - - [DispId(1610678284)] - public virtual extern uint PortNumber - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - get; - } -} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchQueryHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchQueryHelper.cs deleted file mode 100644 index c4a08859ff..0000000000 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchQueryHelper.cs +++ /dev/null @@ -1,18 +0,0 @@ -// 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.Indexer.Indexer.SystemSearch; - -[Guid("AB310581-AC80-11D1-8DF3-00C04FB6EF63")] -[CoClass(typeof(CSearchQueryHelperClass))] -[ComImport] -[System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1715:Identifiers should have correct prefix", Justification = "Using original name from type lib")] -[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1302:Interface names should begin with I", Justification = "Using original name from type lib")] -[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Using original name from type lib")] -public interface CSearchQueryHelper : ISearchQueryHelper -{ -} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchQueryHelperClass.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchQueryHelperClass.cs deleted file mode 100644 index f89de6050b..0000000000 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchQueryHelperClass.cs +++ /dev/null @@ -1,134 +0,0 @@ -// 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.Indexer.Indexer.SystemSearch; - -[ClassInterface((short)0)] -[Guid("B271E955-09E1-42E1-9B95-5994A534B613")] -[ComImport] -[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1212:Property accessors should follow order", Justification = "The order of the property accessors must match the order in which the methods were defined in the vtable")] -public class CSearchQueryHelperClass : ISearchQueryHelper, CSearchQueryHelper -{ - [DispId(1610678272)] - public virtual extern string ConnectionString - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.LPWStr)] - get; - } - - [DispId(1610678273)] - public virtual extern uint QueryContentLocale - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - get; - } - - [DispId(1610678275)] - public virtual extern uint QueryKeywordLocale - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - get; - } - - [DispId(1610678277)] - public virtual extern object QueryTermExpansion - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - get; - } - - [DispId(1610678279)] - public virtual extern object QuerySyntax - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - get; - } - - [DispId(1610678281)] - public virtual extern string QueryContentProperties - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: MarshalAs(UnmanagedType.LPWStr)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.LPWStr)] - get; - } - - [DispId(1610678283)] - public virtual extern string QuerySelectColumns - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: MarshalAs(UnmanagedType.LPWStr)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.LPWStr)] - get; - } - - [DispId(1610678285)] - public virtual extern string QueryWhereRestrictions - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: MarshalAs(UnmanagedType.LPWStr)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.LPWStr)] - get; - } - - [DispId(1610678287)] - public virtual extern string QuerySorting - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: MarshalAs(UnmanagedType.LPWStr)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.LPWStr)] - get; - } - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.LPWStr)] - public virtual extern string GenerateSQLFromUserQuery([MarshalAs(UnmanagedType.LPWStr), In] string pszQuery); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public virtual extern void WriteProperties( - [In] int itemID, - [In] uint dwNumberOfColumns, - [In] ref object pColumns, - [In] ref object pValues, - [In] ref object pftGatherModifiedTime); - - [DispId(1610678291)] - public virtual extern int QueryMaxResults - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - get; - } -} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ICommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ICommand.cs new file mode 100644 index 0000000000..e04da52d1c --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ICommand.cs @@ -0,0 +1,21 @@ +// 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.Runtime.InteropServices.Marshalling; +using Microsoft.CmdPal.Ext.Indexer.Indexer.OleDB; + +namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch; + +[Guid("0C733A63-2A1C-11CE-ADE5-00AA0044773D")] +[GeneratedComInterface(StringMarshalling = StringMarshalling.Utf16)] +public partial interface ICommand +{ + void Cancel(); + + void Execute([MarshalAs(UnmanagedType.Interface)] object pUnkOuter, ref Guid riid, [Optional][MarshalAs(UnmanagedType.Interface)] object pParams, [Optional]out int pcRowsAffected, out IRowset ppRowset); + + void GetDBSession(ref Guid riid, out IntPtr ppSession); +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ICommandText.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ICommandText.cs new file mode 100644 index 0000000000..f01aa48810 --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ICommandText.cs @@ -0,0 +1,23 @@ +// 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.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; +using System.Text; +using System.Threading.Tasks; +using Microsoft.CmdPal.Ext.Indexer.Indexer.OleDB; + +namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch; + +[Guid("0C733A27-2A1C-11CE-ADE5-00AA0044773D")] +[GeneratedComInterface(StringMarshalling = StringMarshalling.Utf16)] +public partial interface ICommandText : ICommand +{ + void GetCommandText([Optional] ref Guid pguidDialect, out IntPtr ppwszCommand); + + void SetCommandText(ref Guid rguidDialect, string pwszCommand); +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/IDBCreateCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/IDBCreateCommand.cs new file mode 100644 index 0000000000..25bd1c1c4e --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/IDBCreateCommand.cs @@ -0,0 +1,16 @@ +// 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.Runtime.InteropServices.Marshalling; + +namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch; + +[Guid("0C733A1D-2A1C-11CE-ADE5-00AA0044773D")] +[GeneratedComInterface(StringMarshalling = StringMarshalling.Utf16)] +public partial interface IDBCreateCommand +{ + void CreateCommand(IntPtr pUnkOuter, ref Guid riid, out ICommandText ppCommand); +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/IDBCreateSession.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/IDBCreateSession.cs new file mode 100644 index 0000000000..6d2f0cafef --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/IDBCreateSession.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; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch; + +[Guid("0C733A5D-2A1C-11CE-ADE5-00AA0044773D")] +[GeneratedComInterface(StringMarshalling = StringMarshalling.Utf16)] +public partial interface IDBCreateSession +{ + void CreateSession(IntPtr pUnkOuter, ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out object ppDBSession); +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/IDBInitialize.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/IDBInitialize.cs new file mode 100644 index 0000000000..40f8532427 --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/IDBInitialize.cs @@ -0,0 +1,22 @@ +// 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.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch; + +[Guid("0C733A8B-2A1C-11CE-ADE5-00AA0044773D")] +[GeneratedComInterface(StringMarshalling = StringMarshalling.Utf16)] +public partial interface IDBInitialize +{ + void Initialize(); + + void Uninitialize(); +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/IGetRow.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/IGetRow.cs new file mode 100644 index 0000000000..581ee9e8ec --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/IGetRow.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. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch; + +[Guid("0C733AAF-2A1C-11CE-ADE5-00AA0044773D")] +[GeneratedComInterface(StringMarshalling = StringMarshalling.Utf16)] +public partial interface IGetRow +{ + unsafe void GetRowFromHROW([MarshalAs(UnmanagedType.Interface)] object pUnkOuter, nuint hRow, ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out IPropertyStore ppUnk); + + unsafe string GetURLFromHROW(nuint hRow); +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/IPropertyStore.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/IPropertyStore.cs new file mode 100644 index 0000000000..1bd733f7b9 --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/IPropertyStore.cs @@ -0,0 +1,26 @@ +// 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.Runtime.InteropServices.Marshalling; + +[assembly: System.Runtime.CompilerServices.DisableRuntimeMarshalling] + +namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch; + +[Guid("886d8eeb-8cf2-4446-8d02-cdba1dbdcf99")] +[GeneratedComInterface(StringMarshalling = StringMarshalling.Utf16)] +public partial interface IPropertyStore +{ + uint GetCount(); + + PropertyKey GetAt(uint iProp); + + void GetValue(in PropertyKey pkey, out PropVariant pv); + + void SetValue(in PropertyKey pkey, in PropVariant pv); + + void Commit(); +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ISearchCatalogManager.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ISearchCatalogManager.cs index 04f6f76be0..f89c882669 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ISearchCatalogManager.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ISearchCatalogManager.cs @@ -5,125 +5,68 @@ using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch; [Guid("AB310581-AC80-11D1-8DF3-00C04FB6EF50")] -[ComConversionLoss] -[InterfaceType(1)] -[ComImport] -[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1212:Property accessors should follow order", Justification = "The order of the property accessors must match the order in which the methods were defined in the vtable")] -public interface ISearchCatalogManager +[GeneratedComInterface(StringMarshalling = StringMarshalling.Utf16)] +[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "Please do not change the function name")] +public partial interface ISearchCatalogManager { - [DispId(1610678272)] - string Name - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.LPWStr)] - get; - } + string get_Name(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - IntPtr GetParameter([MarshalAs(UnmanagedType.LPWStr), In] string pszName); + void GetParameter(string pszName, out IntPtr pValue); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - void SetParameter([MarshalAs(UnmanagedType.LPWStr), In] string pszName, [In] ref object pValue); + void SetParameter(string pszName, ref IntPtr pValue); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - void GetCatalogStatus(out object pStatus, out object pPausedReason); + void GetCatalogStatus(out uint pdwStatus, out uint pdwPausedReason); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] void Reset(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] void Reindex(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - void ReindexMatchingURLs([MarshalAs(UnmanagedType.LPWStr), In] string pszPattern); + void ReindexMatchingURLs(string pszPattern); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - void ReindexSearchRoot([MarshalAs(UnmanagedType.LPWStr), In] string pszRoot); + void ReindexSearchRoot(string pszRoot); - [DispId(1610678280)] - uint ConnectTimeout - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - get; - } + uint get_ConnectTimeout(); - [DispId(1610678282)] - uint DataTimeout - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - get; - } + void put_ConnectTimeout(uint dwTimeout); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - int NumberOfItems(); + uint get_DataTimeout(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - void NumberOfItemsToIndex( - out int plIncrementalCount, - out int plNotificationQueue, - out int plHighPriorityQueue); + void put_DataTimeout(uint dwTimeout); + + uint NumberOfItems(); + + uint NumberOfItemsToIndex(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] [return: MarshalAs(UnmanagedType.LPWStr)] string URLBeingIndexed(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - uint GetURLIndexingState([MarshalAs(UnmanagedType.LPWStr), In] string psz); + void GetURLIndexingState(string pszURL, out uint pdwState); + + IntPtr GetPersistentItemsChangedSink(); + + void RegisterViewForNotification(string pszView, IntPtr pViewNotify, out uint pdwCookie); + + IntPtr GetItemsChangedSink(); + + void UnregisterViewForNotification(uint dwCookie); + + void SetExtensionClusion(string pszExtension, [MarshalAs(UnmanagedType.Bool)] bool fExclude); + + void EnumerateExcludedExtensions(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] [return: MarshalAs(UnmanagedType.Interface)] - object GetPersistentItemsChangedSink(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - void RegisterViewForNotification( - [MarshalAs(UnmanagedType.LPWStr), In] string pszView, - [MarshalAs(UnmanagedType.Interface), In] object pViewChangedSink, - out uint pdwCookie); + ISearchQueryHelper GetQueryHelper(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - void GetItemsChangedSink( - [MarshalAs(UnmanagedType.Interface), In] object pISearchNotifyInlineSite, - [In] ref Guid riid, - out IntPtr ppv, - out Guid pGUIDCatalogResetSignature, - out Guid pGUIDCheckPointSignature, - out uint pdwLastCheckPointNumber); + [return: MarshalAs(UnmanagedType.Bool)] + bool get_DiacriticSensitivity(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - void UnregisterViewForNotification([In] uint dwCookie); + void put_DiacriticSensitivity([MarshalAs(UnmanagedType.Bool)] bool fDiacriticSensitive); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - void SetExtensionClusion([MarshalAs(UnmanagedType.LPWStr), In] string pszExtension, [In] int fExclude); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.Interface)] - object EnumerateExcludedExtensions(); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.Interface)] - CSearchQueryHelper GetQueryHelper(); - - [DispId(1610678295)] - int DiacriticSensitivity - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - get; - } - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.Interface)] - object GetCrawlScopeManager(); + IntPtr GetCrawlScopeManager(); } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ISearchManager.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ISearchManager.cs index 042e0f7660..c8e7a37544 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ISearchManager.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ISearchManager.cs @@ -3,87 +3,46 @@ // See the LICENSE file in the project root for more information. using System; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch; -[ComConversionLoss] [Guid("AB310581-AC80-11D1-8DF3-00C04FB6EF69")] -[InterfaceType(1)] -[ComImport] -public interface ISearchManager +[GeneratedComInterface(StringMarshalling = StringMarshalling.Utf16)] +[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "Please do not change the function name")] +public partial interface ISearchManager { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - void GetIndexerVersionStr([MarshalAs(UnmanagedType.LPWStr)] out string ppszVersionString); + string GetIndexerVersionStr(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] void GetIndexerVersion(out uint pdwMajor, out uint pdwMinor); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - IntPtr GetParameter([MarshalAs(UnmanagedType.LPWStr), In] string pszName); + void GetParameter(string pszName, [MarshalAs(UnmanagedType.Interface)] out object pValue); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - void SetParameter([MarshalAs(UnmanagedType.LPWStr), In] string pszName, [In] ref object pValue); + void SetParameter(string pszName, [MarshalAs(UnmanagedType.Interface)] ref object pValue); - [DispId(1610678276)] - string ProxyName - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.LPWStr)] - get; - } + [return: MarshalAs(UnmanagedType.Bool)] + bool get_UseProxy(); - [DispId(1610678277)] - string BypassList - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.LPWStr)] - get; - } + string get_BypassList(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] void SetProxy( - [In] object sUseProxy, - [In] int fLocalByPassProxy, - [In] uint dwPortNumber, - [MarshalAs(UnmanagedType.LPWStr), In] string pszProxyName, - [MarshalAs(UnmanagedType.LPWStr), In] string pszByPassList); + string pszProxyName, + [MarshalAs(UnmanagedType.Bool)] bool fLocalBypass, + string pszBypassList, + uint dwPortNumber); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] [return: MarshalAs(UnmanagedType.Interface)] - CSearchCatalogManager GetCatalog([MarshalAs(UnmanagedType.LPWStr), In] string pszCatalog); + ISearchCatalogManager GetCatalog(string pszCatalog); - [DispId(1610678280)] - string UserAgent - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.LPWStr)] - get; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: MarshalAs(UnmanagedType.LPWStr)] - [param: In] - set; - } + string get_UserAgent(); - [DispId(1610678282)] - object UseProxy - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - get; - } + void put_UserAgent(string pszUserAgent); - [DispId(1610678283)] - int LocalBypass - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - get; - } + string get_ProxyName(); - [DispId(1610678284)] - uint PortNumber - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - get; - } + [return: MarshalAs(UnmanagedType.Bool)] + bool get_LocalBypass(); + + uint get_PortNumber(); } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ISearchQueryHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ISearchQueryHelper.cs index e560935639..d1a49e83bd 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ISearchQueryHelper.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ISearchQueryHelper.cs @@ -5,130 +5,74 @@ using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch; [Guid("AB310581-AC80-11D1-8DF3-00C04FB6EF63")] -[InterfaceType(1)] -[ComImport] -[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1212:Property accessors should follow order", Justification = "The order of the property accessors must match the order in which the methods were defined in the vtable")] -public interface ISearchQueryHelper +[GeneratedComInterface(StringMarshalling = StringMarshalling.Utf16)] +[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "I don't want to change the name")] +public partial interface ISearchQueryHelper { - [DispId(1610678272)] - string ConnectionString - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.LPWStr)] - get; - } + string GetConnectionString(); - [DispId(1610678273)] - uint QueryContentLocale - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - get; - } + void SetQueryContentLocale(int lcid); - [DispId(1610678275)] - uint QueryKeywordLocale - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - get; - } + uint GetQueryContentLocale(); - [DispId(1610678277)] - object QueryTermExpansion - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - get; - } + void SetQueryKeywordLocale(int lcid); - [DispId(1610678279)] - object QuerySyntax - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - get; - } + uint GetQueryKeywordLocale(); - [DispId(1610678281)] - string QueryContentProperties - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: MarshalAs(UnmanagedType.LPWStr)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.LPWStr)] - get; - } + void SetQueryTermExpansion(SEARCH_TERM_EXPANSION expandTerms); - [DispId(1610678283)] - string QuerySelectColumns - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: MarshalAs(UnmanagedType.LPWStr)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.LPWStr)] - get; - } + void GetQueryTermExpansion(out SEARCH_TERM_EXPANSION pExpandTerms); - [DispId(1610678285)] - string QueryWhereRestrictions - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: MarshalAs(UnmanagedType.LPWStr)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.LPWStr)] - get; - } + void SetQuerySyntax(SEARCH_QUERY_SYNTAX querySyntax); - [DispId(1610678287)] - string QuerySorting - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: MarshalAs(UnmanagedType.LPWStr)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.LPWStr)] - get; - } + [return: MarshalAs(UnmanagedType.Interface)] + object GetQuerySyntax(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [return: MarshalAs(UnmanagedType.LPWStr)] - string GenerateSQLFromUserQuery([MarshalAs(UnmanagedType.LPWStr), In] string pszQuery); + void SetQueryContentProperties(string pszContentProperties); + + string GetQueryContentProperties(); + + void SetQuerySelectColumns(string pszColumns); + + string GetQuerySelectColumns(); + + void SetQueryWhereRestrictions(string pszRestrictions); + + string GetQueryWhereRestrictions(); + + void SetQuerySorting(string pszSorting); + + string GetQuerySorting(); + + string GenerateSQLFromUserQuery(string pszQuery); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] void WriteProperties( - [In] int itemID, - [In] uint dwNumberOfColumns, - [In] ref object pColumns, - [In] ref object pValues, - [In] ref object pftGatherModifiedTime); + int itemID, + uint dwNumberOfColumns, + [MarshalAs(UnmanagedType.Interface)] ref object pColumns, + [MarshalAs(UnmanagedType.Interface)] ref object pValues, + [MarshalAs(UnmanagedType.Interface)] ref object pftGatherModifiedTime); - [DispId(1610678291)] - int QueryMaxResults - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - [param: In] - set; - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - get; - } + void SetQueryMaxResults(int lMaxResults); + + int GetQueryMaxResults(); +} + +public enum SEARCH_TERM_EXPANSION +{ + SEARCH_TERM_NO_EXPANSION, + SEARCH_TERM_PREFIX_ALL, + SEARCH_TERM_STEM_ALL, +} + +public enum SEARCH_QUERY_SYNTAX +{ + SEARCH_NO_QUERY_SYNTAX, + SEARCH_ADVANCED_QUERY_SYNTAX, + SEARCH_NATURAL_QUERY_SYNTAX, } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/PropVariant.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/PropVariant.cs new file mode 100644 index 0000000000..0a48a42ee5 --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/PropVariant.cs @@ -0,0 +1,70 @@ +// 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.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using VARTYPE = System.Runtime.InteropServices.VarEnum; + +namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch; + +#pragma warning disable SA1307 // Accessible fields should begin with upper-case letter +[StructLayout(LayoutKind.Explicit, Pack = 8)] +public struct PropVariant +{ + /// Value type tag. + [FieldOffset(0)] + public ushort vt; + + [FieldOffset(2)] + public ushort wReserved1; + + /// Reserved for future use. + [FieldOffset(4)] + public ushort wReserved2; + + /// Reserved for future use. + [FieldOffset(6)] + public ushort wReserved3; + + /// The decimal value when VT_DECIMAL. + [FieldOffset(0)] + internal decimal _decimal; + + /// The raw data pointer. + [FieldOffset(8)] + internal IntPtr _ptr; + + /// The FILETIME when VT_FILETIME. + [FieldOffset(8)] + internal System.Runtime.InteropServices.ComTypes.FILETIME _ft; + + [FieldOffset(8)] + internal BLOB _blob; + + /// The value when a numeric value less than 8 bytes. + [FieldOffset(8)] + internal ulong _ulong; + + public PropVariant(string value) + { + ArgumentNullException.ThrowIfNull(value); + vt = (ushort)VarEnum.VT_LPWSTR; + _ptr = Marshal.StringToCoTaskMemUni(value); + } + + public VarEnum VarType { get => (VarEnum)vt; set => vt = (ushort)(VARTYPE)value; } +} + +[StructLayout(LayoutKind.Sequential, Pack = 0)] +public struct BLOB +{ + /// The count of bytes + public uint cbSize; + + /// A pointer to the allocated array of bytes. + public IntPtr pBlobData; +} + +#pragma warning restore SA1307 // Accessible fields should begin with upper-case letter diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/PropertyKey.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/PropertyKey.cs new file mode 100644 index 0000000000..e37177d759 --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/PropertyKey.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.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch; + +[StructLayout(LayoutKind.Sequential)] +public struct PropertyKey +{ + public Guid FmtID; + + public uint PID; + + public PropertyKey(Guid fmtid, uint pid) + { + this.FmtID = fmtid; + this.PID = pid; + } +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/Utils/NativeHelpers.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/Utils/NativeHelpers.cs new file mode 100644 index 0000000000..ec7604a7b3 --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/Utils/NativeHelpers.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. +using System; +using Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch; + +namespace Microsoft.CmdPal.Ext.Indexer.Indexer.Utils; + +public sealed partial class NativeHelpers +{ + public const uint SEEMASKINVOKEIDLIST = 12; + + public struct PropertyKeys + { + public static readonly PropertyKey PKEYItemNameDisplay = new() { FmtID = new Guid("B725F130-47EF-101A-A5F1-02608C9EEBAC"), PID = 10 }; + public static readonly PropertyKey PKEYItemUrl = new() { FmtID = new Guid("49691C90-7E17-101A-A91C-08002B2ECDA9"), PID = 9 }; + public static readonly PropertyKey PKEYKindText = new() { FmtID = new Guid("F04BEF95-C585-4197-A2B7-DF46FDC9EE6D"), PID = 100 }; + } + + public static class OleDb + { + public static readonly Guid DbGuidDefault = new("C8B521FB-5CF3-11CE-ADE5-00AA0044773D"); + } +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/Utils/QueryStringBuilder.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/Utils/QueryStringBuilder.cs index f835acafaa..997d364b4d 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/Utils/QueryStringBuilder.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/Utils/QueryStringBuilder.cs @@ -2,12 +2,18 @@ // 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.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; +using ManagedCommon; +using ManagedCsWin32; using Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch; namespace Microsoft.CmdPal.Ext.Indexer.Indexer.Utils; -internal sealed class QueryStringBuilder +internal sealed partial class QueryStringBuilder { private const string Properties = "System.ItemUrl, System.ItemNameDisplay, path, System.Search.EntryID, System.Kind, System.KindText"; private const string SystemIndex = "SystemIndex"; @@ -24,16 +30,36 @@ internal sealed class QueryStringBuilder { if (queryHelper == null) { - var searchManager = new CSearchManager(); - ISearchCatalogManager catalogManager = searchManager.GetCatalog(SystemIndex); - queryHelper = catalogManager.GetQueryHelper(); + ISearchManager searchManager; - queryHelper.QuerySelectColumns = Properties; - queryHelper.QueryContentProperties = "System.FileName"; - queryHelper.QuerySorting = OrderConditions; + try + { + searchManager = ComHelper.CreateComInstance(ref Unsafe.AsRef(in CLSID.SearchManager), CLSCTX.LocalServer); + } + catch (Exception ex) + { + Logger.LogError($"Failed to create searchManager. ex: {ex.Message}"); + throw; + } + + ISearchCatalogManager catalogManager = searchManager.GetCatalog(SystemIndex); + if (catalogManager == null) + { + throw new ArgumentException($"Failed to get catalog manager for {SystemIndex}"); + } + + queryHelper = catalogManager.GetQueryHelper(); + if (queryHelper == null) + { + throw new ArgumentException("Failed to get query helper from catalog manager"); + } + + queryHelper.SetQuerySelectColumns(Properties); + queryHelper.SetQueryContentProperties("System.FileName"); + queryHelper.SetQuerySorting(OrderConditions); } - queryHelper.QueryWhereRestrictions = "AND " + ScopeFileConditions + "AND ReuseWhere(" + whereId.ToString(CultureInfo.InvariantCulture) + ")"; + queryHelper.SetQueryWhereRestrictions("AND " + ScopeFileConditions + "AND ReuseWhere(" + whereId.ToString(CultureInfo.InvariantCulture) + ")"); return queryHelper.GenerateSQLFromUserQuery(searchText); } } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Microsoft.CmdPal.Ext.Indexer.csproj b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Microsoft.CmdPal.Ext.Indexer.csproj index 70e6a10c5a..af8fcff41a 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Microsoft.CmdPal.Ext.Indexer.csproj +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Microsoft.CmdPal.Ext.Indexer.csproj @@ -1,12 +1,12 @@ - + + Microsoft.CmdPal.Ext.Indexer - $(SolutionDir)$(Platform)\$(Configuration)\WinUI3Apps\CmdPal false false - + true @@ -15,8 +15,9 @@ runtime; build; native; contentfiles; analyzers + - + @@ -34,6 +35,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -46,4 +50,9 @@ + + + + + diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Native/NativeHelpers.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Native/NativeHelpers.cs deleted file mode 100644 index 9851573843..0000000000 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Native/NativeHelpers.cs +++ /dev/null @@ -1,25 +0,0 @@ -// 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 Windows.Win32.UI.Shell.PropertiesSystem; - -namespace Microsoft.CmdPal.Ext.Indexer.Native; - -internal sealed class NativeHelpers -{ - public const uint SEEMASKINVOKEIDLIST = 12; - - internal static class PropertyKeys - { - public static readonly PROPERTYKEY PKEYItemNameDisplay = new() { fmtid = new System.Guid("B725F130-47EF-101A-A5F1-02608C9EEBAC"), pid = 10 }; - public static readonly PROPERTYKEY PKEYItemUrl = new() { fmtid = new System.Guid("49691C90-7E17-101A-A91C-08002B2ECDA9"), pid = 9 }; - public static readonly PROPERTYKEY PKEYKindText = new() { fmtid = new System.Guid("F04BEF95-C585-4197-A2B7-DF46FDC9EE6D"), pid = 100 }; - } - - internal static class OleDb - { - public static readonly Guid DbGuidDefault = new("C8B521FB-5CF3-11CE-ADE5-00AA0044773D"); - } -} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/NativeMethods.json b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/NativeMethods.json new file mode 100644 index 0000000000..02fff599f2 --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/NativeMethods.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://aka.ms/CsWin32.schema.json", + "allowMarshaling": false, + "comInterop": { + "preserveSigMethods": [ "*" ] + } +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/NativeMethods.txt b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/NativeMethods.txt index f0702ba24c..029b6fe6c2 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/NativeMethods.txt +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/NativeMethods.txt @@ -1,11 +1,2 @@ DBID -SHOW_WINDOW_CMD -CoCreateInstance -GetErrorInfo -ICommandText -IDBCreateCommand -IDBCreateSession -IDBInitialize -IGetRow -IPropertyStore -ShellExecuteEx \ No newline at end of file +SHOW_WINDOW_CMD \ No newline at end of file diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Pages/ActionsListContextItem.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Pages/ActionsListContextItem.cs new file mode 100644 index 0000000000..d191a5a08c --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Pages/ActionsListContextItem.cs @@ -0,0 +1,93 @@ +// 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.Globalization; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CmdPal.Ext.Indexer.Commands; +using Microsoft.CmdPal.Ext.Indexer.Data; +using Microsoft.CmdPal.Ext.Indexer.Properties; +using Microsoft.CommandPalette.Extensions.Toolkit; +using Windows.AI.Actions; +using Windows.System; + +namespace Microsoft.CmdPal.Ext.Indexer.Pages; + +internal sealed partial class ActionsListContextItem : CommandContextItem +{ + private readonly string fullPath; + private readonly List actions = []; + private static readonly Lock UpdateMoreCommandsLock = new(); + private static ActionRuntime actionRuntime; + + public ActionsListContextItem(string fullPath) + : base(new NoOpCommand()) + { + Title = Resources.Indexer_Command_Actions; + Icon = Icons.Actions; + RequestedShortcut = KeyChordHelpers.FromModifiers(alt: true, vkey: VirtualKey.A); + this.fullPath = fullPath; + UpdateMoreCommands(); + } + + public bool AnyActions() => actions.Count != 0; + + private void ActionCatalog_Changed(global::Windows.AI.Actions.Hosting.ActionCatalog sender, object args) + { + UpdateMoreCommands(); + } + + private void UpdateMoreCommands() + { + try + { + lock (UpdateMoreCommandsLock) + { + if (actionRuntime == null) + { + actionRuntime = ActionRuntimeFactory.CreateActionRuntime(); + Task.Delay(500).Wait(); + } + + actionRuntime.ActionCatalog.Changed -= ActionCatalog_Changed; + actionRuntime.ActionCatalog.Changed += ActionCatalog_Changed; + } + + var extension = System.IO.Path.GetExtension(fullPath).ToLower(CultureInfo.InvariantCulture); + ActionEntity entity = null; + if (extension != null) + { + if (extension == ".jpg" || extension == ".jpeg" || extension == ".png") + { + entity = actionRuntime.EntityFactory.CreatePhotoEntity(fullPath); + } + else if (extension == ".docx" || extension == ".doc" || extension == ".pdf" || extension == ".txt") + { + entity = actionRuntime.EntityFactory.CreateDocumentEntity(fullPath); + } + } + + if (entity == null) + { + entity = actionRuntime.EntityFactory.CreateFileEntity(fullPath); + } + + lock (actions) + { + actions.Clear(); + foreach (var actionInstance in actionRuntime.ActionCatalog.GetActionsForInputs([entity])) + { + actions.Add(new CommandContextItem(new ExecuteActionCommand(actionInstance))); + } + + MoreCommands = [.. actions]; + } + } + catch + { + actionRuntime = null; + } + } +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Pages/Icons.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Pages/Icons.cs index a55cbe506e..a07bce2016 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Pages/Icons.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Pages/Icons.cs @@ -12,6 +12,8 @@ internal sealed class Icons internal static IconInfo FileExplorer { get; } = IconHelpers.FromRelativePath("Assets\\FileExplorer.png"); + internal static IconInfo Actions { get; } = IconHelpers.FromRelativePath("Assets\\Actions.png"); + internal static IconInfo OpenFile { get; } = new("\uE8E5"); // OpenFile internal static IconInfo Document { get; } = new("\uE8A5"); // Document diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Properties/Resources.Designer.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Properties/Resources.Designer.cs index 37a53c1cef..f5d1ba2d61 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Properties/Resources.Designer.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Properties/Resources.Designer.cs @@ -60,6 +60,15 @@ namespace Microsoft.CmdPal.Ext.Indexer.Properties { } } + /// + /// Looks up a localized string similar to Actions. + /// + internal static string Indexer_Command_Actions { + get { + return ResourceManager.GetString("Indexer_Command_Actions", resourceCulture); + } + } + /// /// Looks up a localized string similar to Browse. /// diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Properties/Resources.resx b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Properties/Resources.resx index 8e4f70bc1a..61d51998b2 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Properties/Resources.resx +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Properties/Resources.resx @@ -138,6 +138,9 @@ Open with + + Actions... + Show in folder diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/FallbackExecuteItem.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/FallbackExecuteItem.cs index d1bdb0e7be..d3bc979419 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/FallbackExecuteItem.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/FallbackExecuteItem.cs @@ -12,10 +12,12 @@ namespace Microsoft.CmdPal.Ext.Shell; internal sealed partial class FallbackExecuteItem : FallbackCommandItem { private readonly ExecuteItem _executeItem; + private readonly SettingsManager _settings; public FallbackExecuteItem(SettingsManager settings) : base(new ExecuteItem(string.Empty, settings), Resources.shell_command_display_title) { + _settings = settings; _executeItem = (ExecuteItem)this.Command!; Title = string.Empty; _executeItem.Name = string.Empty; @@ -28,5 +30,9 @@ internal sealed partial class FallbackExecuteItem : FallbackCommandItem _executeItem.Cmd = query; _executeItem.Name = string.IsNullOrEmpty(query) ? string.Empty : Properties.Resources.generic_run_command; Title = query; + MoreCommands = [ + new CommandContextItem(new ExecuteItem(query, _settings, RunAsType.Administrator)), + new CommandContextItem(new ExecuteItem(query, _settings, RunAsType.OtherUser)), + ]; } } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Helpers/Commands.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Helpers/Commands.cs index a981b2ed00..70c1b7f8c6 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Helpers/Commands.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Helpers/Commands.cs @@ -110,6 +110,13 @@ internal static class Commands }); } + results.Add(new ListItem(new ExecuteCommandConfirmation(Resources.Microsoft_plugin_sys_RestartShell_name!, confirmCommands, Resources.Microsoft_plugin_sys_RestartShell_confirmation!, static () => OpenInShellHelper.OpenInShell("cmd", "/C tskill explorer && start explorer", runWithHiddenWindow: true))) + { + Title = Resources.Microsoft_plugin_sys_RestartShell!, + Subtitle = Resources.Microsoft_plugin_sys_RestartShell_description!, + Icon = Icons.RestartShellIcon, + }); + // UEFI command/result. It is only available on systems booted in UEFI mode. if (isUefi) { diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Helpers/Icons.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Helpers/Icons.cs index be64ddb181..313ba773f8 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Helpers/Icons.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Helpers/Icons.cs @@ -20,6 +20,8 @@ public static partial class Icons public static IconInfo RestartIcon { get; } = new IconInfo("\uE777"); + public static IconInfo RestartShellIcon { get; } = new IconInfo("\uEC50"); + public static IconInfo ShutdownIcon { get; } = new IconInfo("\uE7E8"); public static IconInfo SleepIcon { get; } = new IconInfo("\uE708"); diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Helpers/Native.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Helpers/Native.cs deleted file mode 100644 index 3daf2a4be4..0000000000 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Helpers/Native.cs +++ /dev/null @@ -1,144 +0,0 @@ -// 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.Diagnostics.CodeAnalysis; -using System.Runtime.InteropServices; - -namespace Microsoft.CmdPal.Ext.System.Helpers; - -[SuppressMessage("Interoperability", "CA1401:P/Invokes should not be visible", Justification = "We want plugins to share this NativeMethods class, instead of each one creating its own.")] -public sealed class Native -{ - public enum HRESULT : uint - { - /// - /// Operation successful. - /// - S_OK = 0x00000000, - - /// - /// Operation successful. (negative condition/no operation) - /// - S_FALSE = 0x00000001, - - /// - /// Not implemented. - /// - E_NOTIMPL = 0x80004001, - - /// - /// No such interface supported. - /// - E_NOINTERFACE = 0x80004002, - - /// - /// Pointer that is not valid. - /// - E_POINTER = 0x80004003, - - /// - /// Operation aborted. - /// - E_ABORT = 0x80004004, - - /// - /// Unspecified failure. - /// - E_FAIL = 0x80004005, - - /// - /// Unexpected failure. - /// - E_UNEXPECTED = 0x8000FFFF, - - /// - /// General access denied error. - /// - E_ACCESSDENIED = 0x80070005, - - /// - /// Handle that is not valid. - /// - E_HANDLE = 0x80070006, - - /// - /// Failed to allocate necessary memory. - /// - E_OUTOFMEMORY = 0x8007000E, - - /// - /// One or more arguments are not valid. - /// - E_INVALIDARG = 0x80070057, - - /// - /// The operation was canceled by the user. (Error source 7 means Win32.) - /// - /// - /// - E_CANCELLED = 0x800704C7, - } - - public static class ShellItemTypeConstants - { - /// - /// Guid for type IShellItem. - /// - public static readonly Guid ShellItemGuid = new("43826d1e-e718-42ee-bc55-a1e261c37bfe"); - - /// - /// Guid for type IShellItem2. - /// - public static readonly Guid ShellItem2Guid = new("7E9FB0D3-919F-4307-AB2E-9B1860310C93"); - } - - /// - /// The following are ShellItem DisplayName types. - /// - [Flags] - public enum SIGDN : uint - { - NORMALDISPLAY = 0, - PARENTRELATIVEPARSING = 0x80018001, - PARENTRELATIVEFORADDRESSBAR = 0x8001c001, - DESKTOPABSOLUTEPARSING = 0x80028000, - PARENTRELATIVEEDITING = 0x80031001, - DESKTOPABSOLUTEEDITING = 0x8004c000, - FILESYSPATH = 0x80058000, - URL = 0x80068000, - } - - [ComImport] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - [Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe")] - public interface IShellItem - { - void BindToHandler( - nint pbc, - [MarshalAs(UnmanagedType.LPStruct)] Guid bhid, - [MarshalAs(UnmanagedType.LPStruct)] Guid riid, - out nint ppv); - - void GetParent(out IShellItem ppsi); - - void GetDisplayName(SIGDN sigdnName, [MarshalAs(UnmanagedType.LPWStr)] out string ppszName); - - void GetAttributes(uint sfgaoMask, out uint psfgaoAttribs); - - void Compare(IShellItem psi, uint hint, out int piOrder); - } - - /// - /// see all STGM values - /// - [Flags] - public enum STGM : long - { - READ = 0x00000000L, - WRITE = 0x00000001L, - READWRITE = 0x00000002L, - CREATE = 0x00001000L, - } -} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Properties/Resources.Designer.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Properties/Resources.Designer.cs index 1cfa1a6353..f2ec8c1218 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Properties/Resources.Designer.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Properties/Resources.Designer.cs @@ -645,6 +645,42 @@ namespace Microsoft.CmdPal.Ext.System { } } + /// + /// Looks up a localized string similar to Restart Windows Explorer. + /// + public static string Microsoft_plugin_sys_RestartShell { + get { + return ResourceManager.GetString("Microsoft_plugin_sys_RestartShell", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You are about to restart Windows Explorer, are you sure?. + /// + public static string Microsoft_plugin_sys_RestartShell_confirmation { + get { + return ResourceManager.GetString("Microsoft_plugin_sys_RestartShell_confirmation", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to End and restart the Windows Explorer shell process. + /// + public static string Microsoft_plugin_sys_RestartShell_description { + get { + return ResourceManager.GetString("Microsoft_plugin_sys_RestartShell_description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Restart. + /// + public static string Microsoft_plugin_sys_RestartShell_name { + get { + return ResourceManager.GetString("Microsoft_plugin_sys_RestartShell_name", resourceCulture); + } + } + /// /// Looks up a localized string similar to ip; mac; address. /// diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Properties/Resources.resx b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Properties/Resources.resx index 23d27a86a2..bf7ab04da0 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Properties/Resources.resx +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Properties/Resources.resx @@ -417,4 +417,16 @@ Open System Command + + Restart Windows Explorer + + + End and restart the Windows Explorer shell process + + + Restart + + + You are about to restart Windows Explorer, are you sure? + \ No newline at end of file diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/FallbackTimeDateItem.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/FallbackTimeDateItem.cs index 267ab43989..4ecc469f82 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/FallbackTimeDateItem.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/FallbackTimeDateItem.cs @@ -68,6 +68,7 @@ internal sealed partial class FallbackTimeDateItem : FallbackCommandItem Title = result.Title; Subtitle = result.Subtitle; Icon = result.Icon; + Command = result.Command; } else { diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/Pages/TimeDateExtensionPage.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/Pages/TimeDateExtensionPage.cs index 024a559087..8dce2128ce 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/Pages/TimeDateExtensionPage.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/Pages/TimeDateExtensionPage.cs @@ -18,8 +18,6 @@ internal sealed partial class TimeDateExtensionPage : DynamicListPage private IList _results = new List(); - private bool initialized; - private SettingsManager _settingsManager; public TimeDateExtensionPage(SettingsManager settingsManager) @@ -35,12 +33,9 @@ internal sealed partial class TimeDateExtensionPage : DynamicListPage public override IListItem[] GetItems() { - if (!initialized) - { - DoExecuteSearch(string.Empty); - } + DoExecuteSearch(string.Empty); - lock (_resultsLock) + lock (_resultsLock) { ListItem[] results = _results.ToArray(); return results; @@ -49,11 +44,6 @@ internal sealed partial class TimeDateExtensionPage : DynamicListPage public override void UpdateSearchText(string oldSearch, string newSearch) { - if (newSearch == oldSearch) - { - return; - } - DoExecuteSearch(newSearch); } @@ -84,7 +74,6 @@ internal sealed partial class TimeDateExtensionPage : DynamicListPage { lock (_resultsLock) { - initialized = true; this._results = result; } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/Helpers/DefaultBrowserInfo.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/Helpers/DefaultBrowserInfo.cs index b3df3fc492..87b87c7ff5 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/Helpers/DefaultBrowserInfo.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/Helpers/DefaultBrowserInfo.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Runtime.InteropServices; using System.Text; using System.Threading; using ManagedCommon; @@ -78,8 +79,11 @@ public static class DefaultBrowserInfo try { var progId = GetRegistryValue( - @"HKEY_CURRENT_USER\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice", - "ProgId"); + @"HKEY_CURRENT_USER\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoiceLatest\ProgId", + "ProgId") + ?? GetRegistryValue( + @"HKEY_CURRENT_USER\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice", + "ProgId"); var appName = GetRegistryValue($@"HKEY_CLASSES_ROOT\{progId}\Application", "ApplicationName") ?? GetRegistryValue($@"HKEY_CLASSES_ROOT\{progId}", "FriendlyTypeName"); @@ -188,17 +192,20 @@ public static class DefaultBrowserInfo { var buffer = stackalloc char[128]; var capacity = 128; - void* reserved = null; + var firstChar = str[0]; + var strPtr = &firstChar; // S_OK == 0 - if (global::Windows.Win32.PInvoke.SHLoadIndirectString( - str, + fixed (char* pszSourceLocal = str) + { + if (global::Windows.Win32.PInvoke.SHLoadIndirectString( + pszSourceLocal, buffer, (uint)capacity, - ref reserved) - == 0) - { - return new string(buffer); + default) == 0) + { + return new string(buffer); + } } } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Microsoft.CmdPal.Ext.WinGet.csproj b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Microsoft.CmdPal.Ext.WinGet.csproj index 7c5c6d2dd3..2f9da761d2 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Microsoft.CmdPal.Ext.WinGet.csproj +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Microsoft.CmdPal.Ext.WinGet.csproj @@ -32,6 +32,11 @@ + + + + + diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/NativeMethods.json b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/NativeMethods.json new file mode 100644 index 0000000000..02fff599f2 --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/NativeMethods.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://aka.ms/CsWin32.schema.json", + "allowMarshaling": false, + "comInterop": { + "preserveSigMethods": [ "*" ] + } +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/WindowsPackageManager.Interop/WindowsPackageManagerStandardFactory.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/WindowsPackageManager.Interop/WindowsPackageManagerStandardFactory.cs index d8b6064a86..677f688ac6 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/WindowsPackageManager.Interop/WindowsPackageManagerStandardFactory.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/WindowsPackageManager.Interop/WindowsPackageManagerStandardFactory.cs @@ -20,20 +20,25 @@ public class WindowsPackageManagerStandardFactory : WindowsPackageManagerFactory protected override T CreateInstance(Guid clsid, Guid iid) { var pUnknown = IntPtr.Zero; - try + unsafe { - var hr = PInvoke.CoCreateInstance(clsid, null, CLSCTX.CLSCTX_ALL, iid, out var result); - Marshal.ThrowExceptionForHR(hr); - pUnknown = Marshal.GetIUnknownForObject(result); - return MarshalGeneric.FromAbi(pUnknown); - } - finally - { - // CoCreateInstance and FromAbi both AddRef on the native object. - // Release once to prevent memory leak. - if (pUnknown != IntPtr.Zero) + try { - Marshal.Release(pUnknown); + var hr = PInvoke.CoCreateInstance(clsid, null, CLSCTX.CLSCTX_ALL, iid, out var result); + Marshal.ThrowExceptionForHR(hr); + + pUnknown = new IntPtr(result); + + return MarshalGeneric.FromAbi(pUnknown); + } + finally + { + // CoCreateInstance and FromAbi both AddRef on the native object. + // Release once to prevent memory leak. + if (pUnknown != IntPtr.Zero) + { + Marshal.Release(pUnknown); + } } } } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/app.manifest b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/app.manifest index bcafb9bc5b..16492c3b79 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/app.manifest +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/app.manifest @@ -13,6 +13,7 @@ + true/PM PerMonitorV2 diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Components/ContextMenuHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Components/ContextMenuHelper.cs index cbadadc699..a2064b0369 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Components/ContextMenuHelper.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Components/ContextMenuHelper.cs @@ -36,6 +36,7 @@ internal sealed class ContextMenuHelper contextMenu.Add(new CommandContextItem(new KillProcessCommand(windowData)) { RequestedShortcut = KeyChordHelpers.FromModifiers(true, false, false, false, (int)VirtualKey.Delete, 0), + IsCritical = true, }); } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/IVirtualDesktopManager.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/IVirtualDesktopManager.cs index 070e24cc26..75170a56cc 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/IVirtualDesktopManager.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/IVirtualDesktopManager.cs @@ -4,6 +4,7 @@ using System; using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; namespace Microsoft.CmdPal.Ext.WindowWalker.Helpers; @@ -11,18 +12,16 @@ namespace Microsoft.CmdPal.Ext.WindowWalker.Helpers; /// Interface for accessing Virtual Desktop Manager. /// Code used from /// -[ComImport] -[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] +[GeneratedComInterface] [Guid("a5cd92ff-29be-454c-8d04-d82879fb3f1b")] -[System.Security.SuppressUnmanagedCodeSecurity] -internal interface IVirtualDesktopManager +public partial interface IVirtualDesktopManager { [PreserveSig] - int IsWindowOnCurrentVirtualDesktop([In] IntPtr hTopLevelWindow, [Out] out int onCurrentDesktop); + int IsWindowOnCurrentVirtualDesktop(IntPtr hTopLevelWindow, out int onCurrentDesktop); [PreserveSig] - int GetWindowDesktopId([In] IntPtr hTopLevelWindow, [Out] out Guid desktop); + int GetWindowDesktopId(IntPtr hTopLevelWindow, out Guid desktop); [PreserveSig] - int MoveWindowToDesktop([In] IntPtr hTopLevelWindow, [MarshalAs(UnmanagedType.LPStruct)][In] Guid desktop); + int MoveWindowToDesktop(IntPtr hTopLevelWindow, ref Guid desktop); } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/NativeMethods.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/NativeMethods.cs index e60cb262fe..99d29a9949 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/NativeMethods.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/NativeMethods.cs @@ -13,7 +13,7 @@ using SuppressMessageAttribute = System.Diagnostics.CodeAnalysis.SuppressMessage namespace Microsoft.CmdPal.Ext.WindowWalker.Helpers; [SuppressMessage("Interoperability", "CA1401:P/Invokes should not be visible", Justification = "We want plugins to share this NativeMethods class, instead of each one creating its own.")] -public static class NativeMethods +public static partial class NativeMethods { [DllImport("user32.dll", CharSet = CharSet.Unicode)] public static extern int EnumWindows(EnumWindowsProc callPtr, IntPtr lParam); @@ -98,32 +98,6 @@ public static class NativeMethods [DllImport("kernel32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool GetFirmwareType(ref FirmwareType FirmwareType); - - [DllImport("user32.dll")] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool ExitWindowsEx(uint uFlags, uint dwReason); - - [DllImport("user32")] - public static extern void LockWorkStation(); - - [DllImport("Powrprof.dll", CharSet = CharSet.Auto, ExactSpelling = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool SetSuspendState(bool hibernate, bool forceCritical, bool disableWakeEvent); - - [DllImport("Shell32.dll", CharSet = CharSet.Unicode)] - public static extern uint SHEmptyRecycleBin(IntPtr hWnd, uint dwFlags); - - [DllImport("shlwapi.dll", CharSet = CharSet.Unicode)] - public static extern HRESULT SHLoadIndirectString(string pszSource, StringBuilder pszOutBuf, uint cchOutBuf, IntPtr ppvReserved); - - [DllImport("shlwapi.dll", CharSet = CharSet.Unicode)] - public static extern HRESULT SHCreateStreamOnFileEx(string fileName, STGM grfMode, uint attributes, bool create, System.Runtime.InteropServices.ComTypes.IStream reserved, out System.Runtime.InteropServices.ComTypes.IStream stream); - - [DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = true)] - public static extern int SHCreateItemFromParsingName([MarshalAs(UnmanagedType.LPWStr)] string path, IntPtr pbc, ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out IShellItem shellItem); - - [DllImport("rpcrt4.dll")] - public static extern int UuidCreateSequential(out GUIDDATA Uuid); } [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "These are the names used by win32.")] @@ -161,19 +135,6 @@ public static class Win32Constants public const int RPC_S_UUID_LOCAL_ONLY = 0x720; } -public static class ShellItemTypeConstants -{ - /// - /// Guid for type IShellItem. - /// - public static readonly Guid ShellItemGuid = new("43826d1e-e718-42ee-bc55-a1e261c37bfe"); - - /// - /// Guid for type IShellItem2. - /// - public static readonly Guid ShellItem2Guid = new("7E9FB0D3-919F-4307-AB2E-9B1860310C93"); -} - public enum HRESULT : uint { /// @@ -1124,26 +1085,6 @@ public enum ExtendedWindowStyles : uint WS_EX_NOACTIVATE = 0x8000000, } -[ComImport] -[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] -[Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe")] -public interface IShellItem -{ - void BindToHandler( - IntPtr pbc, - [MarshalAs(UnmanagedType.LPStruct)] Guid bhid, - [MarshalAs(UnmanagedType.LPStruct)] Guid riid, - out IntPtr ppv); - - void GetParent(out IShellItem ppsi); - - void GetDisplayName(SIGDN sigdnName, [MarshalAs(UnmanagedType.LPWStr)] out string ppszName); - - void GetAttributes(uint sfgaoMask, out uint psfgaoAttribs); - - void Compare(IShellItem psi, uint hint, out int piOrder); -} - /// /// The following are ShellItem DisplayName types. /// diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/VirtualDesktopHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/VirtualDesktopHelper.cs index 023bd8ed33..1cdc9f82b9 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/VirtualDesktopHelper.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/VirtualDesktopHelper.cs @@ -5,9 +5,11 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; using System.Text; - +using ManagedCsWin32; using Microsoft.CmdPal.Ext.WindowWalker.Properties; using Microsoft.CommandPalette.Extensions.Toolkit; using Microsoft.Win32; @@ -61,9 +63,11 @@ public class VirtualDesktopHelper /// Setting to configure if the list of available desktops should update automatically or only when calling . Per default this is set to manual update (false) to have less registry queries. public VirtualDesktopHelper(bool desktopListUpdate = false) { + var cw = new StrategyBasedComWrappers(); + try { - _virtualDesktopManager = (IVirtualDesktopManager)new CVirtualDesktopManager(); + _virtualDesktopManager = ComHelper.CreateComInstance(ref Unsafe.AsRef(in CLSID.VirtualDesktopManager), CLSCTX.InProcServer); } catch (COMException ex) { @@ -409,7 +413,7 @@ public class VirtualDesktopHelper /// Handle of the top level window. /// Guid of the target desktop. /// on success and on failure. - public bool MoveWindowToDesktop(IntPtr hWindow, in Guid desktopId) + public bool MoveWindowToDesktop(IntPtr hWindow, ref Guid desktopId) { if (_virtualDesktopManager == null) { @@ -417,7 +421,7 @@ public class VirtualDesktopHelper return false; } - var hr = _virtualDesktopManager.MoveWindowToDesktop(hWindow, desktopId); + var hr = _virtualDesktopManager.MoveWindowToDesktop(hWindow, ref desktopId); if (hr != (int)HRESULT.S_OK) { ExtensionHost.LogMessage(new LogMessage() { Message = "VirtualDesktopHelper.MoveWindowToDesktop() failed: An exception was thrown when moving the window ({hWindow}) to another desktop ({desktopId})." }); @@ -455,7 +459,7 @@ public class VirtualDesktopHelper } Guid newDesktop = _availableDesktops[windowDesktopNumber - 1]; - return MoveWindowToDesktop(hWindow, newDesktop); + return MoveWindowToDesktop(hWindow, ref newDesktop); } /// @@ -486,7 +490,7 @@ public class VirtualDesktopHelper } Guid newDesktop = _availableDesktops[windowDesktopNumber + 1]; - return MoveWindowToDesktop(hWindow, newDesktop); + return MoveWindowToDesktop(hWindow, ref newDesktop); } /// diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Microsoft.CmdPal.Ext.WindowWalker.csproj b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Microsoft.CmdPal.Ext.WindowWalker.csproj index f53237e632..4da6458258 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Microsoft.CmdPal.Ext.WindowWalker.csproj +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Microsoft.CmdPal.Ext.WindowWalker.csproj @@ -15,6 +15,7 @@ + diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Pages/WindowsSettingsListPage.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Pages/WindowsSettingsListPage.cs index 994db10445..3f5a6c6217 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Pages/WindowsSettingsListPage.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Pages/WindowsSettingsListPage.cs @@ -31,57 +31,81 @@ internal sealed partial class WindowsSettingsListPage : DynamicListPage } var filteredList = _windowsSettings.Settings - .Where(Predicate) - .OrderBy(found => found.Name); + .Select(SearchScoringPredicate) + .Where(scoredSetting => scoredSetting.Score > 0) + .OrderByDescending(scoredSetting => scoredSetting.Score) + .Select(scoredSetting => scoredSetting.Setting); var newList = ResultHelper.GetResultList(filteredList, query); return newList; - bool Predicate(WindowsSetting found) + // Rank settings by how they matched the search query. Order is: + // 1. Exact Name (10 points) + // 2. Name Starts With (8 points) + // 3. Name (5 points) + // 4. Area (4 points) + // 5. AltName (2 points) + // 6. Settings path (1 point) + (WindowsSetting Setting, int Score) SearchScoringPredicate(WindowsSetting setting) { if (string.IsNullOrWhiteSpace(query)) { // If no search string is entered skip query comparison. - return true; + return (setting, 0); } - if (found.Name.Contains(query, StringComparison.CurrentCultureIgnoreCase)) + if (string.Equals(setting.Name, query, StringComparison.OrdinalIgnoreCase)) { - return true; + return (setting, 10); } - if (!(found.Areas is null)) + if (setting.Name.StartsWith(query, StringComparison.CurrentCultureIgnoreCase)) { - foreach (var area in found.Areas) + return (setting, 8); + } + + if (setting.Name.Contains(query, StringComparison.CurrentCultureIgnoreCase)) + { + return (setting, 5); + } + + if (!(setting.Areas is null)) + { + foreach (var area in setting.Areas) { // Search for areas on normal queries. if (area.Contains(query, StringComparison.CurrentCultureIgnoreCase)) { - return true; + return (setting, 4); } // Search for Area only on queries with action char. if (area.Contains(query.Replace(":", string.Empty), StringComparison.CurrentCultureIgnoreCase) && query.EndsWith(":", StringComparison.CurrentCultureIgnoreCase)) { - return true; + return (setting, 4); } } } - if (!(found.AltNames is null)) + if (!(setting.AltNames is null)) { - foreach (var altName in found.AltNames) + foreach (var altName in setting.AltNames) { if (altName.Contains(query, StringComparison.CurrentCultureIgnoreCase)) { - return true; + return (setting, 2); } } } // Search by key char '>' for app name and settings path - return query.Contains('>') ? ResultHelper.FilterBySettingsPath(found, query) : false; + if (query.Contains('>') && ResultHelper.FilterBySettingsPath(setting, query)) + { + return (setting, 1); + } + + return (setting, 0); } } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Commands/LaunchProfileAsAdminCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Commands/LaunchProfileAsAdminCommand.cs index 39155115ca..1d1fcac873 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Commands/LaunchProfileAsAdminCommand.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Commands/LaunchProfileAsAdminCommand.cs @@ -3,18 +3,14 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Resources; -using System.Text; -using System.Threading.Tasks; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; using ManagedCommon; +using ManagedCsWin32; using Microsoft.CmdPal.Ext.WindowsTerminal.Helpers; using Microsoft.CmdPal.Ext.WindowsTerminal.Properties; -using Microsoft.CommandPalette.Extensions; using Microsoft.CommandPalette.Extensions.Toolkit; -using Windows.UI; namespace Microsoft.CmdPal.Ext.WindowsTerminal.Commands; @@ -68,7 +64,18 @@ internal sealed partial class LaunchProfileAsAdminCommand : InvokableCommand private void Launch(string id, string profile) { - var appManager = new ApplicationActivationManager(); + IApplicationActivationManager appManager; + + try + { + appManager = ComHelper.CreateComInstance(ref Unsafe.AsRef(in CLSID.ApplicationActivationManager), CLSCTX.InProcServer); + } + catch (Exception e) + { + Logger.LogError($"Failed to create IApplicationActivationManager instance. ex: {e.Message}"); + throw; + } + const ActivateOptions noFlags = ActivateOptions.None; var queryArguments = TerminalHelper.GetArguments(profile, _openNewTab, _openQuake); try diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Commands/LaunchProfileCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Commands/LaunchProfileCommand.cs index a879dc2410..9951ad3d68 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Commands/LaunchProfileCommand.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Commands/LaunchProfileCommand.cs @@ -3,18 +3,14 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Resources; -using System.Text; -using System.Threading.Tasks; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; using ManagedCommon; +using ManagedCsWin32; using Microsoft.CmdPal.Ext.WindowsTerminal.Helpers; using Microsoft.CmdPal.Ext.WindowsTerminal.Properties; -using Microsoft.CommandPalette.Extensions; using Microsoft.CommandPalette.Extensions.Toolkit; -using Windows.UI; namespace Microsoft.CmdPal.Ext.WindowsTerminal.Commands; @@ -38,7 +34,18 @@ internal sealed partial class LaunchProfileCommand : InvokableCommand private void Launch(string id, string profile) { - var appManager = new ApplicationActivationManager(); + IApplicationActivationManager appManager; + + try + { + appManager = ComHelper.CreateComInstance(ref Unsafe.AsRef(in CLSID.ApplicationActivationManager), CLSCTX.InProcServer); + } + catch (Exception e) + { + Logger.LogError($"Failed to create IApplicationActivationManager instance. ex: {e.Message}"); + throw; + } + const ActivateOptions noFlags = ActivateOptions.None; var queryArguments = TerminalHelper.GetArguments(profile, _openNewTab, _openQuake); try diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/ApplicationActivationManager.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/ApplicationActivationManager.cs deleted file mode 100644 index a074d9c86d..0000000000 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/ApplicationActivationManager.cs +++ /dev/null @@ -1,24 +0,0 @@ -// 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.WindowsTerminal.Helpers; - -// 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/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/IApplicationActivationManager.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/IApplicationActivationManager.cs index e332eee6fd..c023323d7f 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/IApplicationActivationManager.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/IApplicationActivationManager.cs @@ -4,6 +4,7 @@ using System; using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; namespace Microsoft.CmdPal.Ext.WindowsTerminal.Helpers; @@ -18,14 +19,13 @@ public enum ActivateOptions } // ApplicationActivationManager -[ComImport] +[GeneratedComInterface(StringMarshalling = StringMarshalling.Utf16)] [Guid("2e941141-7f97-4756-ba1d-9decde894a3d")] -[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] -public interface IApplicationActivationManager +public partial interface IApplicationActivationManager { - IntPtr ActivateApplication([In] string appUserModelId, [In] string arguments, [In] ActivateOptions options, [Out] out uint processId); + void ActivateApplication(string appUserModelId, string arguments, ActivateOptions options, out uint processId); - IntPtr ActivateForFile([In] string appUserModelId, [In] IntPtr /*IShellItemArray* */ itemArray, [In] string verb, [Out] out uint processId); + void ActivateForFile(string appUserModelId, IntPtr /*IShellItemArray* */ itemArray, string verb, out uint processId); - IntPtr ActivateForProtocol([In] string appUserModelId, [In] IntPtr /* IShellItemArray* */itemArray, [Out] out uint processId); + void ActivateForProtocol(string appUserModelId, IntPtr /* IShellItemArray* */itemArray, out uint processId); } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Microsoft.CmdPal.Ext.WindowsTerminal.csproj b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Microsoft.CmdPal.Ext.WindowsTerminal.csproj index e3ba9e6d46..bbd0a9530d 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Microsoft.CmdPal.Ext.WindowsTerminal.csproj +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Microsoft.CmdPal.Ext.WindowsTerminal.csproj @@ -17,6 +17,7 @@ + diff --git a/src/modules/cmdpal/ext/ProcessMonitorExtension/app.manifest b/src/modules/cmdpal/ext/ProcessMonitorExtension/app.manifest index a620209321..cf7490b1cb 100644 --- a/src/modules/cmdpal/ext/ProcessMonitorExtension/app.manifest +++ b/src/modules/cmdpal/ext/ProcessMonitorExtension/app.manifest @@ -13,6 +13,7 @@ + true/PM PerMonitorV2 diff --git a/src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleListPageWithDetails.cs b/src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleListPageWithDetails.cs index 9495a7f6bf..018c11720c 100644 --- a/src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleListPageWithDetails.cs +++ b/src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleListPageWithDetails.cs @@ -5,6 +5,7 @@ using System; using Microsoft.CommandPalette.Extensions; using Microsoft.CommandPalette.Extensions.Toolkit; +using Microsoft.UI.Xaml; namespace SamplePagesExtension; @@ -129,6 +130,25 @@ internal sealed partial class SampleListPageWithDetails : ListPage ], }, }, + new DetailsElement() + { + Key = "Commands", + Data = new DetailsCommands() + { + Commands = [ + new ToastCommand("Hey! You clicked it!", MessageState.Success) + { + Name = "Do something amazing", + Icon = new("\uE945"), + }, + new ToastCommand("I warned you!", MessageState.Error) + { + Name = "Don't click me", + Icon = new("\uEA39"), + }, + ], + }, + }, ], }, } diff --git a/src/modules/cmdpal/ext/SamplePagesExtension/app.manifest b/src/modules/cmdpal/ext/SamplePagesExtension/app.manifest index 6a7c7d68a7..13894ddf39 100644 --- a/src/modules/cmdpal/ext/SamplePagesExtension/app.manifest +++ b/src/modules/cmdpal/ext/SamplePagesExtension/app.manifest @@ -13,6 +13,7 @@ + true/PM PerMonitorV2 diff --git a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/DetailsCommand.cs b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/DetailsCommands.cs similarity index 70% rename from src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/DetailsCommand.cs rename to src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/DetailsCommands.cs index 0d08458ebf..32b11faad4 100644 --- a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/DetailsCommand.cs +++ b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/DetailsCommands.cs @@ -4,7 +4,7 @@ namespace Microsoft.CommandPalette.Extensions.Toolkit; -public partial class DetailsCommand : IDetailsCommand +public partial class DetailsCommands : IDetailsCommands { - public ICommand? Command { get; set; } + public ICommand[]? Commands { get; set; } } diff --git a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/ThumbnailHelper.cs b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/ThumbnailHelper.cs index cf7c5c2073..e06dbfcb22 100644 --- a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/ThumbnailHelper.cs +++ b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/ThumbnailHelper.cs @@ -123,7 +123,7 @@ public class ThumbnailHelper private static nint GetLargestIcon(string path) { var shinfo = default(NativeMethods.SHFILEINFO); - NativeMethods.SHGetFileInfo(path, 0, ref shinfo, (uint)Marshal.SizeOf(shinfo), SHGFI_ICON | SHGFI_SYSICONINDEX); + NativeMethods.SHGetFileInfo(path, 0, ref shinfo, (uint)Marshal.SizeOf(shinfo), SHGFI_SYSICONINDEX); var hIcon = IntPtr.Zero; var iID_IImageList = new Guid("46EB5926-582E-4017-9FDF-E8998DAA0950"); diff --git a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/Utilities.cs b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/Utilities.cs index ca88c11c0f..50e56bdefb 100644 --- a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/Utilities.cs +++ b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/Utilities.cs @@ -37,7 +37,7 @@ public class Utilities var FOLDERID_LocalAppData = new Guid("F1B32785-6FBA-4FCF-9D55-7B8E7F157091"); var hr = PInvoke.SHGetKnownFolderPath( FOLDERID_LocalAppData, - (uint)KNOWN_FOLDER_FLAG.KF_FLAG_FORCE_APP_DATA_REDIRECTION, + KNOWN_FOLDER_FLAG.KF_FLAG_FORCE_APP_DATA_REDIRECTION, null, out var localAppDataFolder); diff --git a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/Microsoft.CommandPalette.Extensions.idl b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/Microsoft.CommandPalette.Extensions.idl index df4b442cdf..3105b0647d 100644 --- a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/Microsoft.CommandPalette.Extensions.idl +++ b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/Microsoft.CommandPalette.Extensions.idl @@ -182,8 +182,8 @@ namespace Microsoft.CommandPalette.Extensions String Text { get; }; } [contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)] - interface IDetailsCommand requires IDetailsData { - ICommand Command { get; }; + interface IDetailsCommands requires IDetailsData { + ICommand[] Commands { get; }; } [uuid("58070392-02bb-4e89-9beb-47ceb8c3d741")] [contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)] diff --git a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/Microsoft.CommandPalette.Extensions.vcxproj b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/Microsoft.CommandPalette.Extensions.vcxproj index e7d8875326..e620524c77 100644 --- a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/Microsoft.CommandPalette.Extensions.vcxproj +++ b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/Microsoft.CommandPalette.Extensions.vcxproj @@ -1,10 +1,10 @@ - + ..\..\..\..\..\ - $(PathToRoot)packages\Microsoft.WindowsAppSDK.1.7.250401001 + $(PathToRoot)packages\Microsoft.WindowsAppSDK.1.7.250513003 $(PathToRoot)packages\Microsoft.Windows.CppWinRT.2.0.240111.5 - $(PathToRoot)packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428 + $(PathToRoot)packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188 $(PathToRoot)packages\Microsoft.Web.WebView2.1.0.2903.40 @@ -24,7 +24,7 @@ Windows Store 10.0 10.0.19041.0 - 10.0.22621.0 + 10.0.26100.0 diff --git a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/packages.config b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/packages.config index 608661db25..fc2bc5a5df 100644 --- a/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/packages.config +++ b/src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions/packages.config @@ -2,6 +2,6 @@ - - + + \ No newline at end of file diff --git a/src/modules/colorPicker/ColorPickerUI/App.manifest b/src/modules/colorPicker/ColorPickerUI/App.manifest index 94b792d3fe..c99f120d43 100644 --- a/src/modules/colorPicker/ColorPickerUI/App.manifest +++ b/src/modules/colorPicker/ColorPickerUI/App.manifest @@ -52,10 +52,8 @@ - true - - PerMonitor - + true/PM + PerMonitorV2 diff --git a/src/modules/colorPicker/ColorPickerUI/Helpers/AppStateHandler.cs b/src/modules/colorPicker/ColorPickerUI/Helpers/AppStateHandler.cs index b3350702c7..705324cb93 100644 --- a/src/modules/colorPicker/ColorPickerUI/Helpers/AppStateHandler.cs +++ b/src/modules/colorPicker/ColorPickerUI/Helpers/AppStateHandler.cs @@ -107,21 +107,14 @@ namespace ColorPicker.Helpers } } - public void OnColorPickerMouseDown() + public void OpenColorEditor() { - if (_userSettings.ActivationAction.Value == ColorPickerActivationAction.OpenColorPickerAndThenEditor || _userSettings.ActivationAction.Value == ColorPickerActivationAction.OpenEditor) + lock (_colorPickerVisibilityLock) { - lock (_colorPickerVisibilityLock) - { - HideColorPicker(); - } + HideColorPicker(); + } - ShowColorPickerEditor(); - } - else - { - EndUserSession(); - } + ShowColorPickerEditor(); } public static void SetTopMost() diff --git a/src/modules/colorPicker/ColorPickerUI/Mouse/IMouseInfoProvider.cs b/src/modules/colorPicker/ColorPickerUI/Mouse/IMouseInfoProvider.cs index 3361b95cb4..51bb35ac51 100644 --- a/src/modules/colorPicker/ColorPickerUI/Mouse/IMouseInfoProvider.cs +++ b/src/modules/colorPicker/ColorPickerUI/Mouse/IMouseInfoProvider.cs @@ -16,10 +16,12 @@ namespace ColorPicker.Mouse // position and bool indicating zoom in or zoom out event EventHandler> OnMouseWheel; - event MouseUpEventHandler OnMouseDown; + event PrimaryMouseDownEventHandler OnPrimaryMouseDown; event SecondaryMouseUpEventHandler OnSecondaryMouseUp; + event MiddleMouseDownEventHandler OnMiddleMouseDown; + System.Windows.Point CurrentPosition { get; } Color CurrentColor { get; } diff --git a/src/modules/colorPicker/ColorPickerUI/Mouse/MouseHook.cs b/src/modules/colorPicker/ColorPickerUI/Mouse/MouseHook.cs index 8476398c99..72fffd1445 100644 --- a/src/modules/colorPicker/ColorPickerUI/Mouse/MouseHook.cs +++ b/src/modules/colorPicker/ColorPickerUI/Mouse/MouseHook.cs @@ -7,17 +7,18 @@ using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows.Input; -using ColorPicker.Helpers; using ManagedCommon; using static ColorPicker.NativeMethods; namespace ColorPicker.Mouse { - public delegate void MouseUpEventHandler(object sender, System.Drawing.Point p); + public delegate void PrimaryMouseDownEventHandler(object sender, IntPtr wParam); public delegate void SecondaryMouseUpEventHandler(object sender, IntPtr wParam); + public delegate void MiddleMouseDownEventHandler(object sender, IntPtr wParam); + internal class MouseHook { [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "Interop object")] @@ -30,23 +31,25 @@ namespace ColorPicker.Mouse private const int WM_RBUTTONUP = 0x0205; [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "Interop object")] private const int WM_RBUTTONDOWN = 0x0204; + [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "Interop object")] + private const int WM_MBUTTONDOWN = 0x0207; private IntPtr _mouseHookHandle; private HookProc _mouseDelegate; - private event MouseUpEventHandler MouseDown; + private event PrimaryMouseDownEventHandler PrimaryMouseDown; - public event MouseUpEventHandler OnMouseDown + public event PrimaryMouseDownEventHandler OnPrimaryMouseDown { add { Subscribe(); - MouseDown += value; + PrimaryMouseDown += value; } remove { - MouseDown -= value; + PrimaryMouseDown -= value; Unsubscribe(); } } @@ -68,6 +71,23 @@ namespace ColorPicker.Mouse } } + private event MiddleMouseDownEventHandler MiddleMouseDown; + + public event MiddleMouseDownEventHandler OnMiddleMouseDown + { + add + { + Subscribe(); + MiddleMouseDown += value; + } + + remove + { + MiddleMouseDown -= value; + Unsubscribe(); + } + } + private event MouseWheelEventHandler MouseWheel; public event MouseWheelEventHandler OnMouseWheel @@ -126,9 +146,9 @@ namespace ColorPicker.Mouse MSLLHOOKSTRUCT mouseHookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT)); if (wParam.ToInt32() == WM_LBUTTONDOWN) { - if (MouseDown != null) + if (PrimaryMouseDown != null) { - MouseDown.Invoke(null, new System.Drawing.Point(mouseHookStruct.pt.x, mouseHookStruct.pt.y)); + PrimaryMouseDown.Invoke(null, wParam); } return new IntPtr(-1); @@ -150,6 +170,16 @@ namespace ColorPicker.Mouse return new IntPtr(-1); } + if (wParam.ToInt32() == WM_MBUTTONDOWN) + { + if (MiddleMouseDown != null) + { + MiddleMouseDown.Invoke(null, wParam); + } + + return new IntPtr(-1); + } + if (wParam.ToInt32() == WM_MOUSEWHEEL) { if (MouseWheel != null) diff --git a/src/modules/colorPicker/ColorPickerUI/Mouse/MouseInfoProvider.cs b/src/modules/colorPicker/ColorPickerUI/Mouse/MouseInfoProvider.cs index c28dcc1ae6..4d6596bc3f 100644 --- a/src/modules/colorPicker/ColorPickerUI/Mouse/MouseInfoProvider.cs +++ b/src/modules/colorPicker/ColorPickerUI/Mouse/MouseInfoProvider.cs @@ -56,10 +56,12 @@ namespace ColorPicker.Mouse public event EventHandler> OnMouseWheel; - public event MouseUpEventHandler OnMouseDown; + public event PrimaryMouseDownEventHandler OnPrimaryMouseDown; public event SecondaryMouseUpEventHandler OnSecondaryMouseUp; + public event MiddleMouseDownEventHandler OnMiddleMouseDown; + public System.Windows.Point CurrentPosition { get @@ -148,9 +150,10 @@ namespace ColorPicker.Mouse _timer.Start(); } - _mouseHook.OnMouseDown += MouseHook_OnMouseDown; + _mouseHook.OnPrimaryMouseDown += MouseHook_OnPrimaryMouseDown; _mouseHook.OnMouseWheel += MouseHook_OnMouseWheel; _mouseHook.OnSecondaryMouseUp += MouseHook_OnSecondaryMouseUp; + _mouseHook.OnMiddleMouseDown += MouseHook_OnMiddleMouseDown; if (_userSettings.ChangeCursor.Value) { @@ -169,10 +172,10 @@ namespace ColorPicker.Mouse OnMouseWheel?.Invoke(this, new Tuple(_previousMousePosition, zoomIn)); } - private void MouseHook_OnMouseDown(object sender, Point p) + private void MouseHook_OnPrimaryMouseDown(object sender, IntPtr wParam) { DisposeHook(); - OnMouseDown?.Invoke(this, p); + OnPrimaryMouseDown?.Invoke(this, wParam); } private void MouseHook_OnSecondaryMouseUp(object sender, IntPtr wParam) @@ -181,6 +184,12 @@ namespace ColorPicker.Mouse OnSecondaryMouseUp?.Invoke(this, wParam); } + private void MouseHook_OnMiddleMouseDown(object sender, IntPtr wParam) + { + DisposeHook(); + OnMiddleMouseDown?.Invoke(this, wParam); + } + private void CopiedColorRepresentation_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { _colorFormatChanged = true; @@ -194,9 +203,10 @@ namespace ColorPicker.Mouse } _previousMousePosition = new System.Windows.Point(-1, 1); - _mouseHook.OnMouseDown -= MouseHook_OnMouseDown; + _mouseHook.OnPrimaryMouseDown -= MouseHook_OnPrimaryMouseDown; _mouseHook.OnMouseWheel -= MouseHook_OnMouseWheel; _mouseHook.OnSecondaryMouseUp -= MouseHook_OnSecondaryMouseUp; + _mouseHook.OnMiddleMouseDown -= MouseHook_OnMiddleMouseDown; if (_userSettings.ChangeCursor.Value) { diff --git a/src/modules/colorPicker/ColorPickerUI/Settings/IUserSettings.cs b/src/modules/colorPicker/ColorPickerUI/Settings/IUserSettings.cs index ab92e64081..170b58c944 100644 --- a/src/modules/colorPicker/ColorPickerUI/Settings/IUserSettings.cs +++ b/src/modules/colorPicker/ColorPickerUI/Settings/IUserSettings.cs @@ -21,6 +21,12 @@ namespace ColorPicker.Settings SettingItem ActivationAction { get; } + SettingItem PrimaryClickAction { get; } + + SettingItem MiddleClickAction { get; } + + SettingItem SecondaryClickAction { get; } + RangeObservableCollection ColorHistory { get; } SettingItem ColorHistoryLimit { get; } diff --git a/src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs b/src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs index eb6a9db7aa..e15a792259 100644 --- a/src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs +++ b/src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs @@ -49,7 +49,10 @@ namespace ColorPicker.Settings ChangeCursor = new SettingItem(true); ActivationShortcut = new SettingItem(DefaultActivationShortcut); CopiedColorRepresentation = new SettingItem(ColorRepresentationType.HEX.ToString()); - ActivationAction = new SettingItem(ColorPickerActivationAction.OpenEditor); + ActivationAction = new SettingItem(ColorPickerActivationAction.OpenColorPicker); + PrimaryClickAction = new SettingItem(ColorPickerClickAction.PickColorThenEditor); + MiddleClickAction = new SettingItem(ColorPickerClickAction.PickColorAndClose); + SecondaryClickAction = new SettingItem(ColorPickerClickAction.Close); ColorHistoryLimit = new SettingItem(20); ColorHistory.CollectionChanged += ColorHistory_CollectionChanged; ShowColorName = new SettingItem(false); @@ -78,6 +81,12 @@ namespace ColorPicker.Settings public SettingItem ActivationAction { get; private set; } + public SettingItem PrimaryClickAction { get; private set; } + + public SettingItem MiddleClickAction { get; private set; } + + public SettingItem SecondaryClickAction { get; private set; } + public RangeObservableCollection ColorHistory { get; private set; } = new RangeObservableCollection(); public SettingItem ColorHistoryLimit { get; } @@ -121,6 +130,9 @@ namespace ColorPicker.Settings CopiedColorRepresentation.Value = settings.Properties.CopiedColorRepresentation; CopiedColorRepresentationFormat = new SettingItem(string.Empty); ActivationAction.Value = settings.Properties.ActivationAction; + PrimaryClickAction.Value = settings.Properties.PrimaryClickAction; + MiddleClickAction.Value = settings.Properties.MiddleClickAction; + SecondaryClickAction.Value = settings.Properties.SecondaryClickAction; ColorHistoryLimit.Value = settings.Properties.ColorHistoryLimit; ShowColorName.Value = settings.Properties.ShowColorName; diff --git a/src/modules/colorPicker/ColorPickerUI/ViewModels/MainViewModel.cs b/src/modules/colorPicker/ColorPickerUI/ViewModels/MainViewModel.cs index 518f253456..e4d0a54b44 100644 --- a/src/modules/colorPicker/ColorPickerUI/ViewModels/MainViewModel.cs +++ b/src/modules/colorPicker/ColorPickerUI/ViewModels/MainViewModel.cs @@ -16,6 +16,7 @@ using ColorPicker.Settings; using ColorPicker.ViewModelContracts; using Common.UI; using ManagedCommon; +using Microsoft.PowerToys.Settings.UI.Library.Enumerations; using PowerToys.Interop; namespace ColorPicker.ViewModels @@ -79,9 +80,10 @@ namespace ColorPicker.ViewModels { SetColorDetails(mouseInfoProvider.CurrentColor); mouseInfoProvider.MouseColorChanged += Mouse_ColorChanged; - mouseInfoProvider.OnMouseDown += MouseInfoProvider_OnMouseDown; + mouseInfoProvider.OnPrimaryMouseDown += MouseInfoProvider_OnPrimaryMouseDown; mouseInfoProvider.OnMouseWheel += MouseInfoProvider_OnMouseWheel; mouseInfoProvider.OnSecondaryMouseUp += MouseInfoProvider_OnSecondaryMouseUp; + mouseInfoProvider.OnMiddleMouseDown += MouseInfoProvider_OnMiddleMouseDown; } _userSettings.ShowColorName.PropertyChanged += (s, e) => { OnPropertyChanged(nameof(ShowColorName)); }; @@ -113,7 +115,7 @@ namespace ColorPicker.ViewModels private void AppStateHandler_EnterPressed(object sender, EventArgs e) { - MouseInfoProvider_OnMouseDown(null, default(System.Drawing.Point)); + MouseInfoProvider_OnPrimaryMouseDown(null, default); } /// @@ -167,18 +169,50 @@ namespace ColorPicker.ViewModels SetColorDetails(color); } - /// - /// Tell the color picker that the user have press a mouse button (after release the button) - /// - /// The sender of this event - /// The current of the mouse cursor - private void MouseInfoProvider_OnMouseDown(object sender, System.Drawing.Point p) + private void MouseInfoProvider_OnPrimaryMouseDown(object sender, IntPtr wParam) { - ClipboardHelper.CopyToClipboard(ColorText); + HandleMouseClickAction(_userSettings.PrimaryClickAction.Value); + } - var color = GetColorString(); + private void MouseInfoProvider_OnMiddleMouseDown(object sender, IntPtr wParam) + { + HandleMouseClickAction(_userSettings.MiddleClickAction.Value); + } - var oldIndex = _userSettings.ColorHistory.IndexOf(color); + private void MouseInfoProvider_OnSecondaryMouseUp(object sender, IntPtr wParam) + { + HandleMouseClickAction(_userSettings.SecondaryClickAction.Value); + } + + private void HandleMouseClickAction(ColorPickerClickAction action) + { + switch (action) + { + case ColorPickerClickAction.PickColorThenEditor: + ClipboardHelper.CopyToClipboard(ColorText); + UpdateColorHistory(GetColorString()); + + _appStateHandler.OpenColorEditor(); + + break; + + case ColorPickerClickAction.PickColorAndClose: + ClipboardHelper.CopyToClipboard(ColorText); + UpdateColorHistory(GetColorString()); + + _appStateHandler.EndUserSession(); + + break; + + case ColorPickerClickAction.Close: + _appStateHandler.EndUserSession(); + break; + } + } + + private void UpdateColorHistory(string color) + { + int oldIndex = _userSettings.ColorHistory.IndexOf(color); if (oldIndex != -1) { _userSettings.ColorHistory.Move(oldIndex, 0); @@ -192,13 +226,6 @@ namespace ColorPicker.ViewModels { _userSettings.ColorHistory.RemoveAt(_userSettings.ColorHistory.Count - 1); } - - _appStateHandler.OnColorPickerMouseDown(); - } - - private void MouseInfoProvider_OnSecondaryMouseUp(object sender, IntPtr wParam) - { - _appStateHandler.EndUserSession(); } private string GetColorString() diff --git a/src/modules/fancyzones/FancyZones.FuzzTests/FancyZones.FuzzTests.csproj b/src/modules/fancyzones/FancyZones.FuzzTests/FancyZones.FuzzTests.csproj new file mode 100644 index 0000000000..9890b67217 --- /dev/null +++ b/src/modules/fancyzones/FancyZones.FuzzTests/FancyZones.FuzzTests.csproj @@ -0,0 +1,36 @@ + + + + + + + + ..\..\..\..\$(Platform)\$(Configuration)\tests\FancyZones.FuzzTests\ + + + + + + + + + + + + + + + + + + + + + + + + PreserveNewest + + + + diff --git a/src/modules/fancyzones/FancyZones.FuzzTests/FuzzTests.cs b/src/modules/fancyzones/FancyZones.FuzzTests/FuzzTests.cs new file mode 100644 index 0000000000..3bc846089f --- /dev/null +++ b/src/modules/fancyzones/FancyZones.FuzzTests/FuzzTests.cs @@ -0,0 +1,86 @@ +// 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.Text.Json; +using FancyZonesEditorCommon.Data; +using FancyZonesEditorCommon.Utils; +using static FancyZonesEditorCommon.Data.CustomLayouts; + +namespace FancyZones.FuzzTests +{ + public class FuzzTests + { + public static void FuzzGridFromJsonElement(ReadOnlySpan input) + { + if (input.Length < 4) + { + return; + } + + int inputData = BitConverter.ToInt32(input.Slice(0, 4)); + + // mock user input for custom-layouts.json + string mockCustomLayouts = $@"{{""custom-layouts"": [{{ + ""uuid"": ""{{B8C275E-A7BC-485F-A35C-67B69164F51F}}"", + ""name"": ""Custom layout 1"", + ""type"": ""grid"", + ""info"": {{ + ""rows"": {inputData}, + ""columns"": {inputData}, + ""rows-percentage"": [ {inputData} ], + ""columns-percentage"": [ {inputData}, {inputData}, {inputData} ], + ""cell-child-map"": [ [{inputData}, {inputData}, {inputData}] ], + ""show-spacing"": true, + ""spacing"": {inputData}, + ""sensitivity-radius"": {inputData} + }} + }}]}}"; + + CustomLayoutListWrapper wrapper; + try + { + wrapper = JsonSerializer.Deserialize(mockCustomLayouts, JsonOptions); + } + catch (JsonException) + { + return; + } + + List customLayouts = wrapper.CustomLayouts; + + if (customLayouts == null) + { + return; + } + + // Get Layout Info from mockCustomLayouts + foreach (var zoneSet in customLayouts) + { + if (zoneSet.Uuid == null || zoneSet.Uuid.Length == 0) + { + return; + } + + CustomLayouts deserializer = new CustomLayouts(); + + // Fuzzing the deserializer + _ = deserializer.GridFromJsonElement(zoneSet.Info.GetRawText()); + } + } + + private static JsonSerializerOptions JsonOptions + { + get + { + return new JsonSerializerOptions + { + PropertyNamingPolicy = new DashCaseNamingPolicy(), + WriteIndented = true, + }; + } + } + } +} diff --git a/src/modules/fancyzones/FancyZones.FuzzTests/MSTestSettings.cs b/src/modules/fancyzones/FancyZones.FuzzTests/MSTestSettings.cs new file mode 100644 index 0000000000..5b05c0b86e --- /dev/null +++ b/src/modules/fancyzones/FancyZones.FuzzTests/MSTestSettings.cs @@ -0,0 +1,5 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)] diff --git a/src/modules/fancyzones/FancyZones.FuzzTests/OneFuzzConfig.json b/src/modules/fancyzones/FancyZones.FuzzTests/OneFuzzConfig.json new file mode 100644 index 0000000000..9bbb66816a --- /dev/null +++ b/src/modules/fancyzones/FancyZones.FuzzTests/OneFuzzConfig.json @@ -0,0 +1,51 @@ +{ + "configVersion": 3, + "entries": [ + { + "fuzzer": { + "$type": "libfuzzerDotNet", + "dll": "FancyZones.FuzzTests.dll", + "class": "FancyZones.FuzzTests.FuzzTests", + "method": "FuzzGridFromJsonElement", + "FuzzingTargetBinaries": [ + "PowerToys.FancyZones.dll" + ] + }, + "adoTemplate": { + // supply the values appropriate to your + // project, where bugs will be filed + "org": "microsoft", + "project": "OS", + "AssignedTo": "mengyuanchen@microsoft.com", + "AreaPath": "OS\\Windows Client and Services\\WinPD\\DFX-Developer Fundamentals and Experiences\\DEFT\\SALT", + "IterationPath": "OS\\Future" + }, + "jobNotificationEmail": "PowerToys@microsoft.com", + "skip": false, + "rebootAfterSetup": false, + "oneFuzzJobs": [ + // at least one job is required + { + "projectName": "FancyZones", + "targetName": "FancyZones-dotnet-fuzzer-FuzzGridFromJsonElement" + } + ], + "jobDependencies": [ + // this should contain, at minimum, + // the DLL and PDB files + // you will need to add any other files required + // (globs are supported) + "FancyZones.FuzzTests.dll", + "FancyZones.FuzzTests.pdb", + "Microsoft.Windows.SDK.NET.dll", + "Newtonsoft.Json.dll", + "System.IO.Abstractions.dll", + "Testably.Abstractions.FileSystem.Interface.dll", + "TestableIO.System.IO.Abstractions.dll", + "TestableIO.System.IO.Abstractions.Wrappers.dll", + "WinRT.Runtime.dll" + ], + "SdlWorkItemId": 49911822 + } + ] +} \ No newline at end of file diff --git a/src/modules/fancyzones/FancyZonesLib/WorkArea.cpp b/src/modules/fancyzones/FancyZonesLib/WorkArea.cpp index 638776c25a..a2cae59ca8 100644 --- a/src/modules/fancyzones/FancyZonesLib/WorkArea.cpp +++ b/src/modules/fancyzones/FancyZonesLib/WorkArea.cpp @@ -23,6 +23,7 @@ namespace NonLocalizable { const wchar_t ToolWindowClassName[] = L"FancyZones_ZonesOverlay"; + const wchar_t ToolWindowName[] = L"FancyZones_ZonesOverlay"; } using namespace FancyZonesUtils; @@ -59,7 +60,7 @@ namespace HWND windowFromPool = ExtractWindow(); if (windowFromPool == NULL) { - HWND window = CreateWindowExW(WS_EX_TOOLWINDOW, NonLocalizable::ToolWindowClassName, L"", WS_POPUP, position.left(), position.top(), position.width(), position.height(), nullptr, nullptr, hinstance, owner); + HWND window = CreateWindowExW(WS_EX_TOOLWINDOW, NonLocalizable::ToolWindowClassName, NonLocalizable::ToolWindowName, WS_POPUP, position.left(), position.top(), position.width(), position.height(), nullptr, nullptr, hinstance, owner); Logger::info("Creating new ZonesOverlay window, hWnd = {}", (void*)window); FancyZonesWindowUtils::MakeWindowTransparent(window); diff --git a/src/modules/fancyzones/UITests-FancyZones/DragWindowTests.cs b/src/modules/fancyzones/UITests-FancyZones/DragWindowTests.cs new file mode 100644 index 0000000000..61d88e2121 --- /dev/null +++ b/src/modules/fancyzones/UITests-FancyZones/DragWindowTests.cs @@ -0,0 +1,673 @@ +// 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 System.Windows.Forms; +using FancyZonesEditor.Models; +using FancyZonesEditorCommon.Data; +using Microsoft.FancyZones.UITests.Utils; +using Microsoft.FancyZonesEditor.UITests.Utils; +using Microsoft.FancyZonesEditor.UnitTests.Utils; +using Microsoft.PowerToys.UITest; +using Microsoft.VisualBasic.Logging; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using OpenQA.Selenium.Appium; +using OpenQA.Selenium.Appium.Windows; +using static FancyZonesEditorCommon.Data.CustomLayouts; + +namespace UITests_FancyZones +{ + [TestClass] + public class DragWindowTests : UITestBase + { + private static readonly IOTestHelper AppZoneHistory = new FancyZonesEditorFiles().AppZoneHistoryIOHelper; + private static string nonPrimaryMouseButton = "Right"; + + private static string highlightColor = "#008CFF"; // set highlight color + private static string inactivateColor = "#AACDFF"; // set inactivate zone color + + // set screen margin + private static int screenMarginTop; + private static int screenMarginLeft; + private static int screenMarginRight; + private static int screenMarginBottom; + + // set 1/4 margin + private static int quarterX; + private static int quarterY; + + private static string powertoysWindowName = "PowerToys Settings"; // set powertoys settings window name + + public DragWindowTests() + : base(PowerToysModule.PowerToysSettings, WindowSize.Medium) + { + } + + [TestInitialize] + public void TestInitialize() + { + // ClearOpenWindows + ClearOpenWindows(); + + // kill all processes related to FancyZones Editor to ensure a clean state + Session.KillAllProcessesByName("PowerToys.FancyZonesEditor"); + + AppZoneHistory.DeleteFile(); + this.RestartScopeExe(); + FancyZonesEditorHelper.Files.Restore(); + + // Set a custom layout with 1 subzones and clear app zone history + SetupCustomLayouts(); + + // Get the current mouse button setting + nonPrimaryMouseButton = SystemInformation.MouseButtonsSwapped ? "Left" : "Right"; + + // get PowerToys window Name + powertoysWindowName = ZoneSwitchHelper.GetActiveWindowTitle(); + + // Ensure FancyZones settings page is visible and enable FancyZones + LaunchFancyZones(); + } + + /// + /// Test Use Shift key to activate zones while dragging a window in FancyZones Zone Behaviour Settings + /// + /// + /// Verifies that holding Shift while dragging shows all zones as expected. + /// + /// + /// + [TestMethod("FancyZones.Settings.TestShowZonesOnShiftDuringDrag")] + [TestCategory("FancyZones_Dragging #1")] + public void TestShowZonesOnShiftDuringDrag() + { + string testCaseName = nameof(TestShowZonesOnShiftDuringDrag); + Pane dragElement = Find(By.Name("Non Client Input Sink Window")); // element to drag + var offSet = ZoneSwitchHelper.GetOffset(dragElement, quarterX, quarterY); + + var (initialColor, withShiftColor) = RunDragInteractions( + preAction: () => + { + dragElement.DragAndHold(offSet.Dx, offSet.Dy); + }, + postAction: () => + { + Session.PressKey(Key.Shift); + Task.Delay(500).Wait(); + }, + releaseAction: () => + { + Session.ReleaseKey(Key.Shift); + Task.Delay(5000).Wait(); // Optional: Wait for a moment to ensure window switch + }, + testCaseName: testCaseName); + + string zoneColorWithoutShift = GetOutWindowPixelColor(30); + + Assert.AreNotEqual(initialColor, withShiftColor, $"[{testCaseName}] Zone display failed."); + Assert.IsTrue( + withShiftColor == inactivateColor || withShiftColor == highlightColor, + $"[{testCaseName}] Zone display failed: withShiftColor was {withShiftColor}, expected {inactivateColor} or {highlightColor}."); + Assert.AreEqual(inactivateColor, withShiftColor, $"[{testCaseName}] Zone display failed."); + + Assert.AreEqual(zoneColorWithoutShift, initialColor, $"[{testCaseName}] Zone deactivated failed."); + dragElement.ReleaseDrag(); + + Clean(); + } + + /// + /// Test dragging a window during Shift key press in FancyZones Zone Behaviour Settings + /// + /// + /// Verifies that dragging activates zones as expected. + /// + /// + /// + [TestMethod("FancyZones.Settings.TestShowZonesOnDragDuringShift")] + [TestCategory("FancyZones_Dragging #2")] + public void TestShowZonesOnDragDuringShift() + { + string testCaseName = nameof(TestShowZonesOnDragDuringShift); + + var dragElement = Find(By.Name("Non Client Input Sink Window")); + var offSet = ZoneSwitchHelper.GetOffset(dragElement, quarterX, quarterY); + + var (initialColor, withDragColor) = RunDragInteractions( + preAction: () => + { + dragElement.Drag(offSet.Dx, offSet.Dy); + Session.PressKey(Key.Shift); + }, + postAction: () => + { + dragElement.DragAndHold(0, 0); + Task.Delay(5000).Wait(); + }, + releaseAction: () => + { + dragElement.ReleaseDrag(); + Session.ReleaseKey(Key.Shift); + }, + testCaseName: testCaseName); + + Assert.AreNotEqual(initialColor, withDragColor, $"[{testCaseName}] Zone color did not change; zone activation failed."); + Assert.AreEqual(highlightColor, withDragColor, $"[{testCaseName}] Zone color did not match the highlight color; activation failed."); + + // double check by app-zone-history.json + string appZoneHistoryJson = AppZoneHistory.GetData(); + string? zoneNumber = ZoneSwitchHelper.GetZoneIndexSetByAppName(powertoysWindowName, appZoneHistoryJson); + Assert.IsNull(zoneNumber, $"[{testCaseName}] AppZoneHistory layout was unexpectedly set."); + + Clean(); + } + + /// + /// Test toggling zones using a non-primary mouse click during window dragging. + /// + /// + /// Verifies that clicking a non-primary mouse button deactivates zones while dragging a window. + /// + /// + /// + [TestMethod("FancyZones.Settings.TestToggleZonesWithNonPrimaryMouseClick")] + [TestCategory("FancyZones_Dragging #3")] + public void TestToggleZonesWithNonPrimaryMouseClick() + { + string testCaseName = nameof(TestToggleZonesWithNonPrimaryMouseClick); + var dragElement = Find(By.Name("Non Client Input Sink Window")); + var offSet = ZoneSwitchHelper.GetOffset(dragElement, quarterX, quarterY); + + var (initialColor, withMouseColor) = RunDragInteractions( + preAction: () => + { + // activate zone + dragElement.DragAndHold(offSet.Dx, offSet.Dy); + }, + postAction: () => + { + // press non-primary mouse button to toggle zones + Session.PerformMouseAction( + nonPrimaryMouseButton == "Right" ? MouseActionType.RightClick : MouseActionType.LeftClick); + }, + releaseAction: () => + { + dragElement.ReleaseDrag(); + }, + testCaseName: testCaseName); + + // check the zone color is deactivated + Assert.AreNotEqual(highlightColor, withMouseColor, $"[{testCaseName}] Zone deactivation failed."); + + // check the zone color is activated + Assert.AreEqual(highlightColor, initialColor, $"[{testCaseName}] Zone activation failed."); + + Clean(); + } + + /// + /// Test both use Shift and non primary mouse off settings. + /// + /// + /// Verifies that pressing the Shift key deactivates zones during a window drag-and-hold action. + /// + /// + /// + [TestMethod("FancyZones.Settings.TestShowZonesWhenShiftAndMouseOff")] + [TestCategory("FancyZones_Dragging #4")] + public void TestShowZonesWhenShiftAndMouseOff() + { + string testCaseName = nameof(TestShowZonesWhenShiftAndMouseOff); + Pane dragElement = Find(By.Name("Non Client Input Sink Window")); + var offSet = ZoneSwitchHelper.GetOffset(dragElement, quarterX, quarterY); + + var (initialColor, withShiftColor) = RunDragInteractions( + preAction: () => + { + // activate zone + dragElement.DragAndHold(offSet.Dx, offSet.Dy); + }, + postAction: () => + { + // press Shift Key to deactivate zones + Session.PressKey(Key.Shift); + Task.Delay(500).Wait(); + }, + releaseAction: () => + { + dragElement.ReleaseDrag(); + Session.ReleaseKey(Key.Shift); + }, + testCaseName: testCaseName); + + Assert.AreEqual(highlightColor, initialColor, $"[{testCaseName}] Zone activation failed."); + Assert.AreNotEqual(highlightColor, withShiftColor, $"[{testCaseName}] Zone deactivation failed."); + + Clean(); + } + + /// + /// Test zone visibility when both Shift key and mouse settings are involved. + /// + /// + /// Verifies that zones are activated when Shift is pressed during drag, and deactivated by a non-primary mouse click. + /// + /// + /// + [TestMethod("FancyZones.Settings.TestShowZonesWhenShiftAndMouseOn")] + [TestCategory("FancyZones_Dragging #5")] + public void TestShowZonesWhenShiftAndMouseOn() + { + string testCaseName = nameof(TestShowZonesWhenShiftAndMouseOn); + + var dragElement = Find(By.Name("Non Client Input Sink Window")); + var offSet = ZoneSwitchHelper.GetOffset(dragElement, quarterX, quarterY); + var (initialColor, withShiftColor) = RunDragInteractions( + preAction: () => + { + dragElement.DragAndHold(offSet.Dx, offSet.Dy); + }, + postAction: () => + { + Session.PressKey(Key.Shift); + }, + releaseAction: () => + { + }, + testCaseName: testCaseName); + + Assert.AreEqual(inactivateColor, withShiftColor, $"[{testCaseName}] show zone failed."); + + Session.PerformMouseAction( + nonPrimaryMouseButton == "Right" ? MouseActionType.RightClick : MouseActionType.LeftClick); + + string zoneColorWithMouse = GetOutWindowPixelColor(30); + Assert.AreEqual(initialColor, zoneColorWithMouse, $"[{nameof(TestShowZonesWhenShiftAndMouseOff)}] Zone deactivate failed."); + + Session.ReleaseKey(Key.Shift); + dragElement.ReleaseDrag(); + + Clean(); + } + + /// + /// Test that a window becomes transparent during dragging when the transparent window setting is enabled. + /// + /// + /// Verifies that the window appears transparent while being dragged. + /// + /// + /// + [TestMethod("FancyZones.Settings.TestMakeDraggedWindowTransparentOn")] + [TestCategory("FancyZones_Dragging #8")] + public void TestMakeDraggedWindowTransparentOn() + { + var pixel = GetPixelWhenMakeDraggedWindow(); + Assert.AreNotEqual(pixel.PixelInWindow, pixel.TransPixel, $"[{nameof(TestMakeDraggedWindowTransparentOn)}] Window transparency failed."); + + Clean(); + } + + /// + /// Test that a window remains opaque during dragging when the transparent window setting is disabled. + /// + /// + /// Verifies that the window is not transparent while being dragged. + /// + /// + /// + [TestMethod("FancyZones.Settings.TestMakeDraggedWindowTransparentOff")] + [TestCategory("FancyZones_Dragging #8")] + public void TestMakeDraggedWindowTransparentOff() + { + var pixel = GetPixelWhenMakeDraggedWindow(); + Assert.AreEqual(pixel.PixelInWindow, pixel.TransPixel, $"[{nameof(TestMakeDraggedWindowTransparentOff)}] Window without transparency failed."); + + Clean(); + } + + private void Clean() + { + // clean app zone history file + AppZoneHistory.DeleteFile(); + } + + // Helper method to ensure the desktop has no open windows by clicking the "Show Desktop" button + private void ClearOpenWindows() + { + string desktopButtonName; + + // Check for both possible button names (Win10/Win11) + if (this.FindAll("Show Desktop", 5000, true).Count == 0) + { + // win10 + desktopButtonName = "Show desktop"; + } + else + { + // win11 + desktopButtonName = "Show Desktop"; + } + + this.Find(By.Name(desktopButtonName), 5000, true).Click(false, 500, 2000); + } + + // Setup custom layout with 1 subzones + private void SetupCustomLayouts() + { + var customLayouts = new CustomLayouts(); + var customLayoutListWrapper = CustomLayoutsList; + + if (TestContext.TestName == "TestMakeDraggedWindowTransparentOff") + { + customLayoutListWrapper = CustomLayoutsListWithTwo; + } + + FancyZonesEditorHelper.Files.CustomLayoutsIOHelper.WriteData(customLayouts.Serialize(customLayoutListWrapper)); + } + + // launch FancyZones settings page + private void LaunchFancyZones() + { + if (this.FindAll("FancyZones").Count == 0) + { + this.Find("Windowing & Layouts").Click(); + } + + this.Find("FancyZones").Click(); + this.Find("Enable FancyZones").Toggle(true); + + this.Session.SetMainWindowSize(WindowSize.Large); + Find(By.AccessibilityId("HeaderPresenter")).Click(); + this.Scroll(6, "Down"); // Pull the settings page up to make sure the settings are visible + ZoneBehaviourSettings(TestContext.TestName); + + this.Find("Launch layout editor").Click(false, 500, 10000); + this.Session.Attach(PowerToysModule.FancyZone); + + // pipeline machine may have an unstable delays, causing the custom layout to be unavailable as we set. then A retry is required. + // Console.WriteLine($"after launch, Custom layout data: {customLayoutData}"); + try + { + this.Find("Maximize").Click(); + + // Set the FancyZones layout to a custom layout + this.Find(By.Name("Custom Column")).Click(); + } + catch (Exception) + { + // Console.WriteLine($"[Exception] Failed to attach to FancyZones window. Retrying...{ex.Message}"); + this.Find("Close").Click(); + this.Session.Attach(PowerToysModule.PowerToysSettings); + SetupCustomLayouts(); + this.Find("Launch layout editor").Click(false, 5000, 5000); + this.Session.Attach(PowerToysModule.FancyZone); + this.Find("Maximize").Click(); + + // customLayoutData = FancyZonesEditorHelper.Files.CustomLayoutsIOHelper.GetData(); + // Console.WriteLine($"after retry, Custom layout data: {customLayoutData}"); + + // Set the FancyZones layout to a custom layout + this.Find(By.Name("Custom Column")).Click(); + } + + // Get screen margins for positioning checks + GetScreenMargins(); + + // Close layout editor window + SendKeys(Key.Alt, Key.F4); + + // make window small to detect zone easily + Session.Attach(powertoysWindowName, WindowSize.Small); + } + + // Get the screen margins to calculate the dragged window position + private void GetScreenMargins() + { + var rect = Session.GetMainWindowRect(); + screenMarginTop = rect.Top; + screenMarginLeft = rect.Left; + screenMarginRight = rect.Right; + screenMarginBottom = rect.Bottom; + (quarterX, quarterY) = ZoneSwitchHelper.GetScreenMargins(rect, 4); + } + + // Get the mouse color of the pixel when make dragged window + private (string PixelInWindow, string TransPixel) GetPixelWhenMakeDraggedWindow() + { + var dragElement = Find(By.Name("Non Client Input Sink Window")); + + // maximize the window to make sure get pixel color more accurate + dragElement.DoubleClick(); + + var offSet = ZoneSwitchHelper.GetOffset(dragElement, quarterX, quarterY); + Session.PressKey(Key.Shift); + dragElement.DragAndHold(offSet.Dx, offSet.Dy); + Task.Delay(1000).Wait(); // Optional: Wait for a moment to ensure the window is in position + Tuple pos = GetMousePosition(); + string pixelInWindow = this.GetPixelColorString(pos.Item1, pos.Item2); + Session.ReleaseKey(Key.Shift); + Task.Delay(1000).Wait(); // Optional: Wait for a moment to ensure the window is in position + string transPixel = this.GetPixelColorString(pos.Item1, pos.Item2); + dragElement.ReleaseDrag(); + + return (pixelInWindow, transPixel); + } + + /// + /// Gets the color of a pixel located just outside the application's window. + /// + /// + /// The minimum spacing (in pixels) required between the window edge and screen margin + /// to determine a safe pixel sampling area outside the window. + /// + /// + /// A string representing the color of the pixel at the computed location outside the window, + /// + private string GetOutWindowPixelColor(int spacing) + { + var rect = Session.GetMainWindowRect(); + int checkX, checkY; + + if ((rect.Top - screenMarginTop) >= spacing) + { + checkX = rect.Left; + checkY = screenMarginTop + (spacing / 2); + } + else if ((screenMarginBottom - rect.Bottom) >= spacing) + { + checkX = rect.Left; + checkY = rect.Bottom + (spacing / 2); + } + else if ((rect.Left - screenMarginLeft) >= spacing) + { + checkX = rect.Left - (spacing / 2); + checkY = rect.Top; + } + else if ((screenMarginRight - rect.Right) >= spacing) + { + checkX = rect.Right + (spacing / 2); + checkY = rect.Top; + } + else + { + throw new ArgumentOutOfRangeException(nameof(spacing), "No sufficient margin to sample outside the window."); + } + + Task.Delay(1000).Wait(); // Optional: Wait for a moment to ensure the mouse is in position + string zoneColor = this.GetPixelColorString(checkX, checkY); + return zoneColor; + } + + /// + /// Runs drag interactions during a FancyZones test and returns the initial and final zone highlight colors. + /// + /// An optional action to execute before the drag starts (e.g., setup or key press). + /// An optional action to execute after the drag is initiated but before it's released. + /// An optional action to execute when releasing the dragged window (e.g., mouse up). + /// The name of the test case for logging or diagnostics. + /// + /// A tuple containing: + /// + /// InitialZoneColor: The zone highlight color before interaction completes. + /// FinalZoneColor: The zone highlight color after interaction completes. + /// + /// + private (string InitialZoneColor, string FinalZoneColor) RunDragInteractions( + Action? preAction, + Action? postAction, + Action? releaseAction, + string testCaseName) + { + // Invoke the pre-action + preAction?.Invoke(); + + // Capture initial window state and zone color + var initialWindowRect = Session.GetMainWindowRect(); + string initialZoneColor = GetOutWindowPixelColor(30); + + // Invoke the post-action + postAction?.Invoke(); + + // Capture final zone color after the interaction + string finalZoneColor = GetOutWindowPixelColor(30); + + releaseAction?.Invoke(); + + // Return initial and final zone colors + return (initialZoneColor, finalZoneColor); + } + + // set the custom layout + private static readonly CustomLayouts.CustomLayoutListWrapper CustomLayoutsList = new CustomLayouts.CustomLayoutListWrapper + { + CustomLayouts = new List + { + new CustomLayouts.CustomLayoutWrapper + { + Uuid = "{63F09977-D327-4DAC-98F4-0C886CAE9517}", + Type = CustomLayout.Grid.TypeToString(), + Name = "Custom Column", + Info = new CustomLayouts().ToJsonElement(new CustomLayouts.GridInfoWrapper + { + Rows = 1, + Columns = 1, + RowsPercentage = new List { 10000 }, + ColumnsPercentage = new List { 10000 }, + CellChildMap = new int[][] { [0] }, + SensitivityRadius = 20, + ShowSpacing = true, + Spacing = 10, // set spacing to 0 make sure the zone is full of the screen + }), + }, + }, + }; + + // set the custom layout with 1 subzones + private static readonly CustomLayouts.CustomLayoutListWrapper CustomLayoutsListWithTwo = new CustomLayouts.CustomLayoutListWrapper + { + CustomLayouts = new List + { + new CustomLayouts.CustomLayoutWrapper + { + Uuid = "{63F09977-D327-4DAC-98F4-0C886CAE9517}", + Type = CustomLayout.Grid.TypeToString(), + Name = "Custom Column", + Info = new CustomLayouts().ToJsonElement(new CustomLayouts.GridInfoWrapper + { + Rows = 1, + Columns = 2, + RowsPercentage = new List { 10000 }, + ColumnsPercentage = new List { 5000, 5000 }, + CellChildMap = new int[][] { [0, 1] }, + SensitivityRadius = 20, + ShowSpacing = true, + Spacing = 10, + }), + }, + }, + }; + + private string GetZoneColor(string color) + { + // Click on the "Highlight color" group + Find(color).Click(); + + // Optional: Ensure the hex textbox is found (to wait until the UI loads) + var hexBox = Find(By.AccessibilityId("HexTextBox")); + Task.Delay(500).Wait(); // Optional: Wait for the UI to update + + // Get and return the RGB hex value text + var hexColorElement = Find("RGB hex"); + + // return mouse to color set position + Find(color).Click(); + + return hexColorElement.Text; + } + + // set the zone behaviour settings + private void ZoneBehaviourSettings(string? testName) + { + // test settings + Microsoft.PowerToys.UITest.CheckBox useShiftCheckBox = this.Find("Hold Shift key to activate zones while dragging a window"); + Microsoft.PowerToys.UITest.CheckBox useNonPrimaryMouseCheckBox = this.Find("Use a non-primary mouse button to toggle zone activation"); + Microsoft.PowerToys.UITest.CheckBox makeDraggedWindowTransparent = this.Find("Make dragged window transparent"); + + Find("Show zone number").SetCheck(false, 100); + Find("Opacity (%)").QuickSetValue(100); // make highlight color visible with opacity 100 + + // Get the highlight and inactivate color from appearance settings + Find("Zone appearance").Click(); + Find("Custom colors").Click(); + + // get the highlight (activated) and inactivate zone color + highlightColor = GetZoneColor("Highlight color"); + inactivateColor = GetZoneColor("Inactive color"); + + this.Scroll(2, "Down"); + makeDraggedWindowTransparent.SetCheck(false, 500); // set make dragged window transparent to false or will influence the color comparison + this.Scroll(6, "Up"); + + switch (testName) + { + case "TestShowZonesOnShiftDuringDrag": + useShiftCheckBox.SetCheck(true, 500); + useNonPrimaryMouseCheckBox.SetCheck(false, 500); + break; + case "TestShowZonesOnDragDuringShift": + useShiftCheckBox.SetCheck(true, 500); + useNonPrimaryMouseCheckBox.SetCheck(false, 500); + break; + case "TestToggleZonesWithNonPrimaryMouseClick": + useShiftCheckBox.SetCheck(false, 500); + useNonPrimaryMouseCheckBox.SetCheck(true, 500); + break; + case "TestShowZonesWhenShiftAndMouseOff": + useShiftCheckBox.SetCheck(false, 500); + useNonPrimaryMouseCheckBox.SetCheck(false, 500); + break; + case "TestShowZonesWhenShiftAndMouseOn": + useShiftCheckBox.SetCheck(true, 500); + useNonPrimaryMouseCheckBox.SetCheck(true, 500); + break; + case "TestMakeDraggedWindowTransparentOff": + useShiftCheckBox.SetCheck(true, 500); + useNonPrimaryMouseCheckBox.SetCheck(false, 500); + break; // Added break to prevent fall-through + case "TestMakeDraggedWindowTransparentOn": + useNonPrimaryMouseCheckBox.SetCheck(false, 500); + useShiftCheckBox.SetCheck(true, 500); + this.Scroll(5, "Down"); // Pull the settings page up to make sure the settings are visible + makeDraggedWindowTransparent.SetCheck(true, 500); + this.Scroll(5, "Up"); + break; // Added break to prevent fall-through + default: + throw new ArgumentException("Unsupported Test Case.", testName); + } + } + } +} diff --git a/src/modules/fancyzones/UITests-FancyZones/LayoutApplyHotKeyTests.cs b/src/modules/fancyzones/UITests-FancyZones/LayoutApplyHotKeyTests.cs new file mode 100644 index 0000000000..8956032a0b --- /dev/null +++ b/src/modules/fancyzones/UITests-FancyZones/LayoutApplyHotKeyTests.cs @@ -0,0 +1,673 @@ +// 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; +using System.Threading.Tasks; +using System.Windows.Automation; +using FancyZonesEditor.Models; +using FancyZonesEditorCommon.Data; +using Microsoft.FancyZonesEditor.UITests; +using Microsoft.FancyZonesEditor.UnitTests.Utils; +using Microsoft.PowerToys.UITest; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using static FancyZonesEditorCommon.Data.CustomLayouts; +using static Microsoft.FancyZonesEditor.UnitTests.Utils.FancyZonesEditorHelper; + +namespace UITests_FancyZones +{ + [TestClass] + public class LayoutApplyHotKeyTests : UITestBase + { + public LayoutApplyHotKeyTests() + : base(PowerToysModule.PowerToysSettings, WindowSize.Large) + { + } + + private static readonly EditorParameters.ParamsWrapper Parameters = new EditorParameters.ParamsWrapper + { + ProcessId = 1, + SpanZonesAcrossMonitors = false, + Monitors = new List + { + new EditorParameters.NativeMonitorDataWrapper + { + Monitor = "monitor-1", + MonitorInstanceId = "instance-id-1", + MonitorSerialNumber = "serial-number-1", + MonitorNumber = 1, + VirtualDesktop = "{FF34D993-73F3-4B8C-AA03-73730A01D6A8}", + Dpi = 96, + LeftCoordinate = 0, + TopCoordinate = 0, + WorkAreaHeight = 1040, + WorkAreaWidth = 1920, + MonitorHeight = 1080, + MonitorWidth = 1920, + IsSelected = true, + }, + new EditorParameters.NativeMonitorDataWrapper + { + Monitor = "monitor-2", + MonitorInstanceId = "instance-id-2", + MonitorSerialNumber = "serial-number-2", + MonitorNumber = 2, + VirtualDesktop = "{FF34D993-73F3-4B8C-AA03-73730A01D6A8}", + Dpi = 96, + LeftCoordinate = 1920, + TopCoordinate = 0, + WorkAreaHeight = 1040, + WorkAreaWidth = 1920, + MonitorHeight = 1080, + MonitorWidth = 1920, + IsSelected = false, + }, + }, + }; + + private static readonly CustomLayouts.CustomLayoutListWrapper CustomLayoutsList = new CustomLayouts.CustomLayoutListWrapper + { + CustomLayouts = new List + { + new CustomLayoutWrapper + { + Uuid = "{0D6D2F58-9184-4804-81E4-4E4CC3476DC1}", + Type = CustomLayout.Grid.TypeToString(), + Name = "Grid custom layout", + Info = new CustomLayouts().ToJsonElement(new GridInfoWrapper + { + Rows = 2, + Columns = 2, + RowsPercentage = new List { 5000, 5000 }, + ColumnsPercentage = new List { 5000, 5000 }, + CellChildMap = new int[][] { [0, 1], [2, 3] }, + SensitivityRadius = 30, + Spacing = 26, + ShowSpacing = false, + }), + }, + new CustomLayoutWrapper + { + Uuid = "{0EB9BF3E-010E-46D7-8681-1879D1E111E1}", + Type = CustomLayout.Grid.TypeToString(), + Name = "Grid-9", + Info = new CustomLayouts().ToJsonElement(new GridInfoWrapper + { + Rows = 3, + Columns = 3, + RowsPercentage = new List { 2333, 3333, 4334 }, + ColumnsPercentage = new List { 2333, 3333, 4334 }, + CellChildMap = new int[][] { [0, 1, 2], [3, 4, 5], [6, 7, 8] }, + SensitivityRadius = 20, + Spacing = 3, + ShowSpacing = false, + }), + }, + new CustomLayoutWrapper + { + Uuid = "{E7807D0D-6223-4883-B15B-1F3883944C09}", + Type = CustomLayout.Canvas.TypeToString(), + Name = "Canvas custom layout", + Info = new CustomLayouts().ToJsonElement(new CanvasInfoWrapper + { + RefHeight = 1040, + RefWidth = 1920, + SensitivityRadius = 10, + Zones = new List + { + new CanvasInfoWrapper.CanvasZoneWrapper + { + X = 0, + Y = 0, + Width = 500, + Height = 250, + }, + new CanvasInfoWrapper.CanvasZoneWrapper + { + X = 500, + Y = 0, + Width = 1420, + Height = 500, + }, + new CanvasInfoWrapper.CanvasZoneWrapper + { + X = 0, + Y = 250, + Width = 1920, + Height = 500, + }, + }, + }), + }, + }, + }; + + private static readonly LayoutHotkeys.LayoutHotkeysWrapper LayoutHotkeysList = new LayoutHotkeys.LayoutHotkeysWrapper + { + LayoutHotkeys = new List + { + new LayoutHotkeys.LayoutHotkeyWrapper + { + Key = 0, + LayoutId = "{0D6D2F58-9184-4804-81E4-4E4CC3476DC1}", + }, + new LayoutHotkeys.LayoutHotkeyWrapper + { + Key = 1, + LayoutId = "{0EB9BF3E-010E-46D7-8681-1879D1E111E1}", + }, + new LayoutHotkeys.LayoutHotkeyWrapper + { + Key = 2, + LayoutId = "{E7807D0D-6223-4883-B15B-1F3883944C09}", + }, + }, + }; + + private static readonly LayoutTemplates.TemplateLayoutsListWrapper TemplateLayoutsList = new LayoutTemplates.TemplateLayoutsListWrapper + { + LayoutTemplates = new List + { + new LayoutTemplates.TemplateLayoutWrapper + { + Type = LayoutType.Blank.TypeToString(), + }, + new LayoutTemplates.TemplateLayoutWrapper + { + Type = LayoutType.Focus.TypeToString(), + ZoneCount = 10, + }, + new LayoutTemplates.TemplateLayoutWrapper + { + Type = LayoutType.Rows.TypeToString(), + ZoneCount = 2, + ShowSpacing = true, + Spacing = 10, + SensitivityRadius = 10, + }, + new LayoutTemplates.TemplateLayoutWrapper + { + Type = LayoutType.Columns.TypeToString(), + ZoneCount = 2, + ShowSpacing = true, + Spacing = 20, + SensitivityRadius = 20, + }, + new LayoutTemplates.TemplateLayoutWrapper + { + Type = LayoutType.Grid.TypeToString(), + ZoneCount = 4, + ShowSpacing = false, + Spacing = 10, + SensitivityRadius = 30, + }, + new LayoutTemplates.TemplateLayoutWrapper + { + Type = LayoutType.PriorityGrid.TypeToString(), + ZoneCount = 3, + ShowSpacing = true, + Spacing = 1, + SensitivityRadius = 40, + }, + }, + }; + + [TestInitialize] + public void TestInitialize() + { + FancyZonesEditorHelper.Files.Restore(); + EditorParameters editorParameters = new EditorParameters(); + FancyZonesEditorHelper.Files.ParamsIOHelper.WriteData(editorParameters.Serialize(Parameters)); + + LayoutTemplates layoutTemplates = new LayoutTemplates(); + FancyZonesEditorHelper.Files.LayoutTemplatesIOHelper.WriteData(layoutTemplates.Serialize(TemplateLayoutsList)); + + CustomLayouts customLayouts = new CustomLayouts(); + FancyZonesEditorHelper.Files.CustomLayoutsIOHelper.WriteData(customLayouts.Serialize(CustomLayoutsList)); + + DefaultLayouts defaultLayouts = new DefaultLayouts(); + DefaultLayouts.DefaultLayoutsListWrapper defaultLayoutsListWrapper = new DefaultLayouts.DefaultLayoutsListWrapper + { + DefaultLayouts = new List + { + new DefaultLayouts.DefaultLayoutWrapper + { + MonitorConfiguration = MonitorConfigurationType.Horizontal.TypeToString(), + Layout = new DefaultLayouts.DefaultLayoutWrapper.LayoutWrapper + { + Type = LayoutType.Focus.TypeToString(), + ZoneCount = 4, + ShowSpacing = true, + Spacing = 5, + SensitivityRadius = 20, + }, + }, + new DefaultLayouts.DefaultLayoutWrapper + { + MonitorConfiguration = MonitorConfigurationType.Vertical.TypeToString(), + Layout = new DefaultLayouts.DefaultLayoutWrapper.LayoutWrapper + { + Type = LayoutType.Custom.TypeToString(), + Uuid = "{0D6D2F58-9184-4804-81E4-4E4CC3476DC1}", + ZoneCount = 0, + ShowSpacing = false, + Spacing = 0, + SensitivityRadius = 0, + }, + }, + }, + }; + FancyZonesEditorHelper.Files.DefaultLayoutsIOHelper.WriteData(defaultLayouts.Serialize(defaultLayoutsListWrapper)); + + LayoutHotkeys layoutHotkeys = new LayoutHotkeys(); + FancyZonesEditorHelper.Files.LayoutHotkeysIOHelper.WriteData(layoutHotkeys.Serialize(LayoutHotkeysList)); + + AppliedLayouts appliedLayouts = new AppliedLayouts(); + AppliedLayouts.AppliedLayoutsListWrapper appliedLayoutsWrapper = new AppliedLayouts.AppliedLayoutsListWrapper + { + AppliedLayouts = new List { }, + }; + FancyZonesEditorHelper.Files.AppliedLayoutsIOHelper.WriteData(appliedLayouts.Serialize(appliedLayoutsWrapper)); + + this.RestartScopeExe(); + } + + [TestMethod("FancyZones.Settings.TestApplyHotKey")] + [TestCategory("FancyZones #1")] + public void TestApplyHotKey() + { + this.OpenFancyZonesPanel(); + this.ControlQuickLayoutSwitch(true); + + // Set Hotkey + this.AttachFancyZonesEditor(); + var layout = "Grid custom layout"; + Session.Find(layout).Find