15 Commits

Author SHA1 Message Date
77e9b0505a Fix csproj encoding issue in workflow
All checks were successful
Build / build (push) Successful in 8h0m9s
Build and Release / build (push) Successful in 9h0m16s
Use System.IO.File methods with UTF-8 BOM to preserve special characters

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 11:52:10 -05:00
526974da24 Add auto-update check and DDC/CI timeout fix
Some checks failed
Build / build (push) Successful in 8h0m12s
Build and Release / build (push) Failing after 9h0m41s
- Auto-update checker on startup via Gitea API
- Balloon notification when update available, click to download
- 5-second timeout on DDC/CI commands to prevent hangs
- Simplified version scheme to match release tags
- Workflow auto-updates version in csproj from tag

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 11:48:39 -05:00
b7597fd4d0 Test with PowerShell enabled
All checks were successful
Build / build (push) Successful in 8h0m23s
2026-01-07 16:34:55 +00:00
260379a330 Test with Git in PATH
Some checks failed
Build / build (push) Failing after 8h0m26s
2026-01-07 16:31:14 +00:00
e189731d24 Test with nvm PATH
Some checks failed
Build / build (push) Failing after 8h0m26s
2026-01-07 16:27:17 +00:00
c6a2b5b22f Test nvm node
Some checks failed
Build / build (push) Failing after 9h0m33s
2026-01-07 16:25:24 +00:00
c394320a3e Trigger build
Some checks failed
Build / build (push) Failing after 9h0m40s
2026-01-07 16:23:06 +00:00
0d810bbf49 Add debug logging with Show Log button
Some checks failed
Build / build (push) Failing after 8h0m9s
Build and Release / build (push) Successful in 8h0m16s
- DebugLogger class saves logs to %TEMP%\CMM\debug.log
- Show Log button appears after 3s if loading takes too long
- Log viewer with Copy to Clipboard and Open Log File options
- Detailed logging throughout LoadMonitors for debugging

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 09:46:09 -05:00
4039e70d0c Copy to C:\build to avoid path length limits
All checks were successful
Build and Release / build (push) Successful in 9h0m10s
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-04 10:56:07 -05:00
28c6cad153 Add SourceDir to ISS and set TEMP for ISCC
All checks were successful
Build and Release / build (push) Successful in 8h0m19s
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-04 10:52:11 -05:00
dda6bf1002 Try cmd shell with working-directory for ISCC
All checks were successful
Build and Release / build (push) Successful in 9h0m11s
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-04 10:29:56 -05:00
821f119547 Add verbose output to ISCC for debugging
All checks were successful
Build and Release / build (push) Successful in 8h0m9s
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-04 10:05:04 -05:00
a33430faa0 Use Push-Location and Start-Process for ISCC
All checks were successful
Build and Release / build (push) Successful in 9h0m11s
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-04 10:01:45 -05:00
401d717adf Add debug output and create installer dir before ISCC
All checks were successful
Build and Release / build (push) Successful in 8h0m10s
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-04 09:57:48 -05:00
cfac39a838 Fix Inno Setup path issue - use PowerShell Set-Location
All checks were successful
Build and Release / build (push) Successful in 9h0m9s
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-04 08:20:38 -05:00
9 changed files with 428 additions and 36 deletions

View File

