Skip to content
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

Add taskbar progress #2158

Merged
merged 8 commits into from
Oct 27, 2022
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 127 additions & 0 deletions src/BenchmarkDotNet/Helpers/Taskbar/TaskbarProgress.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
using System;
using System.Runtime.InteropServices;

namespace BenchmarkDotNet.Helpers
{
internal class TaskbarProgress : IDisposable
{
private static readonly bool OsVersionIsSupported = Portability.RuntimeInformation.IsWindows()
// Must be windows 7 or greater
&& Environment.OSVersion.Version >= new Version(6, 1);

private IntPtr consoleWindowHandle = IntPtr.Zero;

[DllImport("kernel32.dll")]
private static extern IntPtr GetConsoleWindow();

[DllImport("user32.dll", ExactSpelling = true)]
private static extern IntPtr GetAncestor(IntPtr hwnd, GetAncestorFlags flags);

internal TaskbarProgress()
{
if (OsVersionIsSupported)
{
consoleWindowHandle = GetConsoleWindow();
if (consoleWindowHandle != IntPtr.Zero)
{
IntPtr rootOwnerHandle = GetAncestor(consoleWindowHandle, GetAncestorFlags.RootOwner);
if (rootOwnerHandle != IntPtr.Zero)
{
consoleWindowHandle = rootOwnerHandle;
}
TaskbarProgressCom.SetState(consoleWindowHandle, TaskbarProgressState.Normal);
Console.CancelKeyPress += OnConsoleCancelEvent;
}
}
}

internal void SetProgress(ulong currentValue, ulong maximumValue)
{
if (consoleWindowHandle != IntPtr.Zero)
{
TaskbarProgressCom.SetValue(consoleWindowHandle, currentValue, maximumValue);
}
}

private void OnConsoleCancelEvent(object sender, ConsoleCancelEventArgs e)
{
Dispose();
}

public void Dispose()
{
if (consoleWindowHandle != IntPtr.Zero)
{
TaskbarProgressCom.SetState(consoleWindowHandle, TaskbarProgressState.NoProgress);
consoleWindowHandle = IntPtr.Zero;
Console.CancelKeyPress -= OnConsoleCancelEvent;
}
}
}

internal enum GetAncestorFlags : uint
{
Parent = 1,
Root = 2,
RootOwner = 3
}

internal enum TaskbarProgressState
{
NoProgress = 0,
Indeterminate = 0x1,
Normal = 0x2,
Error = 0x4,
Paused = 0x8
}

internal static class TaskbarProgressCom
{
[ComImport]
[Guid("ea1afb91-9e28-4b86-90e9-9e9f8a5eefaf")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface ITaskbarList3
{
// ITaskbarList
[PreserveSig]
void HrInit();
[PreserveSig]
void AddTab(IntPtr hwnd);
[PreserveSig]
void DeleteTab(IntPtr hwnd);
[PreserveSig]
void ActivateTab(IntPtr hwnd);
[PreserveSig]
void SetActiveAlt(IntPtr hwnd);

// ITaskbarList2
[PreserveSig]
void MarkFullscreenWindow(IntPtr hwnd, [MarshalAs(UnmanagedType.Bool)] bool fFullscreen);

// ITaskbarList3
[PreserveSig]
void SetProgressValue(IntPtr hwnd, ulong ullCompleted, ulong ullTotal);
[PreserveSig]
void SetProgressState(IntPtr hwnd, TaskbarProgressState state);
}

[Guid("56FDF344-FD6D-11d0-958A-006097C9A090")]
[ClassInterface(ClassInterfaceType.None)]
[ComImport]
private class TaskbarInstance
{
}

private static readonly ITaskbarList3 s_taskbarInstance = (ITaskbarList3) new TaskbarInstance();

internal static void SetState(IntPtr windowHandle, TaskbarProgressState taskbarState)
{
s_taskbarInstance.SetProgressState(windowHandle, taskbarState);
}

internal static void SetValue(IntPtr windowHandle, ulong progressValue, ulong progressMax)
{
s_taskbarInstance.SetProgressValue(windowHandle, progressValue, progressMax);
}
}
}
7 changes: 5 additions & 2 deletions src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ private static Summary Run(BenchmarkRunInfo benchmarkRunInfo,

UpdateTitle(totalBenchmarkCount, benchmarksToRunCount);

using var taskbarProgress = new TaskbarProgress();

using (var powerManagementApplier = new PowerManagementApplier(logger))
{
bool stop = false;
Expand Down Expand Up @@ -224,7 +226,7 @@ private static Summary Run(BenchmarkRunInfo benchmarkRunInfo,

benchmarksToRunCount -= stop ? benchmarks.Length - i : 1;

LogProgress(logger, in runsChronometer, totalBenchmarkCount, benchmarksToRunCount);
LogProgress(logger, in runsChronometer, totalBenchmarkCount, benchmarksToRunCount, taskbarProgress);
}
}

Expand Down Expand Up @@ -643,7 +645,7 @@ private static void UpdateTitle(int totalBenchmarkCount, int benchmarksToRunCoun
}
}

private static void LogProgress(ILogger logger, in StartedClock runsChronometer, int totalBenchmarkCount, int benchmarksToRunCount)
private static void LogProgress(ILogger logger, in StartedClock runsChronometer, int totalBenchmarkCount, int benchmarksToRunCount, TaskbarProgress taskbarProgress)
{
int executedBenchmarkCount = totalBenchmarkCount - benchmarksToRunCount;
TimeSpan fromNow = GetEstimatedFinishTime(runsChronometer, benchmarksToRunCount, executedBenchmarkCount);
Expand All @@ -656,6 +658,7 @@ private static void LogProgress(ILogger logger, in StartedClock runsChronometer,
{
Console.Title = $"{benchmarksToRunCount}/{totalBenchmarkCount} Remaining - {(int)fromNow.TotalHours}h {fromNow.Minutes}m to finish";
}
taskbarProgress.SetProgress((ulong) executedBenchmarkCount, (ulong) totalBenchmarkCount);
}

private static TimeSpan GetEstimatedFinishTime(in StartedClock runsChronometer, int benchmarksToRunCount, int executedBenchmarkCount)
Expand Down