-
Notifications
You must be signed in to change notification settings - Fork 1
Single executable: GUI + CLI in AsusFanControl.exe #25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,144 @@ | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Diagnostics; | ||
| using System.Linq; | ||
|
|
||
| namespace AsusFanControl.Core | ||
| { | ||
| public class FanProfile | ||
| { | ||
| public string Name { get; set; } | ||
| public FanCurve Curve { get; set; } | ||
| public List<string> TriggerProcesses { get; set; } = new List<string>(); | ||
|
|
||
| public FanProfile() { } | ||
|
|
||
| public FanProfile(string name, FanCurve curve, IEnumerable<string> triggerProcesses) | ||
| { | ||
| Name = name; | ||
| Curve = curve; | ||
| TriggerProcesses = triggerProcesses.ToList(); | ||
| } | ||
|
|
||
| public override string ToString() | ||
| { | ||
| var curveStr = Curve?.ToString() ?? ""; | ||
| var procStr = string.Join(";", TriggerProcesses.Select(p => Uri.EscapeDataString(p))); | ||
| return $"{Uri.EscapeDataString(Name)}|{curveStr}|{procStr}"; | ||
| } | ||
|
|
||
| public static FanProfile FromString(string data) | ||
| { | ||
| if (string.IsNullOrWhiteSpace(data)) return null; | ||
| var parts = data.Split('|'); | ||
| if (parts.Length < 3) return null; | ||
|
|
||
| return new FanProfile | ||
| { | ||
| Name = Uri.UnescapeDataString(parts[0]), | ||
| Curve = FanCurve.FromString(parts[1]), | ||
| TriggerProcesses = parts[2].Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries) | ||
| .Select(Uri.UnescapeDataString).ToList() | ||
| }; | ||
| } | ||
| } | ||
|
|
||
| public class ProfileManager | ||
| { | ||
| private readonly List<FanProfile> _profiles = new List<FanProfile>(); | ||
| private readonly object _lock = new object(); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
| private string _activeProfileName; | ||
|
|
||
| public IReadOnlyList<FanProfile> Profiles => _profiles; | ||
| public string ActiveProfileName => _activeProfileName; | ||
|
|
||
| public void LoadProfiles(string serialized) | ||
| { | ||
| lock (_lock) | ||
| { | ||
| _profiles.Clear(); | ||
| _activeProfileName = null; | ||
| if (string.IsNullOrWhiteSpace(serialized)) return; | ||
|
|
||
| var entries = serialized.Split(new[] { "||" }, StringSplitOptions.RemoveEmptyEntries); | ||
| foreach (var entry in entries) | ||
| { | ||
| var profile = FanProfile.FromString(entry); | ||
| if (profile != null) | ||
| _profiles.Add(profile); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| public string SaveProfiles() | ||
| { | ||
| lock (_lock) | ||
| { | ||
| return string.Join("||", _profiles.Select(p => p.ToString())); | ||
| } | ||
| } | ||
|
|
||
| public void AddProfile(FanProfile profile) | ||
| { | ||
| if (profile == null) throw new ArgumentNullException(nameof(profile)); | ||
| lock (_lock) | ||
| { | ||
| _profiles.RemoveAll(p => string.Equals(p.Name, profile.Name, StringComparison.OrdinalIgnoreCase)); | ||
| _profiles.Add(profile); | ||
| } | ||
| } | ||
|
|
||
| public void RemoveProfile(string name) | ||
| { | ||
| lock (_lock) | ||
| { | ||
| _profiles.RemoveAll(p => string.Equals(p.Name, name, StringComparison.OrdinalIgnoreCase)); | ||
| } | ||
| } | ||
|
|
||
| private static string NormalizeProcessName(string processName) | ||
| { | ||
| if (string.IsNullOrEmpty(processName)) return null; | ||
| var name = processName; | ||
| if (name.EndsWith(".exe", StringComparison.OrdinalIgnoreCase)) | ||
| name = name[..^4]; | ||
|
Check failure on line 104 in AsusFanControl.Core/ProfileManager.cs
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The project targets .NET Framework 4.7.2 and explicitly sets if (name.EndsWith(".exe", StringComparison.OrdinalIgnoreCase))
name = name.Substring(0, name.Length - 4); |
||
| return name.ToLowerInvariant(); | ||
| } | ||
|
|
||
| public FanProfile CheckActiveProfile(FanCurve defaultCurve) | ||
| { | ||
| try | ||
| { | ||
| var runningProcesses = new HashSet<string>( | ||
| Process.GetProcesses().Select(p => | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| { | ||
| try { return NormalizeProcessName(p.ProcessName); } | ||
| catch { return null; } | ||
| }).Where(n => n != null), | ||
| StringComparer.OrdinalIgnoreCase | ||
| ); | ||
|
|
||
| foreach (var profile in _profiles) | ||
| { | ||
| if (profile.TriggerProcesses == null) continue; | ||
| if (profile.TriggerProcesses.Any(tp => | ||
| { | ||
| var normalized = NormalizeProcessName(tp); | ||
| return normalized != null && runningProcesses.Contains(normalized); | ||
| })) | ||
| { | ||
| _activeProfileName = profile.Name; | ||
| return profile; | ||
| } | ||
| } | ||
| } | ||
| catch | ||
| { | ||
| // Ignore process enumeration errors | ||
| } | ||
|
|
||
| _activeProfileName = null; | ||
| return null; | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| using System; | ||
| using System.Diagnostics; | ||
| using System.IO; | ||
|
|
||
| namespace AsusFanControl.Core | ||
| { | ||
| public static class TaskSchedulerHelper | ||
| { | ||
| private const string TaskName = "AsusFanControl_AutoStart"; | ||
|
|
||
| private static bool WaitForExitSafely(Process proc, int timeoutMs, out int exitCode) | ||
| { | ||
| if (!proc.WaitForExit(timeoutMs)) | ||
| { | ||
| try { proc.Kill(true); } catch { } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| try { proc.WaitForExit(1000); } catch { } | ||
| exitCode = -1; | ||
| return false; | ||
| } | ||
| exitCode = proc.ExitCode; | ||
| return true; | ||
| } | ||
|
|
||
| public static bool IsTaskRegistered() | ||
| { | ||
| try | ||
| { | ||
| var psi = new ProcessStartInfo | ||
| { | ||
| FileName = "schtasks.exe", | ||
| Arguments = $"/Query /TN \"{TaskName}\" /FO CSV /NH", | ||
| UseShellExecute = false, | ||
| RedirectStandardOutput = true, | ||
| RedirectStandardError = true, | ||
| CreateNoWindow = true | ||
| }; | ||
| using (var proc = Process.Start(psi)) | ||
| { | ||
| if (!WaitForExitSafely(proc, 5000, out int exitCode)) | ||
| return false; | ||
| return exitCode == 0; | ||
| } | ||
| } | ||
| catch | ||
| { | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| public static bool RegisterTask(string exePath) | ||
| { | ||
| try | ||
| { | ||
| var safePath = Path.GetFullPath(exePath); | ||
| if (!File.Exists(safePath)) | ||
| return false; | ||
|
|
||
| var args = exePath.IndexOfAny(new[] { '"', '\n', '\r', ';', '&', '|', '>', '<' }) >= 0; | ||
| if (args) | ||
| return false; | ||
|
Comment on lines
+58
to
+60
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This validation is overly restrictive. Including the double quote character var args = exePath.IndexOfAny(new[] { '\n', '\r', ';', '&', '|', '>', '<' }) >= 0;
if (args)
return false; |
||
|
|
||
| var psi = new ProcessStartInfo | ||
| { | ||
| FileName = "schtasks.exe", | ||
| Arguments = $"/Create /TN \"{TaskName}\" /TR \"\\\"{safePath}\\\"\" /SC ONLOGON /RL HIGHEST /F", | ||
| UseShellExecute = false, | ||
| RedirectStandardOutput = true, | ||
| RedirectStandardError = true, | ||
| CreateNoWindow = true | ||
| }; | ||
| using (var proc = Process.Start(psi)) | ||
| { | ||
| if (!WaitForExitSafely(proc, 10000, out int exitCode)) | ||
| return false; | ||
| return exitCode == 0; | ||
| } | ||
| } | ||
| catch | ||
| { | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| public static bool UnregisterTask() | ||
| { | ||
| try | ||
| { | ||
| var psi = new ProcessStartInfo | ||
| { | ||
| FileName = "schtasks.exe", | ||
| Arguments = $"/Delete /TN \"{TaskName}\" /F", | ||
| UseShellExecute = false, | ||
| RedirectStandardOutput = true, | ||
| RedirectStandardError = true, | ||
| CreateNoWindow = true | ||
| }; | ||
| using (var proc = Process.Start(psi)) | ||
| { | ||
| if (!WaitForExitSafely(proc, 10000, out int exitCode)) | ||
| return false; | ||
| return exitCode == 0; | ||
| } | ||
| } | ||
| catch | ||
| { | ||
| return false; | ||
| } | ||
| } | ||
| } | ||
| } | ||
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
WARNING: Unused lock object (
_lock) declared but never used. This suggests the class was intended to be thread-safe but the locking was omitted. If accessed from multiple threads concurrently, this could lead to race conditions and data corruption.