@@ -17,19 +17,32 @@ jobs:
with:
dotnet-version: '9.0.x'
- name: Update version in project files
shell: powershell
run: |
$version = "${{ gitea.ref_name }}".TrimStart("v")
Write-Host "Setting version to: $version"
# Update csproj (preserve UTF-8 BOM encoding)
$csprojPath = "DellMonitorControl/DellMonitorControl.csproj"
$content = [System.IO.File]::ReadAllText($csprojPath)
$content = $content -replace '<Version>.*</Version>', "<Version>$version</Version>"
$content = $content -replace '<AssemblyVersion>.*</AssemblyVersion>', "<AssemblyVersion>$version.0</AssemblyVersion>"
$content = $content -replace '<FileVersion>.*</FileVersion>', "<FileVersion>$version.0</FileVersion>"
[System.IO.File]::WriteAllText($csprojPath, $content, [System.Text.UTF8Encoding]::new($true))
# Update ISS
$issPath = "DellMonitorControl/MonitorControl.iss"
$issContent = [System.IO.File]::ReadAllText($issPath)
$issContent = $issContent -replace '#define MyAppVersion ".*"', "#define MyAppVersion `"$version`""
[System.IO.File]::WriteAllText($issPath, $issContent, [System.Text.UTF8Encoding]::new($true))
- name: Restore dependencies
run: dotnet restore
- name: Build Release
run: dotnet build --configuration Release --no-restore
- name: Update version in ISS
shell: powershell
run: |
$version = "${{ gitea.ref_name }}".TrimStart("v")
$issPath = "DellMonitorControl/MonitorControl.iss"
(Get-Content $issPath) -replace '#define MyAppVersion ".*"', "#define MyAppVersion `"$version`"" | Set-Content $issPath
- name: List build output
shell: cmd
run: |
@@ -47,7 +60,27 @@ jobs:
- name: Build Installer
shell: cmd
run: |
cd DellMonitorControl && "C:\Program Files (x86)\Inno Setup 6\ISCC.exe" MonitorControl.iss
echo Copying to short path to avoid path length issues...
mkdir C:\build 2>nul
xcopy /E /I /Y DellMonitorControl C:\build\app
echo.
echo Working from C:\build\app
cd /d C:\build\app
echo Current directory:
cd
echo.
set TEMP=C:\build\temp
set TMP=C:\build\temp
mkdir C:\build\temp 2>nul
mkdir installer 2>nul
echo.
echo Running Inno Setup...
"C:\Program Files (x86)\Inno Setup 6\ISCC.exe" MonitorControl.iss
echo.
echo ISCC exit code: %ERRORLEVEL%
echo.
echo Installer directory contents:
dir installer
- name: Create Release
shell: powershell
@@ -80,7 +113,7 @@ jobs:
}
# Upload installer
$filePath = "DellMonitorControl/installer/MonitorControl-Setup-$version.exe"
$filePath = "C:\build\app\installer\MonitorControl-Setup-$version.exe"
$fileName = "MonitorControl-Setup-$version.exe"
Write-Host "Uploading $fileName..."

View File

