Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitfcefec4

Browse files
committed
settings manager moved from generic to explicit settings
1 parent779c11b commitfcefec4

File tree

5 files changed

+183
-141
lines changed

5 files changed

+183
-141
lines changed

‎App/App.xaml.cs

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,8 @@ public App()
9292
// FileSyncListMainPage is created by FileSyncListWindow.
9393
services.AddTransient<FileSyncListWindow>();
9494

95-
services.AddSingleton<ISettingsManager>(_=>newSettingsManager("CoderDesktop"));
95+
services.AddSingleton<ISettingsManager,SettingsManager>();
96+
services.AddSingleton<IStartupManager,StartupManager>();
9697
// SettingsWindow views and view models
9798
services.AddTransient<SettingsViewModel>();
9899
// SettingsMainPage is created by SettingsWindow.
@@ -159,10 +160,6 @@ protected override void OnLaunched(LaunchActivatedEventArgs args)
159160

160161
// Start connecting to the manager in the background.
161162
varrpcController=_services.GetRequiredService<IRpcController>();
162-
if(rpcController.GetState().RpcLifecycle==RpcLifecycle.Disconnected)
163-
// Passing in a CT with no cancellation is desired here, because
164-
// the named pipe open will block until the pipe comes up.
165-
_logger.LogDebug("reconnecting with VPN service");
166163
_=rpcController.Reconnect(CancellationToken.None).ContinueWith(t=>
167164
{
168165
if(t.Exception!=null)
@@ -172,22 +169,18 @@ protected override void OnLaunched(LaunchActivatedEventArgs args)
172169
Debug.WriteLine(t.Exception);
173170
Debugger.Break();
174171
#endif
175-
}else
172+
return;
173+
}
174+
if(_settingsManager.ConnectOnLaunch)
176175
{
177-
if(rpcController.GetState().VpnLifecycle==VpnLifecycle.Stopped)
176+
_logger.LogInformation("RPC lifecycle is disconnected, but ConnectOnLaunch is enabled; attempting to connect");
177+
_=rpcController.StartVpn(CancellationToken.None).ContinueWith(connectTask=>
178178
{
179-
if(_settingsManager.Read(SettingsManager.ConnectOnLaunchKey,false))
179+
if(connectTask.Exception!=null)
180180
{
181-
_logger.LogInformation("RPC lifecycle is disconnected, but ConnectOnLaunch is enabled; attempting to connect");
182-
_=rpcController.StartVpn(CancellationToken.None).ContinueWith(connectTask=>
183-
{
184-
if(connectTask.Exception!=null)
185-
{
186-
_logger.LogError(connectTask.Exception,"failed to connect on launch");
187-
}
188-
});
181+
_logger.LogError(connectTask.Exception,"failed to connect on launch");
189182
}
190-
}
183+
});
191184
}
192185
});
193186

‎App/Services/SettingsManager.cs

Lines changed: 83 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -4,110 +4,150 @@
44
usingSystem.Text.Json;
55

