3 Commits

Author SHA1 Message Date
dfec8c07b5 Show 'Unsupported' in config dialog for monitors without input options
All checks were successful
Build / build (push) Successful in 8h0m10s
Build and Release / build (push) Successful in 9h0m14s
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 12:14:28 -05:00
bd7a58a5fe Fix retry loop - don't retry on timeout
All checks were successful
Build / build (push) Successful in 9h0m11s
Build and Release / build (push) Successful in 8h0m13s
- Fixed buggy recursive retry logic
- Reduced max retries from 5 to 2
- Don't retry if timeout occurred (empty result)
- Monitor is unresponsive, retrying just wastes time

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 12:01:47 -05:00
af8b09cfa2 Fix DDC/CI timeout using Task.WhenAny
All checks were successful
Build / build (push) Successful in 9h0m11s
Build and Release / build (push) Successful in 8h0m12s
Previous timeout using CancellationToken didn't work because
ReadToEndAsync blocks waiting for process output. Now using
Task.WhenAny with Task.Delay to properly timeout and kill
hung processes after 5 seconds.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 11:57:13 -05:00
4 changed files with 58 additions and 32 deletions

View File

@@ -34,6 +34,22 @@ public partial class ConfigWindow : Window
spPorts.Children.Clear();
_portRows.Clear();
// Show unsupported message if no ports available
if (_availablePorts == null || _availablePorts.Count == 0)
{
spPorts.Children.Add(new TextBlock
{
Text = "Unsupported",
Foreground = Brushes.Gray,
FontSize = 16,
FontStyle = FontStyles.Italic,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(0, 40, 0, 40)
});
return;
}
foreach (var port in _availablePorts)
{
var existingPortConfig = config.Ports.FirstOrDefault(p => p.VcpValue == port.Value);

View File

@@ -13,9 +13,9 @@
</PropertyGroup>
<PropertyGroup>
<Version>1.0.13</Version>
<AssemblyVersion>1.0.13.0</AssemblyVersion>
<FileVersion>1.0.13.0</FileVersion>
<Version>1.0.16</Version>
<AssemblyVersion>1.0.16.0</AssemblyVersion>
<FileVersion>1.0.16.0</FileVersion>
</PropertyGroup>
<ItemGroup>

View File

@@ -28,18 +28,17 @@ internal class ConsoleHelper
p.StartInfo.FileName = command;
p.Start();
using var cts = new CancellationTokenSource(timeoutMs);
try
var readTask = p.StandardOutput.ReadToEndAsync();
var completedTask = await Task.WhenAny(readTask, Task.Delay(timeoutMs));
if (completedTask != readTask)
{
var output = await p.StandardOutput.ReadToEndAsync(cts.Token);
await p.WaitForExitAsync(cts.Token);
return output;
}
catch (OperationCanceledException)
{
try { p.Kill(); } catch { }
// Timeout - kill the process
try { p.Kill(true); } catch { }
return string.Empty;
}
return await readTask;
}
public static async Task<string> CmdCommandAsync(params string[] cmds) =>
@@ -63,23 +62,28 @@ internal class ConsoleHelper
}
p.StandardInput.WriteLine("exit");
using var cts = new CancellationTokenSource(timeoutMs);
try
var readTask = Task.Run(async () =>
{
var result = await p.StandardOutput.ReadToEndAsync(cts.Token);
var error = await p.StandardError.ReadToEndAsync(cts.Token);
var result = await p.StandardOutput.ReadToEndAsync();
var error = await p.StandardError.ReadToEndAsync();
if (!string.IsNullOrWhiteSpace(error))
result = result + "\r\n<Error Message>:\r\n" + error;
await p.WaitForExitAsync(cts.Token);
p.Close();
Debug.WriteLine(result);
return result;
}
catch (OperationCanceledException)
});
var completedTask = await Task.WhenAny(readTask, Task.Delay(timeoutMs));
if (completedTask != readTask)
{
try { p.Kill(); } catch { }
// Timeout - kill the process
try { p.Kill(true); } catch { }
return string.Empty;
}
var output = await readTask;
p.Close();
Debug.WriteLine(output);
return output;
}
public static string Command(string fileName, params string[] cmds)

View File

@@ -30,26 +30,32 @@ public static class CMMCommand
return ConsoleHelper.CmdCommandAsync($"{CMMexe} /SetValue {monitorSN} D6 4");
}
private static async Task<string> GetMonitorValue(string monitorSN, string vcpCode = "D6", int? reTry = 0)
private static async Task<string> GetMonitorValue(string monitorSN, string vcpCode = "D6", int maxRetries = 2)
{
var value = string.Empty;
while (reTry <= 5)
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);
File.Delete(cmdFileName);
try { File.Delete(cmdFileName); } catch { }
value = values.Split("\r\n", StringSplitOptions.RemoveEmptyEntries).LastOrDefault();
// Empty result means timeout - don't retry, monitor is unresponsive
if (string.IsNullOrEmpty(values))
return string.Empty;
if (!string.IsNullOrEmpty(value) && value != "0") return value;
await Task.Delay(500);
await GetMonitorValue(monitorSN, vcpCode, reTry++);
};
var value = values.Split("\r\n", StringSplitOptions.RemoveEmptyEntries).LastOrDefault();
return value;
if (!string.IsNullOrEmpty(value) && value != "0")
return value;
// Only retry on non-timeout failures
if (attempt < maxRetries)
await Task.Delay(300);
}
return string.Empty;
}
#region Brightness (VCP Code 10)