@@ -7,13 +7,42 @@ namespace DellMonitorControl
{
private TaskbarIcon? _trayIcon;
private MainWindow? _mainWindow;
private UpdateInfo? _pendingUpdate;
private void Application_Startup(object sender, StartupEventArgs e)
private async void Application_Startup(object sender, StartupEventArgs e)
{
_trayIcon = (TaskbarIcon)FindResource("TrayIcon");
_trayIcon.TrayLeftMouseUp += TrayIcon_Click;
_trayIcon.TrayBalloonTipClicked += TrayIcon_BalloonTipClicked;
_mainWindow = new MainWindow();
// Check for updates in background
await CheckForUpdatesAsync();
}
private async System.Threading.Tasks.Task CheckForUpdatesAsync()
{
try
{
_pendingUpdate = await UpdateChecker.CheckForUpdateAsync();
if (_pendingUpdate != null && _trayIcon != null)
{
_trayIcon.ShowBalloonTip(
"Update Available",
$"Monitor Control v{_pendingUpdate.LatestVersion} is available.\nClick to download.",
BalloonIcon.Info);
}
}
catch { }
}
private void TrayIcon_BalloonTipClicked(object sender, RoutedEventArgs e)
{
if (_pendingUpdate != null && !string.IsNullOrEmpty(_pendingUpdate.DownloadUrl))
{
UpdateChecker.OpenDownloadPage(_pendingUpdate.DownloadUrl);
}
}
private async void TrayIcon_Click(object sender, RoutedEventArgs e)

View File

@@ -0,0 +1,77 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace DellMonitorControl;
public static class DebugLogger
{
private static readonly List<string> _logs = new();
private static readonly object _lock = new();
private static readonly string _logFilePath;
static DebugLogger()
{
var tempPath = Path.Combine(Path.GetTempPath(), "CMM");
Directory.CreateDirectory(tempPath);
_logFilePath = Path.Combine(tempPath, "debug.log");
}
public static void Log(string message)
{
var entry = $"[{DateTime.Now:HH:mm:ss.fff}] {message}";
lock (_lock)
{
_logs.Add(entry);
try
{
File.AppendAllText(_logFilePath, entry + Environment.NewLine);
}
catch { }
}
}
public static void LogError(string message, Exception? ex = null)
{
var entry = ex != null
? $"[{DateTime.Now:HH:mm:ss.fff}] ERROR: {message} - {ex.GetType().Name}: {ex.Message}"
: $"[{DateTime.Now:HH:mm:ss.fff}] ERROR: {message}";
lock (_lock)
{
_logs.Add(entry);
if (ex != null)
_logs.Add($" Stack: {ex.StackTrace}");
try
{
File.AppendAllText(_logFilePath, entry + Environment.NewLine);
if (ex != null)
File.AppendAllText(_logFilePath, $" Stack: {ex.StackTrace}" + Environment.NewLine);
}
catch { }
}
}
public static string GetLogs()
{
lock (_lock)
{
return string.Join(Environment.NewLine, _logs);
}
}
public static void Clear()
{
lock (_lock)
{
_logs.Clear();
try
{
File.WriteAllText(_logFilePath, "");
}
catch { }
}
}
public static string LogFilePath => _logFilePath;
}

View File

@@ -12,17 +12,10 @@
<Copyright>Copyright © DangWang $([System.DateTime]::Now.ToString(yyyy))</Copyright>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'">
<Major>1</Major>
<Minor>0</Minor>
<ProjectStartedDate>$([System.DateTime]::op_Subtraction($([System.DateTime]::get_Now().get_Date()),$([System.DateTime]::new(2023,7,2))).get_TotalDays())</ProjectStartedDate>
<DaysSinceProjectStarted>$([System.DateTime]::Now.ToString(Hmm))</DaysSinceProjectStarted>
<DateTimeSuffix>$([System.DateTime]::Now.ToString(yyyyMMdd))</DateTimeSuffix>
<VersionSuffix>$(Major).$(Minor).$(ProjectStartedDate).$(DaysSinceProjectStarted)</VersionSuffix>
<AssemblyVersion Condition=" '$(DateTimeSuffix)' == '' ">0.0.0.1</AssemblyVersion>
<AssemblyVersion Condition=" '$(DateTimeSuffix)' != '' ">$(VersionSuffix)</AssemblyVersion>
<Version Condition=" '$(DateTimeSuffix)' == '' ">0.0.0.1</Version>
<Version Condition=" '$(DateTimeSuffix)' != '' ">$(DateTimeSuffix)</Version>
<PropertyGroup>
<Version>1.0.13</Version>
<AssemblyVersion>1.0.13.0</AssemblyVersion>
<FileVersion>1.0.13.0</FileVersion>
</PropertyGroup>
<ItemGroup>

View File

@@ -215,6 +215,9 @@
</Ellipse>
<TextBlock Name="loadingText" Text="Loading..." Foreground="LightGray" FontSize="12"
HorizontalAlignment="Center"/>
<Button Name="showLogButton" Content="Show Log" Margin="0,10,0,0"
Style="{StaticResource DarkButton}" FontSize="10"
Click="ShowLogButton_Click" Visibility="Collapsed"/>
</StackPanel>
</StackPanel>
</ScrollViewer>

View File

@@ -9,6 +9,7 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Threading;
namespace DellMonitorControl;
@@ -16,15 +17,18 @@ public partial class MainWindow : Window
{
private List<(XMonitor Monitor, List<InputSourceOption> Options)> _loadedMonitors = new();
private Storyboard? _spinnerStoryboard;
private DispatcherTimer? _showLogButtonTimer;
public MainWindow()
{
InitializeComponent();
_spinnerStoryboard = (Storyboard)FindResource("SpinnerAnimation");
DebugLogger.Log("MainWindow initialized");
}
public void ShowNearTray()
{
DebugLogger.Log("ShowNearTray called");
var workArea = SystemParameters.WorkArea;
Left = workArea.Right - Width - 10;
Top = workArea.Bottom - 350;
@@ -34,10 +38,25 @@ public partial class MainWindow : Window
// Start spinner
_spinnerStoryboard?.Begin(this, true);
// Show log button after 3 seconds if still loading
showLogButton.Visibility = Visibility.Collapsed;
_showLogButtonTimer?.Stop();
_showLogButtonTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(3) };
_showLogButtonTimer.Tick += (s, e) =>
{
_showLogButtonTimer.Stop();
if (loadingPanel.Visibility == Visibility.Visible || sp.Children.Contains(loadingPanel))
{
showLogButton.Visibility = Visibility.Visible;
DebugLogger.Log("Show Log button displayed (loading took > 3s)");
}
};
_showLogButtonTimer.Start();
Dispatcher.BeginInvoke(new Action(() =>
{
Top = workArea.Bottom - ActualHeight - 10;
}), System.Windows.Threading.DispatcherPriority.Loaded);
}), DispatcherPriority.Loaded);
}
private void Window_Deactivated(object sender, EventArgs e)
@@ -51,18 +70,86 @@ public partial class MainWindow : Window
Application.Current.Shutdown();
}
private void ShowLogButton_Click(object sender, RoutedEventArgs e)
{
var logWindow = new Window
{
Title = "Debug Log",
Width = 600,
Height = 400,
WindowStartupLocation = WindowStartupLocation.CenterScreen,
Background = new SolidColorBrush(Color.FromRgb(45, 45, 45))
};
var grid = new Grid();
grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
var textBox = new TextBox
{
Text = DebugLogger.GetLogs(),
IsReadOnly = true,
Background = new SolidColorBrush(Color.FromRgb(30, 30, 30)),
Foreground = Brushes.LightGray,
FontFamily = new FontFamily("Consolas"),
FontSize = 11,
VerticalScrollBarVisibility = ScrollBarVisibility.Auto,
HorizontalScrollBarVisibility = ScrollBarVisibility.Auto,
TextWrapping = TextWrapping.NoWrap,
Margin = new Thickness(10)
};
Grid.SetRow(textBox, 0);
grid.Children.Add(textBox);
var buttonPanel = new StackPanel { Orientation = Orientation.Horizontal, HorizontalAlignment = HorizontalAlignment.Right, Margin = new Thickness(10) };
var copyBtn = new Button { Content = "Copy to Clipboard", Padding = new Thickness(10, 5, 10, 5), Margin = new Thickness(0, 0, 10, 0) };
copyBtn.Click += (s, args) =>
{
Clipboard.SetText(DebugLogger.GetLogs());
MessageBox.Show("Log copied to clipboard!", "Debug Log", MessageBoxButton.OK, MessageBoxImage.Information);
};
buttonPanel.Children.Add(copyBtn);
var openFileBtn = new Button { Content = "Open Log File", Padding = new Thickness(10, 5, 10, 5), Margin = new Thickness(0, 0, 10, 0) };
openFileBtn.Click += (s, args) =>
{
try
{
System.Diagnostics.Process.Start("explorer.exe", $"/select,\"{DebugLogger.LogFilePath}\"");
}
catch { }
};
buttonPanel.Children.Add(openFileBtn);
var closeBtn = new Button { Content = "Close", Padding = new Thickness(10, 5, 10, 5) };
closeBtn.Click += (s, args) => logWindow.Close();
buttonPanel.Children.Add(closeBtn);
Grid.SetRow(buttonPanel, 1);
grid.Children.Add(buttonPanel);
logWindow.Content = grid;
logWindow.Show();
}
public async Task LoadMonitors()
{
DebugLogger.Log("LoadMonitors started");
var newChildren = new List<UIElement>();
_loadedMonitors.Clear();
try
{
DebugLogger.Log("Scanning for monitors...");
await CMMCommand.ScanMonitor();
DebugLogger.Log("Scan complete, reading monitor data...");
var monitors = (await CMMCommand.ReadMonitorsData()).ToList();
DebugLogger.Log($"Found {monitors.Count} monitor(s)");
if (!monitors.Any())
{
DebugLogger.Log("No DDC/CI monitors detected");
newChildren.Add(new TextBlock
{
Text = "No DDC/CI monitors detected",
@@ -76,16 +163,33 @@ public partial class MainWindow : Window
{
foreach (var m in monitors)
{
DebugLogger.Log($"Processing monitor: {m.MonitorName} (SN: {m.SerialNumber})");
DebugLogger.Log($" Getting brightness...");
var brightness = await CMMCommand.GetBrightness(m.SerialNumber) ?? 50;
DebugLogger.Log($" Brightness: {brightness}");
DebugLogger.Log($" Getting contrast...");
var contrast = await CMMCommand.GetContrast(m.SerialNumber) ?? 50;
DebugLogger.Log($" Contrast: {contrast}");
DebugLogger.Log($" Getting input source...");
var inputSource = await CMMCommand.GetInputSource(m.SerialNumber);
DebugLogger.Log($" Input source: {inputSource}");
DebugLogger.Log($" Getting input options...");
var inputOptions = await CMMCommand.GetInputSourceOptions(m.SerialNumber);
DebugLogger.Log($" Input options count: {inputOptions.Count}");
DebugLogger.Log($" Getting power status...");
var powerStatus = await CMMCommand.GetMonPowerStatus(m.SerialNumber) ?? "Unknown";
DebugLogger.Log($" Power status: {powerStatus}");
_loadedMonitors.Add((m, inputOptions));
// Apply config to filter hidden ports and use custom labels
var filteredOptions = MonitorConfigManager.ApplyConfigToOptions(m.SerialNumber, inputOptions);
DebugLogger.Log($" Filtered options count: {filteredOptions.Count}");
// Monitor name header with Config button
newChildren.Add(CreateMonitorHeader(m, inputOptions));
@@ -100,6 +204,8 @@ public partial class MainWindow : Window
// Separator
newChildren.Add(new Border { Height = 1, Background = new SolidColorBrush(Color.FromRgb(80, 80, 80)), Margin = new Thickness(0, 10, 0, 2) });
DebugLogger.Log($" Monitor {m.MonitorName} processing complete");
}
// Remove last separator
@@ -108,27 +214,33 @@ public partial class MainWindow : Window
}
// Load quick-switch toolbar
DebugLogger.Log("Loading quick-switch toolbar...");
LoadQuickSwitchToolbar();
DebugLogger.Log("Quick-switch toolbar loaded");
}
catch (Exception ex)
{
DebugLogger.LogError("LoadMonitors failed", ex);
newChildren.Clear();
newChildren.Add(new TextBlock { Text = $"Error: {ex.Message}", Foreground = Brushes.OrangeRed, FontSize = 11, TextWrapping = TextWrapping.Wrap });
}
// Stop spinner
// Stop spinner and timer
_spinnerStoryboard?.Stop(this);
_showLogButtonTimer?.Stop();
sp.VerticalAlignment = VerticalAlignment.Top;
sp.Children.Clear();
foreach (var child in newChildren)
sp.Children.Add(child);
DebugLogger.Log("LoadMonitors complete, UI updated");
Dispatcher.BeginInvoke(new Action(() =>
{
var workArea = SystemParameters.WorkArea;
Top = workArea.Bottom - ActualHeight - 10;
}), System.Windows.Threading.DispatcherPriority.Loaded);
}), DispatcherPriority.Loaded);
}
private void LoadQuickSwitchToolbar()