66
namespaceCoder.Desktop.App.Services;
7+
78
/// <summary>
8-
///Generic persistencecontractfor simple key/value settings.
9+
///Settingscontractexposing properties for app settings.
910
/// </summary>
1011
publicinterfaceISettingsManager
1112
{
1213
/// <summary>
13-
///Saves <paramref name="value"/> under <paramref name="name"/> and returnsthevalue.
14+
///Returns thevalue of the StartOnLogin setting. Returns <c>false</c> ifthekey is not found.
1415
/// </summary>
15-
TSave<T>(stringname,Tvalue);
16+
boolStartOnLogin{get;set;}
1617

1718
/// <summary>
18-
///Reads thesetting or returns <paramref name="defaultValue"/> when the key ismissing.
19+
///Returns thevalue of the ConnectOnLaunch setting. Returns <c>false</c> if the key isnot found.
1920
/// </summary>
20-
TRead<T>(stringname,TdefaultValue);
21+
boolConnectOnLaunch{get;set;}
2122
}
23+
2224
/// <summary>
23-
/// JSON‑file implementation that works in unpackaged Win32/WinUI 3 apps.
25+
/// Implemention of <see cref="ISettingsManager"/> that persists settings to a JSON file
26+
/// located in the user's local application data folder.
2427
/// </summary>
2528
publicsealedclassSettingsManager:ISettingsManager
2629
{
2730
privatereadonlystring_settingsFilePath;
2831
privatereadonlystring_fileName="app-settings.json";
32+
privatereadonlystring_appName="CoderDesktop";
2933
privatereadonlyobject_lock=new();
3034
privateDictionary<string,JsonElement>_cache;
3135

32-
publicstaticreadonlystringConnectOnLaunchKey="ConnectOnLaunch";
33-
publicstaticreadonlystringStartOnLoginKey="StartOnLogin";
36+
publicconststringConnectOnLaunchKey="ConnectOnLaunch";
37+
publicconststringStartOnLoginKey="StartOnLogin";
3438

35-
/// <param name="appName">
36-
/// Sub‑folder under %LOCALAPPDATA% (e.g. "CoderDesktop").
37-
/// If <c>null</c> the folder name defaults to the executable name.
39+
publicboolStartOnLogin
40+
{
41+
get
42+
{
43+
returnRead(StartOnLoginKey,false);
44+
}
45+
set
46+
{
47+
Save(StartOnLoginKey,value);
48+
}
49+
}
50+
51+
publicboolConnectOnLaunch
52+
{
53+
get
54+
{
55+
returnRead(ConnectOnLaunchKey,false);
56+
}
57+
set
58+
{
59+
Save(ConnectOnLaunchKey,value);
60+
}
61+
}
62+
63+
/// <param name="settingsFilePath">
3864
/// For unit‑tests you can pass an absolute path that already exists.
65+
/// Otherwise the settings file will be created in the user's local application data folder.
3966
/// </param>
40-
publicSettingsManager(string?appName=null)
67+
publicSettingsManager(string?settingsFilePath=null)
4168
{
42-
// Allow unit‑tests to inject a fully‑qualified path.
43-
if(appNameis notnull&&Path.IsPathRooted(appName))
69+
if(settingsFilePathisnull)
4470
{
45-
_settingsFilePath=Path.Combine(appName,_fileName);
46-
Directory.CreateDirectory(Path.GetDirectoryName(_settingsFilePath)!);
71+
settingsFilePath=Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
4772
}
48-
else
73+
elseif(!Path.IsPathRooted(settingsFilePath))
4974
{
50-
stringfolder=Path.Combine(
51-
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
52-
appName??AppDomain.CurrentDomain.FriendlyName.ToLowerInvariant());
53-
Directory.CreateDirectory(folder);
54-
_settingsFilePath=Path.Combine(folder,_fileName);
75+
thrownewArgumentException("settingsFilePath must be an absolute path if provided",nameof(settingsFilePath));
76+
}
77+
78+
stringfolder=Path.Combine(
79+
settingsFilePath,
80+
_appName);
81+
82+
Directory.CreateDirectory(folder);
83+
_settingsFilePath=Path.Combine(folder,_fileName);
84+
85+
if(!File.Exists(_settingsFilePath))
86+
{
87+
// Create the settings file if it doesn't exist
88+
stringemptyJson=JsonSerializer.Serialize(new{});
89+
File.WriteAllText(_settingsFilePath,emptyJson);
5590
}
5691

5792
_cache=Load();
5893
}
5994

60-
publicTSave<T>(stringname,Tvalue)
95+
privatevoidSave(stringname,boolvalue)
6196
{
6297
lock(_lock)
6398
{
64-
_cache[name]=JsonSerializer.SerializeToElement(value);
65-
Persist();
66-
returnvalue;
99+
try
100+
{
101+
// Ensure cache is loaded before saving
102+
usingvarfs=newFileStream(_settingsFilePath,
103+
FileMode.OpenOrCreate,
104+
FileAccess.ReadWrite,
105+
FileShare.None);
106+
107+
varcurrentCache=JsonSerializer.Deserialize<Dictionary<string,JsonElement>>(fs)??new();
108+
_cache=currentCache;
109+
_cache[name]=JsonSerializer.SerializeToElement(value);
110+
fs.Position=0;// Reset stream position to the beginning before writing to override the file
111+
varoptions=newJsonSerializerOptions{WriteIndented=true};
112+
JsonSerializer.Serialize(fs,_cache,options);
113+
}
114+
catch
115+
{
116+
thrownewInvalidOperationException($"Failed to persist settings to{_settingsFilePath}. The file may be corrupted, malformed or locked.");
117+
}
67118
}
68119
}
69120

70-
publicTRead<T>(stringname,TdefaultValue)
121+
privateboolRead(stringname,booldefaultValue)
71122
{
72123
lock(_lock)
73124
{
74125
if(_cache.TryGetValue(name,outvarelement))
75126
{
76127
try
77128
{
78-
returnelement.Deserialize<T>()??defaultValue;
129+
returnelement.Deserialize<bool?>()??defaultValue;
79130
}
80131
catch
81132
{
82-
//Malformed value –fall back.
133+
//malformed value –return default value
83134
returndefaultValue;
84135
}
85136
}
86-
returndefaultValue;// key not found – returncaller‑supplieddefault(false etc.)
137+
returndefaultValue;// key not found – return defaultvalue
87138
}
88139
}
89140

90141
privateDictionary<string,JsonElement>Load()
91142
{
92-
if(!File.Exists(_settingsFilePath))
93-
returnnew();
94-
95143
try
96144
{
97145
usingvarfs=File.OpenRead(_settingsFilePath);
98146
returnJsonSerializer.Deserialize<Dictionary<string,JsonElement>>(fs)??new();
99147
}
100-
catch
148+
catch(Exceptionex)
101149
{
102-
// Corrupted file – start fresh.
103-
returnnew();
150+
thrownewInvalidOperationException($"Failed to load settings from{_settingsFilePath}. The file may be corrupted or malformed. Exception:{ex.Message}");
104151
}
105152
}
106-
107-
privatevoidPersist()
108-
{
109-
usingvarfs=File.Create(_settingsFilePath);
110-
varoptions=newJsonSerializerOptions{WriteIndented=true};
111-
JsonSerializer.Serialize(fs,_cache,options);
112-
}
113153
}

