13

What would be the shortest code to set the state of a Windows 7 taskbar button for a known window handle?

The goal is to write a console utility that changes the progress and state (colour) of the console window taskbar item from a batch script. While the script performs different tasks, the taskbar item of its console window should represent the current state.

I get the window handle with the GetConsoleWindow() function, but then it seems to require loads of COM and Shell API stuff that I don't understand. One example I've found uses a whole GUI application with MFC to demonstrate the API, but most of it is way too complicated for my little tool and I don't understand it well enough to remove the stuff I don't need.

The tool should compile on Windows 7 with VS2010 (C++) but also run on earlier Windows versions (doing nothing if a feature is not available).

ygoe
  • 18,655
  • 23
  • 113
  • 210
  • What do you mean by "taskbar state"? Do you mean just displaying progress or something else? – sashoalm Feb 21 '13 at 12:52
  • Yes, just progress and state (indeterminate, paused, error, i.e. the colours green, yellow and red). No jumplists, thumbnails or action buttons. – ygoe Feb 21 '13 at 13:14

2 Answers2

15

I created a class to set the progress in the Win7 taskbar for a project at one time. It's a wrapper for the ITaskBarList3 interface available from the Windows Shell. It's specifically done with ITaskBarList3.SetProgressState and ITaskBarList3.SetProgressValue functions.

This is the code I dug up:

#include <shobjidl.h>
#include <windows.h>
#pragma comment(lib, "Shell32.lib")
#pragma comment(lib, "Ole32.lib")

class Win7TaskbarProgress  
{
public:
    Win7TaskbarProgress();
    virtual ~Win7TaskbarProgress();

    void SetProgressState(HWND hwnd, TBPFLAG flag);
    void SetProgressValue(HWND hwnd, ULONGLONG ullCompleted, ULONGLONG ullTotal);

private:
    bool Init();
    ITaskbarList3* m_pITaskBarList3;
    bool m_bFailed;
};

Win7TaskbarProgress::Win7TaskbarProgress()
{
    m_pITaskBarList3 = NULL;
    m_bFailed = false;
}

Win7TaskbarProgress::~Win7TaskbarProgress()
{
    if (m_pITaskBarList3)   
    {
        m_pITaskBarList3->Release();
        CoUninitialize();
    }
}

void Win7TaskbarProgress::SetProgressState( HWND hwnd, TBPFLAG flag )
{
    if (Init())
        m_pITaskBarList3->SetProgressState(hwnd, flag);
}

void Win7TaskbarProgress::SetProgressValue( HWND hwnd, ULONGLONG ullCompleted, ULONGLONG ullTotal )
{
    if (Init())
        m_pITaskBarList3->SetProgressValue(hwnd, ullCompleted, ullTotal);
}

bool Win7TaskbarProgress::Init()
{
    if (m_pITaskBarList3)
        return true;

    if (m_bFailed)
        return false;
    
    // Initialize COM for this thread...
    CoInitialize(NULL);
    
    CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, IID_ITaskbarList3, (void **)&m_pITaskBarList3);

    if (m_pITaskBarList3)
        return true;

    m_bFailed = true;
    CoUninitialize();
    return false;
}
sashoalm
  • 75,001
  • 122
  • 434
  • 781
  • 1
    I think I need this: `#include ` But what does `VERIFY()` do? – ygoe Feb 21 '13 at 13:33
  • 1
    Nothing important, I removed edited my answer and removed it. It's like an ASSERT() but the statement still gets executed in Release builds. See http://msdn.microsoft.com/en-us/library/fcatwy09(v=vs.80).aspx – sashoalm Feb 21 '13 at 13:55
  • 4
    I've made a handy little tool out of this, extending my previous Flash-only feature. You can find it here: http://dev.unclassified.de/apps/flashconsolewindow – ygoe Feb 24 '13 at 22:58
  • It does not work in multi-threaded application. If the method SetProgressState is called from a worker thread, then it crashes in Release(). – palota Feb 04 '16 at 15:16
0

Note you still need to call RegisterWindowMessage("TaskbarButtonCreated") and ChangeWindowMessageFilterEx() to setup an message filter before SetProgressValue() can work.

According to the MSDN docs you are supposed to recreate your object each time you get the created message but I found I just had to do the ChangeWindowMessageFilterEx() and it works fine for normal circumstances.

BartoszKP
  • 34,786
  • 15
  • 102
  • 130
Sirmabus
  • 636
  • 8
  • 8
  • 1
    I have written an application that uses `SetProgressValue` and works perfectly without `RegisterWindowMessage` nor `ChangeWindowMessageFilterEx`. As always, a lot of informations are missing in the documentation. – v77 Oct 31 '16 at 00:50