View File

@@ -2,8 +2,10 @@
#define MyAppVersion "1.0.0"
#define MyAppPublisher "Dang"
#define MyAppExeName "DellMonitorControl.exe"
#define SourcePath "bin\Release\net9.0-windows"
[Setup]
SourceDir=.
AppId={{8F3E4A2B-1C5D-4E6F-9A8B-7C2D1E3F4A5B}
AppName={#MyAppName}
AppVersion={#MyAppVersion}

View File

@@ -0,0 +1,119 @@
using System;
using System.Diagnostics;
using System.Net.Http;
using System.Reflection;
using System.Text.Json;
using System.Threading.Tasks;
namespace DellMonitorControl;
public class UpdateChecker
{
private const string ReleasesApiUrl = "https://git.marketally.com/api/v1/repos/misc/ControlMyMonitorManagement/releases/latest";
private static readonly HttpClient _httpClient = new() { Timeout = TimeSpan.FromSeconds(10) };
public static Version CurrentVersion => Assembly.GetExecutingAssembly().GetName().Version ?? new Version(1, 0, 0);
public static async Task<UpdateInfo?> CheckForUpdateAsync()
{
try
{
DebugLogger.Log($"Checking for updates... Current version: {CurrentVersion}");
var response = await _httpClient.GetStringAsync(ReleasesApiUrl);
var release = JsonSerializer.Deserialize<GiteaRelease>(response);
if (release == null || string.IsNullOrEmpty(release.tag_name))
{
DebugLogger.Log("No release info found");
return null;
}
var latestVersionStr = release.tag_name.TrimStart('v', 'V');
if (!Version.TryParse(latestVersionStr, out var latestVersion))
{
DebugLogger.Log($"Could not parse version: {release.tag_name}");
return null;
}
DebugLogger.Log($"Latest version: {latestVersion}");
if (latestVersion > CurrentVersion)
{
// Find the installer asset
string? downloadUrl = null;
if (release.assets != null)
{
foreach (var asset in release.assets)
{
if (asset.name?.EndsWith(".exe", StringComparison.OrdinalIgnoreCase) == true)
{
downloadUrl = asset.browser_download_url;
break;
}
}
}
// Fallback to release page if no direct download
downloadUrl ??= release.html_url;
DebugLogger.Log($"Update available: {latestVersion}, URL: {downloadUrl}");
return new UpdateInfo
{
CurrentVersion = CurrentVersion,
LatestVersion = latestVersion,
DownloadUrl = downloadUrl ?? "",
ReleaseNotes = release.body ?? ""
};
}
DebugLogger.Log("No update available");
return null;
}
catch (Exception ex)
{
DebugLogger.LogError("Update check failed", ex);
return null;
}
}
public static void OpenDownloadPage(string url)
{
try
{
Process.Start(new ProcessStartInfo
{
FileName = url,
UseShellExecute = true
});
}
catch (Exception ex)
{
DebugLogger.LogError("Failed to open download page", ex);
}
}
}
public class UpdateInfo
{
public Version CurrentVersion { get; set; } = new(1, 0, 0);
public Version LatestVersion { get; set; } = new(1, 0, 0);
public string DownloadUrl { get; set; } = "";
public string ReleaseNotes { get; set; } = "";
}
public class GiteaRelease
{
public string? tag_name { get; set; }
public string? name { get; set; }
public string? body { get; set; }
public string? html_url { get; set; }
public GiteaAsset[]? assets { get; set; }
}
public class GiteaAsset
{
public string? name { get; set; }
public string? browser_download_url { get; set; }
}

View File

@@ -19,7 +19,7 @@ internal class ConsoleHelper
}
};
public static async Task<string> ExecuteCommand(string command)
public static async Task<string> ExecuteCommand(string command, int timeoutMs = 5000)
{
Process p = new Process();
p.StartInfo.UseShellExecute = false;
@@ -27,10 +27,19 @@ internal class ConsoleHelper
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = command;
p.Start();
var output = await p.StandardOutput.ReadToEndAsync();
await p.WaitForExitAsync();
return output;
using var cts = new CancellationTokenSource(timeoutMs);
try
{
var output = await p.StandardOutput.ReadToEndAsync(cts.Token);
await p.WaitForExitAsync(cts.Token);
return output;
}
catch (OperationCanceledException)
{
try { p.Kill(); } catch { }
return string.Empty;
}
}
public static async Task<string> CmdCommandAsync(params string[] cmds) =>
@@ -40,6 +49,11 @@ internal class ConsoleHelper
Command(cmdFileName, cmds);
public static async Task<string> CommandAsync(string fileName, params string[] cmds)
{
return await CommandAsync(fileName, 5000, cmds);
}
public static async Task<string> CommandAsync(string fileName, int timeoutMs, params string[] cmds)
{
var p = CreatProcess(fileName);
p.Start();
@@ -48,14 +62,24 @@ internal class ConsoleHelper
p.StandardInput.WriteLine(cmd);
}
p.StandardInput.WriteLine("exit");
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();
p.Close();
Debug.WriteLine(result);
return result;
using var cts = new CancellationTokenSource(timeoutMs);
try
{
var result = await p.StandardOutput.ReadToEndAsync(cts.Token);
var error = await p.StandardError.ReadToEndAsync(cts.Token);
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)
{
try { p.Kill(); } catch { }
return string.Empty;
}
}
public static string Command(string fileName, params string[] cmds)