‎App/Services/StartupManager.cs

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,30 @@
44
usingSystem.Security;
55

66
namespaceCoder.Desktop.App.Services;
7-
publicstaticclassStartupManager
7+
8+
publicinterfaceIStartupManager
9+
{
10+
/// <summary>
11+
/// Adds the current executable to the per‑user Run key. Returns <c>true</c> if successful.
12+
/// Fails (returns <c>false</c>) when blocked by policy or lack of permissions.
13+
/// </summary>
14+
boolEnable();
15+
/// <summary>
16+
/// Removes the value from the Run key (no-op if missing).
17+
/// </summary>
18+
voidDisable();
19+
/// <summary>
20+
/// Checks whether the value exists in the Run key.
21+
/// </summary>
22+
boolIsEnabled();
23+
/// <summary>
24+
/// Detects whether group policy disables per‑user startup programs.
25+
/// Mirrors <see cref="Windows.ApplicationModel.StartupTaskState.DisabledByPolicy"/>.
26+
/// </summary>
27+
boolIsDisabledByPolicy();
28+
}
29+
30+
publicclassStartupManager:IStartupManager
831
{
932
privateconststringRunKey=@"Software\\Microsoft\\Windows\\CurrentVersion\\Run";
1033
privateconststringPoliciesExplorerUser=@"Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer";
@@ -14,11 +37,7 @@ public static class StartupManager
1437

1538
privateconststring_defaultValueName="CoderDesktopApp";
1639

17-
/// <summary>
18-
/// Adds the current executable to the per‑user Run key. Returns <c>true</c> if successful.
19-
/// Fails (returns <c>false</c>) when blocked by policy or lack of permissions.
20-
/// </summary>
21-
publicstaticboolEnable()
40+
publicboolEnable()
2241
{
2342
if(IsDisabledByPolicy())
2443
returnfalse;
@@ -35,25 +54,19 @@ public static bool Enable()
3554
catch(SecurityException){returnfalse;}
3655
}
3756

38-
/// <summary>Removes the value from the Run key (no-op if missing).</summary>
39-
publicstaticvoidDisable()
57+
publicvoidDisable()
4058
{
4159
usingvarkey=Registry.CurrentUser.OpenSubKey(RunKey,writable:true);
4260
key?.DeleteValue(_defaultValueName,throwOnMissingValue:false);
4361
}
4462

45-
/// <summary>Checks whether the value exists in the Run key.</summary>
46-
publicstaticboolIsEnabled()
63+
publicboolIsEnabled()
4764
{
4865
usingvarkey=Registry.CurrentUser.OpenSubKey(RunKey);
4966
returnkey?.GetValue(_defaultValueName)!=null;
5067
}
5168

52-
/// <summary>
53-
/// Detects whether group policy disables per‑user startup programs.
54-
/// Mirrors <see cref="Windows.ApplicationModel.StartupTaskState.DisabledByPolicy"/>.
55-
/// </summary>
56-
publicstaticboolIsDisabledByPolicy()
69+
publicboolIsDisabledByPolicy()
5770
{
5871
// User policy – HKCU
5972
using(varkeyUser=Registry.CurrentUser.OpenSubKey(PoliciesExplorerUser))
@@ -65,8 +78,6 @@ public static bool IsDisabledByPolicy()
6578
{
6679
if((int?)keyMachine?.GetValue(DisableLocalMachineRun)==1)returntrue;
6780
}
68-
69-
// Some non‑desktop SKUs report DisabledByPolicy implicitly
7081
returnfalse;
7182
}
7283
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp