From 0c860d19ead18af522b33b20f63e9c256f95acff Mon Sep 17 00:00:00 2001 From: logikonline Date: Sat, 10 Jan 2026 02:29:16 -0500 Subject: [PATCH] Remove batch file creation to reduce AV false positives MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Refactored all process execution to use direct exe calls - Added ExecuteExeAsync method for direct process execution - Removed dynamic .bat file creation that triggered Wacatac detection - All commands now run ControlMyMonitor.exe directly with arguments 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- DellMonitorControl/DellMonitorControl.csproj | 6 +-- Library/Helpers/ConsoleHelper.cs | 40 ++++++++++++++++ Library/Method/CMMCommand.cs | 49 ++++++++++---------- 3 files changed, 67 insertions(+), 28 deletions(-) diff --git a/DellMonitorControl/DellMonitorControl.csproj b/DellMonitorControl/DellMonitorControl.csproj index 33bc968..5233e95 100644 --- a/DellMonitorControl/DellMonitorControl.csproj +++ b/DellMonitorControl/DellMonitorControl.csproj @@ -15,9 +15,9 @@ - 1.1.0 - 1.1.0.0 - 1.1.0.0 + 1.1.1 + 1.1.1.0 + 1.1.1.0 diff --git a/Library/Helpers/ConsoleHelper.cs b/Library/Helpers/ConsoleHelper.cs index e490303..dbf215c 100644 --- a/Library/Helpers/ConsoleHelper.cs +++ b/Library/Helpers/ConsoleHelper.cs @@ -41,6 +41,46 @@ internal class ConsoleHelper return await readTask; } + /// + /// Execute an exe directly with arguments (no cmd.exe or batch file wrapper) + /// + public static async Task<(string Output, int ExitCode)> ExecuteExeAsync(string exePath, string arguments, int timeoutMs = 5000) + { + var p = new Process + { + StartInfo = new ProcessStartInfo + { + FileName = exePath, + Arguments = arguments, + UseShellExecute = false, + CreateNoWindow = true, + RedirectStandardOutput = true, + RedirectStandardError = true + } + }; + + p.Start(); + + var readTask = Task.Run(async () => + { + var output = await p.StandardOutput.ReadToEndAsync(); + await p.WaitForExitAsync(); + return (output, p.ExitCode); + }); + + var completedTask = await Task.WhenAny(readTask, Task.Delay(timeoutMs)); + + if (completedTask != readTask) + { + try { p.Kill(true); } catch { } + return (string.Empty, -1); + } + + var result = await readTask; + p.Close(); + return result; + } + public static async Task CmdCommandAsync(params string[] cmds) => await CommandAsync(cmdFileName, cmds); diff --git a/Library/Method/CMMCommand.cs b/Library/Method/CMMCommand.cs index 87f6df3..fc950af 100644 --- a/Library/Method/CMMCommand.cs +++ b/Library/Method/CMMCommand.cs @@ -17,37 +17,36 @@ public static class CMMCommand public static async Task ScanMonitor() { await BytesToFileAsync(new(CMMexe)); - await ConsoleHelper.CmdCommandAsync($"{CMMexe} /smonitors {CMMsMonitors}"); + await ConsoleHelper.ExecuteExeAsync(CMMexe, $"/smonitors {CMMsMonitors}"); } - public static Task PowerOn(string monitorSN) - { - return ConsoleHelper.CmdCommandAsync($"{CMMexe} /SetValue {monitorSN} D6 1"); - } - - public static Task Sleep(string monitorSN) + public static async Task PowerOn(string monitorSN) { - return ConsoleHelper.CmdCommandAsync($"{CMMexe} /SetValue {monitorSN} D6 4"); + await ConsoleHelper.ExecuteExeAsync(CMMexe, $"/SetValue {monitorSN} D6 1"); + } + + public static async Task Sleep(string monitorSN) + { + await ConsoleHelper.ExecuteExeAsync(CMMexe, $"/SetValue {monitorSN} D6 4"); } private static async Task GetMonitorValue(string monitorSN, string vcpCode = "D6", int maxRetries = 2) { for (int attempt = 0; attempt <= maxRetries; attempt++) { - var cmdFileName = Path.Combine(CMMTmpFolder, $"{Guid.NewGuid()}.bat"); - var cmd = $"{CMMexe} /GetValue {monitorSN} {vcpCode}\r\n" + - $"echo %errorlevel%"; - File.WriteAllText(cmdFileName, cmd); - var values = await ConsoleHelper.ExecuteCommand(cmdFileName); - try { File.Delete(cmdFileName); } catch { } + // Execute directly without batch file wrapper + var (output, exitCode) = await ConsoleHelper.ExecuteExeAsync( + CMMexe, + $"/GetValue {monitorSN} {vcpCode}"); // Empty result means timeout - don't retry, monitor is unresponsive - if (string.IsNullOrEmpty(values)) + if (string.IsNullOrEmpty(output) && exitCode == -1) return string.Empty; - var value = values.Split("\r\n", StringSplitOptions.RemoveEmptyEntries).LastOrDefault(); + // Parse output - ControlMyMonitor outputs the value directly + var value = output?.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries).LastOrDefault()?.Trim(); - if (!string.IsNullOrEmpty(value) && value != "0") + if (!string.IsNullOrEmpty(value) && exitCode == 0) return value; // Only retry on non-timeout failures @@ -60,9 +59,9 @@ public static class CMMCommand #region Brightness (VCP Code 10) - public static Task SetBrightness(string monitorSN, int value) + public static async Task SetBrightness(string monitorSN, int value) { - return ConsoleHelper.CmdCommandAsync($"{CMMexe} /SetValue {monitorSN} 10 {value}"); + await ConsoleHelper.ExecuteExeAsync(CMMexe, $"/SetValue {monitorSN} 10 {value}"); } public static async Task GetBrightness(string monitorSN) @@ -75,9 +74,9 @@ public static class CMMCommand #region Contrast (VCP Code 12) - public static Task SetContrast(string monitorSN, int value) + public static async Task SetContrast(string monitorSN, int value) { - return ConsoleHelper.CmdCommandAsync($"{CMMexe} /SetValue {monitorSN} 12 {value}"); + await ConsoleHelper.ExecuteExeAsync(CMMexe, $"/SetValue {monitorSN} 12 {value}"); } public static async Task GetContrast(string monitorSN) @@ -90,9 +89,9 @@ public static class CMMCommand #region Input Source (VCP Code 60) - public static Task SetInputSource(string monitorSN, int value) + public static async Task SetInputSource(string monitorSN, int value) { - return ConsoleHelper.CmdCommandAsync($"{CMMexe} /SetValue {monitorSN} 60 {value}"); + await ConsoleHelper.ExecuteExeAsync(CMMexe, $"/SetValue {monitorSN} 60 {value}"); } public static async Task GetInputSource(string monitorSN) @@ -106,7 +105,7 @@ public static class CMMCommand var options = new List(); var savePath = Path.Combine(CMMTmpFolder, $"{monitorSN}_vcp.tmp"); - await ConsoleHelper.CmdCommandAsync($"{CMMexe} /sjson {savePath} {monitorSN}"); + await ConsoleHelper.ExecuteExeAsync(CMMexe, $"/sjson {savePath} {monitorSN}"); if (!File.Exists(savePath)) return options; @@ -183,7 +182,7 @@ public static class CMMCommand static async Task ScanMonitorStatus(string savePath, XMonitor mon) { - await ConsoleHelper.CmdCommandAsync($"{CMMexe} /sjson {savePath} {mon.MonitorID}"); + await ConsoleHelper.ExecuteExeAsync(CMMexe, $"/sjson {savePath} {mon.MonitorID}"); var monitorModel = JsonHelper.JsonFormFile>(savePath); var status = monitorModel.ReadMonitorStatus();