Compare commits

...

39 Commits

Author SHA1 Message Date
Leilei Zhang
5fe883b789 cal AOT 2025-06-10 19:20:47 +08:00
Leilei Zhang
d5e457fb90 use exprtk 2025-06-10 13:50:03 +08:00
Yu Leng
b3c533e4c4 update 2025-06-06 15:03:40 +08:00
Yu Leng
2bdb591ac8 init 2025-06-06 14:46:48 +08:00
leileizhang
96c5cf1897 Add missing codec detection and warning message in video preview (#39726)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
This PR adds support for detecting missing video codecs during video
file preview, and displays a warning message to inform the user when the
required codec is not installed.
### Changes
- Added `GetMissingCodecAsync` to detect unsupported video formats using
`MediaEncodingProfile` and `CodecQuery`.
- If a required codec is missing, display a warning in the preview UI.
- Added localized string `MissingVideoCodec_WarningMessage`:
> "This video uses the {0} format, which isn't supported. Please install
the required codec to play it."
- Add Store Search


![image](https://github.com/user-attachments/assets/7325c3fb-b6ea-4d22-b226-f56e617ac5e2)


![search](https://github.com/user-attachments/assets/e30c1aca-beaf-4d31-a704-abd89279f5c1)

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] **Closes:** https://github.com/microsoft/PowerToys/issues/39235,
https://github.com/microsoft/PowerToys/issues/38201
- [ ] **Communication:** I've discussed this with core contributors
already. If work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [x] **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
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [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

<!-- Provide a more detailed description of the PR, other things fixed
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
To simulate a missing codec (e.g., AV1):

1. Check if the AV1 extension is installed:
   ```powershell
   Get-AppxPackage -Name *AV1*
2. If installed, remove it:
   ```powershell
   Get-AppxPackage -Name *AV1* | Remove-AppxPackage
3. Use Peek to preview a video encoded in AV1 format.
4. Confirm that a warning message is shown indicating the missing codec.
2025-06-06 14:08:04 +08:00
Kai Tao
a1cf836c6d [Workspaces] Change workspaces icon store position (#39677)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
Before, workspaces ico are stored in temp folder, which is easily
cleaned up by user.
After clean, user has to explicitly save icon once again.
With this change, icons are stored at the same position with workspaces
settings

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [X] **Closes:** #35004
- [ ] **Communication:** I've discussed this with core contributors
already. If 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
- [ ] **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
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [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

<!-- Provide a more detailed description of the PR, other things fixed
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

![image](https://github.com/user-attachments/assets/66723ba6-bb4e-4768-ba06-df105ea7f65e)

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2025-06-06 13:46:47 +08:00
Mike Griese
c1e49ba915 CmdPal: Don't trim the template proj in debug builds (#39463)
This saves a LOT of compilation time
2025-06-05 21:50:51 -05:00
Davide Giacometti
309582795c [CmdPal] Add system tray menu (#39155)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request

<img width="151" alt="image"
src="https://github.com/user-attachments/assets/994c06d0-e28f-4fa4-a8fe-043ec179ee82"
/>

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] **Closes:** #38303
- [ ] **Communication:** I've discussed this with core contributors
already. If 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
- [ ] **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
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [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

<!-- Provide a more detailed description of the PR, other things fixed
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

- Move all system tray related code from main window code behind to a
dedicated service
- Add system tray menu

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

Manually tested:

- Started CmdPal with tray icon enabled
- Started CmdPal with tray icon disabled
- Enabled/Disabled tray icon
- Tested tray menu commands
- Verified that the tray icon is visible after restarting explorer.exe
2025-06-05 15:29:18 -05:00
Clint Rutkas
2aad949c9e Welcome new friends, Michael, Jessica, Gleb, Zachary, and Jaylyn (#39891)
Adding Jessica and Michael to community.md

demoting myself to overhead :)

---------

Co-authored-by: Kayla Cinnamon <cinnamon@microsoft.com>
Co-authored-by: Mike Griese <migrie@microsoft.com>
2025-06-05 11:18:47 -07:00
Michael Jolley
bf9217ec24 CmdPal: Implement IDetailsCommand in details (#39911)
Implemented IDetailsCommands in details. This will close #38339.

This works very similar to Tags in that it is a list of commands. This
was done to allow for styling without the 12 spacing of the
ItemsRepeater and looks like you'd find in the OS-inbox like:


![image](https://github.com/user-attachments/assets/97ee1952-13bb-4c8b-a074-0347b04e0c2c)


![image](https://github.com/user-attachments/assets/8f6f1f72-4ea0-441d-893e-ae26aabdc922)

Also added to our sample extension:


![image](https://github.com/user-attachments/assets/ab3ce521-3479-448f-b4d6-9dfd09feb24f)
2025-06-05 08:56:13 -05:00
Michael Jolley
78b29c5b66 CmdPal: Display correct context items for run fallback (#39521)
Corrected the Run fallback handler to provide the same context items as
it does on the run page.


![image](https://github.com/user-attachments/assets/04a444b4-dc9f-4b63-8231-8f47f593b60f)

**Closes:** #39092
2025-06-05 08:52:25 -05:00
Yu Leng
dd2e7d17f9 [cmdpal] Re-enable Clipboard History extention (#39800)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
Due to some windows sdk bugs, we can not use those API in main thread.
So, create a separate thread for clipboard.

history:

![image](https://github.com/user-attachments/assets/4da5a4eb-5d9d-475c-ab13-a2d585d2fffc)

success to paste to chat:

![image](https://github.com/user-attachments/assets/3fef43e5-4fc5-492c-b81e-599a9746d413)

![image](https://github.com/user-attachments/assets/1f4232bb-de76-40e5-96dd-43beb0ca8423)



<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] **Closes:** #38344
- [ ] **Communication:** I've discussed this with core contributors
already. If work hasn't been agreed, this work might be rejected
- [x] **Tests:** Added/updated and all pass
- [x] **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
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [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

<!-- Provide a more detailed description of the PR, other things fixed
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

---------

Co-authored-by: Yu Leng (from Dev Box) <yuleng@microsoft.com>
2025-06-05 15:23:16 +08:00
Yu Leng
df8ace3ab6 Release ptr which get from CoCreateInstance to prevent memory leak (#39898)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
We need to release it manually.

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [ ] **Closes:** #xxx
- [ ] **Communication:** I've discussed this with core contributors
already. If 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
- [ ] **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
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [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

<!-- Provide a more detailed description of the PR, other things fixed
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

---------

Co-authored-by: Yu Leng <yuleng@microsoft.com>
2025-06-05 15:02:03 +08:00
leileizhang
88e4ba670c [Fuzzing] Use project reference for PowerRenameLib to fix Debug build link error (#39903)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
This change replaces the manual .lib reference with a proper project
reference to PowerRenameLib in the PowerRename.FuzzingTest project.

Previously, the project used a hardcoded path to link against
PowerRenameLib.lib, which caused build errors in Debug mode when the
.lib file had not yet been generated or the output path was incorrect.

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [ ] **Closes:** #xxx
- [ ] **Communication:** I've discussed this with core contributors
already. If 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
- [ ] **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
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [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

<!-- Provide a more detailed description of the PR, other things fixed
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2025-06-04 23:36:50 +08:00
leileizhang
79958975b4 [AOT][CmdPal] Enable NativeAOT Compatibility for CmdPal Apps Extension (#39678)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request

This PR introduces necessary changes to make the CmdPal.Apps extension
compatible with NativeAOT publishing. The main updates include:

1. Project configuration updates: Added NativeAOT-related properties to
the .csproj.

2. Native interop adjustments:

> - Introduced NativeMethods.json and used
[CsWin32](https://github.com/microsoft/cswin32) to generate P/Invoke
bindings.
> - Replaced some DllImport declarations with source-generated
[LibraryImport] for improved AOT support.
2025-06-04 19:30:11 +08:00
Yu Leng
ddbb6161e3 [CmdPal][AOT] Make fileSearch ext become AOT compatible (#39732)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
* Use source generation to replace some csWin32 function call.
* Clean up some AOT compatible issues.

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] **Closes:** #39889
- [x] **Communication:** I've discussed this with core contributors
already. If work hasn't been agreed, this work might be rejected
- [x] **Tests:** Added/updated and all pass
- [x] **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
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [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

<!-- Provide a more detailed description of the PR, other things fixed
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

If you want to try it out, please see
https://github.com/microsoft/PowerToys/pull/39605

---------

Co-authored-by: Yu Leng <yuleng@microsoft.com>
2025-06-04 17:15:55 +08:00
Noraa Junker-Wildi
78c7c8e88f Changing name from Aaron Junker to Noraa Junker (#39860)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request

Changes my old name to my new name in community.md and the File Explorer
add-ons attribution.

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [ ] **Closes:** #xxx
- [ ] **Communication:** I've discussed this with core contributors
already. If 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
- [ ] **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
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [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

<!-- Provide a more detailed description of the PR, other things fixed
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2025-06-04 11:09:41 +02:00
Yu Leng
693ab67831 [cmdpal][AOT] make some built-in extensions able to be published with native AOT enabled (#39802)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
Base on my test, some built-in extensions can be published with native
AOT enabled with a little changes. (see this branch:
https://github.com/microsoft/PowerToys/pull/39605).

1. SystemCommands: no change need. Removed some unused code.
2. WinGet: disable marshalling. and do a little changes in
CreateInstance.
3. WindowWalker: use source generation to call CoCreateInstance.
4. WindowsTerminal: use source generation to call
IApplicationActivationManager interface.

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] **Closes:** #39869
- [x] **Communication:** I've discussed this with core contributors
already. If work hasn't been agreed, this work might be rejected
- [x] **Tests:** Added/updated and all pass
- [x] **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
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [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

<!-- Provide a more detailed description of the PR, other things fixed
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

---------

Co-authored-by: Yu Leng <yuleng@microsoft.com>
2025-06-03 14:24:13 +08:00
Dustin L. Howett
c6a4ee1441 build: try even harder to find a working VC tools version (#39862)
I can't explain this, but VS 17.14 ships with VisualCpp.Tools.Core
version 14.44.35208 but the files say 14.44.35207.

See https://github.com/microsoft/terminal/pull/18996 for more info.
2025-06-02 15:47:56 -05:00
mbartlett21
89c33fae68 [peek, explorer] Update QOI reader for 3-channel images (#39304)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request

Make sure we account for Bitmap row length being a multiple of 4 when
reading Qoi files.

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] **Closes:** #31948
- [ ] **Communication:** Not discussed, but a bug fix
- [ ] **Tests:** Added/updated and all pass
- [ ] **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
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [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

<!-- Provide a more detailed description of the PR, other things fixed
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments



<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

Built and run and restarted file explorer.
Sample files from my comment on the issue (after rewriting each one to
force the thumbnail to regenrate):

![image](https://github.com/user-attachments/assets/5874f958-0f03-4683-abe3-d54c979ad2a1)
2025-05-30 18:32:40 +08:00
Yu Leng
c9922302e5 [PowerRename] Add ModificationTime and AccessTime support when renaming with $YY-$MM-$DD patterns (#39653)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
1. Add new configuration to control this behaviour. By default, set to
creation time to align with previous version
2. Add UI in PowerRename's MainWindow
3. Implement the logic


![image](https://github.com/user-attachments/assets/fde6731b-73f9-453f-8b68-6ce66589f44a)


Original discussion here:
https://github.com/microsoft/PowerToys/pull/38186

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] **Closes:** #36040
- [x] **Communication:** I've discussed this with core contributors
already. If work hasn't been agreed, this work might be rejected
- [x] **Tests:** Added/updated and all pass
- [x] **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
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [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

<!-- Provide a more detailed description of the PR, other things fixed
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

---------

Co-authored-by: Yu Leng (from Dev Box) <yuleng@microsoft.com>
Co-authored-by: Heiko <61519853+htcfreek@users.noreply.github.com>
2025-05-30 13:07:20 +08:00
Kai Tao
498ef676f4 Runner: Add log when error happen in setting startup (#39801)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
Add log when error happen in setting startup 

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [X] **Closes:** #39798
- [ ] **Communication:** I've discussed this with core contributors
already. If work hasn't been agreed, this work might be rejected
- [X] **Tests:** Added/updated and all pass
- [ ] **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
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [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

<!-- Provide a more detailed description of the PR, other things fixed
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed


![image](https://github.com/user-attachments/assets/2208c65f-4d42-4612-a77e-d92a748fd723)

Set error in code deliberately, and verify that the error appears in the
log.
2025-05-30 10:03:36 +08:00
zhw
3821e8427c [Localization] Add comments in Resources.resw (#39778)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
This PR mainly add comments in
src/modules/AdvancedPaste/AdvancedPaste/Strings/en-us/Resources.resw to
solve issues raised by localization team.

---------

Co-authored-by: ArleneYu <yuzw@pku.edu.cn>
2025-05-29 18:09:05 +08:00
Royce Williams
964e5c9d5e cmdpal README: minor typo (#39764)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
Minor typo it's -> its

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

<!-- Provide a more detailed description of the PR, other things fixed
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments
README-only change

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
n/a
2025-05-28 20:13:05 +00:00
Michael Jolley
48d9d19df1 CmdPal: Styling critical context items using the SystemFillColorCriticalBrush (#39645)
Styles context items based on the IsCritical property.


![image](https://github.com/user-attachments/assets/aa1ee0b0-de09-45af-8f5e-74dff666fbb4)


![image](https://github.com/user-attachments/assets/90a7f750-c949-4f76-b699-db2e29251414)

Closes #38307
2025-05-28 14:35:26 -05:00
Kai Tao
d21b7fac7b Log: Clear mwb module interface old version log and clear dotnet old version log (#39652)
* Initial plan for issue

* Add cleanup of old version log folders for Mouse Without Borders

Co-authored-by: vanzue <69313318+vanzue@users.noreply.github.com>

* Add try-catch block to cleanup_old_logs function

Co-authored-by: vanzue <69313318+vanzue@users.noreply.github.com>

* Refactor Mouse Without Borders log cleanup to use common mechanism

Co-authored-by: vanzue <69313318+vanzue@users.noreply.github.com>

* Investigate .NET logger cleanup mechanism for old version logs

Co-authored-by: vanzue <69313318+vanzue@users.noreply.github.com>

* delete in other thread

* clear mwb previous folder

* slow down the deletion

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2025-05-28 18:53:35 +08:00
Mengyuan
02a9269435 [Fuzz] Add Fuzz testing for FancyZones (#38165)
* Add FancyZones.FuzzTests Module for fuzzing grid from json element

* remove unuse test cs

* fix spell error

* remove specific modify in job-fuzz.yml

* add Testably.Abstractions.FileSystem.Interface.dll

* fix spell error

* add dependency dll

* add annotations

* fix pipeline error  need to import 'Common.Dotnet.CsWinRT.props'
2025-05-28 14:56:37 +08:00
PesBandi
f642720087 [CmdPal][Calc]Trim leading = to support pasting formulas (#39743)
* [CmdPal][Calc]Trim leading `=` to support pasting formulas

* Update README.md

* Update README.md
2025-05-28 14:25:07 +08:00
Bradley Myers
53b989857b Option to toggle the system tray icon (#23220)
* Added option to toggle the system tray icon

At the moment, this hides the icon making the settings window inaccessible without first modifying the general `settings.json` file.

* Use IPC messages to manage the tray icon settings

* Fix launching second window binds to active settings process

* Added context menu option to hide tray icon

* Added Exit PT button to settings ui NavigationView.PaneFooter

* Moved DllImports to NativeMethods.cs

* Sentence case titles

* Fix whitespace

* Re-add exit icon to NavView

* Re-added toggle switch to new UI

* Fix build

* Fix build after merge main

* Fix the string to display

* add shut down buttons

* finish polish

* fix string

* Styling tweaks to titlebar and settingscards

* fix comment

* fix unit test

* fix ut

---------

Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>
Co-authored-by: vanzue <vanzue@outlook.com>
Co-authored-by: Kayla Cinnamon <cinnamon@microsoft.com>
Co-authored-by: Niels Laute <niels.laute@live.nl>
2025-05-26 17:03:35 +08:00
Yu Leng
718e725571 [cmdpal] Fix calculator error when pressing Space (#39679)
Co-authored-by: Yu Leng (from Dev Box) <yuleng@microsoft.com>
2025-05-26 11:37:38 +08:00
Gordon Lam
a804bf86a4 Update to WinAppSDK 1.7.2 (#39592)
Update to WinAppSDK 1.7.2
2025-05-23 11:19:24 +08:00
Kai Tao
bdf0b5ea23 MWB: Fix firewall rule to allow remote connections from IPs outside the local subnet (#39595)
* remove unnecessary restrict for local subnet only in firewal

* remoteip set to any
2025-05-21 13:10:03 +08:00
TheBestWebsite
9b3f8951f8 Update README with 0.91.1 (#39597) 2025-05-20 16:49:07 -07:00
Hao Liu
5517c6d504 Revert "[PowerAccent] Cancel previous ShowToolbar task if a new one is triggered" (#39563)
Revert "[PowerAccent] Cancel previous ShowToolbar task if a new one is triggered (#37757)"

This reverts commit e1ad7e39c6.
2025-05-19 13:00:40 +08:00
Kai Tao
12e23e23a3 workspaces: shell:appsfolder launch does not support the command line (#39433)
shell:appsfolder launch does not respect the command line
2025-05-19 09:12:23 +08:00
Mike Griese
c9656754bd CmdPal: fix a perf regression loading pages (#39415)
This was especially noticable with the icons extension.

Turns out in #39051, when I was experimenting with getting AoT clean,
i accidentally called this twice. Then we actually commited that
straight up.

This PR reverts that. It also moves a similar case where we were
initializing all the tags on the UI thread. That's wrong too - we need
to fetch properties off the UI thread, then update the list on the UI
thread.

Closes nothing, I didn't file this yet.
2025-05-16 10:47:44 -05:00
Gordon Lam
0e9c5a82dd Fix Cmdpal launch without admin mode - both runner and setting. (#39494)
* Change to path and args

* Fix both Setting launch and runner launch
2025-05-16 19:11:57 +08:00
Gordon Lam
75121ca7f3 Fix RunAsAdmin For CmdPal when PowerToys is running as Admin (#39448)
<!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? -->
## Summary of the Pull Request
Since we change the launch method by this PR #39269 , we will start cmdpal as admin too if powertoys run as admin.
The fix is leveraging explorer (which will not run as admin) to start the cmdpal

Moreover, without this fix, some of extension cannot be "loaded" when cmdpal run as admin, e.g. winget will be missing, and my own new extension developed by myself will not be loaded successful as well.
2025-05-15 16:47:40 -05:00
Dustin L. Howett
898e7c6352 build: strong name sign the Extension Toolkit (#39469)
Strong-name signing embeds publisher identity into the signature of a
.NET assembly.

This is required if *any other* strong name signed project wants to take
a dependency on it.

To make this work, we need to delay-sign it with a public key (.snk
file)--e.g. say we are going to sign it, but not actually sign it--to
give it an identity and then later submit it to ESRP for final signing.

The snk file does not contain any private material.

Some minor changes were required to build properly:
- `InternalsVisibleTo` requires a PublicKeyToken, but we aren't using
  it in the SDK build so it's fine to just leave it out.
- I had to mark a class `sealed` and I can only guess it's because
  strong named assemblies have more guarantees?
2025-05-15 16:47:03 -05:00
210 changed files with 49663 additions and 3098 deletions

View File

@@ -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
@@ -95,6 +96,7 @@ Huynh
Ionut
jamrobot
Jaswal
Jaylyn
jefflord
Jordi
jyuwono
@@ -105,6 +107,7 @@ Kantarci
Karthick
kaylacinnamon
kevinguo
Khmyznikov
Krigun
Lambson
Laute
@@ -127,6 +130,8 @@ Naro
nathancartlidge
Nemeth
nielslaute
Noraa
noraajunker
oldnewthing
onegreatworld
palenshus
@@ -146,11 +151,13 @@ ricardosantos
riri
ritchielawrence
robmikh
ruslanlap
Russinovich
Rutkas
ryanbodrug
saahmedm
sachaple
Sameerjs
Santossio
Schoen
Sekan
@@ -169,6 +176,7 @@ TBM
tilovell
Triet
urnotdfs
vednig
waaverecords
wang
Whuihuan
@@ -187,9 +195,6 @@ zhaopy
zhaoqpcn
Zoltan
Zykova
Sameerjs
ruslanlap
vednig
# OTHERS

View File

@@ -14,6 +14,7 @@ AColumn
acrt
ACTIVATEAPP
activationaction
ACTIVATEOPTIONS
ACVS
adaptivecards
ADate
@@ -788,6 +789,7 @@ LCIDTo
Lclean
Ldone
Ldr
LEFTALIGN
LEFTSCROLLBAR
LEFTTEXT
LError
@@ -1535,6 +1537,7 @@ SMALLICON
smartphone
SMTO
SNAPPROCESS
snk
snwprintf
softline
SOURCECLIENTAREAONLY
@@ -1587,6 +1590,7 @@ steamapps
STGC
STGM
STGMEDIUM
STGMREAD
STICKYKEYS
sticpl
storelogo
@@ -1691,6 +1695,7 @@ TLayout
tlb
tlbimp
tlc
TGM
TNP
Toolhelp
toolkitconverters
@@ -1994,3 +1999,15 @@ culori
Evercoder
LCh
CIELCh
CLSCTXINPROCALL
IIDI
irow
lcid
OTHERUNZOOM
OTHERZOOM
PARENTCLOSING
PARENTOPENING
ppwsz
rguid
SCROLLCHILDREN
VARTYPE

Binary file not shown.

View File

@@ -221,6 +221,7 @@
"PowerToys.CmdPalModuleInterface.dll",
"CmdPalKeyboardService.dll",
"CmdPalCalculator.dll",
"*Microsoft.CmdPal.UI_*.msix"
],
"SigningInfo": {

View File

@@ -4,9 +4,66 @@
"SignBatches": [
{
"MatchedPath": [
"Microsoft.CommandPalette.Extensions.dll",
"Microsoft.CommandPalette.Extensions.Toolkit.dll"
],
"SigningInfo": {
"Operations": [
{
"KeyCode": "CP-233904-SN",
"OperationSetCode": "StrongNameSign",
"ToolName": "sign",
"ToolVersion": "1.0",
"Parameters": []
},
{
"KeyCode": "CP-233904-SN",
"OperationSetCode": "StrongNameVerify",
"ToolName": "sign",
"ToolVersion": "1.0",
"Parameters": []
},
{
"KeyCode": "CP-230012",
"OperationSetCode": "SigntoolSign",
"Parameters": [
{
"parameterName": "OpusName",
"parameterValue": "Microsoft"
},
{
"parameterName": "OpusInfo",
"parameterValue": "http://www.microsoft.com"
},
{
"parameterName": "FileDigest",
"parameterValue": "/fd \"SHA256\""
},
{
"parameterName": "PageHash",
"parameterValue": "/NPH"
},
{
"parameterName": "TimeStamp",
"parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
}
],
"ToolName": "sign",
"ToolVersion": "1.0"
},
{
"KeyCode": "CP-230012",
"OperationSetCode": "SigntoolVerify",
"Parameters": [],
"ToolName": "sign",
"ToolVersion": "1.0"
}
]
}
},
{
"MatchedPath": [
"Microsoft.CommandPalette.Extensions.dll"
],
"SigningInfo": {
"Operations": [
{

View File

@@ -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 '<PackageVersion Include="Microsoft.WindowsAppSDK"') {
$newVersionString = '<PackageVersion Include="Microsoft.WindowsAppSDK" Version="' + $WinAppSDKVersion + '" />'
$oldVersionString = '<PackageVersion Include="Microsoft.WindowsAppSDK" Version="[-.0-9a-zA-Z]*" />'
$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

View File

@@ -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"

View File

@@ -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
@@ -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,10 +180,10 @@ 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
- [@niels9001](https://github.com/niels9001/) - Niels Laute - Product Manager
- [@zhiwei-ms](https://github.com/zhiwei-ms) - Zhiwei Yu - Product Manager
- [@dhowett](https://github.com/dhowett) - Dustin Howett - Dev lead
- [@yeelam-gordon](https://github.com/yeelam-gordon) - Gordon Lam - Dev lead
@@ -204,6 +200,12 @@ 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
- [@crutkas](https://github.com/crutkas/) - Clint Rutkas - Overhead
## Former PowerToys core team members

View File

@@ -55,7 +55,7 @@
-->
<PackageVersion Include="Microsoft.Windows.CsWinRT" Version="2.2.0" />
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.2428" />
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.7.250401001" />
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.7.250513003" />
<PackageVersion Include="Microsoft.Xaml.Behaviors.WinUI.Managed" Version="2.0.9" />
<PackageVersion Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.39" />
<PackageVersion Include="ModernWpfUI" Version="0.9.4" />

View File

@@ -1472,7 +1472,7 @@ SOFTWARE.
- Microsoft.Windows.CsWin32 0.2.46-beta
- Microsoft.Windows.CsWinRT 2.2.0
- Microsoft.Windows.SDK.BuildTools 10.0.22621.2428
- Microsoft.WindowsAppSDK 1.7.250401001
- 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

View File

@@ -706,10 +706,14 @@ 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("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CmdPalCalculator", "src\modules\cmdpal\CmdPalCalculator\CmdPalCalculator.vcxproj", "{2CF78CF7-8FEB-4BE1-9591-55FA25B48FC6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM64 = Debug|ARM64
@@ -2582,6 +2586,14 @@ 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
{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 +2608,14 @@ 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
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -2821,13 +2841,13 @@ Global
{367D7543-7DBA-4381-99F1-BF6142A996C4} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
{2CAC093E-5FCF-4102-9C2C-AC7DD5D9EB96} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
{37D07516-4185-43A4-924F-3C7A5D95ECF6} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
{6D56B64D-FF1F-488F-AFED-9B9854A5D399} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
{8F021B46-362B-485C-BFBA-CCF83E820CBD} = {8F62026A-294B-41C6-8839-87463613F216}
{66614C26-314C-4B91-9071-76133422CFEF} = {B6C42F16-73EB-477E-8B0D-4E6CF6C20AAC}
{3846508C-77EB-4034-A702-F8BB263C4F79} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
{ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2} = {3846508C-77EB-4034-A702-F8BB263C4F79}
{6CE438DF-C245-4997-A360-0A0939E4BA34} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
{E09AA983-C755-474F-83D6-A5CDF528C070} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
{6D56B64D-FF1F-488F-AFED-9B9854A5D399} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
{92EC89E4-9972-453A-8A1A-3A9E230C146A} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
{51939B4F-1F62-4BFF-A6A2-C08646E5BE95} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
{D1160404-D3D1-497A-883A-4059C07C2273} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
@@ -2866,8 +2886,10 @@ 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}
{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} = {3846508C-77EB-4034-A702-F8BB263C4F79}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}

View File

@@ -37,17 +37,17 @@ Go to the [Microsoft PowerToys GitHub releases page][github-release-link] and cl
<!-- items that need to be updated release to release -->
[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.92%22
[github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.91%22
[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.91.0/PowerToysUserSetup-0.91.0-x64.exe
[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.91.0/PowerToysUserSetup-0.91.0-arm64.exe
[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.91.0/PowerToysSetup-0.91.0-x64.exe
[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.91.0/PowerToysSetup-0.91.0-arm64.exe
[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.91.1/PowerToysUserSetup-0.91.1-x64.exe
[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.91.1/PowerToysUserSetup-0.91.1-arm64.exe
[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.91.1/PowerToysSetup-0.91.1-x64.exe
[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.91.1/PowerToysSetup-0.91.1-arm64.exe
| Description | Filename | sha256 hash |
|----------------|----------|-------------|
| Per user - x64 | [PowerToysUserSetup-0.91.0-x64.exe][ptUserX64] | 190DD702EDE2D3AC27A253DF8BC2416B1AF05E6594FF25CABEE844E6D3C8CCB0 |
| Per user - ARM64 | [PowerToysUserSetup-0.91.0-arm64.exe][ptUserArm64] | BE6C964C40147B5F7838E51A13837347756CC45E6AC5BC0DD11AF9AF605ABDCD |
| Machine wide - x64 | [PowerToysSetup-0.91.0-x64.exe][ptMachineX64] | 2308D896D9A66C56B98AC8B3CE9B7C945C7A2315551E36C118C7ECAC4A6D05C2 |
| Machine wide - ARM64 | [PowerToysSetup-0.91.0-arm64.exe][ptMachineArm64] | 28BD1FEFA22C52279C6B600E677B425B014D1F9190EA449D6C63FC2702092DA3 |
| Per user - x64 | [PowerToysUserSetup-0.91.1-x64.exe][ptUserX64] | 42EA4A3E8C79A5456476D19E72B3E2AB9393A89C4DC7442EB7EE5A1E3490D38A |
| Per user - ARM64 | [PowerToysUserSetup-0.91.1-arm64.exe][ptUserArm64] | F3F433FE04049F9197411D792AADEBF34E3BE7FE16327BD8B73D2A046ED8BAF6 |
| Machine wide - x64 | [PowerToysSetup-0.91.1-x64.exe][ptMachineX64] | EC4BC3A8625775866B0ED4577CCF83E6EC7B1A0AD267379DDBAF4FE49C7B5BDD |
| Machine wide - ARM64 | [PowerToysSetup-0.91.1-arm64.exe][ptMachineArm64] | 9CB8911008420D0E446AE3D5CE365E447FA4DF9DCF9337F3A80F933C00FC3689 |
This is our preferred method.

View File

@@ -7,6 +7,6 @@
<CsWinRTAotWarningLevel>2</CsWinRTAotWarningLevel>
<!-- Suppress DynamicallyAccessedMemberTypes.PublicParameterlessConstructor in fallback code path of Windows SDK projection -->
<WarningsNotAsErrors>IL2081;$(WarningsNotAsErrors)</WarningsNotAsErrors>
<WarningsNotAsErrors>IL2081;CsWinRT1028;$(WarningsNotAsErrors)</WarningsNotAsErrors>
</PropertyGroup>
</Project>

View File

@@ -92,6 +92,9 @@ namespace Microsoft.PowerToys.FilePreviewCommon
var run = 0;
var chunksLen = fileSize - QOI_PADDING_LENGTH;
var x = 0;
var rowAdd = bitmapData.Stride - (channels * bitmapData.Width);
for (var dataIndex = 0; dataIndex < dataLength; dataIndex += channels)
{
if (run > 0)
@@ -153,6 +156,14 @@ namespace Microsoft.PowerToys.FilePreviewCommon
bitmapPixel[3] = pixel.A;
}
}
x++;
if (x == bitmapData.Width)
{
// We align dataIndex with the stride
dataIndex += rowAdd;
x = 0;
}
}
bitmap.UnlockBits(bitmapData);

View File

@@ -6,9 +6,10 @@ using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using PowerToys.Interop;
namespace ManagedCommon
@@ -28,9 +29,7 @@ namespace ManagedCommon
* If you want to publish it with Native AOT enabled (or publish as a single file).
* You need to find another way to remove Assembly.Location usage.
*/
#pragma warning disable IL3000 // Avoid accessing Assembly file path when publishing as a single file
private static readonly string Version = FileVersionInfo.GetVersionInfo(Assembly.Location).ProductVersion;
#pragma warning restore IL3000 // Avoid accessing Assembly file path when publishing as a single file
private static readonly string Version = "2.0.0";
/// <summary>
/// Initializes the logger and sets the path for logging.
@@ -40,25 +39,72 @@ namespace ManagedCommon
/// <param name="isLocalLow">If the process using Logger is a low-privilege process.</param>
public static void InitializeLogger(string applicationLogPath, bool isLocalLow = false)
{
string basePath;
if (isLocalLow)
{
applicationLogPath = Environment.GetEnvironmentVariable("userprofile") + "\\appdata\\LocalLow\\Microsoft\\PowerToys" + applicationLogPath + "\\" + Version;
basePath = Environment.GetEnvironmentVariable("userprofile") + "\\appdata\\LocalLow\\Microsoft\\PowerToys" + applicationLogPath;
}
else
{
applicationLogPath = Constants.AppDataPath() + applicationLogPath + "\\" + Version;
basePath = Constants.AppDataPath() + applicationLogPath;
}
if (!Directory.Exists(applicationLogPath))
string versionedPath = Path.Combine(basePath, Version);
if (!Directory.Exists(versionedPath))
{
Directory.CreateDirectory(applicationLogPath);
Directory.CreateDirectory(versionedPath);
}
var logFilePath = Path.Combine(applicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".log");
var logFilePath = Path.Combine(versionedPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".log");
Trace.Listeners.Add(new TextWriterTraceListener(logFilePath));
Trace.AutoFlush = true;
// Clean up old version log folders
Task.Run(() => DeleteOldVersionLogFolders(basePath, versionedPath));
}
/// <summary>
/// Deletes old version log folders, keeping only the current version's folder.
/// </summary>
/// <param name="basePath">The base path to the log files folder.</param>
/// <param name="currentVersionPath">The path to the current version's log folder.</param>
private static void DeleteOldVersionLogFolders(string basePath, string currentVersionPath)
{
try
{
if (!Directory.Exists(basePath))
{
return;
}
var dirs = Directory.GetDirectories(basePath)
.Select(d => new DirectoryInfo(d))
.OrderBy(d => d.CreationTime)
.Where(d => !string.Equals(d.FullName, currentVersionPath, StringComparison.OrdinalIgnoreCase))
.Take(3)
.ToList();
foreach (var directory in dirs)
{
try
{
Directory.Delete(directory.FullName, true);
LogInfo($"Deleted old log directory: {directory.FullName}");
Task.Delay(500).Wait();
}
catch (Exception ex)
{
LogError($"Failed to delete old log directory: {directory.FullName}", ex);
}
}
}
catch (Exception ex)
{
LogError("Error cleaning up old log folders", ex);
}
}
public static void LogError(string message, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "", [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0)

View File

@@ -37,17 +37,6 @@ namespace Microsoft.PowerToys.Telemetry
public void WriteEvent<T>(T telemetryEvent)
where T : EventBase, IEvent
{
if (DataDiagnosticsSettings.GetEnabledValue())
{
this.Write<T>(
telemetryEvent.EventName,
new EventSourceOptions()
{
Keywords = ProjectKeywordMeasure,
Tags = ProjectTelemetryTagProductAndServicePerformance,
},
telemetryEvent);
}
}
}
}

View File

@@ -152,6 +152,7 @@
</data>
<data name="ClipboardHistoryImage" xml:space="preserve">
<value>Image data</value>
<comment>Label used to represent an image in the clipboard history</comment>
</data>
<data name="ClipboardHistoryItemMoreOptionsButton.[using:Microsoft.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>More options</value>
@@ -194,15 +195,19 @@
</data>
<data name="TranscodeToMp3" xml:space="preserve">
<value>Transcode to .mp3</value>
<comment>Option to transcode audio files to MP3 format</comment>
</data>
<data name="TranscodeToMp4" xml:space="preserve">
<value>Transcode to .mp4 (H.264/AAC)</value>
<comment>Option to transcode video files to MP4 format with H.264 video codec and AAC audio codec</comment>
</data>
<data name="TranscodeErrorGeneral" xml:space="preserve">
<value>An error occurred while transcoding media file</value>
<comment>Error message displayed when media conversion fails for an unknown or general reason</comment>
</data>
<data name="TranscodeErrorUnsupportedCodec" xml:space="preserve">
<value>The media file contains an unsupported codec</value>
<comment>Error message displayed when media conversion fails due to an unsupported codec in the source file</comment>
</data>
<data name="PasteButtonAutomation.[using:Microsoft.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Paste</value>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.7.250401001\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.7.250401001\build\native\Microsoft.WindowsAppSDK.props')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.7.250513003\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.7.250513003\build\native\Microsoft.WindowsAppSDK.props')" />
<Import Project="..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.props')" />
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
<PropertyGroup Label="Globals">
@@ -141,7 +141,7 @@
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.7.250401001\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.7.250401001\build\native\Microsoft.WindowsAppSDK.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.7.250513003\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.7.250513003\build\native\Microsoft.WindowsAppSDK.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
@@ -153,7 +153,7 @@
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.7.250401001\build\native\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.7.250401001\build\native\Microsoft.WindowsAppSDK.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.7.250401001\build\native\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.7.250401001\build\native\Microsoft.WindowsAppSDK.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.7.250513003\build\native\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.7.250513003\build\native\Microsoft.WindowsAppSDK.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.7.250513003\build\native\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.7.250513003\build\native\Microsoft.WindowsAppSDK.targets'))" />
</Target>
</Project>

View File

@@ -4,5 +4,5 @@
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.231216.1" targetFramework="native" />
<package id="Microsoft.Windows.SDK.BuildTools" version="10.0.22621.2428" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK" version="1.7.250401001" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK" version="1.7.250513003" targetFramework="native" />
</packages>

View File

@@ -13,6 +13,7 @@
#include <common/utils/winapi_error.h>
#include <common/utils/processApi.h>
#include <common/utils/elevation.h>
#include <common/utils/logger_helper.h>
HINSTANCE g_hInst_MouseWithoutBorders = 0;
@@ -409,9 +410,12 @@ public:
{
app_name = L"MouseWithoutBorders";
app_key = app_name;
std::filesystem::path logFilePath(PTSettingsHelper::get_module_save_folder_location(app_key));
logFilePath.append(LogSettings::mouseWithoutBordersLogPath);
Logger::init(LogSettings::mouseWithoutBordersLoggerName, logFilePath.wstring(), PTSettingsHelper::get_log_settings_file_location());
LoggerHelpers::init_logger(app_key, L"ModuleInterface", LogSettings::mouseWithoutBordersLoggerName);
std::filesystem::path oldLogPath(PTSettingsHelper::get_module_save_folder_location(app_key));
oldLogPath.append("LogsModuleInterface");
LoggerHelpers::delete_old_log_folder(oldLogPath);
try
{
@@ -567,7 +571,7 @@ public:
executable_args.append(L"\" & echo \"Adding an inbound firewall rule for PowerToys.MouseWithoutBorders.exe\"");
executable_args.append(L" & netsh advfirewall firewall add rule name=\"PowerToys.MouseWithoutBorders\" dir=in action=allow program=\"");
executable_args.append(executable_path);
executable_args.append(L"\" enable=yes remoteip=LocalSubnet profile=any protocol=tcp & pause\"");
executable_args.append(L"\" enable=yes remoteip=any profile=any protocol=tcp & pause\"");
SHELLEXECUTEINFOW sei{ sizeof(sei) };
sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI };

View File

@@ -193,27 +193,29 @@ namespace WorkspacesEditor.ViewModels
ApplyShortcut(editedProject);
}
private string GetDesktopShortcutAddress(Project project) => Path.Combine(FolderUtils.Desktop(), project.Name + ".lnk");
private string GetShortcutStoreAddress(Project project)
{
var dataFolder = FolderUtils.DataFolder();
Directory.CreateDirectory(dataFolder);
var shortcutStoreFolder = Path.Combine(dataFolder, "WorkspacesIcons");
Directory.CreateDirectory(shortcutStoreFolder);
return Path.Combine(shortcutStoreFolder, project.Id + ".ico");
}
private void ApplyShortcut(Project project)
{
string basePath = AppDomain.CurrentDomain.BaseDirectory;
string shortcutAddress = Path.Combine(FolderUtils.Desktop(), project.Name + ".lnk");
string shortcutIconFilename = Path.Combine(FolderUtils.Temp(), project.Id + ".ico");
if (!project.IsShortcutNeeded)
{
if (File.Exists(shortcutIconFilename))
{
File.Delete(shortcutIconFilename);
}
if (File.Exists(shortcutAddress))
{
File.Delete(shortcutAddress);
}
RemoveShortcut(project);
return;
}
var basePath = AppDomain.CurrentDomain.BaseDirectory;
var shortcutAddress = GetDesktopShortcutAddress(project);
var shortcutIconFilename = GetShortcutStoreAddress(project);
Bitmap icon = WorkspacesIcon.DrawIcon(WorkspacesIcon.IconTextFromProjectName(project.Name), App.ThemeManager.GetCurrentTheme());
WorkspacesIcon.SaveIcon(icon, shortcutIconFilename);
@@ -360,8 +362,8 @@ namespace WorkspacesEditor.ViewModels
private void RemoveShortcut(Project selectedProject)
{
string shortcutAddress = Path.Combine(FolderUtils.Desktop(), selectedProject.Name + ".lnk");
string shortcutIconFilename = Path.Combine(FolderUtils.Temp(), selectedProject.Id + ".ico");
string shortcutAddress = GetDesktopShortcutAddress(selectedProject);
string shortcutIconFilename = GetShortcutStoreAddress(selectedProject);
if (File.Exists(shortcutIconFilename))
{

View File

@@ -25,6 +25,7 @@ namespace AppLauncher
const std::wstring ChromeFilename = L"chrome.exe";
const std::wstring ChromePwaFilename = L"chrome_proxy.exe";
const std::wstring PwaCommandLineAddition = L"--profile-directory=Default --app-id=";
const std::wstring SteamProtocolPrefix = L"steam:";
}
Result<SHELLEXECUTEINFO, std::wstring> LaunchApp(const std::wstring& appPath, const std::wstring& commandLineArgs, bool elevated)
@@ -134,12 +135,11 @@ namespace AppLauncher
}
}
// win32 app with appUserModelId:
// usage example: steam games
if (!launched && !app.appUserModelId.empty())
// protocol launch for steam
if (!launched && !app.appUserModelId.empty() && app.appUserModelId.contains(NonLocalizable::SteamProtocolPrefix))
{
Logger::trace(L"Launching {} as {}", app.name, app.appUserModelId);
auto res = LaunchApp(L"shell:AppsFolder\\" + app.appUserModelId, app.commandLineArgs, app.isElevated);
auto res = LaunchApp(app.appUserModelId, app.commandLineArgs, app.isElevated);
if (res.isOk())
{
launched = true;

View File

@@ -0,0 +1,24 @@
#include "pch.h"
#include "Calculator.h"
#include "Calculator.g.cpp"
#include "ExprtkEvaluator.h"
namespace winrt::CmdPalCalculator::implementation
{
Calculator::Calculator(winrt::Windows::Foundation::Collections::IPropertySet const& constants)
{
for (auto const& pair : constants)
{
auto key = pair.Key();
auto value = winrt::unbox_value<double>(pair.Value());
m_constants.emplace(key.c_str(), value);
}
}
hstring Calculator::EvaluateExpression(hstring const& expression)
{
auto result = ExprtkCalculator::internal::EvaluateExpression(expression.c_str(), m_constants);
return hstring(result);
}
}

View File

@@ -0,0 +1,25 @@
#pragma once
#include "Calculator.g.h"
namespace winrt::CmdPalCalculator::implementation
{
struct Calculator : CalculatorT<Calculator>
{
Calculator() = default;
Calculator(winrt::Windows::Foundation::Collections::IPropertySet const& constants);
winrt::hstring EvaluateExpression(winrt::hstring const& expression);
private:
std::unordered_map<std::wstring, double> m_constants;
};
}
namespace winrt::CmdPalCalculator::factory_implementation
{
struct Calculator : CalculatorT<Calculator, implementation::Calculator>
{
};
}

View File

@@ -0,0 +1,10 @@
namespace CmdPalCalculator
{
[default_interface]
runtimeclass Calculator
{
Calculator();
Calculator(Windows.Foundation.Collections.IPropertySet constants);
String EvaluateExpression(String expression);
}
}

View File

@@ -0,0 +1,3 @@
EXPORTS
DllCanUnloadNow = WINRT_CanUnloadNow PRIVATE
DllGetActivationFactory = WINRT_GetActivationFactory PRIVATE

View File

@@ -0,0 +1,181 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
<PropertyGroup Label="Globals">
<CppWinRTRootNamespaceAutoMerge>true</CppWinRTRootNamespaceAutoMerge>
<CppWinRTGenerateWindowsMetadata>true</CppWinRTGenerateWindowsMetadata>
<ProjectGuid>{2cf78cf7-8feb-4be1-9591-55fa25b48fc6}</ProjectGuid>
<ProjectName>CmdPalCalculator</ProjectName>
<RootNamespace>CmdPalCalculator</RootNamespace>
<AppxPackage>false</AppxPackage>
</PropertyGroup>
<!-- BEGIN common.build.pre.props -->
<PropertyGroup Label="Configuration">
<EnableHybridCRT>true</EnableHybridCRT>
<UseCrtSDKReferenceStaticWarning Condition="'$(EnableHybridCRT)'=='true'">false</UseCrtSDKReferenceStaticWarning>
</PropertyGroup>
<!-- END common.build.pre.props -->
<!-- BEGIN cppwinrt.build.pre.props -->
<PropertyGroup Label="Globals">
<CppWinRTEnabled>true</CppWinRTEnabled>
<CppWinRTOptimized>true</CppWinRTOptimized>
<DefaultLanguage>en-US</DefaultLanguage>
<MinimumVisualStudioVersion>17.0</MinimumVisualStudioVersion>
<ApplicationTypeRevision>10.0</ApplicationTypeRevision>
</PropertyGroup>
<PropertyGroup>
<MinimalCoreWin>true</MinimalCoreWin>
<AppContainerApplication>true</AppContainerApplication>
<WindowsStoreApp>true</WindowsStoreApp>
<ApplicationType>Windows Store</ApplicationType>
<UseCrtSDKReference Condition="'$(EnableHybridCRT)'=='true'">false</UseCrtSDKReference>
<!-- The SDK reference breaks the Hybrid CRT -->
</PropertyGroup>
<PropertyGroup>
<!-- We have to use the Desktop platform for Hybrid CRT to work. -->
<_VC_Target_Library_Platform>Desktop</_VC_Target_Library_Platform>
<_NoWinAPIFamilyApp>true</_NoWinAPIFamilyApp>
</PropertyGroup>
<!-- END cppwinrt.build.pre.props -->
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<UseDebugLibraries>true</UseDebugLibraries>
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets">
<Import Project="PropertySheet.props" />
</ImportGroup>
<PropertyGroup>
<TargetName>CmdPalCalculator</TargetName>
<OutDir>..\..\..\..\$(Platform)\$(Configuration)\</OutDir>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
<WarningLevel>Level4</WarningLevel>
<AdditionalOptions>%(AdditionalOptions) /bigobj</AdditionalOptions>
<PreprocessorDefinitions>_WINRT_DLL;WINRT_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>../../..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
<ModuleDefinitionFile>CmdPalCalculator.def</ModuleDefinitionFile>
<AdditionalDependencies>Shell32.lib;user32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
<ClCompile>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
<ClCompile>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="ExprtkEvaluator.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="exprtk.hpp" />
<ClInclude Include="Calculator.h">
<DependentUpon>Calculator.idl</DependentUpon>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="ExprtkEvaluator.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="pch.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="Calculator.cpp">
<DependentUpon>Calculator.idl</DependentUpon>
</ClCompile>
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
</ItemGroup>
<ItemGroup>
<Midl Include="Calculator.idl" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
<None Include="CmdPalCalculator.def" />
</ItemGroup>
<ItemGroup>
<None Include="PropertySheet.props" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Import Project="..\..\..\..\deps\spdlog.props" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
</Target>
<!-- BEGIN common.build.post.props -->
<!--
The Hybrid CRT model statically links the runtime and STL and dynamically
links the UCRT instead of the VC++ CRT. The UCRT ships with Windows.
WinAppSDK asserts that this is "supported according to the CRT maintainer."
This must come before Microsoft.Cpp.targets because it manipulates ClCompile.RuntimeLibrary.
-->
<ItemDefinitionGroup Condition="'$(EnableHybridCRT)'=='true' and '$(Configuration)'=='Debug'">
<ClCompile>
<!-- We use MultiThreadedDebug, rather than MultiThreadedDebugDLL, to avoid DLL dependencies on VCRUNTIME140d.dll and MSVCP140d.dll. -->
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<LanguageStandard Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<!-- Link statically against the runtime and STL, but link dynamically against the CRT by ignoring the static CRT
lib and instead linking against the Universal CRT DLL import library. This "hybrid" linking mechanism is
supported according to the CRT maintainer. Dynamic linking against the CRT makes the binaries a bit smaller
than they would otherwise be if the CRT, runtime, and STL were all statically linked in. -->
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries);libucrtd.lib</IgnoreSpecificDefaultLibraries>
<AdditionalOptions>%(AdditionalOptions) /defaultlib:ucrtd.lib</AdditionalOptions>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(EnableHybridCRT)'=='true' and ('$(Configuration)'=='Release' or '$(Configuration)'=='AuditMode')">
<ClCompile>
<!-- We use MultiThreaded, rather than MultiThreadedDLL, to avoid DLL dependencies on VCRUNTIME140.dll and MSVCP140.dll. -->
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
<!-- Link statically against the runtime and STL, but link dynamically against the CRT by ignoring the static CRT
lib and instead linking against the Universal CRT DLL import library. This "hybrid" linking mechanism is
supported according to the CRT maintainer. Dynamic linking against the CRT makes the binaries a bit smaller
than they would otherwise be if the CRT, runtime, and STL were all statically linked in. -->
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries);libucrt.lib</IgnoreSpecificDefaultLibraries>
<AdditionalOptions>%(AdditionalOptions) /defaultlib:ucrt.lib</AdditionalOptions>
</Link>
</ItemDefinitionGroup>
<!-- END common.build.post.props -->
</Project>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Resources">
<UniqueIdentifier>accd3aa8-1ba0-4223-9bbe-0c431709210b</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tga;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="Generated Files">
<UniqueIdentifier>{926ab91d-31b4-48c3-b9a4-e681349f27f0}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp" />
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
</ItemGroup>
<ItemGroup>
<Midl Include="Calculator.idl" />
</ItemGroup>
<ItemGroup>
<None Include="CmdPalCalculator.def" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<None Include="PropertySheet.props" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,46 @@
#include "ExprtkEvaluator.h"
#include "exprtk.hpp"
#include <iomanip>
#include <iostream>
#include <sstream>
namespace ExprtkCalculator::internal
{
std::wstring ToWStringFullPrecision(double value)
{
std::wostringstream oss;
oss << std::fixed << std::setprecision(15) << value;
return oss.str();
}
std::string WStringToAscii(const std::wstring& wstr)
{
std::string result;
result.reserve(wstr.size());
for (wchar_t wc : wstr)
result.push_back(static_cast<char>(wc));
return result;
}
std::wstring EvaluateExpression(
const std::wstring& wexpr,
const std::unordered_map<std::wstring, double>& constants)
{
exprtk::symbol_table<double> symbol_table;
for (auto const& [name, value] : constants)
{
symbol_table.add_constant(WStringToAscii(name.c_str()), value);
}
exprtk::expression<double> expression;
expression.register_symbol_table(symbol_table);
exprtk::parser<double> parser;
if (!parser.compile(WStringToAscii(wexpr), expression))
return L"NaN";
return ToWStringFullPrecision(expression.value());
}
}

View File

@@ -0,0 +1,10 @@
#pragma once
#include <string>
#include <unordered_map>
namespace ExprtkCalculator::internal
{
std::wstring EvaluateExpression(
const std::wstring& expression,
const std::unordered_map<std::wstring, double>& constants = {});
}

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ImportGroup Label="PropertySheets" />
<PropertyGroup Label="UserMacros" />
<!--
To customize common C++/WinRT project properties:
* right-click the project node
* expand the Common Properties item
* select the C++/WinRT property page
For more advanced scenarios, and complete documentation, please see:
https://github.com/Microsoft/cppwinrt/tree/master/nuget
-->
<PropertyGroup />
<ItemDefinitionGroup />
</Project>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.220531.1" targetFramework="native" />
</packages>

View File

@@ -0,0 +1 @@
#include "pch.h"

View File

@@ -0,0 +1,4 @@
#pragma once
#include <unknwn.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>

View File

@@ -0,0 +1,23 @@
========================================================================
C++/WinRT CmdPalCalculator Project Overview
========================================================================
This project demonstrates how to get started authoring Windows Runtime
classes directly with standard C++, using the C++/WinRT SDK component
to generate implementation headers from interface (IDL) files. The
generated Windows Runtime component binary and WinMD files should then
be bundled with the Universal Windows Platform (UWP) app consuming them.
Steps:
1. Create an interface (IDL) file to define your Windows Runtime class,
its default interface, and any other interfaces it implements.
2. Build the project once to generate module.g.cpp, module.h.cpp, and
implementation templates under the "Generated Files" folder, as
well as skeleton class definitions under "Generated Files\sources".
3. Use the skeleton class definitions for reference to implement your
Windows Runtime classes.
========================================================================
Learn more about C++/WinRT here:
http://aka.ms/cppwinrt/
========================================================================

View File

@@ -217,7 +217,9 @@ public:
CmdPal::m_enabled.store(true);
std::wstring packageName = L"Microsoft.CommandPalette";
std::wstring launchPath = L"x-cmdpal://background";
// Launch CmdPal as normal user using explorer
std::wstring launchPath = L"explorer.exe";
std::wstring launchArgs = L"x-cmdpal://background";
#ifdef IS_DEV_BRANDING
packageName = L"Microsoft.CommandPalette.Dev";
#endif
@@ -268,13 +270,13 @@ public:
if (!firstEnableCall)
{
Logger::trace("Not first attempt, try to launch");
LaunchApp(launchPath, L"", false /*no elevated*/, false /*error pop up*/);
LaunchApp(launchPath, launchArgs, false /*no elevated*/, false /*error pop up*/);
}
else
{
// If first time enable, do retry launch.
Logger::trace("First attempt, try to launch");
std::thread launchThread(&CmdPal::RetryLaunch, launchPath);
std::thread launchThread(&CmdPal::RetryLaunch, launchPath, launchArgs);
launchThread.detach();
}
@@ -289,14 +291,14 @@ public:
CmdPal::m_enabled.store(false);
}
static void RetryLaunch(std::wstring path)
static void RetryLaunch(std::wstring path, std::wstring cmdArgs)
{
const int base_delay_milliseconds = 1000;
int max_retry = 9; // 2**9 - 1 seconds. Control total wait time within 10 min.
int retry = 0;
do
{
auto launch_result = LaunchApp(path, L"", false, retry < max_retry);
auto launch_result = LaunchApp(path, cmdArgs, false, retry < max_retry);
if (launch_result)
{
Logger::info(L"CmdPal launched successfully after {} retries.", retry);
@@ -348,4 +350,4 @@ std::atomic<bool> CmdPal::m_launched{ false };
extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create()
{
return new CmdPal();
}
}

View File

@@ -1,4 +1,4 @@
<Project>
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
@@ -9,7 +9,7 @@
<PackageVersion Include="Microsoft.Windows.CsWin32" Version="0.2.46-beta" />
<PackageVersion Include="Microsoft.Windows.CsWinRT" Version="2.2.0" />
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.2428" />
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.7.250401001" />
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.7.250513003" />
<PackageVersion Include="Shmuelie.WinRTServer" Version="2.1.1" />
<PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
<PackageVersion Include="System.Text.Json" Version="9.0.5" />

View File

@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<RootNamespace>TemplateCmdPalExtension</RootNamespace>
@@ -56,13 +56,35 @@
</PropertyGroup>
<PropertyGroup>
<PublishSingleFile>true</PublishSingleFile>
<IsAotCompatible>true</IsAotCompatible>
<CsWinRTAotOptimizerEnabled>true</CsWinRTAotOptimizerEnabled>
<CsWinRTAotWarningLevel>2</CsWinRTAotWarningLevel>
<!-- Suppress DynamicallyAccessedMemberTypes.PublicParameterlessConstructor in fallback code path of Windows SDK projection -->
<WarningsNotAsErrors>IL2081</WarningsNotAsErrors>
<WarningsNotAsErrors>IL2081;$(WarningsNotAsErrors)</WarningsNotAsErrors>
<!-- When publishing trimmed, make sure to treat trimming warnings as build errors -->
<ILLinkTreatWarningsAsErrors>true</ILLinkTreatWarningsAsErrors>
<PublishTrimmed>true</PublishTrimmed>
<PublishSingleFile>true</PublishSingleFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
<!-- In Debug builds, trimming is disabled by default, but all the trim &
AOT warnings are enabled. This gives debug builds a tighter inner loop,
while at least warning about future trim violations -->
<PublishTrimmed>false</PublishTrimmed>
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
<EnableSingleFileAnalyzer>true</EnableSingleFileAnalyzer>
<EnableAotAnalyzer>true</EnableAotAnalyzer>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'!='Debug'">
<!-- In Release builds, trimming is enabled by default.
feel free to disable this if needed -->
<PublishTrimmed>true</PublishTrimmed>
</PropertyGroup>
</Project>

View File

@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\Common.Dotnet.CsWinRT.props" />
<Import Project="..\..\..\Common.Dotnet.AotCompatibility.props" />
<PropertyGroup>

View File

@@ -3,11 +3,6 @@
// See the LICENSE file in the project root for more information.
using System.Text.Json;
using AdaptiveCards.ObjectModel.WinUI3;
using AdaptiveCards.Templating;
using CommunityToolkit.Mvvm.Messaging;
using ManagedCommon;
using Microsoft.CmdPal.UI.ViewModels.Messages;
using Microsoft.CmdPal.UI.ViewModels.Models;
using Microsoft.CommandPalette.Extensions;
using Windows.Data.Json;
@@ -27,145 +22,14 @@ public partial class ContentFormViewModel(IFormContent _form, WeakReference<IPag
public string DataJson { get; protected set; } = "{}";
public AdaptiveCardParseResult? Card { get; private set; }
private static string Serialize(string? s) =>
JsonSerializer.Serialize(s, JsonSerializationContext.Default.String);
private static bool TryBuildCard(
string templateJson,
string dataJson,
out AdaptiveCardParseResult? card,
out Exception? error)
{
card = null;
error = null;
try
{
var template = new AdaptiveCardTemplate(templateJson);
var cardJson = template.Expand(dataJson);
card = AdaptiveCard.FromJsonString(cardJson);
return true;
}
catch (Exception ex)
{
Logger.LogError("Error building card from template: {Message}", ex.Message);
error = ex;
return false;
}
}
public override void InitializeProperties()
{
var model = _formModel.Unsafe;
if (model is null)
{
return;
}
}
TemplateJson = model.TemplateJson;
StateJson = model.StateJson;
DataJson = model.DataJson;
if (TryBuildCard(TemplateJson, DataJson, out var builtCard, out var renderingError))
{
Card = builtCard;
UpdateProperty(nameof(Card));
return;
}
var errorPayload = $$"""
public void HandleSubmit()
{
"error_message": {{Serialize(renderingError!.Message)}},
"error_stack": {{Serialize(renderingError.StackTrace)}},
"inner_exception": {{Serialize(renderingError.InnerException?.Message)}},
"template_json": {{Serialize(TemplateJson)}},
"data_json": {{Serialize(DataJson)}}
}
""";
if (TryBuildCard(ErrorCardJson, errorPayload, out var errorCard, out var _))
{
Card = errorCard;
UpdateProperty(nameof(Card));
return;
}
UpdateProperty(nameof(Card));
}
public void HandleSubmit(IAdaptiveActionElement action, JsonObject inputs)
{
if (action is AdaptiveOpenUrlAction openUrlAction)
{
WeakReferenceMessenger.Default.Send<LaunchUriMessage>(new(openUrlAction.Url));
return;
}
if (action is AdaptiveSubmitAction or AdaptiveExecuteAction)
{
// Get the data and inputs
var dataString = (action as AdaptiveSubmitAction)?.DataJson.Stringify() ?? string.Empty;
var inputString = inputs.Stringify();
_ = Task.Run(() =>
{
try
{
var model = _formModel.Unsafe!;
if (model != null)
{
var result = model.SubmitForm(inputString, dataString);
WeakReferenceMessenger.Default.Send<HandleCommandResultMessage>(new(new(result)));
}
}
catch (Exception ex)
{
ShowException(ex);
}
});
}
}
private static readonly string ErrorCardJson = """
{
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.5",
"body": [
{
"type": "TextBlock",
"text": "Error parsing form from extension",
"wrap": true,
"style": "heading",
"size": "ExtraLarge",
"weight": "Bolder",
"color": "Attention"
},
{
"type": "TextBlock",
"wrap": true,
"text": "${error_message}",
"color": "Attention"
},
{
"type": "TextBlock",
"text": "${error_stack}",
"fontType": "Monospace"
},
{
"type": "TextBlock",
"wrap": true,
"text": "Inner exception:"
},
{
"type": "TextBlock",
"wrap": true,
"text": "${inner_exception}",
"color": "Attention"
}
]
}
""";
}

View File

@@ -0,0 +1,42 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.CmdPal.UI.ViewModels.Models;
using Microsoft.CommandPalette.Extensions;
namespace Microsoft.CmdPal.UI.ViewModels;
public partial class DetailsCommandsViewModel(
IDetailsElement _detailsElement,
WeakReference<IPageContext> context) : DetailsElementViewModel(_detailsElement, context)
{
public List<CommandViewModel> Commands { get; private set; } = [];
public bool HasCommands => Commands.Count > 0;
private readonly ExtensionObject<IDetailsCommands> _dataModel =
new(_detailsElement.Data as IDetailsCommands);
public override void InitializeProperties()
{
base.InitializeProperties();
var model = _dataModel.Unsafe;
if (model == null)
{
return;
}
Commands = model
.Commands?
.Select(c =>
{
var vm = new CommandViewModel(c, PageContext);
vm.InitializeProperties();
return vm;
})
.ToList() ?? [];
UpdateProperty(nameof(HasCommands));
UpdateProperty(nameof(Commands));
}
}

View File

@@ -49,6 +49,7 @@ public partial class DetailsViewModel(IDetails _details, WeakReference<IPageCont
{
IDetailsSeparator => new DetailsSeparatorViewModel(element, this.PageContext),
IDetailsLink => new DetailsLinkViewModel(element, this.PageContext),
IDetailsCommands => new DetailsCommandsViewModel(element, this.PageContext),
IDetailsTags => new DetailsTagsViewModel(element, this.PageContext),
_ => null,
};

View File

@@ -107,17 +107,17 @@ public partial class ListItemViewModel(IListItem model, WeakReference<IPageConte
private void UpdateTags(ITag[]? newTagsFromModel)
{
var newTags = newTagsFromModel?.Select(t =>
{
var vm = new TagViewModel(t, PageContext);
vm.InitializeProperties();
return vm;
})
.ToList() ?? [];
DoOnUiThread(
() =>
{
var newTags = newTagsFromModel?.Select(t =>
{
var vm = new TagViewModel(t, PageContext);
vm.InitializeProperties();
return vm;
})
.ToList() ?? [];
// Tags being an ObservableCollection instead of a List lead to
// many COM exception issues.
Tags = new(newTags);

View File

@@ -17,8 +17,6 @@
<ItemGroup>
<PackageReference Include="CommunityToolkit.Common" />
<PackageReference Include="CommunityToolkit.Mvvm" />
<PackageReference Include="AdaptiveCards.Templating" />
<PackageReference Include="AdaptiveCards.ObjectModel.WinUI3" GeneratePathProperty="true" />
<PackageReference Include="Microsoft.Windows.CsWin32">
<PrivateAssets>all</PrivateAssets>

View File

@@ -4,7 +4,6 @@
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using CommunityToolkit.Common;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
@@ -109,13 +108,11 @@ public partial class ShellViewModel(IServiceProvider _serviceProvider, TaskSched
// TODO GH #239 switch back when using the new MD text block
// _ = _queue.EnqueueAsync(() =>
_ = Task.Factory.StartNew(
async () =>
() =>
{
// bool f = await viewModel.InitializeCommand.ExecutionTask.;
// var result = viewModel.InitializeCommand.ExecutionTask.GetResultOrDefault()!;
// var result = viewModel.InitializeCommand.ExecutionTask.GetResultOrDefault<bool?>()!;
var result = await viewModel.InitializeAsync();
CurrentPage = viewModel; // result ? viewModel : null;
////LoadedState = result ? ViewModelLoadedState.Loaded : ViewModelLoadedState.Error;
},

View File

@@ -11,6 +11,7 @@
<!-- Other merged dictionaries here -->
<ResourceDictionary Source="Styles/Button.xaml" />
<ResourceDictionary Source="Styles/Colors.xaml" />
<ResourceDictionary Source="Styles/TextBlock.xaml" />
<ResourceDictionary Source="Styles/TextBox.xaml" />
<ResourceDictionary Source="Styles/Settings.xaml" />
<ResourceDictionary Source="Controls/Tag.xaml" />

View File

@@ -8,6 +8,7 @@ using Microsoft.CmdPal.Common.Services;
using Microsoft.CmdPal.Ext.Apps;
using Microsoft.CmdPal.Ext.Bookmarks;
using Microsoft.CmdPal.Ext.Calc;
using Microsoft.CmdPal.Ext.ClipboardHistory;
using Microsoft.CmdPal.Ext.Indexer;
using Microsoft.CmdPal.Ext.Registry;
using Microsoft.CmdPal.Ext.Shell;
@@ -19,6 +20,7 @@ using Microsoft.CmdPal.Ext.WindowsSettings;
using Microsoft.CmdPal.Ext.WindowsTerminal;
using Microsoft.CmdPal.Ext.WindowWalker;
using Microsoft.CmdPal.Ext.WinGet;
using Microsoft.CmdPal.UI.Helpers;
using Microsoft.CmdPal.UI.ViewModels;
using Microsoft.CmdPal.UI.ViewModels.BuiltinCommands;
using Microsoft.CmdPal.UI.ViewModels.Models;
@@ -96,14 +98,13 @@ public partial class App : Application
var allApps = new AllAppsCommandProvider();
services.AddSingleton<ICommandProvider>(allApps);
services.AddSingleton<ICommandProvider, ShellCommandsProvider>();
services.AddSingleton<ICommandProvider, CalculatorCommandProvider>();
services.AddSingleton<ICommandProvider, IndexerCommandsProvider>();
services.AddSingleton<ICommandProvider, BookmarksCommandProvider>();
// TODO GH #527 re-enable the clipboard commands
// services.AddSingleton<ICommandProvider, ClipboardHistoryCommandsProvider>();
services.AddSingleton<ICommandProvider, ClipboardHistoryCommandsProvider>();
services.AddSingleton<ICommandProvider, WindowWalkerCommandsProvider>();
services.AddSingleton<ICommandProvider, WebSearchCommandsProvider>();
services.AddSingleton<ICommandProvider, CalculatorCommandProvider>();
// GH #38440: Users might not have WinGet installed! Or they might have
// a ridiculously old version. Or might be running as admin.
@@ -140,6 +141,7 @@ public partial class App : Application
var state = AppStateModel.LoadState();
services.AddSingleton(state);
services.AddSingleton<IExtensionService, ExtensionService>();
services.AddSingleton<TrayIconService>();
// ViewModels
services.AddSingleton<ShellViewModel>();

View File

@@ -1,342 +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 AdaptiveCards.Rendering.WinUI3;
namespace Microsoft.CmdPal.UI.Controls;
public sealed class AdaptiveCardsConfig
{
public static AdaptiveHostConfig Light { get; }
public static AdaptiveHostConfig Dark { get; }
static AdaptiveCardsConfig()
{
Light = AdaptiveHostConfig.FromJsonString(LightHostConfigString).HostConfig;
Dark = AdaptiveHostConfig.FromJsonString(DarkHostConfigString).HostConfig;
}
public static readonly string DarkHostConfigString = """
{
"spacing": {
"small": 4,
"default": 8,
"medium": 20,
"large": 30,
"extraLarge": 40,
"padding": 8
},
"separator": {
"lineThickness": 0,
"lineColor": "#C8FFFFFF"
},
"supportsInteractivity": true,
"fontTypes": {
"default": {
"fontFamily": "'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif",
"fontSizes": {
"small": 12,
"default": 12,
"medium": 14,
"large": 20,
"extraLarge": 26
},
"fontWeights": {
"lighter": 200,
"default": 400,
"bolder": 600
}
},
"monospace": {
"fontFamily": "'Courier New', Courier, monospace",
"fontSizes": {
"small": 12,
"default": 12,
"medium": 14,
"large": 18,
"extraLarge": 26
},
"fontWeights": {
"lighter": 200,
"default": 400,
"bolder": 600
}
}
},
"containerStyles": {
"default": {
"backgroundColor": "#00000000",
"borderColor": "#00000000",
"foregroundColors": {
"default": {
"default": "#FFFFFF",
"subtle": "#C8FFFFFF"
},
"accent": {
"default": "#0063B1",
"subtle": "#880063B1"
},
"attention": {
"default": "#FF5555",
"subtle": "#DDFF5555"
},
"good": {
"default": "#54a254",
"subtle": "#DD54a254"
},
"warning": {
"default": "#c3ab23",
"subtle": "#DDc3ab23"
}
}
},
"emphasis": {
"backgroundColor": "#09FFFFFF",
"borderColor": "#09FFFFFF",
"foregroundColors": {
"default": {
"default": "#FFFFFF",
"subtle": "#C8FFFFFF"
},
"accent": {
"default": "#2E89FC",
"subtle": "#882E89FC"
},
"attention": {
"default": "#FF5555",
"subtle": "#DDFF5555"
},
"good": {
"default": "#54a254",
"subtle": "#DD54a254"
},
"warning": {
"default": "#c3ab23",
"subtle": "#DDc3ab23"
}
}
}
},
"imageSizes": {
"small": 16,
"medium": 24,
"large": 32
},
"actions": {
"maxActions": 5,
"spacing": "default",
"buttonSpacing": 8,
"showCard": {
"actionMode": "inline",
"inlineTopMargin": 8
},
"actionsOrientation": "horizontal",
"actionAlignment": "stretch"
},
"adaptiveCard": {
"allowCustomStyle": false
},
"imageSet": {
"imageSize": "medium",
"maxImageHeight": 100
},
"factSet": {
"title": {
"color": "default",
"size": "default",
"isSubtle": false,
"weight": "bolder",
"wrap": true,
"maxWidth": 150
},
"value": {
"color": "default",
"size": "default",
"isSubtle": false,
"weight": "default",
"wrap": true
},
"spacing": 8
},
"textStyles": {
"heading": {
"size": "large",
"weight": "bolder",
"color": "default",
"isSubtle": false,
"fontType": "default"
},
"columnHeader": {
"size": "medium",
"weight": "bolder",
"color": "default",
"isSubtle": false,
"fontType": "default"
}
}
}
""";
public static readonly string LightHostConfigString = """
{
"spacing": {
"small": 4,
"default": 8,
"medium": 20,
"large": 30,
"extraLarge": 40,
"padding": 8
},
"separator": {
"lineThickness": 0,
"lineColor": "#606060"
},
"supportsInteractivity": true,
"fontTypes": {
"default": {
"fontFamily": "'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif",
"fontSizes": {
"small": 12,
"default": 12,
"medium": 14,
"large": 20,
"extraLarge": 26
},
"fontWeights": {
"lighter": 200,
"default": 400,
"bolder": 600
}
},
"monospace": {
"fontFamily": "'Courier New', Courier, monospace",
"fontSizes": {
"small": 12,
"default": 12,
"medium": 14,
"large": 18,
"extraLarge": 26
},
"fontWeights": {
"lighter": 200,
"default": 400,
"bolder": 600
}
}
},
"containerStyles": {
"default": {
"backgroundColor": "#00000000",
"borderColor": "#00000000",
"foregroundColors": {
"default": {
"default": "#E6000000",
"subtle": "#99000000"
},
"accent": {
"default": "#0063B1",
"subtle": "#880063B1"
},
"attention": {
"default": "#C00000",
"subtle": "#DDC00000"
},
"good": {
"default": "#54a254",
"subtle": "#DD54a254"
},
"warning": {
"default": "#c3ab23",
"subtle": "#DDc3ab23"
}
}
},
"emphasis": {
"backgroundColor": "#80F6F6F6",
"borderColor": "#80F6F6F6",
"foregroundColors": {
"default": {
"default": "#E6000000",
"subtle": "#99000000"
},
"accent": {
"default": "#2E89FC",
"subtle": "#882E89FC"
},
"attention": {
"default": "#C00000",
"subtle": "#DDC00000"
},
"good": {
"default": "#54a254",
"subtle": "#DD54a254"
},
"warning": {
"default": "#c3ab23",
"subtle": "#DDc3ab23"
}
}
}
},
"imageSizes": {
"small": 16,
"medium": 24,
"large": 32
},
"actions": {
"maxActions": 5,
"spacing": "default",
"buttonSpacing": 8,
"showCard": {
"actionMode": "inline",
"inlineTopMargin": 8
},
"actionsOrientation": "horizontal",
"actionAlignment": "stretch"
},
"adaptiveCard": {
"allowCustomStyle": false
},
"imageSet": {
"imageSize": "medium",
"maxImageHeight": 100
},
"factSet": {
"title": {
"color": "default",
"size": "default",
"isSubtle": false,
"weight": "bolder",
"wrap": true,
"maxWidth": 150
},
"value": {
"color": "default",
"size": "default",
"isSubtle": false,
"weight": "default",
"wrap": true
},
"spacing": 8
},
"textStyles": {
"heading": {
"size": "large",
"weight": "bolder",
"color": "default",
"isSubtle": false,
"fontType": "default"
},
"columnHeader": {
"size": "medium",
"weight": "bolder",
"color": "default",
"isSubtle": false,
"fontType": "default"
}
}
}
""";
}

View File

@@ -34,8 +34,13 @@
Orientation="Vertical"
Spacing="4" />
<!-- Template for actions in the mode actions dropdown button -->
<DataTemplate x:Key="ContextMenuViewModelTemplate" x:DataType="viewmodels:CommandContextItemViewModel">
<cmdpalUI:ContextItemTemplateSelector
x:Key="ContextItemTemplateSelector"
Critical="{StaticResource CriticalContextMenuViewModelTemplate}"
Default="{StaticResource DefaultContextMenuViewModelTemplate}" />
<!-- Template for context items in the context item menu -->
<DataTemplate x:Key="DefaultContextMenuViewModelTemplate" x:DataType="viewmodels:CommandContextItemViewModel">
<Grid AutomationProperties.Name="{x:Bind Title, Mode=OneWay}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="32" />
@@ -63,6 +68,38 @@
Text="{x:Bind RequestedShortcut, Mode=OneWay, Converter={StaticResource KeyChordToStringConverter}}" />
</Grid>
</DataTemplate>
<!-- Template for context items flagged as critical -->
<DataTemplate x:Key="CriticalContextMenuViewModelTemplate" x:DataType="viewmodels:CommandContextItemViewModel">
<Grid AutomationProperties.Name="{x:Bind Title, Mode=OneWay}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="32" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<cpcontrols:IconBox
Width="16"
Height="16"
Margin="4,0,0,0"
HorizontalAlignment="Left"
Foreground="{ThemeResource SystemFillColorCriticalBrush}"
SourceKey="{x:Bind Icon, Mode=OneWay}"
SourceRequested="{x:Bind help:IconCacheProvider.SourceRequested}" />
<TextBlock
Grid.Column="1"
VerticalAlignment="Center"
Style="{StaticResource ContextItemTitleTextBlockCriticalStyle}"
Text="{x:Bind Title, Mode=OneWay}" />
<TextBlock
Grid.Column="2"
Margin="16,0,0,0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Style="{StaticResource ContextItemCaptionTextBlockCriticalStyle}"
Text="{x:Bind RequestedShortcut, Mode=OneWay, Converter={StaticResource KeyChordToStringConverter}}" />
</Grid>
</DataTemplate>
</ResourceDictionary>
</UserControl.Resources>
@@ -237,7 +274,7 @@
Margin="-16,-12,-16,-12"
IsItemClickEnabled="True"
ItemClick="CommandsDropdown_ItemClick"
ItemTemplate="{StaticResource ContextMenuViewModelTemplate}"
ItemTemplateSelector="{StaticResource ContextItemTemplateSelector}"
ItemsSource="{x:Bind ViewModel.ContextMenu.FilteredItems, Mode=OneWay}"
KeyDown="CommandsDropdown_KeyDown"
SelectionMode="Single">

View File

@@ -1,24 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<UserControl
x:Class="Microsoft.CmdPal.UI.Controls.ContentFormControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converters="using:CommunityToolkit.WinUI.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:Microsoft.CmdPal.UI.Controls"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="using:CommunityToolkit.WinUI"
xmlns:viewmodels="using:Microsoft.CmdPal.UI.ViewModels"
Background="Transparent"
mc:Ignorable="d">
<UserControl.Resources>
<ResourceDictionary x:Name="CardOverrideStyles">
<Style x:Key="Adaptive.TextBlock" TargetType="TextBlock">
<Setter Property="IsTextSelectionEnabled" Value="True" />
</Style>
</ResourceDictionary>
</UserControl.Resources>
<Grid x:Name="ContentGrid" />
</UserControl>

View File

@@ -1,106 +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 AdaptiveCards.ObjectModel.WinUI3;
using AdaptiveCards.Rendering.WinUI3;
using Microsoft.CmdPal.UI.ViewModels;
using Microsoft.UI.Xaml.Controls;
namespace Microsoft.CmdPal.UI.Controls;
public sealed partial class ContentFormControl : UserControl
{
private static readonly AdaptiveCardRenderer _renderer;
private ContentFormViewModel? _viewModel;
// LOAD-BEARING: if you don't hang onto a reference to the RenderedAdaptiveCard
// then the GC might clean it up sometime, even while the card is in the UI
// tree. If this gets GC'd, then it'll revoke our Action handler, and the
// form will do seemingly nothing.
private RenderedAdaptiveCard? _renderedCard;
public ContentFormViewModel? ViewModel { get => _viewModel; set => AttachViewModel(value); }
static ContentFormControl()
{
// We can't use `CardOverrideStyles` here yet, because we haven't called InitializeComponent once.
// But also, the default value isn't `null` here. It's... some other default empty value.
// So clear it out so that we know when the first time we get created is
_renderer = new AdaptiveCardRenderer()
{
OverrideStyles = null,
};
}
public ContentFormControl()
{
this.InitializeComponent();
var lightTheme = ActualTheme == Microsoft.UI.Xaml.ElementTheme.Light;
_renderer.HostConfig = lightTheme ? AdaptiveCardsConfig.Light : AdaptiveCardsConfig.Dark;
// 5% BODGY: if we set this multiple times over the lifetime of the app,
// then the second call will explode, because "CardOverrideStyles is already the child of another element".
// SO only set this once.
if (_renderer.OverrideStyles == null)
{
_renderer.OverrideStyles = CardOverrideStyles;
}
// TODO in the future, we should handle ActualThemeChanged and replace
// our rendered card with one for that theme. But today is not that day
}
private void AttachViewModel(ContentFormViewModel? vm)
{
if (_viewModel != null)
{
_viewModel.PropertyChanged -= ViewModel_PropertyChanged;
}
_viewModel = vm;
if (_viewModel != null)
{
_viewModel.PropertyChanged += ViewModel_PropertyChanged;
var c = _viewModel.Card;
if (c != null)
{
DisplayCard(c);
}
}
}
private void ViewModel_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (ViewModel == null)
{
return;
}
if (e.PropertyName == nameof(ViewModel.Card))
{
var c = ViewModel.Card;
if (c != null)
{
DisplayCard(c);
}
}
}
private void DisplayCard(AdaptiveCardParseResult result)
{
_renderedCard = _renderer.RenderAdaptiveCard(result.AdaptiveCard);
ContentGrid.Children.Clear();
if (_renderedCard.FrameworkElement != null)
{
ContentGrid.Children.Add(_renderedCard.FrameworkElement);
}
_renderedCard.Action += Rendered_Action;
}
private void Rendered_Action(RenderedAdaptiveCard sender, AdaptiveActionEventArgs args) =>
ViewModel?.HandleSubmit(args.Action, args.Inputs.AsJson());
}

View File

@@ -12,7 +12,7 @@ public delegate bool IsActive();
public delegate bool FilterAccessibleKeyboardEvents(int key, UIntPtr extraInfo);
public class HotkeySettingsControlHook : IDisposable
public partial class HotkeySettingsControlHook : IDisposable
{
private const int WmKeyDown = 0x100;
private const int WmKeyUp = 0x101;

View File

@@ -5,82 +5,81 @@
using System;
using System.Runtime.InteropServices;
namespace Microsoft.PowerToys.Settings.UI.Helpers
namespace Microsoft.PowerToys.Settings.UI.Helpers;
internal static class NativeKeyboardHelper
{
internal static class NativeKeyboardHelper
[StructLayout(LayoutKind.Sequential)]
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Matching Native Structure")]
internal struct INPUT
{
[StructLayout(LayoutKind.Sequential)]
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Matching Native Structure")]
internal struct INPUT
{
internal INPUTTYPE type;
internal InputUnion data;
internal INPUTTYPE type;
internal InputUnion data;
internal static int Size
{
get { return Marshal.SizeOf(typeof(INPUT)); }
}
}
[StructLayout(LayoutKind.Explicit)]
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Matching Native Structure")]
internal struct InputUnion
internal static int Size
{
[FieldOffset(0)]
internal MOUSEINPUT mi;
[FieldOffset(0)]
internal KEYBDINPUT ki;
[FieldOffset(0)]
internal HARDWAREINPUT hi;
}
[StructLayout(LayoutKind.Sequential)]
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Matching Native Structure")]
internal struct MOUSEINPUT
{
internal int dx;
internal int dy;
internal int mouseData;
internal uint dwFlags;
internal uint time;
internal UIntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Matching Native Structure")]
internal struct KEYBDINPUT
{
internal short wVk;
internal short wScan;
internal uint dwFlags;
internal int time;
internal UIntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Matching Native Structure")]
internal struct HARDWAREINPUT
{
internal int uMsg;
internal short wParamL;
internal short wParamH;
}
internal enum INPUTTYPE : uint
{
INPUT_MOUSE = 0,
INPUT_KEYBOARD = 1,
INPUT_HARDWARE = 2,
}
[Flags]
internal enum KeyEventF
{
KeyDown = 0x0000,
ExtendedKey = 0x0001,
KeyUp = 0x0002,
Unicode = 0x0004,
Scancode = 0x0008,
get { return Marshal.SizeOf<INPUT>(); }
}
}
[StructLayout(LayoutKind.Explicit)]
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Matching Native Structure")]
internal struct InputUnion
{
[FieldOffset(0)]
internal MOUSEINPUT mi;
[FieldOffset(0)]
internal KEYBDINPUT ki;
[FieldOffset(0)]
internal HARDWAREINPUT hi;
}
[StructLayout(LayoutKind.Sequential)]
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Matching Native Structure")]
internal struct MOUSEINPUT
{
internal int dx;
internal int dy;
internal int mouseData;
internal uint dwFlags;
internal uint time;
internal UIntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Matching Native Structure")]
internal struct KEYBDINPUT
{
internal short wVk;
internal short wScan;
internal uint dwFlags;
internal int time;
internal UIntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Matching Native Structure")]
internal struct HARDWAREINPUT
{
internal int uMsg;
internal short wParamL;
internal short wParamH;
}
internal enum INPUTTYPE : uint
{
INPUT_MOUSE = 0,
INPUT_KEYBOARD = 1,
INPUT_HARDWARE = 2,
}
[Flags]
internal enum KeyEventF
{
KeyDown = 0x0000,
ExtendedKey = 0x0001,
KeyUp = 0x0002,
Unicode = 0x0004,
Scancode = 0x0008,
}
}

View File

@@ -7,7 +7,7 @@ using System.Text;
namespace Microsoft.PowerToys.Settings.UI.Helpers;
public static class NativeMethods
public static partial class NativeMethods
{
private const int WS_POPUP = 1 << 31; // 0x80000000
internal const int GWL_STYLE = -16;
@@ -26,11 +26,11 @@ public static class NativeMethods
[DllImport("user32.dll")]
internal static extern bool GetWindowPlacement(IntPtr hWnd, out WINDOWPLACEMENT lpwndpl);
[DllImport("user32.dll")]
internal static extern uint SendInput(uint nInputs, NativeKeyboardHelper.INPUT[] pInputs, int cbSize);
[LibraryImport("user32.dll")]
internal static partial uint SendInput(uint nInputs, NativeKeyboardHelper.INPUT[] pInputs, int cbSize);
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
internal static extern short GetAsyncKeyState(int vKey);
[LibraryImport("user32.dll")]
internal static partial short GetAsyncKeyState(int vKey);
[DllImport("user32.dll", SetLastError = true)]
internal static extern int GetWindowLong(IntPtr hWnd, int nIndex);

View File

@@ -66,11 +66,6 @@
IsTabStop="{Binding ElementName=ShortcutContentControl, Path=IsWarningAltGr, Mode=OneWay}"
Severity="Warning" />
</Grid>
<tk7controls:MarkdownTextBlock
x:Uid="InvalidShortcutWarningLabel"
Background="Transparent"
FontSize="12"
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
</StackPanel>
</Grid>
</UserControl>

View File

@@ -36,10 +36,5 @@
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<tk7controls:MarkdownTextBlock
Grid.Column="1"
VerticalAlignment="Center"
Background="Transparent"
Text="{x:Bind Text}" />
</Grid>
</UserControl>

View File

@@ -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 Microsoft.CmdPal.UI.ViewModels;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
namespace Microsoft.CmdPal.UI;
internal sealed partial class ContextItemTemplateSelector : DataTemplateSelector
{
public DataTemplate? Default { get; set; }
public DataTemplate? Critical { get; set; }
protected override DataTemplate? SelectTemplateCore(object item)
{
return ((CommandContextItemViewModel)item).IsCritical ? Critical : Default;
}
}

View File

@@ -18,6 +18,8 @@ public partial class DetailsDataTemplateSelector : DataTemplateSelector
public DataTemplate? TagTemplate { get; set; }
public DataTemplate? CommandTemplate { get; set; }
protected override DataTemplate? SelectTemplateCore(object item)
{
if (item is DetailsElementViewModel element)
@@ -27,6 +29,7 @@ public partial class DetailsDataTemplateSelector : DataTemplateSelector
{
DetailsSeparatorViewModel => SeparatorTemplate,
DetailsLinkViewModel => LinkTemplate,
DetailsCommandsViewModel => CommandTemplate,
DetailsTagsViewModel => TagTemplate,
_ => null,
};

View File

@@ -37,37 +37,21 @@
<DataTemplate x:Key="FormContentTemplate" x:DataType="viewmodels:ContentFormViewModel">
<Grid Margin="0,4,4,4" Padding="12,8,8,8">
<cmdPalControls:ContentFormControl ViewModel="{x:Bind}" />
</Grid>
</DataTemplate>
<DataTemplate x:Key="MarkdownContentTemplate" x:DataType="viewmodels:ContentMarkdownViewModel">
<Grid Margin="0,4,4,4" Padding="12,8,8,8">
<toolkit:MarkdownTextBlock
Background="Transparent"
Header3FontSize="12"
Header3FontWeight="Normal"
Header3Foreground="{ThemeResource TextFillColorSecondaryBrush}"
IsTextSelectionEnabled="True"
Text="{x:Bind Body, Mode=OneWay}" />
</Grid>
</DataTemplate>
<DataTemplate x:Key="NestedFormContentTemplate" x:DataType="viewmodels:ContentFormViewModel">
<Grid>
<cmdPalControls:ContentFormControl ViewModel="{x:Bind}" />
</Grid>
</DataTemplate>
<DataTemplate x:Key="NestedMarkdownContentTemplate" x:DataType="viewmodels:ContentMarkdownViewModel">
<Grid>
<toolkit:MarkdownTextBlock
Background="Transparent"
Header3FontSize="12"
Header3FontWeight="Normal"
Header3Foreground="{ThemeResource TextFillColorSecondaryBrush}"
IsTextSelectionEnabled="True"
Text="{x:Bind Body, Mode=OneWay}" />
</Grid>
</DataTemplate>

View File

@@ -0,0 +1,212 @@
// 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.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.CmdPal.UI.ViewModels;
using Microsoft.CmdPal.UI.ViewModels.Messages;
using Microsoft.UI.Xaml;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.UI.Shell;
using Windows.Win32.UI.WindowsAndMessaging;
using WinRT.Interop;
using RS_ = Microsoft.CmdPal.UI.Helpers.ResourceLoaderInstance;
namespace Microsoft.CmdPal.UI.Helpers;
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "Stylistically, window messages are WM_*")]
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1306:Field names should begin with lower-case letter", Justification = "Stylistically, window messages are WM_*")]
internal sealed partial class TrayIconService
{
private const uint MY_NOTIFY_ID = 1000;
private const uint WM_TRAY_ICON = PInvoke.WM_USER + 1;
private readonly SettingsModel _settingsModel;
private readonly uint WM_TASKBAR_RESTART;
private Window? _window;
private HWND _hwnd;
private WNDPROC? _originalWndProc;
private WNDPROC? _trayWndProc;
private NOTIFYICONDATAW? _trayIconData;
private DestroyIconSafeHandle? _largeIcon;
private DestroyMenuSafeHandle? _popupMenu;
public TrayIconService(SettingsModel settingsModel)
{
_settingsModel = settingsModel;
// TaskbarCreated is the message that's broadcast when explorer.exe
// restarts. We need to know when that happens to be able to bring our
// notification area icon back
WM_TASKBAR_RESTART = PInvoke.RegisterWindowMessage("TaskbarCreated");
}
public void SetupTrayIcon(bool? showSystemTrayIcon = null)
{
if (showSystemTrayIcon ?? _settingsModel.ShowSystemTrayIcon)
{
if (_window == null)
{
_window = new Window();
_hwnd = new HWND(WindowNative.GetWindowHandle(_window));
// LOAD BEARING: If you don't stick the pointer to HotKeyPrc into a
// member (and instead like, use a local), then the pointer we marshal
// into the WindowLongPtr will be useless after we leave this function,
// and our **WindProc will explode**.
_trayWndProc = WindowProc;
var hotKeyPrcPointer = Marshal.GetFunctionPointerForDelegate(_trayWndProc);
_originalWndProc = Marshal.GetDelegateForFunctionPointer<WNDPROC>(PInvoke.SetWindowLongPtr(_hwnd, WINDOW_LONG_PTR_INDEX.GWL_WNDPROC, hotKeyPrcPointer));
}
if (_trayIconData == null)
{
// We need to stash this handle, so it doesn't clean itself up. If
// explorer restarts, we'll come back through here, and we don't
// really need to re-load the icon in that case. We can just use
// the handle from the first time.
_largeIcon = GetAppIconHandle();
_trayIconData = new NOTIFYICONDATAW()
{
cbSize = (uint)Marshal.SizeOf<NOTIFYICONDATAW>(),
hWnd = _hwnd,
uID = MY_NOTIFY_ID,
uFlags = NOTIFY_ICON_DATA_FLAGS.NIF_MESSAGE | NOTIFY_ICON_DATA_FLAGS.NIF_ICON | NOTIFY_ICON_DATA_FLAGS.NIF_TIP,
uCallbackMessage = WM_TRAY_ICON,
hIcon = (HICON)_largeIcon.DangerousGetHandle(),
szTip = RS_.GetString("AppStoreName"),
};
}
var d = (NOTIFYICONDATAW)_trayIconData;
// Add the notification icon
PInvoke.Shell_NotifyIcon(NOTIFY_ICON_MESSAGE.NIM_ADD, in d);
if (_popupMenu == null)
{
_popupMenu = PInvoke.CreatePopupMenu_SafeHandle();
PInvoke.InsertMenu(_popupMenu, 0, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_STRING, PInvoke.WM_USER + 1, RS_.GetString("TrayMenu_Settings"));
PInvoke.InsertMenu(_popupMenu, 1, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_STRING, PInvoke.WM_USER + 2, RS_.GetString("TrayMenu_Exit"));
}
}
else
{
Destroy();
}
}
public void Destroy()
{
if (_trayIconData != null)
{
var d = (NOTIFYICONDATAW)_trayIconData;
if (PInvoke.Shell_NotifyIcon(NOTIFY_ICON_MESSAGE.NIM_DELETE, in d))
{
_trayIconData = null;
}
}
if (_popupMenu != null)
{
_popupMenu.Close();
_popupMenu = null;
}
if (_largeIcon != null)
{
_largeIcon.Close();
_largeIcon = null;
}
if (_window != null)
{
_window.Close();
_window = null;
_hwnd = HWND.Null;
}
}
private DestroyIconSafeHandle GetAppIconHandle()
{
var exePath = Path.Combine(AppContext.BaseDirectory, "Microsoft.CmdPal.UI.exe");
DestroyIconSafeHandle largeIcon;
PInvoke.ExtractIconEx(exePath, 0, out largeIcon, out _, 1);
return largeIcon;
}
private LRESULT WindowProc(
HWND hwnd,
uint uMsg,
WPARAM wParam,
LPARAM lParam)
{
switch (uMsg)
{
case PInvoke.WM_COMMAND:
{
if (wParam == PInvoke.WM_USER + 1)
{
WeakReferenceMessenger.Default.Send<OpenSettingsMessage>();
}
else if (wParam == PInvoke.WM_USER + 2)
{
WeakReferenceMessenger.Default.Send<QuitMessage>();
}
}
break;
// Shell_NotifyIcon can fail when we invoke it during the time explorer.exe isn't present/ready to handle it.
// We'll also never receive WM_TASKBAR_RESTART message if the first call to Shell_NotifyIcon failed, so we use
// WM_WINDOWPOSCHANGING which is always received on explorer startup sequence.
case PInvoke.WM_WINDOWPOSCHANGING:
{
if (_trayIconData == null)
{
SetupTrayIcon();
}
}
break;
default:
// WM_TASKBAR_RESTART isn't a compile-time constant, so we can't
// use it in a case label
if (uMsg == WM_TASKBAR_RESTART)
{
// Handle the case where explorer.exe restarts.
// Even if we created it before, do it again
SetupTrayIcon();
}
else if (uMsg == WM_TRAY_ICON)
{
switch ((uint)lParam.Value)
{
case PInvoke.WM_RBUTTONUP:
{
if (_popupMenu != null)
{
PInvoke.GetCursorPos(out var cursorPos);
PInvoke.SetForegroundWindow(_hwnd);
PInvoke.TrackPopupMenuEx(_popupMenu, (uint)TRACK_POPUP_MENU_FLAGS.TPM_LEFTALIGN | (uint)TRACK_POPUP_MENU_FLAGS.TPM_BOTTOMALIGN, cursorPos.X, cursorPos.Y, _hwnd, null);
}
}
break;
case PInvoke.WM_LBUTTONUP:
case PInvoke.WM_LBUTTONDBLCLK:
WeakReferenceMessenger.Default.Send<HotkeySummonMessage>(new(string.Empty, HWND.Null));
break;
}
}
break;
}
return PInvoke.CallWindowProc(_originalWndProc, hwnd, uMsg, wParam, lParam);
}
}

View File

@@ -29,7 +29,6 @@ using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.Graphics.Dwm;
using Windows.Win32.UI.Input.KeyboardAndMouse;
using Windows.Win32.UI.Shell;
using Windows.Win32.UI.WindowsAndMessaging;
using WinRT;
using WinUIEx;
@@ -47,22 +46,8 @@ public sealed partial class MainWindow : WindowEx,
private readonly WNDPROC? _hotkeyWndProc;
private readonly WNDPROC? _originalWndProc;
private readonly List<TopLevelHotkey> _hotkeys = [];
private bool _ignoreHotKeyWhenFullScreen = true;
// Stylistically, window messages are WM_*
#pragma warning disable SA1310 // Field names should not contain underscore
#pragma warning disable SA1306 // Field names should begin with lower-case letter
private const uint MY_NOTIFY_ID = 1000;
private const uint WM_TRAY_ICON = PInvoke.WM_USER + 1;
private readonly uint WM_TASKBAR_RESTART;
#pragma warning restore SA1306 // Field names should begin with lower-case letter
#pragma warning restore SA1310 // Field names should not contain underscore
private readonly KeyboardListener _keyboardListener;
// Notification Area ("Tray") icon data
private NOTIFYICONDATAW? _trayIconData;
private DestroyIconSafeHandle? _largeIcon;
private bool _ignoreHotKeyWhenFullScreen = true;
private DesktopAcrylicController? _acrylicController;
private SystemBackdropConfiguration? _configurationSource;
@@ -79,11 +64,6 @@ public sealed partial class MainWindow : WindowEx,
_keyboardListener.SetProcessCommand(new CmdPalKeyboardService.ProcessCommand(HandleSummon));
// TaskbarCreated is the message that's broadcast when explorer.exe
// restarts. We need to know when that happens to be able to bring our
// notification area icon back
WM_TASKBAR_RESTART = PInvoke.RegisterWindowMessage("TaskbarCreated");
this.SetIcon();
AppWindow.Title = RS_.GetString("AppName");
PositionCentered();
@@ -159,7 +139,7 @@ public sealed partial class MainWindow : WindowEx,
var settings = App.Current.Services.GetService<SettingsModel>()!;
SetupHotkey(settings);
SetupTrayIcon(settings.ShowSystemTrayIcon);
App.Current.Services.GetService<TrayIconService>()!.SetupTrayIcon(settings.ShowSystemTrayIcon);
_ignoreHotKeyWhenFullScreen = settings.IgnoreShortcutWhenFullscreen;
@@ -219,7 +199,7 @@ public sealed partial class MainWindow : WindowEx,
private void ShowHwnd(IntPtr hwndValue, MonitorBehavior target)
{
var hwnd = new HWND(hwndValue);
var hwnd = new HWND(hwndValue != 0 ? hwndValue : _hwnd);
// Remember, IsIconic == "minimized", which is entirely different state
// from "show/hide"
@@ -332,7 +312,7 @@ public sealed partial class MainWindow : WindowEx,
var extensionService = serviceProvider.GetService<IExtensionService>()!;
extensionService.SignalStopExtensionsAsync();
RemoveTrayIcon();
App.Current.Services.GetService<TrayIconService>()!.Destroy();
// WinUI bug is causing a crash on shutdown when FailFastOnErrors is set to true (#51773592).
// Workaround by turning it off before shutdown.
@@ -340,6 +320,7 @@ public sealed partial class MainWindow : WindowEx,
DisposeAcrylic();
_keyboardListener.Stop();
Environment.Exit(0);
}
private void DisposeAcrylic()
@@ -606,100 +587,8 @@ public sealed partial class MainWindow : WindowEx,
return (LRESULT)IntPtr.Zero;
}
// Shell_NotifyIcon can fail when we invoke it during the time explorer.exe isn't present/ready to handle it.
// We'll also never receive WM_TASKBAR_RESTART message if the first call to Shell_NotifyIcon failed, so we use
// WM_WINDOWPOSCHANGING which is always received on explorer startup sequence.
case PInvoke.WM_WINDOWPOSCHANGING:
{
if (_trayIconData == null)
{
SetupTrayIcon();
}
}
break;
default:
// WM_TASKBAR_RESTART isn't a compile-time constant, so we can't
// use it in a case label
if (uMsg == WM_TASKBAR_RESTART)
{
// Handle the case where explorer.exe restarts.
// Even if we created it before, do it again
SetupTrayIcon();
}
else if (uMsg == WM_TRAY_ICON)
{
switch ((uint)lParam.Value)
{
case PInvoke.WM_RBUTTONUP:
case PInvoke.WM_LBUTTONUP:
case PInvoke.WM_LBUTTONDBLCLK:
Summon(string.Empty);
break;
}
}
break;
}
return PInvoke.CallWindowProc(_originalWndProc, hwnd, uMsg, wParam, lParam);
}
private void SetupTrayIcon(bool? showSystemTrayIcon = null)
{
if (showSystemTrayIcon ?? App.Current.Services.GetService<SettingsModel>()!.ShowSystemTrayIcon)
{
// We only need to build the tray data once.
if (_trayIconData == null)
{
// We need to stash this handle, so it doesn't clean itself up. If
// explorer restarts, we'll come back through here, and we don't
// really need to re-load the icon in that case. We can just use
// the handle from the first time.
_largeIcon = GetAppIconHandle();
_trayIconData = new NOTIFYICONDATAW()
{
cbSize = (uint)Marshal.SizeOf(typeof(NOTIFYICONDATAW)),
hWnd = _hwnd,
uID = MY_NOTIFY_ID,
uFlags = NOTIFY_ICON_DATA_FLAGS.NIF_MESSAGE | NOTIFY_ICON_DATA_FLAGS.NIF_ICON | NOTIFY_ICON_DATA_FLAGS.NIF_TIP,
uCallbackMessage = WM_TRAY_ICON,
hIcon = (HICON)_largeIcon.DangerousGetHandle(),
szTip = RS_.GetString("AppStoreName"),
};
}
var d = (NOTIFYICONDATAW)_trayIconData;
// Add the notification icon
PInvoke.Shell_NotifyIcon(NOTIFY_ICON_MESSAGE.NIM_ADD, in d);
}
else
{
RemoveTrayIcon();
}
}
private void RemoveTrayIcon()
{
if (_trayIconData != null)
{
var d = (NOTIFYICONDATAW)_trayIconData;
if (PInvoke.Shell_NotifyIcon(NOTIFY_ICON_MESSAGE.NIM_DELETE, in d))
{
_trayIconData = null;
}
}
_largeIcon?.Close();
}
private DestroyIconSafeHandle GetAppIconHandle()
{
var exePath = System.Reflection.Assembly.GetExecutingAssembly().Location;
DestroyIconSafeHandle largeIcon;
PInvoke.ExtractIconEx(exePath, 0, out largeIcon, out _, 1);
return largeIcon;
}
}

View File

@@ -23,6 +23,16 @@
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
</PropertyGroup>
<PropertyGroup>
<SelfContained>true</SelfContained>
<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
<PublishTrimmed>true</PublishTrimmed>
<PublishSingleFile>false</PublishSingleFile>
<DisableRuntimeMarshalling>false</DisableRuntimeMarshalling>
<PublishAot>true</PublishAot>
<EnableMsixTooling>true</EnableMsixTooling>
</PropertyGroup>
<PropertyGroup>
<GenerateAppxPackageOnBuild>true</GenerateAppxPackageOnBuild>
</PropertyGroup>
@@ -39,9 +49,7 @@
</PropertyGroup>
<!-- BODGY: XES Versioning and WinAppSDK get into a fight about the app manifest, which breaks WinAppSDK. -->
<Target Name="RearrangeXefVersioningAndWinAppSDKResourceGeneration"
DependsOnTargets="GetNewAppManifestValues;CreateWinRTRegistration"
BeforeTargets="XesWriteVersionInfoResourceFile">
<Target Name="RearrangeXefVersioningAndWinAppSDKResourceGeneration" DependsOnTargets="GetNewAppManifestValues;CreateWinRTRegistration" BeforeTargets="XesWriteVersionInfoResourceFile">
<PropertyGroup>
<!-- XES uses this property to store the "final" location of the app manifest, before it erases it.
We have to update it to the value WinAppSDK set it to. -->
@@ -73,7 +81,6 @@
<PackageReference Include="CommunityToolkit.WinUI.Converters" />
<PackageReference Include="CommunityToolkit.WinUI.Animations" />
<PackageReference Include="CommunityToolkit.WinUI.Extensions" />
<PackageReference Include="CommunityToolkit.WinUI.UI.Controls.Markdown" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" />
<PackageReference Include="Microsoft.WindowsAppSDK" />
@@ -92,9 +99,6 @@
<PackageReference Include="System.Text.Json" />
<!-- LOAD BEARING: GeneratePathProperty=true on BOTH the AC dependencies. Don't forget the AdaptiveCardsWorkaround below -->
<PackageReference Include="AdaptiveCards.ObjectModel.WinUI3" GeneratePathProperty="true" />
<PackageReference Include="AdaptiveCards.Rendering.WinUI3" GeneratePathProperty="True" />
<PackageReference Include="AdaptiveCards.Templating" />
<PackageReference Include="System.Text.RegularExpressions" />
</ItemGroup>
@@ -119,7 +123,6 @@
<ProjectReference Include="..\ext\Microsoft.CmdPal.Ext.Apps\Microsoft.CmdPal.Ext.Apps.csproj" />
<ProjectReference Include="..\ext\Microsoft.CmdPal.Ext.Bookmark\Microsoft.CmdPal.Ext.Bookmarks.csproj" />
<ProjectReference Include="..\ext\Microsoft.CmdPal.Ext.Calc\Microsoft.CmdPal.Ext.Calc.csproj" />
<ProjectReference Include="..\ext\Microsoft.CmdPal.Ext.ClipboardHistory\Microsoft.CmdPal.Ext.ClipboardHistory.csproj" />
<ProjectReference Include="..\ext\Microsoft.CmdPal.Ext.Registry\Microsoft.CmdPal.Ext.Registry.csproj" />
<ProjectReference Include="..\ext\Microsoft.CmdPal.Ext.Shell\Microsoft.CmdPal.Ext.Shell.csproj" />
@@ -129,7 +132,7 @@
<ProjectReference Include="..\ext\Microsoft.CmdPal.Ext.WindowsTerminal\Microsoft.CmdPal.Ext.WindowsTerminal.csproj" />
<ProjectReference Include="..\ext\Microsoft.CmdPal.Ext.WindowWalker\Microsoft.CmdPal.Ext.WindowWalker.csproj" />
<ProjectReference Include="..\ext\Microsoft.CmdPal.Ext.WinGet\Microsoft.CmdPal.Ext.WinGet.csproj" />
<ProjectReference Include="..\ext\Microsoft.CmdPal.Ext.Calc\Microsoft.CmdPal.Ext.Calc.csproj" />
<ProjectReference Include="..\Microsoft.Terminal.UI\Microsoft.Terminal.UI.vcxproj">
<ReferenceOutputAssembly>True</ReferenceOutputAssembly>
<Private>True</Private>
@@ -183,10 +186,6 @@
<PropertyGroup>
<AdaptiveCardsNative>runtimes\win10-$(Platform)\native</AdaptiveCardsNative>
</PropertyGroup>
<ItemGroup>
<Content Include="$(PkgAdaptiveCards_ObjectModel_WinUI3)\$(AdaptiveCardsNative)\AdaptiveCards.ObjectModel.WinUI3.dll" Link="AdaptiveCards.ObjectModel.WinUI3.dll" CopyToOutputDirectory="PreserveNewest" />
<Content Include="$(PkgAdaptiveCards_Rendering_WinUI3)\$(AdaptiveCardsNative)\AdaptiveCards.Rendering.WinUI3.dll" Link="AdaptiveCards.Rendering.WinUI3.dll" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
<ItemGroup>
<Content Update="..\Microsoft.CmdPal.UI.ViewModels\Assets\template.zip">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>

View File

@@ -1,7 +1,6 @@
GetPhysicallyInstalledSystemMemory
GlobalMemoryStatusEx
GetSystemInfo
CoCreateInstance
GetForegroundWindow
SetForegroundWindow
GetWindowRect
@@ -21,10 +20,7 @@ SetActiveWindow
MonitorFromWindow
GetMonitorInfo
GetDpiForMonitor
SHCreateStreamOnFileEx
CoAllowSetForegroundWindow
SHCreateStreamOnFileEx
SHLoadIndirectString
WM_HOTKEY
WM_NCLBUTTONDBLCLK
@@ -33,11 +29,15 @@ LoadIcon
WM_USER
WM_WINDOWPOSCHANGING
RegisterWindowMessageW
GetModuleHandleW
ExtractIconEx
TRACK_POPUP_MENU_FLAGS
WM_COMMAND
WM_RBUTTONUP
WM_LBUTTONUP
WM_LBUTTONDBLCLK
CreatePopupMenu
TrackPopupMenuEx
InsertMenu
MessageBox
DwmGetWindowAttribute

View File

@@ -10,7 +10,6 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:help="using:Microsoft.CmdPal.UI.Helpers"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:toolkit="using:CommunityToolkit.WinUI.UI.Controls"
xmlns:ui="using:CommunityToolkit.WinUI"
xmlns:viewModels="using:Microsoft.CmdPal.UI.ViewModels"
Background="Transparent"
@@ -29,6 +28,7 @@
<cmdpalUI:DetailsDataTemplateSelector
x:Key="DetailsDataTemplateSelector"
CommandTemplate="{StaticResource DetailsCommandsTemplate}"
LinkTemplate="{StaticResource DetailsLinkTemplate}"
SeparatorTemplate="{StaticResource DetailsSeparatorTemplate}"
TagTemplate="{StaticResource DetailsTagsTemplate}" />
@@ -50,6 +50,27 @@
ToolTipService.ToolTip="{x:Bind ToolTip, Mode=OneWay}" />
</DataTemplate>
<DataTemplate x:Key="CommandTemplate" x:DataType="viewModels:CommandViewModel">
<StackPanel Orientation="Vertical">
<Button
Name="Command"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Left"
Click="Command_Click"
Style="{StaticResource SubtleButtonStyle}">
<StackPanel VerticalAlignment="Center" Orientation="Horizontal">
<cpcontrols:IconBox
Width="16"
Height="16"
Margin="0,3,8,0"
SourceKey="{x:Bind Icon, Mode=OneWay}"
SourceRequested="{x:Bind help:IconCacheProvider.SourceRequested}" />
<TextBlock Text="{x:Bind Name}" />
</StackPanel>
</Button>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="DetailsLinkTemplate" x:DataType="viewModels:DetailsLinkViewModel">
<StackPanel Orientation="Vertical">
<TextBlock
@@ -71,6 +92,18 @@
Visibility="{x:Bind IsLink, Mode=OneWay}" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="DetailsCommandsTemplate" x:DataType="viewModels:DetailsCommandsViewModel">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock
IsTextSelectionEnabled="True"
Text="{x:Bind Key, Mode=OneWay}"
TextWrapping="WrapWholeWords" />
<ItemsControl
ItemTemplate="{StaticResource CommandTemplate}"
ItemsSource="{x:Bind Commands, Mode=OneWay}"
Visibility="{x:Bind HasCommands, Mode=OneWay}" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="DetailsSeparatorTemplate" x:DataType="viewModels:DetailsSeparatorViewModel">
<StackPanel Margin="0,8,8,0" Orientation="Vertical">
<Border
@@ -98,13 +131,6 @@
Visibility="{x:Bind HasTags, Mode=OneWay}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<toolkit:WrapPanel
x:Name="TagsWrapPanel"
MinWidth="0"
Padding="0"
HorizontalSpacing="4"
Orientation="Horizontal"
VerticalSpacing="4" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
@@ -374,16 +400,6 @@
TextWrapping="WrapWholeWords"
Visibility="{x:Bind ViewModel.Details.Title, Converter={StaticResource StringNotEmptyToVisibilityConverter}, Mode=OneWay}" />
<toolkit:MarkdownTextBlock
Grid.Row="2"
Margin="0,12,0,24"
Background="Transparent"
Header3FontSize="12"
Header3FontWeight="Normal"
Header3Foreground="{ThemeResource TextFillColorSecondaryBrush}"
IsTextSelectionEnabled="True"
Text="{x:Bind ViewModel.Details.Body, Mode=OneWay}" />
<ItemsRepeater
Grid.Row="3"
ItemTemplate="{StaticResource DetailsDataTemplateSelector}"

View File

@@ -629,4 +629,12 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page,
return iconInfoVM?.HasIcon(requestedTheme == Microsoft.UI.Xaml.ElementTheme.Light) ?? false;
}
}
private void Command_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (sender is Button button && button.DataContext is CommandViewModel commandViewModel)
{
WeakReferenceMessenger.Default.Send<PerformCommandMessage>(new(commandViewModel.Model));
}
}
}

View File

@@ -13,7 +13,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
<PublishSingleFile>False</PublishSingleFile>
<PublishReadyToRun Condition="'$(Configuration)' == 'Debug'">False</PublishReadyToRun>
<PublishReadyToRun Condition="'$(Configuration)' != 'Debug'">True</PublishReadyToRun>
<PublishTrimmed Condition="'$(Configuration)' == 'Debug'">False</PublishTrimmed>
<PublishTrimmed Condition="'$(Configuration)' != 'Debug'">False</PublishTrimmed>
<PublishTrimmed Condition="'$(Configuration)' == 'Debug'">True</PublishTrimmed>
<PublishTrimmed Condition="'$(Configuration)' != 'Debug'">True</PublishTrimmed>
</PropertyGroup>
</Project>

View File

@@ -412,4 +412,10 @@ Right-click to remove the key combination, thereby deactivating the shortcut.</v
<data name="MoreCommandsButton.[using:Microsoft.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>More</value>
</data>
<data name="TrayMenu_Settings" xml:space="preserve">
<value>Settings</value>
</data>
<data name="TrayMenu_Exit" xml:space="preserve">
<value>Exit</value>
</data>
</root>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8" ?>
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converters="using:CommunityToolkit.WinUI.Converters">
<ResourceDictionary.MergedDictionaries>
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
<!-- Other merged dictionaries here -->
</ResourceDictionary.MergedDictionaries>
<Style
x:Key="ContextItemTitleTextBlockCriticalStyle"
BasedOn="{StaticResource BaseTextBlockStyle}"
TargetType="TextBlock">
<Setter Property="Foreground" Value="{ThemeResource SystemFillColorCriticalBrush}" />
<Setter Property="FontWeight" Value="Normal" />
</Style>
<Style
x:Key="ContextItemCaptionTextBlockCriticalStyle"
BasedOn="{StaticResource CaptionTextBlockStyle}"
TargetType="TextBlock">
<Setter Property="Foreground" Value="{ThemeResource SystemFillColorCriticalBrush}" />
</Style>
</ResourceDictionary>

View File

@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
<PropertyGroup>
<PathToRoot>..\..\..\..\</PathToRoot>
<WasdkNuget>$(PathToRoot)packages\Microsoft.WindowsAppSDK.1.7.250401001</WasdkNuget>
<WasdkNuget>$(PathToRoot)packages\Microsoft.WindowsAppSDK.1.7.250513003</WasdkNuget>
</PropertyGroup>
<Import Project="$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.props')" />
<PropertyGroup Label="Globals">

View File

@@ -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 <kbd>Win+Alt+Space</kbd>.

View File

@@ -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

View File

@@ -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

View File

@@ -1,11 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\..\Common.Dotnet.CsWinRT.props" />
<Import Project="..\..\..\..\Common.Dotnet.AotCompatibility.props" />
<PropertyGroup>
<RootNamespace>Microsoft.CmdPal.Ext.Apps</RootNamespace>
<Nullable>enable</Nullable>
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\WinUI3Apps\CmdPal</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<DisableRuntimeMarshalling>true</DisableRuntimeMarshalling>
</PropertyGroup>
<ItemGroup>
@@ -49,4 +51,9 @@
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="NativeMethods.txt" />
<AdditionalFiles Include="NativeMethods.json" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,7 @@
{
"$schema": "https://aka.ms/CsWin32.schema.json",
"allowMarshaling": false,
"comInterop": {
"preserveSigMethods": [ "*" ]
}
}

View File

@@ -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
SHCreateItemFromParsingName
IShellItem
ISequentialStream
SHLoadIndirectString
IAppxFactory
AppxFactory
IAppxManifestReader
IAppxManifestApplicationsEnumerator
IAppxManifestApplication
IAppxManifestProperties
IShellLinkW
ShellLink
IPersistFile
CoTaskMemFree
IUnknown

View File

@@ -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
{
}

View File

@@ -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<IAppxManifestApplication> GetAppsFromManifest(IStream stream)
internal static unsafe List<IntPtr> 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<IntPtr>();
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<T>(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;

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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();
}

View File

@@ -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);
}

View File

@@ -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();
}

View File

@@ -5,6 +5,7 @@
using System;
using System.ComponentModel;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
@@ -16,7 +17,7 @@ namespace Microsoft.CmdPal.Ext.Apps.Programs;
/// <summary>
/// Provides access to NTFS reparse points in .Net.
/// </summary>
public static class ReparsePoint
public static partial class ReparsePoint
{
#pragma warning disable SA1310 // Field names should not contain underscore
@@ -36,7 +37,7 @@ public static class ReparsePoint
#pragma warning restore SA1310 // Field names should not contain underscore
[Flags]
private enum FileAccessType : uint
internal enum FileAccessType : uint
{
DELETE = 0x00010000,
READ_CONTROL = 0x00020000,
@@ -100,7 +101,7 @@ public static class ReparsePoint
}
[Flags]
private enum FileShareType : uint
internal enum FileShareType : uint
{
None = 0x00000000,
Read = 0x00000001,
@@ -108,7 +109,7 @@ public static class ReparsePoint
Delete = 0x00000004,
}
private enum CreationDisposition : uint
internal enum CreationDisposition : uint
{
New = 1,
CreateAlways = 2,
@@ -118,7 +119,7 @@ public static class ReparsePoint
}
[Flags]
private enum FileAttributes : uint
internal enum FileAttributes : uint
{
Readonly = 0x00000001,
Hidden = 0x00000002,
@@ -195,8 +196,9 @@ public static class ReparsePoint
public AppExecutionAliasReparseTagBufferLayoutVersion Version;
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool DeviceIoControl(
[LibraryImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static partial bool DeviceIoControl(
IntPtr hDevice,
uint dwIoControlCode,
IntPtr inBuffer,
@@ -206,8 +208,8 @@ public static class ReparsePoint
out int pBytesReturned,
IntPtr lpOverlapped);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern IntPtr CreateFile(
[LibraryImport("kernel32.dll", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)]
internal static partial int CreateFile(
string lpFileName,
FileAccessType dwDesiredAccess,
FileShareType dwShareMode,
@@ -284,15 +286,18 @@ public static class ReparsePoint
ThrowLastWin32Error("Unable to get information about reparse point.");
}
AppExecutionAliasReparseTagHeader aliasReparseHeader = Marshal.PtrToStructure<AppExecutionAliasReparseTagHeader>(outBuffer);
if (aliasReparseHeader.ReparseTag == IO_REPARSE_TAG_APPEXECLINK)
unsafe
{
var metadata = AppExecutionAliasMetadata.FromPersistedRepresentationIntPtr(
outBuffer,
aliasReparseHeader.Version);
var aliasReparseHeader = Unsafe.Read<AppExecutionAliasReparseTagHeader>((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 +324,65 @@ public static class ReparsePoint
public static AppExecutionAliasMetadata FromPersistedRepresentationIntPtr(IntPtr reparseDataBufferPtr, AppExecutionAliasReparseTagBufferLayoutVersion version)
{
var dataOffset = Marshal.SizeOf<AppExecutionAliasReparseTagHeader>();
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<AppExecutionAliasReparseTagHeader>();
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)

View File

@@ -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<UWPApplication>();
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<UWPApplication>();
}
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<UWPApplication>();
}
var appsBag = new ConcurrentBag<UWPApplication>();
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<IPackage> CurrentUserPackages()

View File

@@ -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;
@@ -97,27 +100,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 +169,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 +203,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 +213,40 @@ 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}}}";
void* reserved = null;
try
{
PInvoke.SHLoadIndirectString(source, outBuffer, capacity, ref reserved).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, capacity, ref reserved).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 +262,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 +352,7 @@ public class UWPApplication : IProgram
var prefix = path.Substring(0, end);
var paths = new List<string> { };
const int appIconSize = 36;
var targetSizes = new List<int> { 16, 24, 30, 36, 44, 60, 72, 96, 128, 180, 256 }.AsParallel();
var targetSizes = new List<int> { 16, 24, 30, 36, 44, 60, 72, 96, 128, 180, 256 };
var pathFactorPairs = new Dictionary<string, int>();
foreach (var factor in targetSizes)

View File

@@ -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;
@@ -841,18 +842,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<string>())
.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<string>())
.Where(programPath => disabledProgramsList.All(x => x.UniqueIdentifier != programPath)));
var pathBag = new ConcurrentBag<string>();
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<string>();
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<Win32Program>();
Parallel.ForEach(paths, source =>
{
var program = GetProgramFromPath(source);
if (program != null)
{
programsList.Add(program);
}
});
var runCommandProgramsList = new ConcurrentBag<Win32Program>();
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));
}

View File

@@ -69,11 +69,6 @@ public class ListRepository<T> : IRepository<T>, IEnumerable<T>
}
}
public ParallelQuery<T> AsParallel()
{
return _items.Values.AsParallel();
}
public bool Contains(T item)
{
if (item is not null)

View File

@@ -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<Programs.Win32Program>, 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";

View File

@@ -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>(T* comPtr)
where T : unmanaged
{
if (comPtr != null)
{
((IUnknown*)comPtr)->Release();
}
}
}

View File

@@ -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
{
/// <summary>
/// Operation successful.
/// </summary>
S_OK = 0x00000000,
/// <summary>
/// Operation successful. (negative condition/no operation)
/// </summary>
S_FALSE = 0x00000001,
/// <summary>
/// Not implemented.
/// </summary>
E_NOTIMPL = 0x80004001,
/// <summary>
/// No such interface supported.
/// </summary>
E_NOINTERFACE = 0x80004002,
/// <summary>
/// Pointer that is not valid.
/// </summary>
E_POINTER = 0x80004003,
/// <summary>
/// Operation aborted.
/// </summary>
E_ABORT = 0x80004004,
/// <summary>
/// Unspecified failure.
/// </summary>
E_FAIL = 0x80004005,
/// <summary>
/// Unexpected failure.
/// </summary>
E_UNEXPECTED = 0x8000FFFF,
/// <summary>
/// General access denied error.
/// </summary>
E_ACCESSDENIED = 0x80070005,
/// <summary>
/// Handle that is not valid.
/// </summary>
E_HANDLE = 0x80070006,
/// <summary>
/// Failed to allocate necessary memory.
/// </summary>
E_OUTOFMEMORY = 0x8007000E,
/// <summary>
/// One or more arguments are not valid.
/// </summary>
E_INVALIDARG = 0x80070057,
/// <summary>
/// The operation was canceled by the user. (Error source 7 means Win32.)
/// </summary>
/// <SeeAlso href="https://learn.microsoft.com/windows/win32/debug/system-error-codes--1000-1299-"/>
/// <SeeAlso href="https://en.wikipedia.org/wiki/HRESULT"/>
E_CANCELLED = 0x800704C7,
}
public static class ShellItemTypeConstants
{
/// <summary>
/// Guid for type IShellItem.
/// </summary>
public static readonly Guid ShellItemGuid = new("43826d1e-e718-42ee-bc55-a1e261c37bfe");
/// <summary>
/// Guid for type IShellItem2.
/// </summary>
public static readonly Guid ShellItem2Guid = new("7E9FB0D3-919F-4307-AB2E-9B1860310C93");
}
/// <summary>
/// The following are ShellItem DisplayName types.
/// </summary>
[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);
}
/// <summary>
/// <see href="https://learn.microsoft.com/windows/win32/stg/stgm-constants">see all STGM values</see>
/// </summary>
[Flags]
public enum STGM : long
{
READ = 0x00000000L,
WRITE = 0x00000001L,
READWRITE = 0x00000002L,
CREATE = 0x00001000L,
}
}

View File

@@ -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;
}
}

View File

@@ -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
{
/// <summary>Retrieves the path and file name of a Shell link object</summary>
void GetPath([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxPath, ref WIN32_FIND_DATAW pfd, SLGP_FLAGS fFlags);
/// <summary>Retrieves the list of item identifiers for a Shell link object</summary>
void GetIDList(out nint ppidl);
/// <summary>Sets the pointer to an item identifier list (PIDL) for a Shell link object.</summary>
void SetIDList(nint pidl);
/// <summary>Retrieves the description string for a Shell link object</summary>
void GetDescription([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, int cchMaxName);
/// <summary>Sets the description for a Shell link object. The description can be any application-defined string</summary>
void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName);
/// <summary>Retrieves the name of the working directory for a Shell link object</summary>
void GetWorkingDirectory([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath);
/// <summary>Sets the name of the working directory for a Shell link object</summary>
void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir);
/// <summary>Retrieves the command-line arguments associated with a Shell link object</summary>
void GetArguments([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cchMaxPath);
/// <summary>Sets the command-line arguments for a Shell link object</summary>
void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs);
/// <summary>Retrieves the hot key for a Shell link object</summary>
void GetHotkey(out short pwHotkey);
/// <summary>Sets a hot key for a Shell link object</summary>
void SetHotkey(short wHotkey);
/// <summary>Retrieves the show command for a Shell link object</summary>
void GetShowCmd(out int piShowCmd);
/// <summary>Sets the show command for a Shell link object. The show command sets the initial show state of the window.</summary>
void SetShowCmd(int iShowCmd);
/// <summary>Retrieves the location (path and index) of the icon for a Shell link object</summary>
void GetIconLocation([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, int cchIconPath, out int piIcon);
/// <summary>Sets the location (path and index) of the icon for a Shell link object</summary>
void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon);
/// <summary>Sets the relative path to the Shell link object</summary>
void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, int dwReserved);
/// <summary>Attempts to find the target of a Shell link, even if it has been moved or renamed</summary>
void Resolve(ref nint hwnd, SLR_FLAGS fFlags);
/// <summary>Sets the path and file name of a Shell link object</summary>
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;
}
}

View File

@@ -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
/// </summary>
/// <param name="path">Path to the shell item (e. g. shortcut 'File Explorer.lnk').</param>
/// <returns>The localized name as string or <see cref="string.Empty"/>.</returns>
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;
}
/// <summary>

View File

@@ -6,20 +6,20 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text.RegularExpressions;
using Mages.Core;
using CmdPalCalculator;
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<string, object>
{
{ "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,23 @@ 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);
var formatted = decimalResult.ToString("G29", cultureInfo);
decimalResult = Convert.ToDecimal(formatted, cultureInfo);
var roundedResult = Round(decimalResult);
return new CalculateResult()
@@ -103,25 +98,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;
}
}

View File

@@ -35,7 +35,7 @@ public static class CalculateHelper
{
if (string.IsNullOrWhiteSpace(input))
{
throw new ArgumentNullException(paramName: nameof(input));
return false;
}
if (!RegValidExpressChar.IsMatch(input))

View File

@@ -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<string, int>(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));
}
}

Some files were not shown because too many files have changed in this diff Show More