Compare commits

..

16 Commits

Author SHA1 Message Date
Leilei Zhang
b94bf4fdae update value 2025-12-05 22:07:22 +08:00
Leilei Zhang
2d18303f0a Merge branch 'main' of https://github.com/microsoft/PowerToys into leilzh/fixupdate 2025-12-05 21:44:06 +08:00
Niels Laute
cdf66a70e9 [AdvancedPaste] Fix outdated string in Settings (#44099)
Updating and removing outdated strings.

Updated string:

<img width="1565" height="323" alt="image"
src="https://github.com/user-attachments/assets/3dcad3b9-7ba9-4d87-ab36-405a8e1705db"
/>
2025-12-05 21:33:59 +08:00
Valentin Arthur Thomas
9dcddfd4b8 Quotation mark (#30481)
<!-- 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)?
-->
## Add Quotation mark
Add local quotation based on ~~VK_OEM_7(0xDE)~~ VK_OEM_COMMA(0xBC) key.
Not all quotes have been added, only `‟ „ ” « » ‚ , ‘ ’ › ‹ '「 」 《 》 『
』〈 〉″ ‴ ⁗`
Why not added :
- ` ⹂ ⌜ ⌝ ❛ ❜ ❝ ❞ 🙶 🙷 🙸 ' 「 」 ` its redundant and would make too much
and not readable.
- ` ﹁ ﹂ ﹃ ﹄ `  I did not put them because there use for horizontal text


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

- [x] Closes: https://github.com/microsoft/PowerToys/issues/29371
https://github.com/microsoft/PowerToys/issues/24832
- [ ] **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 (None)
- [ ] [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 -->
This PR is currently a draft, I still need to know if adding
language-related keyboard management is a good idea or specifying the
use of a gobal key to make it easier to manage all the keyboards in one.
Some languages ​​can use different keyboards, I think this would become
problematic if the keyboard does not match the key used by default.
However, using a universal key can also pose an issue to finding the
key. that remains to be discussed
<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2025-12-05 11:57:01 +01:00
Copilot
503bcbdf2d Restore missing "Quick access" menu item in tray icon context menu (#42676)
## Summary
Restores the "Quick access" menu item that was accidentally removed from
the PowerToys tray icon context menu.

## Issue
Fixes #[issue_number]

The "Quick access" menu item was missing from the tray icon's
right-click context menu, preventing users from accessing this feature
via the tray menu.

**Expected menu:**

![Expected
menu](https://github.com/user-attachments/assets/805b1436-5a08-42e7-a34d-b9848fd9a235)

**Actual menu (before this fix):**

![Actual
menu](https://github.com/user-attachments/assets/7584035d-e893-4f73-acc3-84d789e31e81)

## Changes
- Added the missing `MENUITEM "Quick access\tLeft-click",
ID_QUICK_ACCESS_MENU_COMMAND` entry as the first menu item in the
`ID_TRAY_MENU` definition in `src/runner/runner.base.rc`

## Details
The menu item was accidentally removed in commit
f5797a065a. This PR restores it to its
correct position as the first item in the tray menu.

All supporting code was already in place:
- The resource ID `ID_QUICK_ACCESS_MENU_COMMAND` (40006) is defined in
`resource.base.h`
- The resource string `QUICK_ACCESS_MENU_TEXT` is defined in
`Resources.resx`
- The menu command handler in `tray_icon.cpp` opens the Quick Access
flyout window
- The localization code updates the menu text at runtime

## Testing
-  Verified the menu item syntax is correct and matches existing
patterns
-  Confirmed all supporting resource IDs and handler code exist
-  CI build verification pending

After this fix, the tray menu will correctly display:
1. Quick access (Left-click)
2. Settings (Double-click)
3. Documentation
4. Report Bug
5. Close

<!-- START COPILOT CODING AGENT SUFFIX -->



<details>

<summary>Original prompt</summary>

> 
> ----
> 
> *This section details on the original issue you should resolve*
> 
> <issue_title>Quick Access missing in tray menu</issue_title>
> <issue_description>### Microsoft PowerToys version
> 
> 0.95.0
> 
> ### Installation method
> 
> PowerToys auto-update
> 
> ### Area(s) with issue?
> 
> System tray interaction
> 
> ### Steps to reproduce
> 
> 1. Right click the tray icon
> 2. Look at the list of items which can be selected
> 3. Notice that the "Quick access" is missing
> 
> Expected menu ("Exit" is now "Close" I took this image from initial
implementation):
> 
> <img width="234" height="127" alt="Image"
src="https://github.com/user-attachments/assets/805b1436-5a08-42e7-a34d-b9848fd9a235"
/>
> 
> Actual menu right now:
> 
> <img width="239" height="105" alt="Image"
src="https://github.com/user-attachments/assets/7584035d-e893-4f73-acc3-84d789e31e81"
/>
> 
> ### ✔️ Expected Behavior
> 
> The "Quick access" menu item should be there
> 
> ###  Actual Behavior
> 
> The menu item is missing
> 
> ### Additional Information
> 
> Windows 10 Pro 22H2 19045.6332
> 
> (Also it is the same on my other computers)
> 
> ### Other Software
> 
> _No response_</issue_description>
> 
> <agent_instructions>restore
> 
> MENUITEM "Quick access\tLeft-click", ID_QUICK_ACCESS_MENU_COMMAND
> 
> as first menu item of ID_TRAY_MENU MENU in
src/runner/runner.base.rc</agent_instructions>
> 
> ## Comments on the Issue (you are @copilot in this section)
> 
> <comments>
> <comment_new><author>@niels9001</author><body>
> @davidegiacometti is this a regression from the PR where we updated
the string names?</body></comment_new>
> <comment_new><author>@davidegiacometti</author><body>
> Hi @niels9001 
> I just realized that the menu was missing in
https://github.com/microsoft/PowerToys/pull/40714 screenshots, but the
regression was caused by
f5797a065a.
> Unfortunately, many of the `.rc` files in the PT codebase have
UTF16-LE encoding and GitHub doesn't show diff.</body></comment_new>
> </comments>
> 


</details>

Fixes microsoft/PowerToys#42618

<!-- START COPILOT CODING AGENT TIPS -->
---

💬 Share your feedback on Copilot coding agent for the chance to win a
$200 gift card! Click
[here](https://survey3.medallia.com/?EAHeSx-AP01bZqG0Ld9QLQ) to start
the survey.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: davidegiacometti <25966642+davidegiacometti@users.noreply.github.com>
2025-12-05 07:33:37 +01:00
Jiří Polášek
52f2561937 CmdPal: Find app for WinGet package (#43943)
<!-- 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 a bit of dark magic to resolve the correct installed
app for a given WinGet package:

- Packaged apps: matched using their package family name.
- Everything else: matched using the product code (GUID) and heuristic
registry lookup.
- The registry rarely stores the executable path directly, so the logic
compares install locations with known apps.
  - It attempts to pick the best candidate while avoiding uninstallers.
  - It’s not science — let’s call it `#666666` magic.
- MSI API support was removed because it's too slow for this scenario.
- If no reliable match is found, the command is skipped for now. The
future plan is to redirect the user to the list of installed apps and
search by display name, but that needs some supporting infrastructure
first.
- The command order for WinGet list entries was updated: **Install /
Uninstall** is now the primary action, ensuring a stable UI since this
command is always available.


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

- [x] Closes: #43671
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **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-12-03 10:16:25 -06:00
Dustin L. Howett
dc30f3fd8e build: move main and setup to SLNX (#43478)
Closes #37100

This does not migrate the rest of the solutions (why do we have so
many?)

Not migrated:

- TemplateCmdPalExtension.sln
- FancyZonesEditor.sln
- BugReportTool.sln
- CleanUp_tool.sln
- FancyZones_DrawLayoutTest.sln
- FancyZones_zonable_tester.sln
- FancyZone_HitTest.sln
- MonitorReportTool.sln
- PowerToyTemplate.sln
- StylesReportTool.sln

---------

Co-authored-by: vanzue <vanzue@outlook.com>
2025-12-03 17:59:46 +08:00
Jessica Dene Earley-Cha
8f9a2c32cc add missing powertoys events (#44016)
## Summary of the Pull Request
This added missing telemetry events from modules that were not listed in
DATA_AND_PRIVACY

## PR Checklist

- [ ] Closes: #xxx
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [x] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
2025-12-02 09:59:57 -08:00
moooyo
bcd1583bb7 [AOT] Refactor SettingsLib/SettingsUI for Native AOT compatibility (#42644)
<!-- 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

Key Changes:

1. Settings.UI.Library:
- Added SettingsSerializationContext.cs with comprehensive
JsonSerializable attributes for all settings types
- Updated BasePTModuleSettings.ToJsonString() to use AOT-compatible
serialization
- Updated SettingsUtils.GetFile<T>() to use AOT-compatible
deserialization
- Modified all ToString() methods in Properties classes to use
SettingsSerializationContext
- Converted struct fields to properties in SunTimes and
MouseWithoutBordersProperties for serialization compatibility

2. Settings.UI:
- Fixed namespace alias in SourceGenerationContextContext.cs to avoid
conflicts

For any future developers who discover incorrect settings resolution,
please follow up my changes to add your setting type into
JsonSerilizerContext.



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

Co-authored-by: Yu Leng <yuleng@microsoft.com>
2025-12-02 16:31:02 +08:00
Carlos Zamora
b075a021df Bump our telemetry package version (#44015)
Data collection is still hard.

This just makes it so that the build pipeline uses the updated PowerToys
telemetry NuGet package. The updated package switches us over to use a
diagnostic data provider group for compliance with some new regulations
(i.e. DMA and EU Data Act).
2025-12-02 07:09:08 +01:00
Michael Clayton
9e43c23216 Ready for Review - [Mouse Without Borders] - refactoring "Common" classes (Part 6 of 7) (#43208)
## Summary of the Pull Request

**Part 6** of a [slow-running 7-part
refactor](https://github.com/microsoft/PowerToys/issues/35155#issuecomment-2583334110)
of the giant "Common" class in Mouse Without Borders into individual
classes with tighter private scope.

In this PR:

* Extract the "Common" code from the following files:
  * ```Common.Encryption.cs```-> ```Core/Encryption.cs```
  * ```Common.Package.cs``` -> ```Core/<multiple files>.cs```
* ```Common.ShutdownWithPowerToys.cs``` ->
```Core/ShutdownWithPowerToys.cs```
  * ```Common.VK.cs``` -> ```Core/VK.cs```, ```Core/WM.cs```
  * ```Common.WinAPI.cs``` -> ```Core/WinAPI.cs```
* Update references to the types in the new locations
* Update unit test to verify functionality has only changed in an
expected way

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

- [x]      Partially addresses #35155 
- [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
   - no changes in this PR
- [x]     **Dev docs:** Added/updated
   - no changes in this PR
- [x]     **New binaries:** Added on the required places
   - no changes in this PR
- [ ] [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: #xxx
   - no changes in this PR

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

### Run manual tests from [Test Checklist
Template](5bc7201ae2/doc/releases/tests-checklist-template.md (mouse-without-borders)):

* Install PowerToys on two PCs in the same local network:
   - [x]     Verify that PowerToys is properly installed on both PCs.
   - [x]     Configure Windows Firewall Rules
- ```netsh advfirewall firewall add rule
name="PowerToys.MouseWithoutBorders - mc" dir=in action=allow
program="C:\src\mc\PowerToys\x64\Debug\PowerToys.exe" enable=yes
remoteip=any profile=any protocol=tcp```
   
 * Setup Connection:
- [x] Open MWB's settings on the first PC and click the "New Key"
button. Verify that a new security key is generated.
- [x] Copy the generated security key and paste it in the corresponding
input field in the settings of MWB on the second PC. Also enter the name
of the first PC in the required field.
- [x] Press "Connect" and verify that the machine layout now includes
two PC tiles, each displaying their respective PC names.
   
 * Verify Connection Status:
- [x] Ensure that the border of the remote PC turns green, indicating a
successful connection.
- [x] Enter an incorrect security key and verify that the border of the
remote PC turns red, indicating a failed connection.
   
 * Test Remote Mouse/Keyboard Control:
- [x] With the PCs connected, test the mouse/keyboard control from one
PC to another. Verify that the mouse/keyboard inputs are correctly
registered on the other PC.
- [ ] Test remote mouse/keyboard control across all four PCs, if
available. Verify that inputs are correctly registered on each connected
PC when the mouse is active there.
     - unable to test - only 2 machines available
   
 * Test Remote Control with Elevated Apps:
- note - the main PowerToys.exe must be running as a **non**-admin for
these tests
- [x] Open an elevated app on one of the PCs. Verify that without "Use
Service" enabled, PowerToys does not control the elevated app.
- [x] Enable "Use Service" in MWB's settings (need to run PowerToys.exe
as admin to enable "Use Service", then restart PowerToys.exe as
non-admin). Verify that PowerToys can now control the elevated app
remotely. Verify that MWB processes are running as LocalSystem, while
the MWB helper process is running non-elevated.
- ```get-process -Name "PowerToys.MouseWithoutBorders*" -IncludeUserName
| format-table Id, ProcessName, UserName```
- [x] Process: ```PowerToys.MouseWithoutBorders.exe``` - running as
```SYSTEM```
- [x] Process: ```PowerToys.MouseWithoutBorders.Helper.exe``` - running
as current user
- ```get-service -Name "PowerToys.*" | ft Status, Name, UserName;
get-ciminstance -Class "Win32_Service" -Filter "Name like 'PowerToys%'"
| ft ProcessId, Name```
- [x] Service: ```PowerToys.MWB.Service``` - running as ```Local
System```
- [x] Toggle "Use Service" again, verify that each time you do that, the
MWB processes are restarted.
- [x] Run PowerToys elevated on one of the machines, verify that you can
control elevated apps remotely now on that machine.

* Test Module Enable Status:
- [x] For all combinations of "Use Service"/"Run PowerToys as admin",
try enabling/disabling MWB module and verify that it's indeed being
toggled using task manager.
   
 * Test Disconnection/Reconnection:
- [x] Disconnect one of the PCs from network. Verify that the machine
layout updates to reflect the disconnection.
   - [x]     Do the same, but now by exiting PowerToys.
   - [ ]     Start PowerToys again, verify that the PCs are reconnected.
  
 * Test Various Local Network Conditions:
- [ ] Test MWB performance under various network conditions (e.g., low
bandwidth, high latency). Verify that the tool maintains a stable
connection and functions correctly.

 * Clipboard Sharing:
- [x] Copy some text on one PC and verify that the same text can be
pasted on another PC.
- [x] Use the screenshot key and Win+Shift+S to take a screenshot on one
PC and verify that the screenshot can be pasted on another PC.
- [x] Copy a file in Windows Explorer and verify that the file can be
pasted on another PC. Make sure the file size is below 100MB.
- [x] Try to copy multiple files and directories and verify that it's
not possible (only the first selected file is being copied).
 
 * Drag and Drop:
- [ ] Drag a file from Windows Explorer on one PC, cross the screen
border onto another PC, and release it there. Verify that the file is
copied to the other PC. Make sure the file size is below 100MB.
- [ ] While dragging the file, verify that a corresponding icon is
displayed under the mouse cursor.
- [ ] Without moving the mouse from one PC to the target PC, press
CTRL+ALT+F1/2/3/4 hotkey to switch to the target PC directly and verify
that file sharing/dropping is not working.

 * Lock and Unlock with "Use Service" Enabled:
   - [x]     Enable "Use Service" in MWB's settings.
- [x] Lock a remote PC using Win+L, move the mouse to it remotely, and
try to unlock it. Verify that you can unlock the remote PC.
- [x] Disable "Use Service" in MWB's settings, lock the remote PC, move
the mouse to it remotely, and try to unlock it. Verify that you can't
unlock the remote PC.

 * Test Settings:
- [ ] Change the rest of available settings on MWB page and verify that
each setting works as described.

### Group Policy Tests

See https://learn.microsoft.com/en-us/windows/powertoys/grouppolicy

- [ ] Install *.admx / *.adml and check settings behave as expected
  - [ ] I'll expand the list of settings here when I get this far :-)
- [ ] HKEY_LOCAL_MACHINE\SOFTWARE\Policies\PowerToys
  - [x]     ConfigureEnabledUtilityMouseWithoutBorders
- [x] ```[missing]``` - "Activation -> Enable Mouse Without Borders"
enabled, with GPO warning hidden
- ```reg delete HKEY_LOCAL_MACHINE\SOFTWARE\Policies\PowerToys /v
ConfigureEnabledUtilityMouseWithoutBorders /f```
- [x] ```0``` - "Activation -> Enable Mouse Without Borders" set to
"off" and disabled, with GPO warning visible
- ```reg add HKEY_LOCAL_MACHINE\SOFTWARE\Policies\PowerToys /v
ConfigureEnabledUtilityMouseWithoutBorders /t REG_DWORD /d 0 /f```
- [x] ```1``` - "Activation -> Enable Mouse Without Borders" set to "on"
and disabled, with GPO warning visible
- ```reg add HKEY_LOCAL_MACHINE\SOFTWARE\Policies\PowerToys /v
ConfigureEnabledUtilityMouseWithoutBorders /t REG_DWORD /d 1 /f```
  - [ ] MwbClipboardSharingEnabled
  - [ ] MwbFileTransferEnabled
  - [ ] MwbUseOriginalUserInterface
  - [ ] MwbDisallowBlockingScreensaver
  - [ ] MwbSameSubnetOnly
  - [ ] MwbValidateRemoteIp
  - [x]     MwbDisableUserDefinedIpMappingRules
- [x] ```[missing]``` - "Advanced Settings -> IP address mapping"
enabled, with GPO warning hidden
- ```reg delete HKEY_LOCAL_MACHINE\SOFTWARE\Policies\PowerToys /v
MwbDisableUserDefinedIpMappingRules /f```
- [x] ```0``` - "Advanced Settings -> IP address mapping" enabled, with
GPO warning hidden
- ```reg add HKEY_LOCAL_MACHINE\SOFTWARE\Policies\PowerToys /v
MwbDisableUserDefinedIpMappingRules /t REG_DWORD /d 0 /f```
- [x] ```1``` - "Advanced Settings -> IP address mapping" disabled, with
GPO warning visible
- ```reg add HKEY_LOCAL_MACHINE\SOFTWARE\Policies\PowerToys /v
MwbDisableUserDefinedIpMappingRules /t REG_DWORD /d 1 /f```
  - [x]     MwbPolicyDefinedIpMappingRules
- [x] ```[missing]``` - "Advanced Settings -> IP address mapping"
enabled, with GPO warning and GPO values hidden
- ```reg delete HKEY_LOCAL_MACHINE\SOFTWARE\Policies\PowerToys /v
MwbPolicyDefinedIpMappingRules /f```
- [x] ```[empty value]``` - "Advanced Settings -> IP address mapping"
enabled, with GPO warning hidden and GPO values hidden
- ```reg add HKEY_LOCAL_MACHINE\SOFTWARE\Policies\PowerToys /v
MwbPolicyDefinedIpMappingRules /t REG_MULTI_SZ /d "" /f```
- [x] ```[non-empty value]``` - "Advanced Settings -> IP address
mapping" enabled, with GPO warning visible and GPO values visible
- ```reg add HKEY_LOCAL_MACHINE\SOFTWARE\Policies\PowerToys /v
MwbPolicyDefinedIpMappingRules /t REG_MULTI_SZ /d "aaa 10.0.0.1\0bbb
10.0.0.2" /f```
2025-12-02 10:43:08 +08:00
Jiří Polášek
bece9c9217 CmdPal: Adds a back button to Settings window (#44013)
## Summary of the Pull Request

This PR introduces proper navigation support in the Settings window.
- Enables the Go back button in the title bar.
- Adds support for Alt + Left Arrow, mouse X1 button, and the Go back
button.
- Fixes breadcrumb updates and localization issues to prevent them from
breaking during navigation.

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

- [x] Closes: #44011
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **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-12-01 20:25:38 -06:00
Jiří Polášek
32c13cead4 CmdPal: Remove fallbacks from the home page when there's no query (#44005)
## Summary of the Pull Request

This PR stops fallback commands from showing on the homepage when
there’s no query, with the reasons detailed in the linked issue.

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

- [x] Closes: #44004 
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **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-12-01 13:24:54 -06:00
Jiří Polášek
33808fdb9c CmdPal: Hide RDC fallback item by default (#43994)
## Summary of the Pull Request

This fixes few nits with RDC extension:
- hides the RDC fallback item from the home page when there’s no query;
- fixes MSTSC process working directory (must physically exists or be an
empty string)

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

- [ ] Closes: #xxx
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **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-12-01 13:23:48 -06:00
Leilei Zhang
abf5e5bf8d fix build error 2025-11-28 14:43:03 +08:00
Leilei Zhang
e1e74b8fab fix update 2025-11-27 21:35:40 +08:00
129 changed files with 3754 additions and 5077 deletions

View File

@@ -773,6 +773,7 @@ INITGUID
INITTOLOGFONTSTRUCT
INLINEPREFIX
inlines
Inno
INPC
inproc
INPUTHARDWARE
@@ -1629,6 +1630,7 @@ SKIPOWNPROCESS
sku
SLGP
sln
slnx
SMALLICON
smartphone
smileys
@@ -1847,6 +1849,7 @@ UNCPRIORITY
UNDNAME
UNICODETEXT
unins
Uninstaller
uninstalls
Uniquifies
unitconverter

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.PowerToys.Telemetry" version="2.0.3" />
<package id="Microsoft.PowerToys.Telemetry" version="2.0.4" />
</packages>

View File

@@ -192,14 +192,14 @@ jobs:
displayName: Verify XAML formatting
- pwsh: |-
& '.pipelines/verifyNugetPackages.ps1' -solution '$(build.sourcesdirectory)\PowerToys.sln'
displayName: Verify Nuget package versions for PowerToys.sln
& '.pipelines/verifyNugetPackages.ps1' -solution '$(build.sourcesdirectory)\PowerToys.slnx'
displayName: Verify Nuget package versions for PowerToys.slnx
- pwsh: |-
& '.pipelines/verifyArm64Configuration.ps1' -solution '$(build.sourcesdirectory)\PowerToys.sln'
& '.pipelines/verifyArm64Configuration.ps1' -solution '$(build.sourcesdirectory)\PowerToys.slnx'
& '.pipelines/verifyArm64Configuration.ps1' -solution '$(build.sourcesdirectory)\tools\BugReportTool\BugReportTool.sln'
& '.pipelines/verifyArm64Configuration.ps1' -solution '$(build.sourcesdirectory)\tools\StylesReportTool\StylesReportTool.sln'
& '.pipelines/verifyArm64Configuration.ps1' -solution '$(build.sourcesdirectory)\installer\PowerToysSetup.sln'
& '.pipelines/verifyArm64Configuration.ps1' -solution '$(build.sourcesdirectory)\installer\PowerToysSetup.slnx'
displayName: Verify ARM64 configurations
- ${{ if eq(parameters.enablePackageCaching, true) }}:
@@ -252,7 +252,7 @@ jobs:
${{ else }}:
displayName: Build PowerToys main project
inputs:
solution: 'PowerToys.sln'
solution: 'PowerToys.slnx'
vsVersion: 17.0
msbuildArgs: >-
-restore -graph
@@ -275,7 +275,7 @@ jobs:
displayName: Generate DSC artifacts for ARM64
condition: and(succeeded(), eq(variables['BuildPlatform'], 'arm64'))
inputs:
solution: PowerToys.sln
solution: PowerToys.slnx
vsVersion: 17.0
msbuildArgs: >-
-restore

View File

@@ -74,7 +74,7 @@ jobs:
command: restore
feedsToUse: config
configPath: nuget.config
restoreSolution: PowerToys.sln
restoreSolution: PowerToys.slnx
restoreDirectory: '$(Build.SourcesDirectory)\packages'
# Build all UI test projects if no specific modules are specified
@@ -129,4 +129,4 @@ jobs:
- publish: $(JobOutputDirectory)
artifact: $(JobOutputArtifactName)
displayName: Publish UI Test artifacts
condition: always()
condition: always()

View File

@@ -35,7 +35,7 @@ steps:
- task: VSBuild@1
displayName: Build Shared Support DLLs
inputs:
solution: "**/installer/PowerToysSetup.sln"
solution: "**/installer/PowerToysSetup.slnx"
vsVersion: 17.0
msbuildArgs: >-
/t:PowerToysSetupCustomActionsVNext;SilentFilesInUseBAFunction
@@ -74,7 +74,7 @@ steps:
- task: VSBuild@1
displayName: 💻 Build VNext MSI
inputs:
solution: "**/installer/PowerToysSetup.sln"
solution: "**/installer/PowerToysSetup.slnx"
vsVersion: 17.0
msbuildArgs: >-
-restore
@@ -91,7 +91,7 @@ steps:
- task: VSBuild@1
displayName: 👤 Build VNext MSI
inputs:
solution: "**/installer/PowerToysSetup.sln"
solution: "**/installer/PowerToysSetup.slnx"
vsVersion: 17.0
msbuildArgs: >-
/t:PowerToysInstallerVNext
@@ -142,7 +142,7 @@ steps:
- task: VSBuild@1
displayName: 💻 Build VNext Bootstrapper
inputs:
solution: "**/installer/PowerToysSetup.sln"
solution: "**/installer/PowerToysSetup.slnx"
vsVersion: 17.0
msbuildArgs: >-
-restore
@@ -159,7 +159,7 @@ steps:
- task: VSBuild@1
displayName: 👤 Build VNext Bootstrapper
inputs:
solution: "**/installer/PowerToysSetup.sln"
solution: "**/installer/PowerToysSetup.slnx"
vsVersion: 17.0
msbuildArgs: >-
/t:PowerToysBootstrapperVNext

View File

@@ -54,4 +54,13 @@ steps:
feedsToUse: 'config'
nugetConfigPath: '$(build.sourcesdirectory)\nuget.config'
restoreSolution: '$(build.sourcesdirectory)\**\*.sln'
includeNuGetOrg: false
includeNuGetOrg: false
- task: NuGetCommand@2
displayName: 'Restore NuGet packages (slnx)'
inputs:
command: 'restore'
feedsToUse: 'config'
nugetConfigPath: '$(build.sourcesdirectory)\nuget.config'
restoreSolution: '$(build.sourcesdirectory)\**\*.slnx'
includeNuGetOrg: false

File diff suppressed because it is too large Load Diff

1041
PowerToys.slnx Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -134,7 +134,7 @@ If you prefer, you can alternatively build prerequisite projects for the install
#### Locally compiling the installer
1. Open `installer\PowerToysSetup.sln`
1. Open `installer\PowerToysSetup.slnx`
1. In Visual Studio, in the `Solutions Configuration` drop-down menu select `Release`
1. From the `Build` menu choose `Build Solution`.
@@ -144,9 +144,9 @@ To build the installer from the command line, run `Developer Command Prompt for
```
git clean -xfd -e *exe -- .\installer\
MSBuild -t:restore .\installer\PowerToysSetup.sln -p:RestorePackagesConfig=true /p:Platform="x64" /p:Configuration=Release
MSBuild -t:Restore -m .\installer\PowerToysSetup.sln /t:PowerToysInstallerVNext /p:Configuration=Release /p:Platform="x64"
MSBuild -t:Restore -m .\installer\PowerToysSetup.sln /t:PowerToysBootstrapperVNext /p:Configuration=Release /p:Platform="x64"
MSBuild -t:restore .\installer\PowerToysSetup.slnx -p:RestorePackagesConfig=true /p:Platform="x64" /p:Configuration=Release
MSBuild -t:Restore -m .\installer\PowerToysSetup.slnx /t:PowerToysInstallerVNext /p:Configuration=Release /p:Platform="x64"
MSBuild -t:Restore -m .\installer\PowerToysSetup.slnx /t:PowerToysBootstrapperVNext /p:Configuration=Release /p:Platform="x64"
```
### Supported arguments for the .EXE Bootstrapper installer

View File

@@ -19,7 +19,7 @@ You can build the entire solution from the command line, which is sometimes fast
2. Navigate to the repository root directory
3. Run the following command(don't forget to set the correct platform):
```pwsh
msbuild -restore -p:RestorePackagesConfig=true -p:Platform=ARM64 -m PowerToys.sln /tl /p:NuGetInteractive="true"
msbuild -restore -p:RestorePackagesConfig=true -p:Platform=ARM64 -m PowerToys.slnx /tl /p:NuGetInteractive="true"
```
4. This process should complete in approximately 13-14 minutes for a full build

View File

@@ -42,10 +42,10 @@ Or reach out to "tools\build\BUILD-GUIDELINES.md"
### Sample plain msbuild command
```powershell
# Restore:
msbuild powertoys.sln -t:restore -p:configuration=debug -p:platform=x64 -m
msbuild powertoys.slnx -t:restore -p:configuration=debug -p:platform=x64 -m
# Build powertoys sln
msbuild powertoys.sln -p:configuration=debug -p:platform=x64 -m
# Build powertoys slnx
msbuild powertoys.slnx -p:configuration=debug -p:platform=x64 -m
# dotnet project
msbuild src\settings-ui\Settings.UI\PowerToys.Settings.csproj -p:Platform=x64 -p:Configuration=Debug -m
@@ -122,7 +122,7 @@ Similar for attach to managed code.
| Task | Command / Action | Notes |
|------|------------------|-------|
| Clean | `git clean -xdf` (careful) or `msbuild /t:Clean PowerToys.sln` | Deep clean removes packages & build outputs |
| Clean | `git clean -xdf` (careful) or `msbuild /t:Clean PowerToys.slnx` | Deep clean removes packages & build outputs |
| Rebuild single project | `msbuild path\to\proj.vcxproj /t:Rebuild -p:Platform=x64 -p:Configuration=Debug` | Faster than whole solution |
| Generate installer (rare in inner loop) | See `tools\build\build-installer.ps1` | Usually not needed for local debug |
| Resource conversion errors | Re-run restore + build | Triggers custom PowerShell targets |
| Resource conversion errors | Re-run restore + build | Triggers custom PowerShell targets |

View File

@@ -12,7 +12,7 @@
- Exit PowerToys if it's running.
- Open `PowerToys.sln` in Visual Studio and build the solution.
- Open `PowerToys.slnx` in Visual Studio and build the solution.
- Run tests in the Test Explorer (`Test > Test Explorer` or `Ctrl+E, T`).

View File

@@ -86,7 +86,7 @@ The module provides a user interface for configuring settings in the PowerToys S
### Building and Testing
1. Clone the repository: `git clone https://github.com/microsoft/PowerToys.git`
2. Open PowerToys.sln in Visual Studio
2. Open PowerToys.slnx in Visual Studio
3. Select the Release configuration and build the solution
4. Run PowerToys.exe from the output directory to test the module

View File

@@ -161,7 +161,7 @@ FancyZones is divided into several projects:
```
git clone https://github.com/microsoft/PowerToys.git
```
2. Open `PowerToys.sln` in Visual Studio
2. Open `PowerToys.slnx` in Visual Studio
3. Select the Release configuration and build the solution
4. If you encounter build errors, try deleting the x64 output folder and rebuild
@@ -244,7 +244,7 @@ UI tests are implemented using [Windows Application Driver](https://github.com/m
- Exit PowerToys if it's running
- Run WinAppDriver.exe from the installation directory. Skip this step if installed in the default directory (`C:\Program Files (x86)\Windows Application Driver`); in this case, it'll be launched automatically during tests.
- Open `PowerToys.sln` in Visual Studio and build the solution.
- Open `PowerToys.slnx` in Visual Studio and build the solution.
- Run tests in the Test Explorer (`Test > Test Explorer` or `Ctrl+E, T`).
>Note: notifications or other application windows, that are shown above the window under test, can disrupt the testing process.

View File

@@ -11,7 +11,7 @@ Keyboard Manager consists of two main components:
## Development Environment Setup
1. Clone the PowerToys repository
2. Open `PowerToys.sln` in Visual Studio
2. Open `PowerToys.slnx` in Visual Studio
3. Ensure all NuGet packages are restored
4. Build the entire solution in Debug configuration
@@ -91,4 +91,4 @@ If you encounter issues with multiple instances, check the mutex logic in `Keybo
To debug both the Editor and Engine:
1. Launch the Engine first in debug mode
2. Attach the debugger to the Editor process when it starts
2. Attach the debugger to the Editor process when it starts

View File

@@ -92,7 +92,7 @@ The modules settings are exposed in the PowerToys Settings UI. Options includ
3. Build the solution:
```sh
msbuild -restore -p:RestorePackagesConfig=true -p:Platform=ARM64 -m PowerToys.sln
msbuild -restore -p:RestorePackagesConfig=true -p:Platform=ARM64 -m PowerToys.slnx
```
> Note: This may take some time.

View File

@@ -53,7 +53,7 @@ The Screen Ruler module consists of several components:
### Building
1. Open PowerToys.sln in Visual Studio
1. Open PowerToys.slnx in Visual Studio
2. In the Solutions Configuration drop-down menu, select Release or Debug
3. From the Build menu, choose Build Solution
4. The executable app for Screen Ruler is named PowerToys.MeasureToolUI.exe

View File

@@ -19,7 +19,7 @@ Shortcut Guide is a PowerToy that displays an overlay of available keyboard shor
## Build and Debug Instructions
### Build
1. Open PowerToys.sln in Visual Studio
1. Open PowerToys.slnx in Visual Studio
2. Select Release or Debug in the Solutions Configuration drop-down menu
3. From the Build menu, choose Build Solution
4. The executable is named PowerToys.ShortcutGuide.exe

View File

@@ -80,7 +80,7 @@ Once you've discussed your proposed feature/fix/etc. with a team member, and an
### Install Visual Studio dependencies
1. Open the `PowerToys.sln` file.
1. Open the `PowerToys.slnx` file.
1. If you see a dialog that says `install extra components` in the solution explorer pane, click `install`
### Get Submodules to compile
@@ -93,7 +93,7 @@ We have submodules that need to be initialized before you can compile most parts
### Compiling Source Code
- Open `PowerToys.sln` in Visual Studio.
- Open `PowerToys.slnx` in Visual Studio.
- In the `Solutions Configuration` drop-down menu select `Release` or `Debug`.
- From the `Build` menu choose `Build Solution`, or press <kbd>Control</kbd>+<kbd>Shift</kbd>+<kbd>b</kbd> on your keyboard.
- The build process may take several minutes depending on your computer's performance. Once it completes, the PowerToys binaries will be in your repo under `x64\Release\`.
@@ -107,10 +107,10 @@ Our installer is two parts, an EXE and an MSI. The EXE (Bootstrapper) contains
The installer can only be compiled in `Release` mode; steps 1 and 2 must be performed before the MSI can be compiled.
1. Compile `PowerToys.sln`. Instructions are listed above.
1. Compile `PowerToys.slnx`. Instructions are listed above.
1. Compile `BugReportTool.sln` tool. Path from root: `tools\BugReportTool\BugReportTool.sln` (details listed below)
1. Compile `StylesReportTool.sln` tool. Path from root: `tools\StylesReportTool\StylesReportTool.sln` (details listed below)
1. Compile `PowerToysSetup.sln` Path from root: `installer\PowerToysSetup.sln` (details listed below)
1. Compile `PowerToysSetup.slnx` Path from root: `installer\PowerToysSetup.slnx` (details listed below)
See [Installer](core/installer.md) for more details on building and debugging the installer.

View File

@@ -1,96 +0,0 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.1.32414.318
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "spdlog", "..\src\logging\logging.vcxproj", "{7E1E3F13-2BD6-3F75-A6A7-873A2B55C60F}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "logger", "..\src\common\logger\logger.vcxproj", "{D9B8FC84-322A-4F9F-BBB9-20915C47DDFD}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Version", "..\src\common\version\version.vcxproj", "{CC6E41AC-8174-4E8A-8D22-85DD7F4851DF}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EtwTrace", "..\src\common\Telemetry\EtwTrace\EtwTrace.vcxproj", "{8F021B46-362B-485C-BFBA-CCF83E820CBD}"
EndProject
Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "PowerToysInstallerVNext", "PowerToysSetupVNext\PowerToysInstallerVNext.wixproj", "{B6E94700-DF38-41F6-A3FD-18B69674AB1E}"
EndProject
Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "PowerToysBootstrapperVNext", "PowerToysSetupVNext\PowerToysBootstrapperVNext.wixproj", "{DA4E9744-80BE-424C-B0F5-AFD8757DB575}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerToysSetupCustomActionsVNext", "PowerToysSetupCustomActionsVNext\PowerToysSetupCustomActionsVNext.vcxproj", "{B3A354B0-1E54-4B55-A962-FB5AF9330C19}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SilentFilesInUseBAFunction", "PowerToysSetupVNext\SilentFilesInUseBA\SilentFilesInUseBAFunction.vcxproj", "{F8B9F842-F5C3-4A2D-8C85-7F8B9E2B4F1D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM64 = Debug|ARM64
Debug|x64 = Debug|x64
Release|ARM64 = Release|ARM64
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{7E1E3F13-2BD6-3F75-A6A7-873A2B55C60F}.Debug|ARM64.ActiveCfg = Debug|ARM64
{7E1E3F13-2BD6-3F75-A6A7-873A2B55C60F}.Debug|x64.ActiveCfg = Debug|x64
{7E1E3F13-2BD6-3F75-A6A7-873A2B55C60F}.Debug|x64.Build.0 = Debug|x64
{7E1E3F13-2BD6-3F75-A6A7-873A2B55C60F}.Release|ARM64.ActiveCfg = Release|ARM64
{7E1E3F13-2BD6-3F75-A6A7-873A2B55C60F}.Release|ARM64.Build.0 = Release|ARM64
{7E1E3F13-2BD6-3F75-A6A7-873A2B55C60F}.Release|x64.ActiveCfg = Release|x64
{7E1E3F13-2BD6-3F75-A6A7-873A2B55C60F}.Release|x64.Build.0 = Release|x64
{D9B8FC84-322A-4F9F-BBB9-20915C47DDFD}.Debug|ARM64.ActiveCfg = Debug|ARM64
{D9B8FC84-322A-4F9F-BBB9-20915C47DDFD}.Debug|x64.ActiveCfg = Debug|x64
{D9B8FC84-322A-4F9F-BBB9-20915C47DDFD}.Debug|x64.Build.0 = Debug|x64
{D9B8FC84-322A-4F9F-BBB9-20915C47DDFD}.Release|ARM64.ActiveCfg = Release|ARM64
{D9B8FC84-322A-4F9F-BBB9-20915C47DDFD}.Release|ARM64.Build.0 = Release|ARM64
{D9B8FC84-322A-4F9F-BBB9-20915C47DDFD}.Release|x64.ActiveCfg = Release|x64
{D9B8FC84-322A-4F9F-BBB9-20915C47DDFD}.Release|x64.Build.0 = Release|x64
{CC6E41AC-8174-4E8A-8D22-85DD7F4851DF}.Debug|ARM64.ActiveCfg = Debug|ARM64
{CC6E41AC-8174-4E8A-8D22-85DD7F4851DF}.Debug|ARM64.Build.0 = Debug|ARM64
{CC6E41AC-8174-4E8A-8D22-85DD7F4851DF}.Debug|x64.ActiveCfg = Debug|x64
{CC6E41AC-8174-4E8A-8D22-85DD7F4851DF}.Debug|x64.Build.0 = Debug|x64
{CC6E41AC-8174-4E8A-8D22-85DD7F4851DF}.Release|ARM64.ActiveCfg = Release|ARM64
{CC6E41AC-8174-4E8A-8D22-85DD7F4851DF}.Release|ARM64.Build.0 = Release|ARM64
{CC6E41AC-8174-4E8A-8D22-85DD7F4851DF}.Release|x64.ActiveCfg = Release|x64
{CC6E41AC-8174-4E8A-8D22-85DD7F4851DF}.Release|x64.Build.0 = Release|x64
{8F021B46-362B-485C-BFBA-CCF83E820CBD}.Debug|ARM64.ActiveCfg = Debug|ARM64
{8F021B46-362B-485C-BFBA-CCF83E820CBD}.Debug|ARM64.Build.0 = Debug|ARM64
{8F021B46-362B-485C-BFBA-CCF83E820CBD}.Debug|x64.ActiveCfg = Debug|x64
{8F021B46-362B-485C-BFBA-CCF83E820CBD}.Debug|x64.Build.0 = Debug|x64
{8F021B46-362B-485C-BFBA-CCF83E820CBD}.Release|ARM64.ActiveCfg = Release|ARM64
{8F021B46-362B-485C-BFBA-CCF83E820CBD}.Release|ARM64.Build.0 = Release|ARM64
{8F021B46-362B-485C-BFBA-CCF83E820CBD}.Release|x64.ActiveCfg = Release|x64
{8F021B46-362B-485C-BFBA-CCF83E820CBD}.Release|x64.Build.0 = Release|x64
{B6E94700-DF38-41F6-A3FD-18B69674AB1E}.Debug|ARM64.ActiveCfg = Debug|ARM64
{B6E94700-DF38-41F6-A3FD-18B69674AB1E}.Debug|ARM64.Build.0 = Debug|ARM64
{B6E94700-DF38-41F6-A3FD-18B69674AB1E}.Debug|x64.ActiveCfg = Debug|x64
{B6E94700-DF38-41F6-A3FD-18B69674AB1E}.Debug|x64.Build.0 = Debug|x64
{B6E94700-DF38-41F6-A3FD-18B69674AB1E}.Release|ARM64.ActiveCfg = Release|ARM64
{B6E94700-DF38-41F6-A3FD-18B69674AB1E}.Release|ARM64.Build.0 = Release|ARM64
{B6E94700-DF38-41F6-A3FD-18B69674AB1E}.Release|x64.ActiveCfg = Release|x64
{B6E94700-DF38-41F6-A3FD-18B69674AB1E}.Release|x64.Build.0 = Release|x64
{DA4E9744-80BE-424C-B0F5-AFD8757DB575}.Debug|ARM64.ActiveCfg = Debug|ARM64
{DA4E9744-80BE-424C-B0F5-AFD8757DB575}.Debug|ARM64.Build.0 = Debug|ARM64
{DA4E9744-80BE-424C-B0F5-AFD8757DB575}.Debug|x64.ActiveCfg = Debug|x64
{DA4E9744-80BE-424C-B0F5-AFD8757DB575}.Debug|x64.Build.0 = Debug|x64
{DA4E9744-80BE-424C-B0F5-AFD8757DB575}.Release|ARM64.ActiveCfg = Release|ARM64
{DA4E9744-80BE-424C-B0F5-AFD8757DB575}.Release|ARM64.Build.0 = Release|ARM64
{DA4E9744-80BE-424C-B0F5-AFD8757DB575}.Release|x64.ActiveCfg = Release|x64
{DA4E9744-80BE-424C-B0F5-AFD8757DB575}.Release|x64.Build.0 = Release|x64
{B3A354B0-1E54-4B55-A962-FB5AF9330C19}.Debug|ARM64.ActiveCfg = Debug|ARM64
{B3A354B0-1E54-4B55-A962-FB5AF9330C19}.Debug|x64.ActiveCfg = Debug|x64
{B3A354B0-1E54-4B55-A962-FB5AF9330C19}.Debug|x64.Build.0 = Debug|x64
{B3A354B0-1E54-4B55-A962-FB5AF9330C19}.Release|ARM64.ActiveCfg = Release|ARM64
{B3A354B0-1E54-4B55-A962-FB5AF9330C19}.Release|ARM64.Build.0 = Release|ARM64
{B3A354B0-1E54-4B55-A962-FB5AF9330C19}.Release|x64.ActiveCfg = Release|x64
{B3A354B0-1E54-4B55-A962-FB5AF9330C19}.Release|x64.Build.0 = Release|x64
{F8B9F842-F5C3-4A2D-8C85-7F8B9E2B4F1D}.Debug|ARM64.ActiveCfg = Debug|ARM64
{F8B9F842-F5C3-4A2D-8C85-7F8B9E2B4F1D}.Debug|x64.ActiveCfg = Debug|x64
{F8B9F842-F5C3-4A2D-8C85-7F8B9E2B4F1D}.Debug|x64.Build.0 = Debug|x64
{F8B9F842-F5C3-4A2D-8C85-7F8B9E2B4F1D}.Release|ARM64.ActiveCfg = Release|ARM64
{F8B9F842-F5C3-4A2D-8C85-7F8B9E2B4F1D}.Release|ARM64.Build.0 = Release|ARM64
{F8B9F842-F5C3-4A2D-8C85-7F8B9E2B4F1D}.Release|x64.ActiveCfg = Release|x64
{F8B9F842-F5C3-4A2D-8C85-7F8B9E2B4F1D}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B7A3DA30-D443-40FF-AC51-988AD41E3962}
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,22 @@
<Solution>
<Configurations>
<Platform Name="ARM64" />
<Platform Name="x64" />
</Configurations>
<Project Path="../src/common/logger/logger.vcxproj" Id="d9b8fc84-322a-4f9f-bbb9-20915c47ddfd">
<Build Solution="Debug|ARM64" Project="false" />
</Project>
<Project Path="../src/common/Telemetry/EtwTrace/EtwTrace.vcxproj" Id="8f021b46-362b-485c-bfba-ccf83e820cbd" />
<Project Path="../src/common/version/version.vcxproj" Id="cc6e41ac-8174-4e8a-8d22-85dd7f4851df" />
<Project Path="../src/logging/logging.vcxproj" Id="7e1e3f13-2bd6-3f75-a6a7-873a2b55c60f">
<Build Solution="Debug|ARM64" Project="false" />
</Project>
<Project Path="PowerToysSetupCustomActionsVNext/PowerToysSetupCustomActionsVNext.vcxproj" Id="b3a354b0-1e54-4b55-a962-fb5af9330c19">
<Build Solution="Debug|ARM64" Project="false" />
</Project>
<Project Path="PowerToysSetupVNext/PowerToysBootstrapperVNext.wixproj" Type="b7dd6f7e-def8-4e67-b5b7-07ef123db6f0" />
<Project Path="PowerToysSetupVNext/PowerToysInstallerVNext.wixproj" Type="b7dd6f7e-def8-4e67-b5b7-07ef123db6f0" />
<Project Path="PowerToysSetupVNext/SilentFilesInUseBA/SilentFilesInUseBAFunction.vcxproj" Id="f8b9f842-f5c3-4a2d-8c85-7f8b9e2b4f1d">
<Build Solution="Debug|ARM64" Project="false" />
</Project>
</Solution>

View File

@@ -16,9 +16,54 @@
namespace registry
{
namespace detail
{
struct on_exit
{
std::function<void()> f;
on_exit(std::function<void()> f) :
f{ std::move(f) } {}
~on_exit() { f(); }
};
template<class... Ts>
struct overloaded : Ts...
{
using Ts::operator()...;
};
template<class... Ts>
overloaded(Ts...) -> overloaded<Ts...>;
inline const wchar_t* getScopeName(HKEY scope)
{
if (scope == HKEY_LOCAL_MACHINE)
{
return L"HKLM";
}
else if (scope == HKEY_CURRENT_USER)
{
return L"HKCU";
}
else if (scope == HKEY_CLASSES_ROOT)
{
return L"HKCR";
}
else
{
return L"HK??";
}
}
}
namespace install_scope
{
const wchar_t INSTALL_SCOPE_REG_KEY[] = L"Software\\Classes\\powertoys\\";
const wchar_t UNINSTALL_REG_KEY[] = L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
// Bundle UpgradeCode from PowerToys.wxs (with braces as stored in registry)
const wchar_t BUNDLE_UPGRADE_CODE[] = L"{6341382D-C0A9-4238-9188-BE9607E3FAB2}";
enum class InstallScope
{
@@ -26,8 +71,67 @@ namespace registry
PerUser,
};
// Helper function to find PowerToys bundle in Windows Uninstall registry by BundleUpgradeCode
inline bool find_powertoys_bundle_in_uninstall_registry(HKEY rootKey)
{
HKEY uninstallKey{};
if (RegOpenKeyExW(rootKey, UNINSTALL_REG_KEY, 0, KEY_READ, &uninstallKey) != ERROR_SUCCESS)
{
return false;
}
detail::on_exit closeUninstallKey{ [uninstallKey] { RegCloseKey(uninstallKey); } };
DWORD index = 0;
wchar_t subKeyName[256];
// Enumerate all subkeys under Uninstall
while (RegEnumKeyW(uninstallKey, index++, subKeyName, 256) == ERROR_SUCCESS)
{
HKEY productKey{};
if (RegOpenKeyExW(uninstallKey, subKeyName, 0, KEY_READ, &productKey) != ERROR_SUCCESS)
{
continue;
}
detail::on_exit closeProductKey{ [productKey] { RegCloseKey(productKey); } };
// Check BundleUpgradeCode value (specific to WiX Bundle installations)
wchar_t bundleUpgradeCode[256]{};
DWORD bundleUpgradeCodeSize = sizeof(bundleUpgradeCode);
if (RegQueryValueExW(productKey, L"BundleUpgradeCode", nullptr, nullptr,
reinterpret_cast<LPBYTE>(bundleUpgradeCode), &bundleUpgradeCodeSize) == ERROR_SUCCESS)
{
if (_wcsicmp(bundleUpgradeCode, BUNDLE_UPGRADE_CODE) == 0)
{
return true;
}
}
}
return false;
}
inline const InstallScope get_current_install_scope()
{
// 1. Check HKCU Uninstall registry first (user-level bundle)
// Note: MSI components are always in HKLM regardless of install scope,
// but the Bundle entry will be in HKCU for per-user installations
if (find_powertoys_bundle_in_uninstall_registry(HKEY_CURRENT_USER))
{
Logger::info(L"Found user-level PowerToys bundle via BundleUpgradeCode in HKCU");
return InstallScope::PerUser;
}
// 2. Check HKLM Uninstall registry (machine-level bundle)
if (find_powertoys_bundle_in_uninstall_registry(HKEY_LOCAL_MACHINE))
{
Logger::info(L"Found machine-level PowerToys bundle via BundleUpgradeCode in HKLM");
return InstallScope::PerMachine;
}
// 3. Fallback to legacy custom registry key detection
Logger::info(L"PowerToys bundle not found in Uninstall registry, falling back to legacy detection");
// Open HKLM key
HKEY perMachineKey{};
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
@@ -45,6 +149,7 @@ namespace registry
&perUserKey) != ERROR_SUCCESS)
{
// both keys are missing
Logger::warn(L"No PowerToys installation detected, defaulting to PerMachine");
return InstallScope::PerMachine;
}
else
@@ -96,47 +201,6 @@ namespace registry
template<class>
inline constexpr bool always_false_v = false;
namespace detail
{
struct on_exit
{
std::function<void()> f;
on_exit(std::function<void()> f) :
f{ std::move(f) } {}
~on_exit() { f(); }
};
template<class... Ts>
struct overloaded : Ts...
{
using Ts::operator()...;
};
template<class... Ts>
overloaded(Ts...) -> overloaded<Ts...>;
inline const wchar_t* getScopeName(HKEY scope)
{
if (scope == HKEY_LOCAL_MACHINE)
{
return L"HKLM";
}
else if (scope == HKEY_CURRENT_USER)
{
return L"HKCU";
}
else if (scope == HKEY_CLASSES_ROOT)
{
return L"HKCR";
}
else
{
return L"HK??";
}
}
}
struct ValueChange
{
using value_t = std::variant<DWORD, std::wstring>;

View File

@@ -1,248 +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.
// <summary>
// Encrypt/decrypt implementation.
// </summary>
// <history>
// 2008 created by Truong Do (ductdo).
// 2009-... modified by Truong Do (TruongDo).
// 2023- Included in PowerToys.
// </history>
using System;
using System.Collections.Concurrent;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Threading.Tasks;
using MouseWithoutBorders.Core;
namespace MouseWithoutBorders
{
internal partial class Common
{
#pragma warning disable SYSLIB0021
private static AesCryptoServiceProvider symAl;
#pragma warning restore SYSLIB0021
#pragma warning disable SA1307 // Accessible fields should begin with upper-case letter
internal static string myKey;
#pragma warning restore SA1307
private static uint magicNumber;
private static Random ran = new(); // Used for non encryption related functionality.
internal const int SymAlBlockSize = 16;
/// <summary>
/// This is used for the first encryption block, the following blocks will be combined with the cipher text of the previous block.
/// Thus identical blocks in the socket stream would be encrypted to different cipher text blocks.
/// The first block is a handshake one containing random data.
/// Related Unit Test: TestEncryptDecrypt
/// </summary>
internal static readonly string InitialIV = ulong.MaxValue.ToString(CultureInfo.InvariantCulture);
internal static Random Ran
{
get => Common.ran ??= new Random();
set => Common.ran = value;
}
internal static uint MagicNumber
{
get => Common.magicNumber;
set => Common.magicNumber = value;
}
internal static string MyKey
{
get => Common.myKey;
set
{
if (Common.myKey != value)
{
Common.myKey = value;
_ = Task.Factory.StartNew(
() => Common.GenLegalKey(),
System.Threading.CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.Default); // Cache the key to improve UX.
}
}
}
internal static string KeyDisplayedText(string key)
{
string displayedValue = string.Empty;
int i = 0;
do
{
int length = Math.Min(4, key.Length - i);
displayedValue += string.Concat(key.AsSpan(i, length), " ");
i += 4;
}
while (i < key.Length - 1);
return displayedValue.Trim();
}
internal static bool GeneratedKey { get; set; }
internal static bool KeyCorrupted { get; set; }
internal static void InitEncryption()
{
try
{
if (symAl == null)
{
#pragma warning disable SYSLIB0021 // No proper replacement for now
symAl = new AesCryptoServiceProvider();
#pragma warning restore SYSLIB0021
symAl.KeySize = 256;
symAl.BlockSize = SymAlBlockSize * 8;
symAl.Padding = PaddingMode.Zeros;
symAl.Mode = CipherMode.CBC;
symAl.GenerateIV();
}
}
catch (Exception e)
{
Logger.Log(e);
}
}
private static readonly ConcurrentDictionary<string, byte[]> LegalKeyDictionary = new(StringComparer.OrdinalIgnoreCase);
internal static byte[] GenLegalKey()
{
byte[] rv;
string myKey = Common.MyKey;
if (!LegalKeyDictionary.TryGetValue(myKey, out byte[] value))
{
Rfc2898DeriveBytes key = new(
myKey,
Common.GetBytesU(InitialIV),
50000,
HashAlgorithmName.SHA512);
rv = key.GetBytes(32);
_ = LegalKeyDictionary.AddOrUpdate(myKey, rv, (k, v) => rv);
}
else
{
rv = value;
}
return rv;
}
private static byte[] GenLegalIV()
{
string st = InitialIV;
int ivLength = symAl.IV.Length;
if (st.Length > ivLength)
{
st = st[..ivLength];
}
else if (st.Length < ivLength)
{
st = st.PadRight(ivLength, ' ');
}
return GetBytes(st);
}
internal static Stream GetEncryptedStream(Stream encryptedStream)
{
ICryptoTransform encryptor;
encryptor = symAl.CreateEncryptor(GenLegalKey(), GenLegalIV());
return new CryptoStream(encryptedStream, encryptor, CryptoStreamMode.Write);
}
internal static Stream GetDecryptedStream(Stream encryptedStream)
{
ICryptoTransform decryptor;
decryptor = symAl.CreateDecryptor(GenLegalKey(), GenLegalIV());
return new CryptoStream(encryptedStream, decryptor, CryptoStreamMode.Read);
}
internal static uint Get24BitHash(string st)
{
if (string.IsNullOrEmpty(st))
{
return 0;
}
byte[] bytes = new byte[PACKAGE_SIZE];
for (int i = 0; i < PACKAGE_SIZE; i++)
{
if (i < st.Length)
{
bytes[i] = (byte)st[i];
}
}
var hash = SHA512.Create();
byte[] hashValue = hash.ComputeHash(bytes);
for (int i = 0; i < 50000; i++)
{
hashValue = hash.ComputeHash(hashValue);
}
Logger.LogDebug(string.Format(CultureInfo.CurrentCulture, "magic: {0},{1},{2}", hashValue[0], hashValue[1], hashValue[^1]));
hash.Clear();
return (uint)((hashValue[0] << 23) + (hashValue[1] << 16) + (hashValue[^1] << 8) + hashValue[2]);
}
internal static string GetDebugInfo(string st)
{
return string.IsNullOrEmpty(st) ? st : ((byte)(Common.GetBytesU(st).Sum(value => value) % 256)).ToString(CultureInfo.InvariantCulture);
}
internal static string CreateDefaultKey()
{
return CreateRandomKey();
}
private const int PW_LENGTH = 16;
public static string CreateRandomKey()
{
// Not including characters like "'`O0& since they are confusing to users.
string[] chars = new[] { "abcdefghjkmnpqrstuvxyz", "ABCDEFGHJKMNPQRSTUVXYZ", "123456789", "~!@#$%^*()_-+=:;<,>.?/\\|[]" };
char[][] charactersUsedForKey = chars.Select(charset => Enumerable.Range(0, charset.Length - 1).Select(i => charset[i]).ToArray()).ToArray();
byte[] randomData = new byte[1];
string key = string.Empty;
do
{
foreach (string set in chars)
{
randomData = RandomNumberGenerator.GetBytes(1);
key += set[randomData[0] % set.Length];
if (key.Length >= PW_LENGTH)
{
break;
}
}
}
while (key.Length < PW_LENGTH);
return key;
}
internal static bool IsKeyValid(string key, out string error)
{
error = string.IsNullOrEmpty(key) || key.Length < 16
? "Key must have at least 16 characters in length (spaces are discarded). Key must be auto generated in one of the machines."
: null;
return error == null;
}
}
}

View File

@@ -1,262 +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.
// <summary>
// Package format/conversion.
// </summary>
// <history>
// 2008 created by Truong Do (ductdo).
// 2009-... modified by Truong Do (TruongDo).
// 2023- Included in PowerToys.
// </history>
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
// In X64, we are WOW
[module: SuppressMessage("Microsoft.Portability", "CA1900:ValueTypeFieldsShouldBePortable", Scope = "type", Target = "MouseWithoutBorders.DATA", Justification = "Dotnet port with style preservation")]
namespace MouseWithoutBorders
{
internal enum PackageType// : int
{
// Search for PACKAGE_TYPE_RELATED before changing these!
Invalid = 0xFF,
Error = 0xFE,
Hi = 2,
Hello = 3,
ByeBye = 4,
Heartbeat = 20,
Awake = 21,
HideMouse = 50,
Heartbeat_ex = 51,
Heartbeat_ex_l2 = 52,
Heartbeat_ex_l3 = 53,
Clipboard = 69,
ClipboardDragDrop = 70,
ClipboardDragDropEnd = 71,
ExplorerDragDrop = 72,
ClipboardCapture = 73,
CaptureScreenCommand = 74,
ClipboardDragDropOperation = 75,
ClipboardDataEnd = 76,
MachineSwitched = 77,
ClipboardAsk = 78,
ClipboardPush = 79,
NextMachine = 121,
Keyboard = 122,
Mouse = 123,
ClipboardText = 124,
ClipboardImage = 125,
Handshake = 126,
HandshakeAck = 127,
Matrix = 128,
MatrixSwapFlag = 2,
MatrixTwoRowFlag = 4,
}
internal struct PackageMonitor
{
internal ulong Keyboard;
internal ulong Mouse;
internal ulong Heartbeat;
internal ulong ByeBye;
internal ulong Hello;
internal ulong Matrix;
internal ulong ClipboardText;
internal ulong ClipboardImage;
internal ulong Clipboard;
internal ulong ClipboardDragDrop;
internal ulong ClipboardDragDropEnd;
internal ulong ClipboardAsk;
internal ulong ExplorerDragDrop;
internal ulong Nil;
internal PackageMonitor(ulong value)
{
ClipboardDragDrop = ClipboardDragDropEnd = ExplorerDragDrop =
Keyboard = Mouse = Heartbeat = ByeBye = Hello = Clipboard =
Matrix = ClipboardImage = ClipboardText = Nil = ClipboardAsk = value;
}
}
internal enum ID : uint
{
NONE = 0,
ALL = 255,
}
internal enum ClipboardPostAction : uint
{
Other = 0,
Desktop = 1,
Mspaint = 2,
}
[StructLayout(LayoutKind.Sequential)]
internal struct KEYBDDATA
{
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Same name as in winAPI")]
internal int wVk;
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Same name as in winAPI")]
internal int dwFlags;
}
[StructLayout(LayoutKind.Sequential)]
internal struct MOUSEDATA
{
internal int X;
internal int Y;
internal int WheelDelta;
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Same name as in winAPI")]
internal int dwFlags;
}
// The beauty of "union" in C#
[StructLayout(LayoutKind.Explicit)]
internal class DATA
{
[FieldOffset(0)]
internal PackageType Type; // 4 (first byte = package type, 1 = checksum, 2+3 = magic no.)
[FieldOffset(sizeof(PackageType))]
internal int Id; // 4
[FieldOffset(sizeof(PackageType) + sizeof(uint))]
internal ID Src; // 4
[FieldOffset(sizeof(PackageType) + (2 * sizeof(uint)))]
internal ID Des; // 4
[FieldOffset(sizeof(PackageType) + (3 * sizeof(uint)))]
internal long DateTime;
[FieldOffset(sizeof(PackageType) + (3 * sizeof(uint)) + sizeof(long))]
internal KEYBDDATA Kd;
[FieldOffset(sizeof(PackageType) + (3 * sizeof(uint)))]
internal MOUSEDATA Md;
[FieldOffset(sizeof(PackageType) + (3 * sizeof(uint)))]
internal ID Machine1;
[FieldOffset(sizeof(PackageType) + (4 * sizeof(uint)))]
internal ID Machine2;
[FieldOffset(sizeof(PackageType) + (5 * sizeof(uint)))]
internal ID Machine3;
[FieldOffset(sizeof(PackageType) + (6 * sizeof(uint)))]
internal ID Machine4;
[FieldOffset(sizeof(PackageType) + (3 * sizeof(uint)))]
internal ClipboardPostAction PostAction;
[FieldOffset(sizeof(PackageType) + (7 * sizeof(uint)))]
private long machineNameP1;
[FieldOffset(sizeof(PackageType) + (7 * sizeof(uint)) + sizeof(long))]
private long machineNameP2;
[FieldOffset(sizeof(PackageType) + (7 * sizeof(uint)) + (2 * sizeof(long)))]
private long machineNameP3;
[FieldOffset(sizeof(PackageType) + (7 * sizeof(uint)) + (3 * sizeof(long)))]
private long machineNameP4;
internal string MachineName
{
get
{
string name = Common.GetString(BitConverter.GetBytes(machineNameP1))
+ Common.GetString(BitConverter.GetBytes(machineNameP2))
+ Common.GetString(BitConverter.GetBytes(machineNameP3))
+ Common.GetString(BitConverter.GetBytes(machineNameP4));
return name.Trim();
}
set
{
byte[] machineName = Common.GetBytes(value.PadRight(32, ' '));
machineNameP1 = BitConverter.ToInt64(machineName, 0);
machineNameP2 = BitConverter.ToInt64(machineName, 8);
machineNameP3 = BitConverter.ToInt64(machineName, 16);
machineNameP4 = BitConverter.ToInt64(machineName, 24);
}
}
public DATA()
{
}
public DATA(byte[] initialData)
{
Bytes = initialData;
}
internal byte[] Bytes
{
get
{
byte[] buf = new byte[IsBigPackage ? Common.PACKAGE_SIZE_EX : Common.PACKAGE_SIZE];
Array.Copy(StructToBytes(this), buf, IsBigPackage ? Common.PACKAGE_SIZE_EX : Common.PACKAGE_SIZE);
return buf;
}
set
{
Debug.Assert(value.Length <= Common.PACKAGE_SIZE_EX, "Length > package size");
byte[] buf = new byte[Common.PACKAGE_SIZE_EX];
Array.Copy(value, buf, value.Length);
BytesToStruct(buf, this);
}
}
internal bool IsBigPackage
{
get => Type == 0
? throw new InvalidOperationException("Package type not set.")
: Type switch
{
PackageType.Hello or PackageType.Awake or PackageType.Heartbeat or PackageType.Heartbeat_ex or PackageType.Handshake or PackageType.HandshakeAck or PackageType.ClipboardPush or PackageType.Clipboard or PackageType.ClipboardAsk or PackageType.ClipboardImage or PackageType.ClipboardText or PackageType.ClipboardDataEnd => true,
_ => (Type & PackageType.Matrix) == PackageType.Matrix,
};
}
private byte[] StructToBytes(object structObject)
{
byte[] bytes = new byte[Common.PACKAGE_SIZE_EX];
GCHandle bHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
Marshal.StructureToPtr(structObject, Marshal.UnsafeAddrOfPinnedArrayElement(bytes, 0), false);
bHandle.Free();
return bytes;
}
private void BytesToStruct(byte[] value, object structObject)
{
GCHandle bHandle = GCHandle.Alloc(value, GCHandleType.Pinned);
Marshal.PtrToStructure(Marshal.UnsafeAddrOfPinnedArrayElement(value, 0), structObject);
bHandle.Free();
}
}
internal partial class Common
{
internal const byte PACKAGE_SIZE = 32;
internal const byte PACKAGE_SIZE_EX = 64;
internal const byte WP_PACKAGE_SIZE = 6;
internal static PackageMonitor PackageSent;
internal static PackageMonitor PackageReceived;
internal static int PackageID;
}
}

View File

@@ -1,33 +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 ManagedCommon;
using Microsoft.PowerToys.Telemetry;
using MouseWithoutBorders.Class;
using Logger = MouseWithoutBorders.Core.Logger;
namespace MouseWithoutBorders
{
internal class ShutdownWithPowerToys
{
public static void WaitForPowerToysRunner(ETWTrace etwTrace)
{
try
{
RunnerHelper.WaitForPowerToysRunnerExitFallback(() =>
{
etwTrace?.Dispose();
Common.MainForm.Quit(true, false);
});
}
catch (Exception e)
{
Logger.Log(e);
}
}
}
}

View File

@@ -1,131 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
// <summary>
// Virtual key constants.
// </summary>
// <history>
// 2008 created by Truong Do (ductdo).
// 2009-... modified by Truong Do (TruongDo).
// 2023- Included in PowerToys.
// </history>
using System;
namespace MouseWithoutBorders
{
internal enum VK : ushort
{
CAPITAL = 0x14,
NUMLOCK = 0x90,
SHIFT = 0x10,
CONTROL = 0x11,
MENU = 0x12,
ESCAPE = 0x1B,
BACK = 0x08,
TAB = 0x09,
RETURN = 0x0D,
PRIOR = 0x21,
NEXT = 0x22,
END = 0x23,
HOME = 0x24,
LEFT = 0x25,
UP = 0x26,
RIGHT = 0x27,
DOWN = 0x28,
SELECT = 0x29,
PRINT = 0x2A,
EXECUTE = 0x2B,
SNAPSHOT = 0x2C,
INSERT = 0x2D,
DELETE = 0x2E,
HELP = 0x2F,
NUMPAD0 = 0x60,
NUMPAD1 = 0x61,
NUMPAD2 = 0x62,
NUMPAD3 = 0x63,
NUMPAD4 = 0x64,
NUMPAD5 = 0x65,
NUMPAD6 = 0x66,
NUMPAD7 = 0x67,
NUMPAD8 = 0x68,
NUMPAD9 = 0x69,
MULTIPLY = 0x6A,
ADD = 0x6B,
SEPARATOR = 0x6C,
SUBTRACT = 0x6D,
DECIMAL = 0x6E,
DIVIDE = 0x6F,
F1 = 0x70,
F2 = 0x71,
F3 = 0x72,
F4 = 0x73,
F5 = 0x74,
F6 = 0x75,
F7 = 0x76,
F8 = 0x77,
F9 = 0x78,
F10 = 0x79,
F11 = 0x7A,
F12 = 0x7B,
OEM_1 = 0xBA,
OEM_PLUS = 0xBB,
OEM_COMMA = 0xBC,
OEM_MINUS = 0xBD,
OEM_PERIOD = 0xBE,
OEM_2 = 0xBF,
OEM_3 = 0xC0,
MEDIA_NEXT_TRACK = 0xB0,
MEDIA_PREV_TRACK = 0xB1,
MEDIA_STOP = 0xB2,
MEDIA_PLAY_PAUSE = 0xB3,
LWIN = 0x5B,
RWIN = 0x5C,
LSHIFT = 0xA0,
RSHIFT = 0xA1,
LCONTROL = 0xA2,
RCONTROL = 0xA3,
LMENU = 0xA4,
RMENU = 0xA5,
}
internal partial class Common
{
internal const ushort KEYEVENTF_KEYDOWN = 0x0001;
internal const ushort KEYEVENTF_KEYUP = 0x0002;
internal const int WH_MOUSE = 7;
internal const int WH_KEYBOARD = 2;
internal const int WH_MOUSE_LL = 14;
internal const int WH_KEYBOARD_LL = 13;
internal const int WM_MOUSEMOVE = 0x200;
internal const int WM_LBUTTONDOWN = 0x201;
internal const int WM_RBUTTONDOWN = 0x204;
internal const int WM_MBUTTONDOWN = 0x207;
internal const int WM_XBUTTONDOWN = 0x20B;
internal const int WM_LBUTTONUP = 0x202;
internal const int WM_RBUTTONUP = 0x205;
internal const int WM_MBUTTONUP = 0x208;
internal const int WM_XBUTTONUP = 0x20C;
internal const int WM_LBUTTONDBLCLK = 0x203;
internal const int WM_RBUTTONDBLCLK = 0x206;
internal const int WM_MBUTTONDBLCLK = 0x209;
internal const int WM_MOUSEWHEEL = 0x020A;
internal const int WM_MOUSEHWHEEL = 0x020E;
internal const int WM_KEYDOWN = 0x100;
internal const int WM_KEYUP = 0x101;
internal const int WM_SYSKEYDOWN = 0x104;
internal const int WM_SYSKEYUP = 0x105;
[Flags]
internal enum LLKHF
{
EXTENDED = 0x01,
INJECTED = 0x10,
ALTDOWN = 0x20,
UP = 0x80,
}
}
}

View File

@@ -1,363 +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.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
// <summary>
// Screen/Desktop helper functions.
// </summary>
// <history>
// 2008 created by Truong Do (ductdo).
// 2009-... modified by Truong Do (TruongDo).
// 2023- Included in PowerToys.
// </history>
using MouseWithoutBorders.Class;
using MouseWithoutBorders.Core;
using Thread = MouseWithoutBorders.Core.Thread;
namespace MouseWithoutBorders
{
// Desktops, and GetScreenConfig routines
internal partial class Common
{
private static MyRectangle newDesktopBounds;
private static MyRectangle newPrimaryScreenBounds;
private static string activeDesktop;
internal static string ActiveDesktop => Common.activeDesktop;
internal static void SystemEvents_DisplaySettingsChanged(object sender, EventArgs e)
{
GetScreenConfig();
}
internal static readonly List<Point> SensitivePoints = new();
private static bool MonitorEnumProc(IntPtr hMonitor, IntPtr hdcMonitor, ref NativeMethods.RECT lprcMonitor, IntPtr dwData)
{
// lprcMonitor is wrong!!! => using GetMonitorInfo(...)
// Log(String.Format( CultureInfo.CurrentCulture,"MONITOR: l{0}, t{1}, r{2}, b{3}", lprcMonitor.Left, lprcMonitor.Top, lprcMonitor.Right, lprcMonitor.Bottom));
NativeMethods.MonitorInfoEx mi = default;
mi.cbSize = Marshal.SizeOf(mi);
_ = NativeMethods.GetMonitorInfo(hMonitor, ref mi);
try
{
// For logging only
_ = NativeMethods.GetDpiForMonitor(hMonitor, 0, out uint dpiX, out uint dpiY);
Logger.Log(string.Format(CultureInfo.CurrentCulture, "MONITOR: ({0}, {1}, {2}, {3}). DPI: ({4}, {5})", mi.rcMonitor.Left, mi.rcMonitor.Top, mi.rcMonitor.Right, mi.rcMonitor.Bottom, dpiX, dpiY));
}
catch (DllNotFoundException)
{
Logger.Log("GetDpiForMonitor is unsupported in Windows 7 and lower.");
}
catch (EntryPointNotFoundException)
{
Logger.Log("GetDpiForMonitor is unsupported in Windows 7 and lower.");
}
catch (Exception e)
{
Logger.Log(e);
}
if (mi.rcMonitor.Left == 0 && mi.rcMonitor.Top == 0 && mi.rcMonitor.Right != 0 && mi.rcMonitor.Bottom != 0)
{
// Primary screen
_ = Interlocked.Exchange(ref screenWidth, mi.rcMonitor.Right - mi.rcMonitor.Left);
_ = Interlocked.Exchange(ref screenHeight, mi.rcMonitor.Bottom - mi.rcMonitor.Top);
newPrimaryScreenBounds.Left = mi.rcMonitor.Left;
newPrimaryScreenBounds.Top = mi.rcMonitor.Top;
newPrimaryScreenBounds.Right = mi.rcMonitor.Right;
newPrimaryScreenBounds.Bottom = mi.rcMonitor.Bottom;
}
else
{
if (mi.rcMonitor.Left < newDesktopBounds.Left)
{
newDesktopBounds.Left = mi.rcMonitor.Left;
}
if (mi.rcMonitor.Top < newDesktopBounds.Top)
{
newDesktopBounds.Top = mi.rcMonitor.Top;
}
if (mi.rcMonitor.Right > newDesktopBounds.Right)
{
newDesktopBounds.Right = mi.rcMonitor.Right;
}
if (mi.rcMonitor.Bottom > newDesktopBounds.Bottom)
{
newDesktopBounds.Bottom = mi.rcMonitor.Bottom;
}
}
lock (SensitivePoints)
{
SensitivePoints.Add(new Point(mi.rcMonitor.Left, mi.rcMonitor.Top));
SensitivePoints.Add(new Point(mi.rcMonitor.Right, mi.rcMonitor.Top));
SensitivePoints.Add(new Point(mi.rcMonitor.Right, mi.rcMonitor.Bottom));
SensitivePoints.Add(new Point(mi.rcMonitor.Left, mi.rcMonitor.Bottom));
}
return true;
}
internal static void GetScreenConfig()
{
try
{
Logger.LogDebug("==================== GetScreenConfig started");
newDesktopBounds = new MyRectangle();
newPrimaryScreenBounds = new MyRectangle();
newDesktopBounds.Left = newPrimaryScreenBounds.Left = Screen.PrimaryScreen.Bounds.Left;
newDesktopBounds.Top = newPrimaryScreenBounds.Top = Screen.PrimaryScreen.Bounds.Top;
newDesktopBounds.Right = newPrimaryScreenBounds.Right = Screen.PrimaryScreen.Bounds.Right;
newDesktopBounds.Bottom = newPrimaryScreenBounds.Bottom = Screen.PrimaryScreen.Bounds.Bottom;
Logger.Log(string.Format(
CultureInfo.CurrentCulture,
"logon = {0} PrimaryScreenBounds = {1},{2},{3},{4} desktopBounds = {5},{6},{7},{8}",
Common.RunOnLogonDesktop,
Common.newPrimaryScreenBounds.Left,
Common.newPrimaryScreenBounds.Top,
Common.newPrimaryScreenBounds.Right,
Common.newPrimaryScreenBounds.Bottom,
Common.newDesktopBounds.Left,
Common.newDesktopBounds.Top,
Common.newDesktopBounds.Right,
Common.newDesktopBounds.Bottom));
#if USE_MANAGED_ROUTINES
// Managed routines do not work well when running on secure desktop:(
screenWidth = Screen.PrimaryScreen.Bounds.Width;
screenHeight = Screen.PrimaryScreen.Bounds.Height;
screenCount = Screen.AllScreens.Length;
for (int i = 0; i < Screen.AllScreens.Length; i++)
{
if (Screen.AllScreens[i].Bounds.Left < desktopBounds.Left) desktopBounds.Left = Screen.AllScreens[i].Bounds.Left;
if (Screen.AllScreens[i].Bounds.Top < desktopBounds.Top) desktopBounds.Top = Screen.AllScreens[i].Bounds.Top;
if (Screen.AllScreens[i].Bounds.Right > desktopBounds.Right) desktopBounds.Right = Screen.AllScreens[i].Bounds.Right;
if (Screen.AllScreens[i].Bounds.Bottom > desktopBounds.Bottom) desktopBounds.Bottom = Screen.AllScreens[i].Bounds.Bottom;
}
#else
lock (SensitivePoints)
{
SensitivePoints.Clear();
}
NativeMethods.EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, MonitorEnumProc, IntPtr.Zero);
// 1000 calls to EnumDisplayMonitors cost a dozen of milliseconds
#endif
Interlocked.Exchange(ref MachineStuff.desktopBounds, newDesktopBounds);
Interlocked.Exchange(ref MachineStuff.primaryScreenBounds, newPrimaryScreenBounds);
Logger.Log(string.Format(
CultureInfo.CurrentCulture,
"logon = {0} PrimaryScreenBounds = {1},{2},{3},{4} desktopBounds = {5},{6},{7},{8}",
Common.RunOnLogonDesktop,
MachineStuff.PrimaryScreenBounds.Left,
MachineStuff.PrimaryScreenBounds.Top,
MachineStuff.PrimaryScreenBounds.Right,
MachineStuff.PrimaryScreenBounds.Bottom,
MachineStuff.DesktopBounds.Left,
MachineStuff.DesktopBounds.Top,
MachineStuff.DesktopBounds.Right,
MachineStuff.DesktopBounds.Bottom));
Logger.Log("==================== GetScreenConfig ended");
}
catch (Exception e)
{
Logger.Log(e);
}
}
#if USING_SCREEN_SAVER_ROUTINES
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int PostMessage(IntPtr hWnd, int wMsg, int wParam, int lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr OpenDesktop(string hDesktop, int Flags, bool Inherit, UInt32 DesiredAccess);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern bool CloseDesktop(IntPtr hDesktop);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern bool EnumDesktopWindows( IntPtr hDesktop, EnumDesktopWindowsProc callback, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern bool IsWindowVisible(IntPtr hWnd);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern bool SystemParametersInfo(int uAction, int uParam, ref int pvParam, int flags);
private delegate bool EnumDesktopWindowsProc(IntPtr hDesktop, IntPtr lParam);
private const int WM_CLOSE = 16;
private const int SPI_GETSCREENSAVERRUNNING = 114;
internal static bool IsScreenSaverRunning()
{
int isRunning = 0;
SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0,ref isRunning, 0);
return (isRunning != 0);
}
internal static void CloseScreenSaver()
{
IntPtr hDesktop = OpenDesktop("Screen-saver", 0, false, DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS);
if (hDesktop != IntPtr.Zero)
{
LogDebug("Closing screen saver...");
EnumDesktopWindows(hDesktop, new EnumDesktopWindowsProc(CloseScreenSaverFunc), IntPtr.Zero);
CloseDesktop(hDesktop);
}
}
private static bool CloseScreenSaverFunc(IntPtr hWnd, IntPtr lParam)
{
if (IsWindowVisible(hWnd))
{
LogDebug("Posting WM_CLOSE to " + hWnd.ToString(CultureInfo.InvariantCulture));
PostMessage(hWnd, WM_CLOSE, 0, 0);
}
return true;
}
#endif
internal static string GetMyDesktop()
{
byte[] arThreadDesktop = new byte[256];
IntPtr hD = NativeMethods.GetThreadDesktop(NativeMethods.GetCurrentThreadId());
if (hD != IntPtr.Zero)
{
_ = NativeMethods.GetUserObjectInformation(hD, NativeMethods.UOI_NAME, arThreadDesktop, arThreadDesktop.Length, out _);
return GetString(arThreadDesktop).Replace("\0", string.Empty);
}
return string.Empty;
}
internal static string GetInputDesktop()
{
byte[] arInputDesktop = new byte[256];
IntPtr hD = NativeMethods.OpenInputDesktop(0, false, NativeMethods.DESKTOP_READOBJECTS);
if (hD != IntPtr.Zero)
{
_ = NativeMethods.GetUserObjectInformation(hD, NativeMethods.UOI_NAME, arInputDesktop, arInputDesktop.Length, out _);
return GetString(arInputDesktop).Replace("\0", string.Empty);
}
return string.Empty;
}
internal static void StartMMService(string desktopToRunMouseWithoutBordersOn)
{
if (!Common.RunWithNoAdminRight)
{
Logger.LogDebug("*** Starting on active Desktop: " + desktopToRunMouseWithoutBordersOn);
Service.StartMouseWithoutBordersService(desktopToRunMouseWithoutBordersOn);
}
}
internal static void CheckForDesktopSwitchEvent(bool cleanupIfExit)
{
try
{
if (!IsMyDesktopActive() || Common.CurrentProcess.SessionId != NativeMethods.WTSGetActiveConsoleSessionId())
{
Helper.RunDDHelper(true);
int waitCount = 20;
while (NativeMethods.WTSGetActiveConsoleSessionId() == 0xFFFFFFFF && waitCount > 0)
{
waitCount--;
Logger.LogDebug("The session is detached/attached.");
Thread.Sleep(500);
}
string myDesktop = GetMyDesktop();
activeDesktop = GetInputDesktop();
Logger.LogDebug("*** Active Desktop = " + activeDesktop);
Logger.LogDebug("*** My Desktop = " + myDesktop);
if (myDesktop.Equals(activeDesktop, StringComparison.OrdinalIgnoreCase))
{
Logger.LogDebug("*** Active Desktop == My Desktop (TS session)");
}
if (!activeDesktop.Equals("winlogon", StringComparison.OrdinalIgnoreCase) &&
!activeDesktop.Equals("default", StringComparison.OrdinalIgnoreCase) &&
!activeDesktop.Equals("disconnect", StringComparison.OrdinalIgnoreCase))
{
try
{
StartMMService(activeDesktop);
}
catch (Exception e)
{
Logger.Log($"{nameof(CheckForDesktopSwitchEvent)}: {e}");
}
}
else
{
if (!myDesktop.Equals(activeDesktop, StringComparison.OrdinalIgnoreCase))
{
Logger.Log("*** Active Desktop <> My Desktop");
}
uint sid = NativeMethods.WTSGetActiveConsoleSessionId();
if (Process.GetProcessesByName(Common.BinaryName).Any(p => (uint)p.SessionId == sid))
{
Logger.Log("Found MouseWithoutBorders on the active session!");
}
else
{
Logger.Log("MouseWithoutBorders not found on the active session!");
StartMMService(null);
}
}
if (!myDesktop.Equals("winlogon", StringComparison.OrdinalIgnoreCase) &&
!myDesktop.Equals("default", StringComparison.OrdinalIgnoreCase))
{
Logger.LogDebug("*** Desktop inactive, exiting: " + myDesktop);
Setting.Values.LastX = JUST_GOT_BACK_FROM_SCREEN_SAVER;
if (cleanupIfExit)
{
InitAndCleanup.Cleanup();
}
Process.GetCurrentProcess().KillProcess();
}
}
}
catch (Exception e)
{
Logger.Log(e);
}
}
private static Point p;
internal static bool IsMyDesktopActive()
{
return NativeMethods.GetCursorPos(ref p);
}
}
}

View File

@@ -315,7 +315,7 @@ namespace MouseWithoutBorders
if (!acquireMutex)
{
Process[] ps = Process.GetProcessesByName(Common.BinaryName);
Logger.TelemetryLogTrace($"Balance: {socketMutexBalance}, Active: {IsMyDesktopActive()}, Sid/Console: {Process.GetCurrentProcess().SessionId}/{NativeMethods.WTSGetActiveConsoleSessionId()}, Desktop/Input: {GetMyDesktop()}/{GetInputDesktop()}, count: {ps?.Length}.", SeverityLevel.Warning);
Logger.TelemetryLogTrace($"Balance: {socketMutexBalance}, Active: {WinAPI.IsMyDesktopActive()}, Sid/Console: {Process.GetCurrentProcess().SessionId}/{NativeMethods.WTSGetActiveConsoleSessionId()}, Desktop/Input: {WinAPI.GetMyDesktop()}/{WinAPI.GetInputDesktop()}, count: {ps?.Length}.", SeverityLevel.Warning);
}
Logger.LogDebug("SOCKET MUTEX ENDED.");
@@ -358,7 +358,7 @@ namespace MouseWithoutBorders
Logger.TelemetryLogTrace($"[{actionName}] took more than {(long)timeout.TotalSeconds}, restarting the process.", SeverityLevel.Warning, true);
string desktop = Common.GetMyDesktop();
string desktop = WinAPI.GetMyDesktop();
MachineStuff.oneInstanceCheck?.Close();
_ = Process.Start(Application.ExecutablePath, desktop);
Logger.LogDebug($"Started on desktop {desktop}");
@@ -514,7 +514,7 @@ namespace MouseWithoutBorders
internal static void SendHeartBeat(bool initial = false)
{
SendPackage(ID.ALL, initial && Common.GeneratedKey ? PackageType.Heartbeat_ex : PackageType.Heartbeat);
SendPackage(ID.ALL, initial && Encryption.GeneratedKey ? PackageType.Heartbeat_ex : PackageType.Heartbeat);
}
private static long lastSendNextMachine;
@@ -550,7 +550,7 @@ namespace MouseWithoutBorders
internal static void SendAwakeBeat()
{
if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop && Common.IsMyDesktopActive() &&
if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop && WinAPI.IsMyDesktopActive() &&
Setting.Values.BlockScreenSaver && lastRealInputEventCount != Event.RealInputEventCount)
{
SendPackage(ID.ALL, PackageType.Awake);
@@ -568,7 +568,7 @@ namespace MouseWithoutBorders
{
if (lastInputEventCount == Event.InputEventCount)
{
if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop && Common.IsMyDesktopActive())
if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop && WinAPI.IsMyDesktopActive())
{
PokeMyself();
}
@@ -577,13 +577,13 @@ namespace MouseWithoutBorders
lastInputEventCount = Event.InputEventCount;
}
private static void PokeMyself()
internal static void PokeMyself()
{
int x, y = 0;
for (int i = 0; i < 10; i++)
{
x = Ran.Next(-9, 10);
x = Encryption.Ran.Next(-9, 10);
InputSimulation.MoveMouseRelative(x, y);
Thread.Sleep(50);
InputSimulation.MoveMouseRelative(-x, -y);
@@ -677,7 +677,7 @@ namespace MouseWithoutBorders
{
Common.MMSleep(0.2);
InputSimulation.SendKey(new KEYBDDATA() { wVk = (int)VK.SNAPSHOT });
InputSimulation.SendKey(new KEYBDDATA() { dwFlags = (int)Common.LLKHF.UP, wVk = (int)VK.SNAPSHOT });
InputSimulation.SendKey(new KEYBDDATA() { dwFlags = (int)WM.LLKHF.UP, wVk = (int)VK.SNAPSHOT });
Logger.LogDebug("PrepareScreenCapture: SNAPSHOT simulated.");
@@ -710,7 +710,7 @@ namespace MouseWithoutBorders
"\"" + Environment.ExpandEnvironmentVariables(@"%SystemRoot%\System32\Mspaint.exe") +
"\"",
"\"" + file + "\"",
GetInputDesktop(),
WinAPI.GetInputDesktop(),
1);
// CreateNormalIntegrityProcess(Environment.ExpandEnvironmentVariables(@"%SystemRoot%\System32\Mspaint.exe") +
@@ -919,7 +919,7 @@ namespace MouseWithoutBorders
try
{
data.Id = Interlocked.Increment(ref PackageID);
data.Id = Interlocked.Increment(ref Package.PackageID);
bool updateClientSockets = false;
@@ -999,7 +999,7 @@ namespace MouseWithoutBorders
}
else
{
PackageSent.Nil++;
Package.PackageSent.Nil++;
}
}
@@ -1379,7 +1379,7 @@ namespace MouseWithoutBorders
if (string.IsNullOrEmpty(machine_Name))
{
machine_Name = "RANDOM" + Ran.Next().ToString(CultureInfo.CurrentCulture);
machine_Name = "RANDOM" + Encryption.Ran.Next().ToString(CultureInfo.CurrentCulture);
}
}
@@ -1533,13 +1533,13 @@ namespace MouseWithoutBorders
internal static void SendOrReceiveARandomDataBlockPerInitialIV(Stream st, bool send = true)
{
byte[] ranData = new byte[SymAlBlockSize];
byte[] ranData = new byte[Encryption.SymAlBlockSize];
try
{
if (send)
{
ranData = RandomNumberGenerator.GetBytes(SymAlBlockSize);
ranData = RandomNumberGenerator.GetBytes(Encryption.SymAlBlockSize);
st.Write(ranData, 0, ranData.Length);
}
else

View File

@@ -109,7 +109,7 @@ namespace MouseWithoutBorders.Class
// Install Mouse Hook
mouseHookProcedure = new NativeMethods.HookProc(MouseHookProc);
hMouseHook = NativeMethods.SetWindowsHookEx(
Common.WH_MOUSE_LL,
WM.WH_MOUSE_LL,
mouseHookProcedure,
Marshal.GetHINSTANCE(
Assembly.GetExecutingAssembly().GetModules()[0]),
@@ -126,7 +126,7 @@ namespace MouseWithoutBorders.Class
// Install Keyboard Hook
keyboardHookProcedure = new NativeMethods.HookProc(KeyboardHookProc);
hKeyboardHook = NativeMethods.SetWindowsHookEx(
Common.WH_KEYBOARD_LL,
WM.WH_KEYBOARD_LL,
keyboardHookProcedure,
Marshal.GetHINSTANCE(
Assembly.GetExecutingAssembly().GetModules()[0]),
@@ -233,7 +233,7 @@ namespace MouseWithoutBorders.Class
if (nCode >= 0 && MouseEvent != null)
{
if (wParam == Common.WM_LBUTTONUP && SkipMouseUpCount > 0)
if (wParam == WM.WM_LBUTTONUP && SkipMouseUpCount > 0)
{
Logger.LogDebug($"{nameof(SkipMouseUpCount)}: {SkipMouseUpCount}.");
SkipMouseUpCount--;
@@ -241,7 +241,7 @@ namespace MouseWithoutBorders.Class
return rv;
}
if ((wParam == Common.WM_LBUTTONUP || wParam == Common.WM_LBUTTONDOWN) && SkipMouseUpDown)
if ((wParam == WM.WM_LBUTTONUP || wParam == WM.WM_LBUTTONDOWN) && SkipMouseUpDown)
{
rv = NativeMethods.CallNextHookEx(hMouseHook, nCode, wParam, lParam);
return rv;
@@ -370,7 +370,7 @@ namespace MouseWithoutBorders.Class
private bool ProcessKeyEx(int vkCode, int flags, KEYBDDATA hookCallbackKeybdData)
{
if ((flags & (int)Common.LLKHF.UP) == (int)Common.LLKHF.UP)
if ((flags & (int)WM.LLKHF.UP) == (int)WM.LLKHF.UP)
{
EasyMouseKeyDown = false;
@@ -553,7 +553,7 @@ namespace MouseWithoutBorders.Class
KeyboardEvent(hookCallbackKeybdData);
}
hookCallbackKeybdData.dwFlags |= (int)Common.LLKHF.UP;
hookCallbackKeybdData.dwFlags |= (int)WM.LLKHF.UP;
foreach (var code in codes)
{

View File

@@ -112,12 +112,12 @@ namespace MouseWithoutBorders.Class
uint scanCode = 0;
// http://msdn.microsoft.com/en-us/library/ms644967(VS.85).aspx
if ((kd.dwFlags & (int)Common.LLKHF.UP) == (int)Common.LLKHF.UP)
if ((kd.dwFlags & (int)WM.LLKHF.UP) == (int)WM.LLKHF.UP)
{
dwFlags = NativeMethods.KEYEVENTF.KEYUP;
}
if ((kd.dwFlags & (int)Common.LLKHF.EXTENDED) == (int)Common.LLKHF.EXTENDED)
if ((kd.dwFlags & (int)WM.LLKHF.EXTENDED) == (int)WM.LLKHF.EXTENDED)
{
dwFlags |= NativeMethods.KEYEVENTF.EXTENDEDKEY;
}
@@ -173,44 +173,44 @@ namespace MouseWithoutBorders.Class
mouse_input.mi.dy = (int)dy;
mouse_input.mi.mouseData = md.WheelDelta;
if (md.dwFlags != Common.WM_MOUSEMOVE)
if (md.dwFlags != WM.WM_MOUSEMOVE)
{
Logger.LogDebug($"InputSimulation.SendMouse: x = {md.X}, y = {md.Y}, WheelDelta = {md.WheelDelta}, dwFlags = {md.dwFlags}.");
}
switch (md.dwFlags)
{
case Common.WM_MOUSEMOVE:
case WM.WM_MOUSEMOVE:
mouse_input.mi.dwFlags |= (int)(NativeMethods.MOUSEEVENTF.MOVE | NativeMethods.MOUSEEVENTF.ABSOLUTE);
break;
case Common.WM_LBUTTONDOWN:
case WM.WM_LBUTTONDOWN:
mouse_input.mi.dwFlags |= (int)NativeMethods.MOUSEEVENTF.LEFTDOWN;
break;
case Common.WM_LBUTTONUP:
case WM.WM_LBUTTONUP:
mouse_input.mi.dwFlags |= (int)NativeMethods.MOUSEEVENTF.LEFTUP;
break;
case Common.WM_RBUTTONDOWN:
case WM.WM_RBUTTONDOWN:
mouse_input.mi.dwFlags |= (int)NativeMethods.MOUSEEVENTF.RIGHTDOWN;
break;
case Common.WM_RBUTTONUP:
case WM.WM_RBUTTONUP:
mouse_input.mi.dwFlags |= (int)NativeMethods.MOUSEEVENTF.RIGHTUP;
break;
case Common.WM_MBUTTONDOWN:
case WM.WM_MBUTTONDOWN:
mouse_input.mi.dwFlags |= (int)NativeMethods.MOUSEEVENTF.MIDDLEDOWN;
break;
case Common.WM_MBUTTONUP:
case WM.WM_MBUTTONUP:
mouse_input.mi.dwFlags |= (int)NativeMethods.MOUSEEVENTF.MIDDLEUP;
break;
case Common.WM_MOUSEWHEEL:
case WM.WM_MOUSEWHEEL:
mouse_input.mi.dwFlags |= (int)NativeMethods.MOUSEEVENTF.WHEEL;
break;
case Common.WM_MOUSEHWHEEL:
case WM.WM_MOUSEHWHEEL:
mouse_input.mi.dwFlags |= (int)NativeMethods.MOUSEEVENTF.HWHEEL;
break;
case Common.WM_XBUTTONUP:
case WM.WM_XBUTTONUP:
mouse_input.mi.dwFlags |= (int)NativeMethods.MOUSEEVENTF.XUP;
break;
case Common.WM_XBUTTONDOWN:
case WM.WM_XBUTTONDOWN:
mouse_input.mi.dwFlags |= (int)NativeMethods.MOUSEEVENTF.XDOWN;
break;
@@ -373,7 +373,7 @@ namespace MouseWithoutBorders.Class
{
eatKey = false;
if ((flags & (int)Common.LLKHF.UP) == (int)Common.LLKHF.UP)
if ((flags & (int)WM.LLKHF.UP) == (int)WM.LLKHF.UP)
{
switch ((VK)vkCode)
{

View File

@@ -143,7 +143,7 @@ namespace MouseWithoutBorders.Class
return;
}
string myDesktop = Common.GetMyDesktop();
string myDesktop = WinAPI.GetMyDesktop();
if (firstArg.Equals("winlogon", StringComparison.OrdinalIgnoreCase))
{
@@ -305,8 +305,8 @@ namespace MouseWithoutBorders.Class
MachineStuff.ClearComputerMatrix();
Setting.Values.MyKey = securityKey;
Common.MyKey = securityKey;
Common.MagicNumber = Common.Get24BitHash(Common.MyKey);
Encryption.MyKey = securityKey;
Encryption.MagicNumber = Encryption.Get24BitHash(Encryption.MyKey);
MachineStuff.MachineMatrix = new string[MachineStuff.MAX_MACHINE] { pcName.Trim().ToUpper(CultureInfo.CurrentCulture), Common.MachineName.Trim(), string.Empty, string.Empty };
string[] machines = MachineStuff.MachineMatrix;
@@ -328,8 +328,8 @@ namespace MouseWithoutBorders.Class
Setting.Values.EasyMouse = (int)EasyMouseOption.Enable;
MachineStuff.ClearComputerMatrix();
Setting.Values.MyKey = Common.MyKey = Common.CreateRandomKey();
Common.GeneratedKey = true;
Setting.Values.MyKey = Encryption.MyKey = Encryption.CreateRandomKey();
Encryption.GeneratedKey = true;
Setting.Values.PauseInstantSaving = false;
Setting.Values.SaveSettings();

View File

@@ -109,9 +109,9 @@ namespace MouseWithoutBorders.Class
var shouldReopenSockets = false;
if (Common.MyKey != _properties.SecurityKey.Value)
if (Encryption.MyKey != _properties.SecurityKey.Value)
{
Common.MyKey = _properties.SecurityKey.Value;
Encryption.MyKey = _properties.SecurityKey.Value;
shouldReopenSockets = true;
}
@@ -489,7 +489,7 @@ namespace MouseWithoutBorders.Class
}
else
{
string randomKey = Common.CreateDefaultKey();
string randomKey = Encryption.CreateDefaultKey();
_properties.SecurityKey.Value = randomKey;
return randomKey;
@@ -1055,7 +1055,7 @@ namespace MouseWithoutBorders.Class
if (machineId == 0)
{
var newMachineId = Common.Ran.Next();
var newMachineId = Encryption.Ran.Next();
_properties.MachineID.Value = newMachineId;
machineId = newMachineId;
if (!PauseInstantSaving)

View File

@@ -101,7 +101,7 @@ namespace MouseWithoutBorders.Class
{
if (encryptedStream == null && BackingSocket.Connected)
{
encryptedStream = Common.GetEncryptedStream(new NetworkStream(BackingSocket));
encryptedStream = Encryption.GetEncryptedStream(new NetworkStream(BackingSocket));
Common.SendOrReceiveARandomDataBlockPerInitialIV(encryptedStream);
}
@@ -115,7 +115,7 @@ namespace MouseWithoutBorders.Class
{
if (decryptedStream == null && BackingSocket.Connected)
{
decryptedStream = Common.GetDecryptedStream(new NetworkStream(BackingSocket));
decryptedStream = Encryption.GetDecryptedStream(new NetworkStream(BackingSocket));
Common.SendOrReceiveARandomDataBlockPerInitialIV(decryptedStream, false);
}
@@ -181,7 +181,7 @@ namespace MouseWithoutBorders.Class
Logger.LogDebug("SocketStuff started.");
bASE_PORT = port;
Common.Ran = new Random();
Encryption.Ran = new Random();
Logger.LogDebug("Validating session...");
@@ -221,11 +221,11 @@ namespace MouseWithoutBorders.Class
if (Setting.Values.IsMyKeyRandom)
{
Setting.Values.MyKey = Common.MyKey;
Setting.Values.MyKey = Encryption.MyKey;
}
Common.MagicNumber = Common.Get24BitHash(Common.MyKey);
Common.PackageID = Setting.Values.PackageID;
Encryption.MagicNumber = Encryption.Get24BitHash(Encryption.MyKey);
Package.PackageID = Setting.Values.PackageID;
TcpPort = bASE_PORT;
@@ -242,7 +242,7 @@ namespace MouseWithoutBorders.Class
Logger.TelemetryLogTrace($"{nameof(SocketStuff)}: {e.Message}", SeverityLevel.Warning);
}
Common.GetScreenConfig();
WinAPI.GetScreenConfig();
if (firstRun && Common.RunOnScrSaverDesktop)
{
@@ -305,7 +305,7 @@ namespace MouseWithoutBorders.Class
sleepSecs = 10;
// It is reasonable to give a try on restarting MwB processes in other sessions.
if (restartCount++ < 5 && Common.IsMyDesktopActive() && !Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop)
if (restartCount++ < 5 && WinAPI.IsMyDesktopActive() && !Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop)
{
Logger.TelemetryLogTrace("Restarting the service dues to WSAEADDRINUSE.", SeverityLevel.Warning);
Program.StartService();
@@ -361,7 +361,7 @@ namespace MouseWithoutBorders.Class
{
Setting.Values.LastX = Common.LastX;
Setting.Values.LastY = Common.LastY;
Setting.Values.PackageID = Common.PackageID;
Setting.Values.PackageID = Package.PackageID;
// Common.Log("Saving IP: " + Setting.Values.DesMachineID.ToString(CultureInfo.CurrentCulture));
Setting.Values.DesMachineID = (uint)Common.DesMachineID;
@@ -505,10 +505,10 @@ namespace MouseWithoutBorders.Class
throw new ExpectedSocketException(log);
}
bytes[3] = (byte)((Common.MagicNumber >> 24) & 0xFF);
bytes[2] = (byte)((Common.MagicNumber >> 16) & 0xFF);
bytes[3] = (byte)((Encryption.MagicNumber >> 24) & 0xFF);
bytes[2] = (byte)((Encryption.MagicNumber >> 16) & 0xFF);
bytes[1] = 0;
for (int i = 2; i < Common.PACKAGE_SIZE; i++)
for (int i = 2; i < Package.PACKAGE_SIZE; i++)
{
bytes[1] = (byte)(bytes[1] + bytes[i]);
}
@@ -535,13 +535,13 @@ namespace MouseWithoutBorders.Class
magic = (buf[3] << 24) + (buf[2] << 16);
if (magic != (Common.MagicNumber & 0xFFFF0000))
if (magic != (Encryption.MagicNumber & 0xFFFF0000))
{
Logger.Log("Magic number invalid!");
buf[0] = (byte)PackageType.Invalid;
}
for (int i = 2; i < Common.PACKAGE_SIZE; i++)
for (int i = 2; i < Package.PACKAGE_SIZE; i++)
{
checksum = (byte)(checksum + buf[i]);
}
@@ -557,7 +557,7 @@ namespace MouseWithoutBorders.Class
internal static DATA TcpReceiveData(TcpSk tcp, out int bytesReceived)
{
byte[] buf = new byte[Common.PACKAGE_SIZE_EX];
byte[] buf = new byte[Package.PACKAGE_SIZE_EX];
Stream decryptedStream = tcp.DecryptedStream;
if (tcp.BackingSocket == null || !tcp.BackingSocket.Connected || decryptedStream == null)
@@ -571,9 +571,9 @@ namespace MouseWithoutBorders.Class
try
{
bytesReceived = decryptedStream.ReadEx(buf, 0, Common.PACKAGE_SIZE);
bytesReceived = decryptedStream.ReadEx(buf, 0, Package.PACKAGE_SIZE);
if (bytesReceived != Common.PACKAGE_SIZE)
if (bytesReceived != Package.PACKAGE_SIZE)
{
buf[0] = bytesReceived == 0 ? (byte)PackageType.Error : (byte)PackageType.Invalid;
}
@@ -586,9 +586,9 @@ namespace MouseWithoutBorders.Class
if (package.IsBigPackage)
{
bytesReceived = decryptedStream.ReadEx(buf, Common.PACKAGE_SIZE, Common.PACKAGE_SIZE);
bytesReceived = decryptedStream.ReadEx(buf, Package.PACKAGE_SIZE, Package.PACKAGE_SIZE);
if (bytesReceived != Common.PACKAGE_SIZE)
if (bytesReceived != Package.PACKAGE_SIZE)
{
buf[0] = bytesReceived == 0 ? (byte)PackageType.Error : (byte)PackageType.Invalid;
}
@@ -614,28 +614,28 @@ namespace MouseWithoutBorders.Class
switch (type)
{
case PackageType.Keyboard:
Common.PackageSent.Keyboard++;
Package.PackageSent.Keyboard++;
break;
case PackageType.Mouse:
Common.PackageSent.Mouse++;
Package.PackageSent.Mouse++;
break;
case PackageType.Heartbeat:
case PackageType.Heartbeat_ex:
Common.PackageSent.Heartbeat++;
Package.PackageSent.Heartbeat++;
break;
case PackageType.Hello:
Common.PackageSent.Hello++;
Package.PackageSent.Hello++;
break;
case PackageType.ByeBye:
Common.PackageSent.ByeBye++;
Package.PackageSent.ByeBye++;
break;
case PackageType.Matrix:
Common.PackageSent.Matrix++;
Package.PackageSent.Matrix++;
break;
default:
@@ -643,11 +643,11 @@ namespace MouseWithoutBorders.Class
switch (subtype)
{
case (byte)PackageType.ClipboardText:
Common.PackageSent.ClipboardText++;
Package.PackageSent.ClipboardText++;
break;
case (byte)PackageType.ClipboardImage:
Common.PackageSent.ClipboardImage++;
Package.PackageSent.ClipboardImage++;
break;
default:
@@ -1266,7 +1266,7 @@ namespace MouseWithoutBorders.Class
string strIP = string.Empty;
ID remoteID = ID.NONE;
byte[] buf = RandomNumberGenerator.GetBytes(Common.PACKAGE_SIZE_EX);
byte[] buf = RandomNumberGenerator.GetBytes(Package.PACKAGE_SIZE_EX);
d = new DATA(buf);
TcpSk currentTcp = tcp;
@@ -1280,8 +1280,8 @@ namespace MouseWithoutBorders.Class
try
{
currentSocket.SendBufferSize = Common.PACKAGE_SIZE * 10000;
currentSocket.ReceiveBufferSize = Common.PACKAGE_SIZE * 10000;
currentSocket.SendBufferSize = Package.PACKAGE_SIZE * 10000;
currentSocket.ReceiveBufferSize = Package.PACKAGE_SIZE * 10000;
currentSocket.NoDelay = true; // This is very interesting to know:(
currentSocket.SendTimeout = 500;
d.MachineName = Common.MachineName;
@@ -1829,7 +1829,7 @@ namespace MouseWithoutBorders.Class
}
while (rv > 0);
if ((rv = Common.PACKAGE_SIZE - (sentCount % Common.PACKAGE_SIZE)) > 0)
if ((rv = Package.PACKAGE_SIZE - (sentCount % Package.PACKAGE_SIZE)) > 0)
{
Array.Clear(buf, 0, buf.Length);
ecStream.Write(buf, 0, rv);
@@ -1900,7 +1900,7 @@ namespace MouseWithoutBorders.Class
}
while (rv > 0);
if ((rv = sentCount % Common.PACKAGE_SIZE) > 0)
if ((rv = sentCount % Package.PACKAGE_SIZE) > 0)
{
Array.Clear(buf, 0, buf.Length);
ecStream.Write(buf, 0, rv);
@@ -1984,7 +1984,7 @@ namespace MouseWithoutBorders.Class
if (tcp.MachineId == Setting.Values.MachineId)
{
tcp = null;
Setting.Values.MachineId = Common.Ran.Next();
Setting.Values.MachineId = Encryption.Ran.Next();
InitAndCleanup.UpdateMachineTimeAndID();
InitAndCleanup.PleaseReopenSocket = InitAndCleanup.REOPEN_WHEN_HOTKEY;

View File

@@ -70,7 +70,7 @@ namespace MouseWithoutBorders.Class
continue;
}
if (!Common.IsMyDesktopActive())
if (!WinAPI.IsMyDesktopActive())
{
// We can just throw the SocketException but to avoid a redundant log entry:
throw new ExpectedSocketException($"{nameof(StartServer)}: The desktop is no longer active.");

View File

@@ -270,15 +270,15 @@ internal static class Clipboard
int index = 0;
int len;
DATA package = new();
byte[] buf = new byte[Common.PACKAGE_SIZE_EX];
int dataStart = Common.PACKAGE_SIZE_EX - DATA_SIZE;
byte[] buf = new byte[Package.PACKAGE_SIZE_EX];
int dataStart = Package.PACKAGE_SIZE_EX - DATA_SIZE;
while (true)
{
if ((index + DATA_SIZE) > l)
{
len = l - index;
Array.Clear(buf, 0, Common.PACKAGE_SIZE_EX);
Array.Clear(buf, 0, Package.PACKAGE_SIZE_EX);
}
else
{
@@ -315,7 +315,7 @@ internal static class Clipboard
}
MemoryStream m = new();
int dataStart = Common.PACKAGE_SIZE_EX - DATA_SIZE;
int dataStart = Package.PACKAGE_SIZE_EX - DATA_SIZE;
m.Write(data.Bytes, dataStart, DATA_SIZE);
int unexpectedCount = 0;
@@ -809,27 +809,27 @@ internal static class Clipboard
MachineName = Common.MachineName,
};
byte[] buf = new byte[Common.PACKAGE_SIZE_EX];
byte[] buf = new byte[Package.PACKAGE_SIZE_EX];
NetworkStream ns = new(s);
enStream = Common.GetEncryptedStream(ns);
enStream = Encryption.GetEncryptedStream(ns);
Common.SendOrReceiveARandomDataBlockPerInitialIV(enStream);
Logger.LogDebug($"{nameof(ShakeHand)}: Writing header package.");
enStream.Write(package.Bytes, 0, Common.PACKAGE_SIZE_EX);
enStream.Write(package.Bytes, 0, Package.PACKAGE_SIZE_EX);
Logger.LogDebug($"{nameof(ShakeHand)}: Sent: clientPush={clientPushData}, postAction={postAction}.");
deStream = Common.GetDecryptedStream(ns);
deStream = Encryption.GetDecryptedStream(ns);
Common.SendOrReceiveARandomDataBlockPerInitialIV(deStream, false);
Logger.LogDebug($"{nameof(ShakeHand)}: Reading header package.");
int bytesReceived = deStream.ReadEx(buf, 0, Common.PACKAGE_SIZE_EX);
int bytesReceived = deStream.ReadEx(buf, 0, Package.PACKAGE_SIZE_EX);
package.Bytes = buf;
string name = "Unknown";
if (bytesReceived == Common.PACKAGE_SIZE_EX)
if (bytesReceived == Package.PACKAGE_SIZE_EX)
{
if (package.Type is PackageType.Clipboard or PackageType.ClipboardPush)
{

View File

@@ -0,0 +1,25 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
// <summary>
// Package format/conversion.
// </summary>
// <history>
// 2008 created by Truong Do (ductdo).
// 2009-... modified by Truong Do (TruongDo).
// 2023- Included in PowerToys.
// </history>
namespace MouseWithoutBorders.Core;
internal enum ClipboardPostAction : uint
{
Other = 0,
Desktop = 1,
Mspaint = 2,
}

View File

@@ -0,0 +1,150 @@
// 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;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
// In X64, we are WOW
[module: SuppressMessage("Microsoft.Portability", "CA1900:ValueTypeFieldsShouldBePortable", Scope = "type", Target = "MouseWithoutBorders.Core.DATA", Justification = "Dotnet port with style preservation")]
// <summary>
// Package format/conversion.
// </summary>
// <history>
// 2008 created by Truong Do (ductdo).
// 2009-... modified by Truong Do (TruongDo).
// 2023- Included in PowerToys.
// </history>
namespace MouseWithoutBorders.Core;
// The beauty of "union" in C#
[StructLayout(LayoutKind.Explicit)]
internal sealed class DATA
{
[FieldOffset(0)]
internal PackageType Type; // 4 (first byte = package type, 1 = checksum, 2+3 = magic no.)
[FieldOffset(sizeof(PackageType))]
internal int Id; // 4
[FieldOffset(sizeof(PackageType) + sizeof(uint))]
internal ID Src; // 4
[FieldOffset(sizeof(PackageType) + (2 * sizeof(uint)))]
internal ID Des; // 4
[FieldOffset(sizeof(PackageType) + (3 * sizeof(uint)))]
internal long DateTime;
[FieldOffset(sizeof(PackageType) + (3 * sizeof(uint)) + sizeof(long))]
internal KEYBDDATA Kd;
[FieldOffset(sizeof(PackageType) + (3 * sizeof(uint)))]
internal MOUSEDATA Md;
[FieldOffset(sizeof(PackageType) + (3 * sizeof(uint)))]
internal ID Machine1;
[FieldOffset(sizeof(PackageType) + (4 * sizeof(uint)))]
internal ID Machine2;
[FieldOffset(sizeof(PackageType) + (5 * sizeof(uint)))]
internal ID Machine3;
[FieldOffset(sizeof(PackageType) + (6 * sizeof(uint)))]
internal ID Machine4;
[FieldOffset(sizeof(PackageType) + (3 * sizeof(uint)))]
internal ClipboardPostAction PostAction;
[FieldOffset(sizeof(PackageType) + (7 * sizeof(uint)))]
private long machineNameP1;
[FieldOffset(sizeof(PackageType) + (7 * sizeof(uint)) + sizeof(long))]
private long machineNameP2;
[FieldOffset(sizeof(PackageType) + (7 * sizeof(uint)) + (2 * sizeof(long)))]
private long machineNameP3;
[FieldOffset(sizeof(PackageType) + (7 * sizeof(uint)) + (3 * sizeof(long)))]
private long machineNameP4;
internal string MachineName
{
get
{
string name = Common.GetString(BitConverter.GetBytes(machineNameP1))
+ Common.GetString(BitConverter.GetBytes(machineNameP2))
+ Common.GetString(BitConverter.GetBytes(machineNameP3))
+ Common.GetString(BitConverter.GetBytes(machineNameP4));
return name.Trim();
}
set
{
byte[] machineName = Common.GetBytes(value.PadRight(32, ' '));
machineNameP1 = BitConverter.ToInt64(machineName, 0);
machineNameP2 = BitConverter.ToInt64(machineName, 8);
machineNameP3 = BitConverter.ToInt64(machineName, 16);
machineNameP4 = BitConverter.ToInt64(machineName, 24);
}
}
public DATA()
{
}
public DATA(byte[] initialData)
{
Bytes = initialData;
}
internal byte[] Bytes
{
get
{
byte[] buf = new byte[IsBigPackage ? Package.PACKAGE_SIZE_EX : Package.PACKAGE_SIZE];
Array.Copy(StructToBytes(this), buf, IsBigPackage ? Package.PACKAGE_SIZE_EX : Package.PACKAGE_SIZE);
return buf;
}
set
{
Debug.Assert(value.Length <= Package.PACKAGE_SIZE_EX, "Length > package size");
byte[] buf = new byte[Package.PACKAGE_SIZE_EX];
Array.Copy(value, buf, value.Length);
BytesToStruct(buf, this);
}
}
internal bool IsBigPackage
{
get => Type == 0
? throw new InvalidOperationException("Package type not set.")
: Type switch
{
PackageType.Hello or PackageType.Awake or PackageType.Heartbeat or PackageType.Heartbeat_ex or PackageType.Handshake or PackageType.HandshakeAck or PackageType.ClipboardPush or PackageType.Clipboard or PackageType.ClipboardAsk or PackageType.ClipboardImage or PackageType.ClipboardText or PackageType.ClipboardDataEnd => true,
_ => (Type & PackageType.Matrix) == PackageType.Matrix,
};
}
private byte[] StructToBytes(object structObject)
{
byte[] bytes = new byte[Package.PACKAGE_SIZE_EX];
GCHandle bHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
Marshal.StructureToPtr(structObject, Marshal.UnsafeAddrOfPinnedArrayElement(bytes, 0), false);
bHandle.Free();
return bytes;
}
private void BytesToStruct(byte[] value, object structObject)
{
GCHandle bHandle = GCHandle.Alloc(value, GCHandleType.Pinned);
Marshal.PtrToStructure(Marshal.UnsafeAddrOfPinnedArrayElement(value, 0), structObject);
bHandle.Free();
}
}

View File

@@ -67,20 +67,20 @@ internal static class DragDrop
return;
}
if (wParam == Common.WM_LBUTTONDOWN)
if (wParam == WM.WM_LBUTTONDOWN)
{
MouseDown = true;
DragMachine = MachineStuff.desMachineID;
MachineStuff.dropMachineID = ID.NONE;
Logger.LogDebug("DragDropStep01: MouseDown");
}
else if (wParam == Common.WM_LBUTTONUP)
else if (wParam == WM.WM_LBUTTONUP)
{
MouseDown = false;
Logger.LogDebug("DragDropStep01: MouseUp");
}
if (wParam == Common.WM_RBUTTONUP && IsDropping)
if (wParam == WM.WM_RBUTTONUP && IsDropping)
{
IsDropping = false;
Clipboard.LastIDWithClipboardData = ID.NONE;
@@ -252,7 +252,7 @@ internal static class DragDrop
internal static void DragDropStep09(int wParam)
{
if (wParam == Common.WM_MOUSEMOVE && IsDropping)
if (wParam == WM.WM_MOUSEMOVE && IsDropping)
{
// Show/Move form
Common.DoSomethingInUIThread(() =>
@@ -260,7 +260,7 @@ internal static class DragDrop
_ = NativeMethods.PostMessage(Common.MainForm.Handle, NativeMethods.WM_SHOW_DRAG_DROP, (IntPtr)0, (IntPtr)0);
});
}
else if (wParam == Common.WM_LBUTTONUP && (IsDropping || IsDragging))
else if (wParam == WM.WM_LBUTTONUP && (IsDropping || IsDragging))
{
if (IsDropping)
{

View File

@@ -0,0 +1,245 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Concurrent;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Threading.Tasks;
// <summary>
// Encrypt/decrypt implementation.
// </summary>
// <history>
// 2008 created by Truong Do (ductdo).
// 2009-... modified by Truong Do (TruongDo).
// 2023- Included in PowerToys.
// </history>
namespace MouseWithoutBorders.Core;
internal static class Encryption
{
#pragma warning disable SYSLIB0021
private static AesCryptoServiceProvider symAl;
#pragma warning restore SYSLIB0021
#pragma warning disable SA1307 // Accessible fields should begin with upper-case letter
internal static string myKey;
#pragma warning restore SA1307
private static uint magicNumber;
private static Random ran = new(); // Used for non encryption related functionality.
internal const int SymAlBlockSize = 16;
/// <summary>
/// This is used for the first encryption block, the following blocks will be combined with the cipher text of the previous block.
/// Thus identical blocks in the socket stream would be encrypted to different cipher text blocks.
/// The first block is a handshake one containing random data.
/// Related Unit Test: TestEncryptDecrypt
/// </summary>
private static readonly string InitialIV = ulong.MaxValue.ToString(CultureInfo.InvariantCulture);
internal static Random Ran
{
get => Encryption.ran ??= new Random();
set => Encryption.ran = value;
}
internal static uint MagicNumber
{
get => Encryption.magicNumber;
set => Encryption.magicNumber = value;
}
internal static string MyKey
{
get => Encryption.myKey;
set
{
if (Encryption.myKey != value)
{
Encryption.myKey = value;
_ = Task.Factory.StartNew(
() => Encryption.GenLegalKey(),
System.Threading.CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.Default); // Cache the key to improve UX.
}
}
}
private static string KeyDisplayedText(string key)
{
string displayedValue = string.Empty;
int i = 0;
do
{
int length = Math.Min(4, key.Length - i);
displayedValue += string.Concat(key.AsSpan(i, length), " ");
i += 4;
}
while (i < key.Length - 1);
return displayedValue.Trim();
}
internal static bool GeneratedKey { get; set; }
internal static bool KeyCorrupted { get; set; }
internal static void InitEncryption()
{
try
{
if (symAl == null)
{
#pragma warning disable SYSLIB0021 // No proper replacement for now
symAl = new AesCryptoServiceProvider();
#pragma warning restore SYSLIB0021
symAl.KeySize = 256;
symAl.BlockSize = SymAlBlockSize * 8;
symAl.Padding = PaddingMode.Zeros;
symAl.Mode = CipherMode.CBC;
symAl.GenerateIV();
}
}
catch (Exception e)
{
Logger.Log(e);
}
}
private static readonly ConcurrentDictionary<string, byte[]> LegalKeyDictionary = new(StringComparer.OrdinalIgnoreCase);
private static byte[] GenLegalKey()
{
byte[] rv;
string myKey = Encryption.MyKey;
if (!LegalKeyDictionary.TryGetValue(myKey, out byte[] value))
{
Rfc2898DeriveBytes key = new(
myKey,
Common.GetBytesU(InitialIV),
50000,
HashAlgorithmName.SHA512);
rv = key.GetBytes(32);
_ = LegalKeyDictionary.AddOrUpdate(myKey, rv, (k, v) => rv);
}
else
{
rv = value;
}
return rv;
}
private static byte[] GenLegalIV()
{
string st = InitialIV;
int ivLength = symAl.IV.Length;
if (st.Length > ivLength)
{
st = st[..ivLength];
}
else if (st.Length < ivLength)
{
st = st.PadRight(ivLength, ' ');
}
return Common.GetBytes(st);
}
internal static Stream GetEncryptedStream(Stream encryptedStream)
{
ICryptoTransform encryptor;
encryptor = symAl.CreateEncryptor(GenLegalKey(), GenLegalIV());
return new CryptoStream(encryptedStream, encryptor, CryptoStreamMode.Write);
}
internal static Stream GetDecryptedStream(Stream encryptedStream)
{
ICryptoTransform decryptor;
decryptor = symAl.CreateDecryptor(GenLegalKey(), GenLegalIV());
return new CryptoStream(encryptedStream, decryptor, CryptoStreamMode.Read);
}
internal static uint Get24BitHash(string st)
{
if (string.IsNullOrEmpty(st))
{
return 0;
}
byte[] bytes = new byte[Package.PACKAGE_SIZE];
for (int i = 0; i < Package.PACKAGE_SIZE; i++)
{
if (i < st.Length)
{
bytes[i] = (byte)st[i];
}
}
var hash = SHA512.Create();
byte[] hashValue = hash.ComputeHash(bytes);
for (int i = 0; i < 50000; i++)
{
hashValue = hash.ComputeHash(hashValue);
}
Logger.LogDebug(string.Format(CultureInfo.CurrentCulture, "magic: {0},{1},{2}", hashValue[0], hashValue[1], hashValue[^1]));
hash.Clear();
return (uint)((hashValue[0] << 23) + (hashValue[1] << 16) + (hashValue[^1] << 8) + hashValue[2]);
}
internal static string GetDebugInfo(string st)
{
return string.IsNullOrEmpty(st) ? st : ((byte)(Common.GetBytesU(st).Sum(value => value) % 256)).ToString(CultureInfo.InvariantCulture);
}
internal static string CreateDefaultKey()
{
return CreateRandomKey();
}
private const int PW_LENGTH = 16;
internal static string CreateRandomKey()
{
// Not including characters like "'`O0& since they are confusing to users.
string[] chars = new[] { "abcdefghjkmnpqrstuvxyz", "ABCDEFGHJKMNPQRSTUVXYZ", "123456789", "~!@#$%^*()_-+=:;<,>.?/\\|[]" };
char[][] charactersUsedForKey = chars.Select(charset => Enumerable.Range(0, charset.Length - 1).Select(i => charset[i]).ToArray()).ToArray();
byte[] randomData = new byte[1];
string key = string.Empty;
do
{
foreach (string set in chars)
{
randomData = RandomNumberGenerator.GetBytes(1);
key += set[randomData[0] % set.Length];
if (key.Length >= PW_LENGTH)
{
break;
}
}
}
while (key.Length < PW_LENGTH);
return key;
}
internal static bool IsKeyValid(string key, out string error)
{
error = string.IsNullOrEmpty(key) || key.Length < 16
? "Key must have at least 16 characters in length (spaces are discarded). Key must be auto generated in one of the machines."
: null;
return error == null;
}
}

View File

@@ -70,7 +70,7 @@ internal static class Event
// Check if easy mouse setting is enabled.
bool isEasyMouseEnabled = IsSwitchingByMouseEnabled();
if (isEasyMouseEnabled && Common.Sk != null && (Common.DesMachineID == Common.MachineID || !Setting.Values.MoveMouseRelatively) && e.dwFlags == Common.WM_MOUSEMOVE)
if (isEasyMouseEnabled && Common.Sk != null && (Common.DesMachineID == Common.MachineID || !Setting.Values.MoveMouseRelatively) && e.dwFlags == WM.WM_MOUSEMOVE)
{
Point p = MachineStuff.MoveToMyNeighbourIfNeeded(e.X, e.Y, MachineStuff.desMachineID);
@@ -115,7 +115,7 @@ internal static class Event
Common.SkSend(MousePackage, null, false);
if (MousePackage.Md.dwFlags is Common.WM_LBUTTONUP or Common.WM_RBUTTONUP)
if (MousePackage.Md.dwFlags is WM.WM_LBUTTONUP or WM.WM_RBUTTONUP)
{
Thread.Sleep(10);
}
@@ -265,7 +265,7 @@ internal static class Event
KeybdPackage.Kd = e;
KeybdPackage.DateTime = Common.GetTick();
Common.SkSend(KeybdPackage, null, false);
if (KeybdPackage.Kd.dwFlags is Common.WM_KEYUP or Common.WM_SYSKEYUP)
if (KeybdPackage.Kd.dwFlags is WM.WM_KEYUP or WM.WM_SYSKEYUP)
{
Thread.Sleep(10);
}

View File

@@ -290,7 +290,7 @@ internal static class Helper
return;
}
if (!Common.IsMyDesktopActive())
if (!WinAPI.IsMyDesktopActive())
{
return;
}
@@ -314,7 +314,7 @@ internal static class Helper
_ = Launch.CreateProcessInInputDesktopSession(
$"\"{Path.GetDirectoryName(Application.ExecutablePath)}\\{HelperProcessName}.exe\"",
string.Empty,
Common.GetInputDesktop(),
WinAPI.GetInputDesktop(),
0);
Clipboard.HasSwitchedMachineSinceLastCopy = true;
@@ -379,7 +379,7 @@ internal static class Helper
log += "=============================================================================================================================\r\n";
log += $"{Application.ProductName} version {Application.ProductVersion}\r\n";
log += $"{Setting.Values.Username}/{Common.GetDebugInfo(Common.MyKey)}\r\n";
log += $"{Setting.Values.Username}/{Encryption.GetDebugInfo(Encryption.MyKey)}\r\n";
log += $"{Common.MachineName}/{Common.MachineID}/{Common.DesMachineID}\r\n";
log += $"Id: {Setting.Values.DeviceId}\r\n";
log += $"Matrix: {string.Join(",", MachineStuff.MachineMatrix)}\r\n";

View File

@@ -0,0 +1,19 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
// <summary>
// Package format/conversion.
// </summary>
// <history>
// 2008 created by Truong Do (ductdo).
// 2009-... modified by Truong Do (TruongDo).
// 2023- Included in PowerToys.
// </history>
namespace MouseWithoutBorders.Core;
internal enum ID : uint
{
NONE = 0,
ALL = 255,
}

View File

@@ -89,23 +89,23 @@ internal static class InitAndCleanup
internal static void Init()
{
_ = Helper.GetUserName();
Common.GeneratedKey = true;
Encryption.GeneratedKey = true;
try
{
Common.MyKey = Setting.Values.MyKey;
Encryption.MyKey = Setting.Values.MyKey;
int tmp = Setting.Values.MyKeyDaysToExpire;
}
catch (FormatException e)
{
Common.KeyCorrupted = true;
Setting.Values.MyKey = Common.MyKey = Common.CreateRandomKey();
Encryption.KeyCorrupted = true;
Setting.Values.MyKey = Encryption.MyKey = Encryption.CreateRandomKey();
Logger.Log(e.Message);
}
catch (CryptographicException e)
{
Common.KeyCorrupted = true;
Setting.Values.MyKey = Common.MyKey = Common.CreateRandomKey();
Encryption.KeyCorrupted = true;
Setting.Values.MyKey = Encryption.MyKey = Encryption.CreateRandomKey();
Logger.Log(e.Message);
}
@@ -127,14 +127,14 @@ internal static class InitAndCleanup
bool dummy = Setting.Values.DrawMouseEx;
Common.Is64bitOS = IntPtr.Size == 8;
Common.tcpPort = Setting.Values.TcpPort;
Common.GetScreenConfig();
Common.PackageSent = new PackageMonitor(0);
Common.PackageReceived = new PackageMonitor(0);
WinAPI.GetScreenConfig();
Package.PackageSent = new PackageMonitor(0);
Package.PackageReceived = new PackageMonitor(0);
SetupMachineNameAndID();
Common.InitEncryption();
Encryption.InitEncryption();
CreateHelperThreads();
SystemEvents.DisplaySettingsChanged += new EventHandler(Common.SystemEvents_DisplaySettingsChanged);
SystemEvents.DisplaySettingsChanged += new EventHandler(WinAPI.SystemEvents_DisplaySettingsChanged);
NetworkChange.NetworkAvailabilityChanged += new NetworkAvailabilityChangedEventHandler(NetworkChange_NetworkAvailabilityChanged);
SystemEvents.PowerModeChanged += new PowerModeChangedEventHandler(SystemEvents_PowerModeChanged);
PleaseReopenSocket = 9;
@@ -220,7 +220,7 @@ internal static class InitAndCleanup
lastReleaseAllKeysCall = Common.GetTick();
KEYBDDATA kd;
kd.dwFlags = (int)Common.LLKHF.UP;
kd.dwFlags = (int)WM.LLKHF.UP;
VK[] keys = new VK[]
{
@@ -266,7 +266,7 @@ internal static class InitAndCleanup
true);
}
if (!Common.IsMyDesktopActive())
if (!WinAPI.IsMyDesktopActive())
{
PleaseReopenSocket = 0;
}

View File

@@ -0,0 +1,25 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
// <summary>
// Package format/conversion.
// </summary>
// <history>
// 2008 created by Truong Do (ductdo).
// 2009-... modified by Truong Do (TruongDo).
// 2023- Included in PowerToys.
// </history>
namespace MouseWithoutBorders.Core;
[StructLayout(LayoutKind.Sequential)]
internal struct KEYBDDATA
{
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Same name as in winAPI")]
internal int wVk;
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Same name as in winAPI")]
internal int dwFlags;
}

View File

@@ -121,52 +121,52 @@ internal static class Logger
{
string log;
if (!lastPackageSent.Equals(Common.PackageSent))
if (!lastPackageSent.Equals(Package.PackageSent))
{
log = string.Format(
CultureInfo.CurrentCulture,
"SENT:" + HeaderSENT,
Common.PackageSent.Heartbeat,
Common.PackageSent.Keyboard,
Common.PackageSent.Mouse,
Common.PackageSent.Hello,
Common.PackageSent.Matrix,
Common.PackageSent.ClipboardText,
Common.PackageSent.ClipboardImage,
Common.PackageSent.ByeBye,
Common.PackageSent.Clipboard,
Common.PackageSent.ClipboardDragDrop,
Common.PackageSent.ClipboardDragDropEnd,
Common.PackageSent.ExplorerDragDrop,
Package.PackageSent.Heartbeat,
Package.PackageSent.Keyboard,
Package.PackageSent.Mouse,
Package.PackageSent.Hello,
Package.PackageSent.Matrix,
Package.PackageSent.ClipboardText,
Package.PackageSent.ClipboardImage,
Package.PackageSent.ByeBye,
Package.PackageSent.Clipboard,
Package.PackageSent.ClipboardDragDrop,
Package.PackageSent.ClipboardDragDropEnd,
Package.PackageSent.ExplorerDragDrop,
Event.inputEventCount,
Common.PackageSent.Nil);
Package.PackageSent.Nil);
Log(log);
lastPackageSent = Common.PackageSent; // Copy data
lastPackageSent = Package.PackageSent; // Copy data
}
if (!lastPackageReceived.Equals(Common.PackageReceived))
if (!lastPackageReceived.Equals(Package.PackageReceived))
{
log = string.Format(
CultureInfo.CurrentCulture,
"RECEIVED:" + HeaderRECEIVED,
Common.PackageReceived.Heartbeat,
Common.PackageReceived.Keyboard,
Common.PackageReceived.Mouse,
Common.PackageReceived.Hello,
Common.PackageReceived.Matrix,
Common.PackageReceived.ClipboardText,
Common.PackageReceived.ClipboardImage,
Common.PackageReceived.ByeBye,
Common.PackageReceived.Clipboard,
Common.PackageReceived.ClipboardDragDrop,
Common.PackageReceived.ClipboardDragDropEnd,
Common.PackageReceived.ExplorerDragDrop,
Package.PackageReceived.Heartbeat,
Package.PackageReceived.Keyboard,
Package.PackageReceived.Mouse,
Package.PackageReceived.Hello,
Package.PackageReceived.Matrix,
Package.PackageReceived.ClipboardText,
Package.PackageReceived.ClipboardImage,
Package.PackageReceived.ByeBye,
Package.PackageReceived.Clipboard,
Package.PackageReceived.ClipboardDragDrop,
Package.PackageReceived.ClipboardDragDropEnd,
Package.PackageReceived.ExplorerDragDrop,
Event.invalidPackageCount,
Common.PackageReceived.Nil,
Package.PackageReceived.Nil,
Receiver.processedPackageCount,
Receiver.skippedPackageCount);
Log(log);
lastPackageReceived = Common.PackageReceived;
lastPackageReceived = Package.PackageReceived;
}
}
@@ -209,9 +209,9 @@ internal static class Logger
"Private Mem: " + (Process.GetCurrentProcess().PrivateMemorySize64 / 1024).ToString(CultureInfo.CurrentCulture) + "KB",
sb.ToString());
if (!string.IsNullOrEmpty(Common.myKey))
if (!string.IsNullOrEmpty(Encryption.myKey))
{
log = log.Replace(Common.MyKey, Common.GetDebugInfo(Common.MyKey));
log = log.Replace(Encryption.MyKey, Encryption.GetDebugInfo(Encryption.MyKey));
}
log += Thread.DumpThreadsStack();
@@ -251,14 +251,18 @@ internal static class Logger
{
typeof(Clipboard),
typeof(DragDrop),
typeof(Encryption),
typeof(Event),
typeof(InitAndCleanup),
typeof(Helper),
typeof(Launch),
typeof(Logger),
typeof(MachineStuff),
typeof(Package),
typeof(Receiver),
typeof(Service),
typeof(WinAPI),
typeof(WM),
};
foreach (var staticType in staticTypes)
{
@@ -294,7 +298,7 @@ internal static class Logger
// strArr[3] = t.FullName;
strArr[4] = " = ";
strArr[5] = objName.Equals("myKey", StringComparison.OrdinalIgnoreCase)
? Common.GetDebugInfo(objString)
? Encryption.GetDebugInfo(objString)
: objName.Equals("lastClipboardObject", StringComparison.OrdinalIgnoreCase)
? string.Empty
: objString

View File

@@ -0,0 +1,26 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
// <summary>
// Package format/conversion.
// </summary>
// <history>
// 2008 created by Truong Do (ductdo).
// 2009-... modified by Truong Do (TruongDo).
// 2023- Included in PowerToys.
// </history>
namespace MouseWithoutBorders.Core;
[StructLayout(LayoutKind.Sequential)]
internal struct MOUSEDATA
{
internal int X;
internal int Y;
internal int WheelDelta;
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Same name as in winAPI")]
internal int dwFlags;
}

View File

@@ -221,9 +221,9 @@ internal static class MachineStuff
if (Setting.Values.BlockMouseAtCorners)
{
lock (Common.SensitivePoints)
lock (WinAPI.SensitivePoints)
{
foreach (Point p in Common.SensitivePoints)
foreach (Point p in WinAPI.SensitivePoints)
{
if (Math.Abs(p.X - x) < 100 && Math.Abs(p.Y - y) < 100)
{
@@ -793,8 +793,8 @@ internal static class MachineStuff
internal static void ShowSetupForm(bool reopenSockets = false)
{
Logger.LogDebug("========== BEGIN THE SETUP EXPERIENCE ==========", true);
Setting.Values.MyKey = Common.MyKey = Common.CreateRandomKey();
Common.GeneratedKey = true;
Setting.Values.MyKey = Encryption.MyKey = Encryption.CreateRandomKey();
Encryption.GeneratedKey = true;
if (Process.GetCurrentProcess().SessionId != NativeMethods.WTSGetActiveConsoleSessionId())
{
@@ -1067,7 +1067,7 @@ internal static class MachineStuff
internal static void AssertOneInstancePerDesktopSession()
{
string eventName = $"Global\\{Application.ProductName}-{FrmAbout.AssemblyVersion}-{Common.GetMyDesktop()}-{Common.CurrentProcess.SessionId}";
string eventName = $"Global\\{Application.ProductName}-{FrmAbout.AssemblyVersion}-{WinAPI.GetMyDesktop()}-{Common.CurrentProcess.SessionId}";
oneInstanceCheck = new EventWaitHandle(false, EventResetMode.ManualReset, eventName, out bool created);
if (!created)

View File

@@ -0,0 +1,23 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
// <summary>
// Package format/conversion.
// </summary>
// <history>
// 2008 created by Truong Do (ductdo).
// 2009-... modified by Truong Do (TruongDo).
// 2023- Included in PowerToys.
// </history>
namespace MouseWithoutBorders.Core;
internal static class Package
{
internal const byte PACKAGE_SIZE = 32;
internal const byte PACKAGE_SIZE_EX = 64;
private const byte WP_PACKAGE_SIZE = 6;
internal static PackageMonitor PackageSent;
internal static PackageMonitor PackageReceived;
internal static int PackageID;
}

View File

@@ -0,0 +1,38 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
// <summary>
// Package format/conversion.
// </summary>
// <history>
// 2008 created by Truong Do (ductdo).
// 2009-... modified by Truong Do (TruongDo).
// 2023- Included in PowerToys.
// </history>
namespace MouseWithoutBorders.Core;
internal struct PackageMonitor
{
internal ulong Keyboard;
internal ulong Mouse;
internal ulong Heartbeat;
internal ulong ByeBye;
internal ulong Hello;
internal ulong Matrix;
internal ulong ClipboardText;
internal ulong ClipboardImage;
internal ulong Clipboard;
internal ulong ClipboardDragDrop;
internal ulong ClipboardDragDropEnd;
internal ulong ClipboardAsk;
internal ulong ExplorerDragDrop;
internal ulong Nil;
internal PackageMonitor(ulong value)
{
ClipboardDragDrop = ClipboardDragDropEnd = ExplorerDragDrop =
Keyboard = Mouse = Heartbeat = ByeBye = Hello = Clipboard =
Matrix = ClipboardImage = ClipboardText = Nil = ClipboardAsk = value;
}
}

View File

@@ -0,0 +1,57 @@
// 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.
// <summary>
// Package format/conversion.
// </summary>
// <history>
// 2008 created by Truong Do (ductdo).
// 2009-... modified by Truong Do (TruongDo).
// 2023- Included in PowerToys.
// </history>
namespace MouseWithoutBorders.Core;
internal enum PackageType // : int
{
// Search for PACKAGE_TYPE_RELATED before changing these!
Invalid = 0xFF,
Error = 0xFE,
Hi = 2,
Hello = 3,
ByeBye = 4,
Heartbeat = 20,
Awake = 21,
HideMouse = 50,
Heartbeat_ex = 51,
Heartbeat_ex_l2 = 52,
Heartbeat_ex_l3 = 53,
Clipboard = 69,
ClipboardDragDrop = 70,
ClipboardDragDropEnd = 71,
ExplorerDragDrop = 72,
ClipboardCapture = 73,
CaptureScreenCommand = 74,
ClipboardDragDropOperation = 75,
ClipboardDataEnd = 76,
MachineSwitched = 77,
ClipboardAsk = 78,
ClipboardPush = 79,
NextMachine = 121,
Keyboard = 122,
Mouse = 123,
ClipboardText = 124,
ClipboardImage = 125,
Handshake = 126,
HandshakeAck = 127,
Matrix = 128,
MatrixSwapFlag = 2,
MatrixTwoRowFlag = 4,
}

View File

@@ -93,7 +93,7 @@ internal static class Receiver
switch (package.Type)
{
case PackageType.Keyboard:
Common.PackageReceived.Keyboard++;
Package.PackageReceived.Keyboard++;
if (package.Des == Common.MachineID || package.Des == ID.ALL)
{
JustGotAKey = Common.GetTick();
@@ -102,7 +102,7 @@ internal static class Receiver
bool nonElevated = Common.RunWithNoAdminRight && false;
if (nonElevated && Setting.Values.OneWayControlMode)
{
if ((package.Kd.dwFlags & (int)Common.LLKHF.UP) == (int)Common.LLKHF.UP)
if ((package.Kd.dwFlags & (int)WM.LLKHF.UP) == (int)WM.LLKHF.UP)
{
Helper.ShowOneWayModeMessage();
}
@@ -116,7 +116,7 @@ internal static class Receiver
break;
case PackageType.Mouse:
Common.PackageReceived.Mouse++;
Package.PackageReceived.Mouse++;
if (package.Des == Common.MachineID || package.Des == ID.ALL)
{
@@ -127,16 +127,16 @@ internal static class Receiver
// NOTE(@yuyoyuppe): disabled to drop elevation requirement
bool nonElevated = Common.RunWithNoAdminRight && false;
if (nonElevated && Setting.Values.OneWayControlMode && package.Md.dwFlags != Common.WM_MOUSEMOVE)
if (nonElevated && Setting.Values.OneWayControlMode && package.Md.dwFlags != WM.WM_MOUSEMOVE)
{
if (!DragDrop.IsDropping)
{
if (package.Md.dwFlags is Common.WM_LBUTTONDOWN or Common.WM_RBUTTONDOWN)
if (package.Md.dwFlags is WM.WM_LBUTTONDOWN or WM.WM_RBUTTONDOWN)
{
Helper.ShowOneWayModeMessage();
}
}
else if (package.Md.dwFlags is Common.WM_LBUTTONUP or Common.WM_RBUTTONUP)
else if (package.Md.dwFlags is WM.WM_LBUTTONUP or WM.WM_RBUTTONUP)
{
DragDrop.IsDropping = false;
}
@@ -146,7 +146,7 @@ internal static class Receiver
if (Math.Abs(package.Md.X) >= Event.MOVE_MOUSE_RELATIVE && Math.Abs(package.Md.Y) >= Event.MOVE_MOUSE_RELATIVE)
{
if (package.Md.dwFlags == Common.WM_MOUSEMOVE)
if (package.Md.dwFlags == WM.WM_MOUSEMOVE)
{
InputSimulation.MoveMouseRelative(
package.Md.X < 0 ? package.Md.X + Event.MOVE_MOUSE_RELATIVE : package.Md.X - Event.MOVE_MOUSE_RELATIVE,
@@ -203,19 +203,19 @@ internal static class Receiver
break;
case PackageType.ExplorerDragDrop:
Common.PackageReceived.ExplorerDragDrop++;
Package.PackageReceived.ExplorerDragDrop++;
DragDrop.DragDropStep03(package);
break;
case PackageType.Heartbeat:
case PackageType.Heartbeat_ex:
Common.PackageReceived.Heartbeat++;
Package.PackageReceived.Heartbeat++;
Common.GeneratedKey = Common.GeneratedKey || package.Type == PackageType.Heartbeat_ex;
Encryption.GeneratedKey = Encryption.GeneratedKey || package.Type == PackageType.Heartbeat_ex;
if (Common.GeneratedKey)
if (Encryption.GeneratedKey)
{
Setting.Values.MyKey = Common.MyKey;
Setting.Values.MyKey = Encryption.MyKey;
Common.SendPackage(ID.ALL, PackageType.Heartbeat_ex_l2);
}
@@ -230,26 +230,26 @@ internal static class Receiver
break;
case PackageType.Heartbeat_ex_l2:
Common.GeneratedKey = true;
Setting.Values.MyKey = Common.MyKey;
Encryption.GeneratedKey = true;
Setting.Values.MyKey = Encryption.MyKey;
Common.SendPackage(ID.ALL, PackageType.Heartbeat_ex_l3);
break;
case PackageType.Heartbeat_ex_l3:
Common.GeneratedKey = true;
Setting.Values.MyKey = Common.MyKey;
Encryption.GeneratedKey = true;
Setting.Values.MyKey = Encryption.MyKey;
break;
case PackageType.Awake:
Common.PackageReceived.Heartbeat++;
Package.PackageReceived.Heartbeat++;
_ = MachineStuff.AddToMachinePool(package);
Common.HumanBeingDetected();
break;
case PackageType.Hello:
Common.PackageReceived.Hello++;
Package.PackageReceived.Hello++;
Common.SendHeartBeat();
string newMachine = MachineStuff.AddToMachinePool(package);
if (Setting.Values.MachineMatrixString == null)
@@ -262,16 +262,16 @@ internal static class Receiver
break;
case PackageType.Hi:
Common.PackageReceived.Hello++;
Package.PackageReceived.Hello++;
break;
case PackageType.ByeBye:
Common.PackageReceived.ByeBye++;
Package.PackageReceived.ByeBye++;
Common.ProcessByeByeMessage(package);
break;
case PackageType.Clipboard:
Common.PackageReceived.Clipboard++;
Package.PackageReceived.Clipboard++;
if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop)
{
Clipboard.clipboardCopiedTime = Common.GetTick();
@@ -291,7 +291,7 @@ internal static class Receiver
break;
case PackageType.ClipboardCapture:
Common.PackageReceived.Clipboard++;
Package.PackageReceived.Clipboard++;
if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop)
{
if (package.Des == Common.MachineID || package.Des == ID.ALL)
@@ -304,7 +304,7 @@ internal static class Receiver
break;
case PackageType.CaptureScreenCommand:
Common.PackageReceived.Clipboard++;
Package.PackageReceived.Clipboard++;
if (package.Des == Common.MachineID || package.Des == ID.ALL)
{
Common.SendImage(package.Src, Common.CaptureScreen());
@@ -313,7 +313,7 @@ internal static class Receiver
break;
case PackageType.ClipboardAsk:
Common.PackageReceived.ClipboardAsk++;
Package.PackageReceived.ClipboardAsk++;
if (package.Des == Common.MachineID)
{
@@ -344,17 +344,17 @@ internal static class Receiver
break;
case PackageType.ClipboardDragDrop:
Common.PackageReceived.ClipboardDragDrop++;
Package.PackageReceived.ClipboardDragDrop++;
DragDrop.DragDropStep08(package);
break;
case PackageType.ClipboardDragDropOperation:
Common.PackageReceived.ClipboardDragDrop++;
Package.PackageReceived.ClipboardDragDrop++;
DragDrop.DragDropStep08_2(package);
break;
case PackageType.ClipboardDragDropEnd:
Common.PackageReceived.ClipboardDragDropEnd++;
Package.PackageReceived.ClipboardDragDropEnd++;
DragDrop.DragDropStep12();
break;
@@ -363,11 +363,11 @@ internal static class Receiver
Clipboard.clipboardCopiedTime = 0;
if (package.Type == PackageType.ClipboardImage)
{
Common.PackageReceived.ClipboardImage++;
Package.PackageReceived.ClipboardImage++;
}
else
{
Common.PackageReceived.ClipboardText++;
Package.PackageReceived.ClipboardText++;
}
if (tcp != null)
@@ -390,7 +390,7 @@ internal static class Receiver
default:
if ((package.Type & PackageType.Matrix) == PackageType.Matrix)
{
Common.PackageReceived.Matrix++;
Package.PackageReceived.Matrix++;
MachineStuff.UpdateMachineMatrix(package);
break;
}

View File

@@ -0,0 +1,29 @@
// 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 ManagedCommon;
using Microsoft.PowerToys.Telemetry;
namespace MouseWithoutBorders.Core;
internal static class ShutdownWithPowerToys
{
internal static void WaitForPowerToysRunner(ETWTrace etwTrace)
{
try
{
RunnerHelper.WaitForPowerToysRunnerExitFallback(() =>
{
etwTrace?.Dispose();
Common.MainForm.Quit(true, false);
});
}
catch (Exception e)
{
Logger.Log(e);
}
}
}

View File

@@ -0,0 +1,88 @@
// 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.
// <summary>
// Virtual key constants.
// </summary>
// <history>
// 2008 created by Truong Do (ductdo).
// 2009-... modified by Truong Do (TruongDo).
// 2023- Included in PowerToys.
// </history>
namespace MouseWithoutBorders.Core;
internal enum VK : ushort
{
CAPITAL = 0x14,
NUMLOCK = 0x90,
SHIFT = 0x10,
CONTROL = 0x11,
MENU = 0x12,
ESCAPE = 0x1B,
BACK = 0x08,
TAB = 0x09,
RETURN = 0x0D,
PRIOR = 0x21,
NEXT = 0x22,
END = 0x23,
HOME = 0x24,
LEFT = 0x25,
UP = 0x26,
RIGHT = 0x27,
DOWN = 0x28,
SELECT = 0x29,
PRINT = 0x2A,
EXECUTE = 0x2B,
SNAPSHOT = 0x2C,
INSERT = 0x2D,
DELETE = 0x2E,
HELP = 0x2F,
NUMPAD0 = 0x60,
NUMPAD1 = 0x61,
NUMPAD2 = 0x62,
NUMPAD3 = 0x63,
NUMPAD4 = 0x64,
NUMPAD5 = 0x65,
NUMPAD6 = 0x66,
NUMPAD7 = 0x67,
NUMPAD8 = 0x68,
NUMPAD9 = 0x69,
MULTIPLY = 0x6A,
ADD = 0x6B,
SEPARATOR = 0x6C,
SUBTRACT = 0x6D,
DECIMAL = 0x6E,
DIVIDE = 0x6F,
F1 = 0x70,
F2 = 0x71,
F3 = 0x72,
F4 = 0x73,
F5 = 0x74,
F6 = 0x75,
F7 = 0x76,
F8 = 0x77,
F9 = 0x78,
F10 = 0x79,
F11 = 0x7A,
F12 = 0x7B,
OEM_1 = 0xBA,
OEM_PLUS = 0xBB,
OEM_COMMA = 0xBC,
OEM_MINUS = 0xBD,
OEM_PERIOD = 0xBE,
OEM_2 = 0xBF,
OEM_3 = 0xC0,
MEDIA_NEXT_TRACK = 0xB0,
MEDIA_PREV_TRACK = 0xB1,
MEDIA_STOP = 0xB2,
MEDIA_PLAY_PAUSE = 0xB3,
LWIN = 0x5B,
RWIN = 0x5C,
LSHIFT = 0xA0,
RSHIFT = 0xA1,
LCONTROL = 0xA2,
RCONTROL = 0xA3,
LMENU = 0xA4,
RMENU = 0xA5,
}

View File

@@ -0,0 +1,55 @@
// 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;
// <summary>
// Virtual key constants.
// </summary>
// <history>
// 2008 created by Truong Do (ductdo).
// 2009-... modified by Truong Do (TruongDo).
// 2023- Included in PowerToys.
// </history>
namespace MouseWithoutBorders.Core;
internal partial class WM
{
internal const ushort KEYEVENTF_KEYDOWN = 0x0001;
internal const ushort KEYEVENTF_KEYUP = 0x0002;
internal const int WH_MOUSE = 7;
internal const int WH_KEYBOARD = 2;
internal const int WH_MOUSE_LL = 14;
internal const int WH_KEYBOARD_LL = 13;
internal const int WM_MOUSEMOVE = 0x200;
internal const int WM_LBUTTONDOWN = 0x201;
internal const int WM_RBUTTONDOWN = 0x204;
internal const int WM_MBUTTONDOWN = 0x207;
internal const int WM_XBUTTONDOWN = 0x20B;
internal const int WM_LBUTTONUP = 0x202;
internal const int WM_RBUTTONUP = 0x205;
internal const int WM_MBUTTONUP = 0x208;
internal const int WM_XBUTTONUP = 0x20C;
internal const int WM_LBUTTONDBLCLK = 0x203;
internal const int WM_RBUTTONDBLCLK = 0x206;
internal const int WM_MBUTTONDBLCLK = 0x209;
internal const int WM_MOUSEWHEEL = 0x020A;
internal const int WM_MOUSEHWHEEL = 0x020E;
internal const int WM_KEYDOWN = 0x100;
internal const int WM_KEYUP = 0x101;
internal const int WM_SYSKEYDOWN = 0x104;
internal const int WM_SYSKEYUP = 0x105;
[Flags]
internal enum LLKHF
{
EXTENDED = 0x01,
INJECTED = 0x10,
ALTDOWN = 0x20,
UP = 0x80,
}
}

View File

@@ -0,0 +1,359 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
using MouseWithoutBorders.Class;
// <summary>
// Screen/Desktop helper functions.
// </summary>
// <history>
// 2008 created by Truong Do (ductdo).
// 2009-... modified by Truong Do (TruongDo).
// 2023- Included in PowerToys.
// </history>
namespace MouseWithoutBorders.Core;
// Desktops, and GetScreenConfig routines
internal static class WinAPI
{
private static MyRectangle newDesktopBounds;
private static MyRectangle newPrimaryScreenBounds;
private static string activeDesktop;
private static string ActiveDesktop => WinAPI.activeDesktop;
internal static void SystemEvents_DisplaySettingsChanged(object sender, EventArgs e)
{
GetScreenConfig();
}
internal static readonly List<Point> SensitivePoints = new();
private static bool MonitorEnumProc(IntPtr hMonitor, IntPtr hdcMonitor, ref NativeMethods.RECT lprcMonitor, IntPtr dwData)
{
// lprcMonitor is wrong!!! => using GetMonitorInfo(...)
// Log(String.Format( CultureInfo.CurrentCulture,"MONITOR: l{0}, t{1}, r{2}, b{3}", lprcMonitor.Left, lprcMonitor.Top, lprcMonitor.Right, lprcMonitor.Bottom));
NativeMethods.MonitorInfoEx mi = default;
mi.cbSize = Marshal.SizeOf(mi);
_ = NativeMethods.GetMonitorInfo(hMonitor, ref mi);
try
{
// For logging only
_ = NativeMethods.GetDpiForMonitor(hMonitor, 0, out uint dpiX, out uint dpiY);
Logger.Log(string.Format(CultureInfo.CurrentCulture, "MONITOR: ({0}, {1}, {2}, {3}). DPI: ({4}, {5})", mi.rcMonitor.Left, mi.rcMonitor.Top, mi.rcMonitor.Right, mi.rcMonitor.Bottom, dpiX, dpiY));
}
catch (DllNotFoundException)
{
Logger.Log("GetDpiForMonitor is unsupported in Windows 7 and lower.");
}
catch (EntryPointNotFoundException)
{
Logger.Log("GetDpiForMonitor is unsupported in Windows 7 and lower.");
}
catch (Exception e)
{
Logger.Log(e);
}
if (mi.rcMonitor.Left == 0 && mi.rcMonitor.Top == 0 && mi.rcMonitor.Right != 0 && mi.rcMonitor.Bottom != 0)
{
// Primary screen
_ = Interlocked.Exchange(ref Common.screenWidth, mi.rcMonitor.Right - mi.rcMonitor.Left);
_ = Interlocked.Exchange(ref Common.screenHeight, mi.rcMonitor.Bottom - mi.rcMonitor.Top);
newPrimaryScreenBounds.Left = mi.rcMonitor.Left;
newPrimaryScreenBounds.Top = mi.rcMonitor.Top;
newPrimaryScreenBounds.Right = mi.rcMonitor.Right;
newPrimaryScreenBounds.Bottom = mi.rcMonitor.Bottom;
}
else
{
if (mi.rcMonitor.Left < newDesktopBounds.Left)
{
newDesktopBounds.Left = mi.rcMonitor.Left;
}
if (mi.rcMonitor.Top < newDesktopBounds.Top)
{
newDesktopBounds.Top = mi.rcMonitor.Top;
}
if (mi.rcMonitor.Right > newDesktopBounds.Right)
{
newDesktopBounds.Right = mi.rcMonitor.Right;
}
if (mi.rcMonitor.Bottom > newDesktopBounds.Bottom)
{
newDesktopBounds.Bottom = mi.rcMonitor.Bottom;
}
}
lock (SensitivePoints)
{
SensitivePoints.Add(new Point(mi.rcMonitor.Left, mi.rcMonitor.Top));
SensitivePoints.Add(new Point(mi.rcMonitor.Right, mi.rcMonitor.Top));
SensitivePoints.Add(new Point(mi.rcMonitor.Right, mi.rcMonitor.Bottom));
SensitivePoints.Add(new Point(mi.rcMonitor.Left, mi.rcMonitor.Bottom));
}
return true;
}
internal static void GetScreenConfig()
{
try
{
Logger.LogDebug("==================== GetScreenConfig started");
newDesktopBounds = new MyRectangle();
newPrimaryScreenBounds = new MyRectangle();
newDesktopBounds.Left = newPrimaryScreenBounds.Left = Screen.PrimaryScreen.Bounds.Left;
newDesktopBounds.Top = newPrimaryScreenBounds.Top = Screen.PrimaryScreen.Bounds.Top;
newDesktopBounds.Right = newPrimaryScreenBounds.Right = Screen.PrimaryScreen.Bounds.Right;
newDesktopBounds.Bottom = newPrimaryScreenBounds.Bottom = Screen.PrimaryScreen.Bounds.Bottom;
Logger.Log(string.Format(
CultureInfo.CurrentCulture,
"logon = {0} PrimaryScreenBounds = {1},{2},{3},{4} desktopBounds = {5},{6},{7},{8}",
Common.RunOnLogonDesktop,
WinAPI.newPrimaryScreenBounds.Left,
WinAPI.newPrimaryScreenBounds.Top,
WinAPI.newPrimaryScreenBounds.Right,
WinAPI.newPrimaryScreenBounds.Bottom,
WinAPI.newDesktopBounds.Left,
WinAPI.newDesktopBounds.Top,
WinAPI.newDesktopBounds.Right,
WinAPI.newDesktopBounds.Bottom));
#if USE_MANAGED_ROUTINES
// Managed routines do not work well when running on secure desktop:(
screenWidth = Screen.PrimaryScreen.Bounds.Width;
screenHeight = Screen.PrimaryScreen.Bounds.Height;
screenCount = Screen.AllScreens.Length;
for (int i = 0; i < Screen.AllScreens.Length; i++)
{
if (Screen.AllScreens[i].Bounds.Left < desktopBounds.Left) desktopBounds.Left = Screen.AllScreens[i].Bounds.Left;
if (Screen.AllScreens[i].Bounds.Top < desktopBounds.Top) desktopBounds.Top = Screen.AllScreens[i].Bounds.Top;
if (Screen.AllScreens[i].Bounds.Right > desktopBounds.Right) desktopBounds.Right = Screen.AllScreens[i].Bounds.Right;
if (Screen.AllScreens[i].Bounds.Bottom > desktopBounds.Bottom) desktopBounds.Bottom = Screen.AllScreens[i].Bounds.Bottom;
}
#else
lock (SensitivePoints)
{
SensitivePoints.Clear();
}
NativeMethods.EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, MonitorEnumProc, IntPtr.Zero);
// 1000 calls to EnumDisplayMonitors cost a dozen of milliseconds
#endif
Interlocked.Exchange(ref MachineStuff.desktopBounds, newDesktopBounds);
Interlocked.Exchange(ref MachineStuff.primaryScreenBounds, newPrimaryScreenBounds);
Logger.Log(string.Format(
CultureInfo.CurrentCulture,
"logon = {0} PrimaryScreenBounds = {1},{2},{3},{4} desktopBounds = {5},{6},{7},{8}",
Common.RunOnLogonDesktop,
MachineStuff.PrimaryScreenBounds.Left,
MachineStuff.PrimaryScreenBounds.Top,
MachineStuff.PrimaryScreenBounds.Right,
MachineStuff.PrimaryScreenBounds.Bottom,
MachineStuff.DesktopBounds.Left,
MachineStuff.DesktopBounds.Top,
MachineStuff.DesktopBounds.Right,
MachineStuff.DesktopBounds.Bottom));
Logger.Log("==================== GetScreenConfig ended");
}
catch (Exception e)
{
Logger.Log(e);
}
}
#if USING_SCREEN_SAVER_ROUTINES
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int PostMessage(IntPtr hWnd, int wMsg, int wParam, int lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr OpenDesktop(string hDesktop, int Flags, bool Inherit, UInt32 DesiredAccess);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern bool CloseDesktop(IntPtr hDesktop);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern bool EnumDesktopWindows( IntPtr hDesktop, EnumDesktopWindowsProc callback, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern bool IsWindowVisible(IntPtr hWnd);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern bool SystemParametersInfo(int uAction, int uParam, ref int pvParam, int flags);
private delegate bool EnumDesktopWindowsProc(IntPtr hDesktop, IntPtr lParam);
private const int WM_CLOSE = 16;
private const int SPI_GETSCREENSAVERRUNNING = 114;
internal static bool IsScreenSaverRunning()
{
int isRunning = 0;
SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0,ref isRunning, 0);
return (isRunning != 0);
}
internal static void CloseScreenSaver()
{
IntPtr hDesktop = OpenDesktop("Screen-saver", 0, false, DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS);
if (hDesktop != IntPtr.Zero)
{
LogDebug("Closing screen saver...");
EnumDesktopWindows(hDesktop, new EnumDesktopWindowsProc(CloseScreenSaverFunc), IntPtr.Zero);
CloseDesktop(hDesktop);
}
}
private static bool CloseScreenSaverFunc(IntPtr hWnd, IntPtr lParam)
{
if (IsWindowVisible(hWnd))
{
LogDebug("Posting WM_CLOSE to " + hWnd.ToString(CultureInfo.InvariantCulture));
PostMessage(hWnd, WM_CLOSE, 0, 0);
}
return true;
}
#endif
internal static string GetMyDesktop()
{
byte[] arThreadDesktop = new byte[256];
IntPtr hD = NativeMethods.GetThreadDesktop(NativeMethods.GetCurrentThreadId());
if (hD != IntPtr.Zero)
{
_ = NativeMethods.GetUserObjectInformation(hD, NativeMethods.UOI_NAME, arThreadDesktop, arThreadDesktop.Length, out _);
return Common.GetString(arThreadDesktop).Replace("\0", string.Empty);
}
return string.Empty;
}
internal static string GetInputDesktop()
{
byte[] arInputDesktop = new byte[256];
IntPtr hD = NativeMethods.OpenInputDesktop(0, false, NativeMethods.DESKTOP_READOBJECTS);
if (hD != IntPtr.Zero)
{
_ = NativeMethods.GetUserObjectInformation(hD, NativeMethods.UOI_NAME, arInputDesktop, arInputDesktop.Length, out _);
return Common.GetString(arInputDesktop).Replace("\0", string.Empty);
}
return string.Empty;
}
private static void StartMMService(string desktopToRunMouseWithoutBordersOn)
{
if (!Common.RunWithNoAdminRight)
{
Logger.LogDebug("*** Starting on active Desktop: " + desktopToRunMouseWithoutBordersOn);
Service.StartMouseWithoutBordersService(desktopToRunMouseWithoutBordersOn);
}
}
internal static void CheckForDesktopSwitchEvent(bool cleanupIfExit)
{
try
{
if (!IsMyDesktopActive() || Common.CurrentProcess.SessionId != NativeMethods.WTSGetActiveConsoleSessionId())
{
Helper.RunDDHelper(true);
int waitCount = 20;
while (NativeMethods.WTSGetActiveConsoleSessionId() == 0xFFFFFFFF && waitCount > 0)
{
waitCount--;
Logger.LogDebug("The session is detached/attached.");
Thread.Sleep(500);
}
string myDesktop = GetMyDesktop();
activeDesktop = GetInputDesktop();
Logger.LogDebug("*** Active Desktop = " + activeDesktop);
Logger.LogDebug("*** My Desktop = " + myDesktop);
if (myDesktop.Equals(activeDesktop, StringComparison.OrdinalIgnoreCase))
{
Logger.LogDebug("*** Active Desktop == My Desktop (TS session)");
}
if (!activeDesktop.Equals("winlogon", StringComparison.OrdinalIgnoreCase) &&
!activeDesktop.Equals("default", StringComparison.OrdinalIgnoreCase) &&
!activeDesktop.Equals("disconnect", StringComparison.OrdinalIgnoreCase))
{
try
{
StartMMService(activeDesktop);
}
catch (Exception e)
{
Logger.Log($"{nameof(CheckForDesktopSwitchEvent)}: {e}");
}
}
else
{
if (!myDesktop.Equals(activeDesktop, StringComparison.OrdinalIgnoreCase))
{
Logger.Log("*** Active Desktop <> My Desktop");
}
uint sid = NativeMethods.WTSGetActiveConsoleSessionId();
if (Process.GetProcessesByName(Common.BinaryName).Any(p => (uint)p.SessionId == sid))
{
Logger.Log("Found MouseWithoutBorders on the active session!");
}
else
{
Logger.Log("MouseWithoutBorders not found on the active session!");
StartMMService(null);
}
}
if (!myDesktop.Equals("winlogon", StringComparison.OrdinalIgnoreCase) &&
!myDesktop.Equals("default", StringComparison.OrdinalIgnoreCase))
{
Logger.LogDebug("*** Desktop inactive, exiting: " + myDesktop);
Setting.Values.LastX = Common.JUST_GOT_BACK_FROM_SCREEN_SAVER;
if (cleanupIfExit)
{
InitAndCleanup.Cleanup();
}
Process.GetCurrentProcess().KillProcess();
}
}
}
catch (Exception e)
{
Logger.Log(e);
}
}
private static Point p;
internal static bool IsMyDesktopActive()
{
return NativeMethods.GetCursorPos(ref p);
}
}

View File

@@ -42,7 +42,7 @@ namespace MouseWithoutBorders
protected string GetSecureKey()
{
return Common.MyKey;
return Encryption.MyKey;
}
private void BackButton_Click(object sender, EventArgs e)

View File

@@ -89,8 +89,8 @@ namespace MouseWithoutBorders
{
if (GetSecureKey() != SecurityCodeField.Text)
{
Common.MyKey = Regex.Replace(SecurityCodeField.Text, @"\s+", string.Empty);
SecurityCode = Common.MyKey;
Encryption.MyKey = Regex.Replace(SecurityCodeField.Text, @"\s+", string.Empty);
SecurityCode = Encryption.MyKey;
}
MachineStuff.MachineMatrix = new string[MachineStuff.MAX_MACHINE] { ComputerNameField.Text.Trim().ToUpper(CultureInfo.CurrentCulture), Common.MachineName.Trim(), string.Empty, string.Empty };

View File

@@ -135,7 +135,7 @@ namespace MouseWithoutBorders
internal void UpdateKeyTextBox()
{
_ = Helper.GetUserName();
textBoxEnc.Text = Common.MyKey;
textBoxEnc.Text = Encryption.MyKey;
}
private void InitAll()
@@ -505,19 +505,19 @@ namespace MouseWithoutBorders
private bool UpdateKey(string newKey)
{
if (!Common.IsKeyValid(newKey, out string rv))
if (!Encryption.IsKeyValid(newKey, out string rv))
{
ShowKeyErrorMsg(rv);
return false;
}
if (!newKey.Equals(Common.MyKey, StringComparison.OrdinalIgnoreCase))
if (!newKey.Equals(Encryption.MyKey, StringComparison.OrdinalIgnoreCase))
{
Common.MyKey = newKey;
Common.GeneratedKey = false;
Encryption.MyKey = newKey;
Encryption.GeneratedKey = false;
}
Common.MagicNumber = Common.Get24BitHash(Common.MyKey);
Encryption.MagicNumber = Encryption.Get24BitHash(Encryption.MyKey);
return true;
}
@@ -1116,10 +1116,10 @@ namespace MouseWithoutBorders
if (MessageBox.Show(message, Application.ProductName, MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2) == DialogResult.Yes)
{
Setting.Values.MyKey = Common.MyKey = Common.CreateRandomKey();
textBoxEnc.Text = Common.MyKey;
Setting.Values.MyKey = Encryption.MyKey = Encryption.CreateRandomKey();
textBoxEnc.Text = Encryption.MyKey;
checkBoxShowKey.Checked = true;
Common.GeneratedKey = true;
Encryption.GeneratedKey = true;
ButtonOK_Click(null, null);
Common.ShowToolTip("New security key was generated, update other machines to the same key.", 10000, ToolTipIcon.Info, false);
}

View File

@@ -318,7 +318,7 @@ namespace MouseWithoutBorders
try
{
if (!Common.IsMyDesktopActive() || Common.CurrentProcess.SessionId != NativeMethods.WTSGetActiveConsoleSessionId())
if (!WinAPI.IsMyDesktopActive() || Common.CurrentProcess.SessionId != NativeMethods.WTSGetActiveConsoleSessionId())
{
myDesktopNotActive = true;
@@ -348,7 +348,7 @@ namespace MouseWithoutBorders
Common.Hook?.ResetLastSwitchKeys();
});
Common.CheckForDesktopSwitchEvent(true);
WinAPI.CheckForDesktopSwitchEvent(true);
}
}
else
@@ -369,21 +369,21 @@ namespace MouseWithoutBorders
if (myDesktopNotActive)
{
myDesktopNotActive = false;
Common.MyKey = Setting.Values.MyKey;
Encryption.MyKey = Setting.Values.MyKey;
}
MachineStuff.UpdateMachinePoolStringSetting();
if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop && (Setting.Values.FirstRun || Common.KeyCorrupted))
if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop && (Setting.Values.FirstRun || Encryption.KeyCorrupted))
{
if (!shownSetupFormOneTime)
{
shownSetupFormOneTime = true;
MachineStuff.ShowMachineMatrix();
if (Common.KeyCorrupted && !Setting.Values.FirstRun)
if (Encryption.KeyCorrupted && !Setting.Values.FirstRun)
{
Common.KeyCorrupted = false;
Encryption.KeyCorrupted = false;
string msg = "The security key is corrupted for some reason, please re-setup.";
MessageBox.Show(msg, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
@@ -490,9 +490,9 @@ namespace MouseWithoutBorders
if (count == 600)
{
if (!Common.GeneratedKey)
if (!Encryption.GeneratedKey)
{
Common.MyKey = Setting.Values.MyKey;
Encryption.MyKey = Setting.Values.MyKey;
if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop)
{
@@ -505,7 +505,7 @@ namespace MouseWithoutBorders
Common.ShowToolTip("The security key must be auto generated in one of the machines.", 10000);
}
}
else if (!Common.KeyCorrupted && !Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop && !Setting.Values.FirstRun && Common.AtLeastOneSocketConnected())
else if (!Encryption.KeyCorrupted && !Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop && !Setting.Values.FirstRun && Common.AtLeastOneSocketConnected())
{
int myKeyDaysToExpire = Setting.Values.MyKeyDaysToExpire;
@@ -531,7 +531,7 @@ namespace MouseWithoutBorders
#if SHOW_ON_WINLOGON
// if (Common.RunOnLogonDesktop) ShowMouseWithoutBordersUiOnWinLogonDesktop(false);
#endif
Common.CheckForDesktopSwitchEvent(true);
WinAPI.CheckForDesktopSwitchEvent(true);
MachineStuff.UpdateClientSockets("helperTimer_Tick"); // Sockets may be closed by the remote host when both machines switch desktop at the same time.
}
@@ -582,7 +582,7 @@ namespace MouseWithoutBorders
int rv = 0;
if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop && Common.IsMyDesktopActive() && (rv = Helper.SendMessageToHelper(0x400, IntPtr.Zero, IntPtr.Zero)) <= 0)
if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop && WinAPI.IsMyDesktopActive() && (rv = Helper.SendMessageToHelper(0x400, IntPtr.Zero, IntPtr.Zero)) <= 0)
{
Logger.TelemetryLogTrace($"{Helper.HELPER_FORM_TEXT} not found: {rv}", SeverityLevel.Warning);
}

View File

@@ -46,79 +46,6 @@ avgSendTime = 0
maxSendTime = 0
totalSendCount = 0
totalSendTime = 0
magicNumber = 0
ran = System.Random
--_impl = System.Random+XoshiroImpl
----_s0 = ????????????
----_s1 = ????????????
----_s2 = ????????????
----_s3 = ????????????
--<Shared>k__BackingField = System.Random+ThreadSafeRandom
InitialIV = ????????????
<GeneratedKey>k__BackingField = False
<KeyCorrupted>k__BackingField = False
LegalKeyDictionary = Concurrent.ConcurrentDictionary`2[System.String,System.Byte[]]
--_tables = Concurrent.ConcurrentDictionary`2+Tables[System.String,System.Byte[]]
----_comparer = Generic.NonRandomizedStringEqualityComparer+OrdinalIgnoreCaseComparer
----_buckets = Concurrent.ConcurrentDictionary`2+VolatileNode[System.String,System.Byte[]][]
------System.Collections.Concurrent.ConcurrentDictionary`2+VolatileNode[System.String,System.Byte[]][] = Concurrent.ConcurrentDictionary`2+VolatileNode[System.String,System.Byte[]][]: N/A
----_fastModBucketsMultiplier = 498560650640798693
----_locks = O[]
------System.Object[] = O[]: N/A
----_countPerLock = 32[]
------[0] = 0
------[1] = 0
------[2] = 0
------[3] = 0
------[4] = 0
------[5] = 0
------[6] = 0
------[7] = 0
--_budget = ????????????
--_growLockArray = True
--_comparerIsDefaultForClasses = False
PackageSent = MouseWithoutBorders.PackageMonitor
--Keyboard = 0
--Mouse = 0
--Heartbeat = 0
--ByeBye = 0
--Hello = 0
--Matrix = 0
--ClipboardText = 0
--ClipboardImage = 0
--Clipboard = 0
--ClipboardDragDrop = 0
--ClipboardDragDropEnd = 0
--ClipboardAsk = 0
--ExplorerDragDrop = 0
--Nil = 0
PackageReceived = MouseWithoutBorders.PackageMonitor
--Keyboard = 0
--Mouse = 0
--Heartbeat = 0
--ByeBye = 0
--Hello = 0
--Matrix = 0
--ClipboardText = 0
--ClipboardImage = 0
--Clipboard = 0
--ClipboardDragDrop = 0
--ClipboardDragDropEnd = 0
--ClipboardAsk = 0
--ExplorerDragDrop = 0
--Nil = 0
PackageID = 0
SensitivePoints = Generic.List`1[Point]
--_items = Point[]
----System.Drawing.Point[] = Point[]: N/A
--_size = 0
--_version = 0
--s_emptyArray = Point[]
----System.Drawing.Point[] = Point[]: N/A
p = {X=0,Y=0}
--x = 0
--y = 0
--Empty = {X=0,Y=0}
<IpcChannelCreated>k__BackingField = False
TOGGLE_ICONS_SIZE = 4
ICON_ONE = 0
@@ -128,34 +55,6 @@ ICON_BIG_CLIPBOARD = 3
ICON_ERROR = 4
JUST_GOT_BACK_FROM_SCREEN_SAVER = 9999
NETWORK_STREAM_BUF_SIZE = 1048576
SymAlBlockSize = 16
PW_LENGTH = 16
PACKAGE_SIZE = 32
PACKAGE_SIZE_EX = 64
WP_PACKAGE_SIZE = 6
KEYEVENTF_KEYDOWN = 1
KEYEVENTF_KEYUP = 2
WH_MOUSE = 7
WH_KEYBOARD = 2
WH_MOUSE_LL = 14
WH_KEYBOARD_LL = 13
WM_MOUSEMOVE = 512
WM_LBUTTONDOWN = 513
WM_RBUTTONDOWN = 516
WM_MBUTTONDOWN = 519
WM_XBUTTONDOWN = 523
WM_LBUTTONUP = 514
WM_RBUTTONUP = 517
WM_MBUTTONUP = 520
WM_XBUTTONUP = 524
WM_LBUTTONDBLCLK = 515
WM_RBUTTONDBLCLK = 518
WM_MBUTTONDBLCLK = 521
WM_MOUSEWHEEL = 522
WM_KEYDOWN = 256
WM_KEYUP = 257
WM_SYSKEYDOWN = 260
WM_SYSKEYUP = 261
[Clipboard]
===============
Comma = System.Char[]
@@ -193,16 +92,51 @@ dragDropStep05ExCalledByIpc = 0
isDropping = False
dragMachine = NONE
<MouseDown>k__BackingField = False
[Encryption]
===============
magicNumber = 0
ran = System.Random
--_impl = System.Random+XoshiroImpl
----_s0 = ????????????
----_s1 = ????????????
----_s2 = ????????????
----_s3 = ????????????
--<Shared>k__BackingField = System.Random+ThreadSafeRandom
InitialIV = ????????????
<GeneratedKey>k__BackingField = False
<KeyCorrupted>k__BackingField = False
LegalKeyDictionary = Concurrent.ConcurrentDictionary`2[System.String,System.Byte[]]
--_tables = Concurrent.ConcurrentDictionary`2+Tables[System.String,System.Byte[]]
----_comparer = Generic.NonRandomizedStringEqualityComparer+OrdinalIgnoreCaseComparer
----_buckets = Concurrent.ConcurrentDictionary`2+VolatileNode[System.String,System.Byte[]][]
------System.Collections.Concurrent.ConcurrentDictionary`2+VolatileNode[System.String,System.Byte[]][] = Concurrent.ConcurrentDictionary`2+VolatileNode[System.String,System.Byte[]][]: N/A
----_fastModBucketsMultiplier = 498560650640798693
----_locks = O[]
------System.Object[] = O[]: N/A
----_countPerLock = 32[]
------[0] = 0
------[1] = 0
------[2] = 0
------[3] = 0
------[4] = 0
------[5] = 0
------[6] = 0
------[7] = 0
--_budget = ????????????
--_growLockArray = True
--_comparerIsDefaultForClasses = False
SymAlBlockSize = 16
PW_LENGTH = 16
[Event]
===============
KeybdPackage = MouseWithoutBorders.DATA
KeybdPackage = MouseWithoutBorders.Core.DATA
--Type = 0
--Id = 0
--Src = NONE
--Des = NONE
--DateTime = 0
--Kd = MouseWithoutBorders.KEYBDDATA
--Md = MouseWithoutBorders.MOUSEDATA
--Kd = MouseWithoutBorders.Core.KEYBDDATA
--Md = MouseWithoutBorders.Core.MOUSEDATA
--Machine1 = NONE
--Machine2 = NONE
--Machine3 = NONE
@@ -212,14 +146,14 @@ KeybdPackage = MouseWithoutBorders.DATA
--machineNameP2 = 0
--machineNameP3 = 0
--machineNameP4 = 0
MousePackage = MouseWithoutBorders.DATA
MousePackage = MouseWithoutBorders.Core.DATA
--Type = 0
--Id = 0
--Src = NONE
--Des = NONE
--DateTime = 0
--Kd = MouseWithoutBorders.KEYBDDATA
--Md = MouseWithoutBorders.MOUSEDATA
--Kd = MouseWithoutBorders.Core.KEYBDDATA
--Md = MouseWithoutBorders.Core.MOUSEDATA
--Machine1 = NONE
--Machine2 = NONE
--Machine3 = NONE
@@ -296,7 +230,7 @@ LogCounter = Concurrent.ConcurrentDictionary`2[System.String,32]
allLogsIndex = 0
lastHour = 0
exceptionCount = 0
lastPackageSent = MouseWithoutBorders.PackageMonitor
lastPackageSent = MouseWithoutBorders.Core.PackageMonitor
--Keyboard = 0
--Mouse = 0
--Heartbeat = 0
@@ -311,7 +245,7 @@ lastPackageSent = MouseWithoutBorders.PackageMonitor
--ClipboardAsk = 0
--ExplorerDragDrop = 0
--Nil = 0
lastPackageReceived = MouseWithoutBorders.PackageMonitor
lastPackageReceived = MouseWithoutBorders.Core.PackageMonitor
--Keyboard = 0
--Mouse = 0
--Heartbeat = 0
@@ -366,6 +300,42 @@ MAX_SOCKET = 8
HEARTBEAT_TIMEOUT = 1500000
SKIP_PIXELS = 1
JUMP_PIXELS = 2
[Package]
===============
PackageSent = MouseWithoutBorders.Core.PackageMonitor
--Keyboard = 0
--Mouse = 0
--Heartbeat = 0
--ByeBye = 0
--Hello = 0
--Matrix = 0
--ClipboardText = 0
--ClipboardImage = 0
--Clipboard = 0
--ClipboardDragDrop = 0
--ClipboardDragDropEnd = 0
--ClipboardAsk = 0
--ExplorerDragDrop = 0
--Nil = 0
PackageReceived = MouseWithoutBorders.Core.PackageMonitor
--Keyboard = 0
--Mouse = 0
--Heartbeat = 0
--ByeBye = 0
--Hello = 0
--Matrix = 0
--ClipboardText = 0
--ClipboardImage = 0
--Clipboard = 0
--ClipboardDragDrop = 0
--ClipboardDragDropEnd = 0
--ClipboardAsk = 0
--ExplorerDragDrop = 0
--Nil = 0
PackageID = 0
PACKAGE_SIZE = 32
PACKAGE_SIZE_EX = 64
WP_PACKAGE_SIZE = 6
[Receiver]
===============
QUEUE_SIZE = 50
@@ -436,3 +406,41 @@ lastStartServiceTime = ????????????
--MinValue = 01/01/0001 00:00:00
--MaxValue = 31/12/9999 23:59:59
--UnixEpoch = 01/01/1970 00:00:00
[WinAPI]
===============
SensitivePoints = Generic.List`1[Point]
--_items = Point[]
----System.Drawing.Point[] = Point[]: N/A
--_size = 0
--_version = 0
--s_emptyArray = Point[]
----System.Drawing.Point[] = Point[]: N/A
p = {X=0,Y=0}
--x = 0
--y = 0
--Empty = {X=0,Y=0}
[WM]
===============
KEYEVENTF_KEYDOWN = 1
KEYEVENTF_KEYUP = 2
WH_MOUSE = 7
WH_KEYBOARD = 2
WH_MOUSE_LL = 14
WH_KEYBOARD_LL = 13
WM_MOUSEMOVE = 512
WM_LBUTTONDOWN = 513
WM_RBUTTONDOWN = 516
WM_MBUTTONDOWN = 519
WM_XBUTTONDOWN = 523
WM_LBUTTONUP = 514
WM_RBUTTONUP = 517
WM_MBUTTONUP = 520
WM_XBUTTONUP = 524
WM_LBUTTONDBLCLK = 515
WM_RBUTTONDBLCLK = 518
WM_MBUTTONDBLCLK = 521
WM_MOUSEWHEEL = 522
WM_KEYDOWN = 256
WM_KEYUP = 257
WM_SYSKEYDOWN = 260
WM_SYSKEYUP = 261

View File

@@ -161,7 +161,7 @@ public partial class MainListPage : DynamicListPage,
{
return _tlcManager
.TopLevelCommands
.Where(tlc => !string.IsNullOrEmpty(tlc.Title))
.Where(tlc => !tlc.IsFallback && !string.IsNullOrEmpty(tlc.Title))
.ToArray();
}
}

View File

@@ -135,8 +135,9 @@ public partial class App : Application
try
{
var winget = new WinGetExtensionCommandsProvider();
var callback = allApps.LookupApp;
winget.SetAllLookup(callback);
winget.SetAllLookup(
query => allApps.LookupAppByPackageFamilyName(query, requireSingleMatch: true),
query => allApps.LookupAppByProductCode(query, requireSingleMatch: true));
services.AddSingleton<ICommandProvider>(winget);
}
catch (Exception ex)

View File

@@ -20,13 +20,15 @@
<winuiex:WindowEx.SystemBackdrop>
<MicaBackdrop />
</winuiex:WindowEx.SystemBackdrop>
<Grid>
<Grid x:Name="RootElement">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TitleBar
x:Name="AppTitleBar"
BackRequested="TitleBar_BackRequested"
IsBackButtonVisible="{x:Bind NavFrame.CanGoBack, Mode=OneWay}"
IsTabStop="False"
PaneToggleRequested="AppTitleBar_PaneToggleRequested">
<!-- This is a workaround for https://github.com/microsoft/microsoft-ui-xaml/issues/10374, once fixed we should just be using IconSource -->
@@ -56,10 +58,12 @@
</NavigationView.Resources>
<NavigationView.MenuItems>
<NavigationViewItem
x:Name="GeneralPageNavItem"
x:Uid="Settings_GeneralPage_NavigationViewItem_General"
Icon="{ui:FontIcon Glyph=&#xE80F;}"
Tag="General" />
<NavigationViewItem
x:Name="ExtensionPageNavItem"
x:Uid="Settings_GeneralPage_NavigationViewItem_Extensions"
Icon="{ui:FontIcon Glyph=&#xEA86;}"
Tag="Extensions" />
@@ -90,7 +94,10 @@
</BreadcrumbBar.Resources>
</BreadcrumbBar>
</Grid>
<Frame x:Name="NavFrame" Grid.Row="1" />
<Frame
x:Name="NavFrame"
Grid.Row="1"
Navigated="NavFrame_OnNavigated" />
</Grid>
</NavigationView>
</Grid>

View File

@@ -4,14 +4,19 @@
using System.Collections.ObjectModel;
using CommunityToolkit.Mvvm.Messaging;
using ManagedCommon;
using Microsoft.CmdPal.UI.Helpers;
using Microsoft.CmdPal.UI.Messages;
using Microsoft.CmdPal.UI.ViewModels;
using Microsoft.CmdPal.UI.ViewModels.Messages;
using Microsoft.UI.Input;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Automation.Peers;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Navigation;
using Windows.System;
using Windows.UI.Core;
using WinUIEx;
using RS_ = Microsoft.CmdPal.UI.Helpers.ResourceLoaderInstance;
using TitleBar = Microsoft.UI.Xaml.Controls.TitleBar;
@@ -19,9 +24,12 @@ using TitleBar = Microsoft.UI.Xaml.Controls.TitleBar;
namespace Microsoft.CmdPal.UI.Settings;
public sealed partial class SettingsWindow : WindowEx,
IDisposable,
IRecipient<NavigateToExtensionSettingsMessage>,
IRecipient<QuitMessage>
{
private readonly LocalKeyboardListener _localKeyboardListener;
public ObservableCollection<Crumb> BreadCrumbs { get; } = [];
// Gets or sets optional action invoked after NavigationView is loaded.
@@ -40,6 +48,17 @@ public sealed partial class SettingsWindow : WindowEx,
WeakReferenceMessenger.Default.Register<NavigateToExtensionSettingsMessage>(this);
WeakReferenceMessenger.Default.Register<QuitMessage>(this);
_localKeyboardListener = new LocalKeyboardListener();
_localKeyboardListener.KeyPressed += LocalKeyboardListener_OnKeyPressed;
_localKeyboardListener.Start();
Closed += SettingsWindow_Closed;
RootElement.AddHandler(UIElement.PointerPressedEvent, new PointerEventHandler(RootElement_OnPointerPressed), true);
}
private void SettingsWindow_Closed(object sender, WindowEventArgs args)
{
Dispose();
}
// Handles NavigationView loaded event.
@@ -85,10 +104,9 @@ public sealed partial class SettingsWindow : WindowEx,
"Extensions" => typeof(ExtensionsPage),
_ => null,
};
if (pageType is not null)
{
BreadCrumbs.Clear();
BreadCrumbs.Add(new(page, page));
NavFrame.Navigate(pageType);
}
}
@@ -96,7 +114,6 @@ public sealed partial class SettingsWindow : WindowEx,
private void Navigate(ProviderSettingsViewModel extension)
{
NavFrame.Navigate(typeof(ExtensionPage), extension);
BreadCrumbs.Add(new(extension.DisplayName, string.Empty));
}
private void PositionCentered()
@@ -127,9 +144,9 @@ public sealed partial class SettingsWindow : WindowEx,
}
}
private void Window_Activated(object sender, WindowActivatedEventArgs args)
private void Window_Activated(object sender, Microsoft.UI.Xaml.WindowActivatedEventArgs args)
{
WeakReferenceMessenger.Default.Send<WindowActivatedEventArgs>(args);
WeakReferenceMessenger.Default.Send<Microsoft.UI.Xaml.WindowActivatedEventArgs>(args);
}
private void Window_Closed(object sender, WindowEventArgs args)
@@ -141,7 +158,7 @@ public sealed partial class SettingsWindow : WindowEx,
private void NavView_DisplayModeChanged(NavigationView sender, NavigationViewDisplayModeChangedEventArgs args)
{
if (args.DisplayMode == NavigationViewDisplayMode.Compact || args.DisplayMode == NavigationViewDisplayMode.Minimal)
if (args.DisplayMode is NavigationViewDisplayMode.Compact or NavigationViewDisplayMode.Minimal)
{
AppTitleBar.IsPaneToggleButtonVisible = true;
WorkAroundIcon.Margin = new Thickness(8, 0, 16, 0); // Required for workaround, see XAML comment
@@ -149,7 +166,7 @@ public sealed partial class SettingsWindow : WindowEx,
else
{
AppTitleBar.IsPaneToggleButtonVisible = false;
WorkAroundIcon.Margin = new Thickness(16, 0, 0, 0); // Required for workaround, see XAML comment
WorkAroundIcon.Margin = new Thickness(16, 0, 8, 0); // Required for workaround, see XAML comment
}
}
@@ -163,6 +180,93 @@ public sealed partial class SettingsWindow : WindowEx,
{
NavView.IsPaneOpen = !NavView.IsPaneOpen;
}
private void TryGoBack()
{
if (NavFrame.CanGoBack)
{
NavFrame.GoBack();
}
}
private void TitleBar_BackRequested(TitleBar sender, object args)
{
TryGoBack();
}
private void LocalKeyboardListener_OnKeyPressed(object? sender, LocalKeyboardListenerKeyPressedEventArgs e)
{
switch (e.Key)
{
case VirtualKey.GoBack:
case VirtualKey.XButton1:
TryGoBack();
break;
case VirtualKey.Left:
var altPressed = InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Menu).HasFlag(CoreVirtualKeyStates.Down);
if (altPressed)
{
TryGoBack();
}
break;
}
}
private void RootElement_OnPointerPressed(object sender, PointerRoutedEventArgs e)
{
try
{
if (e.Pointer.PointerDeviceType == PointerDeviceType.Mouse)
{
var ptrPt = e.GetCurrentPoint(RootElement);
if (ptrPt.Properties.IsXButton1Pressed)
{
TryGoBack();
}
}
}
catch (Exception ex)
{
Logger.LogError("Error handling mouse button press event", ex);
}
}
public void Dispose()
{
_localKeyboardListener?.Dispose();
}
private void NavFrame_OnNavigated(object sender, NavigationEventArgs e)
{
BreadCrumbs.Clear();
if (e.SourcePageType == typeof(GeneralPage))
{
NavView.SelectedItem = GeneralPageNavItem;
var pageType = RS_.GetString("Settings_PageTitles_GeneralPage");
BreadCrumbs.Add(new(pageType, pageType));
}
else if (e.SourcePageType == typeof(ExtensionsPage))
{
NavView.SelectedItem = ExtensionPageNavItem;
var pageType = RS_.GetString("Settings_PageTitles_ExtensionsPage");
BreadCrumbs.Add(new(pageType, pageType));
}
else if (e.SourcePageType == typeof(ExtensionPage) && e.Parameter is ProviderSettingsViewModel vm)
{
NavView.SelectedItem = ExtensionPageNavItem;
var extensionsPageType = RS_.GetString("Settings_PageTitles_ExtensionsPage");
BreadCrumbs.Add(new(extensionsPageType, extensionsPageType));
BreadCrumbs.Add(new(vm.DisplayName, vm));
}
else
{
BreadCrumbs.Add(new($"[{e.SourcePageType?.Name}]", string.Empty));
Logger.LogError($"Unknown breadcrumb for page type '{e.SourcePageType}'");
}
}
}
public readonly struct Crumb

View File

@@ -550,4 +550,10 @@ Right-click to remove the key combination, thereby deactivating the shortcut.</v
<data name="Settings_GeneralPage_AutoGoHome_SettingsCard.Description" xml:space="preserve">
<value>Automatically returns to home page after a period of inactivity when Command Palette is closed</value>
</data>
<data name="Settings_PageTitles_GeneralPage" xml:space="preserve">
<value>General</value>
</data>
<data name="Settings_PageTitles_ExtensionsPage" xml:space="preserve">
<value>Extensions</value>
</data>
</root>

View File

@@ -58,7 +58,7 @@ public class AllAppsCommandProviderTests : AppsTestBase
var provider = new AllAppsCommandProvider(page);
// Act
var result = provider.LookupApp(string.Empty);
var result = provider.LookupAppByDisplayName(string.Empty);
// Assert
Assert.IsNotNull(result);
@@ -77,7 +77,7 @@ public class AllAppsCommandProviderTests : AppsTestBase
await WaitForPageInitializationAsync();
// Act
var result = provider.LookupApp("TestApp");
var result = provider.LookupAppByDisplayName("TestApp");
// Assert
Assert.IsNotNull(result);
@@ -97,7 +97,7 @@ public class AllAppsCommandProviderTests : AppsTestBase
await WaitForPageInitializationAsync();
// Act
var result = provider.LookupApp("NonExistentApp");
var result = provider.LookupAppByDisplayName("NonExistentApp");
// Assert
Assert.IsNull(result);

View File

@@ -73,13 +73,11 @@ public class FallbackRemoteDesktopItemTests
fallback.UpdateQuery(" ");
// Assert
Assert.AreEqual(Resources.remotedesktop_command_open, fallback.Title);
Assert.AreEqual(string.Empty, fallback.Title);
Assert.AreEqual(string.Empty, fallback.Subtitle);
var command = fallback.Command as OpenRemoteDesktopCommand;
Assert.IsNotNull(command);
Assert.AreEqual(Resources.remotedesktop_command_open, command.Name);
Assert.AreEqual(string.Empty, GetCommandHost(command));
Assert.IsNull(command);
}
[TestMethod]
@@ -93,13 +91,11 @@ public class FallbackRemoteDesktopItemTests
fallback.UpdateQuery("not a valid host");
// Assert
Assert.AreEqual(Resources.remotedesktop_command_open, fallback.Title);
Assert.AreEqual(string.Empty, fallback.Title);
Assert.AreEqual(string.Empty, fallback.Subtitle);
var command = fallback.Command as OpenRemoteDesktopCommand;
Assert.IsNotNull(command);
Assert.AreEqual(Resources.remotedesktop_command_open, command.Name);
Assert.AreEqual(string.Empty, GetCommandHost(command));
Assert.IsNull(command);
}
private static string GetCommandHost(OpenRemoteDesktopCommand command)

View File

@@ -4,6 +4,8 @@
using System;
using System.Collections.Generic;
using Microsoft.CmdPal.Ext.Apps.Helpers;
using Microsoft.CmdPal.Ext.Apps.Programs;
using Microsoft.CmdPal.Ext.Apps.Properties;
using Microsoft.CmdPal.Ext.Apps.State;
using Microsoft.CommandPalette.Extensions;
@@ -66,7 +68,71 @@ public partial class AllAppsCommandProvider : CommandProvider
public override ICommandItem[] TopLevelCommands() => [_listItem, .. _page.GetPinnedApps()];
public ICommandItem? LookupApp(string displayName)
public ICommandItem? LookupAppByPackageFamilyName(string packageFamilyName, bool requireSingleMatch)
{
if (string.IsNullOrEmpty(packageFamilyName))
{
return null;
}
var items = _page.GetItems();
List<ICommandItem> matches = [];
foreach (var item in items)
{
if (item is AppListItem appItem && string.Equals(packageFamilyName, appItem.App.PackageFamilyName, StringComparison.OrdinalIgnoreCase))
{
matches.Add(item);
if (!requireSingleMatch)
{
// Return early if we don't require uniqueness.
return item;
}
}
}
return requireSingleMatch && matches.Count == 1 ? matches[0] : null;
}
public ICommandItem? LookupAppByProductCode(string productCode, bool requireSingleMatch)
{
if (string.IsNullOrEmpty(productCode))
{
return null;
}
if (!UninstallRegistryAppLocator.TryGetInstallInfo(productCode, out _, out var candidates) || candidates.Count <= 0)
{
return null;
}
var items = _page.GetItems();
List<ICommandItem> matches = [];
foreach (var item in items)
{
if (item is not AppListItem appListItem || string.IsNullOrEmpty(appListItem.App.FullExecutablePath))
{
continue;
}
foreach (var candidate in candidates)
{
if (string.Equals(appListItem.App.FullExecutablePath, candidate, StringComparison.OrdinalIgnoreCase))
{
matches.Add(item);
if (!requireSingleMatch)
{
return item;
}
}
}
}
return requireSingleMatch && matches.Count == 1 ? matches[0] : null;
}
public ICommandItem? LookupAppByDisplayName(string displayName)
{
var items = _page.GetItems();

View File

@@ -29,6 +29,10 @@ public sealed class AppItem
public string AppIdentifier { get; set; } = string.Empty;
public string? PackageFamilyName { get; set; }
public string? FullExecutablePath { get; set; }
public AppItem()
{
}

View File

@@ -40,6 +40,8 @@ public sealed partial class AppListItem : ListItem
public string AppIdentifier => _app.AppIdentifier;
public AppItem App => _app;
public AppListItem(AppItem app, bool useThumbnails, bool isPinned)
{
Command = _appCommand = new AppCommand(app);
@@ -82,6 +84,12 @@ public sealed partial class AppListItem : ListItem
metadata.Add(new DetailsElement() { Key = "Path", Data = new DetailsLink() { Text = _app.ExePath } });
}
#if DEBUG
metadata.Add(new DetailsElement() { Key = "[DEBUG] AppIdentifier", Data = new DetailsLink() { Text = _app.AppIdentifier } });
metadata.Add(new DetailsElement() { Key = "[DEBUG] ExePath", Data = new DetailsLink() { Text = _app.ExePath } });
metadata.Add(new DetailsElement() { Key = "[DEBUG] IcoPath", Data = new DetailsLink() { Text = _app.IcoPath } });
#endif
// Icon
IconInfo? heroImage = null;
if (_app.IsPackaged)

View File

@@ -0,0 +1,205 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Win32;
namespace Microsoft.CmdPal.Ext.Apps.Helpers;
internal static class UninstallRegistryAppLocator
{
private static readonly string[] UninstallBaseKeys =
[
@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall",
@"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall",
];
/// <summary>
/// Tries to find install directory and a list of plausible main EXEs from an uninstall key
/// (e.g. Inno Setup keys like "{guid}_is1").
/// <paramref name="exeCandidates"/> may be empty if we couldn't pick any safe EXEs.
/// </summary>
/// <returns>
/// Returns true if the uninstall key is found and an install directory is resolved.
/// </returns>
public static bool TryGetInstallInfo(
string uninstallKeyName,
out string? installDir,
out IReadOnlyList<string> exeCandidates,
string? expectedExeName = null)
{
installDir = null;
exeCandidates = [];
if (string.IsNullOrWhiteSpace(uninstallKeyName))
{
throw new ArgumentException("Key name must not be null or empty.", nameof(uninstallKeyName));
}
uninstallKeyName = uninstallKeyName.Trim();
foreach (var baseKeyPath in UninstallBaseKeys)
{
// HKLM
using (var key = Registry.LocalMachine.OpenSubKey($"{baseKeyPath}\\{uninstallKeyName}"))
{
if (TryFromUninstallKey(key, expectedExeName, out installDir, out exeCandidates))
{
return true;
}
}
// HKCU
using (var key = Registry.CurrentUser.OpenSubKey($"{baseKeyPath}\\{uninstallKeyName}"))
{
if (TryFromUninstallKey(key, expectedExeName, out installDir, out exeCandidates))
{
return true;
}
}
}
return false;
}
private static bool TryFromUninstallKey(
RegistryKey? key,
string? expectedExeName,
out string? installDir,
out IReadOnlyList<string> exeCandidates)
{
installDir = null;
exeCandidates = [];
if (key is null)
{
return false;
}
var location = (key.GetValue("InstallLocation") as string)?.Trim('"', ' ', '\t');
if (string.IsNullOrEmpty(location))
{
location = (key.GetValue("Inno Setup: App Path") as string)?.Trim('"', ' ', '\t');
}
if (string.IsNullOrEmpty(location))
{
var uninstall = key.GetValue("UninstallString") as string;
var uninsExe = ExtractFirstPath(uninstall);
if (!string.IsNullOrEmpty(uninsExe))
{
var dir = Path.GetDirectoryName(uninsExe);
if (!string.IsNullOrEmpty(dir) && Directory.Exists(dir))
{
location = dir;
}
}
}
if (string.IsNullOrEmpty(location) || !Directory.Exists(location))
{
return false;
}
installDir = location;
// Collect safe EXE candidates; may be empty if ambiguous or only uninstall exes exist.
exeCandidates = GetExeCandidates(location, expectedExeName);
return true;
}
private static IReadOnlyList<string> GetExeCandidates(string root, string? expectedExeName)
{
// Look at root and a "bin" subfolder (very common pattern)
var allExes = Directory.EnumerateFiles(root, "*.exe", SearchOption.TopDirectoryOnly)
.Concat(GetBinExes(root))
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToArray();
if (allExes.Length == 0)
{
return [];
}
var result = new List<string>();
// 1) Exact match on expected exe name (if provided), ignoring case, and not uninstall/setup-like.
if (!string.IsNullOrWhiteSpace(expectedExeName))
{
foreach (var exe in allExes)
{
if (string.Equals(Path.GetFileName(exe), expectedExeName, StringComparison.OrdinalIgnoreCase) &&
!LooksLikeUninstallerOrSetup(exe))
{
result.Add(exe);
}
}
}
// 2) All other non-uninstall/setup exes
foreach (var exe in allExes)
{
if (LooksLikeUninstallerOrSetup(exe))
{
continue;
}
// Skip ones already added as expectedExeName matches
if (result.Contains(exe, StringComparer.OrdinalIgnoreCase))
{
continue;
}
result.Add(exe);
}
// 3) We intentionally do NOT add uninstall/setup/update exes here.
// If you ever want them, you can add a separate API to expose them.
return result;
}
private static IEnumerable<string> GetBinExes(string root)
{
var bin = Path.Combine(root, "bin");
return !Directory.Exists(bin)
? []
: Directory.EnumerateFiles(bin, "*.exe", SearchOption.TopDirectoryOnly);
}
private static bool LooksLikeUninstallerOrSetup(string path)
{
var name = Path.GetFileName(path);
return name.StartsWith("unins", StringComparison.OrdinalIgnoreCase) // e.g. Inno: unins000.exe
|| name.Contains("setup", StringComparison.OrdinalIgnoreCase) // setup.exe
|| name.Contains("installer", StringComparison.OrdinalIgnoreCase) // installer.exe / MyAppInstaller.exe
|| name.Contains("update", StringComparison.OrdinalIgnoreCase); // updater/updater.exe
}
private static string? ExtractFirstPath(string? commandLine)
{
if (string.IsNullOrWhiteSpace(commandLine))
{
return null;
}
commandLine = commandLine.Trim();
if (commandLine.StartsWith('"'))
{
var endQuote = commandLine.IndexOf('"', 1);
if (endQuote > 1)
{
return commandLine[1..endQuote];
}
}
var firstSpace = commandLine.IndexOf(' ');
var candidate = firstSpace > 0 ? commandLine[..firstSpace] : commandLine;
candidate = candidate.Trim('"');
return candidate.Length > 0 ? candidate : null;
}
}

View File

@@ -558,6 +558,7 @@ public class UWPApplication : IUWPApplication
IsPackaged = true,
Commands = app.GetCommands(),
AppIdentifier = app.GetAppIdentifier(),
PackageFamilyName = app.Package.FamilyName,
};
return item;
}

View File

@@ -1065,6 +1065,7 @@ public class Win32Program : IProgram
DirPath = app.Location,
Commands = app.GetCommands(),
AppIdentifier = app.GetAppIdentifier(),
FullExecutablePath = app.FullPath,
};
}
}

View File

@@ -23,13 +23,16 @@ internal sealed partial class FallbackRemoteDesktopItem : FallbackCommandItem
];
private static readonly CompositeFormat RemoteDesktopOpenHostFormat = CompositeFormat.Parse(Resources.remotedesktop_open_host);
private readonly IRdpConnectionsManager _rdpConnectionsManager;
private readonly NoOpCommand _emptyCommand = new NoOpCommand();
public FallbackRemoteDesktopItem(IRdpConnectionsManager rdpConnectionsManager)
: base(new OpenRemoteDesktopCommand(string.Empty), Resources.remotedesktop_title)
: base(Resources.remotedesktop_title)
{
_rdpConnectionsManager = rdpConnectionsManager;
Command = _emptyCommand;
Title = string.Empty;
Subtitle = string.Empty;
Icon = Icons.RDPIcon;
@@ -41,7 +44,7 @@ internal sealed partial class FallbackRemoteDesktopItem : FallbackCommandItem
{
Title = string.Empty;
Subtitle = string.Empty;
Command = new OpenRemoteDesktopCommand(string.Empty);
Command = _emptyCommand;
return;
}
@@ -68,7 +71,7 @@ internal sealed partial class FallbackRemoteDesktopItem : FallbackCommandItem
{
Title = string.Empty;
Subtitle = string.Empty;
Command = new OpenRemoteDesktopCommand(string.Empty);
Command = _emptyCommand;
}
}
}

View File

@@ -40,7 +40,7 @@ internal sealed partial class OpenRemoteDesktopCommand : BaseObservable, IInvoka
{
using var process = new Process();
process.StartInfo.UseShellExecute = false;
process.StartInfo.WorkingDirectory = Environment.SpecialFolder.MyDocuments.ToString();
process.StartInfo.WorkingDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
process.StartInfo.FileName = "mstsc";
if (!string.IsNullOrWhiteSpace(_rdpHost))

View File

@@ -62,7 +62,7 @@ public partial class InstallPackageCommand : InvokableCommand
{
PackageInstallCommandState.Install => Icons.DownloadIcon,
PackageInstallCommandState.Update => Icons.UpdateIcon,
PackageInstallCommandState.Uninstall => Icons.CompletedIcon,
PackageInstallCommandState.Uninstall => Icons.DeleteIcon,
_ => throw new NotImplementedException(),
};
Name = InstallCommandState switch

View File

@@ -194,46 +194,95 @@ public partial class InstallPackageListItem : ListItem
var isInstalled = _package.InstalledVersion is not null;
var installedState = isInstalled ?
(_package.IsUpdateAvailable ?
PackageInstallCommandState.Update : PackageInstallCommandState.Uninstall) :
(_package.IsUpdateAvailable ? PackageInstallCommandState.Update : PackageInstallCommandState.Uninstall) :
PackageInstallCommandState.Install;
// might be an uninstall command
InstallPackageCommand installCommand = new(_package, installedState);
if (isInstalled)
if (_package.InstalledVersion is not null)
{
this.Icon = installCommand.Icon;
this.Command = new NoOpCommand();
#if DEBUG
var installerType = _package.InstalledVersion.GetMetadata(PackageVersionMetadataField.InstallerType);
Subtitle = installerType + " | " + Subtitle;
#endif
List<IContextItem> contextMenu = [];
CommandContextItem uninstallContextItem = new(installCommand)
Command = installCommand;
Icon = installedState switch
{
IsCritical = true,
Icon = Icons.DeleteIcon,
PackageInstallCommandState.Install => Icons.DownloadIcon,
PackageInstallCommandState.Update => Icons.UpdateIcon,
PackageInstallCommandState.Uninstall => Icons.CompletedIcon,
_ => Icons.DownloadIcon,
};
if (WinGetStatics.AppSearchCallback is not null)
TryLocateAndAppendActionForApp(contextMenu);
MoreCommands = contextMenu.ToArray();
}
else
{
_installCommand = new InstallPackageCommand(_package, installedState);
_installCommand.InstallStateChanged += InstallStateChangedHandler;
Command = _installCommand;
Icon = _installCommand.Icon;
}
}
private void TryLocateAndAppendActionForApp(List<IContextItem> contextMenu)
{
try
{
// Let's try to connect it to an installed app if possible
// This is a bit of dark magic, since there's no direct link between
// WinGet packages and installed apps.
var lookupByPackageName = WinGetStatics.AppSearchByPackageFamilyNameCallback;
if (lookupByPackageName is not null)
{
var callback = WinGetStatics.AppSearchCallback;
var installedApp = callback(_package.DefaultInstallVersion is null ? _package.Name : _package.DefaultInstallVersion.DisplayName);
if (installedApp is not null)
var names = _package.InstalledVersion.PackageFamilyNames;
for (var i = 0; i < names.Count; i++)
{
this.Command = installedApp.Command;
contextMenu = [.. installedApp.MoreCommands];
var installedAppByPfn = lookupByPackageName(names[i]);
if (installedAppByPfn is not null)
{
contextMenu.Add(new Separator());
contextMenu.Add(new CommandContextItem(installedAppByPfn.Command));
foreach (var item in installedAppByPfn.MoreCommands)
{
contextMenu.Add(item);
}
return;
}
}
}
contextMenu.Add(uninstallContextItem);
this.MoreCommands = contextMenu.ToArray();
return;
var lookupByProductCode = WinGetStatics.AppSearchByProductCodeCallback;
if (lookupByProductCode is not null)
{
var productCodes = _package.InstalledVersion.ProductCodes;
for (var i = 0; i < productCodes.Count; i++)
{
var installedAppByProductCode = lookupByProductCode(productCodes[i]);
if (installedAppByProductCode is not null)
{
contextMenu.Add(new Separator());
contextMenu.Add(new CommandContextItem(installedAppByProductCode.Command));
foreach (var item in installedAppByProductCode.MoreCommands)
{
contextMenu.Add(item);
}
return;
}
}
}
}
catch (Exception ex)
{
Logger.LogError($"Failed to retrieve app context menu items for package '{_package?.Name ?? "Unknown"}'", ex);
}
// didn't find the app
_installCommand = new InstallPackageCommand(_package, installedState);
this.Command = _installCommand;
Icon = _installCommand.Icon;
_installCommand.InstallStateChanged += InstallStateChangedHandler;
}
private void InstallStateChangedHandler(object? sender, InstallPackageCommand e)

View File

@@ -41,5 +41,9 @@ public partial class WinGetExtensionCommandsProvider : CommandProvider
public override void InitializeWithHost(IExtensionHost host) => WinGetExtensionHost.Instance.Initialize(host);
public void SetAllLookup(Func<string, ICommandItem?> callback) => WinGetStatics.AppSearchCallback = callback;
public void SetAllLookup(Func<string, ICommandItem?> lookupByPackageName, Func<string, ICommandItem?> lookupByProductCode)
{
WinGetStatics.AppSearchByPackageFamilyNameCallback = lookupByPackageName;
WinGetStatics.AppSearchByProductCodeCallback = lookupByProductCode;
}
}

View File

@@ -34,7 +34,9 @@ internal static class WinGetStatics
private static readonly StatusMessage _errorMessage = new() { State = MessageState.Error };
public static Func<string, ICommandItem?>? AppSearchCallback { get; set; }
public static Func<string, ICommandItem?>? AppSearchByPackageFamilyNameCallback { get; set; }
public static Func<string, ICommandItem?>? AppSearchByProductCodeCallback { get; set; }
private static readonly CompositeFormat CreateCatalogErrorMessage = System.Text.CompositeFormat.Parse(Properties.Resources.winget_create_catalog_error);

View File

@@ -6,7 +6,12 @@ namespace Microsoft.CommandPalette.Extensions.Toolkit;
public partial class FallbackCommandItem : CommandItem, IFallbackCommandItem, IFallbackHandler
{
private IFallbackHandler? _fallbackHandler;
private readonly IFallbackHandler? _fallbackHandler;
public FallbackCommandItem(string displayTitle)
{
DisplayTitle = displayTitle;
}
public FallbackCommandItem(ICommand command, string displayTitle)
: base(command)

View File

@@ -56,7 +56,7 @@ if ($IsAzurePipelineBuild) {
}
if (($BuildStep -ieq "all") -Or ($BuildStep -ieq "build")) {
& $nugetPath restore (Join-Path $PSScriptRoot "..\..\..\..\..\PowerToys.sln")
& $nugetPath restore (Join-Path $PSScriptRoot "..\..\..\..\..\PowerToys.slnx")
Try {
foreach ($config in $Configuration.Split(",")) {

View File

@@ -224,7 +224,7 @@ namespace PowerAccent.Core
LetterKey.VK_X => new[] { "ẋ", "×" },
LetterKey.VK_Y => new[] { "ẏ", "ꝡ" },
LetterKey.VK_Z => new[] { "ʒ", "ǯ", "" },
LetterKey.VK_COMMA => new[] { "∙", "₋", "⁻", "", "√" }, // is in VK_MINUS for other languages, but not VK_COMMA, so we add it here.
LetterKey.VK_COMMA => new[] { "∙", "₋", "⁻", "", "√", "‟", "《", "》", "", "〈", "〉", "″", "‴", "⁗" }, // is in VK_MINUS for other languages, but not VK_COMMA, so we add it here.
LetterKey.VK_PERIOD => new[] { "…", "⁝", "\u0300", "\u0301", "\u0302", "\u0303", "\u0304", "\u0308", "\u030B", "\u030C" },
LetterKey.VK_MINUS => new[] { "~", "", "", "", "—", "―", "", "", "⸺", "⸻", "∓" },
LetterKey.VK_SLASH_ => new[] { "÷", "√" },
@@ -302,6 +302,7 @@ namespace PowerAccent.Core
LetterKey.VK_E => new[] { "€" },
LetterKey.VK_S => new[] { "š" },
LetterKey.VK_Z => new[] { "ž" },
LetterKey.VK_COMMA => new[] { "„", "“", "»", "«" },
_ => Array.Empty<string>(),
};
}
@@ -317,6 +318,7 @@ namespace PowerAccent.Core
LetterKey.VK_U => new[] { "ü" },
LetterKey.VK_Z => new[] { "ž" },
LetterKey.VK_S => new[] { "š" },
LetterKey.VK_COMMA => new[] { "„", "“", "«", "»" },
_ => Array.Empty<string>(),
};
}
@@ -344,6 +346,7 @@ namespace PowerAccent.Core
LetterKey.VK_A => new[] { "ä", "å" },
LetterKey.VK_E => new[] { "€" },
LetterKey.VK_O => new[] { "ö" },
LetterKey.VK_COMMA => new[] { "”", "", "»" },
_ => Array.Empty<string>(),
};
}
@@ -360,6 +363,7 @@ namespace PowerAccent.Core
LetterKey.VK_O => new[] { "ô", "ö", "ó", "ò", "õ", "œ" },
LetterKey.VK_U => new[] { "û", "ù", "ü", "ú" },
LetterKey.VK_Y => new[] { "ÿ", "ý" },
LetterKey.VK_COMMA => new[] { "«", "»", "", "", "“", "”", "", "" },
_ => Array.Empty<string>(),
};
}
@@ -376,6 +380,7 @@ namespace PowerAccent.Core
LetterKey.VK_U => new[] { "ú" },
LetterKey.VK_Y => new[] { "ý" },
LetterKey.VK_T => new[] { "þ" },
LetterKey.VK_COMMA => new[] { "„", "“", "", "" },
_ => Array.Empty<string>(),
};
}
@@ -393,7 +398,7 @@ namespace PowerAccent.Core
LetterKey.VK_N => new[] { "ñ" },
LetterKey.VK_O => new[] { "ó" },
LetterKey.VK_U => new[] { "ú", "ü" },
LetterKey.VK_COMMA => new[] { "¿", "?", "¡", "!" },
LetterKey.VK_COMMA => new[] { "¿", "?", "¡", "!", "«", "»", "“", "”", "", "" },
_ => Array.Empty<string>(),
};
}
@@ -411,7 +416,7 @@ namespace PowerAccent.Core
LetterKey.VK_O => new[] { "ò", "ó" },
LetterKey.VK_U => new[] { "ù", "ú", "ü" },
LetterKey.VK_L => new[] { "·" },
LetterKey.VK_COMMA => new[] { "¿", "?", "¡", "!" },
LetterKey.VK_COMMA => new[] { "¿", "?", "¡", "!", "«", "»", "“", "”", "", "" },
_ => Array.Empty<string>(),
};
}
@@ -427,6 +432,7 @@ namespace PowerAccent.Core
LetterKey.VK_O => new[] { "ō" },
LetterKey.VK_S => new[] { "$" },
LetterKey.VK_U => new[] { "ū" },
LetterKey.VK_COMMA => new[] { "“", "”", "", "" },
_ => Array.Empty<string>(),
};
}
@@ -443,6 +449,7 @@ namespace PowerAccent.Core
LetterKey.VK_N => new[] { "ñ" },
LetterKey.VK_O => new[] { "ó", "ö", "ô" },
LetterKey.VK_U => new[] { "ú", "ü", "û" },
LetterKey.VK_COMMA => new[] { "“", "„", "”", "", ",", "" },
_ => Array.Empty<string>(),
};
}
@@ -469,6 +476,7 @@ namespace PowerAccent.Core
LetterKey.VK_V => new[] { "ü", "ǖ", "ǘ", "ǚ", "ǜ" },
LetterKey.VK_Y => new[] { "¥" },
LetterKey.VK_Z => new[] { "ẑ" },
LetterKey.VK_COMMA => new[] { "“", "”", "", "", "「", "」", "『", "』" },
_ => Array.Empty<string>(),
};
}
@@ -505,6 +513,7 @@ namespace PowerAccent.Core
LetterKey.VK_S => new[] { "ş" },
LetterKey.VK_T => new[] { "₺" },
LetterKey.VK_U => new[] { "ü", "û" },
LetterKey.VK_COMMA => new[] { "“", "”", "", "", "«", "»", "", "" },
_ => Array.Empty<string>(),
};
}
@@ -522,6 +531,7 @@ namespace PowerAccent.Core
LetterKey.VK_O => new[] { "ó" },
LetterKey.VK_S => new[] { "ś" },
LetterKey.VK_Z => new[] { "ż", "ź" },
LetterKey.VK_COMMA => new[] { "„", "”", "", "", "»", "«" },
_ => Array.Empty<string>(),
};
}
@@ -539,7 +549,7 @@ namespace PowerAccent.Core
LetterKey.VK_P => new[] { "π" },
LetterKey.VK_S => new[] { "$" },
LetterKey.VK_U => new[] { "ú" },
LetterKey.VK_COMMA => new[] { "≤", "≥", "≠", "≈", "≙", "±", "₊", "⁺" },
LetterKey.VK_COMMA => new[] { "≤", "≥", "≠", "≈", "≙", "±", "₊", "⁺", "“", "”", "", "", "«", "»" },
_ => Array.Empty<string>(),
};
}
@@ -594,6 +604,7 @@ namespace PowerAccent.Core
LetterKey.VK_U => new[] { "ú" },
LetterKey.VK_Y => new[] { "ý" },
LetterKey.VK_Z => new[] { "ž" },
LetterKey.VK_COMMA => new[] { "„", "“", "", "", "»", "«", "", "" },
_ => Array.Empty<string>(),
};
}
@@ -608,6 +619,7 @@ namespace PowerAccent.Core
LetterKey.VK_I => new[] { "í" },
LetterKey.VK_O => new[] { "ó" },
LetterKey.VK_U => new[] { "ú" },
LetterKey.VK_COMMA => new[] { "“", "”", "", "" },
_ => Array.Empty<string>(),
};
}
@@ -623,6 +635,7 @@ namespace PowerAccent.Core
LetterKey.VK_O => new[] { "ò" },
LetterKey.VK_P => new[] { "£" },
LetterKey.VK_U => new[] { "ù" },
LetterKey.VK_COMMA => new[] { "“", "”", "", "" },
_ => Array.Empty<string>(),
};
}
@@ -645,6 +658,7 @@ namespace PowerAccent.Core
LetterKey.VK_U => new[] { "ů", "ú" },
LetterKey.VK_Y => new[] { "ý" },
LetterKey.VK_Z => new[] { "ž" },
LetterKey.VK_COMMA => new[] { "„", "“", "", "", "»", "«", "", "" },
_ => Array.Empty<string>(),
};
}
@@ -659,6 +673,7 @@ namespace PowerAccent.Core
LetterKey.VK_O => new[] { "ö" },
LetterKey.VK_S => new[] { "ß" },
LetterKey.VK_U => new[] { "ü" },
LetterKey.VK_COMMA => new[] { "„", "“", "", "", "»", "«", "", "" },
_ => Array.Empty<string>(),
};
}
@@ -689,6 +704,7 @@ namespace PowerAccent.Core
LetterKey.VK_X => new string[] { "ξ" },
LetterKey.VK_Y => new string[] { "υ" },
LetterKey.VK_Z => new string[] { "ζ" },
LetterKey.VK_COMMA => new[] { "“", "”", "«", "»", },
_ => Array.Empty<string>(),
};
}
@@ -710,7 +726,7 @@ namespace PowerAccent.Core
LetterKey.VK_U => new[] { "וֹ", "וּ", "װ", "\u05b9" },
LetterKey.VK_X => new[] { "\u05b6", "\u05b1" },
LetterKey.VK_Y => new[] { "ױ" },
LetterKey.VK_COMMA => new[] { "”", "", "״", "׳" },
LetterKey.VK_COMMA => new[] { "”", "", "'", "״", "׳" },
LetterKey.VK_PERIOD => new[] { "\u05ab", "\u05bd", "\u05bf" },
LetterKey.VK_MINUS => new[] { "", "־" },
_ => Array.Empty<string>(),
@@ -727,6 +743,7 @@ namespace PowerAccent.Core
LetterKey.VK_I => new[] { "í" },
LetterKey.VK_O => new[] { "ó", "ő", "ö" },
LetterKey.VK_U => new[] { "ú", "ű", "ü" },
LetterKey.VK_COMMA => new[] { "„", "”", "»", "«" },
_ => Array.Empty<string>(),
};
}
@@ -740,6 +757,7 @@ namespace PowerAccent.Core
LetterKey.VK_I => new[] { "î" },
LetterKey.VK_S => new[] { "ș" },
LetterKey.VK_T => new[] { "ț" },
LetterKey.VK_COMMA => new[] { "„", "”", "«", "»" },
_ => Array.Empty<string>(),
};
}
@@ -754,6 +772,7 @@ namespace PowerAccent.Core
LetterKey.VK_I => new[] { "ì", "í" },
LetterKey.VK_O => new[] { "ò", "ó" },
LetterKey.VK_U => new[] { "ù", "ú" },
LetterKey.VK_COMMA => new[] { "«", "»", "“", "”", "", "" },
_ => Array.Empty<string>(),
};
}
@@ -772,6 +791,7 @@ namespace PowerAccent.Core
LetterKey.VK_R => new[] { "ř" },
LetterKey.VK_S => new[] { "ş" },
LetterKey.VK_U => new[] { "û", "ü" },
LetterKey.VK_COMMA => new[] { "«", "»", "“", "”" },
_ => Array.Empty<string>(),
};
}
@@ -789,6 +809,7 @@ namespace PowerAccent.Core
LetterKey.VK_U => new[] { "û", "ü", "ù", "ú" },
LetterKey.VK_Y => new[] { "ŷ", "ÿ", "ỳ", "ý" },
LetterKey.VK_W => new[] { "ŵ", "ẅ", "ẁ", "ẃ" },
LetterKey.VK_COMMA => new[] { "", "", "“", "“" },
_ => Array.Empty<string>(),
};
}
@@ -801,6 +822,7 @@ namespace PowerAccent.Core
LetterKey.VK_A => new[] { "å", "ä" },
LetterKey.VK_E => new[] { "é" },
LetterKey.VK_O => new[] { "ö" },
LetterKey.VK_COMMA => new[] { "”", "", "»", "«" },
_ => Array.Empty<string>(),
};
}
@@ -814,6 +836,7 @@ namespace PowerAccent.Core
LetterKey.VK_D => new[] { "đ" },
LetterKey.VK_S => new[] { "š" },
LetterKey.VK_Z => new[] { "ž" },
LetterKey.VK_COMMA => new[] { "„", "“", "", "", "»", "«", "", "" },
_ => Array.Empty<string>(),
};
}
@@ -838,6 +861,7 @@ namespace PowerAccent.Core
{
LetterKey.VK_E => new[] { "ѐ" },
LetterKey.VK_I => new[] { "ѝ" },
LetterKey.VK_COMMA => new[] { "„", "“", "", "" },
_ => Array.Empty<string>(),
};
}
@@ -869,6 +893,7 @@ namespace PowerAccent.Core
LetterKey.VK_E => new[] { "€", "é" },
LetterKey.VK_O => new[] { "ø" },
LetterKey.VK_S => new[] { "$" },
LetterKey.VK_COMMA => new[] { "«", "»", ",", "", "", "„", "“" },
_ => Array.Empty<string>(),
};
}
@@ -881,6 +906,7 @@ namespace PowerAccent.Core
LetterKey.VK_A => new[] { "å", "æ" },
LetterKey.VK_E => new[] { "€" },
LetterKey.VK_O => new[] { "ø" },
LetterKey.VK_COMMA => new[] { "»", "«", "“", "”", "", "", "", "" },
_ => Array.Empty<string>(),
};
}
@@ -897,6 +923,7 @@ namespace PowerAccent.Core
LetterKey.VK_S => new[] { "š" },
LetterKey.VK_U => new[] { "ų", "ū" },
LetterKey.VK_Z => new[] { "ž" },
LetterKey.VK_COMMA => new[] { "„", "“", "", "" },
_ => Array.Empty<string>(),
};
}
@@ -910,6 +937,7 @@ namespace PowerAccent.Core
LetterKey.VK_E => new[] { "€" },
LetterKey.VK_S => new[] { "š" },
LetterKey.VK_Z => new[] { "ž" },
LetterKey.VK_COMMA => new[] { "„", "“", "»", "«" },
_ => Array.Empty<string>(),
};
}

View File

@@ -20,6 +20,7 @@ public partial class PowerAccent : IDisposable
// Keys that show a description (like dashes) when ShowCharacterInfoSetting is 1
private readonly LetterKey[] _letterKeysShowingDescription = new LetterKey[] { LetterKey.VK_O };
private const double ScreenMinPadding = 150;
private bool _visible;
private string[] _characters = Array.Empty<string>();
@@ -332,6 +333,11 @@ public partial class PowerAccent : IDisposable
return Calculation.GetRawCoordinatesFromPosition(position, screen, window);
}
public double GetDisplayMaxWidth()
{
return WindowsFunctions.GetActiveDisplay().Size.Width - ScreenMinPadding;
}
public Position GetToolbarPosition()
{
return _settingService.Position;

View File

@@ -59,6 +59,7 @@ public partial class Selector : FluentWindow, IDisposable, INotifyPropertyChange
_selectedIndex = index;
characters.SelectedIndex = _selectedIndex;
characterName.Text = _powerAccent.CharacterDescriptions[_selectedIndex];
characters.ScrollIntoView(character);
}
private void PowerAccent_OnChangeDisplay(bool isActive, string[] chars)
@@ -73,6 +74,7 @@ public partial class Selector : FluentWindow, IDisposable, INotifyPropertyChange
characters.ItemsSource = chars;
characters.SelectedIndex = _selectedIndex;
this.UpdateLayout(); // Required for filling the actual width/height before positioning.
SetWindowsSize();
SetWindowPosition();
Show();
Microsoft.PowerToys.Telemetry.PowerToysTelemetry.Log.WriteEvent(new PowerAccent.Core.Telemetry.PowerAccentShowAccentMenuEvent());
@@ -96,6 +98,11 @@ public partial class Selector : FluentWindow, IDisposable, INotifyPropertyChange
this.Top = position.Y;
}
private void SetWindowsSize()
{
this.characters.MaxWidth = _powerAccent.GetDisplayMaxWidth();
}
protected override void OnClosed(EventArgs e)
{
_powerAccent.SaveUsageInfo();

Binary file not shown.

View File

@@ -10,7 +10,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library;
public sealed class AdvancedPasteCustomActions
{
private static readonly JsonSerializerOptions _serializerOptions = new()
private static readonly JsonSerializerOptions _serializerOptions = new(SettingsSerializationContext.Default.Options)
{
WriteIndented = true,
};

View File

@@ -104,6 +104,6 @@ namespace Microsoft.PowerToys.Settings.UI.Library
public PasteAIConfiguration PasteAIConfiguration { get; set; }
public override string ToString()
=> JsonSerializer.Serialize(this);
=> JsonSerializer.Serialize(this, SettingsSerializationContext.Default.AdvancedPasteProperties);
}
}

View File

@@ -2,13 +2,32 @@
// 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.Text.Json;
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Library
{
/// <summary>
/// Base class for all PowerToys module settings.
/// </summary>
/// <remarks>
/// <para><strong>IMPORTANT for Native AOT compatibility:</strong></para>
/// <para>When creating a new class that inherits from <see cref="BasePTModuleSettings"/>,
/// you MUST register it in <see cref="SettingsSerializationContext"/> by adding a
/// <c>[JsonSerializable(typeof(YourNewSettingsClass))]</c> attribute.</para>
/// <para>Failure to register the type will cause <see cref="ToJsonString"/> to throw
/// <see cref="InvalidOperationException"/> at runtime.</para>
/// <para>See <see cref="SettingsSerializationContext"/> for registration instructions.</para>
/// </remarks>
public abstract class BasePTModuleSettings
{
// Cached JsonSerializerOptions for Native AOT compatibility
private static readonly JsonSerializerOptions _jsonSerializerOptions = new JsonSerializerOptions
{
TypeInfoResolver = SettingsSerializationContext.Default,
};
// Gets or sets name of the powertoy module.
[JsonPropertyName("name")]
public string Name { get; set; }
@@ -17,11 +36,33 @@ namespace Microsoft.PowerToys.Settings.UI.Library
[JsonPropertyName("version")]
public string Version { get; set; }
// converts the current to a json string.
/// <summary>
/// Converts the current settings object to a JSON string.
/// </summary>
/// <returns>JSON string representation of this settings object.</returns>
/// <exception cref="InvalidOperationException">
/// Thrown when the runtime type is not registered in <see cref="SettingsSerializationContext"/>.
/// All derived types must be registered with <c>[JsonSerializable(typeof(YourType))]</c> attribute.
/// </exception>
/// <remarks>
/// This method uses Native AOT-compatible JSON serialization. The runtime type must be
/// registered in <see cref="SettingsSerializationContext"/> for serialization to work.
/// </remarks>
public virtual string ToJsonString()
{
// By default JsonSerializer will only serialize the properties in the base class. This can be avoided by passing the object type (more details at https://stackoverflow.com/a/62498888)
return JsonSerializer.Serialize(this, GetType());
var runtimeType = GetType();
// For Native AOT compatibility, get JsonTypeInfo from the TypeInfoResolver
var typeInfo = _jsonSerializerOptions.TypeInfoResolver?.GetTypeInfo(runtimeType, _jsonSerializerOptions);
if (typeInfo == null)
{
throw new InvalidOperationException($"Type {runtimeType.FullName} is not registered in SettingsSerializationContext. Please add it to the [JsonSerializable] attributes.");
}
// Use AOT-friendly serialization
return JsonSerializer.Serialize(this, typeInfo);
}
public override int GetHashCode()

View File

@@ -37,7 +37,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
public override string ToString()
{
return JsonSerializer.Serialize(this);
return JsonSerializer.Serialize(this, SettingsSerializationContext.Default.BoolProperty);
}
public bool TryToCmdRepresentable(out string result)

View File

@@ -12,14 +12,14 @@ namespace Microsoft.PowerToys.Settings.UI.Library
{
public override bool Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var boolProperty = JsonSerializer.Deserialize<BoolProperty>(ref reader, options);
var boolProperty = JsonSerializer.Deserialize(ref reader, SettingsSerializationContext.Default.BoolProperty);
return boolProperty.Value;
}
public override void Write(Utf8JsonWriter writer, bool value, JsonSerializerOptions options)
{
var boolProperty = new BoolProperty(value);
JsonSerializer.Serialize(writer, boolProperty, options);
JsonSerializer.Serialize(writer, boolProperty, SettingsSerializationContext.Default.BoolProperty);
}
}
}

View File

@@ -43,7 +43,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
if (doc.RootElement.TryGetProperty(nameof(Hotkey), out JsonElement hotkeyElement))
{
Hotkey = JsonSerializer.Deserialize<HotkeySettings>(hotkeyElement.GetRawText());
Hotkey = JsonSerializer.Deserialize(hotkeyElement.GetRawText(), SettingsSerializationContext.Default.HotkeySettings);
}
}
catch (Exception)

View File

@@ -87,6 +87,6 @@ namespace Microsoft.PowerToys.Settings.UI.Library
public bool ShowColorName { get; set; }
public override string ToString()
=> JsonSerializer.Serialize(this);
=> JsonSerializer.Serialize(this, SettingsSerializationContext.Default.ColorPickerProperties);
}
}

View File

@@ -54,6 +54,6 @@ namespace Microsoft.PowerToys.Settings.UI.Library
public bool ShowColorName { get; set; }
public override string ToString()
=> JsonSerializer.Serialize(this);
=> JsonSerializer.Serialize(this, SettingsSerializationContext.Default.ColorPickerPropertiesVersion1);
}
}

View File

@@ -27,7 +27,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
// Returns a JSON version of the class settings configuration class.
public override string ToString()
{
return JsonSerializer.Serialize(this);
return JsonSerializer.Serialize(this, SettingsSerializationContext.Default.DoubleProperty);
}
}
}

View File

@@ -21,7 +21,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
public string ToJsonString()
{
return JsonSerializer.Serialize(this);
return JsonSerializer.Serialize(this, SettingsSerializationContext.Default.FileLocksmithLocalProperties);
}
// This function is required to implement the ISettingsConfig interface and obtain the settings configurations.

View File

@@ -17,6 +17,6 @@ namespace Microsoft.PowerToys.Settings.UI.Library
[JsonPropertyName("bool_show_extended_menu")]
public BoolProperty ExtendedContextMenuOnly { get; set; }
public override string ToString() => JsonSerializer.Serialize(this);
public override string ToString() => JsonSerializer.Serialize(this, SettingsSerializationContext.Default.FileLocksmithProperties);
}
}

View File

@@ -119,7 +119,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
// converts the current to a json string.
public string ToJsonString()
{
return JsonSerializer.Serialize(this);
return JsonSerializer.Serialize(this, SettingsSerializationContext.Default.GeneralSettings);
}
private static string DefaultPowertoysVersion()

View File

@@ -23,7 +23,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
public override string ToString()
{
return JsonSerializer.Serialize(this);
return JsonSerializer.Serialize(this, SettingsSerializationContext.Default.GeneralSettingsCustomAction);
}
}
}

View File

@@ -12,13 +12,18 @@ namespace Microsoft.PowerToys.Settings.UI.Library.Helpers
{
public struct SunTimes
{
public int SunriseHour;
public int SunriseMinute;
public int SunsetHour;
public int SunsetMinute;
public string Text;
public int SunriseHour { get; set; }
public bool HasSunrise;
public bool HasSunset;
public int SunriseMinute { get; set; }
public int SunsetHour { get; set; }
public int SunsetMinute { get; set; }
public string Text { get; set; }
public bool HasSunrise { get; set; }
public bool HasSunset { get; set; }
}
}

View File

@@ -86,7 +86,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
public string ToJsonString()
{
return JsonSerializer.Serialize(this);
return JsonSerializer.Serialize(this, SettingsSerializationContext.Default.ImageResizerProperties);
}
}

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