Compare commits

..

52 Commits

Author SHA1 Message Date
Kai Tao
56b2094a2c Merge branch 'main' into user/yeelam/IncludeUnitTestDlls 2025-06-13 11:17:43 +08:00
Kai Tao
8506b47544 Workspaceslib ut (#40007)
This pull request introduces a new unit testing framework for the
`WorkspacesLib` module, adds test coverage for various utilities, and
integrates the new test project into the build system. Key changes
include adding the `WorkspacesLibUnitTests` project, implementing tests
for `AppUtils`, `JsonUtils`, and `PwaHelper`, and updating the build
configuration to include the new test project.

### Unit Tests Added:
* **`AppUtilsTests`**:
- Added comprehensive tests for methods such as `GetCurrentFolder`,
`IsEdge`, `IsChrome`, and `IsSteamGame` to validate their behavior under
various conditions.
* **`JsonUtilsTests`**:
- Implemented tests for reading and writing workspace data, including
scenarios for invalid JSON, non-existent files, and valid workspace
lists.
* **`PwaHelperTests`**:
- Added tests to ensure the stability of `PwaHelper` methods, such as
`GetEdgeAppId`, `GetChromeAppId`, and `SearchPwaName`, even with invalid
or empty inputs.

### Build System Updates:
* **New Test Project Integration**:
- Added the `WorkspacesLibUnitTests` project to the solution file
`PowerToys.sln` with appropriate dependencies.
- Updated build configurations to include the new test project for both
Debug and Release builds across architectures.
[[1]](diffhunk://#diff-ca837ce490070b91656ffffe31cbad8865ba9174e0f020231f77baf35ff3f811R2208-R2215)
[[2]](diffhunk://#diff-ca837ce490070b91656ffffe31cbad8865ba9174e0f020231f77baf35ff3f811R2841)

### Pipeline Adjustments:
* **Test Discovery**:
- Updated the pipeline configuration in
`.pipelines/v2/templates/job-build-project.yml` to discover the new
`WorkspacesLibUnitTests.dll` during test runs.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2025-06-13 10:51:22 +08:00
Mengyuan
2b40c4d2f3 [Fuzzing Tests] Use valid areaPath in OneFuzz configuration and Update jobNotificationEmail (#39994)
<!-- 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

![image](https://github.com/user-attachments/assets/53c14889-88d3-4a77-af96-314d29a4a410)

Update the areapath in the OneFuzzConfig.json to "OS\\Windows Client and
Services\\WinPD\\DFX-Developer Fundamentals and
Experiences\\DEFT\\SALT",


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

- [ ] **Closes:** #xxx
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **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-13 10:20:04 +08:00
Clint Rutkas
38cae3ead8 Adjusting community.md (#40021)
adding Zach and removing Zhiwei
2025-06-13 10:00:20 +08:00
HO-COOH
e5b2b7fe82 Fix fancyzone editor dpi awareness (#39356)
<!-- 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
Enable dpi aware of FancyZone Editor.

Context:
I happened to drag the editor window to my other monitor that has a
different dpi, and the text are blurry.

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

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
Manual.
2025-06-13 09:58:59 +08:00
Gordon Lam (SH)
1aa084f167 Try to see how UnitTest pass/failure rate 2025-06-13 07:53:20 +08:00
Muhammad Danish
a2c9517bed Updates to WinGet publish script (#40002)
Few updates to the WinGet publish workflow action
- Use GitHub release event instead of manually making an API call to
GitHub API and then fetching the target release. The target release is
directly accessible via the event. The refactor is similar to the GitHub
action of
[microsoft/edit](https://github.com/microsoft/edit/blob/main/.github/workflows/winget.yml)
repo's workflow
- With the latest winget-create release, the preferred method for
providing the GitHub token in CI/CD environment is via the environment
variable `WINGET_CREATE_GITHUB_TOKEN`. Removed use of `--token` and
switched to environment variable. See https://aka.ms/winget-create-token
for details.
2025-06-12 15:48:05 -07:00
Jakub Marcowski
e9dbcbaebb cziplib: Update to 0.3.3 (#39604)
## Summary of the Pull Request

Updated the `zip` dependency to the latest stable version
([0.3.3](https://github.com/kuba--/zip/releases/tag/v0.3.3)).
2025-06-12 18:38:04 +08:00
Dustin L. Howett
6e0f1819d5 build: adjust for changes in the Az.Accounts module (#40001)
They made secure strings the default. Doing it this way maintains
compatibility with the version before and after the default changed.

This fixes symbol publication.
2025-06-12 09:53:40 +08:00
Kai Tao
3f834d7a6b Build: Change CLR projects to portable type pdb (#39992)
<!-- 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 now githubcopilot is easier to used in vscode, would like to set
up debug in vscode. Full type pdb can't be loaded in visual studio code
debugging
Change the debugging type to make it debuggable from vscode.
<img width="800" alt="image"
src="https://github.com/user-attachments/assets/86e46188-ad42-4fbf-b8af-8e0353b1c615"
/>
from:
e6049bb603/Documentation/diagnostics/portable_pdb.md

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

- [ ] **Closes:** #xxx
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **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
Can debug settings in vscode.
2025-06-12 09:44:32 +08:00
Gordon Lam
e9a79f5d6f Add the detail section (#39991)
<!-- 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
The `verifyNoticeMdAgainstNugetPackages.ps1` script previously only
reported "Notice.md does not match NuGet list." without providing any
details about which packages were different, making it difficult to
debug discrepancies.

## Changes Made

This enhancement adds detailed difference reporting when NuGet packages
don't match:

- **Extracts current package list from NOTICE.md** using regex pattern
matching
- **Shows packages missing from NOTICE.md** (highlighted in red)
- **Shows packages in NOTICE.md but not in generated list** (highlighted
in yellow)
- **Provides summary statistics** with package counts
- **Maintains backward compatibility** - all existing functionality
preserved

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
If there is no difference, the same behaviour.

If there is difference, here is the example:

![image](https://github.com/user-attachments/assets/63dad21a-9db5-4d20-8a2c-ddd44ce1ee80)

## Edge Cases Handled

- **Missing NuGet section**: Shows warning and treats as empty package
list
- **Empty package lists**: Handles gracefully with appropriate counts
- **Matching lists**: No additional output (preserves existing behavior)

This enhancement significantly improves the debugging experience when
NuGet package verification fails by providing specific, actionable
information about which packages need attention.
2025-06-12 09:15:25 +08:00
Dave Rayment
a9c5117f61 Command Palette: fix sample links in README.md (#39983)
Fix links to sample folders.

<!-- 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
Two links were broken in the Command Palette readme because of a folder
name being "Exts" instead of "ext".

<!-- 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
N/A

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

Manually confirmed the following links are correct:


https://github.com/microsoft/PowerToys/tree/main/src/modules/cmdpal/ext/SamplePagesExtension

https://github.com/microsoft/PowerToys/tree/main/src/modules/cmdpal/ext/ProcessMonitorExtension
2025-06-12 07:08:23 +08:00
rovercoder
b9aac70de5 Spell Checking Fixes (#39985)
<!-- 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
Fixes spelling issues and spell checker warnings
2025-06-11 16:32:38 +00:00
Jeremy Sinclair
55b9b52349 [Deps] Update .NET packages from 9.0.5 to 9.0.6 (#39986)
<!-- 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
Updates .NET 9 Runtime / Library packages to the latest 9.0.6 servicing
release for security fixes.
<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [ ] **Closes:** #xxx
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **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
This PR also updates the version of System.Text.Json to 9.0.6 in the
CmdPal extension template.
<!-- 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: Gordon Lam (SH) <yeelam@microsoft.com>
2025-06-11 13:43:53 +08:00
SkandaBT
13a7ceba6d Update documentation (#39603)
<!-- Enter a brief description/summary of your PR here. What does it
fix/change/how was it tested (even manually, if necessary)? -->
## Summary of the Pull Request
This PR applies a series of grammar, punctuation, and stylistic updates
across the main README and GitHub templates to improve consistency,
clarity, and alignment with formatting guidelines.

### File-Level Changes

| Change | Details | Files |
| ------ | ------- | ----- |
| Updated README.md for grammar, punctuation, and phrasing consistency |
<ul><li>Inserted missing commas and adjusted articles for clearer
sentences</li><li>Standardized hyphenation and removed extra spaces
around slashes</li><li>Refined bullet list punctuation and corrected
pluralization (‘number of bugs’)</li></ul> | `README.md` |
| Refined pull request template text for clarity and uniform formatting
| <ul><li>Hyphenated “end-user-facing” and added Oxford comma in
checklist items</li><li>Adjusted phrasing in communication bullet for
parallel structure</li><li>Reworded placeholder comment for detailed PR
description</li></ul> | `.github/pull_request_template.md` |
| Standardized issue templates with grammar and placeholder improvements
| <ul><li>Clarified label descriptions and added missing commas in
placeholders</li><li>Unified question phrasing across bug, feature,
translation, and docs templates</li><li>Corrected capitalization and
refined instructions for consistency</li></ul> |
`.github/ISSUE_TEMPLATE/bug_report.yml`<br/>`.github/ISSUE_TEMPLATE/feature_request.yml`<br/>`.github/ISSUE_TEMPLATE/translation_issue.yml`<br/>`.github/ISSUE_TEMPLATE/documentation-issue.yml`
|

---
## PR Checklist

- [ ] **Closes:** #xxx
- [X] **Communication:** I've already discussed this with core
contributors. If the 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)
- [X] **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:
https://github.com/MicrosoftDocs/windows-dev-docs/pull/5447

<!-- 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: Jay <65828559+Jay-o-Way@users.noreply.github.com>
2025-06-10 19:17:38 +00:00
ruslanlap
e14ace4bfc 📚 Add Definition Plugin for PowerToys Run (#39881)
This PR introduces the **Definition** plugin—a new PowerToys Run
extension that allows you to quickly lookup word definitions, phonetics,
and synonyms without leaving your keyboard.

🔗 **Learn more**  
| Plugin | Author | Description |  
|:-------|:------:|:------------|  
| [Definition](https://github.com/ruslanlap/PowerToysRun-Definition) |
[ruslanlap](https://github.com/ruslanlap) | Lookup word definitions,
phonetics, and synonyms without leaving your keyboard. |

---

##  Features

- **Instant Definitions**  
  Get definitions in real-time via dictionaryapi.dev.
- **Pronunciation Audio**  
  Play phonetic audio directly from your results.
- **Phonetics & Synonyms**  
  View phonetic spelling, synonyms, and antonyms.
- **Usage Examples**  
  See real-world examples of how words are used.
- **Delayed Execution**  
  Shows loading indicator before fetching results.
- **Caching**  
In-memory cache for repeat lookups (up to 100 entries) to improve
performance.
- **Theme Awareness**  
  Automatically switches icons for light/dark mode.
- **Context Menu**  
Copy definitions, play pronunciation, open source URL, or search for
related words via right-click or keyboard shortcuts.
- **Cancellable Requests**  
  Automatically cancels previous requests when typing new queries.
- **Wiktionary Integration**  
Open any word in Wiktionary for additional information and translations.

---

Thank you for reviewing the **Definition** plugin! 📚  
I'm excited to help PowerToys Run users look up words faster than ever.
2025-06-10 19:14:50 +00:00
ruslanlap
549ce60483 🚀 Add SpeedTest Plugin for PowerToys Run to Third-Party plugin (#39880)
# 🚀 Add SpeedTest Plugin for PowerToys Run

This PR introduces the **SpeedTest** plugin—a new PowerToys Run
extension that lets you run internet speed tests instantly, without
opening a browser or separate application. Also was problem with
formating previous Plugin "Linear"

🔗 **Learn more**  
| Plugin | Author | Description |  
|:-------|:------:|:------------|  
| [SpeedTest](https://github.com/ruslanlap/PowerToysRun-SpeedTest) |
[ruslanlap](https://github.com/ruslanlap) | One-command internet speed
tests with real-time results, modern UI, and shareable links. |

---

##  Plugin Highlights

- **One-command tests**  
Run a full speed test (download, upload, ping, server) via PowerToys Run
(). and hit Enter! (or change to or any other keyword in the settings)
- **Modern, theme-aware UI**  
WPF-based window adapts automatically to light or dark Windows modes.
- **Live progress & shareable links**  
Watch your test run in real time and copy a result URL when it finishes.
- **All-local, privacy-first**  
No third-party tracking—uses the bundled Speedtest CLI for 100% local
execution.
- **Robust error handling**  
Friendly messages for network failures, permission issues, or unexpected
errors.
- **x64 & ARM64 builds**  
  Native-optimized releases for both CPU architectures.  
- **Open source & community-driven**  
Contributions, issues, and feature requests are welcome—let's make it
even better!

---

Thank you for reviewing the **SpeedTest** plugin! 🚀  
I'm excited to help PowerToys Run users check their internet speeds
faster than ever.
2025-06-10 19:08:15 +00:00
Clint Rutkas
51b6e41d93 PowerToys.MonacoPreviewHandler crashes when rendering UTF-8 BOM (#39381)
<!-- 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

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

- [ ] **Closes:** https://github.com/microsoft/PowerToys/issues/35258
- [ ] **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-10 22:13:25 +08:00
dependabot[bot]
0d29f6aca6 Bump check-spelling/check-spelling from 0.0.24 to 0.0.25 (#39572)
Bumps
[check-spelling/check-spelling](https://github.com/check-spelling/check-spelling)
from 0.0.24 to 0.0.25.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/check-spelling/check-spelling/releases">check-spelling/check-spelling's
releases</a>.</em></p>
<blockquote>
<h2>Release 0.0.25</h2>
<h2> Upgrading</h2>
<ul>
<li>🧪 Test first <em>on a branch</em> 🏷️ by changing your workflow
tags/references to this release</li>
<li>See 🐣 <strong>Breaking Changes</strong> for how to adapt your
workflow</li>
<li>See 🐛 <strong>Known Issues</strong> for known issues</li>
</ul>
<h2>🐣 Breaking Changes</h2>
<ul>
<li><a
href="https://github.com/check-spelling/check-spelling/wiki/Breaking-Change:-Code-Scanning-action-requires-a-Code-Scanning-Ruleset">Code
Scanning action requires a Code Scanning Ruleset</a>
If you use SARIF reporting, then instead of the workflow yielding an 
when it fails, it will rely on <a
href="https://github.com/apps/github-advanced-security"><strong>github-advanced-security</strong>
🤖</a> to report the failure. You will need to adjust your checks for
PRs.</li>
<li><a
href="https://github.com/check-spelling/check-spelling/wiki/Breaking-change:-Dropping-support-for-on:-schedule">Dropping
support for <code>on: schedule</code></a></li>
<li><a
href="https://github.com/check-spelling/check-spelling/wiki/Breaking-Change:-Dropping-support-for-%60whitelist.txt%60-and-%60advice.txt%60">Dropping
support for <code>whitelist.txt</code> and
<code>advice.txt</code></a></li>
<li><a
href="https://github.com/check-spelling/check-spelling/wiki/Breaking-Change%3A-Dropping-support-for-out-of-tree-project-files">Dropping
support for out of tree project files</a></li>
<li>Dependent actions are referenced by full sha instead of version --
if you're using <a
href="https://docs.github.com/en/organizations/managing-organization-settings/disabling-or-limiting-github-actions-for-your-organization#allowing-select-actions-and-reusable-workflows-to-run">Allow
specified actions and reusable workflows</a>, you will need to add:
<ul>

<li><code>check-spelling/checkout-merge@46bad523dcb6368efab50ff2729c00443785abca</code>
(if you run <code>on: pull_request</code>/<code>on:
pull_requesrt_target</code> and use <code>with:</code>/<code>checkout:
true</code>)</li>
</ul>
</li>
</ul>
<h2> New Features</h2>
<ul>
<li><a
href="https://github.com/check-spelling/check-spelling/wiki/Feature:-Subrepos">Check
submodules</a> using <a
href="https://github.com/check-spelling/check-spelling/wiki/Configuration#submodules"><code>submodules</code></a></li>
<li>Provide instructions for when <code>only_check_changed_files</code>
finds unrecognized words (<a
href="https://api.github.com/repos/check-spelling/check-spelling/issues/86">#86</a>)</li>
<li>Support <a
href="https://github.com/check-spelling/check-spelling/wiki/Configuration/_edit#unknown_file_word_limit"><code>unknown_file_word_limit</code></a>
to limit the number of times a path in a filename is reported when using
<a
href="https://github.com/check-spelling/check-spelling/wiki/Configuration#check_file_names"><code>check_file_names</code></a></li>
<li>Suggest <a
href="https://github.com/check-spelling/check-spelling/wiki/Configuration#checkout"><code>checkout:
true</code></a> for new <a
href="https://github.com/check-spelling/check-spelling/wiki/Event-descriptions#single-line-file"><code>missing-checkout</code></a>
error case</li>
<li>Suggest ignoring files that trigger <a
href="https://github.com/check-spelling/check-spelling/wiki/Event-descriptions#single-line-file"><code>single-line-file</code></a></li>
</ul>
<h2>Dictionaries</h2>
<ul>
<li>Fix <a
href="https://github.com/check-spelling/check-spelling/wiki/Event-descriptions/#fallback-dictionary-not-found"><code>fallback-dictionary-not-found</code></a>
handling</li>
</ul>
<h3>Hunspell dictionaries</h3>
<ul>
<li>Fix support for <code>.dic</code>/<code>.aff</code> dictionaries by
installing hunspell as needed (<a
href="https://api.github.com/repos/check-spelling/check-spelling/issues/79">#79</a>
/ <a
href="https://api.github.com/repos/check-spelling/check-spelling/issues/90">#90</a>)</li>
<li>Ensure that <a
href="https://docs.check-spelling.dev/Feature:-Configurable-word-characters.html#spanish">Spanish</a>
works (a consumer is using this, so it should work reliably)</li>
</ul>
<h2>Fixes</h2>
<ul>
<li>macOS: Consistently use check-spelling dictionary instead of looking
at the system dictionary (<a
href="https://api.github.com/repos/check-spelling/check-spelling/issues/84">#84</a>)</li>
<li>Fix <code>check_for_newline_at_eof</code> for <code>allow.txt</code>
(<a
href="https://api.github.com/repos/check-spelling/check-spelling/issues/81">#81</a>)</li>
<li>Improve handling of <a
href="https://github.com/check-spelling/check-spelling/wiki/Configuration#ignored"><code>inputs.ignored</code></a>
events</li>
<li>Fix <a
href="https://github.com/check-spelling/check-spelling/wiki/Configuration:-Advanced#debug"><code>inputs.debug</code></a>
handling</li>
<li>Fix <strong>merge</strong> instructions order</li>
<li>Fix pattern for validating expect entries</li>
<li>Fix <a
href="https://github.com/check-spelling/check-spelling/wiki/Event-descriptions/#noisy-file-list"><code>noisy-file-list</code></a>
handling</li>
<li>Fix 504 handling for <a
href="https://github.com/check-spelling/check-spelling/wiki/Configuration#only_check_changed_files"><code>only_check_changed_files</code></a>
when unshallowing</li>
<li>Restore comment (<code>#</code>) support for expect files</li>
<li>Include last character in <a
href="https://github.com/check-spelling/check-spelling/wiki/Event-descriptions/#token-is-substring"><code>token-is-substring</code></a>
warning</li>
</ul>
<h2>Improvements</h2>
<ul>
<li>Line ending detection (<a
href="https://api.github.com/repos/check-spelling/check-spelling/issues/83">#83</a>)</li>
<li><a
href="https://www.fileformat.info/info/unicode/char/2019/index.htm">RSQM</a>
handling</li>
<li>Dictionary download times by skipping delays for <code>30x</code>
redirects</li>
<li>Excludes paths generation</li>
<li>Error handling of various components</li>
<li>Documentation links</li>
<li>GitHub error detection patterns for when check-spelling has
bugs</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="c635c2f3f7"><code>c635c2f</code></a>
action: Release v0.0.25</li>
<li><a
href="a72db74f75"><code>a72db74</code></a>
Yaml: Fix REPORT_MATCHING_YAML=1 handling</li>
<li><a
href="72391946fe"><code>7239194</code></a>
checkout: suppress default branch message</li>
<li><a
href="5b54b9ba2d"><code>5b54b9b</code></a>
CheckDictionary: Tolerate empty INPUT_IGNORE_PATTERN</li>
<li><a
href="eedc53a05c"><code>eedc53a</code></a>
test: Add coverage for non-alpha-in-dictionary</li>
<li><a
href="35e2bbcc5b"><code>35e2bbc</code></a>
unknown-words: Split error streams</li>
<li><a
href="b73164407b"><code>b731644</code></a>
unknown-words: Use encoding(UTF-8)</li>
<li><a
href="d6cb009abb"><code>d6cb009</code></a>
test: Expose dependency timing</li>
<li><a
href="eac6da34a8"><code>eac6da3</code></a>
test: Improve local testing</li>
<li><a
href="a9ff41ecae"><code>a9ff41e</code></a>
Suggest using <code>#</code> to suppress candidates</li>
<li>Additional commits viewable in <a
href="67debf5066...c635c2f3f7">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=check-spelling/check-spelling&package-manager=github_actions&previous-version=0.0.24&new-version=0.0.25)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-10 13:10:57 +08:00
Gordon Lam
edffb99073 Log WPF crash exceptions instead of showing Report problem UI (#39918)
<!-- 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
Problem
-------------
We've observed multiple bug reports related to crashes with the
following exceptions:
System.Runtime.InteropServices.COMException (0xD0000701)
System.Runtime.InteropServices.COMException (0x80263001)

The root cause of these crashes has been traced to the WPF framework,
specifically this line in WindowChromeWorker.cs:

3439f20fb8/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Shell/WindowChromeWorker.cs (L1005)

These crashes are not necessarily caused by PowerToys Run itself being
used, or even visible. However, users perceive them as Run-related
because the "Report problem UI" is triggered by our global exception
handler, surfacing the underlying WPF crash.

Fix
------------
This PR:
- Suppresses the launch of the "Report problem UI" for exceptions known
to be triggered by unstable platform conditions (such as COMExceptions
from WPF internals).
- Continues to log the exception to preserve diagnostic data.

This change ensures we:
- Avoid showing an UI when WPF framework itself is already having
problem to handling DWM composition changes.
2025-06-10 13:00:12 +08:00
Mike Griese
310186c44c CmdPal: Auto-focus first content (#39414)
This lets us auto-focus the first input on a `IFormContent`, if there's
only one `IContent` on the page.

This dramatically improves the usability for forms, since we'll
immediately put focus into them

Closes #38436
2025-06-08 19:29:25 -05:00
Mike Griese
6d303af726 CmdPal: Apply the LLKH to all command hotkeys (#39524)
<!-- Let's say you wanted to add a hotkey to the Run command. And let's
say you wanted it to be Win+R. This lets you do that -->

We should apply the LLKH setting to all command hotkeys, not just the
global one.
2025-06-08 12:06:33 -05:00
Nathan Gill
84296b0d89 [CmdPal] Remove redundant flag to prevent resource leak (#39865)
<!-- 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

Remove a redundant flag in the thumbnail helper that leads to a resource
leak.

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

- [x] **Closes:** #39824 
- [ ] **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

Remove the `SHGFI_ICON` flag from the call to
`NativeMethods.SHGetFileInfo`.

This flag opens a handle to the icon (`hIcon`) as well as filling the
index (`iIcon`)
([docs](https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shgetfileinfow#shgfi_icon-0x000000100)).
This handle stored in `shinfo.hIcon` is never used by following code,
and is not freed as suggested by the documentation
([docs](https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shgetfileinfow#remarks)).

The `SHGFI_ICON` flag also fills `shinfo.iIcon`, which is used later.
However, this is also filled when passing the flag `SHGFI_SYSICONINDEX`,
which we already pass.

Therefore, the `SHGFI_ICON` flag is redundant here, and has been
removed.

Thanks to @Androvald for finding  and suggesting the fix.
2025-06-08 12:06:01 -05:00
Muhammad Danish
7417b857e8 Use Microsoft.Windows.Settings module for enabling developer mode (#39334)
<!-- 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

Using the
[Microsoft.Windows.Settings](https://www.powershellgallery.com/packages/Microsoft.Windows.Settings/)
module is the now the recommended way of configuring Windows Settings,
including developer mode.


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

Verified that setting developer mode with the new resource works as
intended
2025-06-06 20:58:10 -07:00
Michael Jolley
4f3f7d649f CmdPal: Improving order of results from settings search (#39525)
The search results when finding settings with `$` was sorted
alphabetically. Updated this behavior to sort them based on relevance to
the query.

Original search results with query provided in issue:

![image](https://github.com/user-attachments/assets/7ffd41bc-42d4-46fe-925e-aed7d7d04f3f)

New search results with query provided in original issue:

![Image](https://github.com/user-attachments/assets/ab47fc25-9b90-4cc7-b476-253b23cee6f9)

Example of results when giving a query that matches many, but is also an
exact match.

![Image](https://github.com/user-attachments/assets/59982f41-d27c-4582-9b01-9a5683a26819)

Closes #38615
2025-06-06 12:47:54 -05: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
207 changed files with 4128 additions and 2383 deletions

View File

@@ -2,14 +2,14 @@
# Reference: https://github.com/microsoft/PowerToys/blob/main/doc/devdocs/readme.md#compiling-powertoys
properties:
resources:
- resource: Microsoft.Windows.Developer/DeveloperMode
- resource: Microsoft.Windows.Settings/WindowsSettings
directives:
description: Enable Developer Mode
allowPrerelease: true
# Requires elevation for the set operation
securityContext: elevated
settings:
Ensure: Present
DeveloperMode: true
- resource: Microsoft.WinGet.DSC/WinGetPackage
id: vsPackage
directives:

View File

@@ -2,14 +2,14 @@
# Reference: https://github.com/microsoft/PowerToys/blob/main/doc/devdocs/readme.md#compiling-powertoys
properties:
resources:
- resource: Microsoft.Windows.Developer/DeveloperMode
- resource: Microsoft.Windows.Settings/WindowsSettings
directives:
description: Enable Developer Mode
allowPrerelease: true
# Requires elevation for the set operation
securityContext: elevated
settings:
Ensure: Present
DeveloperMode: true
- resource: Microsoft.WinGet.DSC/WinGetPackage
id: vsPackage
directives:

View File

@@ -2,14 +2,14 @@
# Reference: https://github.com/microsoft/PowerToys/blob/main/doc/devdocs/readme.md#compiling-powertoys
properties:
resources:
- resource: Microsoft.Windows.Developer/DeveloperMode
- resource: Microsoft.Windows.Settings/WindowsSettings
directives:
description: Enable Developer Mode
allowPrerelease: true
# Requires elevation for the set operation
securityContext: elevated
settings:
Ensure: Present
DeveloperMode: true
- resource: Microsoft.WinGet.DSC/WinGetPackage
id: vsPackage
directives:

View File

@@ -12,7 +12,7 @@ body:
attributes:
label: Microsoft PowerToys version
placeholder: X.XX.X
description: Hover over system tray icon or look at Settings
description: Hover over the system tray icon or look at Settings
validations:
required: true
@@ -20,7 +20,7 @@ body:
type: dropdown
attributes:
label: Installation method
description: How / Where was PowerToys installed from?
description: How / where was PowerToys installed from?
multiple: true
options:
- GitHub

View File

@@ -6,7 +6,7 @@ labels:
body:
- type: textarea
attributes:
label: Provide a description of requested docs changes
label: Describe the requested doc changes
placeholder: Briefly describe which document needs to be corrected and why.
validations:
required: true

View File

@@ -13,7 +13,7 @@ body:
- type: textarea
attributes:
label: Scenario when this would be used?
placeholder: What is the scenario this would be used? Why is this important to your workflow as a power user?
placeholder: What is the scenario this would be used in? Why is this important to your workflow as a power user?
validations:
required: true
- type: textarea

View File

@@ -14,7 +14,7 @@ body:
attributes:
label: Microsoft PowerToys version
placeholder: 0.70.0
description: Hover over system tray icon or look at Settings
description: Hover over the system tray icon or look at Settings
validations:
required: true
- type: dropdown
@@ -65,7 +65,7 @@ body:
- type: textarea
attributes:
label: ❌ Actual phrase(s)
placeholder: What is there? Please include a screenshot as that is extremely helpful.
placeholder: What is there? Please include a screenshot, as that is extremely helpful.
validations:
required: true
- type: textarea

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
@@ -166,9 +173,11 @@ Tadele
talynone
Taras
TBM
Teutsch
tilovell
Triet
urnotdfs
vednig
waaverecords
wang
Whuihuan
@@ -187,9 +196,6 @@ zhaopy
zhaoqpcn
Zoltan
Zykova
Sameerjs
ruslanlap
vednig
# OTHERS

View File

@@ -92,6 +92,7 @@
^\.github/actions/spell-check/
^\.gitmodules$
^\Q.github/workflows/spelling2.yml\E$
^\Q.pipelines/272MSSharedLibSN2048.snk\E$
^\Q.pipelines/ESRPSigning_core.json\E$
^\Qdoc/devdocs/localization.md\E$
^\Qsrc/common/ManagedCommon/ColorFormatHelper.cs\E$
@@ -120,6 +121,7 @@
^src/modules/MouseWithoutBorders/App/Form/.*\.resx$
^src/modules/MouseWithoutBorders/App/Helper/.*\.resx$
^src/modules/previewpane/UnitTests-MarkdownPreviewHandler/HelperFiles/MarkdownWithHTMLImageTag\.txt$
^src/modules/ZoomIt/ZoomIt/ZoomIt\.idc$
^src/Monaco/
^src/common/sysinternals/Eula/
^tools/Verification scripts/Check preview handler registration\.ps1$

View File

@@ -14,6 +14,7 @@ AColumn
acrt
ACTIVATEAPP
activationaction
ACTIVATEOPTIONS
ACVS
adaptivecards
ADate
@@ -327,7 +328,6 @@ DEFAULTTONULL
DEFAULTTOPRIMARY
DEFERERASE
DEFPUSHBUTTON
DEFT
deinitialization
DELA
DELETEDKEYIMAGE
@@ -669,7 +669,6 @@ IBeam
icf
ICONERROR
ICONLOCATION
idc
IDCANCEL
IDD
idk
@@ -680,7 +679,6 @@ IDR
IDXGI
ietf
IEXPLORE
iextn
IFACEMETHOD
IFACEMETHODIMP
IFile
@@ -719,7 +717,6 @@ INPUTMOUSE
INPUTSINK
INPUTTYPE
INSTALLDESKTOPSHORTCUT
INSTALLDIR
installdir
INSTALLFOLDER
INSTALLFOLDERTOBOOTSTRAPPERINSTALLFOLDER
@@ -755,6 +752,7 @@ iwr
jfif
jgeosdfsdsgmkedfgdfgdfgbkmhcgcflmi
jjw
JLO
jobject
jpe
jpnime
@@ -788,6 +786,7 @@ LCIDTo
Lclean
Ldone
Ldr
LEFTALIGN
LEFTSCROLLBAR
LEFTTEXT
LError
@@ -810,7 +809,6 @@ LMENU
lnks
LOADFROMFILE
LOBYTE
localappdata
LOCALDISPLAY
localpackage
LOCALSYSTEM
@@ -975,7 +973,6 @@ msrc
msstore
mst
msvcp
msvsmon
MTND
MULTIPLEUSE
multizone
@@ -1282,7 +1279,6 @@ pstm
PStr
pstream
pstrm
pswd
PSYSTEM
psz
ptb
@@ -1474,16 +1470,12 @@ SHELLDLL
shellex
SHELLEXECUTEINFO
SHELLEXECUTEINFOW
SHELLEXTENSION
SHELLICONSIZE
SHELLNEWVALUE
SHFILEINFO
SHFILEOPSTRUCT
SHGDN
SHGDNF
SHGFI
SHGFIICON
SHGFILARGEICON
SHIL
shinfo
shlwapi
@@ -1517,7 +1509,6 @@ siex
sigdn
SIGNINGSCENARIO
signtool
Signtool
SINGLEKEY
sipolicy
SIZEBOX
@@ -1588,6 +1579,7 @@ steamapps
STGC
STGM
STGMEDIUM
STGMREAD
STICKYKEYS
sticpl
storelogo
@@ -1779,7 +1771,6 @@ uxtheme
vabdq
validmodulename
valuegenerator
VARENUM
variantassignment
vcamp
VCENTER
@@ -1995,3 +1986,10 @@ culori
Evercoder
LCh
CIELCh
CLSCTXINPROCALL
IIDI
irow
lcid
ppwsz
rguid
VARTYPE

View File

@@ -243,4 +243,4 @@ Process Process
# ZoomIt menu items with accelerator keys
E&xit
St&yle
St&yle

View File

@@ -5,9 +5,9 @@
## PR Checklist
- [ ] **Closes:** #xxx
- [ ] **Communication:** I've discussed this with core contributors already. If work hasn't been agreed, this work might be rejected
- [ ] **Communication:** I've discussed this with core contributors already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end user facing strings can be localized
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries
@@ -16,7 +16,7 @@
- [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx
<!-- Provide a more detailed description of the PR, other things fixed or any additional comments/features here -->
<!-- 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 -->

View File

@@ -1,5 +1,4 @@
name: WinGet submission on release
# based off of https://github.com/nushell/nushell/blob/main/.github/workflows/winget-submission.yml
on:
workflow_dispatch:
@@ -9,23 +8,31 @@ on:
jobs:
winget:
name: Publish winget package
# winget-create is only supported on Windows
runs-on: windows-latest
# winget-create will read the following environment variable to access the GitHub token needed for submitting a PR
# See https://aka.ms/winget-create-token
env:
WINGET_CREATE_GITHUB_TOKEN: ${{ secrets.PT_WINGET }}
# Only submit stable releases
if: ${{ !github.event.release.prerelease }}
steps:
- name: Submit Microsoft.PowerToys package to Windows Package Manager Community Repository
run: |
# Get installer info from GitHub release event
$assets = '${{ toJSON(github.event.release.assets) }}' | ConvertFrom-Json
$x64UserInstallerUrl = $assets | Where-Object -Property name -match 'PowerToysUserSetup.*x64' | Select -ExpandProperty browser_download_url
$x64MachineInstallerUrl = $assets | Where-Object -Property name -match 'PowerToysSetup.*x64' | Select -ExpandProperty browser_download_url
$arm64UserInstallerUrl = $assets | Where-Object -Property name -match 'PowerToysUserSetup.*arm64' | Select -ExpandProperty browser_download_url
$arm64MachineInstallerUrl = $assets | Where-Object -Property name -match 'PowerToysSetup.*arm64' | Select -ExpandProperty browser_download_url
$packageVersion = (${{ toJSON(github.event.release.tag_name) }}).Trim('v')
$wingetPackage = "Microsoft.PowerToys"
$gitToken = "${{ secrets.PT_WINGET }}"
$github = Invoke-RestMethod -uri "https://api.github.com/repos/Microsoft/PowerToys/releases"
$targetRelease = $github | Where-Object -Property name -match 'Release'| Select -First 1
$installerUserX64Url = $targetRelease | Select -ExpandProperty assets -First 1 | Where-Object -Property name -match 'PowerToysUserSetup.*x64' | Select -ExpandProperty browser_download_url
$installerMachineX64Url = $targetRelease | Select -ExpandProperty assets -First 1 | Where-Object -Property name -match 'PowerToysSetup.*x64' | Select -ExpandProperty browser_download_url
$installerUserArmUrl = $targetRelease | Select -ExpandProperty assets -First 1 | Where-Object -Property name -match 'PowerToysUserSetup.*arm64' | Select -ExpandProperty browser_download_url
$installerMachineArmUrl = $targetRelease | Select -ExpandProperty assets -First 1 | Where-Object -Property name -match 'PowerToysSetup.*arm64' | Select -ExpandProperty browser_download_url
$ver = $targetRelease.tag_name -ireplace '^v'
# getting latest wingetcreate file
iwr https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe
.\wingetcreate.exe update $wingetPackage -s -v $ver -u "$installerUserX64Url|user" "$installerMachineX64Url|machine" "$installerUserArmUrl|user" "$installerMachineArmUrl|machine" -t $gitToken
# Update package using wingetcreate
curl.exe -JLO https://aka.ms/wingetcreate/latest
.\wingetcreate.exe update Microsoft.PowerToys `
--version $packageVersion `
--urls "$x64UserInstallerUrl|user" "$x64MachineInstallerUrl|machine" "$arm64UserInstallerUrl|user" "$arm64MachineInstallerUrl|machine" `
--submit

View File

@@ -93,7 +93,7 @@ jobs:
steps:
- name: check-spelling
id: spelling
uses: check-spelling/check-spelling@67debf50669c7fc76fc8f5d7f996384535a72b77 # v0.0.24
uses: check-spelling/check-spelling@c635c2f3f714eec2fcf27b643a1919b9a811ef2e # v0.0.25
with:
config: .github/actions/spell-check
suppress_push_for_open_pull_request: ${{ github.actor != 'dependabot[bot]' && 1 }}
@@ -156,7 +156,7 @@ jobs:
if: (success() || failure()) && needs.spelling.outputs.followup && github.event_name == 'push'
steps:
- name: comment
uses: check-spelling/check-spelling@67debf50669c7fc76fc8f5d7f996384535a72b77 # v0.0.24
uses: check-spelling/check-spelling@c635c2f3f714eec2fcf27b643a1919b9a811ef2e # v0.0.25
with:
config: .github/actions/spell-check
checkout: true
@@ -175,7 +175,7 @@ jobs:
if: (success() || failure()) && needs.spelling.outputs.followup && contains(github.event_name, 'pull_request')
steps:
- name: comment
uses: check-spelling/check-spelling@67debf50669c7fc76fc8f5d7f996384535a72b77 # v0.0.24
uses: check-spelling/check-spelling@c635c2f3f714eec2fcf27b643a1919b9a811ef2e # v0.0.25
with:
config: .github/actions/spell-check
checkout: true
@@ -202,7 +202,7 @@ jobs:
cancel-in-progress: false
steps:
- name: apply spelling updates
uses: check-spelling/check-spelling@67debf50669c7fc76fc8f5d7f996384535a72b77 # v0.0.24
uses: check-spelling/check-spelling@c635c2f3f714eec2fcf27b643a1919b9a811ef2e # v0.0.25
with:
experimental_apply_changes_via_bot: ${{ github.repository_owner != 'microsoft' && 1 }}
checkout: true

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

@@ -394,9 +394,7 @@ jobs:
testAssemblyVer2: |
**\KeyboardManagerEngineTest.dll
**\KeyboardManagerEditorTest.dll
**\UnitTests-CommonLib.dll
**\PowerRenameUnitTests.dll
**\UnitTests-FancyZones.dll
**\*UnitTest*.dll
!**\obj\**
- pwsh: |-

View File

@@ -68,7 +68,7 @@ jobs:
pwsh: true
ScriptType: InlineScript
Inline: |-
$AzToken = (Get-AzAccessToken -ResourceUrl api://30471ccf-0966-45b9-a979-065dbedb24c1).Token
$AzToken = (Get-AzAccessToken -AsSecureString -ResourceUrl api://30471ccf-0966-45b9-a979-065dbedb24c1).Token | ConvertFrom-SecureString -AsPlainText
Write-Host "##vso[task.setvariable variable=SymbolAccessToken;issecret=true]$AzToken"

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

@@ -72,9 +72,57 @@ $returnList = [System.Collections.Generic.HashSet[string]]($totalList) -join "`r
Write-Host $returnList
# Extract the current package list from NOTICE.md
$noticePattern = "## NuGet Packages used by PowerToys\s*((?:\r?\n- .+)+)"
$noticeMatch = [regex]::Match($noticeFile, $noticePattern)
if ($noticeMatch.Success) {
$currentNoticePackageList = $noticeMatch.Groups[1].Value.Trim()
} else {
Write-Warning "Warning: Could not find 'NuGet Packages used by PowerToys' section in NOTICE.md"
$currentNoticePackageList = ""
}
if (!$noticeFile.Trim().EndsWith($returnList.Trim()))
{
Write-Host -ForegroundColor Red "Notice.md does not match NuGet list."
# Show detailed differences
$generatedPackages = $returnList -split "`r`n|`n" | Where-Object { $_.Trim() -ne "" } | Sort-Object
$noticePackages = $currentNoticePackageList -split "`r`n|`n" | Where-Object { $_.Trim() -ne "" } | ForEach-Object { $_.Trim() } | Sort-Object
Write-Host ""
Write-Host -ForegroundColor Cyan "=== DETAILED DIFFERENCE ANALYSIS ==="
Write-Host ""
# Find packages in proj file list but not in NOTICE.md
$missingFromNotice = $generatedPackages | Where-Object { $noticePackages -notcontains $_ }
if ($missingFromNotice.Count -gt 0) {
Write-Host -ForegroundColor Red "MissingFromNotice:"
foreach ($pkg in $missingFromNotice) {
Write-Host -ForegroundColor Red " $pkg"
}
Write-Host ""
}
# Find packages in NOTICE.md but not in proj file list
$extraInNotice = $noticePackages | Where-Object { $generatedPackages -notcontains $_ }
if ($extraInNotice.Count -gt 0) {
Write-Host -ForegroundColor Yellow "ExtraInNotice:"
foreach ($pkg in $extraInNotice) {
Write-Host -ForegroundColor Yellow " $pkg"
}
Write-Host ""
}
# Show counts for summary
Write-Host -ForegroundColor Cyan "Summary:"
Write-Host " Proj file list has $($generatedPackages.Count) packages"
Write-Host " NOTICE.md has $($noticePackages.Count) packages"
Write-Host " MissingFromNotice: $($missingFromNotice.Count) packages"
Write-Host " ExtraInNotice: $($extraInNotice.Count) packages"
Write-Host ""
exit 1
}

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
@@ -21,7 +21,7 @@ Connor was the creator of Workspaces and helped create Command Palette (PowerToy
### [@damienleroy](https://github.com/damienleroy) - [Damien Leroy](https://www.linkedin.com/in/Damien-Leroy-b2734416a/)
Damien has helped out by developing and contributing the Quick Accent utility.
### [@daverayment ](https://github.com/daverayment) - [David Rayment](https://www.linkedin.com/in/david-rayment-168b5251/)
### [@daverayment](https://github.com/daverayment) - [David Rayment](https://www.linkedin.com/in/david-rayment-168b5251/)
Dave has helped improve the experience inside of Peek by adding in new features and fixing bugs.
### [@davidegiacometti](https://github.com/davidegiacometti) - [Davide Giacometti](https://www.linkedin.com/in/davidegiacometti/)
@@ -117,10 +117,6 @@ PowerRename is from Chris's SmartRename and icon rendering for SVGs in File Expl
PowerToys Awake is a tool to keep your computer awake.
### [@Niels9001](https://github.com/niels9001/) - [Niels Laute](https://nielslaute.com/)
Niels has helped drive large sums of our update toward a new [consistent and modern UX](https://github.com/microsoft/PowerToys/issues/891). This includes the [launcher work](https://github.com/microsoft/PowerToys/issues/44), color picker UX update and [icon design](https://github.com/microsoft/PowerToys/issues/1118).
### [@randyrants](https://github.com/randyrants) - [Randy Santossio](https://www.randyrants.com)
Randy contributed Registry Preview and some very early conversations about keyboard remapping.
@@ -184,11 +180,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
- [@zhiwei-ms](https://github.com/zhiwei-ms) - Zhiwei Yu - Product Manager
- [@niels9001](https://github.com/niels9001/) - Niels Laute - Product Manager
- [@dhowett](https://github.com/dhowett) - Dustin Howett - Dev lead
- [@yeelam-gordon](https://github.com/yeelam-gordon) - Gordon Lam - Dev lead
- [@jamrobot](https://github.com/jamrobot) - Jerry Xu - Dev lead
@@ -204,6 +199,13 @@ ZoomIt source code was originally implemented by [Sysinternals](https://sysinter
- [@zhaopy536](https://github.com/zhaopy536) - Peiyao Zhao - Dev
- [@wang563681252](https://github.com/wang563681252) - Zhaopeng Wang - Dev
- [@vanzue](https://github.com/vanzue) - Kai Tao - Dev
- [@zadjii-msft](https://github.com/zadjii-msft) - Mike Griese - Dev
- [@khmyznikov](https://github.com/khmyznikov) - Gleb Khmyznikov - Dev
- [@chatasweetie](https://github.com/chatasweetie) - Jessica Earley-Cha - Dev
- [@MichaelJolley](https://github.com/MichaelJolley) - Michael Jolley - Dev
- [@Jaylyn-Barbee](https://github.com/Jaylyn-Barbee) - Jaylyn Barbee - Dev
- [@zateutsch](https://github.com/zateutsch) - Zach Teutsch - Dev
- [@crutkas](https://github.com/crutkas/) - Clint Rutkas - Overhead
## Former PowerToys core team members

View File

@@ -31,22 +31,22 @@
<!-- Including MessagePack to force version, since it's used by StreamJsonRpc but contains vulnerabilities. After StreamJsonRpc updates the version of MessagePack, we can upgrade StreamJsonRpc instead. -->
<PackageVersion Include="MessagePack" Version="3.1.3" />
<PackageVersion Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="9.0.0" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.5" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.6" />
<!-- Including Microsoft.Bcl.AsyncInterfaces to force version, since it's used by Microsoft.SemanticKernel. -->
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.5" />
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.6" />
<PackageVersion Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="3.1.16" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.6" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.6" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.6" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.6" />
<PackageVersion Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.6" />
<PackageVersion Include="Microsoft.SemanticKernel" Version="1.15.0" />
<PackageVersion Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.2" />
<PackageVersion Include="Microsoft.Web.WebView2" Version="1.0.2903.40" />
<!-- Package Microsoft.Win32.SystemEvents added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.1. This is a dependency of System.Drawing.Common but the 8.0.1 version wasn't published to nuget. -->
<PackageVersion Include="Microsoft.Win32.SystemEvents" Version="9.0.5" />
<PackageVersion Include="Microsoft.Win32.SystemEvents" Version="9.0.6" />
<PackageVersion Include="Microsoft.WindowsPackageManager.ComInterop" Version="1.10.340" />
<PackageVersion Include="Microsoft.Windows.Compatibility" Version="9.0.5" />
<PackageVersion Include="Microsoft.Windows.Compatibility" Version="9.0.6" />
<PackageVersion Include="Microsoft.Windows.CsWin32" Version="0.2.46-beta" />
<!-- CsWinRT version needs to be set to have a WinRT.Runtime.dll at the same version contained inside the NET SDK we're currently building on CI. -->
<!--
@@ -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" />
@@ -72,28 +72,28 @@
<PackageVersion Include="StreamJsonRpc" Version="2.21.69" />
<PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
<!-- Package System.CodeDom added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.1. This is a dependency of System.Management but the 8.0.1 version wasn't published to nuget. -->
<PackageVersion Include="System.CodeDom" Version="9.0.5" />
<PackageVersion Include="System.CodeDom" Version="9.0.6" />
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
<PackageVersion Include="System.ComponentModel.Composition" Version="9.0.5" />
<PackageVersion Include="System.Configuration.ConfigurationManager" Version="9.0.5" />
<PackageVersion Include="System.Data.OleDb" Version="9.0.5" />
<PackageVersion Include="System.ComponentModel.Composition" Version="9.0.6" />
<PackageVersion Include="System.Configuration.ConfigurationManager" Version="9.0.6" />
<PackageVersion Include="System.Data.OleDb" Version="9.0.6" />
<!-- Package System.Data.SqlClient added to force it as a dependency of Microsoft.Windows.Compatibility to the latest version available at this time. -->
<PackageVersion Include="System.Data.SqlClient" Version="4.8.6" />
<PackageVersion Include="System.Data.SqlClient" Version="4.9.0" />
<!-- Package System.Diagnostics.EventLog added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.1. This is a dependency of System.Data.OleDb but the 8.0.1 version wasn't published to nuget. -->
<PackageVersion Include="System.Diagnostics.EventLog" Version="9.0.5" />
<PackageVersion Include="System.Diagnostics.EventLog" Version="9.0.6" />
<!-- Package System.Diagnostics.PerformanceCounter added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.11. -->
<PackageVersion Include="System.Diagnostics.PerformanceCounter" Version="9.0.5" />
<PackageVersion Include="System.Drawing.Common" Version="9.0.5" />
<PackageVersion Include="System.Diagnostics.PerformanceCounter" Version="9.0.6" />
<PackageVersion Include="System.Drawing.Common" Version="9.0.6" />
<PackageVersion Include="System.IO.Abstractions" Version="22.0.13" />
<PackageVersion Include="System.IO.Abstractions.TestingHelpers" Version="22.0.13" />
<PackageVersion Include="System.Management" Version="9.0.5" />
<PackageVersion Include="System.Management" Version="9.0.6" />
<PackageVersion Include="System.Net.Http" Version="4.3.4" />
<PackageVersion Include="System.Private.Uri" Version="4.3.2" />
<PackageVersion Include="System.Reactive" Version="6.0.1" />
<PackageVersion Include="System.Runtime.Caching" Version="9.0.5" />
<PackageVersion Include="System.ServiceProcess.ServiceController" Version="9.0.5" />
<PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.5" />
<PackageVersion Include="System.Text.Json" Version="9.0.5" />
<PackageVersion Include="System.Runtime.Caching" Version="9.0.6" />
<PackageVersion Include="System.ServiceProcess.ServiceController" Version="9.0.6" />
<PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.6" />
<PackageVersion Include="System.Text.Json" Version="9.0.6" />
<PackageVersion Include="System.Text.RegularExpressions" Version="4.3.1" />
<PackageVersion Include="UnicodeInformation" Version="2.6.0" />
<PackageVersion Include="UnitsNet" Version="5.56.0" />

View File

@@ -807,30 +807,25 @@ DEALINGS IN THE SOFTWARE.
**Source**: https://github.com/kuba--/zip
This is free and unencumbered software released into the public domain.
All Rights Reserved.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
## Utility: Measure tool
@@ -1453,26 +1448,26 @@ SOFTWARE.
- Mages 3.0.0
- Markdig.Signed 0.34.0
- MessagePack 3.1.3
- Microsoft.Bcl.AsyncInterfaces 9.0.5
- Microsoft.Bcl.AsyncInterfaces 9.0.6
- Microsoft.CodeAnalysis.NetAnalyzers 9.0.0
- Microsoft.Data.Sqlite 9.0.5
- Microsoft.Data.Sqlite 9.0.6
- Microsoft.Diagnostics.Tracing.TraceEvent 3.1.16
- Microsoft.DotNet.ILCompiler (A)
- Microsoft.Extensions.DependencyInjection 9.0.5
- Microsoft.Extensions.Hosting 9.0.5
- Microsoft.Extensions.Hosting.WindowsServices 9.0.5
- Microsoft.Extensions.Logging 9.0.5
- Microsoft.Extensions.Logging.Abstractions 9.0.5
- Microsoft.Extensions.DependencyInjection 9.0.6
- Microsoft.Extensions.Hosting 9.0.6
- Microsoft.Extensions.Hosting.WindowsServices 9.0.6
- Microsoft.Extensions.Logging 9.0.6
- Microsoft.Extensions.Logging.Abstractions 9.0.6
- Microsoft.NET.ILLink.Tasks (A)
- Microsoft.SemanticKernel 1.15.0
- Microsoft.Toolkit.Uwp.Notifications 7.1.2
- Microsoft.Web.WebView2 1.0.2903.40
- Microsoft.Win32.SystemEvents 9.0.5
- Microsoft.Windows.Compatibility 9.0.5
- Microsoft.Win32.SystemEvents 9.0.6
- Microsoft.Windows.Compatibility 9.0.6
- 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
@@ -1487,25 +1482,25 @@ SOFTWARE.
- SharpCompress 0.37.2
- StreamJsonRpc 2.21.69
- StyleCop.Analyzers 1.2.0-beta.556
- System.CodeDom 9.0.5
- System.CodeDom 9.0.6
- System.CommandLine 2.0.0-beta4.22272.1
- System.ComponentModel.Composition 9.0.5
- System.Configuration.ConfigurationManager 9.0.5
- System.Data.OleDb 9.0.5
- System.Data.SqlClient 4.8.6
- System.Diagnostics.EventLog 9.0.5
- System.Diagnostics.PerformanceCounter 9.0.5
- System.Drawing.Common 9.0.5
- System.ComponentModel.Composition 9.0.6
- System.Configuration.ConfigurationManager 9.0.6
- System.Data.OleDb 9.0.6
- System.Data.SqlClient 4.9.0
- System.Diagnostics.EventLog 9.0.6
- System.Diagnostics.PerformanceCounter 9.0.6
- System.Drawing.Common 9.0.6
- System.IO.Abstractions 22.0.13
- System.IO.Abstractions.TestingHelpers 22.0.13
- System.Management 9.0.5
- System.Management 9.0.6
- System.Net.Http 4.3.4
- System.Private.Uri 4.3.2
- System.Reactive 6.0.1
- System.Runtime.Caching 9.0.5
- System.ServiceProcess.ServiceController 9.0.5
- System.Text.Encoding.CodePages 9.0.5
- System.Text.Json 9.0.5
- System.Runtime.Caching 9.0.6
- System.ServiceProcess.ServiceController 9.0.6
- System.Text.Encoding.CodePages 9.0.6
- System.Text.Json 9.0.6
- System.Text.RegularExpressions 4.3.1
- UnicodeInformation 2.6.0
- UnitsNet 5.56.0

View File

@@ -604,6 +604,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WindowProperties", "WindowP
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WorkspacesLib", "src\modules\Workspaces\WorkspacesLib\WorkspacesLib.vcxproj", "{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WorkspacesLibUnitTests", "src\modules\Workspaces\WorkspacesLib.UnitTests\WorkspacesLibUnitTests.vcxproj", "{A85D4D9F-9A39-4B5D-8B5A-9F2D5C9A8B4C}"
ProjectSection(ProjectDependencies) = postProject
{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332} = {B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkspacesLauncherUI", "src\modules\Workspaces\WorkspacesLauncherUI\WorkspacesLauncherUI.csproj", "{9C53CC25-0623-4569-95BC-B05410675EE3}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WorkspacesModuleInterface", "src\modules\Workspaces\WorkspacesModuleInterface\WorkspacesModuleInterface.vcxproj", "{45285DF2-9742-4ECA-9AC9-58951FC26489}"
@@ -706,6 +711,8 @@ 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}"
@@ -2198,6 +2205,14 @@ Global
{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Release|ARM64.Build.0 = Release|ARM64
{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Release|x64.ActiveCfg = Release|x64
{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Release|x64.Build.0 = Release|x64
{A85D4D9F-9A39-4B5D-8B5A-9F2D5C9A8B4C}.Debug|ARM64.ActiveCfg = Debug|ARM64
{A85D4D9F-9A39-4B5D-8B5A-9F2D5C9A8B4C}.Debug|ARM64.Build.0 = Debug|ARM64
{A85D4D9F-9A39-4B5D-8B5A-9F2D5C9A8B4C}.Debug|x64.ActiveCfg = Debug|x64
{A85D4D9F-9A39-4B5D-8B5A-9F2D5C9A8B4C}.Debug|x64.Build.0 = Debug|x64
{A85D4D9F-9A39-4B5D-8B5A-9F2D5C9A8B4C}.Release|ARM64.ActiveCfg = Release|ARM64
{A85D4D9F-9A39-4B5D-8B5A-9F2D5C9A8B4C}.Release|ARM64.Build.0 = Release|ARM64
{A85D4D9F-9A39-4B5D-8B5A-9F2D5C9A8B4C}.Release|x64.ActiveCfg = Release|x64
{A85D4D9F-9A39-4B5D-8B5A-9F2D5C9A8B4C}.Release|x64.Build.0 = Release|x64
{9C53CC25-0623-4569-95BC-B05410675EE3}.Debug|ARM64.ActiveCfg = Debug|ARM64
{9C53CC25-0623-4569-95BC-B05410675EE3}.Debug|ARM64.Build.0 = Debug|ARM64
{9C53CC25-0623-4569-95BC-B05410675EE3}.Debug|x64.ActiveCfg = Debug|x64
@@ -2582,6 +2597,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
@@ -2815,6 +2838,7 @@ Global
{BE126CBB-AE12-406A-9837-A05ACFCA57A7} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
{14CB58B7-D280-4A7A-95DE-4B2DF14EA000} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
{A85D4D9F-9A39-4B5D-8B5A-9F2D5C9A8B4C} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
{9C53CC25-0623-4569-95BC-B05410675EE3} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
{45285DF2-9742-4ECA-9AC9-58951FC26489} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
{3D63307B-9D27-44FD-B033-B26F39245B85} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
@@ -2866,6 +2890,7 @@ 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}
EndGlobalSection

View File

@@ -53,10 +53,10 @@ This is our preferred method.
### Via Microsoft Store
Install from the [Microsoft Store's PowerToys page][microsoft-store-link]. You must be using the [new Microsoft Store](https://blogs.windows.com/windowsExperience/2021/06/24/building-a-new-open-microsoft-store-on-windows-11/) which is available for both Windows 11 and Windows 10.
Install from the [Microsoft Store's PowerToys page][microsoft-store-link]. You must be using the [new Microsoft Store](https://blogs.windows.com/windowsExperience/2021/06/24/building-a-new-open-microsoft-store-on-windows-11/), which is available for both Windows 11 and Windows 10.
### Via WinGet
Download PowerToys from [WinGet][winget-link]. Updating PowerToys via winget will respect current PowerToys installation scope. To install PowerToys, run the following command from the command line / PowerShell:
Download PowerToys from [WinGet][winget-link]. Updating PowerToys via winget will respect the current PowerToys installation scope. To install PowerToys, run the following command from the command line / PowerShell:
#### User scope installer [default]
```powershell
@@ -79,7 +79,7 @@ There is a collection of [third-party plugins](./doc/thirdPartyRunPlugins.md) cr
## Contributing
This project welcomes contributions of all types. Besides coding features / bug fixes, other ways to assist include spec writing, design, documentation, and finding bugs. We are excited to work with the power user community to build a set of tools for helping you get the most out of Windows.
This project welcomes contributions of all types. Besides coding features / bug fixes, other ways to assist include spec writing, design, documentation, and finding bugs. We are excited to work with the power user community to build a set of tools for helping you get the most out of Windows.
We ask that **before you start work on a feature that you would like to contribute**, please read our [Contributor's Guide](CONTRIBUTING.md). We would be happy to work with you to figure out the best approach, provide guidance and mentorship throughout feature development, and help avoid any wasted or duplicate effort.
@@ -99,7 +99,7 @@ In this release, we focused on new features, stability, and automation.
**✨Highlights**
- We focused on greatly improving Command Palette's performance and fixing a large amount of bugs. Some new features we've added are:
- We focused on greatly improving the Command Palette's performance and fixing a large number of bugs. Some new features we've added are:
- Added the ability for Command Palette to search any file using a fallback command.
- Added the ability to make the Command Palette global hotkey a low-level keyboard hook.
- Added open URL fallback command for the WebSearch extension, enabling users to directly open URLs in the browser from Command Palette.
@@ -117,17 +117,17 @@ In this release, we focused on new features, stability, and automation.
### Command Not Found
- Updated the WinGet Command Not Found script to only enable the experimental features if they actually exist.
- Updated the WinGet Command Not Found script to only enable the experimental features if they exist.
### Command Palette
- Updated bug template to include Command Palette module.
- Fixed an issue where the toast window was not scaled for DPI, causing layout issues under display scaling.
- Fixed an issue where Up/Down keyboard navigation didn't move selection when caret was at position 0, and add continuous navigation like PT Run v1. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- Fixed an issue where Up/Down keyboard navigation didn't move selection when the caret was at position 0, and added continuous navigation like PT Run v1. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- Updated the Time and Date extension code to simplify it and improve clarity.
- Fixed an issue where capitalization in the command causes failure when trying to go to the mouse pointer, resolved by adjusting the command to lowercase.
- Added open URL fallback command for the WebSearch extension, enabling users to directly open URLs in the browser from Command Palette. Thanks [@htcfreek](https://github.com/htcfreek)!
- Added setting to enable/disable system tray icon in CmdPal and align terminology with Windows 11. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- Added open URL fallback command for the WebSearch extension, enabling users to directly open URLs in the browser from the Command Palette. Thanks [@htcfreek](https://github.com/htcfreek)!
- Added setting to enable/disable system tray icon in CmdPal and aligned terminology with Windows 11. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- Fixed an alias update issue by removing the old alias when a new one is set.
- Resolved GitHub casing conflict by migrating Exts and exts into a new ext directory, ensuring consistent structure across platforms and preventing path fragmentation.
- Fix an issue where the 'Create New Extension' command generated empty file names.
@@ -155,9 +155,9 @@ In this release, we focused on new features, stability, and automation.
- Refactored CmdPal classes to improve JSON serialization and introduced new serialization contexts for better performance and maintainability.
- Added support for ahead-of-time (AoT) compilation.
- Added retry mechanism for CmdPal launch.
- Removed some unused files from CmdPal.Common to simplify codebase and facilitate marking it as AoT-compatible.
- Removed some unused files from CmdPal.Common to simplify the codebase and facilitate marking it as AoT-compatible.
- Fixed a bug where a race condition in the update of SearchText caused the cursor in the input box to automatically jump to the end of the line, ensuring SearchText is only updated after it has actually been changed.
- Added support for searching any file in fallback command.
- Added support for searching any file in the fallback command.
- Cleaned up AoT-related code to prevent duplicate operations during testing.
- Reduced CmdPal load time by parallelizing extension startup and adding timeouts to prevent misbehaving extensions from blocking others.
- Enhanced UI behavior by dismissing the details pane when the list gets emptied, avoiding inconsistent visual states.
@@ -179,7 +179,7 @@ In this release, we focused on new features, stability, and automation.
### Keyboard Manager
- Fixed an issue where a modifier key, when set without specifying left or right, would get stuck due to incorrect key handling, by tracking the pressed keys and sending the correct key accordingly. Thanks [@mantaionut](https://github.com/mantaionut)!
- Fixed an issue where a modifier key, when set without specifying left or right, would get stuck due to incorrect key handling by tracking the pressed keys and sending the correct key accordingly. Thanks [@mantaionut](https://github.com/mantaionut)!
### PowerRename
@@ -187,7 +187,7 @@ In this release, we focused on new features, stability, and automation.
### PowerToys Run
- Added support for custom formats in the "Time and Date" plugin and improves error messages for invalid input formats. Thanks [@htcfreek](https://github.com/htcfreek)!
- Added support for custom formats in the "Time and Date" plugin and improved error messages for invalid input formats. Thanks [@htcfreek](https://github.com/htcfreek)!
- Fix two crashes: one for WFT on very early dates and another for calculating the week of the month on very late dates (e.g., 31.12.9999), and reorder UI settings. Thanks [@htcfreek](https://github.com/htcfreek)!
- Fix an issue where capitalization in the command causes failure when trying to go to the mouse pointer, resolved by adjusting the command to lowercase.
- Added version details to plugin error messages for 'Loading error' and 'Init error'. Thanks [@htcfreek](https://github.com/htcfreek)!
@@ -254,7 +254,7 @@ For [v0.92][github-next-release-work], we'll work on the items below:
- New UI Automation tests
- Working on installer upgrades
- Upgrading Keyboard Manager's editor UI
- Stability / bug fixes
- Stability, bug fixes
## PowerToys Community
@@ -266,7 +266,7 @@ This project has adopted the [Microsoft Open Source Code of Conduct][oss-conduct
## Privacy Statement
The application logs basic diagnostic data (telemetry). For more information on privacy and what we collect, see our [PowerToys Data and Privacy documentation](https://aka.ms/powertoys-data-and-privacy-documentation).
The application logs basic diagnostic data (telemetry). For more privacy information and what we collect, see our [PowerToys Data and Privacy documentation](https://aka.ms/powertoys-data-and-privacy-documentation).
[oss-CLA]: https://cla.opensource.microsoft.com
[oss-conduct-code]: CODE_OF_CONDUCT.md

2
deps/cziplib vendored

View File

@@ -46,6 +46,7 @@ Contact the developers of a plugin directly for assistance with a specific plugi
| [QuickNotes](https://github.com/ruslanlap/CommunityPowerToysRunPlugin-QuickNotes) | [ruslanlap](https://github.com/ruslanlap) | Create, manage, and search notes directly from PowerToys Run. |
| [Weather](https://github.com/ruslanlap/PowerToysRun-Weather) | [ruslanlap](https://github.com/ruslanlap) | Get real-time weather information directly from PowerToys Run. |
| [Pomodoro](https://github.com/ruslanlap/PowerToysRun-Pomodoro) | [ruslanlap](https://github.com/ruslanlap) | Manage Pomodoro productivity sessions directly from PowerToys Run. |
| [Definition](https://github.com/ruslanlap/PowerToysRun-Definition) | [ruslanlap](https://github.com/ruslanlap) | Lookup word definitions, phonetics, and synonyms directly in PowerToys Run. |
## Extending software plugins
@@ -68,4 +69,5 @@ Below are community created plugins that target a website or software. They are
| [Bilibili](https://github.com/Whuihuan/PowerToysRun-Bilibili) | [Whuihuan](https://github.com/Whuihuan) | Use AVID or BVID to parse and jump to Bilibili |
| [YubicoOauthOTP](https://github.com/dlnilsson/Community.PowerToys.Run.Plugin.YubicoOauthOTP) | [dlnilsson](https://github.com/dlnilsson) | Display generated codes from OATH accounts stored on the YubiKey in powerToys Run |
| [Firefox Bookmark](https://github.com/8LWXpg/PowerToysRun-FirefoxBookmark) | [8LWXpg](https://github.com/8LWXpg) | Open bookmarks in Firefox based browser |
[Linear](https://github.com/vednig/powertoys-linear) | [vednig](https://github.com/vednig) | Create Linear Issues directly from Powertoys Run |
| [Linear](https://github.com/vednig/powertoys-linear) | [vednig](https://github.com/vednig) | Create Linear Issues directly from Powertoys Run |
| [SpeedTest](https://github.com/ruslanlap/PowerToysRun-SpeedTest) | [ruslanlap](https://github.com/ruslanlap) | One-command internet speed tests with real-time results, modern UI, and shareable links. |

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

@@ -22,7 +22,7 @@
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<DebugSymbols>true</DebugSymbols>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<DebugType>portable</DebugType>
<Optimize>false</Optimize>
</PropertyGroup>
@@ -43,4 +43,4 @@
<Analyzer Remove="@(Analyzer)" Condition="%(Analyzer.NuGetPackageId) == 'Microsoft.Windows.CsWinRT'" />
</ItemGroup>
</Target>
</Project>
</Project>

View File

@@ -1,18 +0,0 @@
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ImportGroup Label="ExtensionTargetsAppSdkCppProp">
<Import Project="$(MsbuildThisFileDirectory)\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('$(MsbuildThisFileDirectory)\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
<Import Project="$(MsbuildThisFileDirectory)\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.targets" Condition="Exists('$(MsbuildThisFileDirectory)\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.targets')" />
<Import Project="$(MsbuildThisFileDirectory)\..\packages\Microsoft.WindowsAppSDK.1.7.250401001\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('$(MsbuildThisFileDirectory)\..\packages\Microsoft.WindowsAppSDK.1.7.250401001\build\native\Microsoft.WindowsAppSDK.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImportsWinAppSdkCppProp" 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('$(MsbuildThisFileDirectory)\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '$(MsbuildThisFileDirectory)\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
<Error Condition="!Exists('$(MsbuildThisFileDirectory)\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(MsbuildThisFileDirectory)\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
<Error Condition="!Exists('$(MsbuildThisFileDirectory)\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.props')" Text="$([System.String]::Format('$(ErrorText)', '$(MsbuildThisFileDirectory)\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.props'))" />
<Error Condition="!Exists('$(MsbuildThisFileDirectory)\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(MsbuildThisFileDirectory)\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.targets'))" />
<Error Condition="!Exists('$(MsbuildThisFileDirectory)\..\packages\Microsoft.WindowsAppSDK.1.7.250401001\build\native\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '$(MsbuildThisFileDirectory)\..\packages\Microsoft.WindowsAppSDK.1.7.250401001\build\native\Microsoft.WindowsAppSDK.props'))" />
<Error Condition="!Exists('$(MsbuildThisFileDirectory)\..\packages\Microsoft.WindowsAppSDK.1.7.250401001\build\native\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(MsbuildThisFileDirectory)\..\packages\Microsoft.WindowsAppSDK.1.7.250401001\build\native\Microsoft.WindowsAppSDK.targets'))" />
</Target>
</Project>

View File

@@ -1,5 +0,0 @@
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MsbuildThisFileDirectory)\..\packages\Microsoft.WindowsAppSDK.1.7.250401001\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('$(MsbuildThisFileDirectory)\..\packages\Microsoft.WindowsAppSDK.1.7.250401001\build\native\Microsoft.WindowsAppSDK.props')" />
<Import Project="$(MsbuildThisFileDirectory)\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.props" Condition="Exists('$(MsbuildThisFileDirectory)\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.props')" />
<Import Project="$(MsbuildThisFileDirectory)\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('$(MsbuildThisFileDirectory)\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
</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
@@ -40,25 +41,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

@@ -20,7 +20,7 @@
"AreaPath": "OS\\Windows Client and Services\\WinPD\\DFX-Developer Fundamentals and Experiences\\DEFT\\SALT",
"IterationPath": "OS\\Future"
},
"jobNotificationEmail": "leilzh@microsoft.com",
"jobNotificationEmail": "PowerToys@microsoft.com",
"skip": false,
"rebootAfterSetup": false,
"oneFuzzJobs": [

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

@@ -143,7 +143,7 @@
"AreaPath": "OS\\Windows Client and Services\\WinPD\\DFX-Developer Fundamentals and Experiences\\DEFT\\SALT",
"IterationPath": "OS\\Future"
},
"jobNotificationEmail": "mengyuanchen@microsoft.com",
"jobNotificationEmail": "PowerToys@microsoft.com",
"skip": false,
"rebootAfterSetup": false,
"oneFuzzJobs": [

View File

@@ -1,6 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\..\WinAppSdkCppPre.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">
<CppWinRTOptimized>true</CppWinRTOptimized>
<CppWinRTRootNamespaceAutoMerge>true</CppWinRTRootNamespaceAutoMerge>
@@ -134,16 +136,24 @@
<ResourceCompile Include="PowerToys.MeasureToolCore.rc" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Import Project="..\..\..\WinAppSdkCppPost.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')" />
<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.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>
<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'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
<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.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
{

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

@@ -51,7 +51,7 @@
also set the 'EnableWindowsFormsHighDpiAutoResizing' setting to 'true' in their app.config. -->
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">System</dpiAwareness>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
</windowsSettings>
</application>

View File

@@ -0,0 +1,239 @@
#include "pch.h"
#include <filesystem> // Add this line
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace WorkspacesLibUnitTests
{
TEST_CLASS(AppUtilsTests)
{
public:
TEST_METHOD(GetCurrentFolder_ReturnsNonEmptyPath)
{
// Act
const std::wstring& result = Utils::Apps::GetCurrentFolder();
// Assert
Assert::IsFalse(result.empty());
Assert::IsTrue(std::filesystem::exists(result));
}
TEST_METHOD(GetCurrentFolderUpper_ReturnsUppercasePath)
{
// Act
const std::wstring& currentFolder = Utils::Apps::GetCurrentFolder();
const std::wstring& currentFolderUpper = Utils::Apps::GetCurrentFolderUpper();
// Assert
Assert::IsFalse(currentFolderUpper.empty());
Assert::AreEqual(currentFolder.length(), currentFolderUpper.length());
// Verify it's actually uppercase
std::wstring expectedUpper = currentFolder;
std::transform(expectedUpper.begin(), expectedUpper.end(), expectedUpper.begin(), towupper);
Assert::AreEqual(expectedUpper, currentFolderUpper);
}
TEST_METHOD(GetCurrentFolder_ConsistentResults)
{
// Act
const std::wstring& result1 = Utils::Apps::GetCurrentFolder();
const std::wstring& result2 = Utils::Apps::GetCurrentFolder();
// Assert
Assert::AreEqual(result1, result2);
}
TEST_METHOD(GetCurrentFolderUpper_ConsistentResults)
{
// Act
const std::wstring& result1 = Utils::Apps::GetCurrentFolderUpper();
const std::wstring& result2 = Utils::Apps::GetCurrentFolderUpper();
// Assert
Assert::AreEqual(result1, result2);
}
TEST_METHOD(AppData_IsEdge_EdgePath_ReturnsTrue)
{
// Arrange
Utils::Apps::AppData appData;
appData.installPath = L"C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe";
// Act
bool result = appData.IsEdge();
// Assert
Assert::IsTrue(result);
}
TEST_METHOD(AppData_IsEdge_NonEdgePath_ReturnsFalse)
{
// Arrange
Utils::Apps::AppData appData;
appData.installPath = L"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe";
// Act
bool result = appData.IsEdge();
// Assert
Assert::IsFalse(result);
}
TEST_METHOD(AppData_IsEdge_EmptyPath_ReturnsFalse)
{
// Arrange
Utils::Apps::AppData appData;
appData.installPath = L"";
// Act
bool result = appData.IsEdge();
// Assert
Assert::IsFalse(result);
}
TEST_METHOD(AppData_IsChrome_ChromePath_ReturnsTrue)
{
// Arrange
Utils::Apps::AppData appData;
appData.installPath = L"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe";
// Act
bool result = appData.IsChrome();
// Assert
Assert::IsTrue(result);
}
TEST_METHOD(AppData_IsChrome_NonChromePath_ReturnsFalse)
{
// Arrange
Utils::Apps::AppData appData;
appData.installPath = L"C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe";
// Act
bool result = appData.IsChrome();
// Assert
Assert::IsFalse(result);
}
TEST_METHOD(AppData_IsChrome_EmptyPath_ReturnsFalse)
{
// Arrange
Utils::Apps::AppData appData;
appData.installPath = L"";
// Act
bool result = appData.IsChrome();
// Assert
Assert::IsFalse(result);
}
TEST_METHOD(AppData_IsSteamGame_SteamProtocol_ReturnsTrue)
{
// Arrange
Utils::Apps::AppData appData;
appData.protocolPath = L"steam://run/123456";
// Act
bool result = appData.IsSteamGame();
// Assert
Assert::IsTrue(result);
}
TEST_METHOD(AppData_IsSteamGame_NonSteamProtocol_ReturnsFalse)
{
// Arrange
Utils::Apps::AppData appData;
appData.protocolPath = L"https://example.com";
// Act
bool result = appData.IsSteamGame();
// Assert
Assert::IsFalse(result);
}
TEST_METHOD(AppData_IsSteamGame_EmptyProtocol_ReturnsFalse)
{
// Arrange
Utils::Apps::AppData appData;
appData.protocolPath = L"";
// Act
bool result = appData.IsSteamGame();
// Assert
Assert::IsFalse(result);
}
TEST_METHOD(AppData_IsSteamGame_PartialSteamString_ReturnsFalse)
{
// Arrange
Utils::Apps::AppData appData;
appData.protocolPath = L"http://run/123456";
// Act
bool result = appData.IsSteamGame();
// Assert
Assert::IsFalse(result);
}
TEST_METHOD(AppData_DefaultValues)
{
// Arrange & Act
Utils::Apps::AppData appData;
// Assert
Assert::IsTrue(appData.name.empty());
Assert::IsTrue(appData.installPath.empty());
Assert::IsTrue(appData.packageFullName.empty());
Assert::IsTrue(appData.appUserModelId.empty());
Assert::IsTrue(appData.pwaAppId.empty());
Assert::IsTrue(appData.protocolPath.empty());
Assert::IsFalse(appData.canLaunchElevated);
}
TEST_METHOD(AppData_MultipleBrowserDetection)
{
// Arrange
Utils::Apps::AppData edgeApp;
edgeApp.installPath = L"C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe";
Utils::Apps::AppData chromeApp;
chromeApp.installPath = L"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe";
Utils::Apps::AppData otherApp;
otherApp.installPath = L"C:\\Program Files\\Firefox\\firefox.exe";
// Act & Assert
Assert::IsTrue(edgeApp.IsEdge());
Assert::IsFalse(edgeApp.IsChrome());
Assert::IsFalse(edgeApp.IsSteamGame());
Assert::IsFalse(chromeApp.IsEdge());
Assert::IsTrue(chromeApp.IsChrome());
Assert::IsFalse(chromeApp.IsSteamGame());
Assert::IsFalse(otherApp.IsEdge());
Assert::IsFalse(otherApp.IsChrome());
Assert::IsFalse(otherApp.IsSteamGame());
}
TEST_METHOD(GetAppsList_ReturnsAppList)
{
// Act
Utils::Apps::AppList apps = Utils::Apps::GetAppsList();
// Assert
// The list can be empty or non-empty depending on the system
// But it should not crash and should return a valid list
Assert::IsTrue(apps.size() >= 0);
}
};
}

View File

@@ -0,0 +1,186 @@
#include "pch.h"
#include <filesystem>
#include <fstream>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace WorkspacesLibUnitTests
{
TEST_CLASS (JsonUtilsTests)
{
private:
std::wstring CreateTempJsonFile(const std::wstring& content)
{
std::wstring tempPath = std::filesystem::temp_directory_path();
tempPath += L"\\test_workspace_" + std::to_wstring(GetTickCount64()) + L".json";
std::wofstream file(tempPath);
file << content;
file.close();
return tempPath;
}
void DeleteTempFile(const std::wstring& filePath)
{
if (std::filesystem::exists(filePath))
{
std::filesystem::remove(filePath);
}
}
public:
TEST_METHOD (ReadSingleWorkspace_NonExistentFile_ReturnsEmptyWorkspace)
{
// Arrange
std::wstring nonExistentFile = L"C:\\NonExistent\\File.json";
// Act
auto result = JsonUtils::ReadSingleWorkspace(nonExistentFile);
// Assert
Assert::IsTrue(result.isOk());
auto workspace = result.value();
Assert::IsTrue(workspace.name.empty());
}
TEST_METHOD (ReadSingleWorkspace_InvalidJsonFile_ReturnsError)
{
// Arrange
std::wstring tempFile = CreateTempJsonFile(L"invalid json content {");
// Act
auto result = JsonUtils::ReadSingleWorkspace(tempFile);
// Assert
Assert::IsTrue(result.isError());
Assert::AreEqual(static_cast<int>(JsonUtils::WorkspacesFileError::IncorrectFileError),
static_cast<int>(result.error()));
// Cleanup
DeleteTempFile(tempFile);
}
TEST_METHOD (ReadWorkspaces_NonExistentFile_ReturnsEmptyVector)
{
// Arrange
std::wstring nonExistentFile = L"C:\\NonExistent\\File.json";
// Act
auto result = JsonUtils::ReadWorkspaces(nonExistentFile);
// Assert
Assert::IsTrue(result.isError());
Assert::AreEqual(static_cast<int>(JsonUtils::WorkspacesFileError::IncorrectFileError),
static_cast<int>(result.error()));
}
TEST_METHOD (ReadWorkspaces_InvalidJsonFile_ReturnsError)
{
// Arrange
std::wstring tempFile = CreateTempJsonFile(L"invalid json content {");
// Act
auto result = JsonUtils::ReadWorkspaces(tempFile);
// Assert
Assert::IsTrue(result.isError());
Assert::AreEqual(static_cast<int>(JsonUtils::WorkspacesFileError::IncorrectFileError),
static_cast<int>(result.error()));
// Cleanup
DeleteTempFile(tempFile);
}
TEST_METHOD (Write_ValidWorkspace_ReturnsTrue)
{
// Arrange
std::wstring tempPath = std::filesystem::temp_directory_path();
tempPath += L"\\test_write_workspace_" + std::to_wstring(GetTickCount64()) + L".json";
WorkspacesData::WorkspacesProject workspace;
workspace.name = L"Test Workspace";
// Convert string to time_t
std::tm tm = {};
workspace.creationTime = std::mktime(&tm);
// Act
bool result = JsonUtils::Write(tempPath, workspace);
// Assert
Assert::IsTrue(result);
Assert::IsTrue(std::filesystem::exists(tempPath));
// Cleanup
DeleteTempFile(tempPath);
}
TEST_METHOD (Write_ValidWorkspacesList_ReturnsTrue)
{
// Arrange
std::wstring tempPath = std::filesystem::temp_directory_path();
tempPath += L"\\test_write_workspaces_" + std::to_wstring(GetTickCount64()) + L".json";
std::vector<WorkspacesData::WorkspacesProject> workspaces;
WorkspacesData::WorkspacesProject workspace1;
workspace1.name = L"Test Workspace 1";
workspace1.creationTime = std::time(nullptr);
WorkspacesData::WorkspacesProject workspace2;
workspace2.name = L"Test Workspace 2";
workspace2.creationTime = std::time(nullptr);
workspaces.push_back(workspace1);
workspaces.push_back(workspace2);
// Act
bool result = JsonUtils::Write(tempPath, workspaces);
// Assert
Assert::IsTrue(result);
Assert::IsTrue(std::filesystem::exists(tempPath));
// Cleanup
DeleteTempFile(tempPath);
}
TEST_METHOD (Write_EmptyWorkspacesList_ReturnsTrue)
{
// Arrange
std::wstring tempPath = std::filesystem::temp_directory_path();
tempPath += L"\\test_write_empty_" + std::to_wstring(GetTickCount64()) + L".json";
std::vector<WorkspacesData::WorkspacesProject> emptyWorkspaces;
// Act
bool result = JsonUtils::Write(tempPath, emptyWorkspaces);
// Assert
Assert::IsTrue(result);
Assert::IsTrue(std::filesystem::exists(tempPath));
// Cleanup
DeleteTempFile(tempPath);
}
/*
TEST_METHOD(Write_InvalidPath_ReturnsFalse)
{
// Arrange
std::wstring invalidPath = L"C:\\NonExistent\\Path\\workspace.json";
WorkspacesData::WorkspacesProject workspace;
workspace.name = L"Test Workspace";
workspace.creationTime = std::time(nullptr);
// Act
bool result = JsonUtils::Write(invalidPath, workspace);
// Assert
Assert::IsFalse(result);
}
*/
};
}

View File

@@ -0,0 +1,114 @@
#include "pch.h"
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace WorkspacesLibUnitTests
{
TEST_CLASS (PwaHelperTests)
{
public:
TEST_METHOD (PwaHelper_Constructor_DoesNotThrow)
{
// Act & Assert - Constructor should not crash when called
try
{
Utils::PwaHelper helper;
// If we get here, the constructor didn't throw
Assert::IsTrue(true);
}
catch (...)
{
Assert::Fail(L"PwaHelper constructor should not throw exceptions");
}
}
TEST_METHOD (PwaHelper_GetEdgeAppId_EmptyAumid_ReturnsEmpty)
{
// Arrange
Utils::PwaHelper helper;
std::wstring emptyAumid = L"";
// Act
auto result = helper.GetEdgeAppId(emptyAumid);
// Assert
Assert::IsFalse(result.has_value());
}
TEST_METHOD (PwaHelper_GetChromeAppId_EmptyAumid_ReturnsEmpty)
{
// Arrange
Utils::PwaHelper helper;
std::wstring emptyAumid = L"";
// Act
auto result = helper.GetChromeAppId(emptyAumid);
// Assert
Assert::IsFalse(result.has_value());
}
TEST_METHOD (PwaHelper_SearchPwaName_EmptyParameters_ReturnsEmpty)
{
// Arrange
Utils::PwaHelper helper;
std::wstring emptyPwaAppId = L"";
std::wstring emptyWindowAumid = L"";
// Act
std::wstring result = helper.SearchPwaName(emptyPwaAppId, emptyWindowAumid);
// Assert
Assert::IsTrue(result.empty());
}
TEST_METHOD (PwaHelper_SearchPwaName_NonExistentIds_ReturnsEmpty)
{
// Arrange
Utils::PwaHelper helper;
std::wstring nonExistentPwaAppId = L"nonexistent_app_id";
std::wstring nonExistentWindowAumid = L"nonexistent_aumid";
// Act
std::wstring result = helper.SearchPwaName(nonExistentPwaAppId, nonExistentWindowAumid);
// TODO: is it really expected?
Assert::IsTrue(result == nonExistentWindowAumid);
}
TEST_METHOD (PwaHelper_GetAUMIDFromWindow_InvalidWindow_ReturnsEmpty)
{
// Arrange
Utils::PwaHelper helper;
HWND invalidWindow = nullptr;
// Act
std::wstring result = helper.GetAUMIDFromWindow(invalidWindow);
// Assert
Assert::IsTrue(result.empty());
}
TEST_METHOD (PwaHelper_GetEdgeAppId_ValidConstruction_DoesNotCrash)
{
// Arrange
Utils::PwaHelper helper;
std::wstring testAumid = L"Microsoft.MicrosoftEdge_8wekyb3d8bbwe!App";
// Act & Assert - Should not crash
auto result = helper.GetEdgeAppId(testAumid);
// Result can be empty or have value, but should not crash
}
TEST_METHOD (PwaHelper_GetChromeAppId_ValidConstruction_DoesNotCrash)
{
// Arrange
Utils::PwaHelper helper;
std::wstring testAumid = L"Chrome.App.TestId";
// Act & Assert - Should not crash
auto result = helper.GetChromeAppId(testAumid);
// Result can be empty or have value, but should not crash
}
};
}

View File

@@ -0,0 +1,115 @@
#include "pch.h"
#include <StringUtils.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace WorkspacesLibUnitTests
{
TEST_CLASS(StringUtilsTests)
{
public:
TEST_METHOD(CaseInsensitiveEquals_SameStrings_ReturnsTrue)
{
// Arrange
std::wstring str1 = L"test";
std::wstring str2 = L"test";
// Act
bool result = StringUtils::CaseInsensitiveEquals(str1, str2);
// Assert
Assert::IsTrue(result);
}
TEST_METHOD(CaseInsensitiveEquals_DifferentCase_ReturnsTrue)
{
// Arrange
std::wstring str1 = L"Test";
std::wstring str2 = L"TEST";
// Act
bool result = StringUtils::CaseInsensitiveEquals(str1, str2);
// Assert
Assert::IsTrue(result);
}
TEST_METHOD(CaseInsensitiveEquals_MixedCase_ReturnsTrue)
{
// Arrange
std::wstring str1 = L"TeSt StRiNg";
std::wstring str2 = L"test STRING";
// Act
bool result = StringUtils::CaseInsensitiveEquals(str1, str2);
// Assert
Assert::IsTrue(result);
}
TEST_METHOD(CaseInsensitiveEquals_DifferentStrings_ReturnsFalse)
{
// Arrange
std::wstring str1 = L"test";
std::wstring str2 = L"different";
// Act
bool result = StringUtils::CaseInsensitiveEquals(str1, str2);
// Assert
Assert::IsFalse(result);
}
TEST_METHOD(CaseInsensitiveEquals_DifferentLengths_ReturnsFalse)
{
// Arrange
std::wstring str1 = L"test";
std::wstring str2 = L"testing";
// Act
bool result = StringUtils::CaseInsensitiveEquals(str1, str2);
// Assert
Assert::IsFalse(result);
}
TEST_METHOD(CaseInsensitiveEquals_EmptyStrings_ReturnsTrue)
{
// Arrange
std::wstring str1 = L"";
std::wstring str2 = L"";
// Act
bool result = StringUtils::CaseInsensitiveEquals(str1, str2);
// Assert
Assert::IsTrue(result);
}
TEST_METHOD(CaseInsensitiveEquals_OneEmpty_ReturnsFalse)
{
// Arrange
std::wstring str1 = L"test";
std::wstring str2 = L"";
// Act
bool result = StringUtils::CaseInsensitiveEquals(str1, str2);
// Assert
Assert::IsFalse(result);
}
TEST_METHOD(CaseInsensitiveEquals_SpecialCharacters_ReturnsTrue)
{
// Arrange
std::wstring str1 = L"Test-123_Special!";
std::wstring str2 = L"test-123_special!";
// Act
bool result = StringUtils::CaseInsensitiveEquals(str1, str2);
// Assert
Assert::IsTrue(result);
}
};
}

View File

@@ -0,0 +1,194 @@
#include "pch.h"
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace WorkspacesLibUnitTests
{
TEST_CLASS(WorkspacesDataTests)
{
public:
TEST_METHOD(WorkspacesFile_ReturnsValidPath)
{
// Act
std::wstring result = WorkspacesData::WorkspacesFile();
// Assert
Assert::IsFalse(result.empty());
Assert::IsTrue(result.find(L"workspaces.json") != std::wstring::npos);
}
TEST_METHOD(TempWorkspacesFile_ReturnsValidPath)
{
// Act
std::wstring result = WorkspacesData::TempWorkspacesFile();
// Assert
Assert::IsFalse(result.empty());
Assert::IsTrue(result.find(L"temp-workspaces.json") != std::wstring::npos);
}
TEST_METHOD(WorkspacesFile_TempWorkspacesFile_DifferentPaths)
{
// Act
std::wstring workspacesFile = WorkspacesData::WorkspacesFile();
std::wstring tempWorkspacesFile = WorkspacesData::TempWorkspacesFile();
// Assert
Assert::AreNotEqual(workspacesFile, tempWorkspacesFile);
}
TEST_METHOD(Position_ToRect_ConvertsCorrectly)
{
// Arrange
WorkspacesData::WorkspacesProject::Application::Position position;
position.x = 100;
position.y = 200;
position.width = 800;
position.height = 600;
// Act
RECT rect = position.toRect();
// Assert
Assert::AreEqual(100, static_cast<int>(rect.left));
Assert::AreEqual(200, static_cast<int>(rect.top));
Assert::AreEqual(900, static_cast<int>(rect.right)); // x + width
Assert::AreEqual(800, static_cast<int>(rect.bottom)); // y + height
}
TEST_METHOD(Position_ToRect_ZeroPosition)
{
// Arrange
WorkspacesData::WorkspacesProject::Application::Position position;
position.x = 0;
position.y = 0;
position.width = 0;
position.height = 0;
// Act
RECT rect = position.toRect();
// Assert
Assert::AreEqual(0, static_cast<int>(rect.left));
Assert::AreEqual(0, static_cast<int>(rect.top));
Assert::AreEqual(0, static_cast<int>(rect.right));
Assert::AreEqual(0, static_cast<int>(rect.bottom));
}
TEST_METHOD(Position_ToRect_NegativeCoordinates)
{
// Arrange
WorkspacesData::WorkspacesProject::Application::Position position;
position.x = -100;
position.y = -50;
position.width = 200;
position.height = 150;
// Act
RECT rect = position.toRect();
// Assert
Assert::AreEqual(-100, static_cast<int>(rect.left));
Assert::AreEqual(-50, static_cast<int>(rect.top));
Assert::AreEqual(100, static_cast<int>(rect.right)); // -100 + 200
Assert::AreEqual(100, static_cast<int>(rect.bottom)); // -50 + 150
}
TEST_METHOD(Application_DefaultValues)
{
// Arrange & Act
WorkspacesData::WorkspacesProject::Application app;
// Assert
Assert::IsTrue(app.id.empty());
Assert::IsTrue(app.name.empty());
Assert::IsTrue(app.title.empty());
Assert::IsTrue(app.path.empty());
Assert::IsTrue(app.packageFullName.empty());
Assert::IsTrue(app.appUserModelId.empty());
Assert::IsTrue(app.pwaAppId.empty());
Assert::IsTrue(app.commandLineArgs.empty());
Assert::IsFalse(app.isElevated);
Assert::IsFalse(app.canLaunchElevated);
Assert::IsFalse(app.isMinimized);
Assert::IsFalse(app.isMaximized);
Assert::AreEqual(0, static_cast<int>(app.position.x));
Assert::AreEqual(0, static_cast<int>(app.position.y));
Assert::AreEqual(0, static_cast<int>(app.position.width));
Assert::AreEqual(0, static_cast<int>(app.position.height));
Assert::AreEqual(0u, static_cast<unsigned int>(app.monitor));
}
TEST_METHOD(Application_Comparison_EqualObjects)
{
// Arrange
WorkspacesData::WorkspacesProject::Application app1;
app1.id = L"test-id";
app1.name = L"Test App";
app1.position.x = 100;
app1.position.y = 200;
WorkspacesData::WorkspacesProject::Application app2;
app2.id = L"test-id";
app2.name = L"Test App";
app2.position.x = 100;
app2.position.y = 200;
// Act & Assert
Assert::IsTrue(app1 == app2);
}
TEST_METHOD(Application_Comparison_DifferentObjects)
{
// Arrange
WorkspacesData::WorkspacesProject::Application app1;
app1.id = L"test-id-1";
app1.name = L"Test App 1";
WorkspacesData::WorkspacesProject::Application app2;
app2.id = L"test-id-2";
app2.name = L"Test App 2";
// Act & Assert
Assert::IsTrue(app1 != app2);
}
TEST_METHOD(Position_Comparison_EqualPositions)
{
// Arrange
WorkspacesData::WorkspacesProject::Application::Position pos1;
pos1.x = 100;
pos1.y = 200;
pos1.width = 800;
pos1.height = 600;
WorkspacesData::WorkspacesProject::Application::Position pos2;
pos2.x = 100;
pos2.y = 200;
pos2.width = 800;
pos2.height = 600;
// Act & Assert
Assert::IsTrue(pos1 == pos2);
}
TEST_METHOD(Position_Comparison_DifferentPositions)
{
// Arrange
WorkspacesData::WorkspacesProject::Application::Position pos1;
pos1.x = 100;
pos1.y = 200;
pos1.width = 800;
pos1.height = 600;
WorkspacesData::WorkspacesProject::Application::Position pos2;
pos2.x = 150;
pos2.y = 200;
pos2.width = 800;
pos2.height = 600;
// Act & Assert
Assert::IsTrue(pos1 != pos2);
}
};
}

View File

@@ -0,0 +1,76 @@
<?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">
<ProjectGuid>{A85D4D9F-9A39-4B5D-8B5A-9F2D5C9A8B4C}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>WorkspacesLibUnitTests</RootNamespace>
<ProjectName>WorkspacesLibUnitTests</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<PropertyGroup>
<ConfigurationType>DynamicLibrary</ConfigurationType>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<OutDir>..\..\..\..\$(Platform)\$(Configuration)\tests\Workspaces\</OutDir>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>..\;..\WorkspacesLib\;$(SolutionDir)src\;$(SolutionDir)src\common;$(SolutionDir)src\common\Telemetry;..\..\;..\..\..\;$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<UseFullPaths>true</UseFullPaths>
</ClCompile>
<Link>
<AdditionalLibraryDirectories>$(VCInstallDir)UnitTest\\lib;$(SolutionDir)$(Platform)\\$(Configuration)\\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>propsys.lib;comctl32.lib;pathcch.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;Pathcch.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
<ClInclude Include="targetver.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="WorkspacesDataTests.cpp" />
<ClCompile Include="StringUtilsTests.cpp" />
<ClCompile Include="JsonUtilsTests.cpp" />
<ClCompile Include="AppUtilsTests.cpp" />
<ClCompile Include="PwaHelperTests.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
</ProjectReference>
<ProjectReference Include="..\WorkspacesLib\WorkspacesLib.vcxproj">
<Project>{b31fcc55-b5a4-4ea7-b414-2dceae6af332}</Project>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<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>
</Project>

View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="targetver.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="WorkspacesDataTests.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="StringUtilsTests.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="JsonUtilsTests.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="AppUtilsTests.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="PwaHelperTests.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
</Project>

View File

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

View File

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

View File

@@ -0,0 +1,21 @@
#pragma once
#include "targetver.h"
// Headers for CppUnitTest
#pragma warning(disable : 26466)
#include "CppUnitTest.h"
// Windows headers
#include <windows.h>
#include <string>
#include <memory>
#include <vector>
#include <optional>
// Workspaces headers
#include <WorkspacesLib/WorkspacesData.h>
#include <WorkspacesLib/JsonUtils.h>
#include <WorkspacesLib/Result.h>
#include <WorkspacesLib/AppUtils.h>
#include <WorkspacesLib/PwaHelper.h>

View File

@@ -0,0 +1,8 @@
#pragma once
// Including SDKDDKVer.h defines the highest available Windows platform.
// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
#include <SDKDDKVer.h>

View File

@@ -6,7 +6,7 @@
namespace StringUtils
{
bool CaseInsensitiveEquals(const std::wstring& str1, const std::wstring& str2)
inline bool CaseInsensitiveEquals(const std::wstring& str1, const std::wstring& str2)
{
if (str1.size() != str2.size())
{

View File

@@ -69,10 +69,10 @@ namespace WorkspacesData
std::wstring id;
std::wstring name;
time_t creationTime;
time_t creationTime{};
std::optional<time_t> lastLaunchedTime;
bool isShortcutNeeded;
bool moveExistingWindows;
bool isShortcutNeeded{};
bool moveExistingWindows{};
std::vector<Monitor> monitors;
std::vector<Application> apps;
};

View File

@@ -1,4 +1,4 @@
<Project>
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
@@ -9,9 +9,9 @@
<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" />
<PackageVersion Include="System.Text.Json" Version="9.0.6" />
</ItemGroup>
</Project>

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

@@ -79,6 +79,9 @@ public partial class ContentPageViewModel : PageViewModel, ICommandBarContext
throw;
}
var oneContent = newContent.Count == 1;
newContent.ForEach(c => c.OnlyControlOnPage = oneContent);
// Now, back to a UI thread to update the observable collection
DoOnUiThread(
() =>

View File

@@ -7,4 +7,5 @@ namespace Microsoft.CmdPal.UI.ViewModels;
public abstract partial class ContentViewModel(WeakReference<IPageContext> context) :
ExtensionObjectViewModel(context)
{
public bool OnlyControlOnPage { get; internal set; }
}

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

@@ -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;
@@ -100,8 +102,7 @@ public partial class App : Application
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>();
@@ -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

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

@@ -5,7 +5,9 @@
using AdaptiveCards.ObjectModel.WinUI3;
using AdaptiveCards.Rendering.WinUI3;
using Microsoft.CmdPal.UI.ViewModels;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
namespace Microsoft.CmdPal.UI.Controls;
@@ -16,7 +18,7 @@ public sealed partial class ContentFormControl : UserControl
// 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
// tree. If this gets GC'ed, then it'll revoke our Action handler, and the
// form will do seemingly nothing.
private RenderedAdaptiveCard? _renderedCard;
@@ -96,11 +98,65 @@ public sealed partial class ContentFormControl : UserControl
if (_renderedCard.FrameworkElement != null)
{
ContentGrid.Children.Add(_renderedCard.FrameworkElement);
// Use the Loaded event to ensure we focus after the card is in the visual tree
_renderedCard.FrameworkElement.Loaded += OnFrameworkElementLoaded;
}
_renderedCard.Action += Rendered_Action;
}
private void OnFrameworkElementLoaded(object sender, RoutedEventArgs e)
{
// Unhook the event handler to avoid multiple registrations
if (sender is FrameworkElement element)
{
element.Loaded -= OnFrameworkElementLoaded;
if (!ViewModel?.OnlyControlOnPage ?? true)
{
return;
}
// Focus on the first focusable element asynchronously to ensure the visual tree is fully built
element.DispatcherQueue.TryEnqueue(Microsoft.UI.Dispatching.DispatcherQueuePriority.Normal, () =>
{
var focusableElement = FindFirstFocusableElement(element);
focusableElement?.Focus(FocusState.Programmatic);
});
}
}
private Control? FindFirstFocusableElement(DependencyObject parent)
{
var childCount = VisualTreeHelper.GetChildrenCount(parent);
// Process children first (depth-first search)
for (var i = 0; i < childCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
// If the child is a focusable control like TextBox, ComboBox, etc.
if (child is Control control &&
control.IsEnabled &&
control.IsTabStop &&
control.Visibility == Visibility.Visible &&
control.AllowFocusOnInteraction)
{
return control;
}
// Recursively check children
var result = FindFirstFocusableElement(child);
if (result != null)
{
return result;
}
}
return null;
}
private void Rendered_Action(RenderedAdaptiveCard sender, AdaptiveActionEventArgs args) =>
ViewModel?.HandleSubmit(args.Action, args.Inputs.AsJson());
}

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

@@ -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(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);
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 = System.Reflection.Assembly.GetExecutingAssembly().Location;
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()
@@ -515,18 +496,27 @@ public sealed partial class MainWindow : WindowEx,
if (key != null)
{
var vk = key.Code;
var modifiers =
(key.Alt ? HOT_KEY_MODIFIERS.MOD_ALT : 0) |
(key.Ctrl ? HOT_KEY_MODIFIERS.MOD_CONTROL : 0) |
(key.Shift ? HOT_KEY_MODIFIERS.MOD_SHIFT : 0) |
(key.Win ? HOT_KEY_MODIFIERS.MOD_WIN : 0)
;
var success = PInvoke.RegisterHotKey(_hwnd, _hotkeys.Count, modifiers, (uint)vk);
if (success)
if (settings.UseLowLevelGlobalHotkey)
{
_hotkeys.Add(commandHotkey);
_keyboardListener.SetHotkeyAction(key.Win, key.Ctrl, key.Shift, key.Alt, (byte)key.Code, commandHotkey.CommandId);
_hotkeys.Add(new(globalHotkey, string.Empty));
}
else
{
var vk = key.Code;
var modifiers =
(key.Alt ? HOT_KEY_MODIFIERS.MOD_ALT : 0) |
(key.Ctrl ? HOT_KEY_MODIFIERS.MOD_CONTROL : 0) |
(key.Shift ? HOT_KEY_MODIFIERS.MOD_SHIFT : 0) |
(key.Win ? HOT_KEY_MODIFIERS.MOD_WIN : 0)
;
var success = PInvoke.RegisterHotKey(_hwnd, _hotkeys.Count, modifiers, (uint)vk);
if (success)
{
_hotkeys.Add(commandHotkey);
}
}
}
}
@@ -606,100 +596,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

@@ -39,9 +39,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. -->

View File

@@ -35,9 +35,14 @@ WM_WINDOWPOSCHANGING
RegisterWindowMessageW
GetModuleHandleW
ExtractIconEx
TRACK_POPUP_MENU_FLAGS
WM_COMMAND
WM_RBUTTONUP
WM_LBUTTONUP
WM_LBUTTONDBLCLK
CreatePopupMenu
TrackPopupMenuEx
InsertMenu
MessageBox
DwmGetWindowAttribute

View File

@@ -29,6 +29,7 @@
<cmdpalUI:DetailsDataTemplateSelector
x:Key="DetailsDataTemplateSelector"
CommandTemplate="{StaticResource DetailsCommandsTemplate}"
LinkTemplate="{StaticResource DetailsLinkTemplate}"
SeparatorTemplate="{StaticResource DetailsSeparatorTemplate}"
TagTemplate="{StaticResource DetailsTagsTemplate}" />
@@ -50,6 +51,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 +93,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

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

@@ -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>.
@@ -39,8 +39,8 @@ Projects of interest are:
[Initial SDK Spec]: ./doc/initial-sdk-spec/initial-sdk-spec.md
[generic samples]: ./Exts/SamplePagesExtension
[real samples]: ./Exts/ProcessMonitorExtension
[generic samples]: ./ext/SamplePagesExtension
[real samples]: ./ext/ProcessMonitorExtension
[real extensions that we've "shipped" already]: https://github.com/zadjii/CmdPalExtensions/blob/main/src/extensions

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

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