Blog posts tagged: c++

News and other things I find interesting


RSS Feed


Feb
22
2011

Five Windows 7 taskbar features available to developers

Last modified: Thursday, September 08, 2011

Believe it or not the Windows 1.0 taskbar looked very similar to the Windows 7 taskbar. But it obviously didn't have anywhere near same functionality.

Windows 1.0:

Windows 7:

The Windows 7 taskbar is pretty awesome. And it has some great functionality available to developers who wish to take advantage of it.


The 5 features...

1) Application progress in the taskbar: Progress for anything with visual displays of errors and warnings.

2) Overlay icons: Small icons within your icon for any type of user notification (below shows the online status as an overlay)

3) Custom thumbnail preview groupings: Thumbnail previews across processes or within a single process with tabs

4) Taskbar action buttons (Thumbnail toolbars): Buttons inside the taskbar icon's thumbnail preview

5) Jump lists: Easily create shortcuts to different startup types and other custom actions.


Before you start...

Before jumping into the interesting stuff, you need to get notified that the taskbar button was created.

There is a new message sent to your application called TaskbarButtonCreated, it is sent when Windows creates a taskbar button for your application on your application's behalf.

And once this message is sent to you, you can interact with your taskbar button (Not before!). To get the identifier of the message you need to call RegisterWindowMessage .

The important elements here in a typical MFC application would look like so:

//Get the message identifier
const int TaskBarButtonCreated = RegisterWindowMessage(L"TaskbarButtonCreated");

//Put this in your message map to register a callback
ON_REGISTERED_MESSAGE( TaskBarButtonCreated, CTaskBarTestAppDlg::OnTarbarButtonCreated )

//The actual callback
LRESULT CTaskBarTestAppDlg::OnTarbarButtonCreated(WPARAM wParam, LPARAM lParam)
{
  //Custom code here once the taskbar is created
}

1) Application progress in the taskbar

To show progress in your application icon you need to use 2 simple API calls: ITaskbarList3::SetProgressState and ITaskbarList3::SetProgressValue.

Your taskbar icon can have one of the following states:

  • No progress (default)
  • Intermediate (green and cycles repeatedly along the length of the taskbar button)
  • Paused (yellow progress bar)
  • error (red progress bar)
  • normal (green progress bar)

You can see this in action yourself when Windows explorer is copying a set of files and it encounters a locked file, the windows explorer icon will change to the error progress status which is red.

When you use SHFileOperation or the IFileOperation interface your taskbar icon will automatically update as well with progress.

To get your application icon to show progress for an indeterminate amount of time:

m_pTaskBarlist->SetProgressState(GetSafeHwnd(), TBPF_INDETERMINATE);

To set determinate progress you use one of TBPF_NORMAL, TBPF_ERROR, or TBPF_PAUSED. For example:

m_pTaskBarlist->SetProgressState(GetSafeHwnd(), TBPF_NORMAL);
m_pTaskBarlist->SetProgressValue(GetSafeHwnd(), 10, 100);
//...
m_pTaskBarlist->SetProgressValue(GetSafeHwnd(), 20, 100);
//...
m_pTaskBarlist->SetProgressValue(GetSafeHwnd(), 100, 100);
//...
m_pTaskBarlist->SetProgressState(GetSafeHwnd(), TBPF_NOPROGRESS);

The last parameter of SetProgressValue is the number of steps in your operation total, the second last parameter is the current number of steps already completed.


2) Overlay icons

Icon overlays on the actual taskbar icon can be accomplished by calling TaskbarList3::SetOverlayIcon.

The overlay icon should be a small icon, measuring 16x16 pixels at 96 dpi. In my experience though you can use any icon and Windows will scale it for you. Every time this method is called, the previous icons get removed.

m_pTaskBarlist->SetOverlayIcon(GetSafeHwnd(), hMyIcon, L"Descriptive string for accessibility");

3) Custom thumbnail preview groupings:

A single application which has a single process with tabs (Or an MDI application) can have multiple previews available to it.

