mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-15 11:17:53 +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
|
||||
Superbar
|
||||
sut
|
||||
svchost
|
||||
SVE
|
||||
SVGIn
|
||||
SVGIO
|
||||
|
||||
@@ -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>()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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"
|
||||
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}}" />
|
||||
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user