Skip to content

Commit

Permalink
Add taskbar progress.
Browse files Browse the repository at this point in the history
  • Loading branch information
timcassell committed Oct 18, 2022
1 parent 28bf214 commit 060eded
Show file tree
Hide file tree
Showing 8 changed files with 491 additions and 0 deletions.
96 changes: 96 additions & 0 deletions src/BenchmarkDotNet/Helpers/Taskbar/CoreErrorHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
//Copyright (c) Microsoft Corporation. All rights reserved.

namespace MS.WindowsAPICodePack.Internal
{
/// <summary>HRESULT Wrapper</summary>
public enum HResult
{
/// <summary>S_OK</summary>
Ok = 0x0000,

/// <summary>S_FALSE</summary>
False = 0x0001,

/// <summary>E_INVALIDARG</summary>
InvalidArguments = unchecked((int)0x80070057),

/// <summary>E_OUTOFMEMORY</summary>
OutOfMemory = unchecked((int)0x8007000E),

/// <summary>E_NOINTERFACE</summary>
NoInterface = unchecked((int)0x80004002),

/// <summary>E_FAIL</summary>
Fail = unchecked((int)0x80004005),

/// <summary>E_ELEMENTNOTFOUND</summary>
ElementNotFound = unchecked((int)0x80070490),

/// <summary>TYPE_E_ELEMENTNOTFOUND</summary>
TypeElementNotFound = unchecked((int)0x8002802B),

/// <summary>NO_OBJECT</summary>
NoObject = unchecked((int)0x800401E5),

/// <summary>Win32 Error code: ERROR_CANCELLED</summary>
Win32ErrorCanceled = 1223,

/// <summary>ERROR_CANCELLED</summary>
Canceled = unchecked((int)0x800704C7),

/// <summary>The requested resource is in use</summary>
ResourceInUse = unchecked((int)0x800700AA),

/// <summary>The requested resources is read-only.</summary>
AccessDenied = unchecked((int)0x80030005)
}

/// <summary>Provide Error Message Helper Methods. This is intended for Library Internal use only.</summary>
internal static class CoreErrorHelper
{
/// <summary>This is intended for Library Internal use only.</summary>
public const int Ignored = (int)HResult.Ok;

/// <summary>This is intended for Library Internal use only.</summary>
private const int FacilityWin32 = 7;

/// <summary>This is intended for Library Internal use only.</summary>
/// <param name="result">The error code.</param>
/// <returns>True if the error code indicates failure.</returns>
public static bool Failed(HResult result) => !Succeeded(result);

/// <summary>This is intended for Library Internal use only.</summary>
/// <param name="result">The error code.</param>
/// <returns>True if the error code indicates failure.</returns>
public static bool Failed(int result) => !Succeeded(result);

/// <summary>This is intended for Library Internal use only.</summary>
/// <param name="win32ErrorCode">The Windows API error code.</param>
/// <returns>The equivalent HRESULT.</returns>
public static int HResultFromWin32(int win32ErrorCode)
{
if (win32ErrorCode > 0)
{
win32ErrorCode =
(int)(((uint)win32ErrorCode & 0x0000FFFF) | (FacilityWin32 << 16) | 0x80000000);
}
return win32ErrorCode;
}

/// <summary>This is intended for Library Internal use only.</summary>
/// <param name="result">The COM error code.</param>
/// <param name="win32ErrorCode">The Win32 error code.</param>
/// <returns>Inticates that the Win32 error code corresponds to the COM error code.</returns>
public static bool Matches(int result, int win32ErrorCode) => (result == HResultFromWin32(win32ErrorCode));

/// <summary>This is intended for Library Internal use only.</summary>
/// <param name="result">The error code.</param>
/// <returns>True if the error code indicates success.</returns>
public static bool Succeeded(int result) => result >= 0;

/// <summary>This is intended for Library Internal use only.</summary>
/// <param name="result">The error code.</param>
/// <returns>True if the error code indicates success.</returns>
public static bool Succeeded(HResult result) => Succeeded((int)result);
}
}
25 changes: 25 additions & 0 deletions src/BenchmarkDotNet/Helpers/Taskbar/CoreHelpers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//Copyright (c) Microsoft Corporation. All rights reserved.

using System;

namespace MS.WindowsAPICodePack.Internal
{
/// <summary>Common Helper methods</summary>
public static class CoreHelpers
{
/// <summary>Determines if the application is running on Windows 7 or later</summary>
public static bool RunningOnWin7OrLater =>
// Verifies that OS version is 6.1 or greater, and the Platform is WinNT.
Environment.OSVersion.Platform == PlatformID.Win32NT &&
Environment.OSVersion.Version.CompareTo(new Version(6, 1)) >= 0;

/// <summary>Throws PlatformNotSupportedException if the application is not running on Windows 7</summary>
public static void ThrowIfNotWin7OrLater()
{
if (!RunningOnWin7OrLater)
{
throw new PlatformNotSupportedException("Platform is not Windows 7 or later");
}
}
}
}
94 changes: 94 additions & 0 deletions src/BenchmarkDotNet/Helpers/Taskbar/TaskbarCOMInterfaces.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
//Copyright (c) Microsoft Corporation. All rights reserved.

using MS.WindowsAPICodePack.Internal;
using System;
using System.Runtime.InteropServices;

