Убраны попытки применить переменные окружения к сессии
This commit is contained in:
@@ -42,7 +42,7 @@ dotnet run --project src/Sms.TaskTwo.Avalonia/Sms.TaskTwo.Avalonia.csproj
|
|||||||
{"SMS_MEAL_SERVER_URL":"URL сервера","SMS_MEAL_API_KEY":"ключ API"}
|
{"SMS_MEAL_SERVER_URL":"URL сервера","SMS_MEAL_API_KEY":"ключ API"}
|
||||||
```
|
```
|
||||||
|
|
||||||
Переменная записывается тем же механизмом, что и остальные, и доступна другим процессам после применения окружения ОС.
|
Переменная записывается тем же механизмом, что и остальные, и доступна другим процессам после перезапуска сессии или приложений.
|
||||||
|
|
||||||
## Логирование
|
## Логирование
|
||||||
|
|
||||||
@@ -72,12 +72,10 @@ dotnet run --project src/Sms.TaskTwo.Avalonia/Sms.TaskTwo.Avalonia.csproj
|
|||||||
### Windows
|
### Windows
|
||||||
|
|
||||||
- Чтение/запись: реестр `HKEY_CURRENT_USER\Environment`.
|
- Чтение/запись: реестр `HKEY_CURRENT_USER\Environment`.
|
||||||
- **Применить к сессии** (`ReloadEnvironment`): обновляет env текущего процесса и рассылает `WM_SETTINGCHANGE` для других GUI-приложений.
|
|
||||||
|
|
||||||
### Linux
|
### Linux
|
||||||
|
|
||||||
- Запись в `~/.config/environment.d/` (systemd `KEY=value`).
|
- Запись в `~/.config/environment.d/` (systemd `KEY=value`); переменные подхватываются после перезапуска login-сессии.
|
||||||
- **Применить к сессии** не поддерживается: переменные подхватываются после перезапуска login-сессии.
|
|
||||||
|
|
||||||
## Предположения (ТЗ)
|
## Предположения (ТЗ)
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,6 @@ public sealed class LinuxEnvironmentVariableStore : IEnvironmentVariableStore
|
|||||||
var managed = LoadManagedFile();
|
var managed = LoadManagedFile();
|
||||||
managed[name] = value;
|
managed[name] = value;
|
||||||
WriteManagedFileAtomic(managed);
|
WriteManagedFileAtomic(managed);
|
||||||
System.Environment.SetEnvironmentVariable(name, value);
|
|
||||||
}
|
}
|
||||||
catch (Exception ex) when (ex is UnauthorizedAccessException or IOException)
|
catch (Exception ex) when (ex is UnauthorizedAccessException or IOException)
|
||||||
{
|
{
|
||||||
@@ -115,8 +114,6 @@ public sealed class LinuxEnvironmentVariableStore : IEnvironmentVariableStore
|
|||||||
|
|
||||||
WriteConfFileAtomic(file, variables);
|
WriteConfFileAtomic(file, variables);
|
||||||
}
|
}
|
||||||
|
|
||||||
System.Environment.SetEnvironmentVariable(name, null);
|
|
||||||
}
|
}
|
||||||
catch (Exception ex) when (ex is UnauthorizedAccessException or IOException)
|
catch (Exception ex) when (ex is UnauthorizedAccessException or IOException)
|
||||||
{
|
{
|
||||||
@@ -126,15 +123,6 @@ public sealed class LinuxEnvironmentVariableStore : IEnvironmentVariableStore
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public EnvironmentReloadResult ReloadEnvironment() =>
|
|
||||||
new()
|
|
||||||
{
|
|
||||||
Success = false,
|
|
||||||
Message =
|
|
||||||
"Применение переменных к сессии в Linux не поддерживается. " +
|
|
||||||
"Значения сохраняются в ~/.config/environment.d/ и подхватываются после перезапуска login-сессии.",
|
|
||||||
};
|
|
||||||
|
|
||||||
private Dictionary<string, string> LoadManagedFile()
|
private Dictionary<string, string> LoadManagedFile()
|
||||||
{
|
{
|
||||||
if (!File.Exists(_managedFilePath))
|
if (!File.Exists(_managedFilePath))
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
using System.Runtime.InteropServices;
|
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
using Sms.Environment;
|
using Sms.Environment;
|
||||||
|
|
||||||
@@ -6,8 +5,6 @@ namespace Sms.Environment.Windows;
|
|||||||
|
|
||||||
public sealed class WindowsEnvironmentVariableStore : IEnvironmentVariableStore
|
public sealed class WindowsEnvironmentVariableStore : IEnvironmentVariableStore
|
||||||
{
|
{
|
||||||
private const int HWND_BROADCAST = 0xffff;
|
|
||||||
private const int WM_SETTINGCHANGE = 0x001A;
|
|
||||||
private const string EnvironmentKeyPath = "Environment";
|
private const string EnvironmentKeyPath = "Environment";
|
||||||
|
|
||||||
public string? Get(string name) =>
|
public string? Get(string name) =>
|
||||||
@@ -25,8 +22,6 @@ public sealed class WindowsEnvironmentVariableStore : IEnvironmentVariableStore
|
|||||||
using var key = Registry.CurrentUser.OpenSubKey(EnvironmentKeyPath, writable: true)
|
using var key = Registry.CurrentUser.OpenSubKey(EnvironmentKeyPath, writable: true)
|
||||||
?? Registry.CurrentUser.CreateSubKey(EnvironmentKeyPath, writable: true);
|
?? Registry.CurrentUser.CreateSubKey(EnvironmentKeyPath, writable: true);
|
||||||
key.SetValue(name, value, RegistryValueKind.ExpandString);
|
key.SetValue(name, value, RegistryValueKind.ExpandString);
|
||||||
System.Environment.SetEnvironmentVariable(name, value, EnvironmentVariableTarget.Process);
|
|
||||||
BroadcastEnvironmentChange();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Exists(string name) => Get(name) is not null;
|
public bool Exists(string name) => Get(name) is not null;
|
||||||
@@ -79,27 +74,6 @@ public sealed class WindowsEnvironmentVariableStore : IEnvironmentVariableStore
|
|||||||
{
|
{
|
||||||
using var key = Registry.CurrentUser.OpenSubKey(EnvironmentKeyPath, writable: true);
|
using var key = Registry.CurrentUser.OpenSubKey(EnvironmentKeyPath, writable: true);
|
||||||
key?.DeleteValue(name, throwOnMissingValue: false);
|
key?.DeleteValue(name, throwOnMissingValue: false);
|
||||||
System.Environment.SetEnvironmentVariable(name, null, EnvironmentVariableTarget.Process);
|
|
||||||
BroadcastEnvironmentChange();
|
|
||||||
}
|
|
||||||
|
|
||||||
public EnvironmentReloadResult ReloadEnvironment()
|
|
||||||
{
|
|
||||||
var variables = GetUserPersistedEnvironment();
|
|
||||||
foreach (var pair in variables)
|
|
||||||
{
|
|
||||||
System.Environment.SetEnvironmentVariable(pair.Key, pair.Value, EnvironmentVariableTarget.Process);
|
|
||||||
}
|
|
||||||
|
|
||||||
BroadcastEnvironmentChange();
|
|
||||||
|
|
||||||
return new EnvironmentReloadResult
|
|
||||||
{
|
|
||||||
Success = true,
|
|
||||||
Message = variables.Count == 0
|
|
||||||
? "Пользовательских переменных для применения нет."
|
|
||||||
: $"Применено {variables.Count} переменных к текущему процессу и отправлено WM_SETTINGCHANGE для других приложений.",
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Dictionary<string, string> ToDictionary(System.Collections.IDictionary source)
|
private static Dictionary<string, string> ToDictionary(System.Collections.IDictionary source)
|
||||||
@@ -112,36 +86,4 @@ public sealed class WindowsEnvironmentVariableStore : IEnvironmentVariableStore
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void BroadcastEnvironmentChange()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_ = NativeMethods.SendMessageTimeout(
|
|
||||||
HWND_BROADCAST,
|
|
||||||
WM_SETTINGCHANGE,
|
|
||||||
IntPtr.Zero,
|
|
||||||
"Environment",
|
|
||||||
0,
|
|
||||||
1000,
|
|
||||||
out _);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// Non-critical: new processes still see updated registry values.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class NativeMethods
|
|
||||||
{
|
|
||||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
|
||||||
public static extern IntPtr SendMessageTimeout(
|
|
||||||
int hWnd,
|
|
||||||
int msg,
|
|
||||||
IntPtr wParam,
|
|
||||||
string lParam,
|
|
||||||
int fuFlags,
|
|
||||||
int uTimeout,
|
|
||||||
out IntPtr lpdwResult);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
namespace Sms.Environment;
|
|
||||||
|
|
||||||
public sealed class EnvironmentReloadResult
|
|
||||||
{
|
|
||||||
public required bool Success { get; init; }
|
|
||||||
|
|
||||||
public required string Message { get; init; }
|
|
||||||
}
|
|
||||||
@@ -19,6 +19,4 @@ public interface IEnvironmentVariableStore
|
|||||||
bool IsPersistedInUserStore(string name);
|
bool IsPersistedInUserStore(string name);
|
||||||
|
|
||||||
void RemoveFromUserStore(string name);
|
void RemoveFromUserStore(string name);
|
||||||
|
|
||||||
EnvironmentReloadResult ReloadEnvironment();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
<Grid RowDefinitions="Auto,Auto,Auto,*">
|
<Grid RowDefinitions="Auto,Auto,Auto,*">
|
||||||
<Grid Grid.Row="0"
|
<Grid Grid.Row="0"
|
||||||
Margin="12,12,12,0"
|
Margin="12,12,12,0"
|
||||||
ColumnDefinitions="*,Auto,Auto"
|
ColumnDefinitions="*,Auto"
|
||||||
ColumnSpacing="8">
|
ColumnSpacing="8">
|
||||||
<CheckBox Content="Отображать все переменные"
|
<CheckBox Content="Отображать все переменные"
|
||||||
IsChecked="{Binding ShowAllVariables}"
|
IsChecked="{Binding ShowAllVariables}"
|
||||||
@@ -31,10 +31,6 @@
|
|||||||
Content="Обновить"
|
Content="Обновить"
|
||||||
Command="{Binding RefreshCommand}"
|
Command="{Binding RefreshCommand}"
|
||||||
MinWidth="100" />
|
MinWidth="100" />
|
||||||
<Button Grid.Column="2"
|
|
||||||
Content="Применить к сессии"
|
|
||||||
Command="{Binding ReloadEnvironmentCommand}"
|
|
||||||
MinWidth="140" />
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid Grid.Row="1"
|
<Grid Grid.Row="1"
|
||||||
@@ -62,10 +58,6 @@
|
|||||||
<TextBlock Foreground="#C62828"
|
<TextBlock Foreground="#C62828"
|
||||||
Text="{Binding AddVariableError}"
|
Text="{Binding AddVariableError}"
|
||||||
IsVisible="{Binding AddVariableError, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" />
|
IsVisible="{Binding AddVariableError, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" />
|
||||||
<TextBlock Foreground="#2E7D32"
|
|
||||||
Text="{Binding ReloadEnvironmentMessage}"
|
|
||||||
TextWrapping="Wrap"
|
|
||||||
IsVisible="{Binding ReloadEnvironmentMessage, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" />
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<Border Grid.Row="3"
|
<Border Grid.Row="3"
|
||||||
@@ -107,11 +99,6 @@
|
|||||||
Binding="{Binding RequiredValue, Mode=TwoWay}"
|
Binding="{Binding RequiredValue, Mode=TwoWay}"
|
||||||
Width="2*"
|
Width="2*"
|
||||||
MinWidth="140" />
|
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="240"
|
Width="240"
|
||||||
|
|||||||
@@ -142,17 +142,6 @@ public sealed class EnvironmentVariablesService
|
|||||||
|
|
||||||
public string GetDisplayValue(string name) => ResolveRequiredValue(name);
|
public string GetDisplayValue(string name) => ResolveRequiredValue(name);
|
||||||
|
|
||||||
public string? GetProcessValue(string name) =>
|
|
||||||
_store.GetProcessEnvironment().TryGetValue(name, out var value) ? value : null;
|
|
||||||
|
|
||||||
public EnvironmentReloadResult ReloadEnvironment()
|
|
||||||
{
|
|
||||||
var result = _store.ReloadEnvironment();
|
|
||||||
_log.WriteLine(
|
|
||||||
$"{DateTime.Now:yyyy-MM-dd HH:mm:ss} [INFO] Reload environment: success={result.Success}; {result.Message}");
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SaveValue(string name, string value)
|
public void SaveValue(string name, string value)
|
||||||
{
|
{
|
||||||
var previous = _store.GetUserPersistedValue(name);
|
var previous = _store.GetUserPersistedValue(name);
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ public sealed partial class EnvironmentVariableRowViewModel : ObservableObject
|
|||||||
_service = service;
|
_service = service;
|
||||||
_onCustomRemoved = onCustomRemoved;
|
_onCustomRemoved = onCustomRemoved;
|
||||||
ApplySnapshot(row, suppressSave: true);
|
ApplySnapshot(row, suppressSave: true);
|
||||||
RefreshActualValue(_service.GetProcessValue(Field));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Field { get; }
|
public string Field { get; }
|
||||||
@@ -35,12 +34,6 @@ public sealed partial class EnvironmentVariableRowViewModel : ObservableObject
|
|||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private bool _useUserStore;
|
private bool _useUserStore;
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private string _actualValue = string.Empty;
|
|
||||||
|
|
||||||
public string ActualValueDisplay =>
|
|
||||||
string.IsNullOrEmpty(ActualValue) ? "<нет>" : ActualValue;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private string _requiredValue = string.Empty;
|
private string _requiredValue = string.Empty;
|
||||||
|
|
||||||
@@ -67,7 +60,6 @@ public sealed partial class EnvironmentVariableRowViewModel : ObservableObject
|
|||||||
if (value)
|
if (value)
|
||||||
{
|
{
|
||||||
_service.SaveValue(Field, RequiredValue);
|
_service.SaveValue(Field, RequiredValue);
|
||||||
RefreshActualValue(_service.GetProcessValue(Field));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,11 +74,8 @@ public sealed partial class EnvironmentVariableRowViewModel : ObservableObject
|
|||||||
BeginLoad();
|
BeginLoad();
|
||||||
RequiredValue = _service.GetDisplayValue(Field);
|
RequiredValue = _service.GetDisplayValue(Field);
|
||||||
EndLoad();
|
EndLoad();
|
||||||
RefreshActualValue(_service.GetProcessValue(Field));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
partial void OnActualValueChanged(string value) => OnPropertyChanged(nameof(ActualValueDisplay));
|
|
||||||
|
|
||||||
partial void OnRequiredValueChanged(string value)
|
partial void OnRequiredValueChanged(string value)
|
||||||
{
|
{
|
||||||
if (_isLoading || !UseUserStore)
|
if (_isLoading || !UseUserStore)
|
||||||
@@ -95,7 +84,6 @@ public sealed partial class EnvironmentVariableRowViewModel : ObservableObject
|
|||||||
}
|
}
|
||||||
|
|
||||||
_service.SaveValue(Field, value);
|
_service.SaveValue(Field, value);
|
||||||
RefreshActualValue(_service.GetProcessValue(Field));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public event EventHandler? RowAppearanceChanged;
|
public event EventHandler? RowAppearanceChanged;
|
||||||
@@ -117,11 +105,6 @@ public sealed partial class EnvironmentVariableRowViewModel : ObservableObject
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RefreshActualValue(string? processValue)
|
|
||||||
{
|
|
||||||
ActualValue = processValue ?? string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void BeginLoad() => _isLoading = true;
|
public void BeginLoad() => _isLoading = true;
|
||||||
|
|
||||||
public void EndLoad() => _isLoading = false;
|
public void EndLoad() => _isLoading = false;
|
||||||
|
|||||||
@@ -32,27 +32,12 @@ public sealed partial class MainWindowViewModel : ObservableObject
|
|||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private string? _addVariableError;
|
private string? _addVariableError;
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private string? _reloadEnvironmentMessage;
|
|
||||||
|
|
||||||
partial void OnShowAllVariablesChanged(bool value) => SyncRowsFromService();
|
partial void OnShowAllVariablesChanged(bool value) => SyncRowsFromService();
|
||||||
|
|
||||||
partial void OnNewVariableNameChanged(string value) => AddVariableCommand.NotifyCanExecuteChanged();
|
partial void OnNewVariableNameChanged(string value) => AddVariableCommand.NotifyCanExecuteChanged();
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
private void Refresh()
|
private void Refresh() => SyncRowsFromService();
|
||||||
{
|
|
||||||
SyncRowsFromService();
|
|
||||||
RefreshProcessStates();
|
|
||||||
}
|
|
||||||
|
|
||||||
[RelayCommand]
|
|
||||||
private void ReloadEnvironment()
|
|
||||||
{
|
|
||||||
var result = _service.ReloadEnvironment();
|
|
||||||
ReloadEnvironmentMessage = result.Message;
|
|
||||||
RefreshProcessStates();
|
|
||||||
}
|
|
||||||
|
|
||||||
[RelayCommand(CanExecute = nameof(CanAddVariable))]
|
[RelayCommand(CanExecute = nameof(CanAddVariable))]
|
||||||
private void AddVariable()
|
private void AddVariable()
|
||||||
@@ -142,16 +127,6 @@ 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