A few months ago, we’ve taken a look at how you can extend your MDI or TDI application with an individual thumbnail and live preview for each tab (or document) that will be displayed in the Windows 7 taskbar. We’ve seen the temporary managed wrapper that makes this possible, and in the final 1.0 release of the Windows API Code Pack there is a more polished managed API that does the same thing.
However, in this post I’d like to focus on the underlying details which you will have to deal with if you’re writing your GUI application in C++. There’s a hands-on lab that I wrote for the latest release of the Windows 7 Training Kit that shows how to take a Win32 dialog with a tab control and multiple tab pages, and turn each tab page into a thumbnail with a preview.
You can go ahead and do the hands-on lab—there’s a fairly comprehensive document that tells you everything you need to along the way—or you can read this (more brief) description.
What you need to do to support “tabbed thumbnails” is the following:
- If your application process has a high integrity level (e.g. if it’s usually launched as administrator), make sure that the DWM messages can pass through, or else they will be filtered by UIPI. Specifically, call the ChangeWindowMessageFilter API and enable the WM_DWMSENDICONICTHUMBNAIL and the WM_DWMSENDICONICLIVEPREVIEWBITMAP window messages to pass through.
- Figure out a way to grab a live bitmap rendering of each individual tab—this bitmap must be 32-bit ARGB. This sounds easy, and it indeed might be easy if you have your own custom rendering code. This isn’t easy, however, if you’re just using standard Win32 controls—if the control is not currently visible, you might have to go through hoops to grab a preview of it (e.g. in a TDI application, you could grab a preview of a tab just before switching to another tab).
- For each tab window (which is usually a child window), create a top-level proxy invisible window that will deal with the window messaging required to make the thumbnail work. As far as the taskbar and the DWM are concerned, these top-level proxy windows are the ones showing the tab thumbnails, not your application’s “real” tab windows, so all the API calls below have to be performed with the proxy window handles as the parameters. (The reason for this is a limitation of the DWM, which can’t interact with non-top-level windows.)
- As you create your proxy windows:
- Call the DwmSetWindowAttribute method to set the DWMWA_FORCE_ICONIC_REPRESENTATION and the DWMWA_HAS_ICONIC_THUMBNAIL window attributes. This instructs the DWM to poll your proxy windows for the taskbar thumbnail and live preview.
- Register them to appear in the taskbar by calling the ITaskbarList4::RegisterTab method, and set their relative order with regard to the other tabs by calling the ITaskbarList4::SetTabOrder method. (ITaskbarList4 is a minor extension of ITaskbarList3.)
- When a tab becomes active, call the ITaskbarList4::SetTabActive method so that this tab’s thumbnail appears as active.
- When the proxy window’s procedure receives the WM_DWMSENDICONICTHUMBNAIL message, call the DwmSetIconicThumbnail method and pass to it the window’s thumbnail bitmap. Note that the dimensions of the thumbnail are specified as the high-word and low-word of the lParam message parameter, and you must adhere to these dimensions.
- When the proxy window’s procedure receives the WM_DWMSENDICONICLIVEPREVIEWBITMAP message, call the DwmSetIconicLivePreviewBitmap method and pass to it the window’s live preview bitmap. If the window does not overlap the entire application frame, you can use an additional offset parameter to specify the offset of the provided bitmap from the top-left corner of the application’s window. The rest of the window will be captured by DWM automatically for you. (This means that Internet Explorer, for example, doesn’t have to grab a bitmap of the entire window, only of the tab itself. The menus and toolbars could be taken care of by DWM.)
- When the proxy window’s procedure receives the WM_ACTIVATE message, switch to the corresponding tab window in your application UI.
- When the proxy window’s procedure receives the WM_SYSCOMMAND message, check wParam for the SC_CLOSE code. Any other codes should be forwarded to the application’s main window; SC_CLOSE should be handled by the proxy window’s window procedure (e.g. you can call DefWindowProc that will send you a WM_CLOSE message).
As you can see, there’s a lot of manual labor in having to implement all of this logic every time you want tabbed thumbnails. Even though it’s obviously your responsibility to obtain the thumbnail bitmap and live preview bitmap, and to calculate their offset from the application frame, at least some of the proxy windowing work can be streamlined. For this purpose, the hands-on lab I’ve mentioned earlier provides a TabWindow class, that I will cover in a subsequent post.