namespace Microsoft.WindowsAPICodePack.Taskbar
{
[ComImport]
[Guid("c43dc798-95d1-4bea-9030-bb99e2983a1a")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface ITaskbarList4
{
// 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, TaskbarProgressBarStatus tbpFlags);

[PreserveSig]
void RegisterTab(IntPtr hwndTab, IntPtr hwndMDI);

[PreserveSig]
void UnregisterTab(IntPtr hwndTab);

[PreserveSig]
void SetTabOrder(IntPtr hwndTab, IntPtr hwndInsertBefore);

[PreserveSig]
void SetTabActive(IntPtr hwndTab, IntPtr hwndInsertBefore, uint dwReserved);

[PreserveSig]
HResult ThumbBarAddButtons(
IntPtr hwnd,
uint cButtons,
[MarshalAs(UnmanagedType.LPArray)] ThumbButton[] pButtons);

[PreserveSig]
HResult ThumbBarUpdateButtons(
IntPtr hwnd,
uint cButtons,
[MarshalAs(UnmanagedType.LPArray)] ThumbButton[] pButtons);

[PreserveSig]
void ThumbBarSetImageList(IntPtr hwnd, IntPtr himl);

[PreserveSig]
void SetOverlayIcon(
IntPtr hwnd,
IntPtr hIcon,
[MarshalAs(UnmanagedType.LPWStr)] string pszDescription);

[PreserveSig]
void SetThumbnailTooltip(
IntPtr hwnd,
[MarshalAs(UnmanagedType.LPWStr)] string pszTip);

[PreserveSig]
void SetThumbnailClip(
IntPtr hwnd,
IntPtr prcClip);

// ITaskbarList4
void SetTabProperties(IntPtr hwndTab, SetTabPropertiesOption stpFlags);
}

[Guid("56FDF344-FD6D-11d0-958A-006097C9A090")]
[ClassInterface(ClassInterfaceType.None)]
[ComImport]
internal class CTaskbarList { }
}
35 changes: 35 additions & 0 deletions src/BenchmarkDotNet/Helpers/Taskbar/TaskbarEnums.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//Copyright (c) Microsoft Corporation. All rights reserved.

namespace Microsoft.WindowsAPICodePack.Taskbar
{
/// <summary>
/// Represents the thumbnail progress bar state.
/// </summary>
public enum TaskbarProgressBarState
{
/// <summary>
/// No progress is displayed.
/// </summary>
NoProgress = 0,

/// <summary>
/// The progress is indeterminate (marquee).
/// </summary>
Indeterminate = 0x1,

/// <summary>
/// Normal progress is displayed.
/// </summary>
Normal = 0x2,

/// <summary>
/// An error occurred (red).
/// </summary>
Error = 0x4,

/// <summary>
/// The operation is paused (yellow).
/// </summary>
Paused = 0x8
}
}
32 changes: 32 additions & 0 deletions src/BenchmarkDotNet/Helpers/Taskbar/TaskbarList.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
namespace Microsoft.WindowsAPICodePack.Taskbar
{
/// <summary>
/// Provides internal access to the functions provided by the ITaskbarList4 interface,
/// without being forced to refer to it through another singleton.
/// </summary>
internal static class TaskbarList
{
private static readonly object _syncLock = new object();

private static ITaskbarList4 _taskbarList;
internal static ITaskbarList4 Instance
{
get
{
if (_taskbarList == null)
{
lock (_syncLock)
{
if (_taskbarList == null)
{
_taskbarList = (ITaskbarList4)new CTaskbarList();
_taskbarList.HrInit();
}
}
}

return _taskbarList;
}
}
}
}
71 changes: 71 additions & 0 deletions src/BenchmarkDotNet/Helpers/Taskbar/TaskbarManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//Copyright (c) Microsoft Corporation. All rights reserved.

using MS.WindowsAPICodePack.Internal;
using System;

namespace Microsoft.WindowsAPICodePack.Taskbar
{
/// <summary>
/// Represents an instance of the Windows taskbar
/// </summary>
public class TaskbarManager
{
// Hide the default constructor
private TaskbarManager() => CoreHelpers.ThrowIfNotWin7OrLater();

// Best practice recommends defining a private object to lock on
private static readonly object _syncLock = new object();

private static TaskbarManager _instance;
/// <summary>
/// Represents an instance of the Windows Taskbar
/// </summary>
public static TaskbarManager Instance
{
get
{
if (_instance == null)
{
lock (_syncLock)
{
if (_instance == null)
{
_instance = new TaskbarManager();
}
}
}

return _instance;
}
}

/// <summary>
/// Displays or updates a progress bar hosted in a taskbar button of the given window handle
/// to show the specific percentage completed of the full operation.
/// </summary>
/// <param name="windowHandle">The handle of the window whose associated taskbar button is being used as a progress indicator.
/// This window belong to a calling process associated with the button's application and must be already loaded.</param>
/// <param name="currentValue">An application-defined value that indicates the proportion of the operation that has been completed at the time the method is called.</param>
/// <param name="maximumValue">An application-defined value that specifies the value currentValue will have when the operation is complete.</param>
public void SetProgressValue(int currentValue, int maximumValue, IntPtr windowHandle) => TaskbarList.Instance.SetProgressValue(
windowHandle,
Convert.ToUInt32(currentValue),
Convert.ToUInt32(maximumValue));

/// <summary>
/// Sets the type and state of the progress indicator displayed on a taskbar button
/// of the given window handle
/// </summary>
/// <param name="windowHandle">The handle of the window whose associated taskbar button is being used as a progress indicator.
/// This window belong to a calling process associated with the button's application and must be already loaded.</param>
/// <param name="state">Progress state of the progress button</param>
public void SetProgressState(TaskbarProgressBarState state, IntPtr windowHandle) => TaskbarList.Instance.SetProgressState(windowHandle, (TaskbarProgressBarStatus)state);

/// <summary>
/// Indicates whether this feature is supported on the current platform.
/// </summary>
public static bool IsPlatformSupported =>
// We need Windows 7 onwards ...
CoreHelpers.RunningOnWin7OrLater;
}
}
Loading

0 comments on commit 060eded

Please sign in to comment.