Repository: ZetaSp/PowerToys-Chinese-TransMOD Branch: master Commit: 4a41565ba980 Files: 183 Total size: 2.4 MB Directory structure: gitextract_lcydtlch/ ├── LICENSE ├── Patcher/ │ ├── 安装.CMD │ └── 安装组策略.cmd ├── PowerToys/ │ ├── global.json │ └── src/ │ ├── ActionRunner/ │ │ └── Resources.resx │ ├── Monaco/ │ │ └── index.html │ ├── Update/ │ │ └── Resources.resx │ ├── Version.props │ ├── common/ │ │ ├── interop/ │ │ │ └── keyboard_layout.cpp │ │ └── sysinternals/ │ │ └── Eula/ │ │ └── eula.c │ ├── gpo/ │ │ └── assets/ │ │ └── zh-CN/ │ │ └── PowerToys.adml │ ├── modules/ │ │ ├── AdvancedPaste/ │ │ │ └── AdvancedPaste/ │ │ │ └── Strings/ │ │ │ └── en-us/ │ │ │ └── Resources.resw │ │ ├── CropAndLock/ │ │ │ └── CropAndLock/ │ │ │ └── main.cpp │ │ ├── EnvironmentVariables/ │ │ │ └── EnvironmentVariablesUILib/ │ │ │ └── Strings/ │ │ │ └── en-us/ │ │ │ └── Resources.resw │ │ ├── FileLocksmith/ │ │ │ ├── FileLocksmithContextMenu/ │ │ │ │ ├── Resources.resx │ │ │ │ └── dllmain.cpp │ │ │ ├── FileLocksmithExt/ │ │ │ │ └── Resources.resx │ │ │ └── FileLocksmithUI/ │ │ │ └── Strings/ │ │ │ └── en-us/ │ │ │ └── Resources.resw │ │ ├── Hosts/ │ │ │ └── HostsUILib/ │ │ │ └── Strings/ │ │ │ └── en-us/ │ │ │ └── Resources.resw │ │ ├── MeasureTool/ │ │ │ └── MeasureToolUI/ │ │ │ └── Strings/ │ │ │ └── en-us/ │ │ │ └── Resources.resw │ │ ├── MouseWithoutBorders/ │ │ │ ├── App/ │ │ │ │ ├── Class/ │ │ │ │ │ ├── Common.Clipboard.cs │ │ │ │ │ ├── Common.Encryption.cs │ │ │ │ │ ├── Common.Event.cs │ │ │ │ │ ├── Common.Helper.cs │ │ │ │ │ ├── Common.MachineStuff.cs │ │ │ │ │ ├── Common.Service.cs │ │ │ │ │ ├── EasyMouseOption.cs │ │ │ │ │ ├── Extensions.cs │ │ │ │ │ ├── IClipboardHelper.cs │ │ │ │ │ ├── InputHook.cs │ │ │ │ │ ├── Program.cs │ │ │ │ │ └── SocketStuff.cs │ │ │ │ ├── Control/ │ │ │ │ │ ├── Machine.cs │ │ │ │ │ └── Machine.designer.cs │ │ │ │ ├── Core/ │ │ │ │ │ └── Receiver.cs │ │ │ │ ├── Form/ │ │ │ │ │ ├── Settings/ │ │ │ │ │ │ ├── SettingsFormPage.Designer.cs │ │ │ │ │ │ ├── SettingsFormPage.cs │ │ │ │ │ │ ├── SetupPage1.Designer.cs │ │ │ │ │ │ ├── SetupPage2a.Designer.cs │ │ │ │ │ │ ├── SetupPage2aa.Designer.cs │ │ │ │ │ │ ├── SetupPage2ab.Designer.cs │ │ │ │ │ │ ├── SetupPage2b.Designer.cs │ │ │ │ │ │ ├── SetupPage3a.Designer.cs │ │ │ │ │ │ ├── SetupPage3a.cs │ │ │ │ │ │ ├── SetupPage4.Designer.cs │ │ │ │ │ │ └── SetupPage5.Designer.cs │ │ │ │ │ ├── frmAbout.Designer.cs │ │ │ │ │ ├── frmAbout.cs │ │ │ │ │ ├── frmInputCallback.cs │ │ │ │ │ ├── frmLogon.Designer.cs │ │ │ │ │ ├── frmMatrix.Designer.cs │ │ │ │ │ ├── frmMatrix.cs │ │ │ │ │ ├── frmMatrix.resx │ │ │ │ │ ├── frmMessage.Designer.cs │ │ │ │ │ ├── frmScreen.Designer.cs │ │ │ │ │ └── frmScreen.cs │ │ │ │ └── Helper/ │ │ │ │ └── FormHelper.cs │ │ │ └── ModuleInterface/ │ │ │ └── dllmain.cpp │ │ ├── NewPlus/ │ │ │ ├── NewShellExtensionContextMenu/ │ │ │ │ ├── NewShellExtensionContextMenu.vcxproj │ │ │ │ ├── NewShellExtensionContextMenu.vcxproj.filters │ │ │ │ ├── TemplateExamples/ │ │ │ │ │ ├── 模板文件夹里的一切都会出现在新建+菜单中.txt │ │ │ │ │ └── 示例文件夹/ │ │ │ │ │ ├── 另一个示例文本文件.txt │ │ │ │ │ └── 示例文本文件.txt │ │ │ │ └── resources.resx │ │ │ └── NewShellExtensionContextMenu.win10/ │ │ │ └── resources.resx │ │ ├── PowerOCR/ │ │ │ └── PowerOCR/ │ │ │ └── Properties/ │ │ │ └── Resources.resx │ │ ├── ShortcutGuide/ │ │ │ └── ShortcutGuide/ │ │ │ └── Resources.resx │ │ ├── Workspaces/ │ │ │ ├── WorkspacesEditor/ │ │ │ │ ├── Models/ │ │ │ │ │ └── Project.cs │ │ │ │ └── Properties/ │ │ │ │ └── Resources.resx │ │ │ ├── WorkspacesLauncher/ │ │ │ │ └── Resource.resx │ │ │ ├── WorkspacesLauncherUI/ │ │ │ │ └── Properties/ │ │ │ │ └── Resources.resx │ │ │ └── WorkspacesSnapshotTool/ │ │ │ └── Resource.resx │ │ ├── ZoomIt/ │ │ │ └── ZoomIt/ │ │ │ ├── ZoomIt.rc │ │ │ └── Zoomit.cpp │ │ ├── alwaysontop/ │ │ │ └── AlwaysOnTop/ │ │ │ └── Resources.resx │ │ ├── awake/ │ │ │ └── Awake/ │ │ │ ├── Core/ │ │ │ │ └── Constants.cs │ │ │ └── Properties/ │ │ │ └── Resources.resx │ │ ├── colorPicker/ │ │ │ └── ColorPickerUI/ │ │ │ ├── Properties/ │ │ │ │ └── Resources.resx │ │ │ └── ViewModels/ │ │ │ └── ColorEditorViewModel.cs │ │ ├── fancyzones/ │ │ │ ├── FancyZonesLib/ │ │ │ │ └── Resources.resx │ │ │ └── editor/ │ │ │ └── FancyZonesEditor/ │ │ │ └── Properties/ │ │ │ └── Resources.resx │ │ ├── imageresizer/ │ │ │ ├── ImageResizerContextMenu/ │ │ │ │ └── Resources.resx │ │ │ ├── dll/ │ │ │ │ └── Resources.resx │ │ │ └── ui/ │ │ │ ├── Properties/ │ │ │ │ └── Resources.resx │ │ │ └── Views/ │ │ │ ├── EnumValueConverter.cs │ │ │ └── InputPage.xaml │ │ ├── keyboardmanager/ │ │ │ ├── KeyboardManagerEditor/ │ │ │ │ └── Resources.resx │ │ │ └── KeyboardManagerEngineLibrary/ │ │ │ └── KeyboardEventHandlers.cpp │ │ ├── launcher/ │ │ │ ├── Plugins/ │ │ │ │ ├── Community.PowerToys.Run.Plugin.UnitConverter/ │ │ │ │ │ ├── InputInterpreter.cs │ │ │ │ │ ├── Main.cs │ │ │ │ │ └── Properties/ │ │ │ │ │ └── Resources.resx │ │ │ │ ├── Community.PowerToys.Run.Plugin.VSCodeWorkspaces/ │ │ │ │ │ ├── Main.cs │ │ │ │ │ └── Properties/ │ │ │ │ │ └── Resources.resx │ │ │ │ ├── Community.PowerToys.Run.Plugin.ValueGenerator/ │ │ │ │ │ ├── Generators/ │ │ │ │ │ │ ├── Base64/ │ │ │ │ │ │ │ ├── Base64DecodeRequest.cs │ │ │ │ │ │ │ └── Base64Request.cs │ │ │ │ │ │ ├── GUID/ │ │ │ │ │ │ │ └── GUIDRequest.cs │ │ │ │ │ │ └── Uri/ │ │ │ │ │ │ ├── DataEscapeRequest.cs │ │ │ │ │ │ ├── DataUnescapeRequest.cs │ │ │ │ │ │ ├── HexEscapeRequest.cs │ │ │ │ │ │ ├── HexUnescapeRequest.cs │ │ │ │ │ │ ├── UrlDecodeRequest.cs │ │ │ │ │ │ └── UrlEncodeRequest.cs │ │ │ │ │ ├── InputParser.cs │ │ │ │ │ └── Properties/ │ │ │ │ │ └── Resources.resx │ │ │ │ ├── Community.PowerToys.Run.Plugin.WebSearch/ │ │ │ │ │ ├── Main.cs │ │ │ │ │ └── Properties/ │ │ │ │ │ └── Resources.resx │ │ │ │ ├── Microsoft.Plugin.Folder/ │ │ │ │ │ └── Properties/ │ │ │ │ │ └── Resources.resx │ │ │ │ ├── Microsoft.Plugin.Indexer/ │ │ │ │ │ ├── Main.cs │ │ │ │ │ └── Properties/ │ │ │ │ │ └── Resources.resx │ │ │ │ ├── Microsoft.Plugin.Program/ │ │ │ │ │ ├── Main.cs │ │ │ │ │ ├── Programs/ │ │ │ │ │ │ └── UWPApplication.cs │ │ │ │ │ └── Properties/ │ │ │ │ │ └── Resources.resx │ │ │ │ ├── Microsoft.Plugin.Shell/ │ │ │ │ │ ├── Main.cs │ │ │ │ │ └── Properties/ │ │ │ │ │ └── Resources.resx │ │ │ │ ├── Microsoft.Plugin.Uri/ │ │ │ │ │ ├── Main.cs │ │ │ │ │ └── Properties/ │ │ │ │ │ └── Resources.resx │ │ │ │ ├── Microsoft.Plugin.WindowWalker/ │ │ │ │ │ └── Properties/ │ │ │ │ │ └── Resources.resx │ │ │ │ ├── Microsoft.PowerToys.Run.Plugin.Calculator/ │ │ │ │ │ └── Properties/ │ │ │ │ │ └── Resources.resx │ │ │ │ ├── Microsoft.PowerToys.Run.Plugin.History/ │ │ │ │ │ └── Properties/ │ │ │ │ │ └── Resources.resx │ │ │ │ ├── Microsoft.PowerToys.Run.Plugin.OneNote/ │ │ │ │ │ └── Properties/ │ │ │ │ │ └── Resources.resx │ │ │ │ ├── Microsoft.PowerToys.Run.Plugin.PowerToys/ │ │ │ │ │ └── Properties/ │ │ │ │ │ └── Resources.resx │ │ │ │ ├── Microsoft.PowerToys.Run.Plugin.Registry/ │ │ │ │ │ └── Properties/ │ │ │ │ │ └── Resources.resx │ │ │ │ ├── Microsoft.PowerToys.Run.Plugin.Service/ │ │ │ │ │ └── Properties/ │ │ │ │ │ └── Resources.resx │ │ │ │ ├── Microsoft.PowerToys.Run.Plugin.System/ │ │ │ │ │ ├── Components/ │ │ │ │ │ │ └── ResultHelper.cs │ │ │ │ │ ├── Main.cs │ │ │ │ │ └── Properties/ │ │ │ │ │ └── Resources.resx │ │ │ │ ├── Microsoft.PowerToys.Run.Plugin.TimeDate/ │ │ │ │ │ └── Properties/ │ │ │ │ │ └── Resources.resx │ │ │ │ ├── Microsoft.PowerToys.Run.Plugin.WindowsSettings/ │ │ │ │ │ └── Properties/ │ │ │ │ │ └── Resources.resx │ │ │ │ └── Microsoft.PowerToys.Run.Plugin.WindowsTerminal/ │ │ │ │ ├── Main.cs │ │ │ │ └── Properties/ │ │ │ │ └── Resources.resx │ │ │ └── PowerLauncher/ │ │ │ └── Properties/ │ │ │ └── Resources.resx │ │ ├── peek/ │ │ │ └── Peek.UI/ │ │ │ └── Strings/ │ │ │ └── en-us/ │ │ │ └── Resources.resw │ │ ├── powerrename/ │ │ │ ├── PowerRenameContextMenu/ │ │ │ │ └── Resources.resx │ │ │ ├── PowerRenameUILib/ │ │ │ │ ├── PowerRenameXAML/ │ │ │ │ │ └── MainWindow.xaml.cpp │ │ │ │ └── Strings/ │ │ │ │ └── en-us/ │ │ │ │ └── Resources.resw │ │ │ └── dll/ │ │ │ └── Resources.resx │ │ ├── previewpane/ │ │ │ ├── GcodePreviewHandler/ │ │ │ │ └── Properties/ │ │ │ │ └── Resource.resx │ │ │ ├── MarkdownPreviewHandler/ │ │ │ │ └── Properties/ │ │ │ │ └── Resources.resx │ │ │ ├── MonacoPreviewHandler/ │ │ │ │ └── Properties/ │ │ │ │ └── Resources.resx │ │ │ ├── PdfPreviewHandler/ │ │ │ │ └── Properties/ │ │ │ │ └── Resources.resx │ │ │ ├── QoiPreviewHandler/ │ │ │ │ └── Properties/ │ │ │ │ └── Resource.resx │ │ │ ├── SvgPreviewHandler/ │ │ │ │ └── Properties/ │ │ │ │ └── Resource.resx │ │ │ └── powerpreview/ │ │ │ └── Resources.resx │ │ └── registrypreview/ │ │ ├── RegistryPreview/ │ │ │ └── RegistryPreviewXAML/ │ │ │ └── MainWindow.xaml.cs │ │ └── RegistryPreviewUILib/ │ │ └── Strings/ │ │ └── en-US/ │ │ └── Resources.resw │ ├── runner/ │ │ ├── Resources.resx │ │ └── main.cpp │ └── settings-ui/ │ ├── Settings.UI/ │ │ ├── Assets/ │ │ │ └── Settings/ │ │ │ └── Scripts/ │ │ │ ├── CheckCmdNotFoundRequirements.ps1 │ │ │ ├── DisableModule.ps1 │ │ │ ├── EnableModule.ps1 │ │ │ ├── InstallPowerShell7.ps1 │ │ │ └── InstallWinGetClientModule.ps1 │ │ ├── Converters/ │ │ │ └── ZoomItTypeSpeedSliderConverter.cs │ │ ├── SettingsXAML/ │ │ │ ├── App.xaml.cs │ │ │ ├── OOBE/ │ │ │ │ └── Views/ │ │ │ │ ├── OobeShellPage.xaml │ │ │ │ └── OobeShellPage.xaml.cs │ │ │ └── Views/ │ │ │ ├── ColorPickerPage.xaml │ │ │ ├── ImageResizerPage.xaml │ │ │ ├── MouseUtilsPage.xaml │ │ │ ├── MouseWithoutBordersPage.xaml │ │ │ ├── PowerLauncherPage.xaml │ │ │ ├── PowerOcrPage.xaml │ │ │ ├── PowerPreviewPage.xaml │ │ │ ├── PowerRenamePage.xaml │ │ │ └── ZoomItPage.xaml │ │ ├── Strings/ │ │ │ └── en-us/ │ │ │ └── Resources.resw │ │ └── ViewModels/ │ │ ├── CmdNotFoundViewModel.cs │ │ ├── DashboardViewModel.cs │ │ └── PowerAccentViewModel.cs │ └── Settings.UI.Library/ │ └── ColorFormatModel.cs ├── README.md └── Tools/ ├── README.md ├── autosort.py ├── autosort_newgptbroken.py ├── autosort_old.py ├── blank ├── clean.cmd ├── clean.txt ├── modify.cmd ├── modify_arm64.cmd ├── zhcn_modify_list.txt ├── zhcn_modify_list2.txt └── zhcn_modify_list2.txt.bak ================================================ FILE CONTENTS ================================================ ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2022 Zetaspace Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Patcher/安装.CMD ================================================ @echo off chcp 65001>nul color f0 if not exist "%~dp0组策略文件\安装组策略.cmd" echo 检测到文件缺失,请解压后运行!&pause&exit echo 请允许管理员权限以便安装,谢谢。 %1 mshta vbscript:CreateObject("Shell.Application").ShellExecute("cmd.exe","/c %~s0 ::","","runas",1)(window.close)&&exit cd /d %~dp0 mode con cols=20 lines=10 for /f "tokens=2 delims= " %%i in ('dir /b^|findstr PowerToys') do set modver=%%i set modfulver=%modver% set softname=PowerToys title 修改 %softname% for /l %%i in (1,20,40) do mode con cols=20 lines=%%i for /l %%i in (20,40,160) do mode con cols=%%i lines=40 :start color f0 msg %username% /time:1 /v /w 欢迎使用 Zetaloop 的 %softname% 修改!>nul color 07 for /f "skip=2 tokens=2,*" %%a in ('reg query "HKCR\powertoys\shell\open\command"') do set ptpath=%%b if %errorlevel%==1 echo 警告:找不到注册表信息!你可能未安装 %softname%&set /p =请按任意键退出. . . &exit set ptpath=%ptpath:~1,-6% for /f "delims=" %%a in ('echo %ptpath%') do set installpath=%%~dpa for /f "usebackq delims=" %%a in (`mshta vbscript:CreateObject("Scripting.FileSystemObject"^).GetStandardStream(1^).WriteLine(CStr(CreateObject("Scripting.FileSystemObject"^).GetFileVersion("%ptpath%"^)^)^)(window.close^)`) do set installver=%%a for /f "tokens=1,2,3 delims=." %%a in ('echo %installver%') do set installver=%%a.%%b.%%c echo. echo 修改包版本 %modfulver% echo %softname% 版本 %installver% if not "%modfulver%"=="%installver%" echo ·警告:汉化包版本不对应!&msg %username% /v /w 警告:汉化包版本不对应,强行安装可能导致报错闪退!>nul echo. echo 安装目录 %installpath% if not exist "%installpath%PowerToys.exe" echo 警告:安装目录检测失败! %&set /p =请按任意键退出. . . &exi echo. echo 安装过程: :: echo ·解除安装目录的占用 :: echo ·复制汉化文件到软件目录 echo. echo 为解除文件占用,将会重启 Windows 资源管理器 echo --------------------------------------------------- choice /c thequickbrownfxjmpsvlazydg /n /m "确认无误后,按下 Y 开始:" if %errorlevel%==24 goto install ::if %errorlevel%==22 goto installarm64 echo 取消安装。退出安装器... choice /t 1 /d n >nul exit :installarm64 set modver=%modver% ARM64 :install echo 开始安装! if exist "%tmp%\PowerToys.NewPlus.ShellExtension.dll_*" del "%tmp%\PowerToys.NewPlus.ShellExtension.dll_*" if exist "%tmp%\PowerToys.FileLocksmithExt.dll_*" del "%tmp%\PowerToys.FileLocksmithExt.dll_*" if exist "%tmp%\PowerToys.FileLocksmithContextMenu.dll_*" del "%tmp%\PowerToys.FileLocksmithContextMenu.dll_*" if exist "%tmp%\PowerToys.PowerRenameExt.dll_*" del "%tmp%\PowerToys.PowerRenameExt.dll_*" if exist "%tmp%\PowerToys.PowerRenameContextMenu.dll_*" del "%tmp%\PowerToys.PowerRenameContextMenu.dll_*" if exist "%tmp%\PowerToys.RegistryPreviewExt.dll_*" del "%tmp%\PowerToys.RegistryPreviewExt.dll_*" if exist "%tmp%\PowerToys.ImageResizerExt.dll_*" del "%tmp%\PowerToys.ImageResizerExt.dll_*" :chcp 936>nul call :killpt rmdir /s /q %installpath%WinUI3Apps\Assets\NewPlus\Templates xcopy ".\%softname% %modver%\*" "%installpath%" /e /y if %errorlevel%==0 goto success echo 尝试解锁... taskkill /f /im explorer.exe rmdir /s /q %installpath%WinUI3Apps\Assets\NewPlus\Templates xcopy ".\%softname% %modver%\*" "%installpath%" /e /y if %errorlevel%==0 start explorer&goto success start explorer echo 尝试移走一些文件... echo 这些文件放到了系统临时文件夹。 :: echo 你可能需要重启电脑来使汉化生效。 :: if exist "%tmp%\PowerToys.NewPlus.ShellExtension.dll_*" del "%tmp%\PowerToys.NewPlus.ShellExtension.dll_*" if exist "%installpath%WinUI3Apps\PowerToys.NewPlus.ShellExtension.dll" move "%installpath%WinUI3Apps\PowerToys.NewPlus.ShellExtension.dll" "%tmp%\PowerToys.NewPlus.ShellExtension.dll_%random%.tmp" if exist "%tmp%\PowerToys.FileLocksmithExt.dll_*" del "%tmp%\PowerToys.FileLocksmithExt.dll_*" if exist "%installpath%WinUI3Apps\PowerToys.FileLocksmithExt.dll" move "%installpath%WinUI3Apps\PowerToys.FileLocksmithExt.dll" "%tmp%\PowerToys.FileLocksmithExt.dll_%random%.tmp" if exist "%tmp%\PowerToys.FileLocksmithContextMenu.dll_*" del "%tmp%\PowerToys.FileLocksmithContextMenu.dll_*" if exist "%installpath%WinUI3Apps\PowerToys.FileLocksmithContextMenu.dll" move "%installpath%WinUI3Apps\PowerToys.FileLocksmithContextMenu.dll" "%tmp%\PowerToys.FileLocksmithContextMenu.dll_%random%.tmp" if exist "%tmp%\PowerToys.PowerRenameExt.dll_*" del "%tmp%\PowerToys.PowerRenameExt.dll_*" if exist "%installpath%WinUI3Apps\PowerToys.PowerRenameExt.dll" move "%installpath%WinUI3Apps\PowerToys.PowerRenameExt.dll" "%tmp%\PowerToys.PowerRenameExt.dll_%random%.tmp" if exist "%tmp%\PowerToys.PowerRenameContextMenu.dll_*" del "%tmp%\PowerToys.PowerRenameContextMenu.dll_*" if exist "%installpath%WinUI3Apps\PowerToys.PowerRenameContextMenu.dll" move "%installpath%WinUI3Apps\PowerToys.PowerRenameContextMenu.dll" "%tmp%\PowerToys.PowerRenameContextMenu.dll_%random%.tmp" if exist "%tmp%\PowerToys.RegistryPreviewExt.dll_*" del "%tmp%\PowerToys.RegistryPreviewExt.dll_*" if exist "%installpath%WinUI3Apps\PowerToys.RegistryPreviewExt.dll" move "%installpath%WinUI3Apps\PowerToys.RegistryPreviewExt.dll" "%tmp%\PowerToys.RegistryPreviewExt.dll_%random%.tmp" if exist "%tmp%\PowerToys.ImageResizerExt.dll_*" del "%tmp%\PowerToys.ImageResizerExt.dll_*" if exist "%installpath%PowerToys.ImageResizerExt.dll" move "%installpath%PowerToys.ImageResizerExt.dll" "%tmp%\PowerToys.ImageResizerExt.dll_%random%.tmp" echo 再试一次... echo. echo. call :killpt rmdir /s /q %installpath%WinUI3Apps\Assets\NewPlus\Templates xcopy ".\%softname% %modver%\*" "%installpath%" /e /y if %errorlevel%==0 goto success echo 尝试解锁... taskkill /f /im explorer.exe rmdir /s /q %installpath%WinUI3Apps\Assets\NewPlus\Templates xcopy ".\%softname% %modver%\*" "%installpath%" /e /y if %errorlevel%==0 start explorer&goto success start explorer echo 安装失败!未能解除文件占用,请尝试手动复制,或重启后再试。 :: msg %username% /time:2 /v /w 安装失败!未能解除文件占用,请尝试手动复制,或重启后再试。>nul set /p =请按任意键退出. . . &exit goto :eof :success echo 重启文件资源管理器以加载汉化右键菜单 :: powershell exit taskkill /f /im explorer.exe>nul 2>nul taskkill /f /im explorer.exe>nul 2>nul taskkill /f /im explorer.exe>nul 2>nul powershell exit start explorer powershell exit echo 启动 PowerToys mshta vbscript:CreateObject("Shell.Application").ShellExecute("%installpath%PowerToys.exe","","","runas",1)(window.close) powershell exit echo 打开 PowerToys 设置 mshta vbscript:CreateObject("Shell.Application").ShellExecute("%installpath%PowerToys.exe","","","runas",1)(window.close) powershell exit echo 安装完成 :: Powershell 用于提供一个恰当的启动延迟 :: msg %username% /time:2 /v /w 安装完成!>nul goto :eof :killpt taskkill /F /IM "PowerToys*" taskkill /F /IM "PowerToys*" goto :eof ================================================ FILE: Patcher/安装组策略.cmd ================================================ chcp 65001>nul @echo off echo 正在安装 PowerToys 组策略... %1 mshta vbscript:CreateObject("Shell.Application").ShellExecute("cmd.exe","/c %~s0 ::","","runas",1)(window.close)&&goto :eOF cd /d %~dp0 xcopy /Y /I /E ".\PowerToys.admx" "%systemroot%\PolicyDefinitions\" xcopy /Y /I /E ".\zh-CN\PowerToys.adml" "%systemroot%\PolicyDefinitions\zh-CN\" xcopy /Y /I /E ".\en-US\PowerToys.adml" "%systemroot%\PolicyDefinitions\en-US\" msg %username% 安装完成 ================================================ FILE: PowerToys/global.json ================================================ { "sdk": { "allowPrerelease": false } } ================================================ FILE: PowerToys/src/ActionRunner/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 无法下载 .NET Core Desktop Runtime 3.1,请自行安装。 PowerToys 安装错误 PowerToys 有版本更新。请访问 GitHub 页面下载。 ================================================ FILE: PowerToys/src/Monaco/index.html ================================================  Previewer for developer Files
================================================ FILE: PowerToys/src/Update/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 无法下载 .NET Core Desktop Runtime 3.1,请自行安装。 PowerToys 安装错误 PowerToys 有版本更新。 立刻更新 PowerToys 有版本更新。请访问 GitHub 页面下载。 更多信息 PowerToys 更新 ================================================ FILE: PowerToys/src/Version.props ================================================ 0.88.0 SHA256 ================================================ FILE: PowerToys/src/common/interop/keyboard_layout.cpp ================================================ #include "pch.h" #include #include #include "keyboard_layout_impl.h" #include "shared_constants.h" constexpr DWORD numpadOriginBit = 1ull << 31; LayoutMap::LayoutMap() : impl(new LayoutMap::LayoutMapImpl()) { } LayoutMap::~LayoutMap() { delete impl; } void LayoutMap::UpdateLayout() { impl->UpdateLayout(); } std::wstring LayoutMap::GetKeyName(DWORD key) { return impl->GetKeyName(key); } DWORD LayoutMap::GetKeyFromName(const std::wstring& name) { auto list = impl->GetKeyNameList(false); for (const auto& [value, key] : list) { if (key == name) return value; } return {}; } std::vector LayoutMap::GetKeyCodeList(const bool isShortcut) { return impl->GetKeyCodeList(isShortcut); } std::vector> LayoutMap::GetKeyNameList(const bool isShortcut) { return impl->GetKeyNameList(isShortcut); } // Function to return the unicode string name of the key std::wstring LayoutMap::LayoutMapImpl::GetKeyName(DWORD key) { std::wstring result = L"未定义"; std::lock_guard lock(keyboardLayoutMap_mutex); UpdateLayout(); auto it = keyboardLayoutMap.find(key); if (it != keyboardLayoutMap.end()) { result = it->second; } return result; } bool mapKeycodeToUnicode(const int vCode, HKL layout, const BYTE* keyState, std::array& outBuffer) { // Get the scan code from the virtual key code const UINT scanCode = MapVirtualKeyExW(vCode, MAPVK_VK_TO_VSC, layout); // Get the unicode representation from the virtual key code and scan code pair const UINT wFlags = 1 << 2; // If bit 2 is set, keyboard state is not changed (Windows 10, version 1607 and newer) const int result = ToUnicodeEx(vCode, scanCode, keyState, outBuffer.data(), static_cast(outBuffer.size()), wFlags, layout); return result != 0; } // Update Keyboard layout according to input locale identifier void LayoutMap::LayoutMapImpl::UpdateLayout() { // Get keyboard layout for current thread const HKL layout = GetKeyboardLayout(0); if (layout == previousLayout) { return; } previousLayout = layout; if (!isKeyCodeListGenerated) { unicodeKeys.clear(); unknownKeys.clear(); } std::array btKeys = { 0 }; // Only set the Caps Lock key to on for the key names in uppercase btKeys[VK_CAPITAL] = 1; // Iterate over all the virtual key codes. virtual key 0 is not used for (int i = 1; i < 256; i++) { std::array szBuffer = { 0 }; if (mapKeycodeToUnicode(i, layout, btKeys.data(), szBuffer)) { keyboardLayoutMap[i] = szBuffer.data(); if (!isKeyCodeListGenerated) { unicodeKeys[i] = szBuffer.data(); } continue; } // Store the virtual key code as string std::wstring vk = L"虚拟键值 "; vk += std::to_wstring(i); keyboardLayoutMap[i] = vk; if (!isKeyCodeListGenerated) { unknownKeys[i] = vk; } } // Override special key names like Shift, Ctrl etc because they don't have unicode mappings and key names like Enter, Space as they appear as "\r", " " // To do: localization keyboardLayoutMap[VK_CANCEL] = L"Break"; keyboardLayoutMap[VK_BACK] = L"Backspace"; keyboardLayoutMap[VK_TAB] = L"Tab"; keyboardLayoutMap[VK_CLEAR] = L"Clear"; keyboardLayoutMap[VK_SHIFT] = L"Shift"; keyboardLayoutMap[VK_CONTROL] = L"Ctrl"; keyboardLayoutMap[VK_MENU] = L"Alt"; keyboardLayoutMap[VK_PAUSE] = L"Pause"; keyboardLayoutMap[VK_CAPITAL] = L"Caps Lock"; keyboardLayoutMap[VK_ESCAPE] = L"Esc"; keyboardLayoutMap[VK_SPACE] = L"Space"; keyboardLayoutMap[VK_LEFT] = L"Left"; keyboardLayoutMap[VK_RIGHT] = L"Right"; keyboardLayoutMap[VK_UP] = L"Up"; keyboardLayoutMap[VK_DOWN] = L"Down"; keyboardLayoutMap[VK_INSERT] = L"Insert"; keyboardLayoutMap[VK_DELETE] = L"Delete"; keyboardLayoutMap[VK_PRIOR] = L"PgUp"; keyboardLayoutMap[VK_NEXT] = L"PgDn"; keyboardLayoutMap[VK_HOME] = L"Home"; keyboardLayoutMap[VK_END] = L"End"; keyboardLayoutMap[VK_RETURN] = L"Enter"; keyboardLayoutMap[VK_LEFT | numpadOriginBit] = L"Left (小键盘)"; keyboardLayoutMap[VK_RIGHT | numpadOriginBit] = L"Right (小键盘)"; keyboardLayoutMap[VK_UP | numpadOriginBit] = L"Up (小键盘)"; keyboardLayoutMap[VK_DOWN | numpadOriginBit] = L"Down (小键盘)"; keyboardLayoutMap[VK_INSERT | numpadOriginBit] = L"Insert (小键盘)"; keyboardLayoutMap[VK_DELETE | numpadOriginBit] = L"Delete (小键盘)"; keyboardLayoutMap[VK_PRIOR | numpadOriginBit] = L"PgUp (小键盘)"; keyboardLayoutMap[VK_NEXT | numpadOriginBit] = L"PgDn (小键盘)"; keyboardLayoutMap[VK_HOME | numpadOriginBit] = L"Home (小键盘)"; keyboardLayoutMap[VK_END | numpadOriginBit] = L"End (小键盘)"; keyboardLayoutMap[VK_RETURN | numpadOriginBit] = L"Enter (小键盘)"; keyboardLayoutMap[VK_DIVIDE | numpadOriginBit] = L"/ (小键盘)"; keyboardLayoutMap[VK_SUBTRACT] = L"- (小键盘)"; keyboardLayoutMap[VK_SELECT] = L"Select"; keyboardLayoutMap[VK_PRINT] = L"Print"; keyboardLayoutMap[VK_EXECUTE] = L"Execute"; keyboardLayoutMap[VK_SNAPSHOT] = L"Print Screen"; keyboardLayoutMap[VK_HELP] = L"Help"; keyboardLayoutMap[VK_LWIN] = L"Win (左)"; keyboardLayoutMap[VK_RWIN] = L"Win (右)"; keyboardLayoutMap[VK_APPS] = L"Apps/Menu"; keyboardLayoutMap[VK_SLEEP] = L"Sleep"; keyboardLayoutMap[VK_NUMPAD0] = L"0 (小键盘)"; keyboardLayoutMap[VK_NUMPAD1] = L"1 (小键盘)"; keyboardLayoutMap[VK_NUMPAD2] = L"2 (小键盘)"; keyboardLayoutMap[VK_NUMPAD3] = L"3 (小键盘)"; keyboardLayoutMap[VK_NUMPAD4] = L"4 (小键盘)"; keyboardLayoutMap[VK_NUMPAD5] = L"5 (小键盘)"; keyboardLayoutMap[VK_NUMPAD6] = L"6 (小键盘)"; keyboardLayoutMap[VK_NUMPAD7] = L"7 (小键盘)"; keyboardLayoutMap[VK_NUMPAD8] = L"8 (小键盘)"; keyboardLayoutMap[VK_NUMPAD9] = L"9 (小键盘)"; keyboardLayoutMap[VK_SEPARATOR] = L"Separator"; keyboardLayoutMap[VK_F1] = L"F1"; keyboardLayoutMap[VK_F2] = L"F2"; keyboardLayoutMap[VK_F3] = L"F3"; keyboardLayoutMap[VK_F4] = L"F4"; keyboardLayoutMap[VK_F5] = L"F5"; keyboardLayoutMap[VK_F6] = L"F6"; keyboardLayoutMap[VK_F7] = L"F7"; keyboardLayoutMap[VK_F8] = L"F8"; keyboardLayoutMap[VK_F9] = L"F9"; keyboardLayoutMap[VK_F10] = L"F10"; keyboardLayoutMap[VK_F11] = L"F11"; keyboardLayoutMap[VK_F12] = L"F12"; keyboardLayoutMap[VK_F13] = L"F13"; keyboardLayoutMap[VK_F14] = L"F14"; keyboardLayoutMap[VK_F15] = L"F15"; keyboardLayoutMap[VK_F16] = L"F16"; keyboardLayoutMap[VK_F17] = L"F17"; keyboardLayoutMap[VK_F18] = L"F18"; keyboardLayoutMap[VK_F19] = L"F19"; keyboardLayoutMap[VK_F20] = L"F20"; keyboardLayoutMap[VK_F21] = L"F21"; keyboardLayoutMap[VK_F22] = L"F22"; keyboardLayoutMap[VK_F23] = L"F23"; keyboardLayoutMap[VK_F24] = L"F24"; keyboardLayoutMap[VK_NUMLOCK] = L"Num Lock"; keyboardLayoutMap[VK_SCROLL] = L"Scroll Lock"; keyboardLayoutMap[VK_LSHIFT] = L"Shift (左)"; keyboardLayoutMap[VK_RSHIFT] = L"Shift (右)"; keyboardLayoutMap[VK_LCONTROL] = L"Ctrl (左)"; keyboardLayoutMap[VK_RCONTROL] = L"Ctrl (右)"; keyboardLayoutMap[VK_LMENU] = L"Alt (左)"; keyboardLayoutMap[VK_RMENU] = L"Alt (右)"; keyboardLayoutMap[VK_BROWSER_BACK] = L"Browser Back"; keyboardLayoutMap[VK_BROWSER_FORWARD] = L"Browser Forward"; keyboardLayoutMap[VK_BROWSER_REFRESH] = L"Browser Refresh"; keyboardLayoutMap[VK_BROWSER_STOP] = L"Browser Stop"; keyboardLayoutMap[VK_BROWSER_SEARCH] = L"Browser Search"; keyboardLayoutMap[VK_BROWSER_FAVORITES] = L"Browser Favorites"; keyboardLayoutMap[VK_BROWSER_HOME] = L"Browser Home"; keyboardLayoutMap[VK_VOLUME_MUTE] = L"Volume Mute"; keyboardLayoutMap[VK_VOLUME_DOWN] = L"Volume Down"; keyboardLayoutMap[VK_VOLUME_UP] = L"Volume Up"; keyboardLayoutMap[VK_MEDIA_NEXT_TRACK] = L"Next Track"; keyboardLayoutMap[VK_MEDIA_PREV_TRACK] = L"Previous Track"; keyboardLayoutMap[VK_MEDIA_STOP] = L"Stop Media"; keyboardLayoutMap[VK_MEDIA_PLAY_PAUSE] = L"Play/Pause Media"; keyboardLayoutMap[VK_LAUNCH_MAIL] = L"Start Mail"; keyboardLayoutMap[VK_LAUNCH_MEDIA_SELECT] = L"Select Media"; keyboardLayoutMap[VK_LAUNCH_APP1] = L"Start App 1"; keyboardLayoutMap[VK_LAUNCH_APP2] = L"Start App 2"; keyboardLayoutMap[VK_PACKET] = L"Packet"; keyboardLayoutMap[VK_ATTN] = L"Attn"; keyboardLayoutMap[VK_CRSEL] = L"CrSel"; keyboardLayoutMap[VK_EXSEL] = L"ExSel"; keyboardLayoutMap[VK_EREOF] = L"Erase EOF"; keyboardLayoutMap[VK_PLAY] = L"Play"; keyboardLayoutMap[VK_ZOOM] = L"Zoom"; keyboardLayoutMap[VK_PA1] = L"PA1"; keyboardLayoutMap[VK_OEM_CLEAR] = L"Clear"; keyboardLayoutMap[0xFF] = L"未定义"; keyboardLayoutMap[CommonSharedConstants::VK_WIN_BOTH] = L"Win"; keyboardLayoutMap[VK_KANA] = L"IME Kana"; keyboardLayoutMap[VK_HANGEUL] = L"IME Hangeul"; keyboardLayoutMap[VK_HANGUL] = L"IME Hangul"; keyboardLayoutMap[VK_IME_ON] = L"IME On"; keyboardLayoutMap[VK_JUNJA] = L"IME Junja"; keyboardLayoutMap[VK_FINAL] = L"IME Final"; keyboardLayoutMap[VK_HANJA] = L"IME Hanja"; keyboardLayoutMap[VK_KANJI] = L"IME Kanji"; keyboardLayoutMap[VK_IME_OFF] = L"IME Off"; keyboardLayoutMap[VK_CONVERT] = L"IME Convert"; keyboardLayoutMap[VK_NONCONVERT] = L"IME Non-Convert"; keyboardLayoutMap[VK_ACCEPT] = L"IME Kana"; keyboardLayoutMap[VK_MODECHANGE] = L"IME Mode Change"; keyboardLayoutMap[VK_DECIMAL] = L". (小键盘)"; keyboardLayoutMap[CommonSharedConstants::VK_DISABLED] = L"禁用"; } // Function to return the list of key codes in the order for the drop down. It creates it if it doesn't exist std::vector LayoutMap::LayoutMapImpl::GetKeyCodeList(const bool isShortcut) { std::lock_guard lock(keyboardLayoutMap_mutex); UpdateLayout(); std::vector keyCodes; if (!isKeyCodeListGenerated) { // Add character keys for (auto& it : unicodeKeys) { // If it was not renamed with a special name if (it.second == keyboardLayoutMap[it.first]) { keyCodes.push_back(it.first); } } // Add modifier keys in alphabetical order keyCodes.push_back(VK_MENU); keyCodes.push_back(VK_LMENU); keyCodes.push_back(VK_RMENU); keyCodes.push_back(VK_CONTROL); keyCodes.push_back(VK_LCONTROL); keyCodes.push_back(VK_RCONTROL); keyCodes.push_back(VK_SHIFT); keyCodes.push_back(VK_LSHIFT); keyCodes.push_back(VK_RSHIFT); keyCodes.push_back(CommonSharedConstants::VK_WIN_BOTH); keyCodes.push_back(VK_LWIN); keyCodes.push_back(VK_RWIN); // Add all other special keys std::vector specialKeys; for (int i = 1; i < 256; i++) { // If it is not already been added (i.e. it was either a modifier or had a unicode representation) if (std::find(keyCodes.begin(), keyCodes.end(), i) == keyCodes.end()) { // If it is any other key but it is not named as VK # auto it = unknownKeys.find(i); if (it == unknownKeys.end()) { specialKeys.push_back(i); } else if (unknownKeys[i] != keyboardLayoutMap[i]) { specialKeys.push_back(i); } } } // Add numpad keys for (auto it = keyboardLayoutMap.rbegin(); it->first & numpadOriginBit; ++it) { keyCodes.push_back(it->first); } // Sort the special keys in alphabetical order std::sort(specialKeys.begin(), specialKeys.end(), [&](const DWORD& lhs, const DWORD& rhs) { return keyboardLayoutMap[lhs] < keyboardLayoutMap[rhs]; }); for (int i = 0; i < specialKeys.size(); i++) { keyCodes.push_back(specialKeys[i]); } // Add unknown keys for (auto& it : unknownKeys) { // If it was not renamed with a special name if (it.second == keyboardLayoutMap[it.first]) { keyCodes.push_back(it.first); } } keyCodeList = keyCodes; isKeyCodeListGenerated = true; } else { keyCodes = keyCodeList; } // If it is a key list for the shortcut control then we add a "None" key at the start if (isShortcut) { keyCodes.insert(keyCodes.begin(), 0); } return keyCodes; } std::vector> LayoutMap::LayoutMapImpl::GetKeyNameList(const bool isShortcut) { std::vector> keyNames; std::vector keyCodes = GetKeyCodeList(isShortcut); std::lock_guard lock(keyboardLayoutMap_mutex); // If it is a key list for the shortcut control then we add a "None" key at the start if (isShortcut) { keyNames.push_back({ 0, L"无" }); for (int i = 1; i < keyCodes.size(); i++) { keyNames.push_back({ keyCodes[i], keyboardLayoutMap[keyCodes[i]] }); } } else { for (int i = 0; i < keyCodes.size(); i++) { keyNames.push_back({ keyCodes[i], keyboardLayoutMap[keyCodes[i]] }); } } return keyNames; } ================================================ FILE: PowerToys/src/common/sysinternals/Eula/eula.c ================================================ #pragma once #pragma warning( disable: 4996) #include #include #include #include #include #include #include #include "Eula.h" #include "dll.h" #define IDC_TEXT 500 #define IDC_PRINT 501 #define IDC_TEXT1 502 static const char * EulaText[] = { "{\\rtf1\\ansi\\ansicpg1252\\deff0\\nouicompat\\deflang1033{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 Tahoma;}{\\f1\\fnil\\fcharset0 Calibri;}}", "{\\colortbl ;\\red0\\green0\\blue255;\\red0\\green0\\blue0;}", "{\\*\\generator Riched20 10.0.10240}\\viewkind4\\uc1 ", "\\pard\\brdrb\\brdrs\\brdrw10\\brsp20 \\sb120\\sa120\\b\\f0\\fs24 SYSINTERNALS SOFTWARE LICENSE TERMS\\fs28\\par", "\\pard\\sb120\\sa120\\b0\\fs19 These license terms are an agreement between Sysinternals (a wholly owned subsidiary of Microsoft Corporation) and you. Please read them. They apply to the software you are downloading from Sysinternals.com, which includes the media on which you received it, if any. The terms also apply to any Sysinternals\\par", "\\pard\\fi-363\\li720\\sb120\\sa120\\tx720\\'b7\\tab updates,\\par", "\\pard\\fi-363\\li720\\sb120\\sa120\\'b7\\tab supplements,\\par", "\\'b7\\tab Internet-based services, and \\par", "\\'b7\\tab support services\\par", "\\pard\\sb120\\sa120 for this software, unless other terms accompany those items. If so, those terms apply.\\par", "\\b BY USING THE SOFTWARE, YOU ACCEPT THESE TERMS. IF YOU DO NOT ACCEPT THEM, DO NOT USE THE SOFTWARE.\\par", "\\pard\\brdrt\\brdrs\\brdrw10\\brsp20 \\sb120\\sa120 If you comply with these license terms, you have the rights below.\\par", "\\pard\\fi-357\\li357\\sb120\\sa120\\tx360\\fs20 1.\\tab\\fs19 INSTALLATION AND USE RIGHTS. \\b0 You may install and use any number of copies of the software on your devices.\\b\\par", "\\caps\\fs20 2.\\tab\\fs19 Scope of License\\caps0 .\\b0 The software is licensed, not sold. This agreement only gives you some rights to use the software. Sysinternals reserves all other rights. Unless applicable law gives you more rights despite this limitation, you may use the software only as expressly permitted in this agreement. In doing so, you must comply with any technical limitations in the software that only allow you to use it in certain ways. You may not\\b\\par", "\\pard\\fi-363\\li720\\sb120\\sa120\\tx720\\b0\\'b7\\tab work around any technical limitations in the binary versions of the software;\\par", "\\pard\\fi-363\\li720\\sb120\\sa120\\'b7\\tab reverse engineer, decompile or disassemble the binary versions of the software, except and only to the extent that applicable law expressly permits, despite this limitation;\\par", "\\'b7\\tab make more copies of the software than specified in this agreement or allowed by applicable law, despite this limitation;\\par", "\\'b7\\tab publish the software for others to copy;\\par", "\\'b7\\tab rent, lease or lend the software;\\par", "\\'b7\\tab transfer the software or this agreement to any third party; or\\par", "\\'b7\\tab use the software for commercial software hosting services.\\par", "\\pard\\fi-357\\li357\\sb120\\sa120\\tx360\\b\\fs20 3.\\tab SENSITIVE INFORMATION. \\b0 Please be aware that, similar to other debug tools that capture \\ldblquote process state\\rdblquote information, files saved by Sysinternals tools may include personally identifiable or other sensitive information (such as usernames, passwords, paths to files accessed, and paths to registry accessed). By using this software, you acknowledge that you are aware of this and take sole responsibility for any personally identifiable or other sensitive information provided to Microsoft or any other party through your use of the software.\\b\\par", "5. \\tab\\fs19 DOCUMENTATION.\\b0 Any person that has valid access to your computer or internal network may copy and use the documentation for your internal, reference purposes.\\b\\par", "\\caps\\fs20 6.\\tab\\fs19 Export Restrictions\\caps0 .\\b0 The software is subject to United States export laws and regulations. You must comply with all domestic and international export laws and regulations that apply to the software. These laws include restrictions on destinations, end users and end use. For additional information, see {\\cf1\\ul{\\field{\\*\\fldinst{HYPERLINK www.microsoft.com/exporting }}{\\fldrslt{www.microsoft.com/exporting}}}}\\cf1\\ul\\f0\\fs19 <{{\\field{\\*\\fldinst{HYPERLINK \"http://www.microsoft.com/exporting\"}}{\\fldrslt{http://www.microsoft.com/exporting}}}}\\f0\\fs19 >\\cf0\\ulnone .\\b\\par", "\\caps\\fs20 7.\\tab\\fs19 SUPPORT SERVICES.\\caps0 \\b0 Because this software is \"as is, \" we may not provide support services for it.\\b\\par", "\\caps\\fs20 8.\\tab\\fs19 Entire Agreement.\\b0\\caps0 This agreement, and the terms for supplements, updates, Internet-based services and support services that you use, are the entire agreement for the software and support services.\\par", "\\pard\\keepn\\fi-360\\li360\\sb120\\sa120\\tx360\\cf2\\b\\caps\\fs20 9.\\tab\\fs19 Applicable Law\\caps0 .\\par", "\\pard\\fi-363\\li720\\sb120\\sa120\\tx720\\cf0\\fs20 a.\\tab\\fs19 United States.\\b0 If you acquired the software in the United States, Washington state law governs the interpretation of this agreement and applies to claims for breach of it, regardless of conflict of laws principles. The laws of the state where you live govern all other claims, including claims under state consumer protection laws, unfair competition laws, and in tort.\\b\\par", "\\pard\\fi-363\\li720\\sb120\\sa120\\fs20 b.\\tab\\fs19 Outside the United States.\\b0 If you acquired the software in any other country, the laws of that country apply.\\b\\par", "\\pard\\fi-357\\li357\\sb120\\sa120\\tx360\\caps\\fs20 10.\\tab\\fs19 Legal Effect.\\b0\\caps0 This agreement describes certain legal rights. You may have other rights under the laws of your country. You may also have rights with respect to the party from whom you acquired the software. This agreement does not change your rights under the laws of your country if the laws of your country do not permit it to do so.\\b\\caps\\par", "\\fs20 11.\\tab\\fs19 Disclaimer of Warranty.\\caps0 \\caps The software is licensed \"as - is.\" You bear the risk of using it. SYSINTERNALS gives no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this agreement cannot change. To the extent permitted under your local laws, SYSINTERNALS excludes the implied warranties of merchantability, fitness for a particular purpose and non-infringement.\\par", "\\pard\\fi-360\\li360\\sb120\\sa120\\tx360\\fs20 12.\\tab\\fs19 Limitation on and Exclusion of Remedies and Damages. You can recover from SYSINTERNALS and its suppliers only direct damages up to U.S. $5.00. You cannot recover any other damages, including consequential, lost profits, special, indirect or incidental damages.\\par", "\\pard\\li357\\sb120\\sa120\\b0\\caps0 This limitation applies to\\par", "\\pard\\fi-363\\li720\\sb120\\sa120\\tx720\\'b7\\tab anything related to the software, services, content (including code) on third party Internet sites, or third party programs; and\\par", "\\pard\\fi-363\\li720\\sb120\\sa120\\'b7\\tab claims for breach of contract, breach of warranty, guarantee or condition, strict liability, negligence, or other tort to the extent permitted by applicable law.\\par", "\\pard\\li360\\sb120\\sa120 It also applies even if Sysinternals knew or should have known about the possibility of the damages. The above limitation or exclusion may not apply to you because your country may not allow the exclusion or limitation of incidental, consequential or other damages.\\par", "\\pard\\b Please note: As this software is distributed in Quebec, Canada, some of the clauses in this agreement are provided below in French.\\par", "\\pard\\sb240\\lang1036 Remarque : Ce logiciel \\'e9tant distribu\\'e9 au Qu\\'e9bec, Canada, certaines des clauses dans ce contrat sont fournies ci-dessous en fran\\'e7ais.\\par", "\\pard\\sb120\\sa120 EXON\\'c9RATION DE GARANTIE.\\b0 Le logiciel vis\\'e9 par une licence est offert \\'ab tel quel \\'bb. Toute utilisation de ce logiciel est \\'e0 votre seule risque et p\\'e9ril. Sysinternals n'accorde aucune autre garantie expresse. Vous pouvez b\\'e9n\\'e9ficier de droits additionnels en vertu du droit local sur la protection dues consommateurs, que ce contrat ne peut modifier. La ou elles sont permises par le droit locale, les garanties implicites de qualit\\'e9 marchande, d'ad\\'e9quation \\'e0 un usage particulier et d'absence de contrefa\\'e7on sont exclues.\\par", "\\pard\\keepn\\sb120\\sa120\\b LIMITATION DES DOMMAGES-INT\\'c9R\\'caTS ET EXCLUSION DE RESPONSABILIT\\'c9 POUR LES DOMMAGES.\\b0 Vous pouvez obtenir de Sysinternals et de ses fournisseurs une indemnisation en cas de dommages directs uniquement \\'e0 hauteur de 5,00 $ US. Vous ne pouvez pr\\'e9tendre \\'e0 aucune indemnisation pour les autres dommages, y compris les dommages sp\\'e9ciaux, indirects ou accessoires et pertes de b\\'e9n\\'e9fices.\\par", "\\lang1033 Cette limitation concerne :\\par", "\\pard\\keepn\\fi-360\\li720\\sb120\\sa120\\tx720\\lang1036\\'b7\\tab tout ce qui est reli\\'e9 au logiciel, aux services ou au contenu (y compris le code) figurant sur des sites Internet tiers ou dans des programmes tiers ; et\\par", "\\pard\\fi-363\\li720\\sb120\\sa120\\tx720\\'b7\\tab les r\\'e9clamations au titre de violation de contrat ou de garantie, ou au titre de responsabilit\\'e9 stricte, de n\\'e9gligence ou d'une autre faute dans la limite autoris\\'e9e par la loi en vigueur.\\par", "\\pard\\sb120\\sa120 Elle s'applique \\'e9galement, m\\'eame si Sysinternals connaissait ou devrait conna\\'eetre l'\\'e9ventualit\\'e9 d'un tel dommage. Si votre pays n'autorise pas l'exclusion ou la limitation de responsabilit\\'e9 pour les dommages indirects, accessoires ou de quelque nature que ce soit, il se peut que la limitation ou l'exclusion ci-dessus ne s'appliquera pas \\'e0 votre \\'e9gard.\\par", "\\b EFFET JURIDIQUE.\\b0 Le pr\\'e9sent contrat d\\'e9crit certains droits juridiques. Vous pourriez avoir d'autres droits pr\\'e9vus par les lois de votre pays. Le pr\\'e9sent contrat ne modifie pas les droits que vous conf\\'e8rent les lois de votre pays si celles-ci ne le permettent pas.\\b\\par", "\\pard\\b0\\fs20\\lang1033\\par", "\\pard\\sa200\\sl276\\slmult1\\f1\\fs22\\lang9\\par", "}", NULL }; static const wchar_t *Raw_EulaText = L"SYSINTERNALS SOFTWARE LICENSE TERMS\nThese license terms are an agreement between Sysinternals(a wholly owned subsidiary of Microsoft Corporation) and you.Please read them.They apply to the software you are downloading from technet.microsoft.com / sysinternals, which includes the media on which you received it, if any.The terms also apply to any Sysinternals\n* updates,\n*supplements,\n*Internet - based services,\n*and support services\nfor this software, unless other terms accompany those items.If so, those terms apply.\nBY USING THE SOFTWARE, YOU ACCEPT THESE TERMS.IF YOU DO NOT ACCEPT THEM, DO NOT USE THE SOFTWARE.\n\nIf you comply with these license terms, you have the rights below.\nINSTALLATION AND USER RIGHTS\nYou may install and use any number of copies of the software on your devices.\n\nSCOPE OF LICENSE\nThe software is licensed, not sold.This agreement only gives you some rights to use the software.Sysinternals reserves all other rights.Unless applicable law gives you more rights despite this limitation, you may use the software only as expressly permitted in this agreement.In doing so, you must comply with any technical limitations in the software that only allow you to use it in certain ways.You may not\n* work around any technical limitations in the software;\n*reverse engineer, decompile or disassemble the software, except and only to the extent that applicable law expressly permits, despite this limitation;\n*make more copies of the software than specified in this agreement or allowed by applicable law, despite this limitation;\n*publish the software for others to copy;\n*rent, lease or lend the software;\n*transfer the software or this agreement to any third party; or\n* use the software for commercial software hosting services.\n\nSENSITIVE INFORMATION\nPlease be aware that, similar to other debug tools that capture “process state” information, files saved by Sysinternals tools may include personally identifiable or other sensitive information(such as usernames, passwords, paths to files accessed, and paths to registry accessed).By using this software, you acknowledge that you are aware of this and take sole responsibility for any personally identifiable or other sensitive information provided to Microsoft or any other party through your use of the software.\n\nDOCUMENTATION\nAny person that has valid access to your computer or internal network may copy and use the documentation for your internal, reference purposes.\n\nEXPORT RESTRICTIONS\nThe software is subject to United States export laws and regulations.You must comply with all domestic and international export laws and regulations that apply to the software.These laws include restrictions on destinations, end users and end use.For additional information, see www.microsoft.com / exporting .\n\nSUPPORT SERVICES\nBecause this software is \"as is, \" we may not provide support services for it.\n\nENTIRE AGREEMENT\nThis agreement, and the terms for supplements, updates, Internet - based services and support services that you use, are the entire agreement for the software and support services.\n\nAPPLICABLE LAW\nUnited States.If you acquired the software in the United States, Washington state law governs the interpretation of this agreement and applies to claims for breach of it, regardless of conflict of laws principles.The laws of the state where you live govern all other claims, including claims under state consumer protection laws, unfair competition laws, and in tort.\nOutside the United States.If you acquired the software in any other country, the laws of that country apply.\n\nLEGAL EFFECT\nThis agreement describes certain legal rights.You may have other rights under the laws of your country.You may also have rights with respect to the party from whom you acquired the software.This agreement does not change your rights under the laws of your country if the laws of your country do not permit it to do so.\n\nDISCLAIMER OF WARRANTY\nThe software is licensed \"as - is.\" You bear the risk of using it.Sysinternals gives no express warranties, guarantees or conditions.You may have additional consumer rights under your local laws which this agreement cannot change.To the extent permitted under your local laws, sysinternals excludes the implied warranties of merchantability, fitness for a particular purpose and non - infringement.\n\nLIMITATION ON AND EXCLUSION OF REMEDIES AND DAMAGES\nYou can recover from sysinternals and its suppliers only direct damages up to U.S.$5.00.You cannot recover any other damages, including consequential, lost profits, special, indirect or incidental damages.\nThis limitation applies to\n* anything related to the software, services, content(including code) on third party Internet sites, or third party programs; and\n* claims for breach of contract, breach of warranty, guarantee or condition, strict liability, negligence, or other tort to the extent permitted by applicable law.\nIt also applies even if Sysinternals knew or should have known about the possibility of the damages.The above limitation or exclusion may not apply to you because your country may not allow the exclusion or limitation of incidental, consequential or other damages.\nPlease note : As this software is distributed in Quebec, Canada, some of the clauses in this agreement are provided below in French.\nRemarque : Ce logiciel étant distribué au Québec, Canada, certaines des clauses dans ce contrat sont fournies ci - dessous en français.\n EXONÉRATION DE GARANTIE.Le logiciel visé par une licence est offert « tel quel ».Toute utilisation de ce logiciel est à votre seule risque et péril.Sysinternals n'accorde aucune autre garantie expresse. Vous pouvez bénéficier de droits additionnels en vertu du droit local sur la protection dues consommateurs, que ce contrat ne peut modifier. La ou elles sont permises par le droit locale, les garanties implicites de qualité marchande, d'adéquation à un usage particulier et d'absence de contrefaçon sont exclues.\n LIMITATION DES DOMMAGES - INTÉRÊTS ET EXCLUSION DE RESPONSABILITÉ POUR LES DOMMAGES.Vous pouvez obtenir de Sysinternals et de ses fournisseurs une indemnisation en cas de dommages directs uniquement à hauteur de 5, 00 $ US.Vous ne pouvez prétendre à aucune indemnisation pour les autres dommages, y compris les dommages spéciaux, indirects ou accessoires et pertes de bénéfices.\n\n Cette limitation concerne :\ntout ce qui est relié au logiciel, aux services ou au contenu(y compris le code) figurant sur des sites Internet tiers ou dans des programmes tiers; et\nles réclamations au titre de violation de contrat ou de garantie, ou au titre de responsabilité stricte, de négligence ou d'une autre faute dans la limite autorisée par la loi en vigueur.\n\nElle s'applique également, même si Sysinternals connaissait ou devrait connaître l'éventualité d'un tel dommage. Si votre pays n'autorise pas l'exclusion ou la limitation de responsabilité pour les dommages indirects, accessoires ou de quelque nature que ce soit, il se peut que la limitation ou l'exclusion ci - dessus ne s'appliquera pas à votre égard.\nEFFET JURIDIQUE.Le présent contrat décrit certains droits juridiques.Vous pourriez avoir d'autres droits prévus par les lois de votre pays. Le présent contrat ne modifie pas les droits que vous confèrent les lois de votre pays si celles-ci ne le permettent pas.\n\n"; BOOL IsEulaRegkeyAdded(const TCHAR * ToolName); static BOOL EulaCenter( HWND hwndChild, HWND hwndParent ) { RECT rcChild, rcParent; int cxChild, cyChild, cxParent, cyParent; int cxScreen, cyScreen, xNew, yNew; HDC hdc; // Get the Height and Width of the child window GetWindowRect(hwndChild, &rcChild); cxChild = rcChild.right - rcChild.left; cyChild = rcChild.bottom - rcChild.top; // Get the Height and Width of the parent window GetWindowRect(hwndParent, &rcParent); cxParent = rcParent.right - rcParent.left; cyParent = rcParent.bottom - rcParent.top; // Get the display limits hdc = GetDC(hwndChild); cxScreen = GetDeviceCaps(hdc, HORZRES); cyScreen = GetDeviceCaps(hdc, VERTRES); ReleaseDC(hwndChild, hdc); // Calculate new X position, then adjust for screen xNew = rcParent.left + ((cxParent - cxChild) / 2); if (xNew < 0) { xNew = 0; } else if ((xNew + cxChild) > cxScreen) { xNew = cxScreen - cxChild; } // Calculate new Y position, then adjust for screen yNew = rcParent.top + ((cyParent - cyChild) / 2); if (yNew < 0) { yNew = 0; } else if ((yNew + cyChild) > cyScreen) { yNew = cyScreen - cyChild; } // Set it, and return return SetWindowPos(hwndChild, NULL, xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER); } static BOOL PrintRichedit( HWND hRichedit ) { // Get the printer. PRINTDLG pd = { 0 }; pd.lStructSize = sizeof pd; pd.hwndOwner = hRichedit; pd.hInstance = GetModuleHandle(NULL); pd.Flags = PD_RETURNDC | PD_NOPAGENUMS | PD_NOSELECTION | PD_PRINTSETUP; if ( !PrintDlg( &pd ) ) return FALSE; { HCURSOR oldCursor = SetCursor( LoadCursor( NULL, IDC_WAIT ) ); int nHorzRes = GetDeviceCaps( pd.hDC, HORZRES ); int nVertRes = GetDeviceCaps( pd.hDC, VERTRES ); int nLogPixelsX = GetDeviceCaps( pd.hDC, LOGPIXELSX ); int nLogPixelsY = GetDeviceCaps( pd.hDC, LOGPIXELSY ); FORMATRANGE fr = { 0 }; DOCINFO di = { 0 }; int TotalLength; // Ensure the printer DC is in MM_TEXT mode. SetMapMode( pd.hDC, MM_TEXT ); // Rendering to the same DC we are measuring. fr.hdc = pd.hDC; fr.hdcTarget = pd.hDC; // Set up the page. fr.rcPage.top = 0; fr.rcPage.left = 0; fr.rcPage.bottom = (nVertRes/nLogPixelsY) * 1440; fr.rcPage.right = (nHorzRes/nLogPixelsX) * 1440; // Set up 1" margins all around. fr.rc = fr.rcPage; InflateRect( &fr.rc, -1440, -1440 ); // Default the range of text to print as the entire document. fr.chrg.cpMin = 0; fr.chrg.cpMax = -1; // Set up the print job (standard printing stuff here). di.cbSize = sizeof di; di.lpszDocName = _T("Sysinternals License"); // Start the document. StartDoc( pd.hDC, &di ); // Find out real size of document in characters. TotalLength = (int) SendMessage ( hRichedit, WM_GETTEXTLENGTH, 0, 0 ); for (;;) { int NextPage; // Start the page. StartPage( pd.hDC ); // Print as much text as can fit on a page. The return value is // the index of the first character on the next page. NextPage = (int) SendMessage( hRichedit, EM_FORMATRANGE, TRUE, (LPARAM)&fr ); // Print last page. EndPage( pd.hDC ); if ( NextPage >= TotalLength ) break; // Adjust the range of characters to start printing at the first character of the next page. fr.chrg.cpMin = NextPage; fr.chrg.cpMax = -1; } // Tell the control to release cached information. SendMessage( hRichedit, EM_FORMATRANGE, 0, (LPARAM)NULL ); EndDoc( pd.hDC ); SetCursor( oldCursor ); } return TRUE; } // combine all text strings into a single string char * GetEulaText() { char * text; DWORD len = 1; int i; for ( i = 0; EulaText[i]; ++i ) len += (DWORD) strlen( EulaText[i] ); text = (char *) malloc( len ); len = 0; for ( i = 0; EulaText[i]; ++i ) { strcpy( text+len, EulaText[i] ); len += (DWORD) strlen( EulaText[i] ); } text[len] = 0; return text; } DWORD CALLBACK StreamCallback( DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG * pcb ) { const char ** ptr = (const char **) dwCookie; LONG_PTR len = strlen(*ptr); if ( cb > len ) cb = (int) len; memcpy( pbBuff, *ptr, cb ); *pcb = cb; *ptr += cb; return 0; } static INT_PTR CALLBACK EulaProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) { switch ( uMsg ) { case WM_INITDIALOG: { TCHAR title[MAX_PATH]; char * text = GetEulaText(); char * textptr = text; EDITSTREAM stream = { 0, 0, StreamCallback }; stream.dwCookie = (DWORD_PTR) &textptr; _stprintf_s( title, MAX_PATH, _T("%s 许可协议"), (TCHAR *) lParam ); SetWindowText( hwndDlg, title ); // enter RTF into edit box SendMessage( GetDlgItem(hwndDlg,IDC_TEXT), EM_EXLIMITTEXT, 0, 1024*1024 ); SendMessage( GetDlgItem(hwndDlg,IDC_TEXT), EM_STREAMIN, SF_RTF, (LPARAM)&stream ); free( text ); } return TRUE; case WM_CTLCOLORSTATIC: // force background of read-only text window to be white if ( (HWND)lParam == GetDlgItem( hwndDlg, IDC_TEXT) ) { return (INT_PTR)GetSysColorBrush( COLOR_WINDOW ); } break; case WM_COMMAND: switch( LOWORD( wParam )) { case IDOK: EndDialog( hwndDlg, TRUE ); return TRUE; case IDCANCEL: EndDialog( hwndDlg, FALSE ); return TRUE; case IDC_PRINT: PrintRichedit( GetDlgItem(hwndDlg,IDC_TEXT) ); return TRUE; } break; } return FALSE; } static WORD * Align2( WORD * pos ) { return (WORD *)(((DWORD_PTR)pos + 1) & ~((DWORD_PTR) 1)); } static WORD * Align4( WORD * pos ) { return (WORD *)(((DWORD_PTR)pos + 3) & ~((DWORD_PTR) 3)); } static int CopyText( WORD * pos, const WCHAR * text ) { int len = (int) wcslen( text ) + 1; wcscpy( (PWCHAR) pos, text ); return len; } BOOL ShowEulaInternal( const TCHAR * ToolName, DWORD eulaAccepted ) { #if !defined(SYSMON_SHARED) HKEY hKey = NULL; TCHAR keyName[MAX_PATH]; _stprintf_s( keyName, MAX_PATH, _T("Software\\Sysinternals\\%s"), ToolName ); // // check the regkey value if no -accepteula switch append // if (!eulaAccepted) { eulaAccepted = IsEulaRegkeyAdded(ToolName); } #endif if( !eulaAccepted ) { if (IsIoTEdition()) { eulaAccepted = ShowEulaConsole(); // display Eula to console and prompt for Eula Accepted. { } } else if (IsRemoteOnlyEdition() || IsRunningRemotely()) // Nano and in remote session will not be able to accept eula from prompt { ShowEulaConsoleNoPrompt(); } else { DLGTEMPLATE * dlg = (DLGTEMPLATE *)LocalAlloc(LPTR, 1000); WORD * extra = (WORD *)(dlg + 1); DLGITEMTEMPLATE * item; #if defined(SYSMON_SHARED) printf( "正在打开 EULA 许可协议对话框 ... (使用 -accepteula 可跳过)。\n" ); #endif LoadLibrarySafe(_T("Riched32.dll"), DLL_LOAD_LOCATION_SYSTEM ); // Richedit 1.0 library // header dlg->style = DS_MODALFRAME | DS_CENTER | DS_SETFONT | WS_POPUP | WS_CAPTION | WS_SYSMENU | DS_NOFAILCREATE; dlg->x = 0; dlg->y = 0; dlg->cx = 312; dlg->cy = 180; dlg->cdit = 0; // number of controls *extra++ = 0; // menu *extra++ = 0; // class extra += CopyText(extra, L"许可协议"); *extra++ = 8; // font size extra += CopyText(extra, L"MS Shell Dlg"); // Command-line message item = (DLGITEMTEMPLATE *)Align4(extra); item->x = 7; item->y = 3; item->cx = 298; item->cy = 14; item->id = IDC_TEXT1; item->style = WS_CHILD | WS_VISIBLE; extra = (WORD *)(item + 1); *extra++ = 0xFFFF; // class is ordinal *extra++ = 0x0082; // class is static extra += CopyText(extra, L"您也可使用 /accepteula 命令行开关来同意 EULA。"); *extra++ = 0; // creation data dlg->cdit++; // Agree button item = (DLGITEMTEMPLATE *)Align4(extra); item->x = 201; item->y = 159; item->cx = 50; item->cy = 14; item->id = IDOK; item->style = BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP; // | WS_DEFAULT; extra = (WORD *)(item + 1); *extra++ = 0xFFFF; // class is ordinal *extra++ = 0x0080; // class is button extra += CopyText(extra, L"同意(&A)"); *extra++ = 0; // creation data dlg->cdit++; // Decline button item = (DLGITEMTEMPLATE *)Align4(extra); item->x = 255; item->y = 159; item->cx = 50; item->cy = 14; item->id = IDCANCEL; item->style = BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP; extra = (WORD *)(item + 1); *extra++ = 0xFFFF; // class is ordinal *extra++ = 0x0080; // class is button extra += CopyText(extra, L"拒绝(&D)"); *extra++ = 0; // creation data dlg->cdit++; // Print button item = (DLGITEMTEMPLATE *)Align4(extra); item->x = 7; item->y = 159; item->cx = 50; item->cy = 14; item->id = IDC_PRINT; item->style = BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP; extra = (WORD *)(item + 1); *extra++ = 0xFFFF; // class is ordinal *extra++ = 0x0080; // class is button extra += CopyText(extra, L"打印(&P)"); *extra++ = 0; // creation data dlg->cdit++; // Edit box item = (DLGITEMTEMPLATE *)Align4(extra); item->x = 7; item->y = 14; item->cx = 298; item->cy = 140; item->id = IDC_TEXT; item->style = WS_BORDER | ES_MULTILINE | ES_AUTOVSCROLL | ES_WANTRETURN | WS_VSCROLL | ES_READONLY | WS_CHILD | WS_VISIBLE | WS_TABSTOP; extra = (WORD *)(item + 1); extra += CopyText(extra, L"RICHEDIT"); extra += CopyText(extra, L"拒绝(&D)"); *extra++ = 0; // creation data dlg->cdit++; eulaAccepted = (DWORD)DialogBoxIndirectParam(NULL, dlg, NULL, EulaProc, (LPARAM)ToolName); LocalFree(dlg); } } #if !defined(SYSMON_SHARED) if ( eulaAccepted ) { if (RegCreateKey(HKEY_CURRENT_USER, keyName, &hKey) == ERROR_SUCCESS) { RegSetValueEx(hKey, _T("EulaAccepted"), 0, REG_DWORD, (BYTE *)&eulaAccepted, sizeof(eulaAccepted)); RegCloseKey(hKey); } } #endif return eulaAccepted != 0; } BOOL ShowEulaW( const TCHAR * ToolName, int *argc, PWCHAR argv[] ) { DWORD eulaAccepted = 0; int i; if ( argc == NULL || argv == NULL ) { typedef LPWSTR * (WINAPI * type_CommandLineToArgvW)( LPCWSTR lpCmdLine, int *pNumArgs ); type_CommandLineToArgvW pCommandLineToArgvW = (type_CommandLineToArgvW) GetProcAddress( LoadLibrarySafe(_T("Shell32.dll"), DLL_LOAD_LOCATION_SYSTEM), "CommandLineToArgvW" ); if ( pCommandLineToArgvW ) { static int argc2; argc = &argc2; argv = (*pCommandLineToArgvW)( GetCommandLineW(), argc ); } else { argc = NULL; } } // // See if its accepted via command line switch // if( argc ) { for( i = 0; i < *argc; i++ ) { eulaAccepted = (!_wcsicmp( argv[i], L"/accepteula") || !_wcsicmp( argv[i], L"-accepteula")); if( eulaAccepted ) { for( ; i < *argc - 1; i++ ) { argv[i] = argv[i+1]; } (*argc)--; break; } } } if( ShowEulaInternal( ToolName, eulaAccepted )) { eulaAccepted = 1; } return eulaAccepted != 0; } BOOL ShowEula( const TCHAR * ToolName, int *argc, PTCHAR argv[] ) { DWORD eulaAccepted = 0; int i; if ( argc == NULL || argv == NULL ) { return ShowEulaW( ToolName, NULL, NULL ); } // // See if its accepted via command line switch // if( argc ) { for( i = 0; i < *argc; i++ ) { eulaAccepted = (!_tcsicmp( argv[i], _T("/accepteula")) || !_tcsicmp( argv[i], _T("-accepteula"))); if( eulaAccepted ) { for( ; i < *argc - 1; i++ ) { argv[i] = argv[i+1]; } (*argc)--; break; } } } if( ShowEulaInternal( ToolName, eulaAccepted )) { eulaAccepted = 1; } return eulaAccepted != 0; } // Determine whether we are on the IoT SKU by looking at the ProductName. BOOL IsIoTEdition() { HKEY hKey = NULL; wchar_t ProductName[MAX_PATH]; BOOL bRet = FALSE; // assume "not" IoT Edition DWORD dwSize = sizeof(ProductName); DWORD type = 0; if (ERROR_SUCCESS == RegOpenKey(HKEY_LOCAL_MACHINE, _T("Software\\Microsoft\\windows nt\\currentversion"), &hKey)) { if (ERROR_SUCCESS == RegQueryValueExW(hKey, L"ProductName", 0, &type, (LPBYTE)ProductName, &dwSize)) { if (!_wcsicmp(L"iotuap", ProductName)) bRet = TRUE; } RegCloseKey(hKey); } return bRet; } // Determine whether we are on the remote only edition, where we cannot prompt for user input. BOOL IsRemoteOnlyEdition() { HKEY hKey = NULL; DWORD dwNanoServer = 0; BOOL bRet = FALSE; DWORD dwSize = sizeof(dwNanoServer); DWORD type = 0; // Currently Nano is the only remote only edtion. if (ERROR_SUCCESS == RegOpenKey(HKEY_LOCAL_MACHINE, _T("Software\\Microsoft\\Windows NT\\CurrentVersion\\Server\\ServerLevels"), &hKey)) { if (ERROR_SUCCESS == RegQueryValueEx(hKey, _T("NanoServer"), 0, &type, (LPBYTE)&dwNanoServer, &dwSize)) { if (type == REG_DWORD && dwNanoServer == 1) bRet = TRUE; } RegCloseKey(hKey); } return bRet; } BOOL IsRunningRemotely() { // running from a remote session will not support input interaction DWORD fileType = GetFileType(GetStdHandle(STD_OUTPUT_HANDLE)); return fileType == FILE_TYPE_PIPE; } DWORD ShowEulaConsole() { DWORD dwRet = 0; char ch; BOOLEAN eulaAcknowledged = FALSE; wprintf(Raw_EulaText); while( eulaAcknowledged != TRUE ) { printf("是否接受 EULA (Y/N)?"); ch = (char) _getch(); printf("%c\n", ch); if ('y' == ch || 'Y' == ch) { dwRet = 1; // EULA Accepted. eulaAcknowledged = TRUE; } if ('n' == ch || 'N' == ch) { // EULA not accepted. eulaAcknowledged = TRUE; } } return dwRet; } void ShowEulaConsoleNoPrompt() { wprintf_s(L"%ls", Raw_EulaText); wprintf_s(L"这是您首次运行此程序,需要同意 EULA 才能继续。\n"); wprintf_s(L"可使用 -accepteula 来同意 EULA。\n\n"); // exit here to avoid printing the misleading "Eula declined". exit(1); } BOOL IsEulaAcceptedValueExist(HKEY hKeyRoot, LPCTSTR lpSubKey) { HKEY hKey = NULL; DWORD length; DWORD eulaAccepted = 0; DWORD ret; // // check if it is set by external channel for all tools // assuming external channel do not set to WOW6432Node // if (RegOpenKeyEx(hKeyRoot, lpSubKey, 0, KEY_QUERY_VALUE | KEY_WOW64_64KEY, &hKey) == ERROR_SUCCESS) { length = sizeof(eulaAccepted); ret = RegQueryValueEx(hKey, _T("EulaAccepted"), NULL, NULL, (LPBYTE)&eulaAccepted, &length); RegCloseKey(hKey); if (ret == ERROR_SUCCESS && eulaAccepted) { return TRUE; } } return FALSE; } BOOL IsEulaRegkeyAdded(const TCHAR * ToolName) { TCHAR perToolRegKey[MAX_PATH]; PTCHAR suiteRegKey = _T("Software\\Sysinternals"); _stprintf_s(perToolRegKey, MAX_PATH, _T("%s\\%s"), suiteRegKey, ToolName); // // check if it is set by external channel for all tools // assuming external channel do not set to WOW6432Node // if (IsEulaAcceptedValueExist(HKEY_LOCAL_MACHINE, suiteRegKey) || IsEulaAcceptedValueExist(HKEY_CURRENT_USER, suiteRegKey)) { return TRUE; } // // per tool check // if (IsEulaAcceptedValueExist(HKEY_CURRENT_USER, perToolRegKey)) { return TRUE; } return FALSE; } BOOL IsEulaSwitchAppended(int *argc, PTCHAR argv[]) { DWORD eulaAccepted = 0; int i; // // See if its accepted via command line switch // if (*argc > 1) { for (i = 1; i < *argc; i++) { eulaAccepted = (!_tcsicmp(argv[i], _T("/accepteula")) || !_tcsicmp(argv[i], _T("-accepteula"))); if (eulaAccepted) { break; } } } return eulaAccepted; } // // Determine if Eula is accepted, either already have regkey added // or have -accepteula switch appended // BOOL IsEulaAccepted(const TCHAR * ToolName, int *argc, PTCHAR argv[]) { return IsEulaRegkeyAdded(ToolName) || IsEulaSwitchAppended(argc, argv); } ================================================ FILE: PowerToys/src/gpo/assets/zh-CN/PowerToys.adml ================================================ PowerToys PowerToys Microsoft PowerToys 安装与更新 快捷启动器 高级粘贴 无界鼠标 常规设置 新建+ 已弃用 PowerToys 版本 0.64.0 或更高版本 PowerToys 版本 0.68.0 或更高版本 PowerToys 版本 0.69.0 或更高版本 PowerToys 版本 0.70.0 或更高版本 PowerToys 版本 0.73.0 或更高版本 PowerToys 版本 0.75.0 或更高版本 PowerToys 版本 0.76.0 或更高版本 PowerToys 版本 0.77.0 或更高版本 PowerToys 版本 0.78.0 或更高版本 PowerToys 版本 0.81.0 或更高版本 PowerToys 版本 0.81.1 或更高版本 PowerToys 版本 0.83.0 或更高版本 PowerToys 版本 0.84.0 或更高版本 PowerToys 版本 0.85.0 或更高版本 PowerToys 版本 0.86.0 或更高版本 PowerToys 版本 0.88.0 或更高版本 PowerToys 版本 0.64.0 至 0.87.1 此策略控制所有 PowerToys 功能的启用状态。 如果策略设置为“已启用”,所有功能将始终启用,用户不可以禁用任何功能。 如果策略设置为“已禁用”,所有功能将始终禁用,用户不可以启用任何功能。 如果策略设置为“未配置”,用户可以自由地启用和禁用功能。 特定某个功能的启用状态策略将会覆盖此策略。 此策略控制一个 PowerToys 功能的启用状态。 如果策略设置为“已启用”,该功能将始终启用,用户不可以禁用这个功能。 如果策略设置为“已禁用”,该功能将始终禁用,用户不可以启用这个功能。 如果策略设置为“未配置”,用户可以自由地启用和禁用这个功能。 该策略比“配置所有功能的启用状态”策略优先级更高,将会覆盖其设置。 (注意:有用户反馈 PDF 预览与 Outlook 不兼容) 此策略控制一个 PowerToys 功能的启用状态。 如果策略设置为“已启用”,该功能将始终启用,用户不可以禁用这个功能。 如果策略设置为“已禁用”,该功能将始终禁用,用户不可以启用这个功能。 如果策略设置为“未配置”,用户可以自由地启用和禁用这个功能。 该策略比“配置所有功能的启用状态”策略优先级更高,将会覆盖其设置。 此策略控制是否允许单用户安装。 如果策略设置为“已启用”,禁用单用户安装。 如果策略设置为“已禁用”或“未配置”,用户可以选择仅为单用户安装 PowerToys。 此策略控制是否禁用自动下载和安装更新。(不过,在按流量计费的网络上,永远不会自动下载更新。) 如果策略设置为“已启用”,禁用自动下载和安装更新。 如果策略设置为“已禁用”或“未配置”,用户可以在设置中自行选择。 此策略控制是否在两次小版本更新内,不显示更新通知。(比如:如果安装的版本为 v0.60.0,则下一次显示更新提示将会是版本 v0.63.*) 如果策略设置为“已启用”,减少更新通知。 如果策略设置为“已禁用”或“未配置”,正常显示所有更新通知。 注意:大版本更新通知始终会显示。 如果启用“禁用新版本更新消息推送”策略或者在软件设置中关闭了自动更新,则该策略无效。 此策略控制是否禁用更新通知。 如果策略设置为“已启用”,不显示更新通知。 如果策略设置为“已禁用”或“未配置”,用户可以选择是否显示更新通知。 此策略控制更新完成后是否显示更新日志窗口。 如果策略设置为“已启用”,更新完成后不会自动打开更新日志。 如果策略设置为“已禁用”或“未配置”,用户可以在设置中自行选择。 此策略控制是否允许进行 PowerToys 功能测试。如果允许测试,当用户被选中为测试组时,可以用到实验性的新功能。(功能测试只会在 Windows Insider 版本上进行。) 如果策略设置为“已启用”,用户可以在 PowerToys 设置里选择是否参与功能测试。 如果策略设置为“已禁用”或“未配置”,不允许参与功能测试。 此策略控制是否允许发送 PowerToys 诊断数据。发送诊断数据可以帮助修复错误和优化性能。 如果策略设置为“已启用”或“未配置”,用户可以在 PowerToys 设置里选择是否发送诊断数据。 如果策略设置为“已禁用”或“未配置”,不允许发送诊断数据。 此策略控制所有快捷启动器插件的启用状态。所有插件将会拥有相同的启用状态。 如果策略设置为“已启用”,所有插件将始终启用,用户不可以禁用任何插件。 如果策略设置为“已禁用”,所有插件将始终禁用,用户不可以启用任何插件。 如果策略设置为“未配置”,用户可以自由地启用和禁用插件。 可以通过“配置特定插件的启用状态”策略来为特定插件配置启用状态覆盖此设置。特定某个功能的启用状态策略将会覆盖此策略。 注意:更改需要重新启动快捷启动器才能应用。 此策略控制列表中每个快捷启动器插件的启用状态。 如果策略设置为“已启用”,你可以在列表中定义某个插件和其启用状态: - 值名称(第一列)填入插件 ID,可以在插件文件夹内 plugin.json 文件中找到对应 ID。 - 值(第二列)填入数字,0 代表禁用,1 代表启用,2 代表由用户控制。 - 这是禁用“应用程序”插件的例子: 791FC278BA414111B8D1886DFE447410 | 0 如果策略设置为“已禁用”或“未配置”,用户或“配置所有插件的启用状态”可以控制插件的启用状态。 启用该策略后,你可以通过“配置所有插件的启用状态”策略,来控制列表外的其他插件的启用状态。 注意:更改需要重新启动快捷启动器才能应用。 此策略可以禁用高级粘贴的在线 AI 模型。 如果策略设置为“已启用”或“未配置”,用户可以设置是否启用 AI 粘贴功能。 如果策略设置为“已禁用”,用户不可以启用 AI 粘贴功能,不可以在粘贴面板中输入 AI 提示词,也不能配置 OpenAI 密钥。 此策略控制是否允许在多台设备之间同步剪贴板。 如果策略设置为“已启用”或“未配置”,用户可以自由地启用共享剪贴板功能。 如果策略设置为“已禁用”,该功能将始终禁用,用户不可以启用这个功能。 此策略控制是否允许在多台设备间传输剪贴板文件。 如果策略设置为“已启用”或“未配置”,用户可以自由地启用共享文件功能。 如果策略设置为“已禁用”,该功能将始终禁用,用户不可以启用这个功能。 注意:共享文件需要开启共享剪贴板。如果禁用共享剪贴板,共享文件也将自动禁用。 此策略控制是否允许使用旧版无界鼠标界面。 如果策略设置为“已启用”或“未配置”,用户可以自由地启用旧版界面。 如果策略设置为“已禁用”,用户不可以启用旧版界面。 此策略控制是否允许关闭被控电脑上的屏幕保护程序。 如果策略设置为“已启用”,用户无法启用阻止屏保功能,被控电脑的屏幕保护程序不会受到影响。 如果策略设置为“已禁用”或“未配置”,用户可以自由地启用阻止屏保。 此策略控制是否仅允许同一内网的连接。 如果策略设置为“已启用”,该功能将始终启用,只允许连接到同一内网的电脑。 如果策略设置为“已禁用”,该功能将始终禁用,允许所有连接。 如果策略设置为“未配置”,用户可以自由地启用和禁用这个功能。 此策略控制是否通过反向 DNS 查找来验证被控设备 IP 地址。 如果策略设置为“已启用”,该功能将始终启用,验证 IP 地址。 如果策略设置为“已禁用”,该功能将始终禁用,不验证 IP 地址。 如果策略设置为“未配置”,用户可以自由地启用和禁用这个功能。 此策略控制是否允许用户设定 IP 地址映射。 如果策略设置为“已启用”,该功能将始终禁用,用户无法设定和使用 IP 地址映射规则。 如果策略设置为“已禁用”或“未配置”,用户可以自由地启用和禁用这个功能。 注意:启用该策略并不会影响组策略的预设 IP 地址映射生效。 此策略可以设定 IP 地址映射规则。 如果策略设置为“已启用”,可以设定强制的 IP 地址映射规则,用户无法改动或禁用。 请按此格式输入,每行一个:"hostname IP" 如果策略设置为“已禁用”或“未配置”,不使用预设规则。 此策略控制是否显示模板文件的扩展名。 如果策略设置为“已启用”,该功能将始终启用,隐藏模板文件扩展名。 如果策略设置为“已禁用”,该功能将始终禁用,显示模板文件扩展名。 如果策略设置为“未配置”,用户可以自由地启用和禁用这个功能。 配置所有功能的启用状态 高级粘贴: 启用该功能 窗口置顶: 启用该功能 阻止睡眠: 启用该功能 取色器: 启用该功能 未找到命令: 启用该功能 窗口镜像: 启用该功能 环境变量: 启用该功能 窗口布局: 启用该功能 文件开锁匠: 启用该功能 预览增强 - SVG 文件预览: 启用该功能 预览增强 - Markdown 文件预览: 启用该功能 预览增强 - 代码文件预览: 启用该功能 预览增强 - PDF 文件预览: 启用该功能 预览增强 - Gcode 文件预览: 启用该功能 预览增强 - SVG 文件缩略图: 启用该功能 预览增强 - PDF 文件缩略图: 启用该功能 预览增强 - Gcode 文件缩略图: 启用该功能 预览增强 - STL 文件缩略图: 启用该功能 域名表: 启用该功能 图像裁剪器: 启用该功能 键盘修改器: 启用该功能 鼠标工具箱 - 鼠标定位: 启用该功能 鼠标工具箱 - 鼠标高亮: 启用该功能 鼠标工具箱 - 鼠标跳转: 启用该功能 鼠标工具箱 - 鼠标准心: 启用该功能 无界鼠标: 启用该功能 新建+: 启用该功能 快速查看: 启用该功能 批量重命名: 启用该功能 快捷启动器: 启用该功能 工作区: 启用该功能 字母助手: 启用该功能 注册表预览: 启用该功能 屏幕标尺: 启用该功能 快捷键指南: 启用该功能 文本提取: 启用该功能 直播工具: 启用该功能 演示工具:启用该功能 禁用单用户安装 禁用自动下载更新 禁用更新日志弹窗 减少新版本更新消息推送 禁用新版本更新消息推送 允许功能测试 配置所有插件的启用状态 配置特定插件的启用状态 预览增强 - QOI 文件预览: 启用该功能 预览增强 - QOI 文件缩略图: 启用该功能 允许使用在线 AI 模型 启用共享剪贴板 启用文件传输 启用原版界面 禁用阻止屏保 仅允许内网控制 验证远程设备 IP 地址 禁用自定义 IP 地址映射 预设 IP 地址映射规则 隐藏模板文件扩展名 允许发送诊断数据 插件管理列表: IP 地址映射列表: ================================================ FILE: PowerToys/src/modules/AdvancedPaste/AdvancedPaste/Strings/en-us/Resources.resw ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 注意 AI 也可能会犯错 剪贴板为空 未启用 AI 粘贴功能 API 密钥或接入点无效 API 密钥额度已用尽 OpenAI 请求失败,返回状态码: 粘贴时出现问题 内容包含敏感信息,无法处理。请试试其他内容。 剪贴板历史记录 剪贴板历史记录 图片数据 更多选项 删除 告诉我你想要什么格式.. 告诉我你想要什么格式.. 隐私政策 正在生成结果.. 粘贴为 JSON 粘贴为 Markdown 粘贴为纯文本 图像转文本 粘贴为 .txt 文件 粘贴为 .png 文件 粘贴为 .html 文件 粘贴 粘贴 结果 回到上一次的提示词与剪贴板数据 回到上一次的提示词与剪贴板数据 生成并粘贴数据 重新生成 重新生成 生成并粘贴数据 打开设置 打开设置 AI 助手给出了如下消息: 差评 好评 高级粘贴 上一条 上一条 下一条 下一条 OpenAI 隐私政策 OpenAI 使用条款 组策略已禁止 AI 功能 Ctrl PowerToys_Paste_ ================================================ FILE: PowerToys/src/modules/CropAndLock/CropAndLock/main.cpp ================================================ #include "pch.h" #include "SettingsWindow.h" #include "OverlayWindow.h" #include "CropAndLockWindow.h" #include "ThumbnailCropAndLockWindow.h" #include "ReparentCropAndLockWindow.h" #include "ModuleConstants.h" #include "trace.h" #include #include #include #include #include #include #include #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") namespace winrt { using namespace Windows::Foundation; using namespace Windows::Foundation::Numerics; using namespace Windows::UI; using namespace Windows::UI::Composition; } namespace util { using namespace robmikh::common::desktop; } const std::wstring instanceMutexName = L"Local\\PowerToys_CropAndLock_InstanceMutex"; bool m_running = true; int WINAPI wWinMain(_In_ HINSTANCE, _In_opt_ HINSTANCE, _In_ PWSTR lpCmdLine, _In_ int) { // Initialize COM winrt::init_apartment(winrt::apartment_type::single_threaded); Trace::CropAndLock::RegisterProvider(); Shared::Trace::ETWTrace trace; trace.UpdateState(true); // Initialize logger automatic logging of exceptions. LoggerHelpers::init_logger(NonLocalizable::ModuleKey, L"", LogSettings::cropAndLockLoggerName); InitUnhandledExceptionHandler(); if (powertoys_gpo::getConfiguredCropAndLockEnabledValue() == powertoys_gpo::gpo_rule_configured_disabled) { Logger::warn(L"Tried to start with a GPO policy setting the utility to always be disabled. Please contact your systems administrator."); return 0; } // Before we do anything, check to see if we're already running. If we are, // the hotkey won't register and we'll fail. Instead, we should tell the user // to kill the other instance and exit this one. auto mutex = CreateMutex(nullptr, true, instanceMutexName.c_str()); if (mutex == nullptr) { Logger::error(L"Failed to create mutex. {}", get_last_error_or_default(GetLastError())); } if (GetLastError() == ERROR_ALREADY_EXISTS) { // CropAndLock is already open. return 1; } std::wstring pid = std::wstring(lpCmdLine); if (pid.empty()) { Logger::warn(L"Tried to run Crop And Lock as a standalone."); MessageBoxW(nullptr, L"窗口镜像功能不能独立使用,请从 PowerToys 启动。", L"CropAndLock", MB_ICONERROR); return 1; } auto mainThreadId = GetCurrentThreadId(); ProcessWaiter::OnProcessTerminate(pid, [mainThreadId](int err) { if (err != ERROR_SUCCESS) { Logger::error(L"Failed to wait for parent process exit. {}", get_last_error_or_default(err)); } else { Logger::trace(L"PowerToys runner exited."); } Logger::trace(L"Exiting CropAndLock"); PostThreadMessage(mainThreadId, WM_QUIT, 0, 0); }); // NOTE: reparenting a window with a different DPI context has consequences. // See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setparent#remarks // for more info. winrt::check_bool(SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)); // Create the DispatcherQueue that the compositor needs to run auto controller = util::CreateDispatcherQueueControllerForCurrentThread(); // Setup Composition auto compositor = winrt::Compositor(); // Create our overlay window std::unique_ptr overlayWindow; // Keep a list of our cropped windows std::vector> croppedWindows; // Handles and thread for the events sent from runner HANDLE m_reparent_event_handle; HANDLE m_thumbnail_event_handle; HANDLE m_exit_event_handle; std::thread m_event_triggers_thread; std::function removeWindowCallback = [&](HWND windowHandle) { if (!m_running) { // If we're not running, the reference to croppedWindows might no longer be valid and cause a crash at exit time, due to being called by destructors after wWinMain returns. return; } auto pos = std::find_if(croppedWindows.begin(), croppedWindows.end(), [windowHandle](auto window) { return window->Handle() == windowHandle; }); if (pos != croppedWindows.end()) { croppedWindows.erase(pos); } }; std::function ProcessCommand = [&](CropAndLockType mode) { std::function windowCroppedCallback = [&, mode](HWND targetWindow, RECT cropRect) { auto targetInfo = util::WindowInfo(targetWindow); // TODO: Fix WindowInfo.h to not contain the null char at the end. auto nullCharIndex = std::wstring::npos; do { nullCharIndex = targetInfo.Title.rfind(L'\0'); if (nullCharIndex != std::wstring::npos) { targetInfo.Title.erase(nullCharIndex); } } while (nullCharIndex != std::wstring::npos); std::wstringstream titleStream; titleStream << targetInfo.Title << L" (镜像)"; auto title = titleStream.str(); std::shared_ptr croppedWindow; switch (mode) { case CropAndLockType::Reparent: croppedWindow = std::make_shared(title, 800, 600); Logger::trace(L"Creating a reparent window"); Trace::CropAndLock::CreateReparentWindow(); break; case CropAndLockType::Thumbnail: croppedWindow = std::make_shared(title, 800, 600); Logger::trace(L"Creating a thumbnail window"); Trace::CropAndLock::CreateThumbnailWindow(); break; default: return; } croppedWindow->CropAndLock(targetWindow, cropRect); croppedWindow->OnClosed(removeWindowCallback); croppedWindows.push_back(croppedWindow); }; overlayWindow.reset(); // Get the current window with focus auto foregroundWindow = GetForegroundWindow(); if (foregroundWindow != nullptr) { bool match = false; for (auto&& croppedWindow : croppedWindows) { if (foregroundWindow == croppedWindow->Handle()) { match = true; break; } } if (!match) { overlayWindow = std::make_unique(compositor, foregroundWindow, windowCroppedCallback); } } }; // Start a thread to listen on the events. m_reparent_event_handle = CreateEventW(nullptr, false, false, CommonSharedConstants::CROP_AND_LOCK_REPARENT_EVENT); m_thumbnail_event_handle = CreateEventW(nullptr, false, false, CommonSharedConstants::CROP_AND_LOCK_THUMBNAIL_EVENT); m_exit_event_handle = CreateEventW(nullptr, false, false, CommonSharedConstants::CROP_AND_LOCK_EXIT_EVENT); if (!m_reparent_event_handle || !m_thumbnail_event_handle || !m_exit_event_handle) { Logger::warn(L"Failed to create events. {}", get_last_error_or_default(GetLastError())); return 1; } m_event_triggers_thread = std::thread([&]() { MSG msg; HANDLE event_handles[3] = { m_reparent_event_handle, m_thumbnail_event_handle, m_exit_event_handle }; while (m_running) { DWORD dwEvt = MsgWaitForMultipleObjects(3, event_handles, false, INFINITE, QS_ALLINPUT); if (!m_running) { break; } switch (dwEvt) { case WAIT_OBJECT_0: { // Reparent Event bool enqueueSucceeded = controller.DispatcherQueue().TryEnqueue([&]() { ProcessCommand(CropAndLockType::Reparent); }); if (!enqueueSucceeded) { Logger::error("Couldn't enqueue message to reparent a window."); } break; } case WAIT_OBJECT_0 + 1: { // Thumbnail Event bool enqueueSucceeded = controller.DispatcherQueue().TryEnqueue([&]() { ProcessCommand(CropAndLockType::Thumbnail); }); if (!enqueueSucceeded) { Logger::error("Couldn't enqueue message to thumbnail a window."); } break; } case WAIT_OBJECT_0 + 2: { // Exit Event Logger::trace(L"Received an exit event."); PostThreadMessage(mainThreadId, WM_QUIT, 0, 0); break; } case WAIT_OBJECT_0 + 3: if (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessageW(&msg); } break; default: break; } } }); // Message pump MSG msg = {}; while (GetMessageW(&msg, nullptr, 0, 0)) { TranslateMessage(&msg); DispatchMessageW(&msg); } trace.Flush(); Trace::CropAndLock::UnregisterProvider(); m_running = false; // Needed to unblock MsgWaitForMultipleObjects one last time SetEvent(m_reparent_event_handle); CloseHandle(m_reparent_event_handle); CloseHandle(m_thumbnail_event_handle); CloseHandle(m_exit_event_handle); m_event_triggers_thread.join(); return util::ShutdownDispatcherQueueControllerAndWait(controller, static_cast(msg.wParam)); } ================================================ FILE: PowerToys/src/modules/EnvironmentVariables/EnvironmentVariablesUILib/Strings/en-us/Resources.resw ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 默认变量 默认变量 新建模板 通过模板来快速设定一组环境变量预设,模板变量优先于用户和系统变量。 模板 系统 用户 环境变量编辑器 Title of the window when running as user 取消 编辑变量 名称 保存 保存 新建模板 立即应用 添加变量 名称 Name as in Environment variable name Value as in Environment variable value 已有变量 新建变量 管理员权限: 环境变量编辑器 Title of the window when running as administrator 取消 添加 当前所有环境变量,包括模板、用户和系统所设置的变量。 当前变量 变量 删除 确定删除此模板?如果该模板已启用,它添加的变量也将被删除。 编辑系统环境变量需要管理员权限 变量发生改动 由模板添加的变量已被修改,在重新启用模板前,请检查改动情况 取消 变量已被其他程序修改,刷新获取最新值 添加变量 确定删除这个变量吗? 编辑模板 添加变量 修改用户和系统的环境变量。 编辑 更多选项 下移 上移 在前面插入 在后面插入 应用到系统? 删除 删除 添加变量 无法应用模板。 变量或备份变量无效。 该变量由已启用的模板写入 ================================================ FILE: PowerToys/src/modules/FileLocksmith/FileLocksmithContextMenu/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 查看文件占用 This text will be shown when the user opens the context menu (right clicks) a file. File Locksmith is the product name, do not loc. 文件开锁匠 ================================================ FILE: PowerToys/src/modules/FileLocksmith/FileLocksmithContextMenu/dllmain.cpp ================================================ // dllmain.cpp : Defines the entry point for the DLL application. #include "pch.h" #include #include #include #include #include "FileLocksmithLib/IPC.h" #include "FileLocksmithLib/Settings.h" #include "FileLocksmithLib/Trace.h" #include #include #include #include #include "Generated Files/resource.h" using namespace Microsoft::WRL; HINSTANCE g_hInst = 0; Shared::Trace::ETWTrace trace(L"FileLocksmithContextMenu"); BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: g_hInst = hModule; Trace::RegisterProvider(); break; case DLL_PROCESS_DETACH: Trace::UnregisterProvider(); break; } return TRUE; } class __declspec(uuid("AAF1E27D-4976-49C2-8895-AAFA743C0A7E")) FileLocksmithContextMenuCommand final : public RuntimeClass, IExplorerCommand, IObjectWithSite> { public: virtual const wchar_t* Title() { return L"File Locksmith"; } virtual const EXPCMDFLAGS Flags() { return ECF_DEFAULT; } virtual const EXPCMDSTATE State(_In_opt_ IShellItemArray* selection) { return ECS_ENABLED; } // IExplorerCommand IFACEMETHODIMP GetTitle(_In_opt_ IShellItemArray* items, _Outptr_result_nullonfailure_ PWSTR* name) { return SHStrDup(context_menu_caption.c_str(), name); } IFACEMETHODIMP GetIcon(_In_opt_ IShellItemArray*, _Outptr_result_nullonfailure_ PWSTR* icon) { std::wstring iconResourcePath = get_module_folderpath(g_hInst); iconResourcePath += L"\\Assets\\FileLocksmith\\"; iconResourcePath += L"FileLocksmith.ico"; return SHStrDup(iconResourcePath.c_str(), icon); } IFACEMETHODIMP GetToolTip(_In_opt_ IShellItemArray*, _Outptr_result_nullonfailure_ PWSTR* infoTip) { *infoTip = nullptr; return E_NOTIMPL; } IFACEMETHODIMP GetCanonicalName(_Out_ GUID* guidCommandName) { *guidCommandName = __uuidof(this); return S_OK; } IFACEMETHODIMP GetState(_In_opt_ IShellItemArray* selection, _In_ BOOL okToBeSlow, _Out_ EXPCMDSTATE* cmdState) { *cmdState = ECS_ENABLED; if (!FileLocksmithSettingsInstance().GetEnabled()) { *cmdState = ECS_HIDDEN; } if (FileLocksmithSettingsInstance().GetShowInExtendedContextMenu()) { *cmdState = ECS_HIDDEN; } return S_OK; } IFACEMETHODIMP Invoke(_In_opt_ IShellItemArray* selection, _In_opt_ IBindCtx*) noexcept { trace.UpdateState(true); Trace::Invoked(); ipc::Writer writer; if (selection == nullptr) { return S_OK; } if (HRESULT result = writer.start(); FAILED(result)) { Trace::InvokedRet(result); trace.Flush(); trace.UpdateState(false); return result; } std::wstring path = get_module_folderpath(g_hInst); path = path + L"\\PowerToys.FileLocksmithUI.exe"; HRESULT result; if (!RunNonElevatedEx(path.c_str(), L"", get_module_folderpath(g_hInst))) { result = E_FAIL; Trace::InvokedRet(result); trace.Flush(); trace.UpdateState(false); return result; } DWORD num_items; selection->GetCount(&num_items); for (DWORD i = 0; i < num_items; i++) { IShellItem* item; result = selection->GetItemAt(i, &item); if (SUCCEEDED(result)) { LPWSTR file_path; result = item->GetDisplayName(SIGDN_FILESYSPATH, &file_path); if (SUCCEEDED(result)) { // TODO Aggregate items and send to UI writer.add_path(file_path); CoTaskMemFree(file_path); } item->Release(); } } Trace::InvokedRet(S_OK); trace.Flush(); trace.UpdateState(false); return S_OK; } IFACEMETHODIMP GetFlags(_Out_ EXPCMDFLAGS* flags) { *flags = Flags(); return S_OK; } IFACEMETHODIMP EnumSubCommands(_COM_Outptr_ IEnumExplorerCommand** enumCommands) { *enumCommands = nullptr; return E_NOTIMPL; } // IObjectWithSite IFACEMETHODIMP SetSite(_In_ IUnknown* site) noexcept { m_site = site; return S_OK; } IFACEMETHODIMP GetSite(_In_ REFIID riid, _COM_Outptr_ void** site) noexcept { return m_site.CopyTo(riid, site); } protected: ComPtr m_site; private: std::wstring context_menu_caption = GET_RESOURCE_STRING_FALLBACK(IDS_FILE_LOCKSMITH_CONTEXT_MENU_ENTRY, L"鿴ļռ"); // ANSI PLZ }; CoCreatableClass(FileLocksmithContextMenuCommand) CoCreatableClassWrlCreatorMapInclude(FileLocksmithContextMenuCommand) STDAPI DllGetActivationFactory(_In_ HSTRING activatableClassId, _COM_Outptr_ IActivationFactory** factory) { return Module::GetModule().GetActivationFactory(activatableClassId, factory); } STDAPI DllCanUnloadNow() { return Module::GetModule().GetObjectCount() == 0 ? S_OK : S_FALSE; } STDAPI DllGetClassObject(_In_ REFCLSID rclsid, _In_ REFIID riid, _COM_Outptr_ void** instance) { return Module::GetModule().GetClassObject(rclsid, riid, instance); } ================================================ FILE: PowerToys/src/modules/FileLocksmith/FileLocksmithExt/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 查看文件占用 This text will be shown when the user opens the context menu (right clicks) a file. File Locksmith is the product name, do not loc. 文件开锁匠 Localized name of the PowerToy. ================================================ FILE: PowerToys/src/modules/FileLocksmith/FileLocksmithUI/Strings/en-us/Resources.resw ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 文件开锁匠 Title of the window when running as user. 无占用 结束任务 占用文件 占用文件 关闭 As in, close a dialog prompt. 点击查看所有选中文件 Paths as in file paths that were selected for the utility to check. 此为系统程序,结束它可能导致系统故障。 选中文件列表 Paths as in file paths that were selected for the utility to check. 关闭 As in, close a dialog prompt. 未选择文件 进程 ID 刷新 以管理员身份重启 用户 管理员权限: 文件开锁匠 Title of the window when running as administrator. 查看 Show files for the selected process ================================================ FILE: PowerToys/src/modules/Hosts/HostsUILib/Strings/en-us/Resources.resw ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 启用 启用 添加 添加条目 添加条目 添加条目 (Ctrl+N) 添加一个条目 注释 注释 取消 保存 注释 添加条目 地址 "Address" refers to the IP address of the entry 地址 "Address" refers to the IP address of the entry 清除筛选 清除筛选 清除筛选 备注 "Comment" refers to the comment of the entry 备注 "Comment" refers to the comment of the entry 删除 确定删除此条目? 删除 删除 (Delete) "Delete" between parentheses refers to the Delete keyboard key 复制 Refers to the action of duplicate an existing entry 地址重复 编辑 无筛选结果 未储存任何条目 "Hosts" refers to the system hosts file, do not loc 条目 取消 域名表文件被其他软件改动。 "Hosts" refers to the system hosts file, do not loc 无法保存域名表文件,因为文件被其他软件所占用。 "Hosts" refers to the system hosts file, do not loc 无法保存域名表文件。 "Hosts" refers to the system hosts file, do not loc 无法保存域名表文件,需要管理员权限。 "Hosts" refers to the system hosts file, do not loc 无法保存域名表文件,文件不允许写入。 "Hosts" refers to the system hosts file, do not loc 筛选 筛选 域名 "Hosts" refers to the system hosts file, do not loc // 并不是 空格分隔多个域名(比如:server server.local),一次最多九个 Do not localize "server" and "server.local" 域名 "Hosts" refers to the system hosts file, do not loc // 并不是 获取写入权限 下移 上移 打开文本编辑器 "Hosts" refers to the system hosts file, do not loc 打开文本编辑器 "Hosts" refers to the system hosts file, do not loc Ping 测试 "Ping" refers to the command-line utility, do not loc Ping 结果 "Ping" refers to the command-line utility, do not loc 重新加载 设置 设置 仅显示地址重复的条目 每条最多填写九个域名,太长的条目已被拆分,保存后生效。 "Hosts" refers to the system hosts file, do not loc 域名数量过多 "Hosts" refers to the system hosts file, do not loc 地址不符合 IPv4 或 IPv6 格式 域名列表不符合格式 更新 更新条目 继续 退出 这是 Hosts 域名表文件编辑器。 错误修改域名表可能会导致无法解析域名、访问网站,因此请谨慎使用。 "Hosts" refers to the system hosts file, do not loc 警告 管理员权限: 域名表编辑器 Title of the window when running as administrator. "Hosts File Editor" is the name of the utility. "Hosts" refers to the system hosts file, do not loc 域名表编辑器 Title of the window when running as user. "Hosts File Editor" is the name of the utility. "Hosts" refers to the system hosts file, do not loc ================================================ FILE: PowerToys/src/modules/MeasureTool/MeasureToolUI/Strings/en-us/Resources.resw ================================================  text/microsoft-resx 1.3 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 框选 (Ctrl+1) 框选 (Ctrl+1) 间距 (Ctrl+2) 间距 (Ctrl+2) 水平间距 (Ctrl+3) 水平间距 (Ctrl+3) 垂直间距 (Ctrl+4) 垂直间距 (Ctrl+4) 关闭 (Esc) 像素 英寸 厘米 毫米 ================================================ FILE: PowerToys/src/modules/MouseWithoutBorders/App/Class/Common.Clipboard.cs ================================================ // 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.Collections.Specialized; using System.Diagnostics; using System.Drawing; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Net.Sockets; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using Microsoft.PowerToys.Telemetry; // // Clipboard related routines. // // // 2008 created by Truong Do (ductdo). // 2009-... modified by Truong Do (TruongDo). // 2023- Included in PowerToys. // using MouseWithoutBorders.Class; using MouseWithoutBorders.Core; using MouseWithoutBorders.Exceptions; using SystemClipboard = System.Windows.Forms.Clipboard; using Thread = MouseWithoutBorders.Core.Thread; namespace MouseWithoutBorders { internal partial class Common { internal static readonly char[] Comma = new char[] { ',' }; internal static readonly char[] Star = new char[] { '*' }; internal static readonly char[] NullSeparator = new char[] { '\0' }; internal const uint BIG_CLIPBOARD_DATA_TIMEOUT = 60000; // [CN] 30s -> 60s private const uint MAX_CLIPBOARD_DATA_SIZE_CAN_BE_SENT_INSTANTLY_TCP = 1024 * 1024; // 1MB private const uint MAX_CLIPBOARD_FILE_SIZE_CAN_BE_SENT = 500 * 1024 * 1024; // 100MB [CN]-> 500MB private const int TEXT_HEADER_SIZE = 12; private const int DATA_SIZE = 48; private const string TEXT_TYPE_SEP = "{4CFF57F7-BEDD-43d5-AE8F-27A61E886F2F}"; private static long lastClipboardEventTime; private static string lastMachineWithClipboardData; private static string lastDragDropFile; #pragma warning disable SA1307 // Accessible fields should begin with upper-case letter internal static long clipboardCopiedTime; #pragma warning restore SA1307 internal static ID LastIDWithClipboardData { get; set; } internal static string LastDragDropFile { get => Common.lastDragDropFile; set => Common.lastDragDropFile = value; } internal static string LastMachineWithClipboardData { get => Common.lastMachineWithClipboardData; set => Common.lastMachineWithClipboardData = value; } internal static long LastClipboardEventTime { get => Common.lastClipboardEventTime; set => Common.lastClipboardEventTime = value; } internal static IntPtr NextClipboardViewer { get; set; } internal static bool IsClipboardDataImage { get; private set; } internal static byte[] LastClipboardData { get; private set; } private static object lastClipboardObject = string.Empty; internal static bool HasSwitchedMachineSinceLastCopy { get; set; } internal static bool CheckClipboardEx(ByteArrayOrString data, bool isFilePath) { Logger.LogDebug($"{nameof(CheckClipboardEx)}: ShareClipboard = {Setting.Values.ShareClipboard}, TransferFile = {Setting.Values.TransferFile}, data = {data}."); Logger.LogDebug($"{nameof(CheckClipboardEx)}: {nameof(Setting.Values.OneWayClipboardMode)} = {Setting.Values.OneWayClipboardMode}."); if (!Setting.Values.ShareClipboard) { return false; } if (Common.RunWithNoAdminRight && Setting.Values.OneWayClipboardMode) { return false; } if (GetTick() - LastClipboardEventTime < 1000) { Logger.LogDebug("GetTick() - lastClipboardEventTime < 1000"); LastClipboardEventTime = GetTick(); return false; } LastClipboardEventTime = GetTick(); try { IsClipboardDataImage = false; LastClipboardData = null; LastDragDropFile = null; GC.Collect(); string stringData = null; byte[] byteData = null; if (data.IsByteArray) { byteData = data.GetByteArray(); } else { stringData = data.GetString(); } if (stringData != null) { if (!HasSwitchedMachineSinceLastCopy) { if (lastClipboardObject is string lastStringData && lastStringData.Equals(stringData, StringComparison.OrdinalIgnoreCase)) { Logger.LogDebug("CheckClipboardEx: Same string data."); return false; } } HasSwitchedMachineSinceLastCopy = false; if (isFilePath) { Logger.LogDebug("Clipboard contains FileDropList"); if (!Setting.Values.TransferFile) { Logger.LogDebug("TransferFile option is unchecked."); return false; } string filePath = stringData; _ = Common.ImpersonateLoggedOnUserAndDoSomething(() => { if (File.Exists(filePath) || Directory.Exists(filePath)) { if (File.Exists(filePath) && new FileInfo(filePath).Length <= MAX_CLIPBOARD_FILE_SIZE_CAN_BE_SENT) { Logger.LogDebug("Clipboard contains: " + filePath); LastDragDropFile = filePath; SendClipboardBeat(); SetToggleIcon(new int[TOGGLE_ICONS_SIZE] { ICON_BIG_CLIPBOARD, -1, ICON_BIG_CLIPBOARD, -1 }); } else { if (Directory.Exists(filePath)) { Logger.LogDebug("Clipboard contains a directory: " + filePath); LastDragDropFile = filePath; SendClipboardBeat(); } else { LastDragDropFile = filePath + " - 文件过大无法粘贴,请改为拖放文件!"; SendClipboardBeat(); Logger.Log("Clipboard: File too big: " + filePath); } SetToggleIcon(new int[TOGGLE_ICONS_SIZE] { ICON_ERROR, -1, ICON_ERROR, -1 }); } } else { Logger.Log("CheckClipboardEx: File not found: " + filePath); } }); } else { byte[] texts = Common.GetBytesU(stringData); using MemoryStream ms = new(); using (DeflateStream s = new(ms, CompressionMode.Compress, true)) { s.Write(texts, 0, texts.Length); } Logger.LogDebug("Plain/Zip = " + texts.Length.ToString(CultureInfo.CurrentCulture) + "/" + ms.Length.ToString(CultureInfo.CurrentCulture)); LastClipboardData = ms.GetBuffer(); } } else if (byteData != null) { if (!HasSwitchedMachineSinceLastCopy) { if (lastClipboardObject is byte[] lastByteData && Enumerable.SequenceEqual(lastByteData, byteData)) { Logger.LogDebug("CheckClipboardEx: Same byte[] data."); return false; } } HasSwitchedMachineSinceLastCopy = false; Logger.LogDebug("Clipboard contains image"); IsClipboardDataImage = true; LastClipboardData = byteData; } else { Logger.LogDebug("*** Clipboard contains something else!"); return false; } lastClipboardObject = data; if (LastClipboardData != null && LastClipboardData.Length > 0) { if (LastClipboardData.Length > MAX_CLIPBOARD_DATA_SIZE_CAN_BE_SENT_INSTANTLY_TCP) { SendClipboardBeat(); SetToggleIcon(new int[TOGGLE_ICONS_SIZE] { ICON_BIG_CLIPBOARD, -1, ICON_BIG_CLIPBOARD, -1 }); } else { SetToggleIcon(new int[TOGGLE_ICONS_SIZE] { ICON_SMALL_CLIPBOARD, -1, -1, -1 }); SendClipboardDataUsingTCP(LastClipboardData, IsClipboardDataImage); } return true; } } catch (Exception e) { Logger.Log(e); } return false; } private static void SendClipboardDataUsingTCP(byte[] bytes, bool image) { if (Sk == null) { return; } new Task(() => { // SuppressFlow fixes an issue on service mode, where the helper process can't get enough permissions to be started again. // More details can be found on: https://github.com/microsoft/PowerToys/pull/36892 using var asyncFlowControl = ExecutionContext.SuppressFlow(); System.Threading.Thread thread = Thread.CurrentThread; thread.Name = $"{nameof(SendClipboardDataUsingTCP)}.{thread.ManagedThreadId}"; Thread.UpdateThreads(thread); int l = bytes.Length; int index = 0; int len; DATA package = new(); byte[] buf = new byte[PACKAGE_SIZE_EX]; int dataStart = PACKAGE_SIZE_EX - DATA_SIZE; while (true) { if ((index + DATA_SIZE) > l) { len = l - index; Array.Clear(buf, 0, PACKAGE_SIZE_EX); } else { len = DATA_SIZE; } Array.Copy(bytes, index, buf, dataStart, len); package.Bytes = buf; package.Type = image ? PackageType.ClipboardImage : PackageType.ClipboardText; package.Des = ID.ALL; SkSend(package, (uint)MachineID, false); index += DATA_SIZE; if (index >= l) { break; } } package.Type = PackageType.ClipboardDataEnd; package.Des = ID.ALL; SkSend(package, (uint)MachineID, false); }).Start(); } internal static void ReceiveClipboardDataUsingTCP(DATA data, bool image, TcpSk tcp) { try { if (Sk == null || RunOnLogonDesktop || RunOnScrSaverDesktop) { return; } MemoryStream m = new(); int dataStart = PACKAGE_SIZE_EX - DATA_SIZE; m.Write(data.Bytes, dataStart, DATA_SIZE); int unexpectedCount = 0; bool done = false; do { data = SocketStuff.TcpReceiveData(tcp, out int err); switch (data.Type) { case PackageType.ClipboardImage: case PackageType.ClipboardText: m.Write(data.Bytes, dataStart, DATA_SIZE); break; case PackageType.ClipboardDataEnd: done = true; break; default: Receiver.ProcessPackage(data, tcp); if (++unexpectedCount > 100) { Logger.Log("ReceiveClipboardDataUsingTCP: unexpectedCount > 100!"); done = true; } break; } } while (!done); LastClipboardEventTime = GetTick(); if (image) { Image im = Image.FromStream(m); Clipboard.SetImage(im); LastClipboardEventTime = GetTick(); } else { Common.SetClipboardData(m.GetBuffer()); LastClipboardEventTime = GetTick(); } m.Dispose(); SetToggleIcon(new int[TOGGLE_ICONS_SIZE] { ICON_SMALL_CLIPBOARD, -1, ICON_SMALL_CLIPBOARD, -1 }); } catch (Exception e) { Logger.Log("ReceiveClipboardDataUsingTCP: " + e.Message); } } private static readonly Lock ClipboardThreadOldLock = new(); private static System.Threading.Thread clipboardThreadOld; internal static void GetRemoteClipboard(string postAction) { if (!RunOnLogonDesktop && !RunOnScrSaverDesktop) { if (Common.LastMachineWithClipboardData == null || Common.LastMachineWithClipboardData.Length < 1) { return; } new Task(() => { // SuppressFlow fixes an issue on service mode, where the helper process can't get enough permissions to be started again. // More details can be found on: https://github.com/microsoft/PowerToys/pull/36892 using var asyncFlowControl = ExecutionContext.SuppressFlow(); System.Threading.Thread thread = Thread.CurrentThread; thread.Name = $"{nameof(ConnectAndGetData)}.{thread.ManagedThreadId}"; Thread.UpdateThreads(thread); ConnectAndGetData(postAction); }).Start(); } } private static Stream m; private static void ConnectAndGetData(object postAction) { if (Sk == null) { Logger.Log("ConnectAndGetData: Sk == null!"); return; } string remoteMachine; TcpClient clipboardTcpClient = null; string postAct = (string)postAction; Logger.LogDebug("ConnectAndGetData.postAction: " + postAct); ClipboardPostAction clipboardPostAct = postAct.Contains("mspaint,") ? ClipboardPostAction.Mspaint : postAct.Equals("desktop", StringComparison.OrdinalIgnoreCase) ? ClipboardPostAction.Desktop : ClipboardPostAction.Other; try { remoteMachine = postAct.Contains("mspaint,") ? postAct.Split(Comma)[1] : Common.LastMachineWithClipboardData; remoteMachine = remoteMachine.Trim(); if (!IsConnectedByAClientSocketTo(remoteMachine)) { Logger.Log($"No potential inbound connection from {MachineName} to {remoteMachine}, ask for a push back instead."); ID machineId = MachinePool.ResolveID(remoteMachine); if (machineId != ID.NONE) { SkSend( new DATA() { Type = PackageType.ClipboardAsk, Des = machineId, MachineName = MachineName, PostAction = clipboardPostAct, }, null, false); } else { Logger.Log($"Unable to resolve {remoteMachine} to its long IP."); } return; } ShowToolTip("正在连接 " + remoteMachine, 2000, ToolTipIcon.Info, Setting.Values.ShowClipNetStatus); clipboardTcpClient = ConnectToRemoteClipboardSocket(remoteMachine); } catch (ThreadAbortException) { Logger.Log("The current thread is being aborted (1)."); if (clipboardTcpClient != null && clipboardTcpClient.Connected) { clipboardTcpClient.Client.Close(); } return; } catch (Exception e) { Logger.Log(e); Common.SetToggleIcon(new int[Common.TOGGLE_ICONS_SIZE] { Common.ICON_BIG_CLIPBOARD, -1, Common.ICON_BIG_CLIPBOARD, -1, }); ShowToolTip(e.Message, 1000, ToolTipIcon.Warning, Setting.Values.ShowClipNetStatus); return; } bool clientPushData = false; if (!ShakeHand(ref remoteMachine, clipboardTcpClient.Client, out Stream enStream, out Stream deStream, ref clientPushData, ref clipboardPostAct)) { return; } ReceiveAndProcessClipboardData(remoteMachine, clipboardTcpClient.Client, enStream, deStream, postAct); } internal static void ReceiveAndProcessClipboardData(string remoteMachine, Socket s, Stream enStream, Stream deStream, string postAct) { lock (ClipboardThreadOldLock) { // Do not enable two connections at the same time. if (clipboardThreadOld != null && clipboardThreadOld.ThreadState != System.Threading.ThreadState.AbortRequested && clipboardThreadOld.ThreadState != System.Threading.ThreadState.Aborted && clipboardThreadOld.IsAlive && clipboardThreadOld.ManagedThreadId != Thread.CurrentThread.ManagedThreadId) { if (clipboardThreadOld.Join(3000)) { if (m != null) { m.Flush(); m.Close(); m = null; } } } clipboardThreadOld = Thread.CurrentThread; } try { byte[] header = new byte[1024]; byte[] buf = new byte[NETWORK_STREAM_BUF_SIZE]; string fileName = null; string tempFile = "data", savingFolder = string.Empty; Common.ToggleIconsIndex = 0; int rv; long receivedCount = 0; if ((rv = deStream.ReadEx(header, 0, header.Length)) < header.Length) { Logger.Log("Reading header failed: " + rv.ToString(CultureInfo.CurrentCulture)); Common.SetToggleIcon(new int[Common.TOGGLE_ICONS_SIZE] { Common.ICON_BIG_CLIPBOARD, -1, -1, -1, }); return; } fileName = Common.GetStringU(header).Replace("\0", string.Empty); Logger.LogDebug("Header: " + fileName); string[] headers = fileName.Split(Star); if (headers.Length < 2 || !long.TryParse(headers[0], out long dataSize)) { Logger.Log(string.Format( CultureInfo.CurrentCulture, "Reading header failed: {0}:{1}", headers.Length, fileName)); Common.SetToggleIcon(new int[Common.TOGGLE_ICONS_SIZE] { Common.ICON_BIG_CLIPBOARD, -1, -1, -1, }); return; } fileName = headers[1]; Logger.LogDebug(string.Format( CultureInfo.CurrentCulture, "Receiving {0}:{1} from {2}...", Path.GetFileName(fileName), dataSize, remoteMachine)); ShowToolTip( string.Format( CultureInfo.CurrentCulture, "正在从 {1} 接收 {0} ...", Path.GetFileName(fileName), remoteMachine), 5000, ToolTipIcon.Info, Setting.Values.ShowClipNetStatus); if (fileName.StartsWith("image", StringComparison.CurrentCultureIgnoreCase) || fileName.StartsWith("text", StringComparison.CurrentCultureIgnoreCase)) { m = new MemoryStream(); } else { if (postAct.Equals("desktop", StringComparison.OrdinalIgnoreCase)) { _ = ImpersonateLoggedOnUserAndDoSomething(() => { savingFolder = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\MouseWithoutBorders\\"; if (!Directory.Exists(savingFolder)) { _ = Directory.CreateDirectory(savingFolder); } }); tempFile = savingFolder + Path.GetFileName(fileName); m = new FileStream(tempFile, FileMode.Create); } else if (postAct.Contains("mspaint")) { tempFile = GetMyStorageDir() + @"ScreenCapture-" + remoteMachine + ".png"; m = new FileStream(tempFile, FileMode.Create); } else { tempFile = GetMyStorageDir(); tempFile += Path.GetFileName(fileName); m = new FileStream(tempFile, FileMode.Create); } Logger.Log("==> " + tempFile); } ShowToolTip( string.Format( CultureInfo.CurrentCulture, "正在从 {1} 接收 {0} ...", Path.GetFileName(fileName), remoteMachine), 5000, ToolTipIcon.Info, Setting.Values.ShowClipNetStatus); do { rv = deStream.ReadEx(buf, 0, buf.Length); if (rv > 0) { receivedCount += rv; if (receivedCount > dataSize) { rv -= (int)(receivedCount - dataSize); } m.Write(buf, 0, rv); } if (Common.ToggleIcons == null) { Common.SetToggleIcon(new int[Common.TOGGLE_ICONS_SIZE] { Common.ICON_SMALL_CLIPBOARD, -1, Common.ICON_SMALL_CLIPBOARD, -1, }); } string text = string.Format(CultureInfo.CurrentCulture, "{0}KB received: {1}", m.Length / 1024, Path.GetFileName(fileName)); DoSomethingInUIThread(() => { MainForm.SetTrayIconText(text); }); } while (rv > 0); if (m != null && fileName != null) { m.Flush(); Logger.LogDebug(m.Length.ToString(CultureInfo.CurrentCulture) + " bytes received."); Common.LastClipboardEventTime = Common.GetTick(); string toolTipText = null; string sizeText = m.Length >= 1024 ? (m.Length / 1024).ToString(CultureInfo.CurrentCulture) + "KB" : m.Length.ToString(CultureInfo.CurrentCulture) + "Bytes"; PowerToysTelemetry.Log.WriteEvent(new MouseWithoutBorders.Telemetry.MouseWithoutBordersClipboardFileTransferEvent()); if (fileName.StartsWith("image", StringComparison.CurrentCultureIgnoreCase)) { Clipboard.SetImage(Image.FromStream(m)); toolTipText = string.Format( CultureInfo.CurrentCulture, "{0} {1} 来自 {2} 已存入剪贴板.", sizeText, "image", remoteMachine); } else if (fileName.StartsWith("text", StringComparison.CurrentCultureIgnoreCase)) { byte[] data = (m as MemoryStream).GetBuffer(); toolTipText = string.Format( CultureInfo.CurrentCulture, "{0} {1} 来自 {2} 已存入剪贴板.", sizeText, "text", remoteMachine); Common.SetClipboardData(data); } else if (tempFile != null) { if (postAct.Equals("desktop", StringComparison.OrdinalIgnoreCase)) { toolTipText = string.Format( CultureInfo.CurrentCulture, "{0} {1} 来自 {2}!", sizeText, Path.GetFileName(fileName), remoteMachine); _ = ImpersonateLoggedOnUserAndDoSomething(() => { ProcessStartInfo startInfo = new(); startInfo.UseShellExecute = true; startInfo.WorkingDirectory = savingFolder; startInfo.FileName = savingFolder; startInfo.Verb = "open"; _ = Process.Start(startInfo); }); } else if (postAct.Contains("mspaint")) { m.Close(); m = null; OpenImage(tempFile); toolTipText = string.Format( CultureInfo.CurrentCulture, "{0} {1} 来自 {2} 已放入画图.", sizeText, Path.GetFileName(tempFile), remoteMachine); } else { StringCollection filePaths = new() { tempFile, }; Clipboard.SetFileDropList(filePaths); toolTipText = string.Format( CultureInfo.CurrentCulture, "{0} {1} 来自 {2} 已存入剪贴板.", sizeText, Path.GetFileName(fileName), remoteMachine); } } if (!string.IsNullOrWhiteSpace(toolTipText)) { Common.ShowToolTip(toolTipText, 5000, ToolTipIcon.Info, Setting.Values.ShowClipNetStatus); } DoSomethingInUIThread(() => { MainForm.UpdateNotifyIcon(); }); m?.Close(); m = null; } } catch (ThreadAbortException) { Logger.Log("The current thread is being aborted (3)."); s.Close(); if (m != null) { m.Close(); m = null; } return; } catch (Exception e) { if (e is IOException) { string log = $"{nameof(ReceiveAndProcessClipboardData)}: Exception accessing the socket: {e.InnerException?.GetType()}/{e.Message}. (This is expected when the remote machine closes the connection during desktop switch or reconnection.)"; Logger.Log(log); } else { Logger.Log(e); } Common.SetToggleIcon(new int[Common.TOGGLE_ICONS_SIZE] { Common.ICON_BIG_CLIPBOARD, -1, Common.ICON_BIG_CLIPBOARD, -1, }); ShowToolTip(e.Message, 1000, ToolTipIcon.Info, Setting.Values.ShowClipNetStatus); if (m != null) { m.Close(); m = null; } return; } s.Close(); } internal static bool ShakeHand(ref string remoteName, Socket s, out Stream enStream, out Stream deStream, ref bool clientPushData, ref ClipboardPostAction postAction) { const int CLIPBOARD_HANDSHAKE_TIMEOUT = 30; s.ReceiveTimeout = CLIPBOARD_HANDSHAKE_TIMEOUT * 1000; s.NoDelay = true; s.SendBufferSize = s.ReceiveBufferSize = 1024000; bool handShaken = false; enStream = deStream = null; try { DATA package = new() { Type = clientPushData ? PackageType.ClipboardPush : PackageType.Clipboard, PostAction = postAction, Src = MachineID, MachineName = MachineName, }; byte[] buf = new byte[PACKAGE_SIZE_EX]; NetworkStream ns = new(s); enStream = Common.GetEncryptedStream(ns); Common.SendOrReceiveARandomDataBlockPerInitialIV(enStream); Logger.LogDebug($"{nameof(ShakeHand)}: Writing header package."); enStream.Write(package.Bytes, 0, PACKAGE_SIZE_EX); Logger.LogDebug($"{nameof(ShakeHand)}: Sent: clientPush={clientPushData}, postAction={postAction}."); deStream = Common.GetDecryptedStream(ns); Common.SendOrReceiveARandomDataBlockPerInitialIV(deStream, false); Logger.LogDebug($"{nameof(ShakeHand)}: Reading header package."); int bytesReceived = deStream.ReadEx(buf, 0, Common.PACKAGE_SIZE_EX); package.Bytes = buf; string name = "Unknown"; if (bytesReceived == Common.PACKAGE_SIZE_EX) { if (package.Type is PackageType.Clipboard or PackageType.ClipboardPush) { name = remoteName = package.MachineName; Logger.LogDebug($"{nameof(ShakeHand)}: Connection from {name}:{package.Src}"); if (Common.MachinePool.ResolveID(name) == package.Src && Common.IsConnectedTo(package.Src)) { clientPushData = package.Type == PackageType.ClipboardPush; postAction = package.PostAction; handShaken = true; Logger.LogDebug($"{nameof(ShakeHand)}: Received: clientPush={clientPushData}, postAction={postAction}."); } else { Logger.LogDebug($"{nameof(ShakeHand)}: No active connection to the machine: {name}."); } } else { Logger.LogDebug($"{nameof(ShakeHand)}: Unexpected package type: {package.Type}."); } } else { Logger.LogDebug($"{nameof(ShakeHand)}: BytesTransferred != PACKAGE_SIZE_EX: {bytesReceived}"); } if (!handShaken) { string msg = $"剪贴板连接被拒绝: {name}:{remoteName}/{package.Src}\r\n\r\n请确保软件版本相同."; Logger.Log(msg); Common.ShowToolTip(msg, 3000, ToolTipIcon.Warning); Common.SetToggleIcon(new int[Common.TOGGLE_ICONS_SIZE] { Common.ICON_BIG_CLIPBOARD, -1, -1, -1 }); } } catch (ThreadAbortException) { Logger.Log($"{nameof(ShakeHand)}: The current thread is being aborted."); s.Close(); } catch (Exception e) { if (e is IOException) { string log = $"{nameof(ShakeHand)}: Exception accessing the socket: {e.InnerException?.GetType()}/{e.Message}. (This is expected when the remote machine closes the connection during desktop switch or reconnection.)"; Logger.Log(log); } else { Logger.Log(e); } Common.SetToggleIcon(new int[Common.TOGGLE_ICONS_SIZE] { Common.ICON_BIG_CLIPBOARD, -1, Common.ICON_BIG_CLIPBOARD, -1, }); MainForm.UpdateNotifyIcon(); ShowToolTip(e.Message + "\r\n\r\n请确保软件版本相同.", 1000, ToolTipIcon.Warning, Setting.Values.ShowClipNetStatus); if (m != null) { m.Close(); m = null; } } return handShaken; } internal static TcpClient ConnectToRemoteClipboardSocket(string remoteMachine) { TcpClient clipboardTcpClient; clipboardTcpClient = new TcpClient(AddressFamily.InterNetworkV6); clipboardTcpClient.Client.DualMode = true; SocketStuff sk = Common.Sk; if (sk != null) { Common.DoSomethingInUIThread(() => Common.MainForm.ChangeIcon(Common.ICON_SMALL_CLIPBOARD)); System.Net.IPAddress ip = GetConnectedClientSocketIPAddressFor(remoteMachine); Logger.LogDebug($"{nameof(ConnectToRemoteClipboardSocket)}Connecting to {remoteMachine}:{ip}:{sk.TcpPort}..."); if (ip != null) { clipboardTcpClient.Connect(ip, sk.TcpPort); } else { clipboardTcpClient.Connect(remoteMachine, sk.TcpPort); } Logger.LogDebug($"Connected from {clipboardTcpClient.Client.LocalEndPoint}. Getting data..."); return clipboardTcpClient; } else { throw new ExpectedSocketException($"{nameof(ConnectToRemoteClipboardSocket)}: No longer connected."); } } internal static void SetClipboardData(byte[] data) { if (data == null || data.Length <= 0) { Logger.Log("data is null or empty!"); return; } if (data.Length > 1024000) { ShowToolTip( string.Format( CultureInfo.CurrentCulture, "正在解压 {0} 剪贴板数据 ...", (data.Length / 1024).ToString(CultureInfo.CurrentCulture) + "KB"), 5000, ToolTipIcon.Info, Setting.Values.ShowClipNetStatus); } string st = string.Empty; using (MemoryStream ms = new(data)) { using DeflateStream s = new(ms, CompressionMode.Decompress, true); const int BufferSize = 1024000; // Buffer size should be big enough, this is critical to performance! int rv = 0; do { byte[] buffer = new byte[BufferSize]; rv = s.ReadEx(buffer, 0, BufferSize); if (rv > 0) { st += Common.GetStringU(buffer); } else { break; } } while (true); } int textTypeCount = 0; string[] texts = st.Split(new string[] { TEXT_TYPE_SEP }, StringSplitOptions.RemoveEmptyEntries); string tmp; DataObject data1 = new(); foreach (string txt in texts) { if (string.IsNullOrEmpty(txt.Trim(NullSeparator))) { continue; } tmp = txt[3..]; if (txt.StartsWith("RTF", StringComparison.CurrentCultureIgnoreCase)) { Logger.LogDebug(((double)tmp.Length / 1024).ToString("0.00", CultureInfo.InvariantCulture) + "KB of RTF <-"); data1.SetData(DataFormats.Rtf, tmp); } else if (txt.StartsWith("HTM", StringComparison.CurrentCultureIgnoreCase)) { Logger.LogDebug(((double)tmp.Length / 1024).ToString("0.00", CultureInfo.InvariantCulture) + "KB of HTM <-"); data1.SetData(DataFormats.Html, tmp); } else if (txt.StartsWith("TXT", StringComparison.CurrentCultureIgnoreCase)) { Logger.LogDebug(((double)tmp.Length / 1024).ToString("0.00", CultureInfo.InvariantCulture) + "KB of TXT <-"); data1.SetData(DataFormats.UnicodeText, tmp); } else { if (textTypeCount == 0) { Logger.LogDebug(((double)txt.Length / 1024).ToString("0.00", CultureInfo.InvariantCulture) + "KB of UNI <-"); data1.SetData(DataFormats.UnicodeText, txt); } Logger.Log("Invalid clipboard format received!"); } textTypeCount++; } if (textTypeCount > 0) { Clipboard.SetDataObject(data1); } } } internal static class Clipboard { public static void SetFileDropList(StringCollection filePaths) { Common.DoSomethingInUIThread(() => { try { _ = Common.Retry( nameof(SystemClipboard.SetFileDropList), () => { SystemClipboard.SetFileDropList(filePaths); return true; }, (log) => Logger.TelemetryLogTrace( log, SeverityLevel.Information), () => Common.LastClipboardEventTime = Common.GetTick()); } catch (ExternalException e) { Logger.Log(e); } catch (ThreadStateException e) { Logger.Log(e); } catch (ArgumentNullException e) { Logger.Log(e); } catch (ArgumentException e) { Logger.Log(e); } }); } public static void SetImage(Image image) { Common.DoSomethingInUIThread(() => { try { _ = Common.Retry( nameof(SystemClipboard.SetImage), () => { SystemClipboard.SetImage(image); return true; }, (log) => Logger.TelemetryLogTrace(log, SeverityLevel.Information), () => Common.LastClipboardEventTime = Common.GetTick()); } catch (ExternalException e) { Logger.Log(e); } catch (ThreadStateException e) { Logger.Log(e); } catch (ArgumentNullException e) { Logger.Log(e); } }); } public static void SetText(string text) { Common.DoSomethingInUIThread(() => { try { _ = Common.Retry( nameof(SystemClipboard.SetText), () => { SystemClipboard.SetText(text); return true; }, (log) => Logger.TelemetryLogTrace(log, SeverityLevel.Information), () => Common.LastClipboardEventTime = Common.GetTick()); } catch (ExternalException e) { Logger.Log(e); } catch (ThreadStateException e) { Logger.Log(e); } catch (ArgumentNullException e) { Logger.Log(e); } }); } public static void SetDataObject(DataObject dataObject) { Common.DoSomethingInUIThread(() => { try { SystemClipboard.SetDataObject(dataObject, true, 10, 200); } catch (ExternalException e) { string dataFormats = string.Join(",", dataObject.GetFormats()); Logger.Log($"{e.Message}: {dataFormats}"); } catch (ThreadStateException e) { Logger.Log(e); } catch (ArgumentNullException e) { Logger.Log(e); } }); } } } ================================================ FILE: PowerToys/src/modules/MouseWithoutBorders/App/Class/Common.Encryption.cs ================================================ // 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. // // Encrypt/decrypt implementation. // // // 2008 created by Truong Do (ductdo). // 2009-... modified by Truong Do (TruongDo). // 2023- Included in PowerToys. // 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; /// /// 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 /// 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 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 ? "密码必须至少包含 16 个字符,空格不计,必须由程序自动生成。" : null; return error == null; } } } ================================================ FILE: PowerToys/src/modules/MouseWithoutBorders/App/Class/Common.Event.cs ================================================ // Copyright (c) Microsoft Corporation // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Diagnostics.CodeAnalysis; using System.Drawing; using System.Globalization; using System.Threading; using System.Threading.Tasks; // // Keyboard/Mouse hook callback implementation. // // // 2008 created by Truong Do (ductdo). // 2009-... modified by Truong Do (TruongDo). // 2023- Included in PowerToys. // using MouseWithoutBorders.Class; using MouseWithoutBorders.Core; using MouseWithoutBorders.Form; using Thread = MouseWithoutBorders.Core.Thread; namespace MouseWithoutBorders { internal partial class Common { private static readonly DATA KeybdPackage = new(); private static readonly DATA MousePackage = new(); #pragma warning disable SA1307 // Accessible fields should begin with upper-case names internal static ulong inputEventCount; internal static ulong invalidPackageCount; #pragma warning restore SA1307 internal static int MOVE_MOUSE_RELATIVE = 100000; internal static int XY_BY_PIXEL = 300000; static Common() { } internal static ulong InvalidPackageCount { get => Common.invalidPackageCount; set => Common.invalidPackageCount = value; } internal static ulong InputEventCount { get => Common.inputEventCount; set => Common.inputEventCount = value; } internal static ulong RealInputEventCount { get; set; } private static Point actualLastPos; private static int myLastX; private static int myLastY; [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Dotnet port with style preservation")] internal static void MouseEvent(MOUSEDATA e, int dx, int dy) { try { PaintCount = 0; bool switchByMouseEnabled = IsSwitchingByMouseEnabled(); if (switchByMouseEnabled && Sk != null && (DesMachineID == MachineID || !Setting.Values.MoveMouseRelatively) && e.dwFlags == WM_MOUSEMOVE) { Point p = MoveToMyNeighbourIfNeeded(e.X, e.Y, desMachineID); if (!p.IsEmpty) { HasSwitchedMachineSinceLastCopy = true; Logger.LogDebug(string.Format( CultureInfo.CurrentCulture, "***** Host Machine: newDesMachineIdEx set = [{0}]. Mouse is now at ({1},{2})", newDesMachineIdEx, e.X, e.Y)); myLastX = e.X; myLastY = e.Y; PrepareToSwitchToMachine(newDesMachineIdEx, p); } } if (desMachineID != MachineID && SwitchLocation.Count <= 0) { MousePackage.Des = desMachineID; MousePackage.Type = PackageType.Mouse; MousePackage.Md.dwFlags = e.dwFlags; MousePackage.Md.WheelDelta = e.WheelDelta; // Relative move if (Setting.Values.MoveMouseRelatively && Math.Abs(dx) >= MOVE_MOUSE_RELATIVE && Math.Abs(dy) >= MOVE_MOUSE_RELATIVE) { MousePackage.Md.X = dx; MousePackage.Md.Y = dy; } else { MousePackage.Md.X = (e.X - primaryScreenBounds.Left) * 65535 / screenWidth; MousePackage.Md.Y = (e.Y - primaryScreenBounds.Top) * 65535 / screenHeight; } SkSend(MousePackage, null, false); if (MousePackage.Md.dwFlags is WM_LBUTTONUP or WM_RBUTTONUP) { Thread.Sleep(10); } NativeMethods.GetCursorPos(ref actualLastPos); if (actualLastPos != Common.LastPos) { Logger.LogDebug($"Mouse cursor has moved unexpectedly: Expected: {Common.LastPos}, actual: {actualLastPos}."); Common.LastPos = actualLastPos; } } #if SHOW_ON_WINLOGON_EX if (RunOnLogonDesktop && e.dwFlags == WM_RBUTTONUP && desMachineID == machineID && e.x > 2 && e.x < 100 && e.y > 2 && e.y < 20) { DoSomethingInUIThread(delegate() { MainForm.HideMenuWhenRunOnLogonDesktop(); MainForm.MainMenu.Hide(); MainForm.MainMenu.Show(e.x - 5, e.y - 3); }); } #endif } catch (Exception ex) { Logger.Log(ex); } } internal static bool IsSwitchingByMouseEnabled() { return (EasyMouseOption)Setting.Values.EasyMouse == EasyMouseOption.开启 || InputHook.EasyMouseKeyDown; } internal static void PrepareToSwitchToMachine(ID newDesMachineID, Point desMachineXY) { Logger.LogDebug($"PrepareToSwitchToMachine: newDesMachineID = {newDesMachineID}, desMachineXY = {desMachineXY}"); if (((GetTick() - lastJump < 100) && (GetTick() - lastJump > 0)) || desMachineID == ID.ALL) { Logger.LogDebug("PrepareToSwitchToMachine: lastJump"); return; } lastJump = GetTick(); string newDesMachineName = NameFromID(newDesMachineID); if (!IsConnectedTo(newDesMachineID)) {// Connection lost, cancel switching Logger.LogDebug("No active connection found for " + newDesMachineName); // ShowToolTip("No active connection found for [" + newDesMachineName + "]!", 500); } else { Common.newDesMachineID = newDesMachineID; SwitchLocation.X = desMachineXY.X; SwitchLocation.Y = desMachineXY.Y; SwitchLocation.ResetCount(); _ = EvSwitch.Set(); // PostMessage(mainForm.Handle, WM_SWITCH, IntPtr.Zero, IntPtr.Zero); if (newDesMachineID != DragMachine) { if (!IsDragging && !IsDropping) { if (MouseDown && !RunOnLogonDesktop && !RunOnScrSaverDesktop) { DragDropStep02(); } } else if (DragMachine != (ID)1) { ChangeDropMachine(); } } else { DragDropStep11(); } // Change des machine if (desMachineID != newDesMachineID) { Logger.LogDebug("MouseEvent: Switching to new machine:" + newDesMachineName); // Ask current machine to hide the Mouse cursor if (newDesMachineID != ID.ALL && desMachineID != MachineID) { SendPackage(desMachineID, PackageType.HideMouse); } DesMachineID = newDesMachineID; if (desMachineID == MachineID) { if (GetTick() - clipboardCopiedTime < BIG_CLIPBOARD_DATA_TIMEOUT) { clipboardCopiedTime = 0; Common.GetRemoteClipboard("PrepareToSwitchToMachine"); } } else { // Ask the new active machine to get clipboard data (if the data is too big) SendPackage(desMachineID, PackageType.MachineSwitched); } _ = Interlocked.Increment(ref switchCount); } } } internal static void SaveSwitchCount() { if (SwitchCount > 0) { _ = Task.Run(() => { Setting.Values.SwitchCount += SwitchCount; _ = Interlocked.Exchange(ref switchCount, 0); }); } } internal static void KeybdEvent(KEYBDDATA e) { try { PaintCount = 0; if (desMachineID != newDesMachineID) { Logger.LogDebug("KeybdEvent: Switching to new machine..."); DesMachineID = newDesMachineID; } if (desMachineID != MachineID) { KeybdPackage.Des = desMachineID; KeybdPackage.Type = PackageType.Keyboard; KeybdPackage.Kd = e; KeybdPackage.DateTime = GetTick(); SkSend(KeybdPackage, null, false); if (KeybdPackage.Kd.dwFlags is WM_KEYUP or WM_SYSKEYUP) { Thread.Sleep(10); } } } catch (Exception ex) { Logger.Log(ex); } } } } ================================================ FILE: PowerToys/src/modules/MouseWithoutBorders/App/Class/Common.Helper.cs ================================================ // 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.IO; using System.Linq; using System.Security.Principal; using System.Windows.Forms; // // Some other helper methods. // // // 2008 created by Truong Do (ductdo). // 2009-... modified by Truong Do (TruongDo). // 2023- Included in PowerToys. // using Microsoft.Win32; using MouseWithoutBorders.Class; using MouseWithoutBorders.Core; using static System.Windows.Forms.Control; namespace MouseWithoutBorders { internal partial class Common { internal const string HELPER_FORM_TEXT = "Mouse without Borders Helper"; internal const string HelperProcessName = "PowerToys.MouseWithoutBordersHelper"; private static bool signalHelperToExit; private static bool signalWatchDogToExit; internal static long WndProcCounter; private static void WatchDogThread() { long oldCounter = WndProcCounter; do { for (int i = 0; i < 10; i++) { Thread.Sleep(1000); if (signalWatchDogToExit) { break; } } while (BlockingUI) { Thread.Sleep(1000); } if (WndProcCounter == oldCounter) { Process p = Process.GetCurrentProcess(); string procInfo = $"{p.PrivateMemorySize64 / 1024 / 1024}MB, {p.TotalProcessorTime}, {Environment.ProcessorCount}."; string threadStacks = $"{procInfo} {Thread.DumpThreadsStack()}"; Logger.TelemetryLogTrace(threadStacks, SeverityLevel.Error); break; } oldCounter = WndProcCounter; } while (true); } private static void HelperThread() { // SuppressFlow fixes an issue on service mode, where the helper process can't get enough permissions to be started again. // More details can be found on: https://github.com/microsoft/PowerToys/pull/36892 using var asyncFlowControl = System.Threading.ExecutionContext.SuppressFlow(); try { while (true) { _ = EvSwitch.WaitOne(); // Switching to another machine? if (signalHelperToExit) { break; } if (Common.NewDesMachineID != Common.MachineID && Common.NewDesMachineID != ID.ALL) { HideMouseCursor(false); Common.MainFormDotEx(true); } else { if (Common.SwitchLocation.Count > 0) { Common.SwitchLocation.Count--; // When we want to move mouse by pixels, we add 300k to x and y (search for XY_BY_PIXEL for other related code). Logger.LogDebug($"+++++ Moving mouse to {Common.SwitchLocation.X}, {Common.SwitchLocation.Y}"); // MaxXY = 65535 so 100k is safe. if (Common.SwitchLocation.X > XY_BY_PIXEL - 100000 || Common.SwitchLocation.Y > XY_BY_PIXEL - 100000) { InputSimulation.MoveMouse(Common.SwitchLocation.X - XY_BY_PIXEL, Common.SwitchLocation.Y - XY_BY_PIXEL); } else { InputSimulation.MoveMouseEx(Common.SwitchLocation.X, Common.SwitchLocation.Y); } Common.MainFormDot(); } } if (Common.NewDesMachineID == Common.MachineID) { ReleaseAllKeys(); } } } catch (Exception e) { Logger.Log(e); } signalHelperToExit = false; Logger.LogDebug("^^^Helper Thread exiting...^^^"); } internal static void MainFormDotEx(bool bCheckTS) { Logger.LogDebug("***** MainFormDotEx:"); if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop) { int left = Common.PrimaryScreenBounds.Left + ((Common.PrimaryScreenBounds.Right - Common.PrimaryScreenBounds.Left) / 2) - 1; int top = Setting.Values.HideMouse ? 3 : Common.PrimaryScreenBounds.Top + ((Common.PrimaryScreenBounds.Bottom - Common.PrimaryScreenBounds.Top) / 2); Common.MainFormVisible = true; if (Setting.Values.HideMouse && Setting.Values.StealFocusWhenSwitchingMachine && Common.SendMessageToHelper(0x407, new IntPtr(left), new IntPtr(top), true) == 0) { try { /* When user just switches to the Logon desktop, user is actually on the "Windows Default Lock Screen" (LockApp). * If a click is sent to this during switch, it actually triggers a desktop switch on the local machine causing a reconnection affecting the machine switch. * We can detect and skip in this case. * */ IntPtr foreGroundWindow = NativeMethods.GetForegroundWindow(); string foreGroundWindowText = GetText(foreGroundWindow); bool mouseClick = true; if (foreGroundWindowText.Equals("Windows Default Lock Screen", StringComparison.OrdinalIgnoreCase)) { mouseClick = false; } // Window title may be localized, check process name: if (mouseClick) { _ = NativeMethods.GetWindowThreadProcessId(foreGroundWindow, out uint pid); if (pid > 0) { string foreGroundWindowProcess = Process.GetProcessById((int)pid)?.ProcessName; if (foreGroundWindowProcess.Equals("LockApp", StringComparison.OrdinalIgnoreCase)) { mouseClick = false; } } } if (mouseClick) { InputSimulation.MouseClickDotForm(left + 1, top + 1); } } catch (Exception e) { Logger.Log(e); } } } CustomCursor.ShowFakeMouseCursor(int.MinValue, int.MinValue); } internal static void MainForm3Pixels() { Logger.LogDebug("***** MainFormDotLarge:"); DoSomethingInUIThread( () => { MainForm.Left = Common.PrimaryScreenBounds.Left + ((Common.PrimaryScreenBounds.Right - Common.PrimaryScreenBounds.Left) / 2) - 2; MainForm.Top = Setting.Values.HideMouse ? 3 : Common.PrimaryScreenBounds.Top + ((Common.PrimaryScreenBounds.Bottom - Common.PrimaryScreenBounds.Top) / 2) - 1; MainForm.Width = 3; MainForm.Height = 3; MainForm.Opacity = 0.11D; MainForm.TopMost = true; if (Setting.Values.HideMouse) { MainForm.BackColor = Color.Black; MainForm.Show(); Common.MainFormVisible = true; } else { MainForm.BackColor = Color.White; MainForm.Hide(); Common.MainFormVisible = false; } }, true); CustomCursor.ShowFakeMouseCursor(int.MinValue, int.MinValue); } internal static void MainFormDot() { DoSomethingInUIThread( () => { _ = Common.SendMessageToHelper(0x408, IntPtr.Zero, IntPtr.Zero, false); MainForm.Left = Common.PrimaryScreenBounds.Left + ((Common.PrimaryScreenBounds.Right - Common.PrimaryScreenBounds.Left) / 2) - 1; MainForm.Top = Setting.Values.HideMouse ? 3 : Common.PrimaryScreenBounds.Top + ((Common.PrimaryScreenBounds.Bottom - Common.PrimaryScreenBounds.Top) / 2); MainForm.Width = 1; MainForm.Height = 1; MainForm.Opacity = 0.15; MainForm.Hide(); Common.MainFormVisible = false; }, true); CustomCursor.ShowFakeMouseCursor(int.MinValue, int.MinValue); } internal static void ToggleIcon() { try { if (toggleIconsIndex < TOGGLE_ICONS_SIZE) { Common.DoSomethingInUIThread(() => Common.MainForm.ChangeIcon(toggleIcons[toggleIconsIndex++])); } else { toggleIconsIndex = 0; toggleIcons = null; } } catch (Exception e) { Logger.Log(e); } } internal static void RunDDHelper(bool cleanUp = false) { if (Common.RunOnLogonDesktop || Common.RunOnScrSaverDesktop) { return; } if (cleanUp) { try { Process[] ps = Process.GetProcessesByName(HelperProcessName); foreach (Process p in ps) { p.KillProcess(); } } catch (Exception e) { Logger.Log(e); _ = Common.SendMessageToHelper(SharedConst.QUIT_CMD, IntPtr.Zero, IntPtr.Zero); } return; } if (!Common.IsMyDesktopActive()) { return; } if (!Common.IpcChannelCreated) { Logger.TelemetryLogTrace($"{nameof(Common.IpcChannelCreated)} = {Common.IpcChannelCreated}. {Logger.GetStackTrace(new StackTrace())}", SeverityLevel.Warning); return; } if (!MainForm.IsDisposed) { MainForm.NotifyIcon.Visible = false; MainForm.NotifyIcon.Visible = Setting.Values.ShowOriginalUI; } IntPtr h = (IntPtr)NativeMethods.FindWindow(null, Common.HELPER_FORM_TEXT); if (h.ToInt32() <= 0) { _ = Common.CreateProcessInInputDesktopSession( $"\"{Path.GetDirectoryName(Application.ExecutablePath)}\\{HelperProcessName}.exe\"", string.Empty, Common.GetInputDesktop(), 0); HasSwitchedMachineSinceLastCopy = true; // Common.CreateLowIntegrityProcess("\"" + Path.GetDirectoryName(Application.ExecutablePath) + "\\MouseWithoutBordersHelper.exe\"", string.Empty, 0, false, 0); var processes = Process.GetProcessesByName(HelperProcessName); if (processes?.Length == 0) { Logger.Log("Unable to start helper process."); Common.ShowToolTip("无法启动无界鼠标帮助模块,剪贴板共享功能失效!", 5000, ToolTipIcon.Error); } else { Logger.Log("Helper process started."); } } else { var processes = Process.GetProcessesByName(HelperProcessName); if (processes?.Length > 0) { Logger.Log("Helper process found running."); } else { Logger.Log("Invalid helper process found running."); Common.ShowToolTip("找不到无界鼠标帮助模块,剪贴板共享功能失效!", 5000, ToolTipIcon.Error); } } } internal static int SendMessageToHelper(int msg, IntPtr wparam, IntPtr lparam, bool wait = true, bool log = true) { int h = NativeMethods.FindWindow(null, Common.HELPER_FORM_TEXT); int rv = -1; if (h > 0) { rv = wait ? (int)NativeMethods.SendMessage((IntPtr)h, msg, wparam, lparam) : NativeMethods.PostMessage((IntPtr)h, msg, wparam, lparam) ? 1 : 0; } if (log) { Logger.LogDebug($"SendMessageToHelper: HelperWindow={h}, Return={rv}, msg={msg}, w={wparam.ToInt32()}, l={lparam.ToInt32()}, Post={!wait}"); } return rv; } internal static bool IsWindows8AndUp() { return (Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor >= 2) || Environment.OSVersion.Version.Major > 6; } internal static string GetMiniLog(IEnumerable optionControls) { string log = string.Empty; log += "=============================================================================================================================\r\n"; log += $"{Application.ProductName} version {Application.ProductVersion}\r\n"; log += $"{Setting.Values.Username}/{GetDebugInfo(MyKey)}\r\n"; log += $"{MachineName}/{MachineID}/{DesMachineID}\r\n"; log += $"Id: {Setting.Values.DeviceId}\r\n"; log += $"Matrix: {string.Join(",", MachineMatrix)}\r\n"; log += $"McPool: {Setting.Values.MachinePoolString}\r\n"; log += "\r\nOPTIONS:\r\n"; foreach (ControlCollection controlCollection in optionControls) { foreach (object c in controlCollection) { if (c is CheckBox checkBox) { log += $"({(checkBox.Checked ? 1 : 0)}) {checkBox.Text}\r\n"; continue; } if (c is RadioButton radioButton) { log += $"({(radioButton.Checked ? 1 : 0)}) {radioButton.Name}.[{radioButton.Text}]\r\n"; continue; } if (c is ComboBox comboBox) { log += $"{comboBox.Name} = {comboBox.Text}\r\n"; continue; } } } log += "\r\n"; SocketStuff sk = Sk; if (sk?.TcpSockets != null) { foreach (TcpSk tcp in sk.TcpSockets) { log += $"{Common.MachineName}{(tcp.IsClient ? "=>" : "<=")}{tcp.MachineName}({tcp.MachineId}):{tcp.Status}\r\n"; } } log += string.Format(CultureInfo.CurrentCulture, "Helper:{0}\r\n", SendMessageToHelper(0x400, IntPtr.Zero, IntPtr.Zero)); log += Setting.Values.LastPersonalizeLogonScr + "\r\n"; log += "Name2IP =\r\n" + Setting.Values.Name2IP + "\r\n"; log += "Last 10 trace messages:\r\n"; log += string.Join(Environment.NewLine, Logger.LogCounter.Select(item => $"({item.Value}): {item.Key}").Take(10)); log += "\r\n============================================================================================================================="; return log; } internal static bool GetUserName() { if (string.IsNullOrEmpty(Setting.Values.Username) && !Common.RunOnLogonDesktop) { if (Program.User.Contains("system", StringComparison.CurrentCultureIgnoreCase)) { _ = Common.ImpersonateLoggedOnUserAndDoSomething(() => { Setting.Values.Username = WindowsIdentity.GetCurrent(true).Name; }); } else { Setting.Values.Username = Program.User; } Logger.LogDebug("[Username] = " + Setting.Values.Username); } return !string.IsNullOrEmpty(Setting.Values.Username); } internal static void ShowOneWayModeMessage() { ToggleShowTopMostMessage( @" Due to Security Controls, a remote device cannot control a SAW device. Please use the keyboard and Mouse from the SAW device. (Press Esc to hide this message) ", string.Empty, 10); } internal static void ApplyCADSetting() { try { if (Setting.Values.DisableCAD) { RegistryKey k = Registry.LocalMachine.CreateSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System"); if (k != null) { k.SetValue("DisableCAD", 1, RegistryValueKind.DWord); k.Close(); } } else { RegistryKey k = Registry.LocalMachine.CreateSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System"); if (k != null) { k.SetValue("DisableCAD", 0, RegistryValueKind.DWord); k.Close(); } } } catch (Exception e) { Logger.Log(e); } } } } ================================================ FILE: PowerToys/src/modules/MouseWithoutBorders/App/Class/Common.MachineStuff.cs ================================================ // 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.Drawing; using System.Globalization; using System.Linq; using System.Threading; using System.Windows.Forms; using Microsoft.PowerToys.Telemetry; // // Machine setup/switching implementation. // // // 2008 created by Truong Do (ductdo). // 2009-... modified by Truong Do (TruongDo). // 2023- Included in PowerToys. // using MouseWithoutBorders.Class; using MouseWithoutBorders.Core; namespace MouseWithoutBorders { internal struct MachineInf { internal string Name; internal ID Id; internal long Time; } internal class MyRectangle { internal int Left; internal int Top; internal int Right; internal int Bottom; } internal partial class Common { private static readonly Lock McMatrixLock = new(); internal const byte MAX_MACHINE = 4; internal const byte MAX_SOCKET = MAX_MACHINE * 2; internal const long HEARTBEAT_TIMEOUT = 1500000; // 30 Mins private const int SKIP_PIXELS = 1; private const int JUMP_PIXELS = 2; #pragma warning disable SA1307 // Accessible fields should begin with upper-case letter internal static ID desMachineID; #pragma warning restore SA1307 internal static string DesMachineName = string.Empty; private static ID newDesMachineID; #pragma warning disable SA1307 // Accessible fields should begin with upper-case letter internal static ID newDesMachineIdEx; #pragma warning restore SA1307 private static ID dropMachineID; private static long lastJump = Common.GetTick(); private static MyRectangle desktopBounds = new(); private static MyRectangle primaryScreenBounds = new(); private static MachinePool _machinePool; internal static MachinePool MachinePool { get { _machinePool ??= new MachinePool(); return _machinePool; } } internal static MyRectangle PrimaryScreenBounds => Common.primaryScreenBounds; internal static MouseLocation SwitchLocation = new(); internal static ID NewDesMachineID { get => Common.newDesMachineID; set => Common.newDesMachineID = value; } internal static MyRectangle DesktopBounds => Common.desktopBounds; #if OLD_VERSION static bool MoveToMyNeighbourIfNeeded(int x, int y) { if (Math.Abs(x) > 10) LastX = x; if (Math.Abs(y) > 10) LastY = y; if (GetTick() - lastJump < 500 || desMachineID == IP.ALL) return false; if (desMachineID == machineID) { if (x < desktopBounds.Left + skipPixels) return MoveLeft(x, y, x - desktopBounds.Left, 0); } else { if (x < primaryScreenBounds.Left + skipPixels) { if (MoveLeft(x, y, x - primaryScreenBounds.Left, 0)) { return true; } else { if (desktopBounds.Left < primaryScreenBounds.Left) { RequestedX_Ex = primaryScreenBounds.Left; RequestedY_Ex = y; return true; } } } } if (desMachineID == machineID) { if (x > desktopBounds.Right - skipPixels) return MoveRight(x, y, x - desktopBounds.Right, 0); } else { if (x > primaryScreenBounds.Right - skipPixels) { if (MoveRight(x, y, x - primaryScreenBounds.Right, 0)) { return true; } else { if (desktopBounds.Right > primaryScreenBounds.Right) { RequestedX_Ex = primaryScreenBounds.Right; RequestedY_Ex = y; return true; } } } } if (desMachineID == machineID) { if (y < desktopBounds.Top + skipPixels) return MoveUp(x, y, 0, y - desktopBounds.Top); } else { if (y < primaryScreenBounds.Top + skipPixels) { if (MoveUp(x, y, 0, y - primaryScreenBounds.Top)) { return true; } else { if (desktopBounds.Top < primaryScreenBounds.Top) { RequestedX_Ex = x; RequestedY_Ex = primaryScreenBounds.Top; return true; } } } } if (desMachineID == machineID) { if (y > desktopBounds.Bottom - skipPixels) return MoveDown(x, y, 0, y - desktopBounds.Bottom); } else { if (y > primaryScreenBounds.Bottom - skipPixels) { if (MoveDown(x, y, 0, y - primaryScreenBounds.Bottom)) { return true; } else { if (desktopBounds.Bottom > primaryScreenBounds.Bottom) { RequestedX_Ex = x; RequestedY_Ex = primaryScreenBounds.Bottom; return true; } } } } return false; } #else private static Point ConvertToUniversalValue(Point p, MyRectangle r) { if (!p.IsEmpty) { p.X = (p.X - r.Left) * 65535 / (r.Right - r.Left); p.Y = (p.Y - r.Top) * 65535 / (r.Bottom - r.Top); } return p; } /* Let's say we have 3 machines A, B, and C. A is the controller machine. * (x, y) is the current Mouse position in pixel. * If Setting.Values.MoveMouseRelatively then (x, y) can be from any machine having the value bounded by desktopBounds (can be negative) * Else (x, y) is from the controller machine which is bounded by ONLY primaryScreenBounds (>=0); * * The return point is from 0 to 65535 which is then mapped to the desktop of the new controlled machine by the SendInput method. * Let's say user is switching from machine B to machine C: * If Setting.Values.MoveMouseRelatively the this method is called by B and the return point is calculated by B and sent back to A, A will use it to move Mouse to the right position when switching to C. * Else this method is called by A and the return point is calculated by A. * */ internal static Point MoveToMyNeighbourIfNeeded(int x, int y, ID desMachineID) { newDesMachineIdEx = desMachineID; if (Math.Abs(x) > 10) { LastX = x; } if (Math.Abs(y) > 10) { LastY = y; } if ((GetTick() - lastJump < 100) || desMachineID == ID.ALL) { return Point.Empty; } if (Setting.Values.BlockMouseAtCorners) { lock (SensitivePoints) { foreach (Point p in SensitivePoints) { if (Math.Abs(p.X - x) < 100 && Math.Abs(p.Y - y) < 100) { return Point.Empty; } } } } /* If Mouse is moving in the controller machine and this method is called by the controller machine. * Or if Mouse is moving in the controlled machine and this method is called by the controlled machine and Setting.Values.MoveMouseRelative. * */ if (desMachineID == MachineID) { if (x < desktopBounds.Left + SKIP_PIXELS) { return MoveLeft(x, y); } else if (x >= desktopBounds.Right - SKIP_PIXELS) { return MoveRight(x, y); } else if (y < desktopBounds.Top + SKIP_PIXELS) { return MoveUp(x, y); } else if (y >= desktopBounds.Bottom - SKIP_PIXELS) { return MoveDown(x, y); } } /* If Mouse is moving in the controlled machine and this method is called by the controller machine and !Setting.Values.MoveMouseRelative. * Mouse location is scaled from the primary screen bound of the controller machine regardless of how many monitors the controlled machine may have. * */ else { if (x < primaryScreenBounds.Left + SKIP_PIXELS) { return MoveLeft(x, y); } else if (x >= primaryScreenBounds.Right - SKIP_PIXELS) { return MoveRight(x, y); } else if (y < primaryScreenBounds.Top + SKIP_PIXELS) { return MoveUp(x, y); } else if (y >= primaryScreenBounds.Bottom - SKIP_PIXELS) { return MoveDown(x, y); } } return Point.Empty; } #endif [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Dotnet port with style preservation")] private static Point MoveRight(int x, int y) { string[] mc = LiveMachineMatrix; if (mc == null) { return Point.Empty; } bool oneRow = Setting.Values.MatrixOneRow; string currentMachine = NameFromID(desMachineID); if (currentMachine == null) { return Point.Empty; } ID newID; if (oneRow) { bool found = false; for (int i = 0; i < MAX_MACHINE; i++) { if (currentMachine.Trim().Equals(mc[i], StringComparison.OrdinalIgnoreCase)) { for (int j = i; j < MAX_MACHINE - 1; j++) { if (mc[j + 1] != null && mc[j + 1].Length > 0) { if ((newID = IdFromName(mc[j + 1])) > 0) { newDesMachineIdEx = newID; found = true; break; } } } if (!found && Setting.Values.MatrixCircle) { for (int j = 0; j < i; j++) { if (mc[j] != null && mc[j].Length > 0) { if ((newID = IdFromName(mc[j])) > 0) { newDesMachineIdEx = newID; break; } } } } break; } } } else { if (currentMachine.Trim().Equals(mc[0], StringComparison.OrdinalIgnoreCase) && (mc[1] != null) && (mc[1].Length > 0)) { if ((newID = IdFromName(mc[1])) > 0) { newDesMachineIdEx = newID; } } else if (currentMachine.Trim().Equals(mc[2], StringComparison.OrdinalIgnoreCase) && (mc[3] != null) && (mc[3].Length > 0)) { if ((newID = IdFromName(mc[3])) > 0) { newDesMachineIdEx = newID; } } else if (Setting.Values.MatrixCircle && currentMachine.Trim().Equals(mc[1], StringComparison.OrdinalIgnoreCase) && (mc[0] != null) && (mc[0].Length > 0)) { if ((newID = IdFromName(mc[0])) > 0) { newDesMachineIdEx = newID; } } else if (Setting.Values.MatrixCircle && currentMachine.Trim().Equals(mc[3], StringComparison.OrdinalIgnoreCase) && (mc[2] != null) && (mc[2].Length > 0)) { if ((newID = IdFromName(mc[2])) > 0) { newDesMachineIdEx = newID; } } } // THIS LOGIC IS THE SAME FOR Move*(int x, int y) METHODS. if (newDesMachineIdEx != desMachineID) { Logger.LogDebug("Move Right"); if (!Setting.Values.MoveMouseRelatively) { if (newDesMachineIdEx == MachineID) { /* Switching back to the controller machine, we need to scale up to the desktopBounds from primaryScreenBounds (sine !Setting.Values.MoveMouseRelatively). * primaryScreenBounds => 65535 => desktopBounds, so that the Mouse position is mapped to the right position when the controller machine has multiple monitors. * */ return ConvertToUniversalValue(new Point(primaryScreenBounds.Left + JUMP_PIXELS, y), primaryScreenBounds); } else { if (desMachineID == MachineID) { /* Switching FROM the controller machine, since Mouse was not bounded/locked to the primary screen, * Mouse position can just be mapped from desktopBounds to desktopBounds * desktopBounds => 65535 => desktopBounds. * */ return ConvertToUniversalValue(new Point(desktopBounds.Left + JUMP_PIXELS, y), desktopBounds); } else { /* Switching between two machines where non of them is the controller machine. * Since the current Mouse position is "mapped" from the primary monitor of the controller machine, * new Mouse position for the new controlled machine needs to be calculated from this as well. * primaryScreenBounds => 65535 => desktopBounds * */ return ConvertToUniversalValue(new Point(primaryScreenBounds.Left + JUMP_PIXELS, y), primaryScreenBounds); } } } else { /* In the case where Mouse is moved relatively, Mouse position is simply mapped from desktopBounds to desktopBounds. * desktopBounds => 65535 => desktopBounds. * */ return ConvertToUniversalValue(new Point(desktopBounds.Left + JUMP_PIXELS, y), desktopBounds); } } return Point.Empty; } [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Dotnet port with style preservation")] private static Point MoveLeft(int x, int y) { string[] mc = LiveMachineMatrix; if (mc == null) { return Point.Empty; } bool oneRow = Setting.Values.MatrixOneRow; string currentMachine = NameFromID(desMachineID); if (currentMachine == null) { return Point.Empty; } ID newID; if (oneRow) { bool found = false; for (int i = MAX_MACHINE - 1; i >= 0; i--) { if (currentMachine.Trim().Equals(mc[i], StringComparison.OrdinalIgnoreCase)) { for (int j = i; j > 0; j--) { if (mc[j - 1] != null && mc[j - 1].Length > 0) { if ((newID = IdFromName(mc[j - 1])) != ID.NONE) { newDesMachineIdEx = newID; found = true; break; } } } if (!found && Setting.Values.MatrixCircle) { for (int j = MAX_MACHINE - 1; j > i; j--) { if (mc[j] != null && mc[j].Length > 0) { if ((newID = IdFromName(mc[j])) != ID.NONE) { newDesMachineIdEx = newID; break; } } } } break; } } } else { if (currentMachine.Trim().Equals(mc[1], StringComparison.OrdinalIgnoreCase) && (mc[0] != null) && (mc[0].Length > 0)) { if ((newID = IdFromName(mc[0])) != ID.NONE) { newDesMachineIdEx = newID; } } else if (currentMachine.Trim().Equals(mc[3], StringComparison.OrdinalIgnoreCase) && (mc[2] != null) && (mc[2].Length > 0)) { if ((newID = IdFromName(mc[2])) != ID.NONE) { newDesMachineIdEx = newID; } } else if (Setting.Values.MatrixCircle && currentMachine.Trim().Equals(mc[0], StringComparison.OrdinalIgnoreCase) && (mc[1] != null) && (mc[1].Length > 0)) { if ((newID = IdFromName(mc[1])) != ID.NONE) { newDesMachineIdEx = newID; } } else if (Setting.Values.MatrixCircle && currentMachine.Trim().Equals(mc[2], StringComparison.OrdinalIgnoreCase) && (mc[3] != null) && (mc[3].Length > 0)) { if ((newID = IdFromName(mc[3])) != ID.NONE) { newDesMachineIdEx = newID; } } } if (newDesMachineIdEx != desMachineID) { Logger.LogDebug("Move Left"); return !Setting.Values.MoveMouseRelatively ? newDesMachineIdEx == MachineID ? ConvertToUniversalValue(new Point(primaryScreenBounds.Right - JUMP_PIXELS, y), primaryScreenBounds) : desMachineID == MachineID ? ConvertToUniversalValue(new Point(desktopBounds.Right - JUMP_PIXELS, y), desktopBounds) : ConvertToUniversalValue(new Point(primaryScreenBounds.Right - JUMP_PIXELS, y), primaryScreenBounds) : ConvertToUniversalValue(new Point(desktopBounds.Right - JUMP_PIXELS, y), desktopBounds); } return Point.Empty; } private static Point MoveUp(int x, int y) { if (Setting.Values.MatrixOneRow) { return Point.Empty; } string[] mc = LiveMachineMatrix; if (mc == null) { return Point.Empty; } string currentMachine = NameFromID(desMachineID); if (currentMachine == null) { return Point.Empty; } ID newID; if (currentMachine.Trim().Equals(mc[2], StringComparison.OrdinalIgnoreCase) && (mc[0] != null) && (mc[0].Length > 0)) { if ((newID = IdFromName(mc[0])) != ID.NONE) { newDesMachineIdEx = newID; } } else if (currentMachine.Trim().Equals(mc[3], StringComparison.OrdinalIgnoreCase) && (mc[1] != null) && (mc[1].Length > 0)) { if ((newID = IdFromName(mc[1])) != ID.NONE) { newDesMachineIdEx = newID; } } else if (Setting.Values.MatrixCircle && currentMachine.Trim().Equals(mc[0], StringComparison.OrdinalIgnoreCase) && (mc[2] != null) && (mc[2].Length > 0)) { if ((newID = IdFromName(mc[2])) != ID.NONE) { newDesMachineIdEx = newID; } } else if (Setting.Values.MatrixCircle && currentMachine.Trim().Equals(mc[1], StringComparison.OrdinalIgnoreCase) && (mc[3] != null) && (mc[3].Length > 0)) { if ((newID = IdFromName(mc[3])) != ID.NONE) { newDesMachineIdEx = newID; } } if (newDesMachineIdEx != desMachineID) { Logger.LogDebug("Move Up"); return !Setting.Values.MoveMouseRelatively ? newDesMachineIdEx == MachineID ? ConvertToUniversalValue(new Point(x, primaryScreenBounds.Bottom - JUMP_PIXELS), primaryScreenBounds) : desMachineID == MachineID ? ConvertToUniversalValue(new Point(x, desktopBounds.Bottom - JUMP_PIXELS), desktopBounds) : ConvertToUniversalValue(new Point(x, primaryScreenBounds.Bottom - JUMP_PIXELS), primaryScreenBounds) : ConvertToUniversalValue(new Point(x, desktopBounds.Bottom - JUMP_PIXELS), desktopBounds); } return Point.Empty; } private static Point MoveDown(int x, int y) { if (Setting.Values.MatrixOneRow) { return Point.Empty; } string[] mc = LiveMachineMatrix; if (mc == null) { return Point.Empty; } string currentMachine = NameFromID(desMachineID); if (currentMachine == null) { return Point.Empty; } ID newID; if (currentMachine.Trim().Equals(mc[0], StringComparison.OrdinalIgnoreCase) && (mc[2] != null) && (mc[2].Length > 0)) { if ((newID = IdFromName(mc[2])) != ID.NONE) { newDesMachineIdEx = newID; } } else if (currentMachine.Trim().Equals(mc[1], StringComparison.OrdinalIgnoreCase) && (mc[3] != null) && (mc[3].Length > 0)) { if ((newID = IdFromName(mc[3])) != ID.NONE) { newDesMachineIdEx = newID; } } if (Setting.Values.MatrixCircle && currentMachine.Trim().Equals(mc[2], StringComparison.OrdinalIgnoreCase) && (mc[0] != null) && (mc[0].Length > 0)) { if ((newID = IdFromName(mc[0])) != ID.NONE) { newDesMachineIdEx = newID; } } else if (Setting.Values.MatrixCircle && currentMachine.Trim().Equals(mc[3], StringComparison.OrdinalIgnoreCase) && (mc[1] != null) && (mc[1].Length > 0)) { if ((newID = IdFromName(mc[1])) != ID.NONE) { newDesMachineIdEx = newID; } } if (newDesMachineIdEx != desMachineID) { Logger.LogDebug("Move Down"); return !Setting.Values.MoveMouseRelatively ? newDesMachineIdEx == MachineID ? ConvertToUniversalValue(new Point(x, primaryScreenBounds.Top + JUMP_PIXELS), primaryScreenBounds) : desMachineID == MachineID ? ConvertToUniversalValue(new Point(x, desktopBounds.Top + JUMP_PIXELS), desktopBounds) : ConvertToUniversalValue(new Point(x, primaryScreenBounds.Top + JUMP_PIXELS), primaryScreenBounds) : ConvertToUniversalValue(new Point(x, desktopBounds.Top + JUMP_PIXELS), desktopBounds); } return Point.Empty; } internal static bool RemoveDeadMachines(ID ip) { bool rv = false; // Here we are removing a dead machine by IP. foreach (MachineInf inf in Common.MachinePool.ListAllMachines()) { if (inf.Id == ip) { if (MachinePool.SetMachineDisconnected(inf.Name)) { rv = true; } Logger.LogDebug("<><><><><>>><><><<><><><><><><><><><><>><><><><><><><><><><><" + inf.Name); } } return rv; } internal static void RemoveDeadMachines() { // list of live/dead machines is now automatically up-to-date // if it changed we need to update the UI. // for now assume it changed. // Common.MachinePool.ResetIPAddressesForDeadMachines(); // DoSomethingInUIThread(UpdateMenu); Common.UpdateMachinePoolStringSetting(); // Make sure MachinePool still holds this machine. if (Common.MachinePool.LearnMachine(MachineName)) { _ = Common.MachinePool.TryUpdateMachineID(MachineName, MachineID, false); } } internal static string AddToMachinePool(DATA package) { // Log("********** AddToMachinePool called: " + package.src.ToString(CultureInfo.InvariantCulture)); // There should be no duplicates in machine pool. string name = package.MachineName; // a few things happening here: // 1) find a matching machine (by name) // 2) update its ID and time // 3) logging // 4) updating some variables - desMachineID/newDesMachineID // 5) return the matched name (trimmed) - only in the event of a match if (Common.MachinePool.TryFindMachineByName(name, out MachineInf machineInfo)) { _ = Common.MachinePool.TryUpdateMachineID(machineInfo.Name, machineInfo.Id, true); _ = Common.MachinePool.TryUpdateMachineID(machineInfo.Name, package.Src, true); if (machineInfo.Name.Equals(DesMachineName, StringComparison.OrdinalIgnoreCase)) { Logger.LogDebug("AddToMachinePool: Des ID updated: " + DesMachineID.ToString() + "/" + package.Src.ToString()); newDesMachineID = desMachineID = package.Src; } return machineInfo.Name; } else { if (Common.MachinePool.LearnMachine(name)) { _ = Common.MachinePool.TryUpdateMachineID(name, package.Src, true); } else { Logger.LogDebug("AddToMachinePool: could not add a new machine: " + name); return "The 5th machine"; } } // if (machineCount != saved) { // DoSomethingInUIThread(UpdateMenu); Common.UpdateMachinePoolStringSetting(); } // NOTE(yuyoyuppe): automatically active "bidirectional" control between the machines. string[] st = new string[Common.MAX_MACHINE]; Array.Fill(st, string.Empty); var machines = Common.MachinePool.ListAllMachines(); for (int i = 0; i < machines.Count; ++i) { if (machines[i].Id != ID.NONE && machines[i].Id != ID.ALL) { st[i] = machines[i].Name; } } Common.MachineMatrix = st; Common.ReopenSockets(true); Common.SendMachineMatrix(); Logger.LogDebug("Machine added: " + name + "/" + package.Src.ToString()); UpdateClientSockets("AddToMachinePool"); return name; } internal static void UpdateClientSockets(string logHeader) { Logger.LogDebug("UpdateClientSockets: " + logHeader); Sk?.UpdateTCPClients(); } private static SettingsForm settings; internal static SettingsForm Settings { get => Common.settings; set => Common.settings = value; } internal static void ShowSetupForm(bool reopenSockets = false) { Logger.LogDebug("========== BEGIN THE SETUP EXPERIENCE ==========", true); Setting.Values.MyKey = Common.MyKey = Common.CreateRandomKey(); Common.GeneratedKey = true; if (Process.GetCurrentProcess().SessionId != NativeMethods.WTSGetActiveConsoleSessionId()) { Logger.Log("Not physical console session."); _ = MessageBox.Show( "请在物理终端会话中运行.\r\n本软件不支持在远程连接和虚拟机中使用.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Stop); return; } if (settings == null) { settings = new SettingsForm(); settings.Show(); } else { settings.Close(); Common.MMSleep(0.3); settings = new SettingsForm(); settings.Show(); } if (reopenSockets) { Common.ReopenSockets(true); } } internal static void CloseSetupForm() { if (settings != null) { settings.Close(); settings = null; } } internal static void ShowMachineMatrix() { if (!Setting.Values.ShowOriginalUI) { return; } if (Process.GetCurrentProcess().SessionId != NativeMethods.WTSGetActiveConsoleSessionId()) { Common.ShowToolTip("本软件不支持在远程连接和虚拟机中使用.", 5000); } #if NEW_SETTINGS_FORM Common.ShowSetupForm(); #else if (Setting.Values.FirstRun && !Common.AtLeastOneSocketConnected()) { Common.ShowSetupForm(); } else { PowerToysTelemetry.Log.WriteEvent(new MouseWithoutBorders.Telemetry.MouseWithoutBordersOldUIOpenedEvent()); if (MatrixForm == null) { MatrixForm = new FrmMatrix(); MatrixForm.Show(); if (MainForm != null) { MainForm.NotifyIcon.Visible = false; MainForm.NotifyIcon.Visible = Setting.Values.ShowOriginalUI; } } else { MatrixForm.WindowState = FormWindowState.Normal; MatrixForm.Activate(); } } #endif } private static string[] mcMatrix; internal static string[] MachineMatrix { get { lock (McMatrixLock) { if (mcMatrix == null) { string s = Setting.Values.MachineMatrixString; if (!string.IsNullOrEmpty(s)) { mcMatrix = s.Split(new char[] { ',' }); if (mcMatrix == null || mcMatrix.Length != MAX_MACHINE) { mcMatrix = new string[MAX_MACHINE] { string.Empty, string.Empty, string.Empty, string.Empty }; } } else { mcMatrix = new string[MAX_MACHINE] { string.Empty, string.Empty, string.Empty, string.Empty }; } } return mcMatrix; } } set { lock (McMatrixLock) { if (value == null) { mcMatrix = null; // Force read from registry next time. return; } else { Setting.Values.MachineMatrixString = string.Join(",", mcMatrix = value); } } DoSomethingInUIThread(() => { MainForm.ChangeIcon(-1); MainForm.UpdateNotifyIcon(); }); } } private static string[] LiveMachineMatrix { get { bool twoRow = !Setting.Values.MatrixOneRow; string[] connectedMachines = twoRow ? MachineMatrix : MachineMatrix.Select(m => IsConnectedTo(IdFromName(m)) ? m : string.Empty).ToArray(); Logger.LogDebug($"Matrix: {string.Join(",", MachineMatrix)}, Connected: {string.Join(",", connectedMachines)}"); return connectedMachines; } } internal static void UpdateMachinePoolStringSetting() { Setting.Values.MachinePoolString = Common.MachinePool.SerializedAsString(); } internal static void SendMachineMatrix() { if (MachineMatrix == null) { return; } DATA package = new(); for (int i = 0; i < MachineMatrix.Length; i++) { package.MachineName = MachineMatrix[i]; package.Type = PackageType.Matrix | (Setting.Values.MatrixCircle ? PackageType.MatrixSwapFlag : 0) | (Setting.Values.MatrixOneRow ? 0 : PackageType.MatrixTwoRowFlag); package.Src = (ID)(i + 1); package.Des = ID.ALL; SkSend(package, null, false); Logger.LogDebug($"matrixIncludedMachine sent: [{i + 1}]:[{MachineMatrix[i]}]"); } } internal static void UpdateMachineMatrix(DATA package) { uint i = (uint)package.Src; string matrixIncludedMachine = package.MachineName; if (i is > 0 and <= MAX_MACHINE) { Logger.LogDebug($"matrixIncludedMachine: [{i}]:[{matrixIncludedMachine}]"); MachineMatrix[i - 1] = matrixIncludedMachine; if (i == MAX_MACHINE) { Setting.Values.MatrixCircle = (package.Type & PackageType.MatrixSwapFlag) == PackageType.MatrixSwapFlag; Setting.Values.MatrixOneRow = !((package.Type & PackageType.MatrixTwoRowFlag) == PackageType.MatrixTwoRowFlag); MachineMatrix = MachineMatrix; // Save Common.ReopenSocketDueToReadError = true; UpdateClientSockets("UpdateMachineMatrix"); Setting.Values.Changed = true; } } else { Logger.LogDebug("Invalid machine Matrix package!"); } } internal static void SwitchToMachine(string name) { ID id = Common.MachinePool.ResolveID(name); if (id != ID.NONE) { // Ask current machine to hide the Mouse cursor if (desMachineID != MachineID) { SendPackage(desMachineID, PackageType.HideMouse); } NewDesMachineID = DesMachineID = id; SwitchLocation.X = XY_BY_PIXEL + primaryScreenBounds.Left + ((primaryScreenBounds.Right - primaryScreenBounds.Left) / 2); SwitchLocation.Y = XY_BY_PIXEL + primaryScreenBounds.Top + ((primaryScreenBounds.Bottom - primaryScreenBounds.Top) / 2); SwitchLocation.ResetCount(); Common.UpdateMultipleModeIconAndMenu(); HideMouseCursor(false); _ = EvSwitch.Set(); } } internal static void SwitchToMultipleMode(bool multipleMode, bool centerScreen) { if (multipleMode) { PowerToysTelemetry.Log.WriteEvent(new MouseWithoutBorders.Telemetry.MouseWithoutBordersMultipleModeEvent()); NewDesMachineID = DesMachineID = ID.ALL; } else { NewDesMachineID = DesMachineID = MachineID; } if (centerScreen) { MoveMouseToCenter(); } ReleaseAllKeys(); Common.UpdateMultipleModeIconAndMenu(); } internal static bool CheckSecondInstance(bool sendMessage = false) { int h; if ((h = NativeMethods.FindWindow(null, Setting.Values.MyID)) > 0) { return true; } return false; } private static EventWaitHandle oneInstanceCheck; internal static void AssertOneInstancePerDesktopSession() { string eventName = $"Global\\{Application.ProductName}-{FrmAbout.AssemblyVersion}-{GetMyDesktop()}-{CurrentProcess.SessionId}"; oneInstanceCheck = new EventWaitHandle(false, EventResetMode.ManualReset, eventName, out bool created); if (!created) { Logger.TelemetryLogTrace($"Second instance found: {eventName}.", SeverityLevel.Warning, true); CurrentProcess.KillProcess(true); } } internal static ID IdFromName(string name) { return Common.MachinePool.ResolveID(name); } internal static string NameFromID(ID id) { foreach (MachineInf inf in Common.MachinePool.TryFindMachineByID(id)) { if (!string.IsNullOrEmpty(inf.Name)) { return inf.Name; } } return null; } internal static bool InMachineMatrix(string name) { if (MachineMatrix == null || string.IsNullOrWhiteSpace(name)) { return false; } foreach (string st in MachineMatrix) { if (!string.IsNullOrWhiteSpace(st) && st.Trim().Equals(name.Trim(), StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } internal static void ClearComputerMatrix() { Common.MachineMatrix = new string[Common.MAX_MACHINE] { Common.MachineName.Trim(), string.Empty, string.Empty, string.Empty }; Common.MachinePool.Initialize(new string[] { Common.MachineName }); Common.UpdateMachinePoolStringSetting(); } } } ================================================ FILE: PowerToys/src/modules/MouseWithoutBorders/App/Class/Common.Service.cs ================================================ // 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.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.ServiceProcess; using System.Threading.Tasks; using System.Windows.Forms; // // Service control code. // // // 2008 created by Truong Do (ductdo). // 2009-... modified by Truong Do (TruongDo). // 2023- Included in PowerToys. // using MouseWithoutBorders.Class; using MouseWithoutBorders.Core; [module: SuppressMessage("Microsoft.Globalization", "CA1300:SpecifyMessageBoxOptions", Scope = "member", Target = "MouseWithoutBorders.Common.#StartMouseWithoutBordersService()", Justification = "Dotnet port with style preservation")] namespace MouseWithoutBorders { internal partial class Common { private static bool shownErrMessage; private static DateTime lastStartServiceTime = DateTime.UtcNow; internal static void StartMouseWithoutBordersService(string desktopToRunMouseWithoutBordersOn = null, string startTag1 = "byapp", string startTag2 = null) { // NOTE(@yuyoyuppe): the new flow assumes we run both mwb processes directly from the svc. if (Common.RunWithNoAdminRight || true) { return; } Logger.Log($"{nameof(StartMouseWithoutBordersService)}: {Logger.GetStackTrace(new StackTrace())}."); Task task = Task.Run(() => { Process[] ps = Process.GetProcessesByName("MouseWithoutBordersSvc"); if (ps.Length != 0) { if (DateTime.UtcNow - lastStartServiceTime < TimeSpan.FromSeconds(5)) { Logger.Log($"{nameof(StartMouseWithoutBordersService)}: Aborted."); return; } foreach (Process pp in ps) { Logger.Log(string.Format(CultureInfo.InvariantCulture, "Killing process MouseWithoutBordersSvc {0}.", pp.Id)); pp.KillProcess(); } } lastStartServiceTime = DateTime.UtcNow; ServiceController service = new("MouseWithoutBordersSvc"); try { Logger.Log("Starting " + service.ServiceName); } catch (Exception) { if (!shownErrMessage) { shownErrMessage = true; _ = MessageBox.Show( Application.ProductName + " 尚未安装,请先运行 Setup.exe!", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error); } return; } try { int c = 0; while (service.Status != ServiceControllerStatus.Stopped && c++ < 5) { Thread.Sleep(1000); service = new ServiceController("MouseWithoutBordersSvc"); } if (string.IsNullOrWhiteSpace(desktopToRunMouseWithoutBordersOn)) { startTag2 ??= Process.GetCurrentProcess().SessionId.ToString(CultureInfo.InvariantCulture); service.Start(new string[] { startTag1, startTag2 }); } else { service.Start(new string[] { desktopToRunMouseWithoutBordersOn }); } } catch (Exception e) { Logger.Log(e); // ERROR_SERVICE_ALREADY_RUNNING if (!(shownErrMessage || ((e?.InnerException as Win32Exception)?.NativeErrorCode == 1056))) { shownErrMessage = true; _ = MessageBox.Show( "无法启动服务 " + service.ServiceName + ": " + e.Message, Common.BinaryName, MessageBoxButtons.OK, MessageBoxIcon.Error); } return; } }); // Wait for the task while not blocking the UI thread. do { MMSleep(1); if (task.IsCanceled || task.IsCompleted || task.IsFaulted) { break; } } while (true); } internal static void StartServiceAndSendLogoffSignal() { try { Process[] p = Process.GetProcessesByName("winlogon"); Process me = Process.GetCurrentProcess(); string myWinlogon = p?.FirstOrDefault(item => item.SessionId == me.SessionId)?.Id.ToString(CultureInfo.InvariantCulture) ?? null; if (string.IsNullOrWhiteSpace(myWinlogon)) { StartMouseWithoutBordersService(null, "logoff"); } else { StartMouseWithoutBordersService(null, "logoff", myWinlogon); } } catch (Exception e) { Logger.Log($"{nameof(StartServiceAndSendLogoffSignal)}: {e.Message}"); } } } } ================================================ FILE: PowerToys/src/modules/MouseWithoutBorders/App/Class/EasyMouseOption.cs ================================================ // Copyright (c) Microsoft Corporation // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. namespace MouseWithoutBorders.Class { internal enum EasyMouseOption : int { 关闭 = 0, 开启 = 1, Ctrl = 2, Shift = 3, } } ================================================ FILE: PowerToys/src/modules/MouseWithoutBorders/App/Class/Extensions.cs ================================================ // 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.ComponentModel; using System.Diagnostics; using System.IO; using MouseWithoutBorders.Core; // Disable the warning to preserve original code #pragma warning disable CA1716 namespace MouseWithoutBorders.Class #pragma warning restore CA1716 { internal static class Extensions { internal static int ReadEx(this Stream st, byte[] buf, int bufIndex, int length) { int bytesReceived = st.Read(buf, bufIndex, length); int receivedCount = bytesReceived; while (receivedCount != 0 && bytesReceived < length) { bytesReceived += receivedCount = st.Read(buf, bufIndex + bytesReceived, length - bytesReceived); } return bytesReceived; } internal static void KillProcess(this Process process, bool keepTrying = false) { string processName = process.ProcessName; int processId = process.Id; do { try { process.Kill(); break; } catch (Win32Exception e) { string log = $"The process {processName} (PID={processId}) could not be terminated, error: {e.Message}"; string logcn = $"进程 {processName} (PID={processId}) 无法被终止,错误: {e.Message}"; Logger.TelemetryLogTrace(log, SeverityLevel.Error); Common.ShowToolTip(logcn, 5000); if (!keepTrying) { Thread.Sleep(1000); break; } } } while (true); } } } ================================================ FILE: PowerToys/src/modules/MouseWithoutBorders/App/Class/IClipboardHelper.cs ================================================ // 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; using System.Collections.Specialized; using System.Diagnostics; using System.Drawing; using System.IO; using System.IO.Pipes; using System.Reflection; using System.Runtime.InteropServices; using System.Security.AccessControl; using System.Security.Principal; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using Microsoft.VisualStudio.Threading; using Newtonsoft.Json; using StreamJsonRpc; #if !MM_HELPER using MouseWithoutBorders.Class; using MouseWithoutBorders.Core; #endif using SystemClipboard = System.Windows.Forms.Clipboard; #if !MM_HELPER using Thread = MouseWithoutBorders.Core.Thread; #endif namespace MouseWithoutBorders { [JsonObject(MemberSerialization.OptIn)] public struct ByteArrayOrString { private enum ValueType { ByteArray, String, } [JsonProperty] private ValueType _type; private byte[] _byteArrayValue; private string _stringValue; public ByteArrayOrString(byte[] byteArray) { _type = ValueType.ByteArray; _byteArrayValue = byteArray; _stringValue = null; } public ByteArrayOrString(string str) { _type = ValueType.String; _byteArrayValue = null; _stringValue = str; } public bool IsByteArray => _type == ValueType.ByteArray; public bool IsString => _type == ValueType.String; [JsonProperty("byteArray")] public byte[] ByteArray { get => _byteArrayValue; set { _byteArrayValue = value; _type = ValueType.ByteArray; } } [JsonProperty("string")] public string TheString { get => _stringValue; set { _stringValue = value; _type = ValueType.String; } } public bool ShouldSerializeByteArray() => IsByteArray; public bool ShouldSerializeString() => IsString; public byte[] GetByteArray() { if (IsByteArray) { return _byteArrayValue; } throw new InvalidOperationException("The value is not a byte array."); } public string GetString() { if (IsString) { return _stringValue; } throw new InvalidOperationException("The value is not a string."); } public static implicit operator ByteArrayOrString(byte[] byteArray) => new ByteArrayOrString(byteArray); public static implicit operator ByteArrayOrString(string str) => new ByteArrayOrString(str); } public interface IClipboardHelper { void SendLog(string log); void SendDragFile(string fileName); void SendClipboardData(ByteArrayOrString data, bool isFilePath); } #if !MM_HELPER public class ClipboardHelper : IClipboardHelper { public void SendLog(string log) { Logger.LogDebug("FROM HELPER: " + log); if (!string.IsNullOrEmpty(log)) { if (log.StartsWith("Screen capture ended", StringComparison.InvariantCulture)) { /* TODO: Telemetry for screen capture. */ if (Setting.Values.FirstCtrlShiftS) { Setting.Values.FirstCtrlShiftS = false; Common.ShowToolTip("区块截屏被触发,您可以在设置中调整快捷键.", 10000); } } else if (log.StartsWith("Trace:", StringComparison.InvariantCulture)) { Logger.TelemetryLogTrace(log, SeverityLevel.Information); } } } public void SendDragFile(string fileName) { Common.DragDropStep05Ex(fileName); } public void SendClipboardData(ByteArrayOrString data, bool isFilePath) { _ = Common.CheckClipboardEx(data, isFilePath); } } #endif internal sealed class IpcChannel where T : new() { public static T StartIpcServer(string pipeName, CancellationToken cancellationToken) { SecurityIdentifier securityIdentifier = new SecurityIdentifier( WellKnownSidType.AuthenticatedUserSid, null); PipeSecurity pipeSecurity = new PipeSecurity(); pipeSecurity.AddAccessRule(new PipeAccessRule( securityIdentifier, PipeAccessRights.ReadWrite | PipeAccessRights.CreateNewInstance, AccessControlType.Allow)); _ = Task.Factory.StartNew( async () => { try { while (!cancellationToken.IsCancellationRequested) { using (var serverChannel = NamedPipeServerStreamAcl.Create(pipeName, PipeDirection.InOut, NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Byte, PipeOptions.Asynchronous, 0, 0, pipeSecurity)) { await serverChannel.WaitForConnectionAsync(); var taskRpc = JsonRpc.Attach(serverChannel, new T()); await taskRpc.Completion; } } } catch (OperationCanceledException) { } catch (Exception e) { #if MM_HELPER _ = e; #else Logger.Log(e); #endif } }, cancellationToken, TaskCreationOptions.None, TaskScheduler.Default); return default(T); } } internal sealed class IpcHelper { private const string ChannelName = "MouseWithoutBorders"; private const string RemoteObjectName = "ClipboardHelper"; #if !MM_HELPER private static void CleanupStream() { if (_serverTaskCancellationSource != null) { _serverTaskCancellationSource.Cancel(); _serverTaskCancellationSource.Dispose(); _serverTaskCancellationSource = null; } } private static CancellationTokenSource _serverTaskCancellationSource; internal static void CreateIpcServer(bool cleanup) { try { if (cleanup) { CleanupStream(); return; } _serverTaskCancellationSource = new CancellationTokenSource(); CancellationToken cancellationToken = _serverTaskCancellationSource.Token; IpcChannel.StartIpcServer(ChannelName + "/" + RemoteObjectName, cancellationToken); Common.IpcChannelCreated = true; } catch (Exception e) { Common.IpcChannelCreated = false; Common.ShowToolTip("Error setting up clipboard sharing, clipboard sharing will not work!", 5000, ToolTipIcon.Error); Logger.Log(e); } } #else internal static IClipboardHelper CreateIpcClient() { try { var stream = new NamedPipeClientStream(".", ChannelName + "/" + RemoteObjectName, PipeDirection.InOut, PipeOptions.Asynchronous); stream.Connect(); return JsonRpc.Attach(stream); } catch (Exception e) { EventLogger.LogEvent(e.Message, EventLogEntryType.Error); } return null; } #endif } internal static class EventLogger { #if MM_HELPER private const string EventSourceName = "MouseWithoutBordersHelper"; #else private const string EventSourceName = "MouseWithoutBorders"; #endif internal static void LogEvent(string message, EventLogEntryType logType = EventLogEntryType.Information) { try { if (!EventLog.SourceExists(EventSourceName)) { EventLog.CreateEventSource(EventSourceName, "Application"); } EventLog.WriteEntry(EventSourceName, message, logType); } catch (Exception e) { Debug.WriteLine(message + ": " + e.Message); } } } #if MM_HELPER internal static class ClipboardMMHelper { internal static IntPtr NextClipboardViewer = IntPtr.Zero; private static FormHelper helperForm; private static bool addClipboardFormatListenerResult; private static void Log(string log) { helperForm.SendLog(log); } private static void Log(Exception e) { Log($"Trace: {e}"); } internal static void HookClipboard(FormHelper f) { helperForm = f; try { addClipboardFormatListenerResult = NativeMethods.AddClipboardFormatListener(f.Handle); int err = addClipboardFormatListenerResult ? 0 : Marshal.GetLastWin32Error(); if (err != 0) { Log($"Trace: {nameof(NativeMethods.AddClipboardFormatListener)}: GetLastError = {err}"); } } catch (EntryPointNotFoundException e) { Log($"{nameof(NativeMethods.AddClipboardFormatListener)} is unavailable in this version of Windows."); Log(e); } catch (Exception e) { Log(e); } // Fallback if (!addClipboardFormatListenerResult) { NextClipboardViewer = NativeMethods.SetClipboardViewer(f.Handle); int err = NextClipboardViewer == IntPtr.Zero ? Marshal.GetLastWin32Error() : 0; if (err != 0) { Log($"Trace: {nameof(NativeMethods.SetClipboardViewer)}: GetLastError = {err}"); } } Log($"Trace: Clipboard monitor method {(addClipboardFormatListenerResult ? nameof(NativeMethods.AddClipboardFormatListener) : NextClipboardViewer != IntPtr.Zero ? nameof(NativeMethods.SetClipboardViewer) : "(none)")} is used."); } internal static void UnhookClipboard() { if (addClipboardFormatListenerResult) { addClipboardFormatListenerResult = false; _ = NativeMethods.RemoveClipboardFormatListener(helperForm.Handle); } else { _ = NativeMethods.ChangeClipboardChain(helperForm.Handle, NextClipboardViewer); NextClipboardViewer = IntPtr.Zero; } } private static void ReHookClipboard() { UnhookClipboard(); HookClipboard(helperForm); } internal static bool UpdateNextClipboardViewer(Message m) { if (m.WParam == NextClipboardViewer) { NextClipboardViewer = m.LParam; return true; } return false; } internal static void PassMessageToTheNextViewer(Message m) { if (NextClipboardViewer != IntPtr.Zero && NextClipboardViewer != helperForm.Handle) { _ = NativeMethods.SendMessage(NextClipboardViewer, m.Msg, m.WParam, m.LParam); } } public static bool ContainsFileDropList() { bool rv = false; try { rv = Common.Retry(nameof(SystemClipboard.ContainsFileDropList), () => { return SystemClipboard.ContainsFileDropList(); }, (log) => Log(log)); } catch (ExternalException e) { Log(e); ReHookClipboard(); } catch (ThreadStateException e) { Log(e); ReHookClipboard(); } return rv; } public static bool ContainsImage() { bool rv = false; try { rv = Common.Retry(nameof(SystemClipboard.ContainsImage), () => { return SystemClipboard.ContainsImage(); }, (log) => Log(log)); } catch (ExternalException e) { Log(e); ReHookClipboard(); } catch (ThreadStateException e) { Log(e); ReHookClipboard(); } return rv; } public static bool ContainsText() { bool rv = false; try { rv = Common.Retry(nameof(SystemClipboard.ContainsText), () => { return SystemClipboard.ContainsText(); }, (log) => Log(log)); } catch (ExternalException e) { Log(e); ReHookClipboard(); } catch (ThreadStateException e) { Log(e); ReHookClipboard(); } return rv; } public static StringCollection GetFileDropList() { StringCollection rv = null; try { rv = Common.Retry(nameof(SystemClipboard.GetFileDropList), () => { return SystemClipboard.GetFileDropList(); }, (log) => Log(log)); } catch (ExternalException e) { Log(e); ReHookClipboard(); } catch (ThreadStateException e) { Log(e); ReHookClipboard(); } return rv; } public static Image GetImage() { Image rv = null; try { rv = Common.Retry(nameof(SystemClipboard.GetImage), () => { return SystemClipboard.GetImage(); }, (log) => Log(log)); } catch (ExternalException e) { Log(e); ReHookClipboard(); } catch (ThreadStateException e) { Log(e); ReHookClipboard(); } return rv; } public static string GetText(TextDataFormat format) { string rv = null; try { rv = Common.Retry(nameof(SystemClipboard.GetText), () => { return SystemClipboard.GetText(format); }, (log) => Log(log)); } catch (ExternalException e) { Log(e); ReHookClipboard(); } catch (ThreadStateException e) { Log(e); ReHookClipboard(); } catch (System.ComponentModel.InvalidEnumArgumentException e) { Log(e); } return rv; } public static void SetImage(Image image) { try { _ = Common.Retry( nameof(SystemClipboard.SetImage), () => { SystemClipboard.SetImage(image); return true; }, (log) => Log(log)); } catch (ExternalException e) { Log(e); ReHookClipboard(); } catch (ThreadStateException e) { Log(e); ReHookClipboard(); } catch (ArgumentNullException e) { Log(e); } } public static void SetText(string text) { try { _ = Common.Retry( nameof(SystemClipboard.SetText), () => { SystemClipboard.SetText(text); return true; }, (log) => Log(log)); } catch (ExternalException e) { Log(e); ReHookClipboard(); } catch (ThreadStateException e) { Log(e); ReHookClipboard(); } catch (ArgumentNullException e) { Log(e); } } } #endif internal sealed class SharedConst { internal const int QUIT_CMD = 0x409; } internal sealed partial class Common { internal static bool IpcChannelCreated { get; set; } internal static T Retry(string name, Func func, Action log, Action preRetry = null) { int count = 0; do { try { T rv = func(); if (count > 0) { log($"Trace: {name} has been successful after {count} retry."); } return rv; } catch (Exception) { count++; preRetry?.Invoke(); if (count > 10) { throw; } Application.DoEvents(); Thread.Sleep(200); } } while (true); } } } ================================================ FILE: PowerToys/src/modules/MouseWithoutBorders/App/Class/InputHook.cs ================================================ // 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. // // Keyboard/Mouse hook callbacks, pre-process before calling to routines in Common.Event. // // // 2008 created by Truong Do (ductdo). // 2009-... modified by Truong Do (TruongDo). // 2023- Included in PowerToys. // using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Reflection; using System.Runtime.InteropServices; using System.Windows.Forms; using Microsoft.PowerToys.Settings.UI.Library; using MouseWithoutBorders.Core; [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.InputHook.#MouseHookProc(System.Int32,System.Int32,System.IntPtr)", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Scope = "member", Target = "MouseWithoutBorders.InputHook.#ProcessKeyEx(System.Int32,System.Int32)", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Globalization", "CA1300:SpecifyMessageBoxOptions", Scope = "member", Target = "MouseWithoutBorders.InputHook.#Start()", Justification = "Dotnet port with style preservation")] namespace MouseWithoutBorders.Class { internal class InputHook { internal delegate void MouseEvHandler(MOUSEDATA e, int dx, int dy); internal delegate void KeybdEvHandler(KEYBDDATA e); [StructLayout(LayoutKind.Sequential)] private struct MouseHookStruct { internal NativeMethods.POINT Pt; internal int Hwnd; internal int WHitTestCode; internal int DwExtraInfo; } // http://msdn.microsoft.com/en-us/library/ms644970(VS.85).aspx [StructLayout(LayoutKind.Sequential)] private struct MouseLLHookStruct { internal NativeMethods.POINT Pt; internal int MouseData; internal int Flags; internal int Time; internal int DwExtraInfo; } [StructLayout(LayoutKind.Sequential)] private struct KeyboardHookStruct { internal int VkCode; internal int ScanCode; internal int Flags; internal int Time; internal int DwExtraInfo; } internal event MouseEvHandler MouseEvent; internal event KeybdEvHandler KeyboardEvent; private int hMouseHook; private int hKeyboardHook; private static NativeMethods.HookProc mouseHookProcedure; private static NativeMethods.HookProc keyboardHookProcedure; private static MouseLLHookStruct mouseHookStruct; private static KeyboardHookStruct keyboardHookStruct; private static MOUSEDATA hookCallbackMouseData; private static KEYBDDATA hookCallbackKeybdData; private static bool winDown; private static bool altDown; private static bool shiftDown; internal static bool RealData { get; set; } = true; internal static int SkipMouseUpCount { get; set; } internal static bool SkipMouseUpDown { get; set; } internal static bool CtrlDown { get; private set; } internal static bool EasyMouseKeyDown { get; set; } internal InputHook() { Start(); } ~InputHook() { Stop(); } internal void Start() { int le; bool er = false; // Install Mouse Hook mouseHookProcedure = new NativeMethods.HookProc(MouseHookProc); hMouseHook = NativeMethods.SetWindowsHookEx( Common.WH_MOUSE_LL, mouseHookProcedure, Marshal.GetHINSTANCE( Assembly.GetExecutingAssembly().GetModules()[0]), 0); if (hMouseHook == 0) { le = Marshal.GetLastWin32Error(); Logger.Log("Error installing Mouse hook: " + le.ToString(CultureInfo.CurrentCulture)); er = true; Stop(); } // Install Keyboard Hook keyboardHookProcedure = new NativeMethods.HookProc(KeyboardHookProc); hKeyboardHook = NativeMethods.SetWindowsHookEx( Common.WH_KEYBOARD_LL, keyboardHookProcedure, Marshal.GetHINSTANCE( Assembly.GetExecutingAssembly().GetModules()[0]), 0); if (hKeyboardHook == 0) { le = Marshal.GetLastWin32Error(); Logger.Log("Error installing keyboard hook: " + le.ToString(CultureInfo.CurrentCulture)); er = true; Stop(); } if (er) { if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop) { _ = MessageBox.Show( "安装键盘/鼠标侦听器失败!", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error); } } else { Common.InitLastInputEventCount(); } } internal void Stop() { if (hMouseHook != 0) { int retMouse = NativeMethods.UnhookWindowsHookEx(hMouseHook); hMouseHook = 0; if (retMouse == 0) { int errorCode = Marshal.GetLastWin32Error(); // throw new Win32Exception(errorCode); Logger.Log("Exception uninstalling Mouse hook, error code: " + errorCode.ToString(CultureInfo.CurrentCulture)); } } if (hKeyboardHook != 0) { int retKeyboard = NativeMethods.UnhookWindowsHookEx(hKeyboardHook); hKeyboardHook = 0; if (retKeyboard == 0) { int errorCode = Marshal.GetLastWin32Error(); // throw new Win32Exception(errorCode); Logger.Log("Exception uninstalling keyboard hook, error code: " + errorCode.ToString(CultureInfo.CurrentCulture)); } } } // Better performance, compared to Marshal.PtrToStructure. private static MouseLLHookStruct LParamToMouseLLHookStruct(IntPtr lParam) { unsafe { return *(MouseLLHookStruct*)lParam; } } private static KeyboardHookStruct LParamToKeyboardHookStruct(IntPtr lParam) { unsafe { return *(KeyboardHookStruct*)lParam; } } private int MouseHookProc(int nCode, int wParam, IntPtr lParam) { int rv = 1, dx = 0, dy = 0; bool local = false; Common.InputEventCount++; try { if (!RealData) { RealData = true; // Common.Log("MouseHookProc: Not real data!"); // return rv; rv = NativeMethods.CallNextHookEx(hMouseHook, nCode, wParam, lParam); } else { Common.RealInputEventCount++; if (Common.NewDesMachineID == Common.MachineID || Common.NewDesMachineID == ID.ALL) { local = true; if (Common.MainFormVisible && !Common.IsDropping) { Common.MainFormDot(); } } if (nCode >= 0 && MouseEvent != null) { if (wParam == Common.WM_LBUTTONUP && SkipMouseUpCount > 0) { Logger.LogDebug($"{nameof(SkipMouseUpCount)}: {SkipMouseUpCount}."); SkipMouseUpCount--; rv = NativeMethods.CallNextHookEx(hMouseHook, nCode, wParam, lParam); return rv; } if ((wParam == Common.WM_LBUTTONUP || wParam == Common.WM_LBUTTONDOWN) && SkipMouseUpDown) { rv = NativeMethods.CallNextHookEx(hMouseHook, nCode, wParam, lParam); return rv; } mouseHookStruct = LParamToMouseLLHookStruct(lParam); hookCallbackMouseData.dwFlags = wParam; // Use WheelDelta to store XBUTTON1/XBUTTON2 data. hookCallbackMouseData.WheelDelta = (short)((mouseHookStruct.MouseData >> 16) & 0xffff); if (local) { hookCallbackMouseData.X = mouseHookStruct.Pt.x; hookCallbackMouseData.Y = mouseHookStruct.Pt.y; if (Setting.Values.DrawMouse && Common.MouseCursorForm != null) { CustomCursor.ShowFakeMouseCursor(int.MinValue, int.MinValue); } } else { if (Common.SwitchLocation.Count > 0 && Common.NewDesMachineID != Common.MachineID && Common.NewDesMachineID != ID.ALL) { Common.SwitchLocation.Count--; if (Common.SwitchLocation.X > Common.XY_BY_PIXEL - 100000 || Common.SwitchLocation.Y > Common.XY_BY_PIXEL - 100000) { hookCallbackMouseData.X = Common.SwitchLocation.X - Common.XY_BY_PIXEL; hookCallbackMouseData.Y = Common.SwitchLocation.Y - Common.XY_BY_PIXEL; } else { hookCallbackMouseData.X = (Common.SwitchLocation.X * Common.ScreenWidth / 65535) + Common.PrimaryScreenBounds.Left; hookCallbackMouseData.Y = (Common.SwitchLocation.Y * Common.ScreenHeight / 65535) + Common.PrimaryScreenBounds.Top; } Common.HideMouseCursor(false); } else { dx = mouseHookStruct.Pt.x - Common.LastPos.X; dy = mouseHookStruct.Pt.y - Common.LastPos.Y; hookCallbackMouseData.X += dx; hookCallbackMouseData.Y += dy; if (hookCallbackMouseData.X < Common.PrimaryScreenBounds.Left) { hookCallbackMouseData.X = Common.PrimaryScreenBounds.Left - 1; } else if (hookCallbackMouseData.X > Common.PrimaryScreenBounds.Right) { hookCallbackMouseData.X = Common.PrimaryScreenBounds.Right + 1; } if (hookCallbackMouseData.Y < Common.PrimaryScreenBounds.Top) { hookCallbackMouseData.Y = Common.PrimaryScreenBounds.Top - 1; } else if (hookCallbackMouseData.Y > Common.PrimaryScreenBounds.Bottom) { hookCallbackMouseData.Y = Common.PrimaryScreenBounds.Bottom + 1; } dx += dx < 0 ? -Common.MOVE_MOUSE_RELATIVE : Common.MOVE_MOUSE_RELATIVE; dy += dy < 0 ? -Common.MOVE_MOUSE_RELATIVE : Common.MOVE_MOUSE_RELATIVE; } } MouseEvent(hookCallbackMouseData, dx, dy); Common.DragDropStep01(wParam); Common.DragDropStep09(wParam); } if (local) { rv = NativeMethods.CallNextHookEx(hMouseHook, nCode, wParam, lParam); } } } catch (Exception e) { Logger.Log(e); rv = NativeMethods.CallNextHookEx(hMouseHook, nCode, wParam, lParam); } return rv; } private int KeyboardHookProc(int nCode, int wParam, IntPtr lParam) { Common.InputEventCount++; if (!RealData) { return NativeMethods.CallNextHookEx(hKeyboardHook, nCode, wParam, lParam); } Common.RealInputEventCount++; keyboardHookStruct = LParamToKeyboardHookStruct(lParam); hookCallbackKeybdData.dwFlags = keyboardHookStruct.Flags; hookCallbackKeybdData.wVk = (short)keyboardHookStruct.VkCode; if (nCode >= 0 && KeyboardEvent != null) { if (!ProcessKeyEx(keyboardHookStruct.VkCode, keyboardHookStruct.Flags, hookCallbackKeybdData)) { return 1; } KeyboardEvent(hookCallbackKeybdData); } if (Common.DesMachineID == ID.NONE || Common.DesMachineID == ID.ALL || Common.DesMachineID == Common.MachineID) { return NativeMethods.CallNextHookEx(hKeyboardHook, nCode, wParam, lParam); } else { return 1; } } private bool ProcessKeyEx(int vkCode, int flags, KEYBDDATA hookCallbackKeybdData) { if ((flags & (int)Common.LLKHF.UP) == (int)Common.LLKHF.UP) { EasyMouseKeyDown = false; switch ((VK)vkCode) { case VK.LWIN: case VK.RWIN: winDown = false; break; case VK.LCONTROL: case VK.RCONTROL: CtrlDown = false; break; case VK.LMENU: case VK.RMENU: altDown = false; break; case VK.LSHIFT: shiftDown = false; break; default: break; } } else { UpdateEasyMouseKeyDown((VK)vkCode); switch ((VK)vkCode) { case VK.LWIN: case VK.RWIN: winDown = true; break; case VK.LCONTROL: case VK.RCONTROL: CtrlDown = true; break; case VK.LMENU: case VK.RMENU: altDown = true; break; case VK.LSHIFT: shiftDown = true; break; case VK.DELETE: if (CtrlDown && altDown) { CtrlDown = altDown = false; KeyboardEvent(hookCallbackKeybdData); if (Common.DesMachineID != ID.ALL) { Common.SwitchToMachine(Common.MachineName.Trim()); } /* #if CUSTOMIZE_LOGON_SCREEN Common.DoSomethingInUIThread(delegate() { Common.MainForm.LoadNewLogonBackground(); }); #endif * */ } break; case VK.ESCAPE: if (Common.IsTopMostMessageNotNull()) { Common.HideTopMostMessage(); } break; default: Logger.LogDebug("X"); return ProcessHotKeys(vkCode, hookCallbackKeybdData); } } return true; } private void UpdateEasyMouseKeyDown(VK vkCode) { EasyMouseOption easyMouseOption = (EasyMouseOption)Setting.Values.EasyMouse; EasyMouseKeyDown = (easyMouseOption == EasyMouseOption.Ctrl && (vkCode == VK.LCONTROL || vkCode == VK.RCONTROL)) || (easyMouseOption == EasyMouseOption.Shift && (vkCode == VK.LSHIFT || vkCode == VK.RSHIFT)); } private static long lastHotKeyLockMachine; private void ResetModifiersState(HotkeySettings matchingHotkey) { CtrlDown = CtrlDown && matchingHotkey.Ctrl; altDown = altDown && matchingHotkey.Alt; shiftDown = shiftDown && matchingHotkey.Shift; winDown = winDown && matchingHotkey.Win; } private List GetVkCodesList(HotkeySettings hotkey) { var list = new List(); if (hotkey.Alt) { list.Add((short)VK.MENU); } if (hotkey.Shift) { list.Add((short)VK.SHIFT); } if (hotkey.Win) { list.Add((short)VK.LWIN); } if (hotkey.Ctrl) { list.Add((short)VK.CONTROL); } if (hotkey.Code != 0) { list.Add((short)hotkey.Code); } return list; } private bool ProcessHotKeys(int vkCode, KEYBDDATA hookCallbackKeybdData) { if (Common.HotkeyMatched(vkCode, winDown, CtrlDown, altDown, shiftDown, Setting.Values.HotKeySwitch2AllPC)) { ResetLastSwitchKeys(); Common.SwitchToMultipleMode(Common.DesMachineID != ID.ALL, true); } if (Common.HotkeyMatched(vkCode, winDown, CtrlDown, altDown, shiftDown, Setting.Values.HotKeyToggleEasyMouse)) { if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop) { EasyMouseOption easyMouseOption = (EasyMouseOption)Setting.Values.EasyMouse; if (easyMouseOption is EasyMouseOption.关闭 or EasyMouseOption.开启) { Setting.Values.EasyMouse = (int)(easyMouseOption == EasyMouseOption.关闭 ? EasyMouseOption.开启 : EasyMouseOption.关闭); Common.ShowToolTip($"易动模式已通过快捷键切换为 [{(EasyMouseOption)Setting.Values.EasyMouse}]. 可以在设置中调整快捷键.", 5000); return false; } } } else if (Common.HotkeyMatched(vkCode, winDown, CtrlDown, altDown, shiftDown, Setting.Values.HotKeyLockMachine)) { if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop) { if (Common.GetTick() - lastHotKeyLockMachine < 500) { Common.SwitchToMultipleMode(true, true); var codes = GetVkCodesList(Setting.Values.HotKeyLockMachine); foreach (var code in codes) { hookCallbackKeybdData.wVk = code; KeyboardEvent(hookCallbackKeybdData); } hookCallbackKeybdData.dwFlags |= (int)Common.LLKHF.UP; foreach (var code in codes) { hookCallbackKeybdData.wVk = code; KeyboardEvent(hookCallbackKeybdData); } Common.SwitchToMultipleMode(false, true); _ = NativeMethods.LockWorkStation(); } else { KeyboardEvent(hookCallbackKeybdData); } lastHotKeyLockMachine = Common.GetTick(); return false; } } else if (Common.HotkeyMatched(vkCode, winDown, CtrlDown, altDown, shiftDown, Setting.Values.HotKeyReconnect)) { Common.ShowToolTip("正在重新连接...", 2000); Common.LastReconnectByHotKeyTime = Common.GetTick(); Common.PleaseReopenSocket = Common.REOPEN_WHEN_HOTKEY; return false; } if (CtrlDown && altDown) { if (shiftDown && vkCode == Setting.Values.HotKeyExitMM && (Common.DesMachineID == Common.MachineID || Common.DesMachineID == ID.ALL)) { Common.DoSomethingInUIThread(() => { Common.MainForm.NotifyIcon.Visible = false; for (int i = 1; i < 10; i++) { Application.DoEvents(); Thread.Sleep(20); } Common.MainForm.Quit(false, false); }); } else if (shiftDown || winDown) { // The following else cases should work if control and alt modifiers are pressed. The hotkeys should still be captured. // But if any of the other 2 modifiers (shift or win) are pressed, they hotkeys should not be activated. // Issue #26597 return true; } else if (vkCode == Setting.Values.HotKeySwitchMachine || vkCode == Setting.Values.HotKeySwitchMachine + 1 || vkCode == Setting.Values.HotKeySwitchMachine + 2 || vkCode == Setting.Values.HotKeySwitchMachine + 3) { if (Switch2(vkCode - Setting.Values.HotKeySwitchMachine)) { return false; } } } return true; } private static bool Switch2(int index) { if (Common.MachineMatrix != null && Common.MachineMatrix.Length > index) { string mcName = Common.MachineMatrix[index].Trim(); if (!string.IsNullOrEmpty(mcName)) { // Common.DoSomethingInUIThread(delegate() { Common.ReleaseAllKeys(); } // ); Common.SwitchToMachine(mcName); if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop) { Common.ShowToolTip( string.Format( CultureInfo.CurrentCulture, "控制已通过快捷键 Ctrl+Alt+{1}{2} 切换到设备 {0}.", mcName, Setting.Values.HotKeySwitchMachine == (int)VK.F1 ? "F" : string.Empty, index + 1), 3000); } return true; } } return false; } internal void ResetLastSwitchKeys() { CtrlDown = winDown = altDown = false; } } } ================================================ FILE: PowerToys/src/modules/MouseWithoutBorders/App/Class/Program.cs ================================================ // 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. // // Application entry and pre-process/initialization. // // // 2008 created by Truong Do (ductdo). // 2009-... modified by Truong Do (TruongDo). // 2023- Included in PowerToys. // using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Drawing.Printing; using System.Globalization; using System.IO; using System.IO.Pipes; using System.Linq; using System.Security.Authentication.ExtendedProtection; using System.Security.Principal; using System.ServiceModel.Channels; using System.ServiceProcess; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using System.Xml.Linq; using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library.Utilities; using Microsoft.PowerToys.Telemetry; using Newtonsoft.Json; using StreamJsonRpc; using Logger = MouseWithoutBorders.Core.Logger; using Thread = MouseWithoutBorders.Core.Thread; [module: SuppressMessage("Microsoft.MSInternal", "CA904:DeclareTypesInMicrosoftOrSystemNamespace", Scope = "namespace", Target = "MouseWithoutBorders", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Design", "CA1014:MarkAssembliesWithClsCompliant", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Globalization", "CA1304:SpecifyCultureInfo", Scope = "member", Target = "MouseWithoutBorders.Program.#Main()", MessageId = "System.String.ToLower", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Globalization", "CA1300:SpecifyMessageBoxOptions", Scope = "member", Target = "MouseWithoutBorders.Program.#Main()", Justification = "Dotnet port with style preservation")] namespace MouseWithoutBorders.Class { internal static class Program { private static readonly string ServiceName = "PowerToys.MWB.Service"; private static readonly string ServiceModeArg = "UseService"; public static bool ShowServiceModeErrorTooltip; [STAThread] private static void Main() { ManagedCommon.Logger.InitializeLogger("\\MouseWithoutBorders\\Logs"); Logger.Log(Application.ProductName + " Started!"); ETWTrace etwTrace = new ETWTrace(); if (PowerToys.GPOWrapper.GPOWrapper.GetConfiguredMouseWithoutBordersEnabledValue() == PowerToys.GPOWrapper.GpoRuleConfigured.Disabled) { Logger.Log("Tried to start with a GPO policy setting the utility to always be disabled. Please contact your systems administrator."); return; } Thread.CurrentThread.Name = Application.ProductName + " main thread"; Common.BinaryName = Path.GetFileNameWithoutExtension(Application.ExecutablePath); WindowsIdentity currentUser = WindowsIdentity.GetCurrent(); SecurityIdentifier currentUserSID = currentUser.User; SecurityIdentifier localSystemSID = new SecurityIdentifier(WellKnownSidType.LocalSystemSid, null); bool runningAsSystem = currentUserSID.Equals(localSystemSID); Common.RunWithNoAdminRight = !runningAsSystem; try { string[] args = Environment.GetCommandLineArgs(); string firstArg = string.Empty; if (args.Length > 1 && args[1] != null) { firstArg = args[1].Trim(); } User = WindowsIdentity.GetCurrent().Name; Logger.LogDebug("*** Started as " + User); Logger.Log(Environment.CommandLine); bool serviceMode = firstArg == ServiceModeArg; // If we're started from the .dll module or from the service process, we should // assume the service mode. if (serviceMode && !runningAsSystem) { try { var sc = new ServiceController(ServiceName); sc.Start(); return; } catch (Exception ex) { Logger.Log("Couldn't start the service. Will try to continue as not a service."); Logger.Log(ex); ShowServiceModeErrorTooltip = true; serviceMode = false; Setting.Values.UseService = false; } } if (serviceMode || runningAsSystem) { if (args.Length > 2) { Helper.UserLocalAppDataPath = args[2].Trim(); } } ShutdownWithPowerToys.WaitForPowerToysRunner(etwTrace); if (firstArg != string.Empty) { if (Common.CheckSecondInstance(Common.RunWithNoAdminRight)) { Logger.Log("*** Second instance, exiting..."); return; } string myDesktop = Common.GetMyDesktop(); if (firstArg.Equals("winlogon", StringComparison.OrdinalIgnoreCase)) { // Executed by service, running on logon desktop Logger.Log("*** Running on " + firstArg + " desktop"); Common.RunOnLogonDesktop = true; } else if (args[1].Trim().Equals("default", StringComparison.OrdinalIgnoreCase)) { Logger.Log("*** Running on " + firstArg + " desktop"); } else if (args[1].Equals(myDesktop, StringComparison.OrdinalIgnoreCase)) { Logger.Log("*** Running on " + myDesktop); if (myDesktop.Equals("Screen-saver", StringComparison.OrdinalIgnoreCase)) { Common.RunOnScrSaverDesktop = true; Setting.Values.LastX = Common.JUST_GOT_BACK_FROM_SCREEN_SAVER; } } } else { if (Common.CheckSecondInstance(true)) { Logger.Log("*** Second instance, exiting..."); return; } } PowerToysTelemetry.Log.WriteEvent(new MouseWithoutBorders.Telemetry.MouseWithoutBordersStartedEvent()); try { Common.CurrentProcess = Process.GetCurrentProcess(); Common.CurrentProcess.PriorityClass = ProcessPriorityClass.RealTime; } catch (Exception e) { Logger.Log(e); } Logger.Log(Environment.OSVersion.ToString()); // Environment.OSVersion is unreliable from 6.2 and up, so just forcefully call the APIs and log the exception unsupported by Windows: int setProcessDpiAwarenessResult = -1; try { setProcessDpiAwarenessResult = NativeMethods.SetProcessDpiAwareness(2); Logger.Log(string.Format(CultureInfo.InvariantCulture, "SetProcessDpiAwareness: {0}.", setProcessDpiAwarenessResult)); } catch (DllNotFoundException) { Logger.Log("SetProcessDpiAwareness is unsupported in Windows 7 and lower."); } catch (EntryPointNotFoundException) { Logger.Log("SetProcessDpiAwareness is unsupported in Windows 7 and lower."); } catch (Exception e) { Logger.Log(e); } try { if (setProcessDpiAwarenessResult != 0) { Logger.Log(string.Format(CultureInfo.InvariantCulture, "SetProcessDPIAware: {0}.", NativeMethods.SetProcessDPIAware())); } } catch (Exception e) { Logger.Log(e); } System.Threading.Thread mainUIThread = Thread.CurrentThread; Common.UIThreadID = mainUIThread.ManagedThreadId; Thread.UpdateThreads(mainUIThread); StartInputCallbackThread(); if (!Common.RunOnLogonDesktop) { StartSettingSyncThread(); } Application.EnableVisualStyles(); _ = Application.SetHighDpiMode(HighDpiMode.PerMonitorV2); Application.SetCompatibleTextRenderingDefault(false); Common.Init(); Common.WndProcCounter++; var formScreen = new FrmScreen(); Application.Run(formScreen); etwTrace?.Dispose(); } catch (Exception e) { Logger.Log(e); } } private interface ISettingsSyncHelper { [JsonObject(MemberSerialization.OptIn)] public struct MachineSocketState { // Disable false-positive warning due to IPC #pragma warning disable CS0649 [JsonProperty] public string Name; [JsonProperty] public SocketStatus Status; #pragma warning restore CS0649 } void Shutdown(); void Reconnect(); void GenerateNewKey(); void ConnectToMachine(string machineName, string securityKey); Task RequestMachineSocketStateAsync(); } private sealed class SettingsSyncHelper : ISettingsSyncHelper { public Task RequestMachineSocketStateAsync() { var machineStates = new Dictionary(); if (Common.Sk == null || Common.Sk.TcpSockets == null) { return Task.FromResult(Array.Empty()); } foreach (var client in Common.Sk.TcpSockets .Where(t => t != null && t.IsClient && !string.IsNullOrEmpty(t.MachineName))) { var exists = machineStates.TryGetValue(client.MachineName, out var existingStatus); if (!exists || existingStatus == SocketStatus.NA) { machineStates[client.MachineName] = client.Status; } } return Task.FromResult(machineStates.Select((state) => new ISettingsSyncHelper.MachineSocketState { Name = state.Key, Status = state.Value }).ToArray()); } public void ConnectToMachine(string pcName, string securityKey) { Setting.Values.PauseInstantSaving = true; Common.ClearComputerMatrix(); Setting.Values.MyKey = securityKey; Common.MyKey = securityKey; Common.MagicNumber = Common.Get24BitHash(Common.MyKey); Common.MachineMatrix = new string[Common.MAX_MACHINE] { pcName.Trim().ToUpper(CultureInfo.CurrentCulture), Common.MachineName.Trim(), string.Empty, string.Empty }; string[] machines = Common.MachineMatrix; Common.MachinePool.Initialize(machines); Common.UpdateMachinePoolStringSetting(); SocketStuff.InvalidKeyFound = false; Common.ReopenSocketDueToReadError = true; Common.ReopenSockets(true); Common.SendMachineMatrix(); Setting.Values.PauseInstantSaving = false; Setting.Values.SaveSettings(); } public void GenerateNewKey() { Setting.Values.PauseInstantSaving = true; Setting.Values.EasyMouse = (int)EasyMouseOption.开启; Common.ClearComputerMatrix(); Setting.Values.MyKey = Common.MyKey = Common.CreateRandomKey(); Common.GeneratedKey = true; Setting.Values.PauseInstantSaving = false; Setting.Values.SaveSettings(); Reconnect(); } public void Reconnect() { SocketStuff.InvalidKeyFound = false; Common.ReopenSocketDueToReadError = true; Common.ReopenSockets(true); for (int i = 0; i < 10; i++) { if (Common.AtLeastOneSocketConnected()) { Common.MMSleep(0.5); break; } Common.MMSleep(0.2); } Common.SendMachineMatrix(); } public void Shutdown() { Process[] ps = Process.GetProcessesByName("PowerToys.MouseWithoutBorders"); Process me = Process.GetCurrentProcess(); foreach (Process p in ps) { if (p.Id != me.Id) { p.Kill(); } } Common.MainForm.Quit(true, false); } } internal static void StartSettingSyncThread() { var serverTaskCancellationSource = new CancellationTokenSource(); CancellationToken cancellationToken = serverTaskCancellationSource.Token; IpcChannel.StartIpcServer("MouseWithoutBorders/SettingsSync", cancellationToken); } internal static void StartInputCallbackThread() { Thread inputCallback = new(new ThreadStart(InputCallbackThread), "InputCallback Thread"); inputCallback.SetApartmentState(ApartmentState.STA); inputCallback.Priority = ThreadPriority.Highest; inputCallback.Start(); } private static void InputCallbackThread() { // SuppressFlow fixes an issue on service mode, where the helper process can't get enough permissions to be started again. // More details can be found on: https://github.com/microsoft/PowerToys/pull/36892 using var asyncFlowControl = ExecutionContext.SuppressFlow(); Common.InputCallbackThreadID = Thread.CurrentThread.ManagedThreadId; while (!Common.InitDone) { Thread.Sleep(100); } Application.Run(new FrmInputCallback()); } internal static void StartService() { if (Common.RunWithNoAdminRight) { return; } try { // Kill all but me Process me = Process.GetCurrentProcess(); Process[] ps = Process.GetProcessesByName(Common.BinaryName); foreach (Process pp in ps) { if (pp.Id != me.Id) { Logger.Log(string.Format(CultureInfo.InvariantCulture, "Killing process {0}.", pp.Id)); pp.KillProcess(); } } } catch (Exception e) { Logger.Log(e); } Common.StartMouseWithoutBordersService(); } internal static string User { get; set; } } } ================================================ FILE: PowerToys/src/modules/MouseWithoutBorders/App/Class/SocketStuff.cs ================================================ // 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.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Linq; using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; using System.Security.Cryptography; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using MouseWithoutBorders.Core; // // Socket code. // // // 2008 created by Truong Do (ductdo). // 2009-... modified by Truong Do (TruongDo). // 2023- Included in PowerToys. // using MouseWithoutBorders.Exceptions; using Thread = MouseWithoutBorders.Core.Thread; [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.SocketStuff.#SendData(System.Byte[])", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.SocketStuff.#Close()", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.SocketStuff.#CreateSocket(System.Boolean)", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.SocketStuff.#SendData(System.Byte[],MouseWithoutBorders.IP)", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.SocketStuff.#SendData(System.Object)", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.SocketStuff.#SendFile(System.Net.Sockets.Socket,System.String)", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.SocketStuff.#MainTCPRoutine(System.Net.Sockets.Socket,System.String)", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.SocketStuff.#TCPServerThread(System.Object)", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.SocketStuff.#SendClipboardData(System.Object)", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.SocketStuff.#StartNewTcpClient(MouseWithoutBorders.MachineInf)", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.SocketStuff.#StartNewTcpServer(System.Net.Sockets.Socket,System.String)", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.SocketStuff.#UpdateTCPClients()", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.SocketStuff.#UpdateTcpSockets(System.Net.Sockets.Socket,MouseWithoutBorders.SocketStatus)", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.SocketStuff.#.ctor(System.Int32,System.Boolean)", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.SocketStuff.#SendData(System.Byte[],MouseWithoutBorders.IP,System.Int32)", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable", Scope = "type", Target = "MouseWithoutBorders.SocketStuff", Justification = "Dotnet port with style preservation")] namespace MouseWithoutBorders.Class { internal enum SocketStatus : int { NA = 0, Resolving = 1, Connecting = 2, Handshaking = 3, Error = 4, ForceClosed = 5, InvalidKey = 6, Timeout = 7, SendError = 8, Connected = 9, } internal class TcpSk : IDisposable { public TcpSk(bool isClient, Socket s, SocketStatus status, string machineName, IPAddress address = null) { IsClient = isClient; BackingSocket = s; Status = status; MachineName = machineName; Address = address; BirthTime = Common.GetTick(); } public bool IsClient { get; set; } public Socket BackingSocket { get; set; } public SocketStatus Status { get; set; } public string MachineName { get; set; } public long BirthTime { get; set; } public uint MachineId { get; set; } public IPAddress Address { get; set; } private Stream encryptedStream; private Stream decryptedStream; private Stream socketStream; public Stream EncryptedStream { get { if (encryptedStream == null && BackingSocket.Connected) { encryptedStream = Common.GetEncryptedStream(new NetworkStream(BackingSocket)); Common.SendOrReceiveARandomDataBlockPerInitialIV(encryptedStream); } return encryptedStream; } } public Stream DecryptedStream { get { if (decryptedStream == null && BackingSocket.Connected) { decryptedStream = Common.GetDecryptedStream(new NetworkStream(BackingSocket)); Common.SendOrReceiveARandomDataBlockPerInitialIV(decryptedStream, false); } return decryptedStream; } } public Stream SocketStream { get { if (socketStream == null && BackingSocket.Connected) { socketStream = new NetworkStream(BackingSocket); } return socketStream; } } public void Dispose() { encryptedStream?.Dispose(); decryptedStream?.Dispose(); socketStream?.Dispose(); } } internal class SocketStuff { private readonly int bASE_PORT; private TcpServer skClipboardServer; private TcpServer skMessageServer; internal object TcpSocketsLock = new(); internal static bool InvalidKeyFound; internal static bool InvalidKeyFoundOnClientSocket; internal const int CONNECT_TIMEOUT = 60000; private static readonly ConcurrentDictionary FailedAttempt = new(); internal List TcpSockets { get; private set; // set { tcpSockets = value; } } internal int TcpPort { get; // set { tcpPort = value; } } private static bool firstRun; private readonly long connectTimeout; private static int restartCount; internal SocketStuff(int port, bool byUser) { Logger.LogDebug("SocketStuff started."); bASE_PORT = port; Common.Ran = new Random(); Logger.LogDebug("Validating session..."); if (Common.CurrentProcess.SessionId != NativeMethods.WTSGetActiveConsoleSessionId()) { if (Common.DesMachineID != Common.MachineID) { Common.SwitchToMultipleMode(false, true); } if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop) { Common.MainForm.SetTrayIconText("Not physical console session."); if (byUser) { Common.ShowToolTip("非物理控制台会话.", 5000); } } Common.MMSleep(1); Logger.Log("Not physical console session."); throw new NotPhysicalConsoleException("Not physical console session."); } Logger.LogDebug("Creating socket list and mutex..."); try { lock (TcpSocketsLock) { TcpSockets = new List(); } bool dummy1 = Setting.Values.MatrixOneRow; // Reading from reg to variable dummy1 = Setting.Values.MatrixCircle; if (Setting.Values.IsMyKeyRandom) { Setting.Values.MyKey = Common.MyKey; } Common.MagicNumber = Common.Get24BitHash(Common.MyKey); Common.PackageID = Setting.Values.PackageID; TcpPort = bASE_PORT; if (Common.SocketMutex == null) { firstRun = true; Common.SocketMutex = new Mutex(false, $"Global\\{Application.ProductName}-{FrmAbout.AssemblyVersion}-FF7CDABE-1015-0904-1103-24670FA5D16E"); } Common.AcquireSocketMutex(); } catch (AbandonedMutexException e) { Logger.TelemetryLogTrace($"{nameof(SocketStuff)}: {e.Message}", SeverityLevel.Warning); } Common.GetScreenConfig(); if (firstRun && Common.RunOnScrSaverDesktop) { firstRun = false; } // JUST_GOT_BACK_FROM_SCREEN_SAVER: For bug: monitor does not turn off after logon screen saver exits else if (!Common.RunOnScrSaverDesktop) { if (Setting.Values.LastX == Common.JUST_GOT_BACK_FROM_SCREEN_SAVER) { Common.NewDesMachineID = Common.DesMachineID = Common.MachineID; } else { // Common.Log("Getting IP: " + Setting.Values.DesMachineID.ToString(CultureInfo.CurrentCulture)); Common.LastX = Setting.Values.LastX; Common.LastY = Setting.Values.LastY; if (Common.RunOnLogonDesktop && Setting.Values.DesMachineID == (uint)ID.ALL) { Common.SwitchToMultipleMode(true, false); } else { Common.SwitchToMultipleMode(false, false); } } } connectTimeout = Common.GetTick() + (CONNECT_TIMEOUT / 2); Exception openSocketErr; /* * The machine might be getting a new IP address from its DHCP server * for ex, when a laptop with a wireless connection just wakes up, might take long time:( * */ Common.GetMachineName(); // IPs might have been changed Common.UpdateMachineTimeAndID(); Logger.LogDebug("Creating sockets..."); openSocketErr = CreateSocket(); int sleepSecs = 0, errCode = 0; if (openSocketErr != null) { if (openSocketErr is SocketException) { errCode = (openSocketErr as SocketException).ErrorCode; switch (errCode) { case 0: // No error. break; case 10048: // WSAEADDRINUSE 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) { Logger.TelemetryLogTrace("Restarting the service dues to WSAEADDRINUSE.", SeverityLevel.Warning); Program.StartService(); Common.PleaseReopenSocket = Common.REOPEN_WHEN_WSAECONNRESET; } break; case 10049: // WSAEADDRNOTAVAIL sleepSecs = 1; break; default: sleepSecs = 5; break; } } else { sleepSecs = 10; } if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop) { if (byUser) { Common.ShowToolTip(errCode.ToString(CultureInfo.CurrentCulture) + ": " + openSocketErr.Message, 5000, ToolTipIcon.Warning, Setting.Values.ShowClipNetStatus); } } Common.MMSleep(sleepSecs); Common.ReleaseSocketMutex(); throw new ExpectedSocketException(openSocketErr.Message); } else { restartCount = 0; if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop) { IpcHelper.CreateIpcServer(false); } Common.MainForm.UpdateNotifyIcon(); } } internal void Close(bool sentWait) { try { if (!Common.RunOnScrSaverDesktop) { Setting.Values.LastX = Common.LastX; Setting.Values.LastY = Common.LastY; Setting.Values.PackageID = Common.PackageID; // Common.Log("Saving IP: " + Setting.Values.DesMachineID.ToString(CultureInfo.CurrentCulture)); Setting.Values.DesMachineID = (uint)Common.DesMachineID; } _ = Common.ExecuteAndTrace( "Closing sockets", () => { Logger.LogDebug($"Closing socket [{skMessageServer?.Name}]."); skMessageServer?.Close(); // The original ones, not the socket instances produced by the accept() method. skMessageServer = null; Logger.LogDebug($"Closing socket [{skClipboardServer?.Name}]."); skClipboardServer?.Close(); skClipboardServer = null; try { // If these sockets are failed to be closed then the tool would not function properly, more logs are added for debugging. lock (TcpSocketsLock) { int c = 0; if (TcpSockets != null) { Logger.LogDebug("********** Closing Sockets: " + TcpSockets.Count.ToString(CultureInfo.InvariantCulture)); List notClosedSockets = new(); foreach (TcpSk t in TcpSockets) { if (t != null && t.BackingSocket != null && t.Status != SocketStatus.Resolving) { try { t.MachineName = "$*NotUsed*$"; t.Status = t.Status >= 0 ? 0 : t.Status - 1; if (sentWait) { t.BackingSocket.Close(1); } else { t.BackingSocket.Close(); } c++; continue; } catch (SocketException e) { string log = $"Socket.Close error: {e.GetType()}/{e.Message}. This is expected when the socket is already closed by remote host."; Logger.Log(log); } catch (ObjectDisposedException e) { string log = $"Socket.Close error: {e.GetType()}/{e.Message}. This is expected when the socket is already disposed."; Logger.Log(log); } catch (Exception e) { Logger.Log(e); } // If there was an error closing the socket: if ((int)t.Status > -5) { notClosedSockets.Add(t); // Try to give a few times to close the socket later on. } } } TcpSockets = notClosedSockets; } Logger.LogDebug("********** Sockets Closed: " + c.ToString(CultureInfo.CurrentCulture)); } } catch (Exception e) { Logger.Log(e); } }, TimeSpan.FromSeconds(3), true); } catch (Exception e) { Logger.Log(e); } finally { Common.ReleaseSocketMutex(); } if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop) { try { IpcHelper.CreateIpcServer(true); } catch (Exception e) { Logger.Log(e); } } } private Exception CreateSocket() { try { skMessageServer = new TcpServer(TcpPort + 1, new ParameterizedThreadStart(TCPServerThread)); skClipboardServer = new TcpServer(TcpPort, new ParameterizedThreadStart(AcceptConnectionAndSendClipboardData)); } catch (SocketException e) { Logger.Log(e); return e; } catch (Exception e) { Logger.Log(e); return e; } Logger.LogDebug("=================================================="); return null; } private static int TcpSendData(TcpSk tcp, byte[] bytes) { Stream encryptedStream = tcp.EncryptedStream; if (tcp.BackingSocket == null || !tcp.BackingSocket.Connected || encryptedStream == null) { string log = $"{nameof(TcpSendData)}: The socket is no longer connected, it could have been closed by the remote host."; Logger.Log(log); throw new ExpectedSocketException(log); } bytes[3] = (byte)((Common.MagicNumber >> 24) & 0xFF); bytes[2] = (byte)((Common.MagicNumber >> 16) & 0xFF); bytes[1] = 0; for (int i = 2; i < Common.PACKAGE_SIZE; i++) { bytes[1] = (byte)(bytes[1] + bytes[i]); } try { encryptedStream.Write(bytes, 0, bytes.Length); } catch (IOException e) { string log = $"{nameof(TcpSendData)}: Exception writing to the socket: {tcp.MachineName}: {e.InnerException?.GetType()}/{e.Message}. (This is expected when the remote machine closes the connection during desktop switch or reconnection.)"; Logger.Log(log); throw e.InnerException is SocketException se ? new ExpectedSocketException(se) : new ExpectedSocketException(log); } return bytes.Length; } private static void ProcessReceivedDataEx(byte[] buf) { int magic; byte checksum = 0; magic = (buf[3] << 24) + (buf[2] << 16); if (magic != (Common.MagicNumber & 0xFFFF0000)) { Logger.Log("Magic number invalid!"); buf[0] = (byte)PackageType.Invalid; } for (int i = 2; i < Common.PACKAGE_SIZE; i++) { checksum = (byte)(checksum + buf[i]); } if (buf[1] != checksum) { Logger.Log("Checksum invalid!"); buf[0] = (byte)PackageType.Invalid; } buf[3] = buf[2] = buf[1] = 0; } internal static DATA TcpReceiveData(TcpSk tcp, out int bytesReceived) { byte[] buf = new byte[Common.PACKAGE_SIZE_EX]; Stream decryptedStream = tcp.DecryptedStream; if (tcp.BackingSocket == null || !tcp.BackingSocket.Connected || decryptedStream == null) { string log = $"{nameof(TcpReceiveData)}: The socket is no longer connected, it could have been closed by the remote host."; Logger.Log(log); throw new ExpectedSocketException(log); } DATA package; try { bytesReceived = decryptedStream.ReadEx(buf, 0, Common.PACKAGE_SIZE); if (bytesReceived != Common.PACKAGE_SIZE) { buf[0] = bytesReceived == 0 ? (byte)PackageType.Error : (byte)PackageType.Invalid; } else { ProcessReceivedDataEx(buf); } package = new DATA(buf); if (package.IsBigPackage) { bytesReceived = decryptedStream.ReadEx(buf, Common.PACKAGE_SIZE, Common.PACKAGE_SIZE); if (bytesReceived != Common.PACKAGE_SIZE) { buf[0] = bytesReceived == 0 ? (byte)PackageType.Error : (byte)PackageType.Invalid; } else { package.Bytes = buf; } } } catch (IOException e) { string log = $"{nameof(TcpReceiveData)}: Exception reading from the socket: {tcp.MachineName}: {e.InnerException?.GetType()}/{e.Message}. (This is expected when the remote machine closes the connection during desktop switch or reconnection.)"; Logger.Log(log); throw e.InnerException is SocketException se ? new ExpectedSocketException(se) : new ExpectedSocketException(log); } return package; } private static void PreProcessData(PackageType type) { switch (type) { case PackageType.Keyboard: Common.PackageSent.Keyboard++; break; case PackageType.Mouse: Common.PackageSent.Mouse++; break; case PackageType.Heartbeat: case PackageType.Heartbeat_ex: Common.PackageSent.Heartbeat++; break; case PackageType.Hello: Common.PackageSent.Hello++; break; case PackageType.ByeBye: Common.PackageSent.ByeBye++; break; case PackageType.Matrix: Common.PackageSent.Matrix++; break; default: byte subtype = (byte)((uint)type & 0x000000FF); switch (subtype) { case (byte)PackageType.ClipboardText: Common.PackageSent.ClipboardText++; break; case (byte)PackageType.ClipboardImage: Common.PackageSent.ClipboardImage++; break; default: // Common.Log("Send: Other type (1-17)"); break; } break; } } internal int TcpSend(TcpSk tcp, DATA data) { PreProcessData(data.Type); if (data.Src == ID.NONE) { data.Src = Common.MachineID; } byte[] dataAsBytes = data.Bytes; int rv = TcpSendData(tcp, dataAsBytes); if (rv < dataAsBytes.Length) { Logger.Log("TcpSend error! Length of sent data is unexpected."); UpdateTcpSockets(tcp, SocketStatus.SendError); throw new SocketException((int)SocketStatus.SendError); } return rv; } private void TCPServerThread(object param) { // SuppressFlow fixes an issue on service mode, where the helper process can't get enough permissions to be started again. // More details can be found on: https://github.com/microsoft/PowerToys/pull/36892 using var asyncFlowControl = ExecutionContext.SuppressFlow(); try { TcpListener server = param as TcpListener; do { Logger.LogDebug("TCPServerThread: Waiting for request..."); Socket s = server.AcceptSocket(); _ = Task.Run(() => { try { AddSocket(s); } catch (Exception e) { Logger.Log(e); } }); } while (true); } catch (InvalidOperationException e) { string log = $"TCPServerThread.AcceptSocket: The server socket could have been closed. {e.Message}"; Logger.Log(log); } catch (SocketException e) { if (e.ErrorCode == (int)SocketError.Interrupted) { Logger.Log("TCPServerThread.AcceptSocket: A blocking socket call was canceled."); } else { Logger.Log(e); } } catch (Exception e) { Logger.Log(e); } } private static string GetMachineNameFromSocket(Socket socket) { string stringIP = socket.RemoteEndPoint.ToString(); string name = null; try { // Remote machine has IP changed, update it. name = Dns.GetHostEntry((socket.RemoteEndPoint as IPEndPoint).Address).HostName; } catch (SocketException e) { Logger.Log($"{nameof(GetMachineNameFromSocket)}: {e.Message}"); return stringIP; } // Remove the domain part. if (!string.IsNullOrEmpty(name)) { int dotPos = name.IndexOf('.'); if (dotPos > 0) { Logger.LogDebug("Removing domain part from the full machine name: {0}.", name); name = name[..dotPos]; } } return string.IsNullOrEmpty(name) ? stringIP : name; } private void AddSocket(Socket s) { string machineName = GetMachineNameFromSocket(s); Logger.Log($"New connection from client: [{machineName}]."); TcpSk tcp = AddTcpSocket(false, s, SocketStatus.Connecting, machineName); StartNewTcpServer(tcp, machineName); } private void StartNewTcpServer(TcpSk tcp, string machineName) { void ServerThread() { // SuppressFlow fixes an issue on service mode, where the helper process can't get enough permissions to be started again. // More details can be found on: https://github.com/microsoft/PowerToys/pull/36892 using var asyncFlowControl = ExecutionContext.SuppressFlow(); try { // Receiving packages MainTCPRoutine(tcp, machineName, false); } catch (Exception e) { Logger.Log(e); } } Thread t = new(ServerThread, "TCP Server Thread " + tcp.BackingSocket.LocalEndPoint.ToString() + " : " + machineName); t.SetApartmentState(ApartmentState.STA); t.Start(); } internal void UpdateTCPClients() { if (InvalidKeyFound) { return; } Logger.LogDebug("!!!!! UpdateTCPClients !!!!!"); try { if (Common.MachineMatrix != null) { Logger.LogDebug("MachineMatrix = " + string.Join(", ", Common.MachineMatrix)); foreach (string st in Common.MachineMatrix) { string machineName = st.Trim(); if (!string.IsNullOrEmpty(machineName) && !machineName.Equals(Common.MachineName.Trim(), StringComparison.OrdinalIgnoreCase)) { bool found = false; found = Common.IsConnectedByAClientSocketTo(machineName); if (found) { Logger.LogDebug(machineName + " is already connected! ^^^^^^^^^^^^^^^^^^^^^"); continue; } StartNewTcpClient(machineName); } } } } catch (Exception e) { Logger.Log(e); } } private static readonly Dictionary> BadIPs = new(); private static readonly Lock BadIPsLock = new(); private static bool IsBadIP(string machineName, IPAddress ip) { lock (BadIPsLock) { return BadIPs.ContainsKey(machineName) && BadIPs.TryGetValue(machineName, out List ips) && ips.Contains(ip); } } private static void AddBadIP(string machineName, IPAddress ip) { if (!IsBadIP(machineName, ip)) { lock (BadIPsLock) { List ips; if (BadIPs.ContainsKey(machineName)) { _ = BadIPs.TryGetValue(machineName, out ips); } else { ips = new List(); BadIPs.Add(machineName, ips); } ips.Add(ip); } } } internal static void ClearBadIPs() { lock (BadIPsLock) { if (BadIPs.Count > 0) { BadIPs.Clear(); } } } internal void StartNewTcpClient(string machineName) { void ClientThread(object obj) { // SuppressFlow fixes an issue on service mode, where the helper process can't get enough permissions to be started again. // More details can be found on: https://github.com/microsoft/PowerToys/pull/36892 using var asyncFlowControl = ExecutionContext.SuppressFlow(); IPHostEntry host; bool useName2IP = false; List validAddresses = new(); List validatedAddresses = new(); string validAddressesSt = string.Empty; // Add a dummy socket to show the status. Socket dummySocket = new(AddressFamily.Unspecified, SocketType.Stream, ProtocolType.Tcp); TcpSk dummyTcp = AddTcpSocket(true, dummySocket, SocketStatus.Resolving, machineName); Logger.LogDebug("Connecting to: " + machineName); if (!string.IsNullOrEmpty(Setting.Values.Name2IP)) { string combinedName2ipList = Setting.Values.Name2IpPolicyList + Separator + Setting.Values.Name2IP; string[] name2ip = combinedName2ipList.Split(Separator, StringSplitOptions.RemoveEmptyEntries); string[] nameNip; if (name2ip != null) { foreach (string st in name2ip) { nameNip = st.Split(BlankSeparator, StringSplitOptions.RemoveEmptyEntries); if (nameNip != null && nameNip.Length >= 2 && nameNip[0].Trim().Equals(machineName, StringComparison.OrdinalIgnoreCase) && IPAddress.TryParse(nameNip[1].Trim(), out IPAddress ip) && !validAddressesSt.Contains("[" + ip.ToString() + "]") ) { validatedAddresses.Add(ip); validAddressesSt += "[" + ip.ToString() + "]"; } } } if (validatedAddresses.Count > 0) { useName2IP = true; Logger.LogDebug("Using both user-defined Name-to-IP mappings and DNS result for " + machineName); Common.ShowToolTip("为 " + machineName + " 同时使用用户自定义 IP 映射和 DNS 结果", 3000, ToolTipIcon.Info, false); if (!CheckForSameSubNet(validatedAddresses, machineName)) { return; } foreach (IPAddress vip in validatedAddresses) { StartNewTcpClientThread(machineName, vip); } validatedAddresses.Clear(); } } try { host = Dns.GetHostEntry(machineName); } catch (SocketException e) { host = null; UpdateTcpSockets(dummyTcp, SocketStatus.Timeout); Common.ShowToolTip(e.Message + ": " + machineName, 10000, ToolTipIcon.Warning, Setting.Values.ShowClipNetStatus); Logger.Log($"{nameof(StartNewTcpClient)}.{nameof(Dns.GetHostEntry)}: {e.Message}"); } UpdateTcpSockets(dummyTcp, SocketStatus.NA); if (!Common.InMachineMatrix(machineName)) { // While Resolving from name to IP, user may have changed the machine name and clicked on Apply. return; } if (host != null) { string ipLog = string.Empty; foreach (IPAddress ip in host.AddressList) { ipLog += "<" + ip.ToString() + ">"; if ((ip.AddressFamily == AddressFamily.InterNetwork || ip.AddressFamily == AddressFamily.InterNetworkV6) && !validAddressesSt.Contains("[" + ip.ToString() + "]")) { validAddresses.Add(ip); validAddressesSt += "[" + ip.ToString() + "]"; } } Logger.LogDebug(machineName + ipLog); } if (validAddresses.Count > 0) { if (!Setting.Values.ReverseLookup) { validatedAddresses = validAddresses; ClearBadIPs(); } else { foreach (IPAddress ip in validAddresses) { if (IsBadIP(machineName, ip)) { Logger.Log($"Skip bad IP address: {ip}"); continue; } try { // Reverse lookup to validate the IP Address. string hn = Dns.GetHostEntry(ip).HostName; if (hn.StartsWith(machineName, StringComparison.CurrentCultureIgnoreCase) || hn.Equals(ip.ToString(), StringComparison.OrdinalIgnoreCase)) { validatedAddresses.Add(ip); } else { Logger.Log($"DNS information of machine not matched: {machineName} => {ip} => {hn}."); AddBadIP(machineName, ip); } } catch (SocketException se) { Logger.Log($"{nameof(StartNewTcpClient)}: DNS information of machine not matched: {machineName} => {ip} => {se.Message}."); AddBadIP(machineName, ip); } catch (ArgumentException ae) { Logger.Log($"{nameof(StartNewTcpClient)}: DNS information of machine not matched: {machineName} => {ip} => {ae.Message}."); AddBadIP(machineName, ip); } } } } if (validatedAddresses.Count > 0) { if (!CheckForSameSubNet(validatedAddresses, machineName)) { return; } foreach (IPAddress ip in validatedAddresses) { StartNewTcpClientThread(machineName, ip); } } else { Logger.Log("Cannot resolve IPv4 Addresses of machine: " + machineName); if (!useName2IP) { Common.ShowToolTip($"无法解析对方电脑的 IP 地址: {machineName}.\r\n请解决 DNS 问题,或者手动设置 IP 地址映射.", 10000, ToolTipIcon.Warning, Setting.Values.ShowClipNetStatus); } } } Thread t = new( ClientThread, "StartNewTcpClient." + machineName); t.SetApartmentState(ApartmentState.STA); t.Start(); } private bool CheckForSameSubNet(List validatedAddresses, string machineName) { if (!Setting.Values.SameSubNetOnly) { return true; } // Only support if IPv4 addresses found in both. IEnumerable remoteIPv4Addresses = validatedAddresses.Where(addr => addr?.AddressFamily == AddressFamily.InterNetwork); if (!remoteIPv4Addresses.Any()) { Logger.Log($"No IPv4 resolved from the remote machine: {machineName}."); return true; } List localIPv4Addresses = GetMyIPv4Addresses().ToList(); if (localIPv4Addresses.Count == 0) { Logger.Log($"No IPv4 resolved from the local machine: {Common.MachineName}"); return true; } foreach (IPAddress remote in remoteIPv4Addresses) { foreach (IPAddress local in localIPv4Addresses) { byte[] myIPAddressBytes = local.GetAddressBytes(); byte[] yourIPAddressBytes = remote.GetAddressBytes(); // Same WAN? if (myIPAddressBytes[0] == yourIPAddressBytes[0] && myIPAddressBytes[1] == yourIPAddressBytes[1]) { return true; } } } Logger.Log($"Skip machine not in the same network: {machineName}."); return false; } private IEnumerable GetMyIPv4Addresses() { try { IEnumerable ip4addresses = NetworkInterface.GetAllNetworkInterfaces()? .Where(networkInterface => (networkInterface.NetworkInterfaceType == NetworkInterfaceType.Ethernet || networkInterface.NetworkInterfaceType == NetworkInterfaceType.Wireless80211) && networkInterface.OperationalStatus == OperationalStatus.Up) .SelectMany(ni => ni?.GetIPProperties()?.UnicastAddresses.Select(uni => uni?.Address)) .Where(addr => addr?.AddressFamily == AddressFamily.InterNetwork); return ip4addresses; } catch (Exception e) { Logger.Log(e); return Enumerable.Empty(); } } private void StartNewTcpClientThread(string machineName, IPAddress ip) { void NewTcpClient() { // SuppressFlow fixes an issue on service mode, where the helper process can't get enough permissions to be started again. // More details can be found on: https://github.com/microsoft/PowerToys/pull/36892 using var asyncFlowControl = ExecutionContext.SuppressFlow(); TcpClient tcpClient = null; try { tcpClient = new TcpClient(AddressFamily.InterNetworkV6); tcpClient.Client.DualMode = true; if (Common.IsConnectedByAClientSocketTo(machineName)) { Logger.LogDebug(machineName + " is already connected by another client socket."); return; } if (Common.IsConnectingByAClientSocketTo(machineName, ip)) { Logger.LogDebug($"{machineName}:{ip} is already being connected by another client socket."); return; } TcpSk tcp = AddTcpSocket(true, tcpClient.Client, SocketStatus.Connecting, machineName, ip); // Update the other server socket's machine name based on this corresponding client socket. UpdateTcpSockets(tcp, SocketStatus.Connecting); Logger.LogDebug(string.Format(CultureInfo.CurrentCulture, "=====> Connecting to: {0}:{1}", machineName, ip.ToString())); long timeoutLeft; do { try { tcpClient.Connect(ip, TcpPort + 1); } catch (ObjectDisposedException) { // When user reconnects. Logger.LogDebug($"tcpClient.Connect: The socket has already been disposed: {machineName}:{ip}"); return; } catch (SocketException e) { timeoutLeft = connectTimeout - Common.GetTick(); if (timeoutLeft > 0) { Logger.LogDebug($"tcpClient.Connect: {timeoutLeft}: {e.Message}"); Thread.Sleep(1000); continue; } else { Logger.Log($"tcpClient.Connect: Unable to connect after a timeout: {machineName}:{ip} : {e.Message}"); string message = $"连接超时: {machineName}:{ip}"; Common.ShowToolTip(message, 5000, ToolTipIcon.Warning, Setting.Values.ShowClipNetStatus); UpdateTcpSockets(tcp, SocketStatus.Timeout); return; } } break; } while (true); Logger.LogDebug($"=====> Connected: {tcpClient.Client.LocalEndPoint} => {machineName}: {ip}"); // Sending/Receiving packages MainTCPRoutine(tcp, machineName, true); } catch (ObjectDisposedException e) { Logger.Log($"{nameof(StartNewTcpClientThread)}: The socket could have been closed/disposed due to machine switch: {e.Message}"); } catch (SocketException e) { // DHCP error, etc. string localIP = tcpClient?.Client?.LocalEndPoint?.ToString(); if (localIP != null && (localIP.StartsWith("169.254", StringComparison.InvariantCulture) || localIP.ToString().StartsWith("0.0", StringComparison.InvariantCulture))) { Common.ShowToolTip($"错误: 该机器对于 [{localIP}] 的连接性有限.", 5000, ToolTipIcon.Warning, Setting.Values.ShowClipNetStatus); } else { Logger.TelemetryLogTrace($"{nameof(StartNewTcpClientThread)}: Error: {e.Message} on the IP Address: {localIP}", SeverityLevel.Error); } } catch (Exception e) { Logger.Log(e); } } Thread t = new(NewTcpClient, "TCP Client Thread " + machineName + " " + ip.ToString()); t.SetApartmentState(ApartmentState.STA); t.Start(); } private void FlagReopenSocketIfNeeded(Exception e) { /* SCENARIO: MachineA has MM blocked by firewall but MachineA can connect to MachineB so the tool would work normally (MM is not blocked by firewall in MachineB). * 1. a connection from A to B is working. Mouse/Keyboard is connected to A. * 2. User moves Mouse to B and lock B by Ctrl+Alt+L. * 3. B closes all sockets before switches to logon desktop. The client socket in A gets reset by B (the only connection between A and B). * 4. B is now on the logon desktop and tries to connect to A, connection fails since it is block by firewall in A. * 5. When the client socket gets reset in A, it should retry to connect to B => this is the fix implemented by few lines of code below. * */ // WSAECONNRESET if (e is ExpectedSocketException se && se.ShouldReconnect) { Common.PleaseReopenSocket = Common.REOPEN_WHEN_WSAECONNRESET; Logger.Log($"MainTCPRoutine: {nameof(FlagReopenSocketIfNeeded)}"); } } private long lastRemoteMachineID; internal static readonly string[] Separator = new string[] { "\r\n" }; internal static readonly char[] BlankSeparator = new char[] { ' ' }; private void MainTCPRoutine(TcpSk tcp, string machineName, bool isClient) { int packageCount = 0; DATA d; string remoteMachine = string.Empty; string strIP = string.Empty; ID remoteID = ID.NONE; byte[] buf = RandomNumberGenerator.GetBytes(Common.PACKAGE_SIZE_EX); d = new DATA(buf); TcpSk currentTcp = tcp; Socket currentSocket = currentTcp.BackingSocket; if (currentSocket == null) { Logger.LogDebug($"{nameof(MainTCPRoutine)}: The socket could have been closed/disposed by other threads."); return; } try { currentSocket.SendBufferSize = Common.PACKAGE_SIZE * 10000; currentSocket.ReceiveBufferSize = Common.PACKAGE_SIZE * 10000; currentSocket.NoDelay = true; // This is very interesting to know:( currentSocket.SendTimeout = 500; d.MachineName = Common.MachineName; d.Type = PackageType.Handshake; for (int i = 0; i < 10; i++) { _ = TcpSend(currentTcp, d); } d.Machine1 = ~d.Machine1; d.Machine2 = ~d.Machine2; d.Machine3 = ~d.Machine3; d.Machine4 = ~d.Machine4; UpdateTcpSockets(currentTcp, SocketStatus.Handshaking); strIP = Common.GetRemoteStringIP(currentSocket, true); remoteMachine = string.IsNullOrEmpty(machineName) ? GetMachineNameFromSocket(currentSocket) : machineName; Logger.LogDebug($"MainTCPRoutine: Remote machineName/IP = {remoteMachine}/{strIP}"); } catch (ObjectDisposedException e) { Common.PleaseReopenSocket = Common.REOPEN_WHEN_WSAECONNRESET; UpdateTcpSockets(currentTcp, SocketStatus.ForceClosed); currentSocket.Close(); Logger.Log($"{nameof(MainTCPRoutine)}: The socket could have been closed/disposed by other threads: {e.Message}"); } catch (Exception e) { UpdateTcpSockets(currentTcp, SocketStatus.ForceClosed); FlagReopenSocketIfNeeded(e); currentSocket.Close(); Logger.Log(e); } int errCount = 0; while (true) { try { DATA package = TcpReceiveData(currentTcp, out int receivedCount); remoteID = package.Src; if (package.Type == PackageType.Error) { errCount++; string log = $"{nameof(MainTCPRoutine)}.TcpReceive error, invalid package from {remoteMachine}: {receivedCount}"; Logger.Log(log); if (receivedCount > 0) { Common.ShowToolTip($"来自 {remoteMachine} 的数据包无效. 请确保连接密码一致.", 5000, ToolTipIcon.Warning, false); } if (errCount > 5) { Common.MMSleep(1); UpdateTcpSockets(currentTcp, SocketStatus.Error); currentSocket.Close(); /* * Sometimes when the peer machine closes the connection, we do not actually get an exception. * Socket status is still connected and a read on the socket stream returns 0 byte. * In this case, we should give ONE try to reconnect. */ if (Common.ReopenSocketDueToReadError) { Common.PleaseReopenSocket = Common.REOPEN_WHEN_WSAECONNRESET; Common.ReopenSocketDueToReadError = false; } break; } } else { errCount = 0; } if (package.Type == PackageType.Handshake) { // Common.Log("Got a Handshake signal!"); package.Type = PackageType.HandshakeAck; package.Src = ID.NONE; package.MachineName = Common.MachineName; package.Machine1 = ~package.Machine1; package.Machine2 = ~package.Machine2; package.Machine3 = ~package.Machine3; package.Machine4 = ~package.Machine4; _ = TcpSend(currentTcp, package); } else { if (packageCount >= 0) { if (++packageCount >= 10) { // Common.ShowToolTip("Invalid Security Key from " + remoteMachine, 5000); Logger.Log("More than 10 invalid packages received!"); package.Type = PackageType.Invalid; for (int i = 0; i < 10; i++) { _ = TcpSend(currentTcp, package); } Common.MMSleep(2); UpdateTcpSockets(currentTcp, SocketStatus.InvalidKey); currentSocket.Close(); break; } else if (package.Type == PackageType.HandshakeAck) { if (package.Machine1 == d.Machine1 && package.Machine2 == d.Machine2 && package.Machine3 == d.Machine3 && package.Machine4 == d.Machine4) { string claimedMachineName = package.MachineName; if (!remoteMachine.Equals(claimedMachineName, StringComparison.Ordinal)) { Logger.LogDebug($"DNS.RemoteMachineName({remoteMachine}) <> Claimed.MachineName({claimedMachineName}), using the claimed machine name."); remoteMachine = claimedMachineName; currentTcp.MachineName = remoteMachine; } // Double check to avoid a redundant client socket. if (isClient && Common.IsConnectedByAClientSocketTo(remoteMachine)) { Logger.LogDebug("=====> Duplicate connected client socket for: " + remoteMachine + ":" + strIP + " is being removed."); UpdateTcpSockets(currentTcp, SocketStatus.ForceClosed); currentSocket.Close(); return; } if (remoteMachine.Equals(Common.MachineName, StringComparison.OrdinalIgnoreCase)) { Logger.LogDebug("Connected to/from local socket: " + strIP + (isClient ? "-Client" : "-Server")); UpdateTcpSockets(currentTcp, SocketStatus.NA); Common.MMSleep(1); currentSocket.Close(); return; } packageCount = -1; // Trusted InvalidKeyFound = false; currentTcp.MachineId = (uint)remoteID; currentTcp.Status = SocketStatus.Connected; UpdateTcpSockets(currentTcp, SocketStatus.Connected); Logger.LogDebug("))))))))))))))) Machine got trusted: " + remoteMachine + ":" + strIP + ", Is client: " + isClient); if (Math.Abs(Common.GetTick() - Common.LastReconnectByHotKeyTime) < 5000) { Common.ShowToolTip("已连接 " + remoteMachine, 1000, ToolTipIcon.Info, Setting.Values.ShowClipNetStatus); } Common.SendHeartBeat(initial: true); if (Common.MachinePool.TryFindMachineByName(remoteMachine, out MachineInf machineInfo)) { Logger.LogDebug("Machine updated: " + remoteMachine + "/" + remoteID.ToString()); if (machineInfo.Name.Equals(Common.DesMachineName, StringComparison.OrdinalIgnoreCase)) { Logger.LogDebug("Des ID updated: " + Common.DesMachineID.ToString() + "/" + remoteID.ToString()); Common.NewDesMachineID = Common.DesMachineID = remoteID; } _ = Common.MachinePool.TryUpdateMachineID(remoteMachine, remoteID, true); Common.UpdateMachinePoolStringSetting(); } else { Logger.LogDebug("New machine connected: {0}.", remoteMachine); if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop) { Common.ShowToolTip("已建立新连接 " + remoteMachine, 1000, ToolTipIcon.Info, Setting.Values.ShowClipNetStatus); } } if (!isClient) { Common.UpdateClientSockets("MainTCPRoutine"); } } else { Logger.LogDebug("Invalid ACK from " + remoteMachine); UpdateTcpSockets(currentTcp, SocketStatus.InvalidKey); string remoteEP = currentSocket.RemoteEndPoint.ToString(); if (FailedAttempt.AddOrUpdate(remoteEP, 1, (key, value) => value + 1) > 10) { _ = FailedAttempt.AddOrUpdate(remoteEP, 0, (key, value) => 0); _ = MessageBox.Show($"来自 [{remoteEP}] 的连接尝试次数过多!\r\n重启软件后再试.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error); Common.MainForm.Quit(true, false); } currentSocket.Close(); break; } } else if (package.Type == PackageType.Mouse) { if (packageCount > 5) { packageCount--; } } else if (package.Type is PackageType.Heartbeat or PackageType.Heartbeat_ex) { if (packageCount > 5) { packageCount--; } } else { if (packageCount > 5) { UpdateTcpSockets(currentTcp, SocketStatus.InvalidKey); } else { Logger.Log(string.Format( CultureInfo.CurrentCulture, "Unexpected package, size = {0}, type = {1}", receivedCount, package.Type)); } } } else if (receivedCount > 0) { // Add some log when remote machine switches. if (lastRemoteMachineID != (long)remoteID) { _ = Interlocked.Exchange(ref lastRemoteMachineID, (long)remoteID); Logger.LogDebug($"MainTCPRoutine: Remote machine = {strIP}/{lastRemoteMachineID}"); } if (package.Type == PackageType.HandshakeAck) { Logger.LogDebug("Skipping the rest of the Handshake packages."); } else { Receiver.ProcessPackage(package, currentTcp); } } } } catch (Exception e) { UpdateTcpSockets(currentTcp, SocketStatus.Error); FlagReopenSocketIfNeeded(e); currentSocket.Close(); Logger.Log(e); break; } } if (remoteID != ID.NONE) { _ = Common.RemoveDeadMachines(remoteID); } } private static void AcceptConnectionAndSendClipboardData(object param) { // SuppressFlow fixes an issue on service mode, where the helper process can't get enough permissions to be started again. // More details can be found on: https://github.com/microsoft/PowerToys/pull/36892 using var asyncFlowControl = ExecutionContext.SuppressFlow(); TcpListener server = param as TcpListener; do { Logger.LogDebug("SendClipboardData: Waiting for request..."); Socket s = null; try { s = server.AcceptSocket(); } catch (InvalidOperationException e) { Logger.Log($"The clipboard socket could have been closed. {e.Message}"); break; } catch (SocketException e) { if (e.ErrorCode == (int)SocketError.Interrupted) { Logger.Log("server.AcceptSocket: A blocking socket call was canceled."); continue; } else { Logger.Log(e); break; } } catch (Exception e) { Logger.Log(e); break; } if (s != null) { try { new Task(() => { // SuppressFlow fixes an issue on service mode, where the helper process can't get enough permissions to be started again. // More details can be found on: https://github.com/microsoft/PowerToys/pull/36892 using var asyncFlowControl = ExecutionContext.SuppressFlow(); System.Threading.Thread thread = Thread.CurrentThread; thread.Name = $"{nameof(SendOrReceiveClipboardData)}.{thread.ManagedThreadId}"; Thread.UpdateThreads(thread); SendOrReceiveClipboardData(s); }).Start(); } catch (Exception e) { Logger.Log(e); } } } while (true); } internal static void SendOrReceiveClipboardData(Socket s) { try { string remoteEndPoint = s.RemoteEndPoint.ToString(); Logger.LogDebug("SendClipboardData: Request accepted: " + s.LocalEndPoint.ToString() + "/" + remoteEndPoint); Common.IsDropping = false; Common.IsDragging = false; Common.DragMachine = (ID)1; bool clientPushData = true; ClipboardPostAction postAction = ClipboardPostAction.Other; bool handShaken = Common.ShakeHand(ref remoteEndPoint, s, out Stream enStream, out Stream deStream, ref clientPushData, ref postAction); if (!handShaken) { s.Close(); return; } else { Logger.LogDebug($"{nameof(SendOrReceiveClipboardData)}: Clipboard connection accepted: " + remoteEndPoint); Common.SetToggleIcon(new int[Common.TOGGLE_ICONS_SIZE] { Common.ICON_SMALL_CLIPBOARD, -1, -1, -1 }); } if (clientPushData) { Common.ReceiveAndProcessClipboardData(remoteEndPoint, s, enStream, deStream, $"{postAction}"); } else { SendClipboardData(s, enStream); } } catch (Exception e) { Logger.Log(e); } } internal static void SendClipboardData(Socket s, Stream ecStream) { if (Common.RunWithNoAdminRight && Setting.Values.OneWayClipboardMode) { s?.Close(); return; } const int CLOSE_TIMEOUT = 10; byte[] header = new byte[1024]; string headerString = string.Empty; if (Common.LastDragDropFile != null) { string fileName = null; if (!Common.ImpersonateLoggedOnUserAndDoSomething(() => { if (!File.Exists(Common.LastDragDropFile)) { headerString = Directory.Exists(Common.LastDragDropFile) ? $"{0}*{Common.LastDragDropFile} - Folder is not supported, zip it first!" : Common.LastDragDropFile.Contains("- File too big") ? $"{0}*{Common.LastDragDropFile}" : $"{0}*{Common.LastDragDropFile} not found!"; } else { fileName = Common.LastDragDropFile; headerString = $"{new FileInfo(fileName).Length}*{fileName}"; } })) { s?.Close(); return; } Common.GetBytesU(headerString).CopyTo(header, 0); try { ecStream.Write(header, 0, header.Length); if (!string.IsNullOrEmpty(fileName)) { if (SendFile(s, ecStream, fileName)) { s.Close(CLOSE_TIMEOUT); } } else { s.Close(CLOSE_TIMEOUT); } } catch (IOException e) { string log = $"{nameof(SendClipboardData)}: Exception accessing the socket: {e.InnerException?.GetType()}/{e.Message}. (This is expected when the remote machine closes the connection during desktop switch or reconnection.)"; Logger.Log(log); } catch (SocketException e) { string log = $"{nameof(SendClipboardData)}: {e.GetType()}/{e.Message}. This is expected when the connection is closed by the remote host."; Logger.Log(log); } catch (ObjectDisposedException e) { string log = $"{nameof(SendClipboardData)}: {e.GetType()}/{e.Message}. This is expected when the socket is disposed by a machine switch for ex.."; Logger.Log(log); } } else if (!Common.IsClipboardDataImage && Common.LastClipboardData != null) { try { byte[] data = Common.LastClipboardData; headerString = $"{data.Length}*{"text"}"; Common.GetBytesU(headerString).CopyTo(header, 0); if (data != null) { ecStream.Write(header, 0, header.Length); _ = SendData(s, ecStream, data); Logger.LogDebug("Text sent: " + data.Length.ToString(CultureInfo.CurrentCulture)); } s.Close(CLOSE_TIMEOUT); } catch (IOException e) { string log = $"{nameof(SendClipboardData)}: Exception accessing the socket: {e.InnerException?.GetType()}/{e.Message}. (This is expected when the remote machine closes the connection during desktop switch or reconnection.)"; Logger.Log(log); } catch (SocketException e) { string log = $"{nameof(SendClipboardData)}: {e.GetType()}/{e.Message}. This is expected when the connection is closed by the remote host."; Logger.Log(log); } catch (ObjectDisposedException e) { string log = $"{nameof(SendClipboardData)}: {e.GetType()}/{e.Message}. This is expected when the socket is disposed by a machine switch for ex.."; Logger.Log(log); } } else if (Common.LastClipboardData != null && Common.LastClipboardData.Length > 0) { byte[] data = Common.LastClipboardData; headerString = $"{data.Length}*{"image"}"; Common.GetBytesU(headerString).CopyTo(header, 0); try { ecStream.Write(header, 0, header.Length); _ = SendData(s, ecStream, data); Logger.LogDebug("Image sent: " + data.Length.ToString(CultureInfo.CurrentCulture)); s.Close(CLOSE_TIMEOUT); } catch (IOException e) { string log = $"{nameof(SendClipboardData)}: Exception accessing the socket: {e.InnerException?.GetType()}/{e.Message}. (This is expected when the remote machine closes the connection during desktop switch or reconnection.)"; Logger.Log(log); } catch (SocketException e) { string log = $"{nameof(SendClipboardData)}: {e.GetType()}/{e.Message}. This is expected when the connection is closed by the remote host."; Logger.Log(log); } catch (ObjectDisposedException e) { string log = $"{nameof(SendClipboardData)}: {e.GetType()}/{e.Message}. This is expected when the socket is disposed by a machine switch for ex.."; Logger.Log(log); } } else { Logger.Log("No data available in clipboard or LastDragDropFile!"); s.Close(); } } private static bool SendFileEx(Socket s, Stream ecStream, string fileName) { try { using (FileStream f = File.OpenRead(fileName)) { byte[] buf = new byte[Common.NETWORK_STREAM_BUF_SIZE]; int rv, sentCount = 0; do { if ((rv = f.Read(buf, 0, Common.NETWORK_STREAM_BUF_SIZE)) > 0) { ecStream.Write(buf, 0, rv); sentCount += rv; } } while (rv > 0); if ((rv = Common.PACKAGE_SIZE - (sentCount % Common.PACKAGE_SIZE)) > 0) { Array.Clear(buf, 0, buf.Length); ecStream.Write(buf, 0, rv); } ecStream.Flush(); Logger.LogDebug("File sent: " + fileName); } return true; } catch (Exception e) { if (e is IOException) { string log = $"{nameof(SendFileEx)}: Exception accessing the socket: {e.InnerException?.GetType()}/{e.Message}. (This is expected when the remote machine closes the connection during desktop switch or reconnection.)"; Logger.Log(log); } else { Logger.Log(e); } Common.ShowToolTip(e.Message, 1000, ToolTipIcon.Warning, Setting.Values.ShowClipNetStatus); s.Close(); } return false; } private static bool SendFile(Socket s, Stream ecStream, string fileName) { bool r = false; if (Common.RunOnLogonDesktop || Common.RunOnScrSaverDesktop) { if (fileName.StartsWith(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles) + @"\" + Common.BinaryName + @"\ScreenCaptures\", StringComparison.CurrentCultureIgnoreCase)) { r = SendFileEx(s, ecStream, fileName); } } else { _ = Common.ImpersonateLoggedOnUserAndDoSomething(() => { r = SendFileEx(s, ecStream, fileName); }); } return r; } private static bool SendData(Socket s, Stream ecStream, byte[] data) { bool r = false; try { using MemoryStream f = new(data); byte[] buf = new byte[Common.NETWORK_STREAM_BUF_SIZE]; int rv, sentCount = 0; do { if ((rv = f.Read(buf, 0, Common.NETWORK_STREAM_BUF_SIZE)) > 0) { ecStream.Write(buf, 0, rv); sentCount += rv; } } while (rv > 0); if ((rv = sentCount % Common.PACKAGE_SIZE) > 0) { Array.Clear(buf, 0, buf.Length); ecStream.Write(buf, 0, rv); } ecStream.Flush(); Logger.LogDebug("Data sent: " + data.Length.ToString(CultureInfo.InvariantCulture)); r = true; } catch (Exception e) { if (e is IOException) { string log = $"{nameof(SendData)}: Exception accessing the socket: {e.InnerException?.GetType()}/{e.Message}. (This is expected when the remote machine closes the connection during desktop switch or reconnection.)"; Logger.Log(log); } else { Logger.Log(e); } Common.ShowToolTip(e.Message, 1000, ToolTipIcon.Warning, Setting.Values.ShowClipNetStatus); ecStream.Close(); s.Close(); } return r; } private TcpSk AddTcpSocket(bool isClient, Socket s, SocketStatus status, string machineName) { Common.CloseAnUnusedSocket(); TcpSk tcp = new(isClient, s, status, machineName); lock (TcpSocketsLock) { #if ENABLE_LEGACY_DOS_PROTECTION PreventDoS(TcpSockets); #endif TcpSockets.Add(tcp); } return tcp; } private TcpSk AddTcpSocket(bool isClient, Socket s, SocketStatus status, string machineName, IPAddress ip) { Common.CloseAnUnusedSocket(); TcpSk tcp = new(isClient, s, status, machineName, ip); lock (TcpSocketsLock) { #if ENABLE_LEGACY_DOS_PROTECTION PreventDoS(TcpSockets); #endif TcpSockets.Add(tcp); } return tcp; } private void UpdateTcpSockets(TcpSk tcp, SocketStatus status) { if (status == SocketStatus.InvalidKey) { InvalidKeyFound = true; } InvalidKeyFoundOnClientSocket = tcp.IsClient && InvalidKeyFound; try { lock (TcpSocketsLock) { if (TcpSockets != null) { if (status == SocketStatus.Connected && tcp.IsClient) { List tobeRemovedSockets = TcpSockets; if (tcp.MachineId == Setting.Values.MachineId) { tcp = null; Setting.Values.MachineId = Common.Ran.Next(); Common.UpdateMachineTimeAndID(); Common.PleaseReopenSocket = Common.REOPEN_WHEN_HOTKEY; Logger.TelemetryLogTrace("MachineID conflict.", SeverityLevel.Information); } else { // Keep the first connected one. tobeRemovedSockets = TcpSockets.Where(item => item.IsClient && !ReferenceEquals(item, tcp) && item.MachineName.Equals(tcp.MachineName, StringComparison.OrdinalIgnoreCase)).ToList(); } foreach (TcpSk t in tobeRemovedSockets) { t.Status = SocketStatus.ForceClosed; Logger.LogDebug($"Closing duplicated socket {t.MachineName}: {t.Address}"); } } List toBeRemoved = new(); foreach (TcpSk t in TcpSockets) { if (t.Status is SocketStatus.Error or SocketStatus.ForceClosed or // SocketStatus.InvalidKey or SocketStatus.NA or SocketStatus.Timeout or SocketStatus.SendError) { try { if (t.BackingSocket != null) { t.MachineName = "$*NotUsed*$"; t.Status = t.Status >= 0 ? 0 : t.Status - 1; // If error closing, the socket will be closed again at SocketStuff.Close(). t.BackingSocket.Close(); } toBeRemoved.Add(t); } catch (SocketException e) { string log = $"{nameof(UpdateTcpSockets)}: {e.GetType()}/{e.Message}. This is expected when the connection is closed by the remote host."; Logger.Log(log); } catch (ObjectDisposedException e) { string log = $"{nameof(UpdateTcpSockets)}: {e.GetType()}/{e.Message}. This is expected when the socket is disposed by a machine switch for ex.."; Logger.Log(log); } } } if (tcp != null) { tcp.Status = status; if (status == SocketStatus.Connected) { // Update the socket's machine name based on its corresponding client/server socket. foreach (TcpSk t in TcpSockets) { if (t.MachineId == tcp.MachineId && t.IsClient != tcp.IsClient) { if ((string.IsNullOrEmpty(tcp.MachineName) || tcp.MachineName.Contains('.') || tcp.MachineName.Contains(':')) && !(string.IsNullOrEmpty(t.MachineName) || t.MachineName.Contains('.') || t.MachineName.Contains(':'))) { tcp.MachineName = t.MachineName; } else if ((string.IsNullOrEmpty(t.MachineName) || t.MachineName.Contains('.') || t.MachineName.Contains(':')) && !(string.IsNullOrEmpty(tcp.MachineName) || tcp.MachineName.Contains('.') || tcp.MachineName.Contains(':'))) { t.MachineName = tcp.MachineName; } } } if (string.IsNullOrEmpty(tcp.MachineName) || tcp.MachineName.Contains('.') || tcp.MachineName.Contains(':')) { tcp.MachineName = Common.NameFromID((ID)tcp.MachineId); } if (string.IsNullOrEmpty(tcp.MachineName) || tcp.MachineName.Contains('.') || tcp.MachineName.Contains(':')) { tcp.MachineName = Common.GetRemoteStringIP(tcp.BackingSocket); } } } else { Logger.Log("UpdateTcpSockets.Exception: Socket not found!"); } foreach (TcpSk t in toBeRemoved) { _ = TcpSockets.Remove(t); } } } } catch (Exception e) { Logger.Log(e); } } private void PreventDoS(List sockets) { if (sockets.Count > 100) { TcpSk tcp; try { string msg = Application.ProductName + " has been terminated, too many connections."; for (int i = 0; i < 10; i++) { tcp = sockets[i * 10]; if (tcp != null) { msg += $"\r\n{Common.MachineName}{(tcp.IsClient ? "=>" : "<=")}{tcp.MachineName}:{tcp.Status}"; } } _ = Common.CreateLowIntegrityProcess( "\"" + Path.GetDirectoryName(Application.ExecutablePath) + "\\MouseWithoutBordersHelper.exe\"", "InternalError" + " \"" + msg + "\"", 0, false, 0, (short)ProcessWindowStyle.Hidden); } finally { Common.MainForm.Quit(true, false); } } } } } ================================================ FILE: PowerToys/src/modules/MouseWithoutBorders/App/Control/Machine.cs ================================================ // 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.ComponentModel; using System.Windows.Forms; // // User control, used in the Matrix form. // // // 2008 created by Truong Do (ductdo). // 2009-... modified by Truong Do (TruongDo). // 2023- Included in PowerToys. // using MouseWithoutBorders.Class; using MouseWithoutBorders.Properties; namespace MouseWithoutBorders { internal partial class Machine : UserControl { // private int ip; // private Point mouseDownPos; private SocketStatus statusClient; private SocketStatus statusServer; private bool localhost; internal Machine() { InitializeComponent(); Visible = false; MachineEnabled = false; } [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] internal string MachineName { get => textBoxName.Text; set => textBoxName.Text = value; } [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] internal bool MachineEnabled { get => checkBoxEnabled.Checked; set { checkBoxEnabled.Checked = value; Editable = value; pictureBoxLogo.Image = value ? Images.MachineEnabled : (System.Drawing.Image)Images.MachineDisabled; OnEnabledChanged(EventArgs.Empty); // Borrow this event since we do not use it for any other purpose:) (we can create one but l...:)) } } [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] internal bool Editable { set => textBoxName.Enabled = value; // get { return textBoxName.Enabled; } } [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] internal bool CheckAble { set { if (!value) { checkBoxEnabled.Checked = true; Editable = false; } checkBoxEnabled.Enabled = value; } } [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] internal bool LocalHost { // get { return localhost; } set { localhost = value; if (localhost) { labelStatusClient.Text = "此电脑"; labelStatusServer.Text = "..."; CheckAble = false; } else { labelStatusClient.Text = "..."; labelStatusServer.Text = "..."; CheckAble = true; } } } private void PictureBoxLogo_MouseDown(object sender, MouseEventArgs e) { // mouseDownPos = e.Location; OnMouseDown(e); } /* internal Point MouseDownPos { get { return mouseDownPos; } } */ private void CheckBoxEnabled_CheckedChanged(object sender, EventArgs e) { MachineEnabled = checkBoxEnabled.Checked; } private static string StatusString(SocketStatus status) { string rv = string.Empty; switch (status) { case SocketStatus.Resolving: rv = "正在查找地址"; break; case SocketStatus.Connected: rv = "已连接"; break; case SocketStatus.Connecting: rv = "正在连接"; break; case SocketStatus.Error: rv = "出错"; break; case SocketStatus.ForceClosed: rv = "已关闭"; break; case SocketStatus.Handshaking: rv = "正在握手"; break; case SocketStatus.SendError: rv = "发送错误"; break; case SocketStatus.InvalidKey: rv = "密码错误"; break; case SocketStatus.Timeout: rv = "连接超时"; break; case SocketStatus.NA: rv = "..."; break; default: break; } return rv; } [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] internal SocketStatus StatusClient { get => statusClient; set { statusClient = value; if (statusClient is SocketStatus.Connected or SocketStatus.Handshaking) { Editable = false; } labelStatusClient.Text = StatusString(statusClient) + " -->"; } } [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] internal SocketStatus StatusServer { get => statusServer; set { statusServer = value; if (statusServer is SocketStatus.Connected or SocketStatus.Handshaking) { Editable = false; } labelStatusServer.Text = StatusString(statusServer) + " <--"; } } private void PictureBoxLogo_Paint(object sender, PaintEventArgs e) { // e.Graphics.DrawString("(Draggable)", this.Font, Brushes.WhiteSmoke, 20, 15); } } } ================================================ FILE: PowerToys/src/modules/MouseWithoutBorders/App/Control/Machine.designer.cs ================================================ namespace MouseWithoutBorders { partial class Machine { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.components = new System.ComponentModel.Container(); this.labelStatusServer = new System.Windows.Forms.Label(); this.textBoxName = new System.Windows.Forms.TextBox(); this.checkBoxEnabled = new System.Windows.Forms.CheckBox(); this.pictureBoxLogo = new System.Windows.Forms.PictureBox(); this.labelStatusClient = new System.Windows.Forms.Label(); this.toolTipHelp = new System.Windows.Forms.ToolTip(this.components); ((System.ComponentModel.ISupportInitialize)(this.pictureBoxLogo)).BeginInit(); this.SuspendLayout(); // // labelStatusServer // this.labelStatusServer.Dock = System.Windows.Forms.DockStyle.Bottom; this.labelStatusServer.Location = new System.Drawing.Point(0, 90); this.labelStatusServer.Name = "labelStatusServer"; this.labelStatusServer.Size = new System.Drawing.Size(106, 16); this.labelStatusServer.TabIndex = 0; this.labelStatusServer.Text = "..."; this.labelStatusServer.TextAlign = System.Drawing.ContentAlignment.TopCenter; // // textBoxName // this.textBoxName.Dock = System.Windows.Forms.DockStyle.Bottom; this.textBoxName.Location = new System.Drawing.Point(0, 54); this.textBoxName.Name = "textBoxName"; this.textBoxName.Size = new System.Drawing.Size(106, 20); this.textBoxName.TabIndex = 1; this.textBoxName.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; // // checkBoxEnabled // this.checkBoxEnabled.AutoSize = true; this.checkBoxEnabled.Checked = true; this.checkBoxEnabled.CheckState = System.Windows.Forms.CheckState.Checked; this.checkBoxEnabled.Location = new System.Drawing.Point(91, 40); this.checkBoxEnabled.Name = "checkBoxEnabled"; this.checkBoxEnabled.Size = new System.Drawing.Size(15, 14); this.checkBoxEnabled.TabIndex = 3; this.checkBoxEnabled.UseVisualStyleBackColor = true; this.checkBoxEnabled.CheckedChanged += new System.EventHandler(this.CheckBoxEnabled_CheckedChanged); // // pictureBoxLogo // this.pictureBoxLogo.Dock = System.Windows.Forms.DockStyle.Fill; this.pictureBoxLogo.ErrorImage = null; this.pictureBoxLogo.InitialImage = null; this.pictureBoxLogo.Location = new System.Drawing.Point(0, 0); this.pictureBoxLogo.Name = "pictureBoxLogo"; this.pictureBoxLogo.Size = new System.Drawing.Size(106, 54); this.pictureBoxLogo.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; this.pictureBoxLogo.TabIndex = 2; this.pictureBoxLogo.TabStop = false; this.toolTipHelp.SetToolTip(this.pictureBoxLogo, "拖放改变设备布局,选中输入设备名称。"); this.pictureBoxLogo.Paint += new System.Windows.Forms.PaintEventHandler(this.PictureBoxLogo_Paint); this.pictureBoxLogo.MouseDown += new System.Windows.Forms.MouseEventHandler(this.PictureBoxLogo_MouseDown); // // labelStatusClient // this.labelStatusClient.Dock = System.Windows.Forms.DockStyle.Bottom; this.labelStatusClient.Location = new System.Drawing.Point(0, 74); this.labelStatusClient.Name = "labelStatusClient"; this.labelStatusClient.Size = new System.Drawing.Size(106, 16); this.labelStatusClient.TabIndex = 4; this.labelStatusClient.Text = "..."; this.labelStatusClient.TextAlign = System.Drawing.ContentAlignment.TopCenter; // // Machine // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.BackColor = System.Drawing.Color.Transparent; this.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch; this.Controls.Add(this.checkBoxEnabled); this.Controls.Add(this.pictureBoxLogo); this.Controls.Add(this.textBoxName); this.Controls.Add(this.labelStatusClient); this.Controls.Add(this.labelStatusServer); this.Name = "Machine"; this.Size = new System.Drawing.Size(106, 106); ((System.ComponentModel.ISupportInitialize)(this.pictureBoxLogo)).EndInit(); this.ResumeLayout(false); this.PerformLayout(); } #endregion private System.Windows.Forms.Label labelStatusServer; private System.Windows.Forms.TextBox textBoxName; private System.Windows.Forms.PictureBox pictureBoxLogo; private System.Windows.Forms.CheckBox checkBoxEnabled; private System.Windows.Forms.Label labelStatusClient; private System.Windows.Forms.ToolTip toolTipHelp; } } ================================================ FILE: PowerToys/src/modules/MouseWithoutBorders/App/Core/Receiver.cs ================================================ // Copyright (c) Microsoft Corporation // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Drawing; using System.Globalization; using System.IO; using System.Threading.Tasks; using System.Windows.Forms; using MouseWithoutBorders.Class; [module: SuppressMessage("Microsoft.Reliability", "CA2002:DoNotLockOnObjectsWithWeakIdentity", Scope = "member", Target = "MouseWithoutBorders.Common.#PreProcess(MouseWithoutBorders.DATA)", Justification = "Dotnet port with style preservation")] // // Back-end thread for the socket. // // // 2008 created by Truong Do (ductdo). // 2009-... modified by Truong Do (TruongDo). // 2023- Included in PowerToys. // namespace MouseWithoutBorders.Core; internal static class Receiver { private static readonly uint QUEUE_SIZE = 50; private static readonly int[] RecentProcessedPackageIDs = new int[QUEUE_SIZE]; private static int recentProcessedPackageIndex; #pragma warning disable SA1307 // Accessible fields should begin with upper-case letter internal static long processedPackageCount; internal static long skippedPackageCount; #pragma warning restore SA1307 private static long JustGotAKey { get; set; } private static bool PreProcess(DATA package) { if (package.Type == PackageType.Invalid) { if ((Common.InvalidPackageCount % 100) == 0) { Common.ShowToolTip("收到无效数据包!", 1000, ToolTipIcon.Warning, false); } Common.InvalidPackageCount++; Logger.Log("Invalid packages received!"); return false; } else if (package.Type == 0) { Logger.Log("Got an unknown package!"); return false; } else if (package.Type is not PackageType.ClipboardText and not PackageType.ClipboardImage // BEGIN: These package types are sent by TcpSend which is single direction. and not PackageType.Handshake and not PackageType.HandshakeAck) { // END lock (RecentProcessedPackageIDs) { for (int i = 0; i < QUEUE_SIZE; i++) { if (RecentProcessedPackageIDs[i] == package.Id) { skippedPackageCount++; return false; } } processedPackageCount++; recentProcessedPackageIndex = (int)((recentProcessedPackageIndex + 1) % QUEUE_SIZE); RecentProcessedPackageIDs[recentProcessedPackageIndex] = package.Id; } } return true; } private static System.Drawing.Point lastXY; internal static void ProcessPackage(DATA package, TcpSk tcp) { if (!PreProcess(package)) { return; } switch (package.Type) { case PackageType.Keyboard: Common.PackageReceived.Keyboard++; if (package.Des == Common.MachineID || package.Des == ID.ALL) { JustGotAKey = Common.GetTick(); // NOTE(@yuyoyuppe): disabled to drop elevation requirement bool nonElevated = Common.RunWithNoAdminRight && false; if (nonElevated && Setting.Values.OneWayControlMode) { if ((package.Kd.dwFlags & (int)Common.LLKHF.UP) == (int)Common.LLKHF.UP) { Common.ShowOneWayModeMessage(); } return; } InputSimulation.SendKey(package.Kd); } break; case PackageType.Mouse: Common.PackageReceived.Mouse++; if (package.Des == Common.MachineID || package.Des == ID.ALL) { if (Common.desMachineID != Common.MachineID) { Common.NewDesMachineID = Common.DesMachineID = Common.MachineID; } // NOTE(@yuyoyuppe): disabled to drop elevation requirement bool nonElevated = Common.RunWithNoAdminRight && false; if (nonElevated && Setting.Values.OneWayControlMode && package.Md.dwFlags != Common.WM_MOUSEMOVE) { if (!Common.IsDropping) { if (package.Md.dwFlags is Common.WM_LBUTTONDOWN or Common.WM_RBUTTONDOWN) { Common.ShowOneWayModeMessage(); } } else if (package.Md.dwFlags is Common.WM_LBUTTONUP or Common.WM_RBUTTONUP) { Common.IsDropping = false; } return; } if (Math.Abs(package.Md.X) >= Common.MOVE_MOUSE_RELATIVE && Math.Abs(package.Md.Y) >= Common.MOVE_MOUSE_RELATIVE) { if (package.Md.dwFlags == Common.WM_MOUSEMOVE) { InputSimulation.MoveMouseRelative( package.Md.X < 0 ? package.Md.X + Common.MOVE_MOUSE_RELATIVE : package.Md.X - Common.MOVE_MOUSE_RELATIVE, package.Md.Y < 0 ? package.Md.Y + Common.MOVE_MOUSE_RELATIVE : package.Md.Y - Common.MOVE_MOUSE_RELATIVE); _ = NativeMethods.GetCursorPos(ref lastXY); Point p = Common.MoveToMyNeighbourIfNeeded(lastXY.X, lastXY.Y, Common.MachineID); if (!p.IsEmpty) { Common.HasSwitchedMachineSinceLastCopy = true; Logger.LogDebug(string.Format( CultureInfo.CurrentCulture, "***** Controlled Machine: newDesMachineIdEx set = [{0}]. Mouse is now at ({1},{2})", Common.newDesMachineIdEx, lastXY.X, lastXY.Y)); Common.SendNextMachine(package.Src, Common.newDesMachineIdEx, p); } } else { _ = NativeMethods.GetCursorPos(ref lastXY); package.Md.X = lastXY.X * 65535 / Common.screenWidth; package.Md.Y = lastXY.Y * 65535 / Common.screenHeight; _ = InputSimulation.SendMouse(package.Md); } } else { _ = InputSimulation.SendMouse(package.Md); _ = NativeMethods.GetCursorPos(ref lastXY); } Common.LastX = lastXY.X; Common.LastY = lastXY.Y; CustomCursor.ShowFakeMouseCursor(Common.LastX, Common.LastY); } Common.DragDropStep01(package.Md.dwFlags); Common.DragDropStep09(package.Md.dwFlags); break; case PackageType.NextMachine: Logger.LogDebug("PackageType.NextMachine received!"); if (Common.IsSwitchingByMouseEnabled()) { Common.PrepareToSwitchToMachine((ID)package.Md.WheelDelta, new Point(package.Md.X, package.Md.Y)); } break; case PackageType.ExplorerDragDrop: Common.PackageReceived.ExplorerDragDrop++; Common.DragDropStep03(package); break; case PackageType.Heartbeat: case PackageType.Heartbeat_ex: Common.PackageReceived.Heartbeat++; Common.GeneratedKey = Common.GeneratedKey || package.Type == PackageType.Heartbeat_ex; if (Common.GeneratedKey) { Setting.Values.MyKey = Common.MyKey; Common.SendPackage(ID.ALL, PackageType.Heartbeat_ex_l2); } string desMachine = Common.AddToMachinePool(package); if (Setting.Values.FirstRun && !string.IsNullOrEmpty(desMachine)) { Common.UpdateSetupMachineMatrix(desMachine); Common.UpdateClientSockets("UpdateSetupMachineMatrix"); } break; case PackageType.Heartbeat_ex_l2: Common.GeneratedKey = true; Setting.Values.MyKey = Common.MyKey; Common.SendPackage(ID.ALL, PackageType.Heartbeat_ex_l3); break; case PackageType.Heartbeat_ex_l3: Common.GeneratedKey = true; Setting.Values.MyKey = Common.MyKey; break; case PackageType.Awake: Common.PackageReceived.Heartbeat++; _ = Common.AddToMachinePool(package); Common.HumanBeingDetected(); break; case PackageType.Hello: Common.PackageReceived.Hello++; Common.SendHeartBeat(); string newMachine = Common.AddToMachinePool(package); if (Setting.Values.MachineMatrixString == null) { string tip = newMachine + " 报到!"; tip += "\r\n 右键打开设备布局"; Common.ShowToolTip(tip); } break; case PackageType.Hi: Common.PackageReceived.Hello++; break; case PackageType.ByeBye: Common.PackageReceived.ByeBye++; Common.ProcessByeByeMessage(package); break; case PackageType.Clipboard: Common.PackageReceived.Clipboard++; if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop) { Common.clipboardCopiedTime = Common.GetTick(); GetNameOfMachineWithClipboardData(package); SignalBigClipboardData(); } break; case PackageType.MachineSwitched: if (Common.GetTick() - Common.clipboardCopiedTime < Common.BIG_CLIPBOARD_DATA_TIMEOUT && (package.Des == Common.MachineID)) { Common.clipboardCopiedTime = 0; Common.GetRemoteClipboard("PackageType.MachineSwitched"); } break; case PackageType.ClipboardCapture: Common.PackageReceived.Clipboard++; if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop) { if (package.Des == Common.MachineID || package.Des == ID.ALL) { GetNameOfMachineWithClipboardData(package); Common.GetRemoteClipboard("mspaint," + Common.LastMachineWithClipboardData); } } break; case PackageType.CaptureScreenCommand: Common.PackageReceived.Clipboard++; if (package.Des == Common.MachineID || package.Des == ID.ALL) { Common.SendImage(package.Src, Common.CaptureScreen()); } break; case PackageType.ClipboardAsk: Common.PackageReceived.ClipboardAsk++; if (package.Des == Common.MachineID) { _ = Task.Run(() => { try { System.Threading.Thread thread = Thread.CurrentThread; thread.Name = $"{nameof(PackageType.ClipboardAsk)}.{thread.ManagedThreadId}"; Thread.UpdateThreads(thread); string remoteMachine = package.MachineName; System.Net.Sockets.TcpClient client = Common.ConnectToRemoteClipboardSocket(remoteMachine); bool clientPushData = true; if (Common.ShakeHand(ref remoteMachine, client.Client, out Stream enStream, out Stream deStream, ref clientPushData, ref package.PostAction)) { SocketStuff.SendClipboardData(client.Client, enStream); } } catch (Exception e) { Logger.Log(e); } }); } break; case PackageType.ClipboardDragDrop: Common.PackageReceived.ClipboardDragDrop++; Common.DragDropStep08(package); break; case PackageType.ClipboardDragDropOperation: Common.PackageReceived.ClipboardDragDrop++; Common.DragDropStep08_2(package); break; case PackageType.ClipboardDragDropEnd: Common.PackageReceived.ClipboardDragDropEnd++; Common.DragDropStep12(); break; case PackageType.ClipboardText: case PackageType.ClipboardImage: Common.clipboardCopiedTime = 0; if (package.Type == PackageType.ClipboardImage) { Common.PackageReceived.ClipboardImage++; } else { Common.PackageReceived.ClipboardText++; } if (tcp != null) { Common.ReceiveClipboardDataUsingTCP( package, package.Type == PackageType.ClipboardImage, tcp); } break; case PackageType.HideMouse: Common.HasSwitchedMachineSinceLastCopy = true; Common.HideMouseCursor(true); Common.MainFormDotEx(false); Common.ReleaseAllKeys(); break; default: if ((package.Type & PackageType.Matrix) == PackageType.Matrix) { Common.PackageReceived.Matrix++; Common.UpdateMachineMatrix(package); break; } else { // We should never get to this point! Logger.Log("Invalid package received!"); return; } } } internal static void GetNameOfMachineWithClipboardData(DATA package) { Common.LastIDWithClipboardData = package.Src; List matchingMachines = Common.MachinePool.TryFindMachineByID(Common.LastIDWithClipboardData); if (matchingMachines.Count >= 1) { Common.LastMachineWithClipboardData = matchingMachines[0].Name.Trim(); } /* lastMachineWithClipboardData = Common.GetString(BitConverter.GetBytes(package.machineNameHead)); lastMachineWithClipboardData += Common.GetString(BitConverter.GetBytes(package.machineNameTail)); lastMachineWithClipboardData = lastMachineWithClipboardData.Trim(); * */ } private static void SignalBigClipboardData() { Logger.LogDebug("SignalBigClipboardData"); Common.SetToggleIcon(new int[Common.TOGGLE_ICONS_SIZE] { Common.ICON_BIG_CLIPBOARD, -1, Common.ICON_BIG_CLIPBOARD, -1 }); } } ================================================ FILE: PowerToys/src/modules/MouseWithoutBorders/App/Form/Settings/SettingsFormPage.Designer.cs ================================================ namespace MouseWithoutBorders { partial class SettingsFormPage { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.buttonSkip = new System.Windows.Forms.Button(); this.BackButton = new MouseWithoutBorders.ImageButton(); ((System.ComponentModel.ISupportInitialize)(this.BackButton)).BeginInit(); this.SuspendLayout(); // // buttonSkip // this.buttonSkip.FlatAppearance.BorderSize = 0; this.buttonSkip.FlatAppearance.MouseDownBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(192)))), ((int)(((byte)(128))))); this.buttonSkip.FlatAppearance.MouseOverBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(128)))), ((int)(((byte)(0))))); this.buttonSkip.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.buttonSkip.ForeColor = System.Drawing.Color.White; this.buttonSkip.Location = new System.Drawing.Point(0, 36); this.buttonSkip.Name = "buttonSkip"; this.buttonSkip.Size = new System.Drawing.Size(40, 23); this.buttonSkip.TabIndex = 1; this.buttonSkip.Text = "跳过(&S)"; this.buttonSkip.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; this.buttonSkip.UseVisualStyleBackColor = true; this.buttonSkip.Click += new System.EventHandler(this.ButtonSkip_Click); // // BackButton // this.BackButton.DisabledImage = null; this.BackButton.DownImage = global::MouseWithoutBorders.Properties.Images.back_button_click; this.BackButton.HoverImage = global::MouseWithoutBorders.Properties.Images.back_button_hover; this.BackButton.Image = global::MouseWithoutBorders.Properties.Images.back_button_normal; this.BackButton.Location = new System.Drawing.Point(6, 6); this.BackButton.Name = "BackButton"; this.BackButton.NormalImage = global::MouseWithoutBorders.Properties.Images.back_button_normal; this.BackButton.Size = new System.Drawing.Size(24, 24); this.BackButton.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; this.BackButton.TabIndex = 0; this.BackButton.TabStop = false; this.BackButton.Visible = false; this.BackButton.Click += new System.EventHandler(this.BackButton_Click); // // SettingsFormPage // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.BackColor = System.Drawing.Color.DeepSkyBlue; this.Controls.Add(this.buttonSkip); this.Controls.Add(this.BackButton); this.Name = "SettingsFormPage"; this.Size = new System.Drawing.Size(396, 345); ((System.ComponentModel.ISupportInitialize)(this.BackButton)).EndInit(); this.ResumeLayout(false); } #endregion private ImageButton BackButton; private System.Windows.Forms.Button buttonSkip; } } ================================================ FILE: PowerToys/src/modules/MouseWithoutBorders/App/Form/Settings/SettingsFormPage.cs ================================================ // 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.Windows.Forms; using MouseWithoutBorders.Class; using MouseWithoutBorders.Form.Settings; namespace MouseWithoutBorders { public partial class SettingsFormPage : UserControl { public event EventHandler NextPage; protected bool BackButtonVisible { get => BackButton.Visible; set => BackButton.Visible = value; } public SettingsFormPage() { InitializeComponent(); } public virtual void OnPageClosing() { } protected void SendNextPage(SettingsFormPage page) { NextPage?.Invoke(this, new PageEventArgs(page)); } protected virtual SettingsFormPage CreateBackPage() { return null; } protected string GetSecureKey() { return Common.MyKey; } private void BackButton_Click(object sender, EventArgs e) { SendNextPage(CreateBackPage()); } private void ButtonSkip_Click(object sender, EventArgs e) { if (MessageBox.Show( "强烈推荐您先完成初始配置!确定需要跳过这些步骤吗?", Application.ProductName, MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2) == DialogResult.Yes) { Setting.Values.FirstRun = false; Common.CloseSetupForm(); Common.ShowMachineMatrix(); } } } } ================================================ FILE: PowerToys/src/modules/MouseWithoutBorders/App/Form/Settings/SetupPage1.Designer.cs ================================================ using System.Windows.Forms; namespace MouseWithoutBorders { partial class SetupPage1 { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.panel1 = new System.Windows.Forms.Panel(); this.label1 = new System.Windows.Forms.Label(); this.panel2 = new System.Windows.Forms.Panel(); this.label2 = new System.Windows.Forms.Label(); this.label3 = new System.Windows.Forms.Label(); this.label4 = new System.Windows.Forms.Label(); this.NoButton = new MouseWithoutBorders.ImageButton(); this.YesButton = new MouseWithoutBorders.ImageButton(); this.pictureBox1 = new System.Windows.Forms.PictureBox(); ((System.ComponentModel.ISupportInitialize)(this.NoButton)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.YesButton)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); this.SuspendLayout(); // // panel1 // this.panel1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.panel1.BackColor = System.Drawing.Color.White; this.panel1.Location = new System.Drawing.Point(41, 96); this.panel1.Name = "panel1"; this.panel1.Size = new System.Drawing.Size(370, 1); this.panel1.TabIndex = 1; // // label1 // this.label1.Font = new System.Drawing.Font(Control.DefaultFont.Name, 30F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label1.ForeColor = System.Drawing.Color.White; this.label1.Location = new System.Drawing.Point(61, 102); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(330, 52); this.label1.TabIndex = 2; this.label1.Text = "让我们开始吧"; this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; // // panel2 // this.panel2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.panel2.BackColor = System.Drawing.Color.White; this.panel2.Location = new System.Drawing.Point(41, 166); this.panel2.Name = "panel2"; this.panel2.Size = new System.Drawing.Size(370, 1); this.panel2.TabIndex = 2; // // label2 // this.label2.Font = new System.Drawing.Font(Control.DefaultFont.Name, 9.75F); this.label2.ForeColor = System.Drawing.Color.White; this.label2.Location = new System.Drawing.Point(61, 185); this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(310, 40); this.label2.TabIndex = 3; this.label2.Text = "我们需要知道您是否已经在要连接的电脑上完成了配置。"; // // label3 // this.label3.Font = new System.Drawing.Font(Control.DefaultFont.Name, 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label3.ForeColor = System.Drawing.Color.White; this.label3.Location = new System.Drawing.Point(61, 240); this.label3.Name = "label3"; this.label3.Size = new System.Drawing.Size(310, 60); this.label3.TabIndex = 4; this.label3.Text = "您是否已经在另一台电脑上安装了无界鼠标?"; // // label4 // this.label4.Font = new System.Drawing.Font(Control.DefaultFont.Name, 9.75F); this.label4.ForeColor = System.Drawing.Color.White; this.label4.Location = new System.Drawing.Point(61, 278); this.label4.Name = "label4"; this.label4.Size = new System.Drawing.Size(310, 20); this.label4.TabIndex = 5; this.label4.Text = ""; // // NoButton // this.NoButton.BackColor = System.Drawing.Color.Transparent; this.NoButton.DisabledImage = null; this.NoButton.DownImage = global::MouseWithoutBorders.Properties.Images.no_button_click; this.NoButton.HoverImage = global::MouseWithoutBorders.Properties.Images.no_button_hover; this.NoButton.Image = global::MouseWithoutBorders.Properties.Images.no_button_normal; this.NoButton.InitialImage = global::MouseWithoutBorders.Properties.Images.yes_button_normal; this.NoButton.Location = new System.Drawing.Point(234, 366); this.NoButton.Name = "NoButton"; this.NoButton.NormalImage = global::MouseWithoutBorders.Properties.Images.no_button_normal; this.NoButton.Size = new System.Drawing.Size(55, 55); this.NoButton.TabIndex = 7; this.NoButton.TabStop = false; this.NoButton.Click += new System.EventHandler(this.NoButtonClick); // // YesButton // this.YesButton.BackColor = System.Drawing.Color.Transparent; this.YesButton.DisabledImage = null; this.YesButton.DownImage = global::MouseWithoutBorders.Properties.Images.yes_button_click; this.YesButton.HoverImage = global::MouseWithoutBorders.Properties.Images.yes_button_hover; this.YesButton.Image = global::MouseWithoutBorders.Properties.Images.yes_button_normal; this.YesButton.InitialImage = global::MouseWithoutBorders.Properties.Images.yes_button_normal; this.YesButton.Location = new System.Drawing.Point(164, 366); this.YesButton.Name = "YesButton"; this.YesButton.NormalImage = global::MouseWithoutBorders.Properties.Images.yes_button_normal; this.YesButton.Size = new System.Drawing.Size(55, 55); this.YesButton.TabIndex = 6; this.YesButton.TabStop = false; this.YesButton.Click += new System.EventHandler(this.YesButtonClick); // // pictureBox1 // this.pictureBox1.Image = global::MouseWithoutBorders.Properties.Images.Mouse; this.pictureBox1.Location = new System.Drawing.Point(206, 40); this.pictureBox1.Margin = new System.Windows.Forms.Padding(0); this.pictureBox1.Name = "pictureBox1"; this.pictureBox1.Size = new System.Drawing.Size(41, 36); this.pictureBox1.TabIndex = 0; this.pictureBox1.TabStop = false; // // SetupPage1 // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.BackColor = System.Drawing.Color.DodgerBlue; this.Controls.Add(this.NoButton); this.Controls.Add(this.YesButton); this.Controls.Add(this.label4); this.Controls.Add(this.label3); this.Controls.Add(this.label2); this.Controls.Add(this.panel2); this.Controls.Add(this.label1); this.Controls.Add(this.panel1); this.Controls.Add(this.pictureBox1); this.DoubleBuffered = true; this.Name = "SetupPage1"; this.Size = new System.Drawing.Size(453, 438); this.Controls.SetChildIndex(this.pictureBox1, 0); this.Controls.SetChildIndex(this.panel1, 0); this.Controls.SetChildIndex(this.label1, 0); this.Controls.SetChildIndex(this.panel2, 0); this.Controls.SetChildIndex(this.label2, 0); this.Controls.SetChildIndex(this.label3, 0); this.Controls.SetChildIndex(this.label4, 0); this.Controls.SetChildIndex(this.YesButton, 0); this.Controls.SetChildIndex(this.NoButton, 0); ((System.ComponentModel.ISupportInitialize)(this.NoButton)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.YesButton)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); this.ResumeLayout(false); } #endregion private System.Windows.Forms.PictureBox pictureBox1; private System.Windows.Forms.Panel panel1; private System.Windows.Forms.Label label1; private System.Windows.Forms.Panel panel2; private System.Windows.Forms.Label label2; private System.Windows.Forms.Label label3; private System.Windows.Forms.Label label4; private ImageButton YesButton; private ImageButton NoButton; } } ================================================ FILE: PowerToys/src/modules/MouseWithoutBorders/App/Form/Settings/SetupPage2a.Designer.cs ================================================ namespace MouseWithoutBorders { using System.Windows.Forms; partial class SetupPage2a { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.label6 = new System.Windows.Forms.Label(); this.SecurityCodeField = new MouseWithoutBorders.ColorBorderField(); this.ComputerNameField = new MouseWithoutBorders.ColorBorderField(); this.label3 = new System.Windows.Forms.Label(); this.LinkButton = new MouseWithoutBorders.ImageButton(); this.label2 = new System.Windows.Forms.Label(); this.ExpandHelpButton = new MouseWithoutBorders.ImageButton(); this.CollapseHelpButton = new MouseWithoutBorders.ImageButton(); this.HelpLabel = new System.Windows.Forms.Label(); ((System.ComponentModel.ISupportInitialize)(this.LinkButton)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.ExpandHelpButton)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.CollapseHelpButton)).BeginInit(); this.SuspendLayout(); // // label6 // this.label6.Font = new System.Drawing.Font(Control.DefaultFont.Name, 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label6.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(77)))), ((int)(((byte)(208)))), ((int)(((byte)(238))))); this.label6.Location = new System.Drawing.Point(98, 208); this.label6.Name = "label6"; this.label6.Size = new System.Drawing.Size(130, 18); this.label6.TabIndex = 7; this.label6.Text = ""; this.label6.TextAlign = System.Drawing.ContentAlignment.BottomLeft; // // SecurityCodeField // this.SecurityCodeField.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(177)))), ((int)(((byte)(208)))), ((int)(((byte)(238))))); this.SecurityCodeField.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(177)))), ((int)(((byte)(208)))), ((int)(((byte)(238))))); this.SecurityCodeField.BorderSize = 2; this.SecurityCodeField.FocusColor = System.Drawing.Color.FromArgb(((int)(((byte)(251)))), ((int)(((byte)(176)))), ((int)(((byte)(64))))); this.SecurityCodeField.Font = new System.Drawing.Font(Control.DefaultFont.Name, 18F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.SecurityCodeField.Location = new System.Drawing.Point(80, 226); this.SecurityCodeField.Margin = new System.Windows.Forms.Padding(0); this.SecurityCodeField.MaximumLength = 22; this.SecurityCodeField.Name = "SecurityCodeField"; this.SecurityCodeField.Size = new System.Drawing.Size(300, 30); this.SecurityCodeField.TabIndex = 0; this.SecurityCodeField.FieldTextChanged += new System.EventHandler(this.SecurityCodeFieldFieldTextChanged); // // ComputerNameField // this.ComputerNameField.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(177)))), ((int)(((byte)(208)))), ((int)(((byte)(238))))); this.ComputerNameField.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(177)))), ((int)(((byte)(208)))), ((int)(((byte)(238))))); this.ComputerNameField.BorderSize = 2; this.ComputerNameField.FocusColor = System.Drawing.Color.FromArgb(((int)(((byte)(251)))), ((int)(((byte)(176)))), ((int)(((byte)(64))))); this.ComputerNameField.Font = new System.Drawing.Font(Control.DefaultFont.Name, 18F); this.ComputerNameField.Location = new System.Drawing.Point(80, 280); this.ComputerNameField.Margin = new System.Windows.Forms.Padding(0); this.ComputerNameField.MaximumLength = 126; this.ComputerNameField.Name = "ComputerNameField"; this.ComputerNameField.Size = new System.Drawing.Size(300, 30); this.ComputerNameField.TabIndex = 1; this.ComputerNameField.FieldTextChanged += new System.EventHandler(this.ComputerNameFieldFieldTextChanged); // // label3 // this.label3.Font = new System.Drawing.Font(Control.DefaultFont.Name, 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label3.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(77)))), ((int)(((byte)(208)))), ((int)(((byte)(238))))); this.label3.Location = new System.Drawing.Point(98, 262); this.label3.Name = "label3"; this.label3.Size = new System.Drawing.Size(262, 18); this.label3.TabIndex = 13; this.label3.Text = "豸"; this.label3.TextAlign = System.Drawing.ContentAlignment.BottomLeft; // // LinkButton // this.LinkButton.DisabledImage = global::MouseWithoutBorders.Properties.Images.link_button_disabled; this.LinkButton.DownImage = global::MouseWithoutBorders.Properties.Images.link_button_click; this.LinkButton.Enabled = false; this.LinkButton.HoverImage = global::MouseWithoutBorders.Properties.Images.link_button_hover; this.LinkButton.Image = global::MouseWithoutBorders.Properties.Images.link_button_normal; this.LinkButton.Location = new System.Drawing.Point(199, 366); this.LinkButton.Name = "LinkButton"; this.LinkButton.NormalImage = global::MouseWithoutBorders.Properties.Images.link_button_normal; this.LinkButton.Size = new System.Drawing.Size(55, 55); this.LinkButton.TabIndex = 15; this.LinkButton.TabStop = false; this.LinkButton.Click += new System.EventHandler(this.LinkButtonClick); // // label2 // this.label2.AutoSize = true; this.label2.BackColor = System.Drawing.Color.Transparent; this.label2.Font = new System.Drawing.Font(Control.DefaultFont.Name, 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label2.ForeColor = System.Drawing.Color.White; this.label2.Location = new System.Drawing.Point(213, 208); this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(128, 16); this.label2.TabIndex = 23; this.label2.Text = "ȥ룿"; // // ExpandHelpButton // this.ExpandHelpButton.DisabledImage = null; this.ExpandHelpButton.DownImage = global::MouseWithoutBorders.Properties.Images.expand_button_click; this.ExpandHelpButton.HoverImage = global::MouseWithoutBorders.Properties.Images.expand_button_highlight; this.ExpandHelpButton.Image = global::MouseWithoutBorders.Properties.Images.expand_button_normal; this.ExpandHelpButton.Location = new System.Drawing.Point(360, 211); this.ExpandHelpButton.Name = "ExpandHelpButton"; this.ExpandHelpButton.NormalImage = global::MouseWithoutBorders.Properties.Images.expand_button_normal; this.ExpandHelpButton.Size = new System.Drawing.Size(11, 11); this.ExpandHelpButton.TabIndex = 24; this.ExpandHelpButton.TabStop = false; this.ExpandHelpButton.Click += new System.EventHandler(this.ExpandHelpButtonClick); // // CollapseHelpButton // this.CollapseHelpButton.DisabledImage = null; this.CollapseHelpButton.DownImage = global::MouseWithoutBorders.Properties.Images.collapse_button_click; this.CollapseHelpButton.HoverImage = global::MouseWithoutBorders.Properties.Images.collapse_button_hover; this.CollapseHelpButton.Image = global::MouseWithoutBorders.Properties.Images.collapse_button_normal; this.CollapseHelpButton.Location = new System.Drawing.Point(360, 211); this.CollapseHelpButton.Name = "CollapseHelpButton"; this.CollapseHelpButton.NormalImage = global::MouseWithoutBorders.Properties.Images.collapse_button_normal; this.CollapseHelpButton.Size = new System.Drawing.Size(11, 11); this.CollapseHelpButton.TabIndex = 25; this.CollapseHelpButton.TabStop = false; this.CollapseHelpButton.Visible = false; this.CollapseHelpButton.Click += new System.EventHandler(this.CollapseHelpButtonClick); // // HelpLabel // this.HelpLabel.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(49)))), ((int)(((byte)(159)))), ((int)(((byte)(217))))); this.HelpLabel.Font = new System.Drawing.Font(Control.DefaultFont.Name, 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.HelpLabel.ForeColor = System.Drawing.Color.White; this.HelpLabel.Location = new System.Drawing.Point(101, 226); this.HelpLabel.Name = "HelpLabel"; this.HelpLabel.Size = new System.Drawing.Size(250, 94); this.HelpLabel.TabIndex = 26; this.HelpLabel.Text = "ҪӵĵϣҼ޽ͼ꣬ý棬нչʾ롣豸һ̨Եơ"; this.HelpLabel.Visible = false; // // SetupPage2a // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.BackColor = System.Drawing.Color.DodgerBlue; this.Controls.Add(this.HelpLabel); this.Controls.Add(this.LinkButton); this.Controls.Add(this.ComputerNameField); this.Controls.Add(this.label3); this.Controls.Add(this.SecurityCodeField); this.Controls.Add(this.CollapseHelpButton); this.Controls.Add(this.ExpandHelpButton); this.Controls.Add(this.label2); this.Controls.Add(this.label6); this.DoubleBuffered = true; this.Name = "SetupPage2a"; this.Size = new System.Drawing.Size(453, 438); this.Controls.SetChildIndex(this.label6, 0); this.Controls.SetChildIndex(this.label2, 0); this.Controls.SetChildIndex(this.ExpandHelpButton, 0); this.Controls.SetChildIndex(this.CollapseHelpButton, 0); this.Controls.SetChildIndex(this.SecurityCodeField, 0); this.Controls.SetChildIndex(this.label3, 0); this.Controls.SetChildIndex(this.ComputerNameField, 0); this.Controls.SetChildIndex(this.LinkButton, 0); this.Controls.SetChildIndex(this.HelpLabel, 0); ((System.ComponentModel.ISupportInitialize)(this.LinkButton)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.ExpandHelpButton)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.CollapseHelpButton)).EndInit(); this.ResumeLayout(false); this.PerformLayout(); } #endregion private System.Windows.Forms.Label label6; private ColorBorderField SecurityCodeField; private ColorBorderField ComputerNameField; private System.Windows.Forms.Label label3; private ImageButton LinkButton; private System.Windows.Forms.Label label2; private ImageButton ExpandHelpButton; private ImageButton CollapseHelpButton; private System.Windows.Forms.Label HelpLabel; } } ================================================ FILE: PowerToys/src/modules/MouseWithoutBorders/App/Form/Settings/SetupPage2aa.Designer.cs ================================================ using System.Windows.Forms; namespace MouseWithoutBorders { partial class SetupPage2aa { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.label1 = new System.Windows.Forms.Label(); this.label6 = new System.Windows.Forms.Label(); this.label2 = new System.Windows.Forms.Label(); this.panel1 = new System.Windows.Forms.Panel(); this.SuspendLayout(); // // label1 // this.label1.Font = new System.Drawing.Font(Control.DefaultFont.Name, 24F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label1.ForeColor = System.Drawing.Color.White; this.label1.Location = new System.Drawing.Point(49, 30); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(356, 150); this.label1.TabIndex = 2; this.label1.Text = "ֻһӣ"; this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; // // label6 // this.label6.Font = new System.Drawing.Font(Control.DefaultFont.Name, 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label6.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(77)))), ((int)(((byte)(208)))), ((int)(((byte)(238))))); this.label6.Location = new System.Drawing.Point(98, 185); this.label6.Name = "label6"; this.label6.Size = new System.Drawing.Size(130, 18); this.label6.TabIndex = 7; this.label6.Text = ""; this.label6.TextAlign = System.Drawing.ContentAlignment.BottomLeft; // // label2 // this.label2.AutoSize = true; this.label2.BackColor = System.Drawing.Color.Transparent; this.label2.Font = new System.Drawing.Font(Control.DefaultFont.Name, 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label2.ForeColor = System.Drawing.Color.White; this.label2.Location = new System.Drawing.Point(213, 185); this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(128, 16); this.label2.TabIndex = 23; this.label2.Text = "ȥ룿"; // // panel1 // this.panel1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.panel1.BackColor = System.Drawing.Color.White; this.panel1.Location = new System.Drawing.Point(41, 170); this.panel1.Name = "panel1"; this.panel1.Size = new System.Drawing.Size(370, 1); this.panel1.TabIndex = 1; // // SetupPage2aa // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.BackColor = System.Drawing.Color.DodgerBlue; this.Controls.Add(this.panel1); this.Controls.Add(this.label1); this.Name = "SetupPage2aa"; this.Controls.SetChildIndex(this.label1, 0); this.Controls.SetChildIndex(this.panel1, 0); this.ResumeLayout(false); this.PerformLayout(); } #endregion private System.Windows.Forms.Label label1; private System.Windows.Forms.Label label6; private System.Windows.Forms.Label label2; private System.Windows.Forms.Panel panel1; } } ================================================ FILE: PowerToys/src/modules/MouseWithoutBorders/App/Form/Settings/SetupPage2ab.Designer.cs ================================================ using System.Windows.Forms; namespace MouseWithoutBorders { partial class SetupPage2ab { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.label1 = new System.Windows.Forms.Label(); this.label6 = new System.Windows.Forms.Label(); this.label4 = new System.Windows.Forms.Label(); this.label2 = new System.Windows.Forms.Label(); this.ExpandHelpButton = new MouseWithoutBorders.ImageButton(); this.CollapseHelpButton = new MouseWithoutBorders.ImageButton(); this.panel1 = new System.Windows.Forms.Panel(); this.panel2 = new System.Windows.Forms.Panel(); ((System.ComponentModel.ISupportInitialize)(this.ExpandHelpButton)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.CollapseHelpButton)).BeginInit(); this.SuspendLayout(); // // label1 // this.label1.Font = new System.Drawing.Font(Control.DefaultFont.Name, 24F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label1.ForeColor = System.Drawing.Color.White; this.label1.Location = new System.Drawing.Point(49, 30); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(356, 53); this.label1.TabIndex = 2; this.label1.Text = "Ǹ"; this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; // // label6 // this.label6.Font = new System.Drawing.Font(Control.DefaultFont.Name, 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label6.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(77)))), ((int)(((byte)(208)))), ((int)(((byte)(238))))); this.label6.Location = new System.Drawing.Point(98, 208); this.label6.Name = "label6"; this.label6.Size = new System.Drawing.Size(130, 18); this.label6.TabIndex = 7; this.label6.Text = ""; this.label6.TextAlign = System.Drawing.ContentAlignment.BottomLeft; // // label4 // this.label4.Font = new System.Drawing.Font(Control.DefaultFont.Name, 9.75F); this.label4.ForeColor = System.Drawing.Color.White; this.label4.Location = new System.Drawing.Point(61, 95); this.label4.Name = "label4"; this.label4.Size = new System.Drawing.Size(332, 79); this.label4.TabIndex = 27; this.label4.Text = "ƺϱĵԡȷеԶͬһ£û豸"; // // label2 // this.label2.AutoSize = true; this.label2.BackColor = System.Drawing.Color.Transparent; this.label2.Font = new System.Drawing.Font(Control.DefaultFont.Name, 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label2.ForeColor = System.Drawing.Color.White; this.label2.Location = new System.Drawing.Point(213, 208); this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(128, 16); this.label2.TabIndex = 23; this.label2.Text = "ȥ룿"; // // ExpandHelpButton // this.ExpandHelpButton.DisabledImage = null; this.ExpandHelpButton.DownImage = null; this.ExpandHelpButton.HoverImage = null; this.ExpandHelpButton.Location = new System.Drawing.Point(0, 0); this.ExpandHelpButton.Name = "ExpandHelpButton"; this.ExpandHelpButton.NormalImage = null; this.ExpandHelpButton.Size = new System.Drawing.Size(80, 78); this.ExpandHelpButton.TabIndex = 0; this.ExpandHelpButton.TabStop = false; // // CollapseHelpButton // this.CollapseHelpButton.DisabledImage = null; this.CollapseHelpButton.DownImage = null; this.CollapseHelpButton.HoverImage = null; this.CollapseHelpButton.Location = new System.Drawing.Point(0, 0); this.CollapseHelpButton.Name = "CollapseHelpButton"; this.CollapseHelpButton.NormalImage = null; this.CollapseHelpButton.Size = new System.Drawing.Size(80, 78); this.CollapseHelpButton.TabIndex = 0; this.CollapseHelpButton.TabStop = false; // // panel1 // this.panel1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.panel1.BackColor = System.Drawing.Color.White; this.panel1.Location = new System.Drawing.Point(41, 80); this.panel1.Name = "panel1"; this.panel1.Size = new System.Drawing.Size(370, 1); this.panel1.TabIndex = 1; // // panel2 // this.panel2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.panel2.BackColor = System.Drawing.Color.White; this.panel2.Location = new System.Drawing.Point(41, 170); this.panel2.Name = "panel2"; this.panel2.Size = new System.Drawing.Size(370, 1); this.panel2.TabIndex = 28; // // SetupPage2ab // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.BackColor = System.Drawing.Color.DodgerBlue; this.Controls.Add(this.panel2); this.Controls.Add(this.label4); this.Controls.Add(this.panel1); this.Controls.Add(this.label1); this.Name = "SetupPage2ab"; this.Controls.SetChildIndex(this.label1, 0); this.Controls.SetChildIndex(this.panel1, 0); this.Controls.SetChildIndex(this.label4, 0); this.Controls.SetChildIndex(this.panel2, 0); ((System.ComponentModel.ISupportInitialize)(this.ExpandHelpButton)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.CollapseHelpButton)).EndInit(); this.ResumeLayout(false); this.PerformLayout(); } #endregion private System.Windows.Forms.Label label1; private System.Windows.Forms.Label label6; private System.Windows.Forms.Label label2; private ImageButton ExpandHelpButton; private ImageButton CollapseHelpButton; private System.Windows.Forms.Panel panel1; private System.Windows.Forms.Label label4; private System.Windows.Forms.Panel panel2; } } ================================================ FILE: PowerToys/src/modules/MouseWithoutBorders/App/Form/Settings/SetupPage2b.Designer.cs ================================================ using System.Windows.Forms; namespace MouseWithoutBorders { partial class SetupPage2b { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.panel1 = new System.Windows.Forms.Panel(); this.label1 = new System.Windows.Forms.Label(); this.panel2 = new System.Windows.Forms.Panel(); this.label2 = new System.Windows.Forms.Label(); this.SecurityCodeLabel = new System.Windows.Forms.Label(); this.pictureBox1 = new System.Windows.Forms.PictureBox(); this.label5 = new System.Windows.Forms.Label(); this.label6 = new System.Windows.Forms.Label(); this.label4 = new System.Windows.Forms.Label(); this.MachineNameLabel = new System.Windows.Forms.Label(); ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); this.SuspendLayout(); // // panel1 // this.panel1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.panel1.BackColor = System.Drawing.Color.White; this.panel1.Location = new System.Drawing.Point(41, 326); this.panel1.Name = "panel1"; this.panel1.Size = new System.Drawing.Size(370, 1); this.panel1.TabIndex = 1; // // label1 // this.label1.Font = new System.Drawing.Font(Control.DefaultFont.Name, 28F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label1.ForeColor = System.Drawing.Color.White; this.label1.Location = new System.Drawing.Point(61, 28); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(330, 56); this.label1.TabIndex = 2; this.label1.Text = "Ͼͺ..."; this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; // // panel2 // this.panel2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.panel2.BackColor = System.Drawing.Color.White; this.panel2.Location = new System.Drawing.Point(41, 87); this.panel2.Name = "panel2"; this.panel2.Size = new System.Drawing.Size(370, 1); this.panel2.TabIndex = 2; // // label2 // this.label2.Font = new System.Drawing.Font(Control.DefaultFont.Name, 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label2.ForeColor = System.Drawing.Color.White; this.label2.Location = new System.Drawing.Point(61, 103); this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(338, 80); this.label2.TabIndex = 3; this.label2.Text = "ֻϢ߿ڣȻڱĵϰװ޽꣬ʼ̳üɡ"; // // SecurityCodeLabel // this.SecurityCodeLabel.Font = new System.Drawing.Font(Control.DefaultFont.Name, 18F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.SecurityCodeLabel.ForeColor = System.Drawing.Color.White; this.SecurityCodeLabel.Location = new System.Drawing.Point(68, 207); this.SecurityCodeLabel.Name = "SecurityCodeLabel"; this.SecurityCodeLabel.Size = new System.Drawing.Size(310, 40); this.SecurityCodeLabel.TabIndex = 4; this.SecurityCodeLabel.Text = "IlIlIlIlIl"; this.SecurityCodeLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; // // pictureBox1 // this.pictureBox1.Image = global::MouseWithoutBorders.Properties.Images.Mouse; this.pictureBox1.Location = new System.Drawing.Point(206, 365); this.pictureBox1.Margin = new System.Windows.Forms.Padding(0); this.pictureBox1.Name = "pictureBox1"; this.pictureBox1.Size = new System.Drawing.Size(44, 35); this.pictureBox1.TabIndex = 0; this.pictureBox1.TabStop = false; // // label5 // this.label5.Font = new System.Drawing.Font(Control.DefaultFont.Name, 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label5.ForeColor = System.Drawing.Color.White; this.label5.Location = new System.Drawing.Point(61, 170); this.label5.Name = "label5"; this.label5.Size = new System.Drawing.Size(310, 24); this.label5.TabIndex = 6; this.label5.Text = "ȻǾú!"; // // label6 // this.label6.Font = new System.Drawing.Font(Control.DefaultFont.Name, 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label6.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(77)))), ((int)(((byte)(208)))), ((int)(((byte)(238))))); this.label6.Location = new System.Drawing.Point(71, 199); this.label6.Name = "label6"; this.label6.Size = new System.Drawing.Size(310, 15); this.label6.TabIndex = 7; this.label6.Text = ""; this.label6.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; // // label4 // this.label4.Font = new System.Drawing.Font(Control.DefaultFont.Name, 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label4.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(77)))), ((int)(((byte)(208)))), ((int)(((byte)(238))))); this.label4.Location = new System.Drawing.Point(71, 258); this.label4.Name = "label4"; this.label4.Size = new System.Drawing.Size(310, 15); this.label4.TabIndex = 9; this.label4.Text = "豸"; this.label4.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; // // MachineNameLabel // this.MachineNameLabel.Font = new System.Drawing.Font(Control.DefaultFont.Name, 18F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.MachineNameLabel.ForeColor = System.Drawing.Color.White; this.MachineNameLabel.Location = new System.Drawing.Point(50, 266); this.MachineNameLabel.Name = "MachineNameLabel"; this.MachineNameLabel.Size = new System.Drawing.Size(340, 40); this.MachineNameLabel.TabIndex = 8; this.MachineNameLabel.Text = "Сĵ"; this.MachineNameLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; // // SetupPage2b // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.BackColor = System.Drawing.Color.DodgerBlue; this.Controls.Add(this.label4); this.Controls.Add(this.MachineNameLabel); this.Controls.Add(this.label6); this.Controls.Add(this.label5); this.Controls.Add(this.SecurityCodeLabel); this.Controls.Add(this.label2); this.Controls.Add(this.panel2); this.Controls.Add(this.panel1); this.Controls.Add(this.pictureBox1); this.Controls.Add(this.label1); this.DoubleBuffered = true; this.Name = "SetupPage2b"; this.Size = new System.Drawing.Size(453, 438); this.Controls.SetChildIndex(this.label1, 0); this.Controls.SetChildIndex(this.pictureBox1, 0); this.Controls.SetChildIndex(this.panel1, 0); this.Controls.SetChildIndex(this.panel2, 0); this.Controls.SetChildIndex(this.label2, 0); this.Controls.SetChildIndex(this.SecurityCodeLabel, 0); this.Controls.SetChildIndex(this.label5, 0); this.Controls.SetChildIndex(this.label6, 0); this.Controls.SetChildIndex(this.MachineNameLabel, 0); this.Controls.SetChildIndex(this.label4, 0); ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); this.ResumeLayout(false); } #endregion private System.Windows.Forms.PictureBox pictureBox1; private System.Windows.Forms.Panel panel1; private System.Windows.Forms.Label label1; private System.Windows.Forms.Panel panel2; private System.Windows.Forms.Label label2; private System.Windows.Forms.Label SecurityCodeLabel; private System.Windows.Forms.Label label5; private System.Windows.Forms.Label label6; private System.Windows.Forms.Label label4; private System.Windows.Forms.Label MachineNameLabel; } } ================================================ FILE: PowerToys/src/modules/MouseWithoutBorders/App/Form/Settings/SetupPage3a.Designer.cs ================================================ using System.Windows.Forms; namespace MouseWithoutBorders { partial class SetupPage3a { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.label1 = new System.Windows.Forms.Label(); this.panel2 = new System.Windows.Forms.Panel(); this.MessageLabel = new System.Windows.Forms.Label(); this.ExamplePicture = new System.Windows.Forms.PictureBox(); this.labelStatus = new System.Windows.Forms.Label(); ((System.ComponentModel.ISupportInitialize)(this.ExamplePicture)).BeginInit(); this.SuspendLayout(); // // label1 // this.label1.Font = new System.Drawing.Font(Control.DefaultFont.Name, 24F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label1.ForeColor = System.Drawing.Color.White; this.label1.Location = new System.Drawing.Point(65, 56); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(330, 116); this.label1.TabIndex = 2; this.label1.Text = "ӣܿͿ...\r\n"; this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; // // panel2 // this.panel2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.panel2.BackColor = System.Drawing.Color.White; this.panel2.Location = new System.Drawing.Point(41, 178); this.panel2.Name = "panel2"; this.panel2.Size = new System.Drawing.Size(370, 1); this.panel2.TabIndex = 2; // // MessageLabel // this.MessageLabel.Font = new System.Drawing.Font(Control.DefaultFont.Name, 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.MessageLabel.ForeColor = System.Drawing.Color.White; this.MessageLabel.Location = new System.Drawing.Point(71, 197); this.MessageLabel.Name = "MessageLabel"; this.MessageLabel.Size = new System.Drawing.Size(310, 24); this.MessageLabel.TabIndex = 4; this.MessageLabel.Text = "Ըճ"; this.MessageLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; // // ExamplePicture // this.ExamplePicture.Image = global::MouseWithoutBorders.Properties.Images.copy_paste_example; this.ExamplePicture.Location = new System.Drawing.Point(101, 251); this.ExamplePicture.Name = "ExamplePicture"; this.ExamplePicture.Size = new System.Drawing.Size(251, 79); this.ExamplePicture.TabIndex = 7; this.ExamplePicture.TabStop = false; // // labelStatus // this.labelStatus.Dock = System.Windows.Forms.DockStyle.Bottom; this.labelStatus.Font = new System.Drawing.Font(Control.DefaultFont.Name, 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.labelStatus.ForeColor = System.Drawing.Color.White; this.labelStatus.Location = new System.Drawing.Point(0, 418); this.labelStatus.Name = "labelStatus"; this.labelStatus.Size = new System.Drawing.Size(453, 20); this.labelStatus.TabIndex = 8; this.labelStatus.Text = "..."; this.labelStatus.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; // // SetupPage3a // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.BackColor = System.Drawing.Color.DodgerBlue; this.Controls.Add(this.labelStatus); this.Controls.Add(this.ExamplePicture); this.Controls.Add(this.MessageLabel); this.Controls.Add(this.panel2); this.Controls.Add(this.label1); this.DoubleBuffered = true; this.Name = "SetupPage3a"; this.Size = new System.Drawing.Size(453, 438); this.Controls.SetChildIndex(this.label1, 0); this.Controls.SetChildIndex(this.panel2, 0); this.Controls.SetChildIndex(this.MessageLabel, 0); this.Controls.SetChildIndex(this.ExamplePicture, 0); this.Controls.SetChildIndex(this.labelStatus, 0); ((System.ComponentModel.ISupportInitialize)(this.ExamplePicture)).EndInit(); this.ResumeLayout(false); } #endregion private System.Windows.Forms.Label label1; private System.Windows.Forms.Panel panel2; private System.Windows.Forms.Label MessageLabel; private System.Windows.Forms.PictureBox ExamplePicture; private System.Windows.Forms.Label labelStatus; } } ================================================ FILE: PowerToys/src/modules/MouseWithoutBorders/App/Form/Settings/SetupPage3a.cs ================================================ // 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.ComponentModel; using System.Drawing; using System.Windows.Forms; using MouseWithoutBorders.Class; using MouseWithoutBorders.Core; using MouseWithoutBorders.Properties; namespace MouseWithoutBorders { public partial class SetupPage3a : SettingsFormPage { private readonly Image[] _frames = { Images.copy_paste_example, Images.drag_example, Images.keyboard_example }; private readonly string[] _messages = { "跨电脑复制粘贴", "跨电脑拖拽文件", "共享一套鼠标键盘", }; private readonly int[] _timing = { 1000, 1000, 2000 }; #pragma warning disable CA2213 // Disposing is done by ComponentResourceManager private readonly System.Timers.Timer _animationTimer; #pragma warning restore CA2213 private bool connected; private bool done; private bool invalidKey; private int _animationFrame; [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] public bool ReturnToSettings { get; set; } public SetupPage3a() { InitializeComponent(); _animationTimer = new System.Timers.Timer { Interval = 1000 }; } private void ShowStatus(string status) { labelStatus.Text = status; Logger.Log(status); } public override void OnPageClosing() { _animationTimer.Stop(); base.OnPageClosing(); } protected override void OnLoad(EventArgs e) { base.OnLoad(e); _animationTimer.Elapsed += AnimationTimerTick; Common.GetMachineName(); invalidKey = false; UpdateAnimation(); _animationTimer.Start(); SocketStuff.InvalidKeyFound = false; SocketStuff.InvalidKeyFoundOnClientSocket = false; ShowStatus($"正在连接中..."); Common.SwitchToMultipleMode(false, false); Common.ReopenSockets(true); int timeOut = 0; TcpSk connectedClientSocket; // Client sockets run in different threads. while (timeOut < 10) { Common.MMSleep(1); if ((connectedClientSocket = Common.GetConnectedClientSocket()) != null) { ShowStatus($"已连接本地 IP 地址: {connectedClientSocket.Address}."); Common.UpdateMachineTimeAndID(); Common.MMSleep(1); connected = true; return; } else if (SocketStuff.InvalidKeyFoundOnClientSocket) { invalidKey = true; ShowStatus("状态: 密码无效."); Common.MMSleep(3); break; } timeOut++; } done = true; } private void AnimationTimerTick(object sender, EventArgs e) { _ = Invoke(new MethodInvoker(UpdateAnimation)); } private void UpdateAnimation() { if ((ModifierKeys & Keys.Control) != 0) { _animationTimer.Stop(); SendNextPage(new SetupPage2ab()); return; } ExamplePicture.Image = _frames[_animationFrame]; MessageLabel.Text = _messages[_animationFrame]; _animationTimer.Interval = _timing[_animationFrame]; _animationFrame = (_animationFrame + 1) % _frames.Length; if (connected) { _animationTimer.Stop(); SendNextPage(new SetupPage4()); } else if (done) { _animationTimer.Stop(); SendNextPage(new SetupPage2ab()); if (invalidKey) { Common.ShowToolTip( "密码不正确。\r\n请检查是否在所有电脑上都输入了同一个密码。\r\n并检查您运行的是否都是同一版本的 " + Application.ProductName + "。\r\n本软件版本: " + FrmAbout.AssemblyVersion, 20000); } else { string helpText = "连接出错!"; helpText += "\r\n请检查您的电脑是否连接到同一个网络,一般推荐插网线更稳定。"; helpText += "\r\n再三检查 " + Application.ProductName + " 是不是被系统防火墙阻拦了。"; Common.ShowToolTip(helpText, 30000); } } } } } ================================================ FILE: PowerToys/src/modules/MouseWithoutBorders/App/Form/Settings/SetupPage4.Designer.cs ================================================ using System.Windows.Forms; namespace MouseWithoutBorders { partial class SetupPage4 { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.label1 = new System.Windows.Forms.Label(); this.panel2 = new System.Windows.Forms.Panel(); this.label5 = new System.Windows.Forms.Label(); this.ExamplePicture = new System.Windows.Forms.PictureBox(); this.label2 = new System.Windows.Forms.Label(); this.label3 = new System.Windows.Forms.Label(); this.panel1 = new System.Windows.Forms.Panel(); this.NextButton = new MouseWithoutBorders.ImageButton(); this.label4 = new System.Windows.Forms.Label(); this.label6 = new System.Windows.Forms.Label(); ((System.ComponentModel.ISupportInitialize)(this.ExamplePicture)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.NextButton)).BeginInit(); this.SuspendLayout(); // // label1 // this.label1.Font = new System.Drawing.Font(Control.DefaultFont.Name, 30F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label1.ForeColor = System.Drawing.Color.White; this.label1.Location = new System.Drawing.Point(65, 58); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(330, 52); this.label1.TabIndex = 2; this.label1.Text = "ɹ"; this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; // // panel2 // this.panel2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.panel2.BackColor = System.Drawing.Color.White; this.panel2.Location = new System.Drawing.Point(41, 175); this.panel2.Name = "panel2"; this.panel2.Size = new System.Drawing.Size(370, 1); this.panel2.TabIndex = 2; // // label5 // this.label5.Font = new System.Drawing.Font(Control.DefaultFont.Name, 30F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label5.ForeColor = System.Drawing.Color.White; this.label5.Location = new System.Drawing.Point(57, 108); this.label5.Name = "label5"; this.label5.Size = new System.Drawing.Size(344, 52); this.label5.TabIndex = 6; this.label5.Text = "Ͳһ..."; this.label5.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; // // ExamplePicture // this.ExamplePicture.Image = global::MouseWithoutBorders.Properties.Images.combined_example; this.ExamplePicture.Location = new System.Drawing.Point(75, 283); this.ExamplePicture.Name = "ExamplePicture"; this.ExamplePicture.Size = new System.Drawing.Size(307, 25); this.ExamplePicture.TabIndex = 7; this.ExamplePicture.TabStop = false; // // label2 // this.label2.Font = new System.Drawing.Font(Control.DefaultFont.Name, 9.75F); this.label2.ForeColor = System.Drawing.Color.White; this.label2.Location = new System.Drawing.Point(61, 200); this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(310, 25); this.label2.TabIndex = 8; this.label2.Text = "ͨ޽ʵЩ¡"; // // label3 // this.label3.Font = new System.Drawing.Font(Control.DefaultFont.Name, 9.75F); this.label3.ForeColor = System.Drawing.Color.White; this.label3.Location = new System.Drawing.Point(71, 242); this.label3.Name = "label3"; this.label3.Size = new System.Drawing.Size(88, 38); this.label3.TabIndex = 9; this.label3.Text = "Ըճ"; this.label3.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; // // panel1 // this.panel1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.panel1.BackColor = System.Drawing.Color.White; this.panel1.Location = new System.Drawing.Point(41, 345); this.panel1.Name = "panel1"; this.panel1.Size = new System.Drawing.Size(370, 1); this.panel1.TabIndex = 3; // // NextButton // this.NextButton.DisabledImage = null; this.NextButton.DownImage = global::MouseWithoutBorders.Properties.Images.next_button_click; this.NextButton.HoverImage = global::MouseWithoutBorders.Properties.Images.next_button_hover; this.NextButton.Image = global::MouseWithoutBorders.Properties.Images.next_button_normal; this.NextButton.Location = new System.Drawing.Point(199, 366); this.NextButton.Name = "NextButton"; this.NextButton.NormalImage = global::MouseWithoutBorders.Properties.Images.next_button_normal; this.NextButton.Size = new System.Drawing.Size(55, 55); this.NextButton.TabIndex = 16; this.NextButton.TabStop = false; this.NextButton.Click += new System.EventHandler(this.NextButtonClick); // // label4 // this.label4.Font = new System.Drawing.Font(Control.DefaultFont.Name, 9.75F); this.label4.ForeColor = System.Drawing.Color.White; this.label4.Location = new System.Drawing.Point(185, 242); this.label4.Name = "label4"; this.label4.Size = new System.Drawing.Size(88, 38); this.label4.TabIndex = 17; this.label4.Text = "קļ"; this.label4.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; // // label6 // this.label6.Font = new System.Drawing.Font(Control.DefaultFont.Name, 9.75F); this.label6.ForeColor = System.Drawing.Color.White; this.label6.Location = new System.Drawing.Point(294, 242); this.label6.Name = "label6"; this.label6.Size = new System.Drawing.Size(97, 38); this.label6.TabIndex = 18; this.label6.Text = "һ׼"; this.label6.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; // // SetupPage4 // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.BackColor = System.Drawing.Color.DodgerBlue; this.Controls.Add(this.label6); this.Controls.Add(this.label4); this.Controls.Add(this.NextButton); this.Controls.Add(this.panel1); this.Controls.Add(this.label3); this.Controls.Add(this.label2); this.Controls.Add(this.ExamplePicture); this.Controls.Add(this.label5); this.Controls.Add(this.panel2); this.Controls.Add(this.label1); this.DoubleBuffered = true; this.Name = "SetupPage4"; this.Size = new System.Drawing.Size(453, 438); this.Controls.SetChildIndex(this.label1, 0); this.Controls.SetChildIndex(this.panel2, 0); this.Controls.SetChildIndex(this.label5, 0); this.Controls.SetChildIndex(this.ExamplePicture, 0); this.Controls.SetChildIndex(this.label2, 0); this.Controls.SetChildIndex(this.label3, 0); this.Controls.SetChildIndex(this.panel1, 0); this.Controls.SetChildIndex(this.NextButton, 0); this.Controls.SetChildIndex(this.label4, 0); this.Controls.SetChildIndex(this.label6, 0); ((System.ComponentModel.ISupportInitialize)(this.ExamplePicture)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.NextButton)).EndInit(); this.ResumeLayout(false); } #endregion private System.Windows.Forms.Label label1; private System.Windows.Forms.Panel panel2; private System.Windows.Forms.Label label5; private System.Windows.Forms.PictureBox ExamplePicture; private System.Windows.Forms.Label label2; private System.Windows.Forms.Label label3; private System.Windows.Forms.Panel panel1; private ImageButton NextButton; private System.Windows.Forms.Label label4; private System.Windows.Forms.Label label6; } } ================================================ FILE: PowerToys/src/modules/MouseWithoutBorders/App/Form/Settings/SetupPage5.Designer.cs ================================================ using System.Windows.Forms; namespace MouseWithoutBorders { partial class SetupPage5 { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.pictureBox1 = new System.Windows.Forms.PictureBox(); this.label3 = new System.Windows.Forms.Label(); this.panel1 = new System.Windows.Forms.Panel(); this.label2 = new System.Windows.Forms.Label(); this.panel2 = new System.Windows.Forms.Panel(); this.label1 = new System.Windows.Forms.Label(); this.DoneButton = new MouseWithoutBorders.ImageButton(); ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.DoneButton)).BeginInit(); this.SuspendLayout(); // // pictureBox1 // this.pictureBox1.Image = global::MouseWithoutBorders.Properties.Images.Mouse; this.pictureBox1.Location = new System.Drawing.Point(206, 40); this.pictureBox1.Margin = new System.Windows.Forms.Padding(0); this.pictureBox1.Name = "pictureBox1"; this.pictureBox1.Size = new System.Drawing.Size(41, 36); this.pictureBox1.TabIndex = 25; this.pictureBox1.TabStop = false; // // label3 // this.label3.Font = new System.Drawing.Font(Control.DefaultFont.Name, 12F, System.Drawing.FontStyle.Bold); this.label3.ForeColor = System.Drawing.Color.White; this.label3.Location = new System.Drawing.Point(61, 271); this.label3.Name = "label3"; this.label3.Size = new System.Drawing.Size(310, 25); this.label3.TabIndex = 24; this.label3.Text = "ףÿģ"; // // panel1 // this.panel1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.panel1.BackColor = System.Drawing.Color.White; this.panel1.Location = new System.Drawing.Point(41, 96); this.panel1.Name = "panel1"; this.panel1.Size = new System.Drawing.Size(370, 1); this.panel1.TabIndex = 20; // // label2 // this.label2.Font = new System.Drawing.Font(Control.DefaultFont.Name, 9.75F); this.label2.ForeColor = System.Drawing.Color.White; this.label2.Location = new System.Drawing.Point(61, 226); this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(310, 40); this.label2.TabIndex = 22; this.label2.Text = "ʱͨͼá"; // // panel2 // this.panel2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.panel2.BackColor = System.Drawing.Color.White; this.panel2.Location = new System.Drawing.Point(41, 208); this.panel2.Name = "panel2"; this.panel2.Size = new System.Drawing.Size(370, 1); this.panel2.TabIndex = 18; // // label1 // this.label1.Font = new System.Drawing.Font(Control.DefaultFont.Name, 30F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label1.ForeColor = System.Drawing.Color.White; this.label1.Location = new System.Drawing.Point(62, 99); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(330, 109); this.label1.TabIndex = 19; this.label1.Text = "ɡ\r\nɵƯ"; this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; // // DoneButton // this.DoneButton.DisabledImage = null; this.DoneButton.DownImage = global::MouseWithoutBorders.Properties.Images.done_button_click; this.DoneButton.HoverImage = global::MouseWithoutBorders.Properties.Images.done_button_hover; this.DoneButton.Image = global::MouseWithoutBorders.Properties.Images.done_button_normal; this.DoneButton.Location = new System.Drawing.Point(199, 366); this.DoneButton.Name = "DoneButton"; this.DoneButton.NormalImage = global::MouseWithoutBorders.Properties.Images.done_button_normal; this.DoneButton.Size = new System.Drawing.Size(55, 56); this.DoneButton.TabIndex = 26; this.DoneButton.TabStop = false; this.DoneButton.Click += new System.EventHandler(this.DoneButtonClick); // // SetupPage5 // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.BackColor = System.Drawing.Color.DodgerBlue; this.Controls.Add(this.DoneButton); this.Controls.Add(this.panel1); this.Controls.Add(this.label1); this.Controls.Add(this.pictureBox1); this.Controls.Add(this.label3); this.Controls.Add(this.label2); this.Controls.Add(this.panel2); this.DoubleBuffered = true; this.Name = "SetupPage5"; this.Size = new System.Drawing.Size(453, 438); this.Controls.SetChildIndex(this.panel2, 0); this.Controls.SetChildIndex(this.label2, 0); this.Controls.SetChildIndex(this.label3, 0); this.Controls.SetChildIndex(this.pictureBox1, 0); this.Controls.SetChildIndex(this.label1, 0); this.Controls.SetChildIndex(this.panel1, 0); this.Controls.SetChildIndex(this.DoneButton, 0); ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.DoneButton)).EndInit(); this.ResumeLayout(false); } #endregion private System.Windows.Forms.PictureBox pictureBox1; private System.Windows.Forms.Label label3; private System.Windows.Forms.Panel panel1; private System.Windows.Forms.Label label2; private System.Windows.Forms.Panel panel2; private System.Windows.Forms.Label label1; private ImageButton DoneButton; } } ================================================ FILE: PowerToys/src/modules/MouseWithoutBorders/App/Form/frmAbout.Designer.cs ================================================ namespace MouseWithoutBorders { partial class FrmAbout { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FrmAbout)); this.logoPictureBox = new System.Windows.Forms.PictureBox(); this.labelProductName = new System.Windows.Forms.Label(); this.labelCopyright = new System.Windows.Forms.Label(); this.labelCompanyName = new System.Windows.Forms.Label(); this.groupBoxContributors = new System.Windows.Forms.GroupBox(); this.textBoxContributors = new System.Windows.Forms.TextBox(); this.okButton = new System.Windows.Forms.Button(); ((System.ComponentModel.ISupportInitialize)(this.logoPictureBox)).BeginInit(); this.groupBoxContributors.SuspendLayout(); this.SuspendLayout(); // // logoPictureBox // this.logoPictureBox.ErrorImage = null; this.logoPictureBox.Image = global::MouseWithoutBorders.Properties.Images.Logo; this.logoPictureBox.Location = new System.Drawing.Point(12, 12); this.logoPictureBox.Name = "logoPictureBox"; this.logoPictureBox.Size = new System.Drawing.Size(131, 136); this.logoPictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; this.logoPictureBox.TabIndex = 12; this.logoPictureBox.TabStop = false; // // labelProductName // this.labelProductName.AutoSize = true; this.labelProductName.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.labelProductName.Location = new System.Drawing.Point(152, 12); this.labelProductName.Margin = new System.Windows.Forms.Padding(6, 0, 3, 0); this.labelProductName.MaximumSize = new System.Drawing.Size(0, 17); this.labelProductName.Name = "labelProductName"; this.labelProductName.Size = new System.Drawing.Size(87, 13); this.labelProductName.TabIndex = 27; this.labelProductName.Text = "Product Name"; this.labelProductName.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // // labelCopyright // this.labelCopyright.AutoSize = true; this.labelCopyright.Location = new System.Drawing.Point(152, 31); this.labelCopyright.Margin = new System.Windows.Forms.Padding(6, 0, 3, 0); this.labelCopyright.MaximumSize = new System.Drawing.Size(0, 17); this.labelCopyright.Name = "labelCopyright"; this.labelCopyright.Size = new System.Drawing.Size(51, 13); this.labelCopyright.TabIndex = 28; this.labelCopyright.Text = "Copyright"; this.labelCopyright.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // // labelCompanyName // this.labelCompanyName.AutoSize = true; this.labelCompanyName.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.labelCompanyName.Location = new System.Drawing.Point(9, 162); this.labelCompanyName.Margin = new System.Windows.Forms.Padding(6, 0, 3, 0); this.labelCompanyName.MaximumSize = new System.Drawing.Size(0, 17); this.labelCompanyName.Name = "labelCompanyName"; this.labelCompanyName.Size = new System.Drawing.Size(94, 13); this.labelCompanyName.TabIndex = 29; this.labelCompanyName.Text = "Company Name"; this.labelCompanyName.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // // groupBoxContributors // this.groupBoxContributors.Controls.Add(this.textBoxContributors); this.groupBoxContributors.Location = new System.Drawing.Point(12, 178); this.groupBoxContributors.Name = "groupBoxContributors"; this.groupBoxContributors.Size = new System.Drawing.Size(466, 350); this.groupBoxContributors.TabIndex = 30; this.groupBoxContributors.TabStop = false; this.groupBoxContributors.Text = " 贡献者 "; // // textBoxContributors // this.textBoxContributors.Dock = System.Windows.Forms.DockStyle.Fill; this.textBoxContributors.Location = new System.Drawing.Point(3, 16); this.textBoxContributors.Multiline = true; this.textBoxContributors.Name = "textBoxContributors"; this.textBoxContributors.ReadOnly = true; this.textBoxContributors.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; this.textBoxContributors.Size = new System.Drawing.Size(460, 331); this.textBoxContributors.TabIndex = 31; // // okButton // this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.okButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; this.okButton.Location = new System.Drawing.Point(403, 534); this.okButton.Name = "okButton"; this.okButton.Size = new System.Drawing.Size(75, 23); this.okButton.TabIndex = 24; this.okButton.Text = "确定"; // // frmAbout // this.AcceptButton = this.okButton; this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; this.ClientSize = new System.Drawing.Size(487, 561); this.Controls.Add(this.okButton); this.Controls.Add(this.logoPictureBox); this.Controls.Add(this.labelProductName); this.Controls.Add(this.labelCopyright); this.Controls.Add(this.labelCompanyName); this.Controls.Add(this.groupBoxContributors); this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.MaximizeBox = false; this.MinimizeBox = false; this.Name = "frmAbout"; this.Opacity = 0.9D; this.Padding = new System.Windows.Forms.Padding(9); this.ShowInTaskbar = false; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; this.Text = "frmAbout"; this.TopMost = true; this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.FrmAbout_FormClosed); ((System.ComponentModel.ISupportInitialize)(this.logoPictureBox)).EndInit(); this.groupBoxContributors.ResumeLayout(false); this.groupBoxContributors.PerformLayout(); this.ResumeLayout(false); this.PerformLayout(); } #endregion private System.Windows.Forms.PictureBox logoPictureBox; private System.Windows.Forms.Label labelProductName; private System.Windows.Forms.Label labelCopyright; private System.Windows.Forms.Label labelCompanyName; private System.Windows.Forms.GroupBox groupBoxContributors; private System.Windows.Forms.Button okButton; private System.Windows.Forms.TextBox textBoxContributors; } } ================================================ FILE: PowerToys/src/modules/MouseWithoutBorders/App/Form/frmAbout.cs ================================================ // 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. // // About box. // // // 2008 created by Truong Do (ductdo). // 2009-... modified by Truong Do (TruongDo). // 2023- Included in PowerToys. // using System; using System.Globalization; using System.Reflection; using System.Windows.Forms; namespace MouseWithoutBorders { internal partial class FrmAbout : System.Windows.Forms.Form, IDisposable { internal FrmAbout() { InitializeComponent(); Text = string.Format(CultureInfo.CurrentCulture, "关于 {0}", AssemblyTitle); labelProductName.Text = string.Format(CultureInfo.CurrentCulture, "{0} {1}", AssemblyProduct, AssemblyVersion); labelCopyright.Text = AssemblyCopyright; labelCompanyName.Text = "项目创建者: Truong Do (Đỗ Đức Trường)"; textBoxContributors.Text += "* 微软车库: Quinn Hawkins, Michael Low, Joe Coplen, Nino Yuniardi, Gwyneth Marshall, David Andrews, Karen Luecking"; textBoxContributors.Text += "\r\n* Peter Hauge\t\t- Visual Studio"; textBoxContributors.Text += "\r\n* Bruce Dawson\t\t- Windows Fundamentals"; textBoxContributors.Text += "\r\n* Alan Myrvold\t\t- Office Security"; textBoxContributors.Text += "\r\n* Adrian Garside\t\t- WEX"; textBoxContributors.Text += "\r\n* Scott Bradner\t\t- Surface"; textBoxContributors.Text += "\r\n* Aleks Gershaft\t\t- Windows Azure"; textBoxContributors.Text += "\r\n* Chinh Huynh\t\t- Windows Azure"; textBoxContributors.Text += "\r\n* Long Nguyen\t\t- Data Center"; textBoxContributors.Text += "\r\n* Triet Le\t\t\t- Cloud Engineering"; textBoxContributors.Text += "\r\n* Luke Schoen\t\t- Excel"; textBoxContributors.Text += "\r\n* Bao Nguyen\t\t- Bing"; textBoxContributors.Text += "\r\n* Ross Nichols\t\t- Windows"; textBoxContributors.Text += "\r\n* Ryan Baltazar\t\t- Windows"; textBoxContributors.Text += "\r\n* Ed Essey\t\t\t- The Garage"; textBoxContributors.Text += "\r\n* Mario Madden\t\t- The Garage"; textBoxContributors.Text += "\r\n* Karthick Mahalingam\t- ACE"; textBoxContributors.Text += "\r\n* Pooja Kamra\t\t- ACE"; textBoxContributors.Text += "\r\n* Justin White\t\t- SA"; textBoxContributors.Text += "\r\n* Chris Ransom\t\t- SA"; textBoxContributors.Text += "\r\n* Mike Ricks\t\t- Red Team"; textBoxContributors.Text += "\r\n* Randy Santossio\t\t- Surface"; textBoxContributors.Text += "\r\n* Ashish Sen Jaswal\t\t- Device Health"; textBoxContributors.Text += "\r\n* Zoltan Harmath\t\t- Security Tools"; textBoxContributors.Text += "\r\n* Luciano Krigun\t\t- Security Products"; textBoxContributors.Text += "\r\n* Jo Hemmerlein\t\t- Red Team"; textBoxContributors.Text += "\r\n* Chris Johnson\t\t- Surface Hub"; textBoxContributors.Text += "\r\n* Loren Ponten\t\t- Surface Hub"; textBoxContributors.Text += "\r\n* Paul Schmitt\t\t- WWL"; textBoxContributors.Text += "\r\n\r\n* 还有我们的众多用户!"; } internal static string AssemblyTitle { get { object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyTitleAttribute), false); if (attributes.Length > 0) { AssemblyTitleAttribute titleAttribute = (AssemblyTitleAttribute)attributes[0]; if (titleAttribute.Title != null && titleAttribute.Title.Length > 0) { return titleAttribute.Title; } } return System.IO.Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().Location); } } internal static string AssemblyVersion => Assembly.GetExecutingAssembly().GetName().Version.ToString(); internal static string AssemblyDescription { get { object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyDescriptionAttribute), false); return attributes.Length == 0 ? string.Empty : ((AssemblyDescriptionAttribute)attributes[0]).Description; } } internal static string AssemblyProduct { get { object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyProductAttribute), false); return attributes.Length == 0 ? string.Empty : ((AssemblyProductAttribute)attributes[0]).Product; } } internal static string AssemblyCopyright { get { object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyCopyrightAttribute), false); return attributes.Length == 0 ? string.Empty : ((AssemblyCopyrightAttribute)attributes[0]).Copyright; } } internal static string AssemblyCompany { get { object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyCompanyAttribute), false); return attributes.Length == 0 ? string.Empty : ((AssemblyCompanyAttribute)attributes[0]).Company; } } private void FrmAbout_FormClosed(object sender, FormClosedEventArgs e) { Common.AboutForm = null; } } } ================================================ FILE: PowerToys/src/modules/MouseWithoutBorders/App/Form/frmInputCallback.cs ================================================ // 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.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Windows.Forms; // // Keyboard/Mouse hook callbacks are handled in the message loop of this form. // // // 2008 created by Truong Do (ductdo). // 2009-... modified by Truong Do (TruongDo). // 2023- Included in PowerToys. // using MouseWithoutBorders.Class; using MouseWithoutBorders.Core; [module: SuppressMessage("Microsoft.Globalization", "CA1300:SpecifyMessageBoxOptions", Scope = "member", Target = "MouseWithoutBorders.frmInputCallback.#InstallKeyboardAndMouseHook()", Justification = "Dotnet port with style preservation")] namespace MouseWithoutBorders { internal partial class FrmInputCallback : System.Windows.Forms.Form { internal FrmInputCallback() { InitializeComponent(); } private void FrmInputCallback_Load(object sender, EventArgs e) { InstallKeyboardAndMouseHook(); Left = 0; Top = 0; Width = 0; Height = 0; } private void FrmInputCallback_Shown(object sender, EventArgs e) { Common.InputCallbackForm = this; Visible = false; } private void FrmInputCallback_FormClosing(object sender, FormClosingEventArgs e) { if (e.CloseReason != CloseReason.WindowsShutDown) { // e.Cancel = true; } } internal void InstallKeyboardAndMouseHook() { /* * Install hooks: * According to http://msdn.microsoft.com/en-us/library/ms644986(VS.85).aspx * we need to process the hook callback message fast enough so make the message loop thread a * high priority thread. (Mouse/Keyboard event is important) * */ try { Common.Hook = new InputHook(); Common.Hook.MouseEvent += new InputHook.MouseEvHandler(Common.MouseEvent); Common.Hook.KeyboardEvent += new InputHook.KeybdEvHandler(Common.KeybdEvent); Logger.Log("(((((Keyboard/Mouse hooks installed/reinstalled!)))))"); /* The hook is called in the context of the thread that installed it. * The call is made by sending a message to the thread that installed the hook. * Therefore, the thread that installed the hook must have a message loop!!! * */ } catch (Win32Exception e) { _ = MessageBox.Show( "安装鼠标/键盘侦听器失败,错误代码: " + e.ErrorCode, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error); Common.MainForm.Quit(false, false); // we are [STAThread] :) } // Thread.CurrentThread.Priority = ThreadPriority.Highest; } } } ================================================ FILE: PowerToys/src/modules/MouseWithoutBorders/App/Form/frmLogon.Designer.cs ================================================ namespace MouseWithoutBorders { partial class frmLogon { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(frmLogon)); this.labelDesktop = new System.Windows.Forms.Label(); this.SuspendLayout(); // // labelDesktop // this.labelDesktop.AutoSize = true; this.labelDesktop.BackColor = System.Drawing.Color.White; this.labelDesktop.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; this.labelDesktop.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.labelDesktop.ForeColor = System.Drawing.Color.LimeGreen; this.labelDesktop.Location = new System.Drawing.Point(1, 1); this.labelDesktop.Name = "labelDesktop"; this.labelDesktop.Size = new System.Drawing.Size(80, 15); this.labelDesktop.TabIndex = 0; this.labelDesktop.Text = "微软无界鼠标"; // // frmLogon // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(83, 17); this.ControlBox = false; this.Controls.Add(this.labelDesktop); this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.MaximizeBox = false; this.MinimizeBox = false; this.Name = "frmLogon"; this.Opacity = 0.5; this.ShowIcon = false; this.ShowInTaskbar = false; this.Text = "微软无界鼠标"; this.TopMost = true; this.Load += new System.EventHandler(this.frmLogon_Load); this.Shown += new System.EventHandler(this.frmLogon_Shown); this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.frmLogon_FormClosing); this.ResumeLayout(false); this.PerformLayout(); } #endregion private System.Windows.Forms.Label labelDesktop; } } ================================================ FILE: PowerToys/src/modules/MouseWithoutBorders/App/Form/frmMatrix.Designer.cs ================================================ using System.Windows.Forms; using System.Collections.Generic; using System.Drawing; using Windows.UI.Notifications; namespace MouseWithoutBorders { partial class FrmMatrix { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.components = new System.ComponentModel.Container(); System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FrmMatrix)); this.pictureBoxMouseWithoutBorders0 = new System.Windows.Forms.PictureBox(); this.toolTip = new System.Windows.Forms.ToolTip(this.components); this.comboBoxLockMachine = new System.Windows.Forms.ComboBox(); this.comboBoxSwitchToAllPC = new System.Windows.Forms.ComboBox(); this.comboBoxReconnect = new System.Windows.Forms.ComboBox(); this.checkBoxTwoRow = new System.Windows.Forms.CheckBox(); this.comboBoxEasyMouse = new System.Windows.Forms.ComboBox(); this.checkBoxDrawMouse = new System.Windows.Forms.CheckBox(); this.checkBoxMouseMoveRelatively = new System.Windows.Forms.CheckBox(); this.checkBoxHideMouse = new System.Windows.Forms.CheckBox(); this.checkBoxBlockMouseAtCorners = new System.Windows.Forms.CheckBox(); this.checkBoxCircle = new System.Windows.Forms.CheckBox(); this.checkBoxBlockScreenSaver = new System.Windows.Forms.CheckBox(); this.checkBoxHideLogo = new System.Windows.Forms.CheckBox(); this.checkBoxShareClipboard = new System.Windows.Forms.CheckBox(); this.checkBoxDisableCAD = new System.Windows.Forms.CheckBox(); this.buttonOK = new System.Windows.Forms.Button(); this.checkBoxReverseLookup = new System.Windows.Forms.CheckBox(); this.checkBoxVKMap = new System.Windows.Forms.CheckBox(); this.comboBoxScreenCapture = new System.Windows.Forms.ComboBox(); this.checkBoxSameSubNet = new System.Windows.Forms.CheckBox(); this.checkBoxClipNetStatus = new System.Windows.Forms.CheckBox(); this.checkBoxSendLog = new System.Windows.Forms.CheckBox(); this.groupBoxKeySetup = new System.Windows.Forms.GroupBox(); this.buttonNewKey = new System.Windows.Forms.Button(); this.textBoxEnc = new System.Windows.Forms.TextBox(); this.LabelEnc = new System.Windows.Forms.Label(); this.checkBoxShowKey = new System.Windows.Forms.CheckBox(); this.labelEasyMouse = new System.Windows.Forms.Label(); this.comboBoxEasyMouseOption = new System.Windows.Forms.ComboBox(); this.checkBoxTransferFile = new System.Windows.Forms.CheckBox(); this.toolTipManual = new System.Windows.Forms.ToolTip(this.components); this.tabPageOther = new System.Windows.Forms.TabPage(); this.groupBoxOtherOptions = new System.Windows.Forms.GroupBox(); this.groupBoxShortcuts = new System.Windows.Forms.GroupBox(); this.labelScreenCapture = new System.Windows.Forms.Label(); this.LabelToggleEasyMouse = new System.Windows.Forms.Label(); this.comboBoxExitMM = new System.Windows.Forms.ComboBox(); this.comboBoxShowSettings = new System.Windows.Forms.ComboBox(); this.labelReconnect = new System.Windows.Forms.Label(); this.labelSwitch2AllPCMode = new System.Windows.Forms.Label(); this.radioButtonDisable = new System.Windows.Forms.RadioButton(); this.radioButtonNum = new System.Windows.Forms.RadioButton(); this.radioButtonF1 = new System.Windows.Forms.RadioButton(); this.labelLockMachine = new System.Windows.Forms.Label(); this.labelExitMM = new System.Windows.Forms.Label(); this.labelShowSettings = new System.Windows.Forms.Label(); this.labelSwitchBetweenMachine = new System.Windows.Forms.Label(); this.tabPageMain = new System.Windows.Forms.TabPage(); this.buttonCancel = new System.Windows.Forms.Button(); this.groupBoxMachineMatrix = new System.Windows.Forms.GroupBox(); this.linkLabelReConfigure = new System.Windows.Forms.LinkLabel(); this.tabControlSetting = new System.Windows.Forms.TabControl(); this.tabPageAdvancedSettings = new System.Windows.Forms.TabPage(); this.groupBoxName2IPPolicyList = new System.Windows.Forms.GroupBox(); this.textBoxMachineName2IPPolicyList = new System.Windows.Forms.TextBox(); this.pictureBoxMouseWithoutBorders = new System.Windows.Forms.PictureBox(); this.groupBoxDNS = new System.Windows.Forms.GroupBox(); this.textBoxMachineName2IP = new System.Windows.Forms.TextBox(); this.textBoxDNS = new System.Windows.Forms.TextBox(); this.linkLabelHelp = new System.Windows.Forms.LinkLabel(); this.linkLabelMiniLog = new System.Windows.Forms.LinkLabel(); ((System.ComponentModel.ISupportInitialize)(this.pictureBoxMouseWithoutBorders0)).BeginInit(); this.groupBoxKeySetup.SuspendLayout(); this.tabPageOther.SuspendLayout(); this.groupBoxOtherOptions.SuspendLayout(); this.groupBoxShortcuts.SuspendLayout(); this.tabPageMain.SuspendLayout(); this.groupBoxMachineMatrix.SuspendLayout(); this.tabControlSetting.SuspendLayout(); this.tabPageAdvancedSettings.SuspendLayout(); this.groupBoxName2IPPolicyList.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.pictureBoxMouseWithoutBorders)).BeginInit(); this.groupBoxDNS.SuspendLayout(); this.SuspendLayout(); // // pictureBoxMouseWithoutBorders0 // this.pictureBoxMouseWithoutBorders0.BackgroundImage = ((System.Drawing.Image)(resources.GetObject("pictureBoxMouseWithoutBorders0.BackgroundImage"))); this.pictureBoxMouseWithoutBorders0.BackgroundImageLayout = System.Windows.Forms.ImageLayout.None; this.pictureBoxMouseWithoutBorders0.Location = new System.Drawing.Point(590, 271); this.pictureBoxMouseWithoutBorders0.Name = "pictureBoxMouseWithoutBorders0"; this.pictureBoxMouseWithoutBorders0.Size = new System.Drawing.Size(190, 54); this.pictureBoxMouseWithoutBorders0.TabIndex = 16; this.pictureBoxMouseWithoutBorders0.TabStop = false; this.pictureBoxMouseWithoutBorders0.Visible = false; // // toolTip // this.toolTip.AutomaticDelay = 100; this.toolTip.AutoPopDelay = 5000; this.toolTip.InitialDelay = 100; this.toolTip.ReshowDelay = 20; this.toolTip.ToolTipIcon = System.Windows.Forms.ToolTipIcon.Info; this.toolTip.ToolTipTitle = "Microsoft® Visual Studio® 2010"; // // comboBoxLockMachine // this.comboBoxLockMachine.Enabled = true; this.comboBoxLockMachine.FormattingEnabled = true; this.comboBoxLockMachine.Items.AddRange(new object[] { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "关闭"}); this.comboBoxLockMachine.Location = new System.Drawing.Point(230, 68); this.comboBoxLockMachine.Name = "comboBoxLockMachine"; this.comboBoxLockMachine.Size = new System.Drawing.Size(54, 21); this.comboBoxLockMachine.TabIndex = 205; this.comboBoxLockMachine.Text = "L"; this.toolTip.SetToolTip(this.comboBoxLockMachine, "双击锁定所有电脑。"); this.comboBoxLockMachine.TextChanged += new System.EventHandler(this.ComboBoxLockMachine_TextChanged); // // comboBoxSwitchToAllPC // this.comboBoxSwitchToAllPC.Enabled = true; this.comboBoxSwitchToAllPC.FormattingEnabled = true; this.comboBoxSwitchToAllPC.Items.AddRange(new object[] { "Ctrl*3", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "关闭"}); this.comboBoxSwitchToAllPC.Location = new System.Drawing.Point(490, 68); this.comboBoxSwitchToAllPC.Name = "comboBoxSwitchToAllPC"; this.comboBoxSwitchToAllPC.Size = new System.Drawing.Size(56, 21); this.comboBoxSwitchToAllPC.TabIndex = 206; this.comboBoxSwitchToAllPC.Text = "关闭"; this.toolTip.SetToolTip(this.comboBoxSwitchToAllPC, "按三次 Ctrl 或者 Ctrl+Alt+[?]"); this.comboBoxSwitchToAllPC.TextChanged += new System.EventHandler(this.ComboBoxSwitchToAllPC_TextChanged); // // comboBoxReconnect // this.comboBoxReconnect.Enabled = true; this.comboBoxReconnect.FormattingEnabled = true; this.comboBoxReconnect.Items.AddRange(new object[] { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "关闭"}); this.comboBoxReconnect.Location = new System.Drawing.Point(230, 95); this.comboBoxReconnect.Name = "comboBoxReconnect"; this.comboBoxReconnect.Size = new System.Drawing.Size(54, 21); this.comboBoxReconnect.TabIndex = 207; this.comboBoxReconnect.Text = "R"; this.toolTip.SetToolTip(this.comboBoxReconnect, "防止连接丢失。"); this.comboBoxReconnect.TextChanged += new System.EventHandler(this.ComboBoxReconnect_TextChanged); // // checkBoxTwoRow // this.checkBoxTwoRow.AutoSize = true; this.checkBoxTwoRow.Location = new System.Drawing.Point(9, 205); this.checkBoxTwoRow.Name = "checkBoxTwoRow"; this.checkBoxTwoRow.Size = new System.Drawing.Size(72, 17); this.checkBoxTwoRow.TabIndex = 6; this.checkBoxTwoRow.Text = "双行排列(&R)"; this.toolTip.SetToolTip(this.checkBoxTwoRow, "选中改为上下两行,可上下移动鼠标来切换设备。"); this.checkBoxTwoRow.UseVisualStyleBackColor = true; this.checkBoxTwoRow.CheckedChanged += new System.EventHandler(this.CheckBoxTwoRow_CheckedChanged); // // comboBoxEasyMouse // this.comboBoxEasyMouse.Enabled = true; this.comboBoxEasyMouse.FormattingEnabled = true; this.comboBoxEasyMouse.Items.AddRange(new object[] { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "关闭"}); this.comboBoxEasyMouse.Location = new System.Drawing.Point(490, 122); this.comboBoxEasyMouse.Name = "comboBoxEasyMouse"; this.comboBoxEasyMouse.Size = new System.Drawing.Size(56, 21); this.comboBoxEasyMouse.TabIndex = 208; this.comboBoxEasyMouse.Text = "E"; this.toolTip.SetToolTip(this.comboBoxEasyMouse, "切换易动功能的开关,易动设置为按住按键开启时该切换无效。"); this.comboBoxEasyMouse.TextChanged += new System.EventHandler(this.ComboBoxEasyMouse_TextChanged); // // checkBoxDrawMouse // this.checkBoxDrawMouse.AutoSize = true; this.checkBoxDrawMouse.Checked = true; this.checkBoxDrawMouse.CheckState = System.Windows.Forms.CheckState.Checked; this.checkBoxDrawMouse.Location = new System.Drawing.Point(9, 118); this.checkBoxDrawMouse.Name = "checkBoxDrawMouse"; this.checkBoxDrawMouse.Size = new System.Drawing.Size(117, 17); this.checkBoxDrawMouse.TabIndex = 171; this.checkBoxDrawMouse.Text = "绘制鼠标光标(&D)"; this.toolTip.SetToolTip(this.checkBoxDrawMouse, "在 Win10 以上,如果电脑没插鼠标,那么默认不会显示鼠标光标,需要单独绘制。"); this.checkBoxDrawMouse.UseVisualStyleBackColor = true; this.checkBoxDrawMouse.CheckedChanged += new System.EventHandler(this.CheckBoxDrawMouse_CheckedChanged); // // checkBoxMouseMoveRelatively // this.checkBoxMouseMoveRelatively.AutoSize = true; this.checkBoxMouseMoveRelatively.Checked = true; this.checkBoxMouseMoveRelatively.CheckState = System.Windows.Forms.CheckState.Checked; this.checkBoxMouseMoveRelatively.Location = new System.Drawing.Point(268, 53); this.checkBoxMouseMoveRelatively.Name = "checkBoxMouseMoveRelatively"; this.checkBoxMouseMoveRelatively.Size = new System.Drawing.Size(131, 17); this.checkBoxMouseMoveRelatively.TabIndex = 170; this.checkBoxMouseMoveRelatively.Text = "鼠标相对移动(&M)"; this.toolTip.SetToolTip(this.checkBoxMouseMoveRelatively, "提供不同屏幕配置和多屏使用的支持。"); this.checkBoxMouseMoveRelatively.UseVisualStyleBackColor = true; this.checkBoxMouseMoveRelatively.CheckedChanged += new System.EventHandler(this.CheckBoxMouseMoveRelatively_CheckedChanged); // // checkBoxHideMouse // this.checkBoxHideMouse.AutoSize = true; this.checkBoxHideMouse.Checked = true; this.checkBoxHideMouse.CheckState = System.Windows.Forms.CheckState.Checked; this.checkBoxHideMouse.Location = new System.Drawing.Point(9, 96); this.checkBoxHideMouse.Name = "checkBoxHideMouse"; this.checkBoxHideMouse.Size = new System.Drawing.Size(156, 17); this.checkBoxHideMouse.TabIndex = 169; this.checkBoxHideMouse.Text = "移出屏幕隐藏鼠标(&H)"; this.toolTip.SetToolTip(this.checkBoxHideMouse, "鼠标移出屏幕后,把本设备光标隐藏在屏幕顶部,消除当前窗口焦点,使键盘输入转移到被控电脑。"); this.checkBoxHideMouse.UseVisualStyleBackColor = true; this.checkBoxHideMouse.CheckedChanged += new System.EventHandler(this.CheckBoxHideMouse_CheckedChanged); // // checkBoxBlockMouseAtCorners // this.checkBoxBlockMouseAtCorners.AutoSize = true; this.checkBoxBlockMouseAtCorners.Location = new System.Drawing.Point(268, 75); this.checkBoxBlockMouseAtCorners.Name = "checkBoxBlockMouseAtCorners"; this.checkBoxBlockMouseAtCorners.Size = new System.Drawing.Size(172, 17); this.checkBoxBlockMouseAtCorners.TabIndex = 172; this.checkBoxBlockMouseAtCorners.Text = "屏幕四角阻挡"; this.toolTip.SetToolTip(this.checkBoxBlockMouseAtCorners, "在屏幕四角处阻挡跨界,防止误触。"); this.checkBoxBlockMouseAtCorners.UseVisualStyleBackColor = true; this.checkBoxBlockMouseAtCorners.CheckedChanged += new System.EventHandler(this.CheckBoxBlockMouseAtCorners_CheckedChanged); // // checkBoxCircle // this.checkBoxCircle.AutoSize = true; this.checkBoxCircle.Location = new System.Drawing.Point(9, 11); this.checkBoxCircle.Name = "checkBoxCircle"; this.checkBoxCircle.Size = new System.Drawing.Size(87, 17); this.checkBoxCircle.TabIndex = 163; this.checkBoxCircle.Text = "循环移动(&W)"; this.toolTip.SetToolTip(this.checkBoxCircle, "鼠标移动超过最后一台电脑屏幕后,回到第一台电脑上。"); this.checkBoxCircle.UseVisualStyleBackColor = true; this.checkBoxCircle.CheckedChanged += new System.EventHandler(this.CheckBoxCircle_CheckedChanged); // // checkBoxBlockScreenSaver // this.checkBoxBlockScreenSaver.AutoSize = true; this.checkBoxBlockScreenSaver.Checked = true; this.checkBoxBlockScreenSaver.CheckState = System.Windows.Forms.CheckState.Checked; this.checkBoxBlockScreenSaver.Location = new System.Drawing.Point(268, 32); this.checkBoxBlockScreenSaver.Name = "checkBoxBlockScreenSaver"; this.checkBoxBlockScreenSaver.Size = new System.Drawing.Size(211, 17); this.checkBoxBlockScreenSaver.TabIndex = 168; this.checkBoxBlockScreenSaver.Text = "阻止屏保(&B)"; this.toolTip.SetToolTip(this.checkBoxBlockScreenSaver, "阻止被控电脑启动屏幕保护程序。"); this.checkBoxBlockScreenSaver.UseVisualStyleBackColor = true; this.checkBoxBlockScreenSaver.CheckedChanged += new System.EventHandler(this.CheckBoxBlockScreenSaver_CheckedChanged); // // checkBoxHideLogo // this.checkBoxHideLogo.AutoSize = true; this.checkBoxHideLogo.Location = new System.Drawing.Point(9, 75); this.checkBoxHideLogo.Name = "checkBoxHideLogo"; this.checkBoxHideLogo.Size = new System.Drawing.Size(164, 17); this.checkBoxHideLogo.TabIndex = 167; this.checkBoxHideLogo.Text = "登录时不显示 &Logo"; this.toolTip.SetToolTip(this.checkBoxHideLogo, "隐藏登录桌面上的“微软无界鼠标”字样。"); this.checkBoxHideLogo.UseVisualStyleBackColor = true; this.checkBoxHideLogo.CheckedChanged += new System.EventHandler(this.CheckBoxHideLogo_CheckedChanged); // // checkBoxShareClipboard // this.checkBoxShareClipboard.AutoSize = true; this.checkBoxShareClipboard.Checked = true; this.checkBoxShareClipboard.CheckState = System.Windows.Forms.CheckState.Checked; this.checkBoxShareClipboard.Location = new System.Drawing.Point(9, 32); this.checkBoxShareClipboard.Name = "checkBoxShareClipboard"; this.checkBoxShareClipboard.Size = new System.Drawing.Size(101, 17); this.checkBoxShareClipboard.TabIndex = 165; this.checkBoxShareClipboard.Text = "共享剪贴板(&S)"; this.toolTip.SetToolTip(this.checkBoxShareClipboard, "共享复制的内容,如果功能突然失效,按 Ctrl + Alt + Del 然后按 Esc 也许可以解决问题。"); this.checkBoxShareClipboard.UseVisualStyleBackColor = true; this.checkBoxShareClipboard.CheckedChanged += new System.EventHandler(this.CheckBoxShareClipboard_CheckedChanged); // // checkBoxDisableCAD // this.checkBoxDisableCAD.AutoSize = true; this.checkBoxDisableCAD.Checked = true; this.checkBoxDisableCAD.CheckState = System.Windows.Forms.CheckState.Checked; this.checkBoxDisableCAD.Location = new System.Drawing.Point(268, 11); this.checkBoxDisableCAD.Name = "checkBoxDisableCAD"; this.checkBoxDisableCAD.Size = new System.Drawing.Size(86, 17); this.checkBoxDisableCAD.TabIndex = 166; this.checkBoxDisableCAD.Text = "禁用 Ctrl+Alt+Del 登录(&C)"; this.toolTip.SetToolTip(this.checkBoxDisableCAD, "不需要按 Ctrl+Alt+Del 登录"); this.checkBoxDisableCAD.UseVisualStyleBackColor = true; this.checkBoxDisableCAD.CheckedChanged += new System.EventHandler(this.CheckBoxDisableCAD_CheckedChanged); // // buttonOK // this.buttonOK.Location = new System.Drawing.Point(187, 328); this.buttonOK.Name = "buttonOK"; this.buttonOK.Size = new System.Drawing.Size(75, 23); this.buttonOK.TabIndex = 20; this.buttonOK.Text = "应用(&A)"; this.toolTip.SetToolTip(this.buttonOK, "保存设置,刷新连接。"); this.buttonOK.UseVisualStyleBackColor = true; this.buttonOK.Click += new System.EventHandler(this.ButtonOK_Click); // // checkBoxReverseLookup // this.checkBoxReverseLookup.AutoSize = true; this.checkBoxReverseLookup.Location = new System.Drawing.Point(9, 141); this.checkBoxReverseLookup.Name = "checkBoxReverseLookup"; this.checkBoxReverseLookup.Size = new System.Drawing.Size(196, 17); this.checkBoxReverseLookup.TabIndex = 173; this.checkBoxReverseLookup.Text = "验证远程设备地址(&V)"; this.toolTip.SetToolTip(this.checkBoxReverseLookup, "通过反向 DNS 查找来验证远程电脑的 IP 地址(高级功能,如有疑问查看在线帮助)"); this.checkBoxReverseLookup.UseVisualStyleBackColor = true; this.checkBoxReverseLookup.CheckedChanged += new System.EventHandler(this.CheckBoxReverseLookup_CheckedChanged); // // checkBoxVKMap // this.checkBoxVKMap.Enabled = false; this.checkBoxVKMap.AutoSize = true; this.checkBoxVKMap.Location = new System.Drawing.Point(268, 98); this.checkBoxVKMap.Name = "checkBoxVKMap"; this.checkBoxVKMap.Size = new System.Drawing.Size(115, 17); this.checkBoxVKMap.TabIndex = 174; this.checkBoxVKMap.Text = "启用按键映射(&U)"; this.toolTip.SetToolTip(this.checkBoxVKMap, "使用按键映射来改变快捷键,详情参阅在线帮助。"); this.checkBoxVKMap.UseVisualStyleBackColor = true; this.checkBoxVKMap.CheckedChanged += new System.EventHandler(this.CheckBoxVKMap_CheckedChanged); // // comboBoxScreenCapture // this.comboBoxScreenCapture.Enabled = false; this.comboBoxScreenCapture.FormattingEnabled = true; this.comboBoxScreenCapture.Items.AddRange(new object[] { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "关闭"}); this.comboBoxScreenCapture.Location = new System.Drawing.Point(230, 121); this.comboBoxScreenCapture.Name = "comboBoxScreenCapture"; this.comboBoxScreenCapture.Size = new System.Drawing.Size(54, 21); this.comboBoxScreenCapture.TabIndex = 210; this.comboBoxScreenCapture.Text = "S"; this.toolTip.SetToolTip(this.comboBoxScreenCapture, "按下快捷键,按住鼠标左键拖动选择屏幕区域,松手完成截屏。"); this.comboBoxScreenCapture.TextChanged += new System.EventHandler(this.ComboBoxScreenCapture_TextChanged); // // checkBoxSameSubNet // this.checkBoxSameSubNet.AutoSize = true; this.checkBoxSameSubNet.Location = new System.Drawing.Point(9, 162); this.checkBoxSameSubNet.Name = "checkBoxSameSubNet"; this.checkBoxSameSubNet.Size = new System.Drawing.Size(114, 18); this.checkBoxSameSubNet.TabIndex = 175; this.checkBoxSameSubNet.Text = "仅限内网控制"; this.toolTip.SetToolTip(this.checkBoxSameSubNet, "仅允许连接到同一内网 NNN.NNN.*.* 下的电脑,只支持 IPv4。"); this.checkBoxSameSubNet.UseCompatibleTextRendering = true; this.checkBoxSameSubNet.UseVisualStyleBackColor = true; this.checkBoxSameSubNet.CheckedChanged += new System.EventHandler(this.CheckBoxSameSubNet_CheckedChanged); // // checkBoxClipNetStatus // this.checkBoxClipNetStatus.AutoSize = true; this.checkBoxClipNetStatus.Location = new System.Drawing.Point(268, 119); this.checkBoxClipNetStatus.Name = "checkBoxClipNetStatus"; this.checkBoxClipNetStatus.Size = new System.Drawing.Size(231, 18); this.checkBoxClipNetStatus.TabIndex = 176; this.checkBoxClipNetStatus.Text = "显示剪贴板/网络活动通知(&N)"; this.toolTip.SetToolTip(this.checkBoxClipNetStatus, "剪贴板和网络状态发生变化时,发送系统通知。"); this.checkBoxClipNetStatus.UseCompatibleTextRendering = true; this.checkBoxClipNetStatus.UseVisualStyleBackColor = true; this.checkBoxClipNetStatus.CheckedChanged += new System.EventHandler(this.CheckBoxClipNetStatus_CheckedChanged); // // checkBoxSendLog // this.checkBoxSendLog.AutoSize = true; this.checkBoxSendLog.Location = new System.Drawing.Point(268, 142); this.checkBoxSendLog.Name = "checkBoxSendLog"; this.checkBoxSendLog.Size = new System.Drawing.Size(95, 18); this.checkBoxSendLog.TabIndex = 177; this.checkBoxSendLog.Text = "发送错误日志(&E)"; this.toolTip.SetToolTip(this.checkBoxSendLog, "发送匿名错误日志到微软车库来帮助改进本软件。"); this.checkBoxSendLog.UseCompatibleTextRendering = true; this.checkBoxSendLog.UseVisualStyleBackColor = true; this.checkBoxSendLog.Visible = false; this.checkBoxSendLog.CheckedChanged += new System.EventHandler(this.CheckBoxSendLog_CheckedChanged); // // groupBoxKeySetup // this.groupBoxKeySetup.Controls.Add(this.buttonNewKey); this.groupBoxKeySetup.Controls.Add(this.textBoxEnc); this.groupBoxKeySetup.Controls.Add(this.LabelEnc); this.groupBoxKeySetup.Controls.Add(this.checkBoxShowKey); this.groupBoxKeySetup.Location = new System.Drawing.Point(3, 6); this.groupBoxKeySetup.Name = "groupBoxKeySetup"; this.groupBoxKeySetup.Size = new System.Drawing.Size(558, 66); this.groupBoxKeySetup.TabIndex = 0; this.groupBoxKeySetup.TabStop = false; this.groupBoxKeySetup.Text = " 连接(&S)"; this.toolTip.SetToolTip(this.groupBoxKeySetup, "数据通信使用该密码加密。"); // // buttonNewKey // this.buttonNewKey.Location = new System.Drawing.Point(471, 19); this.buttonNewKey.Name = "buttonNewKey"; this.buttonNewKey.Size = new System.Drawing.Size(75, 23); this.buttonNewKey.TabIndex = 22; this.buttonNewKey.Text = "生成(&K)"; this.buttonNewKey.UseVisualStyleBackColor = true; this.buttonNewKey.Click += new System.EventHandler(this.ButtonNewKey_Click); // // textBoxEnc // this.textBoxEnc.Location = new System.Drawing.Point(86, 19); this.textBoxEnc.MaxLength = 22; this.textBoxEnc.Name = "textBoxEnc"; this.textBoxEnc.PasswordChar = '*'; this.textBoxEnc.Size = new System.Drawing.Size(304, 20); this.textBoxEnc.TabIndex = 3; this.toolTip.SetToolTip(this.textBoxEnc, "密码必须自动生成,不可以手动输入。将密码输入到其他设备建立连接。"); // // LabelEnc // this.LabelEnc.AutoSize = true; this.LabelEnc.Location = new System.Drawing.Point(7, 25); this.LabelEnc.Name = "LabelEnc"; this.LabelEnc.Size = new System.Drawing.Size(69, 13); this.LabelEnc.TabIndex = 19; this.LabelEnc.Text = "密码:"; // // checkBoxShowKey // this.checkBoxShowKey.AutoSize = true; this.checkBoxShowKey.Location = new System.Drawing.Point(396, 21); this.checkBoxShowKey.Name = "checkBoxShowKey"; this.checkBoxShowKey.Size = new System.Drawing.Size(73, 17); this.checkBoxShowKey.TabIndex = 4; this.checkBoxShowKey.Text = "显示(&S)"; this.checkBoxShowKey.UseVisualStyleBackColor = true; this.checkBoxShowKey.CheckedChanged += new System.EventHandler(this.CheckBoxShowKey_CheckedChanged); // // labelEasyMouse // this.labelEasyMouse.AutoSize = true; this.labelEasyMouse.Location = new System.Drawing.Point(309, 98); this.labelEasyMouse.Name = "labelEasyMouse"; this.labelEasyMouse.Size = new System.Drawing.Size(68, 13); this.labelEasyMouse.TabIndex = 211; this.labelEasyMouse.Text = "易动模式:"; this.toolTip.SetToolTip(this.labelEasyMouse, "如果易动不选择常开,你也可以选择按住 Ctrl 或 Shift 来快速移动到其他电脑。"); // // comboBoxEasyMouseOption // this.comboBoxEasyMouseOption.Enabled = true; this.comboBoxEasyMouseOption.FormattingEnabled = true; this.comboBoxEasyMouseOption.Items.AddRange(new object[] { "开启", "Ctrl", "Shift", "关闭"}); this.comboBoxEasyMouseOption.Location = new System.Drawing.Point(490, 94); this.comboBoxEasyMouseOption.Name = "comboBoxEasyMouseOption"; this.comboBoxEasyMouseOption.Size = new System.Drawing.Size(56, 21); this.comboBoxEasyMouseOption.TabIndex = 212; this.comboBoxEasyMouseOption.Text = "开启"; this.toolTip.SetToolTip(this.comboBoxEasyMouseOption, "移动鼠标到屏幕边缘即可跨电脑移动。"); this.comboBoxEasyMouseOption.TextChanged += new System.EventHandler(this.ComboBoxEasyMouseOption_TextChanged); // // checkBoxTransferFile // this.checkBoxTransferFile.AutoSize = true; this.checkBoxTransferFile.Checked = true; this.checkBoxTransferFile.CheckState = System.Windows.Forms.CheckState.Checked; this.checkBoxTransferFile.Font = new System.Drawing.Font("Microsoft Sans Serif", 7.8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.checkBoxTransferFile.Location = new System.Drawing.Point(26, 51); this.checkBoxTransferFile.Name = "checkBoxTransferFile"; this.checkBoxTransferFile.Size = new System.Drawing.Size(81, 17); this.checkBoxTransferFile.TabIndex = 178; this.checkBoxTransferFile.Text = "自动传输文件(&T)"; this.toolTip.SetToolTip(this.checkBoxTransferFile, "共享复制的文件,复制文件小于 100MB 时,自动传输到别的电脑的剪贴板。"); this.checkBoxTransferFile.UseVisualStyleBackColor = true; this.checkBoxTransferFile.CheckedChanged += new System.EventHandler(this.CheckBoxTransferFile_CheckedChanged); // // toolTipManual // this.toolTipManual.ToolTipTitle = "Microsoft® Visual Studio® 2010"; // // tabPageOther // this.tabPageOther.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(246)))), ((int)(((byte)(245)))), ((int)(((byte)(242))))); this.tabPageOther.Controls.Add(this.groupBoxOtherOptions); this.tabPageOther.Controls.Add(this.groupBoxShortcuts); this.tabPageOther.Location = new System.Drawing.Point(4, 25); this.tabPageOther.Name = "tabPageOther"; this.tabPageOther.Padding = new System.Windows.Forms.Padding(3); this.tabPageOther.Size = new System.Drawing.Size(563, 362); this.tabPageOther.TabIndex = 1; this.tabPageOther.Text = "配置"; // // groupBoxOtherOptions // this.groupBoxOtherOptions.Controls.Add(this.checkBoxTransferFile); this.groupBoxOtherOptions.Controls.Add(this.checkBoxSendLog); this.groupBoxOtherOptions.Controls.Add(this.checkBoxClipNetStatus); this.groupBoxOtherOptions.Controls.Add(this.checkBoxSameSubNet); this.groupBoxOtherOptions.Controls.Add(this.checkBoxVKMap); this.groupBoxOtherOptions.Controls.Add(this.checkBoxReverseLookup); this.groupBoxOtherOptions.Controls.Add(this.checkBoxDrawMouse); this.groupBoxOtherOptions.Controls.Add(this.checkBoxMouseMoveRelatively); this.groupBoxOtherOptions.Controls.Add(this.checkBoxHideMouse); this.groupBoxOtherOptions.Controls.Add(this.checkBoxBlockMouseAtCorners); this.groupBoxOtherOptions.Controls.Add(this.checkBoxCircle); this.groupBoxOtherOptions.Controls.Add(this.checkBoxBlockScreenSaver); this.groupBoxOtherOptions.Controls.Add(this.checkBoxHideLogo); this.groupBoxOtherOptions.Controls.Add(this.checkBoxShareClipboard); this.groupBoxOtherOptions.Controls.Add(this.checkBoxDisableCAD); this.groupBoxOtherOptions.Dock = System.Windows.Forms.DockStyle.Top; this.groupBoxOtherOptions.Location = new System.Drawing.Point(3, 3); this.groupBoxOtherOptions.Name = "groupBoxOtherOptions"; this.groupBoxOtherOptions.Size = new System.Drawing.Size(557, 189); this.groupBoxOtherOptions.TabIndex = 163; this.groupBoxOtherOptions.TabStop = false; // // groupBoxShortcuts // this.groupBoxShortcuts.Controls.Add(this.comboBoxEasyMouseOption); this.groupBoxShortcuts.Controls.Add(this.labelEasyMouse); this.groupBoxShortcuts.Controls.Add(this.comboBoxScreenCapture); this.groupBoxShortcuts.Controls.Add(this.labelScreenCapture); this.groupBoxShortcuts.Controls.Add(this.comboBoxEasyMouse); this.groupBoxShortcuts.Controls.Add(this.LabelToggleEasyMouse); this.groupBoxShortcuts.Controls.Add(this.comboBoxReconnect); this.groupBoxShortcuts.Controls.Add(this.comboBoxSwitchToAllPC); this.groupBoxShortcuts.Controls.Add(this.comboBoxExitMM); this.groupBoxShortcuts.Controls.Add(this.comboBoxLockMachine); this.groupBoxShortcuts.Controls.Add(this.comboBoxShowSettings); this.groupBoxShortcuts.Controls.Add(this.labelReconnect); this.groupBoxShortcuts.Controls.Add(this.labelSwitch2AllPCMode); this.groupBoxShortcuts.Controls.Add(this.radioButtonDisable); this.groupBoxShortcuts.Controls.Add(this.radioButtonNum); this.groupBoxShortcuts.Controls.Add(this.radioButtonF1); this.groupBoxShortcuts.Controls.Add(this.labelLockMachine); this.groupBoxShortcuts.Controls.Add(this.labelExitMM); this.groupBoxShortcuts.Controls.Add(this.labelShowSettings); this.groupBoxShortcuts.Controls.Add(this.labelSwitchBetweenMachine); this.groupBoxShortcuts.Dock = System.Windows.Forms.DockStyle.Bottom; this.groupBoxShortcuts.Location = new System.Drawing.Point(3, 198); this.groupBoxShortcuts.Name = "groupBoxShortcuts"; this.groupBoxShortcuts.Size = new System.Drawing.Size(557, 161); this.groupBoxShortcuts.TabIndex = 200; this.groupBoxShortcuts.TabStop = false; this.groupBoxShortcuts.Text = " 快捷键(&K) "; ToolTip groupBoxToolTip = new ToolTip(); groupBoxToolTip.SetToolTip(this.groupBoxShortcuts, "以下设置由 PowerToys 设置界面控制."); foreach (Control control in this.groupBoxShortcuts.Controls) { control.Enabled = false; } // // labelScreenCapture // this.labelScreenCapture.AutoSize = true; this.labelScreenCapture.Location = new System.Drawing.Point(6, 124); this.labelScreenCapture.Name = "labelScreenCapture"; this.labelScreenCapture.Size = new System.Drawing.Size(173, 13); this.labelScreenCapture.TabIndex = 209; this.labelScreenCapture.Text = "区块截屏 Ctrl+Shift+:"; // // LabelToggleEasyMouse // this.LabelToggleEasyMouse.AutoSize = true; this.LabelToggleEasyMouse.Location = new System.Drawing.Point(309, 125); this.LabelToggleEasyMouse.Name = "LabelToggleEasyMouse"; this.LabelToggleEasyMouse.Size = new System.Drawing.Size(149, 13); this.LabelToggleEasyMouse.TabIndex = 114; this.LabelToggleEasyMouse.Text = "切换易动 Ctrl+Alt+:"; // // comboBoxExitMM // this.comboBoxExitMM.Enabled = false; this.comboBoxExitMM.FormattingEnabled = true; this.comboBoxExitMM.Items.AddRange(new object[] { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "关闭"}); this.comboBoxExitMM.Location = new System.Drawing.Point(490, 41); this.comboBoxExitMM.Name = "comboBoxExitMM"; this.comboBoxExitMM.Size = new System.Drawing.Size(56, 21); this.comboBoxExitMM.TabIndex = 204; this.comboBoxExitMM.Text = "Q"; this.comboBoxExitMM.TextChanged += new System.EventHandler(this.ComboBoxExitMM_TextChanged); // // comboBoxShowSettings // this.comboBoxShowSettings.Enabled = false; this.comboBoxShowSettings.FormattingEnabled = true; this.comboBoxShowSettings.Items.AddRange(new object[] { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "关闭"}); this.comboBoxShowSettings.Location = new System.Drawing.Point(230, 41); this.comboBoxShowSettings.Name = "comboBoxShowSettings"; this.comboBoxShowSettings.Size = new System.Drawing.Size(54, 21); this.comboBoxShowSettings.TabIndex = 203; this.comboBoxShowSettings.Text = "M"; this.comboBoxShowSettings.TextChanged += new System.EventHandler(this.ComboBoxShowSettings_TextChanged); // // labelReconnect // this.labelReconnect.AutoSize = true; this.labelReconnect.Location = new System.Drawing.Point(6, 98); this.labelReconnect.Name = "labelReconnect"; this.labelReconnect.Size = new System.Drawing.Size(195, 13); this.labelReconnect.TabIndex = 59; this.labelReconnect.Text = "刷新连接 Ctrl+Alt+:"; // // labelSwitch2AllPCMode // this.labelSwitch2AllPCMode.AutoSize = true; this.labelSwitch2AllPCMode.Location = new System.Drawing.Point(309, 71); this.labelSwitch2AllPCMode.Name = "labelSwitch2AllPCMode"; this.labelSwitch2AllPCMode.Size = new System.Drawing.Size(122, 13); this.labelSwitch2AllPCMode.TabIndex = 33; this.labelSwitch2AllPCMode.Text = "同步模式:"; // // radioButtonDisable // this.radioButtonDisable.AutoSize = true; this.radioButtonDisable.Location = new System.Drawing.Point(434, 14); this.radioButtonDisable.Name = "radioButtonDisable"; this.radioButtonDisable.Size = new System.Drawing.Size(60, 17); this.radioButtonDisable.TabIndex = 202; this.radioButtonDisable.TabStop = true; this.radioButtonDisable.Text = "关闭(&D)"; this.radioButtonDisable.UseVisualStyleBackColor = true; this.radioButtonDisable.CheckedChanged += new System.EventHandler(this.RadioButton_CheckedChanged); // // radioButtonNum // this.radioButtonNum.AutoSize = true; this.radioButtonNum.Location = new System.Drawing.Point(346, 15); this.radioButtonNum.Name = "radioButtonNum"; this.radioButtonNum.Size = new System.Drawing.Size(67, 17); this.radioButtonNum.TabIndex = 201; this.radioButtonNum.TabStop = true; this.radioButtonNum.Text = "&1, 2, 3, 4"; this.radioButtonNum.UseVisualStyleBackColor = true; this.radioButtonNum.CheckedChanged += new System.EventHandler(this.RadioButton_CheckedChanged); // // radioButtonF1 // this.radioButtonF1.AutoSize = true; this.radioButtonF1.Location = new System.Drawing.Point(230, 14); this.radioButtonF1.Name = "radioButtonF1"; this.radioButtonF1.Size = new System.Drawing.Size(91, 17); this.radioButtonF1.TabIndex = 200; this.radioButtonF1.TabStop = true; this.radioButtonF1.Text = "&F1, F2, F3, F4"; this.radioButtonF1.UseVisualStyleBackColor = true; this.radioButtonF1.CheckedChanged += new System.EventHandler(this.RadioButton_CheckedChanged); // // labelLockMachine // this.labelLockMachine.AutoSize = true; this.labelLockMachine.Location = new System.Drawing.Point(6, 71); this.labelLockMachine.Name = "labelLockMachine"; this.labelLockMachine.Size = new System.Drawing.Size(133, 13); this.labelLockMachine.TabIndex = 31; this.labelLockMachine.Text = "全部锁屏 Ctrl+Alt+:"; // // labelExitMM // this.labelExitMM.AutoSize = true; this.labelExitMM.Location = new System.Drawing.Point(309, 44); this.labelExitMM.Name = "labelExitMM"; this.labelExitMM.Size = new System.Drawing.Size(138, 13); this.labelExitMM.TabIndex = 29; this.labelExitMM.Text = "退出软件 Ctrl+Alt+Shift+:"; // // labelShowSettings // this.labelShowSettings.AutoSize = true; this.labelShowSettings.Location = new System.Drawing.Point(6, 44); this.labelShowSettings.Name = "labelShowSettings"; this.labelShowSettings.Size = new System.Drawing.Size(149, 13); this.labelShowSettings.TabIndex = 27; this.labelShowSettings.Text = "打开设置 Ctrl+Alt+:"; // // labelSwitchBetweenMachine // this.labelSwitchBetweenMachine.AutoSize = true; this.labelSwitchBetweenMachine.Location = new System.Drawing.Point(6, 18); this.labelSwitchBetweenMachine.Name = "labelSwitchBetweenMachine"; this.labelSwitchBetweenMachine.Size = new System.Drawing.Size(179, 13); this.labelSwitchBetweenMachine.TabIndex = 24; this.labelSwitchBetweenMachine.Text = "切换设备 Ctrl+Alt+:"; // // tabPageMain // this.tabPageMain.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(246)))), ((int)(((byte)(245)))), ((int)(((byte)(242))))); this.tabPageMain.Controls.Add(this.buttonCancel); this.tabPageMain.Controls.Add(this.groupBoxMachineMatrix); this.tabPageMain.Controls.Add(this.groupBoxKeySetup); this.tabPageMain.Controls.Add(this.buttonOK); this.tabPageMain.Location = new System.Drawing.Point(4, 25); this.tabPageMain.Name = "tabPageMain"; this.tabPageMain.Padding = new System.Windows.Forms.Padding(3); this.tabPageMain.Size = new System.Drawing.Size(563, 362); this.tabPageMain.TabIndex = 0; this.tabPageMain.Text = "设备"; // // buttonCancel // this.buttonCancel.Location = new System.Drawing.Point(290, 328); this.buttonCancel.Name = "buttonCancel"; this.buttonCancel.Size = new System.Drawing.Size(75, 23); this.buttonCancel.TabIndex = 21; this.buttonCancel.Text = "关闭(&C)"; this.buttonCancel.UseVisualStyleBackColor = true; this.buttonCancel.Click += new System.EventHandler(this.ButtonCancel_Click); // // groupBoxMachineMatrix // this.groupBoxMachineMatrix.BackColor = System.Drawing.Color.Transparent; this.groupBoxMachineMatrix.Controls.Add(this.checkBoxTwoRow); this.groupBoxMachineMatrix.Controls.Add(this.linkLabelReConfigure); this.groupBoxMachineMatrix.Location = new System.Drawing.Point(3, 78); this.groupBoxMachineMatrix.Name = "groupBoxMachineMatrix"; this.groupBoxMachineMatrix.Size = new System.Drawing.Size(558, 244); this.groupBoxMachineMatrix.TabIndex = 5; this.groupBoxMachineMatrix.TabStop = false; this.groupBoxMachineMatrix.Text = " 设备布局(&M) - 拖动设备图标排序,选中图标旁边的勾选框以输入设备名称。 "; // // linkLabelReConfigure // this.linkLabelReConfigure.Dock = System.Windows.Forms.DockStyle.Bottom; this.linkLabelReConfigure.Location = new System.Drawing.Point(3, 221); this.linkLabelReConfigure.Name = "linkLabelReConfigure"; this.linkLabelReConfigure.Size = new System.Drawing.Size(552, 20); this.linkLabelReConfigure.TabIndex = 304; this.linkLabelReConfigure.TabStop = true; this.linkLabelReConfigure.Text = "重新执行软件初始化"; this.linkLabelReConfigure.TextAlign = System.Drawing.ContentAlignment.MiddleRight; this.linkLabelReConfigure.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.LinkLabelReConfigure_LinkClicked); // // tabControlSetting // this.tabControlSetting.Appearance = System.Windows.Forms.TabAppearance.FlatButtons; this.tabControlSetting.Controls.Add(this.tabPageMain); this.tabControlSetting.Controls.Add(this.tabPageOther); this.tabControlSetting.Controls.Add(this.tabPageAdvancedSettings); this.tabControlSetting.Dock = System.Windows.Forms.DockStyle.Fill; this.tabControlSetting.Location = new System.Drawing.Point(0, 0); this.tabControlSetting.Name = "tabControlSetting"; this.tabControlSetting.SelectedIndex = 0; this.tabControlSetting.Size = new System.Drawing.Size(571, 391); this.tabControlSetting.TabIndex = 20; // // tabPageAdvancedSettings // this.tabPageAdvancedSettings.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(246)))), ((int)(((byte)(245)))), ((int)(((byte)(242))))); this.tabPageAdvancedSettings.Controls.Add(this.groupBoxName2IPPolicyList); this.tabPageAdvancedSettings.Controls.Add(this.pictureBoxMouseWithoutBorders); this.tabPageAdvancedSettings.Controls.Add(this.groupBoxDNS); this.tabPageAdvancedSettings.Controls.Add(this.textBoxDNS); this.tabPageAdvancedSettings.Location = new System.Drawing.Point(4, 25); this.tabPageAdvancedSettings.Name = "tabPageAdvancedSettings"; this.tabPageAdvancedSettings.Padding = new System.Windows.Forms.Padding(3); this.tabPageAdvancedSettings.Size = new System.Drawing.Size(563, 362); this.tabPageAdvancedSettings.TabIndex = 2; this.tabPageAdvancedSettings.Text = "高级"; // // groupBoxName2IPPolicyList // this.groupBoxName2IPPolicyList.Controls.Add(this.textBoxMachineName2IPPolicyList); this.groupBoxName2IPPolicyList.Dock = System.Windows.Forms.DockStyle.Top; this.groupBoxName2IPPolicyList.Location = new System.Drawing.Point(3, 241); this.groupBoxName2IPPolicyList.Name = "groupBoxName2IPPolicyList"; this.groupBoxName2IPPolicyList.Size = new System.Drawing.Size(357, 150); this.groupBoxName2IPPolicyList.TabIndex = 1; this.groupBoxName2IPPolicyList.TabStop = false; this.groupBoxName2IPPolicyList.Text = " 组策略设定的 IP 地址映射 [组策略控制]"; this.groupBoxName2IPPolicyList.ForeColor = Color.DimGray; this.groupBoxName2IPPolicyList.Visible = false; // // textBoxMachineName2IPPolicyList // this.textBoxMachineName2IPPolicyList.Dock = System.Windows.Forms.DockStyle.Fill; this.textBoxMachineName2IPPolicyList.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.textBoxMachineName2IPPolicyList.Location = new System.Drawing.Point(3, 172); // 3,172 this.textBoxMachineName2IPPolicyList.MaxLength = 1024; this.textBoxMachineName2IPPolicyList.Multiline = true; this.textBoxMachineName2IPPolicyList.Name = "textBoxMachineName2IPPolicyList"; this.textBoxMachineName2IPPolicyList.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; this.textBoxMachineName2IPPolicyList.Size = new System.Drawing.Size(351, 131); this.textBoxMachineName2IPPolicyList.TabIndex = 1; this.textBoxMachineName2IPPolicyList.ReadOnly = true; this.textBoxMachineName2IPPolicyList.Visible = false; // // pictureBoxMouseWithoutBorders // this.pictureBoxMouseWithoutBorders.BackgroundImageLayout = System.Windows.Forms.ImageLayout.None; this.pictureBoxMouseWithoutBorders.Dock = System.Windows.Forms.DockStyle.Bottom; this.pictureBoxMouseWithoutBorders.Location = new System.Drawing.Point(3, 241); this.pictureBoxMouseWithoutBorders.Name = "pictureBoxMouseWithoutBorders"; this.pictureBoxMouseWithoutBorders.Size = new System.Drawing.Size(557, 118); this.pictureBoxMouseWithoutBorders.TabIndex = 16; this.pictureBoxMouseWithoutBorders.TabStop = false; // // groupBoxDNS // this.groupBoxDNS.Controls.Add(this.textBoxMachineName2IP); this.groupBoxDNS.Dock = System.Windows.Forms.DockStyle.Top; this.groupBoxDNS.Location = new System.Drawing.Point(3, 85); this.groupBoxDNS.Name = "groupBoxDNS"; this.groupBoxDNS.Size = new System.Drawing.Size(557, 150); this.groupBoxDNS.TabIndex = 0; this.groupBoxDNS.TabStop = false; this.groupBoxDNS.Text = " IP 地址映射 "; // // textBoxMachineName2IP // this.textBoxMachineName2IP.Dock = System.Windows.Forms.DockStyle.Fill; this.textBoxMachineName2IP.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.textBoxMachineName2IP.Location = new System.Drawing.Point(3, 16); this.textBoxMachineName2IP.MaxLength = 1024; this.textBoxMachineName2IP.Multiline = true; this.textBoxMachineName2IP.Name = "textBoxMachineName2IP"; this.textBoxMachineName2IP.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; this.textBoxMachineName2IP.Size = new System.Drawing.Size(551, 131); this.textBoxMachineName2IP.TabIndex = 0; // // textBoxDNS // this.textBoxDNS.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(246)))), ((int)(((byte)(245)))), ((int)(((byte)(242))))); this.textBoxDNS.BorderStyle = System.Windows.Forms.BorderStyle.None; this.textBoxDNS.Dock = System.Windows.Forms.DockStyle.Top; this.textBoxDNS.Location = new System.Drawing.Point(3, 3); this.textBoxDNS.Multiline = true; this.textBoxDNS.Name = "textBoxDNS"; this.textBoxDNS.ReadOnly = true; this.textBoxDNS.Size = new System.Drawing.Size(557, 82); this.textBoxDNS.TabIndex = 0; this.textBoxDNS.TabStop = false; this.textBoxDNS.Text = resources.GetString("textBoxDNS.Text"); // // linkLabelHelp // this.linkLabelHelp.AutoSize = true; this.linkLabelHelp.Dock = System.Windows.Forms.DockStyle.Right; this.linkLabelHelp.Location = new System.Drawing.Point(400, 0); this.linkLabelHelp.Name = "linkLabelHelp"; this.linkLabelHelp.Size = new System.Drawing.Size(124, 13); this.linkLabelHelp.TabIndex = 300; this.linkLabelHelp.TabStop = true; this.linkLabelHelp.Text = "在线帮助(&H)"; this.linkLabelHelp.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.LinkLabelHelp_LinkClicked); // // linkLabelMiniLog // this.linkLabelMiniLog.AutoSize = true; this.linkLabelMiniLog.Dock = System.Windows.Forms.DockStyle.Right; this.linkLabelMiniLog.Location = new System.Drawing.Point(524, 0); this.linkLabelMiniLog.Name = "linkLabelMiniLog"; this.linkLabelMiniLog.Size = new System.Drawing.Size(47, 13); this.linkLabelMiniLog.TabIndex = 301; this.linkLabelMiniLog.TabStop = true; this.linkLabelMiniLog.Text = "运行日志"; this.linkLabelMiniLog.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.LinkLabelMiniLog_LinkClicked); // // frmMatrix // this.AllowDrop = true; this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; this.BackColor = System.Drawing.SystemColors.Control; this.ClientSize = new System.Drawing.Size(571, 391); this.Controls.Add(this.linkLabelHelp); this.Controls.Add(this.linkLabelMiniLog); this.Controls.Add(this.tabControlSetting); this.Controls.Add(this.pictureBoxMouseWithoutBorders0); this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.MaximizeBox = false; this.Name = "frmMatrix"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; this.Text = "无界鼠标 - 设置"; this.TopMost = true; this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.FrmMatrix_FormClosed); this.Load += new System.EventHandler(this.FrmMatrix_Load); this.Shown += new System.EventHandler(this.FrmMatrix_Shown); this.DragDrop += new System.Windows.Forms.DragEventHandler(this.Form_DragDrop); this.DragEnter += new System.Windows.Forms.DragEventHandler(this.Form_DragEnter); this.DragOver += new System.Windows.Forms.DragEventHandler(this.Form_DragOver); this.DragLeave += new System.EventHandler(this.FrmMatrix_DragLeave); this.Resize += new System.EventHandler(this.FrmMatrix_Resize); ((System.ComponentModel.ISupportInitialize)(this.pictureBoxMouseWithoutBorders0)).EndInit(); this.groupBoxKeySetup.ResumeLayout(false); this.groupBoxKeySetup.PerformLayout(); this.tabPageOther.ResumeLayout(false); this.groupBoxOtherOptions.ResumeLayout(false); this.groupBoxOtherOptions.PerformLayout(); this.groupBoxShortcuts.ResumeLayout(false); this.groupBoxShortcuts.PerformLayout(); this.tabPageMain.ResumeLayout(false); this.groupBoxMachineMatrix.ResumeLayout(false); this.groupBoxMachineMatrix.PerformLayout(); this.tabControlSetting.ResumeLayout(false); this.tabPageAdvancedSettings.ResumeLayout(false); this.tabPageAdvancedSettings.PerformLayout(); this.groupBoxName2IPPolicyList.ResumeLayout(false); this.groupBoxName2IPPolicyList.PerformLayout(); ((System.ComponentModel.ISupportInitialize)(this.pictureBoxMouseWithoutBorders)).EndInit(); this.groupBoxDNS.ResumeLayout(false); this.groupBoxDNS.PerformLayout(); this.ResumeLayout(false); this.PerformLayout(); } #endregion private PictureBox pictureBoxMouseWithoutBorders0; private ToolTip toolTip; private ToolTip toolTipManual; private TabPage tabPageOther; private GroupBox groupBoxShortcuts; private ComboBox comboBoxReconnect; private ComboBox comboBoxSwitchToAllPC; private ComboBox comboBoxExitMM; private ComboBox comboBoxLockMachine; private ComboBox comboBoxShowSettings; private Label labelReconnect; private Label labelSwitch2AllPCMode; private RadioButton radioButtonDisable; private RadioButton radioButtonNum; private RadioButton radioButtonF1; private Label labelLockMachine; private Label labelExitMM; private Label labelShowSettings; private Label labelSwitchBetweenMachine; private TabPage tabPageMain; private Button buttonCancel; private GroupBox groupBoxMachineMatrix; private CheckBox checkBoxTwoRow; private Button buttonOK; private TabControl tabControlSetting; private ComboBox comboBoxEasyMouse; private Label LabelToggleEasyMouse; private LinkLabel linkLabelHelp; private TabPage tabPageAdvancedSettings; private GroupBox groupBoxDNS; private TextBox textBoxDNS; private TextBox textBoxMachineName2IP; private GroupBox groupBoxName2IPPolicyList; private TextBox textBoxMachineName2IPPolicyList; private PictureBox pictureBoxMouseWithoutBorders; private GroupBox groupBoxOtherOptions; private CheckBox checkBoxDrawMouse; private CheckBox checkBoxMouseMoveRelatively; private CheckBox checkBoxHideMouse; private CheckBox checkBoxBlockMouseAtCorners; private CheckBox checkBoxCircle; private CheckBox checkBoxBlockScreenSaver; private CheckBox checkBoxHideLogo; private CheckBox checkBoxShareClipboard; private CheckBox checkBoxDisableCAD; private CheckBox checkBoxReverseLookup; private CheckBox checkBoxVKMap; private LinkLabel linkLabelMiniLog; private ComboBox comboBoxScreenCapture; private Label labelScreenCapture; private LinkLabel linkLabelReConfigure; private CheckBox checkBoxSameSubNet; private CheckBox checkBoxClipNetStatus; private CheckBox checkBoxSendLog; private GroupBox groupBoxKeySetup; private Button buttonNewKey; private TextBox textBoxEnc; private Label LabelEnc; private CheckBox checkBoxShowKey; private ComboBox comboBoxEasyMouseOption; private Label labelEasyMouse; private CheckBox checkBoxTransferFile; } } ================================================ FILE: PowerToys/src/modules/MouseWithoutBorders/App/Form/frmMatrix.cs ================================================ // 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.Drawing; using System.Globalization; using System.Text.RegularExpressions; using System.Windows.Forms; using Microsoft.PowerToys.Telemetry; // // Matrix/Settings form. // // // 2008 created by Truong Do (ductdo). // 2009-... modified by Truong Do (TruongDo). // 2023- Included in PowerToys. // using MouseWithoutBorders.Class; using MouseWithoutBorders.Core; using Timer = System.Windows.Forms.Timer; [module: SuppressMessage("Microsoft.Globalization", "CA1300:SpecifyMessageBoxOptions", Scope = "member", Target = "MouseWithoutBorders.frmMatrix.#buttonOK_Click(System.Object,System.EventArgs)", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.frmMatrix.#buttonSendHello_Click(System.Object,System.EventArgs)", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Globalization", "CA1304:SpecifyCultureInfo", Scope = "member", Target = "MouseWithoutBorders.frmMatrix.#frmMatrix_Load(System.Object,System.EventArgs)", MessageId = "System.String.ToLower", Justification = "Dotnet port with style preservation")] // [module: SuppressMessage("Microsoft.Mobility", "CA1601:DoNotUseTimersThatPreventPowerStateChanges", Scope = "member", Target = "MouseWithoutBorders.frmMatrix.#ChangeUI2OneRow(System.Boolean)")] [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.frmMatrix.#logoTimer_Tick(System.Object,System.EventArgs)", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed", Scope = "member", Target = "MouseWithoutBorders.frmMatrix.#Dispose(System.Boolean)", MessageId = "logoBitmap", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Mobility", "CA1601:DoNotUseTimersThatPreventPowerStateChanges", Scope = "member", Target = "MouseWithoutBorders.frmMatrix.#frmMatrix_Shown(System.Object,System.EventArgs)", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.frmMatrix.#PaintMyLogo()", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Style", "IDE1006:Naming Styles", Scope = "member", Target = "~M:MouseWithoutBorders.FrmMatrix.M_EnabledChanged(System.Object,System.EventArgs)", Justification = "Dotnet port with style preservation")] namespace MouseWithoutBorders { internal partial class FrmMatrix : System.Windows.Forms.Form, IDisposable { #pragma warning disable CA2213 // Disposing is done by ComponentResourceManager private Timer helperTimer; #pragma warning restore CA2213 private bool formShown; private int formOrgHeight; private bool matrixOneRow; internal FrmMatrix() { InitializeComponent(); textBoxEnc.Font = new System.Drawing.Font(Control.DefaultFont.Name, 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, 0); Text = Application.ProductName + " " + Application.ProductVersion + " - 设置"; toolTip.ToolTipTitle = Application.ProductName; toolTipManual.ToolTipTitle = Application.ProductName; labelExitMM.Text = "退出软件, Ctrl+Alt+Shift+:"; textBoxMachineName2IP.Text = Setting.Values.Name2IP; } private void ButtonCancel_Click(object sender, EventArgs e) { buttonCancel.Enabled = false; Close(); Common.MatrixForm = null; } private void ButtonOK_Click(object sender, EventArgs e) { buttonOK.Enabled = false; if (!UpdateKey(Regex.Replace(textBoxEnc.Text, @"\s+", string.Empty))) { buttonOK.Enabled = true; return; } string[] st = new string[Common.MAX_MACHINE]; for (int i = 0; i < Common.MAX_MACHINE; i++) { if (machines[i].MachineEnabled) { for (int j = 0; j < i; j++) { if (st[j].Equals(machines[i].MachineName, StringComparison.OrdinalIgnoreCase)) { machines[i].MachineName = string.Empty; machines[i].MachineEnabled = false; } } st[i] = machines[i].MachineName; } else { st[i] = string.Empty; } } Common.MachineMatrix = st; Setting.Values.MatrixOneRow = matrixOneRow = !checkBoxTwoRow.Checked; if (Process.GetCurrentProcess().SessionId != NativeMethods.WTSGetActiveConsoleSessionId()) { Program.StartService(); Common.ShowToolTip("新设置应用于物理控制台会话!", 3000, ToolTipIcon.Warning, false); } else { SocketStuff.InvalidKeyFound = false; showInvalidKeyMessage = false; Common.ReopenSocketDueToReadError = true; Common.ReopenSockets(true); for (int i = 0; i < 10; i++) { if (Common.AtLeastOneSocketConnected()) { Common.MMSleep(0.5); break; } Common.MMSleep(0.2); } Common.SendMachineMatrix(); } buttonOK.Enabled = true; } internal void UpdateKeyTextBox() { _ = Common.GetUserName(); textBoxEnc.Text = Common.MyKey; } private void InitAll() { formOrgHeight = Height; matrixOneRow = Setting.Values.MatrixOneRow; CreateMachines(); LoadSettingsToUI(); UpdateKeyTextBox(); } private void LoadMachines() { bool meAdded = false; string machineName; if (Common.MachineMatrix != null && Common.MachineMatrix.Length == Common.MAX_MACHINE) { Logger.LogDebug("LoadMachines: Machine Matrix: " + Setting.Values.MachineMatrixString); for (int i = 0; i < Common.MAX_MACHINE; i++) { machineName = Common.MachineMatrix[i].Trim(); machines[i].MachineName = machineName; if (string.IsNullOrEmpty(machineName)) { machines[i].CheckAble = true; } else { machines[i].MachineEnabled = true; } bool found = Common.MachinePool.TryFindMachineByName(machineName, out MachineInf machineInfo); if (found) { if (machineInfo.Id == Common.MachineID) { machines[i].LocalHost = true; meAdded = true; } } } } if (!meAdded) { foreach (Machine m in machines) { if (string.IsNullOrEmpty(m.MachineName)) { m.MachineName = Common.MachineName.Trim(); m.LocalHost = true; meAdded = true; break; } } } } private void CheckBoxShowKey_CheckedChanged(object sender, EventArgs e) { textBoxEnc.PasswordChar = checkBoxShowKey.Checked ? (char)0 : '*'; } private void FrmMatrix_Shown(object sender, EventArgs e) { if (Setting.Values.FirstRun) { Setting.Values.FirstRun = false; Common.ReopenSockets(false); /* string fireWallLog = Path.GetDirectoryName(Application.ExecutablePath) + "\\FirewallError.log"; if (File.Exists(fireWallLog)) { //@"http://bing.com/search?q=Allow+a+program+through+Windows+Firewall" MessageBox.Show(Application.ProductName + " was unable to add itself to the Firewall exception list.\r\n" + "The following application needs to be added to the Firewall exception list:\r\n\r\n" + Application.ExecutablePath + "\r\n\r\nYou can go to bing.com and do a search on" + "\r\n'Allow a program through Windows Firewall' to know how.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error); } linkLabelHelp_LinkClicked(null, null); * */ } InitAll(); if (Setting.Values.IsMyKeyRandom) { Setting.Values.IsMyKeyRandom = false; checkBoxShowKey.Checked = true; } if (helperTimer == null) { helperTimer = new Timer(); helperTimer.Interval = 200; helperTimer.Tick += new EventHandler(HelperTimer_Tick); helperTimer.Start(); } formShown = true; } private void FrmMatrix_FormClosed(object sender, FormClosedEventArgs e) { /* if (logoTimer != null) { logoTimer.Stop(); logoTimer.Dispose(); logoTimer = null; } * */ if (helperTimer != null) { helperTimer.Stop(); helperTimer.Dispose(); helperTimer = null; } // if (logoBitmap != null) logoBitmap.Dispose(); Common.MatrixForm = null; } private int pivot; private Bitmap logoBitmap; private void PaintMyLogo() { if (!Visible || !(tabControlSetting.SelectedTab == tabPageAdvancedSettings)) { return; } uint rv = 0; try { Color c; uint cl; double dC; logoBitmap ??= new Bitmap(pictureBoxMouseWithoutBorders0.BackgroundImage); int bWidth = logoBitmap.Width; int bHeight = logoBitmap.Height; double dx = (double)pictureBoxMouseWithoutBorders.Width / bWidth; double dy = (double)pictureBoxMouseWithoutBorders.Height / bHeight; IntPtr hdc = NativeMethods.GetWindowDC(pictureBoxMouseWithoutBorders.Handle); for (int i = 0; i < bWidth; i++) { for (int j = 0; j < bHeight; j++) { c = logoBitmap.GetPixel(i, j); // c.G > 245 if (c.R < 240 && c.B < 240) { dC = Math.Abs(pivot - i); if (bWidth - pivot + i < dC) { dC = bWidth - pivot + i; } if (bWidth - i + pivot < dC) { dC = bWidth - i + pivot; } dC /= bWidth; // c = Color.FromArgb(80, (int)(255 - 255 * dC), 80); cl = (160 << 16) | ((uint)(255 - (255 * dC)) << 8) | 160; // Using GDI SetPixel so we dont have to assign the image later on // b.SetPixel(i, j, c); rv = NativeMethods.SetPixel(hdc, (int)(i * dx), (int)(j * dy), cl); } } } // Image im = pictureBoxMouseWithoutBorders.BackgroundImage; // pictureBoxMouseWithoutBorders.BackgroundImage = b; // if (im != null) im.Dispose(); rv = (uint)NativeMethods.ReleaseDC(pictureBoxMouseWithoutBorders.Handle, hdc); pivot = (pivot + 5) % bWidth; } catch (Exception ee) { Logger.Log(ee); Logger.Log(rv.ToString(CultureInfo.CurrentCulture)); } } private void AddNewMachine() { string newMachine; Machine unUsedMachine; foreach (MachineInf inf in Common.MachinePool.ListAllMachines()) { bool found = false; unUsedMachine = null; newMachine = inf.Name.Trim(); foreach (Machine m in machines) { if (m.MachineName.Equals( newMachine, StringComparison.OrdinalIgnoreCase)) { found = true; } else if (unUsedMachine == null && string.IsNullOrEmpty(m.MachineName.Trim())) { unUsedMachine = m; } } if (!found && unUsedMachine != null) { unUsedMachine.MachineName = newMachine; } } } private int helperTimerCounter; private bool showInvalidKeyMessage; private void HelperTimer_Tick(object sender, EventArgs e) { string keyNotMatchedMachines = string.Empty; if (Setting.Values.Changed) { Setting.Values.Changed = false; matrixOneRow = Setting.Values.MatrixOneRow; LoadSettingsToUI(); /* if (!Common.InMachineMatrix(Common.MachineName)) { foreach (Machine m in machines) { if (!m.LocalHost) { m.MachineEnabled = false; } } } * */ } helperTimerCounter++; // 1 sec if (helperTimerCounter % 5 == 0) { comboBoxEasyMouseOption.Text = ((EasyMouseOption)Setting.Values.EasyMouse).ToString(); if (!textBoxMachineName2IP.Text.Equals(Setting.Values.Name2IP, StringComparison.OrdinalIgnoreCase)) { Setting.Values.Name2IP = textBoxMachineName2IP.Text; } // 2 times if (helperTimerCounter < 15) { Common.SendHello(); } AddNewMachine(); } // NOTE(@yuyoyuppe): this option is deprecated // checkBoxVKMap.Checked = Setting.Values.UseVKMap; foreach (Machine m in machines) { if (m.StatusClient != SocketStatus.NA) { m.StatusClient = SocketStatus.NA; } if (m.StatusServer != SocketStatus.NA) { m.StatusServer = SocketStatus.NA; } } SocketStuff sk = Common.Sk; if (sk != null) { lock (sk.TcpSocketsLock) { if (sk.TcpSockets != null) { foreach (TcpSk t in sk.TcpSockets) { if (t.Status == SocketStatus.InvalidKey) { keyNotMatchedMachines += string.Format(CultureInfo.CurrentCulture, "[{0}]", t.MachineName); } foreach (Machine m in machines) { if (m.MachineEnabled) { if (m.MachineName.Equals(t.MachineName, StringComparison.OrdinalIgnoreCase)) { if (t.IsClient) { if (t.Status > m.StatusClient) { m.StatusClient = t.Status; } } else { if (t.Status > m.StatusServer) { m.StatusServer = t.Status; } } } } } } } } } if (SocketStuff.InvalidKeyFound) { if (!showInvalidKeyMessage) { showInvalidKeyMessage = true; Common.ShowToolTip( "密码不正确。\r\n请检查是否在所有电脑上都输入了同一个密码。\r\n并检查您运行的是否都是同一版本的 " + Application.ProductName + "。\r\n" + keyNotMatchedMachines + "\r\n本软件版本: " + FrmAbout.AssemblyVersion, 20000, ToolTipIcon.Warning, Setting.Values.ShowClipNetStatus); } } else { showInvalidKeyMessage = false; } PaintMyLogo(); } private void ShowKeyErrorMsg(string msg) { Common.ShowToolTip(msg, 10000, ToolTipIcon.Error, false); _ = textBoxEnc.Focus(); textBoxEnc.SelectAll(); } private bool UpdateKey(string newKey) { if (!Common.IsKeyValid(newKey, out string rv)) { ShowKeyErrorMsg(rv); return false; } if (!newKey.Equals(Common.MyKey, StringComparison.OrdinalIgnoreCase)) { Common.MyKey = newKey; Common.GeneratedKey = false; } Common.MagicNumber = Common.Get24BitHash(Common.MyKey); return true; } private readonly Machine[] machines = new Machine[Common.MAX_MACHINE]; private Machine dragDropMachine; private Machine desMachine; private Machine desMachineX; private Machine desMachineY; private Machine oldDesMachine; private Point desMachinePos; private Point oldDesMachinePos; private void CreateMachines() { for (int i = 0; i < Common.MAX_MACHINE; i++) { Machine m = new(); m.MouseDown += Machine_MouseDown; m.EnabledChanged += new EventHandler(M_EnabledChanged); m.Parent = groupBoxMachineMatrix; m.MachineEnabled = false; machines[i] = m; } FrmMatrix_Resize(this, EventArgs.Empty); ArrangeMachines(); } private void ArrangeMachines() { Height = matrixOneRow ? formOrgHeight : formOrgHeight + 60; int dx = (groupBoxMachineMatrix.Width - 40) / 4; int yOffset = groupBoxMachineMatrix.Height / 3; for (int i = 0; i < Common.MAX_MACHINE; i++) { machines[i].Left = matrixOneRow ? 22 + (i * dx) : 22 + dx + ((i % 2) * dx); machines[i].Top = matrixOneRow ? yOffset : (yOffset / 2) + (i / 2 * (machines[i].Width + 2)); machines[i].Visible = true; } } private void M_EnabledChanged(object sender, EventArgs e) { Machine m = sender as Machine; SocketStuff sk = Common.Sk; if (!m.MachineEnabled && sk != null) { lock (sk.TcpSocketsLock) { if (sk.TcpSockets != null) { foreach (TcpSk t in sk.TcpSockets) { if (t.MachineName != null && t.MachineName.Equals(m.MachineName.Trim(), StringComparison.OrdinalIgnoreCase)) { t.Status = SocketStatus.NA; } } } } } } private void Machine_MouseDown(object sender, MouseEventArgs e) { oldDesMachine = desMachine = dragDropMachine = sender as Machine; desMachinePos.X = desMachine.Left; desMachinePos.Y = desMachine.Top; oldDesMachinePos.X = oldDesMachine.Left; oldDesMachinePos.Y = oldDesMachine.Top; dragDropMachineOrgX = dragDropMachine.Left; dragDropMachineOrgY = dragDropMachine.Top; dragDropMachine.BringToFront(); _ = DoDragDrop(dragDropMachine, DragDropEffects.Move); } private int startX; private int startY; private int dragDropMachineOrgX; private int dragDropMachineOrgY; private void Form_DragEnter(object sender, DragEventArgs e) { startX = e.X; startY = e.Y; e.Effect = DragDropEffects.Move; } private bool IsOnSameRow(Machine m1, Machine m2) { return matrixOneRow || (m1 == dragDropMachine ? desMachinePos.Y : m1.Top) == m2.Top; } private bool IsOnSameCol(Machine m1, Machine m2) { return !matrixOneRow && (m1 == dragDropMachine ? desMachinePos.X : m1.Left) == m2.Left; } private long lastMove; private void Form_DragOver(object sender, DragEventArgs e) { if (dragDropMachine == null) { return; } e.Effect = DragDropEffects.Move; dragDropMachine.Left = dragDropMachineOrgX + (e.X - startX); dragDropMachine.Top = dragDropMachineOrgY + (e.Y - startY); /* dragDropMachine.Left = e.X - dragDropMachine.MouseDownPos.X - Left - 3 - groupBoxMachineMatrix.Left - tabControlSetting.Left; dragDropMachine.Top = e.Y - dragDropMachine.MouseDownPos.Y - Top - 25 - groupBoxMachineMatrix.Top - tabControlSetting.Top; * */ if (!matrixOneRow && Common.GetTick() - lastMove < 500) { return; } int minX = Math.Abs(dragDropMachine.Left - desMachinePos.X); int minY = Math.Abs(dragDropMachine.Top - desMachinePos.Y); desMachineX = desMachineY = desMachine; for (int i = 0; i < Common.MAX_MACHINE; i++) { if (machines[i] == dragDropMachine) { continue; } if (IsOnSameRow(oldDesMachine, machines[i])) { if (minX > Math.Abs(dragDropMachine.Left - machines[i].Left)) { minX = Math.Abs(dragDropMachine.Left - machines[i].Left); desMachineX = machines[i]; } } if (IsOnSameCol(oldDesMachine, machines[i])) { if (minY > Math.Abs(dragDropMachine.Top - machines[i].Top)) { minY = Math.Abs(dragDropMachine.Top - machines[i].Top); desMachineY = machines[i]; } } } oldDesMachine = desMachine; desMachine = desMachineY == oldDesMachine ? desMachineX : desMachineX == oldDesMachine ? desMachineY : minX < minY ? desMachineX : desMachineY; if (desMachine != oldDesMachine) { oldDesMachinePos.X = desMachinePos.X; desMachinePos.X = desMachine.Left; oldDesMachinePos.Y = desMachinePos.Y; desMachinePos.Y = desMachine.Top; desMachine.Left = oldDesMachinePos.X; desMachine.Top = oldDesMachinePos.Y; desMachine = dragDropMachine; lastMove = Common.GetTick(); } } private void Form_DragDrop(object sender, DragEventArgs e) { if (desMachine != null) { dragDropMachine.Left = desMachinePos.X; dragDropMachine.Top = desMachinePos.Y; Machine tmp; for (int i = 0; i < Common.MAX_MACHINE - 1; i++) { for (int j = 0; j < Common.MAX_MACHINE - 1 - i; j++) { if (machines[j + 1].Top < machines[j].Top || (machines[j + 1].Top == machines[j].Top && machines[j + 1].Left < machines[j].Left)) { tmp = machines[j]; machines[j] = machines[j + 1]; machines[j + 1] = tmp; } } } } } private void FrmMatrix_DragLeave(object sender, EventArgs e) { Form_DragDrop(sender, null); InputSimulation.MouseUp(); } private void LoadSettingsToUI() { checkBoxCircle.Checked = Setting.Values.MatrixCircle; checkBoxTwoRow.Checked = !matrixOneRow; checkBoxBlockMouseAtCorners.Checked = Setting.Values.BlockMouseAtCorners; checkBoxDrawMouse.Checked = Setting.Values.DrawMouse; checkBoxReverseLookup.Checked = Setting.Values.ReverseLookup; checkBoxSameSubNet.Checked = Setting.Values.SameSubNetOnly; // NOTE(@yuyoyuppe): this option is deprecated // checkBoxVKMap.Checked = Setting.Values.UseVKMap; foreach (Machine m in machines) { m.MachineName = string.Empty; m.MachineEnabled = false; m.LocalHost = false; } LoadMachines(); } internal static readonly string[] Separator = new string[] { "\r\n" }; internal void ShowTip(ToolTipIcon icon, string text, int duration) { int x = 0; text += "\r\n "; int y = (-text.Split(Separator, StringSplitOptions.None).Length * 15) - 30; toolTipManual.Hide(this); toolTipManual.ToolTipIcon = icon; toolTipManual.Show(text, this, x, y, duration); } private void LinkLabelHelp_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { linkLabelHelp.Enabled = false; linkLabelHelp.Enabled = true; } private void CheckBoxShareClipboard_CheckedChanged(object sender, EventArgs e) { Setting.Values.ShareClipboard = checkBoxShareClipboard.Checked; checkBoxTransferFile.Enabled = checkBoxTransferFile.Checked = Setting.Values.ShareClipboard; ShowUpdateMessage(); } private void CheckBoxTransferFile_CheckedChanged(object sender, EventArgs e) { Setting.Values.TransferFile = checkBoxTransferFile.Checked; ShowUpdateMessage(); Common.HasSwitchedMachineSinceLastCopy = true; } private void CheckBoxDisableCAD_CheckedChanged(object sender, EventArgs e) { if (!Common.RunWithNoAdminRight) { Common.ApplyCADSetting(); ShowUpdateMessage(); } } private void FrmMatrix_Load(object sender, EventArgs e) { if (Common.RunWithNoAdminRight) { checkBoxDisableCAD.Enabled = false; checkBoxHideLogo.Enabled = false; } // Note(@htcfreek): Disable checkboxes of settings that we don't support in the PowerToys implementation checkBoxDisableCAD.Enabled = false; checkBoxDisableCAD.Text = checkBoxDisableCAD.Text + " [不支持!]"; checkBoxHideLogo.Enabled = false; checkBoxHideLogo.Text = checkBoxHideLogo.Text + " [不支持!]"; checkBoxSendLog.Enabled = false; checkBoxSendLog.Text = checkBoxSendLog.Text + " [不支持!]"; checkBoxShareClipboard.Checked = Setting.Values.ShareClipboard; if (!Setting.Values.ShareClipboard) { checkBoxTransferFile.Enabled = checkBoxTransferFile.Checked = false; } else { checkBoxTransferFile.Checked = Setting.Values.TransferFile; } checkBoxDisableCAD.Checked = Setting.Values.DisableCAD; checkBoxHideLogo.Checked = Setting.Values.HideLogonLogo; checkBoxHideMouse.Checked = Setting.Values.HideMouse; checkBoxBlockScreenSaver.Checked = Setting.Values.BlockScreenSaver; checkBoxMouseMoveRelatively.Checked = Setting.Values.MoveMouseRelatively; checkBoxClipNetStatus.Checked = Setting.Values.ShowClipNetStatus; checkBoxSendLog.Checked = Setting.Values.SendErrorLogV2; if (Setting.Values.HotKeySwitchMachine == (int)VK.F1) { radioButtonF1.Checked = true; } else if (Setting.Values.HotKeySwitchMachine == '1') { radioButtonNum.Checked = true; } else { radioButtonDisable.Checked = true; } comboBoxShowSettings.Text = "关闭"; comboBoxExitMM.Text = Setting.Values.HotKeyExitMM == 0 ? "关闭" : new string(new char[] { (char)Setting.Values.HotKeyExitMM }); #if OBSOLETE_SHORTCUTS comboBoxLockMachine.Text = Setting.Values.HotKeyLockMachine == 0 ? "关闭" : new string(new char[] { (char)Setting.Values.HotKeyLockMachine }); comboBoxReconnect.Text = Setting.Values.HotKeyReconnect == 0 ? "关闭" : new string(new char[] { (char)Setting.Values.HotKeyReconnect }); comboBoxSwitchToAllPC.Text = Setting.Values.HotKeySwitch2AllPC == 1 ? "Ctrl*3" : Setting.Values.HotKeySwitch2AllPC == 0 ? "关闭" : new string(new char[] { (char)Setting.Values.HotKeySwitch2AllPC }); comboBoxEasyMouseOption.Text = ((EasyMouseOption)Setting.Values.EasyMouse).ToString(); comboBoxEasyMouse.Text = Setting.Values.HotKeyToggleEasyMouse == 0 ? "关闭" : new string(new char[] { (char)Setting.Values.HotKeyToggleEasyMouse }); #endif // Apply policy configuration on UI elements // Has to be the last action if (Setting.Values.ShareClipboardIsGpoConfigured) { checkBoxShareClipboard.Enabled = false; checkBoxShareClipboard.Text += " [组策略控制]"; // transfer file setting depends on clipboard sharing checkBoxTransferFile.Enabled = false; } if (Setting.Values.TransferFileIsGpoConfigured) { checkBoxTransferFile.Enabled = false; checkBoxTransferFile.Text += " [组策略控制]"; } if (Setting.Values.BlockScreenSaverIsGpoConfigured) { checkBoxBlockScreenSaver.Enabled = false; checkBoxBlockScreenSaver.Text += " [组策略控制]"; } if (Setting.Values.SameSubNetOnlyIsGpoConfigured) { checkBoxSameSubNet.Enabled = false; checkBoxSameSubNet.Text += " [组策略控制]"; } if (Setting.Values.ReverseLookupIsGpoConfigured) { checkBoxReverseLookup.Enabled = false; checkBoxReverseLookup.Text += " [组策略控制]"; } if (Setting.Values.Name2IpIsGpoConfigured) { textBoxMachineName2IP.Enabled = false; groupBoxDNS.ForeColor = Color.DimGray; groupBoxDNS.Text += " [组策略控制]"; } if (Setting.Values.Name2IpPolicyListIsGpoConfigured) { pictureBoxMouseWithoutBorders.Visible = false; groupBoxName2IPPolicyList.Visible = true; textBoxMachineName2IPPolicyList.Visible = true; textBoxMachineName2IPPolicyList.Text = Setting.Values.Name2IpPolicyList; } } private void RadioButton_CheckedChanged(object sender, EventArgs e) { RadioButton r = sender as RadioButton; if (r.Checked) { Setting.Values.HotKeySwitchMachine = sender.Equals(radioButtonF1) ? (int)VK.F1 : sender.Equals(radioButtonNum) ? '1' : 0; ShowUpdateMessage(); } } private void ComboBoxShowSettings_TextChanged(object sender, EventArgs e) { ShowUpdateMessage(); } private void ComboBoxExitMM_TextChanged(object sender, EventArgs e) { ShowUpdateMessage(); } private void ComboBoxLockMachine_TextChanged(object sender, EventArgs e) { #if OBSOLETE_SHORTCUTS if (comboBoxLockMachine.Text.Contains("关闭")) { Setting.Values.HotKeyLockMachine = 0; } else if (comboBoxLockMachine.Text.Length > 0) { Setting.Values.HotKeyLockMachine = comboBoxLockMachine.Text[0]; } #endif } private void ComboBoxSwitchToAllPC_TextChanged(object sender, EventArgs e) { #if OBSOLETE_SHORTCUTS if (comboBoxSwitchToAllPC.Text.Contains("关闭")) { Setting.Values.HotKeySwitch2AllPC = 0; } else if (comboBoxSwitchToAllPC.Text.Contains("Ctrl*3")) { Setting.Values.HotKeySwitch2AllPC = 1; } else if (comboBoxSwitchToAllPC.Text.Length > 0) { Setting.Values.HotKeySwitch2AllPC = comboBoxSwitchToAllPC.Text[0]; } #endif ShowUpdateMessage(); } private void CheckBoxHideLogo_CheckedChanged(object sender, EventArgs e) { ShowUpdateMessage(); } private void ShowUpdateMessage() { if (!formShown) { return; } foreach (Control c in tabPageOther.Controls) { if (c != groupBoxShortcuts) { c.Enabled = false; } } foreach (Control c in groupBoxShortcuts.Controls) { if (c != pictureBoxMouseWithoutBorders) { c.Enabled = false; } } for (int i = 0; i < 3; i++) { Application.DoEvents(); Thread.Sleep(20); } foreach (Control c in tabPageOther.Controls) { if (c != groupBoxShortcuts) { c.Enabled = true; } } foreach (Control c in groupBoxShortcuts.Controls) { if (c != pictureBoxMouseWithoutBorders && c != comboBoxExitMM && c != comboBoxShowSettings && c != comboBoxScreenCapture) { c.Enabled = true; } } } private void CheckBoxBlockScreenSaver_CheckedChanged(object sender, EventArgs e) { Setting.Values.BlockScreenSaver = checkBoxBlockScreenSaver.Checked; ShowUpdateMessage(); } private void ComboBoxReconnect_TextChanged(object sender, EventArgs e) { #if OBSOLETE_SHORTCUTS if (comboBoxReconnect.Text.Contains("关闭")) { Setting.Values.HotKeyReconnect = 0; } else if (comboBoxReconnect.Text.Length > 0) { Setting.Values.HotKeyReconnect = comboBoxReconnect.Text[0]; } #endif ShowUpdateMessage(); } private void CheckBoxCircle_CheckedChanged(object sender, EventArgs e) { if (Setting.Values.MatrixCircle != checkBoxCircle.Checked) { Setting.Values.MatrixCircle = checkBoxCircle.Checked; ShowUpdateMessage(); Common.SendMachineMatrix(); } } private void CheckBoxBlockMouseAtCorners_CheckedChanged(object sender, EventArgs e) { Setting.Values.BlockMouseAtCorners = checkBoxBlockMouseAtCorners.Checked; ShowUpdateMessage(); } private void CheckBoxHideMouse_CheckedChanged(object sender, EventArgs e) { Setting.Values.HideMouse = checkBoxHideMouse.Checked; ShowUpdateMessage(); } private void ComboBoxEasyMouseOption_TextChanged(object sender, EventArgs e) { string selectedOption = comboBoxEasyMouseOption.Text; int oldEasyMouseOption = Setting.Values.EasyMouse; Setting.Values.EasyMouse = Enum.TryParse(selectedOption, out EasyMouseOption easyMouseOption) ? (int)easyMouseOption : (int)EasyMouseOption.开启; if (oldEasyMouseOption != Setting.Values.EasyMouse) { ShowUpdateMessage(); } } private void ComboBoxEasyMouse_TextChanged(object sender, EventArgs e) { #if OBSOLETE_SHORTCUTS if (comboBoxEasyMouse.Text.Contains("关闭")) { Setting.Values.HotKeyToggleEasyMouse = 0; } else if (comboBoxEasyMouse.Text.Length > 0) { Setting.Values.HotKeyToggleEasyMouse = comboBoxEasyMouse.Text[0]; } #endif ShowUpdateMessage(); } private void CheckBoxMouseMoveRelatively_CheckedChanged(object sender, EventArgs e) { Setting.Values.MoveMouseRelatively = checkBoxMouseMoveRelatively.Checked; ShowUpdateMessage(); } private void CheckBoxDrawMouse_CheckedChanged(object sender, EventArgs e) { if (!(Setting.Values.DrawMouse = checkBoxDrawMouse.Checked)) { CustomCursor.ShowFakeMouseCursor(int.MinValue, int.MinValue); } ShowUpdateMessage(); } private void CheckBoxTwoRow_CheckedChanged(object sender, EventArgs e) { matrixOneRow = !checkBoxTwoRow.Checked; ArrangeMachines(); } private void ButtonNewKey_Click(object sender, EventArgs e) { string message = "确定要生成新密码吗?\r\n" + "所有现有的连接会被取消,您需要重新输入密码来建立所有连接。"; 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; checkBoxShowKey.Checked = true; Common.GeneratedKey = true; ButtonOK_Click(null, null); Common.ShowToolTip("新密码已生成,需要在其他电脑上输入新的密码来重新连接。", 10000, ToolTipIcon.Info, false); } } private void CheckBoxClipNetStatus_CheckedChanged(object sender, EventArgs e) { Setting.Values.ShowClipNetStatus = checkBoxClipNetStatus.Checked; ShowUpdateMessage(); } private void CheckBoxSendLog_CheckedChanged(object sender, EventArgs e) { ShowUpdateMessage(); } private void FrmMatrix_Resize(object sender, EventArgs e) { if (WindowState != FormWindowState.Minimized) { groupBoxMachineMatrix.Top = groupBoxKeySetup.Top + groupBoxKeySetup.Height + 10; groupBoxMachineMatrix.Height = ClientSize.Height - groupBoxKeySetup.Height - (int)(buttonOK.Height * 3.5); checkBoxTwoRow.Top = groupBoxMachineMatrix.Height - (int)(checkBoxTwoRow.Height * 1.4); buttonOK.Top = groupBoxMachineMatrix.Bottom + (int)(buttonOK.Height * 0.3); buttonCancel.Top = groupBoxMachineMatrix.Bottom + (int)(buttonCancel.Height * 0.3); groupBoxShortcuts.Height = ClientSize.Height - groupBoxOtherOptions.Bottom - 40; groupBoxDNS.Height = ClientSize.Height - pictureBoxMouseWithoutBorders.Height - textBoxDNS.Height - 70; } } private void CheckBoxReverseLookup_CheckedChanged(object sender, EventArgs e) { Setting.Values.ReverseLookup = checkBoxReverseLookup.Checked; ShowUpdateMessage(); } private void CheckBoxVKMap_CheckedChanged(object sender, EventArgs e) { // NOTE(@yuyoyuppe): this option is deprecated // Setting.Values.UseVKMap = checkBoxVKMap.Checked; ShowUpdateMessage(); } private void LinkLabelMiniLog_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { string miniLog = Common.GetMiniLog(new[] { groupBoxOtherOptions.Controls, groupBoxShortcuts.Controls }); Clipboard.SetText(miniLog); Common.ShowToolTip("运行日志已复制到剪贴板", 30000, ToolTipIcon.Info, false); } private void ComboBoxScreenCapture_TextChanged(object sender, EventArgs e) { ShowUpdateMessage(); } private void LinkLabelReConfigure_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { string message = "警告:这将清除当前布局,重置所有设置,重新运行软件初始教程。对于 PowerToys 用户,请使用 PowerToys 设置,而不是这个初始化。\r\n"; message += "你将需要重新初始化所有要连接的电脑。在接下去的选择窗口中,对于第一台电脑请选“否”,剩下的选“是”。\r\n"; message += "接着按照步骤完成配置。\r\n\r\n"; message += "您确定要继续吗?"; if (MessageBox.Show(message, Application.ProductName, MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2) == System.Windows.Forms.DialogResult.Yes) { PowerToysTelemetry.Log.WriteEvent(new MouseWithoutBorders.Telemetry.MouseWithoutBordersOldUIReconfigureEvent()); ButtonCancel_Click(this, new EventArgs()); Setting.Values.FirstRun = true; Setting.Values.EasyMouse = (int)EasyMouseOption.开启; Common.ClearComputerMatrix(); Common.ShowSetupForm(true); } } private void CheckBoxSameSubNet_CheckedChanged(object sender, EventArgs e) { Setting.Values.SameSubNetOnly = checkBoxSameSubNet.Checked; ShowUpdateMessage(); } #if USE_TO_CREATE_LOGO_BITMAP private void PaintMyLogo() { Graphics g = Graphics.FromHwnd(this.Handle); Font font = new Font("Chiller", 40); g.DrawString(Common.BinaryName, font, Brushes.Lime, comboBoxIPs.Right + 50, comboBoxIPs.Bottom - 5); Bitmap b = new Bitmap(220, 100); Graphics g2 = Graphics.FromImage(b); g2.FillRectangle(Brushes.WhiteSmoke, 0, 0, 220, 100); g2.DrawString(Common.BinaryName, font, Brushes.Lime, 0, 0); b.Save("c:\\zzz.bmp"); string p = ""; Color c; int l = 0; for (int i = 0; i < b.Width; i++) { for (int j = 0; j < b.Height; j++) { c = b.GetPixel(i, j); if (c.G > 0) { p += "{" + i + "," + j + "},"; l++; } } p += "\r\n"; } //File.WriteAllText("c:\\zzz.txt", l + ":" + p, Encoding.Unicode); b.Dispose(); g2.Dispose(); } #endif } } ================================================ FILE: PowerToys/src/modules/MouseWithoutBorders/App/Form/frmMatrix.resx ================================================ text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 iVBORw0KGgoAAAANSUhEUgAAAN4AAAAaCAYAAADYHuIVAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO vAAADrwBlbxySQAABjpJREFUeF7tmzF25DYQROeCvocPsUfwCXwB58433tSpQ4fOfAOZf96WWFtqkIA4 Inae+CtYTRMgGo1GExxpby8XFw/mj++6aNPceF++6+Li4vFcT7yLh/H3olunfl3036LPyvDG+7rIA/jv oovjkIQe178WPRu/LPI5uH5bpFxh02H7fdHZaGzp26IZvNl4BEdO/bPIqSoawb44TibEM8bV/Zco1ImK zIw5rp6tmsGbUXmvk0P5guzXXBfHyafFjKfBUdx/tPU0UZuzIac1Npr1PcYPM88nGlVY8PTza66Ll/tR Cr0X+npMn/H9x/1HLZgbhWZe0q86smZH+CE6WnyvvjqX57ud62KFAvWe994/Fz1zTPMdFbUgRuTYrKe6 H+uro/AZvEbHA8eTj6Dws44LW99YfXZy07xnMb2wzXj3eQTyX2pB4jPHWU91f2XK7zHO4jU6vvCgjaZH MclFsPi2Te2kz46fEFrxIMm2Es3j70f8Gez52iKPyxUkPfGa+W34no9n8DqykscXnc/YgYCh6l3vs+Mx qd4ZdHpAFLCKLGiz6PG1Bacj9UWOjpfk18xNBy0fz+Q+sv8Kwc/dOhKQFPzMQlQbb9bj+lkgdh6viozr LHp8bZFzIK/8WOca3dSPxP2YxX1kr1T+fuLVjwXh+OGbVLo23jZ+BCMRW6gNmkWvry3U10Xu6A8CfHOe 8UcCjI0cjY9mcR/ZN5j/7sW/UPFFkE068+jA5s/fxaCf5fhSfbGS7z4ur/xuT52RpNDrawtvX8UCPK8+ mmoc2dL+SPby9D5ydUFoIQiWoIKsPd4639qwvTAmcphIHmVcehcV1ZMZ9SawH5Fam1pjZDw0ZxLV7Yhr rS8u9J7dkq8BHIlzxnjU1xbefwsV+9ycvXNyf7f+J4Riyroj5qN+yOnNma2xe/P0zcbrgQXzPr44OTHU SlwCqzaanDud+JM55Unki8dEGT/77h2PfRPkUYXAI1W0rGyKh89P8kXEJyRyA+Mj7fXZ2/bGuTfGo75W pE9baI08tiO5k0XKN4OSGzJXU2IkZ/bGzn4u5ektG/XgyZB93AlEMJVQmexrqzVYSoqq2vmEW8cY8Dlp E+SiarwW1RxZDP8sfzyRUeLXWokEHjuvpNrY7nNvnNcWfTFeW2/7WuHxQXtku5HcyfWR3OcsiORFK3dH cmZvbOUFauXpzRuhXrw6O15hlDzuqAcwq5ESgX+rpPD2XG8lhtpsiUDv4e3BF8c3G7hvTi7eFh4nKrAj u+iN80iMR3ytIMm8vxcKzzN8An0WvXMSsqOcC/h1jdmao9ta8pxxe47t8+Balac3LqhRTzIKryaOB1iV A/TNqR8t0kGNL1tFVrGqonhRcOEbfrhfW3hfFj1jpZ9FfobRZG61S3tvnEdiPOqrk32RNg9koVb71ubc mpNQW+RfCkL6wwYWbhejOeNtcmzYy9Ox6BpeiRzZ0g5przavP0mywjl+Jkf+hPDKewRfDBKWRdC/squS gmyJ7NW1hPv5PUX212e3CbePxlh2NIr3RWx0oc2D8InPXN/bEKKyy5Z20bpe2UdzRm332rfy9LUXSZYV ZQ/dzBfPK6zbqRjc35OqOkYoUUjurBK8A3Dd0YL6IoPum+1HyHcO/Nc8ZPP5yJbIXl1L8LfyOfv3xnk0 xmqLRsj3O8nj4ycGxia+Tu+chNqiCj+V+P1ka92vJ2fUFiU9eToW3UCB8sXLBWCHEzg9JTKgasd1JQnO Ve3VNqnsLT9QL14FkYLpG88DLFsie3UtIQaMm35m/944Y1ObkRijEXxTVWJcfKmO6GIkd0DtuN5CbRD3 93jkxhvJGbWpxta1xO1j0Q1wjBvpvUEwAU9OifYJk/JKRz9sCoJPzBeNCsL9tCitKpVPLUQS9KD5ZT/G ks3nJFviSbmH5pTzqfr3xnkkxiO+OloHF/GqfMx8cUZyR9dyAzncr/INtfr15Izs1T168vTQxgOChM5C j2xXHlkeiS8aiwgsgmw9T+RciC2qzSDbGXEe8dXxmFSb5CNQkRhdf9aMfh7jUfbG3svTp9t4Z6OnW1Y2 BdPRYmiDOp7QtNuC63ttPpIRX58Rnvaa3ywOj8z7iL/jXVw8AxQUnnhswvN5efkf+nnqL5jaAGcAAAAA SUVORK5CYII= 17, 17 107, 17 一般可以直接用设备名连接到其他电脑。如果名称解析出错,可以手动指定 IP 地址映射。 注意:如果电脑使用动态 IP 地址,那么每次对方 IP 变动时都要手动更改。 58 AAABAAIAICAAAAAAGACoDAAAJgAAABAQAAAAABgAaAMAAM4MAAAoAAAAIAAAAEAAAAABABgAAAAAAAAM AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAEo32AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAEo32AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEo32FIv3Eo32AAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEo32EYv3Eo32AAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAEo32BIf3GJD2Eov3Eo32AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAEo32CYn3GZD2DIf4Eo32AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA QZv0FI72GpH2FIv3Eo32AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEo32Con3HJH2FI72QJv0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANJb1FI72GZD2Ho/2Eo32AAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAEo32Eo32G5H2FI72NJb1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAPpr0Fo72GZD2Don3Eo32AAAAAAAAAAAAAAAAAAAAAAAAEo32BIj3HJH2 Fo72Ppr0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOpn0 Fo/2GZD2II/2Eo32AAAAAAAAAAAAAAAAEo32E432HJH2Fo/2Opn0AAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIo/2HJH2GZD2Foz3Eo32AAAAAAAAEo32 Cor3HJH2HJH2Io/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUZ7zA4f3Con3Con3Con3Con3 Con3Con3Con3DYv3HJH2HpL2HZH2GZD2GIz2Eo32Eo32C4v3HJH2HpL2HpL2HJH2DYv3Con3Con3Con3 Con3Con3Con3Con3AIX4Eo32WqLzD432Fo/2Fo/2Fo/2Fo/2Fo/2Fo/2Fo/2F4/2HZH2HpL2HpL2HZH2 EY32O5j0Npf0FI72HZH2HpL2HpL2HJH2Fo/2Fo/2Fo/2Fo/2Fo/2Fo/2Fo/2Fo/2C4v3Eo32frPvSaPy TqTyTqTyTqTyTqTyTqTyTqTyT6XyV6jxIJH2HZH2HpL2Eo32Opv0AAAAAAAALJT2FY/2HpL2HJH2K5T1 V6nxT6TyTqTyTqTyTqTyTqTyTqTyTqTyRqHzEo32AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHZH2 GY72HZH2Eo32OJr0AAAAAAAAAAAAAAAAKZP1FY/2G5H2I5H2Eo32AAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHZH2Ho/2GpH2Eo32PZz0AAAAAAAAAAAAAAAAAAAA AAAAL5b1FY/2GJD2KJT2Eo32AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAHZH2G472GZD2E432Npn0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKJL1Fo/2Fo/2J5P2Eo32AAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHZH2HpD2GJD2FI72MZj1AAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAJZL2Fo/2Fo/2KpT1Eo32AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAHZH2FIv3GZD2E472MZf1AAAAAAAABAQEBAQEAAAAAAAAAAAAAAAABAQEBAQEAAAAAAAA JZL2Fo/2Fo/2H4/3Eo32AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEo32H5D2Doz2M5j0AAAA AAAABAQEBAQEBAQEBAQEAAAAAAAABAQEBAQEBAQEBAQEAAAAAAAAJpL1D4z2J5T1Eo32AAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEo32NJj1AAAAAAAAAAAABAQE//jwBAQEBAQEAAAAAAAABAQE //jwBAQEBAQEAAAAAAAAAAAAMJb1Eo32AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEAAAAAAAABAQEBAQEBAQEBAQEAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQE AAAAAAAAAAAAAAAABAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////////////////////////v//f/x/ /j/4P/wf/B/4P/4P8H//B+D//4PB///Bg/8AAAAAAAAAAAABgAD/g8H//wfg//4P8H/8H/g/+DPMH/hh hh/84Yc//+GH///zz/////////////////////////////////8oAAAAEAAAACAAAAABABgAAAAAAAAD AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEo32AAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEo32GJD2Eo32AAAAAAAAAAAAAAAAAAAA AAAACYn3DIf4AAAAAAAAAAAAAAAAAAAAAAAANJb1GZD2Eo32AAAAAAAAAAAAAAAAEo32FI72AAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAOpn0GZD2Eo32AAAAAAAAE432Fo/2AAAAAAAAAAAAAAAAAAAAA4f3Con3 Con3Con3DYv3HpL2GZD2Eo32C4v3HpL2HJH2Con3Con3Con3Con3Eo32SaPyTqTyTqTyTqTyV6jxHZH2 Eo32AAAALJT2HpL2K5T1T6TyTqTyTqTyTqTyEo32AAAAAAAAAAAAAAAAHo/2Eo32AAAAAAAAAAAAL5b1 GJD2Eo32AAAAAAAAAAAAAAAAAAAAAAAAAAAAHpD2FI72AAAAAAAAAAAAAAAAAAAAJZL2Fo/2Eo32AAAA AAAAAAAAAAAAAAAAEo32Doz2AAAABAQEBAQEAAAABAQEBAQEAAAAJpL1J5T1AAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAABAQEBAQEAAAABAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAA//8AAP//AAD//wAA7/8AAMfnAADjzwAA8Z8AAAAAAAABAAAA848AAOfHAADJJwAA+T8AAP// AAD//wAA//8AAA== ================================================ FILE: PowerToys/src/modules/MouseWithoutBorders/App/Form/frmMessage.Designer.cs ================================================ namespace MouseWithoutBorders { partial class FrmMessage { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.components = new System.ComponentModel.Container(); System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FrmMessage)); this.labelTitle = new System.Windows.Forms.Label(); this.textExtraInfo = new System.Windows.Forms.TextBox(); this.timerLife = new System.Windows.Forms.Timer(this.components); this.labelLifeTime = new System.Windows.Forms.Label(); this.textBoxMessage = new System.Windows.Forms.TextBox(); this.pictureBoxIcon = new System.Windows.Forms.PictureBox(); ((System.ComponentModel.ISupportInitialize)(this.pictureBoxIcon)).BeginInit(); this.SuspendLayout(); // // labelTitle // this.labelTitle.AutoSize = true; this.labelTitle.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.labelTitle.Location = new System.Drawing.Point(21, 0); this.labelTitle.Name = "labelTitle"; this.labelTitle.Size = new System.Drawing.Size(168, 16); this.labelTitle.TabIndex = 0; this.labelTitle.Text = "无界鼠标"; // // textExtraInfo // this.textExtraInfo.BackColor = System.Drawing.SystemColors.Info; this.textExtraInfo.BorderStyle = System.Windows.Forms.BorderStyle.None; this.textExtraInfo.Dock = System.Windows.Forms.DockStyle.Bottom; this.textExtraInfo.Enabled = false; this.textExtraInfo.Font = new System.Drawing.Font("Microsoft Sans Serif", 63.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.textExtraInfo.Location = new System.Drawing.Point(0, 161); this.textExtraInfo.Margin = new System.Windows.Forms.Padding(20); this.textExtraInfo.Multiline = true; this.textExtraInfo.Name = "textExtraInfo"; this.textExtraInfo.ReadOnly = true; this.textExtraInfo.Size = new System.Drawing.Size(720, 239); this.textExtraInfo.TabIndex = 3; this.textExtraInfo.Text = "???\r\nLLL"; this.textExtraInfo.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; // // timerLife // this.timerLife.Enabled = true; this.timerLife.Interval = 1000; this.timerLife.Tick += new System.EventHandler(this.TimerLife_Tick); // // labelLifeTime // this.labelLifeTime.AutoSize = true; this.labelLifeTime.Dock = System.Windows.Forms.DockStyle.Right; this.labelLifeTime.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.labelLifeTime.Location = new System.Drawing.Point(720, 0); this.labelLifeTime.Name = "labelLifeTime"; this.labelLifeTime.Size = new System.Drawing.Size(0, 16); this.labelLifeTime.TabIndex = 4; // // textBoxMessage // this.textBoxMessage.BackColor = System.Drawing.SystemColors.Info; this.textBoxMessage.BorderStyle = System.Windows.Forms.BorderStyle.None; this.textBoxMessage.Dock = System.Windows.Forms.DockStyle.Fill; this.textBoxMessage.Enabled = false; this.textBoxMessage.Font = new System.Drawing.Font("Microsoft Sans Serif", 20.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.textBoxMessage.Location = new System.Drawing.Point(0, 0); this.textBoxMessage.Margin = new System.Windows.Forms.Padding(20); this.textBoxMessage.Multiline = true; this.textBoxMessage.Name = "textBoxMessage"; this.textBoxMessage.ReadOnly = true; this.textBoxMessage.Size = new System.Drawing.Size(720, 400); this.textBoxMessage.TabIndex = 2; this.textBoxMessage.Text = "Message Text"; this.textBoxMessage.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; // // pictureBoxIcon // this.pictureBoxIcon.Image = ((System.Drawing.Image)(resources.GetObject("pictureBoxIcon.Image"))); this.pictureBoxIcon.Location = new System.Drawing.Point(0, 0); this.pictureBoxIcon.Name = "pictureBoxIcon"; this.pictureBoxIcon.Size = new System.Drawing.Size(16, 16); this.pictureBoxIcon.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize; this.pictureBoxIcon.TabIndex = 5; this.pictureBoxIcon.TabStop = false; // // frmMessage // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(255)))), ((int)(((byte)(231))))); this.BackgroundImageLayout = System.Windows.Forms.ImageLayout.None; this.ClientSize = new System.Drawing.Size(720, 400); this.Controls.Add(this.pictureBoxIcon); this.Controls.Add(this.labelLifeTime); this.Controls.Add(this.textExtraInfo); this.Controls.Add(this.labelTitle); this.Controls.Add(this.textBoxMessage); this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.Name = "frmMessage"; this.StartPosition = System.Windows.Forms.FormStartPosition.Manual; this.Text = "frmMessage"; this.TopMost = true; this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.FrmMessage_FormClosed); ((System.ComponentModel.ISupportInitialize)(this.pictureBoxIcon)).EndInit(); this.ResumeLayout(false); this.PerformLayout(); } #endregion private System.Windows.Forms.Label labelTitle; private System.Windows.Forms.TextBox textExtraInfo; private System.Windows.Forms.Timer timerLife; private System.Windows.Forms.Label labelLifeTime; private System.Windows.Forms.TextBox textBoxMessage; private System.Windows.Forms.PictureBox pictureBoxIcon; } } ================================================ FILE: PowerToys/src/modules/MouseWithoutBorders/App/Form/frmScreen.Designer.cs ================================================ namespace MouseWithoutBorders { partial class FrmScreen { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.components = new System.ComponentModel.Container(); System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FrmScreen)); this.MainMenu = new System.Windows.Forms.ContextMenuStrip(this.components); this.menuExit = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator(); this.menuAbout = new System.Windows.Forms.ToolStripMenuItem(); this.menuHelp = new System.Windows.Forms.ToolStripMenuItem(); this.menuGenDumpFile = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator(); this.menuGetScreenCapture = new System.Windows.Forms.ToolStripMenuItem(); this.menuGetFromAll = new System.Windows.Forms.ToolStripMenuItem(); this.menuSendScreenCapture = new System.Windows.Forms.ToolStripMenuItem(); this.allToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.menuSend2Myself = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator(); this.menuWindowsPhone = new System.Windows.Forms.ToolStripMenuItem(); this.menuWindowsPhoneEnable = new System.Windows.Forms.ToolStripMenuItem(); this.menuWindowsPhoneDownload = new System.Windows.Forms.ToolStripMenuItem(); this.menuWindowsPhoneInformation = new System.Windows.Forms.ToolStripMenuItem(); this.menuReinstallKeyboardAndMouseHook = new System.Windows.Forms.ToolStripMenuItem(); this.menuMachineMatrix = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripSeparator(); this.MenuAllPC = new System.Windows.Forms.ToolStripMenuItem(); this.dUCTDOToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.tRUONG2DToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.NotifyIcon = new System.Windows.Forms.NotifyIcon(this.components); this.picLogonLogo = new System.Windows.Forms.PictureBox(); this.MainMenu.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.picLogonLogo)).BeginInit(); this.SuspendLayout(); // // MainMenu // this.MainMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.menuExit, this.toolStripSeparator2, this.menuAbout, this.menuHelp, this.menuGenDumpFile, this.toolStripSeparator1, this.menuGetScreenCapture, this.menuSendScreenCapture, this.toolStripMenuItem1, this.menuWindowsPhone, this.menuReinstallKeyboardAndMouseHook, this.menuMachineMatrix, this.toolStripMenuItem2, this.MenuAllPC, this.dUCTDOToolStripMenuItem, this.tRUONG2DToolStripMenuItem}); this.MainMenu.Name = "MainMenu"; this.MainMenu.Size = new System.Drawing.Size(197, 292); this.MainMenu.Opening += new System.ComponentModel.CancelEventHandler(this.MainMenu_Opening); this.MainMenu.MouseLeave += new System.EventHandler(this.MainMenu_MouseLeave); // // menuExit // this.menuExit.ForeColor = System.Drawing.Color.Black; this.menuExit.Name = "menuExit"; this.menuExit.Size = new System.Drawing.Size(196, 22); this.menuExit.Text = "退出(&E)"; this.menuExit.Click += new System.EventHandler(this.MenuExit_Click); // // toolStripSeparator2 // this.toolStripSeparator2.Name = "toolStripSeparator2"; this.toolStripSeparator2.Size = new System.Drawing.Size(193, 6); // // menuAbout // this.menuAbout.ForeColor = System.Drawing.Color.Black; this.menuAbout.Name = "menuAbout"; this.menuAbout.Size = new System.Drawing.Size(196, 22); this.menuAbout.Text = "关于(&B)"; this.menuAbout.Click += new System.EventHandler(this.MenuAbout_Click); // // menuHelp // this.menuHelp.Name = "menuHelp"; this.menuHelp.Size = new System.Drawing.Size(196, 22); this.menuHelp.Text = "帮助(&H)"; this.menuHelp.Click += new System.EventHandler(this.MenuHelp_Click); // // menuGenDumpFile // this.menuGenDumpFile.Name = "menuGenDumpFile"; this.menuGenDumpFile.Size = new System.Drawing.Size(196, 22); this.menuGenDumpFile.Text = "运行日志(&G)"; this.menuGenDumpFile.ToolTipText = "创建用于分析问题的日志文件,日志文件将在程序目录下生成。"; this.menuGenDumpFile.Visible = false; this.menuGenDumpFile.Click += new System.EventHandler(this.MenuGenDumpFile_Click); // // toolStripSeparator1 // this.toolStripSeparator1.Name = "toolStripSeparator1"; this.toolStripSeparator1.Size = new System.Drawing.Size(193, 6); // // menuGetScreenCapture // this.menuGetScreenCapture.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.menuGetFromAll}); this.menuGetScreenCapture.ForeColor = System.Drawing.Color.Black; this.menuGetScreenCapture.Name = "menuGetScreenCapture"; this.menuGetScreenCapture.Size = new System.Drawing.Size(196, 22); this.menuGetScreenCapture.Text = "获取截屏自(&G)"; // // menuGetFromAll // this.menuGetFromAll.Enabled = false; this.menuGetFromAll.Name = "menuGetFromAll"; this.menuGetFromAll.Size = new System.Drawing.Size(85, 22); this.menuGetFromAll.Text = "所有电脑"; this.menuGetFromAll.Click += new System.EventHandler(this.MenuGetScreenCaptureClick); // // menuSendScreenCapture // this.menuSendScreenCapture.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.allToolStripMenuItem, this.menuSend2Myself}); this.menuSendScreenCapture.ForeColor = System.Drawing.Color.Black; this.menuSendScreenCapture.Name = "menuSendScreenCapture"; this.menuSendScreenCapture.Size = new System.Drawing.Size(196, 22); this.menuSendScreenCapture.Text = "发送截屏到(&C)"; // // allToolStripMenuItem // this.allToolStripMenuItem.Name = "allToolStripMenuItem"; this.allToolStripMenuItem.Size = new System.Drawing.Size(105, 22); this.allToolStripMenuItem.Text = "所有电脑"; this.allToolStripMenuItem.Click += new System.EventHandler(this.MenuSendScreenCaptureClick); // // menuSend2Myself // this.menuSend2Myself.Name = "menuSend2Myself"; this.menuSend2Myself.Size = new System.Drawing.Size(105, 22); this.menuSend2Myself.Text = "此电脑"; this.menuSend2Myself.Click += new System.EventHandler(this.MenuSendScreenCaptureClick); // // toolStripMenuItem1 // this.toolStripMenuItem1.Name = "toolStripMenuItem1"; this.toolStripMenuItem1.Size = new System.Drawing.Size(193, 6); // // menuReinstallKeyboardAndMouseHook // this.menuReinstallKeyboardAndMouseHook.Name = "menuReinstallKeyboardAndMouseHook"; this.menuReinstallKeyboardAndMouseHook.Size = new System.Drawing.Size(196, 22); this.menuReinstallKeyboardAndMouseHook.Text = "重新安装快捷键侦听器(&R)"; this.menuReinstallKeyboardAndMouseHook.ToolTipText = "This might help when keyboard/Mouse redirection stops working"; this.menuReinstallKeyboardAndMouseHook.Visible = false; this.menuReinstallKeyboardAndMouseHook.Click += new System.EventHandler(this.MenuReinstallKeyboardAndMouseHook_Click); // // menuMachineMatrix // this.menuMachineMatrix.Font = new System.Drawing.Font(System.Windows.Forms.Control.DefaultFont.Name, System.Windows.Forms.Control.DefaultFont.Size, System.Drawing.FontStyle.Bold); this.menuMachineMatrix.ForeColor = System.Drawing.Color.Black; this.menuMachineMatrix.Name = "menuMachineMatrix"; this.menuMachineMatrix.Size = new System.Drawing.Size(196, 22); this.menuMachineMatrix.Text = "设置(&S)"; this.menuMachineMatrix.Click += new System.EventHandler(this.MenuMachineMatrix_Click); // // toolStripMenuItem2 // this.toolStripMenuItem2.Name = "toolStripMenuItem2"; this.toolStripMenuItem2.Size = new System.Drawing.Size(193, 6); // // menuAllPC // this.MenuAllPC.CheckOnClick = true; this.MenuAllPC.Name = "menuAllPC"; this.MenuAllPC.Size = new System.Drawing.Size(196, 22); this.MenuAllPC.Text = "所有电脑(&A)"; this.MenuAllPC.ToolTipText = "Repeat Mouse/keyboard in all machines."; this.MenuAllPC.Click += new System.EventHandler(this.MenuAllPC_Click); // // dUCTDOToolStripMenuItem // this.dUCTDOToolStripMenuItem.Name = "dUCTDOToolStripMenuItem"; this.dUCTDOToolStripMenuItem.Size = new System.Drawing.Size(196, 22); this.dUCTDOToolStripMenuItem.Tag = "MACHINE: TEST1"; this.dUCTDOToolStripMenuItem.Text = "DUCTDO"; // // tRUONG2DToolStripMenuItem // this.tRUONG2DToolStripMenuItem.Name = "tRUONG2DToolStripMenuItem"; this.tRUONG2DToolStripMenuItem.Size = new System.Drawing.Size(196, 22); this.tRUONG2DToolStripMenuItem.Tag = "MACHINE: TEST2"; this.tRUONG2DToolStripMenuItem.Text = "TRUONG2D"; // // notifyIcon // this.NotifyIcon.BalloonTipIcon = System.Windows.Forms.ToolTipIcon.Info; this.NotifyIcon.BalloonTipText = "Microsoft® Visual Studio® 2010"; this.NotifyIcon.BalloonTipTitle = "Microsoft® Visual Studio® 2010"; this.NotifyIcon.ContextMenuStrip = this.MainMenu; this.NotifyIcon.Icon = ((System.Drawing.Icon)(resources.GetObject("notifyIcon.Icon"))); this.NotifyIcon.Text = "Microsoft® Visual Studio® 2010"; this.NotifyIcon.MouseDown += new System.Windows.Forms.MouseEventHandler(this.NotifyIcon_MouseDown); // // picLogonLogo // this.picLogonLogo.BackgroundImageLayout = System.Windows.Forms.ImageLayout.None; this.picLogonLogo.Image = global::MouseWithoutBorders.Properties.Images.MouseWithoutBorders; this.picLogonLogo.Location = new System.Drawing.Point(99, 62); this.picLogonLogo.Name = "picLogonLogo"; this.picLogonLogo.Size = new System.Drawing.Size(95, 17); this.picLogonLogo.TabIndex = 1; this.picLogonLogo.TabStop = false; this.picLogonLogo.Visible = false; // // frmScreen // this.AllowDrop = true; this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.BackColor = System.Drawing.Color.Black; this.BackgroundImageLayout = System.Windows.Forms.ImageLayout.None; this.ClientSize = new System.Drawing.Size(10, 10); this.ControlBox = false; this.Controls.Add(this.picLogonLogo); this.Cursor = System.Windows.Forms.Cursors.Default; this.Enabled = false; this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.MaximizeBox = false; this.MinimizeBox = false; this.Name = "frmScreen"; this.Opacity = 0.5D; this.ShowIcon = false; this.ShowInTaskbar = false; this.StartPosition = System.Windows.Forms.FormStartPosition.Manual; this.Text = "Microsoft® Visual Studio® 2010 Application"; this.TopMost = true; this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FrmScreen_FormClosing); this.Load += new System.EventHandler(this.FrmScreen_Load); this.Shown += new System.EventHandler(this.FrmScreen_Shown); this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.FrmScreen_MouseMove); this.MainMenu.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.picLogonLogo)).EndInit(); this.ResumeLayout(false); } #endregion private System.Windows.Forms.ToolStripMenuItem menuExit; private System.Windows.Forms.ToolStripSeparator toolStripMenuItem1; private System.Windows.Forms.ToolStripMenuItem menuMachineMatrix; private System.Windows.Forms.ToolStripSeparator toolStripMenuItem2; private System.Windows.Forms.ToolStripMenuItem dUCTDOToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem tRUONG2DToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem menuSendScreenCapture; private System.Windows.Forms.ToolStripMenuItem menuSend2Myself; private System.Windows.Forms.ToolStripMenuItem menuGetScreenCapture; private System.Windows.Forms.ToolStripMenuItem menuGetFromAll; private System.Windows.Forms.ToolStripSeparator toolStripSeparator1; private System.Windows.Forms.ToolStripMenuItem allToolStripMenuItem; private System.Windows.Forms.ToolStripSeparator toolStripSeparator2; private System.Windows.Forms.ToolStripMenuItem menuAbout; private System.Windows.Forms.PictureBox picLogonLogo; private System.Windows.Forms.ToolStripMenuItem menuHelp; private System.Windows.Forms.ToolStripMenuItem menuReinstallKeyboardAndMouseHook; private System.Windows.Forms.ToolStripMenuItem menuGenDumpFile; private System.Windows.Forms.ToolStripMenuItem menuWindowsPhone; private System.Windows.Forms.ToolStripMenuItem menuWindowsPhoneEnable; private System.Windows.Forms.ToolStripMenuItem menuWindowsPhoneInformation; private System.Windows.Forms.ToolStripMenuItem menuWindowsPhoneDownload; } } ================================================ FILE: PowerToys/src/modules/MouseWithoutBorders/App/Form/frmScreen.cs ================================================ // 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.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Drawing; using System.Globalization; using System.IO; using System.Windows.Forms; using Microsoft.PowerToys.Telemetry; // // Startup/main form + helper methods. // // // 2008 created by Truong Do (ductdo). // 2009-... modified by Truong Do (TruongDo). // 2023- Included in PowerToys. // using MouseWithoutBorders.Class; using MouseWithoutBorders.Core; using MouseWithoutBorders.Properties; using Timer = System.Windows.Forms.Timer; [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.frmScreen.#ShowMouseWithoutBordersUiOnWinLogonDesktop(System.Boolean)", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.frmScreen.#ChangeIcon(System.Int32)", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.frmScreen.#UpdateMenu(MouseWithoutBorders.MachineInf[])", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.frmScreen.#frmScreen_Load(System.Object,System.EventArgs)", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.frmScreen.#MenuNewVersion(System.Object,System.EventArgs)", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.frmScreen.#.ctor()", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.frmScreen.#helperTimer_Tick(System.Object,System.EventArgs)", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.frmScreen.#LoadPlugins()", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.frmScreen.#menuPersonalizeLogonScrPluginClick(System.Object,System.EventArgs)", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Globalization", "CA1304:SpecifyCultureInfo", Scope = "member", Target = "MouseWithoutBorders.frmScreen.#helperTimer_Tick(System.Object,System.EventArgs)", MessageId = "System.String.ToLower", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Scope = "member", Target = "MouseWithoutBorders.frmScreen.#helperTimer_Tick(System.Object,System.EventArgs)", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Performance", "CA1814:PreferJaggedArraysOverMultidimensional", Scope = "member", Target = "MouseWithoutBorders.frmScreen.#LogonLogo", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Performance", "CA1814:PreferJaggedArraysOverMultidimensional", Scope = "member", Target = "MouseWithoutBorders.frmScreen.#frmScreen_Load(System.Object,System.EventArgs)", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Scope = "member", Target = "MouseWithoutBorders.frmScreen.#LoadNewLogonBackground()", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Scope = "member", Target = "MouseWithoutBorders.frmScreen.#UncheckAllMenus()", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", Scope = "member", Target = "MouseWithoutBorders.frmScreen.#WndProc(System.Windows.Forms.Message&)", MessageId = "MouseWithoutBorders.NativeMethods.SendMessage(System.IntPtr,System.Int32,System.IntPtr,System.IntPtr)", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", Scope = "member", Target = "MouseWithoutBorders.frmScreen.#Destroy()", MessageId = "MouseWithoutBorders.NativeMethods.SendMessage(System.IntPtr,System.Int32,System.IntPtr,System.IntPtr)", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", Scope = "member", Target = "MouseWithoutBorders.frmScreen.#helperTimer_Tick(System.Object,System.EventArgs)", MessageId = "MouseWithoutBorders.NativeMethods.SendMessage(System.IntPtr,System.Int32,System.IntPtr,System.IntPtr)", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Globalization", "CA1300:SpecifyMessageBoxOptions", Scope = "member", Target = "MouseWithoutBorders.frmScreen.#menuPersonalizeLogonScrPluginClick(System.Object,System.EventArgs)", Justification = "Dotnet port with style preservation")] namespace MouseWithoutBorders { internal partial class FrmScreen : System.Windows.Forms.Form { #pragma warning disable CA2213 // Disposing is done by ComponentResourceManager private Cursor dotCur; private Cursor dropCur; private int[,] logonLogo; private Timer helperTimer; #pragma warning restore CA2213 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] internal int CurIcon { get; set; } [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] internal NotifyIcon NotifyIcon { get; set; } [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] internal System.Windows.Forms.ToolStripMenuItem MenuAllPC { get; set; } [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] internal System.Windows.Forms.ContextMenuStrip MainMenu { get; set; } internal FrmScreen() { InitializeComponent(); Text = Setting.Values.MyID; NotifyIcon.BalloonTipText = Application.ProductName; NotifyIcon.BalloonTipTitle = Application.ProductName; menuGenDumpFile.Visible = true; Common.WndProcCounter++; try { menuWindowsPhone.Visible = false; // No longer supported. } catch (Exception e) { Logger.Log(e); } } private void FrmScreen_FormClosing(object sender, FormClosingEventArgs e) { if (e.CloseReason != CloseReason.WindowsShutDown) { if (Tag == null) { e.Cancel = true; } } else { Common.StartServiceAndSendLogoffSignal(); Quit(true, true); } } internal void Destroy() { if (helperTimer != null) { helperTimer.Stop(); helperTimer.Dispose(); helperTimer = null; } NotifyIcon.Visible = false; NotifyIcon.Dispose(); if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop) { Common.RunDDHelper(true); } // Common.UnhookClipboard(); Tag = "此电脑"; Close(); } internal void Quit(bool cleanup, bool isFormClosing) { Tag = "Quitting..."; Setting.Values.SwitchCount += Common.SwitchCount; Process me = Process.GetCurrentProcess(); Common.WndProcCounter++; try { if (cleanup) { Common.Cleanup(); } Common.WndProcCounter++; if (!Common.RunOnScrSaverDesktop) { Common.ReleaseAllKeys(); } Common.RunDDHelper(true); } catch (Exception e) { _ = MessageBox.Show(e.ToString()); } Common.MMSleep(1); Application.Exit(); me.KillProcess(); } private void MenuExit_Click(object sender, EventArgs e) { PowerToysTelemetry.Log.WriteEvent(new MouseWithoutBorders.Telemetry.MouseWithoutBordersOldUIQuitEvent()); Quit(true, false); } internal void MenuOnClick(object sender, EventArgs e) { string name = (sender as ToolStripMenuItem).Text; Common.SwitchToMachine(name); } internal void UpdateMenu() { try { ChangeIcon(-1); while (MainMenu.Items[MainMenu.Items.Count - 1].Tag != null && ((string)MainMenu.Items[MainMenu.Items.Count - 1].Tag).StartsWith("MACHINE: ", StringComparison.CurrentCultureIgnoreCase)) { MainMenu.Items.Remove(MainMenu.Items[MainMenu.Items.Count - 1]); } while (!menuSendScreenCapture.DropDown.Items[ menuSendScreenCapture.DropDown.Items.Count - 1].Text.Equals("此电脑", StringComparison.OrdinalIgnoreCase)) { menuSendScreenCapture.DropDown.Items.Remove(menuSendScreenCapture.DropDown.Items[ menuSendScreenCapture.DropDown.Items.Count - 1]); } while (!menuGetScreenCapture.DropDown.Items[ menuGetScreenCapture.DropDown.Items.Count - 1].Text.Equals("所有电脑", StringComparison.OrdinalIgnoreCase)) { menuGetScreenCapture.DropDown.Items.Remove(menuGetScreenCapture.DropDown.Items[ menuGetScreenCapture.DropDown.Items.Count - 1]); } for (int i = 0; i < Common.MAX_MACHINE; i++) { string newMachine = Common.MachineMatrix[i].Trim(); if (Common.MachinePool.TryFindMachineByName(newMachine, out MachineInf inf) && MachinePool.IsAlive(inf)) { ToolStripMenuItem newItem = new( newMachine, null, new EventHandler(MenuOnClick)); newItem.Tag = "MACHINE: " + inf.Name; newItem.ToolTipText = "Switch Mouse/keyboard to " + newMachine; newItem.Visible = true; _ = MainMenu.Items.Add(newItem); if (!newMachine.Equals(Common.MachineName.Trim(), StringComparison.OrdinalIgnoreCase)) { ToolStripMenuItem newItem2 = new( inf.Name.Trim(), null, new EventHandler(MenuSendScreenCaptureClick)); newItem2.Visible = true; _ = menuSendScreenCapture.DropDown.Items.Add(newItem2); ToolStripMenuItem newItem3 = new( inf.Name.Trim(), null, new EventHandler(MenuGetScreenCaptureClick)); newItem3.Visible = true; _ = menuGetScreenCapture.DropDown.Items.Add(newItem3); } } } menuGetScreenCapture.DropDown.Items[0].Enabled = menuGetScreenCapture.DropDown.Items.Count > 1; } catch (Exception e) { Logger.Log(e); } } // We dont do something heavy in our timer private void FrmScreen_Load(object sender, EventArgs e) { try { // Common.UpdateMenu(); // Common.HookClipboard(); CurIcon = Common.ICON_ONE; ChangeIcon(-1); dotCur = CustomCursor.CreateDotCursor(); Cursor = dotCur; dropCur = CustomCursor.CreateCursor(Icon.ToBitmap(), 0, 0); BackColor = Color.White; Opacity = 0.15; UpdateNotifyIcon(); if (picLogonLogo.Image != null) { Bitmap b = new(picLogonLogo.Image); logonLogo = new int[b.Width, b.Height]; for (int i = 0; i < b.Width; i++) { for (int j = 0; j < b.Height; j++) { Color c = b.GetPixel(i, j); logonLogo[i, j] = !(c.G == 255 && c.R == 0 && c.B == 0) ? (c.B << 16) | (c.G << 8) | c.R : -1; } } b.Dispose(); } } catch (Exception ex) { BackColor = Color.White; Opacity = 0.15; Logger.Log(ex); } helperTimer = new System.Windows.Forms.Timer(); helperTimer.Interval = 100; helperTimer.Tick += new EventHandler(HelperTimer_Tick); helperTimer.Start(); Common.WndProcCounter++; if (Environment.OSVersion.Version.Major > 6 || (Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor >= 1)) { // Win7 up _ = NativeMethods.ChangeWindowMessageFilterEx(Handle, NativeMethods.WM_SHOW_SETTINGS_FORM, 1, IntPtr.Zero); } } private long count = 21; // private bool checkForNewVersion; #if USING_FORM private static frmLogon fLogon = null; #endif private bool busy; private bool shownSetupFormOneTime; private bool myDesktopNotActive; private void HelperTimer_Tick(object sender, EventArgs e) { Common.WndProcCounter++; if (busy) { return; } busy = true; try { if (!Common.IsMyDesktopActive() || Common.CurrentProcess.SessionId != NativeMethods.WTSGetActiveConsoleSessionId()) { myDesktopNotActive = true; SocketStuff sk = Common.Sk; // We are not on application desktop if (sk != null) { turnedOff = true; Common.SecondOpenSocketTry = false; sk.Close(false); Common.Sk = null; count = 21; #if USING_FORM if (fLogon != null) fLogon.Hide(); #endif if (Common.MainFormVisible) { Common.MainFormDot(); } InputSimulation.ResetSystemKeyFlags(); Common.DoSomethingInTheInputCallbackThread(() => { Common.Hook?.ResetLastSwitchKeys(); }); Common.CheckForDesktopSwitchEvent(true); } } else { if (Common.Sk == null) { if (!Common.SecondOpenSocketTry) { if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop && !Common.GetUserName()) { // While Windows 8 is hybrid-shutting down, user name would be empty (as returned from the .Net API), we should not do anything in this case. Logger.LogDebug("No active user."); Thread.Sleep(1000); busy = false; return; } if (myDesktopNotActive) { myDesktopNotActive = false; Common.MyKey = Setting.Values.MyKey; } Common.UpdateMachinePoolStringSetting(); if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop && (Setting.Values.FirstRun || Common.KeyCorrupted)) { if (!shownSetupFormOneTime) { shownSetupFormOneTime = true; Common.ShowMachineMatrix(); if (Common.KeyCorrupted && !Setting.Values.FirstRun) { Common.KeyCorrupted = false; string msg = "连接密码意外损坏,请重新生成。"; MessageBox.Show(msg, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Warning); } } } } Common.ReopenSockets(false); Common.SecondOpenSocketTry = true; if (Common.Sk != null) { Common.SendHello(); Common.SendHeartBeat(); if (!Common.RunOnScrSaverDesktop && !Common.RunOnLogonDesktop) { NotifyIcon.Visible = false; NotifyIcon.Visible = Setting.Values.ShowOriginalUI; // Common.ReHookClipboard(); } Common.RunDDHelper(); } count = 0; Common.InitDone = true; #if SHOW_ON_WINLOGON if (Common.RunOnLogonDesktop) { ShowMouseWithoutBordersUiOnWinLogonDesktop(true); } #endif } if ((count % 2) == 0) { if (Common.PleaseReopenSocket == 10 || (Common.PleaseReopenSocket > 0 && count > 0 && count % 300 == 0)) { if (!Common.AtLeastOneSocketEstablished() || Common.PleaseReopenSocket == 10) { Thread.Sleep(1000); if (Common.PleaseReopenSocket > 0) { Common.PleaseReopenSocket--; } // Double check. if (!Common.AtLeastOneSocketEstablished()) { Common.GetMachineName(); Logger.LogDebug("Common.pleaseReopenSocket: " + Common.PleaseReopenSocket.ToString(CultureInfo.InvariantCulture)); Common.ReopenSockets(false); Common.NewDesMachineID = Common.DesMachineID = Common.MachineID; } } else { Common.PleaseReopenSocket = 0; } } if (Common.PleaseReopenSocket == Common.REOPEN_WHEN_HOTKEY) { Common.PleaseReopenSocket = 0; Common.ReopenSockets(true); } else if (Common.PleaseReopenSocket == Common.REOPEN_WHEN_WSAECONNRESET) { Common.PleaseReopenSocket = 0; Thread.Sleep(1000); Common.UpdateClientSockets("REOPEN_WHEN_WSAECONNRESET"); } if (Common.RunOnLogonDesktop) { PaintMyNameOnDesktop(); } /* if (checkClipboard) { Common.CtrlC = -1; checkClipboard = false; Common.CheckClipboard(); } else if (--Common.CtrlC == 0)//Giving some timeout from the time Ctrl+C { if ((Common.GetTick() - Common.LastClipboardEventTime > 5000) && Common.CheckClipboard()) { Common.ReHookClipboard(); } } * */ } // One more time after 1/3 minutes (Sometimes XP has explorer started late) if (count == 600 || count == 1800) { Common.RunDDHelper(); } if (count == 600) { if (!Common.GeneratedKey) { Common.MyKey = Setting.Values.MyKey; if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop) { Common.ShowMachineMatrix(); Common.MatrixForm?.UpdateKeyTextBox(); Common.Sk?.Close(false); Common.ShowToolTip("连接密码不可以手动修改,必须点击生成按钮自动生成.", 10000); } } else if (!Common.KeyCorrupted && !Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop && !Setting.Values.FirstRun && Common.AtLeastOneSocketConnected()) { int myKeyDaysToExpire = Setting.Values.MyKeyDaysToExpire; if (myKeyDaysToExpire <= 0) { Common.ShowMachineMatrix(); Common.Sk?.Close(false); string msg = "连接密码已过期,请重新生成."; Common.ShowToolTip(msg, 10000); } else if (myKeyDaysToExpire <= 15) { Common.ShowToolTip($"连接密码将在 {myKeyDaysToExpire} 天内过期! 请注意重新生成密码.", 10000); } } } } if (count == 20) { #if SHOW_ON_WINLOGON // if (Common.RunOnLogonDesktop) ShowMouseWithoutBordersUiOnWinLogonDesktop(false); #endif Common.CheckForDesktopSwitchEvent(true); Common.UpdateClientSockets("helperTimer_Tick"); // Sockets may be closed by the remote host when both machines switch desktop at the same time. } count++; if (count % 5 == 0) { if (Common.RunOnLogonDesktop) { ShowMessageOnLogonDesktop(Common.DesMachineID != ID.ALL); } if (Common.ToggleIcons != null) { Common.ToggleIcon(); } if (count % 20 == 0) { Logger.LogAll(); // Need to review this code on why it is needed (moved from MoveToMyNeighbourIfNeeded(...)) for (int i = 0; i < Common.MachineMatrix.Length; i++) { if (string.IsNullOrEmpty(Common.MachineMatrix[i]) && !Common.InMachineMatrix(Common.MachineName)) { Common.MachineMatrix[i] = Common.MachineName; } } if (count % 600 == 0 && Common.Sk != null) { // Send out Heartbeat every 1 or 5 mins if (Setting.Values.BlockScreenSaver || count % 3000 == 0) { Common.SendAwakeBeat(); Common.RemoveDeadMachines(); // GC.Collect(); // GC.WaitForPendingFinalizers(); } Common.CloseAnUnusedSocket(); } } else if ((count % 36005) == 0) {// One hour Common.SaveSwitchCount(); int rv = 0; if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop && Common.IsMyDesktopActive() && (rv = Common.SendMessageToHelper(0x400, IntPtr.Zero, IntPtr.Zero)) <= 0) { Logger.TelemetryLogTrace($"{Common.HELPER_FORM_TEXT} not found: {rv}", SeverityLevel.Warning); } } } } catch (Exception ex) { Logger.Log(ex); } finally { busy = false; } } private void MenuMachineMatrix_Click(object sender, EventArgs e) { Common.ShowMachineMatrix(); } private void FrmScreen_MouseMove(object sender, MouseEventArgs e) { if (!Common.IsDropping) { if (Cursor != dotCur) { Cursor = dotCur; Refresh(); // Application.DoEvents(); } } } internal void ChangeIcon(int iconCode) { try { Graphics g; Pen p; Bitmap bm = Images.notify_default; /* if (curIcon == Common.ICON_ONE) { string[] x = Common.MachineMatrix; Graphics g = Graphics.FromImage(bm); if (x != null) { for (int i = 0; i < Common.MAX_MACHINE; i++) { if (x[i].Trim().Length > 0) g.FillEllipse( #if DEBUG Brushes.White, #else Brushes.Salmon, #endif 3 + (i % 2) * 16, 3 + (i / 2) * 16, 10, 10); } } g.Dispose(); } */ if (CurIcon != Common.ICON_ONE) { p = new Pen(Color.Red, 2); g = Graphics.FromImage(bm); g.DrawRectangle(p, 1, 1, bm.Width - 2, bm.Height - 2); g.Dispose(); } if (iconCode == Common.ICON_SMALL_CLIPBOARD) { g = Graphics.FromImage(bm); g.FillEllipse(Brushes.Blue, 8, 8, 16, 16); g.Dispose(); } else if (iconCode == Common.ICON_BIG_CLIPBOARD) { g = Graphics.FromImage(bm); g.FillEllipse(Brushes.Yellow, 8, 8, 16, 16); g.Dispose(); } else if (iconCode == Common.ICON_ERROR) { g = Graphics.FromImage(bm); g.FillEllipse(Brushes.Red, 8, 8, 16, 16); g.Dispose(); } #if DEBUG p = new Pen(Color.White, 4); g = Graphics.FromImage(bm); g.DrawRectangle(p, 6, 6, bm.Width - 12, bm.Height - 12); g.Dispose(); p.Dispose(); #endif Logger.LogDebug($"Changing icon to {iconCode}."); if (NotifyIcon.Icon != null) { NativeMethods.DestroyIcon(NotifyIcon.Icon.Handle); } NotifyIcon.Icon = Icon.FromHandle(bm.GetHicon()); bm.Dispose(); } catch (Exception e) { Logger.Log(e); } } internal void MenuAllPC_Click(object sender, EventArgs e) { Logger.LogDebug("menuAllPC_Click"); Common.SwitchToMultipleMode(MenuAllPC.Checked, true); CurIcon = MenuAllPC.Checked ? Common.ICON_ALL : Common.ICON_ONE; ChangeIcon(CurIcon); } internal void UpdateMultipleModeIconAndMenu() { Common.DoSomethingInUIThread(() => { MenuAllPC.Checked = Common.NewDesMachineID == ID.ALL; CurIcon = MenuAllPC.Checked ? Common.ICON_ALL : Common.ICON_ONE; ChangeIcon(CurIcon); }); } // private bool checkClipboard = false; protected override void WndProc(ref Message m) { const int WM_ENDSESSION = 0x0016; const int WM_QUERYENDSESSION = 0x0011; // const int WM_DRAWCLIPBOARD = 0x0308; // const int WM_CHANGECBCHAIN = 0x030D; switch (m.Msg) { /* case WM_DRAWCLIPBOARD: if (Common.NextClipboardViewer != IntPtr.Zero && Common.NextClipboardViewer != this.Handle) { int rv = NativeMethods.SendMessage(Common.NextClipboardViewer, m.Msg, m.WParam, m.LParam); Common.Log("SendMessage returned " + rv.ToString(CultureInfo.CurrentCulture)); } if (Common.GetTick() - Common.LastClipboardEventTime < 1000) { Common.Log("GetTick() - lastClipboardEventTime < 1000"); Common.LastClipboardEventTime = Common.GetTick(); return; } Common.LastClipboardEventTime = Common.GetTick(); checkClipboard = true;//Do the task in a next message loop. break; case WM_CHANGECBCHAIN: if (m.WParam == Common.NextClipboardViewer) Common.NextClipboardViewer = m.LParam; else if (Common.NextClipboardViewer != IntPtr.Zero && Common.NextClipboardViewer != this.Handle) { int rv = NativeMethods.SendMessage(Common.NextClipboardViewer, m.Msg, m.WParam, m.LParam); Common.Log("SendMessage returned " + rv.ToString(CultureInfo.CurrentCulture)); } break; */ case NativeMethods.WM_SHOW_DRAG_DROP: Point p = default; _ = NativeMethods.GetCursorPos(ref p); Width = 70; Height = 70; Left = p.X - (Width / 3); Top = p.Y - (Height / 3); BackColor = Color.White; Opacity = 0.15; if (Cursor != dropCur) { Cursor = dropCur; } Show(); break; case NativeMethods.WM_HIDE_DRAG_DROP: Common.MainFormDot(); /* this.Width = 1; this.Height = 1; this.Left = 0; this.Top = 0; //this.Hide(); //Common.MainFormVisible = false; * */ if (Cursor != dotCur) { Cursor = dotCur; } break; case NativeMethods.WM_HIDE_DD_HELPER: Common.MainForm3Pixels(); Common.MMSleep(0.2); if (m.WParam.ToInt32() == 1) { InputSimulation.MouseUp(); // A file is being dragged } IntPtr h = (IntPtr)NativeMethods.FindWindow(null, Common.HELPER_FORM_TEXT); if (h.ToInt32() > 0) { Logger.LogDebug("Hide Mouse Without Borders Helper."); // Common.ShowWindow(h, 1); _ = NativeMethods.ShowWindow(h, 0); // Common.SetWindowPos(h, Common.HWND_NOTOPMOST, 10, 10, 50, 50, 0); // Common.SetWindowPos(h, Common.HWND_TOPMOST, 0, 0, 800, 60, 0);// For debug } break; case NativeMethods.WM_CHECK_EXPLORER_DRAG_DROP: Logger.LogDebug("Got WM_CHECK_EXPLORER_DRAG_DROP!"); Common.DragDropStep04(); break; case NativeMethods.WM_QUIT: // Quit(true); break; case NativeMethods.WM_SWITCH: break; case WM_QUERYENDSESSION: Logger.LogDebug("WM_QUERYENDSESSION..."); Common.StartServiceAndSendLogoffSignal(); break; case WM_ENDSESSION: Quit(true, true); break; case NativeMethods.WM_SHOW_SETTINGS_FORM: if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop) { Common.ShowMachineMatrix(); } break; default: base.WndProc(ref m); break; } } private void NotifyIcon_MouseDown(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) { Common.ShowMachineMatrix(); } } internal void SetTrayIconText(string value) { if (value.Length > 63) { value = value[..63]; } NotifyIcon.Text = value; } internal void UpdateNotifyIcon() { string[] x = Common.MachineMatrix; string iconText; if (x != null && (x[0].Length > 0 || x[1].Length > 0 || x[2].Length > 0 || x[3].Length > 0)) { iconText = Setting.Values.MatrixOneRow ? string.Format( CultureInfo.CurrentCulture, "[{0}][{1}][{2}][{3}]", x[0].Trim(), x[1].Trim(), x[2].Trim(), x[3].Trim()) : string.Format( CultureInfo.CurrentCulture, "[{0}][{1}]\r\n[{2}][{3}]", x[0].Trim(), x[1].Trim(), x[2].Trim(), x[3].Trim()); SetTrayIconText(iconText); } else { SetTrayIconText(Application.ProductName); } } internal void ShowToolTip(string txt, int timeOutInMilliseconds, ToolTipIcon icon = ToolTipIcon.Info, bool forceEvenIfHidingOldUI = false) { if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop) { var oldNotifyVisibility = NotifyIcon.Visible; // In order to show tooltips, the icon needs to be shown. if (forceEvenIfHidingOldUI) { NotifyIcon.Visible = true; } NotifyIcon.ShowBalloonTip(timeOutInMilliseconds, Application.ProductName, txt, icon); if (forceEvenIfHidingOldUI) { NotifyIcon.Visible = oldNotifyVisibility; } } } private void MenuSendScreenCaptureClick(object sender, EventArgs e) { string menuCaption = (sender as ToolStripMenuItem).Text; string captureFile = Common.CaptureScreen(); if (captureFile != null && File.Exists(captureFile)) { if (menuCaption.Equals("此电脑", StringComparison.OrdinalIgnoreCase)) { Common.OpenImage(captureFile); } else { Common.SendImage(menuCaption, captureFile); } } } private void MenuGetScreenCaptureClick(object sender, EventArgs e) { string menuCaption = (sender as ToolStripMenuItem).Text; // Send CaptureScreenCommand ID des = menuCaption.Equals("所有电脑", StringComparison.OrdinalIgnoreCase) ? ID.ALL : Common.IdFromName(menuCaption); Common.SendPackage(des, PackageType.CaptureScreenCommand); } private void FrmScreen_Shown(object sender, EventArgs e) { Common.AssertOneInstancePerDesktopSession(); Common.MainForm = this; Hide(); if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop) { NotifyIcon.Visible = false; NotifyIcon.Visible = Setting.Values.ShowOriginalUI; } if (Program.ShowServiceModeErrorTooltip) { Common.ShowToolTip("服务启动失败,将以一般模式继续运行,请在设置中重新打开服务模式.", 10000, forceEvenIfHidingOldUI: true); } } private void MenuAbout_Click(object sender, EventArgs e) { if (Common.AboutForm == null) { _ = (Common.AboutForm = new FrmAbout()).ShowDialog(); } else { Common.AboutForm.Activate(); } } #if SHOW_ON_WINLOGON private void ShowMouseWithoutBordersUiOnWinLogonDesktop(bool initMenuState) { Common.PaintCount = 0; try { if (initMenuState) { HideMenuWhenRunOnLogonDesktop(); } #if !USING_FORM PaintMyNameOnDesktop(); #else if (fLogon == null) fLogon = new frmLogon(); fLogon.Show(); fLogon.LabelDesktop.ContextMenuStrip = fLogon.ContextMenuStrip = MainMenu; #endif } catch (Exception e) { Logger.Log(e); } } internal void HideMenuWhenRunOnLogonDesktop() { menuHelp.Visible = false; menuGetScreenCapture.Visible = false; menuSendScreenCapture.Visible = false; toolStripSeparator1.Visible = false; toolStripSeparator2.Visible = false; menuAbout.Visible = false; menuMachineMatrix.Visible = false; menuReinstallKeyboardAndMouseHook.Visible = false; toolStripMenuItem1.Visible = false; toolStripMenuItem2.Visible = false; menuExit.Visible = false; } #endif private void MainMenu_MouseLeave(object sender, EventArgs e) { #if SHOW_ON_WINLOGON if (Common.RunOnLogonDesktop) { MainMenu.Hide(); Thread.Sleep(50); PaintMyNameOnDesktop(); } #endif } private int colorB = 240; private int colorBlueDelta = 5; private bool turnedOff = true; internal void ShowMessageOnLogonDesktop(bool turnOff) { if (turnOff && turnedOff) { colorB = 240; colorBlueDelta = 5; return; } turnedOff = false; IntPtr hDesktop = NativeMethods.GetDesktopWindow(); IntPtr hdc = NativeMethods.GetWindowDC(hDesktop); int textLengthInPixels; if (hdc != IntPtr.Zero) { NativeMethods.RECT r; string machineMatrix = Application.ProductName + " is in multiple mode, keyboard may repeat in all machines: "; r.Top = 0; r.Left = Common.ScreenWidth / 5; r.Right = Common.ScreenWidth - (Common.ScreenWidth / 5); r.Bottom = 20; for (int i = 0; i < Common.MAX_MACHINE; i++) { string newMachine = Common.MachineMatrix[i].Trim(); if (Common.MachinePool.TryFindMachineByName(newMachine, out MachineInf inf) && MachinePool.IsAlive(inf)) { machineMatrix += "[" + inf.Name.Trim() + "]"; } } if (turnOff) { turnedOff = true; colorBlueDelta = -40; colorB = 0; } else { colorBlueDelta = colorB == 255 ? -colorBlueDelta : colorB == 240 ? colorBlueDelta > 0 ? 5 : -40 : colorB == 0 ? -colorBlueDelta : colorBlueDelta; colorB += colorBlueDelta; } _ = NativeMethods.SetBkColor(hdc, 0); _ = NativeMethods.SetTextColor(hdc, colorB << 8); _ = NativeMethods.DrawText(hdc, machineMatrix, machineMatrix.Length, ref r, 0x1 | 0x4 | 0x100 | 0x400); textLengthInPixels = r.Right - r.Left; r.Left = (Common.ScreenWidth - textLengthInPixels) / 2; r.Right = r.Left + textLengthInPixels; _ = NativeMethods.DrawText(hdc, machineMatrix, machineMatrix.Length, ref r, 0x1 | 0x4 | 0x100); _ = NativeMethods.ReleaseDC(hDesktop, hdc); } } #if SHOW_ON_WINLOGON private const byte MIN_COLOR = 185; private const byte MAX_COLOR = 255; private const byte MIN_COLOR_DOWN = 10; private byte paintColorR = MIN_COLOR; private byte paintColorG = MIN_COLOR; private byte paintColorB = MIN_COLOR; private bool paintColorDown; internal void PaintMyNameOnDesktop() { if (Setting.Values.HideLogonLogo) { return; } if (Common.PaintCount > 1500) { return; // running for 5 mins only } Common.PaintCount++; IntPtr hDesktop = NativeMethods.GetDesktopWindow(); IntPtr hdc = NativeMethods.GetWindowDC(hDesktop); if (hdc != IntPtr.Zero) { int c; // int rv = NativeMethods.DrawText(hdc, Common.BinaryName, 10, ref r, 0); for (int i = 0; i < logonLogo.GetUpperBound(0); i++) { for (int j = 0; j < logonLogo.GetUpperBound(1); j++) { if (logonLogo[i, j] != -1) { c = logonLogo[i, j]; if (c == 0xFFFFFF) { c = (paintColorB << 16) | (paintColorG << 8) | paintColorR; } int rv = (int)NativeMethods.SetPixel(hdc, i, j, (uint)c); } } } // Common.Log("PaintMyNameOnDesktop: last rv = " + rv.ToString(CultureInfo.CurrentCulture)); _ = NativeMethods.ReleaseDC(hDesktop, hdc); } if (!paintColorDown) { if (paintColorR < MAX_COLOR) { paintColorR += MIN_COLOR_DOWN; } else { if (paintColorG < MAX_COLOR) { paintColorG += MIN_COLOR_DOWN; } else { if (paintColorB < MAX_COLOR) { paintColorB += MIN_COLOR_DOWN; } else { paintColorDown = true; } } } } else { if (paintColorB > MIN_COLOR) { paintColorB -= MIN_COLOR_DOWN; } else { if (paintColorR > MIN_COLOR) { paintColorR -= MIN_COLOR_DOWN; } else { if (paintColorG > MIN_COLOR) { paintColorG -= MIN_COLOR_DOWN; } else { paintColorDown = false; } } } } } #endif private void MenuHelp_Click(object sender, EventArgs e) { } internal void MenuReinstallKeyboardAndMouseHook_Click(object sender, EventArgs e) { Common.DoSomethingInTheInputCallbackThread(() => { if (Common.Hook != null) { Common.Hook.Stop(); Common.Hook = null; } Common.InputCallbackForm.Close(); Common.InputCallbackForm = null; Program.StartInputCallbackThread(); }); } private void MenuGenDumpFile_Click(object sender, EventArgs e) { Logger.GenerateLog(); } private void MainMenu_Opening(object sender, CancelEventArgs e) { UpdateMenu(); } } } ================================================ FILE: PowerToys/src/modules/MouseWithoutBorders/App/Helper/FormHelper.cs ================================================ // 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.Collections.Specialized; using System.Diagnostics; using System.Drawing; using System.Drawing.Imaging; using System.Globalization; using System.IO; using System.Runtime.InteropServices; using System.Threading; using System.Windows.Forms; namespace MouseWithoutBorders { public partial class FormHelper : System.Windows.Forms.Form { private readonly List focusZone = new(); private readonly Lock bmScreenLock = new(); private long lastClipboardEventTime; private IClipboardHelper remoteClipboardHelper; private const string TEXT_TYPE_SEP = "{4CFF57F7-BEDD-43d5-AE8F-27A61E886F2F}"; private const int MAX_TEXT_SIZE = 20 * 1024 * 1024; private const int MAX_IMAGE_SIZE = 50 * 1024 * 1024; private struct FocusArea { internal RectangleF Rec; internal Color Color; internal FocusArea(RectangleF rec, Color color) { Rec = rec; Color = color; } } private Point focus1 = Point.Empty; private Point focus2 = Point.Empty; public FormHelper() { remoteClipboardHelper = IpcHelper.CreateIpcClient(); if (remoteClipboardHelper == null) { QuitDueToCommunicationError(); return; } SetDPIAwareness(); InitializeComponent(); lastClipboardEventTime = GetTick(); ClipboardMMHelper.HookClipboard(this); } private void SetDPIAwareness() { int setProcessDpiAwarenessResult = -1; try { setProcessDpiAwarenessResult = NativeMethods.SetProcessDpiAwareness(2); SendLog(string.Format(CultureInfo.InvariantCulture, "SetProcessDpiAwareness: {0}.", setProcessDpiAwarenessResult)); } catch (DllNotFoundException) { SendLog("SetProcessDpiAwareness is unsupported in Windows 7 and lower."); } catch (Exception e) { SendLog(e.ToString()); } try { if (setProcessDpiAwarenessResult != 0) { SendLog(string.Format(CultureInfo.InvariantCulture, "SetProcessDPIAware: {0}.", NativeMethods.SetProcessDPIAware())); } } catch (Exception e) { SendLog(e.ToString()); } } private bool quitDueToCommunicationError; private void QuitDueToCommunicationError() { if (!quitDueToCommunicationError) { quitDueToCommunicationError = true; if (Process.GetCurrentProcess().SessionId != NativeMethods.WTSGetActiveConsoleSessionId()) { EventLogger.LogEvent(Application.ProductName + " cannot be used in a remote desktop or virtual machine session."); } else { _ = MessageBox.Show( "无法连接到无界鼠标进程,剪贴板共享功能失效.\r\n查看事件日志来获取更多信息.", Text, MessageBoxButtons.OK, MessageBoxIcon.Error); } Process.GetCurrentProcess().Kill(); } } private void FormHelper_DragEnter(object sender, DragEventArgs e) { object o = e.Data.GetData(DataFormats.FileDrop); if (o != null) { e.Effect = DragDropEffects.Copy; Array ar = (string[])o; if (ar.Length > 0) { string fileName = ar.GetValue(0).ToString(); Hide(); try { remoteClipboardHelper.SendDragFile(fileName); } catch (Exception ex) { EventLogger.LogEvent("FormHelper_DragEnter: " + ex.Message, EventLogEntryType.Error); QuitDueToCommunicationError(); } } } } private void TimerHelper_Tick(object sender, EventArgs e) { lock (bmScreenLock) { if (picScr == null) { timerHelper.Stop(); Hide(); } } } private void FormHelper_Shown(object sender, EventArgs e) { timerHelper.Start(); // To be sure. Hide(); } internal void SendLog(string log) { try { EventLogger.LogEvent(log, EventLogEntryType.Warning); } catch (Exception e) { EventLogger.LogEvent(log + " ==> SendLog Exception: " + e.Message, EventLogEntryType.Warning); } } private long GetTick() // ms { return DateTime.Now.Ticks / 10000; } private string GetClipboardText() { string st = string.Empty, tmp = ClipboardMMHelper.GetText(TextDataFormat.UnicodeText); int txtL = 0, rtfL = 0, htmL = 0; if (tmp != null && (txtL = tmp.Length) > 0) { st += "TXT" + tmp + TEXT_TYPE_SEP; } tmp = ClipboardMMHelper.GetText(TextDataFormat.Rtf); if (tmp != null && (rtfL = tmp.Length) > 0) { st += "RTF" + tmp + TEXT_TYPE_SEP; } tmp = ClipboardMMHelper.GetText(TextDataFormat.Html); if (tmp != null && (htmL = tmp.Length) > 0) { st += "HTM" + tmp + TEXT_TYPE_SEP; } if (st.Length > 0) { if (st.Length > MAX_TEXT_SIZE) { st = null; SendLog(string.Format(CultureInfo.CurrentCulture, "GetClipboardText, Text too big: TXT = {0}, RTF = {1}, HTM = {2}.", txtL, rtfL, htmL)); } else { SendLog(string.Format(CultureInfo.CurrentCulture, "GetClipboardText: TXT = {0}, RTF = {1}, HTM = {2}.", txtL, rtfL, htmL)); } } return st; } #pragma warning disable CA2213 // Disposing is done by ComponentResourceManager private Image bmScreen; private Point startOrg; private Point start; private Point stop; private PictureBox left; private PictureBox top; private PictureBox right; private PictureBox bottom; private PictureBox picScr; #pragma warning restore CA2213 private int customScreenCaptureInProgress; private int screenLeft; private int screenTop; protected override void WndProc(ref Message m) { const int WM_DRAWCLIPBOARD = 0x0308; const int WM_CHANGECBCHAIN = 0x030D; const int WM_CLIPBOARDUPDATE = 0x031D; switch (m.Msg) { case WM_DRAWCLIPBOARD: case WM_CLIPBOARDUPDATE: ClipboardMMHelper.PassMessageToTheNextViewer(m); if (GetTick() - lastClipboardEventTime < 1000) { lastClipboardEventTime = GetTick(); return; } if (customScreenCaptureInProgress > 0) { // 10 secs timeout for a failed capture. if (GetTick() - lastClipboardEventTime < 10000) { return; } else { customScreenCaptureInProgress = 0; } } lastClipboardEventTime = GetTick(); ByteArrayOrString? data = null; bool isFile = false; if (ClipboardMMHelper.ContainsText()) { data = GetClipboardText(); } else if (ClipboardMMHelper.ContainsImage()) { MemoryStream ms = new(); Image im = ClipboardMMHelper.GetImage(); if (im != null) { im.Save(ms, ImageFormat.Png); if (ms.Length > 0) { if (ms.Length > MAX_IMAGE_SIZE) { SendLog("Image from clipboard, image too big: " + ms.Length.ToString(CultureInfo.InvariantCulture)); } else { data = ms.GetBuffer(); SendLog("Image from clipboard: " + ms.Length.ToString(CultureInfo.InvariantCulture)); } } else { SendLog("ClipboardMMHelper image is 0 in length."); } } else { SendLog("ClipboardMMHelper image (GetImage) is null."); } ms.Dispose(); } else if (ClipboardMMHelper.ContainsFileDropList()) { StringCollection files = ClipboardMMHelper.GetFileDropList(); if (files != null) { if (files.Count > 0) { data = files[0]; isFile = true; SendLog("File from clipboard: " + files[0]); } else { SendLog("GetFileDropList returned no file."); } } else { SendLog("GetFileDropList returned null."); } } else { SendLog("ClipboardMMHelper does not have text/image/file data."); return; } if (data != null) { try { remoteClipboardHelper.SendClipboardData((ByteArrayOrString)data, isFile); } catch (Exception ex) { EventLogger.LogEvent("WM_DRAWCLIPBOARD: " + ex.Message, EventLogEntryType.Error); QuitDueToCommunicationError(); } GC.Collect(); } else { SendLog("Null clipboard data returned. See previous messages (if any) for more information."); } break; case WM_CHANGECBCHAIN: if (!ClipboardMMHelper.UpdateNextClipboardViewer(m)) { ClipboardMMHelper.PassMessageToTheNextViewer(m); } break; case 0x401: screenLeft = 0; screenTop = 0; foreach (Screen s in Screen.AllScreens) { if (s.Bounds.Left < screenLeft) { screenLeft = s.Bounds.Left; } if (s.Bounds.Top < screenTop) { screenTop = s.Bounds.Top; } } customScreenCaptureInProgress = 1; lastClipboardEventTime = GetTick(); SendLog("**************************************************\r\nScreen capture triggered."); m.Result = new IntPtr(1); break; case 0x406: if (m.Msg == 0x406) { lock (bmScreenLock) { bmScreen = null; for (int i = 0; i < 30; i++) { Thread.Sleep(100); if (ClipboardMMHelper.ContainsImage()) { bmScreen = ClipboardMMHelper.GetImage(); customScreenCaptureInProgress = 1; break; } } if (bmScreen == null) { customScreenCaptureInProgress = 0; SendLog("No image found in the clipboard."); } else { Opacity = 1; picScr = new PictureBox(); picScr.Dock = DockStyle.Fill; picScr.SizeMode = PictureBoxSizeMode.StretchImage; picScr.Image = bmScreen; picScr.Refresh(); Controls.Add(picScr); AssignEventHandlers(picScr); } } } MouseMoveHandler(); break; case 0x407: Program.DotForm.SetPosition(m.WParam.ToInt32(), m.LParam.ToInt32()); Program.DotForm.TopMost = true; Program.DotForm.Show(); Application.DoEvents(); // Simulate input to help bring to the foreground, as it doesn't seem to work in every case otherwise. NativeMethods.INPUT input = new NativeMethods.INPUT { type = (int)NativeMethods.InputType.INPUT_MOUSE, mi = { } }; NativeMethods.INPUT[] inputs = new NativeMethods.INPUT[] { input }; _ = NativeMethods.SendInput(1, inputs, Marshal.SizeOf(typeof(NativeMethods.INPUT))); m.Result = SetForeGround() ? new IntPtr(1) : IntPtr.Zero; break; case 0x408: Program.DotForm.Hide(); break; case 0x400: m.Result = Handle; break; case SharedConst.QUIT_CMD: Process.GetCurrentProcess().Kill(); break; default: base.WndProc(ref m); break; } } private bool SetForeGround() { string logTag = nameof(SetForeGround); IntPtr foreGroundWindow = NativeMethods.GetForegroundWindow(); if (foreGroundWindow == Program.DotForm.Handle) { SendLog($"{logTag}.Foreground window is already the dot form: {foreGroundWindow}."); return true; } Program.DotForm.Activate(); bool setForegroundWindow = NativeMethods.SetForegroundWindow(Program.DotForm.Handle); SendLog($"{logTag}.{nameof(NativeMethods.SetForegroundWindow)}({Program.DotForm.Handle}) returned: {setForegroundWindow}."); return LogForeGroundWindow(logTag); } private bool LogForeGroundWindow(string logTag) { IntPtr foreGroundWindow = NativeMethods.GetForegroundWindow(); if (foreGroundWindow == Program.DotForm.Handle) { SendLog($"{logTag}.Foreground window is now the dot form: {Program.DotForm.Handle}."); return true; } else { SendLog($"{logTag}.Foreground window is: [{foreGroundWindow}]."); return false; } } private void AssignEventHandlers(PictureBox p) { p.MouseDown += new System.Windows.Forms.MouseEventHandler(FormHelper_MouseDown); p.MouseMove += new System.Windows.Forms.MouseEventHandler(FormHelper_MouseMove); p.MouseUp += new System.Windows.Forms.MouseEventHandler(FormHelper_MouseUp); } private void MouseMoveHandler() { lock (bmScreenLock) { if (bmScreen != null && Opacity == 1) { focusZone.Clear(); TopMost = true; Show(); startOrg = new Point(MousePosition.X - screenLeft, MousePosition.Y - screenTop); if (left == null) { left = new PictureBox(); left.BackColor = Color.Red; Controls.Add(left); left.BringToFront(); AssignEventHandlers(left); } left.Left = startOrg.X; left.Top = startOrg.Y; left.Width = 2; left.Height = 100; left.Show(); left.Refresh(); if (top == null) { top = new PictureBox(); top.BackColor = Color.Red; Controls.Add(top); top.BringToFront(); AssignEventHandlers(top); } top.Left = startOrg.X; top.Top = startOrg.Y; top.Height = 2; top.Width = 100; top.Show(); top.Refresh(); } } } private void MouseDownMoveHandler() { lock (bmScreenLock) { if (bmScreen != null && left != null && top != null) { int x = MousePosition.X - screenLeft; int y = MousePosition.Y - screenTop; start = new Point(startOrg.X < x ? startOrg.X : x, startOrg.Y < y ? startOrg.Y : y); stop = new Point(startOrg.X > x ? startOrg.X : x, startOrg.Y > y ? startOrg.Y : y); left.Left = start.X; left.Top = start.Y; left.Width = 2; left.Height = stop.Y - start.Y; left.Show(); top.Left = start.X; top.Top = start.Y; top.Height = 2; top.Width = stop.X - start.X; top.Show(); if (right == null) { right = new PictureBox(); right.BackColor = Color.Red; Controls.Add(right); right.BringToFront(); AssignEventHandlers(right); } right.Left = stop.X; right.Top = start.Y; right.Width = 2; right.Height = stop.Y - start.Y; right.Show(); if (bottom == null) { bottom = new PictureBox(); bottom.BackColor = Color.Red; Controls.Add(bottom); bottom.BringToFront(); AssignEventHandlers(bottom); } bottom.Left = start.X; bottom.Top = stop.Y; bottom.Height = 2; bottom.Width = stop.X - start.X; bottom.Show(); TopMost = true; Show(); } } } private void MouseUpHandler(bool canceled = false, bool rightMouseButton = false) { lock (bmScreenLock) { MouseDownMoveHandler(); customScreenCaptureInProgress = 0; if (!canceled && bmScreen != null && left != null && top != null && right != null && bottom != null && stop.X - start.X > 0 && stop.Y - start.Y > 0) { start = PointInStandardDPI(start); stop = PointInStandardDPI(stop); Bitmap bm = new(stop.X - start.X, stop.Y - start.Y); Graphics g = Graphics.FromImage(bm); try { g.DrawImage(bmScreen, 0, 0, new Rectangle(start, bm.Size), GraphicsUnit.Pixel); g.DrawRectangle(Pens.DarkOrange, 0, 0, stop.X - start.X - 1, stop.Y - start.Y - 1); foreach (FocusArea f in focusZone) { RectangleF rec = RectangleFInStandardDPI(f.Rec); rec.X -= start.X; rec.Y -= start.Y; g.DrawEllipse(new Pen(f.Color, 2), rec); } if (rightMouseButton) { try { string tempFile = Path.GetTempPath() + Path.GetFileNameWithoutExtension(Path.GetTempFileName()) + ".png"; SendLog(tempFile); bm.Save(tempFile, ImageFormat.Png); lastClipboardEventTime = GetTick(); ClipboardMMHelper.SetText(tempFile); } catch (IOException ioException) { SendLog("IO!Exception!: " + ioException.Message); } catch (ArgumentNullException argException) { SendLog("ArgNull!Exception!: " + argException.Message); } catch (ExternalException internalException) { SendLog("Internal!Exception!: " + internalException.Message); } } else { lastClipboardEventTime = GetTick() - 10000; ClipboardMMHelper.SetImage(bm); } } finally { g.Dispose(); bm.Dispose(); bmScreen.Dispose(); bmScreen = null; } } ShowInTaskbar = false; Hide(); Controls.Clear(); Opacity = 0.11; left?.Dispose(); top?.Dispose(); right?.Dispose(); bottom?.Dispose(); picScr?.Dispose(); left = top = right = bottom = picScr = null; SendLog("Screen capture ended.\r\n**************************************************"); } } private void FormHelper_FormClosed(object sender, FormClosedEventArgs e) { ClipboardMMHelper.UnhookClipboard(); } private void FormHelper_LocationChanged(object sender, EventArgs e) { lock (bmScreenLock) { if (picScr == null && !timerHelper.Enabled) { Opacity = 0.11; timerHelper.Start(); } } } private void FormHelper_KeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Escape) { MouseUpHandler(true); } if (focus1.IsEmpty) { focus1 = stop; } } private void FormHelper_KeyUp(object sender, KeyEventArgs e) { focus2 = stop; float x = Math.Min(focus1.X, focus2.X); float y = Math.Min(focus1.Y, focus2.Y); float w = Math.Abs(focus1.X - focus2.X); float h = Math.Abs(focus1.Y - focus2.Y); x -= 0.25F * w; y -= 0.25F * h; w *= 1.5F; h *= 1.5F; focusZone.Add(new FocusArea(new RectangleF(x, y, w, h), e.KeyCode == Keys.B ? Color.Blue : e.KeyCode == Keys.G ? Color.Green : Color.Red)); focus1 = focus2 = Point.Empty; } private void FormHelper_FormClosing(object sender, FormClosingEventArgs e) { if (e.CloseReason == CloseReason.UserClosing) { e.Cancel = true; } } private void FormHelper_MouseDown(object sender, MouseEventArgs e) { SendLog("Screen capture Mouse down."); MouseMoveHandler(); customScreenCaptureInProgress = 2; } private void FormHelper_MouseMove(object sender, MouseEventArgs e) { if (customScreenCaptureInProgress == 1) { MouseMoveHandler(); } else if (customScreenCaptureInProgress == 2) { MouseDownMoveHandler(); } } private void FormHelper_MouseUp(object sender, MouseEventArgs e) { MouseUpHandler(false, e.Button == MouseButtons.Right); } private Point PointInStandardDPI(Point p) { // Since the process is DPI awareness, just log and return. // TODO: Test in Win8/7/XP. SendLog(string.Format(CultureInfo.CurrentCulture, "this.Width={0}, this.Height={1}, bmScreen.Width={2}, bmScreen.Height={3}, x={4}, y={5}", Width, Height, bmScreen.Width, bmScreen.Height, p.X, p.Y)); return p; } private RectangleF RectangleFInStandardDPI(RectangleF r) { // Since the process is DPI awareness, just return. return r; } } } ================================================ FILE: PowerToys/src/modules/MouseWithoutBorders/ModuleInterface/dllmain.cpp ================================================ #include "pch.h" #include #include #include #include "trace.h" #include "generateSecurityDescriptor.h" #include #include #include #include #include #include #include HINSTANCE g_hInst_MouseWithoutBorders = 0; BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID /*lpReserved*/) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: g_hInst_MouseWithoutBorders = hModule; Trace::MouseWithoutBorders::RegisterProvider(); break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: break; case DLL_PROCESS_DETACH: Trace::MouseWithoutBorders::UnregisterProvider(); break; } return TRUE; } bool GetUserSid(const wchar_t* username, PSID& sid) { DWORD sidSize = 0; DWORD domainNameSize = 0; SID_NAME_USE sidNameUse; LookupAccountName(nullptr, username, nullptr, &sidSize, nullptr, &domainNameSize, &sidNameUse); if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { Logger::error("Failed to get buffer sizes"); return false; } sid = LocalAlloc(LPTR, sidSize); LPWSTR domainName = static_cast(LocalAlloc(LPTR, domainNameSize * sizeof(wchar_t))); if (!LookupAccountNameW(nullptr, username, sid, &sidSize, domainName, &domainNameSize, &sidNameUse)) { Logger::error("Failed to lookup account name"); LocalFree(sid); LocalFree(domainName); return false; } LocalFree(domainName); return true; } std::wstring GetCurrentUserSid() { wchar_t username[UNLEN + 1]; DWORD usernameSize = UNLEN + 1; std::wstring result; if (!GetUserNameW(username, &usernameSize)) { Logger::error("Failed to get the current user name"); return result; } PSID sid; if (GetUserSid(username, sid)) { LPWSTR sidString; if (ConvertSidToStringSid(sid, &sidString)) { result = sidString; LocalFree(sidString); } LocalFree(sid); } else { Logger::error(L"Failed to get SID for user \""); } return result; } std::wstring escapeDoubleQuotes(const std::wstring& input) { std::wstring output; output.reserve(input.size()); for (const wchar_t& ch : input) { if (ch == L'"') { output += L'\\'; } output += ch; } return output; } const static wchar_t* MODULE_NAME = L"MouseWithoutBorders"; const static wchar_t* MODULE_DESC = L"A module to move your mouse across computers."; const static wchar_t* SERVICE_NAME = L"PowerToys.MWB.Service"; const static std::wstring_view USE_SERVICE_PROPERTY_NAME = L"UseService"; class MouseWithoutBorders : public PowertoyModuleIface { std::wstring app_name; std::wstring app_key; private: bool m_enabled = false; bool run_in_service_mode = false; PROCESS_INFORMATION p_info = {}; bool is_enabled_by_default() const override { return false; } bool is_process_running() { return WaitForSingleObject(p_info.hProcess, 0) == WAIT_TIMEOUT; } void launch_process() { Logger::trace(L"Launching PowerToys MouseWithoutBorders process"); const std::wstring application_path = L"PowerToys.MouseWithoutBorders.exe"; STARTUPINFO info = { sizeof(info) }; std::wstring full_command_path = application_path; if (run_in_service_mode) { full_command_path += L" "; full_command_path += USE_SERVICE_PROPERTY_NAME; } if (!CreateProcessW(application_path.c_str(), full_command_path.data(), nullptr, nullptr, true, {}, nullptr, nullptr, &info, &p_info)) { DWORD error = GetLastError(); std::wstring message = L"PowerToys MouseWithoutBorders failed to start with error: "; message += std::to_wstring(error); Logger::error(message); } Trace::MouseWithoutBorders::Activate(); } void unregister_service() { SC_HANDLE schSCManager = OpenSCManagerW(nullptr, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS); SC_HANDLE hService = OpenServiceW(schSCManager, SERVICE_NAME, SERVICE_STOP | DELETE); if (!hService) { Logger::error("Failed to open MWB service"); return; } SERVICE_STATUS ss; if (ControlService(hService, SERVICE_CONTROL_STOP, &ss)) { Sleep(1000); for (int i = 0; i < 5; ++i) { while (QueryServiceStatus(hService, &ss)) { if (ss.dwCurrentState == SERVICE_STOP_PENDING) { Sleep(1000); } else { goto outer; } } } } outer: BOOL deleteResult = DeleteService(hService); CloseServiceHandle(hService); if (!deleteResult) { Logger::error("Failed to delete MWB service"); return; } Trace::MouseWithoutBorders::ToggleServiceRegistration(false); } void register_service() { SC_HANDLE schSCManager = OpenSCManagerW(nullptr, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS); if (schSCManager == nullptr) { Logger::error(L"Couldn't open sc manager"); return; } const auto closeSCM = wil::scope_exit([&] { CloseServiceHandle(schSCManager); }); SC_HANDLE schService = OpenServiceW(schSCManager, SERVICE_NAME, SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG); const auto closeService = wil::scope_exit([&] { CloseServiceHandle(schService); }); const auto servicePath = get_module_folderpath(g_hInst_MouseWithoutBorders) + L"/PowerToys.MouseWithoutBordersService.exe"; // Check that the service doesn't exist already and is not disabled DWORD bytesNeeded; LPQUERY_SERVICE_CONFIGW pServiceConfig = nullptr; if (!QueryServiceConfigW(schService, nullptr, 0, &bytesNeeded)) { if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { pServiceConfig = static_cast(LocalAlloc(LMEM_FIXED, bytesNeeded)); if (!QueryServiceConfigW(schService, pServiceConfig, bytesNeeded, &bytesNeeded)) { LocalFree(pServiceConfig); pServiceConfig = nullptr; CloseServiceHandle(schService); } } } // Pass local app data of the current user to the service wil::unique_cotaskmem_string cLocalAppPath; winrt::check_hresult(SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &cLocalAppPath)); std::wstring localAppPath{ cLocalAppPath.get() }; std::wstring binaryWithArgsPath = L"\""; binaryWithArgsPath += servicePath; binaryWithArgsPath += L"\" "; binaryWithArgsPath += escapeDoubleQuotes(localAppPath); bool alreadyRegistered = false; bool isServicePathCorrect = true; if (pServiceConfig) { std::wstring_view existingServicePath{ pServiceConfig->lpBinaryPathName }; alreadyRegistered = true; isServicePathCorrect = (existingServicePath == binaryWithArgsPath); if (isServicePathCorrect) { Logger::warn(L"The service path is not correct. Current: {} Expected: {}", existingServicePath, binaryWithArgsPath); } if (alreadyRegistered && pServiceConfig->dwStartType == SERVICE_DISABLED) { if (!ChangeServiceConfigW(schService, SERVICE_NO_CHANGE, SERVICE_DEMAND_START, SERVICE_NO_CHANGE, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr)) { const bool markedForDelete = GetLastError() == ERROR_SERVICE_MARKED_FOR_DELETE; // We cannot remove the mark for deletion from the service. if (markedForDelete) { alreadyRegistered = false; CloseServiceHandle(schService); } } } LocalFree(pServiceConfig); } if (alreadyRegistered) { if (!isServicePathCorrect) { if (!ChangeServiceConfigW(schService, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, binaryWithArgsPath.c_str(), nullptr, nullptr, nullptr, nullptr, nullptr, nullptr)) { Logger::error(L"Failed to update the service's path. ERROR: {}", GetLastError()); } else { Logger::info(L"Updated the service's path."); } } return; } schService = CreateServiceW( schSCManager, SERVICE_NAME, SERVICE_NAME, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, binaryWithArgsPath.c_str(), nullptr, nullptr, nullptr, nullptr, nullptr); if (schService == nullptr) { Logger::error(L"Failed to create service"); return; } // Set up the security descriptor to allow non-elevated users to start the service PSECURITY_DESCRIPTOR pSD = nullptr; ULONG szSD = 0; std::wstring securityDescriptor = generateSecurityDescriptor(GetCurrentUserSid()); if (!ConvertStringSecurityDescriptorToSecurityDescriptorW( securityDescriptor.c_str(), SDDL_REVISION_1, &pSD, &szSD)) { Logger::error(L"Failed to convert security descriptor string"); CloseServiceHandle(schService); return; } if (!SetServiceObjectSecurity(schService, DACL_SECURITY_INFORMATION, pSD)) { Logger::error("Failed to set service object security"); } LocalFree(pSD); CloseServiceHandle(schService); } void update_state_from_settings(const PowerToysSettings::PowerToyValues& values) { const bool new_run_in_service_mode = values.get_bool_value(USE_SERVICE_PROPERTY_NAME).value_or(false); if (new_run_in_service_mode != run_in_service_mode) { run_in_service_mode = new_run_in_service_mode; shutdown_processes(); if (new_run_in_service_mode) { register_service(); } // Wait until Settings -> MWB IPC Shutdown() call is completed else { const auto ps = getProcessHandlesByName(L"PowerToys.MouseWithoutBorders.exe", PROCESS_QUERY_LIMITED_INFORMATION); for (const auto& p : ps) { DWORD status = STILL_ACTIVE; do { GetExitCodeProcess(p.get(), &status); } while (status == STILL_ACTIVE); } Sleep(1000); } if (m_enabled) { launch_process(); } Trace::MouseWithoutBorders::ToggleServiceRegistration(new_run_in_service_mode); } } public: MouseWithoutBorders() { app_name = L"MouseWithoutBorders"; app_key = app_name; std::filesystem::path logFilePath(PTSettingsHelper::get_module_save_folder_location(app_key)); logFilePath.append(LogSettings::mouseWithoutBordersLogPath); Logger::init(LogSettings::mouseWithoutBordersLoggerName, logFilePath.wstring(), PTSettingsHelper::get_log_settings_file_location()); try { PowerToysSettings::PowerToyValues values = PowerToysSettings::PowerToyValues::load_from_settings_file(MODULE_NAME); update_state_from_settings(values); } catch (std::exception&) { // Initial start/ } }; // Return the configured status for the gpo policy for the module virtual powertoys_gpo::gpo_rule_configured_t gpo_policy_enabled_configuration() override { return powertoys_gpo::getConfiguredMouseWithoutBordersEnabledValue(); } void shutdown_process(HANDLE handle) { auto cb = [](HWND hwnd, LPARAM lParam) -> BOOL { DWORD processId; GetWindowThreadProcessId(hwnd, &processId); if (processId == lParam) { PostMessageW(hwnd, WM_CLOSE, 0, 0); return FALSE; } return TRUE; }; DWORD processId = GetProcessId(handle); EnumWindows(cb, processId); DWORD waitResult = WaitForSingleObject(handle, 3000); if (waitResult != WAIT_OBJECT_0) { TerminateProcess(handle, 0); } } void shutdown_processes() { const auto services = getProcessHandlesByName(L"PowerToys.MouseWithoutBordersService.exe", PROCESS_TERMINATE); for (const auto& svc : services) TerminateProcess(svc.get(), 0); wil::unique_process_handle s; std::array processes_names = { L"PowerToys.MouseWithoutBorders.exe", L"PowerToys.MouseWithoutBordersHelper.exe" }; for (const auto process : processes_names) { const auto apps = getProcessHandlesByName(process, PROCESS_TERMINATE); for (const auto& app : apps) shutdown_process(app.get()); } } virtual void destroy() override { shutdown_processes(); TerminateProcess(p_info.hProcess, 1); delete this; } virtual const wchar_t* get_name() override { return MODULE_NAME; } virtual bool get_config(wchar_t* buffer, int* buffer_size) override { HINSTANCE hinstance = reinterpret_cast(&__ImageBase); PowerToysSettings::Settings settings(hinstance, get_name()); settings.set_description(MODULE_DESC); return settings.serialize_to_buffer(buffer, buffer_size); } virtual const wchar_t* get_key() override { return app_key.c_str(); } virtual void set_config(const wchar_t* config) override { try { // Parse the input JSON string. PowerToysSettings::PowerToyValues values = PowerToysSettings::PowerToyValues::from_json_string(config, get_key()); update_state_from_settings(values); // If you don't need to do any custom processing of the settings, proceed // to persists the values. values.save_to_settings_file(); } catch (std::exception&) { // Improper JSON. } } virtual void enable() { Trace::MouseWithoutBorders::Enable(true); launch_process(); m_enabled = true; }; virtual void disable() { if (m_enabled) { Trace::MouseWithoutBorders::Enable(false); Logger::trace(L"Disabling MouseWithoutBorders..."); Logger::trace(L"Signaled exit event for PowerToys MouseWithoutBorders."); TerminateProcess(p_info.hProcess, 1); shutdown_processes(); CloseHandle(p_info.hProcess); } m_enabled = false; } virtual bool is_enabled() override { return m_enabled; } void launch_add_firewall_process() { Logger::trace(L"Starting Process to add firewall rule"); std::wstring executable_path = get_module_folderpath(); executable_path.append(L"\\PowerToys.MouseWithoutBorders.exe"); std::wstring executable_args = L""; executable_args.append(L"/S /c \""); executable_args.append(L"chcp 65001>nul"); executable_args.append(L" & echo 为 PowerToys.MouseWithoutBorders.exe 删除所有原有的入站规则"); executable_args.append(L" & netsh advfirewall firewall delete rule dir=in name=all program=\""); executable_args.append(executable_path); executable_args.append(L"\" & echo 为 PowerToys.MouseWithoutBorders.exe 添加一条允许通过的入站规则"); executable_args.append(L" & netsh advfirewall firewall add rule name=\"PowerToys.MouseWithoutBorders\" dir=in action=allow program=\""); executable_args.append(executable_path); executable_args.append(L"\" enable=yes remoteip=LocalSubnet profile=any protocol=tcp & echo 现在可以关掉这个窗口了 & pause\""); SHELLEXECUTEINFOW sei{ sizeof(sei) }; sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI }; sei.lpFile = L"cmd.exe"; sei.nShow = SW_SHOWNORMAL; sei.lpParameters = executable_args.data(); sei.lpVerb = L"runas"; if (ShellExecuteExW(&sei)) { Logger::trace("Successfully started the firewall rule adding process"); } else { Logger::error(L"The firewall rule adding process failed to start. {}", get_last_error_or_default(GetLastError())); } } virtual void call_custom_action(const wchar_t* action) override { try { PowerToysSettings::CustomActionObject action_object = PowerToysSettings::CustomActionObject::from_json_string(action); if (action_object.get_name() == L"add_firewall") { launch_add_firewall_process(); Trace::MouseWithoutBorders::AddFirewallRule(); } else if (action_object.get_name() == L"uninstall_service") { unregister_service(); } } catch (std::exception&) { Logger::error(L"Failed to parse action. {}", action); } } }; extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create() { return new MouseWithoutBorders(); } ================================================ FILE: PowerToys/src/modules/NewPlus/NewShellExtensionContextMenu/NewShellExtensionContextMenu.vcxproj ================================================ 17.0 Win32Proj {8acb33d9-c95b-47d4-8363-9731ee0930a0} NewPlusShellExtension 10.0.20348.0 NewPlus.ShellExtension DynamicLibrary true v143 Unicode DynamicLibrary false v143 true Unicode .dll ..\..\..\..\$(Platform)\$(Configuration)\WinUI3Apps\ PowerToys.NewPlus.ShellExtension $(SolutionDir)$(Platform)\$(Configuration)\TemporaryBuild\obj\$(ProjectName)\ ..\..\..\..\$(Platform)\$(Configuration)\WinUI3Apps\ PowerToys.NewPlus.ShellExtension $(SolutionDir)$(Platform)\$(Configuration)\TemporaryBuild\obj\$(ProjectName)\ Level3 true WIN32;_DEBUG;NEWPLUSCONTEXTMENU_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) true Use pch.h stdcpplatest ..\..\..\common\Telemetry;..\..\;..\..\..\;%(AdditionalIncludeDirectories) Windows true false dll.def runtimeobject.lib;$(CoreLibraryDependencies) del $(OutDir)\NewPlusPackage.msix /q MakeAppx.exe pack /d . /p $(OutDir)NewPlusPackage.msix /nv Level3 true true true WIN32;NDEBUG;NEWPLUSCONTEXTMENU_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) true Use pch.h stdcpplatest ..\..\..\common\Telemetry;..\..\;..\..\..\;%(AdditionalIncludeDirectories) Windows true true true false dll.def runtimeobject.lib;$(CoreLibraryDependencies) del $(OutDir)\NewPlusPackage.msix /q MakeAppx.exe pack /d . /p $(OutDir)NewPlusPackage.msix /nv Create true Document $(OutDir)\Assets\NewPlus\Templates true Document $(OutDir)\Assets\NewPlus\Templates\示例文件夹 true Document $(OutDir)\Assets\NewPlus\Templates\示例文件夹 {d9b8fc84-322a-4f9f-bbb9-20915c47ddfd} {6955446d-23f7-4023-9bb3-8657f904af99} {8f021b46-362b-485c-bfba-ccf83e820cbd} {98537082-0fdb-40de-abd8-0dc5a4269bab} {cc6e41ac-8174-4e8a-8d22-85dd7f4851df} PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest Designer This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. ================================================ FILE: PowerToys/src/modules/NewPlus/NewShellExtensionContextMenu/NewShellExtensionContextMenu.vcxproj.filters ================================================  Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Generated Files Header Files Asset Files Asset Files Asset Files Asset Files Asset Files Asset Files Asset Files Asset Files Asset Files Asset Files Asset Files Asset Files Resource Files Resource Files Source Files Source Files Asset Files Asset Files Asset Files Asset Files Asset Files Asset Files Asset Files Asset Files Asset Files Asset Files Asset Files {82bf7d46-1201-4fc2-99e0-6c1f48f97f1f} {0c915b9c-0e6a-4d16-8620-507a10fc29c9} {0c64a1a0-1f9e-4663-8649-454da469d15c} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms {4f319851-7d86-4180-b3a3-fd497a320c34} {a998f674-d126-488b-8457-7673fe986f94} {b442cb0f-9f62-46e8-b269-fefa8ceacb21} {e7904759-7b6c-4609-9c6d-e3eae3cb866c} Generated Files ================================================ FILE: PowerToys/src/modules/NewPlus/NewShellExtensionContextMenu/TemplateExamples/模板文件夹里的一切都会出现在新建+菜单中.txt ================================================ 访问 https://aka.ms/PowerToysOverview_NewPlus 了解如何使用新建+ ================================================ FILE: PowerToys/src/modules/NewPlus/NewShellExtensionContextMenu/TemplateExamples/示例文件夹/另一个示例文本文件.txt ================================================ 另一个示例文本文件 ================================================ FILE: PowerToys/src/modules/NewPlus/NewShellExtensionContextMenu/TemplateExamples/示例文件夹/示例文本文件.txt ================================================ 示例文本文件 ================================================ FILE: PowerToys/src/modules/NewPlus/NewShellExtensionContextMenu/resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 新建+ The main context menu item that users click on. This should be localized to match New in Windows. e.g. Danish it would become Ny+ 打开模板文件夹 The menu item in the context menu that enables user to open the folder that contains their templates. 模板 Default subfolder name where templates are stored. ================================================ FILE: PowerToys/src/modules/NewPlus/NewShellExtensionContextMenu.win10/resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 新建+ The main context menu item that users click on. This should be localized to match New in Windows. e.g. Danish it would become Ny+ 打开模板文件夹 The menu item in the context menu that enables user to open the folder that contains their templates. 模板 Default subfolder name where templates are stored. ================================================ FILE: PowerToys/src/modules/PowerOCR/PowerOCR/Properties/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 关闭 关闭 (Esc) (Esc) indicates the keyboard shortcut 单行 单行 (S) (S) indicates the keyboard shortcut 表格 表格 (T) (T) indicates the keyboard shortcut 当前语言 设置 ================================================ FILE: PowerToys/src/modules/ShortcutGuide/ShortcutGuide/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Opacity of the Shortcut Guide's overlay background (%) Choose Shortcut Guide overlay color Light Dark System default app mode Shows a help overlay with Windows shortcuts when the Windows key is pressed. Shortcut Guide 无动作 还原 右贴靠 左贴靠 右上贴靠 左上贴靠 右下贴靠 左下贴靠 最小化 最大化 ================================================ FILE: PowerToys/src/modules/Workspaces/WorkspacesEditor/Models/Project.cs ================================================ // Copyright (c) Microsoft Corporation // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Globalization; using System.Linq; using System.Text.Json.Serialization; using System.Threading.Tasks; using System.Windows.Media.Imaging; using ManagedCommon; using WorkspacesEditor.Data; using WorkspacesEditor.Utils; namespace WorkspacesEditor.Models { public class Project : INotifyPropertyChanged { [JsonIgnore] public string EditorWindowTitle { get; set; } public string Id { get; private set; } private string _name; public string Name { get => _name; set { _name = value; OnPropertyChanged(new PropertyChangedEventArgs(nameof(Name))); OnPropertyChanged(new PropertyChangedEventArgs(nameof(CanBeSaved))); } } public long CreationTime { get; } // in seconds public long LastLaunchedTime { get; } // in seconds public bool IsShortcutNeeded { get; set; } public bool MoveExistingWindows { get; set; } public string LastLaunched { get { string lastLaunched = WorkspacesEditor.Properties.Resources.LastLaunched + ":"; if (LastLaunchedTime == 0) { return lastLaunched + WorkspacesEditor.Properties.Resources.Never; } const int SECOND = 1; const int MINUTE = 60 * SECOND; const int HOUR = 60 * MINUTE; const int DAY = 24 * HOUR; const int MONTH = 30 * DAY; DateTime lastLaunchDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddSeconds(LastLaunchedTime); TimeSpan ts = DateTime.UtcNow - lastLaunchDateTime; double delta = Math.Abs(ts.TotalSeconds); if (delta < 1 * MINUTE) { return lastLaunched + WorkspacesEditor.Properties.Resources.Recently; } if (delta < 2 * MINUTE) { return lastLaunched + WorkspacesEditor.Properties.Resources.OneMinuteAgo; } if (delta < 45 * MINUTE) { return lastLaunched + ts.Minutes + " " + WorkspacesEditor.Properties.Resources.MinutesAgo; } if (delta < 90 * MINUTE) { return lastLaunched + WorkspacesEditor.Properties.Resources.OneHourAgo; } if (delta < 24 * HOUR) { return lastLaunched + ts.Hours + " " + WorkspacesEditor.Properties.Resources.HoursAgo; } if (delta < 48 * HOUR) { return lastLaunched + WorkspacesEditor.Properties.Resources.Yesterday; } if (delta < 30 * DAY) { return lastLaunched + ts.Days + " " + WorkspacesEditor.Properties.Resources.DaysAgo; } if (delta < 12 * MONTH) { int months = Convert.ToInt32(Math.Floor((double)ts.Days / 30)); return lastLaunched + (months <= 1 ? WorkspacesEditor.Properties.Resources.OneMonthAgo : months + " " + WorkspacesEditor.Properties.Resources.MonthsAgo); } else { int years = Convert.ToInt32(Math.Floor((double)ts.Days / 365)); return lastLaunched + (years <= 1 ? WorkspacesEditor.Properties.Resources.OneYearAgo : years + " " + WorkspacesEditor.Properties.Resources.YearsAgo); } } } public bool CanBeSaved => Name.Length > 0 && Applications.Count > 0; private bool _isRevertEnabled; public bool IsRevertEnabled { get => _isRevertEnabled; set { if (_isRevertEnabled != value) { _isRevertEnabled = value; OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsRevertEnabled))); } } } private bool _isPopupVisible; [JsonIgnore] public bool IsPopupVisible { get => _isPopupVisible; set { _isPopupVisible = value; OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsPopupVisible))); } } public List Applications { get; set; } public List ApplicationsListed { get { List applicationsListed = []; ILookup apps = Applications.Where(x => !x.Minimized).ToLookup(x => x.MonitorSetup); foreach (IGrouping appItem in apps.OrderBy(x => x.Key.MonitorDpiUnawareBounds.Left).ThenBy(x => x.Key.MonitorDpiUnawareBounds.Top)) { MonitorHeaderRow headerRow = new() { MonitorName = "显示器 " + appItem.Key.MonitorNumber, SelectString = Properties.Resources.SelectAllAppsOnMonitor.Replace("{0}", appItem.Key.MonitorInfo) }; applicationsListed.Add(headerRow); foreach (Application app in appItem) { applicationsListed.Add(app); } } IEnumerable minimizedApps = Applications.Where(x => x.Minimized); if (minimizedApps.Any()) { MonitorHeaderRow headerRow = new() { MonitorName = Properties.Resources.Minimized_Apps, SelectString = Properties.Resources.SelectAllMinimizedApps }; applicationsListed.Add(headerRow); foreach (Application app in minimizedApps) { applicationsListed.Add(app); } } return applicationsListed; } } [JsonIgnore] public string AppsCountString { get { int count = Applications.Count; return count.ToString(CultureInfo.InvariantCulture) + " " + (count == 1 ? Properties.Resources.App : Properties.Resources.Apps); } } public List Monitors { get; } public bool IsPositionChangedManually { get; set; } // telemetry private BitmapImage _previewIcons; private BitmapImage _previewImage; private double _previewImageWidth; public Project(Project selectedProject) { Id = selectedProject.Id; Name = selectedProject.Name; PreviewIcons = selectedProject.PreviewIcons; PreviewImage = selectedProject.PreviewImage; IsShortcutNeeded = selectedProject.IsShortcutNeeded; MoveExistingWindows = selectedProject.MoveExistingWindows; int screenIndex = 1; Monitors = []; foreach (MonitorSetup item in selectedProject.Monitors.OrderBy(x => x.MonitorDpiAwareBounds.Left).ThenBy(x => x.MonitorDpiAwareBounds.Top)) { Monitors.Add(item); screenIndex++; } Applications = []; foreach (Application item in selectedProject.Applications) { Application newApp = new(item); newApp.Parent = this; newApp.InitializationFinished(); Applications.Add(newApp); } } public Project(ProjectData.ProjectWrapper project) { Id = project.Id; Name = project.Name; CreationTime = project.CreationTime; LastLaunchedTime = project.LastLaunchedTime; IsShortcutNeeded = project.IsShortcutNeeded; MoveExistingWindows = project.MoveExistingWindows; Monitors = []; Applications = []; foreach (ProjectData.ApplicationWrapper app in project.Applications) { Models.Application newApp = new() { Id = string.IsNullOrEmpty(app.Id) ? $"{{{Guid.NewGuid()}}}" : app.Id, AppName = app.Application, AppPath = app.ApplicationPath, AppTitle = app.Title, PwaAppId = string.IsNullOrEmpty(app.PwaAppId) ? string.Empty : app.PwaAppId, PackageFullName = app.PackageFullName, AppUserModelId = app.AppUserModelId, Parent = this, CommandLineArguments = app.CommandLineArguments, IsElevated = app.IsElevated, CanLaunchElevated = app.CanLaunchElevated, Maximized = app.Maximized, Minimized = app.Minimized, IsNotFound = false, Position = new Models.Application.WindowPosition() { Height = app.Position.Height, Width = app.Position.Width, X = app.Position.X, Y = app.Position.Y, }, MonitorNumber = app.Monitor, }; newApp.InitializationFinished(); Applications.Add(newApp); } foreach (ProjectData.MonitorConfigurationWrapper monitor in project.MonitorConfiguration) { System.Windows.Rect dpiAware = new(monitor.MonitorRectDpiAware.Left, monitor.MonitorRectDpiAware.Top, monitor.MonitorRectDpiAware.Width, monitor.MonitorRectDpiAware.Height); System.Windows.Rect dpiUnaware = new(monitor.MonitorRectDpiUnaware.Left, monitor.MonitorRectDpiUnaware.Top, monitor.MonitorRectDpiUnaware.Width, monitor.MonitorRectDpiUnaware.Height); Monitors.Add(new MonitorSetup(monitor.Id, monitor.InstanceId, monitor.MonitorNumber, monitor.Dpi, dpiAware, dpiUnaware)); } } public BitmapImage PreviewIcons { get => _previewIcons; set { _previewIcons = value; OnPropertyChanged(new PropertyChangedEventArgs(nameof(PreviewIcons))); } } public BitmapImage PreviewImage { get => _previewImage; set { _previewImage = value; OnPropertyChanged(new PropertyChangedEventArgs(nameof(PreviewImage))); } } public double PreviewImageWidth { get => _previewImageWidth; set { _previewImageWidth = value; OnPropertyChanged(new PropertyChangedEventArgs(nameof(PreviewImageWidth))); } } public event PropertyChangedEventHandler PropertyChanged; public void OnPropertyChanged(PropertyChangedEventArgs e) { PropertyChanged?.Invoke(this, e); } public async void Initialize(Theme currentTheme) { PreviewIcons = await Task.Run(() => DrawHelper.DrawPreviewIcons(this)); Rectangle commonBounds = GetCommonBounds(); PreviewImage = await Task.Run(() => DrawHelper.DrawPreview(this, commonBounds, currentTheme)); PreviewImageWidth = commonBounds.Width / (commonBounds.Height * 1.2 / 200); } private Rectangle GetCommonBounds() { double minX = Monitors.First().MonitorDpiAwareBounds.Left; double minY = Monitors.First().MonitorDpiAwareBounds.Top; double maxX = Monitors.First().MonitorDpiAwareBounds.Right; double maxY = Monitors.First().MonitorDpiAwareBounds.Bottom; for (int monitorIndex = 1; monitorIndex < Monitors.Count; monitorIndex++) { Monitor monitor = Monitors[monitorIndex]; minX = Math.Min(minX, monitor.MonitorDpiAwareBounds.Left); minY = Math.Min(minY, monitor.MonitorDpiAwareBounds.Top); maxX = Math.Max(maxX, monitor.MonitorDpiAwareBounds.Right); maxY = Math.Max(maxY, monitor.MonitorDpiAwareBounds.Bottom); } return new Rectangle((int)minX, (int)minY, (int)(maxX - minX), (int)(maxY - minY)); } public void UpdateAfterLaunchAndEdit(Project projectBeforeLaunch) { Id = projectBeforeLaunch.Id; Name = projectBeforeLaunch.Name; IsRevertEnabled = true; MoveExistingWindows = projectBeforeLaunch.MoveExistingWindows; foreach (Application app in Applications) { var sameAppBefore = projectBeforeLaunch.Applications.Where(x => x.Id.Equals(app.Id, StringComparison.OrdinalIgnoreCase)); if (sameAppBefore.Any()) { var appBefore = sameAppBefore.FirstOrDefault(); app.CommandLineArguments = appBefore.CommandLineArguments; app.IsElevated = appBefore.IsElevated; } } } internal void CloseExpanders() { foreach (Application app in Applications) { app.IsExpanded = false; } } internal MonitorSetup GetMonitorForApp(Application app) { MonitorSetup monitorSetup = Monitors.Where(x => x.MonitorNumber == app.MonitorNumber).FirstOrDefault(); if (monitorSetup == null) { // monitors changed: try to determine monitor id based on middle point int middleX = app.Position.X + (app.Position.Width / 2); int middleY = app.Position.Y + (app.Position.Height / 2); MonitorSetup monitorCandidate = Monitors.Where(x => (x.MonitorDpiUnawareBounds.Left < middleX) && (x.MonitorDpiUnawareBounds.Right > middleX) && (x.MonitorDpiUnawareBounds.Top < middleY) && (x.MonitorDpiUnawareBounds.Bottom > middleY)).FirstOrDefault(); if (monitorCandidate != null) { app.MonitorNumber = monitorCandidate.MonitorNumber; return monitorCandidate; } else { // monitors and even the app's area unknown, set the main monitor (which is closer to (0,0)) as the app's monitor monitorCandidate = Monitors.OrderBy(x => Math.Abs(x.MonitorDpiUnawareBounds.Left) + Math.Abs(x.MonitorDpiUnawareBounds.Top)).FirstOrDefault(); if (monitorCandidate != null) { app.MonitorNumber = monitorCandidate.MonitorNumber; return monitorCandidate; } else { // no monitors defined at all. Logger.LogError($"Wrong workspace setup. No monitors defined for the workspace: {Name}."); return null; } } } return monitorSetup; } } } ================================================ FILE: PowerToys/src/modules/Workspaces/WorkspacesEditor/Properties/Resources.resx ================================================ text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 取消删除 管理员权限 个应用 个应用 App name 没用到 Are you sure? 没用到 Are you sure you want to delete this Workspace? 没用到 参数 Arguments 取消 命令行参数 创建时间 创建工作区 在桌面上创建快捷方式 自定义 几天前 工作区 删除 删除选中应用 Delete Workspace dialog. 没用到 编辑 编辑工作区 opened 没用到 无法解析工作区数据。 高度 小时前 上次启动时间 启动 管理员权限 编辑 Launch args 没用到 左侧坐标 the left x coordinate 工作区编辑器 最大化 最小化 最小化到任务栏 分钟前 个月前 若应用已打开则仅移动窗口 名称 从未启动 New Workspace 没用到 未找到符合条件的工作区。 找不到该应用 尚未创建工作区。 1 小时前 1 分钟前 1 个月前 1 秒前 1 年前 Pin Workspaces to taskbar 没用到 工作区名称 工作区 刚才 撤销 保存工作区 搜索 搜索工作区和应用 秒前 选择所有位于 {0} 的应用 选择所有最小化的应用 Select all apps in Workspace 没用到 请调整好您的窗口位置,然后点击 "捕捉" 按钮。 快照创建器 排序 捕捉 顶部坐标 the top y coordinate 宽度 窗口位置 Write arguments here 没用到 年前 昨天 ================================================ FILE: PowerToys/src/modules/Workspaces/WorkspacesLauncher/Resource.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 文件 {0} 为空。 无法打开文件 {0}。 命令行参数不正确。 文件 {0} 不正确。 工作区 Name of the module 找不到工作区 {0}。 ================================================ FILE: PowerToys/src/modules/Workspaces/WorkspacesLauncherUI/Properties/Resources.resx ================================================ text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 取消启动 知道了 您的工作区正在启动,请稍等... ================================================ FILE: PowerToys/src/modules/Workspaces/WorkspacesSnapshotTool/Resource.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 工作区 工作区 检测到一个以管理员权限运行的软件,工作区无法控制它。 不再显示 了解更多 ================================================ FILE: PowerToys/src/modules/ZoomIt/ZoomIt/ZoomIt.rc ================================================ // Microsoft Visual C++ generated resource script. // #include "resource.h" // version.h and branding.h are different in the Sysinternals repository, // keep the includes as such, here. // From $(MSBuildThisFileDirectory)..\..\..\common\version #include "version.h" // From $(MSBuildThisFileDirectory)PowerToys #include "branding.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include "winres.h" ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // Chinese (Simplified) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED #pragma code_page(936) #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE BEGIN "resource.h\0" END 2 TEXTINCLUDE BEGIN "#include ""winres.h""\r\n" "\0" END 3 TEXTINCLUDE BEGIN "#include ""binres.rc""\0" END #endif // APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Cursor // NULLCURSOR CURSOR "cursor1.cur" ///////////////////////////////////////////////////////////////////////////// // // Icon // // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. APPICON ICON "appicon.ico" ///////////////////////////////////////////////////////////////////////////// // // Version // VS_VERSION_INFO VERSIONINFO FILEVERSION FILE_VERSION PRODUCTVERSION PRODUCT_VERSION FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L #else FILEFLAGS 0x0L #endif FILEOS 0x40004L FILETYPE 0x1L FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904b0" BEGIN VALUE "CompanyName", COMPANY_NAME VALUE "FileDescription", FILE_DESCRIPTION VALUE "FileVersion", FILE_VERSION_STRING VALUE "InternalName", INTERNAL_NAME VALUE "LegalCopyright", COPYRIGHT_NOTE VALUE "OriginalFilename", ORIGINAL_FILENAME VALUE "ProductName", ZOOMIT_PRODUCT_NAME VALUE "ProductVersion", PRODUCT_VERSION_STRING END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1200 END END ///////////////////////////////////////////////////////////////////////////// // // Dialog // OPTIONS DIALOGEX 0, 0, 279, 325 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CLIPSIBLINGS | WS_CAPTION | WS_SYSMENU EXSTYLE WS_EX_CONTROLPARENT CAPTION "ZoomIt - Sysinternals: www.sysinternals.com" FONT 8, "MS Shell Dlg", 0, 0, 0x0 BEGIN DEFPUSHBUTTON "ȷ",IDOK,166,306,50,14 PUSHBUTTON "ȡ",IDCANCEL,223,306,50,14 LTEXT "ZoomIt v9.0",IDC_VERSION,42,7,73,10 LTEXT "Ȩ 17 2006-2024 Mark Russinovich",IDC_COPYRIGHT,42,17,176,8 CONTROL "Sysinternals - www.sysinternals.com",IDC_LINK, "SysLink",WS_TABSTOP,42,26,150,9 ICON "APPICON",IDC_STATIC,12,9,20,20 CONTROL "ʾͼ",IDC_SHOW_TRAY_ICON,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,295,105,10 CONTROL "",IDC_TAB,"SysTabControl32",TCS_MULTILINE | WS_TABSTOP,8,46,265,245 CONTROL "",IDC_AUTOSTART,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,309,122,10 END ADVANCED_BREAK DIALOGEX 0, 0, 209, 219 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "ʱ߼" FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN CONTROL "ʱ:",IDC_CHECK_SOUND_FILE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,3,11,98,10,WS_EX_RIGHT EDITTEXT IDC_SOUND_FILE,61,38,125,12,ES_AUTOHSCROLL | ES_READONLY PUSHBUTTON "&...",IDC_SOUND_BROWSE,187,38,13,11 COMBOBOX IDC_OPACITY,62,58,38,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP CONTROL "",IDC_TIMER_POS1,"Button",BS_AUTORADIOBUTTON,63,78,10,10 CONTROL "",IDC_TIMER_POS2,"Button",BS_AUTORADIOBUTTON,79,78,10,10 CONTROL "",IDC_TIMER_POS3,"Button",BS_AUTORADIOBUTTON,97,78,10,10 CONTROL "",IDC_TIMER_POS4,"Button",BS_AUTORADIOBUTTON,63,93,10,10 CONTROL "",IDC_TIMER_POS5,"Button",BS_AUTORADIOBUTTON,79,93,10,10 CONTROL "",IDC_TIMER_POS6,"Button",BS_AUTORADIOBUTTON,97,93,10,10 CONTROL "",IDC_TIMER_POS7,"Button",BS_AUTORADIOBUTTON,63,108,10,10 CONTROL "",IDC_TIMER_POS8,"Button",BS_AUTORADIOBUTTON,79,108,10,10 CONTROL "",IDC_TIMER_POS9,"Button",BS_AUTORADIOBUTTON,97,108,10,10 CONTROL "ʾͼƬ:",IDC_CHECK_BACKGROUND_FILE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,3,122,99,10,WS_EX_RIGHT CONTROL "ʹñ䰵Ļͼ",IDC_STATIC_DESKTOP_BACKGROUND, "Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,46,135,125,10 CONTROL "ʹԶͼļ",IDC_STATIC_BACKGROUND_FILE, "Button",BS_AUTORADIOBUTTON | WS_TABSTOP,46,149,109,10 EDITTEXT IDC_BACKGROUND_FILE,62,164,125,12,ES_AUTOHSCROLL | ES_READONLY PUSHBUTTON "&...",IDC_BACKGROUND_BROWSE,188,164,13,11 CONTROL "ȫ:",IDC_CHECK_BACKGROUND_STRETCH,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,58,180,67,10,WS_EX_RIGHT DEFPUSHBUTTON "ȷ",IDOK,97,201,50,14 PUSHBUTTON "ȡ",IDCANCEL,150,201,50,14 LTEXT "Ƶļ:",IDC_STATIC_SOUND_FILE,61,26,56,8 LTEXT "ڲ͸:",IDC_STATIC,8,59,48,8 LTEXT "ʱʾλ:",IDC_STATIC,8,77,48,8 CONTROL "",IDC_STATIC,"Static",SS_BLACKFRAME | SS_SUNKEN,7,196,193,1,WS_EX_CLIENTEDGE END ZOOM DIALOGEX 0, 0, 260, 158 STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_SYSMENU FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN CONTROL "",IDC_HOTKEY,"msctls_hotkey32",WS_BORDER | WS_TABSTOP,59,57,80,12 LTEXT "¿ݼŴ֮󣬹ֻ·ŴС Esc Ҽ˳Ŵ",IDC_STATIC,7,6,246,26 LTEXT "Ŵݼ:",IDC_STATIC,7,59,51,8 CONTROL "",IDC_ZOOM_SLIDER,"msctls_trackbar32",TBS_AUTOTICKS | TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,53,104,150,15,WS_EX_TRANSPARENT LTEXT "ĬϷŴ:",IDC_STATIC,7,91,215,10 LTEXT "1.25",IDC_STATIC,52,122,16,8 LTEXT "1.5",IDC_STATIC,82,122,12,8 LTEXT "1.75",IDC_STATIC,108,122,16,8 LTEXT "2.0",IDC_STATIC,138,122,12,8 LTEXT "3.0",IDC_STATIC,164,122,12,8 LTEXT "4.0",IDC_STATIC,190,122,12,8 CONTROL "ŴС:",IDC_ANIMATE_ZOOM,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,7,74,116,10 LTEXT " Ctrl+C ƽͼCtrl+S ͼCtrl+Shift+C ѡƣCtrl+Shift+S ѡ档",IDC_STATIC,7,34,246,17 END DRAW DIALOGEX 0, 0, 260, 228 STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN LTEXT "Ŵ֮󣬵ʼͼҼ˳ͼ Ctrl+Z أ E գոƵĻ롣",IDC_STATIC,7,7,246,24 LTEXT "ϸ",IDC_PEN_CONTROL,7,38,40,8 LTEXT " Ctrl+ֻ· ʴϸ",IDC_STATIC,19,48,233,16 LTEXT "ɫ",IDC_COLORS,7,70,21,8 LTEXT " R ɫG ɫB ɫO ɫY ɫP ɫ",IDC_STATIC,19,80,233,16 LTEXT "Ч",IDC_HIGHLIGHT_AND_BLUR,7,102,58,8 LTEXT " Shift+ɫ ɶӦİ͸߹ɫ X ģShift+X Ǹǿģ",IDC_STATIC,19,113,233,16 LTEXT "״",IDC_SHAPES,7,134,23,8 LTEXT "ס Shift ֱߣס Ctrl Σס Tab Բס Ctrl+Shift ͷ",IDC_STATIC,19,144,233,16 LTEXT "",IDC_SCREEN,7,166,22,8 LTEXT " W ȫɰɫK ɫ֧ͬ Ctrl+C ƺ Ctrl+S ȡ",IDC_STATIC,19,176,233,24 CONTROL "",IDC_DRAW_HOTKEY,"msctls_hotkey32",WS_BORDER | WS_TABSTOP,73,207,80,12 LTEXT "ͼݼ:",IDC_STATIC,7,210,63,11 END TYPE DIALOGEX 0, 0, 260, 104 STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN LTEXT "ڻͼʱ T ıShift+T Ҷı Esc 򵥻ı롣ֻ·ıС",IDC_STATIC,7,7,246,32 LTEXT "ıɫǵǰĻɫ",IDC_STATIC,7,47,211,9 PUSHBUTTON "(&F)",IDC_FONT,112,69,41,14 GROUPBOX "",IDC_TEXT_FONT,8,61,99,28 END BREAK DIALOGEX 0, 0, 260, 123 STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN CONTROL "",IDC_BREAK_HOTKEY,"msctls_hotkey32",WS_BORDER | WS_TABSTOP,52,67,80,12 EDITTEXT IDC_TIMER,31,86,31,13,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER CONTROL "",IDC_SPIN_TIMER,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,45,86,11,12 LTEXT "",IDC_STATIC,67,88,25,8 PUSHBUTTON "߼(&A)",IDC_ADVANCED_BREAK,212,102,41,14 LTEXT "ݼʹ̲˵ʼʱʱ䣬 Esc ˳;ʹ Alt+Tab лڣͼԷؼʱڡ",IDC_STATIC,7,7,246,33 LTEXT "ʱݼ:",IDC_STATIC,7,70,39,8 LTEXT "ʱ:",IDC_STATIC,7,88,27,8 LTEXT "ʱɫ԰ɫлǻͼı塣",IDC_STATIC,7,45,219,20 CONTROL "ʱʾʱ:",IDC_CHECK_SHOW_EXPIRED, "Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,8,104,132,10 END 1543 DIALOGEX 100, 50, 216, 131 STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "ZoomIt " FONT 8, "MS Shell Dlg", 0, 0, 0x0 BEGIN LTEXT "(&F):",1088,6,0,40,9 COMBOBOX 1136,6,10,94,64,CBS_SIMPLE | CBS_OWNERDRAWFIXED | CBS_AUTOHSCROLL | CBS_SORT | CBS_HASSTRINGS | CBS_DISABLENOSCROLL | WS_VSCROLL | WS_TABSTOP LTEXT "(&Y):",1089,108,0,44,9 COMBOBOX 1137,108,10,64,64,CBS_SIMPLE | CBS_DISABLENOSCROLL | WS_VSCROLL | WS_TABSTOP LTEXT "ֺ(&S):",1090,179,0,30,9 COMBOBOX 1138,179,10,32,64,CBS_SIMPLE | CBS_OWNERDRAWFIXED | CBS_SORT | CBS_HASSTRINGS | CBS_DISABLENOSCROLL | WS_VSCROLL | WS_TABSTOP DEFPUSHBUTTON "ȷ",IDOK,166,94,45,14,WS_GROUP PUSHBUTTON "ȡ",IDCANCEL,166,111,45,14,WS_GROUP GROUPBOX "Sample",1073,7,75,143,51,WS_GROUP CTEXT "AaBbYyZz",1092,16,88,127,31,SS_NOPREFIX | NOT WS_VISIBLE END LIVEZOOM DIALOGEX 0, 0, 260, 134 STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN CONTROL "",IDC_LIVE_HOTKEY,"msctls_hotkey32",WS_BORDER | WS_TABSTOP,69,108,80,12 LTEXT "ģʽ´ܶҪ Windows 7",IDC_STATIC,7,7,246,18 LTEXT "ʵʱŴݼ:",IDC_STATIC,7,110,62,8 LTEXT "ݼ˳ʵʱŴ",IDC_STATIC,7,94,218,13 LTEXT " Ctrl+· ŴС ͼݼ ử濪ʼͼ갴 Esc صʵʱС",IDC_STATIC,7,30,246,27 LTEXT " Shift+ʵʱŴݼ ʼʵʱͼ治ᶳᣬ Esc ء",IDC_STATIC,7,62,246,32 END RECORD DIALOGEX 0, 0, 260, 169 STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN CONTROL "",IDC_RECORD_HOTKEY,"msctls_hotkey32",WS_BORDER | WS_TABSTOP,61,96,80,12 LTEXT "¼ݼ:",IDC_STATIC,7,98,54,8 LTEXT "ݼԿʼ¼ȫٰһ¿Խ¼",IDC_STATIC,7,7,246,28 LTEXT "¼Ҫ Windows 10 汾 1903 ϡ",IDC_STATIC,7,77,246,19 LTEXT "ֱʱ:",IDC_STATIC,7,115,54,8 COMBOBOX IDC_RECORD_SCALING,61,114,26,30,CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | CBS_OEMCONVERT | CBS_SORT | WS_VSCROLL | WS_TABSTOP LTEXT "ˢ:",IDC_STATIC,119,115,44,8,NOT WS_VISIBLE COMBOBOX IDC_RECORD_FRAME_RATE,166,114,42,30,CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | CBS_OEMCONVERT | CBS_SORT | NOT WS_VISIBLE | WS_VSCROLL | WS_TABSTOP LTEXT " Shift+¼ݼ Կѡ¼ơ",IDC_STATIC,7,32,246,19 LTEXT " Alt+¼ݼ ¼Ƶǰڡ",IDC_STATIC,7,55,246,19 CONTROL "¼˷Ƶ(&C):",IDC_CAPTURE_AUDIO,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,7,137,83,10 COMBOBOX IDC_MICROPHONE,81,152,172,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP LTEXT "˷:",IDC_STATIC,32,154,47,8 END SNIP DIALOGEX 0, 0, 260, 68 STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_SYSMENU FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN CONTROL "",IDC_SNIP_HOTKEY,"msctls_hotkey32",WS_BORDER | WS_TABSTOP,55,32,80,12 LTEXT "ͼݼ:",IDC_STATIC,7,33,45,8 LTEXT " ͼݼ ͼƣ Shift+ͼݼ ͼ档",IDC_STATIC,7,7,246,19 END DEMOTYPE DIALOGEX 0, 0, 259, 249 STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_SYSMENU FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN CONTROL "",IDC_DEMOTYPE_HOTKEY,"msctls_hotkey32",WS_BORDER | WS_TABSTOP,74,154,80,12 LTEXT "Զֿݼ:",IDC_STATIC,7,157,63,8 PUSHBUTTON "&...",IDC_DEMOTYPE_BROWSE,231,137,16,13 CONTROL "",IDC_DEMOTYPE_SPEED_SLIDER,"msctls_trackbar32",TBS_AUTOTICKS | TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,52,202,150,11,WS_EX_TRANSPARENT CONTROL "Զģʽ:",IDC_DEMOTYPE_USER_DRIVEN,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,7,173,88,10 LTEXT "ٶ:",IDC_STATIC,7,189,215,10 LTEXT "",IDC_DEMOTYPE_STATIC1,51,213,18,8 LTEXT "",IDC_DEMOTYPE_STATIC2,186,213,17,8 EDITTEXT IDC_DEMOTYPE_FILE,44,137,187,12,ES_AUTOHSCROLL | ES_READONLY LTEXT "ļ:",IDC_STATIC,7,139,32,8 LTEXT "ļ֮󣬽¼ļصͷ Shift+Զֿݼ صһΡ",IDC_STATIC,7,108,248,24 LTEXT "ѡһļΪıݼԶһֵشҲԴӼııҪ [start] ͷ",IDC_STATIC,7,7,248,24 LTEXT " - [pause:n] Եȴ n 롣",IDC_STATIC,19,34,212,11 LTEXT "ĬԶԶģʽǾֶһ¼һ֡ԶֵĹУ̽ᱻơ",IDC_STATIC,7,68,248,16 LTEXT "Զģʽ£һ֮ҪոơȫԶģʽ»Զơ",IDC_STATIC,7,88,248,16 LTEXT "- [paste] [/paste] IJֿһճ",IDC_STATIC,23,45,178,8 LTEXT "- [enter][up][down][left][right] ʾسҰ",IDC_STATIC,23,56,211,8 END ///////////////////////////////////////////////////////////////////////////// // // DESIGNINFO // #ifdef APSTUDIO_INVOKED GUIDELINES DESIGNINFO BEGIN "OPTIONS", DIALOG BEGIN RIGHTMARGIN, 273 BOTTOMMARGIN, 320 END "ADVANCED_BREAK", DIALOG BEGIN RIGHTMARGIN, 207 BOTTOMMARGIN, 215 END "ZOOM", DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 253 TOPMARGIN, 7 BOTTOMMARGIN, 151 END "DRAW", DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 253 TOPMARGIN, 7 BOTTOMMARGIN, 221 END "TYPE", DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 253 TOPMARGIN, 7 BOTTOMMARGIN, 97 END "BREAK", DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 253 TOPMARGIN, 7 BOTTOMMARGIN, 116 END 1543, DIALOG BEGIN RIGHTMARGIN, 211 BOTTOMMARGIN, 127 END "LIVEZOOM", DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 253 TOPMARGIN, 7 BOTTOMMARGIN, 127 END "RECORD", DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 253 TOPMARGIN, 7 BOTTOMMARGIN, 164 END "SNIP", DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 253 TOPMARGIN, 7 BOTTOMMARGIN, 61 END "DEMOTYPE", DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 255 TOPMARGIN, 7 BOTTOMMARGIN, 205 END END #endif // APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Accelerator // ACCELERATORS ACCELERATORS BEGIN "C", IDC_COPY, VIRTKEY, CONTROL, NOINVERT "S", IDC_SAVE, VIRTKEY, CONTROL, NOINVERT "C", IDC_COPY_CROP, VIRTKEY, SHIFT, CONTROL, NOINVERT "S", IDC_SAVE_CROP, VIRTKEY, SHIFT, CONTROL, NOINVERT END ///////////////////////////////////////////////////////////////////////////// // // AFX_DIALOG_LAYOUT // OPTIONS AFX_DIALOG_LAYOUT BEGIN 0 END LIVEZOOM AFX_DIALOG_LAYOUT BEGIN 0 END DRAW AFX_DIALOG_LAYOUT BEGIN 0 END RECORD AFX_DIALOG_LAYOUT BEGIN 0 END TYPE AFX_DIALOG_LAYOUT BEGIN 0 END ZOOM AFX_DIALOG_LAYOUT BEGIN 0 END SNIP AFX_DIALOG_LAYOUT BEGIN 0 END BREAK AFX_DIALOG_LAYOUT BEGIN 0 END DEMOTYPE AFX_DIALOG_LAYOUT BEGIN 0 END #endif // English (United States) resources ///////////////////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // #include "binres.rc" ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED ================================================ FILE: PowerToys/src/modules/ZoomIt/ZoomIt/Zoomit.cpp ================================================ //============================================================================ // // Zoomit // Copyright (C) Mark Russinovich // Sysinternals - www.sysinternals.com // // Screen zoom and annotation tool. // // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. //============================================================================ #include "pch.h" #include "zoomit.h" #include "Utility.h" #include "WindowsVersions.h" #include "ZoomItSettings.h" #ifdef __ZOOMIT_POWERTOYS__ #include #include #include #include "../ZoomItModuleInterface/trace.h" #include #include #include #include #include #endif // __ZOOMIT_POWERTOYS__ namespace winrt { using namespace Windows::Foundation; using namespace Windows::Graphics; using namespace Windows::Graphics::Capture; using namespace Windows::Graphics::Imaging; using namespace Windows::Storage; using namespace Windows::UI::Composition; using namespace Windows::Storage::Pickers; using namespace Windows::System; using namespace Windows::Devices::Enumeration; } namespace util { using namespace robmikh::common::uwp; using namespace robmikh::common::desktop; } // This workaround keeps live zoom enabled after zooming out at level 1 (not zoomed) and disables // live zoom when recording is stopped. #define WINDOWS_CURSOR_RECORDING_WORKAROUND 1 HINSTANCE g_hInstance; COLORREF g_CustomColors[16]; #define ZOOM_HOTKEY 0 #define DRAW_HOTKEY 1 #define BREAK_HOTKEY 2 #define LIVE_HOTKEY 3 #define LIVE_DRAW_HOTKEY 4 #define RECORD_HOTKEY 5 #define RECORD_CROP_HOTKEY 6 #define RECORD_WINDOW_HOTKEY 7 #define SNIP_HOTKEY 8 #define SNIP_SAVE_HOTKEY 9 #define DEMOTYPE_HOTKEY 10 #define DEMOTYPE_RESET_HOTKEY 11 #define ZOOM_PAGE 0 #define LIVE_PAGE 1 #define DRAW_PAGE 2 #define TYPE_PAGE 3 #define DEMOTYPE_PAGE 4 #define BREAK_PAGE 5 #define RECORD_PAGE 6 #define SNIP_PAGE 7 OPTION_TABS g_OptionsTabs[] = { { _T("Zoom"), NULL }, { _T("LiveZoom"), NULL }, { _T("Draw"), NULL }, { _T("Type"), NULL }, { _T("DemoType"), NULL }, { _T("Break"), NULL }, { _T("Record"), NULL }, { _T("Snip"), NULL } }; float g_ZoomLevels[] = { 1.25, 1.50, 1.75, 2.00, 3.00, 4.00 }; DWORD g_FramerateOptions[] = { 30, 60 }; // // For typing mode // typedef enum { TypeModeOff = 0, TypeModeLeftJustify, TypeModeRightJustify } TypeModeState; const DWORD CURSOR_ARM_LENGTH = 4; const float NORMAL_BLUR_RADIUS = 20; const float STRONG_BLUR_RADIUS = 40; DWORD g_ToggleMod; DWORD g_LiveZoomToggleMod; DWORD g_DrawToggleMod; DWORD g_BreakToggleMod; DWORD g_DemoTypeToggleMod; DWORD g_RecordToggleMod; DWORD g_SnipToggleMod; BOOLEAN g_ZoomOnLiveZoom = FALSE; DWORD g_PenWidth = PEN_WIDTH; float g_BlurRadius = NORMAL_BLUR_RADIUS; HWND hWndOptions = NULL; BOOLEAN g_DrawPointer = FALSE; BOOLEAN g_PenDown = FALSE; BOOLEAN g_PenInverted = FALSE; DWORD g_OsVersion; HWND g_hWndLiveZoom = NULL; HWND g_hWndLiveZoomMag = NULL; HWND g_hWndMain; int g_AlphaBlend = 0x80; BOOL g_fullScreenWorkaround = FALSE; bool g_bSaveInProgress = false; std::wstring g_TextBuffer; // This is useful in the context of right-justified text only. std::list g_TextBufferPreviousLines; #if WINDOWS_CURSOR_RECORDING_WORKAROUND bool g_LiveZoomLevelOne = false; #endif // True if ZoomIt was started by PowerToys instead of standalone. BOOLEAN g_StartedByPowerToys = FALSE; BOOLEAN g_running = TRUE; // Screen recording globals #define DEFAULT_RECORDING_FILE L"录屏.mp4" BOOL g_RecordToggle = FALSE; BOOL g_RecordCropping = FALSE; SelectRectangle g_SelectRectangle; std::wstring g_RecordingSaveLocation; winrt::IDirect3DDevice g_RecordDevice{ nullptr }; std::shared_ptr g_RecordingSession = nullptr; type_pGetMonitorInfo pGetMonitorInfo; type_MonitorFromPoint pMonitorFromPoint; type_pSHAutoComplete pSHAutoComplete; type_pSetLayeredWindowAttributes pSetLayeredWindowAttributes; type_pSetProcessDPIAware pSetProcessDPIAware; type_pMagSetWindowSource pMagSetWindowSource; type_pMagSetWindowTransform pMagSetWindowTransform; type_pMagSetFullscreenTransform pMagSetFullscreenTransform; type_pMagSetInputTransform pMagSetInputTransform; type_pMagShowSystemCursor pMagShowSystemCursor; type_pMagSetWindowFilterList pMagSetWindowFilterList; type_pMagInitialize pMagInitialize; type_pDwmIsCompositionEnabled pDwmIsCompositionEnabled; type_pGetPointerType pGetPointerType; type_pGetPointerPenInfo pGetPointerPenInfo; type_pSystemParametersInfoForDpi pSystemParametersInfoForDpi; type_pGetDpiForWindow pGetDpiForWindow; type_pSHQueryUserNotificationState pSHQueryUserNotificationState; type_pCreateDirect3D11DeviceFromDXGIDevice pCreateDirect3D11DeviceFromDXGIDevice; type_pCreateDirect3D11SurfaceFromDXGISurface pCreateDirect3D11SurfaceFromDXGISurface; type_pD3D11CreateDevice pD3D11CreateDevice; ClassRegistry reg( _T("Software\\Sysinternals\\") APPNAME ); ComputerGraphicsInit g_GraphicsInit; //---------------------------------------------------------------------------- // // Saves specified filePath to clipboard. // //---------------------------------------------------------------------------- bool SaveToClipboard( const WCHAR* filePath, HWND hwnd ) { if( filePath == NULL || hwnd == NULL || wcslen( filePath ) == 0 ) { return false; } size_t size = sizeof(DROPFILES) + sizeof(WCHAR) * ( _tcslen( filePath ) + 1 ) + sizeof(WCHAR); HDROP hDrop = static_cast(GlobalAlloc( GHND, size )); if (hDrop == NULL) { return false; } DROPFILES* dFiles = static_cast(GlobalLock( hDrop )); if (dFiles == NULL) { GlobalFree( hDrop ); return false; } dFiles->pFiles = sizeof(DROPFILES); dFiles->fWide = TRUE; wcscpy( reinterpret_cast(& dFiles[1]), filePath); GlobalUnlock( hDrop ); if( OpenClipboard( hwnd ) ) { EmptyClipboard(); SetClipboardData( CF_HDROP, hDrop ); CloseClipboard(); } GlobalFree( hDrop ); return true; } //---------------------------------------------------------------------- // // OutputDebug // //---------------------------------------------------------------------- void OutputDebug(const TCHAR* format, ...) { #if _DEBUG TCHAR msg[1024]; va_list va; #ifdef _MSC_VER // For some reason, ARM64 Debug builds causes an analyzer error on va_start: "error C26492: Don't use const_cast to cast away const or volatile (type.3)." #pragma warning(push) #pragma warning(disable : 26492) #endif va_start(va, format); #ifdef _MSC_VER #pragma warning(pop) #endif _vstprintf_s(msg, format, va); va_end(va); OutputDebugString(msg); #endif } //---------------------------------------------------------------------------- // // InitializeFonts // // Return a bold equivalent of either a DPI aware font face for GUI text or // just the stock object for DEFAULT_GUI_FONT. // //---------------------------------------------------------------------------- void InitializeFonts( HWND hwnd, HFONT *bold ) { LOGFONT logFont; bool haveLogFont = false; if( *bold ) { DeleteObject( *bold ); *bold = nullptr; } if( pSystemParametersInfoForDpi && pGetDpiForWindow ) { NONCLIENTMETRICSW metrics{}; metrics.cbSize = sizeof( metrics ); if( pSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, sizeof( metrics ), &metrics, 0, pGetDpiForWindow( hwnd ) ) ) { CopyMemory( &logFont, &metrics.lfMessageFont, sizeof( logFont ) ); haveLogFont = true; } } if( !haveLogFont ) { auto normal = static_cast(GetStockObject( DEFAULT_GUI_FONT )); GetObject( normal, sizeof( logFont ), &logFont ); haveLogFont = true; // for correctness } logFont.lfWeight = FW_BOLD; *bold = CreateFontIndirect( &logFont ); } //---------------------------------------------------------------------------- // // EnsureForeground // //---------------------------------------------------------------------------- void EnsureForeground() { if( !IsWindowVisible( g_hWndMain ) ) SetForegroundWindow( g_hWndMain ); } //---------------------------------------------------------------------------- // // RestoreForeground // //---------------------------------------------------------------------------- void RestoreForeground() { // If the main window is not visible, move foreground to the next window if( !IsWindowVisible( g_hWndMain ) ) { // Activate the next window by unhiding and hiding the main window MoveWindow( g_hWndMain, 0, 0, 0, 0, FALSE ); ShowWindow( g_hWndMain, SW_SHOWNA ); ShowWindow( g_hWndMain, SW_HIDE ); OutputDebug(L"RESTORE FOREGROUND\n"); } } //---------------------------------------------------------------------------- // // ErrorDialog // //---------------------------------------------------------------------------- VOID ErrorDialog( HWND hParent, PCTSTR message, DWORD _Error ) { LPTSTR lpMsgBuf; TCHAR errmsg[1024]; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, _Error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast(&lpMsgBuf), 0, NULL ); _stprintf( errmsg, L"%s: %s", message, lpMsgBuf ); #ifdef __ZOOMIT_POWERTOYS__ if( g_StartedByPowerToys ) { Logger::error( errmsg ); } #endif // __ZOOMIT_POWERTOYS__ MessageBox( hParent, errmsg, APPNAME, MB_OK|MB_ICONERROR); } //---------------------------------------------------------------------------- // // ErrorDialogString // //---------------------------------------------------------------------------- VOID ErrorDialogString( HWND hParent, PCTSTR Message, const wchar_t *_Error ) { TCHAR errmsg[1024]; _stprintf_s( errmsg, _countof( errmsg ), L"%s: %s", Message, _Error ); if( hParent == g_hWndMain ) { EnsureForeground(); } #ifdef __ZOOMIT_POWERTOYS__ if( g_StartedByPowerToys ) { Logger::error( errmsg ); } #endif // __ZOOMIT_POWERTOYS__ MessageBox(hParent, errmsg, APPNAME, MB_OK | MB_ICONERROR); if( hParent == g_hWndMain ) { RestoreForeground(); } } //-------------------------------------------------------------------- // // SetAutostartFilePath // // Sets the file path for later autostart config. // //-------------------------------------------------------------------- void SetAutostartFilePath() { HKEY hZoomit; DWORD error; TCHAR imageFile[MAX_PATH] = { 0 }; error = RegCreateKeyEx( HKEY_CURRENT_USER, _T( "Software\\Sysinternals\\Zoomit" ), 0, 0, 0, KEY_SET_VALUE, NULL, &hZoomit, NULL ); if( error == ERROR_SUCCESS ) { GetModuleFileName( NULL, imageFile + 1, _countof( imageFile ) - 2 ); imageFile[0] = '"'; *(_tcschr( imageFile, 0 )) = '"'; error = RegSetValueEx( hZoomit, L"FilePath", 0, REG_SZ, (BYTE *) imageFile, static_cast(_tcslen( imageFile ) + 1)* sizeof( TCHAR )); RegCloseKey( hZoomit ); } } //-------------------------------------------------------------------- // // ConfigureAutostart // // Enables or disables Zoomit autostart for the current image file. // //-------------------------------------------------------------------- bool ConfigureAutostart( HWND hParent, bool Enable ) { HKEY hRunKey, hZoomit; DWORD error, length, type; TCHAR imageFile[MAX_PATH]; error = RegOpenKeyEx( HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Run", 0, KEY_SET_VALUE, &hRunKey ); if( error == ERROR_SUCCESS ) { if( Enable ) { error = RegOpenKeyEx( HKEY_CURRENT_USER, _T("Software\\Sysinternals\\Zoomit"), 0, KEY_QUERY_VALUE, &hZoomit ); if( error == ERROR_SUCCESS ) { length = sizeof(imageFile); #ifdef _WIN64 // Unconditionally reset filepath in case this was already set by 32 bit version SetAutostartFilePath(); #endif error = RegQueryValueEx( hZoomit, _T( "Filepath" ), 0, &type, (BYTE *) imageFile, &length ); RegCloseKey( hZoomit ); if( error == ERROR_SUCCESS ) { error = RegSetValueEx( hRunKey, APPNAME, 0, REG_SZ, (BYTE *) imageFile, static_cast(_tcslen(imageFile)+1) * sizeof(TCHAR)); } } } else { error = RegDeleteValue( hRunKey, APPNAME ); if( error == ERROR_FILE_NOT_FOUND ) error = ERROR_SUCCESS; } RegCloseKey( hRunKey ); } if( error != ERROR_SUCCESS ) { ErrorDialog( hParent, L"无法设置开机自启", error ); } return error == ERROR_SUCCESS; } //-------------------------------------------------------------------- // // IsAutostartConfigured // // Is this version of zoomit configured to autostart. // //-------------------------------------------------------------------- bool IsAutostartConfigured() { HKEY hRunKey; TCHAR imageFile[MAX_PATH]; DWORD error, imageFileLength, type; error = RegOpenKeyEx( HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Run", 0, KEY_QUERY_VALUE, &hRunKey ); if( error == ERROR_SUCCESS ) { imageFileLength = sizeof(imageFile); error = RegQueryValueEx( hRunKey, _T("Zoomit"), 0, &type, (BYTE *) imageFile, &imageFileLength ); RegCloseKey( hRunKey ); } return error == ERROR_SUCCESS; } #ifndef _WIN64 //-------------------------------------------------------------------- // // RunningOnWin64 // // Returns true if this is the 32-bit version of the executable // and we're on 64-bit Windows // //-------------------------------------------------------------------- typedef BOOL (__stdcall *P_IS_WOW64PROCESS)( HANDLE hProcess, PBOOL Wow64Process ); BOOL RunningOnWin64( VOID ) { P_IS_WOW64PROCESS pIsWow64Process; BOOL isWow64 = FALSE; pIsWow64Process = (P_IS_WOW64PROCESS) GetProcAddress(GetModuleHandle(_T("kernel32.dll")), "IsWow64Process"); if( pIsWow64Process ) { pIsWow64Process( GetCurrentProcess(), &isWow64 ); } return isWow64; } //-------------------------------------------------------------------- // // ExtractImageResource // // Extracts the specified file that is located in a resource for // this executable. // //-------------------------------------------------------------------- BOOLEAN ExtractImageResource( PTCHAR ResourceName, PTCHAR TargetFile ) { HRSRC hResource; HGLOBAL hImageResource; DWORD dwImageSize; LPVOID lpvImage; FILE *hFile; // Locate the resource hResource = FindResource( NULL, ResourceName, _T("BINRES") ); if( !hResource ) return FALSE; hImageResource = LoadResource( NULL, hResource ); dwImageSize = SizeofResource( NULL, hResource ); lpvImage = LockResource( hImageResource ); // Now copy it out _tfopen_s( &hFile, TargetFile, _T("wb") ); if( hFile == NULL ) return FALSE; fwrite( lpvImage, 1, dwImageSize, hFile ); fclose( hFile ); return TRUE; } //-------------------------------------------------------------------- // // Run64bitVersion // // Returns true if this is the 32-bit version of the executable // and we're on 64-bit Windows // //-------------------------------------------------------------------- DWORD Run64bitVersion( void ) { TCHAR szPath[MAX_PATH]; TCHAR originalPath[MAX_PATH]; TCHAR tmpPath[MAX_PATH]; SHELLEXECUTEINFO info = { 0 }; if ( GetModuleFileName( NULL, szPath, sizeof(szPath)/sizeof(TCHAR)) == 0 ) { return -1; } _tcscpy_s( originalPath, _countof(originalPath), szPath ); *_tcsrchr( originalPath, '.') = 0; _tcscat_s( originalPath, _countof(szPath), _T("64.exe")); // // Extract the 64-bit version // ExpandEnvironmentStrings( L"%TEMP%", tmpPath, sizeof tmpPath / sizeof ( TCHAR)); _tcscat_s( tmpPath, _countof(tmpPath), _tcsrchr( originalPath, '\\')); _tcscpy_s( szPath, _countof(szPath), tmpPath ); if( !ExtractImageResource( _T("RCZOOMIT64"), szPath )) { if( GetFileAttributes( szPath ) == INVALID_FILE_ATTRIBUTES ) { ErrorDialog( NULL,_T("无法启动 64 位版本"), GetLastError()); return -1; } } info.cbSize = sizeof(info); info.fMask = SEE_MASK_NOASYNC | SEE_MASK_NOCLOSEPROCESS; info.lpFile = szPath; info.lpParameters = GetCommandLine(); info.nShow = SW_SHOWNORMAL; if( !ShellExecuteEx( &info ) ) { ErrorDialog( NULL,_T("无法启动 64 位版本"), GetLastError()); DeleteFile( szPath ); return -1; } WaitForSingleObject( info.hProcess, INFINITE ); DWORD result; GetExitCodeProcess( info.hProcess, &result ); CloseHandle( info.hProcess ); DeleteFile( szPath ); return result; } #endif //---------------------------------------------------------------------------- // // IsPresentationMode // //---------------------------------------------------------------------------- BOOLEAN IsPresentationMode() { QUERY_USER_NOTIFICATION_STATE pUserState; pSHQueryUserNotificationState( &pUserState ); return pUserState == QUNS_PRESENTATION_MODE; } //---------------------------------------------------------------------------- // // EnableDisableSecondaryDisplay // // Creates a second display on the secondary monitor for displaying the // break timer. // //---------------------------------------------------------------------------- LONG EnableDisableSecondaryDisplay( HWND hWnd, BOOLEAN Enable, PDEVMODE OriginalDevMode ) { LONG result; DEVMODE devMode{}; if( Enable ) { // // Prepare the position of Display 2 to be right to the right of Display 1 // devMode.dmSize = sizeof(devMode); devMode.dmDriverExtra = 0; EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &devMode); *OriginalDevMode = devMode; // // Enable display 2 in the registry // devMode.dmPosition.x = devMode.dmPelsWidth; devMode.dmFields = DM_POSITION | DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY; result = ChangeDisplaySettingsEx( L"\\\\.\\DISPLAY2", &devMode, NULL, CDS_NORESET | CDS_UPDATEREGISTRY, NULL); } else { OriginalDevMode->dmFields = DM_POSITION | DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY; result = ChangeDisplaySettingsEx( L"\\\\.\\DISPLAY2", OriginalDevMode, NULL, CDS_NORESET | CDS_UPDATEREGISTRY, NULL); } // // Update the hardware // if( result == DISP_CHANGE_SUCCESSFUL ) { if( !ChangeDisplaySettingsEx(NULL, NULL, NULL, 0, NULL)) { result = GetLastError(); } // // If enabling, move zoomit to the second monitor // if( Enable && result == DISP_CHANGE_SUCCESSFUL ) { SetWindowPos(FindWindowW(L"ZoomitClass", NULL), NULL, devMode.dmPosition.x, 0, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); SetCursorPos( devMode.dmPosition.x+1, devMode.dmPosition.y+1 ); } } return result; } //---------------------------------------------------------------------------- // // GetLineBounds // // Gets the rectangle bounding a line, taking into account pen width // //---------------------------------------------------------------------------- Gdiplus::Rect GetLineBounds( POINT p1, POINT p2, int penWidth ) { Gdiplus::Rect rect( min(p1.x, p2.x), min(p1.y, p2.y), abs(p1.x - p2.x), abs( p1.y - p2.y)); rect.Inflate( penWidth, penWidth ); return rect; } //---------------------------------------------------------------------------- // // InvalidateGdiplusRect // // Invalidate portion of window specified by Gdiplus::Rect // //---------------------------------------------------------------------------- void InvalidateGdiplusRect(HWND hWnd, Gdiplus::Rect BoundsRect) { RECT lineBoundsGdi; lineBoundsGdi.left = BoundsRect.X; lineBoundsGdi.top = BoundsRect.Y; lineBoundsGdi.right = BoundsRect.X + BoundsRect.Width; lineBoundsGdi.bottom = BoundsRect.Y + BoundsRect.Height; InvalidateRect(hWnd, &lineBoundsGdi, FALSE); } //---------------------------------------------------------------------------- // // CreateGdiplusBitmap // // Creates a gdiplus bitmap of the specified region of the HDC. // //---------------------------------------------------------------------------- Gdiplus::Bitmap *CreateGdiplusBitmap( HDC hDc, int x, int y, int Width, int Height ) { HBITMAP hBitmap = CreateCompatibleBitmap(hDc, Width, Height); // Create a device context for the new bitmap HDC hdcNewBitmap = CreateCompatibleDC(hDc); SelectObject(hdcNewBitmap, hBitmap); // Copy from the oldest undo bitmap to the new bitmap using the lineBounds as the source rectangle BitBlt(hdcNewBitmap, 0, 0, Width, Height, hDc, x, y, SRCCOPY); Gdiplus::Bitmap *blurBitmap = new Gdiplus::Bitmap(hBitmap, NULL); DeleteDC(hdcNewBitmap); DeleteObject(hBitmap); return blurBitmap; } //---------------------------------------------------------------------------- // // CreateBitmapMemoryDIB // // Creates a memory DC and DIB for the specified region of the screen. // //---------------------------------------------------------------------------- BYTE* CreateBitmapMemoryDIB(HDC hdcScreenCompat, HDC hBitmapDc, Gdiplus::Rect* lineBounds, HDC* hdcMem, HBITMAP* hDIBOrig, HBITMAP* hPreviousBitmap) { // Create a memory DIB for the relevant region of the original bitmap BITMAPINFO bmiOrig = { 0 }; bmiOrig.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmiOrig.bmiHeader.biWidth = lineBounds->Width; bmiOrig.bmiHeader.biHeight = -lineBounds->Height; // Top-down DIB bmiOrig.bmiHeader.biPlanes = 1; bmiOrig.bmiHeader.biBitCount = 32; // 32 bits per pixel bmiOrig.bmiHeader.biCompression = BI_RGB; VOID* pDIBBitsOrig; *hDIBOrig = CreateDIBSection(hdcScreenCompat, &bmiOrig, DIB_RGB_COLORS, &pDIBBitsOrig, NULL, 0); if( *hDIBOrig == NULL ) { OutputDebug(L"NULL DIB: %d\n", GetLastError()); OutputDebug(L"lineBounds: %d %d %d %d\n", lineBounds->X, lineBounds->Y, lineBounds->Width, lineBounds->Height); return NULL; } *hdcMem = CreateCompatibleDC(hdcScreenCompat); *hPreviousBitmap = static_cast(SelectObject(*hdcMem, *hDIBOrig)); // Copy the relevant part of hdcScreenCompat to the DIB BitBlt(*hdcMem, 0, 0, lineBounds->Width, lineBounds->Height, hBitmapDc, lineBounds->X, lineBounds->Y, SRCCOPY); // Pointer to the DIB bits return static_cast(pDIBBitsOrig); } //---------------------------------------------------------------------------- // // LockGdiPlusBitmap // // Locks the Gdi+ bitmap so that we can access its pixels in memory. // //---------------------------------------------------------------------------- #ifdef _MSC_VER // Analyzers want us to use a scoped object instead of new. But given all the operations done in Bitmaps it seems better to leave it as a heap object. #pragma warning(push) #pragma warning(disable : 26402) #endif Gdiplus::BitmapData* LockGdiPlusBitmap(Gdiplus::Bitmap* Bitmap) { Gdiplus::BitmapData *lineData = new Gdiplus::BitmapData(); Bitmap->GetPixelFormat(); Gdiplus::Rect lineBitmapBounds(0, 0, Bitmap->GetWidth(), Bitmap->GetHeight()); Bitmap->LockBits(&lineBitmapBounds, Gdiplus::ImageLockModeRead, Bitmap->GetPixelFormat(), lineData); return lineData; } #ifdef _MSC_VER #pragma warning(pop) #endif //---------------------------------------------------------------------------- // // BlurScreen // // Blur the portion of the screen by copying a blurred bitmap with the // specified shape. // //---------------------------------------------------------------------------- void BlurScreen(HDC hdcScreenCompat, Gdiplus::Rect* lineBounds, Gdiplus::Bitmap *BlurBitmap, BYTE* pPixels) { HDC hdcDIB; HBITMAP hDibOrigBitmap, hDibBitmap; BYTE* pDestPixels = CreateBitmapMemoryDIB(hdcScreenCompat, hdcScreenCompat, lineBounds, &hdcDIB, &hDibBitmap, &hDibOrigBitmap); // Iterate through the pixels for (int y = 0; y < lineBounds->Height; ++y) { for (int x = 0; x < lineBounds->Width; ++x) { int index = (y * lineBounds->Width * 4) + (x * 4); // Assuming 4 bytes per pixel // BYTE b = pPixels[index + 0]; // Blue channel // BYTE g = pPixels[index + 1]; // Green channel // BYTE r = pPixels[index + 2]; // Red channel BYTE a = pPixels[index + 3]; // Alpha channel // Check if this is a drawn pixel if (a != 0) { // get the blur pixel Gdiplus::Color pixel; BlurBitmap->GetPixel(x, y, &pixel); COLORREF newPixel = pixel.GetValue() & 0xFFFFFF; pDestPixels[index + 0] = GetRValue(newPixel); pDestPixels[index + 1] = GetGValue(newPixel); pDestPixels[index + 2] = GetBValue(newPixel); } } } // Copy the updated DIB back to hdcScreenCompat BitBlt(hdcScreenCompat, lineBounds->X, lineBounds->Y, lineBounds->Width, lineBounds->Height, hdcDIB, 0, 0, SRCCOPY); // Clean up SelectObject(hdcDIB, hDibOrigBitmap); DeleteObject(hDibBitmap); DeleteDC(hdcDIB); } //---------------------------------------------------------------------------- // // BitmapBlur // // Blurs the bitmap. // //---------------------------------------------------------------------------- void BitmapBlur(Gdiplus::Bitmap* hBitmap) { // Git bitmap size Gdiplus::Size bitmapSize; bitmapSize.Width = hBitmap->GetWidth(); bitmapSize.Height = hBitmap->GetHeight(); // Blur the new bitmap Gdiplus::Blur blurObject; Gdiplus::BlurParams blurParams; blurParams.radius = g_BlurRadius; blurParams.expandEdge = FALSE; blurObject.SetParameters(&blurParams); // Apply blur to image RECT linesRect; linesRect.left = 0; linesRect.top = 0; linesRect.right = bitmapSize.Width; linesRect.bottom = bitmapSize.Height; hBitmap->ApplyEffect(&blurObject, &linesRect); } //---------------------------------------------------------------------------- // // DrawBlurredShape // // Blur a shaped region of the screen. // //---------------------------------------------------------------------------- void DrawBlurredShape( DWORD Shape, Gdiplus::Pen *pen, HDC hdcScreenCompat, Gdiplus::Graphics *dstGraphics, int x1, int y1, int x2, int y2) { // Create a new bitmap that's the size of the area covered by the line + 2 * g_PenWidth Gdiplus::Rect lineBounds( min( x1, x2 ), min( y1, y2 ), abs( x2 - x1 ), abs( y2 - y1 ) ); // Expand for line drawing if (Shape == DRAW_LINE) lineBounds.Inflate( static_cast(g_PenWidth / 2), static_cast(g_PenWidth / 2) ); Gdiplus::Bitmap* lineBitmap = new Gdiplus::Bitmap(lineBounds.Width, lineBounds.Height, PixelFormat32bppARGB); Gdiplus::Graphics lineGraphics(lineBitmap); static const auto blackBrush = Gdiplus::SolidBrush(Gdiplus::Color::Black); switch (Shape) { case DRAW_RECTANGLE: lineGraphics.FillRectangle(&blackBrush, 0, 0, lineBounds.Width, lineBounds.Height); break; case DRAW_ELLIPSE: lineGraphics.FillEllipse(&blackBrush, 0, 0, lineBounds.Width, lineBounds.Height); break; case DRAW_LINE: OutputDebug(L"BLUR_LINE: %d %d\n", lineBounds.Width, lineBounds.Height); lineGraphics.DrawLine( pen, x1 - lineBounds.X, y1 - lineBounds.Y, x2 - lineBounds.X, y2 - lineBounds.Y ); break; } Gdiplus::BitmapData* lineData = LockGdiPlusBitmap(lineBitmap); BYTE* pPixels = static_cast(lineData->Scan0); // Create a GDI bitmap that's the size of the lineBounds rectangle Gdiplus::Bitmap* blurBitmap = CreateGdiplusBitmap(hdcScreenCompat, lineBounds.X, lineBounds.Y, lineBounds.Width, lineBounds.Height); // Blur it BitmapBlur(blurBitmap); BlurScreen(hdcScreenCompat, &lineBounds, blurBitmap, pPixels); // Unlock the bits lineBitmap->UnlockBits(lineData); delete lineBitmap; delete blurBitmap; } //---------------------------------------------------------------------------- // // CreateDrawingBitmap // // Create a bitmap to draw on. // //---------------------------------------------------------------------------- Gdiplus::Bitmap* CreateDrawingBitmap(Gdiplus::Rect lineBounds ) { Gdiplus::Bitmap* lineBitmap = new Gdiplus::Bitmap(lineBounds.Width, lineBounds.Height, PixelFormat32bppARGB); Gdiplus::Graphics lineGraphics(lineBitmap); return lineBitmap; } //---------------------------------------------------------------------------- // // DrawBitmapLine // // Creates a bitmap and draws a line on it. // //---------------------------------------------------------------------------- Gdiplus::Bitmap* DrawBitmapLine(Gdiplus::Rect lineBounds, POINT p1, POINT p2, Gdiplus::Pen *pen) { Gdiplus::Bitmap* lineBitmap = new Gdiplus::Bitmap(lineBounds.Width, lineBounds.Height, PixelFormat32bppARGB); Gdiplus::Graphics lineGraphics(lineBitmap); // Draw the line on the temporary bitmap lineGraphics.DrawLine(pen, static_cast(p1.x - lineBounds.X), static_cast(p1.y - lineBounds.Y), static_cast(p2.x - lineBounds.X), static_cast(p2.y - lineBounds.Y)); return lineBitmap; } //---------------------------------------------------------------------------- // // ColorFromColorRef // // Returns a color object from the colorRef that includes the alpha channel // //---------------------------------------------------------------------------- Gdiplus::Color ColorFromColorRef(DWORD colorRef) { BYTE a = (colorRef >> 24) & 0xFF; // Extract the alpha channel value BYTE b = (colorRef >> 16) & 0xFF; // Extract the red channel value BYTE g = (colorRef >> 8) & 0xFF; // Extract the green channel value BYTE r = colorRef & 0xFF; // Extract the blue channel value OutputDebug( L"ColorFromColorRef: %d %d %d %d\n", a, r, g, b ); return Gdiplus::Color(a, r, g, b); } //---------------------------------------------------------------------------- // // AdjustHighlighterColor // // Lighten the color. // //---------------------------------------------------------------------------- void AdjustHighlighterColor(BYTE* red, BYTE* green, BYTE* blue) { // Adjust the color to be more visible *red = min( 0xFF, *red ? *red + 0x40 : *red + 0x80 ); *green = min( 0xFF, *green ? *green + 0x40 : *green + 0x80); *blue = min( 0xFF, *blue ? *blue + 0x40 : *blue + 0x80); } //---------------------------------------------------------------------------- // // BlendColors // // Blends two colors together using the alpha channel of the second color. // The highlighter is the second color. // //---------------------------------------------------------------------------- COLORREF BlendColors(COLORREF color1, const Gdiplus::Color& color2) { BYTE redResult, greenResult, blueResult; // Extract the channels from the COLORREF BYTE red1 = GetRValue(color1); BYTE green1 = GetGValue(color1); BYTE blue1 = GetBValue(color1); // Get the channels and alpha from the Gdiplus::Color BYTE blue2 = color2.GetRed(); BYTE green2 = color2.GetGreen(); BYTE red2 = color2.GetBlue(); float alpha2 = color2.GetAlpha() / 255.0f; // Normalize to [0, 1] //alpha2 /= 2; // Use half the alpha for higher contrast // Don't blend grey's as much // int minValue = min(red1, min(green1, blue1)); // int maxValue = max(red1, max(green1, blue1)); if(TRUE) { // red1 > 0x10 && red1 < 0xC0 && (maxValue - minValue < 0x40)) { // This does a standard bright highlight alpha2 = 0; AdjustHighlighterColor( &red2, &green2, &blue2 ); redResult = red2 & red1; greenResult = green2 & green1; blueResult = blue2 & blue1; } else { // Blend each channel redResult = static_cast(red2 * alpha2 + red1 * (1 - alpha2)); greenResult = static_cast(green2 * alpha2 + green1 * (1 - alpha2)); blueResult = static_cast(blue2 * alpha2 + blue1 * (1 - alpha2)); } // Combine the result channels back into a COLORREF return RGB(redResult, greenResult, blueResult); } //---------------------------------------------------------------------------- // // DrawHighlightedShape // // Draws the shape with the highlighter color. // //---------------------------------------------------------------------------- void DrawHighlightedShape( DWORD Shape, HDC hdcScreenCompat, Gdiplus::Brush *pBrush, Gdiplus::Pen *pPen, int x1, int y1, int x2, int y2) { // Create a new bitmap that's the size of the area covered by the line + 2 * g_PenWidth Gdiplus::Rect lineBounds(min(x1, x2), min(y1, y2), abs(x2 - x1), abs(y2 - y1)); // Expand for line drawing if (Shape == DRAW_LINE) lineBounds.Inflate(static_cast(g_PenWidth / 2), static_cast(g_PenWidth / 2)); Gdiplus::Bitmap* lineBitmap = CreateDrawingBitmap(lineBounds); Gdiplus::Graphics lineGraphics(lineBitmap); switch (Shape) { case DRAW_RECTANGLE: lineGraphics.FillRectangle(pBrush, 0, 0, lineBounds.Width, lineBounds.Height); break; case DRAW_ELLIPSE: lineGraphics.FillEllipse( pBrush, 0, 0, lineBounds.Width, lineBounds.Height); break; case DRAW_LINE: lineGraphics.DrawLine(pPen, x1 - lineBounds.X, y1 - lineBounds.Y, x2 - lineBounds.X, y2 - lineBounds.Y); break; } Gdiplus::BitmapData* lineData = LockGdiPlusBitmap(lineBitmap); BYTE* pPixels = static_cast(lineData->Scan0); // Create a DIB section for efficient pixel manipulation BITMAPINFO bmi = { 0 }; bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmi.bmiHeader.biWidth = lineBounds.Width; bmi.bmiHeader.biHeight = -lineBounds.Height; // Top-down DIB bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biBitCount = 32; // 32 bits per pixel bmi.bmiHeader.biCompression = BI_RGB; VOID* pDIBBits; HBITMAP hDIB = CreateDIBSection(hdcScreenCompat, &bmi, DIB_RGB_COLORS, &pDIBBits, NULL, 0); HDC hdcDIB = CreateCompatibleDC(hdcScreenCompat); SelectObject(hdcDIB, hDIB); // Copy the relevant part of hdcScreenCompat to the DIB BitBlt(hdcDIB, 0, 0, lineBounds.Width, lineBounds.Height, hdcScreenCompat, lineBounds.X, lineBounds.Y, SRCCOPY); // Pointer to the DIB bits BYTE* pDestPixels = static_cast(pDIBBits); // Pointer to screen bits HDC hdcDIBOrig; HBITMAP hDibOrigBitmap, hDibBitmap; BYTE* pDestPixels2 = CreateBitmapMemoryDIB(hdcScreenCompat, hdcScreenCompat, &lineBounds, &hdcDIBOrig, &hDibBitmap, &hDibOrigBitmap); for (int y = 0; y < lineBounds.Height; ++y) { for (int x = 0; x < lineBounds.Width; ++x) { int index = (y * lineBounds.Width * 4) + (x * 4); // Assuming 4 bytes per pixel // BYTE b = pPixels[index + 0]; // Blue channel // BYTE g = pPixels[index + 1]; // Green channel // BYTE r = pPixels[index + 2]; // Red channel BYTE a = pPixels[index + 3]; // Alpha channel // Check if this is a drawn pixel if (a != 0) { // Assuming pDestPixels is a valid pointer to the destination bitmap's pixel data BYTE destB = pDestPixels2[index + 0]; // Blue channel BYTE destG = pDestPixels2[index + 1]; // Green channel BYTE destR = pDestPixels2[index + 2]; // Red channel // Create a COLORREF value from the destination pixel data COLORREF currentPixel = RGB(destR, destG, destB); // Blend the colors COLORREF newPixel = BlendColors(currentPixel, g_PenColor); // Update the destination pixel data with the new color pDestPixels[index + 0] = GetBValue(newPixel); pDestPixels[index + 1] = GetGValue(newPixel); pDestPixels[index + 2] = GetRValue(newPixel); } } } // Copy the updated DIB back to hdcScreenCompat BitBlt(hdcScreenCompat, lineBounds.X, lineBounds.Y, lineBounds.Width, lineBounds.Height, hdcDIB, 0, 0, SRCCOPY); // Clean up DeleteObject(hDIB); DeleteDC(hdcDIB); SelectObject(hdcDIBOrig, hDibOrigBitmap); DeleteObject(hDibBitmap); DeleteDC(hdcDIBOrig); // Invalidate the updated rectangle // InvalidateGdiplusRect(hWnd, lineBounds); } //---------------------------------------------------------------------------- // // CreateFadedDesktopBackground // // Creates a snapshot of the desktop that's faded and alpha blended with // black. // //---------------------------------------------------------------------------- HBITMAP CreateFadedDesktopBackground( HDC hdc, LPRECT rcScreen, LPRECT rcCrop ) { // create bitmap int width = rcScreen->right - rcScreen->left; int height = rcScreen->bottom - rcScreen->top; HDC hdcScreen = hdc; HDC hdcMem = CreateCompatibleDC( hdcScreen ); HBITMAP hBitmap = CreateCompatibleBitmap( hdcScreen, width, height ); HBITMAP hOld = static_cast(SelectObject( hdcMem, hBitmap )); HBRUSH hBrush = CreateSolidBrush(RGB(0, 0, 0)); // start with black background FillRect( hdcMem, rcScreen, hBrush ); if(rcCrop != NULL && rcCrop->left != -1 ) { // copy screen contents that are not cropped BitBlt(hdcMem, rcCrop->left, rcCrop->top, rcCrop->right - rcCrop->left, rcCrop->bottom - rcCrop->top, hdcScreen, rcCrop->left, rcCrop->top, SRCCOPY); } // blend screen contents into it BLENDFUNCTION blend = { 0 }; blend.BlendOp = AC_SRC_OVER; blend.BlendFlags = 0; blend.SourceConstantAlpha = 0x4F; blend.AlphaFormat = 0; AlphaBlend( hdcMem,0, 0, width, height, hdcScreen, rcScreen->left, rcScreen->top, width, height, blend ); SelectObject( hdcMem, hOld ); DeleteDC( hdcMem ); DeleteObject(hBrush); ReleaseDC( NULL, hdcScreen ); return hBitmap; } //---------------------------------------------------------------------------- // // AdjustToMoveBoundary // // Shifts to accomodate move boundary. // //---------------------------------------------------------------------------- void AdjustToMoveBoundary( float zoomLevel, int *coordinate, int cursor, int size, int max ) { int diff = static_cast (static_cast(size)/ static_cast(LIVEZOOM_MOVE_REGIONS)); if( cursor - *coordinate < diff ) *coordinate = max( 0, cursor - diff ); else if( (*coordinate + size) - cursor < diff ) *coordinate = min( cursor + diff - size, max - size ); } //---------------------------------------------------------------------------- // // GetZoomedTopLeftCoordinates // // Gets the left top coordinate of the zoomed area of the screen // //---------------------------------------------------------------------------- void GetZoomedTopLeftCoordinates( float zoomLevel, POINT *cursorPos, int *x, int width, int *y, int height ) { // smoother and more natural zoom in float scaledWidth = width/zoomLevel; float scaledHeight = height/zoomLevel; *x = max( 0, min( (int) (width - scaledWidth), (int) (cursorPos->x - (int) (((float) cursorPos->x/ (float) width)*scaledWidth)))); AdjustToMoveBoundary( zoomLevel, x, cursorPos->x, static_cast(scaledWidth), width ); *y = max( 0, min( (int) (height - scaledHeight), (int) (cursorPos->y - (int) (((float) cursorPos->y/ (float) height)*scaledHeight)))); AdjustToMoveBoundary( zoomLevel, y, cursorPos->y, static_cast(scaledHeight), height ); } //---------------------------------------------------------------------------- // // ScaleImage // // Use gdi+ for anti-aliased bitmap stretching. // //---------------------------------------------------------------------------- void ScaleImage( HDC hdcDst, float xDst, float yDst, float wDst, float hDst, HBITMAP bmSrc, float xSrc, float ySrc, float wSrc, float hSrc ) { Gdiplus::Graphics dstGraphics( hdcDst ); { Gdiplus::Bitmap srcBitmap( bmSrc, NULL ); dstGraphics.SetInterpolationMode( Gdiplus::InterpolationModeLowQuality ); dstGraphics.SetPixelOffsetMode( Gdiplus::PixelOffsetModeHalf ); dstGraphics.DrawImage( &srcBitmap, Gdiplus::RectF(xDst,yDst,wDst,hDst), xSrc, ySrc, wSrc, hSrc, Gdiplus::UnitPixel ); } } //---------------------------------------------------------------------------- // // GetEncoderClsid // //---------------------------------------------------------------------------- int GetEncoderClsid(const WCHAR* format, CLSID* pClsid) { UINT num = 0; // number of image encoders UINT size = 0; // size of the image encoder array in bytes using namespace Gdiplus; ImageCodecInfo* pImageCodecInfo = NULL; GetImageEncodersSize(&num, &size); if(size == 0) return -1; // Failure pImageCodecInfo = static_cast(malloc(size)); if(pImageCodecInfo == NULL) return -1; // Failure GetImageEncoders(num, size, pImageCodecInfo); for(UINT j = 0; j < num; ++j) { if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 ) { *pClsid = pImageCodecInfo[j].Clsid; free(pImageCodecInfo); return j; // Success } } free(pImageCodecInfo); return -1; // Failure } //---------------------------------------------------------------------- // // ConvertToUnicode // //---------------------------------------------------------------------- void ConvertToUnicode( PCHAR aString, PWCHAR wString, DWORD wStringLength ) { size_t len; len = MultiByteToWideChar( CP_ACP, 0, aString, static_cast(strlen(aString)), wString, wStringLength ); wString[len] = 0; } //---------------------------------------------------------------------------- // // LoadImageFile // // Use gdi+ to load an image. // //---------------------------------------------------------------------------- HBITMAP LoadImageFile( PTCHAR Filename ) { HBITMAP hBmp; Gdiplus::Bitmap *bitmap; bitmap = Gdiplus::Bitmap::FromFile(Filename); if( bitmap->GetHBITMAP( NULL, &hBmp )) { return NULL; } delete bitmap; return hBmp; } //---------------------------------------------------------------------------- // // SavePng // // Use gdi+ to save a PNG. // //---------------------------------------------------------------------------- DWORD SavePng( PTCHAR Filename, HBITMAP hBitmap ) { Gdiplus::Bitmap bitmap( hBitmap, NULL ); CLSID pngClsid; GetEncoderClsid(L"image/png", &pngClsid); if( bitmap.Save( Filename, &pngClsid, NULL )) { return GetLastError(); } return ERROR_SUCCESS; } //---------------------------------------------------------------------------- // // EnableDisableTrayIcon // //---------------------------------------------------------------------------- void EnableDisableTrayIcon( HWND hWnd, BOOLEAN Enable ) { NOTIFYICONDATA tNotifyIconData; memset( &tNotifyIconData, 0, sizeof(tNotifyIconData)); tNotifyIconData.cbSize = sizeof(NOTIFYICONDATA); tNotifyIconData.hWnd = hWnd; tNotifyIconData.uID = 1; tNotifyIconData.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP; tNotifyIconData.uCallbackMessage = WM_USER_TRAY_ACTIVATE; tNotifyIconData.hIcon = LoadIcon( g_hInstance, L"APPICON" ); lstrcpyn(tNotifyIconData.szTip, APPNAME, sizeof(APPNAME)); Shell_NotifyIcon(Enable ? NIM_ADD : NIM_DELETE, &tNotifyIconData); } //---------------------------------------------------------------------------- // // EnableDisableOpacity // //---------------------------------------------------------------------------- void EnableDisableOpacity( HWND hWnd, BOOLEAN Enable ) { DWORD exStyle; if( pSetLayeredWindowAttributes && g_BreakOpacity != 100 ) { if( Enable ) { exStyle = GetWindowLong(hWnd, GWL_EXSTYLE); SetWindowLong(hWnd, GWL_EXSTYLE, (exStyle | WS_EX_LAYERED)); pSetLayeredWindowAttributes(hWnd, 0, static_cast ((255 * g_BreakOpacity) / 100), LWA_ALPHA); RedrawWindow(hWnd, 0, 0, RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN); } else { exStyle = GetWindowLong(hWnd, GWL_EXSTYLE); SetWindowLong(hWnd, GWL_EXSTYLE, (exStyle & ~WS_EX_LAYERED)); } } } //---------------------------------------------------------------------------- // // EnableDisableScreenSaver // //---------------------------------------------------------------------------- void EnableDisableScreenSaver( BOOLEAN Enable ) { SystemParametersInfo(SPI_SETSCREENSAVEACTIVE,Enable,0,0); SystemParametersInfo(SPI_SETPOWEROFFACTIVE,Enable,0,0); SystemParametersInfo(SPI_SETLOWPOWERACTIVE,Enable,0,0); } //---------------------------------------------------------------------------- // // EnableDisableStickyKeys // //---------------------------------------------------------------------------- void EnableDisableStickyKeys( BOOLEAN Enable ) { static STICKYKEYS prevStickyKeyValue = {0}; STICKYKEYS newStickyKeyValue = {0}; // Need to do this on Vista tablet to stop sticky key popup when you // hold down the shift key and draw with the pen. if( Enable ) { if( prevStickyKeyValue.cbSize == sizeof(STICKYKEYS)) { SystemParametersInfo(SPI_SETSTICKYKEYS, sizeof(STICKYKEYS), &prevStickyKeyValue, SPIF_SENDCHANGE); } } else { prevStickyKeyValue.cbSize = sizeof(STICKYKEYS); if (SystemParametersInfo(SPI_GETSTICKYKEYS, sizeof(STICKYKEYS), &prevStickyKeyValue, 0)) { newStickyKeyValue.cbSize = sizeof(STICKYKEYS); newStickyKeyValue.dwFlags = 0; if( !SystemParametersInfo(SPI_SETSTICKYKEYS, sizeof(STICKYKEYS), &newStickyKeyValue, SPIF_SENDCHANGE)) { // DWORD error = GetLastError(); } } } } //---------------------------------------------------------------------------- // // GetKeyMod // //---------------------------------------------------------------------------- constexpr DWORD GetKeyMod( DWORD Key ) { DWORD keyMod = 0; if( (Key >> 8) & HOTKEYF_ALT ) keyMod |= MOD_ALT; if( (Key >> 8) & HOTKEYF_CONTROL) keyMod |= MOD_CONTROL; if( (Key >> 8) & HOTKEYF_SHIFT) keyMod |= MOD_SHIFT; if( (Key >> 8) & HOTKEYF_EXT) keyMod |= MOD_WIN; return keyMod; } //---------------------------------------------------------------------------- // // AdvancedBreakProc // //---------------------------------------------------------------------------- INT_PTR CALLBACK AdvancedBreakProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam ) { TCHAR opacity[10]; static TCHAR newSoundFile[MAX_PATH]; static TCHAR newBackgroundFile[MAX_PATH]; TCHAR filePath[MAX_PATH], initDir[MAX_PATH]; DWORD i; OPENFILENAME openFileName; switch ( message ) { case WM_INITDIALOG: if( pSHAutoComplete ) { pSHAutoComplete( GetDlgItem( hDlg, IDC_SOUND_FILE), SHACF_FILESYSTEM ); pSHAutoComplete( GetDlgItem( hDlg, IDC_BACKGROUND_FILE), SHACF_FILESYSTEM ); } CheckDlgButton( hDlg, IDC_CHECK_BACKGROUND_FILE, g_BreakShowBackgroundFile ? BST_CHECKED: BST_UNCHECKED ); CheckDlgButton( hDlg, IDC_CHECK_SOUND_FILE, g_BreakPlaySoundFile ? BST_CHECKED: BST_UNCHECKED ); CheckDlgButton( hDlg, IDC_CHECK_SHOW_EXPIRED, g_ShowExpiredTime ? BST_CHECKED : BST_UNCHECKED ); CheckDlgButton( hDlg, IDC_CHECK_BACKGROUND_STRETCH, g_BreakBackgroundStretch ? BST_CHECKED : BST_UNCHECKED ); #if 0 CheckDlgButton( hDlg, IDC_CHECK_SECONDARYDISPLAY, g_BreakOnSecondary ? BST_CHECKED : BST_UNCHECKED ); #endif if( pSetLayeredWindowAttributes == NULL ) { EnableWindow( GetDlgItem( hDlg, IDC_OPACITY ), FALSE ); } // sound file if( !g_BreakPlaySoundFile ) { EnableWindow( GetDlgItem( hDlg, IDC_STATIC_SOUND_FILE ), FALSE ); EnableWindow( GetDlgItem( hDlg, IDC_SOUND_FILE ), FALSE ); EnableWindow( GetDlgItem( hDlg, IDC_SOUND_BROWSE ), FALSE ); } _tcscpy( newSoundFile, g_BreakSoundFile ); _tcscpy( filePath, g_BreakSoundFile ); if( _tcsrchr( filePath, '\\' )) _tcscpy( filePath, _tcsrchr( g_BreakSoundFile, '\\' )+1); if( _tcsrchr( filePath, '.' )) *_tcsrchr( filePath, '.' ) = 0; SetDlgItemText( hDlg, IDC_SOUND_FILE, filePath ); // background file if( !g_BreakShowBackgroundFile ) { EnableWindow( GetDlgItem( hDlg, IDC_STATIC_DESKTOP_BACKGROUND ), FALSE ); EnableWindow( GetDlgItem( hDlg, IDC_STATIC_DESKTOP_BACKGROUND ), FALSE ); EnableWindow( GetDlgItem( hDlg, IDC_STATIC_BACKGROUND_FILE ), FALSE ); EnableWindow( GetDlgItem( hDlg, IDC_BACKGROUND_FILE ), FALSE ); EnableWindow( GetDlgItem( hDlg, IDC_BACKGROUND_BROWSE ), FALSE ); EnableWindow( GetDlgItem( hDlg, IDC_CHECK_BACKGROUND_STRETCH ), FALSE ); } CheckDlgButton( hDlg, g_BreakShowDesktop ? IDC_STATIC_DESKTOP_BACKGROUND : IDC_STATIC_BACKGROUND_FILE, BST_CHECKED ); _tcscpy( newBackgroundFile, g_BreakBackgroundFile ); SetDlgItemText( hDlg, IDC_BACKGROUND_FILE, g_BreakBackgroundFile ); CheckDlgButton( hDlg, IDC_TIMER_POS1 + g_BreakTimerPosition, BST_CHECKED ); for( i = 10; i <= 100; i += 10) { _stprintf( opacity, L"%d%%", i ); SendMessage( GetDlgItem( hDlg, IDC_OPACITY ), CB_ADDSTRING, 0, reinterpret_cast(opacity)); } SendMessage( GetDlgItem( hDlg, IDC_OPACITY ), CB_SETCURSEL, g_BreakOpacity / 10 - 1, 0 ); return TRUE; case WM_COMMAND: switch ( HIWORD( wParam )) { case BN_CLICKED: if( LOWORD( wParam ) == IDC_CHECK_SOUND_FILE ) { EnableWindow( GetDlgItem( hDlg, IDC_STATIC_SOUND_FILE ), IsDlgButtonChecked( hDlg, IDC_CHECK_SOUND_FILE) == BST_CHECKED ); EnableWindow( GetDlgItem( hDlg, IDC_SOUND_FILE ), IsDlgButtonChecked( hDlg, IDC_CHECK_SOUND_FILE) == BST_CHECKED ); EnableWindow( GetDlgItem( hDlg, IDC_SOUND_BROWSE ), IsDlgButtonChecked( hDlg, IDC_CHECK_SOUND_FILE) == BST_CHECKED ); } if( LOWORD( wParam ) == IDC_CHECK_BACKGROUND_FILE ) { EnableWindow( GetDlgItem( hDlg, IDC_CHECK_BACKGROUND_STRETCH ), IsDlgButtonChecked( hDlg, IDC_CHECK_BACKGROUND_FILE) == BST_CHECKED ); EnableWindow( GetDlgItem( hDlg, IDC_STATIC_DESKTOP_BACKGROUND ), IsDlgButtonChecked( hDlg, IDC_CHECK_BACKGROUND_FILE) == BST_CHECKED ); EnableWindow( GetDlgItem( hDlg, IDC_STATIC_BACKGROUND_FILE ), IsDlgButtonChecked( hDlg, IDC_CHECK_BACKGROUND_FILE) == BST_CHECKED ); EnableWindow( GetDlgItem( hDlg, IDC_BACKGROUND_FILE ), IsDlgButtonChecked( hDlg, IDC_CHECK_BACKGROUND_FILE) == BST_CHECKED ); EnableWindow( GetDlgItem( hDlg, IDC_BACKGROUND_BROWSE ), IsDlgButtonChecked( hDlg, IDC_CHECK_BACKGROUND_FILE) == BST_CHECKED ); } break; } switch ( LOWORD( wParam )) { case IDC_SOUND_BROWSE: memset( &openFileName, 0, sizeof(openFileName )); openFileName.lStructSize = OPENFILENAME_SIZE_VERSION_400; openFileName.hwndOwner = hDlg; openFileName.hInstance = static_cast(g_hInstance); openFileName.nMaxFile = sizeof(filePath)/sizeof(filePath[0]); openFileName.Flags = OFN_LONGNAMES; openFileName.lpstrTitle = L"请选择音频文件..."; openFileName.lpstrDefExt = L"*.wav"; openFileName.nFilterIndex = 1; openFileName.lpstrFilter = L"WAV 音频\0*.wav\0所有文件\0*.*\0"; GetDlgItemText( hDlg, IDC_SOUND_FILE, filePath, sizeof(filePath )); if( _tcsrchr( filePath, '\\' )) { _tcscpy( initDir, filePath ); _tcscpy( filePath, _tcsrchr( initDir, '\\' )+1); *(_tcsrchr( initDir, '\\' )+1) = 0; } else { _tcscpy( filePath, L"%WINDIR%\\Media" ); ExpandEnvironmentStrings( filePath, initDir, sizeof(initDir)/sizeof(initDir[0])); GetDlgItemText( hDlg, IDC_SOUND_FILE, filePath, sizeof(filePath )); } openFileName.lpstrInitialDir = initDir; openFileName.lpstrFile = filePath; if( GetOpenFileName( &openFileName )) { _tcscpy( newSoundFile, filePath ); if(_tcsrchr( filePath, '\\' )) _tcscpy( filePath, _tcsrchr( newSoundFile, '\\' )+1); if(_tcsrchr( filePath, '.' )) *_tcsrchr( filePath, '.' ) = 0; SetDlgItemText( hDlg, IDC_SOUND_FILE, filePath ); } break; case IDC_BACKGROUND_BROWSE: memset( &openFileName, 0, sizeof(openFileName )); openFileName.lStructSize = OPENFILENAME_SIZE_VERSION_400; openFileName.hwndOwner = hDlg; openFileName.hInstance = static_cast(g_hInstance); openFileName.nMaxFile = sizeof(filePath)/sizeof(filePath[0]); openFileName.Flags = OFN_LONGNAMES; openFileName.lpstrTitle = L"请选择背景图片文件..."; openFileName.lpstrDefExt = L"*.bmp"; openFileName.nFilterIndex = 5; openFileName.lpstrFilter = L"位图文件 (*.bmp;*.dib)\0*.bmp;*.dib\0" "PNG 文件 (*.png)\0*.png\0" "JPEG 文件 (*.jpg;*.jpeg;*.jpe;*.jfif)\0*.jpg;*.jpeg;*.jpe;*.jfif\0" "GIF 文件 (*.gif)\0*.gif\0" "所有图像文件\0.bmp;*.dib;*.png;*.jpg;*.jpeg;*.jpe;*.jfif;*.gif)\0" "所有文件\0*.*\0\0"; GetDlgItemText( hDlg, IDC_BACKGROUND_FILE, filePath, sizeof(filePath )); if(_tcsrchr( filePath, '\\' )) { _tcscpy( initDir, filePath ); _tcscpy( filePath, _tcsrchr( initDir, '\\' )+1); *(_tcsrchr( initDir, '\\' )+1) = 0; } else { _tcscpy( filePath, L"%USERPROFILE%\\Pictures" ); ExpandEnvironmentStrings( filePath, initDir, sizeof(initDir)/sizeof(initDir[0])); GetDlgItemText( hDlg, IDC_BACKGROUND_FILE, filePath, sizeof(filePath )); } openFileName.lpstrInitialDir = initDir; openFileName.lpstrFile = filePath; if( GetOpenFileName( &openFileName )) { _tcscpy( newBackgroundFile, filePath ); SetDlgItemText( hDlg, IDC_BACKGROUND_FILE, filePath ); } break; case IDOK: // sound file has to be valid g_BreakPlaySoundFile = IsDlgButtonChecked( hDlg, IDC_CHECK_SOUND_FILE ) == BST_CHECKED; g_BreakShowBackgroundFile = IsDlgButtonChecked( hDlg, IDC_CHECK_BACKGROUND_FILE ) == BST_CHECKED; g_BreakBackgroundStretch = IsDlgButtonChecked( hDlg, IDC_CHECK_BACKGROUND_STRETCH ) == BST_CHECKED; #if 0 g_BreakOnSecondary = IsDlgButtonChecked( hDlg, IDC_CHECK_SECONDARYDISPLAY ) == BST_CHECKED; #endif if( g_BreakPlaySoundFile && GetFileAttributes( newSoundFile ) == -1 ) { MessageBox( hDlg, L"无法访问所选的音频文件", L"倒计时高级选项错误", MB_ICONERROR ); break; } _tcscpy( g_BreakSoundFile, newSoundFile ); // Background file g_BreakShowDesktop = IsDlgButtonChecked( hDlg, IDC_STATIC_DESKTOP_BACKGROUND ) == BST_CHECKED; if( !g_BreakShowDesktop && g_BreakShowBackgroundFile && GetFileAttributes( newBackgroundFile ) == -1 ) { MessageBox( hDlg, L"无法访问所选的背景图像文件", L"倒计时高级选项错误", MB_ICONERROR ); break; } _tcscpy( g_BreakBackgroundFile, newBackgroundFile ); for( i = 0; i < 10; i++ ) { if( IsDlgButtonChecked( hDlg, IDC_TIMER_POS1+i) == BST_CHECKED ) { g_BreakTimerPosition = i; break; } } GetDlgItemText( hDlg, IDC_OPACITY, opacity, sizeof(opacity)/sizeof(opacity[0])); _stscanf( opacity, L"%d%%", &g_BreakOpacity ); reg.WriteRegSettings( RegSettings ); EndDialog(hDlg, 0); break; case IDCANCEL: EndDialog( hDlg, 0 ); return TRUE; } break; default: break; } return FALSE; } //---------------------------------------------------------------------------- // // OptionsTabProc // //---------------------------------------------------------------------------- INT_PTR CALLBACK OptionsTabProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam ) { HDC hDC; LOGFONT lf; CHOOSEFONT chooseFont; HFONT hFont; PAINTSTRUCT ps; HWND hTextPreview; HDC hDc; RECT previewRc; TCHAR filePath[MAX_PATH] = {0}; OPENFILENAME openFileName; switch ( message ) { case WM_INITDIALOG: return TRUE; case WM_COMMAND: switch ( LOWORD( wParam )) { case IDC_ADVANCED_BREAK: DialogBox( g_hInstance, L"ADVANCED_BREAK", hDlg, AdvancedBreakProc ); break; case IDC_FONT: hDC = GetDC (hDlg ); lf = g_LogFont; lf.lfHeight = -21; chooseFont.hDC = CreateCompatibleDC (hDC); ReleaseDC (hDlg, hDC); chooseFont.lStructSize = sizeof (CHOOSEFONT); chooseFont.hwndOwner = hDlg; chooseFont.lpLogFont = &lf; chooseFont.Flags = CF_SCREENFONTS|CF_ENABLETEMPLATE| CF_INITTOLOGFONTSTRUCT|CF_LIMITSIZE; chooseFont.rgbColors = RGB (0, 0, 0); chooseFont.lCustData = 0; chooseFont.nSizeMin = 16; chooseFont.nSizeMax = 16; chooseFont.hInstance = g_hInstance; chooseFont.lpszStyle = static_cast(NULL); chooseFont.nFontType = SCREEN_FONTTYPE; chooseFont.lpfnHook = reinterpret_cast(static_cast(NULL)); chooseFont.lpTemplateName = static_cast(MAKEINTRESOURCE (FORMATDLGORD31)); if( ChooseFont( &chooseFont ) ) { g_LogFont = lf; InvalidateRect( hDlg, NULL, TRUE ); } break; case IDC_DEMOTYPE_BROWSE: memset( &openFileName, 0, sizeof( openFileName ) ); openFileName.lStructSize = OPENFILENAME_SIZE_VERSION_400; openFileName.hwndOwner = hDlg; openFileName.hInstance = static_cast(g_hInstance); openFileName.nMaxFile = sizeof( filePath ) / sizeof( filePath[0] ); openFileName.Flags = OFN_LONGNAMES; openFileName.lpstrTitle = L"请选择自动打字文件..."; openFileName.nFilterIndex = 1; openFileName.lpstrFilter = L"所有文件\0*.*\0\0"; openFileName.lpstrFile = filePath; if( GetOpenFileName( &openFileName ) ) { if( GetFileAttributes( filePath ) == -1 ) { MessageBox( hDlg, L"无法访问所选文件", APPNAME, MB_ICONERROR ); } else { SetDlgItemText( g_OptionsTabs[DEMOTYPE_PAGE].hPage, IDC_DEMOTYPE_FILE, filePath ); _tcscpy( g_DemoTypeFile, filePath ); } } break; } break; case WM_PAINT: if( (hTextPreview = GetDlgItem( hDlg, IDC_TEXT_FONT ))) { // 16-pt preview LOGFONT _lf = g_LogFont; _lf.lfHeight = -21; hFont = CreateFontIndirect( &_lf); hDc = BeginPaint(hDlg, &ps); SelectObject( hDc, hFont ); GetWindowRect( hTextPreview, &previewRc ); MapWindowPoints( NULL, hDlg, reinterpret_cast(&previewRc), 2); previewRc.top += 6; DrawText( hDc, L"Sample", static_cast(_tcslen(L"Sample")), &previewRc, DT_CENTER|DT_VCENTER|DT_SINGLELINE ); EndPaint( hDlg, &ps ); DeleteObject( hFont ); } break; default: break; } return FALSE; } //---------------------------------------------------------------------------- // // OptionsAddTabs // //---------------------------------------------------------------------------- VOID OptionsAddTabs( HWND hOptionsDlg, HWND hTabCtrl ) { int i; TCITEM tcItem; RECT rc, pageRc; GetWindowRect( hTabCtrl, &rc ); for( i = 0; i < sizeof( g_OptionsTabs )/sizeof(g_OptionsTabs[0]); i++ ) { TCHAR* displayName = g_OptionsTabs[i].TabTitle; if (_tcscmp(displayName, _T("Zoom")) == 0) displayName = _T("放大"); else if (_tcscmp(displayName, _T("LiveZoom")) == 0) displayName = _T("实时放大"); else if (_tcscmp(displayName, _T("Draw")) == 0) displayName = _T("画图"); else if (_tcscmp(displayName, _T("Type")) == 0) displayName = _T("文本"); else if (_tcscmp(displayName, _T("DemoType")) == 0) displayName = _T("自动打字"); else if (_tcscmp(displayName, _T("Break")) == 0) displayName = _T("倒计时"); else if (_tcscmp(displayName, _T("Record")) == 0) displayName = _T("录屏"); else if (_tcscmp(displayName, _T("Snip")) == 0) displayName = _T("截图"); tcItem.mask = TCIF_TEXT; tcItem.pszText = displayName; TabCtrl_InsertItem( hTabCtrl, i, &tcItem ); g_OptionsTabs[i].hPage = CreateDialog( g_hInstance, g_OptionsTabs[i].TabTitle, hOptionsDlg, OptionsTabProc ); } TabCtrl_AdjustRect( hTabCtrl, FALSE, &rc ); for( i = 0; i < sizeof( g_OptionsTabs )/sizeof(g_OptionsTabs[0]); i++ ) { pageRc = rc; MapWindowPoints( NULL, g_OptionsTabs[i].hPage, reinterpret_cast(&pageRc), 2); SetWindowPos( g_OptionsTabs[i].hPage, HWND_TOP, pageRc.left, pageRc.top, pageRc.right - pageRc.left, pageRc.bottom - pageRc.top, SWP_NOACTIVATE|(i == 0 ? SWP_SHOWWINDOW : SWP_HIDEWINDOW)); if( pEnableThemeDialogTexture ) { pEnableThemeDialogTexture( g_OptionsTabs[i].hPage, ETDT_ENABLETAB ); } } } //---------------------------------------------------------------------------- // // UnregisterAllHotkeys // //---------------------------------------------------------------------------- void UnregisterAllHotkeys( HWND hWnd ) { UnregisterHotKey( hWnd, ZOOM_HOTKEY); UnregisterHotKey( hWnd, LIVE_HOTKEY); UnregisterHotKey( hWnd, LIVE_DRAW_HOTKEY); UnregisterHotKey( hWnd, DRAW_HOTKEY); UnregisterHotKey( hWnd, BREAK_HOTKEY); UnregisterHotKey( hWnd, RECORD_HOTKEY); UnregisterHotKey( hWnd, RECORD_CROP_HOTKEY ); UnregisterHotKey( hWnd, RECORD_WINDOW_HOTKEY ); UnregisterHotKey( hWnd, SNIP_HOTKEY ); UnregisterHotKey( hWnd, SNIP_SAVE_HOTKEY); UnregisterHotKey( hWnd, DEMOTYPE_HOTKEY ); UnregisterHotKey( hWnd, DEMOTYPE_RESET_HOTKEY ); } //---------------------------------------------------------------------------- // // RegisterAllHotkeys // //---------------------------------------------------------------------------- void RegisterAllHotkeys(HWND hWnd) { if (g_ToggleKey) RegisterHotKey(hWnd, ZOOM_HOTKEY, g_ToggleMod, g_ToggleKey & 0xFF); if (g_LiveZoomToggleKey) { RegisterHotKey(hWnd, LIVE_HOTKEY, g_LiveZoomToggleMod, g_LiveZoomToggleKey & 0xFF); RegisterHotKey(hWnd, LIVE_DRAW_HOTKEY, (g_LiveZoomToggleMod ^ MOD_SHIFT), g_LiveZoomToggleKey & 0xFF); } if (g_DrawToggleKey) RegisterHotKey(hWnd, DRAW_HOTKEY, g_DrawToggleMod, g_DrawToggleKey & 0xFF); if (g_BreakToggleKey) RegisterHotKey(hWnd, BREAK_HOTKEY, g_BreakToggleMod, g_BreakToggleKey & 0xFF); if (g_DemoTypeToggleKey) { RegisterHotKey(hWnd, DEMOTYPE_HOTKEY, g_DemoTypeToggleMod, g_DemoTypeToggleKey & 0xFF); RegisterHotKey(hWnd, DEMOTYPE_RESET_HOTKEY, (g_DemoTypeToggleMod ^ MOD_SHIFT), g_DemoTypeToggleKey & 0xFF); } if (g_SnipToggleKey) { RegisterHotKey(hWnd, SNIP_HOTKEY, g_SnipToggleMod, g_SnipToggleKey & 0xFF); RegisterHotKey(hWnd, SNIP_SAVE_HOTKEY, (g_SnipToggleMod ^ MOD_SHIFT), g_SnipToggleKey & 0xFF); } if (g_RecordToggleKey) { RegisterHotKey(hWnd, RECORD_HOTKEY, g_RecordToggleMod | MOD_NOREPEAT, g_RecordToggleKey & 0xFF); RegisterHotKey(hWnd, RECORD_CROP_HOTKEY, (g_RecordToggleMod ^ MOD_SHIFT) | MOD_NOREPEAT, g_RecordToggleKey & 0xFF); RegisterHotKey(hWnd, RECORD_WINDOW_HOTKEY, (g_RecordToggleMod ^ MOD_ALT) | MOD_NOREPEAT, g_RecordToggleKey & 0xFF); } } //---------------------------------------------------------------------------- // // UpdateDrawTabHeaderFont // //---------------------------------------------------------------------------- void UpdateDrawTabHeaderFont() { static HFONT headerFont = nullptr; TCHAR text[64]; if( headerFont != nullptr ) { DeleteObject( headerFont ); headerFont = nullptr; } constexpr int headers[] = { IDC_PEN_CONTROL, IDC_COLORS, IDC_HIGHLIGHT_AND_BLUR, IDC_SHAPES, IDC_SCREEN }; for( int i = 0; i < _countof( headers ); i++ ) { // Change the header font to bold HWND hHeader = GetDlgItem( g_OptionsTabs[DRAW_PAGE].hPage, headers[i] ); if( headerFont == nullptr ) { HFONT hFont = reinterpret_cast(SendMessage( hHeader, WM_GETFONT, 0, 0 )); LOGFONT lf = {}; GetObject( hFont, sizeof( LOGFONT ), &lf ); lf.lfWeight = FW_BOLD; headerFont = CreateFontIndirect( &lf ); } SendMessage( hHeader, WM_SETFONT, reinterpret_cast(headerFont), 0 ); // Resize the control to fit the text GetWindowText( hHeader, text, sizeof( text ) / sizeof( text[0] ) ); RECT rc; GetWindowRect( hHeader, &rc ); MapWindowPoints( NULL, g_OptionsTabs[DRAW_PAGE].hPage, reinterpret_cast(&rc), 2 ); HDC hDC = GetDC( hHeader ); SelectFont( hDC, headerFont ); DrawText( hDC, text, static_cast(_tcslen( text )), &rc, DT_CALCRECT | DT_SINGLELINE | DT_LEFT | DT_VCENTER ); ReleaseDC( hHeader, hDC ); SetWindowPos( hHeader, nullptr, 0, 0, rc.right - rc.left + ScaleForDpi( 4, GetDpiForWindowHelper( hHeader ) ), rc.bottom - rc.top, SWP_NOMOVE | SWP_NOZORDER ); } } //---------------------------------------------------------------------------- // // OptionsProc // //---------------------------------------------------------------------------- INT_PTR CALLBACK OptionsProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam ) { static HFONT hFontBold = nullptr; PNMLINK notify = nullptr; static int curTabSel = 0; static HWND hTabCtrl; static HWND hOpacity; static HWND hToggleKey; TCHAR text[32]; DWORD newToggleKey, newTimeout, newToggleMod, newBreakToggleKey, newDemoTypeToggleKey, newRecordToggleKey, newSnipToggleKey; DWORD newDrawToggleKey, newDrawToggleMod, newBreakToggleMod, newDemoTypeToggleMod, newRecordToggleMod, newSnipToggleMod; DWORD newLiveZoomToggleKey, newLiveZoomToggleMod; static std::vector> microphones; switch ( message ) { case WM_INITDIALOG: { if( hWndOptions ) { BringWindowToTop( hWndOptions ); SetFocus( hWndOptions ); SetForegroundWindow( hWndOptions ); EndDialog( hDlg, 0 ); return FALSE; } hWndOptions = hDlg; SetForegroundWindow( hDlg ); SetActiveWindow( hDlg ); SetWindowPos( hDlg, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE|SWP_SHOWWINDOW ); #if 1 // set version info TCHAR filePath[MAX_PATH]; const TCHAR* verString; GetModuleFileName(NULL, filePath, _countof(filePath)); DWORD zero = 0; DWORD infoSize = GetFileVersionInfoSize(filePath, &zero); void* versionInfo = malloc(infoSize); GetFileVersionInfo(filePath, 0, infoSize, versionInfo); verString = GetVersionString(static_cast(versionInfo), _T("FileVersion")); SetDlgItemText(hDlg, IDC_VERSION, (std::wstring(L"ZoomIt v") + verString).c_str()); // verString = GetVersionString(static_cast(versionInfo), _T("LegalCopyright")); verString = _T("版权所有 (C) Microsoft Corporation。保留所有权利。"); SetDlgItemText(hDlg, IDC_COPYRIGHT, verString); free(versionInfo); #endif // Add tabs hTabCtrl = GetDlgItem( hDlg, IDC_TAB ); OptionsAddTabs( hDlg, hTabCtrl ); InitializeFonts( hDlg, &hFontBold ); UpdateDrawTabHeaderFont(); // Configure options SendMessage( GetDlgItem( g_OptionsTabs[ZOOM_PAGE].hPage, IDC_HOTKEY), HKM_SETRULES, static_cast(HKCOMB_NONE), // invalid key combinations MAKELPARAM(HOTKEYF_ALT, 0)); // add ALT to invalid entries if( g_ToggleKey ) SendMessage( GetDlgItem( g_OptionsTabs[ZOOM_PAGE].hPage, IDC_HOTKEY), HKM_SETHOTKEY, g_ToggleKey, 0 ); if( pMagInitialize ) { if( g_LiveZoomToggleKey ) SendMessage( GetDlgItem( g_OptionsTabs[LIVE_PAGE].hPage, IDC_LIVE_HOTKEY), HKM_SETHOTKEY, g_LiveZoomToggleKey, 0 ); } else { EnableWindow( GetDlgItem( g_OptionsTabs[LIVE_PAGE].hPage, IDC_LIVE_HOTKEY), FALSE ); EnableWindow( GetDlgItem( g_OptionsTabs[LIVE_PAGE].hPage, IDC_ZOOM_LEVEL), FALSE ); EnableWindow( GetDlgItem( g_OptionsTabs[LIVE_PAGE].hPage, IDC_ZOOM_SPIN), FALSE ); } if( g_DrawToggleKey ) SendMessage( GetDlgItem( g_OptionsTabs[DRAW_PAGE].hPage, IDC_DRAW_HOTKEY), HKM_SETHOTKEY, g_DrawToggleKey, 0 ); if( g_BreakToggleKey ) SendMessage( GetDlgItem( g_OptionsTabs[BREAK_PAGE].hPage, IDC_BREAK_HOTKEY), HKM_SETHOTKEY, g_BreakToggleKey, 0 ); if( g_DemoTypeToggleKey ) SendMessage( GetDlgItem( g_OptionsTabs[DEMOTYPE_PAGE].hPage, IDC_DEMOTYPE_HOTKEY ), HKM_SETHOTKEY, g_DemoTypeToggleKey, 0 ); if( g_RecordToggleKey ) SendMessage( GetDlgItem( g_OptionsTabs[RECORD_PAGE].hPage, IDC_RECORD_HOTKEY), HKM_SETHOTKEY, g_RecordToggleKey, 0 ); if( g_SnipToggleKey) SendMessage( GetDlgItem( g_OptionsTabs[SNIP_PAGE].hPage, IDC_SNIP_HOTKEY), HKM_SETHOTKEY, g_SnipToggleKey, 0 ); CheckDlgButton( hDlg, IDC_SHOW_TRAY_ICON, g_ShowTrayIcon ? BST_CHECKED: BST_UNCHECKED ); CheckDlgButton( hDlg, IDC_AUTOSTART, IsAutostartConfigured() ? BST_CHECKED: BST_UNCHECKED ); CheckDlgButton( g_OptionsTabs[ZOOM_PAGE].hPage, IDC_ANIMATE_ZOOM, g_AnimateZoom ? BST_CHECKED: BST_UNCHECKED ); SendMessage( GetDlgItem(g_OptionsTabs[ZOOM_PAGE].hPage, IDC_ZOOM_SLIDER), TBM_SETRANGE, false, MAKELONG(0,_countof(g_ZoomLevels)-1) ); SendMessage( GetDlgItem(g_OptionsTabs[ZOOM_PAGE].hPage, IDC_ZOOM_SLIDER), TBM_SETPOS, true, g_SliderZoomLevel ); _stprintf( text, L"%d", g_PenWidth ); SetDlgItemText( g_OptionsTabs[DRAW_PAGE].hPage, IDC_PEN_WIDTH, text ); SendMessage( GetDlgItem( g_OptionsTabs[DRAW_PAGE].hPage, IDC_PEN_WIDTH ), EM_LIMITTEXT, 1, 0 ); SendMessage (GetDlgItem( g_OptionsTabs[DRAW_PAGE].hPage, IDC_SPIN), UDM_SETRANGE, 0L, MAKELPARAM (19, 1)); _stprintf( text, L"%d", g_BreakTimeout ); SetDlgItemText( g_OptionsTabs[BREAK_PAGE].hPage, IDC_TIMER, text ); SendMessage( GetDlgItem( g_OptionsTabs[BREAK_PAGE].hPage, IDC_TIMER ), EM_LIMITTEXT, 2, 0 ); SendMessage (GetDlgItem( g_OptionsTabs[BREAK_PAGE].hPage, IDC_SPIN_TIMER), UDM_SETRANGE, 0L, MAKELPARAM (99, 1)); CheckDlgButton( g_OptionsTabs[BREAK_PAGE].hPage, IDC_CHECK_SHOW_EXPIRED, g_ShowExpiredTime ? BST_CHECKED : BST_UNCHECKED ); CheckDlgButton( g_OptionsTabs[RECORD_PAGE].hPage, IDC_CAPTURE_AUDIO, g_CaptureAudio ? BST_CHECKED: BST_UNCHECKED ); for (int i = 0; i < _countof(g_FramerateOptions); i++) { _stprintf(text, L"%d", g_FramerateOptions[i]); SendMessage(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_RECORD_FRAME_RATE), static_cast(CB_ADDSTRING), static_cast(0), reinterpret_cast(text)); if (g_RecordFrameRate == g_FramerateOptions[i]) { SendMessage(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_RECORD_FRAME_RATE), CB_SETCURSEL, static_cast(i), static_cast(0)); } } for(unsigned int i = 1; i < 11; i++) { _stprintf(text, L"%2.1f", (static_cast(i)) / 10 ); SendMessage(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_RECORD_SCALING), static_cast(CB_ADDSTRING), static_cast(0), reinterpret_cast(text)); if (g_RecordScaling == i*10 ) { SendMessage(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_RECORD_SCALING), CB_SETCURSEL, static_cast(i)-1, static_cast(0)); } } // Get the current set of microphones microphones.clear(); concurrency::create_task([]{ auto devices = winrt::DeviceInformation::FindAllAsync( winrt::DeviceClass::AudioCapture ).get(); for( auto device : devices ) { microphones.emplace_back( device.Id().c_str(), device.Name().c_str() ); } }).get(); // Add the microphone devices to the combo box and set the current selection SendMessage( GetDlgItem( g_OptionsTabs[RECORD_PAGE].hPage, IDC_MICROPHONE ), static_cast(CB_ADDSTRING), static_cast(0), reinterpret_cast(L"默认麦克风")); size_t selection = 0; for( size_t i = 0; i < microphones.size(); i++ ) { SendMessage( GetDlgItem( g_OptionsTabs[RECORD_PAGE].hPage, IDC_MICROPHONE ), static_cast(CB_ADDSTRING), static_cast(0), reinterpret_cast(microphones[i].second.c_str()) ); if( selection == 0 && wcscmp( microphones[i].first.c_str(), g_MicrophoneDeviceId ) == 0 ) { selection = i + 1; } } SendMessage( GetDlgItem( g_OptionsTabs[RECORD_PAGE].hPage, IDC_MICROPHONE ), CB_SETCURSEL, static_cast(selection), static_cast(0) ); if( GetFileAttributes( g_DemoTypeFile ) == -1 ) { memset( g_DemoTypeFile, 0, sizeof( g_DemoTypeFile ) ); } else { SetDlgItemText( g_OptionsTabs[DEMOTYPE_PAGE].hPage, IDC_DEMOTYPE_FILE, g_DemoTypeFile ); } SendMessage( GetDlgItem( g_OptionsTabs[DEMOTYPE_PAGE].hPage, IDC_DEMOTYPE_SPEED_SLIDER ), TBM_SETRANGE, false, MAKELONG( MAX_TYPING_SPEED, MIN_TYPING_SPEED ) ); SendMessage( GetDlgItem( g_OptionsTabs[DEMOTYPE_PAGE].hPage, IDC_DEMOTYPE_SPEED_SLIDER ), TBM_SETPOS, true, g_DemoTypeSpeedSlider ); CheckDlgButton( g_OptionsTabs[DEMOTYPE_PAGE].hPage, IDC_DEMOTYPE_USER_DRIVEN, g_DemoTypeUserDriven ? BST_CHECKED: BST_UNCHECKED ); UnregisterAllHotkeys(GetParent( hDlg )); PostMessage( hDlg, WM_USER, 0, 0 ); return TRUE; } case WM_USER+100: BringWindowToTop( hDlg ); SetFocus( hDlg ); SetForegroundWindow( hDlg ); return TRUE; case WM_DPICHANGED: InitializeFonts( hDlg, &hFontBold ); UpdateDrawTabHeaderFont(); break; case WM_CTLCOLORSTATIC: if( reinterpret_cast(lParam) == GetDlgItem( hDlg, IDC_TITLE ) || reinterpret_cast(lParam) == GetDlgItem(hDlg, IDC_DRAWING) || reinterpret_cast(lParam) == GetDlgItem(hDlg, IDC_ZOOM) || reinterpret_cast(lParam) == GetDlgItem(hDlg, IDC_BREAK) || reinterpret_cast(lParam) == GetDlgItem( hDlg, IDC_TYPE )) { HDC hdc = reinterpret_cast(wParam); SetBkMode( hdc, TRANSPARENT ); SelectObject( hdc, hFontBold ); return PtrToLong(GetSysColorBrush( COLOR_BTNFACE )); } break; case WM_NOTIFY: notify = reinterpret_cast(lParam); if( notify->hdr.idFrom == IDC_LINK ) { switch( notify->hdr.code ) { case NM_CLICK: case NM_RETURN: ShellExecute( hDlg, _T("open"), notify->item.szUrl, NULL, NULL, SW_SHOWNORMAL ); break; } } else switch( notify->hdr.code ) { case TCN_SELCHANGE: ShowWindow( g_OptionsTabs[curTabSel].hPage, SW_HIDE ); curTabSel = TabCtrl_GetCurSel(hTabCtrl); ShowWindow( g_OptionsTabs[curTabSel].hPage, SW_SHOW ); break; } break; case WM_COMMAND: switch ( LOWORD( wParam )) { case IDOK: { if( !ConfigureAutostart( hDlg, IsDlgButtonChecked( hDlg, IDC_AUTOSTART) == BST_CHECKED )) { break; } g_ShowTrayIcon = IsDlgButtonChecked( hDlg, IDC_SHOW_TRAY_ICON ) == BST_CHECKED; g_AnimateZoom = IsDlgButtonChecked( g_OptionsTabs[ZOOM_PAGE].hPage, IDC_ANIMATE_ZOOM ) == BST_CHECKED; g_DemoTypeUserDriven = IsDlgButtonChecked( g_OptionsTabs[DEMOTYPE_PAGE].hPage, IDC_DEMOTYPE_USER_DRIVEN ) == BST_CHECKED; newToggleKey = static_cast(SendMessage( GetDlgItem( g_OptionsTabs[ZOOM_PAGE].hPage, IDC_HOTKEY), HKM_GETHOTKEY, 0, 0 )); newLiveZoomToggleKey = static_cast(SendMessage( GetDlgItem( g_OptionsTabs[LIVE_PAGE].hPage, IDC_LIVE_HOTKEY), HKM_GETHOTKEY, 0, 0 )); newDrawToggleKey = static_cast(SendMessage( GetDlgItem( g_OptionsTabs[DRAW_PAGE].hPage, IDC_DRAW_HOTKEY), HKM_GETHOTKEY, 0, 0 )); newBreakToggleKey = static_cast(SendMessage( GetDlgItem( g_OptionsTabs[BREAK_PAGE].hPage, IDC_BREAK_HOTKEY), HKM_GETHOTKEY, 0, 0 )); newDemoTypeToggleKey = static_cast(SendMessage( GetDlgItem( g_OptionsTabs[DEMOTYPE_PAGE].hPage, IDC_DEMOTYPE_HOTKEY ), HKM_GETHOTKEY, 0, 0 )); newRecordToggleKey = static_cast(SendMessage(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_RECORD_HOTKEY), HKM_GETHOTKEY, 0, 0)); newSnipToggleKey = static_cast(SendMessage( GetDlgItem( g_OptionsTabs[SNIP_PAGE].hPage, IDC_SNIP_HOTKEY), HKM_GETHOTKEY, 0, 0 )); newToggleMod = GetKeyMod( newToggleKey ); newLiveZoomToggleMod = GetKeyMod( newLiveZoomToggleKey ); newDrawToggleMod = GetKeyMod( newDrawToggleKey ); newBreakToggleMod = GetKeyMod( newBreakToggleKey ); newDemoTypeToggleMod = GetKeyMod( newDemoTypeToggleKey ); newRecordToggleMod = GetKeyMod(newRecordToggleKey); newSnipToggleMod = GetKeyMod( newSnipToggleKey ); g_SliderZoomLevel = static_cast(SendMessage( GetDlgItem(g_OptionsTabs[ZOOM_PAGE].hPage, IDC_ZOOM_SLIDER), TBM_GETPOS, 0, 0 )); g_DemoTypeSpeedSlider = static_cast(SendMessage( GetDlgItem( g_OptionsTabs[DEMOTYPE_PAGE].hPage, IDC_DEMOTYPE_SPEED_SLIDER ), TBM_GETPOS, 0, 0 )); g_ShowExpiredTime = IsDlgButtonChecked( g_OptionsTabs[BREAK_PAGE].hPage, IDC_CHECK_SHOW_EXPIRED ) == BST_CHECKED; g_CaptureAudio = IsDlgButtonChecked(g_OptionsTabs[RECORD_PAGE].hPage, IDC_CAPTURE_AUDIO) == BST_CHECKED; GetDlgItemText( g_OptionsTabs[BREAK_PAGE].hPage, IDC_TIMER, text, 3 ); text[2] = 0; newTimeout = _tstoi( text ); g_RecordFrameRate = g_FramerateOptions[SendMessage(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_RECORD_FRAME_RATE), static_cast(CB_GETCURSEL), static_cast(0), static_cast(0))]; g_RecordScaling = static_cast(SendMessage(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_RECORD_SCALING), static_cast(CB_GETCURSEL), static_cast(0), static_cast(0)) * 10 + 10); // Get the selected microphone int index = static_cast(SendMessage( GetDlgItem( g_OptionsTabs[RECORD_PAGE].hPage, IDC_MICROPHONE ), static_cast(CB_GETCURSEL), static_cast(0), static_cast(0) )); _tcscpy( g_MicrophoneDeviceId, index == 0 ? L"" : microphones[static_cast(index) - 1].first.c_str() ); if( newToggleKey && !RegisterHotKey( GetParent( hDlg ), ZOOM_HOTKEY, newToggleMod, newToggleKey & 0xFF )) { MessageBox( hDlg, L"此快捷键已被占用。\n请重新选择放大快捷键。", APPNAME, MB_ICONERROR ); UnregisterAllHotkeys(GetParent( hDlg )); break; } else if(newLiveZoomToggleKey && (!RegisterHotKey( GetParent( hDlg ), LIVE_HOTKEY, newLiveZoomToggleMod, newLiveZoomToggleKey & 0xFF ) || !RegisterHotKey(GetParent(hDlg), LIVE_DRAW_HOTKEY, (newLiveZoomToggleMod ^ MOD_SHIFT), newLiveZoomToggleKey & 0xFF))) { MessageBox( hDlg, L"此快捷键已被占用。\n请重新选择实时放大快捷键。", APPNAME, MB_ICONERROR ); UnregisterAllHotkeys(GetParent( hDlg )); break; } else if( newDrawToggleKey && !RegisterHotKey( GetParent( hDlg ), DRAW_HOTKEY, newDrawToggleMod, newDrawToggleKey & 0xFF )) { MessageBox( hDlg, L"此快捷键已被占用。\n请重新选择画图快捷键。", APPNAME, MB_ICONERROR ); UnregisterAllHotkeys(GetParent( hDlg )); break; } else if( newBreakToggleKey && !RegisterHotKey( GetParent( hDlg ), BREAK_HOTKEY, newBreakToggleMod, newBreakToggleKey & 0xFF )) { MessageBox( hDlg, L"此快捷键已被占用。\n请重新选择倒计时快捷键。", APPNAME, MB_ICONERROR ); UnregisterAllHotkeys(GetParent( hDlg )); break; } else if( newDemoTypeToggleKey && (!RegisterHotKey( GetParent( hDlg ), DEMOTYPE_HOTKEY, newDemoTypeToggleMod, newDemoTypeToggleKey & 0xFF ) || !RegisterHotKey(GetParent(hDlg), DEMOTYPE_RESET_HOTKEY, (newDemoTypeToggleMod ^ MOD_SHIFT), newDemoTypeToggleKey & 0xFF))) { MessageBox( hDlg, L"此快捷键已被占用。\n请重新选择自动打字快捷键。", APPNAME, MB_ICONERROR ); UnregisterAllHotkeys( GetParent( hDlg ) ); break; } else if (newSnipToggleKey && (!RegisterHotKey(GetParent(hDlg), SNIP_HOTKEY, newSnipToggleMod, newSnipToggleKey & 0xFF) || !RegisterHotKey(GetParent(hDlg), SNIP_SAVE_HOTKEY, (newSnipToggleMod ^ MOD_SHIFT), newSnipToggleKey & 0xFF))) { MessageBox(hDlg, L"此快捷键已被占用。\n请重新选择截图快捷键。", APPNAME, MB_ICONERROR); UnregisterAllHotkeys(GetParent(hDlg)); break; } else if( newRecordToggleKey && (!RegisterHotKey(GetParent(hDlg), RECORD_HOTKEY, newRecordToggleMod | MOD_NOREPEAT, newRecordToggleKey & 0xFF) || !RegisterHotKey(GetParent(hDlg), RECORD_CROP_HOTKEY, (newRecordToggleMod ^ MOD_SHIFT) | MOD_NOREPEAT, newRecordToggleKey & 0xFF) || !RegisterHotKey(GetParent(hDlg), RECORD_WINDOW_HOTKEY, (newRecordToggleMod ^ MOD_ALT) | MOD_NOREPEAT, newRecordToggleKey & 0xFF))) { MessageBox(hDlg, L"此快捷键已被占用。\n请重新选择录屏快捷键。", APPNAME, MB_ICONERROR); UnregisterAllHotkeys(GetParent(hDlg)); break; } else { g_BreakTimeout = newTimeout; g_ToggleKey = newToggleKey; g_LiveZoomToggleKey = newLiveZoomToggleKey; g_ToggleMod = newToggleMod; g_DrawToggleKey = newDrawToggleKey; g_DrawToggleMod = newDrawToggleMod; g_BreakToggleKey = newBreakToggleKey; g_BreakToggleMod = newBreakToggleMod; g_DemoTypeToggleKey = newDemoTypeToggleKey; g_DemoTypeToggleMod = newDemoTypeToggleMod; g_RecordToggleKey = newRecordToggleKey; g_RecordToggleMod = newRecordToggleMod; g_SnipToggleKey = newSnipToggleKey; g_SnipToggleMod = newSnipToggleMod; reg.WriteRegSettings( RegSettings ); EnableDisableTrayIcon( GetParent( hDlg ), g_ShowTrayIcon ); hWndOptions = NULL; EndDialog( hDlg, 0 ); return TRUE; } break; } case IDCANCEL: RegisterAllHotkeys(GetParent(hDlg)); hWndOptions = NULL; EndDialog( hDlg, 0 ); return TRUE; } break; case WM_CLOSE: hWndOptions = NULL; RegisterAllHotkeys(GetParent(hDlg)); EndDialog( hDlg, 0 ); return TRUE; default: break; } return FALSE; } //---------------------------------------------------------------------------- // // DeleteDrawUndoList // //---------------------------------------------------------------------------- void DeleteDrawUndoList( P_DRAW_UNDO *DrawUndoList ) { P_DRAW_UNDO nextUndo; nextUndo = *DrawUndoList; while( nextUndo ) { *DrawUndoList = nextUndo->Next; DeleteObject( nextUndo->hBitmap ); DeleteDC( nextUndo->hDc ); free( nextUndo ); nextUndo = *DrawUndoList; } *DrawUndoList = NULL; } //---------------------------------------------------------------------------- // // PopDrawUndo // //---------------------------------------------------------------------------- BOOLEAN PopDrawUndo( HDC hDc, P_DRAW_UNDO *DrawUndoList, int width, int height ) { P_DRAW_UNDO nextUndo; nextUndo = *DrawUndoList; if( nextUndo ) { BitBlt( hDc, 0, 0, width, height, nextUndo->hDc, 0, 0, SRCCOPY|CAPTUREBLT ); *DrawUndoList = nextUndo->Next; DeleteObject( nextUndo->hBitmap ); DeleteDC( nextUndo->hDc ); free( nextUndo ); return TRUE; } else { Beep( 700, 200 ); return FALSE; } } //---------------------------------------------------------------------------- // // DeleteOldestUndo // //---------------------------------------------------------------------------- void DeleteOldestUndo( P_DRAW_UNDO *DrawUndoList ) { P_DRAW_UNDO nextUndo, freeUndo = NULL, prevUndo = NULL; nextUndo = *DrawUndoList; freeUndo = nextUndo; do { prevUndo = freeUndo; freeUndo = nextUndo; nextUndo = nextUndo->Next; } while( nextUndo ); if( freeUndo ) { DeleteObject( freeUndo->hBitmap ); DeleteDC( freeUndo->hDc ); free( freeUndo ); if( prevUndo != *DrawUndoList ) prevUndo->Next = NULL; else *DrawUndoList = NULL; } } //---------------------------------------------------------------------------- // // GetOldestUndo // //---------------------------------------------------------------------------- P_DRAW_UNDO GetOldestUndo(P_DRAW_UNDO DrawUndoList) { P_DRAW_UNDO nextUndo, oldestUndo = NULL; nextUndo = DrawUndoList; oldestUndo = nextUndo; do { oldestUndo = nextUndo; nextUndo = nextUndo->Next; } while( nextUndo ); return oldestUndo; } //---------------------------------------------------------------------------- // // PushDrawUndo // //---------------------------------------------------------------------------- void PushDrawUndo( HDC hDc, P_DRAW_UNDO *DrawUndoList, int width, int height ) { P_DRAW_UNDO nextUndo, newUndo; int i = 0; HBITMAP hUndoBitmap; OutputDebug(L"PushDrawUndo\n"); // Don't store more than 8 undo's (XP gets really upset when we // exhaust heap with them) nextUndo = *DrawUndoList; do { i++; if( i == MAX_UNDO_HISTORY ) { DeleteOldestUndo( DrawUndoList ); break; } if( nextUndo ) nextUndo = nextUndo->Next; } while( nextUndo ); hUndoBitmap = CreateCompatibleBitmap( hDc, width, height ); if( !hUndoBitmap && *DrawUndoList ) { // delete the oldest and try again DeleteOldestUndo( DrawUndoList ); hUndoBitmap = CreateCompatibleBitmap( hDc, width, height ); } if( hUndoBitmap ) { newUndo = static_cast(malloc( sizeof( DRAW_UNDO ))); if (newUndo != NULL) { newUndo->hDc = CreateCompatibleDC(hDc); newUndo->hBitmap = hUndoBitmap; SelectObject(newUndo->hDc, newUndo->hBitmap); BitBlt(newUndo->hDc, 0, 0, width, height, hDc, 0, 0, SRCCOPY | CAPTUREBLT); newUndo->Next = *DrawUndoList; *DrawUndoList = newUndo; } } } //---------------------------------------------------------------------------- // // DeleteTypedText // //---------------------------------------------------------------------------- void DeleteTypedText( P_TYPED_KEY *TypedKeyList ) { P_TYPED_KEY nextKey; while( *TypedKeyList ) { nextKey = (*TypedKeyList)->Next; free( *TypedKeyList ); *TypedKeyList = nextKey; } } //---------------------------------------------------------------------------- // // BlankScreenArea // //---------------------------------------------------------------------------- void BlankScreenArea( HDC hDc, PRECT Rc, int BlankMode ) { if( BlankMode == 'K' ) { HBRUSH hBrush = CreateSolidBrush( RGB( 0, 0, 0 )); FillRect( hDc, Rc, hBrush ); DeleteObject( static_cast(hBrush) ); } else { FillRect( hDc, Rc, GetSysColorBrush( COLOR_WINDOW )); } } //---------------------------------------------------------------------------- // // ClearTypingCursor // //---------------------------------------------------------------------------- void ClearTypingCursor( HDC hdcScreenCompat, HDC hdcScreenCursorCompat, RECT rc, int BlankMode ) { if( false ) { // BlankMode ) { BlankScreenArea( hdcScreenCompat, &rc, BlankMode ); } else { BitBlt(hdcScreenCompat, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, hdcScreenCursorCompat,0, 0, SRCCOPY|CAPTUREBLT ); } } //---------------------------------------------------------------------------- // // DrawTypingCursor // //---------------------------------------------------------------------------- void DrawTypingCursor( HWND hWnd, POINT *textPt, HDC hdcScreenCompat, HDC hdcScreenCursorCompat, RECT *rc, bool centerUnderSystemCursor = false ) { // Draw the typing cursor rc->left = textPt->x; rc->top = textPt->y; TCHAR vKey = '|'; DrawText( hdcScreenCompat, static_cast(&vKey), 1, rc, DT_CALCRECT ); // LiveDraw uses a layered window which means mouse messages pass through // to lower windows unless the system cursor is above a painted area. // Centering the typing cursor directly under the system cursor allows // us to capture the mouse wheel input required to change font size. if( centerUnderSystemCursor ) { const LONG halfWidth = static_cast( (rc->right - rc->left) / 2 ); const LONG halfHeight = static_cast( (rc->bottom - rc->top) / 2 ); rc->left -= halfWidth; rc->right -= halfWidth; rc->top -= halfHeight; rc->bottom -= halfHeight; textPt->x = rc->left; textPt->y = rc->top; } BitBlt(hdcScreenCursorCompat, 0, 0, rc->right -rc->left, rc->bottom - rc->top, hdcScreenCompat, rc->left, rc->top, SRCCOPY|CAPTUREBLT ); DrawText( hdcScreenCompat, static_cast(&vKey), 1, rc, DT_LEFT ); InvalidateRect( hWnd, NULL, TRUE ); } //---------------------------------------------------------------------------- // // BoundMouse // //---------------------------------------------------------------------------- RECT BoundMouse( float zoomLevel, MONITORINFO *monInfo, int width, int height, POINT *cursorPos ) { RECT rc; int x, y; GetZoomedTopLeftCoordinates( zoomLevel, cursorPos, &x, width, &y, height ); rc.left = monInfo->rcMonitor.left + x; rc.right = rc.left + static_cast(width/zoomLevel); rc.top = monInfo->rcMonitor.top + y; rc.bottom = rc.top + static_cast(height/zoomLevel); OutputDebug( L"x: %d y: %d width: %d height: %d zoomLevel: %g\n", cursorPos->x, cursorPos->y, width, height, zoomLevel); OutputDebug( L"left: %d top: %d right: %d bottom: %d\n", rc.left, rc.top, rc.right, rc.bottom); OutputDebug( L"mon.left: %d mon.top: %d mon.right: %d mon.bottom: %d\n", monInfo->rcMonitor.left, monInfo->rcMonitor.top, monInfo->rcMonitor.right, monInfo->rcMonitor.bottom); ClipCursor( &rc ); return rc; } //---------------------------------------------------------------------------- // // DrawArrow // //---------------------------------------------------------------------------- void DrawArrow( HDC hdc, int x1, int y1, int x2, int y2, double length, double width, bool UseGdiplus ) { // get normalized dx/dy double dx = static_cast(x2) - x1; double dy = static_cast(y2) - y1; double bodyLen = sqrt( dx*dx + dy*dy ); if ( bodyLen ) { dx /= bodyLen; dy /= bodyLen; } else { dx = 1; dy = 0; } // get midpoint of base int xMid = x2 - static_cast(length*dx+0.5); int yMid = y2 - static_cast(length*dy+0.5); // get left wing int xLeft = xMid - static_cast(dy*width+0.5); int yLeft = yMid + static_cast(dx*width+0.5); // get right wing int xRight = xMid + static_cast(dy*width+0.5); int yRight = yMid - static_cast(dx*width+0.5); // Bring in midpoint to make a nicer arrow xMid = x2 - static_cast(length/2*dx+0.5); yMid = y2 - static_cast(length/2*dy+0.5); if (UseGdiplus) { Gdiplus::Graphics dstGraphics(hdc); if( ( GetWindowLong( g_hWndMain, GWL_EXSTYLE ) & WS_EX_LAYERED ) == 0 ) { dstGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias); } Gdiplus::Color color = ColorFromColorRef(g_PenColor); Gdiplus::Pen pen(color, static_cast(g_PenWidth)); pen.SetLineCap(Gdiplus::LineCapRound, Gdiplus::LineCapRound, Gdiplus::DashCapRound); #if 0 Gdiplus::PointF pts[] = { {(Gdiplus::REAL)x1, (Gdiplus::REAL)y1}, {(Gdiplus::REAL)xMid, (Gdiplus::REAL)yMid}, {(Gdiplus::REAL)xLeft, (Gdiplus::REAL)yLeft}, {(Gdiplus::REAL)x2, (Gdiplus::REAL)y2}, {(Gdiplus::REAL)xRight, (Gdiplus::REAL)yRight}, {(Gdiplus::REAL)xMid, (Gdiplus::REAL)yMid} }; dstGraphics.DrawPolygon(&pen, pts, _countof(pts)); #else Gdiplus::GraphicsPath path; path.StartFigure(); path.AddLine(static_cast(x1), static_cast(y1), static_cast(x2), static_cast(y2)); path.AddLine(static_cast(x2), static_cast(y2), static_cast(xMid), static_cast(yMid)); path.AddLine(static_cast(xMid), static_cast(yMid), static_cast(xLeft), static_cast(yLeft)); path.AddLine(static_cast(xLeft), static_cast(yLeft), static_cast(x2), static_cast(y2)); path.AddLine(static_cast(x2), static_cast(y2), static_cast(xRight), static_cast(yRight)); path.AddLine(static_cast(xRight), static_cast(yRight), static_cast(xMid), static_cast(yMid)); pen.SetLineJoin(Gdiplus::LineJoinRound); dstGraphics.DrawPath(&pen, &path); #endif } else { POINT pts[] = { x1, y1, xMid, yMid, xLeft, yLeft, x2, y2, xRight, yRight, xMid, yMid }; // draw arrow head filled with current color HBRUSH hBrush = CreateSolidBrush(g_PenColor); HBRUSH hOldBrush = SelectBrush(hdc, hBrush); Polygon(hdc, pts, sizeof(pts) / sizeof(pts[0])); DeleteObject(hBrush); SelectObject(hdc, hOldBrush); } } //---------------------------------------------------------------------------- // // DrawShape // //---------------------------------------------------------------------------- VOID DrawShape( DWORD Shape, HDC hDc, RECT *Rect, bool UseGdiPlus = false ) { bool isBlur = false; Gdiplus::Graphics dstGraphics(hDc); if( ( GetWindowLong( g_hWndMain, GWL_EXSTYLE ) & WS_EX_LAYERED ) == 0 ) { dstGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias); } Gdiplus::Color color = ColorFromColorRef(g_PenColor); Gdiplus::Pen pen(color, static_cast(g_PenWidth)); pen.SetLineCap(Gdiplus::LineCapRound, Gdiplus::LineCapRound, Gdiplus::DashCapRound); // Check for highlighting or blur Gdiplus::Brush *pBrush = NULL; if (PEN_COLOR_HIGHLIGHT(g_PenColor)) { // Use half the alpha for higher contrast DWORD newColor = g_PenColor & 0xFFFFFF | ((g_AlphaBlend / 2) << 24); pBrush = new Gdiplus::SolidBrush(ColorFromColorRef(newColor)); if(UseGdiPlus && Shape != DRAW_LINE && Shape != DRAW_ARROW) InflateRect(Rect, g_PenWidth/2, g_PenWidth/2); } else if ((g_PenColor & 0xFFFFFF) == COLOR_BLUR) { if (UseGdiPlus && Shape != DRAW_LINE && Shape != DRAW_ARROW) InflateRect(Rect, g_PenWidth / 2, g_PenWidth / 2); isBlur = true; } switch (Shape) { case DRAW_RECTANGLE: if (UseGdiPlus) if(pBrush) DrawHighlightedShape(DRAW_RECTANGLE, hDc, pBrush, NULL, static_cast(Rect->left - 1), static_cast(Rect->top - 1), static_cast(Rect->right), static_cast(Rect->bottom)); else if (isBlur) DrawBlurredShape( DRAW_RECTANGLE, &pen, hDc, &dstGraphics, static_cast(Rect->left - 1), static_cast(Rect->top - 1), static_cast(Rect->right), static_cast(Rect->bottom) ); else dstGraphics.DrawRectangle(&pen, Gdiplus::Rect::Rect(Rect->left - 1, Rect->top - 1, Rect->right - Rect->left, Rect->bottom - Rect->top)); else Rectangle(hDc, Rect->left, Rect->top, Rect->right, Rect->bottom); break; case DRAW_ELLIPSE: if (UseGdiPlus) if (pBrush) DrawHighlightedShape(DRAW_ELLIPSE, hDc, pBrush, NULL, static_cast(Rect->left - 1), static_cast(Rect->top - 1), static_cast(Rect->right), static_cast(Rect->bottom)); else if (isBlur) DrawBlurredShape( DRAW_ELLIPSE, &pen, hDc, &dstGraphics, static_cast(Rect->left - 1), static_cast(Rect->top - 1), static_cast(Rect->right), static_cast(Rect->bottom)); else dstGraphics.DrawEllipse(&pen, Gdiplus::Rect::Rect(Rect->left - 1, Rect->top - 1, Rect->right - Rect->left, Rect->bottom - Rect->top)); else Ellipse(hDc, Rect->left, Rect->top, Rect->right, Rect->bottom); break; case DRAW_LINE: if (UseGdiPlus) if (pBrush) DrawHighlightedShape(DRAW_LINE, hDc, NULL, &pen, static_cast(Rect->left), static_cast(Rect->top), static_cast(Rect->right), static_cast(Rect->bottom)); else if (isBlur) DrawBlurredShape(DRAW_LINE, &pen, hDc, &dstGraphics, static_cast(Rect->left), static_cast(Rect->top), static_cast(Rect->right), static_cast(Rect->bottom)); else dstGraphics.DrawLine(&pen, static_cast(Rect->left - 1), static_cast(Rect->top - 1), static_cast(Rect->right), static_cast(Rect->bottom)); else { MoveToEx(hDc, Rect->left, Rect->top, NULL); LineTo(hDc, Rect->right + 1, Rect->bottom + 1); } break; case DRAW_ARROW: DrawArrow(hDc, Rect->right + 1, Rect->bottom + 1, Rect->left, Rect->top, static_cast(g_PenWidth) * 2.5, static_cast(g_PenWidth) * 1.5, UseGdiPlus); break; } if( pBrush ) delete pBrush; } //---------------------------------------------------------------------------- // // SendPenMessage // // Inserts the pen message marker. // //---------------------------------------------------------------------------- VOID SendPenMessage(HWND hWnd, UINT Message, LPARAM lParam) { WPARAM wParam = 0; // // Get key states // if(GetKeyState(VK_LCONTROL) < 0 ) { wParam |= MK_CONTROL; } if( GetKeyState( VK_LSHIFT) < 0 || GetKeyState( VK_RSHIFT) < 0 ) { wParam |= MK_SHIFT; } SetMessageExtraInfo(static_cast(MI_WP_SIGNATURE)); SendMessage(hWnd, Message, wParam, lParam); } //---------------------------------------------------------------------------- // // ScalePenPosition // // Maps pen input to mouse input coordinates based on zoom level. Returns // 0 if pen is active but we didn't send this message to ourselves (pen // signature will be missing). // //---------------------------------------------------------------------------- LPARAM ScalePenPosition( float zoomLevel, MONITORINFO *monInfo, RECT boundRc, UINT message, LPARAM lParam ) { RECT rc; WORD x, y; LPARAM extraInfo; extraInfo = GetMessageExtraInfo(); if( g_PenDown ) { // ignore messages we didn't tag as pen if (extraInfo == MI_WP_SIGNATURE) { OutputDebug( L"Tablet Pen message\n"); // tablet input: don't bound the cursor ClipCursor(NULL); x = LOWORD(lParam); y = HIWORD(lParam); x = static_cast((x - static_cast(monInfo->rcMonitor.left))/ zoomLevel) + static_cast(boundRc.left - monInfo->rcMonitor.left); y = static_cast((y - static_cast(monInfo->rcMonitor.top)) / zoomLevel) + static_cast(boundRc.top - monInfo->rcMonitor.top); lParam = MAKELPARAM(x, y); } else { OutputDebug(L"Ignore pen message we didn't send\n"); lParam = 0; } } else { if( !GetClipCursor( &rc )) { ClipCursor( &boundRc ); } OutputDebug( L"Mouse message\n"); } return lParam; } //---------------------------------------------------------------------------- // // DrawHighlightedCursor // //---------------------------------------------------------------------------- BOOLEAN DrawHighlightedCursor( float ZoomLevel, int Width, int Height ) { DWORD zoomWidth = static_cast (static_cast(Width)/ZoomLevel); DWORD zoomHeight = static_cast (static_cast(Height)/ZoomLevel); if( g_PenWidth < 5 && zoomWidth > g_PenWidth * 100 && zoomHeight > g_PenWidth * 100 ) { return TRUE; } else { return FALSE; } } //---------------------------------------------------------------------------- // // InvalidateCursorMoveArea // //---------------------------------------------------------------------------- void InvalidateCursorMoveArea( HWND hWnd, float zoomLevel, int width, int height, POINT currentPt, POINT prevPt, POINT cursorPos ) { int x, y; RECT rc; int invWidth = g_PenWidth; if( DrawHighlightedCursor( zoomLevel, width, height ) ) { invWidth = g_PenWidth * 3 + 1; } GetZoomedTopLeftCoordinates( zoomLevel, &cursorPos, &x, width, &y, height ); rc.left = static_cast(max( 0, (int) ((min( prevPt.x, currentPt.x)-invWidth - x) * zoomLevel))); rc.right = static_cast((max( prevPt.x, currentPt.x)+invWidth - x) * zoomLevel); rc.top = static_cast(max( 0, (int) ((min( prevPt.y, currentPt.y)-invWidth - y) * zoomLevel))); rc.bottom = static_cast((max( prevPt.y, currentPt.y)+invWidth -y) * zoomLevel); InvalidateRect( hWnd, &rc, FALSE ); OutputDebug( L"INVALIDATE: (%d, %d) - (%d, %d)\n", rc.left, rc.top, rc.right, rc.bottom); } //---------------------------------------------------------------------------- // // SavCursorArea // //---------------------------------------------------------------------------- void SaveCursorArea( HDC hDcTarget, HDC hDcSource, POINT pt ) { OutputDebug( L"SaveCursorArea\n"); int penWidth = g_PenWidth + 2; BitBlt( hDcTarget, 0, 0, penWidth +CURSOR_ARM_LENGTH*2, penWidth +CURSOR_ARM_LENGTH*2, hDcSource, static_cast (pt.x- penWidth /2)-CURSOR_ARM_LENGTH, static_cast(pt.y- penWidth /2)-CURSOR_ARM_LENGTH, SRCCOPY|CAPTUREBLT ); } //---------------------------------------------------------------------------- // // RestoreCursorArea // //---------------------------------------------------------------------------- void RestoreCursorArea( HDC hDcTarget, HDC hDcSource, POINT pt ) { OutputDebug( L"RestoreCursorArea\n"); int penWidth = g_PenWidth + 2; BitBlt( hDcTarget, static_cast(pt.x- penWidth /2)-CURSOR_ARM_LENGTH, static_cast(pt.y- penWidth /2)-CURSOR_ARM_LENGTH, penWidth +CURSOR_ARM_LENGTH*2, penWidth + CURSOR_ARM_LENGTH*2, hDcSource, 0, 0, SRCCOPY|CAPTUREBLT ); } //---------------------------------------------------------------------------- // // DrawCursor // //---------------------------------------------------------------------------- void DrawCursor( HDC hDcTarget, POINT pt, float ZoomLevel, int Width, int Height ) { RECT rc; if( g_DrawPointer ) { Gdiplus::Graphics dstGraphics(hDcTarget); if( ( GetWindowLong( g_hWndMain, GWL_EXSTYLE ) & WS_EX_LAYERED ) == 0 ) { dstGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias); } Gdiplus::Color color = ColorFromColorRef(g_PenColor); Gdiplus::Pen pen(color, static_cast(g_PenWidth)); rc.left = pt.x - CURSOR_ARM_LENGTH; rc.right = pt.x + CURSOR_ARM_LENGTH; rc.top = pt.y - CURSOR_ARM_LENGTH; rc.bottom = pt.y + CURSOR_ARM_LENGTH; Gdiplus::GraphicsPath path; path.StartFigure(); path.AddLine(static_cast(rc.left) - 1, static_cast(rc.top) - 1, static_cast(rc.right), static_cast(rc.bottom)); path.AddLine(static_cast(rc.left) - 2, static_cast(rc.top) - 1, rc.left + (rc.right - rc.left) / 2, rc.top - 1); path.AddLine(static_cast(rc.left) - 1, static_cast(rc.top) - 2, rc.left - 1, rc.top + (rc.bottom - rc.top) / 2); path.AddLine(static_cast(rc.left) - 1, static_cast(rc.top) - 2, rc.left - 1, rc.top + (rc.bottom - rc.top) / 2); path.AddLine(static_cast(rc.left + (rc.right - rc.left) / 2), rc.top - 1, rc.left - 1, rc.top + (rc.bottom - rc.top) / 2); pen.SetLineJoin(Gdiplus::LineJoinRound); dstGraphics.DrawPath(&pen, &path); OutputDebug(L"DrawPointer: %d %d %d %d\n", rc.left, rc.top, rc.right, rc.bottom); } else if( DrawHighlightedCursor( ZoomLevel, Width, Height )) { OutputDebug(L"DrawHighlightedCursor: %d %d %d %d\n", pt.x, pt.y, g_PenWidth, g_PenWidth); Gdiplus::Graphics dstGraphics(hDcTarget); if( ( GetWindowLong( g_hWndMain, GWL_EXSTYLE ) & WS_EX_LAYERED ) == 0 ) { dstGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias); } Gdiplus::Color color = ColorFromColorRef(g_PenColor); Gdiplus::Pen pen(color, static_cast(g_PenWidth)); Gdiplus::GraphicsPath path; path.StartFigure(); pen.SetLineJoin(Gdiplus::LineJoinRound); path.AddLine(static_cast(pt.x - CURSOR_ARM_LENGTH), pt.y, pt.x + CURSOR_ARM_LENGTH, pt.y); path.CloseFigure(); path.StartFigure(); pen.SetLineJoin(Gdiplus::LineJoinRound); path.AddLine(static_cast(pt.x), pt.y - CURSOR_ARM_LENGTH, pt.x, pt.y + CURSOR_ARM_LENGTH); path.CloseFigure(); dstGraphics.DrawPath(&pen, &path); } else { Gdiplus::Graphics dstGraphics(hDcTarget); { if( ( GetWindowLong( g_hWndMain, GWL_EXSTYLE ) & WS_EX_LAYERED ) == 0 ) { dstGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias); } Gdiplus::Color color = ColorFromColorRef(g_PenColor); Gdiplus::SolidBrush solidBrush(color); dstGraphics.FillEllipse(&solidBrush, static_cast(pt.x-g_PenWidth/2), static_cast(pt.y-g_PenWidth/2), static_cast(g_PenWidth), static_cast(g_PenWidth)); } } } //---------------------------------------------------------------------------- // // ResizePen // //---------------------------------------------------------------------------- void ResizePen( HWND hWnd, HDC hdcScreenCompat, HDC hdcScreenCursorCompat, POINT prevPt, BOOLEAN g_Tracing, BOOLEAN *g_Drawing, float g_LiveZoomLevel, BOOLEAN isUser, int newWidth ) { if( !g_Tracing ) { RestoreCursorArea( hdcScreenCompat, hdcScreenCursorCompat, prevPt ); } OutputDebug( L"RESIZE_PEN-PRE: penWidth: %d ", g_PenWidth ); int prevWidth = g_PenWidth; if( g_ZoomOnLiveZoom ) { if( isUser ) { // Amplify user delta proportional to LiveZoomLevel newWidth = g_PenWidth + static_cast ((newWidth - static_cast(g_PenWidth))*g_LiveZoomLevel); } g_PenWidth = min( max( newWidth, MIN_PEN_WIDTH ), min( static_cast(MAX_PEN_WIDTH * g_LiveZoomLevel), MAX_LIVE_PEN_WIDTH ) ); g_RootPenWidth = static_cast(g_PenWidth / g_LiveZoomLevel); } else { g_PenWidth = min( max( newWidth, MIN_PEN_WIDTH ), MAX_PEN_WIDTH ); g_RootPenWidth = g_PenWidth; } if(prevWidth == static_cast(g_PenWidth) ) { // No change return; } OutputDebug( L"newWidth: %d\nRESIZE_PEN-POST: penWidth: %d\n", newWidth, g_PenWidth ); reg.WriteRegSettings( RegSettings ); SaveCursorArea( hdcScreenCursorCompat, hdcScreenCompat, prevPt ); *g_Drawing = FALSE; EnableDisableStickyKeys( TRUE ); SendMessage( hWnd, WM_LBUTTONDOWN, -1, MAKELPARAM(prevPt.x, prevPt.y) ); } //---------------------------------------------------------------------------- // // IsPenInverted // //---------------------------------------------------------------------------- bool IsPenInverted( WPARAM wParam ) { POINTER_INPUT_TYPE pointerType; POINTER_PEN_INFO penInfo; return pGetPointerType( GET_POINTERID_WPARAM( wParam ), &pointerType ) && ( pointerType == PT_PEN ) && pGetPointerPenInfo( GET_POINTERID_WPARAM( wParam ), &penInfo ) && ( penInfo.penFlags & PEN_FLAG_INVERTED ); } //---------------------------------------------------------------------------- // // CaptureScreenshotAsync // // Captures the specified screen using the capture APIs // //---------------------------------------------------------------------------- std::future> CaptureScreenshotAsync(winrt::IDirect3DDevice const& device, winrt::GraphicsCaptureItem const& item, winrt::DirectXPixelFormat const& pixelFormat) { auto d3dDevice = GetDXGIInterfaceFromObject(device); winrt::com_ptr d3dContext; d3dDevice->GetImmediateContext(d3dContext.put()); // Creating our frame pool with CreateFreeThreaded means that we // will be called back from the frame pool's internal worker thread // instead of the thread we are currently on. It also disables the // DispatcherQueue requirement. auto framePool = winrt::Direct3D11CaptureFramePool::CreateFreeThreaded( device, pixelFormat, 1, item.Size()); auto session = framePool.CreateCaptureSession(item); wil::shared_event captureEvent(wil::EventOptions::ManualReset); winrt::Direct3D11CaptureFrame frame{ nullptr }; framePool.FrameArrived([&frame, captureEvent](auto& framePool, auto&) { frame = framePool.TryGetNextFrame(); // Complete the operation captureEvent.SetEvent(); }); session.IsCursorCaptureEnabled( false ); session.StartCapture(); co_await winrt::resume_on_signal(captureEvent.get()); // End the capture session.Close(); framePool.Close(); auto texture = GetDXGIInterfaceFromObject(frame.Surface()); auto result = util::CopyD3DTexture(d3dDevice, texture, true); co_return result; } //---------------------------------------------------------------------------- // // CaptureScreenshot // // Captures the specified screen using the capture APIs // //---------------------------------------------------------------------------- winrt::com_ptrCaptureScreenshot(winrt::DirectXPixelFormat const& pixelFormat) { auto d3dDevice = util::CreateD3D11Device(); auto dxgiDevice = d3dDevice.as(); auto device = CreateDirect3DDevice(dxgiDevice.get()); // Get the active MONITOR capture device HMONITOR hMon = NULL; POINT cursorPos = { 0, 0 }; if (pMonitorFromPoint) { GetCursorPos(&cursorPos); hMon = pMonitorFromPoint(cursorPos, MONITOR_DEFAULTTONEAREST); } auto item = util::CreateCaptureItemForMonitor(hMon); auto capture = CaptureScreenshotAsync(device, item, pixelFormat); capture.wait(); return capture.get(); } //---------------------------------------------------------------------------- // // CopyD3DTexture // //---------------------------------------------------------------------------- inline auto CopyD3DTexture(winrt::com_ptr const& device, winrt::com_ptr const& texture, bool asStagingTexture) { winrt::com_ptr context; device->GetImmediateContext(context.put()); D3D11_TEXTURE2D_DESC desc = {}; texture->GetDesc(&desc); // Clear flags that we don't need desc.Usage = asStagingTexture ? D3D11_USAGE_STAGING : D3D11_USAGE_DEFAULT; desc.BindFlags = asStagingTexture ? 0 : D3D11_BIND_SHADER_RESOURCE; desc.CPUAccessFlags = asStagingTexture ? D3D11_CPU_ACCESS_READ : 0; desc.MiscFlags = 0; // Create and fill the texture copy winrt::com_ptr textureCopy; winrt::check_hresult(device->CreateTexture2D(&desc, nullptr, textureCopy.put())); context->CopyResource(textureCopy.get(), texture.get()); return textureCopy; } //---------------------------------------------------------------------------- // // PrepareStagingTexture // //---------------------------------------------------------------------------- inline auto PrepareStagingTexture(winrt::com_ptr const& device, winrt::com_ptr const& texture) { // If our texture is already set up for staging, then use it. // Otherwise, create a staging texture. D3D11_TEXTURE2D_DESC desc = {}; texture->GetDesc(&desc); if (desc.Usage == D3D11_USAGE_STAGING && desc.CPUAccessFlags & D3D11_CPU_ACCESS_READ) { return texture; } return CopyD3DTexture(device, texture, true); } //---------------------------------------------------------------------------- // // GetBytesPerPixel // //---------------------------------------------------------------------------- inline size_t GetBytesPerPixel(DXGI_FORMAT pixelFormat) { switch (pixelFormat) { case DXGI_FORMAT_R32G32B32A32_TYPELESS: case DXGI_FORMAT_R32G32B32A32_FLOAT: case DXGI_FORMAT_R32G32B32A32_UINT: case DXGI_FORMAT_R32G32B32A32_SINT: return 16; case DXGI_FORMAT_R32G32B32_TYPELESS: case DXGI_FORMAT_R32G32B32_FLOAT: case DXGI_FORMAT_R32G32B32_UINT: case DXGI_FORMAT_R32G32B32_SINT: return 12; case DXGI_FORMAT_R16G16B16A16_TYPELESS: case DXGI_FORMAT_R16G16B16A16_FLOAT: case DXGI_FORMAT_R16G16B16A16_UNORM: case DXGI_FORMAT_R16G16B16A16_UINT: case DXGI_FORMAT_R16G16B16A16_SNORM: case DXGI_FORMAT_R16G16B16A16_SINT: case DXGI_FORMAT_R32G32_TYPELESS: case DXGI_FORMAT_R32G32_FLOAT: case DXGI_FORMAT_R32G32_UINT: case DXGI_FORMAT_R32G32_SINT: case DXGI_FORMAT_R32G8X24_TYPELESS: return 8; case DXGI_FORMAT_D32_FLOAT_S8X24_UINT: case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS: case DXGI_FORMAT_X32_TYPELESS_G8X24_UINT: case DXGI_FORMAT_R10G10B10A2_TYPELESS: case DXGI_FORMAT_R10G10B10A2_UNORM: case DXGI_FORMAT_R10G10B10A2_UINT: case DXGI_FORMAT_R11G11B10_FLOAT: case DXGI_FORMAT_R8G8B8A8_TYPELESS: case DXGI_FORMAT_R8G8B8A8_UNORM: case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: case DXGI_FORMAT_R8G8B8A8_UINT: case DXGI_FORMAT_R8G8B8A8_SNORM: case DXGI_FORMAT_R8G8B8A8_SINT: case DXGI_FORMAT_R16G16_TYPELESS: case DXGI_FORMAT_R16G16_FLOAT: case DXGI_FORMAT_UNKNOWN: case DXGI_FORMAT_R16G16_UINT: case DXGI_FORMAT_R16G16_SNORM: case DXGI_FORMAT_R16G16_SINT: case DXGI_FORMAT_R32_TYPELESS: case DXGI_FORMAT_D32_FLOAT: case DXGI_FORMAT_R32_FLOAT: case DXGI_FORMAT_R32_UINT: case DXGI_FORMAT_R32_SINT: case DXGI_FORMAT_R24G8_TYPELESS: case DXGI_FORMAT_D24_UNORM_S8_UINT: case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: case DXGI_FORMAT_X24_TYPELESS_G8_UINT: case DXGI_FORMAT_R8G8_B8G8_UNORM: case DXGI_FORMAT_G8R8_G8B8_UNORM: case DXGI_FORMAT_B8G8R8A8_UNORM: case DXGI_FORMAT_B8G8R8X8_UNORM: case DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM: case DXGI_FORMAT_B8G8R8A8_TYPELESS: case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: case DXGI_FORMAT_B8G8R8X8_TYPELESS: case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: return 4; case DXGI_FORMAT_R8G8_TYPELESS: case DXGI_FORMAT_R8G8_UNORM: case DXGI_FORMAT_R8G8_UINT: case DXGI_FORMAT_R8G8_SNORM: case DXGI_FORMAT_R8G8_SINT: case DXGI_FORMAT_R16_TYPELESS: case DXGI_FORMAT_R16_FLOAT: case DXGI_FORMAT_D16_UNORM: case DXGI_FORMAT_R16_UNORM: case DXGI_FORMAT_R16_UINT: case DXGI_FORMAT_R16_SNORM: case DXGI_FORMAT_R16_SINT: case DXGI_FORMAT_B5G6R5_UNORM: case DXGI_FORMAT_B5G5R5A1_UNORM: case DXGI_FORMAT_B4G4R4A4_UNORM: return 2; case DXGI_FORMAT_R8_TYPELESS: case DXGI_FORMAT_R8_UNORM: case DXGI_FORMAT_R8_UINT: case DXGI_FORMAT_R8_SNORM: case DXGI_FORMAT_R8_SINT: case DXGI_FORMAT_A8_UNORM: return 1; default: throw winrt::hresult_invalid_argument(L"Invalid pixel format!"); } } //---------------------------------------------------------------------------- // // CopyBytesFromTexture // //---------------------------------------------------------------------------- inline auto CopyBytesFromTexture(winrt::com_ptr const& texture, uint32_t subresource = 0) { winrt::com_ptr device; texture->GetDevice(device.put()); winrt::com_ptr context; device->GetImmediateContext(context.put()); auto stagingTexture = PrepareStagingTexture(device, texture); D3D11_TEXTURE2D_DESC desc = {}; stagingTexture->GetDesc(&desc); auto bytesPerPixel = GetBytesPerPixel(desc.Format); // Copy the bits D3D11_MAPPED_SUBRESOURCE mapped = {}; winrt::check_hresult(context->Map(stagingTexture.get(), subresource, D3D11_MAP_READ, 0, &mapped)); auto bytesStride = static_cast(desc.Width) * bytesPerPixel; std::vector bytes(bytesStride * static_cast(desc.Height), 0); auto source = static_cast(mapped.pData); auto dest = bytes.data(); for (auto i = 0; i < static_cast(desc.Height); i++) { memcpy(dest, source, bytesStride); source += mapped.RowPitch; dest += bytesStride; } context->Unmap(stagingTexture.get(), 0); return bytes; } //---------------------------------------------------------------------------- // // StopRecording // //---------------------------------------------------------------------------- void StopRecording() { if( g_RecordToggle == TRUE ) { g_SelectRectangle.Stop(); if ( g_RecordingSession != nullptr ) { g_RecordingSession->Close(); g_RecordingSession = nullptr; } g_RecordToggle = FALSE; #if WINDOWS_CURSOR_RECORDING_WORKAROUND if( g_hWndLiveZoom != NULL && g_LiveZoomLevelOne ) { if( IsWindowVisible( g_hWndLiveZoom ) ) { ShowWindow( g_hWndLiveZoom, SW_HIDE ); DestroyWindow( g_hWndLiveZoom ); g_LiveZoomLevelOne = false; } } #endif } } //---------------------------------------------------------------------------- // // GetUniqueRecordingFilename // // Gets a unique file name for recording saves, using the " (N)" suffix // approach so that the user can hit OK without worrying about overwriting // if they are making multiple recordings in one session or don't want to // always see an overwrite dialog or stop to clean up files. // //---------------------------------------------------------------------------- auto GetUniqueRecordingFilename() { std::filesystem::path path{ g_RecordingSaveLocation }; // Chop off index if it's there auto base = std::regex_replace( path.stem().wstring(), std::wregex( L" [(][0-9]+[)]$" ), L"" ); path.replace_filename( base + path.extension().wstring() ); for( int index = 1; std::filesystem::exists( path ); index++ ) { // File exists, so increment number to avoid collision path.replace_filename( base + L" (" + std::to_wstring(index) + L')' + path.extension().wstring() ); } return path.stem().wstring() + path.extension().wstring(); } //---------------------------------------------------------------------------- // // StartRecordingAsync // // Starts the screen recording. // //---------------------------------------------------------------------------- winrt::fire_and_forget StartRecordingAsync( HWND hWnd, LPRECT rcCrop, HWND hWndRecord ) try { auto tempFolderPath = std::filesystem::temp_directory_path().wstring(); auto tempFolder = co_await winrt::StorageFolder::GetFolderFromPathAsync( tempFolderPath ); auto appFolder = co_await tempFolder.CreateFolderAsync( L"ZoomIt", winrt::CreationCollisionOption::OpenIfExists ); auto file = co_await appFolder.CreateFileAsync( L"zoomit.mp4", winrt::CreationCollisionOption::ReplaceExisting ); // Get the device auto d3dDevice = util::CreateD3D11Device(); auto dxgiDevice = d3dDevice.as(); g_RecordDevice = CreateDirect3DDevice( dxgiDevice.get() ); // Get the active MONITOR capture device HMONITOR hMon = NULL; POINT cursorPos = { 0, 0 }; if( pMonitorFromPoint ) { GetCursorPos( &cursorPos ); hMon = pMonitorFromPoint( cursorPos, MONITOR_DEFAULTTONEAREST ); } winrt::Windows::Graphics::Capture::GraphicsCaptureItem item{ nullptr }; if( hWndRecord ) item = util::CreateCaptureItemForWindow( hWndRecord ); else item = util::CreateCaptureItemForMonitor( hMon ); auto stream = co_await file.OpenAsync( winrt::FileAccessMode::ReadWrite ); g_RecordingSession = VideoRecordingSession::Create( g_RecordDevice, item, *rcCrop, g_RecordFrameRate, g_CaptureAudio, stream ); if( g_hWndLiveZoom != NULL ) g_RecordingSession->EnableCursorCapture( false ); co_await g_RecordingSession->StartAsync(); // g_RecordingSession isn't null if we're aborting a recording if( g_RecordingSession == nullptr ) { g_bSaveInProgress = true; SendMessage( g_hWndMain, WM_USER_SAVE_CURSOR, 0, 0 ); winrt::StorageFile destFile = nullptr; HRESULT hr = S_OK; try { auto saveDialog = wil::CoCreateInstance( CLSID_FileSaveDialog ); FILEOPENDIALOGOPTIONS options; if( SUCCEEDED( saveDialog->GetOptions( &options ) ) ) saveDialog->SetOptions( options | FOS_FORCEFILESYSTEM ); wil::com_ptr videosItem; if( SUCCEEDED ( SHGetKnownFolderItem( FOLDERID_Videos, KF_FLAG_DEFAULT, nullptr, IID_IShellItem, (void**) videosItem.put() ) ) ) saveDialog->SetDefaultFolder( videosItem.get() ); saveDialog->SetDefaultExtension( L".mp4" ); COMDLG_FILTERSPEC fileTypes[] = { { L"MP4 视频", L"*.mp4" } }; saveDialog->SetFileTypes( _countof( fileTypes ), fileTypes ); if( g_RecordingSaveLocation.size() == 0) { wil::com_ptr shellItem; wil::unique_cotaskmem_string folderPath; if (SUCCEEDED(saveDialog->GetFolder(shellItem.put())) && SUCCEEDED(shellItem->GetDisplayName(SIGDN_FILESYSPATH, folderPath.put()))) g_RecordingSaveLocation = folderPath.get(); g_RecordingSaveLocation = std::filesystem::path{ g_RecordingSaveLocation } /= DEFAULT_RECORDING_FILE; } auto suggestedName = GetUniqueRecordingFilename(); saveDialog->SetFileName( suggestedName.c_str() ); THROW_IF_FAILED( saveDialog->Show( hWnd ) ); wil::com_ptr shellItem; THROW_IF_FAILED(saveDialog->GetResult(shellItem.put())); wil::unique_cotaskmem_string filePath; THROW_IF_FAILED(shellItem->GetDisplayName(SIGDN_FILESYSPATH, filePath.put())); auto path = std::filesystem::path( filePath.get() ); winrt::StorageFolder folder{ co_await winrt::StorageFolder::GetFolderFromPathAsync( path.parent_path().c_str() ) }; destFile = co_await folder.CreateFileAsync( path.filename().c_str(), winrt::CreationCollisionOption::ReplaceExisting ); } catch( const wil::ResultException& error ) { hr = error.GetErrorCode(); } if( destFile == nullptr ) { co_await file.DeleteAsync(); } else { co_await file.MoveAndReplaceAsync( destFile ); g_RecordingSaveLocation = file.Path(); SaveToClipboard(g_RecordingSaveLocation.c_str(), hWnd); } g_bSaveInProgress = false; SendMessage( g_hWndMain, WM_USER_RESTORE_CURSOR, 0, 0 ); if( hWnd == g_hWndMain ) RestoreForeground(); if( FAILED( hr ) ) throw winrt::hresult_error( hr ); } else { co_await file.DeleteAsync(); g_RecordingSession = nullptr; } } catch( const winrt::hresult_error& error ) { PostMessage( g_hWndMain, WM_USER_STOP_RECORDING, 0, 0 ); // Suppress the error from canceling the save dialog if( error.code() == HRESULT_FROM_WIN32( ERROR_CANCELLED )) co_return; if (g_RecordToggle == FALSE) { MessageBox(g_hWndMain, L"录屏还没开始,已取消", APPNAME, MB_OK | MB_ICONERROR | MB_SYSTEMMODAL); } else { ErrorDialogString(g_hWndMain, L"无法开始录屏", error.message().c_str()); } } //---------------------------------------------------------------------------- // // UpdateMonitorInfo // //---------------------------------------------------------------------------- void UpdateMonitorInfo( POINT point, MONITORINFO* monInfo ) { HMONITOR hMon{}; if( pMonitorFromPoint != nullptr ) { hMon = pMonitorFromPoint( point, MONITOR_DEFAULTTONEAREST ); } if( hMon != nullptr ) { monInfo->cbSize = sizeof *monInfo; pGetMonitorInfo( hMon, monInfo ); } else { *monInfo = {}; HDC hdcScreen = CreateDC( L"DISPLAY", nullptr, nullptr, nullptr ); if( hdcScreen != nullptr ) { monInfo->rcMonitor.right = GetDeviceCaps( hdcScreen, HORZRES ); monInfo->rcMonitor.bottom = GetDeviceCaps( hdcScreen, VERTRES ); DeleteDC( hdcScreen ); } } } #ifdef __ZOOMIT_POWERTOYS__ HRESULT OpenPowerToysSettingsApp() { std::wstring path = get_module_folderpath(g_hInstance); path += L"\\PowerToys.exe"; std::wstring openSettings = L"--open-settings=ZoomIt"; std::wstring full_command_path = path + L" " + openSettings; STARTUPINFO startupInfo; ZeroMemory(&startupInfo, sizeof(STARTUPINFO)); startupInfo.cb = sizeof(STARTUPINFO); startupInfo.wShowWindow = SW_SHOWNORMAL; PROCESS_INFORMATION processInformation; CreateProcess( path.c_str(), full_command_path.data(), NULL, NULL, TRUE, 0, NULL, NULL, &startupInfo, &processInformation); if (!CloseHandle(processInformation.hProcess)) { return HRESULT_FROM_WIN32(GetLastError()); } if (!CloseHandle(processInformation.hThread)) { return HRESULT_FROM_WIN32(GetLastError()); } return S_OK; } #endif // __ZOOMIT_POWERTOYS__ //---------------------------------------------------------------------------- // // ShowMainWindow // //---------------------------------------------------------------------------- void ShowMainWindow(HWND hWnd, const MONITORINFO& monInfo, int width, int height) { // Show the window first SetWindowPos(hWnd, HWND_TOPMOST, monInfo.rcMonitor.left, monInfo.rcMonitor.top, width, height, SWP_SHOWWINDOW | SWP_NOCOPYBITS); // Now invalidate and update the window InvalidateRect(hWnd, NULL, TRUE); UpdateWindow(hWnd); SetForegroundWindow(hWnd); SetActiveWindow(hWnd); } //---------------------------------------------------------------------------- // // MainWndProc // //---------------------------------------------------------------------------- LRESULT APIENTRY MainWndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { static int width, height; static HDC hdcScreen, hdcScreenCompat, hdcScreenCursorCompat, hdcScreenSaveCompat; static HBITMAP hbmpCompat, hbmpDrawingCompat, hbmpCursorCompat; static RECT cropRc{}; static BITMAP bmp; static BOOLEAN g_TimerActive = FALSE; static BOOLEAN g_Zoomed = FALSE; static TypeModeState g_TypeMode = TypeModeOff; static BOOLEAN g_HaveTyped = FALSE; static DEVMODE secondaryDevMode; static RECT g_LiveZoomSourceRect; static float g_LiveZoomLevel; static float zoomLevel; static float zoomTelescopeStep; static float zoomTelescopeTarget; static POINT cursorPos; static POINT savedCursorPos; static RECT cursorRc; static RECT boundRc; static POINT prevPt; static POINT textStartPt; static POINT textPt; static P_DRAW_UNDO drawUndoList = NULL; static P_TYPED_KEY typedKeyList = NULL; static BOOLEAN g_HaveDrawn = FALSE; static DWORD g_DrawingShape = 0; static DWORD prevPenWidth = g_PenWidth; static POINT g_RectangleAnchor; static RECT g_rcRectangle; static BOOLEAN g_Tracing = FALSE; static int g_BlankedScreen = 0; static int g_StraightDirection = 0; static BOOLEAN g_Drawing = FALSE; static HWND g_ActiveWindow = NULL; static int breakTimeout; static HBITMAP g_hBackgroundBmp = NULL; static HDC g_hDcBackgroundFile; static HPEN hDrawingPen; static HFONT hTimerFont; static HFONT hNegativeTimerFont; static HFONT hTypingFont; static MONITORINFO monInfo; static MONITORINFO lastMonInfo; static HWND hTargetWindow = NULL; static RECT rcTargetWindow; static BOOLEAN forcePenResize = TRUE; static BOOLEAN activeBreakShowDesktop = g_BreakShowDesktop; static BOOLEAN activeBreakShowBackgroundFile = g_BreakShowBackgroundFile; static TCHAR activeBreakBackgroundFile[MAX_PATH] = {0}; static UINT wmTaskbarCreated; #if 0 TITLEBARINFO titleBarInfo; WINDOWINFO targetWindowInfo; #endif bool isCaptureSupported = false; RECT rc, rc1; PAINTSTRUCT ps; TCHAR timerText[16]; TCHAR negativeTimerText[16]; BOOLEAN penInverted; BOOLEAN zoomIn; HDC hDc; HWND hWndRecord; int x, y, delta; HMENU hPopupMenu; OPENFILENAME openFileName; static TCHAR filePath[MAX_PATH] = {L"zoomit"}; NOTIFYICONDATA tNotifyIconData; const auto drawAllRightJustifiedLines = [&rc]( long lineHeight, bool doPop = false ) { rc.top = textPt.y - static_cast(g_TextBufferPreviousLines.size()) * lineHeight; for( const auto& line : g_TextBufferPreviousLines ) { DrawText( hdcScreenCompat, line.c_str(), static_cast(line.length()), &rc, DT_CALCRECT ); const auto textWidth = rc.right - rc.left; rc.left = textPt.x - textWidth; rc.right = textPt.x; DrawText( hdcScreenCompat, line.c_str(), static_cast(line.length()), &rc, DT_LEFT ); rc.top += lineHeight; } if( !g_TextBuffer.empty() ) { if( doPop ) { g_TextBuffer.pop_back(); } DrawText( hdcScreenCompat, g_TextBuffer.c_str(), static_cast(g_TextBuffer.length()), &rc, DT_CALCRECT ); rc.left = textPt.x - (rc.right - rc.left); rc.right = textPt.x; DrawText( hdcScreenCompat, g_TextBuffer.c_str(), static_cast(g_TextBuffer.length()), &rc, DT_LEFT ); } }; switch (message) { case WM_CREATE: // get default font GetObject( GetStockObject(DEFAULT_GUI_FONT), sizeof g_LogFont, &g_LogFont ); g_LogFont.lfWeight = FW_NORMAL; hDc = CreateCompatibleDC( NULL ); g_LogFont.lfHeight = -MulDiv(8, GetDeviceCaps(hDc, LOGPIXELSY), 72); DeleteDC( hDc ); reg.ReadRegSettings( RegSettings ); // to support migrating from if ((g_PenColor >> 24) == 0) { g_PenColor |= 0xFF << 24; } g_PenWidth = g_RootPenWidth; g_ToggleMod = GetKeyMod( g_ToggleKey ); g_LiveZoomToggleMod = GetKeyMod( g_LiveZoomToggleKey ); g_DrawToggleMod = GetKeyMod( g_DrawToggleKey ); g_BreakToggleMod = GetKeyMod( g_BreakToggleKey ); g_DemoTypeToggleMod = GetKeyMod( g_DemoTypeToggleKey ); g_SnipToggleMod = GetKeyMod( g_SnipToggleKey ); g_RecordToggleMod = GetKeyMod( g_RecordToggleKey ); if( !g_OptionsShown && !g_StartedByPowerToys ) { // First run should show options when running as standalone. If not running as standalone, // options screen won't show and we should register keys instead. SendMessage( hWnd, WM_COMMAND, IDC_OPTIONS, 0 ); g_OptionsShown = TRUE; reg.WriteRegSettings( RegSettings ); } else { BOOL showOptions = FALSE; if( g_ToggleKey && !RegisterHotKey( hWnd, ZOOM_HOTKEY, g_ToggleMod, g_ToggleKey & 0xFF)) { MessageBox( hWnd, L"此快捷键已被占用。\n请重新选择放大快捷键。", APPNAME, MB_ICONERROR ); showOptions = TRUE; } else if( g_LiveZoomToggleKey && (!RegisterHotKey( hWnd, LIVE_HOTKEY, g_LiveZoomToggleMod, g_LiveZoomToggleKey & 0xFF) || !RegisterHotKey(hWnd, LIVE_DRAW_HOTKEY, (g_LiveZoomToggleMod ^ MOD_SHIFT), g_LiveZoomToggleKey & 0xFF))) { MessageBox( hWnd, L"此快捷键已被占用。\n请重新选择实时放大快捷键。", APPNAME, MB_ICONERROR ); showOptions = TRUE; } else if( g_DrawToggleKey && !RegisterHotKey( hWnd, DRAW_HOTKEY, g_DrawToggleMod, g_DrawToggleKey & 0xFF )) { MessageBox( hWnd, L"此快捷键已被占用。\n请重新选择画图快捷键。", APPNAME, MB_ICONERROR ); showOptions = TRUE; } else if (g_BreakToggleKey && !RegisterHotKey(hWnd, BREAK_HOTKEY, g_BreakToggleMod, g_BreakToggleKey & 0xFF)) { MessageBox(hWnd, L"此快捷键已被占用。\n请重新选择倒计时快捷键。", APPNAME, MB_ICONERROR); showOptions = TRUE; } else if( g_DemoTypeToggleKey && (!RegisterHotKey( hWnd, DEMOTYPE_HOTKEY, g_DemoTypeToggleMod, g_DemoTypeToggleKey & 0xFF ) || !RegisterHotKey(hWnd, DEMOTYPE_RESET_HOTKEY, (g_DemoTypeToggleMod ^ MOD_SHIFT), g_DemoTypeToggleKey & 0xFF))) { MessageBox( hWnd, L"此快捷键已被占用。\n请重新选择自动打字快捷键。", APPNAME, MB_ICONERROR ); showOptions = TRUE; } else if (g_SnipToggleKey && (!RegisterHotKey(hWnd, SNIP_HOTKEY, g_SnipToggleMod, g_SnipToggleKey & 0xFF) || !RegisterHotKey(hWnd, SNIP_SAVE_HOTKEY, (g_SnipToggleMod ^ MOD_SHIFT), g_SnipToggleKey & 0xFF))) { MessageBox(hWnd, L"此快捷键已被占用。\n请重新选择截图快捷键。", APPNAME, MB_ICONERROR); showOptions = TRUE; } else if (g_RecordToggleKey && (!RegisterHotKey(hWnd, RECORD_HOTKEY, g_RecordToggleMod | MOD_NOREPEAT, g_RecordToggleKey & 0xFF) || !RegisterHotKey(hWnd, RECORD_CROP_HOTKEY, (g_RecordToggleMod ^ MOD_SHIFT) | MOD_NOREPEAT, g_RecordToggleKey & 0xFF) || !RegisterHotKey(hWnd, RECORD_WINDOW_HOTKEY, (g_RecordToggleMod ^ MOD_ALT) | MOD_NOREPEAT, g_RecordToggleKey & 0xFF))) { MessageBox(hWnd, L"此快捷键已被占用。\n请重新选择录屏快捷键。", APPNAME, MB_ICONERROR); showOptions = TRUE; } if( showOptions ) { SendMessage( hWnd, WM_COMMAND, IDC_OPTIONS, 0 ); } } SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL ); wmTaskbarCreated = RegisterWindowMessage(_T("TaskbarCreated")); return TRUE; case WM_CLOSE: // Do not allow users to close the main window, for example with Alt-F4. return 0; case WM_HOTKEY: if( g_RecordCropping == TRUE ) { if( wParam != RECORD_CROP_HOTKEY ) { // Cancel cropping on any hotkey. g_SelectRectangle.Stop(); g_RecordCropping = FALSE; // Cropping is handled by a blocking call in WM_HOTKEY, so post // this message to the window for processing after the previous // WM_HOTKEY message completes processing. PostMessage( hWnd, message, wParam, lParam ); } return 0; } // // Magic value that comes from tray context menu // if (lParam == 1) { // // Sleep to let context menu dismiss // Sleep(250); } switch( wParam ) { case LIVE_DRAW_HOTKEY: { OutputDebug(L"LIVE_DRAW_HOTKEY\n"); LONG_PTR exStyle = GetWindowLongPtr(hWnd, GWL_EXSTYLE); if ((exStyle & WS_EX_LAYERED)) { OutputDebug(L"LiveDraw reactivate\n"); // Just focus on the window and re-enter drawing mode SetFocus(hWnd); SetForegroundWindow(hWnd); SendMessage(hWnd, WM_LBUTTONDOWN, 0, MAKELPARAM(cursorPos.x, cursorPos.y)); SendMessage(hWnd, WM_MOUSEMOVE, 0, MAKELPARAM(cursorPos.x, cursorPos.y)); if( IsWindowVisible( g_hWndLiveZoom ) ) { SendMessage( g_hWndLiveZoom, WM_USER_MAGNIFY_CURSOR, FALSE, 0 ); } break; } else { OutputDebug(L"LiveDraw create\n"); exStyle = GetWindowLongPtr(hWnd, GWL_EXSTYLE); SetWindowLongPtr(hWnd, GWL_EXSTYLE, exStyle | WS_EX_LAYERED); SetLayeredWindowAttributes(hWnd, COLORREF(RGB(0, 0, 0)), 0, LWA_COLORKEY); pMagSetWindowFilterList( g_hWndLiveZoomMag, MW_FILTERMODE_EXCLUDE, 0, nullptr ); } [[fallthrough]]; } case DRAW_HOTKEY: // // Enter drawing mode without zoom // #ifdef __ZOOMIT_POWERTOYS__ if( g_StartedByPowerToys ) { Trace::ZoomItActivateDraw(); } #endif // __ZOOMIT_POWERTOYS__ if( !g_Zoomed ) { OutputDebug(L"LiveDraw: %d (%d)\n", wParam, (wParam == LIVE_DRAW_HOTKEY)); #if WINDOWS_CURSOR_RECORDING_WORKAROUND if( IsWindowVisible( g_hWndLiveZoom ) && !g_LiveZoomLevelOne ) { #else if( IsWindowVisible( g_hWndLiveZoom )) { #endif OutputDebug(L" In Live zoom\n"); SendMessage(hWnd, WM_HOTKEY, ZOOM_HOTKEY, wParam == LIVE_DRAW_HOTKEY ? LIVE_DRAW_ZOOM : 0); } else { OutputDebug(L" Not in Live zoom\n"); SendMessage( hWnd, WM_HOTKEY, ZOOM_HOTKEY, wParam == LIVE_DRAW_HOTKEY ? LIVE_DRAW_ZOOM : 0 ); zoomLevel = zoomTelescopeTarget = 1; SendMessage( hWnd, WM_LBUTTONDOWN, 0, MAKELPARAM( cursorPos.x, cursorPos.y )); } if(wParam == LIVE_DRAW_HOTKEY) { SetLayeredWindowAttributes(hWnd, COLORREF(RGB(0, 0, 0)), 0, LWA_COLORKEY); SendMessage(hWnd, WM_KEYDOWN, 'K', LIVE_DRAW_ZOOM); SetTimer(hWnd, 3, 10, NULL); SendMessage(hWnd, WM_MOUSEMOVE, 0, MAKELPARAM(cursorPos.x, cursorPos.y)); ShowMainWindow(hWnd, monInfo, width, height); if( ( g_PenColor & 0xFFFFFF ) == COLOR_BLUR ) { // Blur is not supported in LiveDraw g_PenColor = COLOR_RED; } // Highlight is not supported in LiveDraw g_PenColor |= 0xFF << 24; } } break; case SNIP_SAVE_HOTKEY: case SNIP_HOTKEY: { // Block liveZoom liveDraw snip due to mirroring bug if( IsWindowVisible( g_hWndLiveZoom ) && ( GetWindowLongPtr( hWnd, GWL_EXSTYLE ) & WS_EX_LAYERED ) ) { break; } bool zoomed = true; #ifdef __ZOOMIT_POWERTOYS__ if( g_StartedByPowerToys ) { Trace::ZoomItActivateSnip(); } #endif // __ZOOMIT_POWERTOYS__ // First, static zoom if( !g_Zoomed ) { zoomed = false; if( IsWindowVisible( g_hWndLiveZoom ) && !g_LiveZoomLevelOne ) { SendMessage( hWnd, WM_HOTKEY, ZOOM_HOTKEY, SHALLOW_ZOOM ); } else { SendMessage( hWnd, WM_HOTKEY, ZOOM_HOTKEY, LIVE_DRAW_ZOOM); } zoomLevel = zoomTelescopeTarget = 1; } else if( g_Drawing ) { // Exit drawing mode to hide the drawing cursor SendMessage( hWnd, WM_USER_EXIT_MODE, 0, 0 ); // Exit again if still in drawing mode, which happens from type mode if( g_Drawing ) { SendMessage( hWnd, WM_USER_EXIT_MODE, 0, 0 ); } } ShowMainWindow(hWnd, monInfo, width, height); // Now copy crop or copy+save if( LOWORD( wParam ) == SNIP_SAVE_HOTKEY ) { SendMessage( hWnd, WM_COMMAND, IDC_SAVE_CROP, ( zoomed ? 0 : SHALLOW_ZOOM ) ); } else { SendMessage( hWnd, WM_COMMAND, IDC_COPY_CROP, ( zoomed ? 0 : SHALLOW_ZOOM ) ); } // Now if we weren't zoomed, unzoom if( !zoomed ) { if( g_ZoomOnLiveZoom ) { // hiding the cursor allows for a cleaner transition back to the magnified cursor ShowCursor( false ); SendMessage( hWnd, WM_HOTKEY, ZOOM_HOTKEY, 0 ); ShowCursor( true ); } else { SendMessage( hWnd, WM_HOTKEY, ZOOM_HOTKEY, SHALLOW_ZOOM ); } } // exit zoom if( g_Zoomed ) { // If from liveDraw, extra care is needed to destruct if( GetWindowLong( hWnd, GWL_EXSTYLE ) & WS_EX_LAYERED ) { OutputDebug( L"Exiting liveDraw after snip\n" ); SendMessage( hWnd, WM_KEYDOWN, VK_ESCAPE, 0 ); } else { // Set wparam to 1 to exit without animation OutputDebug(L"Exiting zoom after snip\n" ); SendMessage( hWnd, WM_HOTKEY, ZOOM_HOTKEY, SHALLOW_DESTROY ); } } break; } case BREAK_HOTKEY: // // Go to break timer // #if WINDOWS_CURSOR_RECORDING_WORKAROUND if( !g_Zoomed && ( !IsWindowVisible( g_hWndLiveZoom ) || g_LiveZoomLevelOne ) ) { #else if( !g_Zoomed && !IsWindowVisible( g_hWndLiveZoom )) { #endif SendMessage( hWnd, WM_COMMAND, IDC_BREAK, 0 ); } break; case DEMOTYPE_RESET_HOTKEY: ResetDemoTypeIndex(); break; case DEMOTYPE_HOTKEY: { // // Live type // switch( StartDemoType( g_DemoTypeFile, g_DemoTypeSpeedSlider, g_DemoTypeUserDriven ) ) { case ERROR_LOADING_FILE: ErrorDialog( hWnd, L"加载自动打字文件时出错", GetLastError() ); break; case NO_FILE_SPECIFIED: MessageBox( hWnd, L"未选择自动打字文件", APPNAME, MB_OK ); break; case FILE_SIZE_OVERFLOW: { std::wstring msg = L"自动打字文件过大,最大" + std::to_wstring( MAX_INPUT_SIZE ) + L"字节"; MessageBox( hWnd, msg.c_str(), APPNAME, MB_OK ); break; } case UNKNOWN_FILE_DATA: MessageBox( hWnd, L"无法读取自动打字文件(试试保存为 UTF-8 编码)", APPNAME, MB_OK ); break; default: #ifdef __ZOOMIT_POWERTOYS__ if( g_StartedByPowerToys ) { Trace::ZoomItActivateDemoType(); } #endif // __ZOOMIT_POWERTOYS__ break; } break; } case LIVE_HOTKEY: // // Live zoom // OutputDebug(L"*** LIVE_HOTKEY\n"); // If LiveZoom and LiveDraw are active then exit both if( g_Zoomed && IsWindowVisible( g_hWndLiveZoom ) && ( GetWindowLongPtr( hWnd, GWL_EXSTYLE ) & WS_EX_LAYERED ) ) { SendMessage( hWnd, WM_KEYDOWN, VK_ESCAPE, 0 ); PostMessage(hWnd, WM_HOTKEY, LIVE_HOTKEY, 0); break; } if( !g_Zoomed && !g_TimerActive && ( !g_fullScreenWorkaround || !g_RecordToggle ) ) { #ifdef __ZOOMIT_POWERTOYS__ if( g_StartedByPowerToys ) { Trace::ZoomItActivateLiveZoom(); } #endif // __ZOOMIT_POWERTOYS__ if( g_hWndLiveZoom == NULL ) { OutputDebug(L"Create LIVEZOOM\n"); g_hWndLiveZoom = CreateWindowEx( WS_EX_TOOLWINDOW | WS_EX_LAYERED | WS_EX_TRANSPARENT, L"MagnifierClass", L"演示工具实时放大", WS_POPUP | WS_CLIPSIBLINGS, 0, 0, 0, 0, NULL, NULL, g_hInstance, static_cast(GetForegroundWindow()) ); pSetLayeredWindowAttributes( hWnd, 0, 0, LWA_ALPHA ); EnableWindow( g_hWndLiveZoom, FALSE ); pMagSetWindowFilterList( g_hWndLiveZoomMag, MW_FILTERMODE_EXCLUDE, 1, &hWnd ); } else { #if WINDOWS_CURSOR_RECORDING_WORKAROUND if( g_LiveZoomLevelOne ) { OutputDebug(L"liveZoom level one\n"); SendMessage( g_hWndLiveZoom, WM_USER_SET_ZOOM, static_cast(g_LiveZoomLevel), 0 ); } else { #endif if( IsWindowVisible( g_hWndLiveZoom )) { #if WINDOWS_CURSOR_RECORDING_WORKAROUND if( g_RecordToggle ) g_LiveZoomLevel = g_ZoomLevels[g_SliderZoomLevel]; #endif // Unzoom SendMessage( g_hWndLiveZoom, WM_KEYDOWN, VK_ESCAPE, 0 ); } else { OutputDebug(L"Show liveZoom\n"); ShowWindow( g_hWndLiveZoom, SW_SHOW ); } #if WINDOWS_CURSOR_RECORDING_WORKAROUND } #endif } if ( g_RecordToggle ) { g_SelectRectangle.UpdateOwner( g_hWndLiveZoom ); } } break; case RECORD_HOTKEY: case RECORD_CROP_HOTKEY: case RECORD_WINDOW_HOTKEY: // // Recording // This gets entered twice per recording: // 1. When the hotkey is pressed to start recording // 2. When the hotkey is pressed to stop recording // if( g_fullScreenWorkaround && g_hWndLiveZoom != NULL && IsWindowVisible( g_hWndLiveZoom ) != FALSE ) { break; } if( g_RecordCropping == TRUE ) { break; } // Start screen recording try { isCaptureSupported = winrt::GraphicsCaptureSession::IsSupported(); } catch( const winrt::hresult_error& ) {} if( !isCaptureSupported ) { MessageBox( hWnd, L"录屏功能至少需要 Windows 10 2019年5月版本。", APPNAME, MB_OK ); break; } // If shift, then we're cropping hWndRecord = 0; if( wParam == RECORD_CROP_HOTKEY ) { if( g_RecordToggle == TRUE ) { // Already recording break; } g_RecordCropping = TRUE; POINT savedPoint{}; RECT savedClip = {}; // Handle the cursor for live zoom and static zoom modes. if( ( g_hWndLiveZoom != nullptr ) || ( g_Zoomed == TRUE ) ) { GetCursorPos( &savedPoint ); UpdateMonitorInfo( savedPoint, &monInfo ); } if( g_hWndLiveZoom != nullptr ) { // Hide the magnified cursor. SendMessage( g_hWndLiveZoom, WM_USER_MAGNIFY_CURSOR, FALSE, 0 ); // Show the system cursor where the magnified was. g_LiveZoomSourceRect = *reinterpret_cast( SendMessage( g_hWndLiveZoom, WM_USER_GET_SOURCE_RECT, 0, 0 ) ); savedPoint = ScalePointInRects( savedPoint, g_LiveZoomSourceRect, monInfo.rcMonitor ); SetCursorPos( savedPoint.x, savedPoint.y ); if ( pMagShowSystemCursor != nullptr ) { pMagShowSystemCursor( TRUE ); } } else if( ( g_Zoomed == TRUE ) && ( g_Drawing == TRUE ) ) { // Unclip the cursor. GetClipCursor( &savedClip ); ClipCursor( nullptr ); // Scale the cursor position to the zoomed and move it. auto point = ScalePointInRects( savedPoint, boundRc, monInfo.rcMonitor ); SetCursorPos( point.x, point.y ); } if( g_Zoomed == FALSE ) { SetWindowPos( hWnd, HWND_TOPMOST, monInfo.rcMonitor.left, monInfo.rcMonitor.top, width, height, SWP_SHOWWINDOW ); } // This call blocks with a message loop while cropping. auto canceled = !g_SelectRectangle.Start( ( g_hWndLiveZoom != nullptr ) ? g_hWndLiveZoom : hWnd ); g_RecordCropping = FALSE; // Restore the cursor if applicable. if( g_hWndLiveZoom != nullptr ) { // Hide the system cursor. if ( pMagShowSystemCursor != nullptr ) { pMagShowSystemCursor( FALSE ); } // Show the magnified cursor where the system cursor was. GetCursorPos( &savedPoint ); savedPoint = ScalePointInRects( savedPoint, monInfo.rcMonitor, g_LiveZoomSourceRect ); SetCursorPos( savedPoint.x, savedPoint.y ); SendMessage( g_hWndLiveZoom, WM_USER_MAGNIFY_CURSOR, TRUE, 0 ); } else if( g_Zoomed == TRUE ) { SetCursorPos( savedPoint.x, savedPoint.y ); if ( g_Drawing == TRUE ) { ClipCursor( &savedClip ); } } SetForegroundWindow( hWnd ); if( g_Zoomed == FALSE ) { SetActiveWindow( hWnd ); ShowWindow( hWnd, SW_HIDE ); } if( canceled ) { break; } g_SelectRectangle.UpdateOwner( ( g_hWndLiveZoom != nullptr ) ? g_hWndLiveZoom : hWnd ); cropRc = g_SelectRectangle.SelectedRect(); } else { cropRc = {}; // if we're recording a window, get the window if (wParam == RECORD_WINDOW_HOTKEY) { GetCursorPos(&cursorPos); hWndRecord = WindowFromPoint(cursorPos); while( GetParent(hWndRecord) != NULL) { hWndRecord = GetParent(hWndRecord); } if( hWndRecord == GetDesktopWindow()) { hWndRecord = NULL; } } } if( g_RecordToggle == FALSE ) { g_RecordToggle = TRUE; #ifdef __ZOOMIT_POWERTOYS__ if( g_StartedByPowerToys ) { Trace::ZoomItActivateRecord(); } #endif // __ZOOMIT_POWERTOYS__ StartRecordingAsync( hWnd, &cropRc, hWndRecord ); } else { StopRecording(); } break; case ZOOM_HOTKEY: // // Zoom // // Don't react to hotkey while options are open or we're // saving the screen or live zoom is active // if( hWndOptions ) { break; } OutputDebug( L"ZOOM HOTKEY: %d\n", lParam); if( g_TimerActive ) { // // Finished with break timer // if( g_BreakOnSecondary ) { EnableDisableSecondaryDisplay( hWnd, FALSE, &secondaryDevMode ); } if( lParam != SHALLOW_DESTROY ) { ShowWindow( hWnd, SW_HIDE ); if( g_hBackgroundBmp ) { DeleteObject( g_hBackgroundBmp ); DeleteDC( g_hDcBackgroundFile ); g_hBackgroundBmp = NULL; } } SetFocus( GetDesktopWindow() ); KillTimer( hWnd, 0 ); g_TimerActive = FALSE; DeleteObject( hTimerFont ); DeleteObject( hNegativeTimerFont ); DeleteDC( hdcScreen ); DeleteDC( hdcScreenCompat ); DeleteDC( hdcScreenSaveCompat ); DeleteDC( hdcScreenCursorCompat ); DeleteObject( hbmpCompat ); EnableDisableScreenSaver( TRUE ); EnableDisableOpacity( hWnd, FALSE ); } else { SendMessage( hWnd, WM_USER_TYPING_OFF, 0, 0 ); if( !g_Zoomed ) { g_Zoomed = TRUE; g_DrawingShape = FALSE; OutputDebug( L"Zoom on\n"); #ifdef __ZOOMIT_POWERTOYS__ if( g_StartedByPowerToys ) { Trace::ZoomItActivateZoom(); } #endif // __ZOOMIT_POWERTOYS__ // Hide the cursor before capturing if in live zoom if( g_hWndLiveZoom != nullptr ) { OutputDebug(L"Hide cursor\n"); SendMessage( g_hWndLiveZoom, WM_USER_MAGNIFY_CURSOR, FALSE, 0 ); SendMessage( g_hWndLiveZoom, WM_TIMER, 0, 0 ); SendMessage( g_hWndLiveZoom, WM_USER_MAGNIFY_CURSOR, FALSE, 0 ); } // Get screen DCs hdcScreen = CreateDC(L"DISPLAY", static_cast(NULL), static_cast(NULL), static_cast(NULL)); hdcScreenCompat = CreateCompatibleDC(hdcScreen); hdcScreenSaveCompat = CreateCompatibleDC(hdcScreen); hdcScreenCursorCompat = CreateCompatibleDC(hdcScreen); // Determine what monitor we're on GetCursorPos(&cursorPos); UpdateMonitorInfo( cursorPos, &monInfo ); width = monInfo.rcMonitor.right - monInfo.rcMonitor.left; height = monInfo.rcMonitor.bottom - monInfo.rcMonitor.top; OutputDebug( L"ZOOM x: %d y: %d width: %d height: %d zoomLevel: %g\n", cursorPos.x, cursorPos.y, width, height, zoomLevel ); // Create display bitmap bmp.bmBitsPixel = static_cast(GetDeviceCaps(hdcScreen, BITSPIXEL)); bmp.bmPlanes = static_cast(GetDeviceCaps(hdcScreen, PLANES)); bmp.bmWidth = width; bmp.bmHeight = height; bmp.bmWidthBytes = ((bmp.bmWidth + 15) &~15)/8; hbmpCompat = CreateBitmap(bmp.bmWidth, bmp.bmHeight, bmp.bmPlanes, bmp.bmBitsPixel, static_cast(NULL)); SelectObject(hdcScreenCompat, hbmpCompat); // Create saved bitmap hbmpDrawingCompat = CreateBitmap(bmp.bmWidth, bmp.bmHeight, bmp.bmPlanes, bmp.bmBitsPixel, static_cast(NULL)); SelectObject(hdcScreenSaveCompat, hbmpDrawingCompat); // Create cursor save bitmap // (have to accomodate large fonts and LiveZoom pen scaling) hbmpCursorCompat = CreateBitmap( MAX_LIVE_PEN_WIDTH+CURSOR_ARM_LENGTH*2, MAX_LIVE_PEN_WIDTH+CURSOR_ARM_LENGTH*2, bmp.bmPlanes, bmp.bmBitsPixel, static_cast(NULL)); SelectObject(hdcScreenCursorCompat, hbmpCursorCompat); // Create typing font g_LogFont.lfHeight = height / 15; if (g_LogFont.lfHeight < 20) g_LogFont.lfQuality = NONANTIALIASED_QUALITY; else g_LogFont.lfQuality = ANTIALIASED_QUALITY; hTypingFont = CreateFontIndirect(&g_LogFont); SelectObject(hdcScreenCompat, hTypingFont); SetTextColor(hdcScreenCompat, g_PenColor & 0xFFFFFF); SetBkMode(hdcScreenCompat, TRANSPARENT); // Use the screen DC unless recording, because it contains the yellow border HDC hdcSource = hdcScreen; if( g_RecordToggle ) try { auto capture = CaptureScreenshot( winrt::DirectXPixelFormat::B8G8R8A8UIntNormalized ); auto bytes = CopyBytesFromTexture( capture ); D3D11_TEXTURE2D_DESC desc; capture->GetDesc( &desc ); BITMAPINFO bitmapInfo = {}; bitmapInfo.bmiHeader.biSize = sizeof bitmapInfo.bmiHeader; bitmapInfo.bmiHeader.biWidth = desc.Width; bitmapInfo.bmiHeader.biHeight = -static_cast(desc.Height); bitmapInfo.bmiHeader.biPlanes = 1; bitmapInfo.bmiHeader.biBitCount = 32; bitmapInfo.bmiHeader.biCompression = BI_RGB; void *bits; auto dib = CreateDIBSection( NULL, &bitmapInfo, DIB_RGB_COLORS, &bits, nullptr, 0 ); if( dib ) { CopyMemory( bits, bytes.data(), bytes.size() ); auto hdcCapture = CreateCompatibleDC( hdcScreen ); SelectObject( hdcCapture, dib ); hdcSource = hdcCapture; } } catch( const winrt::hresult_error& ) {} // on any failure, fall back to the screen DC bool captured = hdcSource != hdcScreen; // paint the initial bitmap BitBlt( hdcScreenCompat, 0, 0, bmp.bmWidth, bmp.bmHeight, hdcSource, captured ? 0 : monInfo.rcMonitor.left, captured ? 0 : monInfo.rcMonitor.top, SRCCOPY|CAPTUREBLT ); BitBlt( hdcScreenSaveCompat, 0, 0, bmp.bmWidth, bmp.bmHeight, hdcSource, captured ? 0 : monInfo.rcMonitor.left, captured ? 0 : monInfo.rcMonitor.top, SRCCOPY|CAPTUREBLT ); if( captured ) { OutputDebug(L"Captured screen\n"); auto bitmap = GetCurrentObject( hdcSource, OBJ_BITMAP ); DeleteObject( bitmap ); DeleteDC( hdcSource ); } // Create drawing pen hDrawingPen = CreatePen(PS_SOLID, g_PenWidth, g_PenColor & 0xFFFFFF); g_BlankedScreen = FALSE; g_HaveTyped = FALSE; g_Drawing = FALSE; g_TypeMode = TypeModeOff; g_HaveDrawn = FALSE; EnableDisableStickyKeys( TRUE ); // Go full screen g_ActiveWindow = GetForegroundWindow(); OutputDebug( L"active window: %x\n", PtrToLong(g_ActiveWindow) ); if( lParam != LIVE_DRAW_ZOOM) { OutputDebug(L"Calling ShowMainWindow\n"); ShowMainWindow(hWnd, monInfo, width, height); } // Start telescoping zoom. Lparam is non-zero if this // was a real hotkey and not the message we send ourself to enter // unzoomed drawing mode. // // Are we switching from live zoom to standard zoom? // #if WINDOWS_CURSOR_RECORDING_WORKAROUND if( IsWindowVisible( g_hWndLiveZoom ) && !g_LiveZoomLevelOne ) { #else if( IsWindowVisible( g_hWndLiveZoom )) { #endif // Enter drawing mode OutputDebug(L"Enter liveZoom draw\n"); g_LiveZoomSourceRect = *reinterpret_cast(SendMessage( g_hWndLiveZoom, WM_USER_GET_SOURCE_RECT, 0, 0 )); g_LiveZoomLevel = *reinterpret_cast(SendMessage(g_hWndLiveZoom, WM_USER_GET_ZOOM_LEVEL, 0, 0)); // Set live zoom level to 1 in preparation of us being full screen static zoomLevel = 1.0; zoomTelescopeTarget = 1.0; if (lParam != LIVE_DRAW_ZOOM) { g_ZoomOnLiveZoom = TRUE; } UpdateWindow( hWnd ); // overwrites where cursor erased if( lParam != SHALLOW_ZOOM ) { // Put the drawing cursor where the magnified cursor was OutputDebug(L"Setting cursor\n"); if (lParam != LIVE_DRAW_ZOOM) { cursorPos = ScalePointInRects( cursorPos, g_LiveZoomSourceRect, monInfo.rcMonitor ); SetCursorPos( cursorPos.x, cursorPos.y ); UpdateWindow( hWnd ); // overwrites where cursor erased SendMessage( hWnd, WM_LBUTTONDOWN, 0, MAKELPARAM( cursorPos.x, cursorPos.y )); } } else { InvalidateRect( hWnd, NULL, FALSE ); } UpdateWindow( hWnd ); if( g_RecordToggle ) { g_SelectRectangle.UpdateOwner( hWnd ); } if( lParam != LIVE_DRAW_ZOOM ) { OutputDebug(L"Calling ShowMainWindow 2\n"); ShowWindow( g_hWndLiveZoom, SW_HIDE ); } } else if( lParam != 0 && lParam != LIVE_DRAW_ZOOM ) { zoomTelescopeStep = ZOOM_LEVEL_STEP_IN; zoomTelescopeTarget = g_ZoomLevels[g_SliderZoomLevel]; if( g_AnimateZoom ) zoomLevel = static_cast(1.0) * zoomTelescopeStep; else zoomLevel = zoomTelescopeTarget; SetTimer( hWnd, 1, ZOOM_LEVEL_STEP_TIME, NULL ); } } else { OutputDebug( L"Zoom off: don't animate=%d\n", lParam ); // turn off liveDraw SetLayeredWindowAttributes(hWnd, 0, 255, LWA_ALPHA); if( lParam != SHALLOW_DESTROY && !g_ZoomOnLiveZoom && g_AnimateZoom && g_TelescopeZoomOut && zoomTelescopeTarget != 1 ) { // Start telescoping zoom. zoomTelescopeStep = ZOOM_LEVEL_STEP_OUT; zoomTelescopeTarget = 1.0; SetTimer( hWnd, 2, ZOOM_LEVEL_STEP_TIME, NULL ); } else { // Simulate timer expiration zoomTelescopeStep = 0; zoomTelescopeTarget = zoomLevel = 1.0; SendMessage( hWnd, WM_TIMER, 2, lParam ); } } } break; } return TRUE; case WM_POINTERUPDATE: { penInverted = IsPenInverted(wParam); OutputDebug( L"WM_POINTERUPDATE: contact: %d button down: %d X: %d Y: %d\n", IS_POINTER_INCONTACT_WPARAM(wParam), penInverted, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); if( penInverted != g_PenInverted) { g_PenInverted = penInverted; if (g_PenInverted) { if (PopDrawUndo(hdcScreenCompat, &drawUndoList, width, height)) { SaveCursorArea(hdcScreenCursorCompat, hdcScreenCompat, prevPt); InvalidateRect(hWnd, NULL, FALSE); } } } else if( g_PenDown && !penInverted) { SendPenMessage(hWnd, WM_MOUSEMOVE, lParam); } } return TRUE; case WM_POINTERUP: OutputDebug(L"WM_POINTERUP\n"); penInverted = IsPenInverted(wParam); if (!penInverted) { SendPenMessage(hWnd, WM_LBUTTONUP, lParam); SendPenMessage(hWnd, WM_RBUTTONDOWN, lParam); g_PenDown = FALSE; } break; case WM_POINTERDOWN: OutputDebug(L"WM_POINTERDOWN\n"); penInverted = IsPenInverted(wParam); if (!penInverted) { g_PenDown = TRUE; // Enter drawing mode SendPenMessage(hWnd, WM_LBUTTONDOWN, lParam); SendPenMessage(hWnd, WM_MOUSEMOVE, lParam); SendPenMessage(hWnd, WM_LBUTTONUP, lParam); SendPenMessage(hWnd, WM_MOUSEMOVE, lParam); PopDrawUndo(hdcScreenCompat, &drawUndoList, width, height); // Enter tracing mode SendPenMessage(hWnd, WM_LBUTTONDOWN, lParam); } break; case WM_KILLFOCUS: if( ( g_RecordCropping == FALSE ) && g_Zoomed && !g_bSaveInProgress ) { // Turn off zoom if not in liveDraw DWORD layeringFlag; GetLayeredWindowAttributes(hWnd, NULL, NULL, &layeringFlag); if( !(layeringFlag & LWA_COLORKEY)) { PostMessage(hWnd, WM_HOTKEY, ZOOM_HOTKEY, 0); } } break; case WM_MOUSEWHEEL: // // Zoom or modify break timer // if( GET_WHEEL_DELTA_WPARAM(wParam) < 0 ) wParam -= (WHEEL_DELTA-1) << 16; else wParam += (WHEEL_DELTA-1) << 16; delta = GET_WHEEL_DELTA_WPARAM(wParam)/WHEEL_DELTA; OutputDebug( L"mousewheel: wParam: %d delta: %d\n", GET_WHEEL_DELTA_WPARAM(wParam), delta ); if( g_Zoomed ) { if( g_TypeMode == TypeModeOff ) { if( g_Drawing && (LOWORD( wParam ) & MK_CONTROL) ) { ResizePen( hWnd, hdcScreenCompat, hdcScreenCursorCompat, prevPt, g_Tracing, &g_Drawing, g_LiveZoomLevel, TRUE, g_PenWidth + delta ); // Perform static zoom unless in liveDraw } else if( !( GetWindowLongPtr( hWnd, GWL_EXSTYLE ) & WS_EX_LAYERED ) ) { if( delta > 0 ) zoomIn = TRUE; else { zoomIn = FALSE; delta = -delta; } while( delta-- ) { if( zoomIn ) { if( zoomTelescopeTarget < ZOOM_LEVEL_MAX ) { if( zoomTelescopeTarget < 2 ) { zoomTelescopeTarget = 2; } else { // Start telescoping zoom zoomTelescopeTarget = zoomTelescopeTarget * 2; } zoomTelescopeStep = ZOOM_LEVEL_STEP_IN; if( g_AnimateZoom ) zoomLevel *= zoomTelescopeStep; else zoomLevel = zoomTelescopeTarget; if( zoomLevel > zoomTelescopeTarget ) zoomLevel = zoomTelescopeTarget; else SetTimer( hWnd, 1, ZOOM_LEVEL_STEP_TIME, NULL ); } } else if( zoomTelescopeTarget > ZOOM_LEVEL_MIN ) { // Let them more gradually zoom out from 2x to 1x if( zoomTelescopeTarget <= 2 ) { zoomTelescopeTarget *= .75; if( zoomTelescopeTarget < ZOOM_LEVEL_MIN ) zoomTelescopeTarget = ZOOM_LEVEL_MIN; } else { zoomTelescopeTarget = zoomTelescopeTarget/2; } zoomTelescopeStep = ZOOM_LEVEL_STEP_OUT; if( g_AnimateZoom ) zoomLevel *= zoomTelescopeStep; else zoomLevel = zoomTelescopeTarget; if( zoomLevel < zoomTelescopeTarget ) { zoomLevel = zoomTelescopeTarget; // Force update on final step out InvalidateRect( hWnd, NULL, FALSE ); } else { SetTimer( hWnd, 1, ZOOM_LEVEL_STEP_TIME, NULL ); } } } if( zoomLevel != zoomTelescopeTarget ) { if( g_Drawing ) { if( !g_Tracing ) { RestoreCursorArea( hdcScreenCompat, hdcScreenCursorCompat, prevPt ); } //SetCursorPos( monInfo.rcMonitor.left + cursorPos.x, // monInfo.rcMonitor.top + cursorPos.y ); } InvalidateRect( hWnd, NULL, FALSE ); } } } else { // Resize the text font if( (delta > 0 && g_FontScale > -20) || (delta < 0 && g_FontScale < 50 )) { ClearTypingCursor(hdcScreenCompat, hdcScreenCursorCompat, cursorRc, g_BlankedScreen); g_FontScale -= delta; if( g_FontScale == 0 ) g_FontScale = 1; // Set lParam to 0 as part of message to keyup hander DeleteObject(hTypingFont); g_LogFont.lfHeight = max((int)(height / zoomLevel) / g_FontScale, 12); if (g_LogFont.lfHeight < 20) g_LogFont.lfQuality = NONANTIALIASED_QUALITY; else g_LogFont.lfQuality = ANTIALIASED_QUALITY; hTypingFont = CreateFontIndirect(&g_LogFont); SelectObject(hdcScreenCompat, hTypingFont); DrawTypingCursor( hWnd, &textPt, hdcScreenCompat, hdcScreenCursorCompat, &cursorRc ); } } } else if( g_TimerActive && (breakTimeout > 0 || delta )) { if( delta ) { if( breakTimeout < 0 ) breakTimeout = 0; if( breakTimeout % 60 ) { breakTimeout += (60 - breakTimeout % 60); delta--; } breakTimeout += delta * 60; } else { if( breakTimeout % 60 ) { breakTimeout -= breakTimeout % 60; delta--; } breakTimeout -= delta * 60; } if( breakTimeout < 0 ) breakTimeout = 0; KillTimer( hWnd, 0 ); SetTimer( hWnd, 0, 1000, NULL ); InvalidateRect( hWnd, NULL, TRUE ); } if( zoomLevel != 1 && g_Drawing ) { // Constrain the mouse to the visible region boundRc = BoundMouse( zoomTelescopeTarget, &monInfo, width, height, &cursorPos ); } else { ClipCursor( NULL ); } return TRUE; case WM_IME_CHAR: case WM_CHAR: if( (g_TypeMode != TypeModeOff) && iswprint(static_cast(wParam)) || (static_cast(wParam) == L'&')) { g_HaveTyped = TRUE; TCHAR vKey = static_cast(wParam); g_HaveDrawn = TRUE; // Clear typing cursor rc.left = textPt.x; rc.top = textPt.y; ClearTypingCursor( hdcScreenCompat, hdcScreenCursorCompat, cursorRc, g_BlankedScreen ); if (g_TypeMode == TypeModeRightJustify) { if( !g_TextBuffer.empty() || !g_TextBufferPreviousLines.empty() ) { PopDrawUndo(hdcScreenCompat, &drawUndoList, width, height); //*** } PushDrawUndo(hdcScreenCompat, &drawUndoList, width, height); // Restore previous lines. wParam = 'X'; DrawText(hdcScreenCompat, reinterpret_cast(&wParam), 1, &rc, DT_CALCRECT); const auto lineHeight = rc.bottom - rc.top; rc.top -= static_cast< LONG >( g_TextBufferPreviousLines.size() ) * lineHeight; // Draw the current character on the current line. g_TextBuffer += vKey; drawAllRightJustifiedLines( lineHeight ); } else { DrawText( hdcScreenCompat, &vKey, 1, &rc, DT_CALCRECT|DT_NOPREFIX); DrawText( hdcScreenCompat, &vKey, 1, &rc, DT_LEFT|DT_NOPREFIX); textPt.x += rc.right - rc.left; } InvalidateRect( hWnd, NULL, TRUE ); // Save the key for undo P_TYPED_KEY newKey = static_cast(malloc( sizeof(TYPED_KEY) )); newKey->rc = rc; newKey->Next = typedKeyList; typedKeyList = newKey; // Draw the typing cursor DrawTypingCursor( hWnd, &textPt, hdcScreenCompat, hdcScreenCursorCompat, &cursorRc ); return FALSE; } break; case WM_KEYUP: if( wParam == 'T' && (g_TypeMode == TypeModeOff)) { // lParam is 0 when we're resizing the font and so don't have a cursor that // we need to restore if( !g_Drawing && lParam == 0 ) { OutputDebug(L"Entering typing mode and resetting cursor position\n"); SendMessage( hWnd, WM_LBUTTONDOWN, 0, MAKELPARAM( cursorPos.x, cursorPos.y)); } // Do they want to right-justify text? OutputDebug(L"Keyup Shift: %x\n", GetAsyncKeyState(VK_SHIFT)); if(GetAsyncKeyState(VK_SHIFT) != 0 ) { g_TypeMode = TypeModeRightJustify; g_TextBuffer.clear(); // Also empty all previous lines g_TextBufferPreviousLines = {}; } else { g_TypeMode = TypeModeLeftJustify; } textStartPt = cursorPos; textPt = prevPt; g_HaveTyped = FALSE; // Get a font of a decent size DeleteObject( hTypingFont ); g_LogFont.lfHeight = max( (int) (height / zoomLevel)/g_FontScale, 12 ); if (g_LogFont.lfHeight < 20) g_LogFont.lfQuality = NONANTIALIASED_QUALITY; else g_LogFont.lfQuality = ANTIALIASED_QUALITY; hTypingFont = CreateFontIndirect( &g_LogFont ); SelectObject( hdcScreenCompat, hTypingFont ); // If lparam == 0 that means that we sent the message as part of a font resize if( g_Drawing && lParam != 0) { RestoreCursorArea( hdcScreenCompat, hdcScreenCursorCompat, prevPt ); PushDrawUndo( hdcScreenCompat, &drawUndoList, width, height ); } else if( !g_Drawing ) { textPt = cursorPos; } // Draw the typing cursor DrawTypingCursor( hWnd, &textPt, hdcScreenCompat, hdcScreenCursorCompat, &cursorRc, true ); prevPt = textPt; } break; case WM_KEYDOWN: if( (g_TypeMode != TypeModeOff) && g_HaveTyped && static_cast(wParam) != VK_UP && static_cast(wParam) != VK_DOWN && (isprint( static_cast(wParam)) || wParam == VK_RETURN || wParam == VK_DELETE || wParam == VK_BACK )) { if( wParam == VK_RETURN ) { // Clear the typing cursor ClearTypingCursor( hdcScreenCompat, hdcScreenCursorCompat, cursorRc, g_BlankedScreen ); if( g_TypeMode == TypeModeRightJustify ) { g_TextBufferPreviousLines.push_back( g_TextBuffer ); g_TextBuffer.clear(); } else { // Insert a fake return key in the list to undo. P_TYPED_KEY newKey = static_cast(malloc(sizeof(TYPED_KEY))); newKey->rc.left = textPt.x; newKey->rc.top = textPt.y; newKey->rc.right = newKey->rc.left; newKey->rc.bottom = newKey->rc.top; newKey->Next = typedKeyList; typedKeyList = newKey; } wParam = 'X'; DrawText( hdcScreenCompat, reinterpret_cast(&wParam), 1, &rc, DT_CALCRECT ); textPt.x = prevPt.x; // + g_PenWidth; textPt.y += rc.bottom - rc.top; // Draw the typing cursor DrawTypingCursor( hWnd, &textPt, hdcScreenCompat, hdcScreenCursorCompat, &cursorRc ); } else if( wParam == VK_DELETE || wParam == VK_BACK ) { P_TYPED_KEY deletedKey = typedKeyList; if( deletedKey ) { // Clear the typing cursor ClearTypingCursor( hdcScreenCompat, hdcScreenCursorCompat, cursorRc, g_BlankedScreen ); if( g_TypeMode == TypeModeRightJustify ) { if( !g_TextBuffer.empty() || !g_TextBufferPreviousLines.empty() ) { PopDrawUndo( hdcScreenCompat, &drawUndoList, width, height ); } PushDrawUndo( hdcScreenCompat, &drawUndoList, width, height ); rc.left = textPt.x; rc.top = textPt.y; // Restore the previous lines. wParam = 'X'; DrawText( hdcScreenCompat, reinterpret_cast(&wParam), 1, &rc, DT_CALCRECT ); const auto lineHeight = rc.bottom - rc.top; const bool lineWasEmpty = g_TextBuffer.empty(); drawAllRightJustifiedLines( lineHeight, true ); if( lineWasEmpty && !g_TextBufferPreviousLines.empty() ) { g_TextBuffer = g_TextBufferPreviousLines.back(); g_TextBufferPreviousLines.pop_back(); textPt.y -= lineHeight; } } else { RECT rect = deletedKey->rc; if (g_BlankedScreen) { BlankScreenArea(hdcScreenCompat, &rect, g_BlankedScreen); } else { BitBlt(hdcScreenCompat, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, hdcScreenSaveCompat, rect.left, rect.top, SRCCOPY | CAPTUREBLT ); } InvalidateRect( hWnd, NULL, FALSE ); textPt.x = rect.left; textPt.y = rect.top; typedKeyList = deletedKey->Next; free(deletedKey); // Refresh cursor if we deleted the last key if( typedKeyList == NULL ) { SendMessage( hWnd, WM_MOUSEMOVE, 0, MAKELPARAM( prevPt.x, prevPt.y ) ); } } DrawTypingCursor( hWnd, &textPt, hdcScreenCompat, hdcScreenCursorCompat, &cursorRc ); } } break; } switch (wParam) { case 'R': case 'B': case 'Y': case 'O': case 'G': case 'X': case 'P': if( (g_Zoomed || g_TimerActive) && (g_TypeMode == TypeModeOff)) { PDWORD penColor; if( g_TimerActive ) penColor = &g_BreakPenColor; else penColor = &g_PenColor; if( wParam == 'R' ) *penColor = COLOR_RED; else if( wParam == 'G' ) *penColor = COLOR_GREEN; else if( wParam == 'B' ) *penColor = COLOR_BLUE; else if( wParam == 'Y' ) *penColor = COLOR_YELLOW; else if( wParam == 'O' ) *penColor = COLOR_ORANGE; else if( wParam == 'P' ) *penColor = COLOR_PINK; else if( wParam == 'X' ) { if( GetWindowLong( hWnd, GWL_EXSTYLE ) & WS_EX_LAYERED ) { // Blur is not supported in LiveDraw break; } *penColor = COLOR_BLUR; } bool shift = GetKeyState( VK_SHIFT ) & 0x8000; if( shift && ( GetWindowLong( hWnd, GWL_EXSTYLE ) & WS_EX_LAYERED ) ) { // Highlight is not supported in LiveDraw break; } reg.WriteRegSettings( RegSettings ); DeleteObject( hDrawingPen ); SetTextColor( hdcScreenCompat, *penColor ); // Highlight and blur level if( shift && *penColor != COLOR_BLUR ) { *penColor |= (g_AlphaBlend << 24); } else { if( *penColor == COLOR_BLUR ) { g_BlurRadius = shift ? STRONG_BLUR_RADIUS : NORMAL_BLUR_RADIUS; } *penColor |= (0xFF << 24); } hDrawingPen = CreatePen(PS_SOLID, g_PenWidth, *penColor & 0xFFFFFF); SelectObject( hdcScreenCompat, hDrawingPen ); if( g_Drawing ) { SendMessage( hWnd, WM_MOUSEMOVE, 0, MAKELPARAM( prevPt.x, prevPt.y )); } else if( g_TimerActive ) { InvalidateRect( hWnd, NULL, FALSE ); } else if( g_TypeMode != TypeModeOff ) { ClearTypingCursor( hdcScreenCompat, hdcScreenCursorCompat, cursorRc, g_BlankedScreen ); DrawTypingCursor( hWnd, &textPt, hdcScreenCompat, hdcScreenCursorCompat, &cursorRc ); InvalidateRect( hWnd, NULL, FALSE ); } } break; case 'Z': if( (GetKeyState( VK_CONTROL ) & 0x8000 ) && g_HaveDrawn && !g_Tracing ) { if( PopDrawUndo( hdcScreenCompat, &drawUndoList, width, height )) { if( g_Drawing ) { SaveCursorArea( hdcScreenCursorCompat, hdcScreenCompat, prevPt ); SendMessage( hWnd, WM_MOUSEMOVE, 0, MAKELPARAM( prevPt.x, prevPt.y )); } else { SaveCursorArea(hdcScreenCursorCompat, hdcScreenCompat, prevPt); } InvalidateRect( hWnd, NULL, FALSE ); } } break; case VK_SPACE: if( g_Drawing && !g_Tracing ) { SetCursorPos( boundRc.left + (boundRc.right - boundRc.left)/2, boundRc.top + (boundRc.bottom - boundRc.top)/2 ); SendMessage( hWnd, WM_MOUSEMOVE, 0, MAKELPARAM( (boundRc.right - boundRc.left)/2, (boundRc.bottom - boundRc.top)/2 )); } break; case 'W': case 'K': // Block user-driven sketch pad in liveDraw if( lParam != LIVE_DRAW_ZOOM && ( GetWindowLongPtr( hWnd, GWL_EXSTYLE ) & WS_EX_LAYERED ) ) { break; } // Don't allow screen blanking while we've got the typing cursor active // because we don't really handle going from white to black. if( g_Zoomed && (g_TypeMode == TypeModeOff)) { if( !g_Drawing ) { SendMessage( hWnd, WM_LBUTTONDOWN, 0, MAKELPARAM( cursorPos.x, cursorPos.y)); } // Restore area where cursor was previously RestoreCursorArea( hdcScreenCompat, hdcScreenCursorCompat, prevPt ); PushDrawUndo( hdcScreenCompat, &drawUndoList, width, height ); g_BlankedScreen = static_cast(wParam); rc.top = rc.left = 0; rc.bottom = height; rc.right = width; BlankScreenArea( hdcScreenCompat, &rc, g_BlankedScreen ); InvalidateRect( hWnd, NULL, FALSE ); // Save area that's going to be occupied by new cursor position SaveCursorArea( hdcScreenCursorCompat, hdcScreenCompat, prevPt ); SendMessage( hWnd, WM_MOUSEMOVE, 0, MAKELPARAM( prevPt.x, prevPt.y )); } break; case 'E': // Don't allow erase while we have the typing cursor active if( g_HaveDrawn && (g_TypeMode == TypeModeOff)) { DeleteDrawUndoList( &drawUndoList ); g_HaveDrawn = FALSE; OutputDebug(L"Erase\n"); if(GetWindowLong(hWnd, GWL_EXSTYLE) & WS_EX_LAYERED) { SendMessage(hWnd, WM_KEYDOWN, 'K', 0); } else { BitBlt(hdcScreenCompat, 0, 0, bmp.bmWidth, bmp.bmHeight, hdcScreenSaveCompat, 0, 0, SRCCOPY | CAPTUREBLT); if (g_Drawing) { OutputDebug(L"Erase: draw cursor\n"); SaveCursorArea(hdcScreenCursorCompat, hdcScreenCompat, prevPt); DrawCursor(hdcScreenCompat, prevPt, zoomLevel, width, height); g_HaveDrawn = TRUE; } } InvalidateRect( hWnd, NULL, FALSE ); g_BlankedScreen = FALSE; } break; case VK_UP: SendMessage( hWnd, WM_MOUSEWHEEL, MAKEWPARAM( GetAsyncKeyState( VK_LCONTROL ) != 0 || GetAsyncKeyState( VK_RCONTROL ) != 0 ? MK_CONTROL: 0, WHEEL_DELTA), 0 ); return TRUE; case VK_DOWN: SendMessage( hWnd, WM_MOUSEWHEEL, MAKEWPARAM( GetAsyncKeyState( VK_LCONTROL ) != 0 || GetAsyncKeyState( VK_RCONTROL ) != 0 ? MK_CONTROL: 0, -WHEEL_DELTA), 0 ); return TRUE; case VK_LEFT: case VK_RIGHT: if( wParam == VK_RIGHT ) delta = 10; else delta = -10; if( g_TimerActive && (breakTimeout > 0 || delta )) { if( breakTimeout < 0 ) breakTimeout = 0; breakTimeout += delta; breakTimeout -= (breakTimeout % 10); if( breakTimeout < 0 ) breakTimeout = 0; KillTimer( hWnd, 0 ); SetTimer( hWnd, 0, 1000, NULL ); InvalidateRect( hWnd, NULL, TRUE ); } break; case VK_ESCAPE: if( g_TypeMode != TypeModeOff) { // Turn off SendMessage( hWnd, WM_USER_TYPING_OFF, 0, 0 ); } else { forcePenResize = TRUE; PostMessage( hWnd, WM_HOTKEY, ZOOM_HOTKEY, 0 ); // In case we were in liveDraw if( GetWindowLong(hWnd, GWL_EXSTYLE) & WS_EX_LAYERED) { KillTimer(hWnd, 3); LONG_PTR exStyle = GetWindowLongPtr(hWnd, GWL_EXSTYLE); SetWindowLongPtr(hWnd, GWL_EXSTYLE, exStyle & ~WS_EX_LAYERED); pMagSetWindowFilterList( g_hWndLiveZoomMag, MW_FILTERMODE_EXCLUDE, 1, &hWnd ); SendMessage( g_hWndLiveZoom, WM_USER_MAGNIFY_CURSOR, TRUE, 0 ); } } break; } return TRUE; case WM_RBUTTONDOWN: SendMessage( hWnd, WM_USER_EXIT_MODE, 0, 0 ); break; case WM_MOUSEMOVE: OutputDebug(L"MOUSEMOVE: zoomed: %d drawing: %d tracing: %d\n", g_Zoomed, g_Drawing, g_Tracing); OutputDebug(L"Window visible: %d Topmost: %d\n", IsWindowVisible(hWnd), GetWindowLong(hWnd, GWL_EXSTYLE)& WS_EX_TOPMOST); if( g_Zoomed && (g_TypeMode == TypeModeOff) && !g_bSaveInProgress ) { if( g_Drawing ) { POINT currentPt; // Are we in pen mode on a tablet? lParam = ScalePenPosition( zoomLevel, &monInfo, boundRc, message, lParam); currentPt.x = LOWORD(lParam); currentPt.y = HIWORD(lParam); if(lParam == 0) { // Drop it OutputDebug(L"Mousemove: Dropping\n"); break; } else if(g_DrawingShape) { SetROP2(hdcScreenCompat, R2_NOTXORPEN); // If a previous target rectangle exists, erase // it by drawing another rectangle on top. if( g_rcRectangle.top != g_rcRectangle.bottom || g_rcRectangle.left != g_rcRectangle.right ) { if( prevPenWidth != g_PenWidth ) { auto penWidth = g_PenWidth; g_PenWidth = prevPenWidth; auto prevPen = CreatePen( PS_SOLID, g_PenWidth, g_PenColor & 0xFFFFFF ); SelectObject( hdcScreenCompat, prevPen ); DrawShape( g_DrawingShape, hdcScreenCompat, &g_rcRectangle ); g_PenWidth = penWidth; SelectObject( hdcScreenCompat, hDrawingPen ); DeleteObject( prevPen ); } else { DrawShape( g_DrawingShape, hdcScreenCompat, &g_rcRectangle ); } } // Save the coordinates of the target rectangle. // Avoid invalid rectangles by ensuring that the // value of the left coordinate is greater than // that of the right, and that the value of the // bottom coordinate is greater than that of // the top. if( g_DrawingShape == DRAW_LINE || g_DrawingShape == DRAW_ARROW ) { g_rcRectangle.right = static_cast(LOWORD(lParam)); g_rcRectangle.bottom = static_cast(HIWORD(lParam)); } else { if ((g_RectangleAnchor.x < currentPt.x) && (g_RectangleAnchor.y > currentPt.y)) { SetRect(&g_rcRectangle, g_RectangleAnchor.x, currentPt.y, currentPt.x, g_RectangleAnchor.y); } else if ((g_RectangleAnchor.x > currentPt.x) && (g_RectangleAnchor.y > currentPt.y )) { SetRect(&g_rcRectangle, currentPt.x, currentPt.y, g_RectangleAnchor.x,g_RectangleAnchor.y); } else if ((g_RectangleAnchor.x > currentPt.x) && (g_RectangleAnchor.y < currentPt.y )) { SetRect(&g_rcRectangle, currentPt.x, g_RectangleAnchor.y, g_RectangleAnchor.x, currentPt.y ); } else { SetRect(&g_rcRectangle, g_RectangleAnchor.x, g_RectangleAnchor.y, currentPt.x, currentPt.y ); } } if (g_rcRectangle.left != g_rcRectangle.right || g_rcRectangle.top != g_rcRectangle.bottom) { // Draw the new target rectangle. DrawShape(g_DrawingShape, hdcScreenCompat, &g_rcRectangle); OutputDebug(L"SHAPE: (%d, %d) - (%d, %d)\n", g_rcRectangle.left, g_rcRectangle.top, g_rcRectangle.right, g_rcRectangle.bottom); } prevPenWidth = g_PenWidth; SetROP2( hdcScreenCompat, R2_NOP ); } else if (g_Tracing) { OutputDebug(L"Mousemove: Tracing\n"); g_HaveDrawn = TRUE; Gdiplus::Graphics dstGraphics(hdcScreenCompat); if( ( GetWindowLong( g_hWndMain, GWL_EXSTYLE ) & WS_EX_LAYERED ) == 0 ) { dstGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias); } Gdiplus::Color color = ColorFromColorRef(g_PenColor); Gdiplus::Pen pen(color, static_cast(g_PenWidth)); pen.SetLineCap(Gdiplus::LineCapRound, Gdiplus::LineCapRound, Gdiplus::DashCapRound); // If highlighting, use a double layer approach OutputDebug(L"PenColor: %x\n", g_PenColor); OutputDebug(L"Blur color: %x\n", COLOR_BLUR); if( (g_PenColor & 0xFFFFFF) == COLOR_BLUR) { OutputDebug(L"BLUR\n"); // Restore area where cursor was previously RestoreCursorArea(hdcScreenCompat, hdcScreenCursorCompat, prevPt); // Create a new bitmap that's the size of the area covered by the line + 2 * g_PenWidth Gdiplus::Rect lineBounds = GetLineBounds( prevPt, currentPt, g_PenWidth ); Gdiplus::Bitmap* lineBitmap = DrawBitmapLine(lineBounds, prevPt, currentPt, &pen); Gdiplus::BitmapData* lineData = LockGdiPlusBitmap(lineBitmap); BYTE* pPixels = static_cast(lineData->Scan0); // Copy the contents of the screen bitmap to the temporary bitmap GetOldestUndo(drawUndoList); // Create a GDI bitmap that's the size of the lineBounds rectangle Gdiplus::Bitmap *blurBitmap = CreateGdiplusBitmap( hdcScreenCompat, // oldestUndo->hDc, lineBounds.X, lineBounds.Y, lineBounds.Width, lineBounds.Height); // Blur it BitmapBlur(blurBitmap); BlurScreen(hdcScreenCompat, &lineBounds, blurBitmap, pPixels); // Unlock the bits lineBitmap->UnlockBits(lineData); delete lineBitmap; delete blurBitmap; // Invalidate the updated rectangle InvalidateGdiplusRect( hWnd, lineBounds ); // Save area that's going to be occupied by new cursor position SaveCursorArea(hdcScreenCursorCompat, hdcScreenCompat, currentPt); // Draw new cursor DrawCursor(hdcScreenCompat, currentPt, zoomLevel, width, height); } else if(PEN_COLOR_HIGHLIGHT(g_PenColor)) { // This is a highlighting pen color Gdiplus::Rect lineBounds = GetLineBounds(prevPt, currentPt, g_PenWidth); Gdiplus::Bitmap* lineBitmap = DrawBitmapLine(lineBounds, prevPt, currentPt, &pen); Gdiplus::BitmapData* lineData = LockGdiPlusBitmap(lineBitmap); BYTE* pPixels = static_cast(lineData->Scan0); // Create a DIB section for efficient pixel manipulation BITMAPINFO bmi = { 0 }; bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmi.bmiHeader.biWidth = lineBounds.Width; bmi.bmiHeader.biHeight = -lineBounds.Height; // Top-down DIB bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biBitCount = 32; // 32 bits per pixel bmi.bmiHeader.biCompression = BI_RGB; VOID* pDIBBits; HBITMAP hDIB = CreateDIBSection(hdcScreenCompat, &bmi, DIB_RGB_COLORS, &pDIBBits, NULL, 0); HDC hdcDIB = CreateCompatibleDC(hdcScreenCompat); SelectObject(hdcDIB, hDIB); // Copy the relevant part of hdcScreenCompat to the DIB BitBlt(hdcDIB, 0, 0, lineBounds.Width, lineBounds.Height, hdcScreenCompat, lineBounds.X, lineBounds.Y, SRCCOPY); // Pointer to the DIB bits BYTE* pDestPixels = static_cast(pDIBBits); // Pointer to screen bits HDC hdcDIBOrig; HBITMAP hDibOrigBitmap, hDibBitmap; P_DRAW_UNDO oldestUndo = GetOldestUndo(drawUndoList); BYTE* pDestPixels2 = CreateBitmapMemoryDIB(hdcScreenCompat, oldestUndo->hDc, &lineBounds, &hdcDIBOrig, &hDibBitmap, &hDibOrigBitmap); for (int local_y = 0; local_y < lineBounds.Height; ++local_y) { for (int local_x = 0; local_x < lineBounds.Width; ++local_x) { int index = (local_y * lineBounds.Width * 4) + (local_x * 4); // Assuming 4 bytes per pixel // BYTE b = pPixels[index + 0]; // Blue channel // BYTE g = pPixels[index + 1]; // Green channel // BYTE r = pPixels[index + 2]; // Red channel BYTE a = pPixels[index + 3]; // Alpha channel // Check if this is a drawn pixel if (a != 0) { // Assuming pDestPixels is a valid pointer to the destination bitmap's pixel data BYTE destB = pDestPixels2[index + 0]; // Blue channel BYTE destG = pDestPixels2[index + 1]; // Green channel BYTE destR = pDestPixels2[index + 2]; // Red channel // Create a COLORREF value from the destination pixel data COLORREF currentPixel = RGB(destR, destG, destB); // Blend the colors COLORREF newPixel = BlendColors(currentPixel, g_PenColor); // Update the destination pixel data with the new color pDestPixels[index + 0] = GetBValue(newPixel); pDestPixels[index + 1] = GetGValue(newPixel); pDestPixels[index + 2] = GetRValue(newPixel); } } } // Copy the updated DIB back to hdcScreenCompat BitBlt(hdcScreenCompat, lineBounds.X, lineBounds.Y, lineBounds.Width, lineBounds.Height, hdcDIB, 0, 0, SRCCOPY); // Clean up DeleteObject(hDIB); DeleteDC(hdcDIB); SelectObject(hdcDIBOrig, hDibOrigBitmap); DeleteObject(hDibBitmap); DeleteDC(hdcDIBOrig); // Invalidate the updated rectangle InvalidateGdiplusRect(hWnd, lineBounds); } else { // Normal tracing dstGraphics.DrawLine(&pen, static_cast(prevPt.x), static_cast(prevPt.y), static_cast(currentPt.x), static_cast(currentPt.y)); } } else { OutputDebug(L"Mousemove: Moving cursor\n"); // Restore area where cursor was previously RestoreCursorArea( hdcScreenCompat, hdcScreenCursorCompat, prevPt ); // Save area that's going to be occupied by new cursor position SaveCursorArea( hdcScreenCursorCompat, hdcScreenCompat, currentPt ); // Draw new cursor DrawCursor( hdcScreenCompat, currentPt, zoomLevel, width, height ); } if( g_DrawingShape ) { InvalidateRect( hWnd, NULL, FALSE ); } else { // Invalidate area just modified InvalidateCursorMoveArea( hWnd, zoomLevel, width, height, currentPt, prevPt, cursorPos ); } prevPt = currentPt; // In liveDraw we an miss the mouse up if( GetWindowLong(hWnd, GWL_EXSTYLE) & WS_EX_LAYERED) { if((GetAsyncKeyState(VK_LBUTTON) & 0x8000) == 0) { OutputDebug(L"LIVE_DRAW missed mouse up. Sending synthetic.\n"); SendMessage(hWnd, WM_LBUTTONUP, wParam, lParam); } } } else { cursorPos.x = LOWORD( lParam ); cursorPos.y = HIWORD( lParam ); InvalidateRect( hWnd, NULL, FALSE ); } } else if( g_Zoomed && (g_TypeMode != TypeModeOff) && !g_HaveTyped ) { ClearTypingCursor( hdcScreenCompat, hdcScreenCursorCompat, cursorRc, g_BlankedScreen ); textPt.x = prevPt.x = LOWORD( lParam ); textPt.y = prevPt.y = HIWORD( lParam ); // Draw the typing cursor DrawTypingCursor( hWnd, &textPt, hdcScreenCompat, hdcScreenCursorCompat, &cursorRc, true ); prevPt = textPt; InvalidateRect( hWnd, NULL, FALSE ); } #if 0 { static int index = 0; OutputDebug( L"%d: foreground: %x focus: %x (hwnd: %x)\n", index++, (DWORD) PtrToUlong(GetForegroundWindow()), PtrToUlong(GetFocus()), PtrToUlong(hWnd)); } #endif return TRUE; case WM_LBUTTONDOWN: g_StraightDirection = 0; if( g_Zoomed && (g_TypeMode == TypeModeOff) && zoomTelescopeTarget == zoomLevel ) { OutputDebug(L"LBUTTONDOWN: drawing\n"); // Save current bitmap to undo history if( g_HaveDrawn ) { RestoreCursorArea( hdcScreenCompat, hdcScreenCursorCompat, prevPt ); } // don't push undo if we sent this to ourselves for a pen resize if( wParam != -1 ) { PushDrawUndo( hdcScreenCompat, &drawUndoList, width, height ); } else { wParam = 0; } // Are we in pen mode on a tablet? lParam = ScalePenPosition( zoomLevel, &monInfo, boundRc, message, lParam); if (lParam == 0) { // Drop it break; } else if( g_Drawing ) { // is the user drawing a rectangle? if( wParam & MK_CONTROL || wParam & MK_SHIFT || GetKeyState( VK_TAB ) < 0 ) { // Restore area where cursor was previously RestoreCursorArea( hdcScreenCompat, hdcScreenCursorCompat, prevPt ); if( wParam & MK_SHIFT && wParam & MK_CONTROL ) g_DrawingShape = DRAW_ARROW; else if( wParam & MK_CONTROL ) g_DrawingShape = DRAW_RECTANGLE; else if( wParam & MK_SHIFT ) g_DrawingShape = DRAW_LINE; else g_DrawingShape = DRAW_ELLIPSE; g_RectangleAnchor.x = LOWORD(lParam); g_RectangleAnchor.y = HIWORD(lParam); SetRect(&g_rcRectangle, g_RectangleAnchor.x, g_RectangleAnchor.y, g_RectangleAnchor.x, g_RectangleAnchor.y); } else { Gdiplus::Graphics dstGraphics(hdcScreenCompat); if( ( GetWindowLong( g_hWndMain, GWL_EXSTYLE ) & WS_EX_LAYERED ) == 0 ) { dstGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias); } Gdiplus::Color color = ColorFromColorRef(g_PenColor); Gdiplus::Pen pen(color, static_cast(g_PenWidth)); Gdiplus::GraphicsPath path; pen.SetLineJoin(Gdiplus::LineJoinRound); path.AddLine(static_cast(prevPt.x), prevPt.y, prevPt.x, prevPt.y); dstGraphics.DrawPath(&pen, &path); } g_Tracing = TRUE; SetROP2( hdcScreenCompat, R2_COPYPEN ); prevPt.x = LOWORD(lParam); prevPt.y = HIWORD(lParam); g_HaveDrawn = TRUE; } else { OutputDebug(L"Tracing on\n"); // Turn on drawing if( !g_HaveDrawn ) { // refresh drawing bitmap with original screen image BitBlt(hdcScreenCompat, 0, 0, bmp.bmWidth, bmp.bmHeight, hdcScreenSaveCompat, 0, 0, SRCCOPY|CAPTUREBLT ); g_HaveDrawn = TRUE; } DeleteObject( hDrawingPen ); hDrawingPen = CreatePen(PS_SOLID, g_PenWidth, g_PenColor & 0xFFFFFF); SelectObject( hdcScreenCompat, hDrawingPen ); // is the user drawing a rectangle? if( wParam & MK_CONTROL && g_Drawing ) { // Restore area where cursor was previously RestoreCursorArea( hdcScreenCompat, hdcScreenCursorCompat, prevPt ); // Configure rectangle drawing g_DrawingShape = TRUE; g_RectangleAnchor.x = LOWORD(lParam); g_RectangleAnchor.y = HIWORD(lParam); SetRect(&g_rcRectangle, g_RectangleAnchor.x, g_RectangleAnchor.y, g_RectangleAnchor.x, g_RectangleAnchor.y); OutputDebug( L"RECTANGLE: %d, %d\n", prevPt.x, prevPt.y ); } else { prevPt.x = LOWORD( lParam ); prevPt.y = HIWORD( lParam ); SaveCursorArea( hdcScreenCursorCompat, hdcScreenCompat, prevPt ); Gdiplus::Graphics dstGraphics(hdcScreenCursorCompat); if( ( GetWindowLong( g_hWndMain, GWL_EXSTYLE ) & WS_EX_LAYERED ) == 0 ) { dstGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias); } Gdiplus::Color color = ColorFromColorRef(g_PenColor); Gdiplus::Pen pen(color, static_cast(g_PenWidth)); Gdiplus::GraphicsPath path; pen.SetLineJoin(Gdiplus::LineJoinRound); path.AddLine(static_cast(prevPt.x), prevPt.y, prevPt.x, prevPt.y); dstGraphics.DrawPath(&pen, &path); } InvalidateRect( hWnd, NULL, FALSE ); // If we're in live zoom, make the drawing pen larger to compensate if( g_ZoomOnLiveZoom && forcePenResize ) { forcePenResize = FALSE; ResizePen( hWnd, hdcScreenCompat, hdcScreenCursorCompat, prevPt, g_Tracing, &g_Drawing, g_LiveZoomLevel, FALSE, min( static_cast(g_LiveZoomLevel * g_RootPenWidth), static_cast(g_LiveZoomLevel * MAX_PEN_WIDTH) ) ); OutputDebug( L"LIVEZOOM_DRAW: zoomLevel: %d rootPenWidth: %d penWidth: %d\n", static_cast(g_LiveZoomLevel), g_RootPenWidth, g_PenWidth ); } else if( !g_ZoomOnLiveZoom && forcePenResize ) { forcePenResize = FALSE; // Scale pen down to root for regular drawing mode ResizePen( hWnd, hdcScreenCompat, hdcScreenCursorCompat, prevPt, g_Tracing, &g_Drawing, g_LiveZoomLevel, FALSE, g_RootPenWidth ); } g_Drawing = TRUE; EnableDisableStickyKeys( FALSE ); OutputDebug( L"LBUTTONDOWN: %d, %d\n", prevPt.x, prevPt.y ); // Constrain the mouse to the visible region boundRc = BoundMouse( zoomLevel, &monInfo, width, height, &cursorPos ); } } else if( g_TypeMode != TypeModeOff ) { if( !g_HaveTyped ) { g_HaveTyped = TRUE; } else { SendMessage( hWnd, WM_USER_TYPING_OFF, 0, 0 ); } } return TRUE; case WM_LBUTTONUP: OutputDebug(L"LBUTTONUP: zoomed: %d drawing: %d tracing: %d\n", g_Zoomed, g_Drawing, g_Tracing); if( g_Zoomed && g_Drawing && g_Tracing ) { // Are we in pen mode on a tablet? lParam = ScalePenPosition( zoomLevel, &monInfo, boundRc, message, lParam); OutputDebug(L"LBUTTONUP: %d, %d\n", LOWORD(lParam), HIWORD(lParam)); if (lParam == 0) { // Drop it break; } POINT adjustPos; adjustPos.x = LOWORD(lParam); adjustPos.y = HIWORD(lParam); if( g_StraightDirection == -1 ) { adjustPos.x = prevPt.x; } else { adjustPos.y = prevPt.y; } lParam = MAKELPARAM( adjustPos.x, adjustPos.y ); if( !g_DrawingShape ) { Gdiplus::Graphics dstGraphics(hdcScreenCompat); if( ( GetWindowLong( g_hWndMain, GWL_EXSTYLE ) & WS_EX_LAYERED ) == 0 ) { dstGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias); } Gdiplus::Color color = ColorFromColorRef(g_PenColor); Gdiplus::Pen pen(color, static_cast(g_PenWidth)); Gdiplus::GraphicsPath path; pen.SetLineJoin(Gdiplus::LineJoinRound); path.AddLine(prevPt.x, prevPt.y, LOWORD(lParam), HIWORD(lParam)); dstGraphics.DrawPath(&pen, &path); prevPt.x = LOWORD( lParam ); prevPt.y = HIWORD( lParam ); if ((g_PenColor & 0xFFFFFF) == COLOR_BLUR) { RestoreCursorArea(hdcScreenCompat, hdcScreenCursorCompat, prevPt); } SaveCursorArea( hdcScreenCursorCompat, hdcScreenCompat, prevPt ); DrawCursor( hdcScreenCompat, prevPt, zoomLevel, width, height ); } else if (g_rcRectangle.top != g_rcRectangle.bottom || g_rcRectangle.left != g_rcRectangle.right ) { // erase previous SetROP2(hdcScreenCompat, R2_NOTXORPEN); DrawShape( g_DrawingShape, hdcScreenCompat, &g_rcRectangle ); // Draw the final shape HBRUSH hBrush = static_cast(GetStockObject( NULL_BRUSH )); HBRUSH oldHbrush = static_cast(SelectObject( hdcScreenCompat, hBrush )); SetROP2( hdcScreenCompat, R2_COPYPEN ); // smooth line if( g_SnapToGrid ) { if( g_DrawingShape == DRAW_LINE || g_DrawingShape == DRAW_ARROW ) { if( abs(g_rcRectangle.bottom - g_rcRectangle.top) < abs(g_rcRectangle.right - g_rcRectangle.left)/10 ) { g_rcRectangle.bottom = g_rcRectangle.top-1; } if( abs(g_rcRectangle.right - g_rcRectangle.left) < abs(g_rcRectangle.bottom - g_rcRectangle.top)/10 ) { g_rcRectangle.right = g_rcRectangle.left-1; } } } // Draw final one using Gdi+ DrawShape( g_DrawingShape, hdcScreenCompat, &g_rcRectangle, true ); InvalidateRect( hWnd, NULL, FALSE ); DeleteObject( hBrush ); SelectObject( hdcScreenCompat, oldHbrush ); prevPt.x = LOWORD( lParam ); prevPt.y = HIWORD( lParam ); SaveCursorArea( hdcScreenCursorCompat, hdcScreenCompat, prevPt ); } g_Tracing = FALSE; g_DrawingShape = FALSE; OutputDebug( L"LBUTTONUP:" ); } return TRUE; case WM_GETMINMAXINFO: reinterpret_cast(lParam)->ptMaxSize.x = width; reinterpret_cast(lParam)->ptMaxSize.y = height; reinterpret_cast(lParam)->ptMaxPosition.x = 0; reinterpret_cast(lParam)->ptMaxPosition.y = 0; return TRUE; case WM_USER_TYPING_OFF: { if( g_TypeMode != TypeModeOff ) { g_TypeMode = TypeModeOff; ClearTypingCursor( hdcScreenCompat, hdcScreenCursorCompat, cursorRc, g_BlankedScreen ); InvalidateRect( hWnd, NULL, FALSE ); DeleteTypedText( &typedKeyList ); // 1 means don't reset the cursor. We get that for font resizing // Only move the cursor if we're drawing, because otherwise the screen moves to center // on the new cursor position if( wParam != 1 && g_Drawing ) { prevPt.x = cursorRc.left; prevPt.y = cursorRc.top; SetCursorPos( monInfo.rcMonitor.left + prevPt.x, monInfo.rcMonitor.top + prevPt.y ); SaveCursorArea( hdcScreenCursorCompat, hdcScreenCompat, prevPt ); SendMessage( hWnd, WM_MOUSEMOVE, 0, MAKELPARAM( prevPt.x, prevPt.y )); } else if( !g_Drawing) { // FIX: would be nice to reset cursor so screen doesn't move prevPt = textStartPt; SaveCursorArea( hdcScreenCursorCompat, hdcScreenCompat, prevPt ); SetCursorPos( prevPt.x, prevPt.y ); SendMessage( hWnd, WM_MOUSEMOVE, 0, MAKELPARAM( prevPt.x, prevPt.y )); } } } return TRUE; case WM_USER_TRAY_ACTIVATE: switch( lParam ) { case WM_RBUTTONUP: case WM_LBUTTONUP: case WM_CONTEXTMENU: { // Set the foreground window so the menu can be closed by clicking elsewhere when // opened via right click, and so keyboard navigation works when opened with the menu // key or Shift-F10. SetForegroundWindow( hWndOptions ? hWndOptions : hWnd ); // Pop up context menu POINT pt; GetCursorPos( &pt ); hPopupMenu = CreatePopupMenu(); if(!g_StartedByPowerToys) { // Exiting will happen through disabling in PowerToys, not the context menu. InsertMenu( hPopupMenu, 0, MF_BYPOSITION, IDCANCEL, L"退出(&X)" ); InsertMenu( hPopupMenu, 0, MF_BYPOSITION|MF_SEPARATOR, 0, NULL ); } InsertMenu( hPopupMenu, 0, MF_BYPOSITION | ( g_RecordToggle ? MF_CHECKED : 0 ), IDC_RECORD, L"录屏(&R)" ); InsertMenu( hPopupMenu, 0, MF_BYPOSITION, IDC_ZOOM, L"放大(&Z)" ); InsertMenu( hPopupMenu, 0, MF_BYPOSITION, IDC_DRAW, L"画图(&D)" ); InsertMenu( hPopupMenu, 0, MF_BYPOSITION, IDC_BREAK, L"倒计时(&B)" ); if(!g_StartedByPowerToys) { // When started by PowerToys, options are configured through the PowerToys Settings. InsertMenu( hPopupMenu, 0, MF_BYPOSITION|MF_SEPARATOR, 0, NULL ); InsertMenu( hPopupMenu, 0, MF_BYPOSITION, IDC_OPTIONS, L"设置(&O)" ); } TrackPopupMenu( hPopupMenu, 0, pt.x , pt.y, 0, hWnd, NULL ); DestroyMenu( hPopupMenu ); break; } case WM_LBUTTONDBLCLK: if( !g_TimerActive ) { SendMessage( hWnd, WM_COMMAND, IDC_OPTIONS, 0 ); } else { SetForegroundWindow( hWnd ); } break; } break; case WM_USER_STOP_RECORDING: StopRecording(); break; case WM_USER_SAVE_CURSOR: if( g_Zoomed == TRUE ) { GetCursorPos( &savedCursorPos ); if( g_Drawing == TRUE ) { ClipCursor( NULL ); } } break; case WM_USER_RESTORE_CURSOR: if( g_Zoomed == TRUE ) { if( g_Drawing == TRUE ) { boundRc = BoundMouse( zoomLevel, &monInfo, width, height, &cursorPos ); } SetCursorPos( savedCursorPos.x, savedCursorPos.y ); } break; case WM_USER_EXIT_MODE: if( g_Zoomed ) { // Turn off if( g_TypeMode != TypeModeOff ) { SendMessage( hWnd, WM_USER_TYPING_OFF, 0, 0 ); } else if( !g_Drawing ) { // Turn off PostMessage( hWnd, WM_HOTKEY, ZOOM_HOTKEY, 0 ); } else { if( !g_Tracing ) { RestoreCursorArea( hdcScreenCompat, hdcScreenCursorCompat, prevPt ); // Ensure the cursor area is painted before returning InvalidateRect( hWnd, NULL, FALSE ); UpdateWindow( hWnd ); // Make the magnified cursor visible again if LiveDraw is on in LiveZoom if( GetWindowLong( hWnd, GWL_EXSTYLE ) & WS_EX_LAYERED ) { if( IsWindowVisible( g_hWndLiveZoom ) ) { SendMessage( g_hWndLiveZoom, WM_USER_MAGNIFY_CURSOR, TRUE, 0 ); } } } if( zoomLevel != 1 ) { // Restore the cursor position to prevent moving the view in static zoom SetCursorPos( monInfo.rcMonitor.left + cursorPos.x, monInfo.rcMonitor.top + cursorPos.y ); } g_Drawing = FALSE; g_Tracing = FALSE; EnableDisableStickyKeys( TRUE ); SendMessage( hWnd, WM_USER_TYPING_OFF, 0, 0 ); // Unclip cursor ClipCursor( NULL ); } } else if( g_TimerActive ) { // Turn off PostMessage( hWnd, WM_HOTKEY, ZOOM_HOTKEY, 0 ); } break; case WM_USER_RELOAD_SETTINGS: { // Reload the settings. This message is called from PowerToys after a setting is changed by the user. reg.ReadRegSettings(RegSettings); // Apply tray icon setting EnableDisableTrayIcon(hWnd, g_ShowTrayIcon); // This is also called by ZoomIt when it starts and loads the Settings. Opacity is added after loading from registry, so we use the same pattern. if ((g_PenColor >> 24) == 0) { g_PenColor |= 0xFF << 24; } // Apply hotkey settings UnregisterAllHotkeys(hWnd); g_ToggleMod = GetKeyMod(g_ToggleKey); g_LiveZoomToggleMod = GetKeyMod(g_LiveZoomToggleKey); g_DrawToggleMod = GetKeyMod(g_DrawToggleKey); g_BreakToggleMod = GetKeyMod(g_BreakToggleKey); g_DemoTypeToggleMod = GetKeyMod(g_DemoTypeToggleKey); g_SnipToggleMod = GetKeyMod(g_SnipToggleKey); g_RecordToggleMod = GetKeyMod(g_RecordToggleKey); BOOL showOptions = FALSE; if (g_ToggleKey) { if (!RegisterHotKey(hWnd, ZOOM_HOTKEY, g_ToggleMod, g_ToggleKey & 0xFF)) { MessageBox(hWnd, L"此快捷键已被占用。\n请重新选择放大快捷键。", APPNAME, MB_ICONERROR); showOptions = TRUE; } } if (g_LiveZoomToggleKey) { if (!RegisterHotKey(hWnd, LIVE_HOTKEY, g_LiveZoomToggleMod, g_LiveZoomToggleKey & 0xFF) || !RegisterHotKey(hWnd, LIVE_DRAW_HOTKEY, g_LiveZoomToggleMod ^ MOD_SHIFT, g_LiveZoomToggleKey & 0xFF)) { MessageBox(hWnd, L"此快捷键已被占用。\n请重新选择实时放大快捷键。", APPNAME, MB_ICONERROR); showOptions = TRUE; } } if (g_DrawToggleKey) { if (!RegisterHotKey(hWnd, DRAW_HOTKEY, g_DrawToggleMod, g_DrawToggleKey & 0xFF)) { MessageBox(hWnd, L"此快捷键已被占用。\n请重新选择画图快捷键。", APPNAME, MB_ICONERROR); showOptions = TRUE; } } if (g_BreakToggleKey) { if (!RegisterHotKey(hWnd, BREAK_HOTKEY, g_BreakToggleMod, g_BreakToggleKey & 0xFF)) { MessageBox(hWnd, L"此快捷键已被占用。\n请重新选择倒计时快捷键。", APPNAME, MB_ICONERROR); showOptions = TRUE; } } if (g_DemoTypeToggleKey) { if (!RegisterHotKey(hWnd, DEMOTYPE_HOTKEY, g_DemoTypeToggleMod, g_DemoTypeToggleKey & 0xFF) || !RegisterHotKey(hWnd, DEMOTYPE_RESET_HOTKEY, (g_DemoTypeToggleMod ^ MOD_SHIFT), g_DemoTypeToggleKey & 0xFF)) { MessageBox(hWnd, L"此快捷键已被占用。\n请重新选择自动打字快捷键。", APPNAME, MB_ICONERROR); showOptions = TRUE; } } if (g_SnipToggleKey) { if (!RegisterHotKey(hWnd, SNIP_HOTKEY, g_SnipToggleMod, g_SnipToggleKey & 0xFF) || !RegisterHotKey(hWnd, SNIP_SAVE_HOTKEY, (g_SnipToggleMod ^ MOD_SHIFT), g_SnipToggleKey & 0xFF)) { MessageBox(hWnd, L"此快捷键已被占用。\n请重新选择截图快捷键。", APPNAME, MB_ICONERROR); showOptions = TRUE; } } if (g_RecordToggleKey) { if (!RegisterHotKey(hWnd, RECORD_HOTKEY, g_RecordToggleMod | MOD_NOREPEAT, g_RecordToggleKey & 0xFF) || !RegisterHotKey(hWnd, RECORD_CROP_HOTKEY, (g_RecordToggleMod ^ MOD_SHIFT) | MOD_NOREPEAT, g_RecordToggleKey & 0xFF) || !RegisterHotKey(hWnd, RECORD_WINDOW_HOTKEY, (g_RecordToggleMod ^ MOD_ALT) | MOD_NOREPEAT, g_RecordToggleKey & 0xFF)) { MessageBox(hWnd, L"此快捷键已被占用。\n请重新选择录屏快捷键。", APPNAME, MB_ICONERROR); showOptions = TRUE; } } if (showOptions) { // To open the PowerToys settings in the ZoomIt page. SendMessage(hWnd, WM_COMMAND, IDC_OPTIONS, 0); } break; } case WM_COMMAND: switch(LOWORD( wParam )) { case IDC_SAVE_CROP: case IDC_SAVE: { POINT local_savedCursorPos{}; if( lParam != SHALLOW_ZOOM ) { GetCursorPos(&local_savedCursorPos); } HBITMAP hInterimSaveBitmap; HDC hInterimSaveDc; HBITMAP hSaveBitmap; HDC hSaveDc; int copyX, copyY; int copyWidth, copyHeight; if ( LOWORD( wParam ) == IDC_SAVE_CROP ) { g_RecordCropping = TRUE; SelectRectangle selectRectangle; if( !selectRectangle.Start( hWnd ) ) { g_RecordCropping = FALSE; if( lParam != SHALLOW_ZOOM ) { SetCursorPos(local_savedCursorPos.x, local_savedCursorPos.y); } break; } auto copyRc = selectRectangle.SelectedRect(); selectRectangle.Stop(); g_RecordCropping = FALSE; copyX = copyRc.left; copyY = copyRc.top; copyWidth = copyRc.right - copyRc.left; copyHeight = copyRc.bottom - copyRc.top; } else { copyX = 0; copyY = 0; copyWidth = width; copyHeight = height; } OutputDebug( L"***x: %d, y: %d, width: %d, height: %d\n", copyX, copyY, copyWidth, copyHeight ); RECT oldClipRect{}; GetClipCursor( &oldClipRect ); ClipCursor( NULL ); // Capture the screen before displaying the save dialog hInterimSaveDc = CreateCompatibleDC( hdcScreen ); hInterimSaveBitmap = CreateCompatibleBitmap( hdcScreen, copyWidth, copyHeight ); SelectObject( hInterimSaveDc, hInterimSaveBitmap ); hSaveDc = CreateCompatibleDC( hdcScreen ); #if SCALE_HALFTONE SetStretchBltMode( hInterimSaveDc, HALFTONE ); SetStretchBltMode( hSaveDc, HALFTONE ); #else SetStretchBltMode( hInterimSaveDc, COLORONCOLOR ); SetStretchBltMode( hSaveDc, COLORONCOLOR ); #endif StretchBlt( hInterimSaveDc, 0, 0, copyWidth, copyHeight, hdcScreen, monInfo.rcMonitor.left + copyX, monInfo.rcMonitor.top + copyY, copyWidth, copyHeight, SRCCOPY|CAPTUREBLT ); g_bSaveInProgress = true; memset( &openFileName, 0, sizeof(openFileName )); openFileName.lStructSize = OPENFILENAME_SIZE_VERSION_400; openFileName.hwndOwner = hWnd; openFileName.hInstance = static_cast(g_hInstance); openFileName.nMaxFile = sizeof(filePath)/sizeof(filePath[0]); openFileName.Flags = OFN_LONGNAMES|OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT; openFileName.lpstrTitle = L"保存图像..."; openFileName.lpstrDefExt = NULL; // "*.png"; openFileName.nFilterIndex = 1; openFileName.lpstrFilter = L"压缩尺寸 PNG\0*.png\0" //"Zoomed BMP\0*.bmp\0" "原始尺寸 PNG\0*.png\0\0"; //"Actual size BMP\0*.bmp\0\0"; openFileName.lpstrFile = filePath; if( GetSaveFileName( &openFileName ) ) { TCHAR targetFilePath[MAX_PATH]; _tcscpy( targetFilePath, filePath ); if( !_tcsrchr( targetFilePath, '.' ) ) { _tcscat( targetFilePath, L".png" ); } // Save image at screen size if( openFileName.nFilterIndex == 1 ) { SavePng( targetFilePath, hInterimSaveBitmap ); } // Save image scaled down to actual size else { int saveWidth = static_cast( copyWidth / zoomLevel ); int saveHeight = static_cast( copyHeight / zoomLevel ); hSaveBitmap = CreateCompatibleBitmap( hdcScreen, saveWidth, saveHeight ); SelectObject( hSaveDc, hSaveBitmap ); StretchBlt( hSaveDc, 0, 0, saveWidth, saveHeight, hInterimSaveDc, 0, 0, copyWidth, copyHeight, SRCCOPY | CAPTUREBLT ); SavePng( targetFilePath, hSaveBitmap ); } } g_bSaveInProgress = false; DeleteDC( hInterimSaveDc ); DeleteDC( hSaveDc ); if( lParam != SHALLOW_ZOOM ) { SetCursorPos(local_savedCursorPos.x, local_savedCursorPos.y); } ClipCursor( &oldClipRect ); break; } case IDC_COPY_CROP: case IDC_COPY: { HBITMAP hSaveBitmap; HDC hSaveDc; int copyX, copyY; int copyWidth, copyHeight; if( LOWORD( wParam ) == IDC_COPY_CROP ) { g_RecordCropping = TRUE; POINT local_savedCursorPos{}; if( lParam != SHALLOW_ZOOM ) { GetCursorPos(&local_savedCursorPos); } SelectRectangle selectRectangle; if( !selectRectangle.Start( hWnd ) ) { g_RecordCropping = FALSE; break; } auto copyRc = selectRectangle.SelectedRect(); selectRectangle.Stop(); if( lParam != SHALLOW_ZOOM ) { SetCursorPos(local_savedCursorPos.x, local_savedCursorPos.y); } g_RecordCropping = FALSE; copyX = copyRc.left; copyY = copyRc.top; copyWidth = copyRc.right - copyRc.left; copyHeight = copyRc.bottom - copyRc.top; } else { copyX = 0; copyY = 0; copyWidth = width; copyHeight = height; } OutputDebug( L"***x: %d, y: %d, width: %d, height: %d\n", copyX, copyY, copyWidth, copyHeight ); hSaveBitmap = CreateCompatibleBitmap( hdcScreen, copyWidth, copyHeight ); hSaveDc = CreateCompatibleDC( hdcScreen ); SelectObject( hSaveDc, hSaveBitmap ); #if SCALE_HALFTONE SetStretchBltMode( hSaveDc, HALFTONE ); #else SetStretchBltMode( hSaveDc, COLORONCOLOR ); #endif StretchBlt( hSaveDc, 0, 0, copyWidth, copyHeight, hdcScreen, monInfo.rcMonitor.left + copyX, monInfo.rcMonitor.top + copyY, copyWidth, copyHeight, SRCCOPY|CAPTUREBLT ); if( OpenClipboard( hWnd )) { EmptyClipboard(); SetClipboardData( CF_BITMAP, hSaveBitmap ); CloseClipboard(); } DeleteDC( hSaveDc ); } break; case IDC_DRAW: PostMessage( hWnd, WM_HOTKEY, DRAW_HOTKEY, 1 ); break; case IDC_ZOOM: PostMessage( hWnd, WM_HOTKEY, ZOOM_HOTKEY, 1 ); break; case IDC_RECORD: PostMessage( hWnd, WM_HOTKEY, RECORD_HOTKEY, 1 ); break; case IDC_OPTIONS: // Don't show win32 forms options if started by PowerToys. // Show the PowerToys Settings application instead. if( g_StartedByPowerToys ) { #ifdef __ZOOMIT_POWERTOYS__ OpenPowerToysSettingsApp(); #endif // __ZOOMIT_POWERTOYS__ } else { DialogBox( g_hInstance, L"OPTIONS", hWnd, OptionsProc ); } break; case IDC_BREAK: { // Manage handles, clean visual transitions, and Options delta if( g_TimerActive ) { if( activeBreakShowBackgroundFile != g_BreakShowBackgroundFile || activeBreakShowDesktop != g_BreakShowDesktop ) { if( g_BreakShowBackgroundFile && !g_BreakShowDesktop ) { SendMessage( hWnd, WM_HOTKEY, ZOOM_HOTKEY, SHALLOW_DESTROY ); } else { SendMessage( hWnd, WM_HOTKEY, ZOOM_HOTKEY, 0 ); } } else { SendMessage( hWnd, WM_HOTKEY, ZOOM_HOTKEY, SHALLOW_DESTROY ); g_TimerActive = TRUE; } } hdcScreen = CreateDC( L"DISPLAY", static_cast(NULL), static_cast(NULL), static_cast(NULL) ); // toggle second monitor // FIX: we should save whether or not we've switched to a second monitor // rather than just assume that the setting hasn't changed since the break timer // became active if( g_BreakOnSecondary ) { EnableDisableSecondaryDisplay( hWnd, TRUE, &secondaryDevMode ); } // Determine what monitor we're on GetCursorPos( &cursorPos ); UpdateMonitorInfo( cursorPos, &monInfo ); width = monInfo.rcMonitor.right - monInfo.rcMonitor.left; height = monInfo.rcMonitor.bottom - monInfo.rcMonitor.top; // Trigger desktop recapture as necessary when switching monitors if( g_TimerActive && g_BreakShowDesktop && lastMonInfo.rcMonitor != monInfo.rcMonitor ) { lastMonInfo = monInfo; SendMessage( hWnd, WM_HOTKEY, ZOOM_HOTKEY, 0 ); PostMessage( hWnd, WM_COMMAND, IDC_BREAK, 0 ); break; } lastMonInfo = monInfo; // If the background is a file that hasn't been collected, grab it now if( g_BreakShowBackgroundFile && !g_BreakShowDesktop && ( !g_TimerActive || wcscmp( activeBreakBackgroundFile, g_BreakBackgroundFile ) ) ) { _tcscpy( activeBreakBackgroundFile, g_BreakBackgroundFile ); DeleteObject( g_hBackgroundBmp ); DeleteDC( g_hDcBackgroundFile ); g_hBackgroundBmp = NULL; g_hBackgroundBmp = LoadImageFile( g_BreakBackgroundFile ); if( g_hBackgroundBmp == NULL ) { // Clean up hanging handles SendMessage( hWnd, WM_HOTKEY, ZOOM_HOTKEY, 0 ); ErrorDialog( hWnd, L"无法加载背景位图", GetLastError() ); break; } g_hDcBackgroundFile = CreateCompatibleDC( hdcScreen ); SelectObject( g_hDcBackgroundFile, g_hBackgroundBmp ); } // If the background is a desktop that hasn't been collected, grab it now else if( g_BreakShowBackgroundFile && g_BreakShowDesktop && !g_TimerActive ) { g_hBackgroundBmp = CreateFadedDesktopBackground( GetDC(NULL), & monInfo.rcMonitor, NULL ); g_hDcBackgroundFile = CreateCompatibleDC( hdcScreen ); SelectObject( g_hDcBackgroundFile, g_hBackgroundBmp ); } // Track Options.Break delta activeBreakShowBackgroundFile = g_BreakShowBackgroundFile; activeBreakShowDesktop = g_BreakShowDesktop; g_TimerActive = TRUE; #ifdef __ZOOMIT_POWERTOYS__ if( g_StartedByPowerToys ) { Trace::ZoomItActivateBreak(); } #endif // __ZOOMIT_POWERTOYS__ breakTimeout = g_BreakTimeout * 60 + 1; // Create font g_LogFont.lfHeight = height / 5; hTimerFont = CreateFontIndirect( &g_LogFont ); g_LogFont.lfHeight = height / 8; hNegativeTimerFont = CreateFontIndirect( &g_LogFont ); // Create backing bitmap hdcScreenCompat = CreateCompatibleDC(hdcScreen); bmp.bmBitsPixel = static_cast(GetDeviceCaps(hdcScreen, BITSPIXEL)); bmp.bmPlanes = static_cast(GetDeviceCaps(hdcScreen, PLANES)); bmp.bmWidth = width; bmp.bmHeight = height; bmp.bmWidthBytes = ((bmp.bmWidth + 15) &~15)/8; hbmpCompat = CreateBitmap(bmp.bmWidth, bmp.bmHeight, bmp.bmPlanes, bmp.bmBitsPixel, static_cast(NULL)); SelectObject(hdcScreenCompat, hbmpCompat); SetTextColor( hdcScreenCompat, g_BreakPenColor ); SetBkMode( hdcScreenCompat, TRANSPARENT ); SelectObject( hdcScreenCompat, hTimerFont ); EnableDisableOpacity( hWnd, TRUE ); EnableDisableScreenSaver( FALSE ); SendMessage( hWnd, WM_TIMER, 0, 0 ); SetTimer( hWnd, 0, 1000, NULL ); BringWindowToTop( hWnd ); SetForegroundWindow( hWnd ); SetActiveWindow( hWnd ); SetWindowPos( hWnd, HWND_NOTOPMOST, monInfo.rcMonitor.left, monInfo.rcMonitor.top, width, height, SWP_SHOWWINDOW ); } break; case IDCANCEL: memset( &tNotifyIconData, 0, sizeof(tNotifyIconData)); tNotifyIconData.cbSize = sizeof(NOTIFYICONDATA); tNotifyIconData.hWnd = hWnd; tNotifyIconData.uID = 1; Shell_NotifyIcon(NIM_DELETE, &tNotifyIconData); reg.WriteRegSettings( RegSettings ); if( hWndOptions ) { DestroyWindow( hWndOptions ); } DestroyWindow( hWnd ); break; } break; case WM_TIMER: switch( wParam ) { case 0: // // Break timer // breakTimeout -= 1; InvalidateRect( hWnd, NULL, FALSE ); if( breakTimeout == 0 && g_BreakPlaySoundFile ) { PlaySound( g_BreakSoundFile, NULL, SND_FILENAME|SND_ASYNC ); } break; case 2: case 1: // // Telescoping zoom timer // if( zoomTelescopeStep ) { zoomLevel *= zoomTelescopeStep; if( (zoomTelescopeStep > 1 && zoomLevel >= zoomTelescopeTarget ) || (zoomTelescopeStep < 1 && zoomLevel <= zoomTelescopeTarget )) { zoomLevel = zoomTelescopeTarget; KillTimer( hWnd, wParam ); OutputDebug( L"SETCURSOR mon_left: %x mon_top: %x x: %d y: %d\n", monInfo.rcMonitor.left, monInfo.rcMonitor.top, cursorPos.x, cursorPos.y ); SetCursorPos( monInfo.rcMonitor.left + cursorPos.x, monInfo.rcMonitor.top + cursorPos.y ); } } else { // Case where we didn't zoom at all KillTimer( hWnd, wParam ); } if( wParam == 2 && zoomLevel == 1 ) { g_Zoomed = FALSE; if( g_ZoomOnLiveZoom ) { GetCursorPos( &cursorPos ); cursorPos = ScalePointInRects( cursorPos, monInfo.rcMonitor, g_LiveZoomSourceRect ); SetCursorPos( cursorPos.x, cursorPos.y ); SendMessage(hWnd, WM_HOTKEY, LIVE_HOTKEY, 0); } else if( lParam != SHALLOW_ZOOM ) { // Figure out where final unzoomed cursor should be if (g_Drawing) { cursorPos = prevPt; } OutputDebug(L"FINAL MOUSE: x: %d y: %d\n", cursorPos.x, cursorPos.y ); GetZoomedTopLeftCoordinates(zoomLevel, &cursorPos, &x, width, &y, height); cursorPos.x = monInfo.rcMonitor.left + x + static_cast((cursorPos.x - x) * zoomLevel); cursorPos.y = monInfo.rcMonitor.top + y + static_cast((cursorPos.y - y) * zoomLevel); SetCursorPos(cursorPos.x, cursorPos.y); } if( hTargetWindow ) { SetWindowPos( hTargetWindow, HWND_BOTTOM, rcTargetWindow.left, rcTargetWindow.top, rcTargetWindow.right - rcTargetWindow.left, rcTargetWindow.bottom - rcTargetWindow.top, 0 ); hTargetWindow = NULL; } DeleteDrawUndoList( &drawUndoList ); // Restore live zoom if we came from that mode if( g_ZoomOnLiveZoom ) { SendMessage( g_hWndLiveZoom, WM_USER_SET_ZOOM, static_cast(g_LiveZoomLevel), reinterpret_cast(&g_LiveZoomSourceRect) ); g_ZoomOnLiveZoom = FALSE; forcePenResize = TRUE; } SetForegroundWindow( g_ActiveWindow ); ClipCursor( NULL ); g_HaveDrawn = FALSE; g_TypeMode = TypeModeOff; g_HaveTyped = FALSE; g_Drawing = FALSE; EnableDisableStickyKeys( TRUE ); DeleteObject( hTypingFont ); DeleteDC( hdcScreen ); DeleteDC( hdcScreenCompat ); DeleteDC( hdcScreenCursorCompat ); DeleteDC( hdcScreenSaveCompat ); DeleteObject( hbmpCompat ); DeleteObject( hbmpCursorCompat ); DeleteObject( hbmpDrawingCompat ); DeleteObject( hDrawingPen ); SetFocus( g_ActiveWindow ); ShowWindow( hWnd, SW_HIDE ); } InvalidateRect( hWnd, NULL, FALSE ); break; case 3: POINT mousePos; GetCursorPos(&mousePos); if (mousePos.x != cursorPos.x || mousePos.y != cursorPos.y) { MONITORINFO monitorInfo = { sizeof(MONITORINFO) }; UpdateMonitorInfo(mousePos, &monitorInfo); mousePos.x -= monitorInfo.rcMonitor.left; mousePos.y -= monitorInfo.rcMonitor.top; OutputDebug(L"RETRACKING MOUSE: x: %d y: %d\n", mousePos.x, mousePos.y); SendMessage(hWnd, WM_MOUSEMOVE, 0, MAKELPARAM(mousePos.x, mousePos.y)); } break; } break; case WM_PAINT: hDc = BeginPaint(hWnd, &ps); if( ( ( g_RecordCropping == FALSE ) || ( zoomLevel == 1 ) ) && g_Zoomed ) { OutputDebug( L"PAINT x: %d y: %d width: %d height: %d zoomLevel: %g\n", cursorPos.x, cursorPos.y, width, height, zoomLevel ); GetZoomedTopLeftCoordinates( zoomLevel, &cursorPos, &x, width, &y, height ); #if SCALE_GDIPLUS if ( zoomLevel >= zoomTelescopeTarget ) { // do a high-quality render extern void ScaleImage( HDC hdcDst, float xDst, float yDst, float wDst, float hDst, HBITMAP bmSrc, float xSrc, float ySrc, float wSrc, float hSrc ); ScaleImage( ps.hdc, 0, 0, (float)bmp.bmWidth, (float)bmp.bmHeight, hbmpCompat, (float)x, (float)y, width/zoomLevel, height/zoomLevel ); } else { // do a fast, less accurate render SetStretchBltMode( hDc, HALFTONE ); StretchBlt( ps.hdc, 0, 0, bmp.bmWidth, bmp.bmHeight, hdcScreenCompat, x, y, (int) (width/zoomLevel), (int) (height/zoomLevel), SRCCOPY); } #else #if SCALE_HALFTONE SetStretchBltMode( hDc, zoomLevel == zoomTelescopeTarget ? HALFTONE : COLORONCOLOR ); #else SetStretchBltMode( hDc, COLORONCOLOR ); #endif StretchBlt( ps.hdc, 0, 0, bmp.bmWidth, bmp.bmHeight, hdcScreenCompat, x, y, static_cast(width/zoomLevel), static_cast(height/zoomLevel), SRCCOPY|CAPTUREBLT ); #endif } else if( g_TimerActive ) { // Fill bitmap with white rc.top = rc.left = 0; rc.bottom = height; rc.right = width; FillRect( hdcScreenCompat, &rc, GetSysColorBrush( COLOR_WINDOW )); // If there's a background bitmap, draw it in the center if( g_hBackgroundBmp ) { BITMAP local_bmp; GetObject(g_hBackgroundBmp, sizeof(local_bmp), &local_bmp); SetStretchBltMode( hdcScreenCompat, HALFTONE ); if( g_BreakBackgroundStretch ) { StretchBlt( hdcScreenCompat, 0, 0, width, height, g_hDcBackgroundFile, 0, 0, local_bmp.bmWidth, local_bmp.bmHeight, SRCCOPY|CAPTUREBLT ); } else { BitBlt( hdcScreenCompat, width/2 - local_bmp.bmWidth/2, height/2 - local_bmp.bmHeight/2, local_bmp.bmWidth, local_bmp.bmHeight, g_hDcBackgroundFile, 0, 0, SRCCOPY|CAPTUREBLT ); } } // Draw time if( breakTimeout > 0 ) { _stprintf( timerText, L"% 2d:%02d", breakTimeout/60, breakTimeout % 60 ); } else { _tcscpy( timerText, L"0:00" ); } rc.left = rc.top = 0; DrawText( hdcScreenCompat, timerText, -1, &rc, DT_NOCLIP|DT_LEFT|DT_NOPREFIX|DT_CALCRECT ); rc1.left = rc1.right = rc1.bottom = rc1.top = 0; if( g_ShowExpiredTime && breakTimeout < 0 ) { _stprintf( negativeTimerText, L"(-% 2d:%02d)", -breakTimeout/60, -breakTimeout % 60 ); HFONT prevFont = static_cast(SelectObject( hdcScreenCompat, hNegativeTimerFont )); DrawText( hdcScreenCompat, negativeTimerText, -1, &rc1, DT_NOCLIP|DT_LEFT|DT_NOPREFIX|DT_CALCRECT ); SelectObject( hdcScreenCompat, prevFont ); } // Position time vertically switch( g_BreakTimerPosition ) { case 0: case 1: case 2: rc.top = 50; break; case 3: case 4: case 5: rc.top = (height - (rc.bottom - rc.top))/2; break; case 6: case 7: case 8: rc.top = height - rc.bottom - 50 - rc1.bottom; break; } // Position time horizontally switch( g_BreakTimerPosition ) { case 0: case 3: case 6: rc.left = 50; break; case 1: case 4: case 7: rc.left = (width - (rc.right - rc.left))/2; break; case 2: case 5: case 8: rc.left = width - rc.right - 50; break; } rc.bottom += rc.top; rc.right += rc.left; DrawText( hdcScreenCompat, timerText, -1, &rc, DT_NOCLIP|DT_LEFT|DT_NOPREFIX ); if( g_ShowExpiredTime && breakTimeout < 0 ) { rc1.top = rc.bottom + 10; rc1.left = rc.left + ((rc.right - rc.left)-(rc1.right-rc1.left))/2; HFONT prevFont = static_cast(SelectObject( hdcScreenCompat, hNegativeTimerFont )); DrawText( hdcScreenCompat, negativeTimerText, -1, &rc1, DT_NOCLIP|DT_LEFT|DT_NOPREFIX ); SelectObject( hdcScreenCompat, prevFont ); } // Copy to screen BitBlt( ps.hdc, 0, 0, width, height, hdcScreenCompat, 0, 0, SRCCOPY|CAPTUREBLT ); } EndPaint(hWnd, &ps); return TRUE; case WM_DESTROY: PostQuitMessage( 0 ); break; default: if( message == wmTaskbarCreated ) { if( g_ShowTrayIcon ) { EnableDisableTrayIcon( hWnd, TRUE ); } return TRUE; } return DefWindowProc(hWnd, message, wParam, lParam ); } return 0; } //---------------------------------------------------------------------------- // // LiveZoomWndProc // //---------------------------------------------------------------------------- LRESULT CALLBACK LiveZoomWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { RECT rc; POINT cursorPos; static int width, height; static MONITORINFO monInfo; HDC hdcScreen; #if 0 int delta; BOOLEAN zoomIn; #endif static POINT lastCursorPos; POINT adjustedCursorPos, zoomCenterPos; int moveWidth, moveHeight; int sourceRectHeight, sourceRectWidth; DWORD curTickCount; RECT sourceRect{}; static RECT lastSourceRect; static float zoomLevel; static float zoomTelescopeStep; static float zoomTelescopeTarget; static DWORD prevZoomStepTickCount = 0; static BOOL dwmEnabled = FALSE; static BOOLEAN startedInPresentationMode = FALSE; MAGTRANSFORM matrix; switch (message) { case WM_CREATE: // Initialize pMagInitialize(); if (pDwmIsCompositionEnabled) pDwmIsCompositionEnabled(&dwmEnabled); // Create the zoom window if( !g_fullScreenWorkaround ) { g_hWndLiveZoomMag = CreateWindowEx( 0, WC_MAGNIFIER, TEXT("MagnifierWindow"), WS_CHILD | MS_SHOWMAGNIFIEDCURSOR | WS_VISIBLE, 0, 0, 0, 0, hWnd, NULL, g_hInstance, NULL ); } ShowWindow( hWnd, SW_SHOW ); InvalidateRect( g_hWndLiveZoomMag, NULL, TRUE ); if( !g_fullScreenWorkaround ) SetForegroundWindow(static_cast(reinterpret_cast(lParam)->lpCreateParams)); // If we're not on Win7+, then set a timer to go off two hours from // now if( g_OsVersion < WIN7_VERSION ) { startedInPresentationMode = IsPresentationMode(); // if we're not in presentation mode, kill ourselves after a timeout if( !startedInPresentationMode ) { SetTimer( hWnd, 1, LIVEZOOM_WINDOW_TIMEOUT, NULL ); } } break; case WM_SHOWWINDOW: if( wParam == TRUE ) { // Determine what monitor we're on lastCursorPos.x = -1; hdcScreen = GetDC( NULL ); GetCursorPos( &cursorPos ); UpdateMonitorInfo( cursorPos, &monInfo ); width = monInfo.rcMonitor.right - monInfo.rcMonitor.left; height = monInfo.rcMonitor.bottom - monInfo.rcMonitor.top; lastSourceRect.left = lastSourceRect.right = 0; lastSourceRect.right = width; lastSourceRect.bottom = height; // Set window size if( !g_fullScreenWorkaround ) { SetWindowPos( hWnd, NULL, monInfo.rcMonitor.left, monInfo.rcMonitor.top, monInfo.rcMonitor.right - monInfo.rcMonitor.left, monInfo.rcMonitor.bottom - monInfo.rcMonitor.top, SWP_NOACTIVATE | SWP_NOZORDER ); UpdateWindow(hWnd); } // Are we coming back from a static zoom that // was started while we were live zoomed? if( g_ZoomOnLiveZoom ) { // Force a zoom to 2x without telescope prevZoomStepTickCount = 0; zoomLevel = static_cast(1.9); zoomTelescopeTarget = 2.0; zoomTelescopeStep = 2.0; } else { zoomTelescopeStep = ZOOM_LEVEL_STEP_IN; zoomTelescopeTarget = g_ZoomLevels[g_SliderZoomLevel]; prevZoomStepTickCount = 0; if( dwmEnabled ) { zoomLevel = static_cast(1); } else { zoomLevel = static_cast(1.9); } } RegisterHotKey( hWnd, 0, MOD_CONTROL, VK_UP ); RegisterHotKey( hWnd, 1, MOD_CONTROL, VK_DOWN ); // Hide hardware cursor if( !g_fullScreenWorkaround ) if( pMagShowSystemCursor ) pMagShowSystemCursor( FALSE ); if( g_RecordToggle ) g_RecordingSession->EnableCursorCapture( false ); GetCursorPos( &lastCursorPos ); SetCursorPos( lastCursorPos.x, lastCursorPos.y ); SendMessage( hWnd, WM_TIMER, 0, 0); SetTimer( hWnd, 0, ZOOM_LEVEL_STEP_TIME, NULL ); } else { KillTimer( hWnd, 0 ); if( g_RecordToggle ) g_RecordingSession->EnableCursorCapture(); if( !g_fullScreenWorkaround ) if( pMagShowSystemCursor ) pMagShowSystemCursor( TRUE ); // Reset the timer to expire two hours from now if( g_OsVersion < WIN7_VERSION && !IsPresentationMode()) { KillTimer( hWnd, 1 ); SetTimer( hWnd, 1, LIVEZOOM_WINDOW_TIMEOUT, NULL ); } else { DestroyWindow( hWnd ); } UnregisterHotKey( hWnd, 0 ); UnregisterHotKey( hWnd, 1 ); } break; case WM_TIMER: switch( wParam ) { case 0: { // if we're cropping, do not move if( g_RecordCropping == TRUE ) { // Still redraw to keep the contents live InvalidateRect( g_hWndLiveZoomMag, nullptr, TRUE ); break; } GetCursorPos(&cursorPos); // Reclaim topmost status, to prevent unmagnified menus from remaining in view. memset(&matrix, 0, sizeof(matrix)); if( !g_fullScreenWorkaround ) { pSetLayeredWindowAttributes( hWnd, 0, 255, LWA_ALPHA ); SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); OutputDebug(L"LIVEZOOM RECLAIM\n"); } sourceRectWidth = lastSourceRect.right - lastSourceRect.left; sourceRectHeight = lastSourceRect.bottom - lastSourceRect.top; moveWidth = sourceRectWidth/LIVEZOOM_MOVE_REGIONS; moveHeight = sourceRectHeight/LIVEZOOM_MOVE_REGIONS; curTickCount = GetTickCount(); if( zoomLevel != zoomTelescopeTarget && (prevZoomStepTickCount == 0 || (curTickCount - prevZoomStepTickCount > ZOOM_LEVEL_STEP_TIME)) ) { prevZoomStepTickCount = curTickCount; if( (zoomTelescopeStep > 1 && zoomLevel*zoomTelescopeStep >= zoomTelescopeTarget ) || (zoomTelescopeStep < 1 && zoomLevel*zoomTelescopeStep <= zoomTelescopeTarget )) { zoomLevel = zoomTelescopeTarget; } else { zoomLevel *= zoomTelescopeStep; } // Time to exit zoom mode? if( zoomTelescopeTarget == 1 && zoomLevel == 1 ) { #if WINDOWS_CURSOR_RECORDING_WORKAROUND if( g_RecordToggle ) g_LiveZoomLevelOne = true; else #endif ShowWindow( hWnd, SW_HIDE ); } else { matrix.v[0][0] = zoomLevel; matrix.v[0][2] = (static_cast(-lastSourceRect.left) * zoomLevel); matrix.v[1][1] = zoomLevel; matrix.v[1][2] = (static_cast(-lastSourceRect.top) * zoomLevel ); matrix.v[2][2] = 1.0f; } // // Pre-adjust for monitor boundary // adjustedCursorPos.x = cursorPos.x - monInfo.rcMonitor.left; adjustedCursorPos.y = cursorPos.y - monInfo.rcMonitor.top; GetZoomedTopLeftCoordinates( zoomLevel, &adjustedCursorPos, reinterpret_cast(&zoomCenterPos.x), width, reinterpret_cast(&zoomCenterPos.y), height ); // // Add back monitor boundary // zoomCenterPos.x += monInfo.rcMonitor.left + static_cast(width/zoomLevel/2); zoomCenterPos.y += monInfo.rcMonitor.top + static_cast(height/zoomLevel/2); } else { int xOffset = cursorPos.x - lastSourceRect.left; int yOffset = cursorPos.y - lastSourceRect.top; zoomCenterPos.x = 0; zoomCenterPos.y = 0; if( xOffset < moveWidth ) zoomCenterPos.x = lastSourceRect.left + sourceRectWidth/2 - (moveWidth - xOffset); else if( xOffset > moveWidth * (LIVEZOOM_MOVE_REGIONS-1) ) zoomCenterPos.x = lastSourceRect.left + sourceRectWidth/2 + (xOffset - moveWidth*(LIVEZOOM_MOVE_REGIONS-1)); if( yOffset < moveHeight ) zoomCenterPos.y = lastSourceRect.top + sourceRectHeight/2 - (moveHeight - yOffset); else if( yOffset > moveHeight * (LIVEZOOM_MOVE_REGIONS-1) ) zoomCenterPos.y = lastSourceRect.top + sourceRectHeight/2 + (yOffset - moveHeight*(LIVEZOOM_MOVE_REGIONS-1)); } if( matrix.v[0][0] || zoomCenterPos.x || zoomCenterPos.y ) { if( zoomCenterPos.y == 0 ) zoomCenterPos.y = lastSourceRect.top + sourceRectHeight/2; if( zoomCenterPos.x == 0 ) zoomCenterPos.x = lastSourceRect.left + sourceRectWidth/2; int zoomWidth = static_cast(width / zoomLevel); int zoomHeight = static_cast(height/ zoomLevel); sourceRect.left = zoomCenterPos.x - zoomWidth / 2; sourceRect.top = zoomCenterPos.y - zoomHeight / 2; // Don't scroll outside desktop area. if (sourceRect.left < monInfo.rcMonitor.left) sourceRect.left = monInfo.rcMonitor.left; else if (sourceRect.left > monInfo.rcMonitor.right - zoomWidth ) sourceRect.left = monInfo.rcMonitor.right - zoomWidth; sourceRect.right = sourceRect.left + zoomWidth; if (sourceRect.top < monInfo.rcMonitor.top) sourceRect.top = monInfo.rcMonitor.top; else if (sourceRect.top > monInfo.rcMonitor.bottom - zoomHeight) sourceRect.top = monInfo.rcMonitor.bottom - zoomHeight; sourceRect.bottom = sourceRect.top + zoomHeight; if( g_ZoomOnLiveZoom ) { matrix.v[0][0] = static_cast(1.0); matrix.v[0][2] = (static_cast(-monInfo.rcMonitor.left)); matrix.v[1][1] = static_cast(1.0); matrix.v[1][2] = (static_cast(-monInfo.rcMonitor.top)); matrix.v[2][2] = 1.0f; } else if( lastSourceRect.left != sourceRect.left || lastSourceRect.top != sourceRect.top ) { matrix.v[0][0] = zoomLevel; matrix.v[0][2] = (static_cast(-sourceRect.left) * zoomLevel); matrix.v[1][1] = zoomLevel; matrix.v[1][2] = (static_cast(-sourceRect.top) * zoomLevel); matrix.v[2][2] = 1.0f; } lastSourceRect = sourceRect; } lastCursorPos = cursorPos; // Update source and zoom if necessary if( matrix.v[0][0] ) { OutputDebug(L"LIVEZOOM update\n"); if( g_fullScreenWorkaround ) { pMagSetFullscreenTransform(zoomLevel, sourceRect.left, sourceRect.top); pMagSetInputTransform(TRUE, &sourceRect, &monInfo.rcMonitor); } else { pMagSetWindowTransform(g_hWndLiveZoomMag, &matrix); } } if( !g_fullScreenWorkaround ) { // Force redraw to refresh screen contents InvalidateRect(g_hWndLiveZoomMag, NULL, TRUE); } // are we done zooming? if( zoomLevel == 1 ) { #if WINDOWS_CURSOR_RECORDING_WORKAROUND if( g_RecordToggle ) { g_LiveZoomLevelOne = true; } else { #endif if( g_OsVersion < WIN7_VERSION ) { ShowWindow( hWnd, SW_HIDE ); } else { DestroyWindow( hWnd ); } } #if WINDOWS_CURSOR_RECORDING_WORKAROUND } #endif } break; case 1: { if( !IsWindowVisible( hWnd )) { // This is the cached window timeout. If not in presentation mode, // time to exit if( !IsPresentationMode()) { DestroyWindow( hWnd ); } } } break; } break; case WM_SETTINGCHANGE: if( g_OsVersion < WIN7_VERSION ) { if( startedInPresentationMode && !IsPresentationMode()) { // Existing presentation mode DestroyWindow( hWnd ); } else if( !startedInPresentationMode && IsPresentationMode()) { // Kill the timer if one was configured, because now // we're going to go away when they exit presentation mode KillTimer( hWnd, 1 ); } } break; case WM_HOTKEY: { float newZoomLevel = zoomLevel; switch( wParam ) { case 0: // zoom in if( newZoomLevel < ZOOM_LEVEL_MAX ) newZoomLevel *= 2; zoomTelescopeStep = ZOOM_LEVEL_STEP_IN; break; case 1: if( newZoomLevel > 2 ) newZoomLevel /= 2; else { newZoomLevel *= .75; if( newZoomLevel < ZOOM_LEVEL_MIN ) newZoomLevel = ZOOM_LEVEL_MIN; } zoomTelescopeStep = ZOOM_LEVEL_STEP_OUT; break; } zoomTelescopeTarget = newZoomLevel; if( !dwmEnabled ) { zoomLevel = newZoomLevel; } } break; // NOTE: keyboard and mouse input actually don't get sent to us at all when in live zoom mode case WM_KEYDOWN: switch( wParam ) { case VK_ESCAPE: zoomTelescopeStep = ZOOM_LEVEL_STEP_OUT; zoomTelescopeTarget = 1.0; if( !dwmEnabled ) { zoomLevel = static_cast(1.1); } break; case VK_UP: SendMessage( hWnd, WM_MOUSEWHEEL, MAKEWPARAM( GetAsyncKeyState( VK_LCONTROL ) != 0 ? MK_CONTROL: 0, WHEEL_DELTA), 0 ); return TRUE; case VK_DOWN: SendMessage( hWnd, WM_MOUSEWHEEL, MAKEWPARAM( GetAsyncKeyState( VK_LCONTROL ) != 0 ? MK_CONTROL: 0, -WHEEL_DELTA), 0 ); return TRUE; } break; case WM_DESTROY: g_hWndLiveZoom = NULL; break; case WM_SIZE: GetClientRect(hWnd, &rc); SetWindowPos(g_hWndLiveZoomMag, NULL, rc.left, rc.top, rc.right, rc.bottom, 0 ); break; case WM_USER_GET_ZOOM_LEVEL: return reinterpret_cast(&zoomLevel); case WM_USER_GET_SOURCE_RECT: return reinterpret_cast(&lastSourceRect); case WM_USER_MAGNIFY_CURSOR: { auto style = GetWindowLong( g_hWndLiveZoomMag, GWL_STYLE ); if( wParam == TRUE ) { style |= MS_SHOWMAGNIFIEDCURSOR; } else { style &= ~MS_SHOWMAGNIFIEDCURSOR; } SetWindowLong( g_hWndLiveZoomMag, GWL_STYLE, style ); InvalidateRect( g_hWndLiveZoomMag, nullptr, TRUE ); RedrawWindow( hWnd, nullptr, nullptr, RDW_ALLCHILDREN | RDW_UPDATENOW ); } break; case WM_USER_SET_ZOOM: { if( g_RecordToggle ) { g_SelectRectangle.UpdateOwner( hWnd ); } if( lParam != NULL ) { lastSourceRect = *reinterpret_cast(lParam); } #if WINDOWS_CURSOR_RECORDING_WORKAROUND if( g_LiveZoomLevelOne ) { g_LiveZoomLevelOne = FALSE; zoomTelescopeTarget = static_cast(wParam); zoomTelescopeStep = ZOOM_LEVEL_STEP_IN; prevZoomStepTickCount = 0; zoomLevel = 1.0; break; } #endif zoomLevel = static_cast(wParam); zoomTelescopeTarget = zoomLevel; matrix.v[0][0] = zoomLevel; matrix.v[0][2] = (static_cast(-lastSourceRect.left) * static_cast(wParam)); matrix.v[1][1] = zoomLevel; matrix.v[1][2] = (static_cast(-lastSourceRect.top) * static_cast(wParam)); matrix.v[2][2] = 1.0f; if( g_fullScreenWorkaround ) { pMagSetFullscreenTransform(zoomLevel, lastSourceRect.left, lastSourceRect.top); pMagSetInputTransform(TRUE, &lastSourceRect, &monInfo.rcMonitor); } else { pMagSetWindowTransform(g_hWndLiveZoomMag, &matrix); } } break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } //---------------------------------------------------------------------------- // // Wrapper functions for explicit linking to d3d11.dll // //---------------------------------------------------------------------------- HRESULT __stdcall WrapCreateDirect3D11DeviceFromDXGIDevice( IDXGIDevice *dxgiDevice, IInspectable **graphicsDevice) { if( pCreateDirect3D11DeviceFromDXGIDevice == nullptr ) return E_NOINTERFACE; return pCreateDirect3D11DeviceFromDXGIDevice( dxgiDevice, graphicsDevice ); } HRESULT __stdcall WrapCreateDirect3D11SurfaceFromDXGISurface( IDXGISurface *dxgiSurface, IInspectable **graphicsSurface) { if( pCreateDirect3D11SurfaceFromDXGISurface == nullptr ) return E_NOINTERFACE; return pCreateDirect3D11SurfaceFromDXGISurface( dxgiSurface, graphicsSurface ); } HRESULT __stdcall WrapD3D11CreateDevice( IDXGIAdapter *pAdapter, D3D_DRIVER_TYPE DriverType, HMODULE Software, UINT Flags, const D3D_FEATURE_LEVEL *pFeatureLevels, UINT FeatureLevels, UINT SDKVersion, ID3D11Device **ppDevice, D3D_FEATURE_LEVEL *pFeatureLevel, ID3D11DeviceContext **ppImmediateContext) { if( pD3D11CreateDevice == nullptr ) return E_NOINTERFACE; return pD3D11CreateDevice( pAdapter, DriverType, Software, Flags, pFeatureLevels, FeatureLevels, SDKVersion, ppDevice, pFeatureLevel, ppImmediateContext ); } //---------------------------------------------------------------------------- // // InitInstance // //---------------------------------------------------------------------------- HWND InitInstance( HINSTANCE hInstance, int nCmdShow ) { WNDCLASS wcZoomIt; HWND hWndMain; g_hInstance = hInstance; // If magnification, set default hotkey for live zoom if( pMagInitialize ) { // register live zoom host window wcZoomIt.style = CS_HREDRAW | CS_VREDRAW; wcZoomIt.lpfnWndProc = LiveZoomWndProc; wcZoomIt.cbClsExtra = 0; wcZoomIt.cbWndExtra = 0; wcZoomIt.hInstance = hInstance; wcZoomIt.hIcon = 0; wcZoomIt.hCursor = LoadCursor(NULL, IDC_ARROW); wcZoomIt.hbrBackground = NULL; wcZoomIt.lpszMenuName = NULL; wcZoomIt.lpszClassName = L"MagnifierClass"; RegisterClass(&wcZoomIt); } else { g_LiveZoomToggleKey = 0; } wcZoomIt.style = 0; wcZoomIt.lpfnWndProc = (WNDPROC)MainWndProc; wcZoomIt.cbClsExtra = 0; wcZoomIt.cbWndExtra = 0; wcZoomIt.hInstance = hInstance; wcZoomIt.hIcon = NULL; wcZoomIt.hCursor = LoadCursor( hInstance, L"NULLCURSOR" ); wcZoomIt.hbrBackground = NULL; wcZoomIt.lpszMenuName = NULL; wcZoomIt.lpszClassName = L"ZoomitClass"; if ( ! RegisterClass(&wcZoomIt) ) return FALSE; hWndMain = CreateWindowEx( WS_EX_TOOLWINDOW, L"ZoomitClass", L"演示工具放大窗口", WS_POPUP, 0, 0, 0, 0, NULL, NULL, hInstance, NULL); // If window could not be created, return "failure" if (!hWndMain ) return NULL; // Make the window visible; update its client area; and return "success" ShowWindow(hWndMain, SW_HIDE); // Add tray icon EnableDisableTrayIcon( hWndMain, g_ShowTrayIcon ); return hWndMain; } //---------------------------------------------------------------------------- // // WinMain // //---------------------------------------------------------------------------- int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ PWSTR lpCmdLine, _In_ int nCmdShow ) { MSG msg; HACCEL hAccel; if( !ShowEula( APPNAME, NULL, NULL )) return 1; #ifdef __ZOOMIT_POWERTOYS__ if (powertoys_gpo::getConfiguredZoomItEnabledValue() == powertoys_gpo::gpo_rule_configured_disabled) { Logger::warn(L"Tried to start with a GPO policy setting the utility to always be disabled. Please contact your systems administrator."); return 1; } Shared::Trace::ETWTrace* trace = nullptr; std::wstring pid = std::wstring(lpCmdLine); // The PowerToys pid is the argument to the process. auto mainThreadId = GetCurrentThreadId(); if (!pid.empty()) { g_StartedByPowerToys = TRUE; trace = new Shared::Trace::ETWTrace(); Trace::RegisterProvider(); trace->UpdateState(true); Trace::ZoomItStarted(); // Initialize logger LoggerHelpers::init_logger(L"ZoomIt", L"", LogSettings::zoomItLoggerName); ProcessWaiter::OnProcessTerminate(pid, [mainThreadId](int err) { if (err != ERROR_SUCCESS) { Logger::error(L"Failed to wait for parent process exit. {}", get_last_error_or_default(err)); } else { Logger::trace(L"PowerToys runner exited."); } Logger::trace(L"Exiting ZoomIt"); PostThreadMessage(mainThreadId, WM_QUIT, 0, 0); }); } #endif // __ZOOMIT_POWERTOYS__ #ifndef _WIN64 if(!g_StartedByPowerToys) { // Launch 64-bit version if necessary SetAutostartFilePath(); if( RunningOnWin64()) { // Record where we are if we're the 32-bit version return Run64bitVersion(); } } #endif // Single instance per desktop if( !CreateEvent( NULL, FALSE, FALSE, _T("Local\\ZoomitActive"))) { CreateEvent( NULL, FALSE, FALSE, _T("ZoomitActive")); } if( GetLastError() == ERROR_ALREADY_EXISTS ) { if (g_StartedByPowerToys) { MessageBox(NULL, L"检测到已经有一个 ZoomIt 演示工具程序正在运行了。\n无法再启动 PowerToys ZoomIt。", APPNAME, MB_ICONERROR | MB_SETFOREGROUND); return 1; } // Tell the other instance to show the options dialog g_hWndMain = FindWindow( L"ZoomitClass", NULL ); if( g_hWndMain != NULL ) { PostMessage( g_hWndMain, WM_COMMAND, IDC_OPTIONS, 0 ); int count = 0; while( count++ < 5 ) { HWND local_hWndOptions = FindWindow( NULL, L"ZoomIt - Sysinternals: www.sysinternals.com" ); if( local_hWndOptions ) { SetForegroundWindow( local_hWndOptions ); SetWindowPos( local_hWndOptions, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE|SWP_SHOWWINDOW ); break; } Sleep( 100 ); } } return 0; } g_OsVersion = GetVersion() & 0xFFFF; // load accelerators hAccel = LoadAccelerators( hInstance, TEXT("ACCELERATORS")); if (FAILED(CoInitialize(0))) { return 0; } pEnableThemeDialogTexture = (type_pEnableThemeDialogTexture) GetProcAddress( GetModuleHandle( L"uxtheme.dll" ), "EnableThemeDialogTexture" ); pMonitorFromPoint = (type_MonitorFromPoint) GetProcAddress( LoadLibrarySafe( L"User32.dll", DLL_LOAD_LOCATION_SYSTEM), "MonitorFromPoint" ); pGetMonitorInfo = (type_pGetMonitorInfo) GetProcAddress( LoadLibrarySafe( L"User32.dll", DLL_LOAD_LOCATION_SYSTEM), "GetMonitorInfoA" ); pSHAutoComplete = (type_pSHAutoComplete) GetProcAddress( LoadLibrarySafe(L"Shlwapi.dll", DLL_LOAD_LOCATION_SYSTEM), "SHAutoComplete" ); pSetLayeredWindowAttributes = (type_pSetLayeredWindowAttributes) GetProcAddress( LoadLibrarySafe(L"user32.dll", DLL_LOAD_LOCATION_SYSTEM), "SetLayeredWindowAttributes" ); pMagSetWindowSource = (type_pMagSetWindowSource) GetProcAddress( LoadLibrarySafe(L"magnification.dll", DLL_LOAD_LOCATION_SYSTEM), "MagSetWindowSource" ); pGetPointerType = (type_pGetPointerType)GetProcAddress(LoadLibrarySafe(L"user32.dll", DLL_LOAD_LOCATION_SYSTEM), "GetPointerType" ); pGetPointerPenInfo = (type_pGetPointerPenInfo)GetProcAddress(LoadLibrarySafe(L"user32.dll", DLL_LOAD_LOCATION_SYSTEM), "GetPointerPenInfo" ); pMagInitialize = (type_pMagInitialize)GetProcAddress(LoadLibrarySafe(L"magnification.dll", DLL_LOAD_LOCATION_SYSTEM), "MagInitialize"); pMagSetWindowTransform = (type_pMagSetWindowTransform) GetProcAddress( LoadLibrarySafe(L"magnification.dll", DLL_LOAD_LOCATION_SYSTEM), "MagSetWindowTransform" ); pMagSetFullscreenTransform = (type_pMagSetFullscreenTransform)GetProcAddress(LoadLibrarySafe(L"magnification.dll", DLL_LOAD_LOCATION_SYSTEM), "MagSetFullscreenTransform"); pMagSetInputTransform = (type_pMagSetInputTransform)GetProcAddress(LoadLibrarySafe(L"magnification.dll", DLL_LOAD_LOCATION_SYSTEM), "MagSetInputTransform"); pMagShowSystemCursor = (type_pMagShowSystemCursor)GetProcAddress(LoadLibrarySafe(L"magnification.dll", DLL_LOAD_LOCATION_SYSTEM), "MagShowSystemCursor"); pMagSetWindowFilterList = (type_pMagSetWindowFilterList)GetProcAddress( LoadLibrarySafe( L"magnification.dll", DLL_LOAD_LOCATION_SYSTEM ), "MagSetWindowFilterList" ); pSHQueryUserNotificationState = (type_pSHQueryUserNotificationState) GetProcAddress( LoadLibrarySafe(L"shell32.dll", DLL_LOAD_LOCATION_SYSTEM), "SHQueryUserNotificationState" ); pDwmIsCompositionEnabled = (type_pDwmIsCompositionEnabled) GetProcAddress( LoadLibrarySafe(L"dwmapi.dll", DLL_LOAD_LOCATION_SYSTEM), "DwmIsCompositionEnabled" ); pSetProcessDPIAware = (type_pSetProcessDPIAware) GetProcAddress( LoadLibrarySafe(L"User32.dll", DLL_LOAD_LOCATION_SYSTEM), "SetProcessDPIAware"); pSystemParametersInfoForDpi = (type_pSystemParametersInfoForDpi)GetProcAddress(LoadLibrarySafe(L"User32.dll", DLL_LOAD_LOCATION_SYSTEM), "SystemParametersInfoForDpi"); pGetDpiForWindow = (type_pGetDpiForWindow)GetProcAddress(LoadLibrarySafe(L"User32.dll", DLL_LOAD_LOCATION_SYSTEM), "GetDpiForWindow" ); pCreateDirect3D11DeviceFromDXGIDevice = (type_pCreateDirect3D11DeviceFromDXGIDevice) GetProcAddress( LoadLibrarySafe(L"d3d11.dll", DLL_LOAD_LOCATION_SYSTEM), "CreateDirect3D11DeviceFromDXGIDevice" ); pCreateDirect3D11SurfaceFromDXGISurface = (type_pCreateDirect3D11SurfaceFromDXGISurface) GetProcAddress( LoadLibrarySafe(L"d3d11.dll", DLL_LOAD_LOCATION_SYSTEM), "CreateDirect3D11SurfaceFromDXGISurface" ); pD3D11CreateDevice = (type_pD3D11CreateDevice) GetProcAddress( LoadLibrarySafe(L"d3d11.dll", DLL_LOAD_LOCATION_SYSTEM), "D3D11CreateDevice" ); // Windows Server 2022 (and including Windows 11) introduced a bug where the cursor disappears // in live zoom. Use the full-screen magnifier as a workaround on those versions only. It is // currently impractical as a replacement; it requires calling MagSetInputTransform for all // input to be transformed. Otherwise, some hit-testing is misdirected. MagSetInputTransform // fails without token UI access, which is impractical; it requires copying the executable // under either %ProgramFiles% or %SystemRoot%, which requires elevation. // // TODO: Update the Windows 11 21H2 revision check when the final number is known. Also add a // check for the Windows Server 2022 revision if that bug (https://task.ms/38611091) is // fixed. DWORD windowsRevision, windowsBuild = GetWindowsBuild( &windowsRevision ); if( ( windowsBuild == BUILD_WINDOWS_SERVER_2022 ) || ( ( windowsBuild == BUILD_WINDOWS_11_21H2 ) && ( windowsRevision < 829 ) ) ) { if( pMagSetFullscreenTransform && pMagSetInputTransform ) g_fullScreenWorkaround = TRUE; } #if 1 // Calling this causes Windows to mess with our query of monitor height and width if( pSetProcessDPIAware ) { pSetProcessDPIAware(); } #endif /* Perform initializations that apply to a specific instance */ g_hWndMain = InitInstance(hInstance, nCmdShow); if (!g_hWndMain ) return FALSE; #ifdef __ZOOMIT_POWERTOYS__ HANDLE m_reload_settings_event_handle = NULL; HANDLE m_exit_event_handle = NULL; std::thread m_event_triggers_thread; if( g_StartedByPowerToys ) { // Start a thread to listen to PowerToys Events. m_reload_settings_event_handle = CreateEventW(nullptr, false, false, CommonSharedConstants::ZOOMIT_REFRESH_SETTINGS_EVENT); m_exit_event_handle = CreateEventW(nullptr, false, false, CommonSharedConstants::ZOOMIT_EXIT_EVENT); if (!m_reload_settings_event_handle || !m_exit_event_handle) { Logger::warn(L"Failed to create events. {}", get_last_error_or_default(GetLastError())); return 1; } m_event_triggers_thread = std::thread([&]() { MSG msg; HANDLE event_handles[2] = {m_reload_settings_event_handle, m_exit_event_handle}; while (g_running) { DWORD dwEvt = MsgWaitForMultipleObjects(2, event_handles, false, INFINITE, QS_ALLINPUT); if (!g_running) { break; } switch (dwEvt) { case WAIT_OBJECT_0: { // Reload Settings Event Logger::trace(L"Received a reload settings event."); PostMessage(g_hWndMain, WM_USER_RELOAD_SETTINGS, 0, 0); break; } case WAIT_OBJECT_0 + 1: { // Exit Event Logger::trace(L"Received an exit event."); PostMessage(g_hWndMain, WM_QUIT, 0, 0); break; } case WAIT_OBJECT_0 + 2: if (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessageW(&msg); } break; default: break; } } }); } #endif // __ZOOMIT_POWERTOYS__ /* Acquire and dispatch messages until a WM_QUIT message is received. */ while (GetMessage(&msg, NULL, 0, 0 )) { if( !TranslateAccelerator( g_hWndMain, hAccel, &msg )) { TranslateMessage(&msg); DispatchMessage(&msg); } } int retCode = (int) msg.wParam; g_running = FALSE; #ifdef __ZOOMIT_POWERTOYS__ if(g_StartedByPowerToys) { if (trace!=nullptr) { trace->Flush(); delete trace; } Trace::UnregisterProvider(); // Needed to unblock MsgWaitForMultipleObjects one last time SetEvent(m_reload_settings_event_handle); CloseHandle(m_reload_settings_event_handle); CloseHandle(m_exit_event_handle); m_event_triggers_thread.join(); } #endif // __ZOOMIT_POWERTOYS__ return retCode; } ================================================ FILE: PowerToys/src/modules/alwaysontop/AlwaysOnTop/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 AlwaysOnTop AlwaysOnTop is a product name, keep as is. 检测到一个以管理员权限运行的窗口,无法将其置顶。 administrator is context of user account. 了解更多 不再显示 ================================================ FILE: PowerToys/src/modules/awake/Awake/Core/Constants.cs ================================================ // Copyright (c) Microsoft Corporation // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. namespace Awake.Core { internal static class Constants { internal const string AppName = "Awake"; internal const string FullAppName = "阻止睡眠"; // 这个 FullAppName 好像也只有悬停显示才用上... //internal const string FullAppName = "PowerToys " + AppName; internal const string TrayWindowId = "Awake.MessageWindow"; internal const string BuildRegistryLocation = @"SOFTWARE\Microsoft\Windows NT\CurrentVersion"; // PowerToys Awake build code name. Used for exact logging // that does not map to PowerToys broad version schema to pinpoint // internal issues easier. // Format of the build ID is: CODENAME_MMDDYYYY, where MMDDYYYY // is representative of the date when the last change was made before // the pull request is issued. internal const string BuildId = "TILLSON_11272024"; } } ================================================ FILE: PowerToys/src/modules/awake/Awake/Properties/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 已选中 退出 {0} 小时 {0} shouldn't be removed. It will be replaced by a number greater than 1 at runtime by the application. Used for defining a period to keep the PC awake. 无限模式 Keep the system awake forever 倒计时模式 Keep the system awake for a given time 保持屏幕常亮 定时关闭模式 Keep the system awake until expiration date and time {0} 分钟 {0} shouldn't be removed. It will be replaced by a number greater than 1 at runtime by the application. Used for defining a period to keep the PC awake. 关闭 Don't keep the system awake, use the selected system power plan 未选中 指定 Awake 是否使用 PowerToys 配置文件来管理状态。 设定是否保持屏幕常亮。 设定阻止睡眠的结束时间,到时间后恢复默认的睡眠与屏幕设置。 绑定某个进程来控制阻止睡眠功能。当该进程结束时,恢复默认的睡眠与屏幕设置。 设定阻止睡眠的持续时长秒数。 进程绑定挂钩触发退出。 内部终止处理函数触发退出。 收到结束进程信号,正在确保安全退出... 定时关闭 开启 关闭 倒计时关闭 d Used to display number of days in the system tray tooltip. h Used to display number of hours in the system tray tooltip. m Used to display number of minutes in the system tray tooltip. s Used to display number of seconds in the system tray tooltip. 使用父进程作为绑定目标 - 父进程结束时退出 Awake。 绑定到 Describes the process ID Awake is bound to when running. ================================================ FILE: PowerToys/src/modules/colorPicker/ColorPickerUI/Properties/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 蓝色值 Tool tip that appears when hovering over a textbox that represents the blue HEX value 取消 Cancel button content 复制成功 Message that appears when a user clicked a button that copies the value to the user's clipboard 复制 Tooltip that describes a button that will copy a value to the user's clipboard 调色板 Do not translate product name 绿色值 Tool tip that appears when hovering over a textbox that represents the green HEX value 十六进制值 Tool tip that appears when hovering over a textbox that represents the HEX value 色相滑块 Tool tip that appears when hovering over a slider that represents the color hue (from HSV) 点击取色按钮抓取颜色 Message that shows when the user launches the window for the first time or when no colors have been selected 确定 Select button content 设置 Tooltip that shows that the user can click this button to open PowerToys settings 取色 Tooltip that shows that the user can click this button to pick a new color 红色值 Tool tip that appears when hovering over a textbox that represents the red HEX value 删除 Tooltip that shows that the user can remove an item from a list 导出 Tooltip that shows the user can export the selected items to a file 按颜色分组 Tooltip that shows the user can export the selected items grouped by color to a file 按格式分组 Tooltip that shows the user can export the selected items grouped by format to a file 饱和度滑块 Tool tip that appears when hovering over a slider that represents the color saturation (from HSV) 已选颜色 Tooltip that shows the user what the selected color is 可以调整这个颜色 Narrator message that describes to the user that this color can be adjusted 调色 Tooltip that shows that the user can adjust this color 色值滑块 Tool tip that appears when hovering over a slider that represents the color value (from HSV) 选择该颜色 颜色编码 取色记录 调色板 取色器控制 变暗一点 更加暗一点 变亮一点 更加亮一点 白色 White color #FFFFFF 注:颜色参考官译、office内名称,一定程度修改结合日常通用名称。 黑色 Black color #000000 浅灰 Light gray color #C8C8C8 灰色 Gray color #878787 深灰 Dark gray color #3C3C3C 米色 Coral color #E1D1CE 玫红 Rose color #FF6699 浅橙 Light orange color #FF9966 茶色 Tan color #996633 浅黄 Light yellow color #FFFF66 浅绿 Light green color #66FF66 青绿 Lime color #CCFF33 水绿 Aqua color #00FFFF 天蓝 Sky blue color #66FFFF 浅青绿 Light turquoise color #66CCFF 淡蓝 Pale blue color 不可见?ColorNameHelper.cs 浅蓝 Light blue color #CCECFF 冰蓝 Ice blue color #D8E3EC 粉蓝 Periwinkle color #B1B0D1 淡紫 Lavender color #9933FF 粉红 Pink color #FF66CC 红色 Red color #FF0000 橙色 Orange color #ED8C12 棕色 Brown color #AF804F 金色 Gold color #CC9900 黄色 Yellow color #FFFF00 橄榄绿 Olive green color #7C8260 绿色 Green color #33CC33 翠绿 Bright green color #3CDBC0 青色 Teal color #00FFCC 青绿 Turquoise color #33CCFF 蓝色 Blue color #0000FF 蓝灰 Blue gray color #597BA6 靛蓝 Indigo color #333399 紫色 Purple color #CC00CC 深红 Dark red color #A50021 深黄 Dark yellow color #808000 深绿 Dark green color #006600 深青 Dark teal color #006666 深蓝 Dark blue color #000066 深紫 Dark purple color #660066 梅红 Plum color #660033 取色 Button label to pick a new color 取色器 Do not translate product name ================================================ FILE: PowerToys/src/modules/colorPicker/ColorPickerUI/ViewModels/ColorEditorViewModel.cs ================================================ // 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; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel.Composition; using System.Globalization; using System.IO; using System.Linq; using System.Windows.Input; using System.Windows.Media; using ColorPicker.Common; using ColorPicker.Helpers; using ColorPicker.Models; using ColorPicker.Settings; using ColorPicker.ViewModelContracts; using Microsoft.PowerToys.Settings.UI.Library.Enumerations; using Microsoft.Win32; namespace ColorPicker.ViewModels { [Export(typeof(IColorEditorViewModel))] public class ColorEditorViewModel : ViewModelBase, IColorEditorViewModel { private readonly IUserSettings _userSettings; private readonly List _allColorRepresentations = new List(); private Color _selectedColor; private bool _initializing; private int _selectedColorIndex; [ImportingConstructor] public ColorEditorViewModel(IUserSettings userSettings) { OpenColorPickerCommand = new RelayCommand(() => OpenColorPickerRequested?.Invoke(this, EventArgs.Empty)); OpenSettingsCommand = new RelayCommand(() => OpenSettingsRequested?.Invoke(this, EventArgs.Empty)); RemoveColorsCommand = new RelayCommand(DeleteSelectedColors); ExportColorsGroupedByColorCommand = new RelayCommand(ExportSelectedColorsByColor); ExportColorsGroupedByFormatCommand = new RelayCommand(ExportSelectedColorsByFormat); SelectedColorChangedCommand = new RelayCommand((newColor) => { if (ColorsHistory.Contains((Color)newColor)) { ColorsHistory.Remove((Color)newColor); } ColorsHistory.Insert(0, (Color)newColor); SelectedColorIndex = 0; }); ColorsHistory.CollectionChanged += ColorsHistory_CollectionChanged; _userSettings = userSettings; SetupAllColorRepresentations(); SetupAvailableColorRepresentations(); } public event EventHandler OpenColorPickerRequested; public event EventHandler OpenSettingsRequested; public ICommand OpenColorPickerCommand { get; } public ICommand OpenSettingsCommand { get; } public ICommand RemoveColorsCommand { get; } public ICommand ExportColorsGroupedByColorCommand { get; } public ICommand ExportColorsGroupedByFormatCommand { get; } public ICommand SelectedColorChangedCommand { get; } public ICommand HideColorFormatCommand { get; } public ObservableCollection ColorsHistory { get; } = new ObservableCollection(); public ObservableCollection ColorRepresentations { get; } = new ObservableCollection(); public Color SelectedColor { get { return _selectedColor; } set { _selectedColor = value; OnPropertyChanged(); } } public int SelectedColorIndex { get { return _selectedColorIndex; } set { _selectedColorIndex = value; if (value >= 0) { SelectedColor = ColorsHistory[_selectedColorIndex]; } OnPropertyChanged(); } } public void Initialize() { _initializing = true; ColorsHistory.Clear(); foreach (var item in _userSettings.ColorHistory) { var parts = item.Split('|'); ColorsHistory.Add(new Color() { A = byte.Parse(parts[0], NumberStyles.Integer, CultureInfo.InvariantCulture), R = byte.Parse(parts[1], NumberStyles.Integer, CultureInfo.InvariantCulture), G = byte.Parse(parts[2], NumberStyles.Integer, CultureInfo.InvariantCulture), B = byte.Parse(parts[3], NumberStyles.Integer, CultureInfo.InvariantCulture), }); SelectedColorIndex = 0; } _initializing = false; } private void ColorsHistory_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { if (!_initializing) { _userSettings.ColorHistory.ClearWithoutNotification(); foreach (var item in ColorsHistory) { _userSettings.ColorHistory.AddWithoutNotification(item.A + "|" + item.R + "|" + item.G + "|" + item.B); } _userSettings.ColorHistory.ReleaseNotification(); } } private void DeleteSelectedColors(object selectedColors) { var colorsToRemove = ((IList)selectedColors).OfType().ToList(); var indicesToRemove = colorsToRemove.Select(color => ColorsHistory.IndexOf(color)).ToList(); foreach (var color in colorsToRemove) { ColorsHistory.Remove(color); } SelectedColorIndex = ComputeWhichIndexToSelectAfterDeletion(colorsToRemove.Count + ColorsHistory.Count, indicesToRemove); SessionEventHelper.Event.EditorHistoryColorRemoved = true; } private void ExportSelectedColorsByColor(object selectedColors) { ExportColors(selectedColors, GroupExportedColorsBy.Color); } private void ExportSelectedColorsByFormat(object selectedColors) { ExportColors(selectedColors, GroupExportedColorsBy.Format); } private void ExportColors(object colorsToExport, GroupExportedColorsBy method) { var colors = SerializationHelper.ConvertToDesiredColorFormats((IList)colorsToExport, ColorRepresentations, method); var dialog = new SaveFileDialog { Title = "保存所选颜色", Filter = "文本文件 (*.txt)|*.txt|Json 文件 (*.json)|*.json", }; if (dialog.ShowDialog() == true) { var extension = Path.GetExtension(dialog.FileName); var contentToWrite = extension.ToUpperInvariant() switch { ".TXT" => colors.ToTxt(';'), ".JSON" => colors.ToJson(), _ => string.Empty, }; File.WriteAllText(dialog.FileName, contentToWrite); SessionEventHelper.Event.EditorColorsExported = true; } } // Will select the closest color to the last selected one in color history private static int ComputeWhichIndexToSelectAfterDeletion(int colorsCount, List indicesToRemove) { var newIndices = Enumerable.Range(0, colorsCount).ToList(); foreach (var index in indicesToRemove) { newIndices[index] = -1; } var appearancesOfMinusOne = 0; for (var i = 0; i < newIndices.Count; i++) { if (newIndices[i] < 0) { appearancesOfMinusOne++; continue; } newIndices[i] -= appearancesOfMinusOne; } var lastSelectedIndex = indicesToRemove.Last(); for (int i = lastSelectedIndex - 1, j = lastSelectedIndex + 1; ; i--, j++) { if (j < newIndices.Count && newIndices[j] != -1) { return newIndices[j]; } if (i >= 0 && newIndices[i] != -1) { return newIndices[i]; } if (j >= newIndices.Count && i < 0) { break; } } return -1; } private void SetupAllColorRepresentations() { _allColorRepresentations.Add( new ColorFormatModel() { FormatName = ColorRepresentationType.HEX.ToString(), Convert = (Color color) => ColorRepresentationHelper.GetStringRepresentationFromMediaColor(color, ColorRepresentationType.HEX.ToString()).ToLowerInvariant(), }); _allColorRepresentations.Add( new ColorFormatModel() { FormatName = ColorRepresentationType.RGB.ToString(), Convert = (Color color) => ColorRepresentationHelper.GetStringRepresentationFromMediaColor(color, ColorRepresentationType.RGB.ToString()), }); _allColorRepresentations.Add( new ColorFormatModel() { FormatName = ColorRepresentationType.HSL.ToString(), Convert = (Color color) => ColorRepresentationHelper.GetStringRepresentationFromMediaColor(color, ColorRepresentationType.HSL.ToString()), }); _allColorRepresentations.Add( new ColorFormatModel() { FormatName = ColorRepresentationType.HSV.ToString(), Convert = (Color color) => ColorRepresentationHelper.GetStringRepresentationFromMediaColor(color, ColorRepresentationType.HSV.ToString()), }); _allColorRepresentations.Add( new ColorFormatModel() { FormatName = ColorRepresentationType.CMYK.ToString(), Convert = (Color color) => ColorRepresentationHelper.GetStringRepresentationFromMediaColor(color, ColorRepresentationType.CMYK.ToString()), }); _allColorRepresentations.Add( new ColorFormatModel() { FormatName = ColorRepresentationType.HSB.ToString(), Convert = (Color color) => ColorRepresentationHelper.GetStringRepresentationFromMediaColor(color, ColorRepresentationType.HSB.ToString()), }); _allColorRepresentations.Add( new ColorFormatModel() { FormatName = ColorRepresentationType.HSI.ToString(), Convert = (Color color) => ColorRepresentationHelper.GetStringRepresentationFromMediaColor(color, ColorRepresentationType.HSI.ToString()), }); _allColorRepresentations.Add( new ColorFormatModel() { FormatName = ColorRepresentationType.HWB.ToString(), Convert = (Color color) => ColorRepresentationHelper.GetStringRepresentationFromMediaColor(color, ColorRepresentationType.HWB.ToString()), }); _allColorRepresentations.Add( new ColorFormatModel() { FormatName = ColorRepresentationType.NCol.ToString(), Convert = (Color color) => ColorRepresentationHelper.GetStringRepresentationFromMediaColor(color, ColorRepresentationType.NCol.ToString()), }); _allColorRepresentations.Add( new ColorFormatModel() { FormatName = ColorRepresentationType.CIELAB.ToString(), Convert = (Color color) => ColorRepresentationHelper.GetStringRepresentationFromMediaColor(color, ColorRepresentationType.CIELAB.ToString()), }); _allColorRepresentations.Add( new ColorFormatModel() { FormatName = ColorRepresentationType.CIEXYZ.ToString(), Convert = (Color color) => ColorRepresentationHelper.GetStringRepresentationFromMediaColor(color, ColorRepresentationType.CIEXYZ.ToString()), }); _allColorRepresentations.Add( new ColorFormatModel() { FormatName = ColorRepresentationType.VEC4.ToString(), Convert = (Color color) => ColorRepresentationHelper.GetStringRepresentationFromMediaColor(color, ColorRepresentationType.VEC4.ToString()), }); _allColorRepresentations.Add( new ColorFormatModel() { FormatName = "Decimal", Convert = (Color color) => ColorRepresentationHelper.GetStringRepresentationFromMediaColor(color, "Decimal"), }); _allColorRepresentations.Add( new ColorFormatModel() { FormatName = "HEX Int", Convert = (Color color) => ColorRepresentationHelper.GetStringRepresentationFromMediaColor(color, "HEX Int"), }); _userSettings.VisibleColorFormats.CollectionChanged += VisibleColorFormats_CollectionChanged; // Any other custom format to be added here as well that are read from settings } private void VisibleColorFormats_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { SetupAvailableColorRepresentations(); } private void SetupAvailableColorRepresentations() { ColorRepresentations.Clear(); foreach (var colorFormat in _userSettings.VisibleColorFormats) { ColorRepresentations.Add(new ColorFormatModel() { FormatName = colorFormat.Key.ToUpperInvariant(), Convert = null, FormatString = colorFormat.Value }); } } } } ================================================ FILE: PowerToys/src/modules/fancyzones/FancyZonesLib/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Create window layouts to help make multi-tasking easy this is NOT referring to the operating system. Hold Shift key to activate zones while dragging SHIFT is referring to keyboard key. Use a non-primary mouse button to toggle zone activation Override Windows Snap hotkeys (Win + Arrow) to move windows between zones Windows is referring to OS. Win is referring to Windows Key, "Arrow" is referring to keyboard key. Move windows between zones across all monitors when snapping with (Win + Arrow) windows is referring to the display surfaces like applications, not the OS. Win is referring to Windows Key, "Arrow" is referring to keyboard key. Move windows based on their position when snapping with (Win + Arrow) windows is referring to the display surfaces like applications, not the OS. Win is referring to Windows Key, "Arrow" is referring to keyboard key. Keep windows in their zones when the screen resolution changes windows is referring to the display surfaces like applications, not the OS. During zone layout changes, windows assigned to a zone will match new size/positions windows is referring to the display surfaces like applications, not the OS. Flash zones when the active FancyZones layout changes Flash refers to blinking the zone so it appears/disappears. FancyZones is the product name Show zones on all monitors while dragging a window Allow zones to span across monitors (all monitors must have the same DPI scaling) Make dragged window transparent Zone inactive color (Default #F5FCFF) Zone border color (Default #FFFFFF) Zone highlight color (Default #008CFF) Follow mouse cursor instead of focus when launching editor in a multi-screen environment Move newly created windows to their last known zone windows is referring to the display surfaces like applications, not the OS. Move newly created windows to the current active monitor [EXPERIMENTAL] Restore the original size of windows when unsnapping Zone configuration Edit zones To launch the zone editor, select the Edit zones button below or press the zone editor hotkey anytime "Edit zones" must match value from Setting_Launch_Editor_Button value Configure the zone editor hotkey Toggle shortcuts for switching between windows in the current zone Shortcut for switching to the next window in the current zone Shortcut for switching to the previous window in the current zone To exclude an application from snapping to zones add its name here (one per line). Excluded apps will react to the Windows Snap regardless of all other settings. Windows refers to the Operating system Zone opacity (%) a zone is a snapping region and can be transparent 窗口布局 FancyZones is a product name, keep as is. 检测到一个以管理员权限运行的软件窗口,窗口布局无法对它进行操作。 administrator is context of user account. 了解更多 不再显示 无法安装快捷键监听器。 无法安装 Windows 事件监听器。 Windows refers to the Operating system PowerToys - 窗口布局 do not loc. Both are product names “允许布局跨显示器” 功能需要所有显示器的 DPI 缩放比例都相同。 窗口布局找不到数据储存路径。请将问题反馈到 "Report bug to" will have a URL after. FancyZones is a product name, keep as is. 布局编辑器启动失败。请将问题反馈到 "Report bug to" will have a URL after. FancyZones is a product name, keep as is. 窗口布局配置加载失败。将使用默认配置。 FancyZones is a product name, keep as is. 窗口布局配置保存失败,请过会儿再试。如果仍然不行,请将问题反馈到 "Report bug to" will have a URL after. FancyZones is a product name, keep as is. Enable quick layout switch Flash zones when switching layout Use system theme Zone number color (Default #000000) Show zone number ================================================ FILE: PowerToys/src/modules/fancyzones/editor/FancyZonesEditor/Properties/Resources.resx ================================================ text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 添加布局区 布局区已添加 应用 取消 区块布局编辑器 网格布局编辑器 请将问题反馈到 自定义 自定义布局创建器 激活距离 Distance of when an adjacent zone should light up when the window is close to it 布局编辑器 主编辑器 名称 保存并应用 布局区间隔 // OLD 布局区间隔 模板 布局区数量减 布局区数量加 布局编辑器参数无效。 布局编辑器错误 布局编辑器不能单独运行,请按快捷键或从设置里打开。 解析设备信息数据时出错。 层叠 网格 优先级网格 关闭 应用布局时出错 布局 “{0}” 的数据格式错误 加载自定义布局时出错 暂留自定义布局时出错 //看了下好像根本没用上,无从考证是保存还是暂留 序列化已删除的布局时出错 布局编辑器错误处理程序 请将问题反馈到 找不到匹配项 ({0}) 重置布局 as in Reset to a blank value 请选择布局 创建新的布局 Dialog header that allows the user to select a specific layout type 创建 Button label that creates a new layout 新建布局 Button label that allows the user to create a new layout 自定义布局 删除 Context menu label that allows the user duplicate a layout 复制 Context menu label that allows the user duplicate a layout 编辑 Context menu label that allows the user edit a layout 添加独立、可堆叠的布局区,就像窗口那样 区块 从屏幕大小的区域切分成网格,作为布局区 网格 合并 Button label that allows the user to merge 2 or more zones together 显示器 模板设置 删除布局对话框。 布局区 The name of the zones. 宽度 The name of dimension called width. 高度 The name of dimension called height. 方向键调整大小,Delete 键删除。 Keys and key means the keyboard keys. 提示 确定删除此布局吗? 编辑布局区 创建一个属于自己的布局 删除布局区 A tooltip on a button that allows the user to delete a zone 编辑器数据解析错误。 解析应用的布局时出错。 应用布局 保存 布局区数量 // OLD 空白 创建自定义布局 自定义布局 拖动选择布局区,然后点击“合并”按钮合并。 Click mouse, hold button down and drag across multiple zones in tool // 开头换行,否则最后断开不美观 合并: Title for concept behind Merging two zones together or removing an zone 快捷键: Ctrl+Tab 开始选择。 Tab 切换选择。 Shift+S 切分布局区。 Delete 合并分界线。 方向键 移动分界线。 Ctrl+Tab 开始选择。 Tab 切换选择。 方向键 移动 10 像素。 Ctrl+方向键 移动 1 像素。 Shift+方向键 拉伸 10 像素(两条边各 5 像素)。 Ctrl+Shift+方向键 拉伸 2 像素(两条边各 1 像素)。 单击切分布局区,按住 Shift 改变切分方向。 A segmenter visual for splitting one item into two. This would be the vertical line. Shift key is referring to key on keyboard // 开头换行,否则最后断开不美观 切分: Title for concept: A segmenter visual for splitting one item into two. This would be the vertical line 按 Ctrl + Win + Alt + 切换键,切换到此布局 布局区数量 像素 Abbreviation of pixels 快速切换键 编辑 “{0}” 已打开 自定义布局创建成功。 设置 解析布局快捷键时出错。 解析模板布局时出错。 解析自定义布局时出错。 {0} 滑块,取值范围从 {1} 到 {2} 当前为 {3}。 {0} 像素 {0} 块 设为横屏默认布局 设置为横屏下的默认布局方式 无法解析默认布局。 设为竖屏默认布局 设置为竖屏下的默认布局方式 解析编辑器参数时出现错误。 ================================================ FILE: PowerToys/src/modules/imageresizer/ImageResizerContextMenu/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 图像裁剪器 Image Resizer ================================================ FILE: PowerToys/src/modules/imageresizer/dll/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 图像裁剪器 图像裁剪器 Image Resizer A Windows Shell extension for bulk image resizing. Settings can be accessed from within Image Resizer. Settings header for Image Resizer ================================================ FILE: PowerToys/src/modules/imageresizer/ui/Properties/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 所有文件 取消 图片高度 图像裁剪器 Product name, do not loc (自动) 选择尺寸预设 自定义 自适应图像长宽方向(_O) GIF 动画可能无法裁剪。 覆盖原图(_E) 执行 不执行放大操作(_M) 大号 中号 确定 应用设置 省流 所有图片文件 (默认) 操作中…… 停止 剩余 {0} 小时 {1} 分。 "About" = Approximately, not "on the subject of" 剩余 {0} 小时 {1} 分。 "About" = Approximately, not "on the subject of" 剩余 {0} 小时 {1} 分。 "About" = Approximately, not "on the subject of" 剩余 {0} 小时 {1} 分。 "About" = Approximately, not "on the subject of" 剩余 {1} 分 {2} 秒。 "About" = Approximately, not "on the subject of" 剩余 {1} 分 {2} 秒。 "About" = Approximately, not "on the subject of" 剩余 {1} 分 {2} 秒。 "About" = Approximately, not "on the subject of" 剩余 {1} 分 {2} 秒。 "About" = Approximately, not "on the subject of" 剩余 {2} 秒。 "About" = Approximately, not "on the subject of" 剩余 {2} 秒。 "About" = Approximately, not "on the subject of" 填充 填充到 缩放 缩放到 拉伸 拉伸到 厘米 英寸 百分比 像素 图像裁剪器 操作 关闭 图片调整大小失败 小号 单位 数值必须介于 '{0}' 和 '{1}' 之间。 版本 图片宽度 设置 删除图片元数据(_D) 图像尺寸预设 不执行放大操作(_M) ================================================ FILE: PowerToys/src/modules/imageresizer/ui/Views/EnumValueConverter.cs ================================================ #pragma warning disable IDE0073 // Copyright (c) Brice Lambson // The Brice Lambson licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. // Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/ #pragma warning restore IDE0073 using System; using System.Globalization; using System.Text; using System.Windows.Data; using ImageResizer.Properties; namespace ImageResizer.Views { [ValueConversion(typeof(Enum), typeof(string))] public class EnumValueConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { var type = value?.GetType(); if (!type.IsEnum) { return value; } var builder = new StringBuilder(); builder .Append(type.Name) .Append('_') .Append(Enum.GetName(type, value)); var toLower = false; var replacePercentage = false; if ((string)parameter == "ToLower") { toLower = true; } else if ((string)parameter == "ToLower2") { toLower = true; replacePercentage = true; } else if (parameter != null) { builder .Append('_') .Append(parameter); } // Fixes #16792 - Looks like culture defaults to en-US, so wrong resource is being fetched. #pragma warning disable CA1304 // Specify CultureInfo var targetValue = Resources.ResourceManager.GetString(builder.ToString()); #pragma warning restore CA1304 // Specify CultureInfo if (toLower) { targetValue = targetValue.ToLower(culture); } if (replacePercentage) { targetValue = targetValue.Replace("百分比", "%"); } return targetValue; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => value; } } ================================================ FILE: PowerToys/src/modules/imageresizer/ui/Views/InputPage.xaml ================================================  ================================================ FILE: PowerToys/src/modules/keyboardmanager/KeyboardManagerEditor/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 本功能需要系统版本至少为 Win 10 1903 Windows = the OS 键盘修改器 调用 CreateWindow 创建窗口失败! 错误 Windows 重映射注册失败! This refers to an application window 错误 单键重映射 keys on a keyboard 组合键重映射 编辑按键 选择程序 选择目录 确定 取消 确定 按下 verb, alternative to "type a key on a keyboard" 发送 发送 目标程序 参数 权限 可见性 程序 起始目录 操作 网址 按键 文本 正常 隐藏 若已运行 运行中音效 启动音效 按键被占用 选择文件 以下功能未分配: Key on a keyboard 部分按键映射失败 部分组合键映射失败 选择要修改的按键和按下后发送的功能 例如,若要按下 A 来复制,把 A 映射为发送 Ctrl+C 即可。若要按键互换,可以互相映射。 Key on a keyboard 选择要修改的按键组合和按下后发送的功能 Key on a keyboard 选择要修改的按键组合和按下后发送的功能 Key on a keyboard 例如,若要在 Edge 浏览器中把 Alt+T 变成 Ctrl+T,请把 Alt+T 映射为发送 Ctrl+T,目标程序设为 MSEdge。 目标程序是指 exe 文件名(MSEdge),而不是软件名(Microsoft Edge)。 重映射单键优先于组合键生效。 Key on a keyboard 选择 verb, alternative to "type a key on a keyboard" 请按下单个按键 Key on a keyboard 请按下按键 Keys on a keyboard 当前选择了: Key on a keyboard 当前选择了: Keys on a keyboard 长按空格切换和弦(依次按下两个键触发) 长按退格切换严格匹配 长按回车确定 长按 Esc 取消 (全局生效) 启用和弦序列 严格匹配 程序文件路径 例如 https://website.url 更多高级操作 传入的参数 起始目录 成功映射 部分映射设置失败 一个按键不能同时发送多个功能 Key on a keyboard 您这啥也没改 Key on a keyboard 与另一映射冲突 Key on a keyboard 一个按键组合不能同时发送多个功能 您这啥也没改 与另一映射冲突 无法修改 Win+L Win refers to the key on a keyboard 无法修改 Ctrl+Alt+Del 映射保存失败 必须以修饰键开头 Key on a keyboard 按键重复 至少应有两个键 Key on a keyboard 必须有操作键 Key on a keyboard 最多只能有一个操作键 Key on a keyboard 最多只能有两个修饰键 Key on a keyboard 未知错误 按键下拉选择栏 Key on a keyboard 输入文本 Text as in that the shortcut will be mapped into a sequence of characters. This is not about mobile text messages. 发送按键 Key on a keyboard 运行程序 Run Program 打开网址 URI to open. 添加单键重映射 Key on a keyboard 添加组合键重映射 删除映射 不执行任何操作 打开窗口 再次运行 关闭程序 强制结束进程 关闭并强制结束 正常 管理员 其他用户 映射已删除 滚动行 Row as in horizontal rows in a table, and columns “禁用”不能作为按键 Key on a keyboard 选择 key sequence on a keyboard, example: Ctrl+C to copy a string ================================================ FILE: PowerToys/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardEventHandlers.cpp ================================================ #include "pch.h" #include #include "KeyboardEventHandlers.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace winrt; using namespace Windows::UI::Notifications; using namespace Windows::Data::Xml::Dom; namespace { bool GeneratedByKBM(const LowlevelKeyboardEvent* data) { return data->lParam->dwExtraInfo & CommonSharedConstants::KEYBOARDMANAGER_INJECTED_FLAG; } void UpdateNumpadWithShift(LowlevelKeyboardEvent* data, State& state) { //Function for fixing numpad when used as shift https://github.com/microsoft/PowerToys/issues/22346 //VK_CLEAR is not encoded in IsNumpadOriginated if (Helpers::IsNumpadOriginated(data->lParam->vkCode) || data->lParam->vkCode == VK_CLEAR) { // Decode it. If it is VK_CLEAR it will do nothing DWORD decodedKey = Helpers::ClearKeyNumpadOrigin(data->lParam->vkCode); //check if we already have a stored scanID auto scanKey = MapVirtualKey(decodedKey, MAPVK_VK_TO_VSC); auto it = state.scanMap.find(scanKey); if (it != state.scanMap.end()) { auto keyIt = state.GetSingleKeyRemap(it->second); if (keyIt) { //if key is stored as shift replace it with the numpad key auto keyValue = keyIt.value(); if (keyValue->second.index() == 0) { auto key = std::get(keyValue->second); if (key == VK_LSHIFT || key == VK_RSHIFT || key == VK_SHIFT) { if (state.numpadKeyPressed[it->second]) { //replace it with original numpad data->lParam->vkCode = it->second; } } } if (keyValue->second.index() == 1) { auto key = std::get(keyValue->second); if (key.shiftKey != ModifierKey::Disabled) { if (state.numpadKeyPressed[it->second]) { //replace it with original numpad data->lParam->vkCode = it->second; } } } } } } if (Helpers::IsNumpadKeyThatIsAffectedByShift(data->lParam->vkCode)) { // store if the Numpad key was pressed or not. If numpad numbers were pressed but then we get the same key KEY UP but with Numpad unlocked we will replace it. state.numpadKeyPressed[data->lParam->vkCode] = (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN); } } } namespace KeyboardEventHandlers { // Function to a handle a single key remap intptr_t HandleSingleKeyRemapEvent(KeyboardManagerInput::InputInterface& ii, LowlevelKeyboardEvent* data, State& state) noexcept { // Check if the key event was generated by KeyboardManager to avoid remapping events generated by us. if (!GeneratedByKBM(data)) { UpdateNumpadWithShift(data, state); const auto remapping = state.GetSingleKeyRemap(data->lParam->vkCode); if (remapping) { auto it = remapping.value(); // Check if the remap is to a key or a shortcut const bool remapToKey = it->second.index() == 0; // If mapped to VK_DISABLED then the key is disabled if (remapToKey) { if (std::get(it->second) == CommonSharedConstants::VK_DISABLED) { return 1; } } int key_count; if (remapToKey) { key_count = 1; } else { key_count = std::get(it->second).Size(); } std::vector keyEventList; // Handle remaps to VK_WIN_BOTH DWORD target; if (remapToKey) { target = Helpers::FilterArtificialKeys(std::get(it->second)); } else { target = Helpers::FilterArtificialKeys(std::get(it->second).GetActionKey()); } // If Ctrl/Alt/Shift is being remapped to Caps Lock, then reset the modifier key state to fix issues in certain IME keyboards where the IME shortcut gets invoked since it detects that the modifier and Caps Lock is pressed even though it is suppressed by the hook - More information at the GitHub issue https://github.com/microsoft/PowerToys/issues/3397 if (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN) { ResetIfModifierKeyForLowerLevelKeyHandlers(ii, it->first, target); } if (remapToKey) { if (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP) { Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast(target), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); } else { Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast(target), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); } } else { Shortcut targetShortcut = std::get(it->second); if (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP) { Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast(targetShortcut.GetActionKey()), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); Helpers::SetModifierKeyEvents(targetShortcut, ModifierKey::Disabled, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); // Dummy key is not required here since SetModifierKeyEvents will only add key-up events for the modifiers here, and the action key key-up is already sent before it } else { // Dummy key is not required here since SetModifierKeyEvents will only add key-down events for the modifiers here, and the action key key-down is already sent after it Helpers::SetModifierKeyEvents(targetShortcut, ModifierKey::Disabled, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast(targetShortcut.GetActionKey()), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); } } ii.SendVirtualInput(keyEventList); if (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN) { // If Caps Lock is being remapped to Ctrl/Alt/Shift, then reset the modifier key state to fix issues in certain IME keyboards where the IME shortcut gets invoked since it detects that the modifier and Caps Lock is pressed even though it is suppressed by the hook - More information at the GitHub issue https://github.com/microsoft/PowerToys/issues/3397 if (remapToKey) { ResetIfModifierKeyForLowerLevelKeyHandlers(ii, target, it->first); } else { std::vector shortcutKeys = std::get(it->second).GetKeyCodes(); for (auto& itSk : shortcutKeys) { ResetIfModifierKeyForLowerLevelKeyHandlers(ii, itSk, it->first); } } // Send daily telemetry event for Keyboard Manager key activation. if (remapToKey) { static int dayWeLastSentKeyToKeyTelemetryOn = -1; auto currentDay = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); if (dayWeLastSentKeyToKeyTelemetryOn != currentDay) { Trace::DailyKeyToKeyRemapInvoked(); dayWeLastSentKeyToKeyTelemetryOn = currentDay; } } else { static int dayWeLastSentKeyToShortcutTelemetryOn = -1; auto currentDay = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); if (dayWeLastSentKeyToShortcutTelemetryOn != currentDay) { Trace::DailyKeyToShortcutRemapInvoked(); dayWeLastSentKeyToShortcutTelemetryOn = currentDay; } } } return 1; } } return 0; } /* This feature has not been enabled (code from proof of concept stage) * // Function to a change a key's behavior from toggle to modifier __declspec(dllexport) intptr_t HandleSingleKeyToggleToModEvent(InputInterface& ii, LowlevelKeyboardEvent* data, State& State) noexcept { // Check if the key event was generated by KeyboardManager to avoid remapping events generated by us. if (!(data->lParam->dwExtraInfo & CommonSharedConstants::KEYBOARDMANAGER_INJECTED_FLAG)) { // The mutex should be unlocked before SendInput is called to avoid re-entry into the same mutex. More details can be found at https://github.com/microsoft/PowerToys/pull/1789#issuecomment-607555837 std::unique_lock lock(State.singleKeyToggleToMod_mutex); auto it = State.singleKeyToggleToMod.find(data->lParam->vkCode); if (it != State.singleKeyToggleToMod.end()) { // To avoid long presses (which leads to continuous keydown messages) from toggling the key on and off if (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN) { if (it->second == false) { State.singleKeyToggleToMod[data->lParam->vkCode] = true; } else { lock.unlock(); return 1; } } std::vector keyEventList; Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, (WORD)data->lParam->vkCode, 0, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, (WORD)data->lParam->vkCode, KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); lock.unlock(); ii.SendVirtualInput(keyEventList); // Reset the long press flag when the key has been lifted. if (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP) { lock.lock(); State.singleKeyToggleToMod[data->lParam->vkCode] = false; lock.unlock(); } return 1; } } return 0; } */ // Function to a handle a shortcut remap intptr_t HandleShortcutRemapEvent(KeyboardManagerInput::InputInterface& ii, LowlevelKeyboardEvent* data, State& state, const std::optional& activatedApp) noexcept { auto resetChordsResults = ResetChordsIfNeeded(data, state, activatedApp); // Check if any shortcut is currently in the invoked state bool isShortcutInvoked = state.CheckShortcutRemapInvoked(activatedApp); // Get shortcut table for given activatedApp ShortcutRemapTable& reMap = state.GetShortcutRemapTable(activatedApp); // Iterate through the shortcut remaps and apply whichever has been pressed for (auto& itShortcut : state.GetSortedShortcutRemapVector(activatedApp)) { const auto it = reMap.find(itShortcut); // If a shortcut is currently in the invoked state then skip till the shortcut that is currently invoked if (isShortcutInvoked && !it->second.isShortcutInvoked) { continue; } // Check if the remap is to a key or a shortcut const bool remapToKey = it->second.targetShortcut.index() == 0; const bool remapToShortcut = it->second.targetShortcut.index() == 1; const bool remapToText = it->second.targetShortcut.index() == 2; const bool isRunProgram = (remapToShortcut && std::get(it->second.targetShortcut).IsRunProgram()); const bool isOpenUri = (remapToShortcut && std::get(it->second.targetShortcut).IsOpenURI()); const size_t src_size = it->first.Size(); const size_t dest_size = remapToShortcut ? std::get(it->second.targetShortcut).Size() : 1; bool isMatchOnChordEnd = false; bool isMatchOnChordStart = false; static bool isAltRightKeyInvoked = false; // Check if the right Alt key (AltGr) is pressed. if (data->lParam->vkCode == VK_RMENU && ii.GetVirtualKeyState(VK_LCONTROL)) { isAltRightKeyInvoked = true; } // If the shortcut has been pressed down if (!it->second.isShortcutInvoked && it->first.CheckModifiersKeyboardState(ii)) { // if not a mod key, check for chord stuff if (!resetChordsResults.CurrentKeyIsModifierKey && (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN)) { if (it->first.exactMatch == true && !it->first.IsKeyboardStateClearExceptShortcut(ii)) { continue; } if (itShortcut.HasChord()) { if (!resetChordsResults.AnyChordStarted && data->lParam->vkCode == itShortcut.GetActionKey() && !itShortcut.IsChordStarted() && itShortcut.HasChord()) { // start new chord // Logger::trace(L"ChordKeyboardHandler:new chord started for {}", data->lParam->vkCode); isMatchOnChordStart = true; ResetAllOtherStartedChords(state, activatedApp, data->lParam->vkCode); itShortcut.SetChordStarted(true); continue; } if (itShortcut.IsChordStarted() && itShortcut.HasChord()) { if (data->lParam->vkCode == itShortcut.GetSecondKey()) { Logger::trace(L"ChordKeyboardHandler:found chord match {}, {}", itShortcut.GetActionKey(), itShortcut.GetSecondKey()); isMatchOnChordEnd = true; } // Resets chord status for the shortcut. A key was pressed and we registered if it was the end of the chord. We can reset it. if (data->lParam->vkCode != itShortcut.GetActionKey()) { itShortcut.SetChordStarted(false); } } if (resetChordsResults.AnyChordStarted && !isMatchOnChordEnd) { // Logger::trace(L"ChordKeyboardHandler:waiting on second key of chord, checked {} for {}", itShortcut.GetSecondKey(), data->lParam->vkCode); // this is a key and there is a mod, but it's not the second key of a chord. // we can't do anything with this key, we're waiting. continue; } } } if (isMatchOnChordEnd || (!resetChordsResults.AnyChordStarted && !itShortcut.HasChord() && (data->lParam->vkCode == it->first.GetActionKey() && (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN)))) { ResetAllStartedChords(state, activatedApp); resetChordsResults.AnyChordStarted = false; // Check if any other keys have been pressed apart from the shortcut. If true, then check for the next shortcut. This is to be done only for shortcut to shortcut remaps if (!it->first.IsKeyboardStateClearExceptShortcut(ii) && (remapToShortcut || (remapToKey && std::get(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED))) { continue; } std::vector keyEventList; // Remember which win key was pressed initially if (ii.GetVirtualKeyState(VK_RWIN)) { it->second.winKeyInvoked = ModifierKey::Right; } else if (ii.GetVirtualKeyState(VK_LWIN)) { it->second.winKeyInvoked = ModifierKey::Left; } if (isRunProgram) { auto threadFunction = [it]() { CreateOrShowProcessForShortcut(std::get(it->second.targetShortcut)); }; std::thread myThread(threadFunction); if (myThread.joinable()) { myThread.detach(); } Logger::trace(L"ChordKeyboardHandler:returning.."); return 1; } else if (isOpenUri) { auto shortcut = std::get(it->second.targetShortcut); auto uri = shortcut.uriToOpen; auto newUri = uri; if (!PathIsURL(uri.c_str())) { WCHAR url[1024]; DWORD bufferSize = 1024; if (UrlCreateFromPathW(uri.c_str(), url, &bufferSize, 0) == S_OK) { newUri = url; Logger::trace(L"ChordKeyboardHandler:ConvertPathToURI from {} to {}", uri, url); } else { // need access to text resources, maybe "convert-resx-to-rc.ps1" is not working to get // text from KeyboardManagerEditor to here in KeyboardManagerEngineLibrary land? toast(L"错误", L"遇到了无法理解的路径或网址"); return 1; } } auto threadFunction = [newUri]() { HINSTANCE result = ShellExecute(NULL, L"open", newUri.c_str(), NULL, NULL, SW_SHOWNORMAL); if (result == reinterpret_cast(HINSTANCE_ERROR)) { // need access to text resources, maybe "convert-resx-to-rc.ps1" is not working to get // text from KeyboardManagerEditor to here in KeyboardManagerEngineLibrary land? toast(L"错误", L"遇到了无法理解的路径或网址"); } }; std::thread myThread(threadFunction); if (myThread.joinable()) { myThread.detach(); } Logger::trace(L"ChordKeyboardHandler:returning.."); return 1; } else if (remapToShortcut) { // Get the common keys between the two shortcuts if this is not a runProgram shortcut int commonKeys = it->first.GetCommonModifiersCount(std::get(it->second.targetShortcut)); // If the original shortcut modifiers are a subset of the new shortcut if (commonKeys == src_size - 1) { // key down for all new shortcut keys except the common modifiers keyEventList = std::vector{}; Helpers::SetModifierKeyEvents(std::get(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first); Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast(std::get(it->second.targetShortcut).GetActionKey()), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); } else { // Dummy key, key up for all the original shortcut modifier keys and key down for all the new shortcut keys but common keys in each are not repeated // Send a dummy key event to prevent modifier press+release from being triggered. Example: Win+A->Ctrl+V, press Win+A, since Win will be released here we need to send a dummy event before it Helpers::SetDummyKeyEvent(keyEventList, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); // Release original shortcut state (release in reverse order of shortcut to be accurate) Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, std::get(it->second.targetShortcut)); // Set new shortcut key down state Helpers::SetModifierKeyEvents(std::get(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first); Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast(std::get(it->second.targetShortcut).GetActionKey()), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); } // Modifier state reset might be required for this key depending on the shortcut's action and target modifiers - ex: Win+Caps -> Ctrl+A if (it->first.GetCtrlKey() == NULL && it->first.GetAltKey() == NULL && it->first.GetShiftKey() == NULL) { Shortcut temp = std::get(it->second.targetShortcut); for (auto keys : temp.GetKeyCodes()) { ResetIfModifierKeyForLowerLevelKeyHandlers(ii, keys, data->lParam->vkCode); } } } else if (remapToKey) { // Do not send Disable key if (std::get(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED) { // Since the original shortcut's action key is pressed, set it to true it->second.isOriginalActionKeyPressed = true; } // Send a dummy key event to prevent modifier press+release from being triggered. Example: Win+A->V, press Win+A, since Win will be released here we need to send a dummy event before it Helpers::SetDummyKeyEvent(keyEventList, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); // Release original shortcut state (release in reverse order of shortcut to be accurate) Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); // Set target key down state if (std::get(it->second.targetShortcut) != CommonSharedConstants::VK_DISABLED) { Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast(Helpers::FilterArtificialKeys(std::get(it->second.targetShortcut))), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); } // Modifier state reset might be required for this key depending on the shortcut's action and target modifier - ex: Win+Caps -> Ctrl if (it->first.GetCtrlKey() == NULL && it->first.GetAltKey() == NULL && it->first.GetShiftKey() == NULL) { ResetIfModifierKeyForLowerLevelKeyHandlers(ii, static_cast(Helpers::FilterArtificialKeys(std::get(it->second.targetShortcut))), data->lParam->vkCode); } } // Remapped to text else { auto& remapping = std::get(it->second.targetShortcut); Helpers::SetDummyKeyEvent(keyEventList, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); // Release original shortcut state (release in reverse order of shortcut to be accurate) Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); Helpers::SetTextKeyEvents(keyEventList, remapping); } it->second.isShortcutInvoked = true; // If app specific shortcut is invoked, store the target application if (activatedApp) { state.SetActivatedApp(*activatedApp); } Logger::trace(L"ChordKeyboardHandler:keyEventList.size:{}", keyEventList.size()); ii.SendVirtualInput(keyEventList); if (activatedApp.has_value()) { if (remapToKey) { static int dayWeLastSentAppSpecificShortcutToKeyTelemetryOn = -1; auto currentDay = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); if (dayWeLastSentAppSpecificShortcutToKeyTelemetryOn != currentDay) { Trace::DailyAppSpecificShortcutToKeyRemapInvoked(); dayWeLastSentAppSpecificShortcutToKeyTelemetryOn = currentDay; } } else if (remapToShortcut && (!isRunProgram) && (!isOpenUri)) { static int dayWeLastSentAppSpecificShortcutToShortcutTelemetryOn = -1; auto currentDay = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); if (dayWeLastSentAppSpecificShortcutToShortcutTelemetryOn != currentDay) { Trace::DailyAppSpecificShortcutToShortcutRemapInvoked(); dayWeLastSentAppSpecificShortcutToShortcutTelemetryOn = currentDay; } } } else { if (remapToKey) { static int dayWeLastSentShortcutToKeyTelemetryOn = -1; auto currentDay = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); if (dayWeLastSentShortcutToKeyTelemetryOn != currentDay) { Trace::DailyShortcutToKeyRemapInvoked(); dayWeLastSentShortcutToKeyTelemetryOn = currentDay; } } else if (remapToShortcut && (!isRunProgram) && (!isOpenUri)) { static int dayWeLastSentShortcutToShortcutTelemetryOn = -1; auto currentDay = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); if (dayWeLastSentShortcutToShortcutTelemetryOn != currentDay) { Trace::DailyShortcutToShortcutRemapInvoked(); dayWeLastSentShortcutToShortcutTelemetryOn = currentDay; } } } return 1; } } else if (it->second.isShortcutInvoked) { // The shortcut has already been pressed down at least once, i.e. the shortcut has been invoked // There are 6 cases to be handled if the shortcut has been pressed down // 1. The user lets go of one of the modifier keys - reset the keyboard back to the state of the keys actually being pressed down // 2. The user keeps the shortcut pressed - the shortcut is repeated (for example you could hold down Ctrl+V and it will keep pasting) // 3. The user lets go of the action key - keep modifiers of the new shortcut until some other key event which doesn't apply to the original shortcut // 4. The user presses a modifier key in the original shortcut - suppress that key event since the original shortcut is already held down physically (This case can occur only if a user has a duplicated modifier key (possibly by remapping) or if user presses both L/R versions of a modifier remapped with "Both") // 5. The user presses any key apart from the action key or a modifier key in the original shortcut - revert the keyboard state to just the original modifiers being held down along with the current key press // 6. The user releases any key apart from original modifier or original action key - This can't happen since the key down would have to happen first, which is handled above // Prevents the unintended release of the Ctrl part when AltGr is pressed. AltGr acts as both Ctrl and Alt being pressed. // After triggering a shortcut involving AltGr, the system might attempt to release the Ctrl part. This code ensures Ctrl remains pressed, maintaining the AltGr state correctly. if (isAltRightKeyInvoked && data->lParam->vkCode == VK_LCONTROL && (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP)) { break; } // Get the common keys between the two shortcuts int commonKeys = (remapToShortcut && !isRunProgram) ? it->first.GetCommonModifiersCount(std::get(it->second.targetShortcut)) : 0; // Case 1: If any of the modifier keys of the original shortcut are released before the action key if ((it->first.CheckWinKey(data->lParam->vkCode) || it->first.CheckCtrlKey(data->lParam->vkCode) || it->first.CheckAltKey(data->lParam->vkCode) || it->first.CheckShiftKey(data->lParam->vkCode)) && (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP)) { // Release new shortcut, and set original shortcut keys except the one released std::vector keyEventList; if (remapToShortcut && !isRunProgram) { // If the target shortcut's action key is pressed, then it should be released bool isActionKeyPressed = ii.GetVirtualKeyState(std::get(it->second.targetShortcut).GetActionKey()); // Release new shortcut state (release in reverse order of shortcut to be accurate) if (isActionKeyPressed) { Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast(std::get(it->second.targetShortcut).GetActionKey()), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); } Helpers::SetModifierKeyEvents(std::get(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first, data->lParam->vkCode); if (!isAltRightKeyInvoked) { // Set original shortcut key down state except the action key and the released modifier since the original action key may or may not be held down. If it is held down it will generate its own key message Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, std::get(it->second.targetShortcut), data->lParam->vkCode); } else { isAltRightKeyInvoked = false; } // Send a dummy key event to prevent modifier press+release from being triggered. Example: Win+Ctrl+A->Ctrl+V, press Win+Ctrl+A and release A then Ctrl, since Win will be pressed here we need to send a dummy event after it Helpers::SetDummyKeyEvent(keyEventList, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); } else if (remapToKey) { bool isTargetKeyPressed = (std::get(it->second.targetShortcut) != CommonSharedConstants::VK_DISABLED) && ii.GetVirtualKeyState(Helpers::FilterArtificialKeys(std::get(it->second.targetShortcut))); // Release new key state if (std::get(it->second.targetShortcut) != CommonSharedConstants::VK_DISABLED && isTargetKeyPressed) { Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast(Helpers::FilterArtificialKeys(std::get(it->second.targetShortcut))), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); } // Ensures that after releasing both the action key and AltGr, Ctrl does not remain falsely pressed. if (!isAltRightKeyInvoked) { // Set original shortcut key down state except the action key and the released modifier since the original action key may or may not be held down. If it is held down it will generate its own key message Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, Shortcut(), data->lParam->vkCode); } else { isAltRightKeyInvoked = false; } // Send a dummy key event to prevent modifier press+release from being triggered. Example: Win+Ctrl+A->V, press Win+Ctrl+A and release A then Ctrl, since Win will be pressed here we need to send a dummy event after it Helpers::SetDummyKeyEvent(keyEventList, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); } // Reset the remap state it->second.isShortcutInvoked = false; it->second.winKeyInvoked = ModifierKey::Disabled; it->second.isOriginalActionKeyPressed = false; // If app specific shortcut has finished invoking, reset the target application if (activatedApp) { state.SetActivatedApp(KeyboardManagerConstants::NoActivatedApp); } ii.SendVirtualInput(keyEventList); return 1; } // The system will see the modifiers of the new shortcut as being held down because of the shortcut remap if (!remapToShortcut || (remapToShortcut && std::get(it->second.targetShortcut).CheckModifiersKeyboardState(ii))) { // Case 2: If the original shortcut is still held down the keyboard will get a key down message of the action key in the original shortcut and the new shortcut's modifiers will be held down (keys held down send repeated keydown messages) if (((data->lParam->vkCode == it->first.GetActionKey() && !it->first.HasChord()) || (data->lParam->vkCode == it->first.GetSecondKey() && it->first.HasChord())) && (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN)) { // In case of mapping to disable do not send anything if (remapToKey && std::get(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED) { // Since the original shortcut's action key is pressed, set it to true it->second.isOriginalActionKeyPressed = true; return 1; } std::vector keyEventList; if (remapToShortcut) { Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast(std::get(it->second.targetShortcut).GetActionKey()), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); } else if (remapToKey) { Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast(Helpers::FilterArtificialKeys(std::get(it->second.targetShortcut))), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); } else if (remapToText) { auto& remapping = std::get(it->second.targetShortcut); Helpers::SetTextKeyEvents(keyEventList, remapping); } ii.SendVirtualInput(keyEventList); return 1; } // Case 3: If the action key is released from the original shortcut, keep modifiers of the new shortcut until some other key event which doesn't apply to the original shortcut if (!remapToText && ((!it->first.HasChord() && data->lParam->vkCode == it->first.GetActionKey()) || (it->first.HasChord() && data->lParam->vkCode == it->first.GetSecondKey())) && (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP)) { std::vector keyEventList; if (remapToShortcut && !it->first.HasChord()) { // Just lift the action key for no chords. Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast(std::get(it->second.targetShortcut).GetActionKey()), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); } else if (remapToShortcut && it->first.HasChord()) { // If it has a chord, we'll want a full clean contemplated in the else, since you can't really repeat chords by pressing the end key again. // Key up for all new shortcut keys, key down for original shortcut modifiers and current key press but common keys aren't repeated Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast(std::get(it->second.targetShortcut).GetActionKey()), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); // Release new shortcut state (release in reverse order of shortcut to be accurate) Helpers::SetModifierKeyEvents(std::get(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first); // Set old shortcut key down state Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, std::get(it->second.targetShortcut)); // Reset the remap state it->second.isShortcutInvoked = false; it->second.winKeyInvoked = ModifierKey::Disabled; it->second.isOriginalActionKeyPressed = false; // If app specific shortcut has finished invoking, reset the target application if (activatedApp) { state.SetActivatedApp(KeyboardManagerConstants::NoActivatedApp); } } else if (std::get(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED) { // If remapped to disable, do nothing and suppress the key event // Since the original shortcut's action key is released, set it to false it->second.isOriginalActionKeyPressed = false; return 1; } else { // Check if the keyboard state is clear apart from the target remap key (by creating a temp Shortcut object with the target key) bool isKeyboardStateClear = Shortcut(std::vector({ Helpers::FilterArtificialKeys(std::get(it->second.targetShortcut)) })).IsKeyboardStateClearExceptShortcut(ii); // If the keyboard state is clear, we release the target key but do not reset the remap state if (isKeyboardStateClear) { Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast(Helpers::FilterArtificialKeys(std::get(it->second.targetShortcut))), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); } else { // If any other key is pressed, then the keyboard state must be reverted back to the physical keys. // This is to take cases like Ctrl+A->D remap and user presses B+Ctrl+A and releases A, or Ctrl+A+B and releases A // Release new key state Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast(Helpers::FilterArtificialKeys(std::get(it->second.targetShortcut))), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); if (!isAltRightKeyInvoked) { // Set original shortcut key down state except the action key Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); } // Send a dummy key event to prevent modifier press+release from being triggered. Example: Win+A->V, press Shift+Win+A and release A, since Win will be pressed here we need to send a dummy event after it Helpers::SetDummyKeyEvent(keyEventList, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); if (!isAltRightKeyInvoked) { // Reset the remap state it->second.isShortcutInvoked = false; it->second.winKeyInvoked = ModifierKey::Disabled; it->second.isOriginalActionKeyPressed = false; } // If app specific shortcut has finished invoking, reset the target application if (activatedApp != KeyboardManagerConstants::NoActivatedApp) { state.SetActivatedApp(KeyboardManagerConstants::NoActivatedApp); } } } ii.SendVirtualInput(keyEventList); return 1; } // Case 4: If a modifier key in the original shortcut is pressed then suppress that key event since the original shortcut is already held down physically - This case can occur only if a user has a duplicated modifier key (possibly by remapping) or if user presses both L/R versions of a modifier remapped with "Both" if ((it->first.CheckWinKey(data->lParam->vkCode) || it->first.CheckCtrlKey(data->lParam->vkCode) || it->first.CheckAltKey(data->lParam->vkCode) || it->first.CheckShiftKey(data->lParam->vkCode)) && (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN)) { if (remapToShortcut) { // Modifier state reset might be required for this key depending on the target shortcut action key - ex: Ctrl+A -> Win+Caps if (std::get(it->second.targetShortcut).GetCtrlKey() == NULL && std::get(it->second.targetShortcut).GetAltKey() == NULL && std::get(it->second.targetShortcut).GetShiftKey() == NULL) { ResetIfModifierKeyForLowerLevelKeyHandlers(ii, data->lParam->vkCode, std::get(it->second.targetShortcut).GetActionKey()); } } else if (std::get(it->second.targetShortcut) != CommonSharedConstants::VK_DISABLED) { // If it is not remapped to Disable // Modifier state reset might be required for this key depending on the target key - ex: Ctrl+A -> Caps ResetIfModifierKeyForLowerLevelKeyHandlers(ii, data->lParam->vkCode, Helpers::FilterArtificialKeys(std::get(it->second.targetShortcut))); } // Suppress the modifier as it is already physically pressed return 1; } // Case 5: If any key apart from the action key or a modifier key in the original shortcut is pressed then revert the keyboard state to just the original modifiers being held down along with the current key press if (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN) { if (remapToShortcut) { // Modifier state reset might be required for this key depending on the target shortcut action key - ex: Ctrl+A -> Win+Caps, Shift is pressed. System should not see Shift and Caps pressed together if (std::get(it->second.targetShortcut).GetCtrlKey() == NULL && std::get(it->second.targetShortcut).GetAltKey() == NULL && std::get(it->second.targetShortcut).GetShiftKey() == NULL) { ResetIfModifierKeyForLowerLevelKeyHandlers(ii, data->lParam->vkCode, std::get(it->second.targetShortcut).GetActionKey()); } std::vector keyEventList; // Check if a new remapping should be applied Shortcut currentlyPressed = it->first; currentlyPressed.actionKey = data->lParam->vkCode; auto newRemappingIter = reMap.find(currentlyPressed); if (newRemappingIter != reMap.end() && !newRemappingIter->first.HasChord()) { auto& newRemapping = newRemappingIter->second; Shortcut from = std::get(it->second.targetShortcut); if (newRemapping.RemapToKey()) { DWORD to = std::get<0>(newRemapping.targetShortcut); if (!isAltRightKeyInvoked) { Helpers::SetModifierKeyEvents(from, it->second.winKeyInvoked, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); } if (ii.GetVirtualKeyState(static_cast(from.actionKey))) { // If the action key from the last shortcut is still being pressed, release it. Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast(from.actionKey), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); } Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast(to), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); } else { Shortcut to = std::get(newRemapping.targetShortcut); if (!isAltRightKeyInvoked) { Helpers::SetModifierKeyEvents(from, it->second.winKeyInvoked, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, to); } if (ii.GetVirtualKeyState(static_cast(from.actionKey))) { // If the action key from the last shortcut is still being pressed, release it. Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast(from.actionKey), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); } if (!isAltRightKeyInvoked) { Helpers::SetModifierKeyEvents(to, it->second.winKeyInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, from); } Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast(to.actionKey), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); newRemapping.isShortcutInvoked = true; } // Remember which win key was pressed initially if (ii.GetVirtualKeyState(VK_RWIN)) { newRemapping.winKeyInvoked = ModifierKey::Right; } else if (ii.GetVirtualKeyState(VK_LWIN)) { newRemapping.winKeyInvoked = ModifierKey::Left; } } else { // If the target shortcut's action key is pressed, then it should be released and original shortcut's action key should be set bool isActionKeyPressed = ii.GetVirtualKeyState(std::get(it->second.targetShortcut).GetActionKey()); // Release new shortcut state (release in reverse order of shortcut to be accurate) if (isActionKeyPressed) { Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast(std::get(it->second.targetShortcut).GetActionKey()), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); } if (!isAltRightKeyInvoked) { Helpers::SetModifierKeyEvents(std::get(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first); // Set old shortcut key down state Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, std::get(it->second.targetShortcut)); } // key down for original shortcut action key with shortcut flag so that we don't invoke the same shortcut remap again if (isActionKeyPressed) { Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast(it->first.GetActionKey()), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); } // Send current key pressed without shortcut flag so that it can be reprocessed in case the physical keys pressed are a different remapped shortcut Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast(data->lParam->vkCode), 0, 0); // Do not send a dummy key as we want the current key press to behave as normal i.e. it can do press+release functionality if required. Required to allow a shortcut to Win key remap invoked directly after shortcut to shortcut is released to open start menu } if (!isAltRightKeyInvoked) { // Reset the remap state it->second.isShortcutInvoked = false; it->second.winKeyInvoked = ModifierKey::Disabled; it->second.isOriginalActionKeyPressed = false; } // If app specific shortcut has finished invoking, reset the target application if (activatedApp) { state.SetActivatedApp(KeyboardManagerConstants::NoActivatedApp); } ii.SendVirtualInput(keyEventList); return 1; } else { // For remap to key, if the original action key is not currently pressed, we should revert the keyboard state to the physical keys. If it is pressed we should not suppress the event so that shortcut to key remaps can be pressed with other keys. Example use-case: Alt+D->Win, allows Alt+D+A to perform Win+A // Modifier state reset might be required for this key depending on the target key - ex: Ctrl+A -> Caps, Shift is pressed. System should not see Shift and Caps pressed together auto maybeTargetKey = std::get_if(&it->second.targetShortcut); if (maybeTargetKey) { ResetIfModifierKeyForLowerLevelKeyHandlers(ii, data->lParam->vkCode, Helpers::FilterArtificialKeys(*maybeTargetKey)); } // If the shortcut is remapped to Disable then we have to revert the keyboard state to the physical keys bool isRemapToDisable = maybeTargetKey && (*maybeTargetKey == CommonSharedConstants::VK_DISABLED); bool isOriginalActionKeyPressed = false; if (maybeTargetKey && !isRemapToDisable) { // If the remap target key is currently pressed, then we do not have to revert the keyboard state to the physical keys if (ii.GetVirtualKeyState((Helpers::FilterArtificialKeys(*maybeTargetKey)))) { isOriginalActionKeyPressed = true; } } else { isOriginalActionKeyPressed = it->second.isOriginalActionKeyPressed; } if (isRemapToDisable || !isOriginalActionKeyPressed) { std::vector keyEventList; if (!isAltRightKeyInvoked) { // Set original shortcut key down state Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); } // Send the original action key only if it is physically pressed. For remappings to keys other than disabled we already check earlier that it is not pressed in this scenario. For remap to disable if (isRemapToDisable && isOriginalActionKeyPressed) { // Set original action key Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast(it->first.GetActionKey()), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); } // Send current key pressed without shortcut flag so that it can be reprocessed in case the physical keys pressed are a different remapped shortcut Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast(data->lParam->vkCode), 0, 0); // Do not send a dummy key as we want the current key press to behave as normal i.e. it can do press+release functionality if required. Required to allow a shortcut to Win key remap invoked directly after another shortcut to key remap is released to open start menu if (!isAltRightKeyInvoked) { // Reset the remap state it->second.isShortcutInvoked = false; it->second.winKeyInvoked = ModifierKey::Disabled; it->second.isOriginalActionKeyPressed = false; } // If app specific shortcut has finished invoking, reset the target application if (activatedApp != KeyboardManagerConstants::NoActivatedApp) { state.SetActivatedApp(KeyboardManagerConstants::NoActivatedApp); } ii.SendVirtualInput(keyEventList); return 1; } else { return 0; } } } // Case 6: If any key apart from original modifier or original action key is released - This can't happen since the key down would have to happen first, which is handled above. If a key up message is generated for some other key (maybe by code) do not suppress it } } } return 0; } std::wstring URL_encode(const std::wstring& filepath) { std::wostringstream escaped; escaped.fill('0'); escaped << std::hex; for (wchar_t ch : filepath) { // Encode special characters except for colon after drive letter if (!iswalnum(ch) && ch != L'-' && ch != L'_' && ch != L'.' && ch != L'~' && !(ch == L':' && std::isalpha(filepath[0]))) { escaped << std::uppercase; //escaped << '%' << std::setw(2) << int((unsigned char)ch); escaped << '%' << std::setw(2) << static_cast((static_cast(ch))); escaped << std::nouppercase; } else { escaped << ch; } } return escaped.str(); } std::wstring ConvertPathToURI(const std::wstring& filePath) { std::wstring fileUri = std::filesystem::absolute(filePath).wstring(); std::replace(fileUri.begin(), fileUri.end(), L'\\', L'/'); fileUri = L"file:///" + URL_encode(fileUri); return fileUri; } void ResetAllOtherStartedChords(State& state, const std::optional& activatedApp, DWORD keyToKeep) { for (auto& itShortcut_2 : state.GetSortedShortcutRemapVector(activatedApp)) { if (keyToKeep == NULL || itShortcut_2.actionKey != keyToKeep) { itShortcut_2.SetChordStarted(false); } } } void ResetAllStartedChords(State& state, const std::optional& activatedApp) { ResetAllOtherStartedChords(state, activatedApp, NULL); } ResetChordsResults ResetChordsIfNeeded(LowlevelKeyboardEvent* data, State& state, const std::optional& activatedApp) { ResetChordsResults result; result.AnyChordStarted = false; result.CurrentKeyIsModifierKey = false; bool isNewControlKey = false; bool anyChordStarted = false; if (VK_LWIN == data->lParam->vkCode || VK_RWIN == data->lParam->vkCode) { isNewControlKey = true; } if (VK_LSHIFT == data->lParam->vkCode || VK_RSHIFT == data->lParam->vkCode) { isNewControlKey = true; } if (VK_LMENU == data->lParam->vkCode || VK_RMENU == data->lParam->vkCode) { isNewControlKey = true; } if (VK_LCONTROL == data->lParam->vkCode || VK_RCONTROL == data->lParam->vkCode) { isNewControlKey = true; } if (isNewControlKey) { //Logger::trace(L"ChordKeyboardHandler:reset"); for (auto& itShortcut : state.GetSortedShortcutRemapVector(activatedApp)) { itShortcut.SetChordStarted(false); } result.CurrentKeyIsModifierKey = true; } else { for (auto& itShortcut : state.GetSortedShortcutRemapVector(activatedApp)) { if (itShortcut.IsChordStarted()) { result.AnyChordStarted = true; break; } } } return result; } struct handle_data { unsigned long process_id; HWND window_handle; }; // used for reactivating a window for a program we already started. HWND FindMainWindow(unsigned long process_id, const bool allowNonVisible) { handle_data data; data.process_id = process_id; data.window_handle = 0; if (allowNonVisible) { EnumWindows(EnumWindowsCallbackAllowNonVisible, reinterpret_cast(&data)); } else { EnumWindows(EnumWindowsCallback, reinterpret_cast(&data)); } return data.window_handle; } // used by FindMainWindow BOOL CALLBACK EnumWindowsCallbackAllowNonVisible(HWND handle, LPARAM lParam) { handle_data& data = *reinterpret_cast(lParam); unsigned long process_id = 0; GetWindowThreadProcessId(handle, &process_id); if (data.process_id == process_id) { data.window_handle = handle; return FALSE; } return TRUE; } // used by FindMainWindow BOOL CALLBACK EnumWindowsCallback(HWND handle, LPARAM lParam) { handle_data& data = *reinterpret_cast(lParam); unsigned long process_id = 0; GetWindowThreadProcessId(handle, &process_id); if (data.process_id != process_id || !(GetWindow(handle, GW_OWNER) == static_cast(0) && IsWindowVisible(handle))) { return TRUE; } data.window_handle = handle; return FALSE; } // GetProcessIdByName also used by HandleCreateProcessHotKeysAndChords std::vector GetProcessesIdByName(const std::wstring& processName) { std::vector processIds; HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (snapshot != INVALID_HANDLE_VALUE) { PROCESSENTRY32 processEntry; processEntry.dwSize = sizeof(PROCESSENTRY32); if (Process32First(snapshot, &processEntry)) { do { if (_wcsicmp(processEntry.szExeFile, processName.c_str()) == 0) { processIds.push_back(processEntry.th32ProcessID); } } while (Process32Next(snapshot, &processEntry)); } CloseHandle(snapshot); } return processIds; } DWORD GetProcessIdByName(const std::wstring& processName) { DWORD pid = 0; HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (snapshot != INVALID_HANDLE_VALUE) { PROCESSENTRY32 processEntry; processEntry.dwSize = sizeof(PROCESSENTRY32); if (Process32First(snapshot, &processEntry)) { do { if (_wcsicmp(processEntry.szExeFile, processName.c_str()) == 0) { pid = processEntry.th32ProcessID; break; } } while (Process32Next(snapshot, &processEntry)); } CloseHandle(snapshot); } return pid; } // Use to find a process by its name std::wstring GetFileNameFromPath(const std::wstring& fullPath) { size_t found = fullPath.find_last_of(L"\\"); if (found != std::wstring::npos) { return fullPath.substr(found + 1); } return fullPath; } void toast(param::hstring const& message1, param::hstring const& message2) noexcept { try { // Alternatively can build DOM from code: XmlDocument toastXml; XmlElement toastElement = toastXml.CreateElement(L"toast"); XmlElement visualElement = toastXml.CreateElement(L"visual"); XmlElement bindingElement = toastXml.CreateElement(L"binding"); XmlElement textElement1 = toastXml.CreateElement(L"text"); XmlElement textElement2 = toastXml.CreateElement(L"text"); toastXml.AppendChild(toastElement); toastElement.AppendChild(visualElement); visualElement.AppendChild(bindingElement); bindingElement.AppendChild(textElement1); bindingElement.AppendChild(textElement2); bindingElement.SetAttribute(L"template", L"ToastGeneric"); textElement1.InnerText(message1); textElement2.InnerText(message2); Logger::trace(L"ChordKeyboardHandler:toastXml {}", toastXml.GetXml()); std::wstring APPLICATION_ID = L"Microsoft.PowerToysWin32"; const auto notifier = ToastNotificationManager::ToastNotificationManager::CreateToastNotifier(APPLICATION_ID); ToastNotification notification{ toastXml }; notifier.Show(notification); } catch (...) { } /*std::thread{ [message] { } }.detach();*/ } void CreateOrShowProcessForShortcut(Shortcut shortcut) noexcept { WCHAR fullExpandedFilePath[MAX_PATH]; DWORD result = ExpandEnvironmentStrings(shortcut.runProgramFilePath.c_str(), fullExpandedFilePath, MAX_PATH); auto fileNamePart = GetFileNameFromPath(fullExpandedFilePath); Logger::trace(L"ChordKeyboardHandler:{}, trying to run {}", fileNamePart, fullExpandedFilePath); //lastKeyInChord = 0; DWORD targetPid = GetProcessIdByName(fileNamePart); /*if (fileNamePart != L"explorer.exe" && fileNamePart != L"powershell.exe" && fileNamePart != L"cmd.exe" && fileNamePart != L"msedge.exe") { targetPid = GetProcessIdByName(fileNamePart); }*/ Logger::trace(L"ChordKeyboardHandler:{}, already running, pid:{}, alreadyRunningAction:{}", fileNamePart, targetPid, shortcut.alreadyRunningAction); if (targetPid != 0 && shortcut.alreadyRunningAction != Shortcut::ProgramAlreadyRunningAction::StartAnother) { if (shortcut.alreadyRunningAction == Shortcut::ProgramAlreadyRunningAction::EndTask) { TerminateProcessesByName(fileNamePart); return; } else if (shortcut.alreadyRunningAction == Shortcut::ProgramAlreadyRunningAction::Close) { CloseProcessByName(fileNamePart); Logger::trace(L"ChordKeyboardHandler:{}, CloseProcessByName returning 3", fileNamePart); return; } else if (shortcut.alreadyRunningAction == Shortcut::ProgramAlreadyRunningAction::ShowWindow) { auto processIds = GetProcessesIdByName(fileNamePart); for (DWORD pid : processIds) { ShowProgram(targetPid, fileNamePart, false, false, 0); } //if (!ShowProgram(targetPid, fileNamePart, false, false, 0)) //{ // /*auto future = std::async(std::launch::async, [=] { // std::this_thread::sleep_for(std::chrono::milliseconds(30)); // Logger::trace(L"ChordKeyboardHandler:{}, second try, pid:{}", fileNamePart, targetPid); // ShowProgram(targetPid, fileNamePart, false, false); //});*/ //} return; } } else { DWORD dwAttrib = GetFileAttributesW(fullExpandedFilePath); if (dwAttrib == INVALID_FILE_ATTRIBUTES) { std::wstring title = fmt::format(L"无法运行 {}", fileNamePart); std::wstring message = fmt::format(L"找不到该程序。"); toast(title, message); return; } std::wstring expandedArgs; DWORD dwSize = ExpandEnvironmentStrings(shortcut.runProgramArgs.c_str(), nullptr, 0); expandedArgs.resize(dwSize); DWORD result = ExpandEnvironmentStrings(shortcut.runProgramArgs.c_str(), expandedArgs.data(), dwSize); WCHAR currentDir[MAX_PATH]; WCHAR* currentDirPtr = currentDir; result = ExpandEnvironmentStrings(shortcut.runProgramStartInDir.c_str(), currentDir, MAX_PATH); if (shortcut.runProgramStartInDir == L"") { currentDirPtr = nullptr; } else { DWORD dwAttrib = GetFileAttributesW(currentDir); if (dwAttrib == INVALID_FILE_ATTRIBUTES) { std::wstring title = fmt::format(L"无法运行 {}", fileNamePart); std::wstring message = fmt::format(L"起始目录无效。", currentDir); currentDirPtr = nullptr; toast(title, message); return; } } DWORD processId = 0; HANDLE newProcessHandle; if (shortcut.elevationLevel == Shortcut::ElevationLevel::Elevated) { newProcessHandle = run_elevated(fullExpandedFilePath, expandedArgs, currentDirPtr, (shortcut.startWindowType == Shortcut::StartWindowType::Normal)); processId = GetProcessId(newProcessHandle); } else if (shortcut.elevationLevel == Shortcut::ElevationLevel::NonElevated) { run_non_elevated(fullExpandedFilePath, expandedArgs, &processId, currentDirPtr, (shortcut.startWindowType == Shortcut::StartWindowType::Normal)); } else if (shortcut.elevationLevel == Shortcut::ElevationLevel::DifferentUser) { newProcessHandle = run_as_different_user(fullExpandedFilePath, expandedArgs, currentDirPtr, (shortcut.startWindowType == Shortcut::StartWindowType::Normal)); processId = GetProcessId(newProcessHandle); } if (processId == 0) { std::wstring title = fmt::format(L"无法运行 {}", fileNamePart); std::wstring message = fmt::format(L"程序可能启动失败了。"); toast(title, message); return; } if (shortcut.startWindowType == Shortcut::StartWindowType::Hidden) { HideProgram(processId, fileNamePart, 0); } //ShowProgram(processId, fileNamePart, true, false, (shortcut.startWindowType == Shortcut::StartWindowType::Hidden), 0); } return; } void CloseProcessByName(const std::wstring& fileNamePart) { auto processIds = GetProcessesIdByName(fileNamePart); if (processIds.size() == 0) { Logger::trace(L"ChordKeyboardHandler:{}, Nothing To WM_CLOSE", fileNamePart); return; } auto threadFunction = [fileNamePart]() { auto processIds = GetProcessesIdByName(fileNamePart); auto retryCount = 10; while (processIds.size() > 0 && retryCount-- > 0) { //Logger::trace(L"ChordKeyboardHandler:{}, WM_CLOSE 'ing {}processIds ", fileNamePart, processIds.size()); for (DWORD pid : processIds) { //Logger::trace(L"ChordKeyboardHandler:{}, WM_CLOSE ({}) -> pid:{}", fileNamePart, retryCount, pid); HWND hwnd = FindMainWindow(pid, false); SendMessage(hwnd, WM_CLOSE, 0, 0); // small sleep between when there are a lot might help Sleep(10); } processIds = GetProcessesIdByName(fileNamePart); if (processIds.size() <= 0) { Logger::trace(L"ChordKeyboardHandler:{}, WM_CLOSE done", fileNamePart); break; } else { Sleep(100); } } }; processIds = GetProcessesIdByName(fileNamePart); if (processIds.size() > 0) { std::thread myThread(threadFunction); if (myThread.joinable()) { myThread.detach(); } } Logger::trace(L"ChordKeyboardHandler:{}, CloseProcessByName returning", fileNamePart); } void TerminateProcessesByName(const std::wstring& fileNamePart) { auto processIds = GetProcessesIdByName(fileNamePart); if (processIds.size() == 0) { Logger::trace(L"ChordKeyboardHandler:{}, Nothing To PROCESS_TERMINATE", fileNamePart); return; } for (DWORD pid : processIds) { HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid); Logger::trace(L"ChordKeyboardHandler:{}, PROCESS_TERMINATE (1) -> pid:{}", fileNamePart, pid); if (hProcess != NULL) { if (!TerminateProcess(hProcess, 0)) { CloseHandle(hProcess); } else { CloseHandle(hProcess); } } } } bool HideProgram(DWORD pid, std::wstring programName, int retryCount) { Logger::trace(L"ChordKeyboardHandler:HideProgram starting with {},{}, retryCount:{}", pid, programName, retryCount); HWND hwnd = FindMainWindow(pid, false); if (hwnd == NULL) { if (retryCount < 20) { Logger::trace(L"ChordKeyboardHandler:hwnd not found will retry for pid:{}", pid); auto future = std::async(std::launch::async, [=] { std::this_thread::sleep_for(std::chrono::milliseconds(50)); auto result = HideProgram(pid, programName, retryCount + 1); return false; }); } } hwnd = FindWindow(nullptr, nullptr); auto anyHideResultFailed = false; Logger::trace(L"ChordKeyboardHandler:{}:{},{}, FindWindow, HideProgram (all)", programName, pid, retryCount); while (hwnd) { DWORD pidForHwnd; GetWindowThreadProcessId(hwnd, &pidForHwnd); if (pid == pidForHwnd) { if (IsWindowVisible(hwnd)) { ShowWindow(hwnd, SW_HIDE); Logger::trace(L"ChordKeyboardHandler:{}, tryToHide {}, {}", programName, reinterpret_cast(hwnd), anyHideResultFailed); } } hwnd = FindWindowEx(NULL, hwnd, NULL, NULL); } return true; } bool ShowProgram(DWORD pid, std::wstring programName, bool isNewProcess, bool minimizeIfVisible, int retryCount) { Logger::trace(L"ChordKeyboardHandler:ShowProgram starting with {},{},isNewProcess:{}, tryToHide:{} retryCount:{}", pid, programName, isNewProcess, retryCount); // a good place to look for this... // https://github.com/ritchielawrence/cmdow // try by main window. auto allowNonVisible = false; HWND hwnd = FindMainWindow(pid, allowNonVisible); if (hwnd == NULL) { if (retryCount < 20) { Logger::trace(L"ChordKeyboardHandler:hwnd not found will retry for pid:{}, allowNonVisible:{}", pid, allowNonVisible); auto future = std::async(std::launch::async, [=] { std::this_thread::sleep_for(std::chrono::milliseconds(50)); auto result = ShowProgram(pid, programName, isNewProcess, minimizeIfVisible, retryCount + 1); return false; }); } } else { Logger::trace(L"ChordKeyboardHandler:{}, got hwnd from FindMainWindow", programName); if (hwnd == GetForegroundWindow()) { // only hide if this was a call from a already open program, don't make small if we just opened it. if (!isNewProcess && minimizeIfVisible) { Logger::trace(L"ChordKeyboardHandler:{}, got GetForegroundWindow, doing SW_MINIMIZE", programName); return ShowWindow(hwnd, SW_MINIMIZE); } return false; } else { Logger::trace(L"ChordKeyboardHandler:{}, not ForegroundWindow, doing SW_RESTORE", programName); // Check if the window is minimized if (IsIconic(hwnd)) { // Show the window since SetForegroundWindow fails on minimized windows if (!ShowWindow(hwnd, SW_RESTORE)) { Logger::error(L"ShowWindow failed"); } } INPUT inputs[1] = { { .type = INPUT_MOUSE } }; SendInput(ARRAYSIZE(inputs), inputs, sizeof(INPUT)); if (!SetForegroundWindow(hwnd)) { auto errorCode = GetLastError(); Logger::warn(L"ChordKeyboardHandler:{}, failed to SetForegroundWindow, {}", programName, errorCode); return false; } else { Logger::trace(L"ChordKeyboardHandler:{}, success on SetForegroundWindow", programName); return true; } } } if (isNewProcess) { return true; } if (false) { // try by console. hwnd = FindWindow(nullptr, nullptr); if (AttachConsole(pid)) { Logger::trace(L"ChordKeyboardHandler:{}, success on AttachConsole", programName); // Get the console window handle hwnd = GetConsoleWindow(); auto showByConsoleSuccess = false; if (hwnd != NULL) { Logger::trace(L"ChordKeyboardHandler:{}, success on GetConsoleWindow, doing SW_RESTORE", programName); ShowWindow(hwnd, SW_RESTORE); if (!SetForegroundWindow(hwnd)) { auto errorCode = GetLastError(); Logger::warn(L"ChordKeyboardHandler:{}, failed to SetForegroundWindow, {}", programName, errorCode); } else { Logger::trace(L"ChordKeyboardHandler:{}, success on SetForegroundWindow", programName); showByConsoleSuccess = true; } } // Detach from the console FreeConsole(); if (showByConsoleSuccess) { return true; } } } // try to just show them all (if they have a title)!. hwnd = FindWindow(nullptr, nullptr); auto anyHideResultFailed = false; if (hwnd) { Logger::trace(L"ChordKeyboardHandler:{}:{},{}, FindWindow (show all mode)", programName, pid, retryCount); while (hwnd) { DWORD pidForHwnd; GetWindowThreadProcessId(hwnd, &pidForHwnd); if (pid == pidForHwnd) { int length = GetWindowTextLength(hwnd); if (length > 0) { ShowWindow(hwnd, SW_RESTORE); // hwnd is the window handle with targetPid if (SetForegroundWindow(hwnd)) { Logger::trace(L"ChordKeyboardHandler:{}, success on SetForegroundWindow", programName); return true; } else { auto errorCode = GetLastError(); Logger::warn(L"ChordKeyboardHandler:{}, failed to SetForegroundWindow, {}", programName, errorCode); } } } hwnd = FindWindowEx(NULL, hwnd, NULL, NULL); } } return false; } // Function to a handle an os-level shortcut remap intptr_t HandleOSLevelShortcutRemapEvent(KeyboardManagerInput::InputInterface& ii, LowlevelKeyboardEvent* data, State& state) noexcept { // Check if the key event was generated by KeyboardManager to avoid remapping events generated by us. if (data->lParam->dwExtraInfo != KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG) { bool result = HandleShortcutRemapEvent(ii, data, state); return result; } return 0; } // Function to a handle an app-specific shortcut remap intptr_t HandleAppSpecificShortcutRemapEvent(KeyboardManagerInput::InputInterface& ii, LowlevelKeyboardEvent* data, State& state) noexcept { // Check if the key event was generated by KeyboardManager to avoid remapping events generated by us. if (data->lParam->dwExtraInfo != KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG) { std::wstring process_name; // Allocate MAX_PATH amount of memory process_name.resize(MAX_PATH); ii.GetForegroundProcess(process_name); // Remove elements after null character process_name.erase(std::find(process_name.begin(), process_name.end(), L'\0'), process_name.end()); if (process_name.empty()) { return 0; } // Convert process name to lower case std::transform(process_name.begin(), process_name.end(), process_name.begin(), towlower); std::wstring query_string; AppSpecificShortcutRemapTable::iterator it; // Check if an app-specific shortcut is already activated if (state.GetActivatedApp() == KeyboardManagerConstants::NoActivatedApp) { query_string = process_name; it = state.appSpecificShortcutReMap.find(query_string); // If no entry is found, search for the process name without its file extension if (it == state.appSpecificShortcutReMap.end()) { // Find index of the file extension size_t extensionIndex = process_name.find_last_of(L"."); query_string = process_name.substr(0, extensionIndex); it = state.appSpecificShortcutReMap.find(query_string); } } else { query_string = state.GetActivatedApp(); it = state.appSpecificShortcutReMap.find(query_string); } if (it != state.appSpecificShortcutReMap.end()) { bool result = HandleShortcutRemapEvent(ii, data, state, query_string); return result; } } return 0; } // Function to ensure Ctrl/Shift/Alt modifier key state is not detected as pressed down by applications which detect keys at a lower level than hooks when it is remapped for scenarios where its required void ResetIfModifierKeyForLowerLevelKeyHandlers(KeyboardManagerInput::InputInterface& ii, DWORD key, DWORD target) { // If the target is Caps Lock and the other key is either Ctrl/Alt/Shift then reset the modifier state to lower level handlers if (target == VK_CAPITAL) { // If the argument is either of the Ctrl/Shift/Alt modifier key codes if (Helpers::IsModifierKey(key) && !(key == VK_LWIN || key == VK_RWIN || key == CommonSharedConstants::VK_WIN_BOTH)) { std::vector keyEventList; // Use the suppress flag to ensure these are not intercepted by any remapped keys or shortcuts Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast(key), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG); ii.SendVirtualInput(keyEventList); } } } // Function to generate a unicode string in response to a single keypress intptr_t HandleSingleKeyToTextRemapEvent(KeyboardManagerInput::InputInterface& ii, LowlevelKeyboardEvent* data, State& state) { if (GeneratedByKBM(data)) { return 0; } // Only send the text on keydown event if (data->wParam != WM_KEYDOWN) { return 0; } const auto remapping = state.GetSingleKeyToTextRemapEvent(data->lParam->vkCode); if (!remapping) { return 0; } std::vector keyEventList; Helpers::SetTextKeyEvents(keyEventList, *remapping); ii.SendVirtualInput(keyEventList); return 1; } } ================================================ FILE: PowerToys/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/InputInterpreter.cs ================================================ // 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.Globalization; using System.Linq; using System.Text.RegularExpressions; using UnitsNet; using Wox.Plugin; namespace Community.PowerToys.Run.Plugin.UnitConverter { public static class InputInterpreter { private static readonly string Pattern = @"(?<=\d)(?![,.\-])(?=[\D])|(?<=[\D])(? /// Separates input like: "1ft in cm" to "1 ft in cm" /// public static void InputSpaceInserter(ref string[] split) { if (split.Length != 3) { return; } string[] parseInputWithoutSpace = Regex.Split(split[0], Pattern); if (parseInputWithoutSpace.Length > 1) { string[] firstEntryRemoved = split.Skip(1).ToArray(); string[] newSplit = new string[] { parseInputWithoutSpace[0], parseInputWithoutSpace[1] }; split = newSplit.Concat(firstEntryRemoved).ToArray(); } } /// /// Replaces a split input array with shorthand feet/inch notation (1', 1'2" etc) to 'x foot in cm'. /// public static void ShorthandFeetInchHandler(ref string[] split, CultureInfo culture) { if (!split[0].Contains('\'') && !split[0].Contains('\"')) { return; } // catches 1' || 1" || 1'2 || 1'2" in cm // by converting it to "x foot in cm" if (split.Length == 3) { string[] shortsplit = RegexSplitter(split); switch (shortsplit.Length) { case 2: // ex: 1' & 1" if (shortsplit[1] == "\'") { string[] newInput = new string[] { shortsplit[0], "foot", split[1], split[2] }; split = newInput; } else if (shortsplit[1] == "\"") { string[] newInput = new string[] { shortsplit[0], "inch", split[1], split[2] }; split = newInput; } break; case 3: case 4: // ex: 1'2 and 1'2" if (shortsplit[1] == "\'") { bool isNegative = shortsplit[0].StartsWith('-'); if (isNegative) { shortsplit[0] = shortsplit[0].Remove(0, 1); } bool isFeet = double.TryParse(shortsplit[0], NumberStyles.AllowDecimalPoint, culture, out double feet); bool isInches = double.TryParse(shortsplit[2], NumberStyles.AllowDecimalPoint, culture, out double inches); if (!isFeet || !isInches) { // at least one could not be parsed correctly break; } double convertedTotalInFeet = Length.FromFeetInches(feet, inches).Feet; if (isNegative) { convertedTotalInFeet *= -1; } string[] newInput = new string[] { convertedTotalInFeet.ToString(culture), "foot", split[1], split[2] }; split = newInput; } break; default: break; } } } /// /// Adds degree prefixes to degree units for shorthand notation. E.g. '10 c in fahrenheit' becomes '10 °C in DegreeFahrenheit'. /// public static void DegreePrefixer(ref string[] split) { switch (split[1].ToLower(CultureInfo.CurrentCulture)) { case "celsius": split[1] = "DegreeCelsius"; break; case "fahrenheit": split[1] = "DegreeFahrenheit"; break; case "c": split[1] = "°C"; break; case "f": split[1] = "°F"; break; default: break; } switch (split[3].ToLower(CultureInfo.CurrentCulture)) { case "celsius": split[3] = "DegreeCelsius"; break; case "fahrenheit": split[3] = "DegreeFahrenheit"; break; case "c": split[3] = "°C"; break; case "f": split[3] = "°F"; break; default: break; } #pragma warning disable SA1501 // Statement should not be on a single line // [Chinese Translator] // 前面的单位 // Acceleration 加速度 【自由,无互相冲突】(其他单位由长度、时间合成) split[1] = split[1].Replace("标准重力", "g"); // Energy 能量 在时间前(马力时 冲突 时),在质量前(磅力 冲突) split[1] = split[1].Replace("焦耳", "J").Replace("焦", "J").Replace("卡路里", "cal").Replace("卡", "cal").Replace("英国热力单位", "BTU").Replace("英热", "BTU").Replace("电子伏特", "eV").Replace("磅力", "ft·lb").Replace("尔格", "erg").Replace("马力时", "hp·h"); // Power 功率 【自由,无互相冲突】 split[1] = split[1].Replace("瓦特", "W").Replace("瓦", "W").Replace("机械马力", "hp(I)").Replace("公制马力", "hp(M)").Replace("电气马力", "hp(E)").Replace("锅炉马力", "hp(S)").Replace("液压马力", "hp(H)").Replace("马力", "hp(M)").Replace("匹", "hp(M)"); // Angle 角度 【自由,无互相冲突】 if (split[1] == "度") { split[1] = "deg"; } if (split[1] == "分") { split[1] = "min"; } if (split[1] == "秒") { split[1] = "sec"; } split[1] = split[1].Replace("弧度", "rad").Replace("百分度", "gradian").Replace("圈", "r").Replace("转", "r"); // Duration 时间 在角度后(分秒 分类讨论) split[1] = split[1].Replace("年", "yr").Replace("月", "mo").Replace("星期", "week").Replace("周", "week").Replace("日", "d").Replace("天", "d").Replace("小时", "h").Replace("时", "h").Replace("分钟", "min").Replace("分", "min").Replace("秒钟", "s").Replace("秒", "s"); // Temperature 温度 在体积前(标 修补器 冲突 温标),在角度后(度 冲突 度 修补器) if (split[1].Contains("温标")) { split[1] = "°" + split[1].Replace("温标", string.Empty); } if (split[1].Contains("温度")) { split[1] = "°" + split[1].Replace("温度", string.Empty); } if (split[1].Contains('度')) { split[1] = "°" + split[1].Replace("度", string.Empty); } split[1] = split[1].Replace("开尔文", "K").Replace("开", "K").Replace("°K", "K").Replace("摄尔修斯", "C").Replace("摄氏", "C").Replace("℃", "°C").Replace("德利尔", "De").Replace("德氏", "De").Replace("华伦海特", "F").Replace("华氏", "F").Replace("℉", "°F").Replace("牛顿", "N").Replace("牛", "N").Replace("兰金", "R").Replace("兰氏", "R").Replace("列奥缪尔", "Ré").Replace("列氏", "Ré").Replace("罗默", "Rø").Replace("罗氏", "Rø").Replace("氏", string.Empty).Replace("°太阳", "T⊙"); // Pressure 压强 在能量后(磅力 修补器 依赖),在能量、功率后(力 冲突 马力、马力时,帕斯卡 修补器 依赖 卡),在体积前(标准大气压 冲突 标 修补器)(质量 千克公斤吨+力 合成)(长度 毫米+汞柱 合成)(温度 牛+每+米+方 合成) split[1] = split[1].Replace("帕斯cal", "Pa").Replace("帕", "Pa").Replace("标准大气压", "atm").Replace("工程大气压", "at").Replace("大气压", "atm").Replace("巴", "bar").Replace("千ft·lb每", "kipf/").Replace("ft·lb每", "lb/").Replace("力", "f").Replace("达因", "dyn").Replace("托", "torr").Replace("汞柱", "Hg").Replace("英寸水柱", "wc"); if (split[1].Contains("扬程")) { split[1] = split[1].Replace("扬程", string.Empty) + " of head"; } // Volume 体积 在面积、长度前(英亩英尺 板英尺 冲突) split[1] = split[1].Replace("公升", "L").Replace("立升", "L").Replace("升", "L").Replace("勺", "匙").Replace("式", "制").Replace("标", "制").Replace("英液加仑", "ImperialGallon").Replace("英液盎司", "ImperialOunce").Replace("美液加仑", "UsGallon").Replace("美液盎司", "UsOunce").Replace("美制汤匙", "UsTablespoon").Replace("澳制汤匙", "AuTablespoon").Replace("英制汤匙", "UkTablespoon").Replace("公制茶匙", "MetricTeaspoon").Replace("美制茶匙", "UsTeaspoon").Replace("公制杯", "MetricCup").Replace("美制市杯", "UsCustomaryCup").Replace("美制法杯", "UsLegalCup").Replace("桶油当量", "bbl").Replace("美制酒桶", "UsBeerBarrel").Replace("英制酒桶", "ImperialBeerBarrel").Replace("美制夸脱", "UsQuart").Replace("美制品脱", "UsPint").Replace("英亩英尺", "ac-ft").Replace("英制品脱", "pt").Replace("品脱", "pt").Replace("板英尺", "bf"); // Area 面积 在长度前(平方海里 冲突)(其他单位由长度合成) split[1] = split[1].Replace("英亩", "ac").Replace("公顷", "ha").Replace("平方海里", "nmi²"); // Length 长度 在时间后(天文单位 修补器 依赖 天) split[1] = split[1].Replace("米", "m").Replace("公里", "km").Replace("公尺", "m").Replace("公寸", "dm").Replace("公分", "cm").Replace("公厘", "mm").Replace("英里", "mi").Replace("码", "yd").Replace("英尺", "ft").Replace("英寸", "in").Replace("密耳", "mil").Replace("海里", "NM").Replace("英寻", "fathom").Replace("缆索", "shackle").Replace("缆", "shackle").Replace("缇", "twip").Replace("掌", "hand").Replace("d文单位", "au").Replace("派卡", "pc").Replace("光年", "ly").Replace("太阳半径", "R⊙").Replace("链", "ch"); // Mass 质量 自由 split[1] = split[1].Replace("克", "g").Replace("公斤", "kg").Replace("短吨", "short tn").Replace("长吨", "long tn").Replace("吨", "t").Replace("磅", "lb").Replace("盎司", "oz").Replace("斯勒格", "slug").Replace("石", "st").Replace("格令", "gr").Replace("太阳质量", "M⊙").Replace("地球质量", "em"); // Information 数据 在速度前(节 冲突 字节),在能量、长度后(xx单位 冲突 位) split[1] = split[1].Replace("字节", "B").Replace("位", "b").Replace("比特", "b"); // Speed 速度 在长度、时间后(修补器 处理依赖),在数据后(节 冲突 字节) split[1] = split[1].Replace("节", "kn").Replace("mi每h", "mph").Replace("mi/h", "mph"); // 通用 最后 if (split[1].Contains("平方")) { split[1] = split[1].Replace("平方", string.Empty) + "²"; } if (split[1].Contains("二次方")) { split[1] = split[1].Replace("二次方", string.Empty) + "²"; } if (split[1].Contains("立方")) { split[1] = split[1].Replace("立方", string.Empty) + "³"; } if (split[1].Contains("三次方")) { split[1] = split[1].Replace("三次方", string.Empty) + "³"; } if (split[1].Contains('方')) { split[1] = split[1].Replace("方", string.Empty) + "²"; } split[1] = split[1].Replace("艾", "E").Replace("拍", "P").Replace("太", "T").Replace("吉", "G").Replace("兆", "M").Replace("千", "k").Replace("百", "h").Replace("分", "d").Replace("厘", "c").Replace("毫", "m").Replace("微", "μ").Replace("纳", "n"); split[1] = split[1].Replace("的", string.Empty).Replace("个", string.Empty).Replace("每", "/"); // 后面的单位 // Acceleration 加速度 【自由,无互相冲突】(其他单位由长度、时间合成) split[3] = split[3].Replace("标准重力", "g"); // Energy 能量 在时间前(马力时 冲突 时),在质量前(磅力 冲突) split[3] = split[3].Replace("焦耳", "J").Replace("焦", "J").Replace("卡路里", "cal").Replace("卡", "cal").Replace("英国热力单位", "BTU").Replace("英热", "BTU").Replace("电子伏特", "eV").Replace("磅力", "ft·lb").Replace("尔格", "erg").Replace("马力时", "hp·h"); // Power 功率 【自由,无互相冲突】 split[3] = split[3].Replace("瓦特", "W").Replace("瓦", "W").Replace("机械马力", "hp(I)").Replace("公制马力", "hp(M)").Replace("电气马力", "hp(E)").Replace("锅炉马力", "hp(S)").Replace("液压马力", "hp(H)").Replace("马力", "hp(M)").Replace("匹", "hp(M)"); // Angle 角度 【自由,无互相冲突】 if (split[3] == "度") { split[3] = "deg"; } if (split[3] == "分") { split[3] = "min"; } if (split[3] == "秒") { split[3] = "sec"; } split[3] = split[3].Replace("弧度", "rad").Replace("百分度", "gradian").Replace("圈", "r").Replace("转", "r"); // Duration 时间 在角度后(分秒 分类讨论) split[3] = split[3].Replace("年", "yr").Replace("月", "mo").Replace("星期", "week").Replace("周", "week").Replace("日", "d").Replace("天", "d").Replace("小时", "h").Replace("时", "h").Replace("分钟", "min").Replace("分", "min").Replace("秒钟", "s").Replace("秒", "s"); // Temperature 温度 在体积前(标 修补器 冲突 温标),在角度后(度 冲突 度 修补器) if (split[3].Contains("温标")) { split[3] = "°" + split[3].Replace("温标", string.Empty); } if (split[3].Contains("温度")) { split[3] = "°" + split[3].Replace("温度", string.Empty); } if (split[3].Contains('度')) { split[3] = "°" + split[3].Replace("度", string.Empty); } split[3] = split[3].Replace("开尔文", "K").Replace("开", "K").Replace("°K", "K").Replace("摄尔修斯", "C").Replace("摄氏", "C").Replace("℃", "°C").Replace("德利尔", "De").Replace("德氏", "De").Replace("华伦海特", "F").Replace("华氏", "F").Replace("℉", "°F").Replace("牛顿", "N").Replace("牛", "N").Replace("兰金", "R").Replace("兰氏", "R").Replace("列奥缪尔", "Ré").Replace("列氏", "Ré").Replace("罗默", "Rø").Replace("罗氏", "Rø").Replace("氏", string.Empty).Replace("°太阳", "T⊙"); // Pressure 压强 在能量后(磅力 修补器 依赖),在能量、功率后(力 冲突 马力、马力时,帕斯卡 修补器 依赖 卡),在体积前(标准大气压 冲突 标 修补器)(质量 千克公斤吨+力 合成)(长度 毫米+汞柱 合成)(温度 牛+每+米+方 合成) split[3] = split[3].Replace("帕斯cal", "Pa").Replace("帕", "Pa").Replace("标准大气压", "atm").Replace("工程大气压", "at").Replace("大气压", "atm").Replace("巴", "bar").Replace("千ft·lb每", "kipf/").Replace("ft·lb每", "lb/").Replace("力", "f").Replace("达因", "dyn").Replace("托", "torr").Replace("汞柱", "Hg").Replace("英寸水柱", "wc"); if (split[3].Contains("扬程")) { split[3] = split[3].Replace("扬程", string.Empty) + " of head"; } // Volume 体积 在面积、长度前(英亩英尺 板英尺 冲突) split[3] = split[3].Replace("公升", "L").Replace("立升", "L").Replace("升", "L").Replace("勺", "匙").Replace("式", "制").Replace("标", "制").Replace("英液加仑", "ImperialGallon").Replace("英液盎司", "ImperialOunce").Replace("美液加仑", "UsGallon").Replace("美液盎司", "UsOunce").Replace("美制汤匙", "UsTablespoon").Replace("澳制汤匙", "AuTablespoon").Replace("英制汤匙", "UkTablespoon").Replace("公制茶匙", "MetricTeaspoon").Replace("美制茶匙", "UsTeaspoon").Replace("公制杯", "MetricCup").Replace("美制市杯", "UsCustomaryCup").Replace("美制法杯", "UsLegalCup").Replace("桶油当量", "bbl").Replace("美制酒桶", "UsBeerBarrel").Replace("英制酒桶", "ImperialBeerBarrel").Replace("美制夸脱", "UsQuart").Replace("美制品脱", "UsPint").Replace("英亩英尺", "ac-ft").Replace("英制品脱", "pt").Replace("品脱", "pt").Replace("板英尺", "bf"); // Area 面积 在长度前(平方海里 冲突)(其他单位由长度合成) split[3] = split[3].Replace("英亩", "ac").Replace("公顷", "ha").Replace("平方海里", "nmi²"); // Length 长度 在时间后(天文单位 修补器 依赖 天) split[3] = split[3].Replace("米", "m").Replace("公里", "km").Replace("公尺", "m").Replace("公寸", "dm").Replace("公分", "cm").Replace("公厘", "mm").Replace("英里", "mi").Replace("码", "yd").Replace("英尺", "ft").Replace("英寸", "in").Replace("密耳", "mil").Replace("海里", "NM").Replace("英寻", "fathom").Replace("缆索", "shackle").Replace("缆", "shackle").Replace("缇", "twip").Replace("掌", "hand").Replace("d文单位", "au").Replace("派卡", "pc").Replace("光年", "ly").Replace("太阳半径", "R⊙").Replace("链", "ch"); // Mass 质量 自由 split[3] = split[3].Replace("克", "g").Replace("公斤", "kg").Replace("短吨", "short tn").Replace("长吨", "long tn").Replace("吨", "t").Replace("磅", "lb").Replace("盎司", "oz").Replace("斯勒格", "slug").Replace("石", "st").Replace("格令", "gr").Replace("太阳质量", "M⊙").Replace("地球质量", "em"); // Information 数据 在速度前(节 冲突 字节),在能量、长度后(xx单位 冲突 位) split[3] = split[3].Replace("字节", "B").Replace("位", "b").Replace("比特", "b"); // Speed 速度 在长度、时间后(修补器 处理依赖),在数据后(节 冲突 字节) split[3] = split[3].Replace("节", "kn").Replace("mi每h", "mph").Replace("mi/h", "mph"); // 通用 最后 if (split[3].Contains("平方")) { split[3] = split[3].Replace("平方", string.Empty) + "²"; } if (split[3].Contains("二次方")) { split[3] = split[3].Replace("二次方", string.Empty) + "²"; } if (split[3].Contains("立方")) { split[3] = split[3].Replace("立方", string.Empty) + "³"; } if (split[3].Contains("三次方")) { split[3] = split[3].Replace("三次方", string.Empty) + "³"; } if (split[3].Contains('方')) { split[3] = split[3].Replace("方", string.Empty) + "²"; } split[3] = split[3].Replace("艾", "E").Replace("拍", "P").Replace("太", "T").Replace("吉", "G").Replace("兆", "M").Replace("千", "k").Replace("百", "h").Replace("分", "d").Replace("厘", "c").Replace("毫", "m").Replace("微", "μ").Replace("纳", "n"); split[3] = split[3].Replace("的", string.Empty).Replace("个", string.Empty).Replace("每", "/"); // [For Debug] // using System.Windows; // System.Diagnostics.ProcessStartInfo info = new System.Diagnostics.ProcessStartInfo("msg.exe"); // info.Arguments = "LittleFox /time:1 " + split[1] + " -> " + split[3]; // info.CreateNoWindow = true; // System.Diagnostics.Process proc; // proc = System.Diagnostics.Process.Start(info); } #pragma warning restore SA1501 // Statement should not be on a single line /// /// Converts spelling "kph" to "km/h" /// public static void KPHHandler(ref string[] split) { split[1] = split[1].Replace("cph", "cm/h", System.StringComparison.CurrentCultureIgnoreCase); split[1] = split[1].Replace("kph", "km/h", System.StringComparison.CurrentCultureIgnoreCase); split[1] = split[1].Replace("kmph", "km/h", System.StringComparison.CurrentCultureIgnoreCase); split[1] = split[1].Replace("cmph", "cm/h", System.StringComparison.CurrentCultureIgnoreCase); split[3] = split[3].Replace("cph", "cm/h", System.StringComparison.CurrentCultureIgnoreCase); split[3] = split[3].Replace("kph", "km/h", System.StringComparison.CurrentCultureIgnoreCase); split[3] = split[3].Replace("kmph", "km/h", System.StringComparison.CurrentCultureIgnoreCase); split[3] = split[3].Replace("cmph", "cm/h", System.StringComparison.CurrentCultureIgnoreCase); } /// /// Converts spelling "metre" to "meter", also for centimetre and other variants /// public static void MetreToMeter(ref string[] split) { split[1] = split[1].Replace("metre", "meter", System.StringComparison.CurrentCultureIgnoreCase); split[3] = split[3].Replace("metre", "meter", System.StringComparison.CurrentCultureIgnoreCase); } /// /// Choose "UsGallon" or "ImperialGallon" according to current culture when the input contains "gal" or "gallon". /// public static void GallonHandler(ref string[] split, CultureInfo culture) { HashSet britishCultureNames = new HashSet() { "en-AI", "en-VG", "en-GB", "en-KY", "en-MS", "en-AG", "en-DM", "en-GD", "en-KN", "en-LC", "en-VC", "en-IE", "en-GY", "en-AE" }; if (string.Equals(split[1], "gal", StringComparison.OrdinalIgnoreCase) || string.Equals(split[1], "gallon", StringComparison.OrdinalIgnoreCase)) { if (britishCultureNames.Contains(culture.Name)) { split[1] = "ImperialGallon"; } else { split[1] = "UsGallon"; } } if (string.Equals(split[3], "gal", StringComparison.OrdinalIgnoreCase) || string.Equals(split[3], "gallon", StringComparison.OrdinalIgnoreCase)) { if (britishCultureNames.Contains(culture.Name)) { split[3] = "ImperialGallon"; } else { split[3] = "UsGallon"; } } } /// /// Choose "UsOunce" or "ImperialOunce" according to current culture when the input contains "o.z", "o.z.", "oz" or "ounce". /// public static void OunceHandler(ref string[] split, CultureInfo culture) { HashSet britishCultureNames = new HashSet() { "en-AI", "en-VG", "en-GB", "en-KY", "en-MS", "en-AG", "en-DM", "en-GD", "en-KN", "en-LC", "en-VC", "en-IE", "en-GY", "en-AE" }; if (string.Equals(split[1], "o.z", StringComparison.OrdinalIgnoreCase) || string.Equals(split[1], "ounce", StringComparison.OrdinalIgnoreCase) || string.Equals(split[1], "o.z.", StringComparison.OrdinalIgnoreCase) || string.Equals(split[1], "oz", StringComparison.OrdinalIgnoreCase)) { if (britishCultureNames.Contains(culture.Name)) { split[1] = "ImperialOunce"; } else { split[1] = "UsOunce"; } } if (string.Equals(split[3], "o.z", StringComparison.OrdinalIgnoreCase) || string.Equals(split[3], "ounce", StringComparison.OrdinalIgnoreCase) || string.Equals(split[3], "o.z.", StringComparison.OrdinalIgnoreCase) || string.Equals(split[3], "oz", StringComparison.OrdinalIgnoreCase)) { if (britishCultureNames.Contains(culture.Name)) { split[3] = "ImperialOunce"; } else { split[3] = "UsOunce"; } } } public static ConvertModel Parse(Query query) { string[] split = query.Search.Split(' '); InputInterpreter.ShorthandFeetInchHandler(ref split, CultureInfo.CurrentCulture); InputInterpreter.InputSpaceInserter(ref split); if (split.Length != 4) { // deny any other queries than: // 10 ft in cm // 10 ft to cm return null; } InputInterpreter.DegreePrefixer(ref split); InputInterpreter.MetreToMeter(ref split); InputInterpreter.KPHHandler(ref split); InputInterpreter.GallonHandler(ref split, CultureInfo.CurrentCulture); InputInterpreter.OunceHandler(ref split, CultureInfo.CurrentCulture); if (!double.TryParse(split[0], out double value)) { return null; } return new ConvertModel() { Value = value, FromUnit = split[1], ToUnit = split[3], }; } } } ================================================ FILE: PowerToys/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Main.cs ================================================ // 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.Globalization; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Windows; using System.Windows.Input; using ManagedCommon; using Wox.Plugin; using Wox.Plugin.Logger; namespace Community.PowerToys.Run.Plugin.UnitConverter { public class Main : IPlugin, IPluginI18n, IContextMenu, IDisposable { public string Name => Properties.Resources.plugin_name; public string Description => Properties.Resources.plugin_description; public static string PluginID => "aa0ee9daff654fb7be452c2d77c471b9"; private PluginInitContext _context; private static string _icon_path; private bool _disposed; private static readonly CompositeFormat CopyToClipboard = System.Text.CompositeFormat.Parse(Properties.Resources.copy_to_clipboard); public void Init(PluginInitContext context) { ArgumentNullException.ThrowIfNull(context); _context = context; _context.API.ThemeChanged += OnThemeChanged; UpdateIconPath(_context.API.GetCurrentTheme()); } public List Query(Query query) { ArgumentNullException.ThrowIfNull(query); // Parse ConvertModel convertModel = InputInterpreter.Parse(query); if (convertModel == null) { return new List(); } return UnitHandler.Convert(convertModel) .Select(x => GetResult(x)) .ToList(); } [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1501:Statement should not be on a single line", Justification = "<挂起>")] private Result GetResult(UnitConversionResult result) { string hack = $"{result.QuantityInfo.Name}"; if (hack == "Acceleration") { hack = "加速度"; } else if (hack == "Angle") { hack = "角度"; } else if (hack == "Area") { hack = "面积"; } else if (hack == "Duration") { hack = "时间"; } else if (hack == "Energy") { hack = "能量"; } else if (hack == "Information") { hack = "数据"; } else if (hack == "Length") { hack = "长度"; } else if (hack == "Mass") { hack = "质量"; } else if (hack == "Power") { hack = "功率"; } else if (hack == "Pressure") { hack = "压强"; } else if (hack == "Speed") { hack = "速度"; } else if (hack == "Temperature") { hack = "温度"; } else if (hack == "Volume") { hack = "体积"; } return new Result { ContextData = result, Title = result.ToString(null), IcoPath = _icon_path, Score = 300, SubTitle = string.Format(CultureInfo.CurrentCulture, CopyToClipboard, result.QuantityInfo.Name), Action = c => { var ret = false; var thread = new Thread(() => { try { Clipboard.SetText(result.ConvertedValue.ToString(UnitConversionResult.CopyFormat, CultureInfo.CurrentCulture)); ret = true; } catch (ExternalException ex) { Log.Exception("Copy failed", ex, GetType()); MessageBox.Show(ex.Message, Properties.Resources.copy_failed); } }); thread.SetApartmentState(ApartmentState.STA); thread.Start(); thread.Join(); return ret; }, }; } private ContextMenuResult CreateContextMenuEntry(UnitConversionResult result) { return new ContextMenuResult { PluginName = Name, Title = Properties.Resources.context_menu_copy, Glyph = "\xE8C8", FontFamily = "Segoe Fluent Icons,Segoe MDL2 Assets", AcceleratorKey = Key.Enter, Action = _ => { bool ret = false; var thread = new Thread(() => { try { Clipboard.SetText(result.ConvertedValue.ToString(UnitConversionResult.CopyFormat, CultureInfo.CurrentCulture)); ret = true; } catch (ExternalException ex) { Log.Exception("Copy failed", ex, GetType()); MessageBox.Show(ex.Message, Properties.Resources.copy_failed); } }); thread.SetApartmentState(ApartmentState.STA); thread.Start(); thread.Join(); return ret; }, }; } public List LoadContextMenus(Result selectedResult) { if (!(selectedResult?.ContextData is UnitConversionResult)) { return new List(); } List contextResults = new List(); UnitConversionResult result = selectedResult.ContextData as UnitConversionResult; contextResults.Add(CreateContextMenuEntry(result)); return contextResults; } public string GetTranslatedPluginTitle() { return Properties.Resources.plugin_name; } public string GetTranslatedPluginDescription() { return Properties.Resources.plugin_description; } private void OnThemeChanged(Theme currentTheme, Theme newTheme) { UpdateIconPath(newTheme); } private static void UpdateIconPath(Theme theme) { if (theme == Theme.Light || theme == Theme.HighContrastWhite) { _icon_path = "Images/unitconverter.light.png"; } else { _icon_path = "Images/unitconverter.dark.png"; } } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!_disposed) { if (disposing) { if (_context != null && _context.API != null) { _context.API.ThemeChanged -= OnThemeChanged; } _disposed = true; } } } } } ================================================ FILE: PowerToys/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Properties/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 复制 (回车) 复制失败 复制转换结果 ({0}) 进行单位转换(例如 10厘米 -> 米) 单位转换 ================================================ FILE: PowerToys/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Main.cs ================================================ // Copyright (c) Microsoft Corporation // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Windows; using System.Windows.Input; using Community.PowerToys.Run.Plugin.VSCodeWorkspaces.Properties; using Community.PowerToys.Run.Plugin.VSCodeWorkspaces.RemoteMachinesHelper; using Community.PowerToys.Run.Plugin.VSCodeWorkspaces.VSCodeHelper; using Community.PowerToys.Run.Plugin.VSCodeWorkspaces.WorkspacesHelper; using Wox.Infrastructure; using Wox.Plugin; using Wox.Plugin.Logger; namespace Community.PowerToys.Run.Plugin.VSCodeWorkspaces { public class Main : IPlugin, IPluginI18n, IContextMenu { private PluginInitContext _context; public string Name => GetTranslatedPluginTitle(); public string Description => GetTranslatedPluginDescription(); public static string PluginID => "525995402BEF4A8CA860D92F6D108092"; public Main() { VSCodeInstances.LoadVSCodeInstances(); } private readonly VSCodeWorkspacesApi _workspacesApi = new VSCodeWorkspacesApi(); private readonly VSCodeRemoteMachinesApi _machinesApi = new VSCodeRemoteMachinesApi(); public List Query(Query query) { var results = new List(); if (query != null) { // Search opened workspaces _workspacesApi.Workspaces.ForEach(a => { var title = a.WorkspaceType == WorkspaceType.ProjectFolder ? a.FolderName : a.FolderName.Replace(".code-workspace", $" ({Resources.Workspace})"); var typeWorkspace = a.WorkspaceEnvironmentToString(); if (a.WorkspaceEnvironment != WorkspaceEnvironment.Local) { title = $"{title}{(a.ExtraInfo != null ? $" - {a.ExtraInfo}" : string.Empty)} ({typeWorkspace})"; } var tooltip = new ToolTipData(title, $"{(a.WorkspaceType == WorkspaceType.WorkspaceFile ? Resources.Workspace : Resources.ProjectFolder)}{(a.WorkspaceEnvironment != WorkspaceEnvironment.Local ? $" {Resources.In} {typeWorkspace}" : string.Empty)}: {SystemPath.RealPath(a.RelativePath)}"); results.Add(new Result { Title = title, SubTitle = $"{(a.WorkspaceType == WorkspaceType.WorkspaceFile ? Resources.Workspace : Resources.ProjectFolder)}{(a.WorkspaceEnvironment != WorkspaceEnvironment.Local ? $" {Resources.In} {typeWorkspace}" : string.Empty)}: {SystemPath.RealPath(a.RelativePath)}", Icon = a.VSCodeInstance.WorkspaceIcon, ToolTipData = tooltip, Action = c => { bool hide; try { var process = new ProcessStartInfo { FileName = a.VSCodeInstance.ExecutablePath, UseShellExecute = true, Arguments = a.WorkspaceType == WorkspaceType.ProjectFolder ? $"--folder-uri {a.Path}" : $"--file-uri {a.Path}", WindowStyle = ProcessWindowStyle.Hidden, }; Process.Start(process); hide = true; } catch (Win32Exception ex) { HandleError("无法打开此文件", ex, showMsg: true); hide = false; } return hide; }, ContextData = a, }); }); // Search opened remote machines _machinesApi.Machines.ForEach(a => { var title = $"{a.Host}"; if (a.User != null && a.User != string.Empty && a.HostName != null && a.HostName != string.Empty) { title += $" [{a.User}@{a.HostName}]"; } var tooltip = new ToolTipData(title, Resources.SSHRemoteMachine); results.Add(new Result { Title = title, SubTitle = Resources.SSHRemoteMachine, Icon = a.VSCodeInstance.RemoteIcon, ToolTipData = tooltip, Action = c => { bool hide; try { var process = new ProcessStartInfo() { FileName = a.VSCodeInstance.ExecutablePath, UseShellExecute = true, Arguments = $"--new-window --enable-proposed-api ms-vscode-remote.remote-ssh --remote ssh-remote+{((char)34) + a.Host + ((char)34)}", WindowStyle = ProcessWindowStyle.Hidden, }; Process.Start(process); hide = true; } catch (Win32Exception ex) { HandleError("无法打开此文件", ex, showMsg: true); hide = false; } return hide; }, ContextData = a, }); }); } results = results.Where(a => a.Title.Contains(query.Search, StringComparison.InvariantCultureIgnoreCase)).ToList(); results.ForEach(x => { if (x.Score == 0) { x.Score = 100; } // intersect the title with the query var intersection = Convert.ToInt32(x.Title.ToLowerInvariant().Intersect(query.Search.ToLowerInvariant()).Count() * query.Search.Length); var differenceWithQuery = Convert.ToInt32((x.Title.Length - intersection) * query.Search.Length * 0.7); x.Score = x.Score - differenceWithQuery + intersection; // if is a remote machine give it 12 extra points if (x.ContextData is VSCodeRemoteMachine) { x.Score = Convert.ToInt32(x.Score + (intersection * 2)); } }); results = results.OrderByDescending(x => x.Score).ToList(); if (query.Search == string.Empty || query.Search.Replace(" ", string.Empty) == string.Empty) { results = results.OrderBy(x => x.Title).ToList(); } return results; } public void Init(PluginInitContext context) { _context = context; } public List LoadContextMenus(Result selectedResult) { if (selectedResult?.ContextData is not VSCodeWorkspace workspace) { return new List(); } string realPath = SystemPath.RealPath(workspace.RelativePath); return new List { new ContextMenuResult { PluginName = Name, Title = $"{Resources.CopyPath} (Ctrl+C)", Glyph = "\xE8C8", // Copy FontFamily = "Segoe Fluent Icons,Segoe MDL2 Assets", AcceleratorKey = Key.C, AcceleratorModifiers = ModifierKeys.Control, Action = context => CopyToClipboard(realPath), }, new ContextMenuResult { PluginName = Name, Title = $"{Resources.OpenInExplorer} (Ctrl+Shift+F)", Glyph = "\xEC50", // File Explorer FontFamily = "Segoe Fluent Icons,Segoe MDL2 Assets", AcceleratorKey = Key.F, AcceleratorModifiers = ModifierKeys.Control | ModifierKeys.Shift, Action = context => OpenInExplorer(realPath), }, new ContextMenuResult { PluginName = Name, Title = $"{Resources.OpenInConsole} (Ctrl+Shift+C)", Glyph = "\xE756", // Command Prompt FontFamily = "Segoe Fluent Icons,Segoe MDL2 Assets", AcceleratorKey = Key.C, AcceleratorModifiers = ModifierKeys.Control | ModifierKeys.Shift, Action = context => OpenInConsole(realPath), }, }; } private bool CopyToClipboard(string path) { try { Clipboard.SetText(path); return true; } catch (Exception ex) { HandleError("复制失败", ex, showMsg: true); return false; } } private bool OpenInConsole(string path) { try { Helper.OpenInConsole(path); return true; } catch (Exception ex) { HandleError($"无法在终端打开此路径: {path}", ex, showMsg: true); return false; } } private bool OpenInExplorer(string path) { if (!Helper.OpenInShell("explorer.exe", $"\"{path}\"")) { HandleError($"无法在文件管理器打开此路径: {path}", showMsg: true); return false; } return true; } private void HandleError(string msg, Exception exception = null, bool showMsg = false) { if (exception != null) { Log.Exception(msg, exception, exception.GetType()); } else { Log.Error(msg, typeof(VSCodeWorkspaces.Main)); } if (showMsg) { _context.API.ShowMsg( $"插件: {_context.CurrentPluginMetadata.Name}", msg); } } public string GetTranslatedPluginTitle() { return Resources.PluginTitle; } public string GetTranslatedPluginDescription() { return Resources.PluginDescription; } } } ================================================ FILE: PowerToys/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Properties/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 VS Code 工作区 位于 Used to indicate the location where something is 打开 VS Code 的常用工作区、远程计算机(SSH、Codespaces)和容器,VS Code 路径须在 PATH 环境变量中 VS Code is the name of a product. PATH is the name of an environment variable. Don't translate it. SSH 远程机 容器 As in "Visual Studio Code workspace container type 本地 As in "The workspace is on the local machine" 工作区 It refers to the Visual Studio Code .code-workspace 开发容器 As in "Visual Studio Code Dev Container workspace " 项目文件夹 It refers to the Visual Studio Code Project Folders 复制路径 打开终端 打开路径 ================================================ FILE: PowerToys/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Base64/Base64DecodeRequest.cs ================================================ // 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.Text; using Wox.Plugin.Logger; namespace Community.PowerToys.Run.Plugin.ValueGenerator.Base64 { public class Base64DecodeRequest : IComputeRequest { public byte[] Result { get; set; } public string Description => "Base64 解码结果"; public bool IsSuccessful { get; set; } public string ErrorMessage { get; set; } private string DataToDecode { get; set; } public Base64DecodeRequest(string dataToDecode) { DataToDecode = dataToDecode ?? throw new ArgumentNullException(nameof(dataToDecode)); } public bool Compute() { IsSuccessful = true; try { Result = System.Convert.FromBase64String(DataToDecode); } catch (Exception e) { Log.Exception(e.Message, e, GetType()); ErrorMessage = e.Message; IsSuccessful = false; } return IsSuccessful; } public string ResultToString() { if (Result != null) { return Encoding.UTF8.GetString(Result); } else { return string.Empty; } } } } ================================================ FILE: PowerToys/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Base64/Base64Request.cs ================================================ // 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.Text; using Wox.Plugin.Logger; namespace Community.PowerToys.Run.Plugin.ValueGenerator.Base64 { public class Base64Request : IComputeRequest { public byte[] Result { get; set; } public string Description => "Base64 编码结果"; public bool IsSuccessful { get; set; } public string ErrorMessage { get; set; } private byte[] DataToEncode { get; set; } public Base64Request(byte[] dataToEncode) { DataToEncode = dataToEncode ?? throw new ArgumentNullException(nameof(dataToEncode)); } public bool Compute() { IsSuccessful = true; try { Result = Encoding.UTF8.GetBytes(System.Convert.ToBase64String(DataToEncode)); } catch (Exception e) { Log.Exception(e.Message, e, GetType()); ErrorMessage = e.Message; IsSuccessful = false; } return IsSuccessful; } public string ResultToString() { return Encoding.UTF8.GetString(Result); } } } ================================================ FILE: PowerToys/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/GUID/GUIDRequest.cs ================================================ // 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.Security.Cryptography; using Wox.Plugin.Logger; namespace Community.PowerToys.Run.Plugin.ValueGenerator.GUID { public class GUIDRequest : IComputeRequest { public byte[] Result { get; set; } public bool IsSuccessful { get; set; } public string ErrorMessage { get; set; } private int Version { get; set; } public string Description => Version switch { 1 => "UUID v1:基于当前时间和网卡 MAC 地址", 3 => $"UUID v3:基于给定名称进行 {HashAlgorithmName.MD5} 散列计算", 4 => "UUID v4:随机数值", 5 => $"UUID v5:基于给定名称进行 {HashAlgorithmName.SHA1} 散列计算", 7 => "UUID v7:当前时间+随机数值", _ => string.Empty, }; private Guid? GuidNamespace { get; set; } private string GuidName { get; set; } private Guid GuidResult { get; set; } private static readonly string NullNamespaceError = $"第一个参数需要是一个 UUID 或者是以下选项之一:{string.Join("、", GUIDGenerator.PredefinedNamespaces.Keys)}"; public GUIDRequest(int version, string guidNamespace = null, string name = null) { Version = version; if (Version is < 1 or > 7 or 2 or 6) { throw new ArgumentException("不支持此 GUID 版本,目前仅支持版本 1、3、4、5、7"); } if (version is 3 or 5) { if (guidNamespace == null) { throw new ArgumentNullException(null, NullNamespaceError); } if (GUIDGenerator.PredefinedNamespaces.TryGetValue(guidNamespace.ToLowerInvariant(), out Guid guid)) { GuidNamespace = guid; } else if (Guid.TryParse(guidNamespace, out guid)) { GuidNamespace = guid; } else { throw new ArgumentNullException(null, NullNamespaceError); } if (name == null) { throw new ArgumentNullException(nameof(name)); } else { GuidName = name; } } else { GuidNamespace = null; } ErrorMessage = null; } public bool Compute() { IsSuccessful = true; try { Guid guid = Version switch { 1 => GUIDGenerator.V1(), 3 => GUIDGenerator.V3(GuidNamespace.Value, GuidName), 4 => GUIDGenerator.V4(), 5 => GUIDGenerator.V5(GuidNamespace.Value, GuidName), 7 => GUIDGenerator.V7(), _ => default, }; if (guid != default) { GuidResult = guid; } Result = GuidResult.ToByteArray(); } catch (InvalidOperationException e) { Log.Exception(e.Message, e, GetType()); ErrorMessage = e.Message; IsSuccessful = false; } return IsSuccessful; } public string ResultToString() { if (!IsSuccessful) { return ErrorMessage; } return GuidResult.ToString(); } } } ================================================ FILE: PowerToys/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/DataEscapeRequest.cs ================================================ // 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.Text; using Wox.Plugin.Logger; namespace Community.PowerToys.Run.Plugin.ValueGenerator.Uri { public class DataEscapeRequest : IComputeRequest { public byte[] Result { get; set; } public string Description => "百分比编码后的数据"; public bool IsSuccessful { get; set; } public string ErrorMessage { get; set; } private string DataToEscape { get; set; } public DataEscapeRequest(string dataToEscape) { DataToEscape = dataToEscape ?? throw new ArgumentNullException(nameof(dataToEscape)); } public bool Compute() { IsSuccessful = true; try { Result = Encoding.UTF8.GetBytes(System.Uri.EscapeDataString(DataToEscape)); } catch (Exception e) { Log.Exception(e.Message, e, GetType()); ErrorMessage = e.Message; IsSuccessful = false; } return IsSuccessful; } public string ResultToString() { return Encoding.UTF8.GetString(Result); } } } ================================================ FILE: PowerToys/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/DataUnescapeRequest.cs ================================================ // 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.Text; using Wox.Plugin.Logger; namespace Community.PowerToys.Run.Plugin.ValueGenerator.Uri { public class DataUnescapeRequest : IComputeRequest { public byte[] Result { get; set; } public string Description => "百分比解码后的数据"; public bool IsSuccessful { get; set; } public string ErrorMessage { get; set; } private string DataToUnescape { get; set; } public DataUnescapeRequest(string dataToUnescape) { DataToUnescape = dataToUnescape ?? throw new ArgumentNullException(nameof(dataToUnescape)); } public bool Compute() { IsSuccessful = true; try { Result = Encoding.UTF8.GetBytes(System.Uri.UnescapeDataString(DataToUnescape)); } catch (Exception e) { Log.Exception(e.Message, e, GetType()); ErrorMessage = e.Message; IsSuccessful = false; } return IsSuccessful; } public string ResultToString() { if (Result != null) { return Encoding.UTF8.GetString(Result); } else { return string.Empty; } } } } ================================================ FILE: PowerToys/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/HexEscapeRequest.cs ================================================ // 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.Linq; using System.Text; using Wox.Plugin.Logger; namespace Community.PowerToys.Run.Plugin.ValueGenerator.Uri { public class HexEscapeRequest : IComputeRequest { public byte[] Result { get; set; } public string Description => "百分比编码后的字符"; public bool IsSuccessful { get; set; } public string ErrorMessage { get; set; } private string DataToEscape { get; set; } public HexEscapeRequest(string dataToEscape) { DataToEscape = dataToEscape ?? throw new ArgumentNullException(nameof(dataToEscape)); // Validate that we have only one character if (dataToEscape.Length != 1) { throw new ArgumentOutOfRangeException(nameof(dataToEscape)); } } public bool Compute() { IsSuccessful = true; try { char charToEscape = DataToEscape[0]; Result = Encoding.UTF8.GetBytes(System.Uri.HexEscape(charToEscape)); } catch (Exception e) { Log.Exception(e.Message, e, GetType()); ErrorMessage = e.Message; IsSuccessful = false; } return IsSuccessful; } public string ResultToString() { return Encoding.UTF8.GetString(Result); } } } ================================================ FILE: PowerToys/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/HexUnescapeRequest.cs ================================================ // 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.Text; using Wox.Plugin.Logger; namespace Community.PowerToys.Run.Plugin.ValueGenerator.Uri { public class HexUnescapeRequest : IComputeRequest { public byte[] Result { get; set; } public string Description => "百分比解码后的字符"; public bool IsSuccessful { get; set; } public string ErrorMessage { get; set; } private string DataToUnescape { get; set; } public HexUnescapeRequest(string dataToUnescape) { DataToUnescape = dataToUnescape ?? throw new ArgumentNullException(nameof(dataToUnescape)); } public bool Compute() { IsSuccessful = true; try { int index = 0; if (System.Uri.IsHexEncoding(DataToUnescape, index)) { Result = Encoding.UTF8.GetBytes(System.Uri.HexUnescape(DataToUnescape, ref index).ToString()); } } catch (Exception e) { Log.Exception(e.Message, e, GetType()); ErrorMessage = e.Message; IsSuccessful = false; } return IsSuccessful; } public string ResultToString() { if (Result != null) { return Encoding.UTF8.GetString(Result); } else { return string.Empty; } } } } ================================================ FILE: PowerToys/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/UrlDecodeRequest.cs ================================================ // 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.Security.Policy; using System.Text; using System.Web; using Wox.Plugin.Logger; namespace Community.PowerToys.Run.Plugin.ValueGenerator.Uri { public class UrlDecodeRequest : IComputeRequest { public byte[] Result { get; set; } public string Description => "URL 解码结果"; public bool IsSuccessful { get; set; } public string ErrorMessage { get; set; } private string DataToDecode { get; set; } public UrlDecodeRequest(string dataToDecode) { DataToDecode = dataToDecode ?? throw new ArgumentNullException(nameof(dataToDecode)); } public bool Compute() { IsSuccessful = true; try { Result = Encoding.UTF8.GetBytes(HttpUtility.UrlDecode(DataToDecode)); } catch (Exception e) { Log.Exception(e.Message, e, GetType()); ErrorMessage = e.Message; IsSuccessful = false; } return IsSuccessful; } public string ResultToString() { if (Result != null) { return Encoding.UTF8.GetString(Result); } else { return string.Empty; } } } } ================================================ FILE: PowerToys/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/UrlEncodeRequest.cs ================================================ // 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.Security.Policy; using System.Text; using System.Web; using Wox.Plugin.Logger; namespace Community.PowerToys.Run.Plugin.ValueGenerator.Uri { public class UrlEncodeRequest : IComputeRequest { public byte[] Result { get; set; } public string Description => "URL 编码结果"; public bool IsSuccessful { get; set; } public string ErrorMessage { get; set; } private string DataToEncode { get; set; } public UrlEncodeRequest(string dataToEncode) { DataToEncode = dataToEncode ?? throw new ArgumentNullException(nameof(dataToEncode)); } public bool Compute() { IsSuccessful = true; try { Result = Encoding.UTF8.GetBytes(HttpUtility.UrlEncode(DataToEncode)); } catch (Exception e) { Log.Exception(e.Message, e, GetType()); ErrorMessage = e.Message; IsSuccessful = false; } return IsSuccessful; } public string ResultToString() { return Encoding.UTF8.GetString(Result); } } } ================================================ FILE: PowerToys/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/InputParser.cs ================================================ // 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.Security.Cryptography; using System.Text; using Community.PowerToys.Run.Plugin.ValueGenerator.Base64; using Community.PowerToys.Run.Plugin.ValueGenerator.GUID; using Community.PowerToys.Run.Plugin.ValueGenerator.Hashing; using Community.PowerToys.Run.Plugin.ValueGenerator.Uri; using Wox.Plugin; using Wox.Plugin.Logger; namespace Community.PowerToys.Run.Plugin.ValueGenerator { public class InputParser { public IComputeRequest ParseInput(Query query) { IComputeRequest request; if (query.Terms.Count == 0) { throw new FormatException("Empty request"); } string command = query.Terms[0]; if (command.Equals("md5", StringComparison.OrdinalIgnoreCase)) { int commandIndex = query.RawUserQuery.IndexOf(command, StringComparison.InvariantCultureIgnoreCase); string content = query.RawUserQuery.Substring(commandIndex + command.Length).Trim(); if (content == string.Empty) { throw new FormatException("Empty hash request"); } Log.Debug($"Will calculate MD5 hash for: {content}", GetType()); request = new HashRequest(HashAlgorithmName.MD5, Encoding.UTF8.GetBytes(content)); } else if (command.StartsWith("sha", StringComparison.InvariantCultureIgnoreCase)) { int commandIndex = query.RawUserQuery.IndexOf(command, StringComparison.InvariantCultureIgnoreCase); string content = query.RawUserQuery.Substring(commandIndex + command.Length).Trim(); HashAlgorithmName algorithmName; switch (command.Substring(3)) { case "1": algorithmName = HashAlgorithmName.SHA1; break; case "256": algorithmName = HashAlgorithmName.SHA256; break; case "384": algorithmName = HashAlgorithmName.SHA384; break; case "512": algorithmName = HashAlgorithmName.SHA512; break; default: throw new FormatException("Unknown SHA variant. Supported variants: SHA1, SHA256, SHA384, SHA512"); } if (content == string.Empty) { throw new FormatException("Empty hash request"); } Log.Debug($"Will calculate {algorithmName} hash for: {content}", GetType()); request = new HashRequest(algorithmName, Encoding.UTF8.GetBytes(content)); } else if (command.StartsWith("guid", StringComparison.InvariantCultureIgnoreCase) || command.StartsWith("uuid", StringComparison.InvariantCultureIgnoreCase)) { string content = query.Search.Substring(command.Length).Trim(); // Default to version 4 int version = 4; string versionQuery = command.Substring(4); if (versionQuery.Length > 0) { if (versionQuery.StartsWith("v", StringComparison.InvariantCultureIgnoreCase)) { versionQuery = versionQuery.Substring(1); } if (!int.TryParse(versionQuery, null, out version)) { throw new FormatException("Could not determine requested GUID version. Supported versions are 1, 3, 4, 5, and 7"); } } if (version == 3 || version == 5) { string[] sParameters = content.Split(" "); if (sParameters.Length != 2) { throw new ArgumentException($"UUID v{version} 请输入两个参数:一个命名空间(固定选项或 UUID)和一个名称。\n例如:uuidv{version} ns: <填入名称>"); } string namespaceParameter = sParameters[0]; string nameParameter = sParameters[1]; request = new GUIDRequest(version, namespaceParameter, nameParameter); } else { request = new GUIDRequest(version); } } else if (command.Equals("base64", StringComparison.OrdinalIgnoreCase)) { int commandIndex = query.RawUserQuery.IndexOf(command, StringComparison.InvariantCultureIgnoreCase); string content = query.RawUserQuery.Substring(commandIndex + command.Length).Trim(); request = new Base64Request(Encoding.UTF8.GetBytes(content)); } else if (command.Equals("base64d", StringComparison.OrdinalIgnoreCase)) { int commandIndex = query.RawUserQuery.IndexOf(command, StringComparison.InvariantCultureIgnoreCase); string content = query.RawUserQuery.Substring(commandIndex + command.Length).Trim(); request = new Base64DecodeRequest(content); } else if (command.StartsWith("esc:", StringComparison.OrdinalIgnoreCase)) { // Escape things if (command.Equals("esc:data", StringComparison.OrdinalIgnoreCase)) { int commandIndex = query.RawUserQuery.IndexOf(command, StringComparison.InvariantCultureIgnoreCase); string content = query.RawUserQuery.Substring(commandIndex + command.Length).Trim(); request = new DataEscapeRequest(content); } else if (command.Equals("esc:hex", StringComparison.OrdinalIgnoreCase)) { int commandIndex = query.RawUserQuery.IndexOf(command, StringComparison.InvariantCultureIgnoreCase); string content = query.RawUserQuery.Substring(commandIndex + command.Length).Trim(); // This is only for single chars if (content.Length > 1) { throw new ArgumentException($"无效查询: {query.RawUserQuery},字符数量过多"); } else if (content.Length == 0) { throw new FormatException($"Invalid Query: {query.RawUserQuery}"); } request = new HexEscapeRequest(content); } else { throw new FormatException($"Invalid Query: {query.RawUserQuery}"); } } else if (command.StartsWith("uesc:", StringComparison.OrdinalIgnoreCase)) { // Unescape things if (command.Equals("uesc:data", StringComparison.OrdinalIgnoreCase)) { int commandIndex = query.RawUserQuery.IndexOf(command, StringComparison.InvariantCultureIgnoreCase); string content = query.RawUserQuery.Substring(commandIndex + command.Length).Trim(); request = new DataUnescapeRequest(content); } else if (command.Equals("uesc:hex", StringComparison.OrdinalIgnoreCase)) { int commandIndex = query.RawUserQuery.IndexOf(command, StringComparison.InvariantCultureIgnoreCase); string content = query.RawUserQuery.Substring(commandIndex + command.Length).Trim(); request = new HexUnescapeRequest(content); } else { throw new FormatException($"Invalid Query: {query.RawUserQuery}"); } } else if (command.Equals("url", StringComparison.OrdinalIgnoreCase)) { int commandIndex = query.RawUserQuery.IndexOf(command, StringComparison.InvariantCultureIgnoreCase); string content = query.RawUserQuery.Substring(commandIndex + command.Length).Trim(); request = new UrlEncodeRequest(content); } else if (command.Equals("urld", StringComparison.OrdinalIgnoreCase)) { int commandIndex = query.RawUserQuery.IndexOf(command, StringComparison.InvariantCultureIgnoreCase); string content = query.RawUserQuery.Substring(commandIndex + command.Length).Trim(); request = new UrlDecodeRequest(content); } else { throw new FormatException($"Invalid Query: {query.RawUserQuery}"); } return request; } } } ================================================ FILE: PowerToys/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Properties/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 复制失败 复制 {0} 到剪贴板 编码生成器出错 Base64 编码 Base64 解码 百分号编码 百分号编码,单个十六进制字符 计算 {0} 散列值 百分号解码 百分号解码,单个十六进制字符 URL 编码 URL 解码 生成随机 UUID 生成 UUID 版本 1,基于时间 生成 UUID 版本 3,基于给定名称进行 MD5 散列计算 生成 UUID 版本 4,随机数值 生成 UUID 版本 5,基于给定名称进行 SHA1 散列计算 生成 UUID 版本 7,当前时间+随机数值 输入内容 Usage example: "md5 <your input>" 例如:{0} The arg following is the usage example Used to indicate alternatives or options available 生成特定编码和计算散列值 编码生成器 ================================================ FILE: PowerToys/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/Main.cs ================================================ // 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.Globalization; using System.Linq; using System.Text; using System.Windows.Controls; using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library; using Wox.Infrastructure; using Wox.Plugin; using Wox.Plugin.Logger; using BrowserInfo = Wox.Plugin.Common.DefaultBrowserInfo; namespace Community.PowerToys.Run.Plugin.WebSearch { public class Main : IPlugin, IPluginI18n, IContextMenu, ISettingProvider, IReloadable, IDisposable { // Should only be set in Init() private Action onPluginError; private const string NotGlobalIfUri = nameof(NotGlobalIfUri); /// If true, dont show global result on queries that are URIs private bool _notGlobalIfUri; private PluginInitContext _context; private string _iconPath; private bool _disposed; public string Name => Properties.Resources.plugin_name; public string Description => Properties.Resources.plugin_description; public static string PluginID => "9F1B49201C3F4BF781CAAD5CD88EA4DC"; private static readonly CompositeFormat PluginInBrowserName = System.Text.CompositeFormat.Parse(Properties.Resources.plugin_in_browser_name); private static readonly CompositeFormat PluginOpen = System.Text.CompositeFormat.Parse(Properties.Resources.plugin_open); private static readonly CompositeFormat PluginSearchFailed = System.Text.CompositeFormat.Parse(Properties.Resources.plugin_search_failed); public IEnumerable AdditionalOptions => new List() { new PluginAdditionalOption() { Key = NotGlobalIfUri, DisplayLabel = Properties.Resources.plugin_global_if_uri, Value = false, }, }; public List LoadContextMenus(Result selectedResult) { return new List(0); } public List Query(Query query) { ArgumentNullException.ThrowIfNull(query); var results = new List(); // empty query if (string.IsNullOrEmpty(query.Search)) { string arguments = "? "; results.Add(new Result { Title = Properties.Resources.plugin_description, SubTitle = string.Format(CultureInfo.CurrentCulture, PluginInBrowserName, BrowserInfo.Name ?? BrowserInfo.MSEdgeName), QueryTextDisplay = string.Empty, IcoPath = _iconPath, ProgramArguments = arguments, Action = action => { if (!Helper.OpenCommandInShell(BrowserInfo.Path, BrowserInfo.ArgumentsPattern, arguments)) { onPluginError(); return false; } return true; }, }); return results; } else { string searchTerm = query.Search; // Don't include in global results if the query is a URI (and if the option NotGlobalIfUri is enabled) if (_notGlobalIfUri && AreResultsGlobal() && IsURI(searchTerm)) { return results; } var result = new Result { Title = searchTerm, SubTitle = string.Format(CultureInfo.CurrentCulture, PluginOpen, BrowserInfo.Name ?? BrowserInfo.MSEdgeName), QueryTextDisplay = searchTerm, IcoPath = _iconPath, }; string arguments = $"? {searchTerm}"; result.ProgramArguments = arguments; result.Action = action => { if (!Helper.OpenCommandInShell(BrowserInfo.Path, BrowserInfo.ArgumentsPattern, arguments)) { onPluginError(); return false; } return true; }; results.Add(result); } return results; bool AreResultsGlobal() { return string.IsNullOrEmpty(query.ActionKeyword); } // Checks if input is a URI the same way Microsoft.Plugin.Uri.UriHelper.ExtendedUriParser does bool IsURI(string input) { if (input.EndsWith(":", StringComparison.OrdinalIgnoreCase) && !input.StartsWith("http", StringComparison.OrdinalIgnoreCase) && !input.Contains('/', StringComparison.OrdinalIgnoreCase) && !input.All(char.IsDigit) && System.Text.RegularExpressions.Regex.IsMatch(input, @"^([a-z][a-z0-9+\-.]*):")) { return true; } if (input.EndsWith(":", StringComparison.CurrentCulture) || input.EndsWith(".", StringComparison.CurrentCulture) || input.EndsWith(":/", StringComparison.CurrentCulture) || input.EndsWith("://", StringComparison.CurrentCulture) || input.All(char.IsDigit)) { return false; } try { _ = new UriBuilder(input); } catch (UriFormatException) { return false; } return true; } } public void Init(PluginInitContext context) { _context = context ?? throw new ArgumentNullException(nameof(context)); _context.API.ThemeChanged += OnThemeChanged; UpdateIconPath(_context.API.GetCurrentTheme()); BrowserInfo.UpdateIfTimePassed(); onPluginError = () => { string errorMsgString = string.Format(CultureInfo.CurrentCulture, PluginSearchFailed, BrowserInfo.Name ?? BrowserInfo.MSEdgeName); Log.Error(errorMsgString, this.GetType()); _context.API.ShowMsg( $"插件: {Properties.Resources.plugin_name}", errorMsgString); }; } public string GetTranslatedPluginTitle() { return Properties.Resources.plugin_name; } public string GetTranslatedPluginDescription() { return Properties.Resources.plugin_description; } private void OnThemeChanged(Theme oldtheme, Theme newTheme) { UpdateIconPath(newTheme); } private void UpdateIconPath(Theme theme) { if (theme == Theme.Light || theme == Theme.HighContrastWhite) { _iconPath = "Images/WebSearch.light.png"; } else { _iconPath = "Images/WebSearch.dark.png"; } } public Control CreateSettingPanel() { throw new NotImplementedException(); } public void UpdateSettings(PowerLauncherPluginSettings settings) { _notGlobalIfUri = settings?.AdditionalOptions?.FirstOrDefault(x => x.Key == NotGlobalIfUri)?.Value ?? false; } public void ReloadData() { if (_context is null) { return; } UpdateIconPath(_context.API.GetCurrentTheme()); BrowserInfo.UpdateIfTimePassed(); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!_disposed && disposing) { if (_context != null && _context.API != null) { _context.API.ThemeChanged -= OnThemeChanged; } _disposed = true; } } } } ================================================ FILE: PowerToys/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/Properties/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 默认浏览器 使用默认搜索引擎在线搜索 默认唤醒时输入网址,不显示在线搜索 打开 默认浏览器 打开 {0} Like "Search the web in {the browser name}" 在线搜索 打开 {0} 在线搜索 无法打开 {0} ================================================ FILE: PowerToys/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Properties/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 文件夹 浏览文件夹 复制路径(Ctrl+C) 在命令行打开路径(Ctrl+Shift+C) 打开所在文件夹(Ctrl+Shift+E) 警告:文件夹的其余搜索结果被省略。 显示了 {0:N0} 个结果中的 {1:N0} 个 Example: showing 25 of 540 results 写入剪贴板失败 无法打开文件 提示: 使用 > 在文件夹内进行搜索,使用 * 作为通配符 打开: {0} Open as a verb. Open this folder. Do not translate the placeholder '{0}'. 无法打开 文件: {0} the arg following is file path 文件夹: {0} the arg following is the folder path ================================================ FILE: PowerToys/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Main.cs ================================================ // 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.Globalization; using System.IO.Abstractions; using System.Linq; using System.Text.RegularExpressions; using System.Windows.Controls; using ManagedCommon; using Microsoft.Plugin.Indexer.DriveDetection; using Microsoft.Plugin.Indexer.Interop; using Microsoft.Plugin.Indexer.SearchHelper; using Microsoft.PowerToys.Settings.UI.Library; using Wox.Infrastructure; using Wox.Infrastructure.Storage; using Wox.Plugin; using Wox.Plugin.Logger; namespace Microsoft.Plugin.Indexer { internal class Main : ISettingProvider, IPlugin, ISavable, IPluginI18n, IContextMenu, IDisposable, IDelayedExecutionPlugin { private const string DisableDriveDetectionWarning = nameof(DisableDriveDetectionWarning); private const string ExcludedPatterns = nameof(ExcludedPatterns); private static readonly IFileSystem _fileSystem = new FileSystem(); // This variable contains metadata about the Plugin private PluginInitContext _context; // This variable contains information about the context menus private IndexerSettings _settings; // Contains information about the plugin stored in json format private PluginJsonStorage _storage; // Excluded patterns settings private List _excludedPatterns = new List(); // To access Windows Search functionalities private static readonly OleDBSearch _search = new OleDBSearch(); private readonly WindowsSearchAPI _api = new WindowsSearchAPI(_search); // To obtain information regarding the drives that are indexed private readonly IndexerDriveDetection _driveDetection = new IndexerDriveDetection(new RegistryWrapper(), new DriveDetection.DriveInfoWrapper()); // Reserved keywords in oleDB private readonly string reservedStringPattern = @"^[\/\\\$\%]+$|^.*[<>].*$"; private string WarningIconPath { get; set; } public string Name => Properties.Resources.Microsoft_plugin_indexer_plugin_name; public string Description => Properties.Resources.Microsoft_plugin_indexer_plugin_description; public static string PluginID => "2140FC9819AD43A3A616E2735815C27C"; public IEnumerable AdditionalOptions => new List() { new PluginAdditionalOption() { Key = DisableDriveDetectionWarning, DisplayLabel = Properties.Resources.disable_drive_detection_warning, Value = false, }, new PluginAdditionalOption() { PluginOptionType = PluginAdditionalOption.AdditionalOptionType.MultilineTextbox, Key = ExcludedPatterns, DisplayLabel = Properties.Resources.excluded_patterns_label, DisplayDescription = Properties.Resources.excluded_patterns_description, PlaceholderText = Properties.Resources.excluded_patterns_placeholder, TextValue = string.Empty, }, }; private ContextMenuLoader _contextMenuLoader; private bool disposedValue; // To save the configurations of plugins public void Save() { _storage?.Save(); } // This function uses the Windows indexer and returns the list of results obtained public List Query(Query query, bool isFullQuery) { var results = new List(); if (!string.IsNullOrEmpty(query.Search)) { var searchQuery = query.Search; if (_settings.MaxSearchCount <= 0) { _settings.MaxSearchCount = 30; } var regexMatch = Regex.Match(searchQuery, reservedStringPattern); if (!regexMatch.Success) { try { if (_driveDetection.DisplayWarning()) { results.Add(new Result { Title = Properties.Resources.Microsoft_plugin_indexer_drivedetectionwarning, SubTitle = Properties.Resources.Microsoft_plugin_indexer_disable_warning_in_settings, IcoPath = WarningIconPath, Action = e => { Helper.OpenInShell("ms-settings:cortana-windowssearch"); return true; }, }); } // This uses the Microsoft.Search.Interop assembly var searchManager = new CSearchManager(); var searchResultsList = _api.Search(searchQuery, searchManager, excludedPatterns: _excludedPatterns, maxCount: _settings.MaxSearchCount).ToList(); // If the delayed execution query is not required (since the SQL query is fast) return empty results if (searchResultsList.Count == 0 && isFullQuery) { return new List(); } foreach (var searchResult in searchResultsList) { var path = searchResult.Path; // Using CurrentCulture since this is user facing var toolTipTitle = string.Format(CultureInfo.CurrentCulture, "{0} : {1}", Properties.Resources.Microsoft_plugin_indexer_name, searchResult.Title); var toolTipText = string.Format(CultureInfo.CurrentCulture, "{0} : {1}", Properties.Resources.Microsoft_plugin_indexer_path, path); string workingDir = null; if (_settings.UseLocationAsWorkingDir) { workingDir = _fileSystem.Path.GetDirectoryName(path); } Result r = new Result(); r.Title = searchResult.Title; r.SubTitle = Properties.Resources.Microsoft_plugin_indexer_subtitle_header + ": " + path; r.IcoPath = path; r.ToolTipData = new ToolTipData(toolTipTitle, toolTipText); r.Action = c => { bool hide = true; if (!Helper.OpenInShell(path, null, workingDir)) { hide = false; var name = $"插件: {_context.CurrentPluginMetadata.Name}"; var msg = Properties.Resources.Microsoft_plugin_indexer_file_open_failed; _context.API.ShowMsg(name, msg, string.Empty); } return hide; }; r.ContextData = searchResult; // If the result is a directory, then its display should show a directory. if (_fileSystem.Directory.Exists(path)) { r.QueryTextDisplay = path; } results.Add(r); } } catch (InvalidOperationException) { // The connection has closed, internal error of ExecuteReader() // Not showing this exception to the users } catch (Exception ex) { Log.Exception("Something failed", ex, GetType()); } } } return results; } // This function uses the Windows indexer and returns the list of results obtained. This version is required to implement the interface public List Query(Query query) { // All plugins have to implement IPlugin interface. We return empty collection as we do not want any computation with constant search plugins. return new List(); } public void Init(PluginInitContext context) { // initialize the context of the plugin _context = context; _contextMenuLoader = new ContextMenuLoader(context); _storage = new PluginJsonStorage(); _settings = _storage.Load(); _context.API.ThemeChanged += OnThemeChanged; UpdateIconPath(_context.API.GetCurrentTheme()); } // Todo : Update with theme based IconPath private void UpdateIconPath(Theme theme) { if (theme == Theme.Light || theme == Theme.HighContrastWhite) { WarningIconPath = "Images/Warning.light.png"; } else { WarningIconPath = "Images/Warning.dark.png"; } } private void OnThemeChanged(Theme currentTheme, Theme newTheme) { UpdateIconPath(newTheme); } // Set the Plugin Title public string GetTranslatedPluginTitle() { return Properties.Resources.Microsoft_plugin_indexer_plugin_name; } // Set the plugin Description public string GetTranslatedPluginDescription() { return Properties.Resources.Microsoft_plugin_indexer_plugin_description; } public List LoadContextMenus(Result selectedResult) { return _contextMenuLoader.LoadContextMenus(selectedResult); } public void UpdateSettings(PowerLauncherPluginSettings settings) { var driveDetection = false; if (settings.AdditionalOptions != null) { var driveDetectionOption = settings.AdditionalOptions.FirstOrDefault(x => x.Key == DisableDriveDetectionWarning); driveDetection = driveDetectionOption == null ? false : driveDetectionOption.Value; var excludedPatternsOption = settings.AdditionalOptions.FirstOrDefault(x => x.Key == ExcludedPatterns); _excludedPatterns = excludedPatternsOption == null ? new List() : excludedPatternsOption.TextValueAsMultilineList; } _driveDetection.IsDriveDetectionWarningCheckBoxSelected = driveDetection; } public Control CreateSettingPanel() { throw new NotImplementedException(); } protected virtual void Dispose(bool disposing) { if (!disposedValue) { if (disposing) { } disposedValue = true; } } public void Dispose() { // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method Dispose(disposing: true); GC.SuppressFinalize(this); } } } ================================================ FILE: PowerToys/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Properties/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 复制路径(Ctrl+C) 打开所在文件夹(Ctrl+Shift+E) 以管理员身份打开(Ctrl+Shift+Enter) 在命令行打开路径(Ctrl+Shift+C) 名称 路径 警告:未完全建立索引,搜索结果可能不全。 单击转到 Windows 搜索设置进行修复。 写入剪贴板失败 无法打开文件 无法打开文件夹 搜索文件与文件夹 搜索 搜索 不显示未索引文件警告 以其他用户身份打开(Ctrl+Shift+U) 排除规则 填写要排除的文件名规则,每行一个,名称符合的文件和文件夹将会被隐藏 例如: *.exe ================================================ FILE: PowerToys/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Main.cs ================================================ // 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.Linq; using System.Threading.Tasks; using ManagedCommon; using Microsoft.Plugin.Program.ProgramArgumentParser; using Microsoft.Plugin.Program.Programs; using Microsoft.Plugin.Program.Storage; using Wox.Infrastructure.Storage; using Wox.Plugin; using Wox.Plugin.Common; using Stopwatch = Wox.Infrastructure.Stopwatch; namespace Microsoft.Plugin.Program { public class Main : IPlugin, IPluginI18n, IContextMenu, ISavable, IDisposable { // The order of this array is important! The Parsers will be checked in order (index 0 to index Length-1) and the first parser which is able to parse the Query will be used // NoArgumentsArgumentParser does always succeed and therefore should always be last/fallback private static readonly IProgramArgumentParser[] _programArgumentParsers = new IProgramArgumentParser[] { new DoubleDashProgramArgumentParser(), new InferredProgramArgumentParser(), new NoArgumentsArgumentParser(), }; internal static ProgramPluginSettings Settings { get; set; } internal static readonly ShellLocalization ShellLocalizationHelper = new(); public string Name => Properties.Resources.wox_plugin_program_plugin_name; public string Description => Properties.Resources.wox_plugin_program_plugin_description; public static string PluginID => "791FC278BA414111B8D1886DFE447410"; private static PluginInitContext _context; private readonly PluginJsonStorage _settingsStorage; private bool _disposed; private PackageRepository _packageRepository; private static Win32ProgramFileSystemWatchers _win32ProgramRepositoryHelper; private static Win32ProgramRepository _win32ProgramRepository; public Main() { _settingsStorage = new PluginJsonStorage(); Settings = _settingsStorage.Load(); // This helper class initializes the file system watchers based on the locations to watch _win32ProgramRepositoryHelper = new Win32ProgramFileSystemWatchers(); // Initialize the Win32ProgramRepository with the settings object _win32ProgramRepository = new Win32ProgramRepository(_win32ProgramRepositoryHelper.FileSystemWatchers.Cast().ToList(), Settings, _win32ProgramRepositoryHelper.PathsToWatch); } public void Save() { _settingsStorage.Save(); } public List Query(Query query) { var sources = _programArgumentParsers .Where(programArgumentParser => programArgumentParser.Enabled); foreach (var programArgumentParser in sources) { if (!programArgumentParser.TryParse(query, out var program, out var programArguments)) { continue; } return Query(program, programArguments).ToList(); } return new List(0); } private IEnumerable Query(string program, string programArguments) { var result = _win32ProgramRepository .Concat(_packageRepository) .AsParallel() .Where(p => p.Enabled) .Select(p => p.Result(program, programArguments, _context.API)) .Where(r => r?.Score > 0) .ToArray(); if (result.Length != 0) { var maxScore = result.Max(x => x.Score); return result .Where(x => x.Score > Settings.MinScoreThreshold * maxScore); } return Enumerable.Empty(); } public void Init(PluginInitContext context) { _context = context ?? throw new ArgumentNullException(nameof(context)); _context.API.ThemeChanged += OnThemeChanged; _packageRepository = new PackageRepository(new PackageCatalogWrapper(), _context); var a = Task.Run(() => { Stopwatch.Normal("Microsoft.Plugin.Program.Main - Win32Program index cost", _win32ProgramRepository.IndexPrograms); }); var b = Task.Run(() => { Stopwatch.Normal("Microsoft.Plugin.Program.Main - Package index cost", _packageRepository.IndexPrograms); UpdateUWPIconPath(_context.API.GetCurrentTheme()); }); Task.WaitAll(a, b); Settings.LastIndexTime = DateTime.Today; } public void OnThemeChanged(Theme currentTheme, Theme newTheme) { UpdateUWPIconPath(newTheme); } public void UpdateUWPIconPath(Theme theme) { if (_packageRepository != null) { foreach (UWPApplication app in _packageRepository) { app.UpdateLogoPath(theme); } } } public void IndexPrograms() { var t1 = Task.Run(() => _win32ProgramRepository.IndexPrograms()); var t2 = Task.Run(() => _packageRepository.IndexPrograms()); Task.WaitAll(t1, t2); Settings.LastIndexTime = DateTime.Today; } public string GetTranslatedPluginTitle() { return Properties.Resources.wox_plugin_program_plugin_name; } public string GetTranslatedPluginDescription() { return Properties.Resources.wox_plugin_program_plugin_description; } public List LoadContextMenus(Result selectedResult) { ArgumentNullException.ThrowIfNull(selectedResult); var menuOptions = new List(); if (selectedResult.ContextData is IProgram program) { menuOptions = program.ContextMenus(selectedResult.ProgramArguments, _context.API); } return menuOptions; } public static void StartProcess(Func runProcess, ProcessStartInfo info) { try { ArgumentNullException.ThrowIfNull(runProcess); ArgumentNullException.ThrowIfNull(info); runProcess(info); } catch (Exception ex) { Logger.ProgramLogger.Exception($"Unable to start ", ex, System.Reflection.MethodBase.GetCurrentMethod().DeclaringType, info?.FileName); var name = "插件: " + Properties.Resources.wox_plugin_program_plugin_name; var message = $"{Properties.Resources.powertoys_run_plugin_program_start_failed}: {info?.FileName}"; _context.API.ShowMsg(name, message, string.Empty); } } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!_disposed) { if (disposing) { if (_context != null && _context.API != null) { _context.API.ThemeChanged -= OnThemeChanged; } _win32ProgramRepositoryHelper?.Dispose(); _disposed = true; } } } } } ================================================ FILE: PowerToys/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/UWPApplication.cs ================================================ // 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.Globalization; using System.IO; using System.IO.Abstractions; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Xml; using ManagedCommon; using Microsoft.Plugin.Program.Logger; using Wox.Infrastructure; using Wox.Infrastructure.Image; using Wox.Plugin; using Wox.Plugin.Common; using Wox.Plugin.Common.Win32; using Wox.Plugin.Logger; using PackageVersion = Microsoft.Plugin.Program.Programs.UWP.PackageVersion; namespace Microsoft.Plugin.Program.Programs { [Serializable] public class UWPApplication : IProgram { private static readonly IFileSystem FileSystem = new FileSystem(); private static readonly IPath Path = FileSystem.Path; private static readonly IFile File = FileSystem.File; public string AppListEntry { get; set; } public string UniqueIdentifier { get; set; } public string DisplayName { get; set; } public string Description { get; set; } public string UserModelId { get; set; } public string BackgroundColor { get; set; } public string EntryPoint { get; set; } public string Name => DisplayName; public string Location => Package.Location; // Localized path based on windows display language public string LocationLocalized => Package.LocationLocalized; public bool Enabled { get; set; } public bool CanRunElevated { get; set; } public string LogoPath { get; set; } public LogoType LogoType { get; set; } public UWP Package { get; set; } private string logoUri; private const string ContrastWhite = "contrast-white"; private const string ContrastBlack = "contrast-black"; // Function to calculate the score of a result private int Score(string query) { var displayNameMatch = StringMatcher.FuzzySearch(query, DisplayName); var descriptionMatch = StringMatcher.FuzzySearch(query, Description); var score = new[] { displayNameMatch.Score, descriptionMatch.Score / 2 }.Max(); return score; } // Function to set the subtitle based on the Type of application private static string SetSubtitle() { return Properties.Resources.powertoys_run_plugin_program_packaged_application; } public Result Result(string query, string queryArguments, IPublicAPI api) { ArgumentNullException.ThrowIfNull(api); var score = Score(query); if (score <= 0) { // no need to create result if score is 0 return null; } var result = new Result { SubTitle = SetSubtitle(), Icon = Logo, Score = score, ContextData = this, ProgramArguments = queryArguments, Action = e => { Launch(api, queryArguments); return true; }, }; // To set the title to always be the displayname of the packaged application result.Title = DisplayName; result.TitleHighlightData = StringMatcher.FuzzySearch(query, Name).MatchData; // Using CurrentCulture since this is user facing var toolTipTitle = result.Title; var toolTipText = LocationLocalized; result.ToolTipData = new ToolTipData(toolTipTitle, toolTipText); return result; } public List ContextMenus(string queryArguments, IPublicAPI api) { ArgumentNullException.ThrowIfNull(api); var contextMenus = new List(); if (CanRunElevated) { contextMenus.Add( new ContextMenuResult { PluginName = Assembly.GetExecutingAssembly().GetName().Name, Title = Properties.Resources.wox_plugin_program_run_as_administrator, Glyph = "\xE7EF", FontFamily = "Segoe Fluent Icons,Segoe MDL2 Assets", AcceleratorKey = Key.Enter, AcceleratorModifiers = ModifierKeys.Control | ModifierKeys.Shift, Action = _ => { string command = "shell:AppsFolder\\" + UniqueIdentifier; command = Environment.ExpandEnvironmentVariables(command.Trim()); var info = ShellCommand.SetProcessStartInfo(command, verb: "runas"); info.UseShellExecute = true; info.Arguments = queryArguments; Process.Start(info); return true; }, }); // We don't add context menu to 'run as different user', because UWP applications normally installed per user and not for all users. } contextMenus.Add( new ContextMenuResult { PluginName = Assembly.GetExecutingAssembly().GetName().Name, Title = Properties.Resources.wox_plugin_program_open_containing_folder, Glyph = "\xE838", FontFamily = "Segoe Fluent Icons,Segoe MDL2 Assets", AcceleratorKey = Key.E, AcceleratorModifiers = ModifierKeys.Control | ModifierKeys.Shift, Action = _ => { Helper.OpenInShell(Package.Location); return true; }, }); contextMenus.Add(new ContextMenuResult { PluginName = Assembly.GetExecutingAssembly().GetName().Name, Title = Properties.Resources.wox_plugin_program_open_in_console, Glyph = "\xE756", FontFamily = "Segoe Fluent Icons,Segoe MDL2 Assets", AcceleratorKey = Key.C, AcceleratorModifiers = ModifierKeys.Control | ModifierKeys.Shift, Action = (context) => { try { Helper.OpenInConsole(Package.Location); return true; } catch (Exception e) { Log.Exception($"Failed to open {Name} in console, {e.Message}", e, GetType()); return false; } }, }); return contextMenus; } private async void Launch(IPublicAPI api, string queryArguments) { var appManager = new ApplicationActivationHelper.ApplicationActivationManager(); const ApplicationActivationHelper.ActivateOptions noFlags = ApplicationActivationHelper.ActivateOptions.None; await Task.Run(() => { try { appManager.ActivateApplication(UserModelId, queryArguments, noFlags, out var unusedPid); } catch (Exception ex) { ProgramLogger.Exception($"Unable to launch UWP {DisplayName}", ex, MethodBase.GetCurrentMethod().DeclaringType, queryArguments); var name = "插件: " + Properties.Resources.wox_plugin_program_plugin_name; var message = $"{Properties.Resources.powertoys_run_plugin_program_uwp_failed}: {DisplayName}"; api.ShowMsg(name, message, string.Empty); } }).ConfigureAwait(false); } public UWPApplication(IAppxManifestApplication manifestApp, UWP package) { ArgumentNullException.ThrowIfNull(manifestApp); var hr = manifestApp.GetAppUserModelId(out var tmpUserModelId); UserModelId = AppxPackageHelper.CheckHRAndReturnOrThrow(hr, tmpUserModelId); hr = manifestApp.GetAppUserModelId(out var tmpUniqueIdentifier); UniqueIdentifier = AppxPackageHelper.CheckHRAndReturnOrThrow(hr, tmpUniqueIdentifier); hr = manifestApp.GetStringValue("DisplayName", out var tmpDisplayName); DisplayName = AppxPackageHelper.CheckHRAndReturnOrThrow(hr, tmpDisplayName); hr = manifestApp.GetStringValue("Description", out var tmpDescription); Description = AppxPackageHelper.CheckHRAndReturnOrThrow(hr, tmpDescription); hr = manifestApp.GetStringValue("BackgroundColor", out var tmpBackgroundColor); BackgroundColor = AppxPackageHelper.CheckHRAndReturnOrThrow(hr, tmpBackgroundColor); hr = manifestApp.GetStringValue("EntryPoint", out var tmpEntryPoint); EntryPoint = AppxPackageHelper.CheckHRAndReturnOrThrow(hr, tmpEntryPoint); Package = package ?? throw new ArgumentNullException(nameof(package)); DisplayName = ResourceFromPri(package.FullName, DisplayName); Description = ResourceFromPri(package.FullName, Description); logoUri = LogoUriFromManifest(manifestApp); Enabled = true; CanRunElevated = IfApplicationCanRunElevated(); } private bool IfApplicationCanRunElevated() { if (EntryPoint == "Windows.FullTrustApplication") { return true; } else { var manifest = Package.Location + "\\AppxManifest.xml"; if (File.Exists(manifest)) { try { // Check the manifest to verify if the Trust Level for the application is "mediumIL" var file = File.ReadAllText(manifest); var xmlDoc = new XmlDocument(); xmlDoc.LoadXml(file); var xmlRoot = xmlDoc.DocumentElement; var namespaceManager = new XmlNamespaceManager(xmlDoc.NameTable); namespaceManager.AddNamespace("uap10", "http://schemas.microsoft.com/appx/manifest/uap/windows10/10"); var trustLevelNode = xmlRoot.SelectSingleNode("//*[local-name()='Application' and @uap10:TrustLevel]", namespaceManager); // According to https://learn.microsoft.com/windows/apps/desktop/modernize/grant-identity-to-nonpackaged-apps#create-a-package-manifest-for-the-sparse-package and https://learn.microsoft.com/uwp/schemas/appxpackage/uapmanifestschema/element-application#attributes if (trustLevelNode?.Attributes["uap10:TrustLevel"]?.Value == "mediumIL") { return true; } } catch (Exception e) { ProgramLogger.Exception($"Unable to parse manifest file for {DisplayName}", e, MethodBase.GetCurrentMethod().DeclaringType, manifest); } } } return false; } internal string ResourceFromPri(string packageFullName, string resourceReference) { const string prefix = "ms-resource:"; // Using OrdinalIgnoreCase since this is used internally if (!string.IsNullOrWhiteSpace(resourceReference) && resourceReference.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) { // magic comes from @talynone // https://github.com/talynone/Wox.Plugin.WindowsUniversalAppLauncher/blob/master/StoreAppLauncher/Helpers/NativeApiHelper.cs#L139-L153 string key = resourceReference.Substring(prefix.Length); string parsed; string parsedFallback = string.Empty; // Using Ordinal/OrdinalIgnoreCase since these are used internally if (key.StartsWith("//", StringComparison.Ordinal)) { parsed = prefix + key; } else if (key.StartsWith('/')) { parsed = prefix + "//" + key; } else if (key.Contains("resources", StringComparison.OrdinalIgnoreCase)) { parsed = prefix + key; } else { parsed = prefix + "///resources/" + key; // e.g. for Windows Terminal version >= 1.12 DisplayName and Description resources are not in the 'resources' subtree parsedFallback = prefix + "///" + key; } var outBuffer = new StringBuilder(128); string source = $"@{{{packageFullName}? {parsed}}}"; var capacity = (uint)outBuffer.Capacity; var hResult = NativeMethods.SHLoadIndirectString(source, outBuffer, capacity, IntPtr.Zero); if (hResult != HRESULT.S_OK) { if (!string.IsNullOrEmpty(parsedFallback)) { string sourceFallback = $"@{{{packageFullName}? {parsedFallback}}}"; hResult = NativeMethods.SHLoadIndirectString(sourceFallback, outBuffer, capacity, IntPtr.Zero); if (hResult == HRESULT.S_OK) { var loaded = outBuffer.ToString(); if (!string.IsNullOrEmpty(loaded)) { return loaded; } else { ProgramLogger.Exception($"Can't load null or empty result pri {sourceFallback} in uwp location {Package.Location}", new ArgumentNullException(null), GetType(), Package.Location); return string.Empty; } } } // https://github.com/Wox-launcher/Wox/issues/964 // known hresult 2147942522: // 'Microsoft Corporation' violates pattern constraint of '\bms-resource:.{1,256}'. // for // Microsoft.MicrosoftOfficeHub_17.7608.23501.0_x64__8wekyb3d8bbwe: ms-resource://Microsoft.MicrosoftOfficeHub/officehubintl/AppManifest_GetOffice_Description // Microsoft.BingFoodAndDrink_3.0.4.336_x64__8wekyb3d8bbwe: ms-resource:AppDescription var e = Marshal.GetExceptionForHR((int)hResult); ProgramLogger.Exception($"Load pri failed {source} with HResult {hResult} and location {Package.Location}", e, GetType(), Package.Location); return string.Empty; } else { var loaded = outBuffer.ToString(); if (!string.IsNullOrEmpty(loaded)) { return loaded; } else { ProgramLogger.Exception($"Can't load null or empty result pri {source} in uwp location {Package.Location}", new ArgumentNullException(null), GetType(), Package.Location); return string.Empty; } } } else { return resourceReference; } } private static readonly Dictionary _logoKeyFromVersion = new Dictionary { { PackageVersion.Windows10, "Square44x44Logo" }, { PackageVersion.Windows81, "Square30x30Logo" }, { PackageVersion.Windows8, "SmallLogo" }, }; internal string LogoUriFromManifest(IAppxManifestApplication app) { if (_logoKeyFromVersion.TryGetValue(Package.Version, out var key)) { var hr = app.GetStringValue(key, out var logoUriFromApp); _ = AppxPackageHelper.CheckHRAndReturnOrThrow(hr, logoUriFromApp); return logoUriFromApp; } else { return string.Empty; } } public void UpdateLogoPath(Theme theme) { LogoPathFromUri(logoUri, theme); } // scale factors on win10: https://learn.microsoft.com/windows/uwp/controls-and-patterns/tiles-and-notifications-app-assets#asset-size-tables, private static readonly Dictionary> _scaleFactors = new Dictionary> { { PackageVersion.Windows10, new List { 100, 125, 150, 200, 400 } }, { PackageVersion.Windows81, new List { 100, 120, 140, 160, 180 } }, { PackageVersion.Windows8, new List { 100 } }, }; private bool SetScaleIcons(string path, string colorscheme, bool highContrast = false) { var extension = Path.GetExtension(path); if (extension != null) { var end = path.Length - extension.Length; var prefix = path.Substring(0, end); var paths = new List { }; if (!highContrast) { paths.Add(path); } if (_scaleFactors.TryGetValue(Package.Version, out List factors)) { foreach (var factor in factors) { if (highContrast) { paths.Add($"{prefix}.scale-{factor}_{colorscheme}{extension}"); paths.Add($"{prefix}.{colorscheme}_scale-{factor}{extension}"); } else { paths.Add($"{prefix}.scale-{factor}{extension}"); } } } var selectedIconPath = paths.FirstOrDefault(File.Exists); if (!string.IsNullOrEmpty(selectedIconPath)) { LogoPath = selectedIconPath; if (highContrast) { LogoType = LogoType.HighContrast; } else { LogoType = LogoType.Colored; } return true; } } return false; } private bool SetTargetSizeIcon(string path, string colorscheme, bool highContrast = false) { var extension = Path.GetExtension(path); if (extension != null) { var end = path.Length - extension.Length; var prefix = path.Substring(0, end); var paths = new List { }; const int appIconSize = 36; var targetSizes = new List { 16, 24, 30, 36, 44, 60, 72, 96, 128, 180, 256 }.AsParallel(); var pathFactorPairs = new Dictionary(); foreach (var factor in targetSizes) { if (highContrast) { string suffixThemePath = $"{prefix}.targetsize-{factor}_{colorscheme}{extension}"; string prefixThemePath = $"{prefix}.{colorscheme}_targetsize-{factor}{extension}"; paths.Add(suffixThemePath); paths.Add(prefixThemePath); pathFactorPairs.Add(suffixThemePath, factor); pathFactorPairs.Add(prefixThemePath, factor); } else { string simplePath = $"{prefix}.targetsize-{factor}{extension}"; string altformUnPlatedPath = $"{prefix}.targetsize-{factor}_altform-unplated{extension}"; paths.Add(simplePath); paths.Add(altformUnPlatedPath); pathFactorPairs.Add(simplePath, factor); pathFactorPairs.Add(altformUnPlatedPath, factor); } } var selectedIconPath = paths.OrderBy(x => Math.Abs(pathFactorPairs.GetValueOrDefault(x) - appIconSize)).FirstOrDefault(File.Exists); if (!string.IsNullOrEmpty(selectedIconPath)) { LogoPath = selectedIconPath; if (highContrast) { LogoType = LogoType.HighContrast; } else { LogoType = LogoType.Colored; } return true; } } return false; } private bool SetColoredIcon(string path, string colorscheme) { var isSetColoredScaleIcon = SetScaleIcons(path, colorscheme); if (isSetColoredScaleIcon) { return true; } var isSetColoredTargetIcon = SetTargetSizeIcon(path, colorscheme); if (isSetColoredTargetIcon) { return true; } var isSetHighContrastScaleIcon = SetScaleIcons(path, colorscheme, true); if (isSetHighContrastScaleIcon) { return true; } var isSetHighContrastTargetIcon = SetTargetSizeIcon(path, colorscheme, true); if (isSetHighContrastTargetIcon) { return true; } return false; } private bool SetHighContrastIcon(string path, string colorscheme) { var isSetHighContrastScaleIcon = SetScaleIcons(path, colorscheme, true); if (isSetHighContrastScaleIcon) { return true; } var isSetHighContrastTargetIcon = SetTargetSizeIcon(path, colorscheme, true); if (isSetHighContrastTargetIcon) { return true; } var isSetColoredScaleIcon = SetScaleIcons(path, colorscheme); if (isSetColoredScaleIcon) { return true; } var isSetColoredTargetIcon = SetTargetSizeIcon(path, colorscheme); if (isSetColoredTargetIcon) { return true; } return false; } internal void LogoPathFromUri(string uri, Theme theme) { // all https://learn.microsoft.com/windows/uwp/controls-and-patterns/tiles-and-notifications-app-assets // windows 10 https://msdn.microsoft.com/library/windows/apps/dn934817.aspx // windows 8.1 https://msdn.microsoft.com/library/windows/apps/hh965372.aspx#target_size // windows 8 https://msdn.microsoft.com/library/windows/apps/br211475.aspx string path; bool isLogoUriSet; // Using Ordinal since this is used internally with uri if (uri.Contains('\\', StringComparison.Ordinal)) { path = Path.Combine(Package.Location, uri); } else { // for C:\Windows\MiracastView etc path = Path.Combine(Package.Location, "Assets", uri); } switch (theme) { case Theme.HighContrastBlack: case Theme.HighContrastOne: case Theme.HighContrastTwo: isLogoUriSet = SetHighContrastIcon(path, ContrastBlack); break; case Theme.HighContrastWhite: isLogoUriSet = SetHighContrastIcon(path, ContrastWhite); break; case Theme.Light: isLogoUriSet = SetColoredIcon(path, ContrastWhite); break; default: isLogoUriSet = SetColoredIcon(path, ContrastBlack); break; } if (!isLogoUriSet) { LogoPath = string.Empty; LogoType = LogoType.Error; ProgramLogger.Exception($"|{UserModelId} can't find logo uri for {uri} in package location: {Package.Location}", new FileNotFoundException(), GetType(), Package.Location); } } public ImageSource Logo() { if (LogoType == LogoType.Colored) { var logo = ImageFromPath(LogoPath); var platedImage = PlatedImage(logo); return platedImage; } else { return ImageFromPath(LogoPath); } } private const int _dpiScale100 = 96; private ImageSource PlatedImage(BitmapImage image) { if (!string.IsNullOrEmpty(BackgroundColor)) { string currentBackgroundColor; if (BackgroundColor == "transparent") { // Using InvariantCulture since this is internal currentBackgroundColor = SystemParameters.WindowGlassBrush.ToString(CultureInfo.InvariantCulture); } else { currentBackgroundColor = BackgroundColor; } var padding = 8; var width = image.Width + (2 * padding); var height = image.Height + (2 * padding); var x = 0; var y = 0; var group = new DrawingGroup(); var converted = ColorConverter.ConvertFromString(currentBackgroundColor); if (converted != null) { var color = (Color)converted; var brush = new SolidColorBrush(color); var pen = new Pen(brush, 1); var backgroundArea = new Rect(0, 0, width, height); var rectangleGeometry = new RectangleGeometry(backgroundArea, 8, 8); var rectDrawing = new GeometryDrawing(brush, pen, rectangleGeometry); group.Children.Add(rectDrawing); var imageArea = new Rect(x + padding, y + padding, image.Width, image.Height); var imageDrawing = new ImageDrawing(image, imageArea); group.Children.Add(imageDrawing); // http://stackoverflow.com/questions/6676072/get-system-drawing-bitmap-of-a-wpf-area-using-visualbrush var visual = new DrawingVisual(); var context = visual.RenderOpen(); context.DrawDrawing(group); context.Close(); var bitmap = new RenderTargetBitmap( Convert.ToInt32(width), Convert.ToInt32(height), _dpiScale100, _dpiScale100, PixelFormats.Pbgra32); bitmap.Render(visual); return bitmap; } else { ProgramLogger.Exception($"Unable to convert background string {BackgroundColor} to color for {Package.Location}", new InvalidOperationException(), GetType(), Package.Location); return new BitmapImage(new Uri(Constant.ErrorIcon)); } } else { // todo use windows theme as background return image; } } private BitmapImage ImageFromPath(string path) { if (File.Exists(path)) { var memoryStream = new MemoryStream(); using (var fileStream = File.OpenRead(path)) { fileStream.CopyTo(memoryStream); memoryStream.Position = 0; var image = new BitmapImage(); image.BeginInit(); image.StreamSource = memoryStream; image.EndInit(); return image; } } else { ProgramLogger.Exception($"Unable to get logo for {UserModelId} from {path} and located in {Package.Location}", new FileNotFoundException(), GetType(), path); return new BitmapImage(new Uri(ImageLoader.ErrorIconPath)); } } public override string ToString() { return $"{DisplayName}: {Description}"; } } public enum LogoType { Error, Colored, HighContrast, } } ================================================ FILE: PowerToys/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Properties/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 以管理员身份运行(Ctrl+Shift+Enter) 打开所属文件夹(Ctrl+Shift+E) 在命令行打开路径(Ctrl+Shift+C) 应用程序 搜索应用程序 Win32 应用 网页快捷方式应用 Web 应用 命令行 UWP 应用 名称 路径 文件夹 文件 无法启动 UWP 无法启动 以其他用户身份运行(Ctrl+Shift+U) ================================================ FILE: PowerToys/src/modules/launcher/Plugins/Microsoft.Plugin.Shell/Main.cs ================================================ // Copyright (c) Microsoft Corporation // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Abstractions; using System.Linq; using System.Reflection; using System.Text; using System.Windows.Input; using ManagedCommon; using Microsoft.Plugin.Shell.Properties; using Microsoft.PowerToys.Settings.UI.Library; using Wox.Infrastructure.Storage; using Wox.Plugin; using Wox.Plugin.Common; using Wox.Plugin.Logger; using Control = System.Windows.Controls.Control; namespace Microsoft.Plugin.Shell { public class Main : IPlugin, IPluginI18n, ISettingProvider, IContextMenu, ISavable { private static readonly IFileSystem FileSystem = new FileSystem(); private static readonly IPath Path = FileSystem.Path; private static readonly IFile File = FileSystem.File; private static readonly IDirectory Directory = FileSystem.Directory; private readonly ShellPluginSettings _settings; private readonly PluginJsonStorage _storage; private static readonly CompositeFormat WoxPluginCmdCmdHasBeenExecutedTimes = System.Text.CompositeFormat.Parse(Properties.Resources.wox_plugin_cmd_cmd_has_been_executed_times); private string IconPath { get; set; } public string Name => Properties.Resources.wox_plugin_cmd_plugin_name; public string Description => Properties.Resources.wox_plugin_cmd_plugin_description; public static string PluginID => "D409510CD0D2481F853690A07E6DC426"; public IEnumerable AdditionalOptions => new List() { new PluginAdditionalOption() { Key = "ShellCommandExecution", DisplayLabel = Resources.wox_shell_command_execution, DisplayDescription = Resources.wox_shell_command_execution_description, PluginOptionType = PluginAdditionalOption.AdditionalOptionType.Combobox, ComboBoxItems = new List> { new KeyValuePair(Resources.find_executable_file_and_run_it, "2"), new KeyValuePair(Resources.run_command_in_command_prompt, "0"), new KeyValuePair(Resources.run_command_in_powershell, "1"), new KeyValuePair(Resources.run_command_in_powershell_seven, "6"), new KeyValuePair(Resources.run_command_in_windows_terminal_cmd, "5"), new KeyValuePair(Resources.run_command_in_windows_terminal_powershell, "3"), new KeyValuePair(Resources.run_command_in_windows_terminal_powershell_seven, "4"), }, ComboBoxValue = (int)_settings.Shell, }, new PluginAdditionalOption() { Key = "LeaveShellOpen", DisplayLabel = Resources.wox_leave_shell_open, Value = _settings.LeaveShellOpen, }, }; private PluginInitContext _context; private static readonly char[] Separator = new[] { ' ' }; public Main() { _storage = new PluginJsonStorage(); _settings = _storage.Load(); } public void Save() { _storage.Save(); } public List Query(Query query) { ArgumentNullException.ThrowIfNull(query); List results = new List(); string cmd = query.Search; if (string.IsNullOrEmpty(cmd)) { return ResultsFromlHistory(); } else { var queryCmd = GetCurrentCmd(cmd); results.Add(queryCmd); var history = GetHistoryCmds(cmd, queryCmd); results.AddRange(history); try { IEnumerable folderPluginResults = Folder.Main.GetFolderPluginResults(query); results.AddRange(folderPluginResults); } catch (Exception e) { Log.Exception($"Exception when query for <{query}>", e, GetType()); } return results; } } private List GetHistoryCmds(string cmd, Result result) { IEnumerable history = _settings.Count.Where(o => o.Key.Contains(cmd, StringComparison.CurrentCultureIgnoreCase)) .OrderByDescending(o => o.Value) .Select(m => { if (m.Key == cmd) { // Using CurrentCulture since this is user facing result.SubTitle = Properties.Resources.wox_plugin_cmd_plugin_name + ": " + string.Format(CultureInfo.CurrentCulture, WoxPluginCmdCmdHasBeenExecutedTimes, m.Value); return null; } var ret = new Result { Title = m.Key, // Using CurrentCulture since this is user facing SubTitle = Properties.Resources.wox_plugin_cmd_plugin_name + ": " + string.Format(CultureInfo.CurrentCulture, WoxPluginCmdCmdHasBeenExecutedTimes, m.Value), IcoPath = IconPath, Action = c => { Execute(Process.Start, PrepareProcessStartInfo(m.Key)); return true; }, }; return ret; }).Where(o => o != null).Take(4); return history.ToList(); } private Result GetCurrentCmd(string cmd) { Result result = new Result { Title = cmd, Score = 5000, SubTitle = Properties.Resources.wox_plugin_cmd_plugin_name + ": " + Properties.Resources.wox_plugin_cmd_execute_through_shell, IcoPath = IconPath, Action = c => { Execute(Process.Start, PrepareProcessStartInfo(cmd)); return true; }, }; return result; } private List ResultsFromlHistory() { IEnumerable history = _settings.Count.OrderByDescending(o => o.Value) .Select(m => new Result { Title = m.Key, // Using CurrentCulture since this is user facing SubTitle = Properties.Resources.wox_plugin_cmd_plugin_name + ": " + string.Format(CultureInfo.CurrentCulture, WoxPluginCmdCmdHasBeenExecutedTimes, m.Value), IcoPath = IconPath, Action = c => { Execute(Process.Start, PrepareProcessStartInfo(m.Key)); return true; }, }).Take(5); return history.ToList(); } private ProcessStartInfo PrepareProcessStartInfo(string command, RunAsType runAs = RunAsType.None) { string trimmedCommand = command.Trim(); command = Environment.ExpandEnvironmentVariables(trimmedCommand); var workingDirectory = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); // Set runAsArg string runAsVerbArg = string.Empty; if (runAs == RunAsType.OtherUser) { runAsVerbArg = "runAsUser"; } else if (runAs == RunAsType.Administrator || _settings.RunAsAdministrator) { runAsVerbArg = "runAs"; } ProcessStartInfo info; if (_settings.Shell == ExecutionShell.Cmd) { var arguments = _settings.LeaveShellOpen ? $"/k \"{command}\"" : $"/c \"{command}\" & pause"; info = ShellCommand.SetProcessStartInfo("cmd.exe", workingDirectory, arguments, runAsVerbArg); } else if (_settings.Shell == ExecutionShell.Powershell) { string arguments; if (_settings.LeaveShellOpen) { arguments = $"-NoExit \"{command}\""; } else { arguments = $"\"{command} ; Read-Host -Prompt \\\"{Resources.run_plugin_cmd_wait_message}\\\"\""; } info = ShellCommand.SetProcessStartInfo("powershell.exe", workingDirectory, arguments, runAsVerbArg); } else if (_settings.Shell == ExecutionShell.PowerShellSeven) { string arguments; if (_settings.LeaveShellOpen) { arguments = $"-NoExit -C \"{command}\""; } else { arguments = $"-C \"{command} ; Read-Host -Prompt \\\"{Resources.run_plugin_cmd_wait_message}\\\"\""; } info = ShellCommand.SetProcessStartInfo("pwsh.exe", workingDirectory, arguments, runAsVerbArg); } else if (_settings.Shell == ExecutionShell.WindowsTerminalCmd) { string arguments; if (_settings.LeaveShellOpen) { arguments = $"cmd.exe /k \"{command}\""; } else { arguments = $"cmd.exe /c \"{command}\" & pause"; } info = ShellCommand.SetProcessStartInfo("wt.exe", workingDirectory, arguments, runAsVerbArg); } else if (_settings.Shell == ExecutionShell.WindowsTerminalPowerShell) { string arguments; if (_settings.LeaveShellOpen) { arguments = $"powershell -NoExit -C \"{command}\""; } else { arguments = $"powershell -C \"{command}\""; } info = ShellCommand.SetProcessStartInfo("wt.exe", workingDirectory, arguments, runAsVerbArg); } else if (_settings.Shell == ExecutionShell.WindowsTerminalPowerShellSeven) { string arguments; if (_settings.LeaveShellOpen) { arguments = $"pwsh.exe -NoExit -C \"{command}\""; } else { arguments = $"pwsh.exe -C \"{command}\""; } info = ShellCommand.SetProcessStartInfo("wt.exe", workingDirectory, arguments, runAsVerbArg); } else if (_settings.Shell == ExecutionShell.RunCommand) { // Open explorer if the path is a file or directory if (Directory.Exists(command) || File.Exists(command)) { info = ShellCommand.SetProcessStartInfo("explorer.exe", arguments: command, verb: runAsVerbArg); } else { var parts = command.Split(Separator, 2); if (parts.Length == 2) { var filename = parts[0]; if (ExistInPath(filename)) { var arguments = parts[1]; if (_settings.LeaveShellOpen) { // Wrap the command in a cmd.exe process info = ShellCommand.SetProcessStartInfo("cmd.exe", workingDirectory, $"/k \"{filename} {arguments}\"", runAsVerbArg); } else { info = ShellCommand.SetProcessStartInfo(filename, workingDirectory, arguments, runAsVerbArg); } } else { if (_settings.LeaveShellOpen) { // Wrap the command in a cmd.exe process info = ShellCommand.SetProcessStartInfo("cmd.exe", workingDirectory, $"/k \"{command}\"", runAsVerbArg); } else { info = ShellCommand.SetProcessStartInfo(command, verb: runAsVerbArg); } } } else { if (_settings.LeaveShellOpen) { // Wrap the command in a cmd.exe process info = ShellCommand.SetProcessStartInfo("cmd.exe", workingDirectory, $"/k \"{command}\"", runAsVerbArg); } else { info = ShellCommand.SetProcessStartInfo(command, verb: runAsVerbArg); } } } } else { throw new NotImplementedException(); } info.UseShellExecute = true; _settings.AddCmdHistory(trimmedCommand); return info; } private enum RunAsType { None, Administrator, OtherUser, } private void Execute(Func startProcess, ProcessStartInfo info) { try { startProcess(info); } catch (FileNotFoundException e) { var name = "插件: " + Properties.Resources.wox_plugin_cmd_plugin_name; var message = $"{Properties.Resources.wox_plugin_cmd_command_not_found}: {e.Message}"; _context.API.ShowMsg(name, message); } catch (Win32Exception e) { var name = "插件: " + Properties.Resources.wox_plugin_cmd_plugin_name; var message = $"{Properties.Resources.wox_plugin_cmd_command_failed}: {e.Message}"; _context.API.ShowMsg(name, message); } } private static bool ExistInPath(string filename) { if (File.Exists(filename)) { return true; } else { var values = Environment.GetEnvironmentVariable("PATH"); if (values != null) { foreach (var path in values.Split(';')) { var path1 = Path.Combine(path, filename); var path2 = Path.Combine(path, filename + ".exe"); if (File.Exists(path1) || File.Exists(path2)) { return true; } } return false; } else { return false; } } } public void Init(PluginInitContext context) { this._context = context; _context.API.ThemeChanged += OnThemeChanged; UpdateIconPath(_context.API.GetCurrentTheme()); } // Todo : Update with theme based IconPath private void UpdateIconPath(Theme theme) { if (theme == Theme.Light || theme == Theme.HighContrastWhite) { IconPath = "Images/shell.light.png"; } else { IconPath = "Images/shell.dark.png"; } } private void OnThemeChanged(Theme currentTheme, Theme newTheme) { UpdateIconPath(newTheme); } public Control CreateSettingPanel() { throw new NotImplementedException(); } public string GetTranslatedPluginTitle() { return Properties.Resources.wox_plugin_cmd_plugin_name; } public string GetTranslatedPluginDescription() { return Properties.Resources.wox_plugin_cmd_plugin_description; } public List LoadContextMenus(Result selectedResult) { var resultlist = new List { new ContextMenuResult { PluginName = Assembly.GetExecutingAssembly().GetName().Name, Title = Properties.Resources.wox_plugin_cmd_run_as_administrator, Glyph = "\xE7EF", FontFamily = "Segoe Fluent Icons,Segoe MDL2 Assets", AcceleratorKey = Key.Enter, AcceleratorModifiers = ModifierKeys.Control | ModifierKeys.Shift, Action = c => { Execute(Process.Start, PrepareProcessStartInfo(selectedResult.Title, RunAsType.Administrator)); return true; }, }, new ContextMenuResult { PluginName = Assembly.GetExecutingAssembly().GetName().Name, Title = Properties.Resources.wox_plugin_cmd_run_as_user, Glyph = "\xE7EE", FontFamily = "Segoe Fluent Icons,Segoe MDL2 Assets", AcceleratorKey = Key.U, AcceleratorModifiers = ModifierKeys.Control | ModifierKeys.Shift, Action = _ => { Execute(Process.Start, PrepareProcessStartInfo(selectedResult.Title, RunAsType.OtherUser)); return true; }, }, }; return resultlist; } public void UpdateSettings(PowerLauncherPluginSettings settings) { var leaveShellOpen = false; var shellOption = 2; if (settings != null && settings.AdditionalOptions != null) { var optionLeaveShellOpen = settings.AdditionalOptions.FirstOrDefault(x => x.Key == "LeaveShellOpen"); leaveShellOpen = optionLeaveShellOpen?.Value ?? leaveShellOpen; _settings.LeaveShellOpen = leaveShellOpen; var optionShell = settings.AdditionalOptions.FirstOrDefault(x => x.Key == "ShellCommandExecution"); shellOption = optionShell?.ComboBoxValue ?? shellOption; _settings.Shell = (ExecutionShell)shellOption; } Save(); } } } ================================================ FILE: PowerToys/src/modules/launcher/Plugins/Microsoft.Plugin.Shell/Properties/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 命令行 执行命令(例如 “ping”、“cmd”) 此命令已经执行了 {0} 次 执行此命令 以管理员身份打开/执行(Ctrl+Shift+Enter) 命令执行错误 命令不存在 以管理员身份打开/执行(Ctrl+Shift+U) 保持终端窗口打开 命令执行方式 命令提示符 (cmd.exe) PowerShell (PowerShell.exe) 寻找并执行可执行文件 PowerShell 7 (pwsh.exe) 终端中打开命令提示符 (cmd.exe) 终端中打开 PowerShell (PowerShell.exe) 终端中打开 PowerShell 7 (pwsh.exe) 按下 Enter 继续 "Enter" means the Enter key on the keyboard. 指定使用 Windows 终端来执行就会忽略默认终端应用程序设置 ================================================ FILE: PowerToys/src/modules/launcher/Plugins/Microsoft.Plugin.Uri/Main.cs ================================================ // 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 ManagedCommon; using Microsoft.Plugin.Uri.UriHelper; using Wox.Infrastructure; using Wox.Infrastructure.Storage; using Wox.Plugin; using BrowserInfo = Wox.Plugin.Common.DefaultBrowserInfo; namespace Microsoft.Plugin.Uri { public class Main : IPlugin, IPluginI18n, IContextMenu, ISavable, IReloadable, IDisposable { private readonly ExtendedUriParser _uriParser; private readonly UriResolver _uriResolver; private readonly PluginJsonStorage _storage; private bool _disposed; private UriSettings _uriSettings; public Main() { _storage = new PluginJsonStorage(); _uriSettings = _storage.Load(); _uriParser = new ExtendedUriParser(); _uriResolver = new UriResolver(); } public string DefaultIconPath { get; set; } public PluginInitContext Context { get; protected set; } public string Name => Properties.Resources.Microsoft_plugin_uri_plugin_name; public string Description => Properties.Resources.Microsoft_plugin_uri_plugin_description; public static string PluginID => "03276A39D4E9417C8FFD200B0EE5E871"; public List LoadContextMenus(Result selectedResult) { return new List(0); } public List Query(Query query) { var results = new List(); if (string.IsNullOrWhiteSpace(query?.Search) && BrowserInfo.IsDefaultBrowserSet) { results.Add(new Result { Title = Properties.Resources.Microsoft_plugin_uri_default_browser, SubTitle = BrowserInfo.Path, IcoPath = DefaultIconPath, Action = action => { if (!Helper.OpenInShell(BrowserInfo.Path)) { var title = $"插件: {Properties.Resources.Microsoft_plugin_uri_plugin_name}"; var message = $"{Properties.Resources.Microsoft_plugin_uri_open_failed}: "; Context.API.ShowMsg(title, message); return false; } return true; }, }); return results; } if (!string.IsNullOrEmpty(query?.Search) && _uriParser.TryParse(query.Search, out var webUriResult, out var systemUriResult) && _uriResolver.IsValidHost(webUriResult)) { if (webUriResult is not null) { var resultString = webUriResult.ToString(); results.Add(new Result { Title = resultString, SubTitle = Properties.Resources.Microsoft_plugin_uri_website, IcoPath = BrowserInfo.IconPath, Action = action => { if (!Helper.OpenInShell(resultString)) { var title = $"插件: {Properties.Resources.Microsoft_plugin_uri_plugin_name}"; var message = $"{Properties.Resources.Microsoft_plugin_uri_open_failed}: {resultString}"; Context.API.ShowMsg(title, message); return false; } return true; }, }); } if (systemUriResult is not null) { var resultString = systemUriResult.ToString(); results.Add(new Result { Title = resultString, SubTitle = Properties.Resources.Microsoft_plugin_uri_open, IcoPath = DefaultIconPath, Action = action => { if (!Helper.OpenInShell(resultString)) { var title = $"插件: {Properties.Resources.Microsoft_plugin_uri_plugin_name}"; var message = $"{Properties.Resources.Microsoft_plugin_uri_open_failed}: {resultString}"; Context.API.ShowMsg(title, message); return false; } return true; }, }); } } return results; } public void Init(PluginInitContext context) { Context = context ?? throw new ArgumentNullException(nameof(context)); Context.API.ThemeChanged += OnThemeChanged; UpdateIconPath(Context.API.GetCurrentTheme()); BrowserInfo.UpdateIfTimePassed(); } public string GetTranslatedPluginTitle() { return Properties.Resources.Microsoft_plugin_uri_plugin_name; } public string GetTranslatedPluginDescription() { return Properties.Resources.Microsoft_plugin_uri_plugin_description; } public void Save() { _storage.Save(); } private void OnThemeChanged(Theme oldtheme, Theme newTheme) { UpdateIconPath(newTheme); } private void UpdateIconPath(Theme theme) { if (theme == Theme.Light || theme == Theme.HighContrastWhite) { DefaultIconPath = "Images/uri.light.png"; } else { DefaultIconPath = "Images/uri.dark.png"; } } public void ReloadData() { if (Context is null) { return; } BrowserInfo.UpdateIfTimePassed(); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!_disposed && disposing) { if (Context != null && Context.API != null) { Context.API.ThemeChanged -= OnThemeChanged; } _disposed = true; } } } } ================================================ FILE: PowerToys/src/modules/launcher/Plugins/Microsoft.Plugin.Uri/Properties/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 无法打开默认浏览器 打开默认浏览器 打开网址 无法打开网址 打开网址和 UNC 网络共享 网址 在浏览器中打开 ================================================ FILE: PowerToys/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Properties/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 窗口操作 通过搜索窗口标题来操作窗口 进程 结束进程时弹窗确认 隐藏资源管理器进程 Explorer is here the program File Explorer 需要管理员权限才能结束进程时,隐藏“结束进程”按钮 仅显示在桌面可见的进程 在结果中显示进程所在的虚拟桌面 在结果中显示进程 ID 使用操作后不关闭启动器 虚拟桌面 No. Short version of "Number" 进程名称 进程 ID 文件夹窗口需要是独立的进程,点击修改资源管理器设置 Explorer is here the program File Explorer 提示:无法结束资源管理器进程 Explorer is here the program File Explorer 关闭窗口 结束进程 你即将结束以下进程: 确定执行? 结束进程确认 由于这是一个 UWP 进程,所有的实例都将被结束。确定执行? 结束进程,同时结束其子进程 其仅在使用唤醒词进行搜索时显示 使用该功能须谨慎,结束整个进程树可能导致特定应用程序崩溃 如果启用了弹窗确认,则此功能无法生效 若有多个虚拟桌面,程序在哪个虚拟桌面的信息 无响应 ================================================ FILE: PowerToys/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/Properties/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 计算器 进行数学计算,例如 5*3-2 发生 NaN 错误,结果不是数字 表达式错误或不完整,可能缺少括号 复制计算结果 复制失败,请稍后再试 计算错误 暂不支持使用方括号 结果超过十进制数字范围 输入英语格式数字 强制输入英语格式的数字,例如 '{0}' {0} is a placeholder and will be replaced in code. 输出英语格式数字 强制输出英语格式的数字,例如 '{0}' {0} is a placeholder and will be replaced in code. 零不可以作为除数 式子以等号结尾时,自动填入运算结果 使用唤醒词激活时,在式子后输入 "=" 就用运算结果替换整个式子(例如输入 '=5*3-2=' 就会变成 '=13') 三角函数单位 Title text for trig unit mode. 指定三角函数运算使用的角度单位制 Description text for trig mode setting. 弧度(圆=2π) Text for angle unit. 角度(圆=360) Text to use for angle unit. 梯度(圆=400) Text for angle unit. ================================================ FILE: PowerToys/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.History/Properties/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 历史记录 访问启动器的搜索记录 无法处理该输入 ================================================ FILE: PowerToys/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.OneNote/Properties/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 OneNote 笔记 搜索 OneNone 桌面版中的笔记 ================================================ FILE: PowerToys/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.PowerToys/Properties/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 打开设置 (Ctrl+Shift+S) 管理员权限运行 (Ctrl+Shift+Enter) 取色器 "Color Picker" is the name of the utility 窗口镜像(重定向) "Crop And Lock" is the name of the utility, "Reparent" is the activation mode 窗口镜像(投影) "Crop And Lock" is the name of the utility, "Thumbnail" is the activation mode 环境变量编辑器 "Environment Variables" is the name of the utility 工作区编辑器 "Workspaces" is the name of the utility 窗口布局编辑器 "FancyZones" is the name of the utility 域名表编辑器 "Hosts File Editor" is the name of the utility 打开 PowerToys 功能 PowerToys 注册表预览工具 "Registry Preview" is the name of the utility 屏幕标尺 "Screen Ruler" is the name of the utility 快捷键指南 "Shortcut Guide" is the name of the utility PowerToys 功能 文本提取器 "Text Extractor" is the name of the utility ================================================ FILE: PowerToys/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Properties/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 注册表 浏览 Windows 注册表 "this built into Windows the OS. translate accordingly, https://learn.microsoft.com/troubleshoot/windows-server/performance/windows-registry-advanced-users is an example of it translated in German" 复制路径 'The maximum size of a key name is 255 characters.' 路径: 'The maximum size of a key name is 255 characters.' 名称: 复制值名称 在注册表编辑器中打开 "registry editor" is the name of the OS built-in application 没有权限打开 Windows 注册表编辑器 "registry editor" is the name of the OS built-in application 注册表编辑器打开失败 "registry editor" is the name of the OS built-in application 子项: 值: 注册表项 注册表值 类型: See https://learn.microsoft.com/windows/win32/sysinfo/registry-value-types for proper context of how to translate 'type' 数据: See https://learn.microsoft.com/windows/win32/sysinfo/registry-value-types for proper context of how to translate 'value' 无类型 未知类型 复制值数据 ================================================ FILE: PowerToys/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Properties/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 正在恢复 名称 管理服务(Ctrl+O) 已暂停 正在暂停 管理 Windows 服务 服务 重启(Ctrl+R) 服务已重新启动 重启服务时出错 正在运行 启动(Enter) 已启动 服务已启动 启动类型 启动服务时出错 自动 自动(延迟启动) 开机启动 禁用 手动 系统 正在启动 状态 停止(Enter) 已停止 服务已停止 停止服务时出错 正在停止 ================================================ FILE: PowerToys/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/ResultHelper.cs ================================================ // Copyright (c) Microsoft Corporation // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Collections.Generic; using System.Threading.Tasks; using System.Windows; using System.Windows.Input; using Microsoft.PowerToys.Run.Plugin.System.Properties; using Wox.Plugin; using Wox.Plugin.Common.Win32; using Wox.Plugin.Logger; namespace Microsoft.PowerToys.Run.Plugin.System.Components { internal static class ResultHelper { private static bool executingEmptyRecycleBinTask; internal static bool ExecuteCommand(bool confirm, string confirmationMessage, Action command) { if (confirm) { MessageBoxResult messageBoxResult = MessageBox.Show( confirmationMessage, Resources.Microsoft_plugin_sys_confirmation, MessageBoxButton.YesNo, MessageBoxImage.Warning); if (messageBoxResult == MessageBoxResult.No) { return false; } } command(); return true; } internal static bool CopyToClipBoard(in string text) { try { Clipboard.Clear(); Clipboard.SetText(text); return true; } catch (Exception exception) { Log.Exception("Can't copy to clipboard", exception, typeof(ResultHelper)); return false; } } internal static async void EmptyRecycleBinAsync(bool settingEmptyRBSuccesMsg) { if (executingEmptyRecycleBinTask) { _ = MessageBox.Show(Resources.Microsoft_plugin_sys_RecycleBin_EmptyTaskRunning, "插件: " + Resources.Microsoft_plugin_sys_plugin_name, MessageBoxButton.OK, MessageBoxImage.Information); return; } await Task.Run(() => EmptyRecycleBinTask(settingEmptyRBSuccesMsg)); } internal static List GetContextMenuForResult(Result result, bool settingEmptyRBSuccesMsg) { var contextMenu = new List(); if (!(result?.ContextData is SystemPluginContext contextData)) { return contextMenu; } if (contextData.Type == ResultContextType.NetworkAdapterInfo) { contextMenu.Add(new ContextMenuResult() { AcceleratorKey = Key.C, AcceleratorModifiers = ModifierKeys.Control, FontFamily = "Segoe Fluent Icons,Segoe MDL2 Assets", Glyph = "\xE8C8", // E8C8 => Symbol: Copy Title = Resources.Microsoft_plugin_sys_CopyDetails, Action = _ => CopyToClipBoard(contextData.Data), }); } if (contextData.Type == ResultContextType.RecycleBinCommand) { contextMenu.Add(new ContextMenuResult() { AcceleratorKey = Key.Delete, AcceleratorModifiers = ModifierKeys.Shift, // Shift+Delete is the common key for deleting without recycle bin FontFamily = "Segoe Fluent Icons,Segoe MDL2 Assets", Glyph = "\xE74D", // E74D => Symbol: Delete Title = Resources.Microsoft_plugin_sys_RecycleBin_contextMenu, Action = _ => { EmptyRecycleBinAsync(settingEmptyRBSuccesMsg); return true; }, }); } return contextMenu; } /// /// Method to process the empty recycle bin command in a separate task /// private static void EmptyRecycleBinTask(bool settingEmptyRBSuccesMsg) { executingEmptyRecycleBinTask = true; // https://learn.microsoft.com/windows/win32/api/shellapi/nf-shellapi-shemptyrecyclebina/ // http://www.pinvoke.net/default.aspx/shell32/SHEmptyRecycleBin.html/ // If the recycle bin is already empty, it will return -2147418113 (0x8000FFFF (E_UNEXPECTED)) // If the user canceled the deletion task it will return 2147943623 (0x800704C7 (E_CANCELLED - The operation was canceled by the user.)) // On success it will return 0 (S_OK) var result = NativeMethods.SHEmptyRecycleBin(IntPtr.Zero, 0); if (result == (uint)HRESULT.E_UNEXPECTED) { _ = MessageBox.Show(Resources.Microsoft_plugin_sys_RecycleBin_IsEmpty, "插件: " + Resources.Microsoft_plugin_sys_plugin_name, MessageBoxButton.OK, MessageBoxImage.Information); } else if (result != (uint)HRESULT.S_OK && result != (uint)HRESULT.E_CANCELLED) { var errorDesc = Win32Helpers.MessageFromHResult((int)result); var name = "插件: " + Resources.Microsoft_plugin_sys_plugin_name; var message = $"{Resources.Microsoft_plugin_sys_RecycleBin_ErrorMsg} {errorDesc}"; Log.Error(message + " - Please refer to https://msdn.microsoft.com/library/windows/desktop/aa378137 for more information.", typeof(Commands)); _ = MessageBox.Show(message, name, MessageBoxButton.OK, MessageBoxImage.Error); } if (result == (uint)HRESULT.S_OK && settingEmptyRBSuccesMsg) { _ = MessageBox.Show(Resources.Microsoft_plugin_sys_RecycleBin_EmptySuccessMessage, "插件: " + Resources.Microsoft_plugin_sys_plugin_name, MessageBoxButton.OK, MessageBoxImage.Information); } executingEmptyRecycleBinTask = false; } } } ================================================ FILE: PowerToys/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Main.cs ================================================ // 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.Globalization; using System.Linq; using System.Windows.Controls; using ManagedCommon; using Microsoft.PowerToys.Run.Plugin.System.Components; using Microsoft.PowerToys.Run.Plugin.System.Properties; using Microsoft.PowerToys.Settings.UI.Library; using Wox.Infrastructure; using Wox.Plugin; using Wox.Plugin.Common.Win32; namespace Microsoft.PowerToys.Run.Plugin.System { public class Main : IPlugin, IPluginI18n, ISettingProvider, IContextMenu, IDelayedExecutionPlugin { private PluginInitContext _context; private bool _confirmSystemCommands; private bool _showSuccessOnEmptyRB; // [PowerToysCN] // private bool _localizeSystemCommands; private bool _reduceNetworkResultScore; private bool _separateEmptyRB; public string Name => Resources.Microsoft_plugin_sys_plugin_name; public string Description => Resources.Microsoft_plugin_sys_plugin_description; public static string PluginID => "CEA08895D2544B019B2E9C5009600DF4"; public string IconTheme { get; set; } public bool IsBootedInUefiMode { get; set; } public IEnumerable AdditionalOptions => new List() { new PluginAdditionalOption() { Key = "ConfirmSystemCommands", DisplayLabel = Resources.confirm_system_commands, Value = false, }, new PluginAdditionalOption() { Key = "ShowSuccessOnEmptyRB", DisplayLabel = Resources.Microsoft_plugin_sys_RecycleBin_ShowEmptySuccessMessage, Value = false, }, // [PowerToysCN] // The original en-US resources are modified to Chinese. // No need for an option to switch. // new PluginAdditionalOption() // { // Key = LocalizeSystemCommands, // DisplayLabel = Resources.Use_localized_system_commands, // Value = true, // }, new PluginAdditionalOption() { Key = "SeparateResultEmptyRB", DisplayLabel = Resources.Microsoft_plugin_sys_RecycleBin_ShowEmptySeparate, Value = false, }, new PluginAdditionalOption() { Key = "ReduceNetworkResultScore", DisplayLabel = Resources.Reduce_Network_Result_Score, DisplayDescription = Resources.Reduce_Network_Result_Score_Description, Value = true, }, }; public void Init(PluginInitContext context) { _context = context; _context.API.ThemeChanged += OnThemeChanged; UpdateIconTheme(_context.API.GetCurrentTheme()); IsBootedInUefiMode = Win32Helpers.GetSystemFirmwareType() == FirmwareType.Uefi; // Log info if the system hasn't boot in uefi mode. // (Because this is only going into the log we can ignore the fact that normally UEFI and BIOS are written upper case. No need to convert the enumeration value to upper case.) if (!IsBootedInUefiMode) { Wox.Plugin.Logger.Log.Info($"The UEFI command will not show to the user. The system has not booted in UEFI mode or the system does not have an UEFI firmware! (Detected type: {Win32Helpers.GetSystemFirmwareType()})", typeof(Main)); } } public List Query(Query query) { List results = new List(); // [PowerToysCN] // CultureInfo culture = _localizeSystemCommands ? CultureInfo.CurrentUICulture : new CultureInfo("en-US"); CultureInfo culture = CultureInfo.CurrentUICulture; if (query == null) { return results; } // normal system commands are fast and can be returned immediately var systemCommands = Commands.GetSystemCommands(IsBootedInUefiMode, _separateEmptyRB, _confirmSystemCommands, _showSuccessOnEmptyRB, IconTheme, culture); foreach (var c in systemCommands) { var resultMatch = StringMatcher.FuzzySearch(query.Search, c.Title); if (resultMatch.Score > 0) { c.Score = resultMatch.Score; c.TitleHighlightData = resultMatch.MatchData; results.Add(c); } else if (c?.ContextData is SystemPluginContext contextData) { var searchTagMatch = StringMatcher.FuzzySearch(query.Search, contextData.SearchTag); if (searchTagMatch.Score > 0) { c.Score = resultMatch.Score; results.Add(c); } } } // The following information result is not returned because delayed queries doesn't clear output if no results are available. // On global queries the first word/part has to be 'ip', 'mac' or 'address' for network results // string[] keywordList = Resources.ResourceManager.GetString("Microsoft_plugin_sys_Search_NetworkKeywordList", culture).Split("; "); // if (!string.IsNullOrEmpty(query.ActionKeyword) || keywordList.Any(x => query.Search.StartsWith(x, StringComparison.CurrentCultureIgnoreCase))) // { // results.Add(new Result() // { // Title = "Getting network information. Please wait ...", // IcoPath = $"Images\\networkAdapter.{IconTheme}.png", // Score = StringMatcher.FuzzySearch("address", "ip address").Score, // }); // } return results; } public List Query(Query query, bool delayedExecution) { List results = new List(); // [PowerToysCN] // CultureInfo culture = _localizeSystemCommands ? CultureInfo.CurrentUICulture : new CultureInfo("en-US"); CultureInfo culture = CultureInfo.CurrentUICulture; if (query == null) { return results; } // Network (ip and mac) results are slow with many network cards and returned delayed. // On global queries the first word/part has to be 'ip', 'mac' or 'address' for network results string[] keywordList = Resources.ResourceManager.GetString("Microsoft_plugin_sys_Search_NetworkKeywordList", culture).Split("; "); if (!string.IsNullOrEmpty(query.ActionKeyword) || keywordList.Any(x => query.Search.StartsWith(x, StringComparison.CurrentCultureIgnoreCase))) { var networkConnectionResults = Commands.GetNetworkConnectionResults(IconTheme, culture); foreach (var r in networkConnectionResults) { var resultMatch = StringMatcher.FuzzySearch(query.Search, r.SubTitle); if (resultMatch.Score > 0) { r.Score = _reduceNetworkResultScore ? (int)(resultMatch.Score * 65 / 100) : resultMatch.Score; // Adjust score to improve user experience and priority order r.SubTitleHighlightData = resultMatch.MatchData; results.Add(r); } else if (r?.ContextData is SystemPluginContext contextData) { var searchTagMatch = StringMatcher.FuzzySearch(query.Search, contextData.SearchTag); if (searchTagMatch.Score > 0) { r.Score = resultMatch.Score; results.Add(r); } } } } return results; } public List LoadContextMenus(Result selectedResult) { return ResultHelper.GetContextMenuForResult(selectedResult, _showSuccessOnEmptyRB); } private void UpdateIconTheme(Theme theme) { if (theme == Theme.Light || theme == Theme.HighContrastWhite) { IconTheme = "light"; } else { IconTheme = "dark"; } } private void OnThemeChanged(Theme currentTheme, Theme newTheme) { UpdateIconTheme(newTheme); } public string GetTranslatedPluginDescription() { return Resources.Microsoft_plugin_sys_plugin_description; } public string GetTranslatedPluginTitle() { return Resources.Microsoft_plugin_sys_plugin_name; } public Control CreateSettingPanel() { throw new NotImplementedException(); } public void UpdateSettings(PowerLauncherPluginSettings settings) { var confirmSystemCommands = false; var showSuccessOnEmptyRB = false; // [PowerToysCN] // var localizeSystemCommands = true; var reduceNetworkResultScore = true; var separateEmptyRB = false; if (settings != null && settings.AdditionalOptions != null) { var optionConfirm = settings.AdditionalOptions.FirstOrDefault(x => x.Key == "ConfirmSystemCommands"); confirmSystemCommands = optionConfirm?.Value ?? confirmSystemCommands; var optionEmptyRBSuccessMsg = settings.AdditionalOptions.FirstOrDefault(x => x.Key == "ShowSuccessOnEmptyRB"); showSuccessOnEmptyRB = optionEmptyRBSuccessMsg?.Value ?? showSuccessOnEmptyRB; // [PowerToysCN] // var optionLocalize = settings.AdditionalOptions.FirstOrDefault(x => x.Key == LocalizeSystemCommands); // localizeSystemCommands = optionLocalize?.Value ?? true; var optionNetworkScore = settings.AdditionalOptions.FirstOrDefault(x => x.Key == "ReduceNetworkResultScore"); reduceNetworkResultScore = optionNetworkScore?.Value ?? reduceNetworkResultScore; var optionSeparateEmptyRB = settings.AdditionalOptions.FirstOrDefault(x => x.Key == "SeparateResultEmptyRB"); separateEmptyRB = optionSeparateEmptyRB?.Value ?? separateEmptyRB; } _confirmSystemCommands = confirmSystemCommands; _showSuccessOnEmptyRB = showSuccessOnEmptyRB; // [PowerToysCN] // _localizeSystemCommands = localizeSystemCommands; _reduceNetworkResultScore = reduceNetworkResultScore; _separateEmptyRB = separateEmptyRB; } } } ================================================ FILE: PowerToys/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Properties/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 执行系统命令时弹窗确认 详细信息 适配器名称 网线 请确认操作。 Request confirmation 已连接 详细信息 连接名称 复制详细信息 (Ctrl+C) 'Ctrl+C' is a shortcut DHCP 服务器 已断开 DNS 服务器 默认网关 {0} Gbps Abbreviation of 'Gbits per seconds'. Don't translate the placeholder '{0}' as it is replaced in code. Hibernate 休眠 This should align to the action in Windows of a hibernating your computer. 确定休眠? This should align to the action in Windows of a hibernating your computer. 把运行状态保存在硬盘,然后关机,可以恢复所有状态 This should align to the action in Windows of a hibernating your computer. IPv4 地址 IPv4 子网掩码 {0} 的 IPv4 地址 Don't translate the placeholder '{0}' as it is replaced in code. IPv6 地址 IPv6 本地链路地址 IPv6 本地站点地址 IPv6 临时地址 IPv6 唯一本地地址 {0} 的 IPv6 地址 Don't translate the placeholder '{0}' as it is replaced in code. Lock 锁定屏幕 This should align to the action in Windows of a locking your computer. 确定锁屏? This should align to the action in Windows of a locking your computer. 锁屏,登录以解锁 This should align to the action in Windows of a locking your computer. 回环 {0} ({1}) 的 MAC 地址 Don't translate the placeholders '{0}' and '{1}' as they are replaced in code. {0} Mbps Abbreviation of 'Mbits per seconds'. Don't translate the placeholder '{0}' as it is replaced in code. 移动数据 网卡地址 (MAC) 执行系统命令(例如 “关机”、“锁屏”、“睡眠”) This should align to the actions in Windows relating to shutting down, signing out, locking, sleeping, restarting, emptying the recycle bin, and hibernating your computer. 系统命令 Windows operating system commands. Recycle Bin 回收站 Means the recycle bin folder in Explorer. Empty Recycle Bin 清空回收站 This should align to the action in Windows of emptying the recycle bin on your computer. 删除回收站所有文件 This should align to the action in Windows of emptying the recycle bin on your computer. Open Recycle Bin 打开回收站 Means the recycle bin folder in Explorer. 清空回收站 (Shift+Delete) This should align to the action in Windows of emptying the recycle bin on your computer. 打开回收站 This should align to the action in Windows of emptying the recycle bin on your computer. 回收站已清空。 Means the recycle bin folder in Explorer. 正在清空回收站。 Means the recycle bin folder in Explorer. 回收站清空失败: 回收站已空。 Means the recycle bin folder in Explorer. Empty Recycle Bin 清空回收站 This should align to the action in Windows of emptying the recycle bin on your computer. 分开显示回收站的打开和清空两个功能 清空回收站后提示成功 Means the recycle bin folder in Explorer and "emptying" refers to "Empty Recycle Bin" command. Restart 重启 This should align to the action in Windows of a restarting your computer. 确定重启? This should align to the action in Windows of a restarting your computer. 关闭所有应用,关闭系统,然后重新启动系统 This should align to the action in Windows of a restarting your computer. ip; mac; 地址 Translate 'ip' as 'ip' and not as 'ip address'. Same for 'mac'.) Shutdown 关机 This should align to the action in Windows of a shutting down your computer. 确定关机? This should align to the action in Windows of a shutting down your computer. 关闭所有应用,关闭系统,然后断开电源 This should align to the action in Windows of a shutting down your computer. Sign out 注销 This should align to the action in windows of a signing out back to the lock screen. 确定注销? This should align to the action in windows of a signing out back to the lock screen. 关闭所有应用,注销账户,但不关闭系统 This should align to the action in windows of a signing out back to the lock screen. Sleep 睡眠 This should align to the action in Windows of a making your computer go to sleep. 确定睡眠? This should align to the action in Windows of a making your computer go to sleep. 将运行中的应用冻结,关闭不必要的硬件并待机 This should align to the action in Windows of a making your computer go to sleep. 速度 状态 点击复制 DNS 后缀 隧道 类型 Means type like category. Here it means network interface type (ethernet, wifi, ...). UEFI 固件设置 This should align to the action in Windows Recovery Environment that restart into uefi settings. 你将在电脑重启后进入主板 UEFI 固件设置,是否确定? This should align to the action in Windows Recovery Environment that restart into uefi settings. 重启电脑,然后进入主板 UEFI 固件设置(需要管理员权限) This should align to the action in Windows Recovery Environment that restart into uefi settings. 未知 WINS 服务器 Wifi 降低 “IP” “MAC” 的搜索优先级 优化搜索结果显示顺序(放在更后面),但是更难搜到 喵呜 (owo)/ ================================================ FILE: PowerToys/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Properties/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 复制 (Ctrl+C) 'Ctrl+C' is a shortcut 复制失败 日期 日期和时间 日期和时间(UTC) 'UTC' means here 'Universal Time Convention' 星期 月日 本月第几天 本周第几天 本年第几天 纪元 纪元缩写 可用前缀:“u” 表示 Unix 时间戳,“ums” 表示 Unix 毫秒时间戳, “ft” 表示 Windows 文件时间 错误: 数字输入无效 小时 ISO 8601 ISO 8601(UTC) 'UTC' means here 'Universal Time Convention' ISO 8601 +时区 ISO 8601(UTC)+时区 'UTC' means here 'Universal Time Convention' 文件名时间 The format allows for embedding in filenames 毫秒 分钟 月份 本年第几月 年月 当前时间 当前时间(UTC) 'UTC' means here 'Universal Time Convention' 获取时间与日期,支持自定义时间戳(比如:“{0}” “{1}” “{2}” “{3}”) Do not translate the placeholders like '{0}' because it will be replaced in code. 星期 日期 时间 时间 RFC1123 日期 Don't change order 当前日期; 现在 Don't change order 年份; 时代; 日期 Don't change order 当前年份; 当前时代; 当前日期; 现在 Don't change order 日期和时间; 时间和日期 Don't change order 当前日期和时间; 当前时间和日期; 现在 Don't change order 时间 Don't change order 当前时间; 现在 Don't change order for; and; nor; but; or; so; 与; 和; 或; 是; 的; 几; 什么 List of conjunctions. We don't add 'yet' because this can be a synonym of 'now' which might be problematic on localized searches. 日期显示日月名称 针对 “日期” 和 “现在” 全局搜索时隐藏 “数字输入无效” 结果 全局搜索只显示当前 “时间” “日期” “现在” 无论如何,搜索的第一个字必须对上才能搜到 时间显示秒数 针对 “时间” 和 “现在” 点击或按 Ctrl+C 复制 'Ctrl+C' is a shortcut 时间 时间(UTC) 'UTC' means here 'Universal Time Convention' 备用关键字: 通用时间格式:YYYY-MM-DD hh:mm:ss Unix 纪元时间 本月第几周 本年第几周 Windows 文件时间(64 位整数) 年份 Unix 纪元毫秒时间 每周第一天 星期五 星期一 星期六 星期日 星期四 星期二 星期三 每年第一周 设置每年第一周的计算规则 每年第一天所在的一周 每年第一个满四天的一周 每年第一个完整的一周 使用系统设置 ================================================ FILE: PowerToys/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Properties/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 关于 Area System access.cpl File name, Should not translated 轻松使用设置中心 Area Control Panel (legacy settings) 辅助程序 Area Privacy 连接工作或学校帐户 Area UserAccounts 帐户信息 Area Privacy 账户 Area SurfaceHub 操作中心 Area Control Panel (legacy settings) 激活 Area UpdateAndSecurity 活动历史记录 Area Privacy 添加硬件 添加设备 Area Control Panel (legacy settings) 添加或删除程序 Area Control Panel (legacy settings) 添加手机 Area Phone Windows 工具 Area System 高级显示设置 Area System, only available on devices that support advanced display options 高级图形 广告 ID Area Privacy, Deprecated in Windows 10, version 1809 and later 飞行模式 Area NetworkAndInternet Alt+Tab Means the key combination "Tabulator+Alt" on the keyboard 代称 动画效果 应用颜色 控制面板 Type of the setting is a "(legacy) Control Panel setting" 应用诊断 Area Privacy 应用功能 Area Apps 应用 Short/modern name for application Microsoft 管理控制台 应用和功能 Area Apps 设置 Type of the setting is a "Modern Windows settings". We use the same term as used in start menu search at the moment. 可使用网站打开的应用 Area Apps 应用音量和设备首选项 Area System, Added in Windows 10, version 1903 appwiz.cpl File name, Should not translated 类别 Mean the settings area or settings category 账户 Windows 工具 Area Control Panel (legacy settings) 外观和个性化 应用 蓝牙和其他设备 Area Bluetooth&Devices in Win11 Settings app 时钟和区域 Cortana 设备 轻松使用 附加设置 游戏 硬件和声音 混合现实 网络和 Internet 个性化 手机 隐私 程序 安全和维护 Area Security and Maintenance in legacy Control Panel app. SurfaceHub 系统 系统和安全 系统属性 System Properties dialog sysdm.cpl 时间和语言 更新和安全 用户帐户 受指派的存取权 音频 Area EaseOfAccess 音频通知 音频和语音 Area MixedReality, only available if the Mixed Reality Portal app is installed. 授权管理器 Name of MMC Snap-In. 自动文件下载 Area Privacy 自动播放 Area Device 背景 Area Personalization 后台应用 Area Privacy 备份 Area UpdateAndSecurity 备份和还原 Area Control Panel (legacy settings) 节电模式 Area System, only available on devices that have a battery, such as a tablet 节电模式设置 Area System, only available on devices that have a battery, such as a tablet 节电模式使用情况 电池使用情况 Area System, only available on devices that have a battery, such as a tablet 生物识别设备 Area Control Panel (legacy settings) BitLocker 驱动器加密 Area Control Panel (legacy settings) 蓝光 蓝牙 Area Device 蓝牙和其他设备 Page Bluetooth and devices in Win10 Settings app 蓝牙设备 Area Control Panel (legacy settings) 蓝黄色 注音输入法(Bopomofo IME) Area TimeAndLanguage bpmf Should not translated 广播 Area Gaming bthprops.cpl 日历 Area Privacy 通话记录 Area Privacy 通话 相机 Area Privacy 仓颉输入法(Cangjie IME) Area TimeAndLanguage 大写锁定键 CapsLock Mean the "Caps Lock" key 手机网络和 SIM Area NetworkAndInternet 证书 - 当前用户 Name of MMC Snap-In. 证书 - 本地计算机 Name of MMC Snap-In. 更改程序 更改屏幕保护程序 更改用户帐户控制设置 选择“开始”菜单上显示的文件夹 Area Personalization NetWare 客户端服务 Area Control Panel (legacy settings) 剪贴板 Area System 字幕 Area EaseOfAccess collab.cpl 颜色滤镜 Area EaseOfAccess 颜色管理 Area Control Panel (legacy settings) 颜色 Area Personalization 命令 The command to direct start a setting COM 对象 组件服务 Name of MMC Snap-In. 计算机管理 Name of MMC Snap-In. 可连接的设备 已连接的设备 Area Device 连接面板 连接到无线音频设备 连接到无线显示器 联系人 Area Privacy 复制命令 核心隔离 Means the protection of the system core Cortana Area Cortana 跨设备使用 Cortana Area Cortana Cortana - 语言 Area Cortana 创建与格式化磁盘分区 凭据管理器 Area Control Panel (legacy settings) 跨设备 自定义设备 深色 深色模式 数据使用量 Area NetworkAndInternet 日期和时间 Area TimeAndLanguage 默认应用 Area Apps 默认相机 Area Device 默认位置 Area Control Panel (legacy settings) 默认程序 Area Control Panel (legacy settings) 默认保存位置 Area System 高级安全 Windows Defender 防火墙 Name of MMC Snap-In. 传递优化 Area UpdateAndSecurity desk.cpl File name, Should not translated 桌面主题 Area Control Panel (legacy settings) 绿色盲 Medical: Mean you don't can see red colors 发现设备 设备管理器 Area Control Panel (legacy settings) 设备管理器 Name of MMC Snap-In. 设备 设备和打印机 Area Control Panel (legacy settings) DHCP Should not translated 拨号 Area NetworkAndInternet DirectAccess Area NetworkAndInternet, only available if DirectAccess is enabled 直接打开你的手机 Area EaseOfAccess 磁盘管理 Name of MMC Snap-In. 显示 Area EaseOfAccess 显示属性 Area Control Panel (legacy settings) DNS Should not translated 文档 Area Privacy 当我在使用投影时 Area System 在这些时间段内 Area System 轻松使用设置中心 Area Control Panel (legacy settings) 编辑环境变量 Used as AltName on EditSystemEnvironmentVars settings to make both entries available via 'Edit environment variables'. 发行版 Means the "Windows Edition" 编辑系统环境变量 编辑帐户的环境变量 电子邮件 Area Privacy 电子邮件和帐户 Area UserAccounts 设备加密 Area System 环境 Area MixedReality, only available if the Mixed Reality Portal app is installed. 环境变量 (Env vars) Short english form. Don't translate! 以太网 Area NetworkAndInternet 事件查看器 Name of MMC Snap-In. 漏洞保护 附加设置 Area Extra, , only used for setting of 3rd-Party tools 目视控制 Area EaseOfAccess 目视跟踪器 Area Privacy, requires eyetracker hardware 家庭和其他用户 Area UserAccounts 诊断和反馈 Area Privacy 文件系统 Area Privacy 快速搜索 Area Control Panel (legacy settings) findfast.cpl File name, Should not translated 查找我的设备 Area UpdateAndSecurity 防火墙 Firewall.cpl 专注助手 - 安静时间段 Area System 专注助手 - 安静时刻 Area System 文件资源管理器选项 Area Control Panel (legacy settings) 字体 Area EaseOfAccess 开发者选项 Area UpdateAndSecurity 游戏栏 Area Gaming 游戏控制器 Area Control Panel (legacy settings) 屏幕截图 Area Gaming 游戏模式 Area Gaming 网关 Should not translated 常规 Area Privacy 获得程序 Area Control Panel (legacy settings) 入门 Area Control Panel (legacy settings) 概览 Area Personalization, Deprecated in Windows 10, version 1809 and later GPT 图形设置 Area System 灰度 绿色弱 Mean you don't can see green colors Group Policy hdwwiz.cpl 头戴显示设备显示 Area MixedReality, only available if the Mixed Reality Portal app is installed. 高对比度 Area EaseOfAccess 全息音频 全息环境 全息头显 全息管理 家庭组 Area Control Panel (legacy settings) ID MEans The "Windows Identifier" 图像 索引选项 Area Control Panel (legacy settings) inetcpl.cpl File name, Should not translated 红外线 Area Control Panel (legacy settings) 墨迹书写和键入个性化 Area Privacy Internet 属性 Area Control Panel (legacy settings) intl.cpl File name, Should not translated 反色 IP Should not translated IP 安全监视器 Name of MMC Snap-In. 本地计算机上的 IP 安全策略 Name of MMC Snap-In. irprops.cpl 隔离浏览 日语输入法(Japan IME) Area TimeAndLanguage, available if the Microsoft Japan input method editor is installed joy.cpl File name, Should not translated 游戏控制器属性 Area Control Panel (legacy settings) jpnime Should not translated 键盘 Area EaseOfAccess 数字小键盘 按键 语言 Area TimeAndLanguage 浅色 浅色模式 本地计算机策略 Name of MMC Snap-In. 本地用户和组 Name of MMC Snap-In. 位置 Area Privacy 锁屏界面 Area Personalization 放大镜 Area EaseOfAccess 电子邮件 - Microsoft Exchange 或 Windows Messaging Area Control Panel (legacy settings) main.cpl File name, Should not translated 管理设备 管理已知网络 Area NetworkAndInternet 管理可选功能 Area Apps MBR 消息 Area Privacy 按流量计费的连接 麦克风 Area Privacy Microsoft 邮局 Area Control Panel (legacy settings) mlcfg32.cpl File name, Should not translated azman.msc certlm.msc certmgr.msc comexp.msc compmgmt.msc devmgmt.msc diskmgmt.msc eventvwr.msc fsmgmt.msc gpedit.msc lusrmgr.msc mmc.exe perfmon.msc printmanagement.msc rsop.msc secpol.msc services.msc taskschd.msc tpm.msc WF.msc WmiMgmt.msc mmsys.cpl File name, Should not translated 移动设备 移动热点 Area NetworkAndInternet modem.cpl File name, Should not translated 单声道音频 更多详细信息 Area Cortana 动作 Area Privacy 鼠标 Area EaseOfAccess 鼠标和触摸板 Area Device 鼠标、字体、键盘及打印机属性 Area Control Panel (legacy settings) 光标和指针大小 Area EaseOfAccess 多媒体选项 Area Control Panel (legacy settings) 多任务处理 Area System 讲述人 Area EaseOfAccess 导航栏 Area Personalization ncpa.cpl 就近共享设置 Area System netcpl.cpl File name, Should not translated netsetup.cpl File name, Should not translated 网络 Area NetworkAndInternet 网络和共享中心 Area Control Panel (legacy settings) 网络连接 Area Control Panel (legacy settings) 网络属性 Area Control Panel (legacy settings) 网络会话 网络设置向导 Area Control Panel (legacy settings) 网络状态 Area NetworkAndInternet NFC Area NetworkAndInternet NFC 支付 "NFC should not translated" 夜间模式 夜间模式设置 Area System 注意 仅当您将移动设备连接到您的设备时可用。 仅在支持高级图形选项的设备上可用。 仅适用于配备电池的设备,例如平板电脑。 在 Windows 10 版本 1809(内部版本 17763)及更高版本中已弃用。 仅当 Dial 已配对时可用。 仅当 DirectAccess 启用时可用。 仅在支持高级显示选项的设备上可用。 编辑此设置需要管理员权限。 仅当用户注册 WIP 时出现。 需要目视跟踪设备。 仅在安装了微软日语输入法后可用。 仅在安装了微软拼音输入法后可用。 仅在安装了微软五笔输入法后可用。 仅在安装了混合现实门户应用程序后可用。 仅在企业已部署预配包的移动设备上可用。 此管理单元需要手动添加。 在 Windows 10 版本 1903(内部版本 18362)中加入。 在 Windows 10 版本 2004(内部版本 19041)中加入。 仅当安装了第三方设置应用时可用。 仅当存在触摸板硬件时可用。 仅当设备具有 Wi-Fi 适配器时可用。 设备必须支持 Windows Anywhere。 仅在企业已部署预配包时可用。 通知 Area Privacy 通知和操作 Area System 数字锁定键 NumLock Mean the "Num Lock" key nwc.cpl File name, Should not translated odbccp32.cpl File name, Should not translated ODBC 数据源管理程序 (32 位) Area Control Panel (legacy settings) ODBC 数据源管理程序 (64 位) Area Control Panel (legacy settings) 脱机文件 Area Control Panel (legacy settings) 离线地图 Area Apps 离线地图 - 下载地图 Area Apps 屏幕键盘 控制面板主页 'Control Panel' is here the name of the legacy settings app. 设置主页 'Settings' is here the name of the modern settings app. OS Means the "Operating System" 其他设备 Area Privacy 其他选项 Area EaseOfAccess 其他用户 家长控制 Area Control Panel (legacy settings) 密码 password.cpl File name, Should not translated 密码属性 Area Control Panel (legacy settings) 笔和输入设备 Area Control Panel (legacy settings) 笔和触控 Area Control Panel (legacy settings) 笔和 Windows Ink Area Device 网络邻居 Area Control Panel (legacy settings) 性能信息和工具 Area Control Panel (legacy settings) 性能监视器 Name of MMC Snap-In. 权限和历史记录 Area Cortana 个性化(类别) Area Personalization 手机 Area Phone 电话和调制解调器 Area Control Panel (legacy settings) 电话和调制解调器 - 选项 Area Control Panel (legacy settings) 电话呼叫 Area Privacy 手机 - 默认应用 Area System 手机连接 图片 图片 Area Privacy 微软拼音输入法设置 Area TimeAndLanguage, available if the Microsoft Pinyin input method editor is installed 微软拼音输入法设置 - 专业词典 Area TimeAndLanguage 微软拼音输入法设置 - 按键 Area TimeAndLanguage 微软拼音输入法设置 - 用户自定义短语 Area TimeAndLanguage 当我玩游戏时 Area Gaming 搜索 Windows 设置 {Locked="Windows"} 设置 PNP 设备 电源和睡眠 Area System powercfg.cpl File name, Should not translated 电源选项 Area Control Panel (legacy settings) 演示 打印机 Area Control Panel (legacy settings) 打印机和扫描仪 Area Device 打印后台处理服务 打印管理 Name of MMC Snap-In. 截图键 PrintScreen Mean the "Print screen" key 问题报告和解决方案 Area Control Panel (legacy settings) 处理器 程序和功能 Area Control Panel (legacy settings) 投影到此电脑 Area System 红色盲 Medical: Mean you don't can see green colors 预配置 Area UserAccounts, only available if enterprise has deployed a provisioning package 飞行模式 Area NetworkAndInternet 代理 Area NetworkAndInternet 速成输入法 Area TimeAndLanguage 游戏专注时刻 无线收发器 Area Privacy 内存 Means the Read-Access-Memory (typical the used to inform about the size) 语音识别 恢复 Area UpdateAndSecurity 红眼疲劳 Mean red eye effect by over-the-night flights 红绿色 Mean the weakness you can't differ between red and green colors 红色弱 Mean you don't can see red colors 区域 Area TimeAndLanguage 区域语言 Area TimeAndLanguage 区域属性 Area Control Panel (legacy settings) 区域和语言 Area Control Panel (legacy settings) 区域格式 RemoteApp 和桌面连接 Area Control Panel (legacy settings) 远程桌面 Area System 删除程序 修复程序 策略的结果集 Name of MMC Snap-In. 扫描仪和照相机 Area Control Panel (legacy settings) schedtasks File name, Should not translated 计划任务 任务计划程序 Area Control Panel (legacy settings) 屏幕旋转 Area System 屏幕保护程序 滚动条 滚动锁定键 ScrollLock Mean the "Scroll Lock" key SDNS Should not translated 搜索 Windows Area Cortana SecureDNS Should not translated 安全中心 Area Control Panel (legacy settings) 安全配置和分析 Name of MMC Snap-In. 安全处理器 安全模板 Name of MMC Snap-In. 服务 Name of MMC Snap-In. 会话清理 Area SurfaceHub 设置 设置展台 Area UserAccounts 跨设备共享 Area System 共享体验设置 Area System 共享文件夹 Name of MMC Snap-In. 快捷键 wifi dont translate this, is a short term to find entries 登录选项 Area UserAccounts 登录选项 - 动态锁 Area UserAccounts 文本大小 Size for text and symbols SMB 声音 Area System 语音 Area EaseOfAccess 语音识别 Area Control Panel (legacy settings) 语音输入 开始 Area Personalization 开始菜单 启动应用 Area Apps sticpl.cpl File name, Should not translated 存储 Area System 存储策略 Area System 存储感知 Area System 同步中心 Area Control Panel (legacy settings) 同步你的设置 Area UserAccounts sysdm.cpl File name, Should not translated 系统 Area Control Panel (legacy settings) 系统环境变量 (System env vars) Short english form. Don't translate! 系统属性/添加新硬件向导 Area Control Panel (legacy settings) 系统工具 系统变量 Tab Means the key "Tabulator" on the keyboard 平板模式 Area System TabletPC.cpl 平板电脑设置 Area Control Panel (legacy settings) 说话 对 Cortana 说话 Area Cortana 任务栏 Area Personalization 任务栏颜色 任务 Area Privacy 任务计划程序 Name of MMC Snap-In. 团队会议 Area SurfaceHub 团队设备管理 Area SurfaceHub telephon.cpl 文本到语音转换 Area Control Panel (legacy settings) 主题 Area Personalization themes.cpl File name, Should not translated timedate.cpl File name, Should not translated 时间线 超时 触摸 触摸反馈 触摸板 Area Device TPM 管理 Name of MMC Snap-In. 透明效果 蓝色盲 Medical: Mean you don't can see yellow and blue colors 疑难解答 Area UpdateAndSecurity TruePlay Area Gaming 开启或关闭屏保 输入 Area Device UAC Short version of 'User account control' 卸载 Area MixedReality, only available if the Mixed Reality Portal app is installed. 卸载程序 USB Area Device UserAccountControlSettings.exe Name of the executable 用户帐户 Area Control Panel (legacy settings) 帐户的环境变量 用户环境变量 (User env vars) Short english form. Don't translate! 用户变量 版本 Means The "Windows Version" 视频播放 Area Apps 视频 Area Privacy 虚拟桌面 病毒 Means the virus in computers and software 语音激活 Area Privacy 音量 VPN Area NetworkAndInternet 壁纸 暖色 欢迎中心 Area Control Panel (legacy settings) 欢迎屏幕 Area SurfaceHub wgpocpl.cpl File name, Should not translated 滚轮 Area Device Wi-Fi Area NetworkAndInternet, only available if Wi-Fi calling is enabled Wi-Fi 通话 Area NetworkAndInternet, only available if Wi-Fi calling is enabled Wi-Fi 设置 "Wi-Fi" should not translated 窗口边框 Windows Anytime Upgrade Area Control Panel (legacy settings) Windows Anywhere Area UserAccounts, device must be Windows Anywhere-capable Windows CardSpace Area Control Panel (legacy settings) Windows Defender Area Control Panel (legacy settings) Windows 防火墙 Area Control Panel (legacy settings) Windows Hello 安装程序 - 面部识别 Area UserAccounts Windows Hello 安装程序 - 指纹识别 Area UserAccounts Windows 预览体验计划 Area UpdateAndSecurity Windows Management Instrumentation Windows 移动中心 Area Control Panel (legacy settings) Windows 搜索 Area Cortana Windows 搜索设置 Area Cortana Windows 安全中心 Area UpdateAndSecurity Windows 更新 Area UpdateAndSecurity Windows 更新 - 高级选项 Area UpdateAndSecurity Windows 更新 - 检查更新 Area UpdateAndSecurity Windows 更新 - 计划重启 Area UpdateAndSecurity Windows 更新 - 查看可选更新 Area UpdateAndSecurity Windows 更新 - 查看更新历史记录 Area UpdateAndSecurity 无线网络 WMI 控件 Name of MMC Snap-In. 工作区 工作区预配置 Area UserAccounts wscui.cpl 微软五笔输入法设置 Area TimeAndLanguage, available if the Microsoft Wubi input method editor is installed 微软五笔输入法设置 - 用户自定义短语 Area TimeAndLanguage Xbox 网络 Area Gaming 账户信息 Area UserAccounts 放大 Mean zooming of things via a magnifier ================================================ FILE: PowerToys/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsTerminal/Main.cs ================================================ // Copyright (c) Microsoft Corporation // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Collections.Generic; using System.Linq; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using Microsoft.PowerToys.Run.Plugin.WindowsTerminal.Helpers; using Microsoft.PowerToys.Run.Plugin.WindowsTerminal.Properties; using Microsoft.PowerToys.Settings.UI.Library; using Wox.Infrastructure; using Wox.Plugin; using Wox.Plugin.Logger; namespace Microsoft.PowerToys.Run.Plugin.WindowsTerminal { public class Main : IPlugin, IContextMenu, IPluginI18n, ISettingProvider { private const string OpenNewTab = nameof(OpenNewTab); private const string OpenQuake = nameof(OpenQuake); private const string ShowHiddenProfiles = nameof(ShowHiddenProfiles); private readonly TerminalQuery _terminalQuery = new TerminalQuery(); private PluginInitContext _context; private bool _openNewTab; private bool _openQuake; private bool _showHiddenProfiles; private Dictionary _logoCache = new Dictionary(); public string Name => Resources.plugin_name; public string Description => Resources.plugin_description; public static string PluginID => "F59BA85006B14389A72A0EA756695F1D"; public IEnumerable AdditionalOptions => new List() { new PluginAdditionalOption() { Key = OpenNewTab, DisplayLabel = Resources.open_new_tab, Value = false, }, new PluginAdditionalOption() { Key = OpenQuake, DisplayLabel = Resources.open_quake, DisplayDescription = Resources.open_quake_description, Value = false, }, new PluginAdditionalOption() { Key = ShowHiddenProfiles, DisplayLabel = Resources.show_hidden_profiles, Value = false, }, }; public void Init(PluginInitContext context) { _context = context; } public List Query(Query query) { var search = query?.Search ?? string.Empty; var profiles = _terminalQuery.GetProfiles(); var result = new List(); foreach (var profile in profiles) { if (profile.Hidden && !_showHiddenProfiles) { continue; } // Action keyword only or search query match int score = StringMatcher.FuzzySearch(search, profile.Name).Score; if (string.IsNullOrWhiteSpace(search) || score > 0) { result.Add(new Result { Title = profile.Name, SubTitle = profile.Terminal.DisplayName, Icon = () => GetLogo(profile.Terminal), Score = score, Action = _ => { Launch(profile.Terminal.AppUserModelId, profile.Name); return true; }, ContextData = profile, }); } } return result; } public List LoadContextMenus(Result selectedResult) { if (!(selectedResult?.ContextData is TerminalProfile)) { return new List(); } var result = new List(); if (selectedResult.ContextData is TerminalProfile profile) { result.Add(new ContextMenuResult { Title = Resources.run_as_administrator, Glyph = "\xE7EF", FontFamily = "Segoe Fluent Icons,Segoe MDL2 Assets", AcceleratorKey = Key.Enter, AcceleratorModifiers = ModifierKeys.Control | ModifierKeys.Shift, Action = _ => { LaunchElevated(profile.Terminal.AppUserModelId, profile.Name); return true; }, }); } return result; } public string GetTranslatedPluginTitle() { return Resources.plugin_name; } public string GetTranslatedPluginDescription() { return Resources.plugin_description; } private void Launch(string id, string profile) { var appManager = new ApplicationActivationManager(); const ActivateOptions noFlags = ActivateOptions.None; var queryArguments = TerminalHelper.GetArguments(profile, _openNewTab, _openQuake); try { appManager.ActivateApplication(id, queryArguments, noFlags, out var unusedPid); } catch (Exception ex) { var name = "插件: " + Resources.plugin_name; var message = Resources.run_terminal_failed; Log.Exception("Failed to open Windows Terminal", ex, GetType()); _context.API.ShowMsg(name, message, string.Empty); } } private void LaunchElevated(string id, string profile) { try { string path = "shell:AppsFolder\\" + id; Helper.OpenInShell(path, TerminalHelper.GetArguments(profile, _openNewTab, _openQuake), runAs: Helper.ShellRunAsType.Administrator); } catch (Exception ex) { var name = "插件: " + Resources.plugin_name; var message = Resources.run_terminal_failed; Log.Exception("Failed to open Windows Terminal", ex, GetType()); _context.API.ShowMsg(name, message, string.Empty); } } public Control CreateSettingPanel() { throw new NotImplementedException(); } public void UpdateSettings(PowerLauncherPluginSettings settings) { var openNewTab = false; var openQuake = false; var showHiddenProfiles = false; if (settings != null && settings.AdditionalOptions != null) { openNewTab = settings.AdditionalOptions.FirstOrDefault(x => x.Key == OpenNewTab)?.Value ?? false; openQuake = settings.AdditionalOptions.FirstOrDefault(x => x.Key == OpenQuake)?.Value ?? false; showHiddenProfiles = settings.AdditionalOptions.FirstOrDefault(x => x.Key == ShowHiddenProfiles)?.Value ?? false; } _openNewTab = openNewTab; _openQuake = openQuake; _showHiddenProfiles = showHiddenProfiles; } private BitmapImage GetLogo(TerminalPackage terminal) { var aumid = terminal.AppUserModelId; if (!_logoCache.TryGetValue(aumid, out BitmapImage value)) { value = terminal.GetLogo(); _logoCache.Add(aumid, value); } return value; } } } ================================================ FILE: PowerToys/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsTerminal/Properties/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 在新标签页打开配置文件 在下拉栏中打开配置文件 Quake is a well-known computer game. Don't localize. See https://en.wikipedia.org/wiki/Quake_(video_game) 下拉栏,又名 Quake 模式,即上半屏分屏显示控制台窗口,可以按快捷键打开 Quake is a well-known computer game. Don't localize. See https://en.wikipedia.org/wiki/Quake_(video_game);;Zetaspace:Not at my country. 打开 Windows Terminal 配置文件 Windows Terminal 以管理员权限运行(Ctrl+Shift+Enter) 无法打开 Windows Terminal 显示隐藏配置文件 ================================================ FILE: PowerToys/src/modules/launcher/PowerLauncher/Properties/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 搜点什么... 注册快捷键失败:{0} 执行查询:{0} 上次运行时间:{0} 出现了一些问题。 请汇报这个问题,发送到 reportWindow_file_bug + reportWindow_github_repo string PowerToys GitHub 仓库 reportWindow_file_bug + reportWindow_github_repo string 请上传日志文件和错误信息。 . PowerToys 启动器遇到问题 应用图标 上下文菜单项 上下文菜单项附加信息 上下文菜单图标 上下文菜单项集 查询 结果 搜索图标 子标题 标题 PowerToys 启动器反序列化错误 设置将被重置为默认值,程序仍将继续运行。 无法加载插件: {0} PowerToys 启动器 - 插件初始化失败 Don't translate "PowerToys Run". This is a product name. 控制按钮可用 插件关键字 PowerToys 启动器 - 快捷键错误 Don't translate "PowerToys Run". This is a product name. 请将问题汇报到 https://aka.ms/powerToysReportBug。(第三方插件请联系插件作者) "https://aka.ms/powerToysReportBug" is a web uri. ================================================ FILE: PowerToys/src/modules/peek/Peek.UI/Strings/en-us/Resources.resw ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 快速查看 Name of application. ({0}/{1}) Text for the file count in the title bar. 0: the index of the current file. 1: the total number of files selected. 打开方式 Text for button to launch the application picker. 打开方式 (回车) Tooltip for button to launch the application picker. 用 {0} 打开 Text for button to launch default application. 0: name of the default application. 用 {0} 打开 (回车) Tooltip for button to launch default application. 0: name of the default application. 类型: {0} File Type label for the unsupported files view. {0} is the type. 大小: {0} File Size label for the unsupported files view. {0} is the size. 日期: {0} Date Modified label for the unsupported files view. {0} is the date. B Abbreviation for the size unit byte. KB Abbreviation for the size unit kilobyte. MB Abbreviation for the size unit megabyte. GB Abbreviation for the size unit gigabyte. TB Abbreviation for the size unit terabyte. PB Abbreviation for the size unit petabyte. EB Abbreviation for the size unit exabyte. 名称: {0} Filename for the tooltip of preview. {0} is the name. 类型: {0} Item Type for the tooltip of preview. {0} is the type. 日期: {0} Date Modified label for the tooltip of preview. {0} is the date. 尺寸: {0} × {1} Dimensions label for the tooltip of preview. {0} is the width, {1} is the height. 大小: {0} File Size label for the tooltip of preview. {0} is the size. 快速查看文件 Tooltip of preview when there's no file info available. 固定当前窗口大小 Tooltip for button to pin the Peek window. 取消固定窗口大小 Tooltip for button to unpin the Peek window. 反馈问题 Failed fallback preview Report bug hyperlink button content. 无法查看此文件 Failed fallback preview text. {0} 个目录 {0} is the number of directories in the archive {0} 个文件 {0} is the number of files in the archive {0} (解压后 {1}) {0} is the size of the archive, {1} is the extracted size B Abbreviation for the size bytes 取消 Dialog showed when an URI is clicked. Button to close the dialog. 打开 Dialog showed when an URI is clicked. Button to open the URI. 复制 Dialog showed when an URI is clicked. Button to copy the URI. 确定要使用软件来打开吗? Title of the dialog showed when an URI is clicked,"Peek" is the name of the utility. ({1:N0} B) Displays total number of bytes. Don't localize the "{1:N0}" part. ({1:N0} B) Displays unit byte. Don't localize the "{1:N0}" part. {0} - 快速查看 Title of the Peek window. {0} is the name of the currently previewed item."Peek" is the name of the utility. {0} 可用 {0} is the free space of the drive {0} 已用 {0} is the used space of the drive 容量: {0} {0} is the capacity of the drive 文件系统: {0} {0} is the file system of the drive 类型: {0} {0} is the type of the drive 固定 网络 光盘 "Optical" indicates CD or DVD drives 内存盘 可移动 "Removal" indicates USB or external hard drives 未知 Used for unknown drive type or file system 专辑: {0} {0} is the title of the album read from file metadata 歌手: {0} {0} is the artist read from file metadata 时长: {0} {0} is the duration of the audio read from file metadata 复制 Copy selected text to clipboard 切换自动换行 Toggle whether text in pane is word-wrapped 切换小地图 ================================================ FILE: PowerToys/src/modules/powerrename/PowerRenameContextMenu/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 批量重命名 do not loc, product name 批量重命名 PowerRename is a product name. do not loc it ================================================ FILE: PowerToys/src/modules/powerrename/PowerRenameUILib/PowerRenameXAML/MainWindow.xaml.cpp ================================================ #include "pch.h" #include "MainWindow.xaml.h" #if __has_include("MainWindow.g.cpp") #include "MainWindow.g.cpp" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include "microsoft.ui.xaml.window.h" #include #include #include #include #include using namespace winrt; using namespace Windows::UI::Xaml; using namespace winrt::Microsoft::Windows::ApplicationModel::Resources; // Non-localizable const std::wstring PowerRenameUIIco = L"PowerRenameUI.ico"; const wchar_t c_WindowClass[] = L"PowerRename"; HINSTANCE g_hostHInst; extern std::vector g_files; // Theming ThemeListener theme_listener{}; HWND CurrentWindow; void handleTheme() { auto theme = theme_listener.AppTheme; auto isDark = theme == Theme::Dark; Logger::info(L"Theme is now {}", isDark ? L"Dark" : L"Light"); ThemeHelpers::SetImmersiveDarkMode(CurrentWindow, isDark); } // Disabled for now, because it causes indentation and icons to appear "asynchronously" // #define ENABLE_RECYCLING_VIRTUALIZATION_MODE CComPtr g_prManager; std::function g_itemToggledCallback; winrt::Microsoft::UI::Xaml::Controls::ScrollViewer FindScrollViewer(winrt::Microsoft::UI::Xaml::DependencyObject start) { int childrenCount = winrt::Microsoft::UI::Xaml::Media::VisualTreeHelper::GetChildrenCount(start); for (int i = 0; i < childrenCount; i++) { auto obj = winrt::Microsoft::UI::Xaml::Media::VisualTreeHelper::GetChild(start, i); if (auto sv = obj.try_as()) { return sv; } else { auto result = FindScrollViewer(obj); if (result) { return result; } } } return nullptr; } PowerRenameUI::ExplorerItem FindFirstExplorerItemRecursive(winrt::Microsoft::UI::Xaml::DependencyObject startNode) { auto explorerItem = startNode.try_as(); if (explorerItem) { return explorerItem; } int childCount = winrt::Microsoft::UI::Xaml::Media::VisualTreeHelper::GetChildrenCount(startNode); for (int i = 0; i < childCount; ++i) { auto child = winrt::Microsoft::UI::Xaml::Media::VisualTreeHelper::GetChild(startNode, i); explorerItem = FindFirstExplorerItemRecursive(child); if (explorerItem) { return explorerItem; } } return nullptr; } namespace winrt::PowerRenameUI::implementation { MainWindow::MainWindow() : m_allSelected{ true }, m_managerEvents{ this } { Trace::RegisterProvider(); auto windowNative{ this->try_as<::IWindowNative>() }; winrt::check_bool(windowNative); windowNative->get_WindowHandle(&m_window); CurrentWindow = m_window; // Attach theme handling theme_listener.AddChangedHandler(handleTheme); handleTheme(); Microsoft::UI::WindowId windowId = Microsoft::UI::GetWindowIdFromWindow(m_window); Microsoft::UI::Windowing::AppWindow appWindow = Microsoft::UI::Windowing::AppWindow::GetFromWindowId(windowId); appWindow.SetIcon(PowerRenameUIIco); POINT cursorPosition{}; if (GetCursorPos(&cursorPosition)) { ::Windows::Graphics::PointInt32 point{ cursorPosition.x, cursorPosition.y }; Microsoft::UI::Windowing::DisplayArea displayArea = Microsoft::UI::Windowing::DisplayArea::GetFromPoint(point, Microsoft::UI::Windowing::DisplayAreaFallback::Nearest); HMONITOR hMonitor = MonitorFromPoint(cursorPosition, MONITOR_DEFAULTTOPRIMARY); MONITORINFOEX monitorInfo; monitorInfo.cbSize = sizeof(MONITORINFOEX); GetMonitorInfo(hMonitor, &monitorInfo); UINT x_dpi; GetDpiForMonitor(hMonitor, MONITOR_DPI_TYPE::MDT_EFFECTIVE_DPI, &x_dpi, &x_dpi); UINT window_dpi = GetDpiForWindow(m_window); const auto& [width, height] = LastRunSettingsInstance().GetLastWindowSize(); winrt::Windows::Graphics::RectInt32 rect; // Scale window size rect.Width = static_cast(width * static_cast(window_dpi) / x_dpi); rect.Height = static_cast(height * static_cast(window_dpi) / x_dpi); // Center to screen rect.X = displayArea.WorkArea().X + displayArea.WorkArea().Width / 2 - width / 2; rect.Y = displayArea.WorkArea().Y + displayArea.WorkArea().Height / 2 - height / 2; appWindow.MoveAndResize(rect); } Title(hstring{ L"批量重命名" }); m_searchMRUList = winrt::single_threaded_observable_vector(); m_replaceMRUList = winrt::single_threaded_observable_vector(); std::function toggledCallback = [this] { _TRACER_; UpdateCounts(); }; g_itemToggledCallback.swap(toggledCallback); m_explorerItems = winrt::make(); get_self(m_explorerItems)->SetIsFiltered(false); m_searchRegExShortcuts = winrt::single_threaded_observable_vector(); auto factory = winrt::get_activation_factory(); ResourceManager manager = factory.CreateInstance(L"PowerToys.PowerRename.pri"); m_searchRegExShortcuts.Append(winrt::make(L".", manager.MainResourceMap().GetValue(L"Resources/RegExCheatSheet_MatchAny").ValueAsString())); m_searchRegExShortcuts.Append(winrt::make(L"\\d", manager.MainResourceMap().GetValue(L"Resources/RegExCheatSheet_MatchDigit").ValueAsString())); m_searchRegExShortcuts.Append(winrt::make(L"\\D", manager.MainResourceMap().GetValue(L"Resources/RegExCheatSheet_MatchNonDigit").ValueAsString())); m_searchRegExShortcuts.Append(winrt::make(L"\\w", manager.MainResourceMap().GetValue(L"Resources/RegExCheatSheet_MatchWordChar").ValueAsString())); m_searchRegExShortcuts.Append(winrt::make(L"\\S", manager.MainResourceMap().GetValue(L"Resources/RegExCheatSheet_MatchNonWS").ValueAsString())); m_searchRegExShortcuts.Append(winrt::make(L"\\S+", manager.MainResourceMap().GetValue(L"Resources/RegExCheatSheet_MatchOneOrMoreWS").ValueAsString())); m_searchRegExShortcuts.Append(winrt::make(L"\\b", manager.MainResourceMap().GetValue(L"Resources/RegExCheatSheet_MatchWordBoundary").ValueAsString())); m_dateTimeShortcuts = winrt::single_threaded_observable_vector(); m_dateTimeShortcuts.Append(winrt::make(L"$YYYY", manager.MainResourceMap().GetValue(L"Resources/DateTimeCheatSheet_FullYear").ValueAsString())); m_dateTimeShortcuts.Append(winrt::make(L"$YY", manager.MainResourceMap().GetValue(L"Resources/DateTimeCheatSheet_YearLastTwoDigits").ValueAsString())); m_dateTimeShortcuts.Append(winrt::make(L"$Y", manager.MainResourceMap().GetValue(L"Resources/DateTimeCheatSheet_YearLastDigit").ValueAsString())); m_dateTimeShortcuts.Append(winrt::make(L"$MMMM", manager.MainResourceMap().GetValue(L"Resources/DateTimeCheatSheet_MonthName").ValueAsString())); m_dateTimeShortcuts.Append(winrt::make(L"$MMM", manager.MainResourceMap().GetValue(L"Resources/DateTimeCheatSheet_MonthNameAbbr").ValueAsString())); m_dateTimeShortcuts.Append(winrt::make(L"$MM", manager.MainResourceMap().GetValue(L"Resources/DateTimeCheatSheet_MonthDigitLZero").ValueAsString())); m_dateTimeShortcuts.Append(winrt::make(L"$M", manager.MainResourceMap().GetValue(L"Resources/DateTimeCheatSheet_MonthDigit").ValueAsString())); m_dateTimeShortcuts.Append(winrt::make(L"$DDDD", manager.MainResourceMap().GetValue(L"Resources/DateTimeCheatSheet_DayName").ValueAsString())); m_dateTimeShortcuts.Append(winrt::make(L"$DDD", manager.MainResourceMap().GetValue(L"Resources/DateTimeCheatSheet_DayNameAbbr").ValueAsString())); m_dateTimeShortcuts.Append(winrt::make(L"$DD", manager.MainResourceMap().GetValue(L"Resources/DateTimeCheatSheet_DayDigitLZero").ValueAsString())); m_dateTimeShortcuts.Append(winrt::make(L"$D", manager.MainResourceMap().GetValue(L"Resources/DateTimeCheatSheet_DayDigit").ValueAsString())); m_dateTimeShortcuts.Append(winrt::make(L"$hh", manager.MainResourceMap().GetValue(L"Resources/DateTimeCheatSheet_HoursLZero").ValueAsString())); m_dateTimeShortcuts.Append(winrt::make(L"$h", manager.MainResourceMap().GetValue(L"Resources/DateTimeCheatSheet_Hours").ValueAsString())); m_dateTimeShortcuts.Append(winrt::make(L"$mm", manager.MainResourceMap().GetValue(L"Resources/DateTimeCheatSheet_MinutesLZero").ValueAsString())); m_dateTimeShortcuts.Append(winrt::make(L"$m", manager.MainResourceMap().GetValue(L"Resources/DateTimeCheatSheet_Minutes").ValueAsString())); m_dateTimeShortcuts.Append(winrt::make(L"$ss", manager.MainResourceMap().GetValue(L"Resources/DateTimeCheatSheet_SecondsLZero").ValueAsString())); m_dateTimeShortcuts.Append(winrt::make(L"$s", manager.MainResourceMap().GetValue(L"Resources/DateTimeCheatSheet_Seconds").ValueAsString())); m_dateTimeShortcuts.Append(winrt::make(L"$fff", manager.MainResourceMap().GetValue(L"Resources/DateTimeCheatSheet_MilliSeconds3D").ValueAsString())); m_dateTimeShortcuts.Append(winrt::make(L"$ff", manager.MainResourceMap().GetValue(L"Resources/DateTimeCheatSheet_MilliSeconds2D").ValueAsString())); m_dateTimeShortcuts.Append(winrt::make(L"$f", manager.MainResourceMap().GetValue(L"Resources/DateTimeCheatSheet_MilliSeconds1D").ValueAsString())); m_CounterShortcuts = winrt::single_threaded_observable_vector(); m_CounterShortcuts.Append(winrt::make(L"${}", manager.MainResourceMap().GetValue(L"Resources/CounterCheatSheet_Simple").ValueAsString())); m_CounterShortcuts.Append(winrt::make(L"${start=10}", manager.MainResourceMap().GetValue(L"Resources/CounterCheatSheet_Start").ValueAsString())); m_CounterShortcuts.Append(winrt::make(L"${increment=5}", manager.MainResourceMap().GetValue(L"Resources/CounterCheatSheet_Increment").ValueAsString())); m_CounterShortcuts.Append(winrt::make(L"${padding=8}", manager.MainResourceMap().GetValue(L"Resources/CounterCheatSheet_Padding").ValueAsString())); m_CounterShortcuts.Append(winrt::make(L"${increment=3,padding=4,start=900}", manager.MainResourceMap().GetValue(L"Resources/CounterCheatSheet_Complex").ValueAsString())); m_RandomizerShortcuts = winrt::single_threaded_observable_vector(); m_RandomizerShortcuts.Append(winrt::make(L"${rstringalnum=9}", manager.MainResourceMap().GetValue(L"Resources/RandomizerCheatSheet_Alnum").ValueAsString())); m_RandomizerShortcuts.Append(winrt::make(L"${rstringalpha=13}", manager.MainResourceMap().GetValue(L"Resources/RandomizerCheatSheet_Alpha").ValueAsString())); m_RandomizerShortcuts.Append(winrt::make(L"${rstringdigit=36}", manager.MainResourceMap().GetValue(L"Resources/RandomizerCheatSheet_Digit").ValueAsString())); m_RandomizerShortcuts.Append(winrt::make(L"${ruuidv4}", manager.MainResourceMap().GetValue(L"Resources/RandomizerCheatSheet_Uuid").ValueAsString())); InitializeComponent(); m_etwTrace.UpdateState(true); listView_ExplorerItems().ApplyTemplate(); #ifdef ENABLE_RECYCLING_VIRTUALIZATION_MODE if (auto scrollViewer = FindScrollViewer(listView_ExplorerItems()); scrollViewer) { Microsoft::UI::Xaml::DispatcherTimer debounceTimer = nullptr; scrollViewer.ViewChanged([this, scrollViewer, timer = std::move(debounceTimer)](IInspectable const&, Microsoft::UI::Xaml::Controls::ScrollViewerViewChangedEventArgs const&) mutable { if (!timer) { timer = {}; timer.Tick([this, &timer](auto&, auto&) { get_self(m_explorerItems)->InvalidateCollection(); timer.Stop(); timer = nullptr; }); } using namespace std::chrono_literals; winrt::Windows::Foundation::TimeSpan timeSpan{ 200ms }; timer.Interval(timeSpan); timer.Start(); }); } #endif if (SUCCEEDED(CPowerRenameManager::s_CreateInstance(&m_prManager))) { g_prManager = m_prManager; // Create the factory for our items CComPtr prItemFactory; if (SUCCEEDED(CPowerRenameItem::s_CreateInstance(nullptr, IID_PPV_ARGS(&prItemFactory)))) { if (SUCCEEDED(m_prManager->PutRenameItemFactory(prItemFactory))) { if (SUCCEEDED(m_prManager->Advise(&m_managerEvents, &m_cookie))) { CComPtr shellItemArray; // To test PowerRename uncomment DEBUG_BENCHMARK_100K_ENTRIES define if (!g_files.empty()) { if (SUCCEEDED(CreateShellItemArrayFromPaths(std::move(g_files), &shellItemArray))) { CComPtr enumShellItems; if (SUCCEEDED(shellItemArray->EnumItems(&enumShellItems))) { EnumerateShellItems(enumShellItems); } } } else { Logger::warn(L"No items selected to be renamed."); } } } } else { Logger::error(L"Error creating PowerRenameItemFactory"); } } else { Logger::error(L"Error creating PowerRenameManager"); } try { UpdateCounts(); SetHandlers(); ReadSettings(); } catch (std::exception& e) { Logger::error("Exception thrown during explorer items population: {}", std::string{ e.what() }); } button_rename().IsEnabled(false); toggleButton_enumItems().IsChecked(true); toggleButton_randItems().IsChecked(false); InitAutoComplete(); SearchReplaceChanged(); InvalidateItemListViewState(); SizeChanged({ this, &MainWindow::OnSizeChanged }); Closed({ this, &MainWindow::OnClosed }); } void MainWindow::OnSizeChanged(winrt::Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::WindowSizeChangedEventArgs const& /*args*/) { const auto appWindow = Microsoft::UI::Windowing::AppWindow::GetFromWindowId(Microsoft::UI::GetWindowIdFromWindow(m_window)); const auto [width, height] = appWindow.Size(); m_updatedWindowSize.emplace(std::make_pair(width, height)); } void MainWindow::OnClosed(winrt::Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::WindowEventArgs const&) { if (m_updatedWindowSize) { LastRunSettingsInstance().UpdateLastWindowSize(m_updatedWindowSize->first, m_updatedWindowSize->second); } m_etwTrace.Flush(); m_etwTrace.UpdateState(false); Trace::UnregisterProvider(); } void MainWindow::InvalidateItemListViewState() { get_self(m_explorerItems)->InvalidateCollection(); } winrt::event_token MainWindow::PropertyChanged(Microsoft::UI::Xaml::Data::PropertyChangedEventHandler const& handler) { return m_propertyChanged.add(handler); } void MainWindow::PropertyChanged(winrt::event_token const& token) noexcept { m_propertyChanged.remove(token); } hstring MainWindow::OriginalCount() { UINT count = 0; m_prManager->GetItemCount(&count); return hstring{ std::to_wstring(count) }; } void MainWindow::OriginalCount(hstring) { m_propertyChanged(*this, Microsoft::UI::Xaml::Data::PropertyChangedEventArgs{ L"OriginalCount" }); } hstring MainWindow::RenamedCount() { return hstring{ std::to_wstring(m_renamingCount) }; } void MainWindow::RenamedCount(hstring) { m_propertyChanged(*this, Microsoft::UI::Xaml::Data::PropertyChangedEventArgs{ L"RenamedCount" }); } void MainWindow::SelectAll(winrt::Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::RoutedEventArgs const&) { if (checkBox_selectAll().IsChecked().GetBoolean() != m_allSelected) { ToggleAll(); m_allSelected = !m_allSelected; if (button_showRenamed().IsChecked()) { UpdateCounts(); } InvalidateItemListViewState(); } } void MainWindow::ShowAll(winrt::Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::RoutedEventArgs const&) { button_showAll().IsChecked(true); button_showRenamed().IsChecked(false); DWORD filter = 0; m_prManager->GetFilter(&filter); if (filter != PowerRenameFilters::None) { m_prManager->SwitchFilter(0); get_self(m_explorerItems)->SetIsFiltered(false); InvalidateItemListViewState(); } } void MainWindow::ShowRenamed(winrt::Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::RoutedEventArgs const&) { button_showRenamed().IsChecked(true); button_showAll().IsChecked(false); DWORD filter = 0; m_prManager->GetFilter(&filter); if (filter != PowerRenameFilters::ShouldRename) { m_prManager->SwitchFilter(0); UpdateCounts(); get_self(m_explorerItems)->SetIsFiltered(true); InvalidateItemListViewState(); } } void MainWindow::RegExItemClick(winrt::Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::Controls::ItemClickEventArgs const& e) { auto s = e.ClickedItem().try_as(); RegExFlyout().Hide(); textBox_search().Text(textBox_search().Text() + s->Code()); } void MainWindow::DateTimeItemClick(winrt::Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::Controls::ItemClickEventArgs const& e) { auto s = e.ClickedItem().try_as(); DateTimeFlyout().Hide(); textBox_replace().Text(textBox_replace().Text() + s->Code()); } void MainWindow::button_rename_Click(winrt::Microsoft::UI::Xaml::Controls::SplitButton const&, winrt::Microsoft::UI::Xaml::Controls::SplitButtonClickEventArgs const&) { Rename(false); } void MainWindow::MenuFlyoutItem_Click(winrt::Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::RoutedEventArgs const&) { Rename(true); } void MainWindow::OpenDocs(winrt::Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::RoutedEventArgs const&) { Windows::System::Launcher::LaunchUriAsync(winrt::Windows::Foundation::Uri{ L"https://aka.ms/PowerToysOverview_PowerRename" }); } HRESULT MainWindow::CreateShellItemArrayFromPaths( std::vector files, IShellItemArray** shellItemArray) { _TRACER_; *shellItemArray = nullptr; PIDLIST_ABSOLUTE* itemList = nullptr; itemList = new (std::nothrow) PIDLIST_ABSOLUTE[files.size()]; HRESULT hr = itemList ? S_OK : E_OUTOFMEMORY; UINT itemsCnt = 0; for (const auto& file : files) { const DWORD BUFSIZE = 4096; TCHAR buffer[BUFSIZE] = TEXT(""); auto retval = GetFullPathName(file.c_str(), BUFSIZE, buffer, NULL); if (retval != 0 && PathFileExists(buffer)) { hr = SHParseDisplayName(buffer, nullptr, &itemList[itemsCnt], 0, nullptr); ++itemsCnt; } } if (SUCCEEDED(hr) && itemsCnt > 0) { hr = SHCreateShellItemArrayFromIDLists(itemsCnt, const_cast(itemList), shellItemArray); if (SUCCEEDED(hr)) { for (UINT i = 0; i < itemsCnt; i++) { CoTaskMemFree(itemList[i]); } } else { Logger::error(L"Creating ShellItemArray from path list failed."); } } else { Logger::error(L"Parsing path list display names failed."); hr = E_FAIL; } delete[] itemList; return hr; } HRESULT MainWindow::InitAutoComplete() { _TRACER_; HRESULT hr = S_OK; if (CSettingsInstance().GetMRUEnabled()) { hr = CPowerRenameMRU::CPowerRenameMRUSearch_CreateInstance(&m_searchMRU); if (SUCCEEDED(hr)) { for (const auto& item : m_searchMRU->GetMRUStrings()) { if (!item.empty()) { m_searchMRUList.Append(item); } } } if (SUCCEEDED(hr)) { hr = CPowerRenameMRU::CPowerRenameMRUReplace_CreateInstance(&m_replaceMRU); if (SUCCEEDED(hr)) { for (const auto& item : m_replaceMRU->GetMRUStrings()) { if (!item.empty()) { m_replaceMRUList.Append(item); } } } } } return hr; } HRESULT MainWindow::EnumerateShellItems(_In_ IEnumShellItems* enumShellItems) { _TRACER_; HRESULT hr = S_OK; // Enumerate the data object and populate the manager if (m_prManager) { m_disableCountUpdate = true; // Ensure we re-create the enumerator m_prEnum = nullptr; hr = CPowerRenameEnum::s_CreateInstance(nullptr, m_prManager, IID_PPV_ARGS(&m_prEnum)); if (SUCCEEDED(hr)) { hr = m_prEnum->Start(enumShellItems); } m_disableCountUpdate = false; } return hr; } void MainWindow::SearchReplaceChanged(bool forceRenaming) { _TRACER_; Logger::debug(L"Forced renaming - {}", forceRenaming); // Pass updated search and replace terms to the IPowerRenameRegEx handler CComPtr prRegEx; if (m_prManager && SUCCEEDED(m_prManager->GetRenameRegEx(&prRegEx))) { winrt::hstring searchTerm = textBox_search().Text(); prRegEx->PutSearchTerm(searchTerm.c_str(), forceRenaming); winrt::hstring replaceTerm = textBox_replace().Text(); prRegEx->PutReplaceTerm(replaceTerm.c_str(), forceRenaming); } } void MainWindow::ValidateFlags(PowerRenameFlags flag) { if (flag == Uppercase) { if (toggleButton_upperCase().IsChecked()) { toggleButton_lowerCase().IsChecked(false); toggleButton_titleCase().IsChecked(false); toggleButton_capitalize().IsChecked(false); } } else if (flag == Lowercase) { if (toggleButton_lowerCase().IsChecked()) { toggleButton_upperCase().IsChecked(false); toggleButton_titleCase().IsChecked(false); toggleButton_capitalize().IsChecked(false); } } else if (flag == Titlecase) { if (toggleButton_titleCase().IsChecked()) { toggleButton_upperCase().IsChecked(false); toggleButton_lowerCase().IsChecked(false); toggleButton_capitalize().IsChecked(false); } } else if (flag == Capitalized) { if (toggleButton_capitalize().IsChecked()) { toggleButton_upperCase().IsChecked(false); toggleButton_lowerCase().IsChecked(false); toggleButton_titleCase().IsChecked(false); } } m_flagValidationInProgress = true; } void MainWindow::UpdateFlag(PowerRenameFlags flag, UpdateFlagCommand command) { _TRACER_; DWORD flags{}; m_prManager->GetFlags(&flags); if (command == UpdateFlagCommand::Set) { flags |= flag; } else if (command == UpdateFlagCommand::Reset) { flags &= ~flag; } Logger::debug(L"Flag {} " + std::wstring{ command == UpdateFlagCommand::Set ? L"set" : L"reset" }, flag); // Ensure we update flags if (m_prManager) { m_prManager->PutFlags(flags); } InvalidateItemListViewState(); } void MainWindow::SetHandlers() { _TRACER_; // AutoSuggestBox Search textBox_search().TextChanged([&](auto const&, auto const&) { SearchReplaceChanged(); }); // AutoSuggestBox Replace textBox_replace().TextChanged([&](auto const&, auto const&) { SearchReplaceChanged(); }); // ToggleButton UpperCase toggleButton_upperCase().Checked([&](auto const&, auto const&) { ValidateFlags(Uppercase); UpdateFlag(Uppercase, UpdateFlagCommand::Set); }); toggleButton_upperCase().Unchecked([&](auto const&, auto const&) { UpdateFlag(Uppercase, UpdateFlagCommand::Reset); }); // ToggleButton LowerCase toggleButton_lowerCase().Checked([&](auto const&, auto const&) { ValidateFlags(Lowercase); UpdateFlag(Lowercase, UpdateFlagCommand::Set); }); toggleButton_lowerCase().Unchecked([&](auto const&, auto const&) { UpdateFlag(Lowercase, UpdateFlagCommand::Reset); }); // ToggleButton TitleCase toggleButton_titleCase().Checked([&](auto const&, auto const&) { ValidateFlags(Titlecase); UpdateFlag(Titlecase, UpdateFlagCommand::Set); }); toggleButton_titleCase().Unchecked([&](auto const&, auto const&) { UpdateFlag(Titlecase, UpdateFlagCommand::Reset); }); // ToggleButton Capitalize toggleButton_capitalize().Checked([&](auto const&, auto const&) { ValidateFlags(Capitalized); UpdateFlag(Capitalized, UpdateFlagCommand::Set); }); toggleButton_capitalize().Unchecked([&](auto const&, auto const&) { UpdateFlag(Capitalized, UpdateFlagCommand::Reset); }); // CheckBox Regex checkBox_regex().Checked([&](auto const&, auto const&) { ValidateFlags(UseRegularExpressions); UpdateFlag(UseRegularExpressions, UpdateFlagCommand::Set); }); checkBox_regex().Unchecked([&](auto const&, auto const&) { UpdateFlag(UseRegularExpressions, UpdateFlagCommand::Reset); }); // CheckBox CaseSensitive checkBox_case().Checked([&](auto const&, auto const&) { ValidateFlags(CaseSensitive); UpdateFlag(CaseSensitive, UpdateFlagCommand::Set); }); checkBox_case().Unchecked([&](auto const&, auto const&) { UpdateFlag(CaseSensitive, UpdateFlagCommand::Reset); }); // ComboBox RenameParts comboBox_renameParts().SelectionChanged([&](auto const&, auto const&) { int selectedIndex = comboBox_renameParts().SelectedIndex(); if (selectedIndex == 0) { // Filename + extension UpdateFlag(NameOnly, UpdateFlagCommand::Reset); UpdateFlag(ExtensionOnly, UpdateFlagCommand::Reset); } else if (selectedIndex == 1) // Filename Only { ValidateFlags(NameOnly); UpdateFlag(ExtensionOnly, UpdateFlagCommand::Reset); UpdateFlag(NameOnly, UpdateFlagCommand::Set); } else if (selectedIndex == 2) // Extension Only { ValidateFlags(ExtensionOnly); UpdateFlag(NameOnly, UpdateFlagCommand::Reset); UpdateFlag(ExtensionOnly, UpdateFlagCommand::Set); } }); // CheckBox MatchAllOccurrences checkBox_matchAll().Checked([&](auto const&, auto const&) { ValidateFlags(MatchAllOccurrences); UpdateFlag(MatchAllOccurrences, UpdateFlagCommand::Set); }); checkBox_matchAll().Unchecked([&](auto const&, auto const&) { UpdateFlag(MatchAllOccurrences, UpdateFlagCommand::Reset); }); // ToggleButton IncludeFiles toggleButton_includeFiles().Checked([&](auto const&, auto const&) { ValidateFlags(ExcludeFiles); UpdateFlag(ExcludeFiles, UpdateFlagCommand::Reset); }); toggleButton_includeFiles().Unchecked([&](auto const&, auto const&) { UpdateFlag(ExcludeFiles, UpdateFlagCommand::Set); }); // ToggleButton IncludeFolders toggleButton_includeFolders().Checked([&](auto const&, auto const&) { ValidateFlags(ExcludeFolders); UpdateFlag(ExcludeFolders, UpdateFlagCommand::Reset); }); toggleButton_includeFolders().Unchecked([&](auto const&, auto const&) { UpdateFlag(ExcludeFolders, UpdateFlagCommand::Set); }); // ToggleButton IncludeSubfolders toggleButton_includeSubfolders().Checked([&](auto const&, auto const&) { ValidateFlags(ExcludeSubfolders); UpdateFlag(ExcludeSubfolders, UpdateFlagCommand::Reset); }); toggleButton_includeSubfolders().Unchecked([&](auto const&, auto const&) { UpdateFlag(ExcludeSubfolders, UpdateFlagCommand::Set); }); // CheckBox EnumerateItems toggleButton_enumItems().Checked([&](auto const&, auto const&) { ValidateFlags(EnumerateItems); UpdateFlag(EnumerateItems, UpdateFlagCommand::Set); }); toggleButton_enumItems().Unchecked([&](auto const&, auto const&) { UpdateFlag(EnumerateItems, UpdateFlagCommand::Reset); }); // CheckBox RandomizeItems toggleButton_randItems().Checked([&](auto const&, auto const&) { ValidateFlags(RandomizeItems); UpdateFlag(RandomizeItems, UpdateFlagCommand::Set); }); toggleButton_randItems().Unchecked([&](auto const&, auto const&) { UpdateFlag(RandomizeItems, UpdateFlagCommand::Reset); }); // ButtonSettings button_settings().Click([&](auto const&, auto const&) { OpenSettingsApp(); }); } void MainWindow::ToggleItem(int32_t id, bool checked) { _TRACER_; Logger::debug(L"Toggling item with id = {}", id); CComPtr spItem; if (SUCCEEDED(m_prManager->GetItemById(id, &spItem))) { spItem->PutSelected(checked); } UpdateCounts(); } void MainWindow::ToggleAll() { _TRACER_; UINT itemCount = 0; m_prManager->GetItemCount(&itemCount); bool selected = checkBox_selectAll().IsChecked().GetBoolean(); for (UINT i = 0; i < itemCount; i++) { CComPtr spItem; if (SUCCEEDED(m_prManager->GetItemByIndex(i, &spItem))) { spItem->PutSelected(selected); } } UpdateCounts(); } void MainWindow::SwitchView() { _TRACER_; m_prManager->SwitchFilter(0); UpdateCounts(); } void MainWindow::Rename(bool closeWindow) { _TRACER_; if (m_prManager) { m_prManager->Rename(m_window, closeWindow); } // Persist the current settings. We only do this when // a rename is actually performed. Not when the user // closes/cancels the dialog. WriteSettings(); } HRESULT MainWindow::ReadSettings() { _TRACER_; bool persistState{ CSettingsInstance().GetPersistState() }; Logger::debug(L"ReadSettings with persistState = {}", persistState); // Check if we should read flags from settings // or the defaults from the manager. DWORD flags = 0; if (persistState) { flags = CSettingsInstance().GetFlags(); textBox_search().Text(LastRunSettingsInstance().GetSearchText().c_str()); textBox_replace().Text(LastRunSettingsInstance().GetReplaceText().c_str()); } else { m_prManager->GetFlags(&flags); } m_prManager->PutFlags(flags); SetCheckboxesFromFlags(flags); return S_OK; } HRESULT MainWindow::WriteSettings() { _TRACER_; // Check if we should store our settings if (CSettingsInstance().GetPersistState()) { DWORD flags = 0; m_prManager->GetFlags(&flags); CSettingsInstance().SetFlags(flags); winrt::hstring searchTerm = textBox_search().Text(); LastRunSettingsInstance().SetSearchText(std::wstring{ searchTerm }); if (CSettingsInstance().GetMRUEnabled() && m_searchMRU) { CComPtr spSearchMRU; if (SUCCEEDED(m_searchMRU->QueryInterface(IID_PPV_ARGS(&spSearchMRU)))) { spSearchMRU->AddMRUString(searchTerm.c_str()); } } winrt::hstring replaceTerm = textBox_replace().Text(); LastRunSettingsInstance().SetReplaceText(std::wstring{ replaceTerm }); if (CSettingsInstance().GetMRUEnabled() && m_replaceMRU) { CComPtr spReplaceMRU; if (SUCCEEDED(m_replaceMRU->QueryInterface(IID_PPV_ARGS(&spReplaceMRU)))) { spReplaceMRU->AddMRUString(replaceTerm.c_str()); } } Trace::SettingsChanged(); } return S_OK; } HRESULT MainWindow::OpenSettingsApp() { std::wstring path = get_module_folderpath(g_hostHInst); path += L"\\..\\PowerToys.exe"; std::wstring openSettings = L"--open-settings=PowerRename"; CString commandLine; commandLine.Format(_T("\"%s\""), path.c_str()); commandLine.AppendFormat(_T(" %s"), openSettings.c_str()); int nSize = commandLine.GetLength() + 1; LPTSTR lpszCommandLine = new TCHAR[nSize]; _tcscpy_s(lpszCommandLine, nSize, commandLine); STARTUPINFO startupInfo; ZeroMemory(&startupInfo, sizeof(STARTUPINFO)); startupInfo.cb = sizeof(STARTUPINFO); startupInfo.wShowWindow = SW_SHOWNORMAL; PROCESS_INFORMATION processInformation; // Start the resizer CreateProcess( NULL, lpszCommandLine, NULL, NULL, TRUE, 0, NULL, NULL, &startupInfo, &processInformation); delete[] lpszCommandLine; if (!CloseHandle(processInformation.hProcess)) { return HRESULT_FROM_WIN32(GetLastError()); } if (!CloseHandle(processInformation.hThread)) { return HRESULT_FROM_WIN32(GetLastError()); } return S_OK; } void MainWindow::SetCheckboxesFromFlags(DWORD flags) { if (flags & CaseSensitive) { checkBox_case().IsChecked(true); } if (flags & MatchAllOccurrences) { checkBox_matchAll().IsChecked(true); } if (flags & UseRegularExpressions) { checkBox_regex().IsChecked(true); } if (flags & EnumerateItems) { toggleButton_enumItems().IsChecked(true); } if (flags & RandomizeItems) { toggleButton_randItems().IsChecked(true); } if (flags & ExcludeFiles) { toggleButton_includeFiles().IsChecked(false); } if (flags & ExcludeFolders) { toggleButton_includeFolders().IsChecked(false); } if (flags & ExcludeSubfolders) { toggleButton_includeSubfolders().IsChecked(false); } if (flags & NameOnly) { comboBox_renameParts().SelectedIndex(1); } else if (flags & ExtensionOnly) { comboBox_renameParts().SelectedIndex(2); } if (flags & Uppercase) { toggleButton_upperCase().IsChecked(true); } else if (flags & Lowercase) { toggleButton_lowerCase().IsChecked(true); } else if (flags & Titlecase) { toggleButton_titleCase().IsChecked(true); } else if (flags & Capitalized) { toggleButton_capitalize().IsChecked(true); } } void MainWindow::UpdateCounts() { // This method is CPU intensive. We disable it during certain operations // for performance reasons. if (m_disableCountUpdate) { return; } UINT selectedCount = 0; UINT renamingCount = 0; if (m_prManager) { m_prManager->GetSelectedItemCount(&selectedCount); m_prManager->GetRenameItemCount(&renamingCount); } if (m_selectedCount != selectedCount || m_renamingCount != renamingCount) { m_selectedCount = selectedCount; m_renamingCount = renamingCount; // Update Rename button state button_rename().IsEnabled(renamingCount > 0); } RenamedCount(hstring{ std::to_wstring(m_renamingCount) }); } HRESULT MainWindow::OnRename(_In_ IPowerRenameItem* /*renameItem*/) { UpdateCounts(); return S_OK; } HRESULT MainWindow::OnRegExCompleted(_In_ DWORD) { _TRACER_; if (m_flagValidationInProgress) { m_flagValidationInProgress = false; } UpdateCounts(); InvalidateItemListViewState(); return S_OK; } HRESULT MainWindow::OnRenameCompleted(bool closeUIWindowAfterRenaming) { _TRACER_; Logger::debug(L"Renaming completed. Close UI window - {}", closeUIWindowAfterRenaming); if (closeUIWindowAfterRenaming) { // Close the window PostMessage(m_window, WM_CLOSE, static_cast(0), static_cast(0)); } else { // Force renaming work to start so newly renamed items are processed right away SearchReplaceChanged(true); } InvalidateItemListViewState(); return S_OK; } } ================================================ FILE: PowerToys/src/modules/powerrename/PowerRenameUILib/Strings/en-us/Resources.resw ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 搜索 正则表达式用法 一个任意字符 一个数字,[0-9] 一个非数字,[^0-9] 一个非空白字符,[^\\s] 一个英文字符,[a-zA-Z_0-9] 一个及以上非空白字符 一个英文单词的边界,[a-zA-Z0-9_] 了解正则表达式 替换为 数字编号器用法 普通的递增计数器 自定义起始值 自定义增量 自定义前导零 更复杂的自定义 文件名参数 年份,2023 年份后两位,23 年份后一位,3 月份名,一月 月份缩写,1月 月份补零,01-12 月份,1-12 星期名,星期一 星期缩写,周一 日期补零,01-31 日期,1-31 小时补零,00-23 小时,0-23 分钟补零,00-59 分钟,0-59 秒钟补零,00-59 秒钟,0-59 毫秒,123 毫秒前两位,12 毫秒前一位,1 正则表达式 全部替换 区分大小写 目标 帮助 帮助 文件 文件 文件夹 文件夹 文件夹内 文件夹内 文本格式 转小写 转小写 转大写 转大写 转标题 转标题 转首字母大写 转首字母大写 添加数字编号 添加数字编号 全选或取消全选 筛选 筛选 所有文件 符合搜索的文件 原文件 命名为 设置 设置 执行 应用 执行并关闭 正则用法提示 正则用法提示 参数用法提示 参数用法提示 文件名过长 文件名不能包含以下字符: > < | " : ? * \ / 文件名 + 扩展名 仅文件名 仅扩展名 所设长度的随机数字和大小写字母,0-9 A-z 所设长度的随机大小写字母,A-z 所设长度的随机数字,0-9 随机字符生成器用法 随机 UUID v4 添加随机字符 添加随机字符 ================================================ FILE: PowerToys/src/modules/powerrename/dll/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 批量重命名(&W) do not loc, product name 批量重命名 do not loc, product name 批量重命名 PowerRename is a product name. do not loc it. The & allows to use W as a keyboard accelerator. A Windows Shell extension for more advanced bulk renaming using search and replace or regular expressions. Restore search, replace and flags values on launch from previous run. Enable autocomplete and autosuggest of recently used inputs for search and replace values. Maximum number of items to show in recently used list for autocomplete dropdown. Show icon on context menu. The icon for PowerRename to be inside with Windows File Explorer right click context menu Only show the PowerRename menu item on the extended context menu (Shift + Right-click). Use Boost library (provides extended features but may use different regex syntax). Boost is a product name, should not be translated ================================================ FILE: PowerToys/src/modules/previewpane/GcodePreviewHandler/Properties/Resource.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 由于 PowerToys G-code 预览器出现错误,无法预览此文件。 This text is displayed if G-code fails to preview 此 G-code 文件没有内置缩略图。 组策略已禁止该功能。请联系你的电脑管理员。 GPO stands for the Windows Group Policy Object feature. ================================================ FILE: PowerToys/src/modules/previewpane/MarkdownPreviewHandler/Properties/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 一些图片已被屏蔽,防止电脑访问未知网址引起危险。启用此选项允许查看图片。 This text is displayed if image is blocked from being displayed. 由于 PowerToys Markdown 预览器出现错误,无法预览此文件。 This text is displayed if markdown fails to preview 组策略已禁止该功能。请联系你的电脑管理员。 GPO stands for the Windows Group Policy Object feature. ================================================ FILE: PowerToys/src/modules/previewpane/MonacoPreviewHandler/Properties/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 文件过大,无法预览。 最大文件大小:%1KB Error message which is shown when the file is too big. %1 gets replaced by a value. Leave KB (means Kilobyte) 文件预览发生错误: Will be shown when an exception occurred. 加载中... 未安装 WebView2。 下载 WebView2 即可预览该文件。 组策略已禁止该功能。请联系你的电脑管理员。 GPO stands for the Windows Group Policy Object feature. 拒绝访问: 您当前无权打开该文件。请联系文件所有者或电脑管理员以获得许可。 ================================================ FILE: PowerToys/src/modules/previewpane/PdfPreviewHandler/Properties/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 此 PDF 文档超过 10 页,剩余部分请打开浏览。 This text is displayed if PDF has more than 10 pages. 由于 PowerToys PDF 预览器出现错误,无法预览此文件。 This text is displayed if PDF fails to preview PDF 文档已加密。 This text is displayed if PDF is password protected. 组策略已禁止该功能。请联系你的电脑管理员。 GPO stands for the Windows Group Policy Object feature. ================================================ FILE: PowerToys/src/modules/previewpane/QoiPreviewHandler/Properties/Resource.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 由于 PowerToys QOI 预览器出现错误,无法预览此文件。 This text is displayed if Qoi fails to preview 组策略已禁止该功能。请联系你的电脑管理员。 GPO stands for the Windows Group Policy Object feature. ================================================ FILE: PowerToys/src/modules/previewpane/SvgPreviewHandler/Properties/Resource.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 一些元素已被屏蔽,防止电脑访问未知网址引起危险。启用此选项允许查看所有元素。 由于 PowerToys SVG 预览器出现错误,无法预览此文件。 组策略已禁止该功能。请联系你的电脑管理员。 GPO stands for the Windows Group Policy Object feature. ================================================ FILE: PowerToys/src/modules/previewpane/powerpreview/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 PowerToys add-ons for Windows File Explorer. File Explorer pt-power-preview Settings Group Header Text Explorer Icons Settings Group Header Text Preview pane extensions Markdown Previewer Developer files Previewer Markdown Previewer Svg Previewer SVG Previewer Svg Thumbnail Provider Render SVG images 一些预览组件的更改不支持立即生效,请以管理员身份重启软件以生效。 预览增强组件更改 打开设置 不再显示 PDF Previewer PDF Previewer Pdf Thumbnail Provider G-code Thumbnail Provider G-code Previewer G-code Previewer Stl Thumbnail Provider Qoi Previewer Qoi Thumbnail Provider ================================================ FILE: PowerToys/src/modules/registrypreview/RegistryPreview/RegistryPreviewXAML/MainWindow.xaml.cs ================================================ // 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.UI; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Media; using RegistryPreviewUILib; using Windows.Data.Json; using Windows.Graphics; using WinUIEx; namespace RegistryPreview { public sealed partial class MainWindow : WindowEx { // Const values private const string APPNAME = "注册表预览"; // private members private Microsoft.UI.Windowing.AppWindow appWindow; private JsonObject jsonWindowPlacement; private string settingsFolder = string.Empty; private string windowPlacementFile = "app-placement.json"; private RegistryPreviewMainPage MainPage { get; } internal MainWindow() { this.InitializeComponent(); // Open settings file; this moved to after the window tweak because it gives the window time to start up settingsFolder = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + @"\Microsoft\PowerToys\" + APPNAME; OpenWindowPlacementFile(settingsFolder, windowPlacementFile); // Update the Win32 looking window with the correct icon (and grab the appWindow handle for later) IntPtr windowHandle = this.GetWindowHandle(); WindowId windowId = Win32Interop.GetWindowIdFromWindow(windowHandle); appWindow = Microsoft.UI.Windowing.AppWindow.GetFromWindowId(windowId); appWindow.SetIcon("Assets\\RegistryPreview\\RegistryPreview.ico"); // TODO(stefan) appWindow.Closing += AppWindow_Closing; Activated += MainWindow_Activated; // Extend the canvas to include the title bar so the app can support theming ExtendsContentIntoTitleBar = true; WindowHelpers.ForceTopBorder1PixelInsetOnWindows10(windowHandle); SetTitleBar(titleBar); // if have settings, update the location of the window if (jsonWindowPlacement != null) { // resize the window if (jsonWindowPlacement.ContainsKey("appWindow.Size.Width") && jsonWindowPlacement.ContainsKey("appWindow.Size.Height")) { SizeInt32 size; size.Width = (int)jsonWindowPlacement.GetNamedNumber("appWindow.Size.Width"); size.Height = (int)jsonWindowPlacement.GetNamedNumber("appWindow.Size.Height"); // check to make sure the size values are reasonable before attempting to restore the last saved size if (size.Width >= 320 && size.Height >= 240) { appWindow.Resize(size); } } // reposition the window if (jsonWindowPlacement.ContainsKey("appWindow.Position.X") && jsonWindowPlacement.ContainsKey("appWindow.Position.Y")) { PointInt32 point; point.X = (int)jsonWindowPlacement.GetNamedNumber("appWindow.Position.X"); point.Y = (int)jsonWindowPlacement.GetNamedNumber("appWindow.Position.Y"); // check to make sure the move values are reasonable before attempting to restore the last saved location if (point.X >= 0 && point.Y >= 0) { appWindow.Move(point); } } } MainPage = new RegistryPreviewMainPage(this, this.UpdateWindowTitle, App.AppFilename); WindowHelpers.BringToForeground(windowHandle); } private void MainWindow_Activated(object sender, WindowActivatedEventArgs args) { if (args.WindowActivationState == WindowActivationState.Deactivated) { titleBarText.Foreground = (SolidColorBrush)Application.Current.Resources["WindowCaptionForegroundDisabled"]; } else { titleBarText.Foreground = (SolidColorBrush)Application.Current.Resources["WindowCaptionForeground"]; } } private void Grid_Loaded(object sender, RoutedEventArgs e) { MainGrid.Children.Add(MainPage); Grid.SetRow(MainPage, 1); } public void UpdateWindowTitle(string title) { string filename = title; if (string.IsNullOrEmpty(filename)) { titleBarText.Text = APPNAME; appWindow.Title = APPNAME; } else { string[] file = filename.Split('\\'); if (file.Length > 0) { titleBarText.Text = file[file.Length - 1] + " - " + APPNAME; } else { titleBarText.Text = filename + " - " + APPNAME; } // Continue to update the window's title, after updating the custom title bar appWindow.Title = titleBarText.Text; } } } } ================================================ FILE: PowerToys/src/modules/registrypreview/RegistryPreviewUILib/Strings/en-US/Resources.resw ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 编辑文本 错误 无法打开 .reg 注册表文件编辑器。 文件不存在! 文件无法写入。 所有文件 (*.*) 注册表文件 (*.reg) (无效二进制值) (无效 DWORD (32 位) 值) (无效 QWORD (64 位) 值) 不是有效的注册表文件! 非注册表文件 (无效字符串值) 大于 10MB,注册表预览工具无法打开该文件。 文件过大! 名称 找不到有效的键 好的 as on the OK button 打开 打开注册表文件 重新加载文件 打开注册表编辑器 打开键 另存为 另存为 保存 新建注册表文件 注册表预览 如果不存在,键将被自动创建 二进制值将写入到注册表 键将被删除 值将被删除 键无法解析 值存在语法错误 字符串值将写入到注册表 类型 noun, as in "a type of..." 打开和写入系统注册表需要管理员权限,在刚才的弹窗中请点击允许。 获取管理员权限 数据 写入到注册表 取消 文件有改动,现在保存吗? 保存 不保存 注册表预览 (零长度二进制值) 复制路径 Like "Copy item" 复制名称 Like "Copy item" 复制类型 Like "Copy item" 复制数据 Like "Copy item" 复制此注册表值 Like "Copy item" 复制此注册表值与路径 Like "Copy item" ================================================ FILE: PowerToys/src/runner/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 PowerToys 错误 无法以用户身份启动 PowerToys! 无法以管理员身份启动 PowerToys! PowerToys 已经在运行。 PowerToys 已成功更新! 旧版 MSI 安装包的 PowerToys 正在运行。 无法下载 PowerToys 更新!请在 GitHub 上报告该问题。 已经卸载了旧版 MSIX 安装包的 PowerToys。 PowerToys 已成功更新! 此功能已被管理员通过组策略禁止。 此功能已在 <a href="https://ms_settings_startupapps" target="_blank">启动应用</a> 设置中被手动禁止。 PowerToys 有版本更新。 立刻更新 PowerToys 有版本更新,请访问 GitHub 页面下载。 更多信息 PowerToys 更新 快捷面板\t单击图标 Don't localize "\t" as that is what separates the click portion to be right aligned in the menu. 设置\t双击图标 Don't localize "\t" as that is what separates the click portion to be right aligned in the menu. 帮助 退出 Exit as a verb, as in Exit the application 报告 问题报告已打包为 Zip 文件放在桌面上了。 PowerToys 新版本安装完成,由于程序文件路径变更,需要重启电脑,来重新加载文件管理器集成功能。 File Explorer refers to the Windows File Explorer application. 管理员权限 ================================================ FILE: PowerToys/src/runner/main.cpp ================================================ #include "pch.h" #include #include #include #include #include "tray_icon.h" #include "powertoy_module.h" #include "trace.h" #include "general_settings.h" #include "restart_elevated.h" #include "RestartManagement.h" #include "Generated files/resource.h" #include "settings_telemetry.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "UpdateUtils.h" #include "ActionRunnerUtils.h" #include #include #include #include "centralized_kb_hook.h" #include "centralized_hotkeys.h" #if _DEBUG && _WIN64 #include "unhandled_exception_handler.h" #endif #include #include #include #include #include #include #include #include #include // disabling warning 4458 - declaration of 'identifier' hides class member // to avoid warnings from GDI files - can't add winRT directory to external code // in the Cpp.Build.props #pragma warning(push) #pragma warning(disable : 4458) #include #pragma warning(pop) namespace { const wchar_t PT_URI_PROTOCOL_SCHEME[] = L"powertoys://"; const wchar_t POWER_TOYS_MODULE_LOAD_FAIL[] = L"无法加载 "; // Module name will be appended on this message and it is not localized. } void chdir_current_executable() { // Change current directory to the path of the executable. WCHAR executable_path[MAX_PATH]; GetModuleFileName(NULL, executable_path, MAX_PATH); PathRemoveFileSpec(executable_path); if (!SetCurrentDirectory(executable_path)) { show_last_error_message(L"Change Directory to Executable Path", GetLastError(), L"PowerToys - runner"); } } inline wil::unique_mutex_nothrow create_msi_mutex() { return createAppMutex(POWERTOYS_MSI_MUTEX_NAME); } void open_menu_from_another_instance(std::optional settings_window) { const HWND hwnd_main = FindWindowW(L"PToyTrayIconWindow", nullptr); LPARAM msg = static_cast(ESettingsWindowNames::Dashboard); if (settings_window.has_value() && settings_window.value() != "") { msg = static_cast(ESettingsWindowNames_from_string(settings_window.value())); } PostMessageW(hwnd_main, WM_COMMAND, ID_SETTINGS_MENU_COMMAND, msg); } int runner(bool isProcessElevated, bool openSettings, std::string settingsWindow, bool openOobe, bool openScoobe, bool showRestartNotificationAfterUpdate) { Logger::info("Runner is starting. Elevated={} openOobe={} openScoobe={} showRestartNotificationAfterUpdate={}", isProcessElevated, openOobe, openScoobe, showRestartNotificationAfterUpdate); DPIAware::EnableDPIAwarenessForThisProcess(); #if _DEBUG && _WIN64 //Global error handlers to diagnose errors. //We prefer this not to show any longer until there's a bug to diagnose. //init_global_error_handlers(); #endif Trace::RegisterProvider(); start_tray_icon(isProcessElevated); CentralizedKeyboardHook::Start(); int result = -1; try { if (!openOobe && showRestartNotificationAfterUpdate) { std::thread{ [] { // Wait a bit, because Windows has a delay until it picks up toast notification registration in the registry Sleep(10000); Logger::info("Showing toast notification asking to restart PC"); notifications::show_toast(GET_RESOURCE_STRING(IDS_PT_VERSION_CHANGE_ASK_FOR_COMPUTER_RESTART).c_str(), L"PowerToys"); } }.detach(); } std::thread{ [] { PeriodicUpdateWorker(); } }.detach(); std::thread{ [] { if (updating::uninstall_previous_msix_version_async().get()) { notifications::show_toast(GET_RESOURCE_STRING(IDS_OLDER_MSIX_UNINSTALLED).c_str(), L"PowerToys"); } } }.detach(); chdir_current_executable(); // We deprecated a utility called Video Conference Mute, which registered itself as a video input device. // When running elevated, we try to clean up the device registration from previous installations. // This is done here too because a user-scope installer won't be able to remove the driver registration due to lack of permissions. if (isProcessElevated) { clean_video_conference(); } // Load PowerToys DLLs std::vector knownModules = { L"PowerToys.FancyZonesModuleInterface.dll", L"PowerToys.powerpreview.dll", L"PowerToys.ImageResizerExt.dll", L"PowerToys.KeyboardManager.dll", L"PowerToys.Launcher.dll", L"WinUI3Apps/PowerToys.PowerRenameExt.dll", L"PowerToys.ShortcutGuideModuleInterface.dll", L"PowerToys.ColorPicker.dll", L"PowerToys.AwakeModuleInterface.dll", L"PowerToys.FindMyMouse.dll", L"PowerToys.MouseHighlighter.dll", L"PowerToys.MouseJump.dll", L"PowerToys.AlwaysOnTopModuleInterface.dll", L"PowerToys.MousePointerCrosshairs.dll", L"PowerToys.PowerAccentModuleInterface.dll", L"PowerToys.PowerOCRModuleInterface.dll", L"PowerToys.AdvancedPasteModuleInterface.dll", L"WinUI3Apps/PowerToys.FileLocksmithExt.dll", L"WinUI3Apps/PowerToys.RegistryPreviewExt.dll", L"WinUI3Apps/PowerToys.MeasureToolModuleInterface.dll", L"WinUI3Apps/PowerToys.NewPlus.ShellExtension.dll", L"WinUI3Apps/PowerToys.HostsModuleInterface.dll", L"WinUI3Apps/PowerToys.Peek.dll", L"WinUI3Apps/PowerToys.EnvironmentVariablesModuleInterface.dll", L"PowerToys.MouseWithoutBordersModuleInterface.dll", L"PowerToys.CropAndLockModuleInterface.dll", L"PowerToys.CmdNotFoundModuleInterface.dll", L"PowerToys.WorkspacesModuleInterface.dll", L"PowerToys.ZoomItModuleInterface.dll", }; for (auto moduleSubdir : knownModules) { try { auto pt_module = load_powertoy(moduleSubdir); modules().emplace(pt_module->get_key(), std::move(pt_module)); } catch (...) { std::wstring errorMessage = POWER_TOYS_MODULE_LOAD_FAIL; errorMessage += moduleSubdir; MessageBoxW(NULL, errorMessage.c_str(), L"PowerToys", MB_OK | MB_ICONERROR); } } // Start initial powertoys start_enabled_powertoys(); std::wstring product_version = get_product_version(); Trace::EventLaunch(product_version, isProcessElevated); PTSettingsHelper::save_last_version_run(product_version); if (openSettings) { std::optional window; if (!settingsWindow.empty()) { window = winrt::to_hstring(settingsWindow); } open_settings_window(window, false); } if (openOobe) { PTSettingsHelper::save_oobe_opened_state(); open_oobe_window(); } else if (openScoobe) { open_scoobe_window(); } settings_telemetry::init(); result = run_message_loop(); } catch (std::runtime_error& err) { std::string err_what = err.what(); MessageBoxW(nullptr, std::wstring(err_what.begin(), err_what.end()).c_str(), GET_RESOURCE_STRING(IDS_ERROR).c_str(), MB_OK | MB_ICONERROR | MB_SETFOREGROUND); result = -1; } Trace::UnregisterProvider(); return result; } // If the PT runner is launched as part of some action and manually by a user, e.g. being activated as a COM server // for background toast notification handling, we should execute corresponding code flow instead of the main code flow. enum class SpecialMode { None, Win32ToastNotificationCOMServer, ToastNotificationHandler, ReportSuccessfulUpdate }; SpecialMode should_run_in_special_mode(const int n_cmd_args, LPWSTR* cmd_arg_list) { for (size_t i = 1; i < n_cmd_args; ++i) { if (!wcscmp(notifications::TOAST_ACTIVATED_LAUNCH_ARG, cmd_arg_list[i])) { return SpecialMode::Win32ToastNotificationCOMServer; } else if (n_cmd_args == 2 && !wcsncmp(PT_URI_PROTOCOL_SCHEME, cmd_arg_list[i], wcslen(PT_URI_PROTOCOL_SCHEME))) { return SpecialMode::ToastNotificationHandler; } else if (n_cmd_args == 2 && !wcscmp(cmdArg::UPDATE_REPORT_SUCCESS, cmd_arg_list[i])) { return SpecialMode::ReportSuccessfulUpdate; } } return SpecialMode::None; } int win32_toast_notification_COM_server_mode() { notifications::run_desktop_app_activator_loop(); return 0; } enum class toast_notification_handler_result { exit_success, exit_error }; toast_notification_handler_result toast_notification_handler(const std::wstring_view param) { const std::wstring_view cant_drag_elevated_disable = L"cant_drag_elevated_disable/"; const std::wstring_view couldnt_toggle_powerpreview_modules_disable = L"couldnt_toggle_powerpreview_modules_disable/"; const std::wstring_view open_settings = L"open_settings/"; const std::wstring_view open_overview = L"open_overview/"; const std::wstring_view update_now = L"update_now/"; if (param == cant_drag_elevated_disable) { return notifications::disable_toast(notifications::ElevatedDontShowAgainRegistryPath) ? toast_notification_handler_result::exit_success : toast_notification_handler_result::exit_error; } else if (param.starts_with(update_now)) { std::wstring args{ cmdArg::UPDATE_NOW_LAUNCH_STAGE1 }; LaunchPowerToysUpdate(args.c_str()); return toast_notification_handler_result::exit_success; } else if (param == couldnt_toggle_powerpreview_modules_disable) { return notifications::disable_toast(notifications::PreviewModulesDontShowAgainRegistryPath) ? toast_notification_handler_result::exit_success : toast_notification_handler_result::exit_error; } else if (param == open_settings) { open_menu_from_another_instance(std::nullopt); return toast_notification_handler_result::exit_success; } else if (param == open_overview) { open_menu_from_another_instance("Overview"); return toast_notification_handler_result::exit_success; } else { return toast_notification_handler_result::exit_error; } } int WINAPI WinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR lpCmdLine, int /*nCmdShow*/) { Shared::Trace::ETWTrace trace{}; trace.UpdateState(true); Gdiplus::GdiplusStartupInput gpStartupInput; ULONG_PTR gpToken; GdiplusStartup(&gpToken, &gpStartupInput, NULL); winrt::init_apartment(); const wchar_t* securityDescriptor = L"O:BA" // Owner: Builtin (local) administrator L"G:BA" // Group: Builtin (local) administrator L"D:" L"(A;;0x7;;;PS)" // Access allowed on COM_RIGHTS_EXECUTE, _LOCAL, & _REMOTE for Personal self L"(A;;0x7;;;IU)" // Access allowed on COM_RIGHTS_EXECUTE for Interactive Users L"(A;;0x3;;;SY)" // Access allowed on COM_RIGHTS_EXECUTE, & _LOCAL for Local system L"(A;;0x7;;;BA)" // Access allowed on COM_RIGHTS_EXECUTE, _LOCAL, & _REMOTE for Builtin (local) administrator L"(A;;0x3;;;S-1-15-3-1310292540-1029022339-4008023048-2190398717-53961996-4257829345-603366646)" // Access allowed on COM_RIGHTS_EXECUTE, & _LOCAL for Win32WebViewHost package capability L"S:" L"(ML;;NX;;;LW)"; // Integrity label on No execute up for Low mandatory level initializeCOMSecurity(securityDescriptor); int n_cmd_args = 0; LPWSTR* cmd_arg_list = CommandLineToArgvW(GetCommandLineW(), &n_cmd_args); switch (should_run_in_special_mode(n_cmd_args, cmd_arg_list)) { case SpecialMode::Win32ToastNotificationCOMServer: return win32_toast_notification_COM_server_mode(); case SpecialMode::ToastNotificationHandler: switch (toast_notification_handler(cmd_arg_list[1] + wcslen(PT_URI_PROTOCOL_SCHEME))) { case toast_notification_handler_result::exit_error: return 1; case toast_notification_handler_result::exit_success: return 0; } [[fallthrough]]; case SpecialMode::ReportSuccessfulUpdate: { notifications::remove_toasts_by_tag(notifications::UPDATING_PROCESS_TOAST_TAG); notifications::remove_all_scheduled_toasts(); notifications::show_toast(GET_RESOURCE_STRING(IDS_PT_UPDATE_MESSAGE_BOX_TEXT), L"PowerToys", notifications::toast_params{ notifications::UPDATING_PROCESS_TOAST_TAG }); break; } case SpecialMode::None: // continue as usual break; } std::filesystem::path logFilePath(PTSettingsHelper::get_root_save_folder_location()); logFilePath.append(LogSettings::runnerLogPath); Logger::init(LogSettings::runnerLoggerName, logFilePath.wstring(), PTSettingsHelper::get_log_settings_file_location()); const std::string cmdLine{ lpCmdLine }; Logger::info("Running powertoys with cmd args: {}", cmdLine); auto open_settings_it = cmdLine.find("--open-settings"); const bool open_settings = open_settings_it != std::string::npos; // Check if opening specific settings window open_settings_it = cmdLine.find("--open-settings="); std::string settings_window; if (open_settings_it != std::string::npos) { std::string rest_of_cmd_line{ cmdLine, open_settings_it + std::string{ "--open-settings=" }.size() }; std::istringstream iss(rest_of_cmd_line); iss >> settings_window; } // Check if another instance is already running. wil::unique_mutex_nothrow msi_mutex = create_msi_mutex(); if (!msi_mutex) { open_menu_from_another_instance(settings_window); return 0; } bool openOobe = false; try { openOobe = !PTSettingsHelper::get_oobe_opened_state(); } catch (const std::exception& e) { Logger::error("Failed to get or save OOBE state with an exception: {}", e.what()); } bool openScoobe = false; bool showRestartNotificationAfterUpdate = false; try { std::wstring last_version_run = PTSettingsHelper::get_last_version_run(); const auto product_version = get_product_version(); openScoobe = product_version != last_version_run; showRestartNotificationAfterUpdate = openScoobe; Logger::info(L"Scoobe: product_version={} last_version_run={}", product_version, last_version_run); } catch (const std::exception& e) { Logger::error("Failed to get last version with an exception: {}", e.what()); } int result = 0; try { // Singletons initialization order needs to be preserved, first events and // then modules to guarantee the reverse destruction order. modules(); std::thread{ [] { auto state = UpdateState::read(); if (state.state == UpdateState::upToDate) { updating::cleanup_updates(); } } }.detach(); auto general_settings = load_general_settings(); // Apply the general settings but don't save it as the modules() variable has not been loaded yet apply_general_settings(general_settings, false); const bool elevated = is_process_elevated(); const bool with_dont_elevate_arg = cmdLine.find("--dont-elevate") != std::string::npos; const bool run_elevated_setting = general_settings.GetNamedBoolean(L"run_elevated", false); const bool with_restartedElevated_arg = cmdLine.find("--restartedElevated") != std::string::npos; // Update scoobe behavior based on setting and gpo bool scoobeSettingDisabled = general_settings.GetNamedBoolean(L"show_whats_new_after_updates", true) == false; bool scoobeDisabledByGpo = powertoys_gpo::getDisableShowWhatsNewAfterUpdatesValue() == powertoys_gpo::gpo_rule_configured_enabled; if (openScoobe && (scoobeSettingDisabled || scoobeDisabledByGpo)) { // Scoobe should show after an update, but is disabled by policy or setting Logger::info(L"Scoobe: Showing scoobe after updates is disabled by setting or by GPO."); openScoobe = false; } bool dataDiagnosticsDisabledByGpo = powertoys_gpo::getAllowDataDiagnosticsValue() == powertoys_gpo::gpo_rule_configured_disabled; if (dataDiagnosticsDisabledByGpo) { Logger::info(L"Data diagnostics: Data diagnostics is disabled by GPO."); PTSettingsHelper::save_data_diagnostics(false); } if (elevated && with_dont_elevate_arg && !run_elevated_setting) { Logger::info("Scheduling restart as non elevated"); schedule_restart_as_non_elevated(); result = 0; } else if (elevated || !run_elevated_setting || with_dont_elevate_arg || (!elevated && with_restartedElevated_arg)) { // The condition (!elevated && with_restartedElevated_arg) solves issue #19307. Restart elevated loop detected, running non-elevated if (!elevated && with_restartedElevated_arg) { Logger::info("Restart as elevated failed. Running non-elevated."); } result = runner(elevated, open_settings, settings_window, openOobe, openScoobe, showRestartNotificationAfterUpdate); if (result == 0) { // Save settings on closing, if closed 'normal' PTSettingsHelper::save_general_settings(get_general_settings().to_json()); } } else { Logger::info("Scheduling restart as elevated"); schedule_restart_as_elevated(open_settings); result = 0; } } catch (std::runtime_error& err) { std::string err_what = err.what(); MessageBoxW(nullptr, std::wstring(err_what.begin(), err_what.end()).c_str(), GET_RESOURCE_STRING(IDS_ERROR).c_str(), MB_OK | MB_ICONERROR); result = -1; } trace.Flush(); trace.UpdateState(false); // We need to release the mutexes to be able to restart the application if (msi_mutex) { msi_mutex.reset(nullptr); } if (is_restart_scheduled()) { modules().clear(); if (!restart_if_scheduled()) { // If it's not possible to restart non-elevated due to some condition in the user's configuration, user should start PowerToys manually. Logger::warn("Scheduled restart failed. Couldn't restart non-elevated. PowerToys exits here because retrying it would just mean failing in a loop."); } } stop_tray_icon(); return result; } ================================================ FILE: PowerToys/src/settings-ui/Settings.UI/Assets/Settings/Scripts/CheckCmdNotFoundRequirements.ps1 ================================================ Write-Host $PSVersionTable if ($PSVersionTable.PSVersion -ge 7.4) { Write-Host "已安装 PowerShell 7.4 或更高版本." # This message will be compared against in Command Not Found Settings page code behind. Take care when changing it. } else { Write-Host "未安装 PowerShell 7.4 或更高版本. 如需安装请访问 https://learn.microsoft.com/powershell/scripting/install/installing-powershell-on-windows `r`n" # This message will be compared against in Command Not Found Settings page code behind. Take care when changing it. } if ($mods = Get-Module -ListAvailable -Name Microsoft.WinGet.Client) { Write-Host "已安装 WinGet 客户端模块." # This message will be compared against in Command Not Found Settings page code behind. Take care when changing it. $needsUpdate = $true; foreach ($mod in $mods) { if ($mod.Version -ge "1.8.1133") { $needsUpdate = $false; break; } } if ($needsUpdate) { Write-Host "WinGet 客户端模块需要更新." # This message will be compared against in Command Not Found Settings page code behind. Take care when changing it. } } else { Write-Host "未安装 WinGet 客户端模块. 如需安装请访问 https://www.powershellgallery.com/packages/Microsoft.WinGet.Client `r`n" # This message will be compared against in Command Not Found Settings page code behind. Take care when changing it. } if (!(Test-Path $PROFILE)) { Write-Host "未发现配置文件 $PROFILE". New-Item -Path $PROFILE -ItemType File Write-Host "已创建配置文件 $PROFILE". } $profileContent = Get-Content -Path $PROFILE -Raw if ((-not [string]::IsNullOrEmpty($profileContent)) -and ($profileContent.Contains("34de4b3d-13a8-4540-b76d-b9e8d3851756"))) { Write-Host "已在配置文件中注册旧版模块." # This message will be compared against in Command Not Found Settings page code behind. Take care when changing it. } elseif ((-not [string]::IsNullOrEmpty($profileContent)) -and ($profileContent.Contains("f45873b3-b655-43a6-b217-97c00aa0db58"))) { Write-Host "已在配置文件中注册本模块." # This message will be compared against in Command Not Found Settings page code behind. Take care when changing it. } else { Write-Host "未在配置文件中注册本模块." # This message will be compared against in Command Not Found Settings page code behind. Take care when changing it. } ================================================ FILE: PowerToys/src/settings-ui/Settings.UI/Assets/Settings/Scripts/DisableModule.ps1 ================================================ $profileContent = Get-Content $PROFILE $newContent = "" $linesToDeleteFound = $False $atLeastOneInstanceFound = $False $profileContent | ForEach-Object { if (($_.Contains("34de4b3d-13a8-4540-b76d-b9e8d3851756") -or $_.Contains("f45873b3-b655-43a6-b217-97c00aa0db58")) -and !$linesToDeleteFound) { $linesToDeleteFound = $True $atLeastOneInstanceFound = $True return } if (($_.Contains("34de4b3d-13a8-4540-b76d-b9e8d3851756") -or $_.Contains("f45873b3-b655-43a6-b217-97c00aa0db58")) -and $linesToDeleteFound) { $linesToDeleteFound = $False return } if($linesToDeleteFound) { return } $newContent += $_ + "`r`n" } if($atLeastOneInstanceFound) { Set-Content -Path $PROFILE -Value $newContent Write-Host "已从配置文件中删除本模块." # This message will be compared against in Command Not Found Settings page code behind. Take care when changing it. } else { Write-Host "配置文件中没有发现本模块, 不需要删除." # This message will be compared against in Command Not Found Settings page code behind. Take care when changing it. } ================================================ FILE: PowerToys/src/settings-ui/Settings.UI/Assets/Settings/Scripts/EnableModule.ps1 ================================================ [CmdletBinding()] Param( [Parameter(Mandatory=$True,Position=1)] [string]$scriptPath ) Write-Host "Enabling experimental feature: PSFeedbackProvider" Enable-ExperimentalFeature PSFeedbackProvider Write-Host "Enabling experimental feature: PSCommandNotFoundSuggestion" Enable-ExperimentalFeature PSCommandNotFoundSuggestion $wingetModules = Get-Module -ListAvailable -Name Microsoft.WinGet.Client if ($wingetModules) { $moduleUpToDate = $false; foreach ($mod in $wingetModules) { if ($mod.Version -ge "1.8.1133") { $moduleUpToDate = $true; break; } } if ($moduleUpToDate) { Write-Host "已安装 WinGet 客户端模块" } else { Write-Host "WinGet 客户端模块需要更新. 运行 `"Update-Module -Name Microsoft.WinGet.Client`" 来更新 `r`n" } } else { Write-Host "未安装 WinGet 客户端模块. 如需安装请访问 https://www.powershellgallery.com/packages/Microsoft.WinGet.Client `r`n" } $CNFModule = Get-Module -ListAvailable -Name Microsoft.WinGet.CommandNotFound if ($CNFModule) { Write-Host "已安装 Microsoft.WinGet.CommandNotFound 模块" } else { Write-Host "未安装 Microsoft.WinGet.CommandNotFound 模块. 正在安装它...`r`n" Install-Module -Name Microsoft.WinGet.CommandNotFound -Force Write-Host "成功安装 Microsoft.WinGet.CommandNotFound 模块`r`n" } if (!(Test-Path $PROFILE)) { Write-Host "未发现配置文件 $PROFILE". New-Item -Path $PROFILE -ItemType File Write-Host "已创建配置文件 $PROFILE". } $profileContent = Get-Content -Path $PROFILE -Raw if ((-not [string]::IsNullOrEmpty($profileContent)) -and ($profileContent.Contains("34de4b3d-13a8-4540-b76d-b9e8d3851756"))) { if ($profileContent.Contains("Import-Module `"$scriptPath\WinGetCommandNotFound.psd1`"")) { $profileContent = $profileContent.Replace("Import-Module `"$scriptPath\WinGetCommandNotFound.psd1`"", "Import-Module -Name Microsoft.WinGet.CommandNotFound") $profileContent = $profileContent.Replace("34de4b3d-13a8-4540-b76d-b9e8d3851756", "f45873b3-b655-43a6-b217-97c00aa0db58") Set-Content -Path $PROFILE -Value $profileContent Write-Host "成功在配置文件中更新模块." # This message will be compared against in Command Not Found Settings page code behind. Take care when changing it. } } elseif ((-not [string]::IsNullOrEmpty($profileContent)) -and ($profileContent.Contains("f45873b3-b655-43a6-b217-97c00aa0db58"))) { Write-Host "发现模块已经在配置文件中注册过了." # This message will be compared against in Command Not Found Settings page code behind. Take care when changing it. } else { Add-Content -Path $PROFILE -Value "`r`n#f45873b3-b655-43a6-b217-97c00aa0db58 PowerToys CommandNotFound module" Add-Content -Path $PROFILE -Value "`r`nImport-Module -Name Microsoft.WinGet.CommandNotFound" Add-Content -Path $PROFILE -Value "#f45873b3-b655-43a6-b217-97c00aa0db58" Write-Host "成功在配置文件中注册模块." # This message will be compared against in Command Not Found Settings page code behind. Take care when changing it. } ================================================ FILE: PowerToys/src/settings-ui/Settings.UI/Assets/Settings/Scripts/InstallPowerShell7.ps1 ================================================ if ((Get-AppxPackage microsoft.DesktopAppInstaller).Version -ge [System.Version]"1.21") { Write-Host "已安装 winget, 即将开始安装 PowerShell." } else { Write-Host "未安装 WinGet, 即将开始安装它." # To speed up Invoke-WebRequest. Older versions are very slow when printing the progress. $ProgressPreference = 'SilentlyContinue' $cpuArchitecture="x64" $detectedArchitecture="" if ($env:PROCESSOR_ARCHITEW6432 -eq $null) { $detectedArchitecture=$env:PROCESSOR_ARCHITECTURE } else { $detectedArchitecture=$env:PROCESSOR_ARCHITEW6432 } Write-Host "CPU 架构: $detectedArchitecture" if ($detectedArchitecture -ne "AMD64") { Write-Host "不属于 AMD64, 判定为 arm64, 因为这是本软件通常所处的环境." $cpuArchitecture="arm64" } if((Get-AppxPackage Microsoft.VCLibs.140.00).Version -ge [System.Version]"14.0.30704") { Write-Host "已安装 Microsoft.VCLibs.140.00." } else { Write-Host "未安装 Microsoft.VCLibs.140.00, 即将开始安装它." Invoke-WebRequest -Uri https://aka.ms/Microsoft.VCLibs.$cpuArchitecture.14.00.Desktop.appx -OutFile "$Env:TMP\Microsoft.VCLibs.14.00.appx" Add-AppxPackage -Path "$Env:TMP\Microsoft.VCLibs.14.00.appx" Remove-Item -Path "$Env:TMP\Microsoft.VCLibs.14.00.appx" -Force } if((Get-AppxPackage Microsoft.VCLibs.140.00.UWPDesktop).Version -ge [System.Version]"14.0.30704") { Write-Host "已安装 Microsoft.VCLibs.140.00.UWPDesktop" } else { Write-Host "未安装 Microsoft.VCLibs.140.00.UWPDesktop, 即将开始安装它." Invoke-WebRequest -Uri https://aka.ms/Microsoft.VCLibs.$cpuArchitecture.14.00.Desktop.appx -OutFile "$Env:TMP\Microsoft.VCLibs.14.00.Desktop.appx" Add-AppxPackage -Path "$Env:TMP\Microsoft.VCLibs.14.00.Desktop.appx" Remove-Item -Path "$Env:TMP\Microsoft.VCLibs.14.00.Desktop.appx" -Force } if (Get-AppxPackage Microsoft.UI.Xaml.2.7) { Write-Host "已安装 Microsoft.UI.Xaml.2.7" } else { Write-Host "未安装 Microsoft.UI.Xaml.2.7, 即将开始安装它." Write-Host "正在下载到 $Env:TMP\microsoft.ui.xaml.2.7.3.zip" Invoke-WebRequest -Uri https://www.nuget.org/api/v2/package/Microsoft.UI.Xaml/2.7.3 -OutFile "$Env:TMP\microsoft.ui.xaml.2.7.3.zip" Write-Host "正在解压 $Env:TMP\microsoft.ui.xaml.2.7.3.zip" Expand-Archive "$Env:TMP\microsoft.ui.xaml.2.7.3.zip" -DestinationPath "$Env:TMP\microsoft.ui.xaml.2.7.3" Write-Host "正在安装 $Env:TMP\microsoft.ui.xaml.2.7.3\tools\AppX\$cpuArchitecture\Release\Microsoft.UI.Xaml.2.7.appx" Add-AppxPackage "$Env:TMP\microsoft.ui.xaml.2.7.3\tools\AppX\$cpuArchitecture\Release\Microsoft.UI.Xaml.2.7.appx" Remove-Item -Path "$Env:TMP\microsoft.ui.xaml.2.7.3" -Recurse -Force Remove-Item -Path "$Env:TMP\microsoft.ui.xaml.2.7.3.zip" -Force } Write-Host "正在获取最新稳定版的 winget" Invoke-WebRequest -Uri https://aka.ms/getwinget -OutFile "$Env:TMP\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle" Add-AppxPackage -Path "$Env:TMP\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle" Remove-Item -Path "$Env:TMP\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle" -Force #winget is not visible right away, so reload the PATH variable. $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") } winget install Microsoft.PowerShell --source winget if ($LASTEXITCODE -eq 0) { Write-Host "成功安装 Powershell 7." # This message will be compared against in Command Not Found Settings page code behind. Take care when changing it. } else { Write-Host "未能成功安装 Powershell 7." } ================================================ FILE: PowerToys/src/settings-ui/Settings.UI/Assets/Settings/Scripts/InstallWinGetClientModule.ps1 ================================================ $wingetModules = Get-Module -ListAvailable -Name Microsoft.WinGet.Client if ($wingetModules) { $needsUpdate = $true; foreach ($mod in $wingetModules) { if ($mod.Version -ge "1.8.1133") { $needsUpdate = $false; break; } } if ($needsUpdate) { Update-Module -Name Microsoft.WinGet.Client -Force $wingetModules = Get-Module -ListAvailable -Name Microsoft.WinGet.Client $updated = $false; foreach ($mod in $wingetModules) { if ($mod.Version -ge "1.8.1133") { $updated = $true; break; } } if ($updated) { Write-Host "已更新 WinGet 客户端模块." # This message will be compared against in Command Not Found Settings page code behind. Take care when changing it. } else { Write-Host "已安装 WinGet 客户端模块." # This message will be compared against in Command Not Found Settings page code behind. Take care when changing it. } } else { Write-Host "已安装 WinGet 客户端模块." # This message will be compared against in Command Not Found Settings page code behind. Take care when changing it. } } else { Install-Module -Name Microsoft.WinGet.Client if (Get-Module -ListAvailable -Name Microsoft.WinGet.Client) { Write-Host "已安装 WinGet 客户端模块." # This message will be compared against in Command Not Found Settings page code behind. Take care when changing it. } else { Write-Host "未安装 WinGet 客户端模块. 如需安装请访问 https://www.powershellgallery.com/packages/Microsoft.WinGet.Client `r`n" # This message will be compared against in Command Not Found Settings page code behind. Take care when changing it. } } ================================================ FILE: PowerToys/src/settings-ui/Settings.UI/Converters/ZoomItTypeSpeedSliderConverter.cs ================================================ // Copyright (c) Microsoft Corporation // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using Microsoft.PowerToys.Settings.UI.Helpers; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.UI.Xaml.Data; namespace Microsoft.PowerToys.Settings.UI.Converters { public sealed partial class ZoomItTypeSpeedSliderConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { string targetValue = string.Empty; int zoomLevel = System.Convert.ToInt32((double)value); string explanation = ResourceLoaderInstance.ResourceLoader.GetString("ZoomIt_DemoType_SpeedSlider_Thumbnail_Explanation"); targetValue = $"{zoomLevel}({explanation})"; return targetValue; } public object ConvertBack(object value, Type targetType, object parameter, string language) => throw new NotImplementedException(); } } ================================================ FILE: PowerToys/src/settings-ui/Settings.UI/SettingsXAML/App.xaml.cs ================================================ // 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.IO; using System.Linq; using System.Text.Json; using System.Threading.Tasks; using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Helpers; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Telemetry.Events; using Microsoft.PowerToys.Settings.UI.Services; using Microsoft.PowerToys.Settings.UI.Views; using Microsoft.PowerToys.Telemetry; using Microsoft.UI.Xaml; using PowerToys.Interop; using Windows.UI.Popups; using WinRT.Interop; using WinUIEx; namespace Microsoft.PowerToys.Settings.UI { /// /// Provides application-specific behavior to supplement the default Application class. /// public partial class App : Application { private enum Arguments { PTPipeName = 1, SettingsPipeName, PTPid, Theme, // used in the old settings ElevatedStatus, IsUserAdmin, ShowOobeWindow, ShowScoobeWindow, ShowFlyout, ContainsSettingsWindow, ContainsFlyoutPosition, } private const int RequiredArgumentsSetSettingQty = 4; private const int RequiredArgumentsSetAdditionalSettingsQty = 4; private const int RequiredArgumentsGetSettingQty = 3; private const int RequiredArgumentsLaunchedFromRunnerQty = 12; // Create an instance of the IPC wrapper. private static TwoWayPipeMessageIPCManaged ipcmanager; public static bool IsElevated { get; set; } public static bool IsUserAnAdmin { get; set; } public static int PowerToysPID { get; set; } public bool ShowOobe { get; set; } public bool ShowFlyout { get; set; } public bool ShowScoobe { get; set; } public Type StartupPage { get; set; } = typeof(Views.DashboardPage); public static Action IPCMessageReceivedCallback { get; set; } public ETWTrace EtwTrace { get; private set; } = new ETWTrace(); /// /// Initializes a new instance of the class. /// Initializes the singleton application object. This is the first line of authored code /// executed, and as such is the logical equivalent of main() or WinMain(). /// public App() { Logger.InitializeLogger(@"\Settings\Logs"); string appLanguage = LanguageHelper.LoadLanguage(); if (!string.IsNullOrEmpty(appLanguage)) { Microsoft.Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = appLanguage; } InitializeComponent(); UnhandledException += App_UnhandledException; NativeEventWaiter.WaitForEventLoop( Constants.PowerToysRunnerTerminateSettingsEvent(), () => { EtwTrace?.Dispose(); Environment.Exit(0); }); } private void App_UnhandledException(object sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e) { Logger.LogError("Unhandled exception", e.Exception); } public static void OpenSettingsWindow(Type type = null, bool ensurePageIsSelected = false) { if (settingsWindow == null) { settingsWindow = new MainWindow(); } settingsWindow.Activate(); if (type != null) { settingsWindow.NavigateToSection(type); WindowHelpers.BringToForeground(settingsWindow.GetWindowHandle()); } if (ensurePageIsSelected) { settingsWindow.EnsurePageIsSelected(); } } private void OnLaunchedToSetSetting(string[] cmdArgs) { var settingName = cmdArgs[2]; var settingValue = cmdArgs[3]; try { SetSettingCommandLineCommand.Execute(settingName, settingValue, new SettingsUtils()); } catch (Exception ex) { Logger.LogError($"SetSettingCommandLineCommand exception: '{settingName}' setting couldn't be set to {settingValue}", ex); } Exit(); } private void OnLaunchedToSetAdditionalSetting(string[] cmdArgs) { var moduleName = cmdArgs[2]; var ipcFileName = cmdArgs[3]; try { using (var settings = JsonDocument.Parse(File.ReadAllText(ipcFileName))) { SetAdditionalSettingsCommandLineCommand.Execute(moduleName, settings, new SettingsUtils()); } } catch (Exception ex) { Logger.LogError($"SetAdditionalSettingsCommandLineCommand exception: couldn't set additional settings for '{moduleName}'", ex); } Exit(); } private void OnLaunchedToGetSetting(string[] cmdArgs) { var ipcFileName = cmdArgs[2]; try { var requestedSettings = JsonSerializer.Deserialize>>(File.ReadAllText(ipcFileName)); File.WriteAllText(ipcFileName, GetSettingCommandLineCommand.Execute(requestedSettings)); } catch (Exception ex) { Logger.LogError($"GetSettingCommandLineCommand exception", ex); } Exit(); } private void OnLaunchedFromRunner(string[] cmdArgs) { // Skip the first argument which is prepended when launched by explorer if (cmdArgs[0].EndsWith(".dll", StringComparison.InvariantCultureIgnoreCase) && cmdArgs[1].EndsWith(".exe", StringComparison.InvariantCultureIgnoreCase) && (cmdArgs.Length >= RequiredArgumentsLaunchedFromRunnerQty + 1)) { cmdArgs = cmdArgs.Skip(1).ToArray(); } _ = int.TryParse(cmdArgs[(int)Arguments.PTPid], out int powerToysPID); PowerToysPID = powerToysPID; IsElevated = cmdArgs[(int)Arguments.ElevatedStatus] == "true"; IsUserAnAdmin = cmdArgs[(int)Arguments.IsUserAdmin] == "true"; ShowOobe = cmdArgs[(int)Arguments.ShowOobeWindow] == "true"; ShowScoobe = cmdArgs[(int)Arguments.ShowScoobeWindow] == "true"; ShowFlyout = cmdArgs[(int)Arguments.ShowFlyout] == "true"; bool containsSettingsWindow = cmdArgs[(int)Arguments.ContainsSettingsWindow] == "true"; bool containsFlyoutPosition = cmdArgs[(int)Arguments.ContainsFlyoutPosition] == "true"; // To keep track of variable arguments int currentArgumentIndex = RequiredArgumentsLaunchedFromRunnerQty; if (containsSettingsWindow) { // Open specific window StartupPage = GetPage(cmdArgs[currentArgumentIndex]); currentArgumentIndex++; } int flyout_x = 0; int flyout_y = 0; if (containsFlyoutPosition) { // get the flyout position arguments _ = int.TryParse(cmdArgs[currentArgumentIndex++], out flyout_x); _ = int.TryParse(cmdArgs[currentArgumentIndex++], out flyout_y); } RunnerHelper.WaitForPowerToysRunner(PowerToysPID, () => { Environment.Exit(0); }); ipcmanager = new TwoWayPipeMessageIPCManaged(cmdArgs[(int)Arguments.SettingsPipeName], cmdArgs[(int)Arguments.PTPipeName], (string message) => { if (IPCMessageReceivedCallback != null && message.Length > 0) { IPCMessageReceivedCallback(message); } }); ipcmanager.Start(); if (!ShowOobe && !ShowScoobe && !ShowFlyout) { settingsWindow = new MainWindow(); settingsWindow.Activate(); settingsWindow.ExtendsContentIntoTitleBar = true; settingsWindow.NavigateToSection(StartupPage); // https://github.com/microsoft/microsoft-ui-xaml/issues/7595 - Activate doesn't bring window to the foreground // Need to call SetForegroundWindow to actually gain focus. WindowHelpers.BringToForeground(settingsWindow.GetWindowHandle()); // https://github.com/microsoft/microsoft-ui-xaml/issues/8948 - A window's top border incorrectly // renders as black on Windows 10. WindowHelpers.ForceTopBorder1PixelInsetOnWindows10(WindowNative.GetWindowHandle(settingsWindow)); } else { // Create the Settings window hidden so that it's fully initialized and // it will be ready to receive the notification if the user opens // the Settings from the tray icon. settingsWindow = new MainWindow(true); if (ShowOobe) { PowerToysTelemetry.Log.WriteEvent(new OobeStartedEvent()); OobeWindow oobeWindow = new OobeWindow(OOBE.Enums.PowerToysModules.Overview); oobeWindow.Activate(); oobeWindow.ExtendsContentIntoTitleBar = true; WindowHelpers.ForceTopBorder1PixelInsetOnWindows10(WindowNative.GetWindowHandle(settingsWindow)); SetOobeWindow(oobeWindow); } else if (ShowScoobe) { PowerToysTelemetry.Log.WriteEvent(new ScoobeStartedEvent()); OobeWindow scoobeWindow = new OobeWindow(OOBE.Enums.PowerToysModules.WhatsNew); scoobeWindow.Activate(); scoobeWindow.ExtendsContentIntoTitleBar = true; WindowHelpers.ForceTopBorder1PixelInsetOnWindows10(WindowNative.GetWindowHandle(settingsWindow)); SetOobeWindow(scoobeWindow); } else if (ShowFlyout) { POINT? p = null; if (containsFlyoutPosition) { p = new POINT(flyout_x, flyout_y); } ShellPage.OpenFlyoutCallback(p); } } } /// /// Invoked when the application is launched normally by the end user. Other entry points /// will be used such as when the application is launched to open a specific file. /// /// Details about the launch request and process. protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args) { var cmdArgs = Environment.GetCommandLineArgs(); if (cmdArgs?.Length >= RequiredArgumentsLaunchedFromRunnerQty) { OnLaunchedFromRunner(cmdArgs); } else if (cmdArgs?.Length == RequiredArgumentsSetSettingQty && cmdArgs[1] == "set") { OnLaunchedToSetSetting(cmdArgs); } else if (cmdArgs?.Length == RequiredArgumentsSetAdditionalSettingsQty && cmdArgs[1] == "setAdditional") { OnLaunchedToSetAdditionalSetting(cmdArgs); } else if (cmdArgs?.Length == RequiredArgumentsGetSettingQty && cmdArgs[1] == "get") { OnLaunchedToGetSetting(cmdArgs); } else { #if DEBUG // For debugging purposes // Window is also needed to show MessageDialog settingsWindow = new MainWindow(); settingsWindow.ExtendsContentIntoTitleBar = true; WindowHelpers.ForceTopBorder1PixelInsetOnWindows10(WindowNative.GetWindowHandle(settingsWindow)); settingsWindow.Activate(); settingsWindow.NavigateToSection(StartupPage); ShowMessageDialog("软件正以调试模式运行.", "DEBUG"); #else /* If we try to run Settings as a standalone app, it will start PowerToys.exe if not running and open Settings again through it in the Dashboard page. */ Common.UI.SettingsDeepLink.OpenSettings(Common.UI.SettingsDeepLink.SettingsWindow.Dashboard, true); Exit(); #endif } } #if !DEBUG private async void ShowMessageDialogAndExit(string content, string title = null) #else private async void ShowMessageDialog(string content, string title = null) #endif { await ShowDialogAsync(content, title); #if !DEBUG this.Exit(); #endif } public static Task ShowDialogAsync(string content, string title = null) { var dialog = new MessageDialog(content, title ?? string.Empty); var handle = NativeMethods.GetActiveWindow(); if (handle == IntPtr.Zero) { throw new InvalidOperationException(); } InitializeWithWindow.Initialize(dialog, handle); return dialog.ShowAsync().AsTask(); } public static TwoWayPipeMessageIPCManaged GetTwoWayIPCManager() { return ipcmanager; } public static bool IsDarkTheme() { return ThemeService.Theme == ElementTheme.Dark || (ThemeService.Theme == ElementTheme.Default && ThemeHelpers.GetAppTheme() == AppTheme.Dark); } public static int UpdateUIThemeMethod(string themeName) { return 0; } private static ISettingsUtils settingsUtils = new SettingsUtils(); private static ThemeService themeService = new ThemeService(SettingsRepository.GetInstance(settingsUtils)); public static ThemeService ThemeService => themeService; private static MainWindow settingsWindow; private static OobeWindow oobeWindow; private static FlyoutWindow flyoutWindow; public static void ClearSettingsWindow() { settingsWindow = null; } public static MainWindow GetSettingsWindow() { return settingsWindow; } public static OobeWindow GetOobeWindow() { return oobeWindow; } public static FlyoutWindow GetFlyoutWindow() { return flyoutWindow; } public static void SetOobeWindow(OobeWindow window) { oobeWindow = window; } public static void SetFlyoutWindow(FlyoutWindow window) { flyoutWindow = window; } public static void ClearOobeWindow() { oobeWindow = null; } public static void ClearFlyoutWindow() { flyoutWindow = null; } public static Type GetPage(string settingWindow) { switch (settingWindow) { case "Dashboard": return typeof(DashboardPage); case "Overview": return typeof(GeneralPage); case "AdvancedPaste": return typeof(AdvancedPastePage); case "AlwaysOnTop": return typeof(AlwaysOnTopPage); case "Awake": return typeof(AwakePage); case "CmdNotFound": return typeof(CmdNotFoundPage); case "ColorPicker": return typeof(ColorPickerPage); case "FancyZones": return typeof(FancyZonesPage); case "FileLocksmith": return typeof(FileLocksmithPage); case "Run": return typeof(PowerLauncherPage); case "ImageResizer": return typeof(ImageResizerPage); case "KBM": return typeof(KeyboardManagerPage); case "MouseUtils": return typeof(MouseUtilsPage); case "MouseWithoutBorders": return typeof(MouseWithoutBordersPage); case "PowerRename": return typeof(PowerRenamePage); case "QuickAccent": return typeof(PowerAccentPage); case "FileExplorer": return typeof(PowerPreviewPage); case "ShortcutGuide": return typeof(ShortcutGuidePage); case "PowerOcr": return typeof(PowerOcrPage); case "MeasureTool": return typeof(MeasureToolPage); case "Hosts": return typeof(HostsPage); case "RegistryPreview": return typeof(RegistryPreviewPage); case "Peek": return typeof(PeekPage); case "CropAndLock": return typeof(CropAndLockPage); case "EnvironmentVariables": return typeof(EnvironmentVariablesPage); case "NewPlus": return typeof(NewPlusPage); case "Workspaces": return typeof(WorkspacesPage); case "ZoomIt": return typeof(ZoomItPage); default: // Fallback to Dashboard Debug.Assert(false, "Unexpected SettingsWindow argument value"); return typeof(DashboardPage); } } } } ================================================ FILE: PowerToys/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeShellPage.xaml ================================================  ================================================ FILE: PowerToys/src/settings-ui/Settings.UI/SettingsXAML/Views/ImageResizerPage.xaml ================================================  ================================================ FILE: PowerToys/src/settings-ui/Settings.UI/SettingsXAML/Views/MouseUtilsPage.xaml ================================================  ================================================ FILE: PowerToys/src/settings-ui/Settings.UI/SettingsXAML/Views/MouseWithoutBordersPage.xaml ================================================  ================================================ FILE: PowerToys/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerLauncherPage.xaml ================================================  0 0,0,0,0 ================================================ FILE: PowerToys/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerOcrPage.xaml ================================================  ================================================ FILE: PowerToys/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerPreviewPage.xaml ================================================  ================================================ FILE: PowerToys/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerRenamePage.xaml ================================================  ================================================ FILE: PowerToys/src/settings-ui/Settings.UI/SettingsXAML/Views/ZoomItPage.xaml ================================================