Кнопка обновления и столбец актуального значения
This commit is contained in:
@@ -36,6 +36,16 @@ public sealed class LinuxEnvironmentVariableStore : IEnvironmentVariableStore
|
|||||||
return System.Environment.GetEnvironmentVariable(name);
|
return System.Environment.GetEnvironmentVariable(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string? GetUserPersistedValue(string name)
|
||||||
|
{
|
||||||
|
if (LoadMergedFromDirectory().TryGetValue(name, out var value))
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public void Set(string name, string value)
|
public void Set(string name, string value)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using Microsoft.Win32;
|
||||||
using Sms.Environment;
|
using Sms.Environment;
|
||||||
|
|
||||||
namespace Sms.Environment.Windows;
|
namespace Sms.Environment.Windows;
|
||||||
@@ -7,14 +8,23 @@ public sealed class WindowsEnvironmentVariableStore : IEnvironmentVariableStore
|
|||||||
{
|
{
|
||||||
private const int HWND_BROADCAST = 0xffff;
|
private const int HWND_BROADCAST = 0xffff;
|
||||||
private const int WM_SETTINGCHANGE = 0x001A;
|
private const int WM_SETTINGCHANGE = 0x001A;
|
||||||
|
private const string EnvironmentKeyPath = "Environment";
|
||||||
|
|
||||||
public string? Get(string name) =>
|
public string? Get(string name) =>
|
||||||
System.Environment.GetEnvironmentVariable(name, EnvironmentVariableTarget.User)
|
GetUserPersistedValue(name)
|
||||||
?? System.Environment.GetEnvironmentVariable(name, EnvironmentVariableTarget.Process);
|
?? System.Environment.GetEnvironmentVariable(name, EnvironmentVariableTarget.Process);
|
||||||
|
|
||||||
|
public string? GetUserPersistedValue(string name)
|
||||||
|
{
|
||||||
|
using var key = Registry.CurrentUser.OpenSubKey(EnvironmentKeyPath);
|
||||||
|
return key?.GetValue(name) as string;
|
||||||
|
}
|
||||||
|
|
||||||
public void Set(string name, string value)
|
public void Set(string name, string value)
|
||||||
{
|
{
|
||||||
System.Environment.SetEnvironmentVariable(name, value, EnvironmentVariableTarget.User);
|
using var key = Registry.CurrentUser.OpenSubKey(EnvironmentKeyPath, writable: true)
|
||||||
|
?? Registry.CurrentUser.CreateSubKey(EnvironmentKeyPath, writable: true);
|
||||||
|
key.SetValue(name, value, RegistryValueKind.ExpandString);
|
||||||
System.Environment.SetEnvironmentVariable(name, value, EnvironmentVariableTarget.Process);
|
System.Environment.SetEnvironmentVariable(name, value, EnvironmentVariableTarget.Process);
|
||||||
BroadcastEnvironmentChange();
|
BroadcastEnvironmentChange();
|
||||||
}
|
}
|
||||||
@@ -39,15 +49,36 @@ public sealed class WindowsEnvironmentVariableStore : IEnvironmentVariableStore
|
|||||||
public IReadOnlyDictionary<string, string> GetProcessEnvironment() =>
|
public IReadOnlyDictionary<string, string> GetProcessEnvironment() =>
|
||||||
ToDictionary(System.Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Process));
|
ToDictionary(System.Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Process));
|
||||||
|
|
||||||
public IReadOnlyDictionary<string, string> GetUserPersistedEnvironment() =>
|
public IReadOnlyDictionary<string, string> GetUserPersistedEnvironment()
|
||||||
ToDictionary(System.Environment.GetEnvironmentVariables(EnvironmentVariableTarget.User));
|
{
|
||||||
|
var result = new Dictionary<string, string>(StringComparer.Ordinal);
|
||||||
|
using var key = Registry.CurrentUser.OpenSubKey(EnvironmentKeyPath);
|
||||||
|
if (key is null)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
public bool IsPersistedInUserStore(string name) =>
|
foreach (var name in key.GetValueNames())
|
||||||
System.Environment.GetEnvironmentVariable(name, EnvironmentVariableTarget.User) is not null;
|
{
|
||||||
|
if (key.GetValue(name) is string value)
|
||||||
|
{
|
||||||
|
result[name] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsPersistedInUserStore(string name)
|
||||||
|
{
|
||||||
|
using var key = Registry.CurrentUser.OpenSubKey(EnvironmentKeyPath);
|
||||||
|
return key?.GetValue(name) is not null;
|
||||||
|
}
|
||||||
|
|
||||||
public void RemoveFromUserStore(string name)
|
public void RemoveFromUserStore(string name)
|
||||||
{
|
{
|
||||||
System.Environment.SetEnvironmentVariable(name, null, EnvironmentVariableTarget.User);
|
using var key = Registry.CurrentUser.OpenSubKey(EnvironmentKeyPath, writable: true);
|
||||||
|
key?.DeleteValue(name, throwOnMissingValue: false);
|
||||||
System.Environment.SetEnvironmentVariable(name, null, EnvironmentVariableTarget.Process);
|
System.Environment.SetEnvironmentVariable(name, null, EnvironmentVariableTarget.Process);
|
||||||
BroadcastEnvironmentChange();
|
BroadcastEnvironmentChange();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ public interface IEnvironmentVariableStore
|
|||||||
{
|
{
|
||||||
string? Get(string name);
|
string? Get(string name);
|
||||||
|
|
||||||
|
string? GetUserPersistedValue(string name);
|
||||||
|
|
||||||
void Set(string name, string value);
|
void Set(string name, string value);
|
||||||
|
|
||||||
bool Exists(string name);
|
bool Exists(string name);
|
||||||
|
|||||||
@@ -41,10 +41,18 @@
|
|||||||
Click="OnCloseClick" />
|
Click="OnCloseClick" />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<CheckBox Grid.Row="1"
|
<Grid Grid.Row="1"
|
||||||
Margin="12,8,12,0"
|
Margin="12,8,12,0"
|
||||||
Content="Отображать все переменные"
|
ColumnDefinitions="*,Auto"
|
||||||
IsChecked="{Binding ShowAllVariables}" />
|
ColumnSpacing="8">
|
||||||
|
<CheckBox Content="Отображать все переменные"
|
||||||
|
IsChecked="{Binding ShowAllVariables}"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
<Button Grid.Column="1"
|
||||||
|
Content="Обновить"
|
||||||
|
Command="{Binding RefreshCommand}"
|
||||||
|
MinWidth="100" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
<Grid Grid.Row="2"
|
<Grid Grid.Row="2"
|
||||||
Margin="12,8,12,0"
|
Margin="12,8,12,0"
|
||||||
@@ -92,13 +100,21 @@
|
|||||||
<DataGridTextColumn Header="Поле"
|
<DataGridTextColumn Header="Поле"
|
||||||
Binding="{Binding Field}"
|
Binding="{Binding Field}"
|
||||||
IsReadOnly="True"
|
IsReadOnly="True"
|
||||||
Width="2*" />
|
Width="210"
|
||||||
<DataGridTextColumn Header="Значение"
|
MinWidth="150" />
|
||||||
Binding="{Binding Value, Mode=TwoWay}"
|
<DataGridTextColumn Header="Требуемое значение"
|
||||||
Width="3*" />
|
Binding="{Binding RequiredValue, Mode=TwoWay}"
|
||||||
|
Width="2*"
|
||||||
|
MinWidth="140" />
|
||||||
|
<DataGridTextColumn Header="Актуальное значение"
|
||||||
|
Binding="{Binding ActualValueDisplay}"
|
||||||
|
IsReadOnly="True"
|
||||||
|
Width="2*"
|
||||||
|
MinWidth="140" />
|
||||||
<DataGridTextColumn Header="Комментарий"
|
<DataGridTextColumn Header="Комментарий"
|
||||||
Binding="{Binding Comment, Mode=TwoWay}"
|
Binding="{Binding Comment, Mode=TwoWay}"
|
||||||
Width="3*" />
|
Width="240"
|
||||||
|
MinWidth="180" />
|
||||||
<DataGridTemplateColumn Header=""
|
<DataGridTemplateColumn Header=""
|
||||||
Width="80">
|
Width="80">
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ public sealed class EnvironmentVariablesService
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var value = ResolveValue(name);
|
var value = ResolveRequiredValue(name);
|
||||||
_store.Set(name, value);
|
_store.Set(name, value);
|
||||||
_log.WriteLine(
|
_log.WriteLine(
|
||||||
$"{DateTime.Now:yyyy-MM-dd HH:mm:ss} [INFO] Created configured variable: {name}={value}");
|
$"{DateTime.Now:yyyy-MM-dd HH:mm:ss} [INFO] Created configured variable: {name}={value}");
|
||||||
@@ -64,7 +64,7 @@ public sealed class EnvironmentVariablesService
|
|||||||
|
|
||||||
foreach (var name in _options.Names)
|
foreach (var name in _options.Names)
|
||||||
{
|
{
|
||||||
rows.Add(CreateRow(name, ResolveValue(name)));
|
rows.Add(CreateRow(name, ResolveRequiredValue(name)));
|
||||||
knownNames.Add(name);
|
knownNames.Add(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,7 +72,7 @@ public sealed class EnvironmentVariablesService
|
|||||||
{
|
{
|
||||||
if (knownNames.Add(name))
|
if (knownNames.Add(name))
|
||||||
{
|
{
|
||||||
rows.Add(CreateRow(name, ResolveValue(name)));
|
rows.Add(CreateRow(name, ResolveRequiredValue(name)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ public sealed class EnvironmentVariablesService
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
rows.Add(CreateRow(pair.Key, pair.Value));
|
rows.Add(CreateRow(pair.Key, ResolveRequiredValue(pair.Key)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return rows;
|
return rows;
|
||||||
@@ -137,14 +137,17 @@ public sealed class EnvironmentVariablesService
|
|||||||
public EnvironmentVariableRow GetRowSnapshot(string name)
|
public EnvironmentVariableRow GetRowSnapshot(string name)
|
||||||
{
|
{
|
||||||
ReloadMetadata();
|
ReloadMetadata();
|
||||||
return CreateRow(name, ResolveValue(name));
|
return CreateRow(name, ResolveRequiredValue(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetDisplayValue(string name) => ResolveValue(name);
|
public string GetDisplayValue(string name) => ResolveRequiredValue(name);
|
||||||
|
|
||||||
|
public string? GetProcessValue(string name) =>
|
||||||
|
_store.GetProcessEnvironment().TryGetValue(name, out var value) ? value : null;
|
||||||
|
|
||||||
public void SaveValue(string name, string value)
|
public void SaveValue(string name, string value)
|
||||||
{
|
{
|
||||||
var previous = _store.Get(name);
|
var previous = _store.GetUserPersistedValue(name);
|
||||||
_store.Set(name, value);
|
_store.Set(name, value);
|
||||||
_log.WriteLine(
|
_log.WriteLine(
|
||||||
$"{DateTime.Now:yyyy-MM-dd HH:mm:ss} [INFO] Changed value: {name}={value} (previous: {previous ?? "<none>"})");
|
$"{DateTime.Now:yyyy-MM-dd HH:mm:ss} [INFO] Changed value: {name}={value} (previous: {previous ?? "<none>"})");
|
||||||
@@ -183,7 +186,7 @@ public sealed class EnvironmentVariablesService
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var previous = _store.Get(name);
|
var previous = _store.GetUserPersistedValue(name);
|
||||||
_store.RemoveFromUserStore(name);
|
_store.RemoveFromUserStore(name);
|
||||||
_log.WriteLine(
|
_log.WriteLine(
|
||||||
$"{DateTime.Now:yyyy-MM-dd HH:mm:ss} [INFO] Removed from user store: {name} (previous: {previous ?? "<none>"})");
|
$"{DateTime.Now:yyyy-MM-dd HH:mm:ss} [INFO] Removed from user store: {name} (previous: {previous ?? "<none>"})");
|
||||||
@@ -215,17 +218,21 @@ public sealed class EnvironmentVariablesService
|
|||||||
string.Equals(name, _options.CommentsVariableName, StringComparison.Ordinal)
|
string.Equals(name, _options.CommentsVariableName, StringComparison.Ordinal)
|
||||||
|| string.Equals(name, _options.CustomVariablesVariableName, StringComparison.Ordinal);
|
|| string.Equals(name, _options.CustomVariablesVariableName, StringComparison.Ordinal);
|
||||||
|
|
||||||
private string ResolveValue(string name)
|
private string ResolveRequiredValue(string name)
|
||||||
{
|
{
|
||||||
var existing = _store.Get(name);
|
if (_store.GetUserPersistedValue(name) is { } persisted)
|
||||||
if (existing is not null)
|
|
||||||
{
|
{
|
||||||
return existing;
|
return persisted;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_options.Defaults.TryGetValue(name, out var defaultValue))
|
if (_configuredNames.Contains(name) || _customNames.Contains(name))
|
||||||
{
|
{
|
||||||
return defaultValue;
|
if (_options.Defaults.TryGetValue(name, out var defaultValue))
|
||||||
|
{
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ public sealed partial class EnvironmentVariableRowViewModel : ObservableObject
|
|||||||
IsCustom = row.IsCustom;
|
IsCustom = row.IsCustom;
|
||||||
_service = service;
|
_service = service;
|
||||||
ApplySnapshot(row, suppressSave: true);
|
ApplySnapshot(row, suppressSave: true);
|
||||||
|
RefreshActualValue(_service.GetProcessValue(Field));
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Field { get; }
|
public string Field { get; }
|
||||||
@@ -28,10 +29,16 @@ public sealed partial class EnvironmentVariableRowViewModel : ObservableObject
|
|||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private bool _isPersistedInUserStore;
|
private bool _isPersistedInUserStore;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _actualValue = string.Empty;
|
||||||
|
|
||||||
|
public string ActualValueDisplay =>
|
||||||
|
string.IsNullOrEmpty(ActualValue) ? "<нет>" : ActualValue;
|
||||||
|
|
||||||
public string UserStoreBadge => IsPersistedInUserStore ? "USER" : string.Empty;
|
public string UserStoreBadge => IsPersistedInUserStore ? "USER" : string.Empty;
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private string _value = string.Empty;
|
private string _requiredValue = string.Empty;
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private string _comment = string.Empty;
|
private string _comment = string.Empty;
|
||||||
@@ -43,30 +50,9 @@ public sealed partial class EnvironmentVariableRowViewModel : ObservableObject
|
|||||||
RowAppearanceChanged?.Invoke(this, EventArgs.Empty);
|
RowAppearanceChanged?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
public event EventHandler? RowAppearanceChanged;
|
partial void OnActualValueChanged(string value) => OnPropertyChanged(nameof(ActualValueDisplay));
|
||||||
|
|
||||||
public void ApplySnapshot(EnvironmentVariableRow row, bool suppressSave = false)
|
partial void OnRequiredValueChanged(string value)
|
||||||
{
|
|
||||||
if (suppressSave)
|
|
||||||
{
|
|
||||||
BeginLoad();
|
|
||||||
}
|
|
||||||
|
|
||||||
Value = row.Value;
|
|
||||||
Comment = row.Comment;
|
|
||||||
IsPersistedInUserStore = row.IsPersistedInUserStore;
|
|
||||||
|
|
||||||
if (suppressSave)
|
|
||||||
{
|
|
||||||
EndLoad();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void BeginLoad() => _isLoading = true;
|
|
||||||
|
|
||||||
public void EndLoad() => _isLoading = false;
|
|
||||||
|
|
||||||
partial void OnValueChanged(string value)
|
|
||||||
{
|
{
|
||||||
if (_isLoading)
|
if (_isLoading)
|
||||||
{
|
{
|
||||||
@@ -78,8 +64,38 @@ public sealed partial class EnvironmentVariableRowViewModel : ObservableObject
|
|||||||
{
|
{
|
||||||
IsPersistedInUserStore = true;
|
IsPersistedInUserStore = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RefreshActualValue(_service.GetProcessValue(Field));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public event EventHandler? RowAppearanceChanged;
|
||||||
|
|
||||||
|
public void ApplySnapshot(EnvironmentVariableRow row, bool suppressSave = false)
|
||||||
|
{
|
||||||
|
if (suppressSave)
|
||||||
|
{
|
||||||
|
BeginLoad();
|
||||||
|
}
|
||||||
|
|
||||||
|
RequiredValue = row.Value;
|
||||||
|
Comment = row.Comment;
|
||||||
|
IsPersistedInUserStore = row.IsPersistedInUserStore;
|
||||||
|
|
||||||
|
if (suppressSave)
|
||||||
|
{
|
||||||
|
EndLoad();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RefreshActualValue(string? processValue)
|
||||||
|
{
|
||||||
|
ActualValue = processValue ?? string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BeginLoad() => _isLoading = true;
|
||||||
|
|
||||||
|
public void EndLoad() => _isLoading = false;
|
||||||
|
|
||||||
partial void OnCommentChanged(string value)
|
partial void OnCommentChanged(string value)
|
||||||
{
|
{
|
||||||
if (_isLoading)
|
if (_isLoading)
|
||||||
@@ -103,8 +119,9 @@ public sealed partial class EnvironmentVariableRowViewModel : ObservableObject
|
|||||||
_service.DeleteFromUserStore(Field);
|
_service.DeleteFromUserStore(Field);
|
||||||
BeginLoad();
|
BeginLoad();
|
||||||
IsPersistedInUserStore = false;
|
IsPersistedInUserStore = false;
|
||||||
Value = _service.GetDisplayValue(Field);
|
RequiredValue = _service.GetDisplayValue(Field);
|
||||||
EndLoad();
|
EndLoad();
|
||||||
|
RefreshActualValue(_service.GetProcessValue(Field));
|
||||||
}
|
}
|
||||||
|
|
||||||
public event EventHandler? Removed;
|
public event EventHandler? Removed;
|
||||||
|
|||||||
@@ -35,6 +35,13 @@ public sealed partial class MainWindowViewModel : ObservableObject
|
|||||||
|
|
||||||
partial void OnNewVariableNameChanged(string value) => AddVariableCommand.NotifyCanExecuteChanged();
|
partial void OnNewVariableNameChanged(string value) => AddVariableCommand.NotifyCanExecuteChanged();
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private void Refresh()
|
||||||
|
{
|
||||||
|
SyncRowsFromService();
|
||||||
|
RefreshProcessStates();
|
||||||
|
}
|
||||||
|
|
||||||
[RelayCommand(CanExecute = nameof(CanAddVariable))]
|
[RelayCommand(CanExecute = nameof(CanAddVariable))]
|
||||||
private void AddVariable()
|
private void AddVariable()
|
||||||
{
|
{
|
||||||
@@ -121,6 +128,16 @@ public sealed partial class MainWindowViewModel : ObservableObject
|
|||||||
MoveItem(Rows, currentIndex, targetIndex);
|
MoveItem(Rows, currentIndex, targetIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RefreshProcessStates();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RefreshProcessStates()
|
||||||
|
{
|
||||||
|
foreach (var row in Rows)
|
||||||
|
{
|
||||||
|
row.RefreshActualValue(_service.GetProcessValue(row.Field));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void MoveItem(
|
private static void MoveItem(
|
||||||
|
|||||||
Reference in New Issue
Block a user