Chrome 10 Beta does not take advantage of tab/thumbnail yet; however, IE9 does (as seen in my screenshot above in section #3).

Every time a tab is created you would call both RegisterTab and SetTabOrder like so:

HRESULT hr0 = m_pTaskBarlist->RegisterTab(hTab1, GetSafeHwnd());
m_pTaskBarlist->SetTabOrder(hTab1, NULL);

SetTabOrder can be used to specify the order of previews, passing in NULL appends the tab to the end of the list of previews.

You can remove a tab preview from your taskbar icon by calling UnregisterTab:

m_pTaskBarlist->UnregisterTab(hTab1);

But sometimes applications today span multiple processes. Both IE 9 and Chrome use one process per tab, and I believe that Firefox will eventually go this way too (They already do this for mobile phones).
The Windows 7 taskbar has this architecture covered (People bust Microsoft's ass a lot but in many cases they really deliver).

In this previous blog post, I talked about Sessions, Windows Stations, and Desktops. Well as of Windows 7 there is also something called Application IDs. Application IDs allows Windows to know what you view as an individual application. With multi process applications on the rise this becomes important. Application IDs can be associated with individual Windows. An application ID is a string of up to 128 characters.

The default application ID for a window is the same as the application ID for the process to which the Window belongs. Applications with the same application ID are grouped to the same taskbar icon.

You can set the application ID of a process using the Win32 API SetCurrentProcessExplicitAppUserModelID You can set the application ID of a window using the Win32 API SHGetPropertyStoreForWindow and then calling functions on the return result which is of type IPropertyStore.

Application IDs are assigned dynamically so they can be changed at any time which means you could do some pretty interesting things.


4) Taskbar action buttons (Thumbnail toolbars):

Thumbnail toolbars allow you to add functionality and buttons available to the user without switching their current application. For example Windows Media Player adds previous, pause, and next button support. To do this you create an array of THUMBBUTTON structures.

When a thumbnail button is clicked you receive a WM_COMMAND message with the high word of the wParam set to THBN_CLICKED and the low word set the button ID.

Each button can have either an individual icon being used or a bitmap from the taskbar's image list which was set through ITaskbarList3::ThumbBarSetImageList.

THUMBBUTTON thbButtons[2];

//Initialize the first button
thbButtons[0].dwMask = THB_BITMAP | THB_TOOLTIP | THB_FLAGS;
thbButtons[0].iId = 0;
thbButtons[0].iBitmap = 0;
wcscpy(thbButtons[0].szTip, L"Button 1");
thbButtons[0].dwFlags = THBF_DISMISSONCLICK;

//Initialize the second button
dwMask = THB_BITMAP | THB_TOOLTIP;
thbButtons[1].dwMask = THB_BITMAP | THB_TOOLTIP;
thbButtons[1].iId = 1;
thbButtons[1].iBitmap = 1;
wcscpy(thbButtons[1].szTip, L"Button 2");

//Add the buttons to the thumbnail window
m_pTaskBarlist->ThumbBarAddButtons(hTab1, _countof(thbButtons), thbButtons);

Each tab thumbnail preview can have its own set of thumbnail buttons.

You may notice that the first button above has the THBF_DISMISSONCLICK flag but the second does not. The difference is that when the first button is clicked the thumbnail preview will be closed, whereas if the second button is clicked, the thumbnail preview remains open. Other important flags are THBF_HIDDEN, THBF_ENABLED, and THBF_DISABLED.

After the buttons are created, they can be updated by using ITaskbarList3::ThumbBarUpdateButtons. You would want to update an existing button to for example disable it.

Each time a button is clicked the OnCommand virtual function of your dialog is specified:

virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);

So you simply need to override this function for the window that is associated with the thumbnail preview:

BOOL CTaskBarTestAppDlg::OnCommand(WPARAM wParam, LPARAM lParam)
{
    UINT cmdID = LOWORD(wParam);
    switch(cmdID)
    {
    case ID_BUTTON1:
        MessageBox(_T("Button 1 was clicked"));
        break;
    case ID_BUTTON2:
        MessageBox(_T("Button 2 was clicked"));
        break;
    default:
        return CDialogEx::OnCommand(wParam, lParam);
    }
}

