Show progress bar in C# console application's taskbar button

186 Views Asked by At

I want to show progress in my console application's taskbar button, just like Windows Explorer does when you're copying files.

With "taskbar button" I am referring to the space on the taskbar that represents an open program. Example

I am NOT referring to a textual representation of a progress bar in the console window, itself.

I am using .Net 7 and not using WPF or WinForms.

I have read the following questions, which don't answer mine:

I have the following code, which I swear used to work a couple years ago:

public enum TaskbarStates
{
    NoProgress = 0,
    Indeterminate = 0x1,
    Normal = 0x2,
    Error = 0x4,
    Paused = 0x8
}

[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, TaskbarStates state);
}

[ComImport()]
[Guid("56fdf344-fd6d-11d0-958a-006097c9a090")]
[ClassInterface(ClassInterfaceType.None)]
private class TaskbarInstance { }

private static readonly ITaskbarList3 taskbarInstance = (ITaskbarList3)new TaskbarInstance();
private static readonly bool taskbarSupported = Environment.OSVersion.Version >= new Version(6, 1);

public static void SetState(TaskbarStates taskbarState)
{
    IntPtr handle = Process.GetCurrentProcess().MainWindowHandle;
    if (taskbarSupported)
        taskbarInstance.SetProgressState(handle, taskbarState);
}

public static void SetValue(double progressValue, double progressMax)
{
    IntPtr handle = Process.GetCurrentProcess().MainWindowHandle;
    if (taskbarSupported)
        taskbarInstance.SetProgressValue(handle, (ulong)progressValue, (ulong)progressMax);
}

I would use the code like this:

SetState(TaskbarStates.Normal);
SetValue(20, 30);

However, now this does nothing. I tried with all states and values and waiting for a key press to ensure the message got posted and it isn't being skewed by me setting breakpoints in the code.

Thinking it might be the handle that isn't being captured correctly I also tried replacing the IntPtr handle = Process.GetCurrentProcess().MainWindowHandle; with:

const int STD_OUTPUT_HANDLE = -11;

[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr GetStdHandle(int nStdHandle);

IntPtr handle = GetStdHandle(STD_OUTPUT_HANDLE);

But that doesn't work, either.

What am I doing wrong?

1

There are 1 best solutions below

0
On

As it always happens, 5 minutes after posting the question I got the answer.

I don't know why neither GetStdHandle(STD_OUTPUT_HANDLE) nor Process.GetCurrentProcess().MainWindowHandle worked but what DID work was:

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

IntPtr handle = GetConsoleWindow();

Once the window handle was correct everything was OK.