mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 03:37:59 +01:00
[Hosts]Improved save error handling (#28215)
This commit is contained in:
committed by
GitHub
parent
5e353640d7
commit
6c09565244
1
.github/actions/spell-check/expect.txt
vendored
1
.github/actions/spell-check/expect.txt
vendored
@@ -1849,6 +1849,7 @@ subquery
|
|||||||
subresource
|
subresource
|
||||||
Superbar
|
Superbar
|
||||||
sut
|
sut
|
||||||
|
svchost
|
||||||
SVE
|
SVE
|
||||||
SVGIn
|
SVGIn
|
||||||
SVGIO
|
SVGIO
|
||||||
|
|||||||
@@ -238,5 +238,17 @@ namespace Hosts.Tests
|
|||||||
var result = fileSystem.GetFile(service.HostsFilePath);
|
var result = fileSystem.GetFile(service.HostsFilePath);
|
||||||
Assert.AreEqual(result.TextContents, contentResult);
|
Assert.AreEqual(result.TextContents, contentResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Save_NotRunningElevatedException()
|
||||||
|
{
|
||||||
|
var fileSystem = new CustomMockFileSystem();
|
||||||
|
var userSettings = new Mock<IUserSettings>();
|
||||||
|
var elevationHelper = new Mock<IElevationHelper>();
|
||||||
|
elevationHelper.Setup(m => m.IsElevated).Returns(false);
|
||||||
|
|
||||||
|
var service = new HostsService(fileSystem, userSettings.Object, elevationHelper.Object);
|
||||||
|
await Assert.ThrowsExceptionAsync<NotRunningElevatedException>(async () => await service.WriteAsync("# Empty hosts file", Enumerable.Empty<Entry>()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,11 +122,11 @@ namespace Hosts.Helpers
|
|||||||
return new HostsData(entries, unparsedBuilder.ToString(), splittedEntries);
|
return new HostsData(entries, unparsedBuilder.ToString(), splittedEntries);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> WriteAsync(string additionalLines, IEnumerable<Entry> entries)
|
public async Task WriteAsync(string additionalLines, IEnumerable<Entry> entries)
|
||||||
{
|
{
|
||||||
if (!_elevationHelper.IsElevated)
|
if (!_elevationHelper.IsElevated)
|
||||||
{
|
{
|
||||||
return false;
|
throw new NotRunningElevatedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var lines = new List<string>();
|
var lines = new List<string>();
|
||||||
@@ -195,18 +195,11 @@ namespace Hosts.Helpers
|
|||||||
|
|
||||||
await _fileSystem.File.WriteAllLinesAsync(HostsFilePath, lines, Encoding);
|
await _fileSystem.File.WriteAllLinesAsync(HostsFilePath, lines, Encoding);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logger.LogError("Failed to write hosts file", ex);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
_fileSystemWatcher.EnableRaisingEvents = true;
|
_fileSystemWatcher.EnableRaisingEvents = true;
|
||||||
_asyncLock.Release();
|
_asyncLock.Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> PingAsync(string address)
|
public async Task<bool> PingAsync(string address)
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ namespace Hosts.Helpers
|
|||||||
|
|
||||||
Task<HostsData> ReadAsync();
|
Task<HostsData> ReadAsync();
|
||||||
|
|
||||||
Task<bool> WriteAsync(string additionalLines, IEnumerable<Entry> entries);
|
Task WriteAsync(string additionalLines, IEnumerable<Entry> entries);
|
||||||
|
|
||||||
Task<bool> PingAsync(string address);
|
Task<bool> PingAsync(string address);
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Hosts.Helpers
|
||||||
|
{
|
||||||
|
public class NotRunningElevatedException : Exception
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -390,6 +390,7 @@
|
|||||||
x:Uid="FileSaveError"
|
x:Uid="FileSaveError"
|
||||||
Margin="0,8,0,0"
|
Margin="0,8,0,0"
|
||||||
Severity="Error"
|
Severity="Error"
|
||||||
|
Message="{x:Bind ViewModel.ErrorMessage, Mode=TwoWay}"
|
||||||
IsOpen="{x:Bind ViewModel.Error, Mode=TwoWay}"
|
IsOpen="{x:Bind ViewModel.Error, Mode=TwoWay}"
|
||||||
Visibility="{x:Bind ViewModel.Error, Mode=TwoWay, Converter={StaticResource BoolToVisibilityConverter}}" />
|
Visibility="{x:Bind ViewModel.Error, Mode=TwoWay, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||||
|
|
||||||
|
|||||||
@@ -216,8 +216,16 @@
|
|||||||
<value>Hosts file was modified externally.</value>
|
<value>Hosts file was modified externally.</value>
|
||||||
<comment>"Hosts" refers to the system hosts file, do not loc</comment>
|
<comment>"Hosts" refers to the system hosts file, do not loc</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="FileSaveError.Message" xml:space="preserve">
|
<data name="FileSaveError_FileInUse" xml:space="preserve">
|
||||||
<value>Failed to save hosts file.</value>
|
<value>The hosts file cannot be saved because it is being used by another process.</value>
|
||||||
|
<comment>"Hosts" refers to the system hosts file, do not loc</comment>
|
||||||
|
</data>
|
||||||
|
<data name="FileSaveError_Generic" xml:space="preserve">
|
||||||
|
<value>Unable to save the hosts file.</value>
|
||||||
|
<comment>"Hosts" refers to the system hosts file, do not loc</comment>
|
||||||
|
</data>
|
||||||
|
<data name="FileSaveError_NotElevated" xml:space="preserve">
|
||||||
|
<value>The hosts file cannot be saved because the program isn't running as administrator.</value>
|
||||||
<comment>"Hosts" refers to the system hosts file, do not loc</comment>
|
<comment>"Hosts" refers to the system hosts file, do not loc</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="FilterBtn.[using:Microsoft.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
|
<data name="FilterBtn.[using:Microsoft.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@@ -44,6 +45,9 @@ namespace Hosts.ViewModels
|
|||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private bool _error;
|
private bool _error;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _errorMessage;
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private bool _fileChanged;
|
private bool _fileChanged;
|
||||||
|
|
||||||
@@ -126,12 +130,7 @@ namespace Hosts.ViewModels
|
|||||||
public void UpdateAdditionalLines(string lines)
|
public void UpdateAdditionalLines(string lines)
|
||||||
{
|
{
|
||||||
AdditionalLines = lines;
|
AdditionalLines = lines;
|
||||||
|
_ = Task.Run(SaveAsync);
|
||||||
Task.Run(async () =>
|
|
||||||
{
|
|
||||||
var error = !await _hostsService.WriteAsync(AdditionalLines, _entries);
|
|
||||||
await _dispatcherQueue.EnqueueAsync(() => Error = error);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Move(int oldIndex, int newIndex)
|
public void Move(int oldIndex, int newIndex)
|
||||||
@@ -287,20 +286,12 @@ namespace Hosts.ViewModels
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Task.Run(async () =>
|
_ = Task.Run(SaveAsync);
|
||||||
{
|
|
||||||
var error = !await _hostsService.WriteAsync(AdditionalLines, _entries);
|
|
||||||
await _dispatcherQueue.EnqueueAsync(() => Error = error);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Entries_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
|
private void Entries_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
|
||||||
{
|
{
|
||||||
Task.Run(async () =>
|
_ = Task.Run(SaveAsync);
|
||||||
{
|
|
||||||
var error = !await _hostsService.WriteAsync(AdditionalLines, _entries);
|
|
||||||
await _dispatcherQueue.EnqueueAsync(() => Error = error);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FindDuplicates(CancellationToken cancellationToken)
|
private void FindDuplicates(CancellationToken cancellationToken)
|
||||||
@@ -379,6 +370,41 @@ namespace Hosts.ViewModels
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task SaveAsync()
|
||||||
|
{
|
||||||
|
bool error = true;
|
||||||
|
string errorMessage = string.Empty;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _hostsService.WriteAsync(AdditionalLines, _entries);
|
||||||
|
error = false;
|
||||||
|
}
|
||||||
|
catch (NotRunningElevatedException)
|
||||||
|
{
|
||||||
|
var resourceLoader = ResourceLoaderInstance.ResourceLoader;
|
||||||
|
errorMessage = resourceLoader.GetString("FileSaveError_NotElevated");
|
||||||
|
}
|
||||||
|
catch (IOException ex) when ((ex.HResult & 0x0000FFFF) == 32)
|
||||||
|
{
|
||||||
|
// There are some edge cases where a big hosts file is being locked by svchost.exe https://github.com/microsoft/PowerToys/issues/28066
|
||||||
|
var resourceLoader = ResourceLoaderInstance.ResourceLoader;
|
||||||
|
errorMessage = resourceLoader.GetString("FileSaveError_FileInUse");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.LogError("Failed to save hosts file", ex);
|
||||||
|
var resourceLoader = ResourceLoaderInstance.ResourceLoader;
|
||||||
|
errorMessage = resourceLoader.GetString("FileSaveError_Generic");
|
||||||
|
}
|
||||||
|
|
||||||
|
await _dispatcherQueue.EnqueueAsync(() =>
|
||||||
|
{
|
||||||
|
Error = error;
|
||||||
|
ErrorMessage = errorMessage;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (!_disposed)
|
if (!_disposed)
|
||||||
|
|||||||
Reference in New Issue
Block a user