Skip to content

Single executable: GUI + CLI in AsusFanControl.exe - #25

Merged
beenycool merged 3 commits into
masterfrom
pr/single-exe-cli
Apr 11, 2026
Merged

Single executable: GUI + CLI in AsusFanControl.exe#25
beenycool merged 3 commits into
masterfrom
pr/single-exe-cli

Conversation

@beenycool

@beenycool beenycool commented Apr 11, 2026

Copy link
Copy Markdown
Owner

This pull request removes the separate console project and ships one AsusFanControl.exe built from the WinForms project (with AssemblyName set to AsusFanControl). Command-line mode runs when any arguments are passed; double-click or no args starts the GUI.

Changes include CliProgram (CLI logic, optional --debug-log), console attach for CLI, Costura.Fody to embed dependencies, solution updated to Core + GUI only, CI artifact paths aligned with the new output, and README updates for same-exe usage.

Made with Cursor

Summary by CodeRabbit

  • New Features

    • Single executable now provides both GUI and command-line interfaces—launch with no arguments for GUI mode, or pass command-line flags for CLI operations.
  • Documentation

    • Updated usage instructions to reflect the consolidated application behavior.

Remove the legacy console project and build AsusFanControl.exe from the
WinForms app (AssemblyName AsusFanControl). Add CliProgram for CLI mode with
optional --debug-log, attach console when args are present, and Costura.Fody
to embed managed dependencies. Update the solution to Core + GUI only, tune
CI artifact staging for the new output layout, and document same-exe usage in
the README.

Made-with: Cursor
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@coderabbitai

coderabbitai Bot commented Apr 11, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@beenycool has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 9 minutes and 22 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 9 minutes and 22 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0be7654d-daf4-4b53-aca2-5e2be691dfa2

📥 Commits

Reviewing files that changed from the base of the PR and between 0e542af and 511312f.

📒 Files selected for processing (7)
  • AsusFanControl.Core/ProfileManager.cs
  • AsusFanControl.Core/TaskSchedulerHelper.cs
  • AsusFanControlGUI/AsusFanControlGUI.csproj
  • AsusFanControlGUI/CliProgram.cs
  • AsusFanControlGUI/DarkMenuRenderer.cs
  • AsusFanControlGUI/ProfileEditorDialog.cs
  • AsusFanControlGUI/Program.cs

Walkthrough

The pull request consolidates separate CLI and GUI executables into a single dual-mode executable. The AsusFanControl CLI project is removed, with its command-line logic moved into the AsusFanControlGUI project as CliProgram.cs. The renamed executable (AsusFanControl.exe) now dispatches to GUI or CLI mode based on provided arguments.

Changes

