mirror of
https://github.com/microsoft/PowerToys
synced 2025-09-03 16:05:12 +00:00
Hosts file editor (#20462)
This commit is contained in:
committed by
GitHub
parent
ab41b61e84
commit
b2e1337d4e
@@ -65,6 +65,10 @@
|
|||||||
"modules\\FileExplorerPreview\\PowerToys.SvgThumbnailProvider.dll",
|
"modules\\FileExplorerPreview\\PowerToys.SvgThumbnailProvider.dll",
|
||||||
"modules\\FileExplorerPreview\\PowerToys.SvgThumbnailProvider.comhost.dll",
|
"modules\\FileExplorerPreview\\PowerToys.SvgThumbnailProvider.comhost.dll",
|
||||||
|
|
||||||
|
"modules\\Hosts\\PowerToys.HostsModuleInterface.dll",
|
||||||
|
"modules\\Hosts\\PowerToys.Hosts.dll",
|
||||||
|
"modules\\Hosts\\PowerToys.Hosts.exe",
|
||||||
|
|
||||||
"modules\\ImageResizer\\PowerToys.ImageResizer.exe",
|
"modules\\ImageResizer\\PowerToys.ImageResizer.exe",
|
||||||
"modules\\ImageResizer\\PowerToys.ImageResizer.dll",
|
"modules\\ImageResizer\\PowerToys.ImageResizer.dll",
|
||||||
"modules\\ImageResizer\\PowerToys.ImageResizerExt.dll",
|
"modules\\ImageResizer\\PowerToys.ImageResizerExt.dll",
|
||||||
@@ -217,6 +221,7 @@
|
|||||||
"modules\\FileExplorerPreview\\Microsoft.Web.WebView2.WinForms.dll",
|
"modules\\FileExplorerPreview\\Microsoft.Web.WebView2.WinForms.dll",
|
||||||
"modules\\FileExplorerPreview\\Microsoft.Web.WebView2.Wpf.dll",
|
"modules\\FileExplorerPreview\\Microsoft.Web.WebView2.Wpf.dll",
|
||||||
"modules\\FileExplorerPreview\\WebView2Loader.dll",
|
"modules\\FileExplorerPreview\\WebView2Loader.dll",
|
||||||
|
"modules\\Hosts\\Microsoft.Graphics.Canvas.Interop.dll",
|
||||||
"modules\\launcher\\e_sqlite3.dll",
|
"modules\\launcher\\e_sqlite3.dll",
|
||||||
"modules\\launcher\\LazyCache.dll",
|
"modules\\launcher\\LazyCache.dll",
|
||||||
"modules\\launcher\\SQLitePCLRaw.batteries_v2.dll",
|
"modules\\launcher\\SQLitePCLRaw.batteries_v2.dll",
|
||||||
|
@@ -200,6 +200,7 @@ steps:
|
|||||||
**\PreviewPaneUnitTests.dll
|
**\PreviewPaneUnitTests.dll
|
||||||
**\UnitTests-SvgThumbnailProvider.dll
|
**\UnitTests-SvgThumbnailProvider.dll
|
||||||
**\UnitTests-SvgPreviewHandler.dll
|
**\UnitTests-SvgPreviewHandler.dll
|
||||||
|
**\Hosts.Tests.dll
|
||||||
!**\obj\**
|
!**\obj\**
|
||||||
!**\ref\**
|
!**\ref\**
|
||||||
|
|
||||||
|
@@ -449,6 +449,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MeasureToolUI", "src\module
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerAccentKeyboardService", "src\modules\poweraccent\PowerAccentKeyboardService\PowerAccentKeyboardService.vcxproj", "{C97D9A5D-206C-454E-997E-009E227D7F02}"
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerAccentKeyboardService", "src\modules\poweraccent\PowerAccentKeyboardService\PowerAccentKeyboardService.vcxproj", "{C97D9A5D-206C-454E-997E-009E227D7F02}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hosts", "src\modules\Hosts\Hosts\Hosts.csproj", "{31D1C81D-765F-4446-AA62-E743F6325049}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Hosts", "Hosts", "{F05E590D-AD46-42BE-9C25-6A63ADD2E3EA}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hosts.Tests", "src\modules\Hosts\Hosts.Tests\Hosts.Tests.csproj", "{E2D03E0F-7A75-4813-9F4B-D8763D43FD3A}"
|
||||||
|
EndProject
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HostsModuleInterface", "src\modules\Hosts\HostsModuleInterface\HostsModuleInterface.vcxproj", "{B41B888C-7DB8-4747-B262-4062E05A230D}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|ARM64 = Debug|ARM64
|
Debug|ARM64 = Debug|ARM64
|
||||||
@@ -1804,6 +1812,42 @@ Global
|
|||||||
{C97D9A5D-206C-454E-997E-009E227D7F02}.Release|x64.Build.0 = Release|x64
|
{C97D9A5D-206C-454E-997E-009E227D7F02}.Release|x64.Build.0 = Release|x64
|
||||||
{C97D9A5D-206C-454E-997E-009E227D7F02}.Release|x86.ActiveCfg = Release|x64
|
{C97D9A5D-206C-454E-997E-009E227D7F02}.Release|x86.ActiveCfg = Release|x64
|
||||||
{C97D9A5D-206C-454E-997E-009E227D7F02}.Release|x86.Build.0 = Release|x64
|
{C97D9A5D-206C-454E-997E-009E227D7F02}.Release|x86.Build.0 = Release|x64
|
||||||
|
{31D1C81D-765F-4446-AA62-E743F6325049}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||||
|
{31D1C81D-765F-4446-AA62-E743F6325049}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||||
|
{31D1C81D-765F-4446-AA62-E743F6325049}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{31D1C81D-765F-4446-AA62-E743F6325049}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{31D1C81D-765F-4446-AA62-E743F6325049}.Debug|x86.ActiveCfg = Debug|x64
|
||||||
|
{31D1C81D-765F-4446-AA62-E743F6325049}.Debug|x86.Build.0 = Debug|x64
|
||||||
|
{31D1C81D-765F-4446-AA62-E743F6325049}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||||
|
{31D1C81D-765F-4446-AA62-E743F6325049}.Release|ARM64.Build.0 = Release|ARM64
|
||||||
|
{31D1C81D-765F-4446-AA62-E743F6325049}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{31D1C81D-765F-4446-AA62-E743F6325049}.Release|x64.Build.0 = Release|x64
|
||||||
|
{31D1C81D-765F-4446-AA62-E743F6325049}.Release|x86.ActiveCfg = Release|x64
|
||||||
|
{31D1C81D-765F-4446-AA62-E743F6325049}.Release|x86.Build.0 = Release|x64
|
||||||
|
{E2D03E0F-7A75-4813-9F4B-D8763D43FD3A}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||||
|
{E2D03E0F-7A75-4813-9F4B-D8763D43FD3A}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||||
|
{E2D03E0F-7A75-4813-9F4B-D8763D43FD3A}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{E2D03E0F-7A75-4813-9F4B-D8763D43FD3A}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{E2D03E0F-7A75-4813-9F4B-D8763D43FD3A}.Debug|x86.ActiveCfg = Debug|x64
|
||||||
|
{E2D03E0F-7A75-4813-9F4B-D8763D43FD3A}.Debug|x86.Build.0 = Debug|x64
|
||||||
|
{E2D03E0F-7A75-4813-9F4B-D8763D43FD3A}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||||
|
{E2D03E0F-7A75-4813-9F4B-D8763D43FD3A}.Release|ARM64.Build.0 = Release|ARM64
|
||||||
|
{E2D03E0F-7A75-4813-9F4B-D8763D43FD3A}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{E2D03E0F-7A75-4813-9F4B-D8763D43FD3A}.Release|x64.Build.0 = Release|x64
|
||||||
|
{E2D03E0F-7A75-4813-9F4B-D8763D43FD3A}.Release|x86.ActiveCfg = Release|x64
|
||||||
|
{E2D03E0F-7A75-4813-9F4B-D8763D43FD3A}.Release|x86.Build.0 = Release|x64
|
||||||
|
{B41B888C-7DB8-4747-B262-4062E05A230D}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||||
|
{B41B888C-7DB8-4747-B262-4062E05A230D}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||||
|
{B41B888C-7DB8-4747-B262-4062E05A230D}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{B41B888C-7DB8-4747-B262-4062E05A230D}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{B41B888C-7DB8-4747-B262-4062E05A230D}.Debug|x86.ActiveCfg = Debug|x64
|
||||||
|
{B41B888C-7DB8-4747-B262-4062E05A230D}.Debug|x86.Build.0 = Debug|x64
|
||||||
|
{B41B888C-7DB8-4747-B262-4062E05A230D}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||||
|
{B41B888C-7DB8-4747-B262-4062E05A230D}.Release|ARM64.Build.0 = Release|ARM64
|
||||||
|
{B41B888C-7DB8-4747-B262-4062E05A230D}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{B41B888C-7DB8-4747-B262-4062E05A230D}.Release|x64.Build.0 = Release|x64
|
||||||
|
{B41B888C-7DB8-4747-B262-4062E05A230D}.Release|x86.ActiveCfg = Release|x64
|
||||||
|
{B41B888C-7DB8-4747-B262-4062E05A230D}.Release|x86.Build.0 = Release|x64
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@@ -1956,6 +2000,10 @@ Global
|
|||||||
{92C39820-9F84-4529-BC7D-22AAE514D63B} = {7AC943C9-52E8-44CF-9083-744D8049667B}
|
{92C39820-9F84-4529-BC7D-22AAE514D63B} = {7AC943C9-52E8-44CF-9083-744D8049667B}
|
||||||
{515554D1-D004-4F7F-A107-2211FC0F6B2C} = {7AC943C9-52E8-44CF-9083-744D8049667B}
|
{515554D1-D004-4F7F-A107-2211FC0F6B2C} = {7AC943C9-52E8-44CF-9083-744D8049667B}
|
||||||
{C97D9A5D-206C-454E-997E-009E227D7F02} = {0F14491C-6369-4C45-AAA8-135814E66E6B}
|
{C97D9A5D-206C-454E-997E-009E227D7F02} = {0F14491C-6369-4C45-AAA8-135814E66E6B}
|
||||||
|
{31D1C81D-765F-4446-AA62-E743F6325049} = {F05E590D-AD46-42BE-9C25-6A63ADD2E3EA}
|
||||||
|
{F05E590D-AD46-42BE-9C25-6A63ADD2E3EA} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
|
||||||
|
{E2D03E0F-7A75-4813-9F4B-D8763D43FD3A} = {F05E590D-AD46-42BE-9C25-6A63ADD2E3EA}
|
||||||
|
{B41B888C-7DB8-4747-B262-4062E05A230D} = {F05E590D-AD46-42BE-9C25-6A63ADD2E3EA}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}
|
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}
|
||||||
|
@@ -15,6 +15,7 @@
|
|||||||
<?define MouseUtilsProjectName="MouseUtils"?>
|
<?define MouseUtilsProjectName="MouseUtils"?>
|
||||||
<?define AlwaysOnTopProjectName="AlwaysOnTop"?>
|
<?define AlwaysOnTopProjectName="AlwaysOnTop"?>
|
||||||
<?define MeasureToolProjectName="MeasureTool"?>
|
<?define MeasureToolProjectName="MeasureTool"?>
|
||||||
|
<?define HostsProjectName="Hosts"?>
|
||||||
|
|
||||||
<?define RepoDir="$(var.ProjectDir)..\..\" ?>
|
<?define RepoDir="$(var.ProjectDir)..\..\" ?>
|
||||||
<?if $(var.Platform) = x64?>
|
<?if $(var.Platform) = x64?>
|
||||||
@@ -121,13 +122,17 @@
|
|||||||
|
|
||||||
<?define MeasureToolFiles=Ijwhost.dll;Microsoft.InteractiveExperiences.Projection.dll;Microsoft.Windows.ApplicationModel.DynamicDependency.Projection.dll;Microsoft.Windows.ApplicationModel.Resources.Projection.dll;Microsoft.Windows.ApplicationModel.WindowsAppRuntime.Projection.dll;Microsoft.Windows.AppLifecycle.Projection.dll;Microsoft.Windows.SDK.NET.dll;Microsoft.Windows.System.Power.Projection.dll;Microsoft.WindowsAppRuntime.Bootstrap.Net.dll;Microsoft.WinUI.dll;PowerToys.ManagedCommon.dll;PowerToys.ManagedTelemetry.dll;PowerToys.MeasureToolCore.dll;PowerToys.MeasureToolUI.deps.json;PowerToys.MeasureToolUI.dll;PowerToys.MeasureToolUI.exe;PowerToys.MeasureToolUI.runtimeconfig.json;resources.pri;System.CodeDom.dll;System.Management.dll;WinRT.Runtime.dll;WinUIEx.dll?>
|
<?define MeasureToolFiles=Ijwhost.dll;Microsoft.InteractiveExperiences.Projection.dll;Microsoft.Windows.ApplicationModel.DynamicDependency.Projection.dll;Microsoft.Windows.ApplicationModel.Resources.Projection.dll;Microsoft.Windows.ApplicationModel.WindowsAppRuntime.Projection.dll;Microsoft.Windows.AppLifecycle.Projection.dll;Microsoft.Windows.SDK.NET.dll;Microsoft.Windows.System.Power.Projection.dll;Microsoft.WindowsAppRuntime.Bootstrap.Net.dll;Microsoft.WinUI.dll;PowerToys.ManagedCommon.dll;PowerToys.ManagedTelemetry.dll;PowerToys.MeasureToolCore.dll;PowerToys.MeasureToolUI.deps.json;PowerToys.MeasureToolUI.dll;PowerToys.MeasureToolUI.exe;PowerToys.MeasureToolUI.runtimeconfig.json;resources.pri;System.CodeDom.dll;System.Management.dll;WinRT.Runtime.dll;WinUIEx.dll?>
|
||||||
|
|
||||||
<?define PowerRenameMicrosoftUIXamlAssetsInstallFiles=NoiseAsset_256x256_PNG.png?>
|
<?define HostsFiles=ColorCode.Core.dll;ColorCode.WinUI.dll;CommunityToolkit.Common.dll;CommunityToolkit.Mvvm.dll;CommunityToolkit.WinUI.dll;CommunityToolkit.WinUI.UI.Controls.Core.dll;CommunityToolkit.WinUI.UI.Controls.DataGrid.dll;CommunityToolkit.WinUI.UI.Controls.Input.dll;CommunityToolkit.WinUI.UI.Controls.Layout.dll;CommunityToolkit.WinUI.UI.Controls.Markdown.dll;CommunityToolkit.WinUI.UI.Controls.Media.dll;CommunityToolkit.WinUI.UI.Controls.Primitives.dll;CommunityToolkit.WinUI.UI.dll;ControlzEx.dll;Ijwhost.dll;Microsoft.Extensions.Configuration.Abstractions.dll;Microsoft.Extensions.Configuration.Binder.dll;Microsoft.Extensions.Configuration.CommandLine.dll;Microsoft.Extensions.Configuration.dll;Microsoft.Extensions.Configuration.EnvironmentVariables.dll;Microsoft.Extensions.Configuration.FileExtensions.dll;Microsoft.Extensions.Configuration.Json.dll;Microsoft.Extensions.Configuration.UserSecrets.dll;Microsoft.Extensions.DependencyInjection.Abstractions.dll;Microsoft.Extensions.DependencyInjection.dll;Microsoft.Extensions.FileProviders.Abstractions.dll;Microsoft.Extensions.FileProviders.Physical.dll;Microsoft.Extensions.FileSystemGlobbing.dll;Microsoft.Extensions.Hosting.Abstractions.dll;Microsoft.Extensions.Hosting.dll;Microsoft.Extensions.Logging.Abstractions.dll;Microsoft.Extensions.Logging.Configuration.dll;Microsoft.Extensions.Logging.Console.dll;Microsoft.Extensions.Logging.Debug.dll;Microsoft.Extensions.Logging.dll;Microsoft.Extensions.Logging.EventLog.dll;Microsoft.Extensions.Logging.EventSource.dll;Microsoft.Extensions.Options.ConfigurationExtensions.dll;Microsoft.Extensions.Options.dll;Microsoft.Extensions.Primitives.dll;Microsoft.Graphics.Canvas.Interop.dll;Microsoft.InteractiveExperiences.Projection.dll;Microsoft.Windows.ApplicationModel.DynamicDependency.Projection.dll;Microsoft.Windows.ApplicationModel.Resources.Projection.dll;Microsoft.Windows.ApplicationModel.WindowsAppRuntime.Projection.dll;Microsoft.Windows.AppLifecycle.Projection.dll;Microsoft.Windows.SDK.NET.dll;Microsoft.Windows.System.Power.Projection.dll;Microsoft.WindowsAppRuntime.Bootstrap.Net.dll;Microsoft.WinUI.dll;Microsoft.Xaml.Behaviors.dll;Microsoft.Xaml.Interactions.dll;Microsoft.Xaml.Interactivity.dll;PowerToys.Common.UI.dll;PowerToys.Hosts.deps.json;PowerToys.Hosts.dll;PowerToys.Hosts.exe;PowerToys.Hosts.runtimeconfig.json;PowerToys.ManagedCommon.dll;PowerToys.ManagedTelemetry.dll;PowerToys.Settings.UI.Lib.dll;resources.pri;System.IO.Abstractions.dll;System.Management.dll;System.Text.Json.dll;WinRT.Runtime.dll;WinUIEx.dll;icon.ico?>
|
||||||
|
|
||||||
|
<?define PowerRenameMicrosoftUIXamlAssetsInstallFiles=NoiseAsset_256x256_PNG.png?>
|
||||||
|
|
||||||
<?define WinAppSDKFiles=CoreMessagingXP.dll;DWriteCore.dll;DwmSceneI.dll;MRM.dll;Microsoft.DirectManipulation.dll;Microsoft.InputStateManager.dll;Microsoft.Internal.FrameworkUdk.dll;Microsoft.UI.Composition.OSSupport.dll;Microsoft.UI.Input.dll;Microsoft.UI.Windowing.Core.dll;Microsoft.UI.Xaml.Controls.dll;Microsoft.UI.Xaml.Controls.pri;Microsoft.UI.Xaml.Internal.dll;Microsoft.UI.Xaml.Phone.dll;Microsoft.Web.WebView2.Core.dll;Microsoft.Windows.AppNotifications.Projection.dll;Microsoft.Windows.ApplicationModel.Resources.dll;Microsoft.WindowsAppRuntime.Bootstrap.dll;Microsoft.Windows.PushNotifications.Projection.dll;Microsoft.Windows.System.Projection.dll;Microsoft.WindowsAppRuntime.Insights.Resource.dll;Microsoft.WindowsAppRuntime.Release.Net.dll;Microsoft.WindowsAppRuntime.dll;Microsoft.ui.xaml.dll;Microsoft.ui.xaml.resources.19h1.dll;Microsoft.ui.xaml.resources.common.dll;PushNotificationsLongRunningTask.ProxyStub.dll;WinUIEdit.dll;WindowsAppRuntime.png;WindowsAppSdk.AppxDeploymentExtensions.Desktop.dll;dcompi.dll;dwmcorei.dll;marshal.dll;wuceffectsi.dll?>
|
<?define WinAppSDKFiles=CoreMessagingXP.dll;DWriteCore.dll;DwmSceneI.dll;MRM.dll;Microsoft.DirectManipulation.dll;Microsoft.InputStateManager.dll;Microsoft.Internal.FrameworkUdk.dll;Microsoft.UI.Composition.OSSupport.dll;Microsoft.UI.Input.dll;Microsoft.UI.Windowing.Core.dll;Microsoft.UI.Xaml.Controls.dll;Microsoft.UI.Xaml.Controls.pri;Microsoft.UI.Xaml.Internal.dll;Microsoft.UI.Xaml.Phone.dll;Microsoft.Web.WebView2.Core.dll;Microsoft.Windows.AppNotifications.Projection.dll;Microsoft.Windows.ApplicationModel.Resources.dll;Microsoft.WindowsAppRuntime.Bootstrap.dll;Microsoft.Windows.PushNotifications.Projection.dll;Microsoft.Windows.System.Projection.dll;Microsoft.WindowsAppRuntime.Insights.Resource.dll;Microsoft.WindowsAppRuntime.Release.Net.dll;Microsoft.WindowsAppRuntime.dll;Microsoft.ui.xaml.dll;Microsoft.ui.xaml.resources.19h1.dll;Microsoft.ui.xaml.resources.common.dll;PushNotificationsLongRunningTask.ProxyStub.dll;WinUIEdit.dll;WindowsAppRuntime.png;WindowsAppSdk.AppxDeploymentExtensions.Desktop.dll;dcompi.dll;dwmcorei.dll;marshal.dll;wuceffectsi.dll?>
|
||||||
|
|
||||||
<?define PowerToysInteropFiles=concrt140.dll;msvcp140.dll;msvcp140_1.dll;msvcp140_2.dll;msvcp140_atomic_wait.dll;msvcp140_codecvt_ids.dll;PowerToys.Interop.dll;vcamp140.dll;vccorlib140.dll;vcomp140.dll;vcruntime140.dll;vcruntime140_1.dll?>
|
<?define PowerToysInteropFiles=concrt140.dll;msvcp140.dll;msvcp140_1.dll;msvcp140_2.dll;msvcp140_atomic_wait.dll;msvcp140_codecvt_ids.dll;PowerToys.Interop.dll;vcamp140.dll;vccorlib140.dll;vcomp140.dll;vcruntime140.dll;vcruntime140_1.dll?>
|
||||||
|
|
||||||
<?define MeasureToolMicrosoftUIXamlAssetsInstallFiles=NoiseAsset_256x256_PNG.png?>
|
<?define MeasureToolMicrosoftUIXamlAssetsInstallFiles=NoiseAsset_256x256_PNG.png?>
|
||||||
|
|
||||||
|
<?define HostsMicrosoftUIXamlAssetsInstallFiles=NoiseAsset_256x256_PNG.png?>
|
||||||
|
|
||||||
<?define PowerAccentFiles=ControlzEx.dll;GongSolutions.WPF.DragDrop.dll;Ijwhost.dll;MahApps.Metro.dll;Microsoft.Xaml.Behaviors.dll;PowerAccent.Core.dll;PowerAccent.deps.json;PowerAccent.dll;PowerAccent.exe;PowerAccent.runtimeconfig.json;PowerToys.PowerAccentModuleInterface.dll;PowerToys.ManagedCommon.dll;PowerToys.ManagedTelemetry.dll;PowerToys.PowerAccent.deps.json;PowerToys.PowerAccent.dll;PowerToys.PowerAccent.exe;PowerToys.PowerAccent.runtimeconfig.json;PowerToys.Settings.UI.Lib.dll;System.IO.Abstractions.dll;System.Management.dll;System.Text.Json.dll;Vanara.Core.dll;Vanara.PInvoke.ComCtl32.dll;Vanara.PInvoke.Cryptography.dll;Vanara.PInvoke.Gdi32.dll;Vanara.PInvoke.Kernel32.dll;Vanara.PInvoke.Ole.dll;Vanara.PInvoke.Rpc.dll;Vanara.PInvoke.Security.dll;Vanara.PInvoke.Shared.dll;Vanara.PInvoke.Shell32.dll;Vanara.PInvoke.ShlwApi.dll;Vanara.PInvoke.User32.dll;PowerToys.PowerAccentKeyboardService.dll;Microsoft.Windows.SDK.NET.dll;WinRT.Runtime.dll?>
|
<?define PowerAccentFiles=ControlzEx.dll;GongSolutions.WPF.DragDrop.dll;Ijwhost.dll;MahApps.Metro.dll;Microsoft.Xaml.Behaviors.dll;PowerAccent.Core.dll;PowerAccent.deps.json;PowerAccent.dll;PowerAccent.exe;PowerAccent.runtimeconfig.json;PowerToys.PowerAccentModuleInterface.dll;PowerToys.ManagedCommon.dll;PowerToys.ManagedTelemetry.dll;PowerToys.PowerAccent.deps.json;PowerToys.PowerAccent.dll;PowerToys.PowerAccent.exe;PowerToys.PowerAccent.runtimeconfig.json;PowerToys.Settings.UI.Lib.dll;System.IO.Abstractions.dll;System.Management.dll;System.Text.Json.dll;Vanara.Core.dll;Vanara.PInvoke.ComCtl32.dll;Vanara.PInvoke.Cryptography.dll;Vanara.PInvoke.Gdi32.dll;Vanara.PInvoke.Kernel32.dll;Vanara.PInvoke.Ole.dll;Vanara.PInvoke.Rpc.dll;Vanara.PInvoke.Security.dll;Vanara.PInvoke.Shared.dll;Vanara.PInvoke.Shell32.dll;Vanara.PInvoke.ShlwApi.dll;Vanara.PInvoke.User32.dll;PowerToys.PowerAccentKeyboardService.dll;Microsoft.Windows.SDK.NET.dll;WinRT.Runtime.dll?>
|
||||||
|
|
||||||
@@ -543,7 +548,14 @@
|
|||||||
<Directory Id="MeasureToolMicrosoftUIXamlAssetsInstallFolder" Name="Assets" />
|
<Directory Id="MeasureToolMicrosoftUIXamlAssetsInstallFolder" Name="Assets" />
|
||||||
</Directory>
|
</Directory>
|
||||||
</Directory>
|
</Directory>
|
||||||
|
|
||||||
|
<!-- Hosts -->
|
||||||
|
<Directory Id="HostsInstallFolder" Name="$(var.HostsProjectName)">
|
||||||
|
<Directory Id="HostsMicrosoftUIXamlInstallFolder" Name="Microsoft.UI.Xaml">
|
||||||
|
<Directory Id="HostsMicrosoftUIXamlAssetsInstallFolder" Name="Assets" />
|
||||||
|
</Directory>
|
||||||
|
</Directory>
|
||||||
|
|
||||||
<!-- Launcher -->
|
<!-- Launcher -->
|
||||||
<Directory Id="LauncherInstallFolder" Name="launcher">
|
<Directory Id="LauncherInstallFolder" Name="launcher">
|
||||||
<Directory Id="LauncherImagesFolder" Name="Images" />
|
<Directory Id="LauncherImagesFolder" Name="Images" />
|
||||||
@@ -1079,6 +1091,28 @@
|
|||||||
<?endforeach?>
|
<?endforeach?>
|
||||||
</DirectoryRef>
|
</DirectoryRef>
|
||||||
|
|
||||||
|
<!-- Hosts -->
|
||||||
|
<DirectoryRef Id="HostsInstallFolder" FileSource="$(var.BinDir)modules\$(var.HostsProjectName)">
|
||||||
|
<Component Id="Module_HostsInterface" Win64="yes">
|
||||||
|
<File Source="$(var.BinDir)modules\$(var.HostsProjectName)\PowerToys.HostsModuleInterface.dll" />
|
||||||
|
</Component>
|
||||||
|
|
||||||
|
<?foreach File in $(var.HostsFiles)?>
|
||||||
|
<Component Id="H_$(var.File)" Win64="yes">
|
||||||
|
<File Id="H_$(var.File)" Source="$(var.BinDir)modules\$(var.HostsProjectName)\$(var.File)" />
|
||||||
|
</Component>
|
||||||
|
<?endforeach?>
|
||||||
|
</DirectoryRef>
|
||||||
|
|
||||||
|
<DirectoryRef Id="HostsMicrosoftUIXamlAssetsInstallFolder" FileSource="$(var.BinDir)modules\$(var.HostsProjectName)\Microsoft.UI.Xaml\Assets">
|
||||||
|
<?foreach File in $(var.HostsMicrosoftUIXamlAssetsInstallFiles)?>
|
||||||
|
<Component Id="HostsMicrosoftUIXamlAssets_$(var.File)" Win64="yes">
|
||||||
|
<File Id="HostsMicrosoftUIXamlAssetsFile_$(var.File)" Source="$(var.BinDir)modules\$(var.HostsProjectName)\Microsoft.UI.Xaml\Assets\$(var.File)" />
|
||||||
|
</Component>
|
||||||
|
<?endforeach?>
|
||||||
|
</DirectoryRef>
|
||||||
|
|
||||||
|
|
||||||
<!-- SettingsV2 components -->
|
<!-- SettingsV2 components -->
|
||||||
<DirectoryRef Id="SettingsV2InstallFolder" FileSource="$(var.BinDir)Settings\">
|
<DirectoryRef Id="SettingsV2InstallFolder" FileSource="$(var.BinDir)Settings\">
|
||||||
<?foreach File in $(var.SettingsV2Files)?>
|
<?foreach File in $(var.SettingsV2Files)?>
|
||||||
@@ -1227,6 +1261,13 @@
|
|||||||
<?foreach File in $(var.MeasureToolMicrosoftUIXamlAssetsInstallFiles)?>
|
<?foreach File in $(var.MeasureToolMicrosoftUIXamlAssetsInstallFiles)?>
|
||||||
<ComponentRef Id="MeasureToolMicrosoftUIXamlAssets_$(var.File)" />
|
<ComponentRef Id="MeasureToolMicrosoftUIXamlAssets_$(var.File)" />
|
||||||
<?endforeach?>
|
<?endforeach?>
|
||||||
|
<ComponentRef Id="Module_HostsInterface"/>
|
||||||
|
<?foreach File in $(var.HostsFiles)?>
|
||||||
|
<ComponentRef Id="H_$(var.File)" />
|
||||||
|
<?endforeach?>
|
||||||
|
<?foreach File in $(var.HostsMicrosoftUIXamlAssetsInstallFiles)?>
|
||||||
|
<ComponentRef Id="HostsMicrosoftUIXamlAssets_$(var.File)" />
|
||||||
|
<?endforeach?>
|
||||||
|
|
||||||
<?foreach File in $(var.SettingsV2Files)?>
|
<?foreach File in $(var.SettingsV2Files)?>
|
||||||
<ComponentRef Id="SV2C_$(var.File)" />
|
<ComponentRef Id="SV2C_$(var.File)" />
|
||||||
@@ -1580,7 +1621,7 @@
|
|||||||
<!-- Localization languages shipped with WinAppSDK. We should ship these as well. -->
|
<!-- Localization languages shipped with WinAppSDK. We should ship these as well. -->
|
||||||
<?define WinAppSDKLocLanguageList = af-ZA;ar-SA;az-Latn-AZ;bg-BG;bs-Latn-BA;ca-ES;cs-CZ;cy-GB;da-DK;de-DE;el-GR;en-GB;en-us;es-ES;es-MX;et-EE;eu-ES;fa-IR;fi-FI;fr-CA;fr-FR;gl-ES;he-IL;hi-IN;hr-HR;hu-HU;id-ID;is-IS;it-IT;ja-JP;ka-GE;kk-KZ;ko-KR;lt-LT;lv-LV;ms-MY;nb-NO;nl-NL;nn-NO;pl-PL;pt-BR;pt-PT;ro-RO;ru-RU;sk-SK;sl-SI;sq-AL;sr-Cyrl-RS;sr-Latn-RS;sv-SE;th-TH;tr-TR;uk-UA;vi-VN;zh-CN;zh-TW?>
|
<?define WinAppSDKLocLanguageList = af-ZA;ar-SA;az-Latn-AZ;bg-BG;bs-Latn-BA;ca-ES;cs-CZ;cy-GB;da-DK;de-DE;el-GR;en-GB;en-us;es-ES;es-MX;et-EE;eu-ES;fa-IR;fi-FI;fr-CA;fr-FR;gl-ES;he-IL;hi-IN;hr-HR;hu-HU;id-ID;is-IS;it-IT;ja-JP;ka-GE;kk-KZ;ko-KR;lt-LT;lv-LV;ms-MY;nb-NO;nl-NL;nn-NO;pl-PL;pt-BR;pt-PT;ro-RO;ru-RU;sk-SK;sl-SI;sq-AL;sr-Cyrl-RS;sr-Latn-RS;sv-SE;th-TH;tr-TR;uk-UA;vi-VN;zh-CN;zh-TW?>
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<?foreach ParentDirectory in SettingsV2InstallFolder;PowerRenameInstallFolder;MeasureToolInstallFolder?>
|
<?foreach ParentDirectory in SettingsV2InstallFolder;PowerRenameInstallFolder;MeasureToolInstallFolder;HostsInstallFolder?>
|
||||||
<DirectoryRef Id="$(var.ParentDirectory)">
|
<DirectoryRef Id="$(var.ParentDirectory)">
|
||||||
<?foreach Language in $(var.WinAppSDKLocLanguageList)?>
|
<?foreach Language in $(var.WinAppSDKLocLanguageList)?>
|
||||||
<?if $(var.Language) = af-ZA?>
|
<?if $(var.Language) = af-ZA?>
|
||||||
@@ -1899,6 +1940,13 @@
|
|||||||
<File Id="MeasureTool_WinAppSDKLoc_$(var.IdSafeLanguage)_XamlMui_File" Source="$(var.BinDir)modules\$(var.MeasureToolProjectName)\$(var.Language)\Microsoft.ui.xaml.dll.mui" />
|
<File Id="MeasureTool_WinAppSDKLoc_$(var.IdSafeLanguage)_XamlMui_File" Source="$(var.BinDir)modules\$(var.MeasureToolProjectName)\$(var.Language)\Microsoft.ui.xaml.dll.mui" />
|
||||||
<File Id="MeasureTool_WinAppSDKLoc_$(var.IdSafeLanguage)_XamlPhoneMui_File" Source="$(var.BinDir)modules\$(var.MeasureToolProjectName)\$(var.Language)\Microsoft.UI.Xaml.Phone.dll.mui" />
|
<File Id="MeasureTool_WinAppSDKLoc_$(var.IdSafeLanguage)_XamlPhoneMui_File" Source="$(var.BinDir)modules\$(var.MeasureToolProjectName)\$(var.Language)\Microsoft.UI.Xaml.Phone.dll.mui" />
|
||||||
</Component>
|
</Component>
|
||||||
|
<Component
|
||||||
|
Id="Hosts_WinAppSDKLoc_$(var.IdSafeLanguage)_Component"
|
||||||
|
Directory="WinAppSDKLoc$(var.IdSafeLanguage)HostsInstallFolder"
|
||||||
|
Guid="$(var.CompGUIDPrefix)04">
|
||||||
|
<File Id="Hosts_WinAppSDKLoc_$(var.IdSafeLanguage)_XamlMui_File" Source="$(var.BinDir)modules\$(var.HostsProjectName)\$(var.Language)\Microsoft.ui.xaml.dll.mui" />
|
||||||
|
<File Id="Hosts_WinAppSDKLoc_$(var.IdSafeLanguage)_XamlPhoneMui_File" Source="$(var.BinDir)modules\$(var.HostsProjectName)\$(var.Language)\Microsoft.UI.Xaml.Phone.dll.mui" />
|
||||||
|
</Component>
|
||||||
<?undef IdSafeLanguage?>
|
<?undef IdSafeLanguage?>
|
||||||
<?undef CompGUIDPrefix?>
|
<?undef CompGUIDPrefix?>
|
||||||
<?endforeach?>
|
<?endforeach?>
|
||||||
|
@@ -1049,7 +1049,7 @@ UINT __stdcall CreateWinAppSDKHardlinksCA(MSIHANDLE hInstall)
|
|||||||
{
|
{
|
||||||
HRESULT hr = S_OK;
|
HRESULT hr = S_OK;
|
||||||
UINT er = ERROR_SUCCESS;
|
UINT er = ERROR_SUCCESS;
|
||||||
std::wstring installationFolder, winAppSDKFilesSrcDir, settingsDir, powerRenameDir, measureToolDir;
|
std::wstring installationFolder, winAppSDKFilesSrcDir, settingsDir, powerRenameDir, measureToolDir, hostsFileEditorDir;
|
||||||
|
|
||||||
hr = WcaInitialize(hInstall, "CreateWinAppSDKHardlinksCA");
|
hr = WcaInitialize(hInstall, "CreateWinAppSDKHardlinksCA");
|
||||||
ExitOnFailure(hr, "Failed to initialize");
|
ExitOnFailure(hr, "Failed to initialize");
|
||||||
@@ -1058,6 +1058,7 @@ UINT __stdcall CreateWinAppSDKHardlinksCA(MSIHANDLE hInstall)
|
|||||||
ExitOnFailure(hr, "Failed to get installation folder");
|
ExitOnFailure(hr, "Failed to get installation folder");
|
||||||
|
|
||||||
winAppSDKFilesSrcDir = installationFolder + L"dll\\WinAppSDK\\";
|
winAppSDKFilesSrcDir = installationFolder + L"dll\\WinAppSDK\\";
|
||||||
|
hostsFileEditorDir = installationFolder + L"modules\\Hosts\\";
|
||||||
settingsDir = installationFolder + L"Settings\\";
|
settingsDir = installationFolder + L"Settings\\";
|
||||||
powerRenameDir = installationFolder + L"modules\\PowerRename\\";
|
powerRenameDir = installationFolder + L"modules\\PowerRename\\";
|
||||||
measureToolDir = installationFolder + L"modules\\MeasureTool\\";
|
measureToolDir = installationFolder + L"modules\\MeasureTool\\";
|
||||||
@@ -1065,6 +1066,7 @@ UINT __stdcall CreateWinAppSDKHardlinksCA(MSIHANDLE hInstall)
|
|||||||
for (auto file : winAppSdkFiles)
|
for (auto file : winAppSdkFiles)
|
||||||
{
|
{
|
||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
|
std::filesystem::create_hard_link((winAppSDKFilesSrcDir + file).c_str(), (hostsFileEditorDir + file).c_str(), ec);
|
||||||
std::filesystem::create_hard_link((winAppSDKFilesSrcDir + file).c_str(), (settingsDir + file).c_str(), ec);
|
std::filesystem::create_hard_link((winAppSDKFilesSrcDir + file).c_str(), (settingsDir + file).c_str(), ec);
|
||||||
std::filesystem::create_hard_link((winAppSDKFilesSrcDir + file).c_str(), (powerRenameDir + file).c_str(), ec);
|
std::filesystem::create_hard_link((winAppSDKFilesSrcDir + file).c_str(), (powerRenameDir + file).c_str(), ec);
|
||||||
std::filesystem::create_hard_link((winAppSDKFilesSrcDir + file).c_str(), (measureToolDir + file).c_str(), ec);
|
std::filesystem::create_hard_link((winAppSDKFilesSrcDir + file).c_str(), (measureToolDir + file).c_str(), ec);
|
||||||
@@ -1088,7 +1090,7 @@ UINT __stdcall CreatePTInteropHardlinksCA(MSIHANDLE hInstall)
|
|||||||
HRESULT hr = S_OK;
|
HRESULT hr = S_OK;
|
||||||
UINT er = ERROR_SUCCESS;
|
UINT er = ERROR_SUCCESS;
|
||||||
std::wstring installationFolder, interopFilesSrcDir, colorPickerDir, powerOCRDir, launcherDir, fancyZonesDir,
|
std::wstring installationFolder, interopFilesSrcDir, colorPickerDir, powerOCRDir, launcherDir, fancyZonesDir,
|
||||||
imageResizerDir, settingsDir, awakeDir, measureToolDir, powerAccentDir;
|
imageResizerDir, settingsDir, awakeDir, measureToolDir, powerAccentDir, hostsFileEditorDir;
|
||||||
|
|
||||||
hr = WcaInitialize(hInstall, "CreatePTInteropHardlinksCA");
|
hr = WcaInitialize(hInstall, "CreatePTInteropHardlinksCA");
|
||||||
ExitOnFailure(hr, "Failed to initialize");
|
ExitOnFailure(hr, "Failed to initialize");
|
||||||
@@ -1101,6 +1103,7 @@ UINT __stdcall CreatePTInteropHardlinksCA(MSIHANDLE hInstall)
|
|||||||
powerOCRDir = installationFolder + L"modules\\PowerOCR\\";
|
powerOCRDir = installationFolder + L"modules\\PowerOCR\\";
|
||||||
launcherDir = installationFolder + L"modules\\launcher\\";
|
launcherDir = installationFolder + L"modules\\launcher\\";
|
||||||
fancyZonesDir = installationFolder + L"modules\\FancyZones\\";
|
fancyZonesDir = installationFolder + L"modules\\FancyZones\\";
|
||||||
|
hostsFileEditorDir = installationFolder + L"modules\\Hosts\\";
|
||||||
imageResizerDir = installationFolder + L"modules\\ImageResizer\\";
|
imageResizerDir = installationFolder + L"modules\\ImageResizer\\";
|
||||||
settingsDir = installationFolder + L"Settings\\";
|
settingsDir = installationFolder + L"Settings\\";
|
||||||
awakeDir = installationFolder + L"modules\\Awake\\";
|
awakeDir = installationFolder + L"modules\\Awake\\";
|
||||||
@@ -1114,6 +1117,7 @@ UINT __stdcall CreatePTInteropHardlinksCA(MSIHANDLE hInstall)
|
|||||||
std::filesystem::create_hard_link((interopFilesSrcDir + file).c_str(), (powerOCRDir + file).c_str(), ec);
|
std::filesystem::create_hard_link((interopFilesSrcDir + file).c_str(), (powerOCRDir + file).c_str(), ec);
|
||||||
std::filesystem::create_hard_link((interopFilesSrcDir + file).c_str(), (launcherDir + file).c_str(), ec);
|
std::filesystem::create_hard_link((interopFilesSrcDir + file).c_str(), (launcherDir + file).c_str(), ec);
|
||||||
std::filesystem::create_hard_link((interopFilesSrcDir + file).c_str(), (fancyZonesDir + file).c_str(), ec);
|
std::filesystem::create_hard_link((interopFilesSrcDir + file).c_str(), (fancyZonesDir + file).c_str(), ec);
|
||||||
|
std::filesystem::create_hard_link((interopFilesSrcDir + file).c_str(), (hostsFileEditorDir + file).c_str(), ec);
|
||||||
std::filesystem::create_hard_link((interopFilesSrcDir + file).c_str(), (imageResizerDir + file).c_str(), ec);
|
std::filesystem::create_hard_link((interopFilesSrcDir + file).c_str(), (imageResizerDir + file).c_str(), ec);
|
||||||
std::filesystem::create_hard_link((interopFilesSrcDir + file).c_str(), (settingsDir + file).c_str(), ec);
|
std::filesystem::create_hard_link((interopFilesSrcDir + file).c_str(), (settingsDir + file).c_str(), ec);
|
||||||
std::filesystem::create_hard_link((interopFilesSrcDir + file).c_str(), (awakeDir + file).c_str(), ec);
|
std::filesystem::create_hard_link((interopFilesSrcDir + file).c_str(), (awakeDir + file).c_str(), ec);
|
||||||
@@ -1138,7 +1142,7 @@ UINT __stdcall DeleteWinAppSDKHardlinksCA(MSIHANDLE hInstall)
|
|||||||
{
|
{
|
||||||
HRESULT hr = S_OK;
|
HRESULT hr = S_OK;
|
||||||
UINT er = ERROR_SUCCESS;
|
UINT er = ERROR_SUCCESS;
|
||||||
std::wstring installationFolder, settingsDir, powerRenameDir, measureToolDir;
|
std::wstring installationFolder, settingsDir, powerRenameDir, measureToolDir, hostsFileEditorDir;
|
||||||
|
|
||||||
hr = WcaInitialize(hInstall, "DeleteWinAppSDKHardlinksCA");
|
hr = WcaInitialize(hInstall, "DeleteWinAppSDKHardlinksCA");
|
||||||
ExitOnFailure(hr, "Failed to initialize");
|
ExitOnFailure(hr, "Failed to initialize");
|
||||||
@@ -1146,6 +1150,7 @@ UINT __stdcall DeleteWinAppSDKHardlinksCA(MSIHANDLE hInstall)
|
|||||||
hr = getInstallFolder(hInstall, installationFolder);
|
hr = getInstallFolder(hInstall, installationFolder);
|
||||||
ExitOnFailure(hr, "Failed to get installation folder");
|
ExitOnFailure(hr, "Failed to get installation folder");
|
||||||
|
|
||||||
|
hostsFileEditorDir = installationFolder + L"modules\\Hosts\\";
|
||||||
settingsDir = installationFolder + L"Settings\\";
|
settingsDir = installationFolder + L"Settings\\";
|
||||||
powerRenameDir = installationFolder + L"modules\\PowerRename\\";
|
powerRenameDir = installationFolder + L"modules\\PowerRename\\";
|
||||||
measureToolDir = installationFolder + L"modules\\MeasureTool\\";
|
measureToolDir = installationFolder + L"modules\\MeasureTool\\";
|
||||||
@@ -1154,6 +1159,7 @@ UINT __stdcall DeleteWinAppSDKHardlinksCA(MSIHANDLE hInstall)
|
|||||||
{
|
{
|
||||||
for (auto file : winAppSdkFiles)
|
for (auto file : winAppSdkFiles)
|
||||||
{
|
{
|
||||||
|
DeleteFile((hostsFileEditorDir + file).c_str());
|
||||||
DeleteFile((settingsDir + file).c_str());
|
DeleteFile((settingsDir + file).c_str());
|
||||||
DeleteFile((powerRenameDir + file).c_str());
|
DeleteFile((powerRenameDir + file).c_str());
|
||||||
DeleteFile((measureToolDir + file).c_str());
|
DeleteFile((measureToolDir + file).c_str());
|
||||||
@@ -1178,7 +1184,7 @@ UINT __stdcall DeletePTInteropHardlinksCA(MSIHANDLE hInstall)
|
|||||||
HRESULT hr = S_OK;
|
HRESULT hr = S_OK;
|
||||||
UINT er = ERROR_SUCCESS;
|
UINT er = ERROR_SUCCESS;
|
||||||
std::wstring installationFolder, interopFilesSrcDir, colorPickerDir, powerOCRDir, launcherDir, fancyZonesDir,
|
std::wstring installationFolder, interopFilesSrcDir, colorPickerDir, powerOCRDir, launcherDir, fancyZonesDir,
|
||||||
imageResizerDir, settingsDir, awakeDir, measureToolDir, powerAccentDir;
|
imageResizerDir, settingsDir, awakeDir, measureToolDir, powerAccentDir, hostsFileEditorDir;
|
||||||
|
|
||||||
hr = WcaInitialize(hInstall, "DeletePTInteropHardlinksCA");
|
hr = WcaInitialize(hInstall, "DeletePTInteropHardlinksCA");
|
||||||
ExitOnFailure(hr, "Failed to initialize");
|
ExitOnFailure(hr, "Failed to initialize");
|
||||||
@@ -1190,6 +1196,7 @@ UINT __stdcall DeletePTInteropHardlinksCA(MSIHANDLE hInstall)
|
|||||||
powerOCRDir = installationFolder + L"modules\\PowerOCR\\";
|
powerOCRDir = installationFolder + L"modules\\PowerOCR\\";
|
||||||
launcherDir = installationFolder + L"modules\\launcher\\";
|
launcherDir = installationFolder + L"modules\\launcher\\";
|
||||||
fancyZonesDir = installationFolder + L"modules\\FancyZones\\";
|
fancyZonesDir = installationFolder + L"modules\\FancyZones\\";
|
||||||
|
hostsFileEditorDir = installationFolder + L"modules\\Hosts\\";
|
||||||
imageResizerDir = installationFolder + L"modules\\ImageResizer\\";
|
imageResizerDir = installationFolder + L"modules\\ImageResizer\\";
|
||||||
settingsDir = installationFolder + L"Settings\\";
|
settingsDir = installationFolder + L"Settings\\";
|
||||||
awakeDir = installationFolder + L"modules\\Awake\\";
|
awakeDir = installationFolder + L"modules\\Awake\\";
|
||||||
@@ -1204,6 +1211,7 @@ UINT __stdcall DeletePTInteropHardlinksCA(MSIHANDLE hInstall)
|
|||||||
DeleteFile((powerOCRDir + file).c_str());
|
DeleteFile((powerOCRDir + file).c_str());
|
||||||
DeleteFile((launcherDir + file).c_str());
|
DeleteFile((launcherDir + file).c_str());
|
||||||
DeleteFile((fancyZonesDir + file).c_str());
|
DeleteFile((fancyZonesDir + file).c_str());
|
||||||
|
DeleteFile((hostsFileEditorDir + file).c_str());
|
||||||
DeleteFile((imageResizerDir + file).c_str());
|
DeleteFile((imageResizerDir + file).c_str());
|
||||||
DeleteFile((settingsDir + file).c_str());
|
DeleteFile((settingsDir + file).c_str());
|
||||||
DeleteFile((awakeDir + file).c_str());
|
DeleteFile((awakeDir + file).c_str());
|
||||||
|
@@ -23,6 +23,7 @@ namespace Common.UI
|
|||||||
FileExplorer,
|
FileExplorer,
|
||||||
ShortcutGuide,
|
ShortcutGuide,
|
||||||
VideoConference,
|
VideoConference,
|
||||||
|
Hosts,
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string SettingsWindowNameToString(SettingsWindow value)
|
private static string SettingsWindowNameToString(SettingsWindow value)
|
||||||
@@ -53,6 +54,8 @@ namespace Common.UI
|
|||||||
return "ShortcutGuide";
|
return "ShortcutGuide";
|
||||||
case SettingsWindow.VideoConference:
|
case SettingsWindow.VideoConference:
|
||||||
return "VideoConference";
|
return "VideoConference";
|
||||||
|
case SettingsWindow.Hosts:
|
||||||
|
return "Hosts";
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
|
@@ -35,6 +35,8 @@ struct LogSettings
|
|||||||
inline const static std::string alwaysOnTopLoggerName = "always-on-top";
|
inline const static std::string alwaysOnTopLoggerName = "always-on-top";
|
||||||
inline const static std::string powerOcrLoggerName = "TextExtractor";
|
inline const static std::string powerOcrLoggerName = "TextExtractor";
|
||||||
inline const static std::wstring alwaysOnTopLogPath = L"always-on-top-log.txt";
|
inline const static std::wstring alwaysOnTopLogPath = L"always-on-top-log.txt";
|
||||||
|
inline const static std::string hostsLoggerName = "hosts";
|
||||||
|
inline const static std::wstring hostsLogPath = L"Logs\\hosts-log.txt";
|
||||||
inline const static int retention = 30;
|
inline const static int retention = 30;
|
||||||
std::wstring logLevel;
|
std::wstring logLevel;
|
||||||
LogSettings();
|
LogSettings();
|
||||||
|
83
src/modules/Hosts/Hosts.Tests/EntryTest.cs
Normal file
83
src/modules/Hosts/Hosts.Tests/EntryTest.cs
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
// 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 Hosts.Models;
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
|
||||||
|
namespace Hosts.Tests
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
public class EntryTest
|
||||||
|
{
|
||||||
|
[DataTestMethod]
|
||||||
|
[DataRow("\t\t10.1.1.1\t\thost\t\t", "10.1.1.1", "host", "", true)]
|
||||||
|
[DataRow(" 10.1.1.1 host ", "10.1.1.1", "host", "", true)]
|
||||||
|
[DataRow("10.1.1.1 host", "10.1.1.1", "host", "", true)]
|
||||||
|
[DataRow("\t\t#\t\t10.1.1.1\thost\t\t", "10.1.1.1", "host", "", false)]
|
||||||
|
[DataRow(" # 10.1.1.1 host ", "10.1.1.1", "host", "", false)]
|
||||||
|
[DataRow("#10.1.1.1 host", "10.1.1.1", "host", "", false)]
|
||||||
|
[DataRow("\t\t10.1.1.1\t\thost\t\t#\t\tcomment\t\t", "10.1.1.1", "host", "comment", true)]
|
||||||
|
[DataRow(" 10.1.1.1 host # comment ", "10.1.1.1", "host", "comment", true)]
|
||||||
|
[DataRow("10.1.1.1 host#comment", "10.1.1.1", "host", "comment", true)]
|
||||||
|
[DataRow("\t\t#\t\t10.1.1.1\thost\t\t#\t\tcomment\t\t", "10.1.1.1", "host", "comment", false)]
|
||||||
|
[DataRow(" # 10.1.1.1 host # comment ", "10.1.1.1", "host", "comment", false)]
|
||||||
|
[DataRow("#10.1.1.1 host#comment", "10.1.1.1", "host", "comment", false)]
|
||||||
|
[DataRow("# #10.1.1.1 host#comment", "10.1.1.1", "host", "comment", false)]
|
||||||
|
[DataRow("# #\t10.1.1.1 host#comment", "10.1.1.1", "host", "comment", false)]
|
||||||
|
[DataRow("# # \t10.1.1.1 host#comment", "10.1.1.1", "host", "comment", false)]
|
||||||
|
public void Valid_Entry_SingleHost(string line, string address, string host, string comment, bool active)
|
||||||
|
{
|
||||||
|
var entry = new Entry(line);
|
||||||
|
|
||||||
|
Assert.AreEqual(entry.Address, address);
|
||||||
|
Assert.AreEqual(entry.Hosts, host);
|
||||||
|
Assert.AreEqual(entry.Comment, comment);
|
||||||
|
Assert.AreEqual(entry.Active, active);
|
||||||
|
Assert.IsTrue(entry.Valid);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataTestMethod]
|
||||||
|
[DataRow("\t\t10.1.1.1\t\thost host.local\t\t", "10.1.1.1", "host host.local", "", true)]
|
||||||
|
[DataRow(" 10.1.1.1 host host.local ", "10.1.1.1", "host host.local", "", true)]
|
||||||
|
[DataRow("10.1.1.1 host host.local", "10.1.1.1", "host host.local", "", true)]
|
||||||
|
[DataRow("\t\t#\t\t10.1.1.1\thost\t\thost.local\t\t", "10.1.1.1", "host host.local", "", false)]
|
||||||
|
[DataRow(" # 10.1.1.1 host host.local ", "10.1.1.1", "host host.local", "", false)]
|
||||||
|
[DataRow("#10.1.1.1 host host.local", "10.1.1.1", "host host.local", "", false)]
|
||||||
|
[DataRow("\t\t10.1.1.1\t\thost\t\thost.local\t\t#\t\tcomment\t\t", "10.1.1.1", "host host.local", "comment", true)]
|
||||||
|
[DataRow(" 10.1.1.1 host host.local # comment ", "10.1.1.1", "host host.local", "comment", true)]
|
||||||
|
[DataRow("10.1.1.1 host host.local#comment", "10.1.1.1", "host host.local", "comment", true)]
|
||||||
|
[DataRow("\t\t#\t\t10.1.1.1\thost\t\thost.local\t\t#\t\tcomment\t\t", "10.1.1.1", "host host.local", "comment", false)]
|
||||||
|
[DataRow(" # 10.1.1.1 host host.local # comment ", "10.1.1.1", "host host.local", "comment", false)]
|
||||||
|
[DataRow("#10.1.1.1 host host.local#comment", "10.1.1.1", "host host.local", "comment", false)]
|
||||||
|
public void Valid_Entry_MultipleHosts(string line, string address, string host, string comment, bool active)
|
||||||
|
{
|
||||||
|
var entry = new Entry(line);
|
||||||
|
|
||||||
|
Assert.AreEqual(entry.Address, address);
|
||||||
|
Assert.AreEqual(entry.Hosts, host);
|
||||||
|
Assert.AreEqual(entry.Comment, comment);
|
||||||
|
Assert.AreEqual(entry.Active, active);
|
||||||
|
Assert.IsTrue(entry.Valid);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataTestMethod]
|
||||||
|
[DataRow("\t\t10.1.1.1\t\t")]
|
||||||
|
[DataRow(" 10.1.1.1 ")]
|
||||||
|
[DataRow("10.1.1.1")]
|
||||||
|
[DataRow("\t\thost\t\t")]
|
||||||
|
[DataRow(" host ")]
|
||||||
|
[DataRow("host")]
|
||||||
|
[DataRow("\t\t10\t\thost")]
|
||||||
|
[DataRow(" 10 host ")]
|
||||||
|
[DataRow("10 host")]
|
||||||
|
[DataRow("\t\thost\t\t10.1.1.1")]
|
||||||
|
[DataRow(" host 10.1.1.1")]
|
||||||
|
[DataRow("host 10.1.1.1")]
|
||||||
|
public void Not_Valid_Entry(string line)
|
||||||
|
{
|
||||||
|
var entry = new Entry(line);
|
||||||
|
Assert.IsFalse(entry.Valid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
30
src/modules/Hosts/Hosts.Tests/Hosts.Tests.csproj
Normal file
30
src/modules/Hosts/Hosts.Tests/Hosts.Tests.csproj
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<Import Project="..\..\..\Version.props" />
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net6.0-windows10.0.19041.0</TargetFramework>
|
||||||
|
<RuntimeIdentifiers>win10-x64;win10-arm64</RuntimeIdentifiers>
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||||
|
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||||
|
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\modules\Hosts\Hosts.Tests\</OutputPath>
|
||||||
|
<IntermediateOutputPath>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(AssemblyName)\</IntermediateOutputPath>
|
||||||
|
<RootNamespace>Hosts.Tests</RootNamespace>
|
||||||
|
<AssemblyName>PowerToys.Hosts.Tests</AssemblyName>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
|
||||||
|
<PackageReference Include="Moq" Version="4.16.1" />
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||||
|
<PackageReference Include="MSTest.TestAdapter" Version="2.2.3" />
|
||||||
|
<PackageReference Include="MSTest.TestFramework" Version="2.2.3" />
|
||||||
|
<PackageReference Include="System.IO.Abstractions" Version="12.2.5" />
|
||||||
|
<PackageReference Include="System.IO.Abstractions.TestingHelpers" Version="12.2.3" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Hosts\Hosts.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
255
src/modules/Hosts/Hosts.Tests/HostsServiceTest.cs
Normal file
255
src/modules/Hosts/Hosts.Tests/HostsServiceTest.cs
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO.Abstractions.TestingHelpers;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Hosts.Helpers;
|
||||||
|
using Hosts.Models;
|
||||||
|
using Hosts.Settings;
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using Moq;
|
||||||
|
using Settings.UI.Library.Enumerations;
|
||||||
|
|
||||||
|
namespace Hosts.Tests
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
public class HostsServiceTest
|
||||||
|
{
|
||||||
|
private static Mock<IElevationHelper> _elevationHelper;
|
||||||
|
|
||||||
|
[ClassInitialize]
|
||||||
|
public static void ClassInitialize(TestContext context)
|
||||||
|
{
|
||||||
|
_elevationHelper = new Mock<IElevationHelper>();
|
||||||
|
_elevationHelper.Setup(m => m.IsElevated).Returns(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void Hosts_Exists()
|
||||||
|
{
|
||||||
|
var fileSystem = new MockFileSystem
|
||||||
|
{
|
||||||
|
FileSystemWatcher = new TestFileSystemWatcherFactory(),
|
||||||
|
};
|
||||||
|
|
||||||
|
var userSettings = new Mock<IUserSettings>();
|
||||||
|
|
||||||
|
var service = new HostsService(fileSystem, userSettings.Object, _elevationHelper.Object);
|
||||||
|
fileSystem.AddFile(service.HostsFilePath, new MockFileData(string.Empty));
|
||||||
|
var result = service.Exists();
|
||||||
|
|
||||||
|
Assert.IsTrue(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void Hosts_Not_Exists()
|
||||||
|
{
|
||||||
|
var fileSystem = new MockFileSystem(new Dictionary<string, MockFileData>
|
||||||
|
{
|
||||||
|
})
|
||||||
|
{
|
||||||
|
FileSystemWatcher = new TestFileSystemWatcherFactory(),
|
||||||
|
};
|
||||||
|
|
||||||
|
var userSettings = new Mock<IUserSettings>();
|
||||||
|
|
||||||
|
var service = new HostsService(fileSystem, userSettings.Object, _elevationHelper.Object);
|
||||||
|
var result = service.Exists();
|
||||||
|
|
||||||
|
Assert.IsFalse(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Host_Added()
|
||||||
|
{
|
||||||
|
var content =
|
||||||
|
@"10.1.1.1 host host.local # comment
|
||||||
|
10.1.1.2 host2 host2.local # another comment
|
||||||
|
";
|
||||||
|
|
||||||
|
var contentResult =
|
||||||
|
@" 10.1.1.1 host host.local # comment
|
||||||
|
10.1.1.2 host2 host2.local # another comment
|
||||||
|
# 10.1.1.30 host30 host30.local # new entry
|
||||||
|
";
|
||||||
|
|
||||||
|
var fileSystem = new MockFileSystem
|
||||||
|
{
|
||||||
|
FileSystemWatcher = new TestFileSystemWatcherFactory(),
|
||||||
|
};
|
||||||
|
|
||||||
|
var userSettings = new Mock<IUserSettings>();
|
||||||
|
|
||||||
|
var service = new HostsService(fileSystem, userSettings.Object, _elevationHelper.Object);
|
||||||
|
fileSystem.AddFile(service.HostsFilePath, new MockFileData(content));
|
||||||
|
|
||||||
|
var (_, entries) = await service.ReadAsync();
|
||||||
|
entries.Add(new Entry("10.1.1.30", "host30 host30.local", "new entry", false));
|
||||||
|
await service.WriteAsync(string.Empty, entries);
|
||||||
|
|
||||||
|
var result = fileSystem.GetFile(service.HostsFilePath);
|
||||||
|
Assert.AreEqual(result.TextContents, contentResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Host_Deleted()
|
||||||
|
{
|
||||||
|
var content =
|
||||||
|
@"10.1.1.1 host host.local # comment
|
||||||
|
10.1.1.2 host2 host2.local # another comment
|
||||||
|
";
|
||||||
|
|
||||||
|
var contentResult =
|
||||||
|
@"10.1.1.2 host2 host2.local # another comment
|
||||||
|
";
|
||||||
|
|
||||||
|
var fileSystem = new MockFileSystem
|
||||||
|
{
|
||||||
|
FileSystemWatcher = new TestFileSystemWatcherFactory(),
|
||||||
|
};
|
||||||
|
|
||||||
|
var userSettings = new Mock<IUserSettings>();
|
||||||
|
|
||||||
|
var service = new HostsService(fileSystem, userSettings.Object, _elevationHelper.Object);
|
||||||
|
fileSystem.AddFile(service.HostsFilePath, new MockFileData(content));
|
||||||
|
|
||||||
|
var (_, entries) = await service.ReadAsync();
|
||||||
|
entries.RemoveAt(0);
|
||||||
|
await service.WriteAsync(string.Empty, entries);
|
||||||
|
|
||||||
|
var result = fileSystem.GetFile(service.HostsFilePath);
|
||||||
|
Assert.AreEqual(result.TextContents, contentResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Host_Updated()
|
||||||
|
{
|
||||||
|
var content =
|
||||||
|
@"10.1.1.1 host host.local # comment
|
||||||
|
10.1.1.2 host2 host2.local # another comment
|
||||||
|
";
|
||||||
|
|
||||||
|
var contentResult =
|
||||||
|
@"# 10.1.1.10 host host.local host1.local # updated comment
|
||||||
|
10.1.1.2 host2 host2.local # another comment
|
||||||
|
";
|
||||||
|
|
||||||
|
var fileSystem = new MockFileSystem
|
||||||
|
{
|
||||||
|
FileSystemWatcher = new TestFileSystemWatcherFactory(),
|
||||||
|
};
|
||||||
|
|
||||||
|
var userSettings = new Mock<IUserSettings>();
|
||||||
|
|
||||||
|
var service = new HostsService(fileSystem, userSettings.Object, _elevationHelper.Object);
|
||||||
|
fileSystem.AddFile(service.HostsFilePath, new MockFileData(content));
|
||||||
|
|
||||||
|
var (_, entries) = await service.ReadAsync();
|
||||||
|
var entry = entries[0];
|
||||||
|
entry.Address = "10.1.1.10";
|
||||||
|
entry.Hosts = "host host.local host1.local";
|
||||||
|
entry.Comment = "updated comment";
|
||||||
|
entry.Active = false;
|
||||||
|
await service.WriteAsync(string.Empty, entries);
|
||||||
|
|
||||||
|
var result = fileSystem.GetFile(service.HostsFilePath);
|
||||||
|
Assert.AreEqual(result.TextContents, contentResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Empty_Hosts()
|
||||||
|
{
|
||||||
|
var fileSystem = new MockFileSystem
|
||||||
|
{
|
||||||
|
FileSystemWatcher = new TestFileSystemWatcherFactory(),
|
||||||
|
};
|
||||||
|
|
||||||
|
var userSettings = new Mock<IUserSettings>();
|
||||||
|
|
||||||
|
var service = new HostsService(fileSystem, userSettings.Object, _elevationHelper.Object);
|
||||||
|
fileSystem.AddFile(service.HostsFilePath, new MockFileData(string.Empty));
|
||||||
|
|
||||||
|
await service.WriteAsync(string.Empty, Enumerable.Empty<Entry>());
|
||||||
|
|
||||||
|
var result = fileSystem.GetFile(service.HostsFilePath);
|
||||||
|
Assert.AreEqual(result.TextContents, string.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task AdditionalLines_Top()
|
||||||
|
{
|
||||||
|
var content =
|
||||||
|
@"# header
|
||||||
|
10.1.1.1 host host.local # comment
|
||||||
|
# comment
|
||||||
|
10.1.1.2 host2 host2.local # another comment
|
||||||
|
# footer
|
||||||
|
";
|
||||||
|
|
||||||
|
var contentResult =
|
||||||
|
@"# header
|
||||||
|
# comment
|
||||||
|
# footer
|
||||||
|
10.1.1.1 host host.local # comment
|
||||||
|
10.1.1.2 host2 host2.local # another comment
|
||||||
|
";
|
||||||
|
|
||||||
|
var fileSystem = new MockFileSystem
|
||||||
|
{
|
||||||
|
FileSystemWatcher = new TestFileSystemWatcherFactory(),
|
||||||
|
};
|
||||||
|
|
||||||
|
var userSettings = new Mock<IUserSettings>();
|
||||||
|
userSettings.Setup(m => m.AdditionalLinesPosition).Returns(AdditionalLinesPosition.Top);
|
||||||
|
|
||||||
|
var service = new HostsService(fileSystem, userSettings.Object, _elevationHelper.Object);
|
||||||
|
fileSystem.AddFile(service.HostsFilePath, new MockFileData(content));
|
||||||
|
|
||||||
|
var (additionalLines, entries) = await service.ReadAsync();
|
||||||
|
await service.WriteAsync(additionalLines, entries);
|
||||||
|
|
||||||
|
var result = fileSystem.GetFile(service.HostsFilePath);
|
||||||
|
Assert.AreEqual(result.TextContents, contentResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task AdditionalLines_Bottom()
|
||||||
|
{
|
||||||
|
var content =
|
||||||
|
@"# header
|
||||||
|
10.1.1.1 host host.local # comment
|
||||||
|
# comment
|
||||||
|
10.1.1.2 host2 host2.local # another comment
|
||||||
|
# footer
|
||||||
|
";
|
||||||
|
|
||||||
|
var contentResult =
|
||||||
|
@"10.1.1.1 host host.local # comment
|
||||||
|
10.1.1.2 host2 host2.local # another comment
|
||||||
|
# header
|
||||||
|
# comment
|
||||||
|
# footer
|
||||||
|
";
|
||||||
|
|
||||||
|
var fileSystem = new MockFileSystem
|
||||||
|
{
|
||||||
|
FileSystemWatcher = new TestFileSystemWatcherFactory(),
|
||||||
|
};
|
||||||
|
|
||||||
|
var userSettings = new Mock<IUserSettings>();
|
||||||
|
userSettings.Setup(m => m.AdditionalLinesPosition).Returns(AdditionalLinesPosition.Bottom);
|
||||||
|
|
||||||
|
var service = new HostsService(fileSystem, userSettings.Object, _elevationHelper.Object);
|
||||||
|
fileSystem.AddFile(service.HostsFilePath, new MockFileData(content));
|
||||||
|
|
||||||
|
var (additionalLines, entries) = await service.ReadAsync();
|
||||||
|
await service.WriteAsync(additionalLines, entries);
|
||||||
|
|
||||||
|
var result = fileSystem.GetFile(service.HostsFilePath);
|
||||||
|
Assert.AreEqual(result.TextContents, contentResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
49
src/modules/Hosts/Hosts.Tests/TestFileSystemWatcher.cs
Normal file
49
src/modules/Hosts/Hosts.Tests/TestFileSystemWatcher.cs
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.Abstractions;
|
||||||
|
|
||||||
|
namespace Hosts.Tests
|
||||||
|
{
|
||||||
|
public class TestFileSystemWatcher : FileSystemWatcherBase
|
||||||
|
{
|
||||||
|
public override bool IncludeSubdirectories { get; set; }
|
||||||
|
|
||||||
|
public override bool EnableRaisingEvents { get; set; }
|
||||||
|
|
||||||
|
public override string Filter { get; set; }
|
||||||
|
|
||||||
|
public override int InternalBufferSize { get; set; }
|
||||||
|
|
||||||
|
public override NotifyFilters NotifyFilter { get; set; }
|
||||||
|
|
||||||
|
public override string Path { get; set; }
|
||||||
|
|
||||||
|
public override ISite Site { get; set; }
|
||||||
|
|
||||||
|
public override ISynchronizeInvoke SynchronizingObject { get; set; }
|
||||||
|
|
||||||
|
public override WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType) => default(WaitForChangedResult);
|
||||||
|
|
||||||
|
public override WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType, int timeout) => default(WaitForChangedResult);
|
||||||
|
|
||||||
|
public TestFileSystemWatcher(string path) => Path = path;
|
||||||
|
|
||||||
|
public TestFileSystemWatcher(string path, string filter)
|
||||||
|
{
|
||||||
|
Path = path;
|
||||||
|
Filter = filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void BeginInit()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void EndInit()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,19 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using System.IO.Abstractions;
|
||||||
|
|
||||||
|
namespace Hosts.Tests
|
||||||
|
{
|
||||||
|
public class TestFileSystemWatcherFactory : IFileSystemWatcherFactory
|
||||||
|
{
|
||||||
|
public IFileSystemWatcher CreateNew() => new TestFileSystemWatcher(null);
|
||||||
|
|
||||||
|
public IFileSystemWatcher CreateNew(string path) => new TestFileSystemWatcher(path);
|
||||||
|
|
||||||
|
public IFileSystemWatcher CreateNew(string path, string filter) => new TestFileSystemWatcher(path, filter);
|
||||||
|
|
||||||
|
public IFileSystemWatcher FromPath(string path) => new TestFileSystemWatcher(path);
|
||||||
|
}
|
||||||
|
}
|
115
src/modules/Hosts/Hosts/App.xaml
Normal file
115
src/modules/Hosts/Hosts/App.xaml
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
<Application
|
||||||
|
x:Class="Hosts.App"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:local="using:Hosts">
|
||||||
|
<Application.Resources>
|
||||||
|
<ResourceDictionary>
|
||||||
|
<ResourceDictionary.MergedDictionaries>
|
||||||
|
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
|
||||||
|
<!-- Other merged dictionaries here -->
|
||||||
|
</ResourceDictionary.MergedDictionaries>
|
||||||
|
|
||||||
|
|
||||||
|
<SolidColorBrush x:Key="SubtleButtonBackground" Color="{ThemeResource SubtleFillColorTransparent}" />
|
||||||
|
<SolidColorBrush x:Key="SubtleButtonBackgroundPointerOver" Color="{ThemeResource SubtleFillColorSecondary}" />
|
||||||
|
<SolidColorBrush x:Key="SubtleButtonBackgroundPressed" Color="{ThemeResource SubtleFillColorTertiary}" />
|
||||||
|
<SolidColorBrush x:Key="SubtleButtonBackgroundDisabled" Color="{ThemeResource ControlFillColorDisabled}" />
|
||||||
|
|
||||||
|
<SolidColorBrush x:Key="SubtleButtonForeground" Color="{ThemeResource TextFillColorPrimary}" />
|
||||||
|
<SolidColorBrush x:Key="SubtleButtonForegroundPointerOver" Color="{ThemeResource TextFillColorPrimary}" />
|
||||||
|
<SolidColorBrush x:Key="SubtleButtonForegroundPressed" Color="{ThemeResource TextFillColorSecondary}" />
|
||||||
|
<SolidColorBrush x:Key="SubtleButtonForegroundDisabled" Color="{ThemeResource TextFillColorDisabled}" />
|
||||||
|
|
||||||
|
|
||||||
|
<Style x:Key="SubtleButtonStyle" TargetType="Button">
|
||||||
|
<Setter Property="Background" Value="{ThemeResource SubtleButtonBackground}" />
|
||||||
|
<Setter Property="BackgroundSizing" Value="InnerBorderEdge" />
|
||||||
|
<Setter Property="Foreground" Value="{ThemeResource SubtleButtonForeground}" />
|
||||||
|
<Setter Property="BorderThickness" Value="0" />
|
||||||
|
<Setter Property="Padding" Value="{StaticResource ButtonPadding}" />
|
||||||
|
<Setter Property="HorizontalAlignment" Value="Left" />
|
||||||
|
<Setter Property="VerticalAlignment" Value="Center" />
|
||||||
|
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
|
||||||
|
<Setter Property="FontWeight" Value="Normal" />
|
||||||
|
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
|
||||||
|
<Setter Property="UseSystemFocusVisuals" Value="{StaticResource UseSystemFocusVisuals}" />
|
||||||
|
<Setter Property="FocusVisualMargin" Value="-3" />
|
||||||
|
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}" />
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="Button">
|
||||||
|
<ContentPresenter
|
||||||
|
x:Name="ContentPresenter"
|
||||||
|
Padding="{TemplateBinding Padding}"
|
||||||
|
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||||
|
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||||
|
AnimatedIcon.State="Normal"
|
||||||
|
AutomationProperties.AccessibilityView="Raw"
|
||||||
|
Background="{TemplateBinding Background}"
|
||||||
|
BackgroundSizing="{TemplateBinding BackgroundSizing}"
|
||||||
|
BorderBrush="{TemplateBinding BorderBrush}"
|
||||||
|
BorderThickness="{TemplateBinding BorderThickness}"
|
||||||
|
Content="{TemplateBinding Content}"
|
||||||
|
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||||
|
ContentTransitions="{TemplateBinding ContentTransitions}"
|
||||||
|
CornerRadius="{TemplateBinding CornerRadius}">
|
||||||
|
<ContentPresenter.BackgroundTransition>
|
||||||
|
<BrushTransition Duration="0:0:0.083" />
|
||||||
|
</ContentPresenter.BackgroundTransition>
|
||||||
|
|
||||||
|
<VisualStateManager.VisualStateGroups>
|
||||||
|
<VisualStateGroup x:Name="CommonStates">
|
||||||
|
<VisualState x:Name="Normal" />
|
||||||
|
<VisualState x:Name="PointerOver">
|
||||||
|
<Storyboard>
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBackgroundPointerOver}" />
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonForegroundPointerOver}" />
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
</Storyboard>
|
||||||
|
<VisualState.Setters>
|
||||||
|
<Setter Target="ContentPresenter.(AnimatedIcon.State)" Value="PointerOver" />
|
||||||
|
</VisualState.Setters>
|
||||||
|
</VisualState>
|
||||||
|
|
||||||
|
<VisualState x:Name="Pressed">
|
||||||
|
<Storyboard>
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBackgroundPressed}" />
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonForegroundPressed}" />
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
</Storyboard>
|
||||||
|
<VisualState.Setters>
|
||||||
|
<Setter Target="ContentPresenter.(AnimatedIcon.State)" Value="Pressed" />
|
||||||
|
</VisualState.Setters>
|
||||||
|
</VisualState>
|
||||||
|
|
||||||
|
<VisualState x:Name="Disabled">
|
||||||
|
<Storyboard>
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBackgroundDisabled}" />
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
|
||||||
|
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonForegroundDisabled}" />
|
||||||
|
</ObjectAnimationUsingKeyFrames>
|
||||||
|
</Storyboard>
|
||||||
|
<VisualState.Setters>
|
||||||
|
<!-- DisabledVisual Should be handled by the control, not the animated icon. -->
|
||||||
|
<Setter Target="ContentPresenter.(AnimatedIcon.State)" Value="Normal" />
|
||||||
|
</VisualState.Setters>
|
||||||
|
</VisualState>
|
||||||
|
</VisualStateGroup>
|
||||||
|
</VisualStateManager.VisualStateGroups>
|
||||||
|
</ContentPresenter>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
</ResourceDictionary>
|
||||||
|
</Application.Resources>
|
||||||
|
</Application>
|
113
src/modules/Hosts/Hosts/App.xaml.cs
Normal file
113
src/modules/Hosts/Hosts/App.xaml.cs
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
// 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.IO.Abstractions;
|
||||||
|
using System.Threading;
|
||||||
|
using Hosts.Helpers;
|
||||||
|
using Hosts.Settings;
|
||||||
|
using Hosts.ViewModels;
|
||||||
|
using Hosts.Views;
|
||||||
|
using ManagedCommon;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.UI.Dispatching;
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
|
||||||
|
namespace Hosts
|
||||||
|
{
|
||||||
|
public partial class App : Application
|
||||||
|
{
|
||||||
|
private Window _window;
|
||||||
|
|
||||||
|
public IHost Host
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T GetService<T>()
|
||||||
|
where T : class
|
||||||
|
{
|
||||||
|
if ((App.Current as App)!.Host.Services.GetService(typeof(T)) is not T service)
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"{typeof(T)} needs to be registered in ConfigureServices within App.xaml.cs.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
|
||||||
|
public App()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
Host = Microsoft.Extensions.Hosting.Host.
|
||||||
|
CreateDefaultBuilder().
|
||||||
|
UseContentRoot(AppContext.BaseDirectory).
|
||||||
|
ConfigureServices((context, services) =>
|
||||||
|
{
|
||||||
|
// Core Services
|
||||||
|
services.AddSingleton<IFileSystem, FileSystem>();
|
||||||
|
services.AddSingleton<IHostsService, HostsService>();
|
||||||
|
services.AddSingleton<IUserSettings, UserSettings>();
|
||||||
|
services.AddSingleton<IElevationHelper, ElevationHelper>();
|
||||||
|
|
||||||
|
// Views and ViewModels
|
||||||
|
services.AddTransient<MainPage>();
|
||||||
|
services.AddTransient<MainViewModel>();
|
||||||
|
}).
|
||||||
|
Build();
|
||||||
|
|
||||||
|
UnhandledException += App_UnhandledException;
|
||||||
|
|
||||||
|
new Thread(() =>
|
||||||
|
{
|
||||||
|
// Delete old backups only if running elevated
|
||||||
|
if (!GetService<IElevationHelper>().IsElevated)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
GetService<IHostsService>().CleanupBackup();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.LogError("Failed to delete backup", ex);
|
||||||
|
}
|
||||||
|
}).Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnLaunched(LaunchActivatedEventArgs args)
|
||||||
|
{
|
||||||
|
var cmdArgs = Environment.GetCommandLineArgs();
|
||||||
|
if (cmdArgs?.Length > 1)
|
||||||
|
{
|
||||||
|
if (int.TryParse(cmdArgs[cmdArgs.Length - 1], out int powerToysRunnerPid))
|
||||||
|
{
|
||||||
|
Logger.LogInfo($"Hosts started from the PowerToys Runner. Runner pid={powerToysRunnerPid}");
|
||||||
|
|
||||||
|
var dispatcher = DispatcherQueue.GetForCurrentThread();
|
||||||
|
RunnerHelper.WaitForPowerToysRunner(powerToysRunnerPid, () =>
|
||||||
|
{
|
||||||
|
Logger.LogInfo("PowerToys Runner exited. Exiting Hosts");
|
||||||
|
dispatcher.TryEnqueue(App.Current.Exit);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.LogInfo($"Hosts started detached from PowerToys Runner.");
|
||||||
|
}
|
||||||
|
|
||||||
|
_window = new MainWindow();
|
||||||
|
_window.Activate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void App_UnhandledException(object sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e)
|
||||||
|
{
|
||||||
|
Logger.LogError("Unhandled exception", e.Exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
20
src/modules/Hosts/Hosts/Helpers/ElevationHelper.cs
Normal file
20
src/modules/Hosts/Hosts/Helpers/ElevationHelper.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using System.Security.Principal;
|
||||||
|
|
||||||
|
namespace Hosts.Helpers
|
||||||
|
{
|
||||||
|
public class ElevationHelper : IElevationHelper
|
||||||
|
{
|
||||||
|
private readonly bool _isElevated;
|
||||||
|
|
||||||
|
public bool IsElevated => _isElevated;
|
||||||
|
|
||||||
|
public ElevationHelper()
|
||||||
|
{
|
||||||
|
_isElevated = new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
238
src/modules/Hosts/Hosts/Helpers/HostsService.cs
Normal file
238
src/modules/Hosts/Hosts/Helpers/HostsService.cs
Normal file
@@ -0,0 +1,238 @@
|
|||||||
|
// 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;
|
||||||
|
using System.IO.Abstractions;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.NetworkInformation;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Hosts.Models;
|
||||||
|
using Hosts.Settings;
|
||||||
|
using Settings.UI.Library.Enumerations;
|
||||||
|
|
||||||
|
namespace Hosts.Helpers
|
||||||
|
{
|
||||||
|
public class HostsService : IHostsService, IDisposable
|
||||||
|
{
|
||||||
|
private const string _backupSuffix = $"_PowerToysBackup_";
|
||||||
|
|
||||||
|
private readonly SemaphoreSlim _asyncLock = new SemaphoreSlim(1, 1);
|
||||||
|
private readonly IFileSystem _fileSystem;
|
||||||
|
private readonly IUserSettings _userSettings;
|
||||||
|
private readonly IElevationHelper _elevationHelper;
|
||||||
|
private readonly IFileSystemWatcher _fileSystemWatcher;
|
||||||
|
private readonly string _hostsFilePath;
|
||||||
|
private bool _backupDone;
|
||||||
|
private bool _disposed;
|
||||||
|
|
||||||
|
public string HostsFilePath => _hostsFilePath;
|
||||||
|
|
||||||
|
public event EventHandler FileChanged;
|
||||||
|
|
||||||
|
public HostsService(
|
||||||
|
IFileSystem fileSystem,
|
||||||
|
IUserSettings userSettings,
|
||||||
|
IElevationHelper elevationHelper)
|
||||||
|
{
|
||||||
|
_fileSystem = fileSystem;
|
||||||
|
_userSettings = userSettings;
|
||||||
|
_elevationHelper = elevationHelper;
|
||||||
|
|
||||||
|
_hostsFilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), @"System32\drivers\etc\hosts");
|
||||||
|
|
||||||
|
_fileSystemWatcher = _fileSystem.FileSystemWatcher.CreateNew();
|
||||||
|
_fileSystemWatcher.Path = _fileSystem.Path.GetDirectoryName(HostsFilePath);
|
||||||
|
_fileSystemWatcher.Filter = _fileSystem.Path.GetFileName(HostsFilePath);
|
||||||
|
_fileSystemWatcher.NotifyFilter = NotifyFilters.LastWrite;
|
||||||
|
_fileSystemWatcher.Changed += FileSystemWatcher_Changed;
|
||||||
|
_fileSystemWatcher.EnableRaisingEvents = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Exists()
|
||||||
|
{
|
||||||
|
return _fileSystem.File.Exists(HostsFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<(string Unparsed, List<Entry> Entries)> ReadAsync()
|
||||||
|
{
|
||||||
|
var entries = new List<Entry>();
|
||||||
|
var unparsedBuilder = new StringBuilder();
|
||||||
|
|
||||||
|
if (!Exists())
|
||||||
|
{
|
||||||
|
return (unparsedBuilder.ToString(), entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
var lines = await _fileSystem.File.ReadAllLinesAsync(HostsFilePath);
|
||||||
|
|
||||||
|
for (var i = 0; i < lines.Length; i++)
|
||||||
|
{
|
||||||
|
var line = lines[i];
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(line))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var entry = new Entry(line);
|
||||||
|
|
||||||
|
if (entry.Valid)
|
||||||
|
{
|
||||||
|
entries.Add(entry);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (unparsedBuilder.Length > 0)
|
||||||
|
{
|
||||||
|
unparsedBuilder.Append(Environment.NewLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
unparsedBuilder.Append(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (unparsedBuilder.ToString(), entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> WriteAsync(string additionalLines, IEnumerable<Entry> entries)
|
||||||
|
{
|
||||||
|
if (!_elevationHelper.IsElevated)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var lines = new List<string>();
|
||||||
|
|
||||||
|
if (entries.Any())
|
||||||
|
{
|
||||||
|
var addressPadding = entries.Max(e => e.Address.Length) + 1;
|
||||||
|
var hostsPadding = entries.Max(e => e.Hosts.Length) + 1;
|
||||||
|
var anyDisabled = entries.Any(e => !e.Active);
|
||||||
|
|
||||||
|
foreach (var e in entries)
|
||||||
|
{
|
||||||
|
var lineBuilder = new StringBuilder();
|
||||||
|
|
||||||
|
if (!e.Valid)
|
||||||
|
{
|
||||||
|
lineBuilder.Append(e.GetLine());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!e.Active)
|
||||||
|
{
|
||||||
|
lineBuilder.Append('#').Append(' ');
|
||||||
|
}
|
||||||
|
else if (anyDisabled)
|
||||||
|
{
|
||||||
|
lineBuilder.Append(' ').Append(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
lineBuilder.Append(e.Address.PadRight(addressPadding));
|
||||||
|
lineBuilder.Append(string.Join(' ', e.Hosts).PadRight(hostsPadding));
|
||||||
|
|
||||||
|
if (e.Comment != string.Empty)
|
||||||
|
{
|
||||||
|
lineBuilder.Append('#').Append(' ');
|
||||||
|
lineBuilder.Append(e.Comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.Add(lineBuilder.ToString().TrimEnd());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(additionalLines))
|
||||||
|
{
|
||||||
|
if (_userSettings.AdditionalLinesPosition == AdditionalLinesPosition.Top)
|
||||||
|
{
|
||||||
|
lines.Insert(0, additionalLines);
|
||||||
|
}
|
||||||
|
else if (_userSettings.AdditionalLinesPosition == AdditionalLinesPosition.Bottom)
|
||||||
|
{
|
||||||
|
lines.Add(additionalLines);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _asyncLock.WaitAsync();
|
||||||
|
_fileSystemWatcher.EnableRaisingEvents = false;
|
||||||
|
|
||||||
|
if (!_backupDone && Exists())
|
||||||
|
{
|
||||||
|
_fileSystem.File.Copy(HostsFilePath, HostsFilePath + _backupSuffix + DateTime.Now.ToString("yyyyMMddHHmmss", CultureInfo.InvariantCulture));
|
||||||
|
_backupDone = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _fileSystem.File.WriteAllLinesAsync(HostsFilePath, lines);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.LogError("Failed to write hosts file", ex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_fileSystemWatcher.EnableRaisingEvents = true;
|
||||||
|
_asyncLock.Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> PingAsync(string address)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var ping = new Ping();
|
||||||
|
var reply = await ping.SendPingAsync(address, 4000); // 4000 is the default ping timeout for ping.exe
|
||||||
|
return reply.Status == IPStatus.Success;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CleanupBackup()
|
||||||
|
{
|
||||||
|
Directory.GetFiles(Path.GetDirectoryName(HostsFilePath), $"*{_backupSuffix}*")
|
||||||
|
.Select(f => new FileInfo(f))
|
||||||
|
.Where(f => f.CreationTime < DateTime.Now.AddDays(-15))
|
||||||
|
.ToList()
|
||||||
|
.ForEach(f => f.Delete());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(disposing: true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FileSystemWatcher_Changed(object sender, FileSystemEventArgs e)
|
||||||
|
{
|
||||||
|
_fileSystemWatcher.EnableRaisingEvents = false;
|
||||||
|
FileChanged?.Invoke(this, EventArgs.Empty);
|
||||||
|
_fileSystemWatcher.EnableRaisingEvents = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (!_disposed)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
_asyncLock.Dispose();
|
||||||
|
_disposed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
src/modules/Hosts/Hosts/Helpers/IElevationHelper.cs
Normal file
11
src/modules/Hosts/Hosts/Helpers/IElevationHelper.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
// 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 Hosts.Helpers
|
||||||
|
{
|
||||||
|
public interface IElevationHelper
|
||||||
|
{
|
||||||
|
bool IsElevated { get; }
|
||||||
|
}
|
||||||
|
}
|
26
src/modules/Hosts/Hosts/Helpers/IHostsService.cs
Normal file
26
src/modules/Hosts/Hosts/Helpers/IHostsService.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Hosts.Models;
|
||||||
|
|
||||||
|
namespace Hosts.Helpers
|
||||||
|
{
|
||||||
|
public interface IHostsService : IDisposable
|
||||||
|
{
|
||||||
|
string HostsFilePath { get; }
|
||||||
|
|
||||||
|
event EventHandler FileChanged;
|
||||||
|
|
||||||
|
Task<(string Unparsed, List<Entry> Entries)> ReadAsync();
|
||||||
|
|
||||||
|
Task<bool> WriteAsync(string additionalLines, IEnumerable<Entry> entries);
|
||||||
|
|
||||||
|
Task<bool> PingAsync(string address);
|
||||||
|
|
||||||
|
void CleanupBackup();
|
||||||
|
}
|
||||||
|
}
|
80
src/modules/Hosts/Hosts/Helpers/Logger.cs
Normal file
80
src/modules/Hosts/Hosts/Helpers/Logger.cs
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
// 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.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.Abstractions;
|
||||||
|
using interop;
|
||||||
|
|
||||||
|
namespace Hosts.Helpers
|
||||||
|
{
|
||||||
|
// TODO: use centralized logger https://github.com/microsoft/PowerToys/issues/19650
|
||||||
|
public static class Logger
|
||||||
|
{
|
||||||
|
private static readonly IFileSystem _fileSystem = new FileSystem();
|
||||||
|
private static readonly string ApplicationLogPath = Path.Combine(Constants.AppDataPath(), "Hosts\\Logs");
|
||||||
|
|
||||||
|
static Logger()
|
||||||
|
{
|
||||||
|
if (!_fileSystem.Directory.Exists(ApplicationLogPath))
|
||||||
|
{
|
||||||
|
_fileSystem.Directory.CreateDirectory(ApplicationLogPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Using InvariantCulture since this is used for a log file name
|
||||||
|
var logFilePath = _fileSystem.Path.Combine(ApplicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".txt");
|
||||||
|
|
||||||
|
Trace.Listeners.Add(new TextWriterTraceListener(logFilePath));
|
||||||
|
|
||||||
|
Trace.AutoFlush = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void LogError(string message)
|
||||||
|
{
|
||||||
|
Log(message, "ERROR");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void LogError(string message, Exception ex)
|
||||||
|
{
|
||||||
|
Log(
|
||||||
|
message + Environment.NewLine +
|
||||||
|
ex?.Message + Environment.NewLine +
|
||||||
|
"Inner exception: " + Environment.NewLine +
|
||||||
|
ex?.InnerException?.Message + Environment.NewLine +
|
||||||
|
"Stack trace: " + Environment.NewLine +
|
||||||
|
ex?.StackTrace,
|
||||||
|
"ERROR");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void LogWarning(string message)
|
||||||
|
{
|
||||||
|
Log(message, "WARNING");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void LogInfo(string message)
|
||||||
|
{
|
||||||
|
Log(message, "INFO");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Log(string message, string type)
|
||||||
|
{
|
||||||
|
Trace.WriteLine(type + ": " + DateTime.Now.TimeOfDay);
|
||||||
|
Trace.Indent();
|
||||||
|
Trace.WriteLine(GetCallerInfo());
|
||||||
|
Trace.WriteLine(message);
|
||||||
|
Trace.Unindent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetCallerInfo()
|
||||||
|
{
|
||||||
|
StackTrace stackTrace = new StackTrace();
|
||||||
|
|
||||||
|
var methodName = stackTrace.GetFrame(3)?.GetMethod();
|
||||||
|
var className = methodName?.DeclaringType.Name;
|
||||||
|
return "[Method]: " + methodName?.Name + " [Class]: " + className;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
24
src/modules/Hosts/Hosts/Helpers/NativeMethods.cs
Normal file
24
src/modules/Hosts/Hosts/Helpers/NativeMethods.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
namespace Hosts.Helpers
|
||||||
|
{
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
internal class NativeMethods
|
||||||
|
{
|
||||||
|
[DllImport("user32.dll", SetLastError = true)]
|
||||||
|
internal static extern IntPtr SetForegroundWindow(IntPtr hWnd);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
internal static extern IntPtr GetForegroundWindow();
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
internal static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr processId);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
internal static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
|
||||||
|
}
|
||||||
|
}
|
14
src/modules/Hosts/Hosts/Helpers/StringHelper.cs
Normal file
14
src/modules/Hosts/Hosts/Helpers/StringHelper.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
// 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 Hosts.Helpers
|
||||||
|
{
|
||||||
|
public static class StringHelper
|
||||||
|
{
|
||||||
|
public static string GetHostsWithComment(string hosts, string comment)
|
||||||
|
{
|
||||||
|
return string.IsNullOrWhiteSpace(comment) ? hosts : string.Concat(hosts, " - ", comment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
60
src/modules/Hosts/Hosts/Helpers/ValidationHelper.cs
Normal file
60
src/modules/Hosts/Hosts/Helpers/ValidationHelper.cs
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
// 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.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace Hosts.Helpers
|
||||||
|
{
|
||||||
|
public static class ValidationHelper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the address is a valid IPv4
|
||||||
|
/// </summary>
|
||||||
|
public static bool ValidIPv4(string address)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(address))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var regex = new Regex("^(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$");
|
||||||
|
return regex.IsMatch(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the address is a valid IPv6
|
||||||
|
/// </summary>
|
||||||
|
public static bool ValidIPv6(string address)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(address))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var regex = new Regex("^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$");
|
||||||
|
return regex.IsMatch(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the hosts are valid
|
||||||
|
/// </summary>
|
||||||
|
public static bool ValidHosts(string hosts)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(hosts))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var host in hosts.Split(' '))
|
||||||
|
{
|
||||||
|
if (System.Uri.CheckHostName(host) == System.UriHostNameType.Unknown)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
55
src/modules/Hosts/Hosts/Hosts.csproj
Normal file
55
src/modules/Hosts/Hosts/Hosts.csproj
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<Import Project="..\..\..\Version.props" />
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>WinExe</OutputType>
|
||||||
|
<TargetFramework>net6.0-windows10.0.19041.0</TargetFramework>
|
||||||
|
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
|
||||||
|
<RootNamespace>Hosts</RootNamespace>
|
||||||
|
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||||
|
<RuntimeIdentifiers>win10-x64;win10-arm64</RuntimeIdentifiers>
|
||||||
|
<UseWinUI>true</UseWinUI>
|
||||||
|
<EnablePreviewMsixTooling>true</EnablePreviewMsixTooling>
|
||||||
|
<WindowsPackageType>None</WindowsPackageType>
|
||||||
|
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||||
|
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||||
|
<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
|
||||||
|
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\modules\$(AssemblyName)</OutputPath>
|
||||||
|
<IntermediateOutputPath>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(AssemblyName)</IntermediateOutputPath>
|
||||||
|
<RootNamespace>Hosts</RootNamespace>
|
||||||
|
<AssemblyName>PowerToys.Hosts</AssemblyName>
|
||||||
|
<DefineConstants>DISABLE_XAML_GENERATED_MAIN,TRACE</DefineConstants>
|
||||||
|
<ApplicationIcon>icon.ico</ApplicationIcon>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="icon.ico" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.0.0" />
|
||||||
|
<PackageReference Include="CommunityToolkit.WinUI.UI.Controls" Version="7.1.2" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.1" />
|
||||||
|
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.1.5" />
|
||||||
|
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22000.197" />
|
||||||
|
<PackageReference Include="Microsoft.Xaml.Behaviors.WinUI.Managed" Version="2.0.8" />
|
||||||
|
<PackageReference Include="System.IO.Abstractions" Version="12.2.5" />
|
||||||
|
<PackageReference Include="WinUIEx" Version="1.8.0" />
|
||||||
|
<Manifest Include="$(ApplicationManifest)" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup Condition="'$(DisableMsixProjectCapabilityAddedByProject)'!='true' and '$(EnablePreviewMsixTooling)'=='true'">
|
||||||
|
<ProjectCapability Include="Msix" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\..\common\Common.UI\Common.UI.csproj" />
|
||||||
|
<ProjectReference Include="..\..\..\settings-ui\Settings.UI.Library\Settings.UI.Library.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Page Update="Views\MainPage.xaml">
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
</Page>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
37
src/modules/Hosts/Hosts/MainWindow.xaml
Normal file
37
src/modules/Hosts/Hosts/MainWindow.xaml
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<winuiex:WindowEx
|
||||||
|
x:Uid="Window"
|
||||||
|
x:Class="Hosts.MainWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:local="using:Hosts"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:views="using:Hosts.Views"
|
||||||
|
xmlns:winuiex="using:WinUIEx"
|
||||||
|
Width="680"
|
||||||
|
MinWidth="480"
|
||||||
|
MinHeight="320"
|
||||||
|
mc:Ignorable="d">
|
||||||
|
<winuiex:WindowEx.Backdrop>
|
||||||
|
<winuiex:MicaSystemBackdrop />
|
||||||
|
</winuiex:WindowEx.Backdrop>
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="32" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid x:Name="titleBar">
|
||||||
|
<StackPanel
|
||||||
|
Margin="16,8,8,8"
|
||||||
|
VerticalAlignment="Top"
|
||||||
|
Orientation="Horizontal">
|
||||||
|
<TextBlock FontFamily="{ThemeResource SymbolThemeFontFamily}" Text="" />
|
||||||
|
<TextBlock
|
||||||
|
Margin="12,0,0,0"
|
||||||
|
Style="{StaticResource CaptionTextBlockStyle}"
|
||||||
|
Text="Hosts File Editor" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
<views:MainPage Grid.Row="1" />
|
||||||
|
</Grid>
|
||||||
|
</winuiex:WindowEx>
|
71
src/modules/Hosts/Hosts/MainWindow.xaml.cs
Normal file
71
src/modules/Hosts/Hosts/MainWindow.xaml.cs
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
// 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 Hosts.Helpers;
|
||||||
|
using ManagedCommon;
|
||||||
|
using Microsoft.UI;
|
||||||
|
using Microsoft.UI.Windowing;
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using WinUIEx;
|
||||||
|
|
||||||
|
namespace Hosts
|
||||||
|
{
|
||||||
|
public sealed partial class MainWindow : WindowEx
|
||||||
|
{
|
||||||
|
public MainWindow()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
if (AppWindowTitleBar.IsCustomizationSupported())
|
||||||
|
{
|
||||||
|
SetTitleBar();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
titleBar.Visibility = Visibility.Collapsed;
|
||||||
|
|
||||||
|
// Set window icon
|
||||||
|
var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
|
||||||
|
WindowId windowId = Win32Interop.GetWindowIdFromWindow(hWnd);
|
||||||
|
AppWindow appWindow = AppWindow.GetFromWindowId(windowId);
|
||||||
|
appWindow.SetIcon("icon.ico");
|
||||||
|
|
||||||
|
if (ThemeHelpers.GetAppTheme() == AppTheme.Dark)
|
||||||
|
{
|
||||||
|
ThemeHelpers.SetImmersiveDarkMode(hWnd, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BringToForeground();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetTitleBar()
|
||||||
|
{
|
||||||
|
AppWindow window = this.GetAppWindow();
|
||||||
|
window.TitleBar.ExtendsContentIntoTitleBar = true;
|
||||||
|
window.TitleBar.ButtonBackgroundColor = Colors.Transparent;
|
||||||
|
SetTitleBar(titleBar);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BringToForeground()
|
||||||
|
{
|
||||||
|
var handle = this.GetWindowHandle();
|
||||||
|
var fgHandle = NativeMethods.GetForegroundWindow();
|
||||||
|
|
||||||
|
var threadId1 = NativeMethods.GetWindowThreadProcessId(handle, System.IntPtr.Zero);
|
||||||
|
var threadId2 = NativeMethods.GetWindowThreadProcessId(fgHandle, System.IntPtr.Zero);
|
||||||
|
|
||||||
|
if (threadId1 != threadId2)
|
||||||
|
{
|
||||||
|
NativeMethods.AttachThreadInput(threadId1, threadId2, true);
|
||||||
|
NativeMethods.SetForegroundWindow(handle);
|
||||||
|
NativeMethods.AttachThreadInput(threadId1, threadId2, false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NativeMethods.SetForegroundWindow(handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
149
src/modules/Hosts/Hosts/Models/Entry.cs
Normal file
149
src/modules/Hosts/Hosts/Models/Entry.cs
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
// 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.Net;
|
||||||
|
using System.Text;
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using Hosts.Helpers;
|
||||||
|
|
||||||
|
namespace Hosts.Models
|
||||||
|
{
|
||||||
|
public partial class Entry : ObservableObject
|
||||||
|
{
|
||||||
|
private string _line;
|
||||||
|
|
||||||
|
private string _address;
|
||||||
|
|
||||||
|
public string Address
|
||||||
|
{
|
||||||
|
get => _address;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
SetProperty(ref _address, value);
|
||||||
|
OnPropertyChanged(nameof(Valid));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string _hosts;
|
||||||
|
|
||||||
|
public string Hosts
|
||||||
|
{
|
||||||
|
get => _hosts;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
SetProperty(ref _hosts, value);
|
||||||
|
OnPropertyChanged(nameof(Valid));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _comment;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool _active;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool? _ping;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool _pinging;
|
||||||
|
|
||||||
|
public bool Valid => ValidationHelper.ValidHosts(_hosts) && (ValidationHelper.ValidIPv4(_address) || ValidationHelper.ValidIPv6(_address));
|
||||||
|
|
||||||
|
public Entry()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public Entry(string line)
|
||||||
|
{
|
||||||
|
_line = line.Trim();
|
||||||
|
Parse();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Entry(string address, string hosts, string comment, bool active)
|
||||||
|
{
|
||||||
|
Address = address.Trim();
|
||||||
|
Hosts = hosts.Trim();
|
||||||
|
Comment = comment.Trim();
|
||||||
|
Active = active;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Parse()
|
||||||
|
{
|
||||||
|
Active = !_line.StartsWith("#", StringComparison.InvariantCultureIgnoreCase);
|
||||||
|
|
||||||
|
var lineSplit = _line.TrimStart(' ', '#').Split('#');
|
||||||
|
|
||||||
|
if (lineSplit.Length == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var addressHost = lineSplit[0];
|
||||||
|
|
||||||
|
var addressHostSplit = addressHost.Split(' ', '\t');
|
||||||
|
var hostsBuilder = new StringBuilder();
|
||||||
|
var commentBuilder = new StringBuilder();
|
||||||
|
|
||||||
|
for (var i = 0; i < addressHostSplit.Length; i++)
|
||||||
|
{
|
||||||
|
var element = addressHostSplit[i].Trim();
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(element))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Address == null)
|
||||||
|
{
|
||||||
|
if (IPAddress.TryParse(element, out var _) && (element.Contains(':') || element.Contains('.')))
|
||||||
|
{
|
||||||
|
Address = element;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (hostsBuilder.Length > 0)
|
||||||
|
{
|
||||||
|
hostsBuilder.Append(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
hostsBuilder.Append(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Hosts = hostsBuilder.ToString();
|
||||||
|
|
||||||
|
for (var i = 1; i < lineSplit.Length; i++)
|
||||||
|
{
|
||||||
|
if (commentBuilder.Length > 0)
|
||||||
|
{
|
||||||
|
commentBuilder.Append('#');
|
||||||
|
}
|
||||||
|
|
||||||
|
commentBuilder.Append(lineSplit[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Comment = commentBuilder.ToString().Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Entry Clone()
|
||||||
|
{
|
||||||
|
return new Entry
|
||||||
|
{
|
||||||
|
_line = _line,
|
||||||
|
Address = Address,
|
||||||
|
Hosts = Hosts,
|
||||||
|
Comment = Comment,
|
||||||
|
Active = Active,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetLine()
|
||||||
|
{
|
||||||
|
return _line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
38
src/modules/Hosts/Hosts/Program.cs
Normal file
38
src/modules/Hosts/Hosts/Program.cs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using Hosts.Helpers;
|
||||||
|
using Microsoft.UI.Dispatching;
|
||||||
|
using Microsoft.Windows.AppLifecycle;
|
||||||
|
|
||||||
|
namespace Hosts
|
||||||
|
{
|
||||||
|
public static class Program
|
||||||
|
{
|
||||||
|
[STAThread]
|
||||||
|
public static void Main(string[] args)
|
||||||
|
{
|
||||||
|
WinRT.ComWrappersSupport.InitializeComWrappers();
|
||||||
|
var instanceKey = AppInstance.FindOrRegisterForKey("PowerToys_Hosts_Instance");
|
||||||
|
|
||||||
|
if (instanceKey.IsCurrent)
|
||||||
|
{
|
||||||
|
Microsoft.UI.Xaml.Application.Start((p) =>
|
||||||
|
{
|
||||||
|
var context = new DispatcherQueueSynchronizationContext(DispatcherQueue.GetForCurrentThread());
|
||||||
|
SynchronizationContext.SetSynchronizationContext(context);
|
||||||
|
_ = new App();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.LogWarning("Another instance of Hosts running. Exiting Hosts");
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
src/modules/Hosts/Hosts/Settings/IUserSettings.cs
Normal file
15
src/modules/Hosts/Hosts/Settings/IUserSettings.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
// 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 Settings.UI.Library.Enumerations;
|
||||||
|
|
||||||
|
namespace Hosts.Settings
|
||||||
|
{
|
||||||
|
public interface IUserSettings
|
||||||
|
{
|
||||||
|
public bool ShowStartupWarning { get; }
|
||||||
|
|
||||||
|
public AdditionalLinesPosition AdditionalLinesPosition { get; }
|
||||||
|
}
|
||||||
|
}
|
81
src/modules/Hosts/Hosts/Settings/UserSettings.cs
Normal file
81
src/modules/Hosts/Hosts/Settings/UserSettings.cs
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
// 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.IO.Abstractions;
|
||||||
|
using System.Threading;
|
||||||
|
using Microsoft.PowerToys.Settings.UI.Library;
|
||||||
|
using Microsoft.PowerToys.Settings.UI.Library.Utilities;
|
||||||
|
using Settings.UI.Library.Enumerations;
|
||||||
|
|
||||||
|
namespace Hosts.Settings
|
||||||
|
{
|
||||||
|
public class UserSettings : IUserSettings
|
||||||
|
{
|
||||||
|
private const string HostsModuleName = "Hosts";
|
||||||
|
private const int MaxNumberOfRetry = 5;
|
||||||
|
|
||||||
|
private readonly ISettingsUtils _settingsUtils;
|
||||||
|
private readonly IFileSystemWatcher _watcher;
|
||||||
|
private readonly object _loadingSettingsLock = new object();
|
||||||
|
|
||||||
|
public bool ShowStartupWarning { get; private set; }
|
||||||
|
|
||||||
|
public AdditionalLinesPosition AdditionalLinesPosition { get; private set; }
|
||||||
|
|
||||||
|
public UserSettings()
|
||||||
|
{
|
||||||
|
_settingsUtils = new SettingsUtils();
|
||||||
|
ShowStartupWarning = true;
|
||||||
|
AdditionalLinesPosition = AdditionalLinesPosition.Top;
|
||||||
|
|
||||||
|
LoadSettingsFromJson();
|
||||||
|
|
||||||
|
_watcher = Helper.GetFileWatcher(HostsModuleName, "settings.json", () => LoadSettingsFromJson());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadSettingsFromJson()
|
||||||
|
{
|
||||||
|
lock (_loadingSettingsLock)
|
||||||
|
{
|
||||||
|
var retry = true;
|
||||||
|
var retryCount = 0;
|
||||||
|
|
||||||
|
while (retry)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
retryCount++;
|
||||||
|
|
||||||
|
if (!_settingsUtils.SettingsExists(HostsModuleName))
|
||||||
|
{
|
||||||
|
Logger.LogInfo("Hosts settings.json was missing, creating a new one");
|
||||||
|
var defaultSettings = new HostsSettings();
|
||||||
|
defaultSettings.Save(_settingsUtils);
|
||||||
|
}
|
||||||
|
|
||||||
|
var settings = _settingsUtils.GetSettingsOrDefault<HostsSettings>(HostsModuleName);
|
||||||
|
if (settings != null)
|
||||||
|
{
|
||||||
|
ShowStartupWarning = settings.Properties.ShowStartupWarning;
|
||||||
|
AdditionalLinesPosition = settings.Properties.AdditionalLinesPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
retry = false;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
if (retryCount > MaxNumberOfRetry)
|
||||||
|
{
|
||||||
|
retry = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.LogError("Failed to read changed settings", ex);
|
||||||
|
Thread.Sleep(500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
261
src/modules/Hosts/Hosts/Strings/en-us/Resources.resw
Normal file
261
src/modules/Hosts/Hosts/Strings/en-us/Resources.resw
Normal file
@@ -0,0 +1,261 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<data name="Active.Header" xml:space="preserve">
|
||||||
|
<value>Active</value>
|
||||||
|
</data>
|
||||||
|
<data name="ActiveToggle.[using:Microsoft.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
|
||||||
|
<value>Active</value>
|
||||||
|
</data>
|
||||||
|
<data name="AddBtn" xml:space="preserve">
|
||||||
|
<value>Add</value>
|
||||||
|
</data>
|
||||||
|
<data name="AddEntry.Text" xml:space="preserve">
|
||||||
|
<value>New entry</value>
|
||||||
|
</data>
|
||||||
|
<data name="AddEntryBtn.[using:Microsoft.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
|
||||||
|
<value>New entry</value>
|
||||||
|
</data>
|
||||||
|
<data name="AdditionalLinesBtn.[using:Microsoft.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
|
||||||
|
<value>Additional lines</value>
|
||||||
|
</data>
|
||||||
|
<data name="AdditionalLinesBtn.[using:Microsoft.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
|
||||||
|
<value>Additional lines</value>
|
||||||
|
</data>
|
||||||
|
<data name="AdditionalLinesDialog.CloseButtonText" xml:space="preserve">
|
||||||
|
<value>Cancel</value>
|
||||||
|
</data>
|
||||||
|
<data name="AdditionalLinesDialog.PrimaryButtonText" xml:space="preserve">
|
||||||
|
<value>Save</value>
|
||||||
|
</data>
|
||||||
|
<data name="AdditionalLinesDialog.Title" xml:space="preserve">
|
||||||
|
<value>Additional lines</value>
|
||||||
|
</data>
|
||||||
|
<data name="AddNewEntryDialog_Title" xml:space="preserve">
|
||||||
|
<value>Add new entry</value>
|
||||||
|
</data>
|
||||||
|
<data name="Address.Header" xml:space="preserve">
|
||||||
|
<value>Address</value>
|
||||||
|
<comment>"Address" refers to the IP address of the entry</comment>
|
||||||
|
</data>
|
||||||
|
<data name="AddressFilter.Header" xml:space="preserve">
|
||||||
|
<value>Address</value>
|
||||||
|
<comment>"Address" refers to the IP address of the entry</comment>
|
||||||
|
</data>
|
||||||
|
<data name="ClearFiltersBtn.Content" xml:space="preserve">
|
||||||
|
<value>Clear filters</value>
|
||||||
|
</data>
|
||||||
|
<data name="ClearFiltersBtn.[using:Microsoft.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
|
||||||
|
<value>Clear filters</value>
|
||||||
|
</data>
|
||||||
|
<data name="Comment.Header" xml:space="preserve">
|
||||||
|
<value>Comment</value>
|
||||||
|
<comment>"Comment" refers to the comment of the entry</comment>
|
||||||
|
</data>
|
||||||
|
<data name="CommentFilter.Header" xml:space="preserve">
|
||||||
|
<value>Comment</value>
|
||||||
|
<comment>"Comment" refers to the comment of the entry</comment>
|
||||||
|
</data>
|
||||||
|
<data name="Delete.Text" xml:space="preserve">
|
||||||
|
<value>Delete</value>
|
||||||
|
</data>
|
||||||
|
<data name="DeleteDialog.CloseButtonText" xml:space="preserve">
|
||||||
|
<value>No</value>
|
||||||
|
</data>
|
||||||
|
<data name="DeleteDialog.PrimaryButtonText" xml:space="preserve">
|
||||||
|
<value>Yes</value>
|
||||||
|
</data>
|
||||||
|
<data name="DeleteDialogAreYouSure.Text" xml:space="preserve">
|
||||||
|
<value>Are you sure you want to delete this entry?</value>
|
||||||
|
</data>
|
||||||
|
<data name="Entries.[using:Microsoft.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
|
||||||
|
<value>Entries</value>
|
||||||
|
</data>
|
||||||
|
<data name="EntryDialog.CloseButtonText" xml:space="preserve">
|
||||||
|
<value>Cancel</value>
|
||||||
|
</data>
|
||||||
|
<data name="FileChanged.Message" xml:space="preserve">
|
||||||
|
<value>Hosts file has changed by another application. Do you want to reload it?</value>
|
||||||
|
<comment>"Hosts" refers to the system hosts file, do not loc</comment>
|
||||||
|
</data>
|
||||||
|
<data name="FileSaveError.Message" xml:space="preserve">
|
||||||
|
<value>Failed to save hosts file.</value>
|
||||||
|
<comment>"Hosts" refers to the system hosts file, do not loc</comment>
|
||||||
|
</data>
|
||||||
|
<data name="FilterBtn.[using:Microsoft.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
|
||||||
|
<value>Filters</value>
|
||||||
|
</data>
|
||||||
|
<data name="FilterBtn.[using:Microsoft.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
|
||||||
|
<value>Filters</value>
|
||||||
|
</data>
|
||||||
|
<data name="Hosts.Header" xml:space="preserve">
|
||||||
|
<value>Hosts</value>
|
||||||
|
<comment>"Hosts" refers to the system hosts file, do not loc</comment>
|
||||||
|
</data>
|
||||||
|
<data name="Hosts.[using:Microsoft.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
|
||||||
|
<value>Seperate multiple hosts by space (e.g. server server.local).</value>
|
||||||
|
<comment>Do not localize "server" and "server.local"</comment>
|
||||||
|
</data>
|
||||||
|
<data name="HostsFilter.Header" xml:space="preserve">
|
||||||
|
<value>Hosts</value>
|
||||||
|
<comment>"Hosts" refers to the system hosts file, do not loc</comment>
|
||||||
|
</data>
|
||||||
|
<data name="MoveDown.Text" xml:space="preserve">
|
||||||
|
<value>Move down</value>
|
||||||
|
</data>
|
||||||
|
<data name="MoveUp.Text" xml:space="preserve">
|
||||||
|
<value>Move up</value>
|
||||||
|
</data>
|
||||||
|
<data name="Ping.Text" xml:space="preserve">
|
||||||
|
<value>Ping</value>
|
||||||
|
<comment>"Ping" refers to the command-line utility, do not loc</comment>
|
||||||
|
</data>
|
||||||
|
<data name="Reload.Content" xml:space="preserve">
|
||||||
|
<value>Reload</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsBtn.[using:Microsoft.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
|
||||||
|
<value>Settings</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsBtn.[using:Microsoft.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
|
||||||
|
<value>Settings</value>
|
||||||
|
</data>
|
||||||
|
<data name="UpdateBtn" xml:space="preserve">
|
||||||
|
<value>Update</value>
|
||||||
|
</data>
|
||||||
|
<data name="UpdateEntry_Title" xml:space="preserve">
|
||||||
|
<value>Update the entry</value>
|
||||||
|
</data>
|
||||||
|
<data name="WarningDialog_AcceptBtn" xml:space="preserve">
|
||||||
|
<value>Accept</value>
|
||||||
|
</data>
|
||||||
|
<data name="WarningDialog_QuitBtn" xml:space="preserve">
|
||||||
|
<value>Quit</value>
|
||||||
|
</data>
|
||||||
|
<data name="WarningDialog_Text" xml:space="preserve">
|
||||||
|
<value>Altering hosts file has direct real world impact of how this computer resolves domain names.</value>
|
||||||
|
<comment>"Hosts" refers to the system hosts file, do not loc</comment>
|
||||||
|
</data>
|
||||||
|
<data name="WarningDialog_Title" xml:space="preserve">
|
||||||
|
<value>Warning</value>
|
||||||
|
</data>
|
||||||
|
<data name="Window.Title" xml:space="preserve">
|
||||||
|
<value>Hosts File Editor</value>
|
||||||
|
<comment>"Hosts File Editor" is the name of the utility. "Hosts" refers to the system hosts file, do not loc</comment>
|
||||||
|
</data>
|
||||||
|
</root>
|
242
src/modules/Hosts/Hosts/ViewModels/MainViewModel.cs
Normal file
242
src/modules/Hosts/Hosts/ViewModels/MainViewModel.cs
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
// 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.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using Common.UI;
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
using CommunityToolkit.WinUI;
|
||||||
|
using Hosts.Helpers;
|
||||||
|
using Hosts.Models;
|
||||||
|
using Hosts.Settings;
|
||||||
|
using Microsoft.UI.Dispatching;
|
||||||
|
|
||||||
|
namespace Hosts.ViewModels
|
||||||
|
{
|
||||||
|
public partial class MainViewModel : ObservableObject, IDisposable
|
||||||
|
{
|
||||||
|
private readonly IHostsService _hostsService;
|
||||||
|
private readonly IUserSettings _userSettings;
|
||||||
|
private readonly DispatcherQueue _dispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
||||||
|
private bool _disposed;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private Entry _selected;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool _error;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool _fileChanged;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _addressFilter;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _hostsFilter;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _commentFilter;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool _filtered;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _additionalLines;
|
||||||
|
|
||||||
|
private ObservableCollection<Entry> _entries;
|
||||||
|
|
||||||
|
public ObservableCollection<Entry> Entries
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_filtered)
|
||||||
|
{
|
||||||
|
var filter = _entries.AsEnumerable();
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(_addressFilter))
|
||||||
|
{
|
||||||
|
filter = filter.Where(e => e.Address.Contains(_addressFilter, StringComparison.OrdinalIgnoreCase));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(_hostsFilter))
|
||||||
|
{
|
||||||
|
filter = filter.Where(e => e.Hosts.Contains(_hostsFilter, StringComparison.OrdinalIgnoreCase));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(_commentFilter))
|
||||||
|
{
|
||||||
|
filter = filter.Where(e => e.Comment.Contains(_commentFilter, StringComparison.OrdinalIgnoreCase));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ObservableCollection<Entry>(filter);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return _entries;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_entries = value;
|
||||||
|
OnPropertyChanged(nameof(Entries));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ICommand ReadHostsCommand => new RelayCommand(ReadHosts);
|
||||||
|
|
||||||
|
public ICommand ApplyFiltersCommand => new RelayCommand(ApplyFilters);
|
||||||
|
|
||||||
|
public ICommand ClearFiltersCommand => new RelayCommand(ClearFilters);
|
||||||
|
|
||||||
|
public ICommand OpenSettingsCommand => new RelayCommand(OpenSettings);
|
||||||
|
|
||||||
|
public MainViewModel(
|
||||||
|
IHostsService hostService,
|
||||||
|
IUserSettings userSettings)
|
||||||
|
{
|
||||||
|
_hostsService = hostService;
|
||||||
|
_userSettings = userSettings;
|
||||||
|
|
||||||
|
_hostsService.FileChanged += (s, e) =>
|
||||||
|
{
|
||||||
|
_dispatcherQueue.TryEnqueue(() => FileChanged = true);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(Entry entry)
|
||||||
|
{
|
||||||
|
entry.PropertyChanged += Entry_PropertyChanged;
|
||||||
|
_entries.Add(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update(int index, Entry entry)
|
||||||
|
{
|
||||||
|
var existingEntry = _entries.ElementAt(index);
|
||||||
|
existingEntry.Address = entry.Address;
|
||||||
|
existingEntry.Comment = entry.Comment;
|
||||||
|
existingEntry.Hosts = entry.Hosts;
|
||||||
|
existingEntry.Active = entry.Active;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeleteSelected()
|
||||||
|
{
|
||||||
|
_entries.Remove(Selected);
|
||||||
|
if (Filtered)
|
||||||
|
{
|
||||||
|
OnPropertyChanged(nameof(Entries));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateAdditionalLines(string lines)
|
||||||
|
{
|
||||||
|
_additionalLines = lines;
|
||||||
|
|
||||||
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
var error = !await _hostsService.WriteAsync(_additionalLines, _entries);
|
||||||
|
await _dispatcherQueue.EnqueueAsync(() => Error = error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReadHosts()
|
||||||
|
{
|
||||||
|
FileChanged = false;
|
||||||
|
|
||||||
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
(_additionalLines, var entries) = await _hostsService.ReadAsync();
|
||||||
|
|
||||||
|
await _dispatcherQueue.EnqueueAsync(() =>
|
||||||
|
{
|
||||||
|
Entries = new ObservableCollection<Entry>(entries);
|
||||||
|
|
||||||
|
foreach (var e in _entries)
|
||||||
|
{
|
||||||
|
e.PropertyChanged += Entry_PropertyChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
_entries.CollectionChanged += Entries_CollectionChanged;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ApplyFilters()
|
||||||
|
{
|
||||||
|
if (_entries != null)
|
||||||
|
{
|
||||||
|
Filtered = !string.IsNullOrWhiteSpace(_addressFilter) || !string.IsNullOrWhiteSpace(_hostsFilter) || !string.IsNullOrWhiteSpace(_commentFilter);
|
||||||
|
OnPropertyChanged(nameof(Entries));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClearFilters()
|
||||||
|
{
|
||||||
|
AddressFilter = null;
|
||||||
|
HostsFilter = null;
|
||||||
|
CommentFilter = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task PingSelectedAsync()
|
||||||
|
{
|
||||||
|
var selected = _selected;
|
||||||
|
selected.Ping = null;
|
||||||
|
selected.Pinging = true;
|
||||||
|
selected.Ping = await _hostsService.PingAsync(_selected.Address);
|
||||||
|
selected.Pinging = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OpenSettings()
|
||||||
|
{
|
||||||
|
SettingsDeepLink.OpenSettings(SettingsDeepLink.SettingsWindow.Hosts);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(disposing: true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Entry_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
// Ping should't trigger a file save
|
||||||
|
if (e.PropertyName == nameof(Entry.Ping) || e.PropertyName == nameof(Entry.Pinging))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
var error = !await _hostsService.WriteAsync(_additionalLines, _entries);
|
||||||
|
await _dispatcherQueue.EnqueueAsync(() => Error = error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Entries_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
var error = !await _hostsService.WriteAsync(_additionalLines, _entries);
|
||||||
|
await _dispatcherQueue.EnqueueAsync(() => Error = error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (!_disposed)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
_hostsService?.Dispose();
|
||||||
|
_disposed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
366
src/modules/Hosts/Hosts/Views/MainPage.xaml
Normal file
366
src/modules/Hosts/Hosts/Views/MainPage.xaml
Normal file
@@ -0,0 +1,366 @@
|
|||||||
|
<Page
|
||||||
|
x:Class="Hosts.Views.MainPage"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:converters="using:CommunityToolkit.WinUI.UI.Converters"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:helpers="using:Hosts.Helpers"
|
||||||
|
xmlns:i="using:Microsoft.Xaml.Interactivity"
|
||||||
|
xmlns:ic="using:Microsoft.Xaml.Interactions.Core"
|
||||||
|
xmlns:local="using:Hosts.Views"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:models="using:Hosts.Models"
|
||||||
|
x:Name="Page"
|
||||||
|
Loaded="Page_Loaded"
|
||||||
|
mc:Ignorable="d">
|
||||||
|
<i:Interaction.Behaviors>
|
||||||
|
<ic:EventTriggerBehavior EventName="Loaded">
|
||||||
|
<ic:InvokeCommandAction Command="{x:Bind ViewModel.ReadHostsCommand}" />
|
||||||
|
</ic:EventTriggerBehavior>
|
||||||
|
</i:Interaction.Behaviors>
|
||||||
|
|
||||||
|
<Page.Resources>
|
||||||
|
<converters:StringVisibilityConverter
|
||||||
|
x:Key="StringVisibilityConverter"
|
||||||
|
EmptyValue="Collapsed"
|
||||||
|
NotEmptyValue="Visible" />
|
||||||
|
<converters:BoolNegationConverter x:Key="BoolNegationConverter" />
|
||||||
|
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
|
||||||
|
</Page.Resources>
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="64" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<Button
|
||||||
|
x:Uid="AddEntryBtn"
|
||||||
|
Height="36"
|
||||||
|
Margin="16,0,0,0"
|
||||||
|
Command="{x:Bind NewDialogCommand}">
|
||||||
|
<StackPanel Orientation="Horizontal" Spacing="12">
|
||||||
|
<TextBlock
|
||||||
|
x:Name="Icon"
|
||||||
|
Margin="0,0,0,0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
FontFamily="{ThemeResource SymbolThemeFontFamily}"
|
||||||
|
FontSize="16"
|
||||||
|
Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}"
|
||||||
|
Text="" />
|
||||||
|
<TextBlock x:Uid="AddEntry" />
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<StackPanel
|
||||||
|
Padding="0,0,16,0"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
Orientation="Horizontal">
|
||||||
|
<Button
|
||||||
|
x:Uid="AdditionalLinesBtn"
|
||||||
|
Height="36"
|
||||||
|
Command="{x:Bind AdditionalLinesDialogCommand}"
|
||||||
|
Style="{StaticResource SubtleButtonStyle}">
|
||||||
|
<FontIcon
|
||||||
|
FontFamily="{StaticResource SymbolThemeFontFamily}"
|
||||||
|
FontSize="14"
|
||||||
|
Glyph="" />
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
x:Uid="FilterBtn"
|
||||||
|
Height="36"
|
||||||
|
Style="{StaticResource SubtleButtonStyle}">
|
||||||
|
<FontIcon
|
||||||
|
FontFamily="{StaticResource SymbolThemeFontFamily}"
|
||||||
|
FontSize="14"
|
||||||
|
Glyph="" />
|
||||||
|
<Button.Flyout>
|
||||||
|
<Flyout>
|
||||||
|
<StackPanel
|
||||||
|
Width="320"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
Spacing="12">
|
||||||
|
<AutoSuggestBox
|
||||||
|
x:Uid="AddressFilter"
|
||||||
|
PlaceholderText=""
|
||||||
|
QueryIcon="Find"
|
||||||
|
Text="{x:Bind ViewModel.AddressFilter, Mode=TwoWay}">
|
||||||
|
<i:Interaction.Behaviors>
|
||||||
|
<ic:EventTriggerBehavior EventName="TextChanged">
|
||||||
|
<ic:InvokeCommandAction Command="{x:Bind ViewModel.ApplyFiltersCommand}" />
|
||||||
|
</ic:EventTriggerBehavior>
|
||||||
|
</i:Interaction.Behaviors>
|
||||||
|
</AutoSuggestBox>
|
||||||
|
<AutoSuggestBox
|
||||||
|
x:Uid="HostsFilter"
|
||||||
|
QueryIcon="Find"
|
||||||
|
Text="{x:Bind ViewModel.HostsFilter, Mode=TwoWay}">
|
||||||
|
<i:Interaction.Behaviors>
|
||||||
|
<ic:EventTriggerBehavior EventName="TextChanged">
|
||||||
|
<ic:InvokeCommandAction Command="{x:Bind ViewModel.ApplyFiltersCommand}" />
|
||||||
|
</ic:EventTriggerBehavior>
|
||||||
|
</i:Interaction.Behaviors>
|
||||||
|
</AutoSuggestBox>
|
||||||
|
<AutoSuggestBox
|
||||||
|
x:Uid="CommentFilter"
|
||||||
|
QueryIcon="Find"
|
||||||
|
Text="{x:Bind ViewModel.CommentFilter, Mode=TwoWay}">
|
||||||
|
<i:Interaction.Behaviors>
|
||||||
|
<ic:EventTriggerBehavior EventName="TextChanged">
|
||||||
|
<ic:InvokeCommandAction Command="{x:Bind ViewModel.ApplyFiltersCommand}" />
|
||||||
|
</ic:EventTriggerBehavior>
|
||||||
|
</i:Interaction.Behaviors>
|
||||||
|
</AutoSuggestBox>
|
||||||
|
<Button
|
||||||
|
x:Uid="ClearFiltersBtn"
|
||||||
|
Margin="0,6,0,0"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
Command="{x:Bind ViewModel.ClearFiltersCommand}"
|
||||||
|
Style="{StaticResource AccentButtonStyle}" />
|
||||||
|
</StackPanel>
|
||||||
|
</Flyout>
|
||||||
|
</Button.Flyout>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
x:Uid="SettingsBtn"
|
||||||
|
Height="36"
|
||||||
|
Command="{x:Bind ViewModel.OpenSettingsCommand}"
|
||||||
|
Style="{StaticResource SubtleButtonStyle}">
|
||||||
|
<FontIcon
|
||||||
|
FontFamily="{StaticResource SymbolThemeFontFamily}"
|
||||||
|
FontSize="14"
|
||||||
|
Glyph="" />
|
||||||
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
|
||||||
|
<StackPanel
|
||||||
|
Grid.Row="2"
|
||||||
|
Orientation="Vertical"
|
||||||
|
Visibility="Visible">
|
||||||
|
<InfoBar
|
||||||
|
x:Uid="FileSaveError"
|
||||||
|
CornerRadius="0"
|
||||||
|
IsOpen="{x:Bind ViewModel.Error, Mode=TwoWay}"
|
||||||
|
Severity="Error" />
|
||||||
|
|
||||||
|
<InfoBar
|
||||||
|
x:Uid="FileChanged"
|
||||||
|
IsOpen="{x:Bind ViewModel.FileChanged, Mode=TwoWay}"
|
||||||
|
Severity="Informational">
|
||||||
|
<InfoBar.ActionButton>
|
||||||
|
<Button x:Uid="Reload" Command="{x:Bind ViewModel.ReadHostsCommand}" />
|
||||||
|
</InfoBar.ActionButton>
|
||||||
|
</InfoBar>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
https://github.com/microsoft/microsoft-ui-xaml/issues/7690
|
||||||
|
AllowDrop="{x:Bind ViewModel.Filtered, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}"
|
||||||
|
CanDragItems="{x:Bind ViewModel.Filtered, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}"
|
||||||
|
CanReorderItems="{x:Bind ViewModel.Filtered, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}"
|
||||||
|
-->
|
||||||
|
<ListView
|
||||||
|
x:Uid="Entries"
|
||||||
|
x:Name="Entries"
|
||||||
|
Grid.Row="1"
|
||||||
|
Margin="16,8,16,16"
|
||||||
|
Background="{ThemeResource LayerFillColorDefaultBrush}"
|
||||||
|
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
|
||||||
|
BorderThickness="1"
|
||||||
|
CornerRadius="8"
|
||||||
|
IsItemClickEnabled="True"
|
||||||
|
ItemClick="Entries_ItemClick"
|
||||||
|
ItemsSource="{Binding Entries, Mode=TwoWay}"
|
||||||
|
SelectedItem="{Binding Selected, Mode=TwoWay}">
|
||||||
|
<ListView.ItemTemplate>
|
||||||
|
<DataTemplate x:DataType="models:Entry">
|
||||||
|
<Grid
|
||||||
|
AutomationProperties.Name="{x:Bind Address, Mode=OneWay}"
|
||||||
|
Background="Transparent"
|
||||||
|
IsRightTapEnabled="True"
|
||||||
|
RightTapped="Grid_RightTapped">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="300" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<FlyoutBase.AttachedFlyout>
|
||||||
|
<MenuFlyout>
|
||||||
|
<MenuFlyoutItem
|
||||||
|
x:Uid="Ping"
|
||||||
|
Click="Ping_Click"
|
||||||
|
Icon="TwoBars" />
|
||||||
|
<MenuFlyoutItem
|
||||||
|
x:Uid="MoveUp"
|
||||||
|
Click="ReorderButtonUp_Click"
|
||||||
|
IsEnabled="{Binding DataContext.Filtered, ElementName=Page, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}">
|
||||||
|
<MenuFlyoutItem.Icon>
|
||||||
|
<FontIcon Glyph="" />
|
||||||
|
</MenuFlyoutItem.Icon>
|
||||||
|
</MenuFlyoutItem>
|
||||||
|
<MenuFlyoutItem
|
||||||
|
x:Uid="MoveDown"
|
||||||
|
Click="ReorderButtonDown_Click"
|
||||||
|
IsEnabled="{Binding DataContext.Filtered, ElementName=Page, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}">
|
||||||
|
<MenuFlyoutItem.Icon>
|
||||||
|
<FontIcon Glyph="" />
|
||||||
|
</MenuFlyoutItem.Icon>
|
||||||
|
</MenuFlyoutItem>
|
||||||
|
<MenuFlyoutSeparator />
|
||||||
|
<MenuFlyoutItem
|
||||||
|
x:Uid="Delete"
|
||||||
|
Click="Delete_Click"
|
||||||
|
Icon="Delete" />
|
||||||
|
</MenuFlyout>
|
||||||
|
</FlyoutBase.AttachedFlyout>
|
||||||
|
<TextBlock
|
||||||
|
Grid.Column="0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="{x:Bind Address, Mode=OneWay}"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
<TextBlock
|
||||||
|
Grid.Column="1"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||||
|
Style="{StaticResource CaptionTextBlockStyle}"
|
||||||
|
Text="{x:Bind helpers:StringHelper.GetHostsWithComment(Hosts, Comment), Mode=OneWay}"
|
||||||
|
TextWrapping="WrapWholeWords" />
|
||||||
|
<ProgressRing
|
||||||
|
Grid.Column="2"
|
||||||
|
Width="20"
|
||||||
|
Height="20"
|
||||||
|
Margin="0,0,8,0"
|
||||||
|
IsActive="{x:Bind Pinging, Mode=OneWay}" />
|
||||||
|
<FontIcon
|
||||||
|
x:Name="PingIcon"
|
||||||
|
Grid.Column="2"
|
||||||
|
Margin="0,0,8,0"
|
||||||
|
FontSize="18"
|
||||||
|
FontFamily="{ThemeResource SymbolThemeFontFamily}"
|
||||||
|
Visibility="Collapsed">
|
||||||
|
<i:Interaction.Behaviors>
|
||||||
|
<ic:DataTriggerBehavior
|
||||||
|
Binding="{x:Bind Ping, Mode=OneWay}"
|
||||||
|
ComparisonCondition="Equal"
|
||||||
|
Value="True">
|
||||||
|
<ic:ChangePropertyAction
|
||||||
|
PropertyName="Glyph"
|
||||||
|
TargetObject="{Binding ElementName=PingIcon}"
|
||||||
|
Value="" />
|
||||||
|
<ic:ChangePropertyAction
|
||||||
|
PropertyName="Visibility"
|
||||||
|
TargetObject="{Binding ElementName=PingIcon}"
|
||||||
|
Value="Visible" />
|
||||||
|
<ic:ChangePropertyAction
|
||||||
|
PropertyName="Foreground"
|
||||||
|
TargetObject="{Binding ElementName=PingIcon}"
|
||||||
|
Value="{StaticResource SystemFillColorSuccessBrush}" />
|
||||||
|
</ic:DataTriggerBehavior>
|
||||||
|
<ic:DataTriggerBehavior
|
||||||
|
Binding="{x:Bind Ping, Mode=OneWay}"
|
||||||
|
ComparisonCondition="Equal"
|
||||||
|
Value="False">
|
||||||
|
<ic:ChangePropertyAction
|
||||||
|
PropertyName="Glyph"
|
||||||
|
TargetObject="{Binding ElementName=PingIcon}"
|
||||||
|
Value="" />
|
||||||
|
<ic:ChangePropertyAction
|
||||||
|
PropertyName="Visibility"
|
||||||
|
TargetObject="{Binding ElementName=PingIcon}"
|
||||||
|
Value="Visible" />
|
||||||
|
<ic:ChangePropertyAction
|
||||||
|
PropertyName="Foreground"
|
||||||
|
TargetObject="{Binding ElementName=PingIcon}"
|
||||||
|
Value="{StaticResource SystemFillColorCriticalBrush}" />
|
||||||
|
</ic:DataTriggerBehavior>
|
||||||
|
<ic:DataTriggerBehavior
|
||||||
|
Binding="{x:Bind Ping, Mode=OneWay}"
|
||||||
|
ComparisonCondition="Equal"
|
||||||
|
Value="{x:Null}">
|
||||||
|
<ic:ChangePropertyAction
|
||||||
|
PropertyName="Visibility"
|
||||||
|
TargetObject="{Binding ElementName=PingIcon}"
|
||||||
|
Value="Collapsed" />
|
||||||
|
</ic:DataTriggerBehavior>
|
||||||
|
</i:Interaction.Behaviors>
|
||||||
|
</FontIcon>
|
||||||
|
<ToggleSwitch
|
||||||
|
x:Uid="ActiveToggle"
|
||||||
|
Grid.Column="3"
|
||||||
|
Width="40"
|
||||||
|
MinWidth="0"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
IsOn="{x:Bind Active, Mode=TwoWay}"
|
||||||
|
OffContent=""
|
||||||
|
OnContent="" />
|
||||||
|
</Grid>
|
||||||
|
</DataTemplate>
|
||||||
|
</ListView.ItemTemplate>
|
||||||
|
</ListView>
|
||||||
|
|
||||||
|
<ContentDialog
|
||||||
|
x:Name="EntryDialog"
|
||||||
|
x:Uid="EntryDialog"
|
||||||
|
IsPrimaryButtonEnabled="{Binding Valid, Mode=TwoWay}"
|
||||||
|
PrimaryButtonStyle="{StaticResource AccentButtonStyle}">
|
||||||
|
<ContentDialog.DataContext>
|
||||||
|
<models:Entry />
|
||||||
|
</ContentDialog.DataContext>
|
||||||
|
<StackPanel
|
||||||
|
MinWidth="480"
|
||||||
|
Margin="0,12,0,0"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
Spacing="24">
|
||||||
|
<TextBox
|
||||||
|
x:Uid="Address"
|
||||||
|
IsSpellCheckEnabled="False"
|
||||||
|
Text="{Binding Address, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
<TextBox
|
||||||
|
x:Uid="Hosts"
|
||||||
|
IsSpellCheckEnabled="False"
|
||||||
|
Text="{Binding Hosts, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
<TextBox
|
||||||
|
x:Uid="Comment"
|
||||||
|
IsSpellCheckEnabled="False"
|
||||||
|
Text="{Binding Comment, Mode=TwoWay}" />
|
||||||
|
<ToggleSwitch
|
||||||
|
x:Uid="Active"
|
||||||
|
IsOn="{Binding Active, Mode=TwoWay}"
|
||||||
|
OffContent=""
|
||||||
|
OnContent="" />
|
||||||
|
</StackPanel>
|
||||||
|
</ContentDialog>
|
||||||
|
|
||||||
|
<ContentDialog
|
||||||
|
x:Name="DeleteDialog"
|
||||||
|
x:Uid="DeleteDialog"
|
||||||
|
PrimaryButtonCommand="{x:Bind DeleteCommand}"
|
||||||
|
PrimaryButtonStyle="{StaticResource AccentButtonStyle}">
|
||||||
|
<TextBlock x:Uid="DeleteDialogAreYouSure" />
|
||||||
|
</ContentDialog>
|
||||||
|
|
||||||
|
<ContentDialog
|
||||||
|
x:Name="AdditionalLinesDialog"
|
||||||
|
x:Uid="AdditionalLinesDialog"
|
||||||
|
PrimaryButtonCommand="{x:Bind UpdateAdditionalLinesCommand}"
|
||||||
|
PrimaryButtonStyle="{StaticResource AccentButtonStyle}">
|
||||||
|
|
||||||
|
<TextBox
|
||||||
|
x:Name="AdditionalLines"
|
||||||
|
Height="300"
|
||||||
|
MinWidth="480"
|
||||||
|
Margin="0,12,0,0"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
AcceptsReturn="True"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
|
||||||
|
</ContentDialog>
|
||||||
|
</Grid>
|
||||||
|
</Page>
|
185
src/modules/Hosts/Hosts/Views/MainPage.xaml.cs
Normal file
185
src/modules/Hosts/Hosts/Views/MainPage.xaml.cs
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
using Hosts.Models;
|
||||||
|
using Hosts.Settings;
|
||||||
|
using Hosts.ViewModels;
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using Microsoft.UI.Xaml.Controls.Primitives;
|
||||||
|
using Microsoft.UI.Xaml.Input;
|
||||||
|
using Windows.ApplicationModel.Resources;
|
||||||
|
|
||||||
|
namespace Hosts.Views
|
||||||
|
{
|
||||||
|
public sealed partial class MainPage : Page
|
||||||
|
{
|
||||||
|
public MainViewModel ViewModel { get; private set; }
|
||||||
|
|
||||||
|
public ICommand NewDialogCommand => new AsyncRelayCommand(OpenNewDialogAsync);
|
||||||
|
|
||||||
|
public ICommand AdditionalLinesDialogCommand => new AsyncRelayCommand(OpenAdditionalLinesDialogAsync);
|
||||||
|
|
||||||
|
public ICommand AddCommand => new RelayCommand(Add);
|
||||||
|
|
||||||
|
public ICommand UpdateCommand => new RelayCommand(Update);
|
||||||
|
|
||||||
|
public ICommand DeleteCommand => new RelayCommand(Delete);
|
||||||
|
|
||||||
|
public ICommand UpdateAdditionalLinesCommand => new RelayCommand(UpdateAdditionalLines);
|
||||||
|
|
||||||
|
public ICommand ExitCommand => new RelayCommand(() => { Microsoft.UI.Dispatching.DispatcherQueue.GetForCurrentThread().TryEnqueue(Application.Current.Exit); });
|
||||||
|
|
||||||
|
public MainPage()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
ViewModel = App.GetService<MainViewModel>();
|
||||||
|
DataContext = ViewModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OpenNewDialogAsync()
|
||||||
|
{
|
||||||
|
var resourceLoader = ResourceLoader.GetForViewIndependentUse();
|
||||||
|
EntryDialog.Title = resourceLoader.GetString("AddNewEntryDialog_Title");
|
||||||
|
EntryDialog.PrimaryButtonText = resourceLoader.GetString("AddBtn");
|
||||||
|
EntryDialog.PrimaryButtonCommand = AddCommand;
|
||||||
|
EntryDialog.DataContext = new Entry(string.Empty, string.Empty, string.Empty, true);
|
||||||
|
await EntryDialog.ShowAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OpenAdditionalLinesDialogAsync()
|
||||||
|
{
|
||||||
|
AdditionalLines.Text = ViewModel.AdditionalLines;
|
||||||
|
await AdditionalLinesDialog.ShowAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void Entries_ItemClick(object sender, ItemClickEventArgs e)
|
||||||
|
{
|
||||||
|
var resourceLoader = ResourceLoader.GetForViewIndependentUse();
|
||||||
|
ViewModel.Selected = e.ClickedItem as Entry;
|
||||||
|
EntryDialog.Title = resourceLoader.GetString("UpdateEntry_Title");
|
||||||
|
EntryDialog.PrimaryButtonText = resourceLoader.GetString("UpdateBtn");
|
||||||
|
EntryDialog.PrimaryButtonCommand = UpdateCommand;
|
||||||
|
var clone = ViewModel.Selected.Clone();
|
||||||
|
EntryDialog.DataContext = clone;
|
||||||
|
await EntryDialog.ShowAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Add()
|
||||||
|
{
|
||||||
|
ViewModel.Add(EntryDialog.DataContext as Entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Update()
|
||||||
|
{
|
||||||
|
ViewModel.Update(Entries.SelectedIndex, EntryDialog.DataContext as Entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Delete()
|
||||||
|
{
|
||||||
|
ViewModel.DeleteSelected();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateAdditionalLines()
|
||||||
|
{
|
||||||
|
ViewModel.UpdateAdditionalLines(AdditionalLines.Text);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Grid_RightTapped(object sender, RightTappedRoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var owner = sender as FrameworkElement;
|
||||||
|
if (owner != null)
|
||||||
|
{
|
||||||
|
var flyoutBase = FlyoutBase.GetAttachedFlyout(owner);
|
||||||
|
flyoutBase.ShowAt(owner, new FlyoutShowOptions
|
||||||
|
{
|
||||||
|
Position = e.GetPosition(owner),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void Delete_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var menuFlyoutItem = sender as MenuFlyoutItem;
|
||||||
|
|
||||||
|
if (menuFlyoutItem != null)
|
||||||
|
{
|
||||||
|
var selectedEntry = menuFlyoutItem.DataContext as Entry;
|
||||||
|
ViewModel.Selected = selectedEntry;
|
||||||
|
DeleteDialog.Title = selectedEntry.Address;
|
||||||
|
await DeleteDialog.ShowAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void Ping_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var menuFlyoutItem = sender as MenuFlyoutItem;
|
||||||
|
|
||||||
|
if (menuFlyoutItem != null)
|
||||||
|
{
|
||||||
|
ViewModel.Selected = menuFlyoutItem.DataContext as Entry;
|
||||||
|
await ViewModel.PingSelectedAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void Page_Loaded(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var userSettings = App.GetService<IUserSettings>();
|
||||||
|
if (userSettings.ShowStartupWarning)
|
||||||
|
{
|
||||||
|
var resourceLoader = ResourceLoader.GetForViewIndependentUse();
|
||||||
|
var dialog = new ContentDialog();
|
||||||
|
|
||||||
|
dialog.XamlRoot = XamlRoot;
|
||||||
|
dialog.Style = Application.Current.Resources["DefaultContentDialogStyle"] as Style;
|
||||||
|
dialog.Title = resourceLoader.GetString("WarningDialog_Title");
|
||||||
|
dialog.Content = new TextBlock
|
||||||
|
{
|
||||||
|
Text = resourceLoader.GetString("WarningDialog_Text"),
|
||||||
|
TextWrapping = TextWrapping.Wrap,
|
||||||
|
};
|
||||||
|
dialog.PrimaryButtonText = resourceLoader.GetString("WarningDialog_AcceptBtn");
|
||||||
|
dialog.PrimaryButtonStyle = Application.Current.Resources["AccentButtonStyle"] as Style;
|
||||||
|
dialog.CloseButtonText = resourceLoader.GetString("WarningDialog_QuitBtn");
|
||||||
|
dialog.CloseButtonCommand = ExitCommand;
|
||||||
|
|
||||||
|
await dialog.ShowAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReorderButtonUp_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var menuFlyoutItem = sender as MenuFlyoutItem;
|
||||||
|
|
||||||
|
if (menuFlyoutItem != null)
|
||||||
|
{
|
||||||
|
var entry = menuFlyoutItem.DataContext as Entry;
|
||||||
|
var index = ViewModel.Entries.IndexOf(entry);
|
||||||
|
if (index > 0)
|
||||||
|
{
|
||||||
|
ViewModel.Entries.Move(index, index - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReorderButtonDown_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var menuFlyoutItem = sender as MenuFlyoutItem;
|
||||||
|
|
||||||
|
if (menuFlyoutItem != null)
|
||||||
|
{
|
||||||
|
var entry = menuFlyoutItem.DataContext as Entry;
|
||||||
|
var index = ViewModel.Entries.IndexOf(entry);
|
||||||
|
if (index < ViewModel.Entries.Count - 1)
|
||||||
|
{
|
||||||
|
ViewModel.Entries.Move(index, index + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
src/modules/Hosts/Hosts/app.manifest
Normal file
15
src/modules/Hosts/Hosts/app.manifest
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
|
<assemblyIdentity version="1.0.0.0" name="Hosts.app"/>
|
||||||
|
|
||||||
|
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<windowsSettings>
|
||||||
|
<!-- The combination of below two tags have the following effect:
|
||||||
|
1) Per-Monitor for >= Windows 10 Anniversary Update
|
||||||
|
2) System < Windows 10 Anniversary Update
|
||||||
|
-->
|
||||||
|
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/PM</dpiAware>
|
||||||
|
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
|
||||||
|
</windowsSettings>
|
||||||
|
</application>
|
||||||
|
</assembly>
|
BIN
src/modules/Hosts/Hosts/icon.ico
Normal file
BIN
src/modules/Hosts/Hosts/icon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 52 KiB |
@@ -0,0 +1,40 @@
|
|||||||
|
#include <windows.h>
|
||||||
|
#include "resource.h"
|
||||||
|
#include "../../../common/version/version.h"
|
||||||
|
|
||||||
|
#define APSTUDIO_READONLY_SYMBOLS
|
||||||
|
#include "winres.h"
|
||||||
|
#undef APSTUDIO_READONLY_SYMBOLS
|
||||||
|
|
||||||
|
1 VERSIONINFO
|
||||||
|
FILEVERSION FILE_VERSION
|
||||||
|
PRODUCTVERSION PRODUCT_VERSION
|
||||||
|
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
|
||||||
|
#ifdef _DEBUG
|
||||||
|
FILEFLAGS VS_FF_DEBUG
|
||||||
|
#else
|
||||||
|
FILEFLAGS 0x0L
|
||||||
|
#endif
|
||||||
|
FILEOS VOS_NT_WINDOWS32
|
||||||
|
FILETYPE VFT_DLL
|
||||||
|
FILESUBTYPE VFT2_UNKNOWN
|
||||||
|
BEGIN
|
||||||
|
BLOCK "StringFileInfo"
|
||||||
|
BEGIN
|
||||||
|
BLOCK "040904b0" // US English (0x0409), Unicode (0x04B0) charset
|
||||||
|
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", PRODUCT_NAME
|
||||||
|
VALUE "ProductVersion", PRODUCT_VERSION_STRING
|
||||||
|
END
|
||||||
|
END
|
||||||
|
BLOCK "VarFileInfo"
|
||||||
|
BEGIN
|
||||||
|
VALUE "Translation", 0x409, 1200 // US English (0x0409), Unicode (1200) charset
|
||||||
|
END
|
||||||
|
END
|
@@ -0,0 +1,101 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220418.1\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220418.1\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||||
|
<Target Name="GenerateResourceFiles" BeforeTargets="PrepareForBuild">
|
||||||
|
<Exec Command="powershell -NonInteractive -executionpolicy Unrestricted $(SolutionDir)tools\build\convert-resx-to-rc.ps1 $(MSBuildThisFileDirectory) resource.base.h resource.h HostsModuleInterface.base.rc HostsModuleInterface.rc" />
|
||||||
|
</Target>
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<VCProjectVersion>16.0</VCProjectVersion>
|
||||||
|
<ProjectGuid>{B41B888C-7DB8-4747-B262-4062E05A230D}</ProjectGuid>
|
||||||
|
<Keyword>Win32Proj</Keyword>
|
||||||
|
<RootNamespace>HostsModuleInterface</RootNamespace>
|
||||||
|
<ProjectName>HostsModuleInterface</ProjectName>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
|
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
|
||||||
|
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||||
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
|
||||||
|
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||||
|
<ImportGroup Label="ExtensionSettings">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="Shared">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<PropertyGroup Label="UserMacros" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\Hosts\</OutDir>
|
||||||
|
<TargetName>PowerToys.HostsModuleInterface</TargetName>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
|
||||||
|
<LinkIncremental>true</LinkIncremental>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)'=='Release'">
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemDefinitionGroup>
|
||||||
|
<ClCompile>
|
||||||
|
<AdditionalIncludeDirectories>$(SolutionDir)src\;$(SolutionDir)src\modules;$(SolutionDir)src\common\Telemetry;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
</ClCompile>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(CIBuild)'!='true'">
|
||||||
|
<ClCompile>
|
||||||
|
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||||
|
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||||
|
</ClCompile>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="resource.base.h" />
|
||||||
|
<ClInclude Include="Generated Files\resource.h" />
|
||||||
|
<ClInclude Include="trace.h" />
|
||||||
|
<ClInclude Include="pch.h" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="trace.cpp" />
|
||||||
|
<ClCompile Include="pch.cpp">
|
||||||
|
<PrecompiledHeader Condition="'$(CIBuild)'!='true'">Create</PrecompiledHeader>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="dllmain.cpp" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\..\common\logger\logger.vcxproj">
|
||||||
|
<Project>{d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}</Project>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
|
||||||
|
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="HostsModuleInterface.base.rc" />
|
||||||
|
<ResourceCompile Include="Generated Files\HostsModuleInterface.rc" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="packages.config" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="Resource.resx" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
<Import Project="..\..\..\..\deps\spdlog.props" />
|
||||||
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220418.1\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220418.1\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||||
|
</ImportGroup>
|
||||||
|
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||||
|
<PropertyGroup>
|
||||||
|
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220418.1\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220418.1\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220418.1\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220418.1\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||||
|
</Target>
|
||||||
|
</Project>
|
@@ -0,0 +1,59 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup>
|
||||||
|
<Filter Include="Source Files">
|
||||||
|
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||||
|
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="Header Files">
|
||||||
|
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||||
|
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="Resource Files">
|
||||||
|
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||||
|
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="Generated Files">
|
||||||
|
<UniqueIdentifier>{875a08c6-f610-4667-bd0f-80171ed96072}</UniqueIdentifier>
|
||||||
|
</Filter>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="pch.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="dllmain.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="trace.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="packages.config" />
|
||||||
|
<None Include="HostsModuleInterface.base.rc">
|
||||||
|
<Filter>Resource Files</Filter>
|
||||||
|
</None>
|
||||||
|
<None Include="Resource.resx">
|
||||||
|
<Filter>Resource Files</Filter>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="pch.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="Generated Files\resource.h">
|
||||||
|
<Filter>Generated Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="trace.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="resource.base.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ResourceCompile Include="Generated Files\HostsModuleInterface.rc">
|
||||||
|
<Filter>Resource Files</Filter>
|
||||||
|
</ResourceCompile>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
BIN
src/modules/Hosts/HostsModuleInterface/RCa04224
Normal file
BIN
src/modules/Hosts/HostsModuleInterface/RCa04224
Normal file
Binary file not shown.
124
src/modules/Hosts/HostsModuleInterface/Resource.resx
Normal file
124
src/modules/Hosts/HostsModuleInterface/Resource.resx
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<data name="Hosts_Name" xml:space="preserve">
|
||||||
|
<value>Hosts File Editor</value>
|
||||||
|
<comment>"Hosts" refer to the system hosts file, do not loc</comment>
|
||||||
|
</data>
|
||||||
|
</root>
|
199
src/modules/Hosts/HostsModuleInterface/dllmain.cpp
Normal file
199
src/modules/Hosts/HostsModuleInterface/dllmain.cpp
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
|
||||||
|
#include "trace.h"
|
||||||
|
#include <common/logger/logger.h>
|
||||||
|
#include <common/utils/logger_helper.h>
|
||||||
|
#include <interface/powertoy_module_interface.h>
|
||||||
|
#include "Generated Files/resource.h"
|
||||||
|
|
||||||
|
#include <shellapi.h>
|
||||||
|
#include <common/utils/resources.h>
|
||||||
|
#include <common/utils/winapi_error.h>
|
||||||
|
#include <common/SettingsAPI/settings_objects.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
extern "C" IMAGE_DOS_HEADER __ImageBase;
|
||||||
|
|
||||||
|
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
|
||||||
|
{
|
||||||
|
switch (ul_reason_for_call)
|
||||||
|
{
|
||||||
|
case DLL_PROCESS_ATTACH:
|
||||||
|
Trace::RegisterProvider();
|
||||||
|
break;
|
||||||
|
case DLL_THREAD_ATTACH:
|
||||||
|
case DLL_THREAD_DETACH:
|
||||||
|
break;
|
||||||
|
case DLL_PROCESS_DETACH:
|
||||||
|
Trace::UnregisterProvider();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
// Name of the powertoy module.
|
||||||
|
inline const std::wstring ModuleKey = L"Hosts";
|
||||||
|
}
|
||||||
|
|
||||||
|
class HostsModuleInterface : public PowertoyModuleIface
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
bool m_enabled = false;
|
||||||
|
|
||||||
|
std::wstring app_name;
|
||||||
|
|
||||||
|
//contains the non localized key of the powertoy
|
||||||
|
std::wstring app_key;
|
||||||
|
|
||||||
|
HANDLE m_hProcess;
|
||||||
|
|
||||||
|
bool is_process_running()
|
||||||
|
{
|
||||||
|
return WaitForSingleObject(m_hProcess, 0) == WAIT_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bring_process_to_front()
|
||||||
|
{
|
||||||
|
auto enum_windows = [](HWND hwnd, LPARAM param) -> BOOL {
|
||||||
|
HANDLE process_handle = (HANDLE)param;
|
||||||
|
DWORD window_process_id = 0;
|
||||||
|
|
||||||
|
GetWindowThreadProcessId(hwnd, &window_process_id);
|
||||||
|
if (GetProcessId(process_handle) == window_process_id)
|
||||||
|
{
|
||||||
|
SetForegroundWindow(hwnd);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
};
|
||||||
|
|
||||||
|
EnumWindows(enum_windows, (LPARAM)m_hProcess);
|
||||||
|
}
|
||||||
|
|
||||||
|
void launch_process(bool runas)
|
||||||
|
{
|
||||||
|
Logger::trace(L"Starting Hosts process");
|
||||||
|
unsigned long powertoys_pid = GetCurrentProcessId();
|
||||||
|
|
||||||
|
std::wstring executable_args = L"";
|
||||||
|
executable_args.append(std::to_wstring(powertoys_pid));
|
||||||
|
|
||||||
|
SHELLEXECUTEINFOW sei{ sizeof(sei) };
|
||||||
|
sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI };
|
||||||
|
sei.lpFile = L"modules\\Hosts\\PowerToys.Hosts.exe";
|
||||||
|
sei.nShow = SW_SHOWNORMAL;
|
||||||
|
sei.lpParameters = executable_args.data();
|
||||||
|
|
||||||
|
if (runas)
|
||||||
|
{
|
||||||
|
sei.lpVerb = L"runas";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ShellExecuteExW(&sei))
|
||||||
|
{
|
||||||
|
Logger::trace("Successfully started the Hosts process");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger::error(L"Hosts failed to start. {}", get_last_error_or_default(GetLastError()));
|
||||||
|
}
|
||||||
|
|
||||||
|
m_hProcess = sei.hProcess;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
HostsModuleInterface()
|
||||||
|
{
|
||||||
|
app_name = GET_RESOURCE_STRING(IDS_HOSTS_NAME);
|
||||||
|
app_key = ModuleKey;
|
||||||
|
LoggerHelpers::init_logger(app_key, L"ModuleInterface", LogSettings::hostsLoggerName);
|
||||||
|
}
|
||||||
|
|
||||||
|
~HostsModuleInterface()
|
||||||
|
{
|
||||||
|
m_enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destroy the powertoy and free memory
|
||||||
|
virtual void destroy() override
|
||||||
|
{
|
||||||
|
Logger::trace("HostsModuleInterface::destroy()");
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the localized display name of the powertoy
|
||||||
|
virtual const wchar_t* get_name() override
|
||||||
|
{
|
||||||
|
return app_name.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the non localized key of the powertoy, this will be cached by the runner
|
||||||
|
virtual const wchar_t* get_key() override
|
||||||
|
{
|
||||||
|
return app_key.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool get_config(wchar_t* buffer, int* buffer_size) override
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void call_custom_action(const wchar_t* action) override
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
PowerToysSettings::CustomActionObject action_object =
|
||||||
|
PowerToysSettings::CustomActionObject::from_json_string(action);
|
||||||
|
|
||||||
|
if (is_process_running())
|
||||||
|
{
|
||||||
|
bring_process_to_front();
|
||||||
|
}
|
||||||
|
else if (action_object.get_name() == L"Launch")
|
||||||
|
{
|
||||||
|
launch_process(false);
|
||||||
|
}
|
||||||
|
else if (action_object.get_name() == L"LaunchAdministrator")
|
||||||
|
{
|
||||||
|
launch_process(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (std::exception&)
|
||||||
|
{
|
||||||
|
Logger::error(L"Failed to parse action. {}", action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void set_config(const wchar_t* config) override
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool is_enabled() override
|
||||||
|
{
|
||||||
|
return m_enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void enable()
|
||||||
|
{
|
||||||
|
Logger::trace("HostsModuleInterface::enable()");
|
||||||
|
m_enabled = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual void disable()
|
||||||
|
{
|
||||||
|
Logger::trace("HostsModuleInterface::disable()");
|
||||||
|
if (m_enabled)
|
||||||
|
{
|
||||||
|
TerminateProcess(m_hProcess, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_enabled = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create()
|
||||||
|
{
|
||||||
|
return new HostsModuleInterface();
|
||||||
|
}
|
4
src/modules/Hosts/HostsModuleInterface/packages.config
Normal file
4
src/modules/Hosts/HostsModuleInterface/packages.config
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="Microsoft.Windows.CppWinRT" version="2.0.220418.1" targetFramework="native" />
|
||||||
|
</packages>
|
1
src/modules/Hosts/HostsModuleInterface/pch.cpp
Normal file
1
src/modules/Hosts/HostsModuleInterface/pch.cpp
Normal file
@@ -0,0 +1 @@
|
|||||||
|
#include "pch.h"
|
5
src/modules/Hosts/HostsModuleInterface/pch.h
Normal file
5
src/modules/Hosts/HostsModuleInterface/pch.h
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <windows.h>
|
||||||
|
#include <ProjectTelemetry.h>
|
13
src/modules/Hosts/HostsModuleInterface/resource.base.h
Normal file
13
src/modules/Hosts/HostsModuleInterface/resource.base.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
//{{NO_DEPENDENCIES}}
|
||||||
|
// Microsoft Visual C++ generated include file.
|
||||||
|
// Used by AlwaysOnTopModuleInterface.rc
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
// Non-localizable
|
||||||
|
|
||||||
|
#define FILE_DESCRIPTION "PowerToys Hosts Module"
|
||||||
|
#define INTERNAL_NAME "PowerToys.HostsModuleInterface"
|
||||||
|
#define ORIGINAL_FILENAME "PowerToys.HostsModuleInterface.dll"
|
||||||
|
|
||||||
|
// Non-localizable
|
||||||
|
//////////////////////////////
|
19
src/modules/Hosts/HostsModuleInterface/trace.cpp
Normal file
19
src/modules/Hosts/HostsModuleInterface/trace.cpp
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "trace.h"
|
||||||
|
|
||||||
|
TRACELOGGING_DEFINE_PROVIDER(
|
||||||
|
g_hProvider,
|
||||||
|
"Microsoft.PowerToys",
|
||||||
|
// {38e8889b-9731-53f5-e901-e8a7c1753074}
|
||||||
|
(0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74),
|
||||||
|
TraceLoggingOptionProjectTelemetry());
|
||||||
|
|
||||||
|
void Trace::RegisterProvider() noexcept
|
||||||
|
{
|
||||||
|
TraceLoggingRegister(g_hProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Trace::UnregisterProvider() noexcept
|
||||||
|
{
|
||||||
|
TraceLoggingUnregister(g_hProvider);
|
||||||
|
}
|
8
src/modules/Hosts/HostsModuleInterface/trace.h
Normal file
8
src/modules/Hosts/HostsModuleInterface/trace.h
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
class Trace
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static void RegisterProvider() noexcept;
|
||||||
|
static void UnregisterProvider() noexcept;
|
||||||
|
};
|
@@ -155,8 +155,8 @@ int runner(bool isProcessElevated, bool openSettings, std::string settingsWindow
|
|||||||
L"modules/MouseUtils/PowerToys.MousePointerCrosshairs.dll",
|
L"modules/MouseUtils/PowerToys.MousePointerCrosshairs.dll",
|
||||||
L"modules/PowerAccent/PowerToys.PowerAccentModuleInterface.dll",
|
L"modules/PowerAccent/PowerToys.PowerAccentModuleInterface.dll",
|
||||||
L"modules/PowerOCR/PowerToys.PowerOCRModuleInterface.dll",
|
L"modules/PowerOCR/PowerToys.PowerOCRModuleInterface.dll",
|
||||||
|
|
||||||
L"modules/MeasureTool/PowerToys.MeasureToolModuleInterface.dll",
|
L"modules/MeasureTool/PowerToys.MeasureToolModuleInterface.dll",
|
||||||
|
L"modules/Hosts/PowerToys.HostsModuleInterface.dll",
|
||||||
};
|
};
|
||||||
const auto VCM_PATH = L"modules/VideoConference/PowerToys.VideoConferenceModule.dll";
|
const auto VCM_PATH = L"modules/VideoConference/PowerToys.VideoConferenceModule.dll";
|
||||||
if (const auto mf = LoadLibraryA("mf.dll"))
|
if (const auto mf = LoadLibraryA("mf.dll"))
|
||||||
|
@@ -569,6 +569,8 @@ std::string ESettingsWindowNames_to_string(ESettingsWindowNames value)
|
|||||||
return "ShortcutGuide";
|
return "ShortcutGuide";
|
||||||
case ESettingsWindowNames::VideoConference:
|
case ESettingsWindowNames::VideoConference:
|
||||||
return "VideoConference";
|
return "VideoConference";
|
||||||
|
case ESettingsWindowNames::Hosts:
|
||||||
|
return "Hosts";
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
Logger::error(L"Can't convert ESettingsWindowNames value={} to string", static_cast<int>(value));
|
Logger::error(L"Can't convert ESettingsWindowNames value={} to string", static_cast<int>(value));
|
||||||
@@ -628,6 +630,10 @@ ESettingsWindowNames ESettingsWindowNames_from_string(std::string value)
|
|||||||
{
|
{
|
||||||
return ESettingsWindowNames::VideoConference;
|
return ESettingsWindowNames::VideoConference;
|
||||||
}
|
}
|
||||||
|
else if (value == "Hosts")
|
||||||
|
{
|
||||||
|
return ESettingsWindowNames::Hosts;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger::error(L"Can't convert string value={} to ESettingsWindowNames", winrt::to_hstring(value));
|
Logger::error(L"Can't convert string value={} to ESettingsWindowNames", winrt::to_hstring(value));
|
||||||
|
@@ -15,7 +15,8 @@ enum class ESettingsWindowNames
|
|||||||
PowerRename,
|
PowerRename,
|
||||||
FileExplorer,
|
FileExplorer,
|
||||||
ShortcutGuide,
|
ShortcutGuide,
|
||||||
VideoConference
|
VideoConference,
|
||||||
|
Hosts
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string ESettingsWindowNames_to_string(ESettingsWindowNames value);
|
std::string ESettingsWindowNames_to_string(ESettingsWindowNames value);
|
||||||
|
@@ -287,6 +287,22 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool hosts = true;
|
||||||
|
|
||||||
|
[JsonPropertyName("Hosts")]
|
||||||
|
public bool Hosts
|
||||||
|
{
|
||||||
|
get => hosts;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (hosts != value)
|
||||||
|
{
|
||||||
|
LogTelemetryEvent(value);
|
||||||
|
hosts = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public string ToJsonString()
|
public string ToJsonString()
|
||||||
{
|
{
|
||||||
return JsonSerializer.Serialize(this);
|
return JsonSerializer.Serialize(this);
|
||||||
|
@@ -0,0 +1,12 @@
|
|||||||
|
// 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 Settings.UI.Library.Enumerations
|
||||||
|
{
|
||||||
|
public enum AdditionalLinesPosition
|
||||||
|
{
|
||||||
|
Top = 0,
|
||||||
|
Bottom = 1,
|
||||||
|
}
|
||||||
|
}
|
27
src/settings-ui/Settings.UI.Library/HostsProperties.cs
Normal file
27
src/settings-ui/Settings.UI.Library/HostsProperties.cs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using Settings.UI.Library.Enumerations;
|
||||||
|
|
||||||
|
namespace Microsoft.PowerToys.Settings.UI.Library
|
||||||
|
{
|
||||||
|
public class HostsProperties
|
||||||
|
{
|
||||||
|
[JsonConverter(typeof(BoolPropertyJsonConverter))]
|
||||||
|
public bool ShowStartupWarning { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(BoolPropertyJsonConverter))]
|
||||||
|
public bool LaunchAdministrator { get; set; }
|
||||||
|
|
||||||
|
public AdditionalLinesPosition AdditionalLinesPosition { get; set; }
|
||||||
|
|
||||||
|
public HostsProperties()
|
||||||
|
{
|
||||||
|
ShowStartupWarning = true;
|
||||||
|
LaunchAdministrator = true;
|
||||||
|
AdditionalLinesPosition = AdditionalLinesPosition.Top;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
46
src/settings-ui/Settings.UI.Library/HostsSettings.cs
Normal file
46
src/settings-ui/Settings.UI.Library/HostsSettings.cs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
// 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.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
|
||||||
|
|
||||||
|
namespace Microsoft.PowerToys.Settings.UI.Library
|
||||||
|
{
|
||||||
|
public class HostsSettings : BasePTModuleSettings, ISettingsConfig
|
||||||
|
{
|
||||||
|
public const string ModuleName = "Hosts";
|
||||||
|
|
||||||
|
[JsonPropertyName("properties")]
|
||||||
|
public HostsProperties Properties { get; set; }
|
||||||
|
|
||||||
|
public HostsSettings()
|
||||||
|
{
|
||||||
|
Properties = new HostsProperties();
|
||||||
|
Version = "1.0";
|
||||||
|
Name = ModuleName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void Save(ISettingsUtils settingsUtils)
|
||||||
|
{
|
||||||
|
// Save settings to file
|
||||||
|
var options = new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
WriteIndented = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (settingsUtils == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(settingsUtils));
|
||||||
|
}
|
||||||
|
|
||||||
|
settingsUtils.SaveSettings(JsonSerializer.Serialize(this, options), ModuleName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetModuleName() => Name;
|
||||||
|
|
||||||
|
public bool UpgradeSettingsConfiguration() => false;
|
||||||
|
}
|
||||||
|
}
|
115
src/settings-ui/Settings.UI.Library/ViewModels/HostsViewModel.cs
Normal file
115
src/settings-ui/Settings.UI.Library/ViewModels/HostsViewModel.cs
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using Microsoft.PowerToys.Settings.UI.Library;
|
||||||
|
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
|
||||||
|
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
|
||||||
|
using Microsoft.PowerToys.Settings.UI.Library.ViewModels.Commands;
|
||||||
|
using Settings.UI.Library.Enumerations;
|
||||||
|
|
||||||
|
namespace Settings.UI.Library.ViewModels
|
||||||
|
{
|
||||||
|
public class HostsViewModel : Observable
|
||||||
|
{
|
||||||
|
private bool _isElevated;
|
||||||
|
|
||||||
|
private ISettingsUtils SettingsUtils { get; set; }
|
||||||
|
|
||||||
|
private GeneralSettings GeneralSettingsConfig { get; set; }
|
||||||
|
|
||||||
|
private HostsSettings Settings { get; set; }
|
||||||
|
|
||||||
|
private Func<string, int> SendConfigMSG { get; }
|
||||||
|
|
||||||
|
public ButtonClickCommand LaunchEventHandler => new ButtonClickCommand(Launch);
|
||||||
|
|
||||||
|
public bool IsEnabled
|
||||||
|
{
|
||||||
|
get => GeneralSettingsConfig.Enabled.Hosts;
|
||||||
|
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value != GeneralSettingsConfig.Enabled.Hosts)
|
||||||
|
{
|
||||||
|
// Set the status in the general settings configuration
|
||||||
|
GeneralSettingsConfig.Enabled.Hosts = value;
|
||||||
|
OutGoingGeneralSettings snd = new OutGoingGeneralSettings(GeneralSettingsConfig);
|
||||||
|
|
||||||
|
SendConfigMSG(snd.ToString());
|
||||||
|
OnPropertyChanged(nameof(IsEnabled));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool LaunchAdministratorEnabled => IsEnabled && !_isElevated;
|
||||||
|
|
||||||
|
public bool ShowStartupWarning
|
||||||
|
{
|
||||||
|
get => Settings.Properties.ShowStartupWarning;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value != Settings.Properties.ShowStartupWarning)
|
||||||
|
{
|
||||||
|
Settings.Properties.ShowStartupWarning = value;
|
||||||
|
NotifyPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool LaunchAdministrator
|
||||||
|
{
|
||||||
|
get => Settings.Properties.LaunchAdministrator;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value != Settings.Properties.LaunchAdministrator)
|
||||||
|
{
|
||||||
|
Settings.Properties.LaunchAdministrator = value;
|
||||||
|
NotifyPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int AdditionalLinesPosition
|
||||||
|
{
|
||||||
|
get => (int)Settings.Properties.AdditionalLinesPosition;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value != (int)Settings.Properties.AdditionalLinesPosition)
|
||||||
|
{
|
||||||
|
Settings.Properties.AdditionalLinesPosition = (AdditionalLinesPosition)value;
|
||||||
|
NotifyPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public HostsViewModel(ISettingsUtils settingsUtils, ISettingsRepository<GeneralSettings> settingsRepository, ISettingsRepository<HostsSettings> moduleSettingsRepository, Func<string, int> ipcMSGCallBackFunc, bool isElevated)
|
||||||
|
{
|
||||||
|
SettingsUtils = settingsUtils;
|
||||||
|
GeneralSettingsConfig = settingsRepository.SettingsConfig;
|
||||||
|
Settings = moduleSettingsRepository.SettingsConfig;
|
||||||
|
SendConfigMSG = ipcMSGCallBackFunc;
|
||||||
|
_isElevated = isElevated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Launch()
|
||||||
|
{
|
||||||
|
var actionName = "Launch";
|
||||||
|
|
||||||
|
if (!_isElevated && LaunchAdministrator)
|
||||||
|
{
|
||||||
|
actionName = "LaunchAdministrator";
|
||||||
|
}
|
||||||
|
|
||||||
|
SendConfigMSG("{\"action\":{\"Hosts\":{\"action_name\":\"" + actionName + "\", \"value\":\"\"}}}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
|
||||||
|
{
|
||||||
|
OnPropertyChanged(propertyName);
|
||||||
|
SettingsUtils.SaveSettings(Settings.ToJsonString(), HostsSettings.ModuleName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -129,6 +129,7 @@ namespace Microsoft.PowerToys.Settings.UI
|
|||||||
case "TextExtractor": StartupPage = typeof(Views.PowerOcrPage); break;
|
case "TextExtractor": StartupPage = typeof(Views.PowerOcrPage); break;
|
||||||
case "VideoConference": StartupPage = typeof(Views.VideoConferencePage); break;
|
case "VideoConference": StartupPage = typeof(Views.VideoConferencePage); break;
|
||||||
case "MeasureTool": StartupPage = typeof(Views.MeasureToolPage); break;
|
case "MeasureTool": StartupPage = typeof(Views.MeasureToolPage); break;
|
||||||
|
case "Hosts": StartupPage = typeof(Views.HostsPage); break;
|
||||||
default: Debug.Assert(false, "Unexpected SettingsWindow argument value"); break;
|
default: Debug.Assert(false, "Unexpected SettingsWindow argument value"); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -22,6 +22,7 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Enums
|
|||||||
TextExtractor,
|
TextExtractor,
|
||||||
VideoConference,
|
VideoConference,
|
||||||
MeasureTool,
|
MeasureTool,
|
||||||
|
Hosts,
|
||||||
WhatsNew,
|
WhatsNew,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
28
src/settings-ui/Settings.UI/OOBE/Views/OobeHosts.xaml
Normal file
28
src/settings-ui/Settings.UI/OOBE/Views/OobeHosts.xaml
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<Page
|
||||||
|
x:Class="Microsoft.PowerToys.Settings.UI.OOBE.Views.OobeHosts"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:local="using:Microsoft.PowerToys.Settings.UI.OOBE.Views"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
xmlns:controls="using:Microsoft.PowerToys.Settings.UI.Controls"
|
||||||
|
xmlns:toolkitcontrols="using:CommunityToolkit.WinUI.UI.Controls">
|
||||||
|
|
||||||
|
<controls:OOBEPageControl x:Uid="Oobe_Hosts"
|
||||||
|
HeroImage="ms-appx:///Assets/Modules/OOBE/AlwaysOnTop.png">
|
||||||
|
<controls:OOBEPageControl.PageContent>
|
||||||
|
<StackPanel Orientation="Vertical">
|
||||||
|
<StackPanel Orientation="Horizontal" Spacing="12" Margin="0,24,0,0">
|
||||||
|
<Button x:Uid="Launch_Hosts" Style="{StaticResource AccentButtonStyle}" Click="Launch_Hosts_Click" />
|
||||||
|
<Button x:Uid="OOBE_Settings"
|
||||||
|
Click="Launch_Settings_Click" />
|
||||||
|
<HyperlinkButton NavigateUri="https://aka.ms/PowerToysOverview_HostsFileEditor" Style="{StaticResource TextButtonStyle}">
|
||||||
|
<TextBlock x:Uid="LearnMore_Hosts"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
</HyperlinkButton>
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
|
</controls:OOBEPageControl.PageContent>
|
||||||
|
</controls:OOBEPageControl>
|
||||||
|
</Page>
|
58
src/settings-ui/Settings.UI/OOBE/Views/OobeHosts.xaml.cs
Normal file
58
src/settings-ui/Settings.UI/OOBE/Views/OobeHosts.xaml.cs
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using Microsoft.PowerToys.Settings.UI.Library;
|
||||||
|
using Microsoft.PowerToys.Settings.UI.OOBE.Enums;
|
||||||
|
using Microsoft.PowerToys.Settings.UI.OOBE.ViewModel;
|
||||||
|
using Microsoft.PowerToys.Settings.UI.Views;
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using Microsoft.UI.Xaml.Navigation;
|
||||||
|
|
||||||
|
namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
|
||||||
|
{
|
||||||
|
public sealed partial class OobeHosts : Page
|
||||||
|
{
|
||||||
|
public OobePowerToysModule ViewModel { get; }
|
||||||
|
|
||||||
|
public OobeHosts()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.Hosts]);
|
||||||
|
DataContext = ViewModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||||
|
{
|
||||||
|
ViewModel.LogOpeningModuleEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnNavigatedFrom(NavigationEventArgs e)
|
||||||
|
{
|
||||||
|
ViewModel.LogClosingModuleEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Launch_Hosts_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
bool launchAdmin = SettingsRepository<HostsSettings>.GetInstance(new SettingsUtils()).SettingsConfig.Properties.LaunchAdministrator;
|
||||||
|
var actionName = "Launch";
|
||||||
|
|
||||||
|
if (!App.IsElevated && launchAdmin)
|
||||||
|
{
|
||||||
|
actionName = "LaunchAdministrator";
|
||||||
|
}
|
||||||
|
|
||||||
|
ShellPage.SendDefaultIPCMessage("{\"action\":{\"Hosts\":{\"action_name\":\"" + actionName + "\", \"value\":\"\"}}}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Launch_Settings_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (OobeShellPage.OpenMainWindowCallback != null)
|
||||||
|
{
|
||||||
|
OobeShellPage.OpenMainWindowCallback(typeof(HostsPage));
|
||||||
|
}
|
||||||
|
|
||||||
|
ViewModel.LogOpeningSettingsEvent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -75,6 +75,13 @@
|
|||||||
ShowAsMonochrome="False" />
|
ShowAsMonochrome="False" />
|
||||||
</NavigationViewItem.Icon>
|
</NavigationViewItem.Icon>
|
||||||
</NavigationViewItem>
|
</NavigationViewItem>
|
||||||
|
<NavigationViewItem x:Uid="Shell_Hosts" Tag="Hosts">
|
||||||
|
<NavigationViewItem.Icon>
|
||||||
|
<BitmapIcon
|
||||||
|
UriSource="ms-appx:///Assets/FluentIcons/FluentIconsAlwaysOnTop.png"
|
||||||
|
ShowAsMonochrome="False" />
|
||||||
|
</NavigationViewItem.Icon>
|
||||||
|
</NavigationViewItem>
|
||||||
<NavigationViewItem x:Uid="Shell_ImageResizer" Tag="ImageResizer">
|
<NavigationViewItem x:Uid="Shell_ImageResizer" Tag="ImageResizer">
|
||||||
<NavigationViewItem.Icon>
|
<NavigationViewItem.Icon>
|
||||||
<BitmapIcon
|
<BitmapIcon
|
||||||
|
@@ -139,6 +139,12 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
|
|||||||
IsNew = true,
|
IsNew = true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Modules.Insert((int)PowerToysModules.Hosts, new OobePowerToysModule()
|
||||||
|
{
|
||||||
|
ModuleName = "Hosts",
|
||||||
|
IsNew = true,
|
||||||
|
});
|
||||||
|
|
||||||
Modules.Insert((int)PowerToysModules.WhatsNew, new OobePowerToysModule()
|
Modules.Insert((int)PowerToysModules.WhatsNew, new OobePowerToysModule()
|
||||||
{
|
{
|
||||||
ModuleName = "WhatsNew",
|
ModuleName = "WhatsNew",
|
||||||
@@ -192,6 +198,7 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
|
|||||||
case "VideoConference": NavigationFrame.Navigate(typeof(OobeVideoConference)); break;
|
case "VideoConference": NavigationFrame.Navigate(typeof(OobeVideoConference)); break;
|
||||||
case "MouseUtils": NavigationFrame.Navigate(typeof(OobeMouseUtils)); break;
|
case "MouseUtils": NavigationFrame.Navigate(typeof(OobeMouseUtils)); break;
|
||||||
case "MeasureTool": NavigationFrame.Navigate(typeof(OobeMeasureTool)); break;
|
case "MeasureTool": NavigationFrame.Navigate(typeof(OobeMeasureTool)); break;
|
||||||
|
case "Hosts": NavigationFrame.Navigate(typeof(OobeHosts)); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -35,6 +35,10 @@
|
|||||||
<Optimize>true</Optimize>
|
<Optimize>true</Optimize>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Remove="OOBE\Views\OobeHosts.xaml" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Include="Assets\SplashScreen.scale-200.png" />
|
<Content Include="Assets\SplashScreen.scale-200.png" />
|
||||||
<Content Include="Assets\LockScreenLogo.scale-200.png" />
|
<Content Include="Assets\LockScreenLogo.scale-200.png" />
|
||||||
@@ -68,6 +72,9 @@
|
|||||||
<None Update="icon.ico">
|
<None Update="icon.ico">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
|
<Page Update="OOBE\Views\OobeHosts.xaml">
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
</Page>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Page Update="OOBE\Views\OobePowerOCR.xaml">
|
<Page Update="OOBE\Views\OobePowerOCR.xaml">
|
||||||
|
@@ -2554,4 +2554,72 @@ Activate by holding the key for the character you want to add an accent to, then
|
|||||||
<data name="QuickAccent_SelectedLanguage_Italian.Content" xml:space="preserve">
|
<data name="QuickAccent_SelectedLanguage_Italian.Content" xml:space="preserve">
|
||||||
<value>Italian</value>
|
<value>Italian</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Hosts.ModuleDescription" xml:space="preserve">
|
||||||
|
<value>Quick and simple utility for managing hosts file.</value>
|
||||||
|
<comment>"Hosts" refers to the system hosts file, do not loc</comment>
|
||||||
|
</data>
|
||||||
|
<data name="Hosts.ModuleTitle" xml:space="preserve">
|
||||||
|
<value>Hosts File Editor</value>
|
||||||
|
<comment>"Hosts" refers to the system hosts file, do not loc</comment>
|
||||||
|
</data>
|
||||||
|
<data name="Shell_Hosts.Content" xml:space="preserve">
|
||||||
|
<value>Hosts File Editor</value>
|
||||||
|
<comment>Products name: Navigation view item name for Hosts File Editor</comment>
|
||||||
|
</data>
|
||||||
|
<data name="Hosts_EnableToggleControl_HeaderText.Header" xml:space="preserve">
|
||||||
|
<value>Enable Hosts File Editor</value>
|
||||||
|
<comment>"Hosts File Editor" is the name of the utility</comment>
|
||||||
|
</data>
|
||||||
|
<data name="Hosts_Toggle_ShowStartupWarning.Header" xml:space="preserve">
|
||||||
|
<value>Show a warning at startup</value>
|
||||||
|
</data>
|
||||||
|
<data name="Hosts_Activation_GroupSettings.Header" xml:space="preserve">
|
||||||
|
<value>Activation</value>
|
||||||
|
</data>
|
||||||
|
<data name="Hosts_LaunchButtonControl.Description" xml:space="preserve">
|
||||||
|
<value>Manage your hosts file</value>
|
||||||
|
<comment>"Hosts" refers to the system hosts file, do not loc</comment>
|
||||||
|
</data>
|
||||||
|
<data name="Hosts_LaunchButtonControl.Header" xml:space="preserve">
|
||||||
|
<value>Launch Host File Editor</value>
|
||||||
|
<comment>"Host File Editor" is a product name</comment>
|
||||||
|
</data>
|
||||||
|
<data name="Hosts_LaunchButton_Accessible.[using:Microsoft.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
|
||||||
|
<value>Launch Host File Editor</value>
|
||||||
|
<comment>"Host File Editor" is a product name</comment>
|
||||||
|
</data>
|
||||||
|
<data name="Hosts_AdditionalLinesPosition.Header" xml:space="preserve">
|
||||||
|
<value>Additional lines position</value>
|
||||||
|
</data>
|
||||||
|
<data name="Hosts_AdditionalLinesPosition_Bottom.Content" xml:space="preserve">
|
||||||
|
<value>Bottom</value>
|
||||||
|
</data>
|
||||||
|
<data name="Hosts_AdditionalLinesPosition_Top.Content" xml:space="preserve">
|
||||||
|
<value>Top</value>
|
||||||
|
</data>
|
||||||
|
<data name="Hosts_File_GroupSettings.Header" xml:space="preserve">
|
||||||
|
<value>File</value>
|
||||||
|
</data>
|
||||||
|
<data name="Launch_Hosts.Content" xml:space="preserve">
|
||||||
|
<value>Launch Hosts File Editor</value>
|
||||||
|
<comment>"Hosts File Editor" is the name of the utility</comment>
|
||||||
|
</data>
|
||||||
|
<data name="LearnMore_Hosts.Text" xml:space="preserve">
|
||||||
|
<value>Learn more about Hosts File Editor</value>
|
||||||
|
<comment>"Hosts File Editor" is the name of the utility</comment>
|
||||||
|
</data>
|
||||||
|
<data name="Oobe_Hosts.Description" xml:space="preserve">
|
||||||
|
<value>Hosts File Editor is a quick and simple utility for managing hosts file.</value>
|
||||||
|
<comment>"Hosts File Editor" is the name of the utility</comment>
|
||||||
|
</data>
|
||||||
|
<data name="Oobe_Hosts.Title" xml:space="preserve">
|
||||||
|
<value>Hosts File Editor</value>
|
||||||
|
<comment>"Hosts File Editor" is the name of the utility</comment>
|
||||||
|
</data>
|
||||||
|
<data name="Hosts_Toggle_LaunchAdministrator.Description" xml:space="preserve">
|
||||||
|
<value>Needs to be launched as administrator in order to make changes to the hosts file</value>
|
||||||
|
</data>
|
||||||
|
<data name="Hosts_Toggle_LaunchAdministrator.Header" xml:space="preserve">
|
||||||
|
<value>Launch as administrator</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
79
src/settings-ui/Settings.UI/Views/HostsPage.xaml
Normal file
79
src/settings-ui/Settings.UI/Views/HostsPage.xaml
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
<Page
|
||||||
|
x:Class="Microsoft.PowerToys.Settings.UI.Views.HostsPage"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:local="using:Microsoft.PowerToys.Settings.UI.Views"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:controls="using:Microsoft.PowerToys.Settings.UI.Controls"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||||
|
|
||||||
|
<controls:SettingsPageControl x:Uid="Hosts" IsTabStop="False"
|
||||||
|
ModuleImageSource="ms-appx:///Assets/Modules/AlwaysOnTop.png">
|
||||||
|
<controls:SettingsPageControl.ModuleContent>
|
||||||
|
<StackPanel Orientation="Vertical">
|
||||||
|
|
||||||
|
<controls:Setting x:Uid="Hosts_EnableToggleControl_HeaderText">
|
||||||
|
<controls:Setting.Icon>
|
||||||
|
<BitmapIcon UriSource="ms-appx:///Assets/FluentIcons/FluentIconsAlwaysOnTop.png" ShowAsMonochrome="False" />
|
||||||
|
</controls:Setting.Icon>
|
||||||
|
<controls:Setting.ActionContent>
|
||||||
|
<ToggleSwitch IsOn="{x:Bind ViewModel.IsEnabled, Mode=TwoWay}" x:Uid="ToggleSwitch"/>
|
||||||
|
</controls:Setting.ActionContent>
|
||||||
|
</controls:Setting>
|
||||||
|
|
||||||
|
<controls:SettingsGroup x:Uid="Hosts_Activation_GroupSettings"
|
||||||
|
IsEnabled="{x:Bind Mode=OneWay, Path=ViewModel.IsEnabled}">
|
||||||
|
<Button x:Uid="Hosts_LaunchButton_Accessible"
|
||||||
|
Style="{StaticResource SettingButtonStyle}"
|
||||||
|
Command="{x:Bind ViewModel.LaunchEventHandler}">
|
||||||
|
<controls:Setting x:Uid="Hosts_LaunchButtonControl"
|
||||||
|
Style="{StaticResource ExpanderHeaderSettingStyle}"
|
||||||
|
Icon="">
|
||||||
|
<controls:Setting.ActionContent>
|
||||||
|
<FontIcon Glyph=""
|
||||||
|
FontSize="18"
|
||||||
|
FontFamily="{ThemeResource SymbolThemeFontFamily}" />
|
||||||
|
</controls:Setting.ActionContent>
|
||||||
|
</controls:Setting>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<controls:Setting x:Uid="Hosts_Toggle_LaunchAdministrator"
|
||||||
|
Icon=""
|
||||||
|
IsEnabled="{x:Bind Mode=OneWay, Path=ViewModel.LaunchAdministratorEnabled}">
|
||||||
|
<controls:Setting.ActionContent>
|
||||||
|
<ToggleSwitch x:Uid="ToggleSwitch" IsOn="{x:Bind Mode=TwoWay, Path=ViewModel.LaunchAdministrator}" />
|
||||||
|
</controls:Setting.ActionContent>
|
||||||
|
</controls:Setting>
|
||||||
|
|
||||||
|
<controls:Setting x:Uid="Hosts_Toggle_ShowStartupWarning"
|
||||||
|
Icon=""
|
||||||
|
IsEnabled="{x:Bind Mode=OneWay, Path=ViewModel.IsEnabled}">
|
||||||
|
<controls:Setting.ActionContent>
|
||||||
|
<ToggleSwitch x:Uid="ToggleSwitch" IsOn="{x:Bind Mode=TwoWay, Path=ViewModel.ShowStartupWarning}" />
|
||||||
|
</controls:Setting.ActionContent>
|
||||||
|
</controls:Setting>
|
||||||
|
</controls:SettingsGroup>
|
||||||
|
|
||||||
|
<controls:SettingsGroup x:Uid="Hosts_File_GroupSettings"
|
||||||
|
IsEnabled="{x:Bind Mode=OneWay, Path=ViewModel.IsEnabled}">
|
||||||
|
<controls:Setting x:Uid="Hosts_AdditionalLinesPosition" Icon="" >
|
||||||
|
<controls:Setting.ActionContent>
|
||||||
|
<ComboBox MinWidth="{StaticResource SettingActionControlMinWidth}"
|
||||||
|
SelectedIndex="{x:Bind Path=ViewModel.AdditionalLinesPosition, Mode=TwoWay}" >
|
||||||
|
<ComboBoxItem x:Uid="Hosts_AdditionalLinesPosition_Top" />
|
||||||
|
<ComboBoxItem x:Uid="Hosts_AdditionalLinesPosition_Bottom" />
|
||||||
|
</ComboBox>
|
||||||
|
</controls:Setting.ActionContent>
|
||||||
|
</controls:Setting>
|
||||||
|
</controls:SettingsGroup>
|
||||||
|
|
||||||
|
</StackPanel>
|
||||||
|
</controls:SettingsPageControl.ModuleContent>
|
||||||
|
|
||||||
|
<controls:SettingsPageControl.PrimaryLinks>
|
||||||
|
<controls:PageLink x:Uid="LearnMore_Hosts" Link="https://aka.ms/PowerToysOverview_HostsFileEditor"/>
|
||||||
|
</controls:SettingsPageControl.PrimaryLinks>
|
||||||
|
</controls:SettingsPageControl>
|
||||||
|
</Page>
|
22
src/settings-ui/Settings.UI/Views/HostsPage.xaml.cs
Normal file
22
src/settings-ui/Settings.UI/Views/HostsPage.xaml.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using Microsoft.PowerToys.Settings.UI.Library;
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using Settings.UI.Library.ViewModels;
|
||||||
|
|
||||||
|
namespace Microsoft.PowerToys.Settings.UI.Views
|
||||||
|
{
|
||||||
|
public sealed partial class HostsPage : Page
|
||||||
|
{
|
||||||
|
private HostsViewModel ViewModel { get; }
|
||||||
|
|
||||||
|
public HostsPage()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
var settingsUtils = new SettingsUtils();
|
||||||
|
ViewModel = new HostsViewModel(settingsUtils, SettingsRepository<GeneralSettings>.GetInstance(settingsUtils), SettingsRepository<HostsSettings>.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage, App.IsElevated);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -76,6 +76,12 @@
|
|||||||
</NavigationViewItem.Icon>
|
</NavigationViewItem.Icon>
|
||||||
</NavigationViewItem>
|
</NavigationViewItem>
|
||||||
|
|
||||||
|
<NavigationViewItem x:Uid="Shell_Hosts" helpers:NavHelper.NavigateTo="views:HostsPage" >
|
||||||
|
<NavigationViewItem.Icon>
|
||||||
|
<BitmapIcon ShowAsMonochrome="False" UriSource="ms-appx:///Assets/FluentIcons/FluentIconsSettings.png" />
|
||||||
|
</NavigationViewItem.Icon>
|
||||||
|
</NavigationViewItem>
|
||||||
|
|
||||||
<NavigationViewItem x:Uid="Shell_ImageResizer" helpers:NavHelper.NavigateTo="views:ImageResizerPage">
|
<NavigationViewItem x:Uid="Shell_ImageResizer" helpers:NavHelper.NavigateTo="views:ImageResizerPage">
|
||||||
<NavigationViewItem.Icon>
|
<NavigationViewItem.Icon>
|
||||||
<BitmapIcon ShowAsMonochrome="False" UriSource="ms-appx:///Assets/FluentIcons/FluentIconsImageResizer.png" />
|
<BitmapIcon ShowAsMonochrome="False" UriSource="ms-appx:///Assets/FluentIcons/FluentIconsImageResizer.png" />
|
||||||
@@ -137,9 +143,6 @@
|
|||||||
<BitmapIcon ShowAsMonochrome="False" UriSource="ms-appx:///Assets/FluentIcons/FluentIconsVideoConferenceMute.png" />
|
<BitmapIcon ShowAsMonochrome="False" UriSource="ms-appx:///Assets/FluentIcons/FluentIconsVideoConferenceMute.png" />
|
||||||
</NavigationViewItem.Icon>
|
</NavigationViewItem.Icon>
|
||||||
</NavigationViewItem>
|
</NavigationViewItem>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</NavigationView.MenuItems>
|
</NavigationView.MenuItems>
|
||||||
<NavigationView.PaneFooter>
|
<NavigationView.PaneFooter>
|
||||||
<StackPanel Orientation="Vertical">
|
<StackPanel Orientation="Vertical">
|
||||||
|
@@ -20,5 +20,6 @@ std::vector<std::wstring> processes =
|
|||||||
L"PowerToys.ImageResizer.exe",
|
L"PowerToys.ImageResizer.exe",
|
||||||
L"PowerToys.Update.exe",
|
L"PowerToys.Update.exe",
|
||||||
L"PowerToys.ActionRunner.exe",
|
L"PowerToys.ActionRunner.exe",
|
||||||
L"PowerToys.AlwaysOnTop.exe"
|
L"PowerToys.AlwaysOnTop.exe",
|
||||||
|
L"PowerToys.Hosts.exe"
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user