[Hosts]Improved save error handling (#28215)

This commit is contained in:
Davide Giacometti
2023-09-08 17:25:36 +02:00
committed by GitHub
parent 5e353640d7
commit 6c09565244
8 changed files with 81 additions and 28 deletions

View File

@@ -1849,6 +1849,7 @@ subquery
subresource
Superbar
sut
svchost
SVE
SVGIn
SVGIO

View File

@@ -238,5 +238,17 @@ namespace Hosts.Tests
var result = fileSystem.GetFile(service.HostsFilePath);
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>()));
}
}
}

View File

@@ -122,11 +122,11 @@ namespace Hosts.Helpers
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)
{
return false;
throw new NotRunningElevatedException();
}
var lines = new List<string>();
@@ -195,18 +195,11 @@ namespace Hosts.Helpers
await _fileSystem.File.WriteAllLinesAsync(HostsFilePath, lines, Encoding);
}
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)

View File

@@ -17,7 +17,7 @@ namespace Hosts.Helpers
Task<HostsData> ReadAsync();
Task<bool> WriteAsync(string additionalLines, IEnumerable<Entry> entries);
Task WriteAsync(string additionalLines, IEnumerable<Entry> entries);
Task<bool> PingAsync(string address);

View File

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

View File

@@ -390,6 +390,7 @@
x:Uid="FileSaveError"
Margin="0,8,0,0"
Severity="Error"
Message="{x:Bind ViewModel.ErrorMessage, Mode=TwoWay}"
IsOpen="{x:Bind ViewModel.Error, Mode=TwoWay}"
Visibility="{x:Bind ViewModel.Error, Mode=TwoWay, Converter={StaticResource BoolToVisibilityConverter}}" />

View File

@@ -216,8 +216,16 @@
<value>Hosts file was modified externally.</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>
<data name="FileSaveError_FileInUse" xml:space="preserve">
<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>
</data>
<data name="FilterBtn.[using:Microsoft.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">

View File

@@ -5,6 +5,7 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
@@ -44,6 +45,9 @@ namespace Hosts.ViewModels
[ObservableProperty]
private bool _error;
[ObservableProperty]
private string _errorMessage;
[ObservableProperty]
private bool _fileChanged;
@@ -126,12 +130,7 @@ namespace Hosts.ViewModels
public void UpdateAdditionalLines(string lines)
{
AdditionalLines = lines;
Task.Run(async () =>
{
var error = !await _hostsService.WriteAsync(AdditionalLines, _entries);
await _dispatcherQueue.EnqueueAsync(() => Error = error);
});
_ = Task.Run(SaveAsync);
}
public void Move(int oldIndex, int newIndex)
@@ -287,20 +286,12 @@ namespace Hosts.ViewModels
return;
}
Task.Run(async () =>
{
var error = !await _hostsService.WriteAsync(AdditionalLines, _entries);
await _dispatcherQueue.EnqueueAsync(() => Error = error);
});
_ = Task.Run(SaveAsync);
}
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);
});
_ = Task.Run(SaveAsync);
}
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)
{
if (!_disposed)