Cohort / File(s) Summary
Build Infrastructure
.github/workflows/dotnet-desktop.yml
Artifact preparation now stages a dedicated artifacts/AsusFanControl directory, copies the renamed executable along with PsExec.exe and RunAsAdmin.bat (updated to invoke AsusFanControl.exe), and uploads artifacts under the new AsusFanControl name instead of AsusFanControlGUI.
Solution & Project Structure
AsusFanControl.sln
Removes the AsusFanControl project reference and replaces it with AsusFanControl.Core project reference under a new GUID, switching platform configuration from x64 to Any CPU.
Standalone CLI Project Removal
AsusFanControl/App.config, AsusFanControl/AsusFanControl.csproj, AsusFanControl/Program.cs, AsusFanControl/Properties/AssemblyInfo.cs
Entire AsusFanControl project deleted, including command-line argument parsing, fan control commands (--get-fan-speeds, --set-fan-speed, etc.), and event handlers for graceful shutdown and reset-to-defaults behavior.
GUI → Dual-Mode Executable
AsusFanControlGUI/AsusFanControlGUI.csproj, AsusFanControlGUI/CliProgram.cs, AsusFanControlGUI/Program.cs
Assembly renamed from AsusFanControlGUI to AsusFanControl; added CliProgram.cs with complete CLI logic including argument parsing, logging via TeeTextWriter, and trace forwarding; Program.cs now branches to GUI or CLI mode based on arguments. Fody/Costura NuGet dependencies added for assembly embedding.
Configuration & Metadata
AsusFanControlGUI/FodyWeavers.xml, AsusFanControlGUI/Properties/AssemblyInfo.cs
New Fody weaver configuration added for Costura to embed AsusWinIO64 as unmanaged 64-bit assembly; assembly title and product updated to AsusFanControl.
Documentation
README.md
Updated instructions clarify that AsusFanControl.exe handles both GUI (no arguments) and CLI (with flags) modes; removed references to separate AsusFanControlGUI.exe; noted DLL embedding via Costura/Fody in release builds.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Main as AsusFanControl.exe<br/>(Main)
    participant GUI as WinForms<br/>(Form1)
    participant CLI as CliProgram
    participant Controller as AsusControl<br/>(IFanController)
    
    alt No Arguments (GUI Mode)
        User->>Main: Double-click or no args
        Main->>Main: EnableVisualStyles,<br/>SetCompatibleTextRenderingDefault
        Main->>GUI: Run Form1
        GUI->>User: Display GUI Window
    else Arguments Present (CLI Mode)
        User->>Main: AsusFanControl.exe --get-fan-speeds
        Main->>Main: ExtractDebugLogFlag<br/>(parse arguments)
        Main->>Main: Create DebugLogSession<br/>(if --debug-log)
        Main->>Main: AttachConsole/AllocConsole
        Main->>CLI: Run(args)
        CLI->>Controller: new AsusControl()
        Controller->>CLI: IFanController instance
        CLI->>Controller: Execute commands<br/>(--get-fan-speeds,<br/>--set-fan-speed, etc.)
        Controller->>CLI: Return results
        CLI->>User: Write to Console
        Note over CLI: Register AppDomain handlers<br/>for ProcessExit & Exception
        CLI->>Controller: Reset to defaults<br/>(if not --skip-reset)
        CLI->>Controller: Dispose
        CLI->>Main: Exit code
        Main->>User: Return exit code
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 One exe to rule them all,
GUI or CLI, heed the call!
No args? A window bright and wide,
With flags? The command-line's your guide!
AsusFanControl unites the way, ⚙️✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.11% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and clearly describes the main change: consolidating GUI and CLI functionality into a single executable.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch pr/single-exe-cli

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request consolidates the CLI and GUI versions of AsusFanControl into a single executable. The GUI project now serves as the entry point, detecting command-line arguments to switch between modes. Feedback focuses on correctly initializing the console for CLI mode in a Windows application, specifically by redirecting standard streams and ensuring the console is attached before logging begins. Additionally, it is recommended to use synchronous methods instead of wrapping them in tasks during process exit to avoid redundancy.

