Question

I'm trying to use the ITaskbarList3 interface introduced with Windows 7 so that I can show task progress for a lengthy task in my taskbar icon. The documentation states that I should wait for a TaskbarButtonCreated message before trying to initialize my ITaskbarList3 component, but I don't seem to be getting any TaskbarButtonCreated messages.

Here is what I have so far:

I have a global variable in my .cpp file to store the custom message ID for TaskbarButtonCreated.

static const UINT m_uTaskbarBtnCreatedMsg = 
    RegisterWindowMessage( _T("TaskbarButtonCreated") );

I created a separate WndProc function to handle the new message.

void __fastcall TForm1::WndProcExt(TMessage &Message)
{
    if(Message.Msg == uTaskbarBtnCreatedMsg && uTaskbarBtnCreatedMsg != 0) {
        OnTaskbarBtnCreated();
    }
    else {
        WndProc(Message);
    }
}

In my form constructor, the very first line sets the WindowProc property to WndProcExt to route messages. I also tried tossing in a ChangeWindowMessageFilter to see if the TaskbarButtonCreated message was being filtered for some reason.

__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner)
{
    WindowProc = WndProcExt;
    ChangeWindowMessageFilterEx(Handle, uTaskbarBtnCreatedMsg, MSGFLT_ALLOW, NULL);

    ...
}

In the debugger, the return value from ChangeWindowMessageFilterEx is always true. I've also confirmed my WndProcExt function receives all kinds of Windows messages, just not the one I'm looking for. The OnTaskbarBtnCreated function never gets called.

Am I missing a step? Is the message being filtered out or sent before my message handler is ready for it?

Was it helpful?

Solution

It is not a good idea to have the TForm assign a value to its own WindowProc property. For starters, the Handle window may have already been allocated before your constructor is even entered, due to DFM streaming, so you would miss all of the window's initial messages (which there can be several) before your constructor starts running. You need to override the virtual WndProc() method instead, and do pass the TaskbarButtonCreated message to the default handler, don't block it:

static const UINT m_uTaskbarBtnCreatedMsg = RegisterWindowMessage( _T("TaskbarButtonCreated") );

void __fastcall TForm1::WndProc(TMessage &Message)
{
    TForm::WndProc(Message);
    if ((Message.Msg == uTaskbarBtnCreatedMsg) && (uTaskbarBtnCreatedMsg != 0))
        OnTaskbarBtnCreated();
}

As for ChangeWindowMessageFilterEx(), you need to call that every time the TForm's Handle window gets (re)allocated (which can happen multiple times during the Form's lifetime), so you need to override the virtual CreateWnd() method instead:

void __fastcall TForm1::CreateWnd()
{
    TForm::CreateWnd();
    if (CheckWin32Version(6, 1) && (uTaskbarBtnCreatedMsg != 0))
        ChangeWindowMessageFilterEx(Handle, uTaskbarBtnCreatedMsg, MSGFLT_ALLOW, NULL);
    // any other Handle-specific registrations, etc...
}

void __fastcall TForm1::DestroyWindowHandle()
{
    // any Handle-specific de-registrations, etc...
    TForm::DestroyWindowHandle();
}

Lastly, set the TApplication::ShowMainFormOnTaskbar property to true in the project's WinMain() function before your MainForm is created so its window, rather than the TApplication window, is managing the taskbar button (and to enable other Vista+ related features, like Flip 3D and Taskbar previews). Otherwise, you will have to use the TApplication::HookMainWindow() method to intercept any "TaskbarButtonCreated" messages that may get sent to the TApplication window.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top