5) Jump lists:

There is a great article here on Jump lists, but I will give a quick overview.

Each application is associated with a Jump List.

You can customize the tasks area which is a set of actions. You can also customize the destination area which is a list of Recent items or Frequent items.

The recent items is calculated for you automatically as long as your application is the default handler for the associated document type. If you want to add somethign to the list you can use the Win32 API SHAddToRecentDocs.

You can customize custom areas of the jump list, for example Chrome includes a "Most visited section" and a "Recently closed" section.

Custom destinations are controlled by: ICustomDestinationList.


A couple other things you can do:

You can customize your taskbar thumbnails by specifying a clipped rectangle of your window:

RECT r = {0,0,100,100};
m_pTaskBarlist->SetThumbnailClip(GetSafeHwnd(), &r);

You can set a tooltip for your thumbnail previews as follows:

m_pTaskBarlist->SetThumbnailTooltip(GetSafeHwnd(), L"Test1");

Or if you have multiple tab thumbnail previews:

m_pTaskBarlist->SetThumbnailTooltip(hTab1, L"Test1");
m_pTaskBarlist->SetThumbnailTooltip(hTab2, L"Test1");

What about managed code?

There are a couple of good resources for tying into the taskbar API from managed languages:


Other reading:

Tags:

Add a new comment





Dec
18
2009

One of the joys of Python - Generators

Last modified: Friday, April 22, 2011

This post will use Fibonacci numbers as a tool to build up to explaining the usefulness of Python generators.

This post will feature both C++ and Python code.

Fibonacci numbers are defined as the sequence: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ....

Or in general:

F0 = 0
F1 = 1
Fn = Fn-1 + Fn-2

This can be transferred into a C++ function extremely easily:

size_t Fib(size_t n)
{ 
    //Fib(0) = 0
    if(n == 0)
        return 0;

    //Fib(1) = 1
    if(n == 1)
        return 1;

    //Fib(N) = Fib(N-2) + Fib(N-1)
    return Fib(n-2) + Fib(n-1);
}

But if you want to print the first 6 Fibonacci numbers, you will be recalculating a lot of the values with the above function.

For example: Fib(3) = Fib(2) + Fib(1), but Fib(2) also recalculates Fib(1). The higher the value you want to calculate, the worse off you will be.

So one may be tempted to re-write the above by keeping track of the state in main.

//Not supported for the first 2 elements of Fib
size_t GetNextFib(size_t &pp, size_t &p)
{
    int result = pp + p;
    pp = p;
    p = result;
    return result;
}

int main(int argc, char *argv[])
{
    size_t pp = 0;
    size_t p = 1;
    std::cout << "0 " << "1 ";
    for(size_t i = 0; i <= 4; ++i)
    {
        size_t fibI = GetNextFib(pp, p);            
        std::cout << fibI << " ";
    }   
return 0;
}

But this is very ugly, and it complicates our logic in main, it would be better to not have to worry about state in our main function.

We could return a vector of values and use an iterator to iterate over that set of values, but this requires a lot of memory all at once for a large number of return values.

So back to our old approach, what happens if we wanted to do something else besides print the numbers? We'd have to copy and paste the whole block of code in main and change the output statements to whatever else we wanted to do. And if you copy and paste code, then you should be shot. You don't want to get shot do you?

To solve these problems, and to avoid getting shot, we may re-write this block of code using a callback function. Every time a new Fibonacci number is encountered, we would call the callback function.

void GetFibNumbers(size_t max, void(*FoundNewFibCallback)(size_t))
{    
    if(max-- == 0) return;        
    FoundNewFibCallback(0);        
    if(max-- == 0) return;
    FoundNewFibCallback(1);

    size_t pp = 0;
    size_t p = 1;
    for(;;)
    {
        if(max-- == 0) return;
        int result = pp + p;
        pp = p;
        p = result;
        FoundNewFibCallback(result);
    }
}

void foundNewFib(size_t fibI)
{
    std::cout << fibI << " ";
}

int main(int argc, char *argv[])
{
    GetFibNumbers(6, foundNewFib);  
    return 0;
}

This is clearly an improvement, your logic in main is not as cluttered, and you can do anything you want with the Fibonacci numbers, simply define new callbacks.

But this is still not perfect. What if you wanted to only get the first 2 Fibonacci numbers, and then do something, then get some more, then do something else.

Well we could go on like we have been, and we could start adding state again into main, allowing GetFibNumbers to start from an arbitrary point. But this will further bloat our code, and it already looks too big for a simple task like printing Fibonacci numbers.

We could implement a producer and consumer model via a couple threads. But this complicates the code even more.

Instead let's talk about generators.

Python has a very nice language feature that solves problems like these called generators.

A generator allows you to execute a function, stop at an arbitrary point, and then continue again where you left off. Each time returning a value.

Consider the following code that uses a generator:

def fib():
    pp, p = 0, 1  
    while 1:
        yield pp
        pp, p = p, pp+p

g = fib()
for i in range(6):
    g.next()

Which gives us the results:

0
1
1
2
3
5

The yield statement is used in conjuction with Python generators. It saves the state of the function and returns the yeilded value. The next time you call the next() function on the generator, it will continue where the yield left off.

This is by far more clean than the callback function code. We have cleaner code, smaller code, and not to mention much more functional code (Python allows arbitrarily large integers).

Tags:

Add a new comment





Dec
12
2009

Forward declaring enums in C++

Last modified: Friday, April 22, 2011

Forward declaring things in C++ is very useful because it dramatically speeds up compilation time. You can forward declare several things in C++ including: struct, class, function, etc...

But can you forward declare an enum in C++?

No you can't.

But why not allow it? If it were allowed you could define your enum type in your header file, and your enum values in your source file. Sounds like it should be allowed right?

Wrong.

In C++ there is no default type for enum like there is in C# (int). In C++ your enum type will be determined by the compiler to be any type that will fit the range of values you have for your enum.

What does that mean?

It means that your enum's underlying type cannot be fully determined until you have all of the values of the enum defined. Which mans you cannot separate the declaration and definition of your enum. And therefore you cannot forward declare an enum in C++.

The ISO C++ standard S7.2.5:

The underlying type of an enumeration is an integral type that can represent all the enumerator values defined in the enumeration. It is implementation-defined which integral type is used as the underlying type for an enumeration except that the underlying type shall not be larger than int unless the value of an enumerator cannot fit in an int or unsigned int. If the enumerator-list is empty, the underlying type is as if the enumeration had a single enumerator with value 0. The value of sizeof() applied to an enumeration type, an object of enumeration type, or an enumerator, is the value of sizeof() applied to the underlying type.

You can determine the size of an enumerated type in C++ by using the sizeof operator. The size of the enumerated type is the size of its underlying type. In this way you can guess which type your compiler is using for your enum.

What if you specify the type of your enum explicitly like this:

enum Color : char { Red=0, Green=1, Blue=2};
assert(sizeof Color == 1);

Can you then forward declare your enum?

No. But why not?

Specifying the type of an enum is not actually part of the current C++ standard. It is a VC++ extension. It will be part of C++0x though.

Tags:

Add a new comment





Sep
9
2006

Visual studio C++ suggestion

Last modified: Friday, April 22, 2011

When adding a header file include that is not from a directory in your include directories, it should ask you: "Would you like to add [path] to your include directories?"

Tags:

Add a new comment





Jul
14
2006

Learning Cocoa with Objective C on XCode

Last modified: Friday, April 22, 2011

I've been starting to learn programming on my apple computer. The cool thing is that all dev tools on Mac are free unlike in windows. The bad part is that it is quite a bit different from programming Win32 :(. I guess maybe i should be using Carbon (Which is more for porting applications from C++). Apple claims that they will be updating both Cocoa and Carbon as time goes on equally.

Tags:

Add a new comment





Next page