Comment on lines +17 to +21
static void EnsureConsoleForCli()
{
if (!AttachConsole(ATTACH_PARENT_PROCESS))
AllocConsole();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

In a WinExe project, calling AttachConsole or AllocConsole is not enough to enable Console.WriteLine because the standard streams are initially directed to a null device. You must also redirect the C# Console output and error streams to the newly available console handles.

        static void EnsureConsoleForCli()
        {
            if (!AttachConsole(ATTACH_PARENT_PROCESS))
                AllocConsole();

            // Redirect standard streams to the console
            var standardOutput = new System.IO.StreamWriter(Console.OpenStandardOutput()) { AutoFlush = true };
            Console.SetOut(standardOutput);
            var standardError = new System.IO.StreamWriter(Console.OpenStandardError()) { AutoFlush = true };
            Console.SetError(standardError);
        }

Comment on lines +40 to +55
args = CliProgram.ExtractDebugLogFlag(args, out string debugLogFile);
using (debugLogFile != null ? CliProgram.DebugLogSession.Create(debugLogFile) : null)
{
if (debugLogFile != null)
Console.WriteLine("[debug-log] Writing diagnostics to: " + System.IO.Path.GetFullPath(debugLogFile));

if (args.Length < 1)
{
EnsureConsoleForCli();
CliProgram.PrintUsage();
return 1;
}

EnsureConsoleForCli();
return CliProgram.Run(args);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The current placement of EnsureConsoleForCli() causes several issues in CLI mode:

  1. The diagnostic message on line 44 is printed before the console is attached, so it will be lost when running from a standard command prompt.
  2. DebugLogSession.Create captures Console.Out before the console is attached. Since this is a WinExe, Console.Out is initially a null writer. Even after the console is attached later, the TeeTextWriter will continue to use the null writer as its primary output, resulting in no console output.
  3. Moving EnsureConsoleForCli() to the top of the CLI branch ensures that all subsequent Console calls (including those inside DebugLogSession) work correctly.
            EnsureConsoleForCli();
            args = CliProgram.ExtractDebugLogFlag(args, out string debugLogFile);
            using (debugLogFile != null ? CliProgram.DebugLogSession.Create(debugLogFile) : null)
            {
                if (debugLogFile != null)
                    Console.WriteLine("[debug-log] Writing diagnostics to: " + System.IO.Path.GetFullPath(debugLogFile));

                if (args.Length < 1)
                {
                    CliProgram.PrintUsage();
                    return 1;
                }

                return CliProgram.Run(args);
            }

{
if (!isDisposed && !skipResetOnExit)
{
try { asusControl.ResetToDefaultAsync().GetAwaiter().GetResult(); }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using ResetToDefaultAsync().GetAwaiter().GetResult() is redundant and potentially problematic during process exit, as it unnecessarily wraps a synchronous operation in a Task.Run. Use the synchronous ResetToDefault() method directly.

                    try { asusControl.ResetToDefault(); }

if (!skipResetOnExit)
{
try { asusControl.ResetToDefaultAsync().GetAwaiter().GetResult(); }
catch (Exception ex) { Console.Error.WriteLine("[UnhandledException] Reset error: " + ex.Message); }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using ResetToDefaultAsync().GetAwaiter().GetResult() is redundant here. Use the synchronous ResetToDefault() method directly.

                    try { asusControl.ResetToDefault(); }

{
if (!skipResetOnExit)
{
try { asusControl.ResetToDefaultAsync().GetAwaiter().GetResult(); }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using ResetToDefaultAsync().GetAwaiter().GetResult() is redundant here. Use the synchronous ResetToDefault() method directly.

                    try { asusControl.ResetToDefault(); }

catch (Exception ex) { Console.Error.WriteLine("[ProcessExit] Reset error: " + ex.Message); }
}
};
AppDomain.CurrentDomain.UnhandledException += (s, e) =>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CRITICAL: Resource cleanup logic has double disposal and potential double reset.

In the UnhandledException event handler, asusControl.Dispose() is called without checking isDisposed, which can lead to double disposal when the finally block also calls Dispose(). Additionally, both the UnhandledException handler and the finally block may call ResetToDefaultAsync() if skipResetOnExit is false, causing a double reset. This could result in exceptions or undefined behavior. Ensure cleanup logic executes exactly once by setting isDisposed = true before calling cleanup methods, or by checking isDisposed in all handlers.

@kilo-code-bot

kilo-code-bot Bot commented Apr 11, 2026

Copy link
Copy Markdown

Code Review Summary

Status: 2 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 1
WARNING 1
SUGGESTION 0
Issue Details (click to expand)

CRITICAL

File Line Issue
AsusFanControlGUI/CliProgram.cs 197 Resource cleanup logic has double disposal and potential double reset. UnhandledException handler calls Dispose() without checking isDisposed, leading to double disposal with finally block.

WARNING

File Line Issue
AsusFanControl.Core/ProfileManager.cs 49 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.
Other Observations (not in diff)

None.

Files Reviewed (2 files)
  • AsusFanControlGUI/CliProgram.cs - 1 critical issue (unchanged)
  • AsusFanControl.Core/ProfileManager.cs - 1 warning issue (unchanged)

Reviewed by trinity-large-thinking · 866,929 tokens

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@AsusFanControlGUI/AsusFanControlGUI.csproj`:
- Around line 8-10: The project uses OutputType WinExe so console is not
attached by default; move the console-attachment logic
(EnsureConsoleForCli()/AttachConsole/AllocConsole calls) in Program.cs to
execute before any debug/log messages that write to the console (specifically
the debug-log path message currently printed early in Main), so that those
messages are not lost — call EnsureConsoleForCli() at the very start of the CLI
path in Main (before the debug/log path output) and ensure any helper like
EnsureConsoleForCli or AttachConsole is idempotent and safe to call from GUI
mode.

In `@AsusFanControlGUI/CliProgram.cs`:
- Around line 14-44: TeeTextWriter currently doesn't override Flush so flush
calls won't be forwarded to both underlying writers; add an override for Flush()
in the sealed class (and optionally override FlushAsync(CancellationToken) if
async flushing is used) that calls _primary.Flush() and _secondary.Flush(); also
ensure any Dispose/Close paths in TeeTextWriter forward to both _primary and
_secondary if not already handled.
- Around line 95-118: The Create method in DebugLogSession should defensively
handle empty or whitespace-only path input: check
string.IsNullOrWhiteSpace(path) at the top of DebugLogSession.Create and, if
true, replace path with DefaultDebugLogPath() (or throw ArgumentException based
on desired behavior), then proceed to compute dir via
Path.GetDirectoryName(Path.GetFullPath(path)); ensure you trim the path before
use so Directory.CreateDirectory(dir) and new StreamWriter(path, ...) never
receive an empty string; update references to path, dir, and fileWriter
accordingly.
- Around line 217-240: The parsed speed value for --set-fan-speeds is not
range-validated; update the parsing block that reads parts[1] and the code path
that calls asusControl.SetFanSpeeds(newSpeed) to ensure newSpeed is between 0
and 100 inclusive, printing an error like "Error: Speed must be between 0 and
100" and not calling SetFanSpeeds or setting skipResetOnExit when out of range;
apply the same 0–100 validation to the separate --set-fan-speed parsing branch
(the other handler that calls asusControl.SetFanSpeeds) so negative or >100
values are rejected and only valid values trigger the Test mode/new speed
messages.
- Around line 189-206: The UnhandledException handler can call Dispose twice
because it doesn't check isDisposed; update the
AppDomain.CurrentDomain.UnhandledException lambda to mirror the ProcessExit
handler by checking isDisposed (and skipResetOnExit where already used) before
calling asusControl.ResetToDefaultAsync() and before calling
asusControl.Dispose(), and ensure both
ResetToDefaultAsync().GetAwaiter().GetResult() and asusControl.Dispose() are
wrapped in try/catch blocks that log exceptions; reference the
UnhandledException handler, isDisposed, skipResetOnExit,
asusControl.ResetToDefaultAsync(), and asusControl.Dispose() when making this
change.
- Around line 340-341: The Run method currently always returns 0 even on
parse/argument errors; update Run (in CliProgram.Run) to return a non-zero exit
code on failure cases: detect parsing failures or unknown arguments from the
parser result and return 1 (or other defined error codes) instead of 0, and
ensure callers (e.g., Main) propagate that code to Environment.Exit or return it
to the OS; modify any catch blocks in Run to return a non-zero code on
exceptions and document/centralize the numeric code(s) used for different
failure types.

In `@AsusFanControlGUI/Program.cs`:
- Around line 40-55: The debug-log path write happens before a console is
guaranteed to exist so the message can be lost; after calling
CliProgram.ExtractDebugLogFlag(...) call EnsureConsoleForCli() immediately
(before creating the DebugLogSession and before any Console.WriteLine), then
proceed to create the debug session and write the "[debug-log]" message only
after EnsureConsoleForCli() has been invoked; update the Program.Main flow
around debugLogFile, CliProgram.DebugLogSession.Create, and EnsureConsoleForCli
to ensure console attachment occurs prior to any Console.WriteLine.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 92fa1d17-e97f-4f36-8de0-3f8ac036b9f3

📥 Commits

Reviewing files that changed from the base of the PR and between 2ea2fa3 and 0e542af.

📒 Files selected for processing (12)
  • .github/workflows/dotnet-desktop.yml
  • AsusFanControl.sln
  • AsusFanControl/App.config
  • AsusFanControl/AsusFanControl.csproj
  • AsusFanControl/Program.cs
  • AsusFanControl/Properties/AssemblyInfo.cs
  • AsusFanControlGUI/AsusFanControlGUI.csproj
  • AsusFanControlGUI/CliProgram.cs
  • AsusFanControlGUI/FodyWeavers.xml
  • AsusFanControlGUI/Program.cs
  • AsusFanControlGUI/Properties/AssemblyInfo.cs
  • README.md
💤 Files with no reviewable changes (4)
  • AsusFanControl/App.config
  • AsusFanControl/AsusFanControl.csproj
  • AsusFanControl/Properties/AssemblyInfo.cs
  • AsusFanControl/Program.cs

Comment thread AsusFanControlGUI/AsusFanControlGUI.csproj
Comment thread AsusFanControlGUI/CliProgram.cs
Comment thread AsusFanControlGUI/CliProgram.cs
Comment thread AsusFanControlGUI/CliProgram.cs
Comment thread AsusFanControlGUI/CliProgram.cs
Comment thread AsusFanControlGUI/CliProgram.cs Outdated
Comment thread AsusFanControlGUI/Program.cs
* Add profile manager core types and profile editor UI

Introduce ProfileManager and FanProfile serialization in Core, a Windows
Task Scheduler helper for future automation hooks, and WinForms pieces
(DarkMenuRenderer, ProfileEditorDialog) compiled into the GUI project.
Form wiring can follow in a later change.

Made-with: Cursor

* Apply reviewer suggestions for PR #26
@beenycool

Copy link
Copy Markdown
Owner Author

/gemini review

public class ProfileManager
{
private readonly List<FanProfile> _profiles = new List<FanProfile>();
private readonly object _lock = new object();

Copy link
Copy Markdown

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.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the application to merge the CLI and GUI into a single executable and introduces a profile management system for automatic fan curve switching based on active processes. It also includes a new utility for managing auto-start tasks via the Windows Task Scheduler. The review feedback highlights several compatibility issues where C# 8.0 syntax and .NET Core-specific methods were used in a .NET Framework 4.7.2 project, which will lead to compilation errors. Additionally, the feedback points out a lack of thread safety in the profile manager, restrictive path validation in task registration, and potential performance issues related to frequent process enumeration.

if (string.IsNullOrEmpty(processName)) return null;
var name = processName;
if (name.EndsWith(".exe", StringComparison.OrdinalIgnoreCase))
name = name[..^4];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The project targets .NET Framework 4.7.2 and explicitly sets <LangVersion>7.3</LangVersion> in the project file. The range operator [..^4] is a C# 8.0 feature and requires the System.Index type, which is not available in .NET Framework 4.7.2. This will cause a compilation error.

            if (name.EndsWith(".exe", StringComparison.OrdinalIgnoreCase))
                name = name.Substring(0, name.Length - 4);

{
if (!proc.WaitForExit(timeoutMs))
{
try { proc.Kill(true); } catch { }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The proc.Kill(true) overload (which kills the process tree) was introduced in .NET Core 3.0 and .NET Standard 2.1. It is not available in .NET Framework 4.7.2, which this project targets. This will result in a compilation error.

                try { proc.Kill(); } catch { }

public class ProfileManager
{
private readonly List<FanProfile> _profiles = new List<FanProfile>();
private readonly object _lock = new object();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The _lock object is defined but never used. Since _profiles is a List<FanProfile>, and CheckActiveProfile might be called from a background thread while the UI thread modifies the list (via AddProfile or RemoveProfile), this class is currently not thread-safe and could throw an InvalidOperationException during iteration.

Comment on lines +58 to +60
var args = exePath.IndexOfAny(new[] { '"', '\n', '\r', ';', '&', '|', '>', '<' }) >= 0;
if (args)
return false;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This validation is overly restrictive. Including the double quote character " in the IndexOfAny check will cause RegisterTask to return false if the user provides a quoted path, which is common for paths containing spaces. Additionally, schtasks.exe correctly handles quoted paths in the /TR argument.

                var args = exePath.IndexOfAny(new[] { '\n', '\r', ';', '&', '|', '>', '<' }) >= 0;
                if (args)
                    return false;

try
{
var runningProcesses = new HashSet<string>(
Process.GetProcesses().Select(p =>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Process.GetProcesses() is an expensive operation as it enumerates all system processes. If CheckActiveProfile is called frequently (e.g., from a timer), this will lead to high CPU usage. Consider using a more efficient way to check for specific running processes, such as WMI queries or P/Invoke with EnumProcesses.

{
if (!isDisposed && !skipResetOnExit)
{
try { asusControl.ResetToDefaultAsync().GetAwaiter().GetResult(); }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

There is no need to use GetAwaiter().GetResult() on the async method here, as a synchronous ResetToDefault() method is already available in the IFanController interface. Blocking on async tasks in this manner can lead to deadlocks in certain synchronization contexts.

                    try { asusControl.ResetToDefault(); }

@beenycool
beenycool merged commit d48a42c into master Apr 11, 2026
@beenycool
beenycool deleted the pr/single-exe-cli branch April 11, 2026 09:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant